第140章 RTFエディタににツールバーをつける


ツールバーについては 第61章から第66章 まで解説があります。せっかくですから今まで作ってきたRTFエディタに ツールバーをつけてみます。そして、実際のプログラミング上での 問題点について考えてみます。



ツールバーをつけると見栄えが良くなります。起動時には横書きで 左揃えですから、それぞれのボタンは押された状態です。左から 8番目までのボタンは既製品です。9番目以降は自作のものです。

では、プログラムを見てみましょう。

// rich11.rcの一部 ///////////////////////////////////////////////////////////////////////////// // // Menu // MYMENU MENU DISCARDABLE BEGIN POPUP "ファイル(&F)" BEGIN MENUITEM "新規作成(&N)", IDM_NEW MENUITEM "開く(&O)", IDM_OPEN MENUITEM SEPARATOR MENUITEM "上書き保存(&S)", IDM_SAVE MENUITEM "名前をつけて保存(&A)", IDM_SAVEAS MENUITEM SEPARATOR MENUITEM "プリンターの設定(&U)", IDM_PRNSET MENUITEM "印刷(&P)", IDM_PRINT MENUITEM SEPARATOR MENUITEM "終了(&X)", IDM_END END POPUP "編集(&E)" BEGIN MENUITEM "元に戻す(&U)", IDM_UNDO MENUITEM SEPARATOR MENUITEM "横書き(&H)", IDM_HORIZONTAL MENUITEM "縦書き(&V)", IDM_VERTICAL MENUITEM SEPARATOR MENUITEM "フォントの変更(&F)", IDM_FONT POPUP "段落書式" BEGIN MENUITEM "中央揃え(&C)", IDM_CENTER MENUITEM "左揃え(&L)", IDM_LEFT MENUITEM "右揃え(&R)", IDM_RIGHT END MENUITEM SEPARATOR MENUITEM "コピー(&C)", IDM_COPY MENUITEM "切り取り(&T)", IDM_CUT MENUITEM "貼り付け(&P)", IDM_PASTE MENUITEM SEPARATOR MENUITEM "すべて選択(&L)", IDM_ALL END POPUP "表示(&V)" BEGIN MENUITEM "背景色の変更(&B)", IDM_BACKCOLOR END END MYPOPUP MENU DISCARDABLE BEGIN POPUP "ダミーです" BEGIN MENUITEM "新規作成", IDM_NEW MENUITEM "開く", IDM_OPEN MENUITEM SEPARATOR MENUITEM "上書き保存", IDM_SAVE MENUITEM "名前をつけて保存", IDM_SAVEAS MENUITEM SEPARATOR MENUITEM "横書き", IDM_HORIZONTAL MENUITEM "縦書き", IDM_VERTICAL MENUITEM SEPARATOR MENUITEM "フォントの変更", IDM_FONT MENUITEM SEPARATOR MENUITEM "左揃え", IDM_LEFT MENUITEM "右揃え", IDM_RIGHT MENUITEM "中央揃え", IDM_CENTER MENUITEM SEPARATOR MENUITEM "終了", IDM_END MENUITEM SEPARATOR MENUITEM "コピー", IDM_COPY MENUITEM "切り取り", IDM_CUT MENUITEM "貼り付け", IDM_PASTE MENUITEM "元に戻す", IDM_UNDO MENUITEM SEPARATOR MENUITEM "印刷", IDM_PRINT MENUITEM "プリンタの設定", IDM_PRNSET MENUITEM SEPARATOR MENUITEM "すべて選択", IDM_ALL MENUITEM SEPARATOR MENUITEM "背景色の変更", IDM_BACKCOLOR END END ///////////////////////////////////////////////////////////////////////////// // // Toolbar // ID_MYTOOLBAR TOOLBAR DISCARDABLE 16, 15 BEGIN BUTTON IDM_VERTICAL BUTTON IDM_HORIZONTAL BUTTON IDM_LEFT BUTTON IDM_CENTER BUTTON IDM_RIGHT BUTTON IDM_FONT BUTTON IDM_BACKCOLOR END ///////////////////////////////////////////////////////////////////////////// // // Bitmap // ID_MYTOOLBAR BITMAP DISCARDABLE "mytoolba.bmp"

