今回はとりあえずフォントを変えるだけのプログラムを
作ってみます。
メニューの「編集」「フォントの変更」でフォントを変えます。
さて、リッチテキストエディットコントロールも他のコントロールのように
子供ウィンドウとして作ります。しかし、他のコモンコントロール
と違ってComctl32.dllをリンクする必要はありません。
したがってInitCommonControls関数やcommctrl.hも不要です。
ま、作るのは簡単です。が、これをコントロールするのは結構 面倒くさいです。とりあえず今回はフォントの変更のみできる ようにします。1.richedit.hをインクルードする 2.LoadLibrary("RICHED32.DLL");でRICHED32.DLLをロードする 3.CreateWindowEx関数で子供ウィンドウとしてコントロールを貼り付ける
リッチテキストエディットコントロールから現在の書式を 取得するにはEM_GETCHARFORMATメッセージを送ります。 セットするにはEM_SETCHARFORMATメッセージを送ります。
fSelectionが0のときはデフォルトの文字書式が返されます。 それ以外のときは現在の選択範囲の書式が返されます。EM_GETCHARFORMAT wParam = (WPARAM) (BOOL) fSelection; lParam = (LPARAM) (CHARFORMAT FAR *) lpFmt;
lpFmtはCHARFORMAT構造体へのポインタで、これに 情報が返されます。
uFlags文字書式の適用範囲を示します。SCF_SELECTIONは 現在の選択範囲に書式を適用します。SCF_WORDは選択されている 単語に書式を適用します。これらから0個以上を選択します。EM_SETCHARFORMAT wParam = (WPARAM) (UINT) uFlags; lParam = (LPARAM) (CHARFORMAT FAR *) lpFmt;
lpFmt文字書式を指定するCHARFORMAT構造体へのポインタです。
ヘルプによるとCHARFORMAT構造体の定義は上のようになっています。 しかし_wPad1と_wPad2メンバについてヘルプには何の解説もありません。 さらに_WPAD型もいったい何物なのか解説がありません。 それともこの構造体の定義が間違っていて最初からこのようなメンバは ないのでしょうか。typedef struct _charformat { UINT cbSize; _WPAD _wPad1; DWORD dwMask; DWORD dwEffects; LONG yHeight; LONG yOffset; COLORREF crTextColor; BYTE bCharSet; BYTE bPitchAndFamily; CHAR szFaceName[LF_FACESIZE]; _WPAD _wPad2; } CHARFORMAT;
実は、richedit.hをよく読むと
というように定義されています。 32ビット版では_WPADは単に「//」(コメント)で意味はありません。 (##はトークン連結演算子です)#ifdef _WIN32 # define _WPAD /##/ #else # define _WPAD WORD #endif
cbSizeはこの構造体のサイズです。
dwMaskはどのメンバを有効にするかを設定します。
CFM_BOLDはdwEffectsメンバにCFE_BOLDが含まれています。
CFM_COLORはcrTextColorおよび、dwEffectsのCFE_AUTOCOLORが有効です。
CFM_FACEはszFaceNameが有効です。
CFM_ITALICはdwEfftctsにCFE_ITALICが含まれています。
CFM_OFFSETはyOffsetが有効です。
CFM_PROTECTEDはdwEffectのCFE_PROTECTEDが有効です。
CFM_SIZEはyHeightが有効です。
CFM_STRIKEOUTはdwEffectsのCFE_STRIKEOUTが有効です。
CFM_UNDERLINEはdwEffectsのCFE_UNDERLINEが有効です。
dwEffectは文字の特殊効果を指定します。
CFE_AUTOCOLOR(色がCOLOR_WINDOWTEXTになる)、CFE_BOLD,CFE_ITALIC,
CFE_STRIKEOUT,CFE_UNDERLINEはその名のとおりです。
CFE_PROTECTEDは文字が保護されていることを表します。
yHeightは文字の高さです。
yOffsetはベースラインからの文字のオフセットです。
crTextColorは文字の色です。
bCharSetはキャラクタセットです。
bPitchAndFamilyはフォントファミリととピッチを指定します。
szFaceNameはフォントの書体名を指定します。
これらのメンバを設定してEM_SETCHARFORMATメッセージを送ってやれば 文字書式が設定されます。EM_GETCHARFORMATを送るとこの構造体に 答えが返ってきます。
では、プログラムを見てみることにします。
ごく普通のメニューリソースです。// rich01.rcの一部です。 ///////////////////////////////////////////////////////////////////////////// // // Menu // MYMENU MENU DISCARDABLE BEGIN POPUP "ファイル(&F)" BEGIN MENUITEM "終了(&X)", IDM_END END POPUP "編集(&E)" BEGIN MENUITEM "フォントの変更(&F)", IDM_FONT END END
richedit.hをインクルードするのを忘れないでください。 今回はhInstをグローバル変数にしてあります。// rich01.cpp #define STRICT #include <windows.h> #include <richedit.h> #include "resource.h" LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); BOOL InitApp(HINSTANCE); BOOL InitInstance(HINSTANCE, int); BOOL SetInitialFont(HWND hWnd); BOOL SetMyFont(HWND); char szClassName[] = "rich01"; //ウィンドウクラス HINSTANCE hInst; DWORD dwMyMask; //CHARFORMATで使うマスク値 int WINAPI WinMain(HINSTANCE hCurInst, HINSTANCE hPrevInst, LPSTR lpsCmdLine, int nCmdShow) { MSG msg; if (!InitApp(hCurInst)) return FALSE; if (!InitInstance(hCurInst, nCmdShow)) return FALSE; while (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return msg.wParam; }
いつもと同じです。ただ、インスタンスハンドルをグローバル変数に コピーしていることに注意してください。//ウィンドウ・クラスの登録 BOOL InitApp(HINSTANCE hInst) { WNDCLASSEX wc; wc.cbSize = sizeof(WNDCLASSEX); wc.style = CS_HREDRAW | CS_VREDRAW; wc.lpfnWndProc = WndProc; //プロシージャ名 wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hInstance = hInst; //インスタンス wc.hIcon = LoadIcon(NULL, IDI_APPLICATION); wc.hCursor = LoadCursor(NULL, IDC_ARROW); wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); wc.lpszMenuName = "MYMENU"; //メニュー名 wc.lpszClassName = (LPCSTR)szClassName; wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION); return (RegisterClassEx(&wc)); } //ウィンドウの生成 BOOL InitInstance(HINSTANCE hInstance, int nCmdShow) { HWND hWnd; hInst = hInstance; hWnd = CreateWindow(szClassName, "猫でもわかるRichEdit", //タイトルバーにこの名前が表示されます WS_OVERLAPPEDWINDOW,//ウィンドウの種類 CW_USEDEFAULT, //X座標 CW_USEDEFAULT, //Y座標 CW_USEDEFAULT, //幅 CW_USEDEFAULT, //高さ NULL,//親ウィンドウのハンドル、親を作るときはNULL NULL,//メニューハンドル、クラスメニューを使うときはNULL hInstance,//インスタンスハンドル NULL); if (!hWnd) return FALSE; ShowWindow(hWnd, nCmdShow); UpdateWindow(hWnd); return TRUE; }
WM_CREATEメッセージが着たらリッチテキストエディットコントロールを 作ります。この手のプログラムではよくある手法ですが まずコントロールを幅、高さとも0にしておきます。 どうせすぐにWM_SIZEメッセージが来るのでここで 大きさの調整をする必要はありません。 そして、最初のフォントを指定しておきます。 (自作関数SetInitialFont)//ウィンドウプロシージャ LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp) { int id; static HINSTANCE hRtLib; static HWND hEdit; switch (msg) { case WM_CREATE: hRtLib = LoadLibrary("RICHED32.DLL"); hEdit = CreateWindowEx(WS_EX_CLIENTEDGE, "RICHEDIT", "", WS_CHILD | WS_VISIBLE | WS_BORDER | ES_MULTILINE | WS_VSCROLL | WS_HSCROLL | ES_AUTOVSCROLL | ES_AUTOHSCROLL, 0, 0, 0, 0, //とりあえず幅、高さ0のウィンドウを作る hWnd, (HMENU)1, hInst, NULL); SetInitialFont(hEdit); break; case WM_SIZE: MoveWindow(hEdit, 0, 0, LOWORD(lp), HIWORD(lp), TRUE); break; case WM_COMMAND: switch (LOWORD(wp)) { case IDM_END: SendMessage(hWnd, WM_CLOSE, 0, 0); break; case IDM_FONT: SetMyFont(hEdit); break; } break; case WM_CLOSE: id = MessageBox(hWnd, "終了してもよいですか", "終了確認", MB_YESNO | MB_ICONQUESTION); if (id == IDYES) { DestroyWindow(hEdit); DestroyWindow(hWnd); } 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_SIZEメッセージが着たらエディットコントロールの 大きさを親のクライアント領域にぴったり重ねます。 GetClientRect関数を使っても良いのですが、 ここではWM_SIZEメッセージのLPARAMを利用しています。
メニューから「フォントの変更」(IDM_FONT)が選択されたら 自作関数SetMyFontを呼び出します。
ところでこのプログラムではリッチテキストエディット コントロールを親の子供として作っているので 親を終了すると自動的に子供も終了します。 ところが、実際に作ってみればわかりますが このコントロールは終了するのに時間がかかります。 そこで親のウィンドウを破棄する前にこのコントロール ウィンドウを破棄するようにすればすんなり終了 することができます。
さらに終了前にロードしたライブラリを開放します。 (FreeLibrary)
最初の文字書式を設定する関数です。cfm.dwMaskを一度設定したら この値をグローバル変数にコピーして以後この値を使うことにします。BOOL SetInitialFont(HWND hEdit)//最初のフォントの設定 { CHARFORMAT cfm; memset(&cfm, 0, sizeof(CHARFORMAT)); cfm.cbSize = sizeof(CHARFORMAT); cfm.dwMask = CFM_BOLD | CFM_CHARSET | CFM_COLOR | CFM_FACE | CFM_ITALIC | CFM_SIZE | CFM_STRIKEOUT | CFM_UNDERLINE; dwMyMask = cfm.dwMask;//以後dwMyMaskの値を使う cfm.bCharSet = SHIFTJIS_CHARSET; strcpy(cfm.szFaceName, "MS ゴシック"); if (SendMessage(hEdit, EM_SETCHARFORMAT, SCF_SELECTION | SCF_WORD, (LPARAM)&cfm) == 0) { MessageBox(hEdit, "EM_SETCHARFORMAT失敗です", "Error", MB_OK); return FALSE; } return TRUE; }
現在の文字書式を取得して、フォント選択ダイアログボックスを 表示し、ユーザーの設定を反映させる関数です。BOOL SetMyFont(HWND hEdit) { CHARFORMAT cfm; CHOOSEFONT cf; LOGFONT lf; HDC hDC; memset(&cf, 0, sizeof(CHOOSEFONT)); memset(&lf, 0, sizeof(LOGFONT)); cfm.cbSize = sizeof(CHARFORMAT); //今までの設定を取得してそれをCHOOSEFONT構造体に渡す SendMessage(hEdit, EM_GETCHARFORMAT, TRUE, (LPARAM)&cfm); hDC = GetDC(hEdit); lf.lfHeight = MulDiv(cfm.yHeight, GetDeviceCaps(hDC, LOGPIXELSY), -1440); ReleaseDC(hEdit, hDC); cfm.dwMask = dwMyMask; if (cfm.dwEffects & CFE_BOLD) lf.lfWeight = FW_BOLD; else lf.lfWeight = FW_NORMAL; if (cfm.dwEffects & CFE_ITALIC) lf.lfItalic = TRUE; if (cfm.dwEffects & CFE_UNDERLINE) lf.lfUnderline = TRUE; if (cfm.dwEffects & CFE_STRIKEOUT) lf.lfStrikeOut = TRUE; lf.lfCharSet = cfm.bCharSet; lf.lfQuality = DEFAULT_QUALITY; lf.lfPitchAndFamily = cfm.bPitchAndFamily; strcpy( lf.lfFaceName, cfm.szFaceName ); cf.rgbColors = cfm.crTextColor; cf.lStructSize = sizeof(CHOOSEFONT); cf.hwndOwner = hEdit; cf.lpLogFont = &lf; cf.Flags = CF_SCREENFONTS | CF_EFFECTS | CF_INITTOLOGFONTSTRUCT; //フォント選択ダイアログを出して新しい設定を取得する if (ChooseFont(&cf)) { cfm.cbSize = sizeof(CHARFORMAT); cfm.dwMask = dwMyMask; cfm.yHeight = 2 * cf.iPointSize; cfm.dwEffects = 0; if (lf.lfWeight >= FW_BOLD) cfm.dwEffects |= CFE_BOLD; if (lf.lfItalic) cfm.dwEffects |= CFE_ITALIC; if (lf.lfUnderline) cfm.dwEffects |= CFE_UNDERLINE; if (lf.lfStrikeOut) cfm.dwEffects |= CFE_STRIKEOUT; cfm.crTextColor = (COLORREF)cf.rgbColors; cfm.bPitchAndFamily = lf.lfPitchAndFamily; cfm.bCharSet = lf.lfCharSet; strcpy(cfm.szFaceName, lf.lfFaceName); if(SendMessage(hEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cfm) == 0) { MessageBox(hEdit, "失敗です", "Error", MB_OK); return FALSE; } } SetFocus(hEdit); return TRUE; }
かなり面倒くさいですが中身はそんなに難しくないです。
cfm.dwEffectsにCFE_XXが含まれているかどうか 調べるにはcfm.dwEffects & CFE_XXの値を調べるとわかります。 含まれているとTRUE(0以外)含まれていないとFALSE(0)となります。
最もわかりにくいのがMulDivのところでしょう。
早い話がMulDiv(a, b, c)とあれば(a * b) / cのことです。int MulDiv( int nNumber, int nNumerator, int nDenominator );
さてLOGFONT構造体のヘルプを見るとこの構造体に ポイント単位で高さを指定するには
lfHeight = -MulDiv(PintSize, GetDeviceCaps(hDC, LOGPIXELSY), 72);
とせよ、と書いてあります。ところがリッチテキストエディットコントロール ではtwip単位を使います。(1point=20twip)したがって プログラムのような式になります。(まじめに考えると難しいので決まりきった 式として覚える)
ChooseFont関数については 第38章を参照してください。
Update 30/Jun/1998 By Y.Kumei