第144章 RTFにルーラーをつける


今回は,タブ位置を表示する簡易ルーラーと、段落の頭に 黒丸印をつけます。タブ位置は段落によって異なることがあります。 したがって選択文字列または、カーソル位置が変化したら その段落のタブ位置を表示しなくてはいけません。
また、前回のタブ設定ではメニューに現在の段落がどのタブ設定に なっているかのチェックが入っていませんでした。これを改良します。



左の図はタブを0.5cmに設定したときのものを表しています。 印刷の用紙は「はがき」を指定してあります。 ルーラーを見るとどの範囲が印刷されるかもわかって 便利です。今回のプログラムではツールバーの TBSTYLE_WRAPABLEを指定していません。(いろいろ問題があるため)



さて、ルーラーはどうやって作ればよいのでしょうか。 手っ取り早いのはSTATICなウィンドウを利用することです。 そして、これに描画するにはどうしたら良いでしょうか。 これも、手っ取り早いのはウィンドウをサブクラス化してしまうことです。

hRuler = CreateWindow("static", "", SS_SIMPLE | WS_CHILD | WS_VISIBLE, 0, y, x, 30, hWnd, (HMENU)ID_RULER, hInst, NULL);

でスタティック・ウィンドウができます。どうせWM_SIZEメッセージのとき 位置,大きさを調整するのでここでは(0,0)に幅・高さ0のウィンドウを 作ってもかまいません。なおスタティック・ウィンドウについては 第69章 第86章を参照してください。

さて、このスタティック・ウィンドウにルーラーを描画するのですが まず、これをサブクラス化してWM_PAINTメッセージを横取りします。 この時、どうしてもリッチエディットコントロールのハンドルが 必要になります。どうすればよいのでしょうか。 前章のようにFindWindowを使う手もありますが、これが結構問題を 起こします。一番簡単なのはグローバル変数を使うことですが、今回は 少し回りくどい方法をとってみます。

1.親ウィンドウにSetWindowLong関数でデータをアタッチします。   アタッチするデータはリッチエディットコントールのハンドルです。 2.サブクラス化されたスタティックウィンドウのプロシージャの中で   自分の親のハンドルを取得します。 3.GetWindowLong関数でデータを取得します。

ウィンドウにはエクストラウィンドウデータというものを持っていて ここにデータを蓄えることができます。しかし、プログラム側では 各値の正確なオフセット値を指定しなくてはいけません。 たとえばSetWindowLong関数であれば2番目の引数がこれにあたります。 今回はGWL_USERDATAオフセットを用います。

また、CreateWindow関数の最後の引数はたいていNULL になっていますが、ここにデータのポインタを指定すると WM_CREATEメッセージのLPARAMとして取得することもできます。 しかし、今回はサブクラス化したウィンドウの プロシージャですからいつまでたってもWM_CREATEメッセージは やってきません。

段落の頭に黒丸印をつけるとは左の図のようなことを いいます。別にたいした機能ではありません。

1.PARAFORMAT構造体のdwMaskにPFM_NUMBERINGを加える 2.PARAFORMAT構造体のwNumberingにPFN_BULLETを指定する

これだけのことです。どうにかすると段落の番号付け ができるのかも知れませんがヘルプには書いてありません。

それと,今回最も苦労したのがタブを設定していない 段落の取り扱いです。自分で特別にタブを設定しなくても 0.5インチ幅でタブが設定してあるようです。 これが曲者です。デフォルトで設定してあるなら PARAFORMAT構造体を取得したときcTabCountとか rgxTabsにデフォルトの値を設定しておいてほしいものです。

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

// rich15.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 "段落書式(&P)" BEGIN MENUITEM "中央揃え(&C)", IDM_CENTER MENUITEM "左揃え(&L)", IDM_LEFT MENUITEM "右揃え(&R)", IDM_RIGHT MENUITEM SEPARATOR POPUP "タブの設定(&T)" BEGIN MENUITEM "2cm毎", IDM_2CM MENUITEM "1cm毎", IDM_CM MENUITEM "0.5cm毎", IDM_05CM MENUITEM SEPARATOR MENUITEM "2インチ毎", IDM_2INCH MENUITEM "1インチ毎", IDM_INCH MENUITEM "0.5インチ毎", IDM_05INCH END MENUITEM SEPARATOR MENUITEM "段落マーク付け(&M)", IDM_NUM 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 MENUITEM "ツールバーのカスタマイズ(&C)", IDM_CUSTOMIZE 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 MENUITEM SEPARATOR POPUP "タブの設定" BEGIN MENUITEM "2cm毎", IDM_2CM MENUITEM "1cm毎", IDM_CM MENUITEM "0.5cm毎", IDM_05CM MENUITEM SEPARATOR MENUITEM "2インチ毎", IDM_2INCH MENUITEM "1インチ毎", IDM_INCH MENUITEM "0.5インチ毎", IDM_05INCH END MENUITEM SEPARATOR MENUITEM "段落マーク付け", IDM_NUM 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 //

