テキスト形式での保存・読み込み

今回は、リッチエディットコントロールの内容をファイルに保存したり、ファイルを読み込んでコントロールに表示したりということをしてみます。できることならば、RTF形式のファイルの保存・読み込みができるとよいのですが、それらを行なうにはコールバック関数を準備する必要があるため、関数という概念を持たないHSPでは、拡張DLLなしでは実現することができません。そこで今回はRTFファイルではなくテキストファイルの保存・読み込みを行ってみましょう。

しかし、残念なことに、今回の方法では 64K バイト未満のテキストしか扱うことができません。あらかじめ了承しておいてください。

テキストファイルの読み込み

テキストファイルの内容をリッチエディットコントロールに開くには、 bload 命令でファイルの内容をすべて読み込んでから、 WM_SETTEXT メッセージを送信します。

#define WM_SETTEXT           0x000C

WM_SETTEXT
    wParam = 0;
    lParam = pszText;

このメッセージは、ウィンドウに関連付けられたテキストを設定するためのメッセージです。タイトルバーを持つウィンドウに対してこのメッセージを送信すると、タイトルバーの文字列が指定されたものに置き換えられます。また、標準エディットコントロールやリッチエディットコントロールに対して送信すると、コントロールのテキストの内容すべてを指定されたテキストに置き換えます。

pszText パラメータには、設定するテキストのアドレスを指定します。リッチエディットコントロールに送信する場合に、ここにファイルの内容を読み込んだバッファのアドレスを指定すれば、コントロールのテキストが置きかわります。

sdim filename, 260
dialog "txt", 16
if stat == 0 : stop

exist filename
if strsize != -1 {
    ; ファイルをバッファに読み込む
    sdim buf, strsize + 1
    bload filename, buf, filesize

    ; WM_SETTEXT メッセージ送信
    pm = hEdit, 0x000C, 0
    getptr pm.3, buf
    sendmsg pm
}

テキストファイルへの保存

リッチエディットコントロールの内容をファイルに保存するには、コントロール中のテキストすべてをバッファにコピーして、これを bsave 命令で保存するという手段をとります。

コントロール中のテキストを取得するには、まずコントロールに WM_GETTEXTLENGTH メッセージを送信して、コントロール中のテキストサイズを取得します。そして、そのサイズのテキストを格納できるだけのバッファを確保し、 WM_GETTEXT メッセージを送信してこのバッファにテキストをコピーします。

#define WM_GETTEXTLENGTH     0x000E

WM_GETTEXTLENGTH
    wParam = 0;
    lParam = 0;

WM_GETTEXTLENGTH メッセージは、ウィンドウに関連付けられたテキストの長さを取得します。リッチエディットコントロールに送信すると、戻り値としてコントロール中のテキストの長さがバイト単位で返ります。

#define WM_GETTEXT           0x000D

WM_GETTEXT
    wParam = cchTextMax;
    lParam = pszText;

WM_GETTEXT メッセージは、ウィンドウに関連付けられたテキストを取得します。

pszText には取得したテキストを格納するためのバッファのアドレスを、 cchTextMax にはそのバッファのサイズを指定します。リッチエディットコントロールに送信すると、コントロール中のテキストが取得されます。戻り値として、バッファにコピーされたテキストのサイズが返ります。

これによってコントロール中のテキストがバッファに格納されるので、これを bsave 命令を使ってファイルに保存します。

sdim filename, 260
dialog "txt", 17
if stat == 0 : stop

; WM_GETTEXTSIZEメッセージ送信
pm = hEdit, 0x000E, 0, 0
sendmsg pm
filesize = stat             ; コントロール中のテキストのサイズ

sdim buf, filesize + 1

; WM_GETTEXTメッセージ送信
pm = hEdit, 0x000D, filesize + 1
getptr pm.3, buf            ; バッファのアドレス
sendmsg pm

; バッファの内容をファイルに保存
bsave filename, buf, filesize

内容変更フラグ

保存・読み込みの手順に関しては以上ですが、もう1つ、新規作成やファイルを開くときに、現在編集中の内容が変更されていた場合にそれを保存するかどうかをユーザーに尋ねるようにしたいですね。それを実現する方法を説明します。

標準エディットコントロールやリッチエディットコントロールは、変更フラグ(modification flag)という、内容が変更されたかどうかの状態を入れておく変数を内部に持っています。このようなフラグは一般にダーティーフラグ(dirty flag)とも呼ばれています。

最初はこのフラグはセットされていない( 0 に設定されている)状態なのですが、ユーザーによって内容が変更された瞬間に、このフラグがセットされた( 1 に設定された)状態になるようになっているのです。そこで、新規作成やファイルを開くときにこのフラグを取得して、このフラグがセットされていれば現在の内容を保存するかどうかの確認を取ればよいことになります。現在の変更フラグを取得するには、リッチエディットコントロールに EM_GETMODIFY メッセージを送信します。

#define EM_GETMODIFY          0x00B8

EM_GETMODIFY
    wParam = 0;
    lParam = 0;

