Win32プログラミング講座 〜 Step11. テキスト エディタを作る 〜


今回は、必要最小限の機能がついたテキスト エディタを製作します。
必要最小限の機能ってどんなものがあるの?

・開く、保存

だけです(キッパリ)。しかし、これではさすがに寂しすぎるので、コピー&ペーストなどの編集メニューまでをこのページで解説することにします。

さっそく作ろう!

ノーマル ウィンドウ ベースのプロジェクト(プロジェクト名を "TextEditor" とします)を作成し、メニューの作成(IDは "MainMenu")、メインウィンドウへのメニューの貼り付けを行っておきましょう。

テキスト エディタには欠かせない、エディット コントロールの挿入を行います。挿入する場所は適当で構いません。

こんなチッポケなエディット ボックスでいいの??
はい、このままにしておきましょう。テキストエディタは自由にウィンドウのサイズが変更できますよね?(最大化したり、伸縮したり)
そのため、ユーザーの利用状況により、プログラム側でサイズを変更しなければなりません。具体的には、Resizeイベント(ウィンドウの大きさが変更されたときのイベント)プロシージャ内に、エディット コントロールの大きさをウィンドウの大きさにあわせてやるプログラムをかくのです。Resizeイベントのコーディングは後に行います。

Resizeイベントは初期時にも呼び出されるので、ここでエディット コントロールのサイズを変更しても特に意味はありません。しかし、チッポケなのが気になる方はご自由に、RADツール内のウィンドウの大きさにあわせてもらっても結構です。

挿入したばかりのエディット コントロールでは設定不足な点があるため、Editプロパティを下のように設定しておきましょう。


先ほど挿入した、ウィンドウ内のエディット コントロールを選択し、Editプロパティ内の「複数行」、「水平スクロールバー」、「水平オートスクロール」、「垂直スクロールバー」、「垂直オートスクロール」、「改行を許可」にチェックを入れます。

Resizeイベントのコーディング

"MainWnd" 内の適当な場所を右クリックし、「イベント コード」をクリックします。記述可能なイベントが色々と表示されますが、その中からResizeイベントを選択し、「コーディング」ボタンをクリックします。表示されたプロシージャに下のプログラムを書き込みます。

Sub MainWnd_Resize(SizeType As Long, cx As Integer, cy As Integer)
    Dim rc As RECT
    Dim hEdit As Long

    'クライアント領域のサイズを取得
    GetClientRect(hMainWnd,rc)

    'EditBox1のウィンドウ ハンドルを取得
    hEdit=GetDlgItem(hMainWnd,EditBox1)

    'hEditの大きさを変更する
    MoveWindow(hEdit, 0, 0, rc.right, rc.bottom,0)
End Sub

メニューの編集

"MainMenu" を開き、下のような構成になるよう、メニューの追加を行っていきます。メニュー項目を挿入する場合は、挿入したい場所の1つ上の項目を選択し、「挿入」ボタンをクリックしていくことを思い出しましょう。メニューの編集方法は、ステップ6ステップ7で解説しています。

キャプションID備考
ファイル(&F)-ポップアップ
新規作成(&N)IDM_NEW-
--セパレータ
開く(&O)IDM_OPEN-
保存(&S)IDM_SAVE-
--セパレータ
終了(&X)IDM_EXIT-
編集(&E)-ポップアップ
元に戻す(&U)IDM_UNDO-
--セパレータ
切り取り(&T)IDM_CUT-
コピー(&C)IDM_COPY-
貼り付け(&P)IDM_PASTE-
--セパレータ
すべて選択(&A)IDM_ALLSELECT-
ヘルプ(&H)-ポップアップ
バージョン情報(&A)IDM_ABOUT-

上のような構造を持たせた "MainMenu" を、"MainWnd" に貼り付けたのち、それぞれのメニューに対するイベント コーディングを行います。

「新規作成」メニュー イベント

EditBox1の内容をSetWindowText関数を利用して空にします。EM_SETMODIFYメッセージで、テキストの変更を示すフラグをFALSEにセットしています。