「段落書式」にショートカット(&P)を付けてみました。
「段落マーク付け」の項目が増えました。

それと、メニュー項目でそれを選ぶといきなり コマンドが実行されるのではなくダイアログボックスなどが 出てくるものは「開く...」のように「...」を付けるのが 普通ですが、省略しています。各自で付けてみてください。

// rich15.cpp #define STRICT #include <windows.h> #include <richedit.h> #include "resource.h" #include <commctrl.h> #define ID_TOOLBAR 100 #define ID_STATUS 101 #define ID_RICH 102 #define ID_RULER 103 #define ID_MYTIMER 1000 LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); DWORD CALLBACK MySaveProc(DWORD, LPBYTE, LONG, LONG *); DWORD CALLBACK MyReadProc(DWORD, LPBYTE, LONG, LONG *); LRESULT CALLBACK MyTabDlg(HWND, UINT, WPARAM, LPARAM); LRESULT CALLBACK MyToolProc(HWND, UINT, WPARAM, LPARAM); LRESULT CALLBACK MyStaticProc(HWND, UINT, WPARAM, LPARAM); BOOL InitApp(HINSTANCE); BOOL InitInstance(HINSTANCE, int); BOOL SetInitialFont(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); HWND MakeMyToolbar(HWND); void InsertSep(HWND); HWND MakeMyStatusbar(HWND); void SetStatusFontInfo(HWND, HWND); BOOL IsEditButtonAvailable(HWND, HWND); BOOL IsPasteButtonAvailable(HWND, HWND); void SetStatusClock(HWND); void RTF_SetTab(HWND, int); void RTF_GetTab(HWND); //現在の段落配置法を調べそのようにボタンを設定 void SetAlignmentButton(HWND, HWND); void SetButtonHV(HWND, HWND); void RTF_Num(HWND); //ルーラーを作る HWND MakeMyRuler(HWND, HWND); char szClassName[] = "rich15"; //ウィンドウクラス 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]; WNDPROC Org_ToolProc;//ツールバーのプロシージャ WNDPROC Org_StaticProc;//スタティックコントロールのプロシージャ int nRTFWidth; //RTFに設定する横幅(twip) BOOL bNum = FALSE; //段落マーク付け 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} };

ルーラーのIDを新しくdefineしました。
スタティック・ウィンドウのサブクラス化されたプロシージャの プロトタイプ宣言が増えています。
段落頭にマーク付けをする関数、ルーラーを作る関数も増えています。 また、ルーラーの範囲(実際に印刷される幅)を決めるために nRTFWidthというグローバル変数も用意しました。これは、RTF_SetWYSIWYG関数 で値を与えています。
bNumグローバル変数は選択されている段落がマーク付きかどうかを 知るために作っておきました。(手抜きです)

WinMain, InitApp, InitInstance

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