EM_GETMODIFY メッセージを送信すると、戻り値として現在の変更フラグが返ります。この値が 0 ならば、リッチエディットコントロールのテキストが変更されていないことを示しています。そうでない場合は、内容が変更されたことを示しています。

さて、リッチエディット中のテキストを保存しただけでは、変更フラグはまだセットされたままなので、テキストを保存した後にフラグをクリアする( 0 に設定する)必要があります。これには EM_SETMODIFY メッセージを使います。

#define EM_SETMODIFY          0x00B9

EM_SETMODIFY
    wParam = fModified;
    lParam = 0;

fModified パラメータには、新しく設定するフラグを指定します。ここではフラグをクリアする必要があるので、 0 を指定します。

背景色の設定

今回は新しく背景色の設定を行っています。背景色の設定は EM_SETBKGNDCOLOR メッセージを送信して行ないます。

#define EM_SETBKGNDCOLOR      0x0443

EM_SETBKGNDCOLOR
    wParam = fUseSysColor;
    lParam = clr;

fUseSysColor パラメータには背景色にシステムカラーを使うかどうかを示すフラグ指定します。 0 以外の値を指定した場合は、背景色としてシステムカラーのウィンドウ背景色(GetSysColor 関数で引数に 5 (COLOR_WINDOW) を指定して得られる色)が設定されます。 fUseSysColor パラメータに 0 を指定した場合は、 clr パラメータで指定された色になります。このパラメータには背景色を表すRGB値を指定します。

サンプルスクリプト

それでは、スクリプトを書いてみましょう。

今回はモジュールを定義しています。リッチエディットコントロールの作成および、各メッセージの送信をモジュール命令で定義しました。戻り値を持つものは stat に格納されるようになっています。

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

    #include "llmod.as"

    #module ;#### リッチエディットコントロール作成モジュール ########

    ;===============================================================
    ; リッチエディットコントロールの作成
    ; CreateRichEdit  n1, n2, n3, n4
    ;     n1 : x座標
    ;     n2 : y座標
    ;     n3 : 幅
    ;     n4 : 高さ
    ;   stat : リッチエディットコントロールのハンドルが返る
    ;===============================================================
    #deffunc CreateRichEdit int, int, int, int
    mref cx, 0              ; x位置
    mref cy, 1              ; y位置
    mref sx, 2              ; 幅
    mref sy, 3              ; 高さ
    mref bmscr, 67          ; 描画中ウィンドウのBMSCR構造体
    mref stt, 64            ; stat

    ; RICHEDIT.DLLのロード
    ll_libload@ hdllRichEd, "RICHED32.DLL"

    ; リッチエディットコントロールの作成
    classname = "RichEdit"
    pm.0 = 0
    getptr pm.1, classname
    pm.2 = 0
    pm.3 = 0x50B020C4       ; WS_CHILD | WS_VISIBLE | WS_BORDER
                            ;  | WS_VSCROLL | WS_HSCROLL
                            ;  | ES_MULTILINE | ES_AUTOVSCROLL
                            ;  | ES_AUTOHSCROLL | ES_DISABLENOSCROLL
    pm.4 = cx, cy, sx, sy
    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, 0    ; 64Kバイトに設定
    sendmsg pm

    stt = hEdit
    return

    ;===============================================================
    ; フォント("FixedSys", 白)・背景色(黒)を設定
    ; SetDefaultFormat  n1
    ;     n1 : リッチエディットコントロールのハンドル
    ;===============================================================
    #deffunc SetDefaultFormat int
    mref hEdit, 0           ; リッチエディットコントロールのハンドル

    ; CHARFORMAT 構造体
    dim charfmt, 16
    charfmt.0 = 60                  ; 構造体サイズ
    charfmt.1 = 0x60000000          ; CFM_FACE | CFM_COLOR
    charfmt.5 = 0x00FFFFFF          ; 文字色(白に設定)
    poke charfmt, 26, "FixedSys"    ;フォント名"FixedSys"

    ; EM_SETCHARFORMAT メッセージ送信
    pm = hEdit, 0x0444
    pm.2 = 0x0000               ; SCF_DEFAULT
    getptr pm.3, charfmt        ; CHARFORMAT 構造体のアドレス
    sendmsg pm

    ; EM_SETBKGNDCOLOR メッセージ送信
    pm = hEdit, 0x0443, 0, 0    ; 背景色を黒に
    sendmsg pm
    return

    ;===============================================================
    ; WM_SETTEXT メッセージの送信
    ; SetText  n1, v2
    ;     n1 : リッチエディットコントロールのハンドル
    ;     v2 : テキストを格納した文字列型変数
    ;===============================================================
    #deffunc SetText int, val
    mref hEdit, 0           ; リッチエディットコントロールのハンドル
    mref textbuf, 25        ; テキストを格納した変数

    ; WM_SETTEXT メッセージ送信
    pm = hEdit, 0x000C, 0
    getptr pm.3, textbuf
    sendmsg pm
    return

    ;===============================================================
    ; WM_GETTEXTLENGTH メッセージの送信
    ; GetTextLen  n1
    ;     n1 : リッチエディットコントロールのハンドル
    ;===============================================================
    #deffunc GetTextLen int
    mref hEdit, 0           ; リッチエディットコントロールのハンドル

    ; WM_GETTEXTLENGTH メッセージ送信
    pm = hEdit, 0x000E, 0, 0
    sendmsg pm
    return

    ;===============================================================
    ; WM_GETTEXT メッセージの送信
    ; GetText  n1, v2, n3
    ;     n1 : リッチエディットコントロールのハンドル
    ;     v2 : テキストを格納するための文字列型変数
    ;     n3 : 変数 v2 のサイズ
    ;===============================================================
    #deffunc GetText int, val, int
    mref hEdit, 0           ; リッチエディットコントロールのハンドル
    mref textbuf, 25        ; テキストを受け取るバッファ
    mref maxsize, 2         ; バッファサイズ

    ; WM_GETTEXT メッセージ送信
    pm = hEdit, 0x000D, maxsize
    getptr pm.3, textbuf
    sendmsg pm
    return

    ;===============================================================
    ; EM_GETMODIFY メッセージの送信
    ; GetModify  n1
    ;     n1 : リッチエディットコントロールのハンドル
    ;===============================================================
    #deffunc GetModify int
    mref hEdit, 0           ; リッチエディットコントロールのハンドル

    ; EM_GETMODIFY メッセージ送信
    pm = hEdit, 0x00B8, 0, 0
    sendmsg pm
    return

    ;===============================================================
    ; EM_SETMODIFY メッセージの送信
    ; SetModify  n1, n2
    ;     n1 : リッチエディットコントロールのハンドル
    ;     n2 : 変更フラグの設定値
    ;===============================================================
    #deffunc SetModify int, int
    mref hEdit, 0           ; リッチエディットコントロールのハンドル
    mref mode, 1            ; 変更フラグの設定値

    ; EM_SETMODIFY メッセージ送信
    pm = hEdit, 0x00B9, mode, 0
    sendmsg pm
    return

    #global ;############# モジュール終わり ########################


    objsize winx/4, 24
    pos 0, 0        : button "New", *lb_push      ; Object ID = 0
    pos winx/4, 0   : button "Open", *lb_push     ; Object ID = 1
    pos winx/2, 0   : button "Save", *lb_push     ; Object ID = 2
    pos winx*3/4, 0 : button "SaveAs", *lb_push   ; Object ID = 3

    ; リッチエディットコントロールの作成
    CreateRichEdit 0, 24, winx, winy-24
    hEdit = stat

    ; フォント・背景色設定
    SetDefaultFormat hEdit

    sdim filename, 260          ; ファイル名を格納する変数
    title "(無題)"
    stop

