第301章 HTMLエディタを作ろう


HTMLエディタはいろいろ出回っています。 今回から、自作のHTMLエディタに挑戦してみましょう。 と、言ってもタグを自動的に挿入したり、最初の決まり切ったタグを自動的に 作るタイプのものです。



今回作るプログラムでは、メニューの「編集」「基本項目入力」を選択すると 左の図のようなダイアログが出現します。

ここで、必要項目を入力するとメインウインドウに最低限のタグが 出現します。



あとは、「ファイル」「名前を付けて保存」で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しておきます。(しなくても自動的に 破棄してくれるのでよいのですが・・・)

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

グローバル変数をつなぎ合わせて、エディットコントロールに出力する関数です。 SetWindowText関数でエディットコントロールに出力しても 変更フラグが立たないので自分でEM_SETMODIFYメッセージを送ります。 EM_SETMODIFYメッセージについては第75章などを参照してください。

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の登場以来、少し拡張されているので ヘルプで確認してみてください。

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

上書き保存用の関数です。ここでは、TRUNCATE_EXISTINGでファイルをオープンしているので オープン時にファイルが切りつめられています。

今回は内容的には簡単でした。さて、タグを挿入するプログラムはどうすればよいのでしょうか。 言い換えると、エディットコントロールに文字列を挿入する方法です。まじめに考えると かなりめんどくさいプログラムになりますが、簡単な方法もあります。


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

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