クリップボードを操ろう
Htmlエディタでタグを操作する時は、クリップボードの概念が重要です。今回はまずタグ操作について軽く触れて、なぜクリップボードが必要かについて説明し、具体的なコードを紹介していきます。
5.クリップボードの概念
今回作成するHTMLエディタは、タグ挿入形式のエディタです。何か飾りを付けたい文字を、規定のタグで挟むことで実現できます。一応HTMLエディタですから、タグを挟む機能をより簡単にする必要があります。例えば、太字にしたい場合には、
太字にしたい文字列を選択して、太字ボタンを押すことで、
太字タグを挿入することができます。このタグに関しては、各自お調べになってください。(最近ではHTMLタグ集が\1500〜で販売されていますし、簡単なやつだったらネット上で公開されているかもしれません。)

今回作成しているHTMLエディタでは、この操作を以下のコードで実現しています。(実際には他の関数も使用していますが、概念はこのような感じです。)
選択文字列の取得
実際にタグを挟んだ形の文字列を作りBに代入する。
Bをクリップボードに書き込む
ペーストを実行
ペーストとはクリップボードのデータをクライアント領域に表示させることです。
クリップボードを操ることで、Paste関数を使用することができるのです。では次のセクションで、実際のコードについて考えていこうと思います。
6.クリップボード関数の作成(クリップボードへのデータ書き込み)
まずは、そのコードから示します。多分誰が書いても似たようなコードになってしまう可能性がかなり高い関数でしょう(某雑誌にも同じような…(笑))。
クリップボードにデータを設定する関数
void CMyHtmlView::SetClipBoard(CString str)
{
char *ptr;
HGLOBAL hMem;
hMem = GlobalAlloc(GHND, (DWORD)str.GetLength() + 1);
ptr = (char *)GlobalLock(hMem);
lstrcpy(ptr,str);
::GlobalUnlock(hMem);
::OpenClipboard(m_hWnd);
::EmptyClipboard();
::SetClipboardData(CF_TEXT, hMem);
::CloseClipboard();
GlobalFree(hMem);
}
ここで関数の解説をします。
HGLOBAL hMem;
クリップボード(以下CB)へのハンドル。ハンドルというのは、車のハンドルと同じ意味で、CBを操作するための舵(かじ)見たいなものです。ウィンドウ関連ではよくこのハンドルを使用します。
char *ptr;
CBにデータを複写するための変数です。
hMem = GlobalAlloc(GHND, (DWORD)str.GetLength() + 1);
設定する文字列strの長さより1だけ長いメモリをヒープ領域から確保します。GHNDというのはGlobalAllocの設定フラグであり、これは移動可能メモリ領域とし、かつメモリの中身をクリアするという意味です。1だけ長くする理由は、文字列の終わりの合図にはNULL文字(\0)という記号をつけるのが原則となっているからです。
ptr = (char *)GlobalLock(hMem);
ヒープ領域のメモリを一旦ロック(自分以外の誰にも書き込まれないようにする?)し、メモリブロックの最初の 1 バイトへのポインタを返します。(ポインタを使用するほうが操作が楽ですし、C言語のように記述できるからでしょう。私もこのようにする理由が曖昧です^^;)
lstrcpy(ptr,str);
strの情報を先ほどのポインタにコピーします。ポインタの中身だけを替えるだけなので、hMemなどには影響しません。
::GlobalUnlock(hMem);
先ほどロックしておいたメモリのロックを解除します。メモリのアンロックは、ロックをかけたアプリしかできないので、これを行わないとヒープ領域をどんどん占有してしまいwindowsごと落ちるなーんてこともあるかもしれません(怖くて試せませんが^^;)。MSDNにはGlobalLockとGlobalUnlockはセットで使えと書いてありました。
::OpenClipboard(m_hWnd);
CBをオープンします。ファイルのように開く感覚とは違いますが、このコードで他のアプリがCBの中身を変更できなくします。"::"で書かれているコードはSDK形式といってMFCとは微妙に違うのですが、MFCでもCWnd::OpenClipboard()という関数があって、それで行うことも可能ですが、ここではSDKのコードで記述してしまいます。(きれいに::が並びますし)
m_hWndとは、アプリのウィンドウハンドルです。アプリの中でもこのウィンドウを、CBを唯一使えるオーナーにして!と立候補させていると考えてください。(政治で言うならば、第1党の党首ですね)
::EmptyClipboard();
CBに入っているデータをクリアします。このとき先ほど、オーナーに推薦されたウィンドウにCBの所有権が与えられます!(国会での総理大臣の指名みたいなものです)。よく考えればOpenClipboardでこの関数の内容も実行してしまえばいいのに!と思いますが、MSDNでそうやりなさいって書いてあるので仕方がありません(>_<)。
::SetClipboardData(CF_TEXT, hMem);
この関数がCBにデータを書き込んでいます。CF_TEXTとはANSI (American National Standard Institute : 米国規格協会) テキスト形式のことを指します。まぁ世界中で使えるテキスト形式にするようなものですね。データ自体は、lstrcpy(ptr,str)によってptrに書き込まれたはずなので、hMemを指定してもデータが書き込まれないのではないか…と思われるかもしれませんが、ptrはhMemの先頭メモリアドレスですから、ptrのデータは実際にはhMemに書き込まれているので大丈夫なのです。
::CloseClipboard();
CBをクローズします。オープンしたら閉じる、これは原則ですね。
GlobalFree(hMem);
一応ヒープ領域にメモリを確保したので、開放しておきます。
詳しく解説したつもりですが(一部濁しましたが)分かっていただけたでしょうか?
あとはこの関数をどのように書き込むか?ですが、ワークスペースでCMyHtmlViewを右クリックし、メンバ関数の追加でできます。
7.クリップボード関数の作成(クリップボードからのデータ読み込み)
次にクリップボードからデータを読み込む関数を追加しておきましょう。この関数も、誰が書いても似たようなものになってしまうタイプです。
クリップボードからデータを読み出す関数
CString CMyHtmlView::GetClipBoard(void){
HANDLE hMem;
char *ptr;
::OpenClipboard(m_hWnd);
hMem = ::GetClipboardData(CF_TEXT);
ptr = (char *)::GlobalLock(hMem);
::GlobalUnlock(hMem);
::CloseClipboard();
return (CString)ptr;
}
お決まりの解説をしていきましょう。
HGLOBAL hMem;
クリップボード(以下CB)へのハンドル。ハンドルについては先ほど説明しましたね。
char *ptr;
CBからのデータのアドレスを受ける変数です。
::OpenClipboard(m_hWnd);
CBを開いて、m_hWnd以外のウィンドウ以外にCBを変更させることをできなくします。
hMem = ::GetClipboardData(CF_TEXT);
CBのデータを扱うためのハンドルを取得します。
ptr = (char *)::GlobalLock(hMem);
メモリをロックして、文字列データへのポインタを割り当てます。このデータがCBに保存されてるデータです。
::GlobalUnlock(hMem);
お決まりですが、メモリのアンロックを行います。
::CloseClipboard();
これまたお決まりですが、CBを閉じます。
return (CString)ptr;
データをCString型にキャストして、関数を終了します。
今回は結構乱雑になってしまいましたが、分かっていただけたでしょうか?これからいろんなソフトを作られると思いますが、そのときの参考になればいいなぁと思っております。
8.便利関数を作ってしまいましょう。
クリップボードをある程度いじれる関数を2つ追加しましたが、「ボタン(ツールバー)を押してタグ挿入」を行うわけですから、各ボタンごとその動作の関数を作らなくてはなりません。しかし実際には挿入すべきタグを変えれば、関数の使いまわしができますよね。例えば<B></B>を挿入するのだったら
太字タグを挿入する関数(面倒くさい例)
void CMyHtmlView::OnButtonBold{
/* (1) */
CString strData;//選択された文字列(タグで挟まれる文字列)
CString strTag;//タグを挟んだあとの文字列
CEdit& cEdt = GetEditCtrl();//CEditへのポインタを取得
cEdt.Copy();//クリップボードへ選択された文字列をコピー
strSel = GetClipBoard();//クリップボードからデータを取得
strTag = "<B>" + strData + "</B>";
/* (2) */
SetClipBoard(strTag);//クリップボードにタグを挟んだあとも文字列を挿入
OnEditPaste();//クリップボードのデータを貼り付け
}
なことをやるのではなくて、
太字タグを挿入する関数(スマートな例)
void CMyHtmlView::OnButtonBold{
CString strTag = MakeTag("<B>","</B>");//上の(1)の部分
PasteString(strTag);//上の(2)の部分
}
としてあげた方が賢いというわけですね。
CMyHtmlViewに以下の関数を追加してみましょう。

// 文字列のペースト
void CMyHtmlView::PasteString(CString str){
SetClipBoard(str);
OnEditPaste();
}

// タグの作成
CString CMyHtmlView::MakeTag(CString strTagA,CString strTagB){
CString strSel,strTag;
CEdit& cEdt = GetEditCtrl();
cEdt.Copy();
strSel = GetClipBoard();
strTag = strTagA + strSel + strTagB;
return strTag;
}
これらは、先ほどの面倒くさい方のコードを関数化しただけですから説明は割愛させてもらいます。今回は4つのメンバ関数を追加しましたが、これらはHTMLエディタの柱になりますので、バグがないようにしておきましょう。

[Next]
[Previous]
[Home]