*lb_push
    ; ボタンが押された場合(statはボタンのオブジェクトID)
    if stat == 0 : gosub *lb_new    : stop
    if stat == 1 : gosub *lb_open   : stop
    if stat == 2 : gosub *lb_save   : stop
    if stat == 3 : gosub *lb_saveas : stop
    stop

*lb_new
    GetModify hEdit             ; 内容が変更されているかどうか調べる
    if stat {
        dialog "内容が変更されています。保存しますか", 2
        if stat == 6 : gosub *lb_save
    }
    buf = ""          ; 空文字列を設定
    SetText hEdit, buf
    title "(無題)"
    SetDefaultFormat hEdit      ; デフォルトフォント・背景色設定
    SetModify hEdit, 0          ; 変更フラグをクリアする
    filename = ""
    return

*lb_open
    GetModify hEdit             ; 内容が変更されているかどうか調べる
    if stat {
        dialog "内容が変更されています。保存しますか", 2
        if stat == 6 : gosub *lb_save
    }
    dialog "txt", 16
    if stat == 0 : return       ; キャンセルされた場合はリターン
    filename = refstr
    exist filename
    if strsize == -1 : return
    len = strsize               ; ファイルサイズ
    sdim buf, len+1             ; 必要なバッファを確保
                                ; テキストサイズ+1 (終端ヌル文字を考慮)
    bload filename, buf, len    ; いったんバッファに読み込む
    SetDefaultFormat hEdit      ; デフォルトフォント・背景色設定
    SetText hEdit, buf          ; テキストをセット
    title filename              ; タイトルバーをファイル名に
    SetModify hEdit, 0          ; 変更フラグをクリアする
    return

*lb_save
    if filename == "" {
        gosub *lb_saveas
    } else {
        gosub *lb_filesave
    }
    return

*lb_saveas
    dialog "txt", 17
    if stat == 0 : return       ; キャンセルされた場合はリターン
    filename = refstr
    gosub *lb_filesave
    return

*lb_filesave
    GetTextLen hEdit            ; テキストサイズ取得
    len = stat
    sdim buf, len+1             ; 必要なバッファを確保
                                ; テキストサイズ+1 (終端ヌル文字を考慮)
    GetText hEdit, buf, len+1   ; バッファにテキストを格納する
    bsave filename, buf, len
    title filename              ; タイトルバーをファイル名に
    SetModify hEdit, 0          ; 変更フラグをクリアする
    return