簡易テキストエディタ(2)

クリップボードのサポート

エディットコントロールは、もともとクリップボードをサポートしてます。 以下のキー操作をやってみて下さい。ちゃんと機能するはずです。 あとはこれをメニューから実行可能にします。メニューに
	POPUP        "編集(\036E\037ヘ)"
	BEGIN
		MENUITEM "元に戻す(\036U\037モ)", IDM_UNDO
		MENUITEM SEPARATOR
		MENUITEM "切り取り(\036T\037キ)", IDM_CUT
		MENUITEM "コピー(\036C\037コ)",   IDM_COPY
		MENUITEM "貼り付け(\036P\037ハ)", IDM_PASTE
		MENUITEM "削除(\036L\037サ)",     IDM_DEL
		MENUITEM "すべての範囲を選択(\036L\037ス)", IDM_ALL
		MENUITEM SEPARATOR
		MENUITEM "右端で折り返す(\036W\037ミ)", IDM_FOLD
	END
を追加し、Cソースに
	case IDM_DEL  : SendDlgItemMessage(hDlg, IDEDIT, EM_REPLACESEL, 0, (LPARAM)(LPSTR)""); break;
	case IDM_CUT  : SendDlgItemMessage(hDlg, IDEDIT, WM_CUT  , 0, 0); break;
	case IDM_UNDO : SendDlgItemMessage(hDlg, IDEDIT, EM_UNDO , 0, 0); break;
	case IDM_COPY : SendDlgItemMessage(hDlg, IDEDIT, WM_COPY , 0, 0); break;
	case IDM_PASTE: SendDlgItemMessage(hDlg, IDEDIT, WM_PASTE, 0, 0); break;
	case IDM_ALL:
	#ifdef WIN32
		SendDlgItemMessage(hDlg, IDEDIT, EM_SETSEL, 0, -1); break;
	#else
		SendDlgItemMessage(hDlg, IDEDIT, EM_SETSEL, 0, MAKELONG(0,-1)); break;
	#endif
を追加します。

検索と置換

こればっかりは、自分でゴリゴリ記述しないといけません。 まず、メニューに以下を追加します。
	POPUP        "検索(\036S\037ケ)"
	BEGIN
		MENUITEM "文字列の検索(\036F\037ケ)...", IDM_FIND
		MENUITEM "次を検索(\036N\037ツ)",        IDM_NEXT
		MENUITEM "文字列の置換(\036R\037チ)...", IDM_REP
	END
検索や置換のダイアログは、コモンダイアログを使う手もありますが、今回は自分で作ってみようと 思います。
検索ダイアログ

置換ダイアログ

メインウインドウ以外のモードレスダイアログは、同じダイアログを複数開くと、まずいです。 そこで必ず、すでにダイアログが開かれているかどうかチェックします。 開かれていた場合は、それをアクティブにします。

	case IDM_FIND:
		if (hDlgSearch) SetActiveWindow(hDlgSearch);
		else hDlgSearch=CreateDialog(hInstApp, "searchdlg", hDlg, lpSearch);
		break;
検索や置換のダイアログでは、TABキーをコントロール間の移動に使いたいため、 WinMainのメッセージループで、IsDialogMessage()を呼び出します。
	while (GetMessage(&msg, NULL, 0, 0)) {
		if ((hDlgSearch ==NULL || !IsDialogMessage(hDlgSearch,  &msg)) &&
		    (hDlgReplace==NULL || !IsDialogMessage(hDlgReplace, &msg))){
			TranslateMessage(&msg);
			DispatchMessage(&msg);
		}
	}
検索や置換のダイアログプロシージャでは、実行ボタンが押されたら、文字列などを取得して、 メインダイアログにWM_COMMANDを送ることにします。実際の検索や置換の作業は、 メインダイアログのダイアログプロシージャが行います。
	case IDOK:
		GetDlgItemText(hDlg, IDS_EDIT, searchstr, sizeof(searchstr));
		SendMessage(hDlgMain, WM_COMMAND, IDM_NEXT, 0L);
		return TRUE;
実際の検索処理は、以下のようになります。

最初に、コントロールにEM_GETLINECOUNTを送って、全体の行数を取得しておきます。 それから、現在の選択位置(選択されていない時はカーソル位置)を取得(EM_GETSEL)。 EM_GETSELで取得した位置は、先頭からの文字数なので、それを先頭からの行数と その行の何文字目かに変換します(EM_LINEFROMCHARとEM_LINEINDEX)。 EM_GETLINEで行の内容を取得し、C言語ライブラリのstrstr()で検索します。 見つけたら、EM_SETSELでその範囲を選択します。 見つからなかったら、その旨のメッセージを出して終了します。

置換処理も似たようなものですが、置換はテキストの最初から実行します。 strstr()で検索して、EM_SETSELでその範囲を選択したら、EM_REPLACESELで置換します。

なお、EM_GETLINEで行を取得するとき、そのバッファの先頭2バイトにはバッファサイズを入れておきます。 その情報は上書きされますので、毎回セットする必要があります。

この検索ルーチンは、実は未完成です。なぜなら、strstr()で検索するのは、本当はまずいのです。 テキストに「白血病」検索文字列に「剣」で検索してみてください。誤動作するはずです。 jstrstr()もしくは_mbsstr()のような日本語(マルチバイト)文字列対応の検索関数を使う必要があります。 英語版コンパイラで、このような関数が無い場合、もしくはあってもうまく動作しない場合は、自作しないといけません。 (この問題があるので、大文字小文字の同一視も今回はサポートしていません。単純にtoupper() などを呼び出しても誤動作するのは明白です。)

全ソース

前のページ

次のページ

一つ上のページに戻る