第257章 チャットの内容を編集する


今回は、チャットの内容を編集します。と、いっても単にメモ帳の編集機能と 同じです。また、原始的ツールバーもつけてみます。簡単にできそうですが いざ作ってみると思いがけない、困難がいろいろ出てきます。



メモ帳もどきは第74章でもやっています。 しかし、この時は編集メニューもなく、 ツールバーがありませんでした。また、左の図のように文字列が選択されていないときは メニュー同様、編集関係のツールバーは使用不可にしなくてはいけません。

また、クリップボードにテキストがないときは、張り付けボタンは使用不可です。

文字列を選択後、切り取りボタンで切り取ったときは選択範囲がなくなるので 切り取りボタンは使用不可でなくてはいけません。

さらに名前を付けて保存が実行される前は、上書き保存ボタンも使用不可にしたいものです。



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

// mailslot08.rcの一部 ///////////////////////////////////////////////////////////////////////////// // // Menu // MYMENU MENU DISCARDABLE BEGIN POPUP "ファイル(&F)" BEGIN MENUITEM "新規作成(&N)...", IDM_NEW MENUITEM "開く(&O)...", IDM_OPEN MENUITEM "上書き保存(&S)", IDM_SAVE MENUITEM "名前を付けて保存(&A)...", IDM_SAVEAS MENUITEM SEPARATOR MENUITEM "タスクトレーに格納(&T)", IDM_TASK MENUITEM SEPARATOR MENUITEM "終了(&X)...", IDM_END END POPUP "編集(&E)" BEGIN MENUITEM "元に戻す(&U)", IDM_UNDO, GRAYED MENUITEM SEPARATOR MENUITEM "切り取り(&T)", IDM_CUT, GRAYED MENUITEM "コピー(&C)", IDM_COPY, GRAYED MENUITEM "張り付け(&P)", IDM_PASTE, GRAYED MENUITEM "削除(&D)", IDM_DELETE, GRAYED MENUITEM SEPARATOR MENUITEM "すべて選択(&L)", IDM_ALL END POPUP "表示(&V)" BEGIN MENUITEM "書き込み用ウィンドウ", IDM_WRITE END POPUP "オプション(&O)" BEGIN MENUITEM "IDを表示する(&I)", IDM_ID MENUITEM SEPARATOR MENUITEM ".chtを関連づける(&R)", IDM_REG END END RTRAYMENU MENU DISCARDABLE BEGIN POPUP " ダミーです" BEGIN MENUITEM "タスクトレーから出す", IDM_SHOW MENUITEM "終了", IDM_END END END ///////////////////////////////////////////////////////////////////////////// // // Dialog // MAILSLOTNAME DIALOG DISCARDABLE 0, 0, 118, 61 STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU CAPTION "メールスロット名" FONT 9, "MS Pゴシック" BEGIN EDITTEXT IDC_EDIT1,7,19,104,16,ES_AUTOHSCROLL DEFPUSHBUTTON "OK",IDOK,34,39,50,14 LTEXT "自分のメールスロットの名前",IDC_STATIC,7,7,83,8 END MYWRITE DIALOG DISCARDABLE 0, 0, 163, 153 STYLE DS_MODALFRAME | WS_CHILD FONT 9, "MS Pゴシック" BEGIN EDITTEXT IDC_EDIT3,70,23,86,14,ES_AUTOHSCROLL EDITTEXT IDC_EDIT2,70,44,86,14,ES_AUTOHSCROLL EDITTEXT IDC_EDIT1,7,69,149,56,ES_MULTILINE | ES_AUTOVSCROLL | ES_AUTOHSCROLL | ES_WANTRETURN DEFPUSHBUTTON "送信",IDOK,56,132,50,14 LTEXT "相手のメールスロット",IDC_STATIC,7,45,62,8 LTEXT "送信先コンピュータ",IDC_STATIC,7,25,56,8 CONTROL "他のコンピュータにメッセージを送る",IDC_CHECK1,"Button", BS_AUTOCHECKBOX | WS_TABSTOP,7,7,119,10 END ///////////////////////////////////////////////////////////////////////////// // // Icon // MYICON ICON DISCARDABLE "myicon.ico"