ツールバーの部分と、そのビットマップが増えました。 ビットマップの実物(mytoolba.bmp)は

です。実際にはリソースエディタで作っています。

// rich11.cpp #define STRICT #include <windows.h> #include <richedit.h> #include "resource.h" #include <commctrl.h> #define ID_TOOLBAR 100 LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); DWORD CALLBACK MySaveProc(DWORD, LPBYTE, LONG, LONG *); DWORD CALLBACK MyReadProc(DWORD, LPBYTE, LONG, LONG *); BOOL InitApp(HINSTANCE); BOOL InitInstance(HINSTANCE, int); BOOL SetInitialFont(HWND hWnd); BOOL SetMyFont(HWND); BOOL SetCenter(HWND); BOOL SetLeft(HWND); BOOL SetRight(HWND); void ClearCheck(HMENU);//段落書式のチェックをはずす void RTF_Save(HWND); void RTF_SaveAs(HWND); void RTF_Open(HWND); void RTF_CheckMenu(HWND, HMENU); void RTF_Print(HWND); void RTF_All(HWND); HDC GetPrintInfo(void); int PrinterSet(HWND); int RTF_AddPageNo(HDC, int); void RTF_SetWYSIWYG(HWND); void RTF_New(HWND); void RTF_Vertical(HWND); void RTF_Horizontal(HWND); void RTF_BackColor(HWND); void CheckButtonState(HWND, HWND); HWND MakeMyToolbar(HWND); char szClassName[] = "rich11"; //ウィンドウクラス HINSTANCE hInst; DWORD dwMyMask; //CHARFORMATで使うマスク値 char szFName[MAX_PATH]; int nFileType = 1; //1:RTF 2:TXT 3:All char *szAppTitle = "猫でもわかるRTF"; PRINTER_INFO_5 prninfo[3]; TBBUTTON tbBut[] = { {STD_FILENEW, IDM_NEW, TBSTATE_ENABLED, TBSTYLE_BUTTON, 0, 0, 0}, {STD_FILEOPEN, IDM_OPEN, TBSTATE_ENABLED, TBSTYLE_BUTTON, 0, 0, 0}, {STD_FILESAVE, IDM_SAVE, TBSTATE_ENABLED, TBSTYLE_BUTTON, 0, 0, 0}, {STD_PRINT, IDM_PRINT, TBSTATE_ENABLED, TBSTYLE_BUTTON, 0, 0, 0}, {STD_COPY, IDM_COPY, TBSTATE_ENABLED, TBSTYLE_BUTTON, 0, 0, 0}, {STD_CUT, IDM_CUT, TBSTATE_ENABLED, TBSTYLE_BUTTON, 0, 0, 0}, {STD_PASTE, IDM_PASTE, TBSTATE_ENABLED, TBSTYLE_BUTTON, 0, 0, 0}, {STD_UNDO, IDM_UNDO, TBSTATE_ENABLED, TBSTYLE_BUTTON, 0, 0, 0}, }; TBBUTTON tbSep = {0, 0, TBSTATE_ENABLED, TBSTYLE_SEP, 0, 0, 0}; TBBUTTON tbBmp[] = { {0, IDM_VERTICAL, TBSTATE_ENABLED, TBSTYLE_BUTTON, 0, 0, 0}, {1, IDM_HORIZONTAL, TBSTATE_ENABLED, TBSTYLE_BUTTON, 0, 0, 0}, {2, IDM_LEFT, TBSTATE_ENABLED, TBSTYLE_BUTTON, 0, 0, 0}, {3, IDM_CENTER, TBSTATE_ENABLED, TBSTYLE_BUTTON, 0, 0, 0}, {4, IDM_RIGHT, TBSTATE_ENABLED, TBSTYLE_BUTTON, 0, 0, 0}, {5, IDM_FONT, TBSTATE_ENABLED, TBSTYLE_BUTTON, 0, 0, 0}, {6, IDM_BACKCOLOR, TBSTATE_ENABLED, TBSTYLE_BUTTON, 0, 0, 0} };

