フォント選択ダイアログボックスを使ってみる

フォント選択ダイアログボックス

前回作成したリッチエディットでは、フォントを設定するのに自分で入力しなければなりませんでした。今回は、Windowsがあらかじめ持っているコモンダイアログボックスの1つ、フォント選択ダイアログボックスを使ってフォントを設定してみましょう。

手順

フォント設定ダイアログの作成手順は以下のようになります。

  1. CHOOSEFONT 構造体に必要な情報を格納する。
  2. 構造体アドレスを指定して ChooseFont 関数を呼び出す。

まず、 CHOOSEFONT 構造体の各メンバに情報を格納します。

typedef struct {
    DWORD        lStructSize;    // 構造体サイズ(=60)
    HWND         hwndOwner;      // オーナーウィンドウハンドル
    HDC          hDC;            // プリンタのデバイスハンドル
    LPLOGFONT    lpLogFont;      // LOGFONT構造体のアドレス
    INT          iPointSize;     // サイズ(ポイント数)
    DWORD        Flags;          // フラグ
    DWORD        rgbColors;      // 文字色
    LPARAM       lCustData;      // 32ビット値
    LPCFHOOKPROC lpfnHook;       // フックプロシージャアドレス
    LPTCSTR      lpTemplateName; // テンプレートリソース名
    HINSTANCE    hInstance;      // モジュールインスタンス
    LPTSTR       lpszStyle;      // スタイルデータ
    WORD         nFontType;      // フォントタイプ
    WORD         ___MISSING_ALIGNMENT__; 
    INT          nSizeMin;       // サイズ最小値
    INT          nSizeMax;       // サイズ最大値
} CHOOSEFONT, *LPCHOOSEFONT;

とりあえず、必要なメンバのみ説明しましょう。

lStructSize メンバには構造体サイズの60を指定しておきます。

hwndOwner には、表示するダイアログのオーナーウィンドウのハンドルを指定します。ここにはHSPウィンドウのハンドルを指定しておけばOKです。

lpLogFont メンバには、フォントの情報を格納するための LOGFONT 構造体のアドレスを指定します。この構造体には、選択されたフォントの情報が格納されます。

Flags メンバにはオプションフラグを指定します。CF_SCREENFONTS (0x00000001) をまず指定しましょう。これはシステムがサポートするフォントを表示します。また、CF_INITTOLOGFONTSTRUCT (0x00000040) を指定しておくことで、lpLogFont メンバの指す LOGFONT 構造体の情報で、初期の状態で選択されているフォントを指定することができます。他にも、CF_EFFECTS (0x00000100) を指定することで、打ち消し線・下線・文字色などの指定をすることもできるようになります。

CHOOSEFONT 構造体に情報を格納できたら、 ChooseFont 関数を呼び出します。

BOOL ChooseFontA(
    LPCHOOSEFONT  pcf  // 初期化情報を格納した構造体
);

引数として、情報を格納した CHOOSEFONT 構造体のアドレスを指定します。

フォントが正しく選択されると、 CHOOSEFONT 構造体の iPointSize, rgbColors, nFontType などのメンバおよび lpLogFont メンバが指し示す LOGFONT 構造体の各メンバに、選択されたフォントの情報が格納されます。

リッチエディットコントロールで使用するには

さて、これをリッチエディットで使用するために、フォントダイアログで選択されたフォントの情報を CHARFORMAT 構造体に格納することを考えてみましょう。

先ほども述べた通り、選択されたフォントの情報は CHOOSEFONT 構造体と LOGFONT 構造体に格納されます。各メンバを以下のように対応させればいいでしょう。

構造体 メンバ → CHARFORMAT 構造体メンバ
CHOOSEFONT iPointSize → yHeight(単位換算が必要)
rgbColors → crTextColor
nFontTypeのBOLD_FONTTYPE値 → dwEffectsのCFE_BOLD値
nFontTypeのITALIC_FONTTYPE値 → dwEffectsのCFE_ITALIC値
LOGFONT lfUnderline → dwEffects のCFE_UNDERLINE値
lfStrikeOut → dwEffects のCFE_STRIKEOUT値
lfCharSet → bCharSet
lfPichAndFamily → bPichAndFamily
lfFaceName → szFaceName

CHOOSEFONT 構造体の iPointSize メンバに格納されるフォントサイズは 1/10 ポイント単位の値なので、 CHARFORMAT 構造体の yHeight メンバに格納するには twip 単位に変換します。1 ポイント = 20 twips なので、

yHeight = iPointSize * 2

とします。

LOGFONT 構造体の lfHeight メンバにもフォントの高さが格納されますが、これは論理単位で指定されています。論理単位とはGDIが使用する概念的な長さの単位のことで、ピクセル数などの物理的な単位(デバイス単位)とは異なるものです。論理単位とデバイス単位の間の関係は、デバイスコンテキストに設定されたマッピングモードによって異なりますが、たいていの場合は(HSPウィンドウも)1論理単位は1ピクセルにあたります。この lfHeight メンバに格納された値から高さを設定するには、前回行ったピクセル数から twip への単位換算を行ないます。(GetDeviceCaps 関数で1インチ当たりのピクセル数を取得して計算)

フォントの効果として、太字・イタリック体があります。また、 CHOOSEFONT 構造体の Flags メンバに CF_EFFECTS フラグを指定しておいた場合には、打ち消し線・下線も指定できるようになります。太字・イタリック体の設定に関しては、 CHOOSEFONT 構造体と LOGFONT 構造体のどちらからも取得できますが、今回は、 CHOOSEFONT 構造体の nFontType メンバを参照することにします。