メニューに「新規作成」と編集関係の項目が増えました。他は同じです。

// mailslot08.cpp #ifndef STRICT #define STRICT #endif #include <windows.h> #include <windowsx.h> #include <commctrl.h> #include "resource.h" #define MYMSG_TRAY WM_USER #define ID_MYEDIT 100 #define ID_MYTRAY 101 #define ID_MYTOOL 102 LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); LRESULT CALLBACK MyNameProc(HWND, UINT, WPARAM, LPARAM); LRESULT CALLBACK MyWriteProc(HWND, UINT, WPARAM, LPARAM); LRESULT CALLBACK MyEditProc(HWND, UINT, WPARAM, LPARAM); ATOM InitApp(HINSTANCE); BOOL InitInstance(HINSTANCE, int); DWORD WINAPI ReadFunc(LPVOID); HANDLE MakeMySlot(HWND, BOOL *); void MyTaskTray(HWND, PNOTIFYICONDATA); int MakeRTrayMenu(HWND); int SaveAsMyFile(HWND); int OverWriteMyFile(HWND); int OpenMyFile(HWND); int OpenMyFile2(HWND); int MyReg(void); HWND MakeMyToolbar(HWND); int MakeNewFile(HWND); int CheckMyMenu(HWND); BOOL CanPaste(HWND); char szClassName[] = "mailslot08"; //ウィンドウクラス char szMailSlot[128];//メールスロットの名前(フルパス付き) char szSlotName[64];//メールスロットの名前(パスなし) HANDLE hSlot; HINSTANCE hInst; HWND hMainEdit; BOOL bID = FALSE;//メッセージIDを表示するかどうか BOOL bIsTask = FALSE;//タスクトレーに格納されているかどうか HWND hWriteDlg;//書き込み用モードレスダイアログボックス int DlgX, DlgY;//書き込み用ダイアログの大きさ BOOL bWriteDlg = FALSE;//書き込み用ダイアログが表示されいるかどうか char szFileName[MAX_PATH];//パス付きファイル名 char szFile[64];//ファイルタイトル BOOL bCmdLine;//コマンドライン引数があるかどうか WNDPROC Org_EditProc;//エディットコントロールのオリジナルプロシージャ HWND hTool; TBBUTTON tbButton[] = { {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_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}, };

コモンコントロールを使うため、その前準備(commctrl.hのインクルード、 comctl32.libのリンク)を忘れないでください。

エディットコントロールをサブクラス化するため、新しいエディットコントロールの プロシージャを用意しました(MyEditProc)。

ツールバーを作る関数(MakeMyToolbar)、ファイル新規作成関数(MakeNewFile)、 編集機能関連のメニュー項目やボタンの使用可否を決定する関数(CheckMyMenu)、 張り付けが可能かどうか調査する関数(CanPaste)などを新たに用意しました。

