ショートカットメニュー

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

今回は、右クリックをしたときなどに表示されるショートカットメニュー(コンテキストメニュー)を表示させてみましょう。前回の内容を理解できていれば、さほど難しいことはありません。

ショートカットメニューを作成するには、前回ドロップダウンメニューの作成に使用したのと同じCreatePopupMenu関数を呼び出します。その後、やはり前回と同様にAppendMenu関数でメニューにメニュー項目を追加します。前回と同じなので、何も難しいことはありません。

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

前回は、作成されたメニューオブジェクトをメニューバーに表示される項目として追加すれば、ドロップダウンメニューやサブメニューとして使いましたが、今回は、作成したメニューオブジェクトをショートカットメニューとして任意の場所に表示させます。ショートカットメニューを表示させるには、TrackPopupMenuEx関数を呼び出します。

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

hMenuパラメータは、表示するメニューを識別するメニューハンドルです。ここには、CreatePopupMenu関数で作成したメニューのハンドルを指定します。

fuFlagsパラメータには、どのように表示するかを指定するビットフラグ値を指定します。このパラメータは、以下の表の値を組み合わせて指定することになります。通常は、デフォルト動作である0を指定しておけば大丈夫でしょう。

定数名 意味
TPM_LEFTALIGN 0x0000 xパラメータがショートカットメニューの左端のX座標になるように表示(デフォルト)
TPM_CENTERALIGN 0x0004 xパラメータがショートカットメニューの中央のX座標になるように表示
TPM_RIGHTALIGN 0x0008 xパラメータがショートカットメニューの右端のX座標になるように表示
TPM_TOPALIGN 0x0000 yパラメータがショートカットメニューの上端のY座標になるように表示(デフォルト)
TPM_VCENTERALIGN 0x0010 yパラメータがショートカットメニューの中央のY座標になるように表示
TPM_BOTTOMALIGN 0x0020 yパラメータがショートカットメニューの下端のY座標になるように表示
TPM_NONOTIFY 0x0080 項目がクリックされたとき通知メッセージ送信しない
TPM_RETURNCMD 0x0100 選択された項目IDを関数の戻り値として返す
TPM_LEFTBUTTON 0x0000 メニュー項目をマウスの左ボタンで選択可能(デフォルト)
TPM_RIGHTBUTTON 0x0002 メニュー項目をマウスの左右両方のボタンで選択可能
TPM_HORPOSANIMATION 0x0400 右から左へアニメーション表示
TPM_HORNEGANIMATION 0x0800 左から右へアニメーション表示
TPM_VERPOSANIMATIO 0x1000 上から下へアニメーション表示
TPM_VERNEGANIMATION 0x2000 下から上へアニメーション表示
TPM_NOANIMATION 0x4000 アニメーションなしで表示
TPM_RECURSE 0x0001 別のメニューがすでに表示されている場合でも表示(メニューの中でコンテキストメニューを表示する目的で使用)
TPM_HORIZONTAL 0x0000 ptpmパラメータで指定した領域に重なる場合は水平方向の配置を優先(デフォルト)
TPM_VERTICAL 0x0040 ptpmパラメータで指定した領域に重なる場合は垂直方向の配置を優先

x, yパラメータにはショートカットメニューを表示する座標を指定します。この座標はスクリーン座標で指定しなければなりません。

ptpmパラメータはオーバーラップ(重なり)禁止領域を指定します。特に、禁止領域を指定する必要がない場合には0 (NULL) を指定します。

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


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

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

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

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

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

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

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

サンプルスクリプト

では、実際にスクリプトを作成してみましょう。

今回のサンプルでは、あらかじめメニューを作成しておき、右クリックされた時にマウスの位置に表示します。今回は、上で説明したWM_COMMANDメッセージを受け取らずに、戻り値で選択メニュー項目を取得する方法を使ってみることにします。

今回はマウスクリックを検出するのに、onclick命令を使うことにします。メニューの表示位置にはスクリーン座標を指定するので、マウスの位置をスクリーン座標で取得できるginfo_mxginfo_myを参照します。

#uselib "user32.dll"
#cfunc CreatePopupMenu "CreatePopupMenu"
#func AppendMenu "AppendMenuA" int,int,int,sptr
#func TrackPopupMenuEx "TrackPopupMenuEx" int,int,int,int,int,int
#func DestroyMenu "DestroyMenu" int

#define NULL            0
#define MF_POPUP        0x10
#define MF_SEPARATOR    0x800

#define TPM_NONOTIFY    0x0080
#define TPM_RETURNCMD   0x0100
#define TPM_RIGHTBUTTON 0x0002

; メニュー項目IDを定義
#define CMD_ID_OPEN     1       ; 「開く」項目のID
#define CMD_ID_SAVE     2       ; 「保存」項目のID
#define CMD_ID_QUIT     3       ; 「終了」項目のID

; ショートカットメニューの作成
hPopupMenu = CreatePopupMenu()
AppendMenu hPopupMenu, 0, CMD_ID_OPEN, "開く(&O)"
AppendMenu hPopupMenu, 0, CMD_ID_SAVE, "保存(&S)"
AppendMenu hPopupMenu, MF_SEPARATOR, 0, NULL
AppendMenu hPopupMenu, 0, CMD_ID_QUIT, "終了(&Q)"

onclick gosub *OnMouseClicked   ; マウスクリック時ジャンプ
onexit  gosub *OnAppExit        ; 終了時ジャンプ

stop

*OnMouseClicked
; マウス右クリックされた場合はメニューを表示
if iparam == 3 {
    fOptions = TPM_NONOTIFY | TPM_RETURNCMD | TPM_RIGHTBUTTON
    TrackPopupMenuEx hPopupMenu, fOptions, ginfo_mx, ginfo_my, hwnd, NULL
    if stat != 0 {
        commandID = stat        ; 選択されたメニュー項目IDを保持
        gosub *ExecuteMenuCommand
    }
}
return

*ExecuteMenuCommand
; 選択されたメニュー項目に応じて処理を実行
switch commandID
case CMD_ID_OPEN
    dialog "*", 16
    if stat {
        dialog refstr + "を開きました"
    }
    swbreak
case CMD_ID_SAVE
    dialog "*", 17
    if stat {
        dialog refstr + "を保存しました"
    }
    swbreak
case CMD_ID_QUIT
    gosub *OnAppExit
    swbreak
swend
return

*OnAppExit
; 終了処理
dialog "終了します", 0
; ポップアップメニューの破棄
DestroyMenu hPopupMenu
end