//ウィンドウプロシージャ LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp) { int id; static HINSTANCE hRtLib; static HWND hEdit, hTool, hStatus, hRuler; DWORD dwEvent; MSGFILTER *pmf; HMENU hMenu, hSub; int x, y; POINT pt; int nToolH, nStatusH; RECT rc; LPTBNOTIFY lpTBn; int no; TBSAVEPARAMS tbs; LPNMHDR lpN; switch (msg) { case WM_CREATE: InitCommonControls(); hTool = MakeMyToolbar(hWnd); InsertSep(hTool); hStatus = MakeMyStatusbar(hWnd); hRuler = MakeMyRuler(hWnd, 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)ID_RICH,//統一性を図るためIDを変えました hInst, NULL); //親ウィンドウへデータ(hEdit)のアタッチ SetWindowLong(hWnd, GWL_USERDATA, (LONG)hEdit); dwEvent = SendMessage(hEdit, EM_GETEVENTMASK, 0, 0); dwEvent |= ENM_MOUSEEVENTS | ENM_SELCHANGE; SendMessage(hEdit, EM_SETEVENTMASK, 0, (LPARAM)dwEvent); SetInitialFont(hEdit); //リッチエディットコントロールをWYSIWYGの幅にする //要するにプリンタにセットしてある用紙の幅に合わせる RTF_SetWYSIWYG(hEdit); tbs.hkr = HKEY_CURRENT_USER; tbs.pszSubKey = "Software\\Kumei\\rich13"; tbs.pszValueName = "Setting"; SendMessage(hTool, TB_SAVERESTORE, FALSE, (LPARAM)&tbs); SetStatusFontInfo(hStatus, hEdit); //ボタンを復元してから次の4つの関数を呼んでボタン状態を設定 IsEditButtonAvailable(hEdit, hTool); IsPasteButtonAvailable(hEdit, hTool); SetButtonHV(hEdit, hTool); SetAlignmentButton(hEdit, hTool); SetTimer(hWnd, ID_MYTIMER, 500, NULL); Org_ToolProc = (WNDPROC)GetWindowLong(hTool, GWL_WNDPROC); SetWindowLong(hTool, GWL_WNDPROC, (LONG)MyToolProc); Org_StaticProc = (WNDPROC)GetWindowLong(hRuler, GWL_WNDPROC); SetWindowLong(hRuler, GWL_WNDPROC, (LONG)MyStaticProc); break; case WM_TIMER: IsPasteButtonAvailable(hEdit, hTool); SetStatusClock(hStatus); break; case WM_NOTIFY: if (wp == (WPARAM)ID_TOOLBAR) { lpTBn = (LPTBNOTIFY)lp; switch (lpTBn->hdr.code) { case TBN_QUERYINSERT: return TRUE; case TBN_QUERYDELETE: return TRUE; case TBN_GETBUTTONINFO: no = lpTBn->iItem; if (no <= 7) { lpTBn->tbButton = tbBut[no]; } if (no >= 8 && no <= 14) { lpTBn->tbButton = tbBmp[no - 8]; } if (no >= 15) return FALSE; lpTBn->pszText = ""; lpTBn->cchText = 0; return TRUE; case TBN_RESET: int i, b; b = SendMessage(hTool, TB_BUTTONCOUNT, 0, 0); for (i = 0; i < b; i++) SendMessage(hTool, TB_DELETEBUTTON, 0, 0); SendMessage(hTool, TB_ADDBUTTONS, 8, (LPARAM)tbBut); SendMessage(hTool, TB_ADDBUTTONS, 7, (LPARAM)&tbBmp[0]); InsertSep(hTool); SetAlignmentButton(hEdit, hTool); SetButtonHV(hEdit, hTool); IsEditButtonAvailable(hEdit, hTool); IsPasteButtonAvailable(hEdit, hTool); break; } } if (wp == (WPARAM)ID_RICH) { lpN = (LPNMHDR)lp; switch (lpN->code ) { case EN_SELCHANGE: SetStatusFontInfo(hStatus, hEdit); //「切り取り」「コピー」が可能かどうか調査 IsEditButtonAvailable(hEdit, hTool); // 「張りつけ」可能かどうか IsPasteButtonAvailable(hEdit, hTool); SetAlignmentButton(hEdit, hTool); //ルーラーの書き換え if(InvalidateRect(hRuler, NULL, TRUE)==0) MessageBeep(MB_ICONHAND); break; 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: SendMessage(hTool, WM_SIZE, wp, lp); SendMessage(hStatus, WM_SIZE, wp, lp); GetWindowRect(hTool, &rc); nToolH = rc.bottom - rc.top; GetWindowRect(hStatus, &rc); nStatusH = rc.bottom - rc.top; MoveWindow(hEdit, 0, nToolH + 30, LOWORD(lp), HIWORD(lp) - (nToolH + nStatusH + 30), TRUE); MoveWindow(hRuler, 0, nToolH, LOWORD(lp), 30, TRUE); 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); SetStatusFontInfo(hStatus, hEdit); break; case IDM_CENTER: SetCenter(hEdit); SetAlignmentButton(hEdit, hTool); break; case IDM_LEFT: SetLeft(hEdit); SetAlignmentButton(hEdit, hTool); break; case IDM_RIGHT: SetRight(hEdit); SetAlignmentButton(hEdit, hTool); 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); break; case IDM_PRINT: RTF_Print(hEdit); break; case IDM_PRNSET: PrinterSet(hEdit); RTF_SetWYSIWYG(hEdit); InvalidateRect(hRuler, NULL, TRUE); break; case IDM_VERTICAL: RTF_Vertical(hEdit); SetButtonHV(hEdit, hTool); InvalidateRect(hRuler, NULL, TRUE); break; case IDM_HORIZONTAL: RTF_Horizontal(hEdit); SetButtonHV(hEdit, hTool); InvalidateRect(hRuler, NULL, TRUE); break; case IDM_BACKCOLOR: RTF_BackColor(hEdit); break; case IDM_CUSTOMIZE: SendMessage(hTool, TB_CUSTOMIZE, 0, 0); { int b, i; tbs.hkr = HKEY_CURRENT_USER; tbs.pszSubKey = "Software\\Kumei\\rich13"; tbs.pszValueName = "Setting"; SendMessage(hTool, TB_SAVERESTORE, TRUE, (LPARAM)&tbs); b = SendMessage(hTool, TB_BUTTONCOUNT, 0, 0); for (i = 0; i < b; i++) SendMessage(hTool, TB_DELETEBUTTON, 0, 0); SendMessage(hTool, TB_ADDBUTTONS, 8, (LPARAM)tbBut); SendMessage(hTool, TB_ADDBUTTONS, 7, (LPARAM)tbBmp); tbs.hkr = HKEY_CURRENT_USER; tbs.pszSubKey = "Software\\Kumei\\rich13"; tbs.pszValueName = "Setting"; SendMessage(hTool, TB_SAVERESTORE, FALSE, (LPARAM)&tbs); //ツールバーをリストアしたときは必ず次の関数を呼ぶ IsEditButtonAvailable(hEdit, hTool); IsPasteButtonAvailable(hEdit, hTool); SetButtonHV(hEdit, hTool); SetAlignmentButton(hEdit, hTool); } break; case IDM_2INCH: RTF_SetTab(hEdit, 2880); InvalidateRect(hRuler, NULL, TRUE); break; case IDM_INCH: RTF_SetTab(hEdit, 1440); InvalidateRect(hRuler, NULL, TRUE); break; case IDM_05INCH: RTF_SetTab(hEdit, 720); InvalidateRect(hRuler, NULL, TRUE); break; case IDM_2CM: RTF_SetTab(hEdit, 1134); InvalidateRect(hRuler, NULL, TRUE); break; case IDM_CM: RTF_SetTab(hEdit, 567); InvalidateRect(hRuler, NULL, TRUE); break; case IDM_05CM: RTF_SetTab(hEdit, 284); InvalidateRect(hRuler, NULL, TRUE); break; case IDM_NUM: RTF_Num(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(SetWindowLong(hTool, GWL_WNDPROC, (LONG)Org_ToolProc) == 0) MessageBox(NULL, "サブクラス化解除失敗", "失敗", MB_OK); if(SetWindowLong(hRuler, GWL_WNDPROC, (LONG)Org_StaticProc) == 0) MessageBox(NULL, "サブクラス化解除失敗(hStatic)", "失敗", MB_OK); tbs.hkr = HKEY_CURRENT_USER; tbs.pszSubKey = "Software\\Kumei\\rich13"; tbs.pszValueName = "Setting"; SendMessage(hTool, TB_SAVERESTORE, TRUE, (LPARAM)&tbs); if(KillTimer(hWnd, ID_MYTIMER) == 0) MessageBox(hWnd, "タイマーを正常に殺せませんでした", "Error", MB_OK | MB_ICONHAND); if(FreeLibrary(hRtLib) == 0) MessageBox(NULL, "ライブラリ開放失敗", "Error", MB_OK); PostQuitMessage(0); break; default: return (DefWindowProc(hWnd, msg, wp, lp)); } return 0L; }