int WINAPI WinMain(HINSTANCE hCurInst, HINSTANCE hPrevInst, LPSTR lpsCmdLine, int nCmdShow) { MSG msg; hInst = hCurInst; if (strcmp(lpsCmdLine, "") != 0) { strcpy(szFileName, lpsCmdLine); GetFileTitle(lpsCmdLine, szFile, sizeof(szFile)); bCmdLine = TRUE; } if (!InitApp(hCurInst)) return FALSE; if (!InitInstance(hCurInst, nCmdShow)) return FALSE; while (GetMessage(&msg, NULL, 0, 0)) { if (hWriteDlg == 0 || !IsDialogMessage(hWriteDlg, &msg)) { TranslateMessage(&msg); DispatchMessage(&msg); } } MessageBox(NULL, "メッセージループを抜けました", "OK", MB_OK); return msg.wParam; } //ウィンドウ・クラスの登録 ATOM 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(hInst, "MYICON");//アイコン return (RegisterClassEx(&wc)); } //ウィンドウの生成 BOOL InitInstance(HINSTANCE hInst, int nCmdShow) { HWND hWnd; hWnd = CreateWindow(szClassName, "猫でもわかるメールスロット", //タイトルバーにこの名前が表示されます WS_OVERLAPPEDWINDOW, //ウィンドウの種類 CW_USEDEFAULT, //X座標 CW_USEDEFAULT, //Y座標 600, //幅 309, //高さ NULL, //親ウィンドウのハンドル、親を作るときはNULL NULL, //メニューハンドル、クラスメニューを使うときはNULL hInst, //インスタンスハンドル 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 BOOL bEnd; static HANDLE hThread; static HMENU hMenu; MENUITEMINFO mi; static NOTIFYICONDATA ni; RECT rc; MINMAXINFO *lpmm; INITCOMMONCONTROLSEX ic; static int nToolH; switch (msg) { case WM_CREATE: ic.dwSize = sizeof(INITCOMMONCONTROLSEX); ic.dwICC = ICC_BAR_CLASSES; InitCommonControlsEx(&ic); hMenu = GetMenu(hWnd); hTool = MakeMyToolbar(hWnd); if (!bCmdLine) { EnableMenuItem(hMenu, IDM_SAVE, MF_BYCOMMAND | MF_GRAYED); SendMessage(hTool, TB_ENABLEBUTTON, (WPARAM)IDM_SAVE, (LPARAM)MAKELONG(FALSE, 0)); } GetWindowRect(hTool, &rc); nToolH = rc.bottom - rc.top; hMainEdit = CreateWindow("EDIT", "", WS_CHILD | WS_VISIBLE | ES_MULTILINE | ES_WANTRETURN | ES_NOHIDESEL | ES_AUTOHSCROLL | ES_AUTOVSCROLL | WS_HSCROLL | WS_VSCROLL, 0, nToolH, 0, 0, hWnd, (HMENU)ID_MYEDIT, hInst, NULL); Org_EditProc = (WNDPROC)GetWindowLong(hMainEdit, GWL_WNDPROC); SetWindowLong(hMainEdit, GWL_WNDPROC, (LONG)MyEditProc); DialogBox(hInst, "MAILSLOTNAME", hWnd, (DLGPROC)MyNameProc); bEnd = FALSE; hThread = MakeMySlot(hWnd, &bEnd); if (bCmdLine) { if (OpenMyFile2(hWnd) == 0) { EnableMenuItem(hMenu, IDM_SAVE, MF_BYCOMMAND | MF_ENABLED); SendMessage(hTool, TB_ENABLEBUTTON, (WPARAM)IDM_SAVE, (LPARAM)MAKELONG(TRUE, 0)); } } SetFocus(hMainEdit); CheckMyMenu(hMainEdit); break; case WM_SIZE: SendMessage(hTool, WM_SIZE, wp, lp); if (!bWriteDlg) { MoveWindow(hMainEdit, 0, nToolH, LOWORD(lp), HIWORD(lp)-nToolH, TRUE); } else { MoveWindow(hMainEdit, 0, nToolH, LOWORD(lp) - DlgX, HIWORD(lp) - nToolH, TRUE); MoveWindow(hWriteDlg, LOWORD(lp) - DlgX, nToolH, DlgX, HIWORD(lp)- nToolH, TRUE); } break; case WM_GETMINMAXINFO: if (bWriteDlg) { lpmm = (MINMAXINFO *)lp; lpmm->ptMinTrackSize.y = 281 + nToolH; lpmm->ptMinTrackSize.x = 450; } else { return (DefWindowProc(hWnd, msg, wp, lp)); } break; case MYMSG_TRAY: if (wp == ID_MYTRAY) { switch (lp) { case WM_RBUTTONDOWN: MakeRTrayMenu(hWnd); break; case WM_LBUTTONDBLCLK: bIsTask = FALSE; Shell_NotifyIcon(NIM_DELETE, &ni); ShowWindow(hWnd, SW_SHOWNORMAL); default: break; } } break; case WM_COMMAND: switch (LOWORD(wp)) { case IDM_END: SendMessage(hWnd, WM_CLOSE, 0, 0); break; case IDM_WRITE: if (!IsWindow(hWriteDlg)) { hWriteDlg = CreateDialog(hInst, "MYWRITE", hWnd, (DLGPROC)MyWriteProc); if (hWriteDlg != NULL) { bWriteDlg = TRUE; ShowWindow(hWriteDlg, SW_SHOW); GetWindowRect(hWriteDlg, &rc); DlgX = rc.right - rc.left; DlgY = rc.bottom - rc.top; GetClientRect(hWnd, &rc); MoveWindow(hWriteDlg, rc.right - rc.left - DlgX, rc.top + nToolH, DlgX, rc.bottom -rc.top - nToolH, TRUE); MoveWindow(hMainEdit, 0, nToolH, rc.right - rc.left - DlgX, rc.bottom - rc.top - nToolH, TRUE); memset(&mi, 0, sizeof(MENUITEMINFO)); mi.cbSize = sizeof(MENUITEMINFO); mi.fMask = MIIM_STATE; mi.fState = MFS_CHECKED; SetMenuItemInfo(hMenu, IDM_WRITE, FALSE, &mi); } else { MessageBox(hWnd, "ダイアログボックス作成失敗です", "Error", MB_OK); return 0; } } else { DestroyWindow(hWriteDlg); bWriteDlg = FALSE; GetClientRect(hWnd, &rc); MoveWindow(hMainEdit, 0, nToolH, rc.right - rc.left, rc.bottom - rc.top - nToolH, TRUE); memset(&mi, 0, sizeof(MENUITEMINFO)); mi.cbSize = sizeof(MENUITEMINFO); mi.fMask = MIIM_STATE; mi.fState = MFS_UNCHECKED; SetMenuItemInfo(hMenu, IDM_WRITE, FALSE, &mi); } break; case IDM_NEW: MakeNewFile(hWnd); EnableMenuItem(hMenu, IDM_SAVE, MF_BYCOMMAND | MF_GRAYED); SendMessage(hTool, TB_ENABLEBUTTON, (WPARAM)IDM_SAVE, (LPARAM)MAKELONG(FALSE, 0)); CheckMyMenu(hMainEdit); break; case IDM_OPEN: if (OpenMyFile(hWnd) == 0) { EnableMenuItem(hMenu, IDM_SAVE, MF_BYCOMMAND | MF_ENABLED); SendMessage(hTool, TB_ENABLEBUTTON, (WPARAM)IDM_SAVE, (LPARAM)MAKELONG(TRUE, 0)); } break; case IDM_SAVE: OverWriteMyFile(hWnd); break; case IDM_SAVEAS: SaveAsMyFile(hWnd); break; case IDM_ID: memset(&mi, 0, sizeof(MENUITEMINFO)); mi.cbSize = sizeof(MENUITEMINFO); mi.fMask = MIIM_STATE; if (!bID) { mi.fState = MFS_CHECKED; SetMenuItemInfo(hMenu, IDM_ID, FALSE, &mi); bID = TRUE; break; } else { mi.fState = MFS_UNCHECKED; SetMenuItemInfo(hMenu, IDM_ID, FALSE, &mi); bID = FALSE; } break; case IDM_TASK: MyTaskTray(hWnd, &ni); ShowWindow(hWnd, SW_HIDE); bIsTask = TRUE; break; case IDM_SHOW: Shell_NotifyIcon(NIM_DELETE, &ni); ShowWindow(hWnd, SW_SHOWNORMAL); bIsTask = FALSE; break; case IDM_REG: MyReg(); break; case IDM_COPY: SendMessage(hMainEdit, WM_COPY, 0, 0); CheckMyMenu(hMainEdit); break; case IDM_CUT: SendMessage(hMainEdit, WM_CUT, 0, 0); CheckMyMenu(hMainEdit); break; case IDM_PASTE: SendMessage(hMainEdit, WM_PASTE, 0, 0); break; case IDM_ALL: SendMessage(hMainEdit, EM_SETSEL, 0, -1); CheckMyMenu(hMainEdit); break; case IDM_DELETE: SendMessage(hMainEdit, WM_CLEAR, 0, 0); CheckMyMenu(hMainEdit); break; case IDM_UNDO: if (SendMessage(hMainEdit, EM_CANUNDO, 0, 0)) SendMessage(hMainEdit, WM_UNDO, 0, 0); CheckMyMenu(hMainEdit); break; } break; case WM_CLOSE: if (SendMessage(hMainEdit, EM_GETMODIFY, 0, 0)) { id = MessageBox(hWnd, "チャットの内容が更新されています。\n" "保存せずに終了しますか", "確認", MB_YESNO | MB_ICONQUESTION); if (id == IDNO) break; } id = MessageBox(hWnd, "終了してもよいですか", "終了確認", MB_YESNO | MB_ICONQUESTION); if (id == IDYES) { if (hThread) { bEnd = TRUE; WaitForSingleObject(hThread, INFINITE); if (CloseHandle(hThread)) MessageBox(hWnd, "hThreadをクローズしました", "OK", MB_OK); } if (hSlot) { if (CloseHandle(hSlot)) MessageBox(hWnd, "メールスロットを削除しました", "OK", MB_OK); } if (bIsTask) Shell_NotifyIcon(NIM_DELETE, &ni); SetWindowLong(hMainEdit, GWL_WNDPROC, (LONG)Org_EditProc); DestroyWindow(hMainEdit); DestroyWindow(hTool); if (hWriteDlg) DestroyWindow(hWriteDlg); DestroyWindow(hWnd); } break; case WM_DESTROY: PostQuitMessage(0); break; default: return (DefWindowProc(hWnd, msg, wp, lp)); } return 0; }

