第138章 RTFに縦書き表示する


さて、前回までのプログラムを実際に使ってみると 「ファイル」「開く」でファイルを選択するとき 「読み取り専用ファイルとして開く」にチェックをつけても ファイルの編集ができてしまいました。今回はまず、これを 修正します。次に,この章のメインイベントである「縦書き表示」 について解説します。



まず、読み取り専用ファイルとしてオープンするにはどうすれば よいのでしょうか。これは簡単です。

1.GetOpenFileName関数を実行 2.通常の方法でファイルをオープンする 3.OPENFILENAME構造体(of)のFlagsメンバにOFN_READONLYが含まれるかどうか検査   (of.Flags & OFN_READONLY)を調べるとわかります 4.エディットコントロールにEM_SETOPTIONSメッセージでECO_READONLYを送ります

さて、EM_SETOPTIONSメッセージは次のようになっています

EM_SETOPTIONS wParam = (WPARAM) (UINT) fOperation; lParam = (LPARAM) (UINT) fOptions;

fOperationには次の中から1つを指定します。
ECOOP_SET はfOptionsで指定されたオプションを設定します。
ECOOP_ORは現在の設定に今回の設定を加えます。
ECOOP_ANDは現在の設定とfOptionsでの設定の論理積です。
ECOOP_XORは現在の設定からfOptionsでの設定を除外します。

ECOOP_SETとECOOP_ANDの違いに注意してください。前者はともかく 今回のfOptionsの設定をします。しかし、後者は今までの設定と 今回のfOptionsの指定で一致しているもののみを設定します。

fOptionsは次のものの組み合わせを指定します。
ECO_AUTOWORDSELECTIONはダブルクリックで単語を指定できます。
ECO_AUTOVSCROLL, ECO_AUTOHSCROLL, ECO_NOHIDESEL, ECO_READONLY, ECO_WANTRETURN, ECO_SAVESEL, ECO_SELECTIONBAR, ECO_VETICALは、 それぞれES_***を指定したのと同じ効果があります。 従って読み取り専用にするにはECO_READONLYを指定すればよいことになります。 また、この章の本題である縦書きはECO_VERTICALを指定すればよいですね。

実は筆者はこのメッセージを見逃していてリッチエディットコントロールの ウィンドウスタイルを後から変更するのはSetWindowLongを使うものと 早合点して大変苦労しました。わかってしまうと簡単なことも わかるまではいろいろ苦労が多いものですね。

さて、ここまで解説すると縦書きの説明はほとんど不要ですね。 コントロールが縦書きに設定されているときはメニューの「縦書き」 を選択できないようにしなくてはなりません。逆にコントロールが 横書きに設定されているときはメニューの「横書き」を選択不能 にしておく必要があります。この作業はこれまでのプログラムでは RTF_CheckMenuという自作関数の中でやっていました。 今回もそうします。



さて、現在の設定が横書きか縦書きかを知るにはどうしたら良いでしょうか。 これは、EM_GETOPTIONSメッセージを使えばわかります。

EM_GETOPTIONS wParam = (WPARAM) 0; lParam = (LPARAM) 0;

SendMessage関数の戻り値が現在のオプションになります。 この戻り値にECO_VERTICALが含まれているかどうかを調べます。 (戻り値 & ECO_VERTICAL)が0なら含まれていません。 0以外なら含まれています。

左の図のように縦書きも簡単に表示して印刷することができます。 1つ不満があります。縦書き表示をしたときに 「右揃え」は下端揃え?「左揃え」は通常の上からの表示となります。 縦書きか横書きかでメニューを変える必要がありそうですね。

また、縦書きと横書きを混在させることはできません。



では、早速プログラムを見てみることにしましょう。

// rich09.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 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 END END

IDM_VERTICAとIDM_HORIZONTALが増えました。

// rich09.cpp #define STRICT #include <windows.h> #include <richedit.h> #include "resource.h" 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); char szClassName[] = "rich09"; //ウィンドウクラス HINSTANCE hInst; DWORD dwMyMask; //CHARFORMATで使うマスク値 char szFName[MAX_PATH]; int nFileType; //1:RTF 2:TXT char *szAppTitle = "猫でもわかるRTF"; PRINTER_INFO_5 prninfo[3];

毎回関数のプロトタイプ宣言などを継ぎ足していくと 結構見づらくなってしまいました。プロトタイプ宣言を 独立したヘッダファイルにしてこれをインクルードするようにすれば もう少し見やすくなると思います。

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, szAppTitle, //タイトルバーにこの名前が表示されます 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; }

これは、毎回同じです。

//ウィンドウプロシージャ 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_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: MoveWindow(hEdit, 0, 0, LOWORD(lp), HIWORD(lp), 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); break; case IDM_CENTER: SetCenter(hEdit); break; case IDM_LEFT: SetLeft(hEdit); break; case IDM_RIGHT: SetRight(hEdit); 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); break; case IDM_HORIZONTAL: RTF_Horizontal(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; }

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) { 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_OKCANCEL | MB_ICONQUESTION); if (id == IDOK) 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\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: 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; }

読み取り専用でオープンしたときの処理を付け加えています。 また、タイトルバーに「== READONLY ==」と表示して現在開いている ファイルが読み取り専用であることをユーザーに知らせます。

DWORD CALLBACK MyReadProc(DWORD dwCookie, LPBYTE pbBuf, LONG cb, LONG *pcb)

この関数も前章と全く同じなので省略します。

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; } return; }

現在の設定が縦書きか横書きかでメニュー項目を変化させています。

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) { SendMessage(hEdit, EM_SETOPTIONS, (WPARAM)ECOOP_OR, (LPARAM)ECO_VERTICAL); SetFocus(hEdit); return; } void RTF_Horizontal(HWND hEdit) { SendMessage(hEdit, EM_SETOPTIONS, (WPARAM)ECOOP_XOR, (LPARAM)ECO_VERTICAL); SetFocus(hEdit); return; }

これらの関数は,今までの説明を読んでいただければ特に説明の必要はないですね。 ただ、横書きに戻すとき「横書きのオプション」というものはありません。 これは、縦書きのオプションをはずせばよいのです。
[SDK第2部 Index] [総合Index] [Previous Chapter] [Next Chapter]

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