Sub MainWnd_IDM_NEW_MenuClick()
    Dim hEdit As Long

    'EditBox1のハンドルを取得
    hEdit=GetDlgItem(hMainWnd, EditBox1)

    'hEditの内容を空にする
    SetWindowText(hEdit,"")
End Sub

「開く」メニュー イベント

初めて利用するAPI関数がいくつかあるかもしれません。GetOpenFileName関数は前のステップ8でやりましたね。ファイルオープンにCreateFile関数が、ファイルからのデータの読み込みにReadFile関数などが利用されているところもおさえておくと良いでしょう。
プロシージャの最後の部分で、ファイル データが格納されている文字列変数bufferの内容を、SetWindowText関数を利用してウィンドウに反映させています。

Sub MainWnd_IDM_OPEN_MenuClick()
    Dim hEdit As Long
    Dim ofn As OPENFILENAME
    Dim hFile As Long
    Dim dwFileSize As DWord
    Dim dwAccessByte As DWord
    Dim FileName[MAX_PATH-1] As Byte
    Dim buffer As String

    'OPENFILENAME構造体の初期化
    FillMemory(VarPtr(ofn),Len(ofn),0)
    ofn.lStructSize=Len(ofn)
    ofn.hwndOwner=hMainWnd
    ofn.lpstrFilter=Ex"テキスト ファイル(*.txt)\0*.txt\0すべてのファイル(*.*)\0*\0\0"
    ofn.nFilterIndex=1
    ofn.lpstrFile=FileName
    ofn.nMaxFile=MAX_PATH
    ofn.lpstrTitle="ファイルを開く"
    ofn.Flags=OFN_FILEMUSTEXIST or OFN_HIDEREADONLY or OFN_PATHMUSTEXIST
    ofn.lpstrDefExt="*"

    '「ファイルを開く」ダイアログ ボックスを表示
    If GetOpenFileName(ofn)=0 Then Exit Sub


    '-------------------
    ' ファイル オープン
    '-------------------

    hFile=CreateFile(ofn.lpstrFile, GENERIC_READ, FILE_SHARE_READ or FILE_SHARE_WRITE, _
                     ByVal 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0)
    If hFile=INVALID_HANDLE_VALUE Then
        MessageBox(hMainWnd,"ファイルオープンに失敗","TextEditor",MB_OK or MB_ICONEXCLAMATION)
        Exit Sub
    End If

    'ファイルサイズを取得
    dwFileSize=GetFileSize(hFile,0)

    '文字列バッファ用のメモリを確保
    buffer=ZeroString(dwFileSize)

    'ファイルの内容を文字列変数bufferに読み込む
    ReadFile(hFile,buffer,dwFileSize,VarPtr(dwAccessByte),ByVal 0)

    'ファイル ハンドルを閉じる
    CloseHandle(hFile)

    '-------------------


    'EditBox1のハンドルを取得
    hEdit=GetDlgItem(hMainWnd, EditBox1)

    'hEditにバッファをセットする
    SetWindowText(hEdit,buffer)

    'hEditにフォーカスをセット
    SetFocus(hEdit)
End Sub

「保存」メニュー イベント

「ファイルの保存」ダイアログボックスを表示し、ofn.lpstrFileに格納されたファイルへ保存するプログラムになります。「開く」メニュー イベントと少し似ていますね。