WM_CREATEメッセージが来たらInitCommonControlsEx関数でコモンコントロールの初期化を行います。 エディットコントロールを作った後でこれをサブクラス化しています。

コマンドラインがなければ「上書き保存」メニュー項目とボタンを使用不可にします。 コマンドライン引数が有って、ファイルを開くのに成功したら「上書き保存」メニュー項目とボタンを 使用可能にします。

最後に自作関数CheckMyMenuを呼んで編集関係のメニュー項目やらボタン等の使用可否を決めます。

WM_SIZE, WM_GETMINMAXINFOメッセージが来たときの処理が少し違います。これは、ツールバーが増えてその分を 考慮しなくてはいけないからです。

IDM_WRITE(書き込みダイアログ表示)が来たときのエディットコントロール等の大きさ計算 もツールバーを考慮しなくてはいけません。

IDM_NEW(新規作成)が来たときは、MakeNewFile関数を呼んで 「上書き保存」メニュー項目とボタンを使用不可にします。新規作成時はまだファイル名が ついていないからです。そしてCheckMyMenu関数を呼んで編集関係のメニュー項目、ボタンの 使用可否を調査します。

IDM_OPEN(開く)が来たときは、OpenMyFile関数を呼んでこれが成功したら、「上書き保存」メニュー項目と ボタンを有効にします。