; CHOOSEFONT構造体を変数 chfont にとる
; CHARFORMAT 構造体を変数 charfmt にとる

; nFontType メンバは chfont.12 にあたる
; dwEffects メンバは charfmt.2 にあたる

if (chfont.12 & 0x0100) {           ; BOLD_FONTTYPE タイプを持つ場合
    charfmt.2 = charfmt.2 | 0x0001  ; CFE_BOLD を加える(太字)
}
if (chfont.12 & 0x0200) {           ; ITALIC_FONTTYPE タイプを持つ場合
    charfmt.2 = charfmt.2 | 0x0002  ; CFE_ITALICを加える (イタリック体)
}

下線・打ち消し線の設定は LOGFONT 構造体の lfUnderline メンバと lfStrikeOut メンバを参照します。

; LOGFONT 構造体を変数 logfont にとる

; lfUnderline メンバは logfont.5 の第2バイトにあたる 
; lfStrikeOut メンバは logfont.5 の第3バイトにあたる

if (logfont.5 & 0x0000FF00) {       ; lfUnderlineが指定されている場合
    charfmt.2 = charfmt.2 | 0x0004  ; CFE_UNDERLINEを加える(下線)
}
if (logfont.5 & 0x00FF0000) {       ; lfStrikeOutが指定されている場合
    charfmt.2 = charfmt.2 | 0x0008  ; CFE_STRIKEOUTを加える(打ち消し線)
}

サンプルスクリプト

実際にスクリプトを書いてみましょう。前回のスクリプトのうち書式設定の部分を、フォント選択ダイアログボックスを表示するように変更しています。

サンプルスクリプト main.as

    #include "llmod.as"

    objsize winx, 24 : button "フォント変更", *lb_fontdialog

    ; 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, 24, winx, winy-24
    pm.8 = bmscr.13             ; 描画中HSPウィンドウのハンドル
    pm.9 = 0xFF00               ; コントロールID (大きめに指定)
    _get_instance pm.10         ; インスタンスハンドル取得
    pm.11 = 0
    dllproc "CreateWindowExA", pm, 12, D_USER
    hEdit = stat                ; リッチエディットのハンドル

    ; テキストサイズの上限の設定
    ; EM_EXLIMITTEXT メッセージ送信
    pm = hEdit, 0x0435, 0
    pm.3 = 10 * 1024 * 1024     ; 10Mバイトに設定
    sendmsg pm

    stop

*lb_fontdialog
    ; CHOOSEFONT 構造体を変数 chfont にとる
    ; LOGFONT 構造体を変数 logfont にとる

    mref bmscr, 67              ; HSPウィンドウのBMSCR構造体取得

    ; CHOOSEFONT 構造体
    chfont.0 = 60               ; 構造体サイズ(=60)
    chfont.1 = bmscr.13         ; ウィンドウハンドル
    getptr chfont.3, logfont    ; LOGFONT 構造体アドレス
    chfont.5 = 0x01000141       ; CF_SCREENFONTS | CF_INITTOLOGFONTSTRUCT
                                ;  | CF_EFFECTS | CF_NOVERTFONTS

    ; フォント選択ダイアログボックスの表示
    getptr pm, chfont
    dllproc "ChooseFontA", pm, 1, D_COMDLG
    if stat == 0 : stop         ; キャンセルされた

    ; CHARFORMAT 構造体を変数 charfmt にとる
    dim charfmt, 16

    charfmt.0 = 60              ; 構造体サイズ
    charfmt.1 = 0xE800000F      ; CFM_BOLD | CFM_ITALIC | CFM_UNDERLINE
                                ;  | CFM_STRIKEOUT | CFM_FACE | CFM_COLOR
                                ;  | CFM_SIZE
    charfmt.2 = 0
    if (chfont.12 & 0x0100) {           ; BOLD_FONTTYPEタイプを持つ場合
        charfmt.2 = charfmt.2 | 0x0001  ; CFE_BOLDを加える(太字)
    }
    if (chfont.12 & 0x0200) {           ; ITALIC_FONTTYPEタイプを持つ場合
        charfmt.2 = charfmt.2 | 0x0002  ; CFE_ITALICを加える(イタリック体)
    }
    if (logfont.5 & 0x0000FF00) {       ; lfUnderlineが指定されている場合
        charfmt.2 = charfmt.2 | 0x0004  ; CFE_UNDERLINEを加える(下線)
    }
    if (logfont.5 & 0x00FF0000) {       ; lfStrikeOutが指定されている場合
        charfmt.2 = charfmt.2 | 0x0008  ; CFE_STRIKEOUTを加える(打ち消し線)
    }
    charfmt.3 = chfont.4 * 2        ; サイズ変換(1/10 point → twip)
    charfmt.5 = chfont.6            ; 文字色
    peek t, logfont, 23             ; LOGFONT からキャラクタセット取得
    poke charfmt, 24, t             ; CHARFORMAT にキャラクタセット設定
    peek t, logfont, 27             ; LOGFONT からピッチ・ファミリ値取得
    poke charfmt, 25, t             ; CHARFORMAT にピッチ・ファミリ値設定
    sdim fontname, 32
    peek fontname, logfont, 28      ; LOGFONT からフォント名取得
    poke charfmt, 26, fontname      ; CHARFORMAT にフォント名設定

    ; EM_SETCHARFORMAT メッセージ送信
    pm = hEdit, 0x0444
    pm.2 = 0x0001               ; SCF_SELECTION
    getptr pm.3, charfmt        ; CHARFORMAT構造体のアドレス
    sendmsg pm
    if stat == 0 : dialog "書式の設定に失敗しました。", 1, "エラー"
    stop