今回は、リッチエディットコントロールに挑戦してみたいと思います。
このリッチエディットコントロールも、Windows が提供するコントロールの1つになります。機能は、通常のエディットボックス(HSPの mesbox 命令や input 命令で作成されるオブジェクト)に似ていますが、文字の一部だけのフォントの種類や大きさ、色を変えたり、段落ごとに書式設定したりもできます。
今回はとりあえずコントロールを作成して、文字書式を設定してみることにします。コントロールの作成手順は以下のようになります。
まずは RICHED32.DLL をロードする必要があります。リッチエディットコントロールはこのDLLから提供されている機能であり、DLLをロードすることで自動的にコントロールのウィンドウクラスがシステムに登録されるようになっています。DLLをロードするには LOADLIB.DLL の ll_libload 命令を使います。この操作は最初に1度だけ実行しておけば OK です。
次は CreateWindowEx 関数を呼び出して実際にウィンドウを作成します。リッチエディットコントロールはクラス名 "RichEdit" として登録されているので、これをクラス名に指定する必要があります。
リッチエディットコントロールのスタイルはさまざまなものがあります。縦書きすら行なうことができたりします。今回作成するコントロールには以下のスタイルを適用することにします。
スタイル | コード | 意味 |
---|---|---|
WS_CHILD | 0x40000000 | 子ウィンドウ |
WS_VISIBLE | 0x10000000 | 可視 |
WS_BORDER | 0x00800000 | 境界線あり |
WS_VSCROLL | 0x00200000 | 垂直スクロールバーを持つ |
ES_MULTILINE | 0x0004 | 複数行のエディットコントロール |
ES_AUTOVSCROLL | 0x0040 | テキストを自動的に縦向きにスクロール |
今回作成するものでは、テキストが右端まできたら次の行に移るようにします。右端まできても次の行には移らずに、そのまま横へスクロールするようにするには、上のスタイルにさらに WS_HSCROLL (0x00100000) と ES_AUTOHSCROLL (0x0080) を加えてみましょう。他にもいろいろなスタイルを試してみるといいでしょう。
リッチエディットコントロールを作成する際に注意しなくてはならないのは、 CreateWindowEx 関数の10番目の引数として指定されるコントロールIDです。HSPウィンドウ上に、コントロールIDがある値以下のリッチエディットコントロールを作成すると、操作中に動作がおかしくなったり強制終了してしまったりすることが判明しています。筆者の環境では、コントロールIDを 1536 ($600) 未満にした場合に、コントロールにフォーカスを合わせると強制終了してしまいます。したがって、コントロールIDはある程度大きな値をとる必要があります。
このため、古いバージョンのLLMODモジュールでは _makewnd 命令でコントロールを作成することはできず、 dllproc などで直接 CreateWindowEx 関数を呼び出さなければなりません。
新しいバージョンのLLMOD モジュール(ver 1.11b 以降) では、この問題に対しての対策がとられたようで、 _makewnd 命令でリッチエディットコントロールを作成しても大丈夫なようになっています。
コントロールの作成ができたら次は文字の書式を設定します。まずは CHARFORMAT 構造体に書式情報を格納します。
typedef struct _charformat { UINT cbSize; // 構造体のサイズ(=60) DWORD dwMask; // 有効メンバ DWORD dwEffects; // 文字の効果 LONG yHeight; // 文字の高さ LONG yOffset; // ベースラインからのオフセット COLORREF crTextColor; // 文字の色 BYTE bCharSet; // キャラクタセット BYTE bPitchAndFamily; // フォントファミリとピッチ TCHAR szFaceName[32]; // フォント名 } CHARFORMAT;
cbSize メンバにはこの構造体のサイズである60を指定します。
dwMask メンバは、この構造体のどのメンバや属性設定を有効にするかを指定します。ここで指定しないとその属性の設定が変更されないので注意してください。
yHeight メンバおよび yOffset メンバにはそれぞれ文字の高さ、ベースラインからのオフセットを指定しますが、このときの数値は twip という単位で指定しなければなりません。これに付いては後で述べることにします。
crTextColor メンバに設定する色は、RGB値(赤の輝度を0xRR、緑の輝度を0xGG、青の輝度を0xBBとしたとき、0x00BBGGRR で表される値。COLORREF値)で指定します。
bCharSet メンバはキャラクタセットの値を設定します。日本語フォントでは普通 Shift-JIS を表す 128 (SHIFTJIS_CHARSET) を指定します。
szFaceName メンバにはフォント名を格納します。このメンバのデータは poke または wpoke で以下のようにして格納することができます。
(w)poke cfm, 26, "MS ゴシック"
ところで、この構造体、UINT、DWORD、LONG、COLORREF はそれぞれ4バイト、BYTE、CHAR はそれぞれ1バイトのデータ型だから、計算すれば構造体のサイズは58になるはず、と思うかもしれませんが、実際のサイズは60になっています。これは、構造体のアライメント(境界の整列)処理のために、構造体の境界が4バイト単位に配置されていることによって起こります。実際に、58を指定すると後でメッセージを送信したときにエラーになってしまいます。
さて、構造体に情報をセットしたら、リッチエディットコントロールに EM_SETCHARFORMAT メッセージを送信します。
#define EM_SETCHARFORMAT 0x0444 EM_SETCHARFORMAT wParam = uFlags; lParam = lpFormat;
uFlags パラメータには、設定する書式の適用範囲を指定します。デフォルトの書式として設定するには 0 を、選択中の文字列に対して適用するには 0x0001 (SCF_SELECTION) を、コントロール中にあるすべての文字列に対して適用するには 0x0004 (SCF_ALL) を指定します。
pFormat パラメータには、書式情報を格納した CHARFORMAT 構造体のアドレスを指定します。
さて、 CHARFORMAT 構造体では、高さなどを指定するのに“twip”という単位を使用しなければならないと説明しました。
通常、フォントの大きさの単位として、“ポイント”というものが使われます。1ポイントは 0.013837インチと等しく、一般的には近似して 1/72 インチとします。1 twip は 1/20 ポイントと等しい長さの単位です。すなわち、
1 point = 1/72 inch 1 twip = 1/20 point 1 inch = 72 points = 1440 twips
という関係式が成り立ちます。
1つ注意しておくべきことは、“ポイント”という言葉が「点」という意味を持つことから、何となくこのポイント数を画面におけるピクセル数とみなしてしまうかもしれませんが、それは間違いです。“ポイント”と“ピクセル”とはまったく別の単位になります。
HSPの font 命令を使ってフォントサイズを指定するとき、このサイズはピクセル数で指定しますが、リッチエディットでは直接ピクセル数でサイズ指定することができません。したがって、フォントサイズをピクセル数で指定したい場合には、単位換算をする必要があります。これには、まず、“インチ”と“ピクセル”との間の関係を求めます。この関係は GetDeviceCaps 関数で求めることができます。
int GetDeviceCaps( HDC hDC, // デバイスコンテキストのハンドル int nIndex // 項目の種類 );
第1引数(hDC パラメータ)にはデバイスコンテキストのハンドルを、第2引数(nIndex パラメータ)には 90 (LOGPIXELSY) を指定します。関数の戻り値として、垂直方向の1インチあたりのピクセル数が返ります。
ここで、 GetDeviceCaps 関数を使うにはリッチエディットコントロールのデバイスコンテキストのハンドルが必要になるので、 GetDeviceCaps 関数を呼び出す前に GetDC 関数で取得しておく必要があります。また、 GetDeviceCaps 関数呼出し後は ReleaseDC 関数でこのハンドルを解放します。
さて、上記の方法で呼び出した GetDeviceCaps 関数の戻り値(垂直方向の1インチあたりのピクセル数)を u とすると、
1 inch = u pixels
すなわち、
1 pixel = 1/u inch = 1440/u twips
となります。したがって、高さ n ピクセルのフォントは
n pixels = n * 1440 / u twips
となるので、この値を高さとして指定します。
さて、実際にスクリプトを書いてみます。フォントを“MS ゴシック”に、フォントの高さは 26 ピクセルに設定します。また、文字の色を赤にします。
#include "llmod.as" ; RICHEDIT.DLLのロード ll_libload hRtLib, "RICHED32.DLL" ; リッチエディットコントロールの作成 mref bmscr, 67 ; 描画中ウィンドウのBMSCR構造体 classname = "RichEdit" pm.0 = 0 getptr pm.1, classname pm.2 = 0 pm.3 = 0x50A00044 ; WS_CHILD | WS_VISIBLE | WS_BORDER ; | WS_VSCROLL | ES_MULTILINE ; | ES_AUTOVSCROLL pm.4 = 0, 0, winx, winy pm.8 = bmscr.13 ; 描画中HSPウィンドウのハンドル pm.9 = 0xFF00 ; コントロールID (大きめに指定) _get_instance pm.10 ; インスタンスハンドル取得 pm.11 = 0 dllproc "CreateWindowExA", pm, 12, D_USER hEdit = dllret ; リッチエディットのハンドル ; 文字書式を設定 ; RichEditのデバイスコンテキスト取得 pm = hEdit dllproc "GetDC", pm, 1, D_USER hdcEdit = dllret ; RichEditのy方向の1インチ当たりのピクセル数を取得 pm = hdcEdit, 90 ; LOGPIXELSY dllproc "GetDeviceCaps", pm, 2, D_GDI pxl = dllret ; 1インチ当たりのピクセル数 ; デバイスコンテキスト解放 pm = hEdit, hdcEdit dllproc "ReleaseDC", pm, 2, D_USER ; CHARFORMAT構造体 cfm.0 = 60 cfm.1 = 0xE8000000 ; CFM_CHARSET | CFM_FACE ; | CFM_SIZE | CFM_COLOR cfm.3 = 26 * 1440 / pxl ; 26ピクセル(twip単位で指定) cfm.5 = $0000FF ; 文字色(赤) poke cfm, 24, 128 ; SHIFTJIS_CHARSET poke cfm, 26, "MS ゴシック" ; フォント名 ; EM_SETCHARFORMAT メッセージ送信 pm = hEdit, 0x0444 pm.2 = 0 ; SCF_DEFAULT getptr pm.3, cfm ; CHARFORMAT構造体のアドレス sendmsg pm if dllret == 0 : dialog "書式の設定に失敗しました。", 1, "エラー" stop