巨大なプロシージャですが少しずつ見ていくと簡単です。

WM_CREATEのところでは当然ルーラーを作る関数MakeMyRuler を呼んでいます。
最初にも書いたように親ウィンドウにデータ(hEdit) をアタッチしています。
そして、スタティック・ウィンドウをサブクラス化します。

EN_SELCHANGEのところでは、InvalidateRect関数を使ってスタティック・ ウィンドウにWM_PAINTメッセージを送っています。これで 現在の段落のタブ位置を書きなおさせています。

WM_SIZEのところでは、スタティック・ウィンドウが増えたので リッチエディットコントロールの位置大きさの計算が今までと変わります。 特に難しくはないですね。

IDM_PRNSET, IDM_VERTICAL, IDM_HORIZONTALのところでも InvalidateRect関数を呼んでルーラーを書きなおしています。 プリンタの設定で用紙が変わるかもしれないし、 縦書きのときは今のルーラーでは意味がないので目盛を 表示させないようにするためです。

タブを設定したときも(IDM_**INCH, IDM_**CM)ルーラーを 書きなおしています。

今回増えた段落マーク付け(IDM_NUM)の時に当然 RTF_Num関数を呼んでいます。

今回は新しくスタティック・ウィンドウをサブクラス化しているので プログラム終了時にこれを解除します。