今回新たに作るCheckButtonState関数とMakeMyToolbar関数のプロトタイプ宣言が 増えました。また、TBBUTTON構造体の配列も増えました。 commctrl.hのインクルードも忘れないでください。 また、comctl32.libをプロジェクトに参加させるのも忘れないでください。

int WINAPI WinMain(HINSTANCE hCurInst, HINSTANCE hPrevInst, LPSTR lpsCmdLine, int nCmdShow) BOOL InitApp(HINSTANCE hInst) BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)

は前回までと全く同じなので省略します。

//ウィンドウプロシージャ LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp) { int id; static HINSTANCE hRtLib; static HWND hEdit, hTool; DWORD dwEvent; MSGFILTER *pmf; HMENU hMenu, hSub; int x, y; POINT pt; int nToolH; RECT rc; switch (msg) { case WM_CREATE: hTool = MakeMyToolbar(hWnd); CheckButtonState(hTool); hRtLib = LoadLibrary("RICHED32.DLL"); hEdit = CreateWindowEx(WS_EX_CLIENTEDGE, "RICHEDIT", "", WS_CHILD | WS_VISIBLE | WS_BORDER | ES_MULTILINE | WS_HSCROLL | WS_VSCROLL | ES_AUTOVSCROLL | ES_AUTOHSCROLL | ES_NOHIDESEL, 0, 0, 0, 0, //とりあえず幅、高さ0のウィンドウを作る hWnd, (HMENU)1, hInst, NULL); dwEvent = SendMessage(hEdit, EM_GETEVENTMASK, 0, 0); dwEvent |= ENM_MOUSEEVENTS; SendMessage(hEdit, EM_SETEVENTMASK, 0, (LPARAM)dwEvent); SetInitialFont(hEdit); //リッチエディットコントロールをWYSIWYGの幅にする //要するにプリンタにセットしてある用紙の幅に合わせる RTF_SetWYSIWYG(hEdit); break; case WM_NOTIFY: switch (((NMHDR*)lp)->code ) { case EN_MSGFILTER: pmf = (MSGFILTER *)lp; if (pmf->msg == WM_RBUTTONDOWN){ x = LOWORD(pmf->lParam); y = HIWORD(pmf->lParam); hMenu = LoadMenu(hInst, "MYPOPUP"); hSub = GetSubMenu(hMenu, 0); pt.x = (LONG)x; pt.y = (LONG)y; ClientToScreen(hEdit, &pt); TrackPopupMenu(hSub, TPM_LEFTALIGN, pt.x, pt.y, 0, hWnd, NULL); DestroyMenu(hMenu); } break; } break; case WM_SIZE: GetWindowRect(hTool, &rc); nToolH = rc.bottom - rc.top; MoveWindow(hEdit, 0, nToolH, LOWORD(lp), HIWORD(lp)-nToolH, TRUE); SendMessage(hTool, WM_SIZE, wp, lp); SetFocus(hEdit); break; case WM_INITMENUPOPUP: RTF_CheckMenu(hEdit, (HMENU)wp); break; case WM_COMMAND: switch (LOWORD(wp)) { case IDM_END: SendMessage(hWnd, WM_CLOSE, 0, 0); break; case IDM_ALL: RTF_All(hEdit); break; case IDM_FONT: SetMyFont(hEdit); break; case IDM_CENTER: SetCenter(hEdit); SendMessage(hTool, TB_SETSTATE, (WPARAM)IDM_CENTER, (LPARAM)MAKELONG(TBSTATE_PRESSED, 0)); SendMessage(hTool, TB_SETSTATE, (WPARAM)IDM_LEFT, (LPARAM)MAKELONG(TBSTATE_ENABLED, 0)); SendMessage(hTool, TB_SETSTATE, (WPARAM)IDM_RIGHT, (LPARAM)MAKELONG(TBSTATE_ENABLED, 0)); break; case IDM_LEFT: SetLeft(hEdit); SendMessage(hTool, TB_SETSTATE, (WPARAM)IDM_LEFT, (LPARAM)MAKELONG(TBSTATE_PRESSED, 0)); SendMessage(hTool, TB_SETSTATE, (WPARAM)IDM_CENTER, (LPARAM)MAKELONG(TBSTATE_ENABLED, 0)); SendMessage(hTool, TB_SETSTATE, (WPARAM)IDM_RIGHT, (LPARAM)MAKELONG(TBSTATE_ENABLED, 0)); break; case IDM_RIGHT: SetRight(hEdit); SendMessage(hTool, TB_SETSTATE, (WPARAM)IDM_RIGHT, (LPARAM)MAKELONG(TBSTATE_PRESSED, 0)); SendMessage(hTool, TB_SETSTATE, (WPARAM)IDM_LEFT, (LPARAM)MAKELONG(TBSTATE_ENABLED, 0)); SendMessage(hTool, TB_SETSTATE, (WPARAM)IDM_CENTER, (LPARAM)MAKELONG(TBSTATE_ENABLED, 0)); break; case IDM_NEW: RTF_New(hEdit); break; case IDM_SAVE: RTF_Save(hEdit); break; case IDM_SAVEAS: RTF_SaveAs(hEdit); break; case IDM_OPEN: RTF_Open(hEdit); break; case IDM_COPY: SendMessage(hEdit, WM_COPY, 0, 0); break; case IDM_CUT: SendMessage(hEdit, WM_CUT, 0, 0); break; case IDM_PASTE: SendMessage(hEdit, WM_PASTE, 0, 0); break; case IDM_UNDO: SendMessage(hEdit, WM_UNDO, 0, 0); SendMessage(hEdit, EM_SETMODIFY, (WPARAM)FALSE, 0); break; case IDM_PRINT: RTF_Print(hEdit); break; case IDM_PRNSET: PrinterSet(hEdit); RTF_SetWYSIWYG(hEdit); break; case IDM_VERTICAL: RTF_Vertical(hEdit); SendMessage(hTool, TB_SETSTATE, (WPARAM)IDM_VERTICAL, (LPARAM)MAKELONG(TBSTATE_PRESSED, 0)); SendMessage(hTool, TB_SETSTATE, (WPARAM)IDM_HORIZONTAL, (LPARAM)MAKELONG(TBSTATE_ENABLED, 0)); break; case IDM_HORIZONTAL: RTF_Horizontal(hEdit); SendMessage(hTool, TB_SETSTATE, (WPARAM)IDM_HORIZONTAL, (LPARAM)MAKELONG(TBSTATE_PRESSED, 0)); SendMessage(hTool, TB_SETSTATE, (WPARAM)IDM_VERTICAL, (LPARAM)MAKELONG(TBSTATE_ENABLED, 0)); break; case IDM_BACKCOLOR: RTF_BackColor(hEdit); break; } break; case WM_CLOSE: if (SendMessage(hEdit, EM_GETMODIFY, 0, 0)) { id = MessageBox(hWnd, "文書が変更されています。保存しますか。", "注意!", MB_YESNO); if (id == IDYES) RTF_Save(hEdit); } id = MessageBox(hWnd, "終了してもよいですか", "終了確認", MB_YESNO | MB_ICONQUESTION); if (id == IDYES) { DestroyWindow(hEdit); DestroyWindow(hWnd); } else SetFocus(hEdit); break; case WM_DESTROY: if(FreeLibrary(hRtLib) == 0) MessageBox(NULL, "ライブラリ開放失敗", "Error", MB_OK); PostQuitMessage(0); break; default: return (DefWindowProc(hWnd, msg, wp, lp)); } return 0L; }