メニューから編集関係の項目が選択されたら、それぞれのコマンドを実行してから、CheckMyMenu関数を 呼びます。IDM_PASTEの時は呼んでいません。

さて、これだけでは編集関係のメニュー項目、ボタンの使用可否のチェックは全く不十分です。 文字列が選択されたときなどにまた、CheckMyMenu関数を呼ぶ必要があります。 文字列を選択するにはマウスでドラッグするか、シフトキーを押しながらカーソルキーを押す操作を 行います。エディットコントロールに対してこれらのイベントが起こったときにCheckMyMenuを 呼ぶようにするとよいです。そのためにエディットコントロールをサブクラス化しました。

プログラム終了時に、サブクラス化の解除とツールバーの破棄の作業が増えます。

HANDLE MakeMySlot(HWND hWnd, BOOL *lpbEnd) { HMENU hMenu; hMenu = GetMenu(hWnd); char szWinTitle[64], *szTitle_Org = "猫でもわかるメールスロット[%s][%s]"; DWORD dwThreadID; HANDLE hThread; hSlot = CreateMailslot( szMailSlot, 0, MAILSLOT_WAIT_FOREVER, NULL); if (hSlot == INVALID_HANDLE_VALUE) { MessageBox(hWnd, "メールスロット作成失敗!\n名前を変更してみてください", "Error", MB_OK); DialogBox(hInst, "MAILSLOTNAME", hWnd, (DLGPROC)MyNameProc); MakeMySlot(hWnd, lpbEnd); } else { wsprintf(szWinTitle, szTitle_Org, szSlotName, szFile); SetWindowText(hWnd, szWinTitle); hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)ReadFunc, (LPVOID)lpbEnd, 0, &dwThreadID); if (hThread == NULL) MessageBox(hWnd, "CreateThread Error", "Error", MB_OK); } return hThread; }