SetInitialFont, SetMyFont, SetCenter, SetLeft, SetRight

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

void ClearCheck(HMENU hMenu) { CheckMenuItem(hMenu, IDM_LEFT, MF_BYCOMMAND | MF_UNCHECKED); CheckMenuItem(hMenu, IDM_RIGHT, MF_BYCOMMAND | MF_UNCHECKED); CheckMenuItem(hMenu, IDM_CENTER, MF_BYCOMMAND | MF_UNCHECKED); CheckMenuItem(hMenu, IDM_05CM, MF_BYCOMMAND | MF_UNCHECKED); CheckMenuItem(hMenu, IDM_CM, MF_BYCOMMAND | MF_UNCHECKED); CheckMenuItem(hMenu, IDM_2CM, MF_BYCOMMAND | MF_UNCHECKED); CheckMenuItem(hMenu, IDM_05INCH, MF_BYCOMMAND | MF_UNCHECKED); CheckMenuItem(hMenu, IDM_INCH, MF_BYCOMMAND | MF_UNCHECKED); CheckMenuItem(hMenu, IDM_2INCH, MF_BYCOMMAND | MF_UNCHECKED); CheckMenuItem(hMenu, IDM_NUM, MF_BYCOMMAND | MF_UNCHECKED); return; }

メニュー項目が増えたのでこの関数の中身も増えました。 チェックをつける項目のチェックを一度全部はずしてしまうものです。

RTF_Save, RTF_SaveAs, MySaveProc

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