プロシージャではWM_CREATEのところでMakeMyToolbar関数(後述) とCheckButtonState関数(後述)を呼んでいます。後者は単に 「横書き」「左揃え」ボタンを押された状態にしているだけです。

WM_SIZEメッセージが来たらツールバーとエディットコントロールの 大きさを修正しなくてはいけません。ここではGetWindowRct関数を 使ってツールバーの大きさを計測しています。そして、エディット コントロールの始点のy座標をツールバーの高さだけ下げています。 それと、当たり前ですがその分エディットコントロールの高さが 低くなります。これをわすれると困ったことがおきます。 起動時には見た目上問題ありませんが、文字入力をしてウィンドウサイズを 縮めたとき水平スクロールバーが見えません。 これは、ウィンドウの外にはみ出したので見えないのですね。 これに気がつかないと「ツールバーをつけたときは エディットコントロールのWS_HSCROLLとかES_AUTOHSCROLLが 無効になるのかな?」といらぬ心配をすることになります。

WM_COMMANDのところのIDM_CENTER, IDM_LEFT, IDM_RIGHTを 見てください。これらが選択されたときはこの3つのうちの他の 選択がされるまでボタンが押されたままの状態でなくてはいけません。 また、新しい選択が起こったときは今まで押されていたボタンを 元に戻さなくてはいけません。要するにラジオボタンみたいに グループ内では1つのボタンしかチェックされないように しなくてはいけません。