この関数中にIDM_WRITE, IDM_TASKのメニュー項目を使用可能にする一文が入っていましたが 不要なので削除しました。

MyNameProc, MyWriteProc, ReadFunc, MyTaskTray, MakeRTrayMenu,の各関数に変更はありません

int MakeNewFile(HWND hWnd) { int id; char szWinTitle[64], *szTitle_org = "猫でもわかるメールスロット[%s][%s]"; if (SendMessage(hMainEdit, EM_GETMODIFY, 0, 0) == TRUE) { id = MessageBox( hWnd, "文書が更新されています。保存しますか?", "注意", MB_YESNO | MB_ICONQUESTION); if (id == IDYES) SaveAsMyFile(hWnd); } wsprintf(szWinTitle, szTitle_org, szSlotName, "無題"); SetWindowText(hWnd, szWinTitle); Edit_SetText(hMainEdit, ""); return 0; }

「新規作成」をする関数です。特に説明は不要ですね。

int SaveAsMyFile(HWND hWnd) { OPENFILENAME ofn; HANDLE hFile; HGLOBAL hMem; DWORD dwAccBytes; char szWinTitle[64], *szTitle_org = "猫でもわかるメールスロット[%s][%s]"; char *lpszBuf; int nLen; HMENU hMenu; nLen = GetWindowTextLength(hMainEdit); hMem = GlobalAlloc(GHND, nLen + 1); if (hMem == NULL) { MessageBox(hWnd, "メモリの確保に失敗しました", "Error", MB_OK); return -1; } lpszBuf = (char *)GlobalLock(hMem); GetWindowText(hMainEdit, lpszBuf, nLen + 1); memset(&ofn, 0, sizeof(OPENFILENAME)); ofn.lStructSize = sizeof(OPENFILENAME); ofn.hwndOwner = hWnd; ofn.lpstrFilter = "chat(*.cht)\0*.cht\0text(*.txt)\0*.txt\0All files(*.*)\0*.*\0\0"; ofn.lpstrFile = szFileName; ofn.lpstrFileTitle = szFile; ofn.nFilterIndex = 1; ofn.nMaxFile = sizeof(szFileName); ofn.nMaxFileTitle = sizeof(szFile); ofn.Flags = OFN_OVERWRITEPROMPT | OFN_HIDEREADONLY; ofn.lpstrDefExt = "cht"; ofn.lpstrTitle = "チャットを名前を付けて保存する"; if(GetSaveFileName(&ofn) == 0) return -1; hFile = CreateFile(szFileName, GENERIC_WRITE, 0, 0, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); WriteFile(hFile, lpszBuf, strlen(lpszBuf) + 1, &dwAccBytes, NULL); SetEndOfFile(hFile); wsprintf(szWinTitle, szTitle_org, szSlotName, szFile); SetWindowText(hWnd, szWinTitle); if(CloseHandle(hFile) == 0) MessageBox(hWnd, "Error CloseHandle", "Error", MB_OK); if (GlobalUnlock(hMem) != 0) MessageBox(hWnd, "ロックカウントが0ではありません", "Warning", MB_OK); if (GlobalFree(hMem) != NULL) MessageBox(hWnd, "メモリ解放失敗に失敗しました", "Error", MB_OK); SendMessage(hMainEdit, EM_SETMODIFY, FALSE, 0); hMenu = GetMenu(hWnd); EnableMenuItem(hMenu, IDM_SAVE, MF_BYCOMMAND | MF_ENABLED); SendMessage(hTool, TB_ENABLEBUTTON, (WPARAM)IDM_SAVE, (LPARAM)MAKELONG(TRUE, 0)); return 0; }

