今回作るプログラムでは、メニューの「編集」「基本項目入力」を選択すると
左の図のようなダイアログが出現します。
ここで、必要項目を入力するとメインウインドウに最低限のタグが 出現します。
あとは、「ファイル」「名前を付けて保存」でHTMLファイルにします。
これに、自分でいろいろ付け足していけば良いわけです。
では、プログラムを見てみましょう。
メニューとダイアログボックスのリソース・スクリプトです。 特に説明は必要ないでしょう。// htmledit01.rcの一部 ///////////////////////////////////////////////////////////////////////////// // // Menu // MYMENU MENU DISCARDABLE BEGIN POPUP "ファイル(&F)" BEGIN MENUITEM "新規作成(&N)...", IDM_NEW MENUITEM "上書き保存(&S)", IDM_SAVE MENUITEM "名前を付けて保存(&A)...", IDM_SAVEAS MENUITEM SEPARATOR MENUITEM "アプリケーションの終了(&X)", IDM_END END POPUP "編集(&E)" BEGIN MENUITEM "基本項目入力(&B)...", IDM_BASICINPUT END END ///////////////////////////////////////////////////////////////////////////// // // Dialog // MYBASICINPUT DIALOG DISCARDABLE 0, 0, 187, 93 STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU CAPTION "基本項目入力" FONT 9, "MS Pゴシック" BEGIN EDITTEXT IDC_EDIT1,39,7,141,14,ES_AUTOHSCROLL EDITTEXT IDC_EDIT2,39,27,141,14,ES_AUTOHSCROLL EDITTEXT IDC_EDIT3,39,47,141,14,ES_AUTOHSCROLL DEFPUSHBUTTON "OK",IDOK,39,72,50,14 PUSHBUTTON "キャンセル",IDCANCEL,96,72,50,14 LTEXT "タイトル",IDC_STATIC,7,7,23,8 LTEXT "作者",IDC_STATIC,7,25,15,8 LTEXT "E-Mail",IDC_STATIC,7,43,20,8 END
ま、これを見ただけでどんなプログラムかは想像がつきますね。// htmedit01.cpp #ifndef STRICT #define STRICT #endif #define ID_EDIT 100 #define MY_BUF_SIZE 1024 * 64 - 1 #include <windows.h> #include <windowsx.h> #include "resource.h" LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); LRESULT CALLBACK MyBasicInputProc(HWND, UINT, WPARAM, LPARAM); ATOM InitApp(HINSTANCE); BOOL InitInstance(HINSTANCE, int); BOOL MySetHTML(HWND); int MyBasicInput(HWND); BOOL MySaveAs(HWND); BOOL MySave(HWND); char szClassName[] = "htmedit01"; //ウィンドウクラス char *lpszWinTitleOrg = "猫でもわかるHTMLエディタ[%s]"; char *lpszHead = "<HTML>\r\n<HEAD>\r\n<TITLE>"; char szTitle[64] = "タイトル"; char *lpszBody = "</TITLE>\r\n</HEAD>\r\n<BODY>\r\n"; char *lpszAddress = "<ADDRESS>"; char *lpszMailto = "<A HREF=\"mailto:"; char szEMail[256] = "webmaster@kumei.ne.jp"; char *lpszMailEnd = "\">"; char szName[64] = "Yasutaka Kumei"; char *lpszCloseA = "</A>"; char *lpszEnd = "</ADDRESS>\r\n</BODY>\r\n</HTML>"; char szFileName[MAX_PATH]; char szFileTitle[64];
親ウィンドウを作るまでです。いつもとほとんど同じです。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; } //ウィンドウ・クラスの登録 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(NULL, IDI_APPLICATION); return (RegisterClassEx(&wc)); } //ウィンドウの生成 BOOL InitInstance(HINSTANCE hInst, int nCmdShow) { HWND hWnd; hWnd = CreateWindow(szClassName, "猫でもわかるHTMLエディタ", //タイトルバーにこの名前が表示されます WS_OVERLAPPEDWINDOW, //ウィンドウの種類 CW_USEDEFAULT, //X座標 CW_USEDEFAULT, //Y座標 CW_USEDEFAULT, //幅 CW_USEDEFAULT, //高さ 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 HWND hEdit; HINSTANCE hInst; CREATESTRUCT *lpcs; char szWinTitle[64]; switch (msg) { case WM_CREATE: wsprintf(szWinTitle, lpszWinTitleOrg, "無題"); SetWindowText(hWnd, szWinTitle); lpcs = (CREATESTRUCT *)lp; hInst = lpcs->hInstance; hEdit = CreateWindow("EDIT", "", WS_CHILD | WS_VISIBLE | ES_MULTILINE | ES_WANTRETURN | ES_AUTOVSCROLL | ES_AUTOHSCROLL | WS_HSCROLL | WS_VSCROLL, 0, 0, 0, 0, hWnd, (HMENU)ID_EDIT, hInst, NULL); SetFocus(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_BASICINPUT: if (MyBasicInput(hEdit) == IDCANCEL) break; id = MessageBox(hWnd, "現在のソースファイルが置き換わりますがよろしいですか", "注意", MB_YESNO | MB_ICONQUESTION); if (id == IDNO) break; MySetHTML(hEdit); break; case IDM_SAVEAS: MySaveAs(hEdit); wsprintf(szWinTitle, lpszWinTitleOrg, szFileTitle); SetWindowText(hWnd, szWinTitle); break; case IDM_NEW: if (SendMessage(hEdit, EM_GETMODIFY, 0, 0)) { id = MessageBox(hWnd, "変更を保存しますか", "注意", MB_YESNO | MB_ICONQUESTION); if (id == IDYES) MySave(hEdit); } SetWindowText(hEdit, ""); strcpy(szFileName, ""); strcpy(szFileTitle, ""); wsprintf(szWinTitle, lpszWinTitleOrg, "無題"); SetWindowText(hWnd, szWinTitle); break; case IDM_SAVE: id = MessageBox(hWnd, "上書きしてもよろしいですか", "上書き保存", MB_YESNO | MB_ICONQUESTION); if (id == IDYES) { MySave(hEdit); wsprintf(szWinTitle, lpszWinTitleOrg, szFileTitle); SetWindowText(hWnd, szWinTitle); } break; } break; case WM_CLOSE: id = MessageBox(hWnd, "終了してもよいですか", "終了確認", MB_YESNO | MB_ICONQUESTION); if (id == IDYES) { if (SendMessage(hEdit, EM_GETMODIFY, 0, 0)) { id = MessageBox(hWnd, "変更を保存しますか", "保存", MB_YESNO | MB_ICONQUESTION); if (id == IDYES) MySave(hEdit); } DestroyWindow(hEdit); DestroyWindow(hWnd); } break; case WM_DESTROY: PostQuitMessage(0); break; default: return (DefWindowProc(hWnd, msg, wp, lp)); } return 0; }
WM_CREATEメッセージが来たら、親ウィンドウのウィンドウタイトルを 「無題」にして、エディットコントロールを作ります。
WM_SIZEメッセージが来たら、エディットコントロールの大きさや位置を 調整します。
メニューからIDM_BASICINPUTが選択されたら、自作関数MyBasicInputを呼びます。 これが、ダイアログを呼び出します。ユーザーがキャンセルボタンを押したときは 何もしません。現在親ウィンドウに表示されている内容が、置き換わるという注意を 表示した後、MySetHTML関数を呼んで、基本タグをエディットコントロールに出力します。
メニューからIDM_SAVEASが選択されたら、自作関数MySaveAsを呼んで、名前を付けて保存します。 そして、親ウィンドウのタイトルを変更します。
メニューからIDM_NEWが選択されたら、エディットコントロールに変更があるかどうかを
調べて、変更がある場合は保存するかどうかを尋ねます。保存する場合はMySave関数を呼びます。
そして、エディットコントロールを真っ白にして、グローバル変数のファイル名、ファイルタイトルを
無効にして、親ウィンドウのタイトルバーに「無題」を表示します。
メニューからIDM_SAVEが選択されたら、上書きしてもよいかを尋ねてMySave関数を呼び出します。 この時すでにファイル名が決まっているので、SetWindowTextで親ウィンドウの タイトル名を再表示しなくても良いように思われるかもしれませんが、 実はこのプログラムでは、「名前を付けて保存」せずにいきなり「上書き保存」が選択された時、 MySaveAs関数を呼ぶようになっています。その場合、ここでSetWindowTextしておかないと 親ウィンドウのタイトルが「無題」のままになってしまいます。
プログラム終了時にはエディットコントロールもDestroyWindowしておきます。(しなくても自動的に 破棄してくれるのでよいのですが・・・)
グローバル変数をつなぎ合わせて、エディットコントロールに出力する関数です。 SetWindowText関数でエディットコントロールに出力しても 変更フラグが立たないので自分でEM_SETMODIFYメッセージを送ります。 EM_SETMODIFYメッセージについては第75章などを参照してください。BOOL MySetHTML(HWND hEdit) { char szBuf[1024*10]; strcpy(szBuf, lpszHead); strcat(szBuf, szTitle); strcat(szBuf, lpszBody); strcat(szBuf, lpszAddress); strcat(szBuf, lpszMailto); strcat(szBuf, szEMail); strcat(szBuf, lpszMailEnd); strcat(szBuf, szName); strcat(szBuf, lpszCloseA); strcat(szBuf, lpszEnd); SetWindowText(hEdit, szBuf); SendMessage(hEdit, EM_SETMODIFY, (WPARAM)TRUE, 0); return TRUE; }
ダイアログを出す関数です。int MyBasicInput(HWND hEdit) { HINSTANCE hInst; int id; hInst = (HINSTANCE)GetWindowLong(hEdit, GWL_HINSTANCE); id = DialogBox(hInst, "MYBASICINPUT", hEdit, (DLGPROC)MyBasicInputProc); return id; }
ダイアログボックスのプロシージャです。LRESULT CALLBACK MyBasicInputProc(HWND hDlg, UINT msg, WPARAM wp, LPARAM lp) { static HWND hTitle, hName, hEMail; switch (msg) { case WM_INITDIALOG: hTitle = GetDlgItem(hDlg, IDC_EDIT1); hName = GetDlgItem(hDlg, IDC_EDIT2); hEMail = GetDlgItem(hDlg, IDC_EDIT3); SetWindowText(hTitle, szTitle); SetWindowText(hName, szName); SetWindowText(hEMail, szEMail); SetFocus(hTitle); return TRUE; case WM_COMMAND: switch (LOWORD(wp)) { case IDOK: Edit_GetText(hTitle, szTitle, sizeof(szTitle)); Edit_GetText(hName, szName, sizeof(szName)); Edit_GetText(hEMail, szEMail, sizeof(szEMail)); EndDialog(hDlg, IDOK); return TRUE; case IDCANCEL: EndDialog(hDlg, IDCANCEL); return TRUE; } return FALSE; } return FALSE; }
WM_INITDIALOGメッセージが来たら、ダイアログボックスのエディットコントロールの ハンドルを調べてstaticな変数に保存しておきます。そして、エディットコントロールに 今までの設定を表示します。
OKボタンが押されたら、エディットコントロールの内容を調べて、グローバル変数 に保存しておきます。
名前を付けて保存する関数です。今まで何回も出てきたので特に説明の必要はないですね。BOOL MySaveAs(HWND hEdit) { OPENFILENAME ofn; HANDLE hFile; char szBuf[MY_BUF_SIZE]; DWORD dwWritten; memset(&ofn, 0, sizeof(OPENFILENAME)); ofn.lStructSize = sizeof(OPENFILENAME); ofn.hwndOwner = hEdit; ofn.lpstrFilter = "htmlファイル\0*.htm;*.html\0All Files(*.*)\0*.*\0\0"; ofn.lpstrFile = szFileName; ofn.nMaxFile = MAX_PATH; ofn.lpstrFileTitle = szFileTitle; ofn.nMaxFileTitle = sizeof(szFileTitle); ofn.lpstrDefExt = "html"; ofn.Flags = OFN_OVERWRITEPROMPT | OFN_HIDEREADONLY; if (GetSaveFileName(&ofn)) { hFile = CreateFile(szFileName, GENERIC_WRITE, 0, 0, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); if (hFile == INVALID_HANDLE_VALUE) { MessageBox(hEdit, "ファイルのオープンに失敗しました", "Error", MB_OK); return FALSE; } Edit_GetText(hEdit, szBuf, MY_BUF_SIZE); WriteFile(hFile, szBuf, strlen(szBuf), &dwWritten, NULL); SetEndOfFile(hFile); CloseHandle(hFile); SendMessage(hEdit, EM_SETMODIFY, (WPARAM)FALSE, 0); } return TRUE; }
SetEndOfFile関数については第256章を参照してください。
また、OPENFILENAME構造体についてはWindows2000の登場以来、少し拡張されているので ヘルプで確認してみてください。
上書き保存用の関数です。ここでは、TRUNCATE_EXISTINGでファイルをオープンしているので オープン時にファイルが切りつめられています。BOOL MySave(HWND hEdit) { HANDLE hFile; char szBuf[MY_BUF_SIZE]; DWORD dwWritten; if (strcmp(szFileName, "") == 0) { MessageBox(hEdit, "まず名前を付けて保存してください。", "確認", MB_OK); MySaveAs(hEdit); return TRUE; } Edit_GetText(hEdit, szBuf, MY_BUF_SIZE); hFile = CreateFile(szFileName, GENERIC_WRITE, 0, 0, TRUNCATE_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (hFile == INVALID_HANDLE_VALUE) { MessageBox(hEdit, "ファイルをオープンできません", "Error", MB_OK); return FALSE; } WriteFile(hFile, szBuf, strlen(szBuf), &dwWritten, NULL); if (strlen(szBuf) != dwWritten) { MessageBox(hEdit, "書き込みに失敗しました", "Error", MB_OK); CloseHandle(hFile); return FALSE; } CloseHandle(hFile); SendMessage(hEdit, EM_SETMODIFY, (WPARAM)FALSE, 0); return TRUE; }
今回は内容的には簡単でした。さて、タグを挿入するプログラムはどうすればよいのでしょうか。 言い換えると、エディットコントロールに文字列を挿入する方法です。まじめに考えると かなりめんどくさいプログラムになりますが、簡単な方法もあります。
Update 03/Feb/2001 By Y.Kumei