MDIを使ったエディターの作成 ステップ3


■ エディターに行番号を追加
 今回は、サンプルページにある「エディターに行番号を追加」を利用して、行番号を追加してみましょう。
作成手順は、サンプルページに記載されているので、ここでは大まかな説明だけにします。

@ プロジェクトにファイルを追加
 まず、サンプルのプロジェクトから、このプロジェクトのディレクトリに、LineWnd.cppとLineWnd.cppをコピーします。
(このプロジェクトの原型は、ステップ2のプロジェクトとします。)
次に、コピーした2つのファイルをプロジェクトに追加します。
[プロジェクト]メニューの[プロジェクトへ追加]の下の[ファイル]を選択します。
プロジェクトへファイルを追加のファイルダイアログが表示されるので、2つのファイルを選択し、[OK]ボタンを押します。
Class ViewにCLineWndが追加されればOKです。

Class View

A ソースコードの変更(CLineWnd)
まずLineWnd.cppに、使用するプロジェクトが変わったためLineWnd.cppのインクルードファイルの行だけ、以下のように変更します。
#include "LEditor.h"
の行を
#include "MDIEditor.h"
に変更します。

 また、CLineWndのフォントに関して、サンプルではうまく表示されていましたが、実際はフォントがちゃんと伝わっていなかったので、この例ではその修正が入っています。
まず、LineWnd.hに以下の変数を追加します。この変数はpublicです。
    CFont*      m_pFont;            // 行番号のフォント.
そして、OnPaint内で、親が設定したフォントを使ってテキストを出力するように変更します。
void CLineWnd::OnPaint() 
{
        CPaintDC dc(this); // 描画用のデバイス コンテキスト
        
        // TODO: この位置にメッセージ ハンドラ用のコードを追加してください
        CEditView*      pParent         = ( CEditView*)GetParent();
        CRect           cr;
        CFont*          pOldFont;                       // フォント格納用.
        GetClientRect( cr);
        dc.FillSolidRect( &cr, RGB( 225, 225, 240));    // 背景色ライトグレー

        m_nStart                = pParent->GetEditCtrl().GetFirstVisibleLine();
        m_nLineCount    = pParent->GetEditCtrl().GetLineCount();
        CString                 cs;
        int                             y = m_spt.y;
        LOGFONT                 lf;
        int                             pitch;          // フォントの高さ.

        m_pFont->GetLogFont( &lf);
        pitch = abs( lf.lfHeight);                      // フォントの高さ取り出し.

        pOldFont = dc.SelectObject( m_pFont);   // フォントの設定.
        for( int n=m_nStart; n<m_nLineCount; n++)
        {
                cs.Format( "%5d", n + 1);
                dc.TextOut( m_spt.x, y, cs);
                y += pitch;
                if ( ( y + pitch) > cr.bottom)
                        break;
        }
        dc.SelectObject( pOldFont);             // フォントの解除.
        
        // 描画用メッセージとして CWnd::OnPaint() を呼び出してはいけません
}

B ソースコードの変更(CMDIEditorView)
まず、Class ViewのCMDIEditorViewを右クリックし、[メンバ変数の追加]を選択し以下の変数を追加します。
        CLineWnd*       m_pWnd;

また、MDIEditorView.hに、以下の行を追加します。
#include "LineWnd.h"
次に、MDIEditorView.cppの最初に以下の行を追加します。
#define LINE_WIDTH                   50
まず、コンストラクタ、デストラクタとウィンドウの初期化を変更します。
CMDIEditorView::CMDIEditorView()
{
        // TODO: この場所に構築用のコードを追加してください。
        m_pFindDlg = NULL;
        m_FindStr = _T("");
        m_ReplaceStr = _T("");
        m_bSearchDown = TRUE;           // 下方検索.
        m_bMachCase = FALSE;            // 大文字小文字判定無し.
        // 行番号表示用
        m_pWnd = NULL;
        m_LastLineCount = 0;
}

CMDIEditorView::~CMDIEditorView()
{
        // 行番号表示用
        if ( m_pWnd)
                delete m_pWnd;
}

BOOL CMDIEditorView::PreCreateWindow(CREATESTRUCT& cs)
{
        // TODO: この位置で CREATESTRUCT cs を修正して Window クラスまたはスタイルを
        //  修正してください。

        BOOL bPreCreated = CEditView::PreCreateWindow(cs);
//      cs.style &= ~(ES_AUTOHSCROLL|WS_HSCROLL);       // ワード ラップを使用可能にします
        cs.style |= WS_CLIPCHILDREN;
        cs.style |= (ES_MULTILINE|WS_VSCROLL);          // マルチライン.
        cs.style |= ES_NOHIDESEL;
        cs.style |= WS_HSCROLL;

        return bPreCreated;
}

