
これまで作成したメニューのアイテムはすべて、選択可能なものだけでした。しかし、必要なときにメニューアイテムを選択不可能な状態にしたり、チェックマークを表示したり、文字列を変更したりできたほうが便利でしょう。今回はこういったことを実現してみたいを思います。
まずはAPI関数の説明です。メニューアイテムの状態を変更するには、 SetMenuItemInfo 関数を用います。 SetMenuItemInfo 関数は、メニューアイテムのさまざまな状態を変更することができます。
BOOL SetMenuItemInfoA(
HMENU hMenu, // メニューハンドル
UINT uItem, // アイテムのIDまたは位置
BOOL fByPosition, // フラグ
LPMENUITEMINFO pItemInfo // MENUITEMINFO構造体アドレス
);
第1引数(hMenu パラメータ)には、変更したいアイテムを持つメニューハンドルを指定します。指定するアイテムを持つメニューが、ある別のメニューのサブメニューとなっている場合は、親メニューのハンドルを指定することもできるようになっています。すなわち、ウィンドウに関連付けられているメニューの中のアイテムならば、一番上にあるメニューバーのハンドルを指定するだけで、目的のアイテムを操作できます。
第2引数(uItem パラメータ)は、第3引数(fByPosition パラメータ)によって指定するものが変わります。 fByPosition に 0 (FALSE) を指定した場合は uItem はメニューアイテムIDを示します。また、 fByPosition に 1 (TRUE) を指定した場合は uItem はアイテムの位置のインデックスを示します。
第4引数(pItemInfo パラメータ)は、メニューアイテムの情報を格納した MENUITEMINFO 構造体のアドレスを指定します。
MENUITEMINFO 構造体は以下のように定義されています。
typedef struct tagMENUITEMINFO {
UINT cbSize; // 構造体のサイズ
UINT fMask; // 取得または設定するメンバ
UINT fType; // アイテムのタイプ
UINT fState; // アイテムの状態
UINT wID; // アイテムID
HMENU hSubMenu; // サブメニューのハンドル
HBITMAP hbmpChecked; // チェック表示時のビットマップ
HBITMAP hbmpUnchecked; // チェック非表示時のビットマップ
ULONG_PTR dwItemData; // 任意の32ビット値
LPTSTR dwTypeData; // アイテムの内容
UINT cch; // アイテムの文字列の長さ
HBITMAP hbmpItem; // ビットマップハンドル
} MENUITEMINFO, *LPMENUITEMINFO;
cbSize メンバはこの構造体のサイズを表します。このメンバには 48 を指定しましょう。また、この構造体の古い定義では最後のメンバがなく、構造体サイズは 44 になります。したがって、最後のメンバを使わない場合は 44 を指定してもかまいません。
アイテムの状態(選択可・不可やチェックマーク表示・非表示など)を設定する場合は、 fMask メンバに 1 (MIIM_STATE) を指定し、 fState メンバに状態を示す値を指定します。
表示する文字列を変更したい場合は、 fMask メンバに $10 (MIIM_TYPE) を、 fType メンバに 0 (MFT_STRING) を指定し、 dwTypeData メンバに新しく表示する文字列のアドレスを指定します。
SetMenuItemInfo 関数は、いろいろな変更を細かく行なうことができるのですが、ちょっと状態を変更するためだけにこれだけの操作をするのは少々面倒だと思うかもしれませんね。そこで、次のAPI関数も紹介しておきます。
まずは EnableMenuItem 関数です。この関数は、メニューアイテムの表示状態(選択可・不可の状態)を設定します。
BOOL EnableMenuItem(
HMENU hMenu, // メニューハンドル
UINT uItemID, // アイテムのIDまたは位置
UINT uEnable // 表示状態
);
次に CheckMenuItem 関数です。この関数は、メニューアイテムにつけるチェックマークの表示状態を設定します。
DWORD CheckMenuItem(
HMENU hMenu, // メニューハンドル
UINT uItemID, // アイテムのIDまたは位置
UINT uCheck // チェックマークの表示状態
);
さて、今回はメニューバーとショートカットメニューの両方を作成しますが、その際のちょっとしたテクニックを紹介します。
ポップアップ表示することを目的として作成されたショートカットメニューは、ウィンドウに関連付けられていないので、終了時には DestroyMenu 関数を呼び出してメニューを破棄しなければならないのでした。しかし、このショートカットメニューも、ウィンドウに関連付けられていさえすれば、いちいち破棄する必要はなくなります。
そこで1つの手段として、ポップアップメニューをダミーのアイテムとしてウィンドウのメニューバーに追加してしまうのです。具体的には、ポップアップメニューをメニューバーのアイテムとして追加する際に AppendMenu 関数の第4引数(表示文字列のアドレス)として 0 (NULL) を指定するのです。すると、見かけ上はメニューバーには表示されませんが、実際にはポップアップメニューがウィンドウに関連付けられるのです。
さあ、スクリプトを書いてみましょう。
#include "llmod.as"
#include "hsgetmsg.as"
#define WM_COMMAND 0x0111
; メニューアイテムIDを定義
#define CMD_DISP_WND_2 2 ; ウィンドウID = 2
#define CMD_DISP_WND_3 3 ; ウィンドウID = 3
#define CMD_DISP_WND_4 4 ; ウィンドウID = 4
#define CMD_CHNG_WND_2 12 ;
#define CMD_CHNG_WND_3 13 ;
#define CMD_CHNG_WND_4 14 ;
#define CMD_QUIT 20 ; 終了
buf = ""
getptr lpbuf, buf ; 文字列変数 buf へのポインタ
; 「表示ウィンドウ」メニューの作成
dllproc "CreatePopupMenu", pm, 0, D_USER
hmenuWindow = dllret ; ドロップダウンメニューハンドル
repeat 3, 2
screen cnt, 300, 200
title "Window ID = "+cnt
fdispwnd.cnt = 1
buf = "Window ID = "+cnt
pm = hmenuWindow, 8, cnt, lpbuf
dllproc "AppendMenuA", pm, 4, D_USER
loop
pm = hmenuWindow, $800, 0, 0 ; 区切り線を指定
dllproc "AppendMenuA", pm, 4, D_USER
buf = "終了(&Q)"
pm = hmenuWindow, 0, CMD_QUIT, lpbuf
dllproc "AppendMenuA", pm, 4, D_USER
; ショートカットメニューの作成
dllproc "CreatePopupMenu", pm, 0, D_USER
hmenuPopup = dllret
repeat 3, 2
buf = "Window ID = "+cnt+"をアクティブに"
pm = hmenuPopup, 0, 10+cnt, lpbuf
dllproc "AppendMenuA", pm, 4, D_USER
loop
; メニューバーの作成
dllproc "CreateMenu", pm, 0, D_USER
hmenu = dllret ; メニューハンドル
buf = "表示ウィンドウ(&W)"
pm = hmenu, $10, hmenuWindow, lpbuf ; 「表示ウィンドウ」メニュー追加
dllproc "AppendMenuA", pm, 4, D_USER
pm = hmenu, $10, hmenuPopup, 0 ; ダミーのアイテムとして追加
dllproc "AppendMenuA", pm, 4, D_USER
; ウィンドウのサブクラス化
gsel 0
set_subclass
hwnd = stat ; HSPウィンドウのハンドル
set_message WM_COMMAND ; 取得メッセージ設定
; メニューをウィンドウに割り当てる
pm = hwnd, hmenu
dllproc "SetMenu", pm, 2, D_USER
; メニューを再描画
pm = hwnd ; メニューハンドル
dllproc "DrawMenuBar", pm, 1, D_USER
; メッセージパラメータ用変数
dup msg, msgval.1 ; メッセージが格納される変数
dup wprm, msgval.2 ; wParamパラメータが格納される変数
dup lprm, msgval.3 ; lParamパラメータが格納される変数
; メインループ
*mainloop
wait 10
; ウィンドウメッセージ処理
repeat
get_message msgval
if msgval == 0 : break
if msg == WM_COMMAND : gosub *onCommand
loop
stick kcode,, 1
if kcode & 512 { ; 右クリックされたとき
ginfo 1 ; アクティブウィンドウ取得
activewnd = prmx
if activewnd != -1 {
repeat 3, 2
if (cnt = activewnd) | (fdispwnd.cnt = 0) {
pm = hmenu, 10+cnt, 1
} else {
pm = hmenu, 10+cnt, 0
}
dllproc "EnableMenuItem", pm, 3, D_USER
loop
ginfo 0 ; マウス座標取得
; メッセージはウィンドウID=0 に送られるようにしておく
pm = hmenuPopup, 0, prmx, prmy, hwnd, 0
dllproc "TrackPopupMenuEx", pm, 6, D_USER
}
}
goto *mainloop
;---------------WM_COMMAND が送られたときの処理------------------
*onCommand
itemid = wprm & $FFFF ; 選択されたメニューアイテムID
if (itemid >= 2) & (itemid <= 4) {
if fdispwnd.itemid {
gsel itemid, -1
pm = hmenu, itemid, 0
dllproc "CheckMenuItem", pm, 3, D_USER
fdispwnd.itemid = 0
} else {
gsel itemid, 1
pm = hmenu, itemid, 8
dllproc "CheckMenuItem", pm, 3, D_USER
fdispwnd.itemid = 1
}
}
if (itemid >= 12) & (itemid <= 14) {
gsel itemid - 10, 1
}
if itemid = CMD_QUIT : end
return