「名前を付けて保存」を実行する関数です。「上書き保存」ボタンを有効にする作業が増えています。

OverWriteMyFile, OpenMyFile, OpenMyFile2の各関数に変更はありません

int MyReg() { HKEY hKey; DWORD dwDisposition; LONG lResult; char szCommand[MAX_PATH]; char szIcon[MAX_PATH]; GetCurrentDirectory(sizeof(szCommand), szCommand); strcat(szCommand, "\\mailslot08 %1"); GetCurrentDirectory(sizeof(szIcon), szIcon); strcat(szIcon, "\\mailslot08.exe,0"); lResult = RegCreateKeyEx(HKEY_CLASSES_ROOT, "MyChat\\shell\\猫チャット(&C)\\command", NULL,//予約済み "", REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hKey, &dwDisposition); if (lResult != ERROR_SUCCESS) { MessageBox(NULL, "RegCreateKeyExでエラーが発生しました", "Error", MB_OK); return -1; } lResult = RegSetValueEx(hKey, NULL, 0, REG_SZ, (BYTE *)szCommand, strlen(szCommand)); RegCloseKey(hKey); lResult = RegCreateKeyEx(HKEY_CLASSES_ROOT, ".cht", NULL, "", REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hKey, &dwDisposition); if (lResult != ERROR_SUCCESS) { MessageBox(NULL, "RegCreateKeyExでエラーが発生しました", "Error", MB_OK); return -1; } lResult = RegSetValueEx(hKey, NULL, 0, REG_SZ, (BYTE *)"MyChat", strlen("MyChat")); RegCloseKey(hKey); lResult = RegCreateKeyEx(HKEY_CLASSES_ROOT, "MyChat\\DefaultIcon", NULL, "", REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hKey, &dwDisposition); if (lResult != ERROR_SUCCESS) { MessageBox(NULL, "RegCreateKeyExでエラーが発生しました", "Error", MB_OK); return -1; } lResult = RegSetValueEx(hKey, NULL, 0, REG_SZ, (BYTE *)szIcon, strlen(szIcon)); RegCloseKey(hKey); return 0; }

ほとんど同じですが、EXEの名前が変更になりました。

HWND MakeMyToolbar(HWND hWnd) { HWND hTool; TBADDBITMAP tbab; int stdid, i; hTool = CreateToolbarEx(hWnd, WS_VISIBLE | WS_CHILD, ID_MYTOOL, 0, NULL, NULL, tbButton, 0, 0, 0, 0, 0, sizeof(TBBUTTON)); tbab.hInst = HINST_COMMCTRL; tbab.nID = IDB_STD_SMALL_COLOR; stdid = SendMessage(hTool, TB_ADDBITMAP, 7, (LPARAM)&tbab); for (i = 0; i < 7; i++) tbButton[i].iBitmap += stdid; SendMessage(hTool, TB_ADDBUTTONS, 7, (LONG)&tbButton[0]); SendMessage(hTool, TB_AUTOSIZE, 0, 0); return hTool; }

ツールバーを作る関数です。第61章などを参照してください。 原始的なツールバーなのでフラットツールバーなどにしてみてください。

int CheckMyMenu(HWND hWnd) { DWORD dwStart, dwEnd; HMENU hMenu; HWND hParent; hParent = GetParent(hWnd); hMenu = GetMenu(hParent); if (Edit_GetTextLength(hWnd) == 0) { EnableMenuItem(hMenu, IDM_ALL, MF_BYCOMMAND | MF_GRAYED); } else { EnableMenuItem(hMenu, IDM_ALL, MF_BYCOMMAND | MF_ENABLED); } SendMessage(hMainEdit, EM_GETSEL, (WPARAM)&dwStart, (LPARAM)&dwEnd); if (dwStart == dwEnd) { EnableMenuItem(hMenu, IDM_COPY, MF_BYCOMMAND | MF_GRAYED); EnableMenuItem(hMenu, IDM_CUT, MF_BYCOMMAND | MF_GRAYED); EnableMenuItem(hMenu, IDM_DELETE, MF_BYCOMMAND | MF_GRAYED); SendMessage(hTool, TB_ENABLEBUTTON, (WPARAM)IDM_COPY, (LPARAM)MAKELONG(FALSE, 0)); SendMessage(hTool, TB_ENABLEBUTTON, (WPARAM)IDM_CUT, (LPARAM)MAKELONG(FALSE, 0)); } else { EnableMenuItem(hMenu, IDM_COPY, MF_BYCOMMAND | MF_ENABLED); EnableMenuItem(hMenu, IDM_CUT, MF_BYCOMMAND | MF_ENABLED); EnableMenuItem(hMenu, IDM_DELETE, MF_BYCOMMAND | MF_ENABLED); SendMessage(hTool, TB_ENABLEBUTTON, (WPARAM)IDM_COPY, (LPARAM)MAKELONG(TRUE, 0)); SendMessage(hTool, TB_ENABLEBUTTON, (WPARAM)IDM_CUT, (LPARAM)MAKELONG(TRUE, 0)); } if (SendMessage(hMainEdit, EM_GETMODIFY, 0, 0)) { EnableMenuItem(hMenu, IDM_UNDO, MF_BYCOMMAND | MF_ENABLED); SendMessage(hTool, TB_ENABLEBUTTON, (WPARAM)IDM_UNDO, (LPARAM)MAKELONG(TRUE, 0)); } else { EnableMenuItem(hMenu, IDM_UNDO, MF_BYCOMMAND | MF_GRAYED); SendMessage(hTool, TB_ENABLEBUTTON, (WPARAM)IDM_UNDO, (LPARAM)MAKELONG(FALSE, 0)); } if (CanPaste(hParent)) { EnableMenuItem(hMenu, IDM_PASTE, MF_BYCOMMAND | MF_ENABLED); SendMessage(hTool, TB_ENABLEBUTTON, (WPARAM)IDM_PASTE, (LPARAM)MAKELONG(TRUE, 0)); } else { EnableMenuItem(hMenu, IDM_PASTE, MF_BYCOMMAND | MF_GRAYED); SendMessage(hTool, TB_ENABLEBUTTON, (WPARAM)IDM_PASTE, (LPARAM)MAKELONG(FALSE, 0)); } return 0; }

IDM_ALL, IDM_COPY, IDM_CUT, IDM_DELETE, IDM_UNDO, IDM_PASTEのメニュー項目、ボタンの 使用の可否を決める関数です。張り付けができるかどうかはEM_CANPASTEメッセージなどは 使えないので(リッチ・エディットコントロールでは可能)自作関数(CanPaste)で調べることにしました。

// サブクラス化されたエディットコントロールのプロシージャ LRESULT CALLBACK MyEditProc(HWND hEdit, UINT msg, WPARAM wp, LPARAM lp) { switch (msg) { case WM_LBUTTONUP: CheckMyMenu(hEdit); break; case WM_KEYUP: CheckMyMenu(hEdit); break; default: break; } return (CallWindowProc(Org_EditProc, hEdit, msg, wp, lp)); }

ここでは、エディットコントロールにWM_LBUTTONUP, WM_KEYUPメッセージが 届く前に横取りしてCheckMyMenu関数を呼びます。しかし、横取りしたまま では本来のエディットコントロールの機能を損なうので全部エディットコントロールに 返しています。

BOOL CanPaste(HWND hWnd) { HANDLE hMem; if (!OpenClipboard(hWnd)) { MessageBox(hWnd, "Openclipboard 失敗", "Error", MB_OK); return FALSE; } hMem = GetClipboardData(CF_TEXT); if (hMem == NULL) { CloseClipboard(); return FALSE; } else { CloseClipboard(); return TRUE; } }

張り付け可能かどうかを調べる関数です。引数のhWndはエディットコントロール ではなくてメインウィンドウのハンドルであることに注意してください。

OpenClipboard, GetClipboardData, CloseClipboard関数については 第94章を参照してください。


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

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