Sub MainWnd_IDM_SAVE_MenuClick()
    Dim hEdit As Long
    Dim ofn As OPENFILENAME
    Dim hFile As Long
    Dim length As DWord
    Dim dwAccessByte As DWord
    Dim FileName[MAX_PATH] As Byte
    Dim buffer As String

    'OPENFILENAME構造体の初期化
    FillMemory(VarPtr(ofn),Len(ofn),0)
    ofn.lStructSize=Len(ofn)
    ofn.hwndOwner=hMainWnd
    ofn.lpstrFilter=Ex"テキスト ファイル(*.txt)\0*.txt\0すべてのファイル(*.*)\0*\0\0"
    ofn.nFilterIndex=1
    ofn.lpstrFile=FileName
    ofn.nMaxFile=MAX_PATH
    ofn.lpstrTitle="ファイルの保存"
    ofn.Flags=OFN_FILEMUSTEXIST or OFN_HIDEREADONLY or OFN_PATHMUSTEXIST
    ofn.lpstrDefExt="*"

    '「ファイルの保存」ダイアログ ボックスを表示
    If GetSaveFileName(ofn)=0 Then Exit Sub


    'EditBox1のハンドルを取得
    hEdit=GetDlgItem(hMainWnd, EditBox1)

    'テキスト データを格納するためのバッファ領域を確保
    length=GetWindowTextLength(hEdit)
    buffer=ZeroString(length+1)

    'テキスト バッファを取得
    GetWindowText(hEdit, buffer, length+1)


    '----------------
    ' ファイルへ保存
    '----------------

    hFile=CreateFile(ofn.lpstrFile, GENERIC_WRITE, 0, _
                     ByVal 0, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0)
    If hFile=INVALID_HANDLE_VALUE Then
        MessageBox(hMainWnd,"ファイルオープンに失敗","TextEditor",MB_OK or MB_ICONEXCLAMATION)
        Exit Sub
    End If

    '書き込む
    WriteFile(hFile,buffer,length,VarPtr(dwAccessByte),ByVal 0)

    'ファイル ハンドルを閉じる
    CloseHandle(hFile)
End Sub

「終了」メニュー イベント

hMainWndに対して、WM_CLOSE メッセージを送ります。

Sub MainWnd_IDM_EXIT_MenuClick()
    SendMessage(hMainWnd,WM_CLOSE,0,0)
End Sub

「元に戻す」メニュー イベント

「編集」関連のメニューイベントのほとんどは、(WM_???)というメッセージを送るだけでよいので楽チンです(エディット コントロール任せ)。
Edit1に対して、WM_UNDO メッセージを送ります。

Sub MainWnd_IDM_UNDO_MenuClick()
    SendMessage(GetDlgItem(hMainWnd,EditBox1), WM_UNDO, 0, 0)
End Sub

「切り取り」メニュー イベント

Edit1に対して、WM_CUT メッセージを送ります。

Sub MainWnd_IDM_CUT_MenuClick()
    SendMessage(GetDlgItem(hMainWnd,EditBox1), WM_CUT, 0, 0)
End Sub

「コピー」メニュー イベント

Edit1に対して、WM_COPY メッセージを送ります。

Sub MainWnd_IDM_COPY_MenuClick()
    SendMessage(GetDlgItem(hMainWnd,EditBox1), WM_COPY, 0, 0)
End Sub

「貼り付け」メニュー イベント

Edit1に対して、WM_PASTE メッセージを送ります。

Sub MainWnd_IDM_PASTE_MenuClick()
    SendMessage(GetDlgItem(hMainWnd,EditBox1), WM_PASTE, 0, 0)
End Sub

「すべて選択」メニュー イベント

EM_SETSEL メッセージで、3番目のパラメータに始点を、4番目のパラメータに終点を指定することで、「すべて選択」の機能を実現することができます。

Sub MainWnd_IDM_ALLSELECT_MenuClick()
    Dim hEdit As Long
    Dim length As Long

    'EditBox1のハンドルを取得
    hEdit=GetDlgItem(hMainWnd,EditBox1)

    'テキスト データの長さを取得
    length=GetWindowTextLength(hEdit)

    'すべての部分を選択する
    SendMessage(hEdit,EM_SETSEL,0,length)
End Sub

「バージョン情報」メニュー イベント

ここは、MessageBox関数を利用し、バージョン情報を表示するだけです。

Sub MainWnd_IDM_ABOUT_MenuClick()
    MessageBox(hMainWnd,"TextEditor Ver1.00","バージョン情報",MB_OK or MB_ICONINFORMATION)
End Sub

おまけですが、"MainWnd" のRAD画面を開き、ウィンドウプロパティ内のフォントを、"FixedSys"(サイズ14)に変更してみると見やすくなりますよ☆

これですべての作業が完了です。ファイルの入出力などに気を配ってやれば、後は意外と簡単だったかもしれません。このプログラムはテキスト エディタの基礎部分にすぎないので、やる気のある方は、機能追加にチャレンジしてみても良いでしょう。

前へ戻る - 次へ進む
トップへ戻る


©2005 Discoversoft