ショートカットメニューを表示してみる

ショートカットメニューの作成

今回は、右クリックをしたときなどに表示されるショートカットメニューを表示させてみましょう。前回までのことが分かっているならば、さほど難しいことはありません。

ショートカットメニューを作成するには、前回ドロップダウンメニューの作成に使用したのと同じ CreatePopupMenu 関数を呼び出します。そのあと、やはり前回と同様に AppendMenu 関数でメニューにメニューアイテムを追加しましょう。

ショートカットメニューの表示

前回では、作成されたメニューオブジェクトをアイテムとして追加すれば、ドロップダウンメニュー・サブメニューとして使えましたが、今回は作成した作成されたメニューオブジェクトをショートカットメニューとして任意の場所に表示させる必要があります。ショートカットメニューを表示させるには、 TrackPopupMenuEx 関数を呼び出します。

BOOL TrackPopupMenuEx(
    HMENU hMenu,       // メニューハンドル
    UINT  fuFlags,     // オプションフラグ
    int   x,           // x座標
    int   y,           // y座標
    HWND  hWnd,        // ウィンドウハンドル
    LPTPMPARAMS ptpm   // オーバーラップ禁止範囲
);

第2引数(fuFlags パラメータ)には、どのように表示するかのオプションを指定します。通常は 0 を指定しておけば大丈夫でしょう。

第3、第4引数(x, y パラメータ)にはショートカットメニューを表示する座標を指定します。この座標はスクリーン座標の x, y 位置を指定しなければなりません。

第6引数(ptpm パラメータ)はオーバーラップ禁止範囲を指定しますが、特に何も指定する必要がない場合には 0 (NULL) を指定します。

この関数によってショートカットメニューが表示されると、第5引数(hWnd パラメータ)で指定されたウィンドウにメッセージが送られます。これは前回までと同様に WM_COMMAND メッセージです。


ショートカットメニューを表示する場合、 WM_COMMAND メッセージを取得する以外にも、どのアイテムが選択されたのかを取得する手段があります。第2引数(fuFlags パラメータ)に 0x0100 (TPM_RETURNCMD) を指定すると、この関数の戻り値として、選択されたアイテムのIDが返されることになっています。また、何も選択されなれば戻り値が 0 になります。

この場合、アイテムが選択されたことで WM_COMMAND メッセージが発生してしまうので、関数呼び出し時に同時に 0x0080 (TPM_NONOTIFY) も組み合わせて指定しておきましょう。このようにすると、 WM_COMMAND メッセージが発生しないようになります。

ショートカットメニューの破棄

以前に一度説明しましたが、作成されたメニューオブジェクトは、他のWindowsオブジェクトの場合と同様、必要なくなった時点で削除しなければならないのでしたね。ただ、前回までのスクリプトで作成されていたメニューはすべてウィンドウに関連付けられたものであったため、それらはウィンドウが破棄されるときに同時に削除されていたのでした。しかし、今回はそうではないので、明示的に削除しておかなければならないのです。

メニューオブジェクトを削除することを、一般的に「メニューを破棄する」と呼びます。メニューを破棄するには DestroyMenu 関数を使います。

BOOL DestroyMenu(
    HMENU hMenu     // メニューハンドル
);

この関数にメニューハンドルを渡すことで、そのメニューが破棄されます。メニューを破棄したら、それ以降はそのメニューハンドルは無効なものになるので、そのハンドルをメニュー操作関数に渡したりしてはいけません。

サンプルスクリプト

さて、実際にスクリプトを書いてみましょう。

メニューを作成しておいて、右クリックされた時にマウスの位置に表示すればいいでしょう。今回は、上で説明した WM_COMMAND メッセージを受け取らない方法を使ってみることにします。そして、マウスクリックを検出するのには onclick 命令を使ってみることにしましょう。

メニュー表示位置にはスクリーン座標を指定するので、マウスの座標を求めるのに ginfo 命令を使うことになります。

    #include "llmod.as"

    ; メニューアイテムIDを定義
    #define CMD_OPEN       1      ;「開く」アイテムのID
    #define CMD_SAVE       2      ;「保存」アイテムのID
    #define CMD_QUIT       3      ;「終了」アイテムのID

    ; ショートカットメニューの作成
    dllproc "CreatePopupMenu", pm, 0, D_USER
    hpopup = stat                 ; ショートカットメニューのハンドル

    mesbuf = "開く(&O)"
    pm = hpopup, 0, CMD_OPEN
    getptr pm.3, mesbuf
    dllproc "AppendMenuA", pm, 4, D_USER

    mesbuf = "保存(&S)"
    pm = hpopup, 0, CMD_SAVE
    getptr pm.3, mesbuf
    dllproc "AppendMenuA", pm, 4, D_USER

    pm = hpopup, $800, 0, 0       ; 区切り線を指定
    dllproc "AppendMenuA", pm, 4, D_USER

    mesbuf = "終了(&Q)"
    pm = hpopup, 0, CMD_QUIT
    getptr pm.3, mesbuf
    dllproc "AppendMenuA", pm, 4, D_USER

    onclick goto *lb_on_click     ; マウスクリック時ジャンプ
    onexit  goto *lb_on_quit      ; 終了時ジャンプ

    stop

*lb_on_click
    ; マウスクリックされたときの処理
    if iparam == 3 {
        ; 右クリックのとき
        ginfo 0                   ; マウスカーソル座標取得
        mref bmscr, 67            ; ウィンドウのBMSCR構造体取得
        pm.0 = hpopup             ; ショートカットメニューのハンドル
        pm.1 = $182               ; TPM_RIGHTBUTTON | TPM_RETURNCMD
                                  ;  | TPM_NONOTIFY
        pm.2 = prmx               ; スクリーンx座標
        pm.3 = prmy               ; スクリーンy座標
        pm.4 = bmscr.13           ; ウィンドウハンドル
        pm.5 = 0                  ; NULL
        dllproc "TrackPopupMenuEx", pm, 6, D_USER
        if stat {
            itemid = stat         ; 選択されたメニューアイテムID
            goto *lb_on_command
        }
    }
    stop


*lb_on_command
    ; メニューアイテムが選択されたときの処理
    if itemid == CMD_OPEN {
        dialog "*", 16
        if stat {
            dialog refstr+"を開きました"
        }
    }
    if itemid == CMD_SAVE {
        dialog "*", 17
        if stat {
            dialog refstr+"を保存しました"
        }
    }
    if itemid == CMD_QUIT {
        goto *lb_on_quit
    }
    stop

*lb_on_quit
    ; 終了処理
    dialog "終了します", 0
    ; ポップアップメニューの破棄
    dllproc "DestroyMenu", hpopup, 1, D_USER
    end