TB_SETSTATE wParam = (WPARAM) idButton; lParam = (LPARAM) MAKELONG(fState, 0);

idButtonはボタンのコマンドIDを指定します。

fStateはボタンの状態を指定します。
TBSTATE_CHECKEDはボタンがTBSTYLE_CHECKスタイルで押された状態
TBSTATE_ENABLEDはボタンが使用可能
TBSTATE_HIDDENはボタンが非表示
TBSTATE_INDETERMINATEはボタンが淡色表示
TBSTATE_PRESSEDはボタンが押された状態
TBSTATE_WRAPはボタンの後で改行

成功したらTRUE、失敗したらFALSEが返されます。 さて、ラジオボタン風にボタンにグループを作るには TBBUTTON構造体で設定した4番目のメンバをTBSTYLE_CHECKGROUP に設定する方法がありますが取り扱いが結構めんどうです。 そこでこのプログラムではグループ内のボタン数が少ないので 普通のTBSTYLE_BUTTONにしておいて力任せに操作しています。

IDM_VERTICAL, IDM_HORIZONTALのところも同じです。

BOOL SetInitialFont(HWND hEdit) BOOL SetMyFont(HWND hEdit) BOOL SetCenter(HWND hEdit) BOOL SetLeft(HWND hEdit) BOOL SetRight(HWND hEdit) void ClearCheck(HMENU hMenu) void RTF_Save(HWND hEdit) void RTF_SaveAs(HWND hEdit) DWORD CALLBACK MySaveProc(DWORD dwCookie, LPBYTE pbbuf, LONG cb, LONG *pcb) void RTF_Open(HWND hEdit) DWORD CALLBACK MyReadProc(DWORD dwCookie, LPBYTE pbBuf, LONG cb, LONG *pcb) void RTF_CheckMenu(HWND hEdit, HMENU hMenu) void RTF_Print(HWND hEdit) int PrinterSet(HWND hEdit) HDC GetPrintInfo(void) void RTF_All(HWND hEdit) int RTF_AddPageNo(HDC hdc, int nPage) void RTF_SetWYSIWYG(HWND hEdit) void RTF_New(HWND hEdit) void RTF_Vertical(HWND hEdit) void RTF_Horizontal(HWND hEdit) void RTF_BackColor(HWND hEdit)

