いろいろなフォントと色,段落の揃えを変えて表示すると
左の図のようになります。なかなかカラフルで面白いですね。
段落を設定するとき、ちょっとした注意が必要です。
リッチエディットコントロールを作るときウィンドウスタイルに
つまり、行の幅がわからない(今より長くなればスクロールバーが出る) ので設定ができないのです。わかってしまえばどうということもありませんが、 気がつかないと非常にわかりにくいバグとなります。 今回はリッチエディットコントロールを作るときにこれを削除していますので 注意してください。ES_AUTOHSCROLLが含まれると失敗します!
では、段落書式を設定するにはどうしたらよいのでしょうか。 それには、EM_SETPARAFORMATメッセージをコントロールに送ります。
lParamにPARAFORMAT構造体のポインターを指定します。EM_SETPARAFORMAT wParam = 0; // 使用しません。0にします。 lParam = (LPARAM) (PARAFORMAT FAR *) lpFmt;
cbSizeはこの構造体のサイズです。typedef struct _paraformat { UINT cbSize; _WPAD _wPad1; DWORD dwMask; WORD wNumbering; WORD wReserved; LONG dxStartIndent; LONG dxRightIndent; LONG dxOffset; WORD wAlignment; SHORT cTabCount; LONG rgxTabs[MAX_TAB_STOPS]; } PARAFORMAT;
dwMaskは有効なメンバを指定します。
PFM_ALLIGNMENTはwAlignmentメンバが有効です。
PFM_NUMBERINGはwNumberingメンバが有効です。
PFM_OFFSETはdxOffsetメンバが有効です。
PFM_OFFSETINDENTはdxStartIndentメンバが有効で、相対値を指定している。
PFM_RIGHTINDENTはdxRightIndentメンバが有効です。
PFM_STARTINDENTはdxStartIndentメンバが有効です。
PFM_TABSTOPSはcTabCountとrgxTabsメンバが有効です。
wNumberingはナンバリングオプションの値を指定します。
wReservedは予約済みです。
dxStartIndentは段落の最初の行のインデントです。PFM_OFFSETINDENT が指定されている場合は相対値として扱われます。
dxRightIndent右インデント量です。
wAlignmentは段落の配置方法を指定します。
PFA_CENTERは段落が中央揃えになります。
PFA_LEFTは段落が左揃えになります。
PFA_RIGHTは段落が右揃えになります。
cTabCountはタブストップの数です。
rgxTabsはタブストップの位置を要素とする配列です。
ま、いろいろごちゃごちゃしていますが今回は wAlignmentしか使いません。これを有効にするにはdwMaskをPFM_ALLIGNMENTに する必要があります。
要約すると段落書式を設定するには
ということになります。では、早速プログラムを 見てみましょう。1.PARAFORMAT構造体を設定(pf) 2.SendMessage(hEdit, EM_SETPARAFORMAT, 0, (LPARAM)&pf);
メニュー項目が少し増えました。"MYMENU"も"MYPOPUP"も シンボル値が共通になっていることに注意してください。// rich03.rcの一部 ///////////////////////////////////////////////////////////////////////////// // // Menu // MYMENU MENU DISCARDABLE BEGIN POPUP "ファイル(&F)" BEGIN MENUITEM "終了(&X)", IDM_END END POPUP "編集(&E)" BEGIN MENUITEM "フォントの変更(&F)", IDM_FONT POPUP "段落書式" BEGIN MENUITEM "中央揃え(&C)", IDM_CENTER MENUITEM "左揃え(&L)", IDM_LEFT MENUITEM "右揃え(&R)", IDM_RIGHT END END END MYPOPUP MENU DISCARDABLE BEGIN POPUP "ダミーです" BEGIN MENUITEM "フォントの変更", IDM_FONT MENUITEM SEPARATOR MENUITEM "左揃え", IDM_LEFT MENUITEM "右揃え", IDM_RIGHT MENUITEM "中央揃え", IDM_CENTER MENUITEM SEPARATOR MENUITEM "終了", IDM_END END END
プログラム自体はいつもと同じですが少し自作関数が増えました。 また、グローバル変数も増えています。// rich03.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); BOOL SetInitialPara(HWND); BOOL SetCenter(HWND); BOOL SetLeft(HWND); BOOL SetRight(HWND); void ClearCheck(HMENU);//段落書式のチェックをはずす char szClassName[] = "rich03";//ウィンドウクラス HINSTANCE hInst; DWORD dwMyMask; //CHARFORMATで使うマスク値 int nPara;//現在の段落書式(0:right, 1:left, 2:center) HMENU hSubg; 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; }
EN_MSGFILTERで右クリックが来たときClearCheck自作関数で 一度メニューのすべてのチェックマークをクリアします。 nParaの値でどこにチェックをつけるかを指定します。 nParaの値は実際に段落書式が設定された段階で指定します。//ウィンドウプロシージャ LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp) { int id; static HINSTANCE hRtLib; static HWND hEdit; DWORD dwEvent; MSGFILTER *pmf; HMENU hMenu, hSub; int x, y; POINT pt; 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 | ES_AUTOVSCROLL | 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); SetInitialPara(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); ClearCheck(hSub); switch (nPara) { case 0://右寄せ CheckMenuItem(hSub, IDM_RIGHT, MF_BYCOMMAND | MF_CHECKED); break; case 1://左寄せ CheckMenuItem(hSub, IDM_LEFT, MF_BYCOMMAND | MF_CHECKED); break; case 2://中央 CheckMenuItem(hSub, IDM_CENTER, MF_BYCOMMAND | MF_CHECKED); break; } TrackPopupMenu(hSub, TPM_LEFTALIGN, pt.x, pt.y, 0, hWnd, NULL); DestroyMenu(hMenu); } break; } break; case WM_SIZE: MoveWindow(hEdit, 0, 0, LOWORD(lp), HIWORD(lp), TRUE); SetFocus(hEdit); break; case WM_COMMAND: switch (LOWORD(wp)) { case IDM_END: SendMessage(hWnd, WM_CLOSE, 0, 0); break; case IDM_FONT: SetMyFont(hEdit); break; case IDM_CENTER: SetCenter(hEdit); break; case IDM_LEFT: SetLeft(hEdit); break; case IDM_RIGHT: SetRight(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_COMMANDのところでIDM_CENTER, IDM_LEFT, IDM_RIGHTがきたら それぞれの自作関数を呼んで書式を設定します。
最初のフォントを設定する関数です。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, (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; }
名前からすると最初の書式を設定する関数のように 思えますが実際はクラスメニューの「左揃え」にチェックをつけているだけです。 また、nParaを1にして右クリックメニューにも「左揃え」にチェックがつけられる ようにしています。 (自分で何も設定していないときは「左揃え」になっています。)BOOL SetInitialPara(HWND hEdit) { HMENU hMenu, hSub; HWND hParent; hParent = GetParent(hEdit); hMenu = GetMenu(hParent); hSub = GetSubMenu(hMenu, 1); hSubg = hSub; CheckMenuItem(hSub, IDM_LEFT, MF_BYCOMMAND | MF_CHECKED); nPara = 1; return TRUE; }
段落を「中央揃え」にする関数です。中央揃えにしたら メニューのチェックを全部はずして、新たにIDM_CENTERに チェックをつけます。nParaを2にして右クリックメニューに 今何が選択されているかをわかるようにします。BOOL SetCenter(HWND hEdit) { PARAFORMAT pf; memset(&pf, 0, sizeof(PARAFORMAT)); pf.cbSize = sizeof(PARAFORMAT); pf.dwMask = PFM_ALIGNMENT; pf.wAlignment = PFA_CENTER; SendMessage(hEdit, EM_SETPARAFORMAT, 0, (LPARAM)&pf); ClearCheck(hSubg);//メニューのチェックを全部はずす CheckMenuItem(hSubg, IDM_CENTER, MF_BYCOMMAND | MF_CHECKED); nPara = 2; SetFocus(hEdit); return TRUE; }
左と右揃えをする関数です。中身は中央揃えと同じです。BOOL SetLeft(HWND hEdit) { PARAFORMAT pf; memset(&pf, 0, sizeof(PARAFORMAT)); pf.cbSize = sizeof(PARAFORMAT); pf.dwMask = PFM_ALIGNMENT; pf.wAlignment = PFA_LEFT; SendMessage(hEdit, EM_SETPARAFORMAT, 0, (LPARAM)&pf); ClearCheck(hSubg); CheckMenuItem(hSubg, IDM_LEFT, MF_BYCOMMAND | MF_CHECKED); nPara = 1; SetFocus(hEdit); return TRUE; } BOOL SetRight(HWND hEdit) { PARAFORMAT pf; memset(&pf, 0, sizeof(PARAFORMAT)); pf.cbSize = sizeof(PARAFORMAT); pf.dwMask = PFM_ALIGNMENT; pf.wAlignment = PFA_RIGHT; SendMessage(hEdit, EM_SETPARAFORMAT, 0, (LPARAM)&pf); ClearCheck(hSubg); CheckMenuItem(hSubg, IDM_RIGHT, MF_BYCOMMAND | MF_CHECKED); nPara = 0; SetFocus(hEdit); return TRUE; }
メニューのチェックをはずす関数です。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); return; }
今回も内容的には大した物ではありません。しかし、ちょっとしたことで 非常にわかりにくいバグが発生するので注意が必要です。
Update 12/Jul/1998 By Y.Kumei