次は、CMDIEditorView::OnCreateの変更です。
前回までに追加した後に、今回の変更を加えます。
この部分は、サンプルとは少し違っています。というのは、サンプルの場合デフォルトのフォントを調べてその値を使っていましたが、今回はCMainFrameでフォントを作成しているのでその値を呼び出しています。
int CMDIEditorView::OnCreate(LPCREATESTRUCT lpCreateStruct) 
{
        if (CEditView::OnCreate(lpCreateStruct) == -1)
                return -1;
        
        // TODO: この位置に固有の作成用コードを追加してください
        CMainFrame*             pMainFrame = ( CMainFrame*)theApp.m_pMainWnd;

        SetFont( &pMainFrame->m_EditorFont);
        // 行番号表示.
        DWORD           mgn;
        WORD            ml, mr;

        mgn = GetEditCtrl().GetMargins();
        mr = HIWORD( mgn);
        ml = LINE_WIDTH + 5;            // 行番号表示スペースの確保.
        GetEditCtrl().SetMargins( ml, mr);

        CRect           rc;

        m_pWnd = new CLineWnd;          // 行番号表示用ウィンドウの作成.
        m_pWnd->m_pFont = &pMainFrame->m_EditorFont;
        m_pWnd->Create( NULL, "", WS_CHILD | WS_VISIBLE, rc, this, 0);
        return 0;
}
 ここで、CLineWndに関して少し説明します。
CLineWndは、名前の通りCWndの派生クラスです。派生クラスとは基のクラスCWndを継承したものです。すなわちプログラマーがこのクラスに何も新しい処理を加えないと基のクラスとまったく同じことになります。
ここでは、CWndに行番号を表示する機能を追加してあります。
このウィンドウは、CMDIEditorViewの子供になり、ウィンドウの大きさ、位置は親が指定します。
また親子間のやり取りは、最大行数、現在の行番号を子供に通知し、後はウィンドウを描き直す命令だけです。

続いて、クラスウィザードでOnSize関数を追加して、以下の変更を加えます。

クラスウィザード

void CMDIEditorView::OnSize(UINT nType, int cx, int cy) 
{
    CEditView::OnSize(nType, cx, cy);
        
    // TODO: この位置にメッセージ ハンドラ用のコードを追加してください
    CRect           rc;

    if ( m_pWnd)
    {
        GetClientRect( rc);
        rc.right = rc.left + LINE_WIDTH;
        m_pWnd->MoveWindow( rc);
        GetEditCtrl().GetRect( &rc);
        m_pWnd->m_spt.y = rc.top;
    }
}
同様に、OnInitialUpdateOnChangeOnVScrollOnVscrollも追加し、処理を追加し変更します。
oid CMDIEditorView::OnInitialUpdate() 
{
        CEditView::OnInitialUpdate();
        
        // TODO: この位置に固有の処理を追加するか、または基本クラスを呼び出してください
        if ( m_pWnd)
                m_pWnd->RedrawWindow();
}

void CMDIEditorView::OnChange() 
{
        // TODO: これが RICHEDIT コントロールの場合、コントロールは、 lParam マスク
        // 内での論理和の ENM_CHANGE フラグ付きで CRichEditCrtl().SetEventMask()
        // メッセージをコントロールへ送るために CEditView::OnInitDialog() 関数をオーバー
        // ライドしない限りこの通知を送りません。
        
        // TODO: この位置にコントロール通知ハンドラ用のコードを追加してください
        int                     LCount = GetEditCtrl().GetLineCount();
        if ( LCount != m_LastLineCount)
        {
                if ( m_pWnd)
                        m_pWnd->RedrawWindow();
                m_LastLineCount = LCount;
        }
        CEditView::OnEditChange();              // SetModify()が効かない.
}

void CMDIEditorView::OnVscroll() 
{
        // TODO: この位置にコントロール通知ハンドラ用のコードを追加してください
        if ( m_pWnd)
                m_pWnd->RedrawWindow();
}

void CMDIEditorView::OnVScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar) 
{
        // TODO: この位置にメッセージ ハンドラ用のコードを追加するかまたはデフォルトの処理を呼び出してください
        if ( m_pWnd)
                m_pWnd->RedrawWindow();
        
        CEditView::OnVScroll(nSBCode, nPos, pScrollBar);
}
これらは、サンプルと同じですがOnChangeの最後にCEditViewのOnEditChangeを呼んでいるのは、エディタ終了時に修正があった場合、保存の確認を聞いてきますがこのためのフラグをセットするためです。

■ 実行してみましょう
 ここまでをビルドして実行してみましょう。

上図では、子ウィンドウを最大化してあります。

以上で、今回の変更は終了です。
注意事項ですが、行番号は画面だけで、印刷時は表示されません。
印刷はもちろん可能ですが、処理が複雑になるのでここでは対応していません。
興味がある人はチャレンジしてみてください。
■ サンプルプロジェクトファイルのダウンロード
(VC++6.0プロジェクトファイル25KB)