の各関数は特に変更はありません。

HWND MakeMyToolbar(HWND hWnd) { HWND hTool; HINSTANCE hInst; TBADDBITMAP tab; HBITMAP hBmp; int nIndex, i; hInst = (HINSTANCE)GetWindowLong(hWnd, GWL_HINSTANCE); InitCommonControls(); hTool = CreateToolbarEx(hWnd, WS_CHILD | WS_BORDER | WS_VISIBLE, ID_TOOLBAR, 8, (HINSTANCE)HINST_COMMCTRL, IDB_STD_SMALL_COLOR, tbBut, 8, 0, 0, 0, 0, sizeof(TBBUTTON)); hBmp = CreateMappedBitmap(hInst, ID_MYTOOLBAR, 0, NULL, 0); tab.hInst = NULL; tab.nID = (UINT)hBmp; nIndex = SendMessage(hTool, TB_ADDBITMAP, 7, (LPARAM)&tab); for (i = 0; i <= 6; i++) tbBmp[i].iBitmap += nIndex; SendMessage(hTool, TB_ADDBUTTONS, 7, (LPARAM)&tbBmp[0]); SendMessage(hTool, TB_INSERTBUTTON, 0, (LPARAM)&tbSep); SendMessage(hTool, TB_INSERTBUTTON, 4, (LPARAM)&tbSep); SendMessage(hTool, TB_INSERTBUTTON, 6, (LPARAM)&tbSep); SendMessage(hTool, TB_INSERTBUTTON, 10, (LPARAM)&tbSep); SendMessage(hTool, TB_INSERTBUTTON, 12, (LPARAM)&tbSep); SendMessage(hTool, TB_INSERTBUTTON, 15, (LPARAM)&tbSep); SendMessage(hTool, TB_INSERTBUTTON, 19, (LPARAM)&tbSep); return hTool; }

最初にSTD系のボタンをCreateToolbarEx関数で作っておき、あとから 自作ボタンを追加しています。この場合まず TB_ADDBITMAPメッセージを送信してイメージリストに 自作ビットマップを追加する必要があります。 TB_ADDBITMAPメッセージについては 第62章を参照してください。

ボタンが全部できたら今度は適当な場所にスペースをいれて 見やすくします。この時スペースを入れる場所を間違えないでください。 (第62章

void CheckButtonState(HWND hTool) { SendMessage(hTool, TB_SETSTATE, (WPARAM)IDM_HORIZONTAL, (LPARAM)MAKELONG(TBSTATE_PRESSED, 0)); SendMessage(hTool, TB_SETSTATE, (WPARAM)IDM_LEFT, (LPARAM)MAKELONG(TBSTATE_PRESSED, 0)); return; }

単に「横書き」「左揃え」ボタンを押された状態に設定している だけです。

さて、メニューでは文字列が選択されていると「切り取り」「コピー」 が使用可能、そうでないときは使用不可にしました。これは、 メニュー項目がクリックされてポップアップメニューが出てくるか 右クリックをしたときにWM_INITMENUPOPUPが来ることを 利用して調べていました。しかし、当然のことながら文字列が選択されても このメッセージは来ません。ボタンの「切り取り」「コピー」を 使用可能、不可にするにはどのメッセージを捕まえれば良いのでしょうか。 通知メッセージを調べるとできそうですね。いろいろ試してみてください。 また、ステータスバー,ツールチップなどもつけてみてください。 そして、アクセラレーターキーもつけてみてください。そうすれば かなり立派なアプリケーションになります。


[SDK第2部 Index] [総合Index] [Previous Chapter] [Next Chapter]

Update 27/Aug/1998 By Y.Kumei
当ホーム・ページの一部または全部を無断で複写、複製、 転載あるいはコンピュータ等のファイルに保存することを禁じます。