void RTF_Open(HWND hEdit) { OPENFILENAME of; HANDLE hFile; EDITSTREAM eds; HWND hParent; char szTitle[MAX_PATH]; int id; if (SendMessage(hEdit, EM_GETMODIFY, 0, 0)) { id = MessageBox(hEdit, "文書が変更されています。\n保存しますか?", "注意!", MB_YESNO | MB_ICONQUESTION); if (id == IDYES) RTF_Save(hEdit); SetFocus(hEdit); } memset(&of, 0, sizeof(OPENFILENAME)); of.lStructSize = sizeof(OPENFILENAME); of.hwndOwner = hEdit; of.lpstrFilter = "RTF(*.rtf)\0 *.rtf\0TEXT(*.txt)\0 *.txt\0All Files(*.*)\0 *.*\0\0"; of.lpstrFile = szFName; of.nMaxFile = MAX_PATH; of.Flags = OFN_PATHMUSTEXIST; of.lpstrDefExt = "rtf"; if (GetOpenFileName(&of) == 0) return; nFileType = of.nFilterIndex; hFile = CreateFile(szFName, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (hFile == INVALID_HANDLE_VALUE) { MessageBox(hEdit, "ハンドルが無効です", "Error", MB_OK); return; } eds.dwCookie = (DWORD)hFile; eds.dwError = 0; eds.pfnCallback = MyReadProc; switch (nFileType) { case 1: SendMessage(hEdit, EM_STREAMIN, SF_RTF, (LPARAM)&eds); break; case 2: case 3: SendMessage(hEdit, EM_STREAMIN, SF_TEXT, (LPARAM)&eds); break; } SendMessage(hEdit, EM_SETMODIFY, (WPARAM)FALSE, 0); CloseHandle(hFile); strcpy(szTitle, szAppTitle); strcat(szTitle, " ["); strcat(szTitle, szFName); strcat(szTitle, "]"); hParent = GetParent(hEdit); if (of.Flags & OFN_READONLY) { SendMessage(hEdit, EM_SETOPTIONS, (WPARAM)ECOOP_OR, (LPARAM)ECO_READONLY); strcat(szTitle, " == READONLY =="); } SetWindowText(hParent, szTitle); return; }

たいした変更ではありません。メッセージボックスで、文書が変更になっているとき 保存するかどうかをたずねるメッセージボックスのボタンが「OK」「キャンセル」 だったのを、「はい」「いいえ」に変更しただけです。

MyReadProc,

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

void RTF_CheckMenu(HWND hEdit, HMENU hMenu) { CHARRANGE cr; PARAFORMAT pf; SendMessage(hEdit, EM_EXGETSEL, 0, (LPARAM)&cr); if (cr.cpMin == cr.cpMax) { EnableMenuItem(hMenu, IDM_CUT, MF_GRAYED | MF_BYCOMMAND); EnableMenuItem(hMenu, IDM_COPY, MF_GRAYED | MF_BYCOMMAND); } else { EnableMenuItem(hMenu, IDM_CUT, MF_ENABLED | MF_BYCOMMAND); EnableMenuItem(hMenu, IDM_COPY, MF_ENABLED | MF_BYCOMMAND); } if (SendMessage(hEdit, EM_GETMODIFY, 0, 0)) { EnableMenuItem(hMenu, IDM_UNDO, MF_ENABLED | MF_BYCOMMAND); } else { EnableMenuItem(hMenu, IDM_UNDO, MF_GRAYED | MF_BYCOMMAND); } if (SendMessage(hEdit, EM_CANPASTE, 0, 0)) { EnableMenuItem(hMenu, IDM_PASTE, MF_ENABLED | MF_BYCOMMAND); } else { EnableMenuItem(hMenu, IDM_PASTE, MF_GRAYED | MF_BYCOMMAND); } if (SendMessage(hEdit, EM_GETOPTIONS, 0, 0) & ECO_VERTICAL) { EnableMenuItem(hMenu, IDM_VERTICAL, MF_GRAYED | MF_BYCOMMAND); EnableMenuItem(hMenu, IDM_HORIZONTAL, MF_ENABLED | MF_BYCOMMAND); } else { EnableMenuItem(hMenu, IDM_VERTICAL, MF_ENABLED | MF_BYCOMMAND); EnableMenuItem(hMenu, IDM_HORIZONTAL, MF_GRAYED | MF_BYCOMMAND); } memset(&pf, 0, sizeof(PARAFORMAT)); pf.cbSize = sizeof(PARAFORMAT); SendMessage(hEdit, EM_GETPARAFORMAT, 0, (LPARAM)&pf); ClearCheck(hMenu);//まずメニューのチェックを全部はずす switch (pf.wAlignment) { case PFA_LEFT: CheckMenuItem(hMenu, IDM_LEFT, MF_CHECKED | MF_BYCOMMAND); break; case PFA_RIGHT: CheckMenuItem(hMenu, IDM_RIGHT, MF_CHECKED | MF_BYCOMMAND); break; case PFA_CENTER: CheckMenuItem(hMenu, IDM_CENTER, MF_CHECKED | MF_BYCOMMAND); break; } if (pf.dwMask & PFM_TABSTOPS) { switch (pf.rgxTabs[0]) { case 2880: CheckMenuItem(hMenu, IDM_2INCH, MF_CHECKED | MF_BYCOMMAND); break; case 1440: CheckMenuItem(hMenu, IDM_INCH, MF_CHECKED | MF_BYCOMMAND); break; case 720: CheckMenuItem(hMenu, IDM_05INCH, MF_CHECKED | MF_BYCOMMAND); break; case 567: CheckMenuItem(hMenu, IDM_CM, MF_CHECKED | MF_BYCOMMAND); break; case 1134: CheckMenuItem(hMenu, IDM_2CM, MF_CHECKED | MF_BYCOMMAND); break; case 284: CheckMenuItem(hMenu, IDM_05CM, MF_CHECKED | MF_BYCOMMAND); break; } } if (pf.wNumbering == PFN_BULLET) { CheckMenuItem(hMenu, IDM_NUM, MF_CHECKED | MF_BYCOMMAND); bNum = TRUE; } else { bNum = FALSE; } return; }

タブを設定してあるときメニュー項目にチェックをつけます。 また、段落マークがあるときもチェックをつけます。また現在の 段落にマークがあるかどうかをグローバル変数に書きこんでいます。

RTF_Print, PrinterSet, GetPrintInfo, RTF_All, RTF_AddPageNo

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

void RTF_SetWYSIWYG(HWND hEdit) { HDC hdc; //プリンタのhdc int nPrnW; int nLog; int w; hdc = GetPrintInfo(); nPrnW = GetDeviceCaps(hdc, PHYSICALWIDTH); nLog = GetDeviceCaps(hdc, LOGPIXELSX); //左右1インチあけるので1440*2(twip)引く w = MulDiv(nPrnW, 1440, nLog) - 1440 * 2; nRTFWidth = w; //wをグローバル変数にコピー if (SendMessage(hEdit, EM_SETTARGETDEVICE, (WPARAM)hdc, (LPARAM)w) == 0) MessageBox(hEdit, "RTF横幅設定でエラーが発生しました", "ERROR", MB_OK); return; }

ここでは、実際に印刷される横幅をグローバル変数にコピーしている点が今までと 違うところです。

void RTF_New(HWND hEdit) { char szTitle[MAX_PATH]; int id; if (SendMessage(hEdit, EM_GETMODIFY, 0, 0)) { id = MessageBox(hEdit, "文書が変更されています。\n保存しますか?", "注意!", MB_YESNO | MB_ICONQUESTION); if (id == IDYES) RTF_Save(hEdit); } SetWindowText(hEdit, ""); strcpy(szTitle, szAppTitle); strcat(szTitle, " [ 無題 ]"); SetWindowText(GetParent(hEdit), szTitle); SendMessage(hEdit, EM_SETMODIFY, FALSE, 0); SetFocus(hEdit); return; }

ここもたいした変更ではありません。新規作成のとき の注意を促すメッセージボックスのボタンの変更です。

RTF_Vertical, RTF_Horizontal, RTF_BackColor

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

HWND MakeMyToolbar(HWND hWnd) { HWND hTool; HINSTANCE hInst; TBADDBITMAP tab; HBITMAP hBmp; int nIndex, i; hInst = (HINSTANCE)GetWindowLong(hWnd, GWL_HINSTANCE); hTool = CreateToolbarEx(hWnd, WS_CHILD | WS_BORDER | WS_VISIBLE | CCS_ADJUSTABLE | TBSTYLE_ALTDRAG, 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]); return hTool; }

TBSTYLE_WRAPABLEをやめただけです。

InsertSep, MakeMyStatusbar, SetStatusFontInfo, IsEditButtonAvailable, IsPasteButtonAvailable, SetStatusClock, RTF_SetTab, SetAlignmentButton, SetButtonHV

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

//サブクラス化したツールバープロシージャ LRESULT CALLBACK MyToolProc(HWND hSTool, UINT msg, WPARAM wp, LPARAM lp) { HWND hParent; switch (msg) { case WM_LBUTTONDBLCLK: hParent = GetParent(hSTool); if (hParent == NULL) { MessageBox(hSTool, "メインウィンドウを取得できません", "Error!", MB_OK | MB_ICONHAND); return(CallWindowProc(Org_ToolProc, hSTool, msg, wp, lp)); } SendMessage(hParent, WM_COMMAND, (WPARAM)IDM_CUSTOMIZE, 0); break; default: return(CallWindowProc(Org_ToolProc, hSTool, msg, wp, lp)); } return 0; }

親ウィンドウのハンドルを取得するのにFindWindow関数を使っていましたが これがちょっと問題を起こすので、単純にGetParent関数に変えてみました。 前回のプログラムでは普段は問題ないのですが、ファイルを読みこんだ後 ツールバーをダブルクリックしたとき、なぜかFindWindow関数が 失敗します。失敗するともともとのプロシージャにWM_LBUTTONDBLCLKが 行くようにプログラムしてあるので、カスタマイズ用のダイアログボックスは 出ますが、「区切り」挿入対策(前章参照) が施されません。

void RTF_Num(HWND hEdit) { PARAFORMAT pf; memset(&pf, 0, sizeof(PARAFORMAT)); pf.cbSize = sizeof(PARAFORMAT); if (!bNum) { pf.dwMask |= PFM_NUMBERING; pf.wNumbering = PFN_BULLET; bNum = TRUE; } else { pf.dwMask ^= PFM_NUMBERING; pf.wNumbering = 0; bNum = FALSE; } SendMessage(hEdit, EM_SETPARAFORMAT, 0, (LPARAM)&pf); return; }

段落にマーク付けをしたりはずしたりする関数です。 グローバル変数を見ると現在の段落のマーク付けの有無が わかるので、それによってマークをつけたりはずしたりしています。

グローバル変数を使わなくてもpf.dwMaskの値とか pf.wNumberingの値を調べればわかるじゃないか、といわれそうですが これが案外当てにならないのです。ファイルを読みこんだ時 特に問題が生じます。グローバル変数を使うのがもっとも安全なようです。

HWND MakeMyRuler(HWND hWnd, HWND hTool) { HWND hRuler; RECT rc; int x, y;//Toolbarの幅,高さ GetWindowRect(hTool, &rc); x = rc.right - rc.left; y = rc.bottom - rc.top; hRuler = CreateWindow("static", "", SS_SIMPLE | WS_CHILD | WS_VISIBLE, 0, y, x, 30, hWnd, (HMENU)ID_RULER, hInst, NULL); return hRuler; }

ルーラーのためのウィンドウを作る関数です。

LRESULT CALLBACK MyStaticProc(HWND hStatic, UINT msg, WPARAM wp, LPARAM lp) { HDC hdc; PAINTSTRUCT ps; HBRUSH hBrush; HPEN hPen; RECT rc; int w, i, rate, pix, tab;//rate 1論理inchあたりのピクセル数(横方向) int nParentW, nClientW, div; //親ウィンドウのウィンドウ幅とクライアント幅 COLORREF cr; static HWND hEdit; HWND hParent; PARAFORMAT pf; switch (msg) { case WM_PAINT: hEdit = (HWND)GetWindowLong(GetParent(hStatic), GWL_USERDATA); if (hEdit == NULL) { MessageBox(NULL, "hEdit取得失敗", "Error", MB_OK); return 0; } hdc = BeginPaint(hStatic, &ps); cr = (COLORREF)GetSysColor(COLOR_MENU); hBrush = (HBRUSH)CreateSolidBrush(cr); rate = GetDeviceCaps(hdc, LOGPIXELSX); GetWindowRect(hStatic, &rc); w = rc.right - rc.left; if (w > MulDiv(nRTFWidth, rate, 1440)) w = MulDiv(nRTFWidth, rate, 1440); rc.top = 0; rc.bottom = 30; rc.left = 0; rc.right = rc.right - rc.left; FillRect(hdc, &rc, hBrush); //縦書きなら目盛を表示せず戻る if (SendMessage(hEdit, EM_GETOPTIONS, 0, 0) & ECO_VERTICAL) { DeleteObject(hBrush); EndPaint(hStatic, &ps); return 0; } Rectangle(hdc, 2, 6, w-2, 24); memset(&pf, 0, sizeof(PARAFORMAT)); pf.cbSize = sizeof(PARAFORMAT); SendMessage(hEdit, EM_GETPARAFORMAT,0, (LPARAM)&pf); if (pf.rgxTabs[0] > 0) { tab = pf.rgxTabs[0]; } else { tab = 720; } pix = MulDiv(rate, tab, 1440); hPen = CreatePen(PS_SOLID, 1, RGB(255, 0, 0)); SelectObject(hdc, hPen); hParent = GetParent(hStatic); GetWindowRect(hParent, &rc); nParentW = rc.right - rc.left; GetClientRect(hParent, &rc); nClientW = rc.right - rc.left; div = (nParentW - nClientW) / 2; for (i = pix; i <= w-2; i += pix) { MoveToEx(hdc, i + div, 10, NULL); LineTo(hdc, i + div, 20); } DeleteObject(hPen); DeleteObject(hBrush); EndPaint(hStatic, &ps); return 0; default: return (CallWindowProc(Org_StaticProc, hStatic, msg, wp, lp)); } return 0; }

サブクラス化されたスタティック・ウィンドウのプロシージャです。

WM_PAINTメッセージを捕まえたらまず、リッチエディットコントロールの ハンドルを、親ウィンドウにアタッチされたデータを読むことで取得して います。

次にルーラーの下地の色はメニューの下地の色と同じにしてあります。 こうすると「画面のプロパティ」で色を変えられても大丈夫です。

GetSysColor関数は初めて出てきましたが、名前のとおり システムのいろいろな色を取得する関数です。ヘルプで使い方を 調べてみてください。

次にwをウィンドウ幅に設定しますが、これが印刷幅より 大きいときは印刷幅に設定します。

次にウィンドウをメニューの下地と同じ色で塗りつぶします。

int FillRect( HDC hDC, // デバイスコンテキストハンドル CONST RECT *lprc, // 塗りつぶす矩形のポインタ HBRUSH hbr // 塗りつぶしに使うブラシのハンドル );

塗りつぶしたい矩形をRECT構造体にセットして この関数を実行すればhbrのブラシで塗りつぶしてくれます。 もし、縦書きならここでreturnして、目盛は描画しません。

次にウィンドウよりやや小さい矩形を描画します。

次に目盛の描画ですが、タブが設定されていないときは 0.5インチ刻みに目盛を書きこみます。実際何も設定しないときは 0.5インチ刻みで動きます。この場合rgxTabsの値だけを 指標にしていると痛い目にあいます。


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

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