このドキュメントでは、次のトピックについて説明します。
Natural v4.1.1 から、Natural アプリケーション内で使用するコンテキストメニューを作成できるようになりました。 メニュー内容があらかじめ判明しており、ダイアログエディタで作成できる場合は、コンテキストメニューを完全にスタティックにすることができます。一方、メニュー内容や状態がランタイム時に決まるため設計時に判明していない場合は、コンテキストメニューの全体または一部をダイナミックにすることができます。
コンテキストメニューはサブメニューの概念に非常に似ています。 そこで、コンテキストメニューの編集には、ダイアログのメニューバーの編集に使用するのと同じメニューエディタを使用します。 メニューバーのサブメニューとまったく同じ方法で、メニュー項目をコンテキストメニューに追加し、イベントと関連付けることができます。 [OLE]コンボボックス(最上位メニューバーのサブメニューにのみ使用可能)が常に使用不可であること以外は、メニューバーエディタと機能的な相違はありません。 ただし、コンテキストメニュー項目が存在する限り、そのメニュー項目に定義されたアクセラレータはグローバルに使用できることに注意してください。 さらに、コンテキストメニューが表示されていない場合、または異なるコンテキストメニューを使用しているかまったく使用していないコントロールにフォーカスがある場合でも、アクセラレータは定義されたメニュー項目を起動します。
コンテキストメニューエディタは、Ctrl + Alt + X)、またはツールバーのアイコンから起動します。 ただし、コンテキストメニューエディタは、同時に 1 つのコンテキストメニューエディタしか編集できないため、直接は起動されません。 コンテキストメニューの全体的な操作を行い、選択したコンテキストメニューに対するメニューエディタを起動できる[ダイアログコンテキストメニュー]ウィンドウが表示されます。
メニューの新規メニュー項目である 、このメニュー項目に関連付けられたアクセラレータ(デフォルトではサブメニューとコンテキストメニューを区別するために、コンテキストメニューは内部的に新規タイプ CONTEXT MENU
を持ちます。 このタイプを使わないと、どちらの場合も生成コードが同じになります。 以下のサンプルコードは、2 つのメニュー項目を持つ簡単なコンテキストメニューを構築するために使用するステートメントを示しています。
/* CREATE CONTEXT MENU ITSELF: PROCESS GUI ACTION ADD WITH PARAMETERS HANDLE-VARIABLE = #CONTEXT-MENU-1 TYPE = CONTEXTMENU PARENT = #DLG$WINDOW END-PARAMETERS GIVING *ERROR /* ADD FIRST MENU ITEM: PROCESS GUI ACTION ADD WITH PARAMETERS HANDLE-VARIABLE = #MITEM-1 TYPE = MENUITEM DIL-TEXT = 'Invokes the first item' PARENT = #CONTEXT-MENU-1 STRING = 'Item 1' END-PARAMETERS GIVING *ERROR /* ADD SECOND MENU ITEM: PROCESS GUI ACTION ADD WITH PARAMETERS HANDLE-VARIABLE = #MITEM-2 TYPE = MENUITEM DIL-TEXT = 'Invokes the second item' PARENT = #CONTEXT-MENU-1 STRING = 'Item 2' END-PARAMETERS GIVING *ERROR
コンテキストメニューまたはコンテキストメニュー項目をユーザー記述コード内でダイナミックに作成すると、コンテキストメニューまたはコンテキストメニュー項目がダイアログエディタで表示されないことに注意してください。 例えば、ダイナミックに作成したメニューは、コンテキストメニューリストボックスに表示されません。また、ダイナミックに作成されたメニュー項目はコンテキストメニューエディタに表示されません。
コンテキストメニューの作成後に、コンテキストメニューを Natural オブジェクトと関連付ける必要があります。 コンテキストメニューは、キーボードフォーカスを受け取ることのできるほとんどすべてのコントロールタイプと、ダイアログウィンドウ自体をサポートしています。 コントロールタイプには、ActiveX コントロール、ビットマップ、キャンバス、編集エリアフィールド、入力フィールド、リストボックス、プッシュボタン、ラジオボタン、スクロールバー、選択ボックス、テーブルコントロール、トグルボタン、標準ウィンドウ、MDI 子ウィンドウ、MDI フレームウィンドウなどがあります。
コンテキストメニューをサポートするすべてのオブジェクトタイプについて、ダイアログエディタ内の対応する属性ダイアログには、ダイアログエディタによって作成されるすべてのコンテキストメニューと空のエントリをリストする読み取り専用のコンボボックスが含まれます。 空のエントリを選択すると(デフォルト)、このオブジェクトにコンテキストメニューを使用しないことを意味します。
内部的には、アソシエーションは新規属性 CONTEXT-MENU
によって行われます。また、この属性にコンテキストメニューのハンドルを設定する必要があります。 この属性はオブジェクト作成時または作成後に割り当てることができます。指定しない場合、コンテキストメニューがないことを意味する NULL-HANDLE
にデフォルト設定されます。 ダイアログエディタによって作成されるコンテキストメニューに対しては、以下の例のように、コントロールの作成時にコンテキストメニューが指定されます。
PROCESS GUI ACTION ADD WITH PARAMETERS HANDLE-VARIABLE = #LB-1 TYPE = LISTBOX RECTANGLE-X = 585 RECTANGLE-Y = 293 RECTANGLE-W = 142 RECTANGLE-H = 209 MULTI-SELECTION = TRUE SORTED = FALSE PARENT = #DLG$WINDOW CONTEXT-MENU = #CONTEXT-MENU-1 SUPPRESS-FILL-EVENT = SUPPRESSED END-PARAMETERS GIVING *ERROR
ユーザー記述イベントコード内で作成されるコントロールに対しても、同じ構文を使うことができます。 その他の場合、つまりダイアログエディタによってコントロールは作成されたがコンテキストメニューは作成されなかった場合、コントロールの作成後にダイアログの AFTER-OPEN
イベントなどで、コンテキストメニュー属性をコントロールに割り当てる必要があります。
/* CONTEXT MENU SPECIFIED AFTER CREATION: #LB-2.CONTEXT-MENU := #CONTEXT-MENU-2
コンテキストメニューを使用するオブジェクトが破棄されてもコンテキストメニューは破棄されないことに注意してください。 コンテキストメニューは、親オブジェクト(通常は、コンテキストメニューが定義されたダイアログ)が破棄されたときに破棄されます。 同様に、すでにメニューハンドルが割り当てられている
CONTEXT MENU
属性に新しいメニューハンドルを割り当てても、前のコンテキストメニューは破棄されません。 したがって、上記の例では、以下のステートメントのどちらを実行しても、CONTEXT-MENU-1
は破棄されません。
PROCESS GUI ACTION DELETE WITH #LB-1 /* #CONTEXT-MENU-1 LIVES ON #LB-1.CONTEXT-MENU := #CONTEXT-MENU-2 /* SAME HERE
Natural のコンテキストメニュー起動処理は、次の手順で実行されます。
マウスを使用してコンテキストメニューにアクセスした場合(つまり、マウスの第 2 ボタンをクリックした場合)、ターゲットコントロールはマウスカーソルのすぐ下にあるコントロールであるとみなされます。 そうでない場合、すなわち、キーボードによってコンテキストメニューにアクセスした場合(つまり、コンテキストメニューキーがあればそのキー、またはキーの組み合わせの Shift + F10 を押した場合)、ターゲットコントロールはその時点でキーボードのフォーカスが当たっているコントロールだとみなされます。
コントロールのクリック位置は、ターゲットコントロールのクライアントエリアに対する相対位置で設定されます。 キーボードからコンテキストメニューにアクセスした場合、クリック位置は (0, 0) に設定されます。
イベントが抑制されていない場合、SUPPRESS-CONTEXT-MENU-EVENT
属性に基づいて、ターゲットコントロールに対する CONTEXT-MENU
イベントが発生します。
ターゲットコントロールの CONTEXT-MENU
属性がクエリされます。 ターゲットコントロールの値とタイプによって、以下のアクションが実行されます。
属性が NULL-HANDLE
に設定されていて、ターゲットコントロールがダイアログの場合、何もコンテキストメニューを表示せずにコンテキストメニュー起動処理は終了します。
属性が NULL-HANDLE
に設定されていて、ターゲットコントロールがダイアログエレメントの場合、そのターゲットコントロールはダイアログエレメントの PARENT
であるとみなされ、コンテキストメニュー起動処理は上記の手順 2 から繰り返されます。
属性がコンテキストメニューハンドルに設定されている場合、このコンテキストメニューは表示する必要のあるコンテキストメニュー(つまり、ターゲットコンテキストメニュー)だと判断され、下記の手順 5 から処理が続行されます。
イベントが抑制されていない場合、ターゲットコンテキストメニューに対する BEFORE-OPEN
イベントが発生します。
ターゲットコンテキストメニューの ENABLED
属性がクエリされます。 ENABLED 属性が FALSE
に設定されている場合、コンテキストメニューは表示されません。
そうでない場合、抑制されていなければ、ターゲットダイアログに対する COMMAND-STATUS
イベントが発生します。 ターゲットダイアログとは、ターゲットコントロールがダイアログエレメントの場合はそのコントロールが組み込まれているダイアログを指し、ターゲットコントロールがダイアログの場合はターゲットコントロール自体を指します。
コンテキストメニューは、上記の手順 2 で設定されるクリック位置に表示されます。
コンテキストメニュー内の実際のナビゲーションおよびメニュー項目に関連付けられているイベントの起動は、Windows および Natural によって、アプリケーションの介入を受けずに実行されます。
上記のプロセスは、ダイアログまたはコンテキストメニューを持つダイアログエレメント(存在する場合)が見つかるまで、最初のターゲットコントロールからコントロールの階層に従って続行され、その後でコンテキストメニューが処理されることに注意してください。
CONTEXT-MENU
イベントの目的は、コンテキストに応じて多数の候補の中から、(ターゲットコントロールの CONTEXT-MENU
属性を変更することによって)アプリケーションが適切なコンテキストメニューを選択できるようにすることです。 複数のコンテキストメニューの使用例については、「リストビューコントロールの操作」を参照してください。
同様に、コンテキストメニューの BEFORE-OPEN
イベントは、その時点のプログラムの状態に応じて、アプリケーションによるコンテキストメニューの変更を可能にします。 例えば、メニュー項目を追加または削除したり、特定のメニュー項目を無効化またはチェック状態にしたりできます。 以下に、BEFORE-OPEN
イベントのサンプルコードを示します。
/* DELETE FIRST MENU ITEM: PROCESS GUI ACTION DELETE WITH #MITEM-1 /* CHECK SECOND MENU ITEM: #MITEM-2.CHECKED := CHECKED /* DISABLE THIRD MENU ITEM: #MITEM-3.ENABLED := FALSE /* INSERT NEW MENU ITEM BEFORE #MITEM-3: PROCESS GUI ACTION ADD WITH PARAMETERS HANDLE-VARIABLE = #MITEM-4 TYPE = MENUITEM DIL-TEXT = 'Invokes the first item' PARENT = #CONTEXT-MENU-1 STRING = 'Item 3' SUCCESSOR = #MITEM-3 END-PARAMETERS GIVING *ERROR
ダイアログエディタ以外で作成されたコンテキストメニューについては、ダイアログの DEFAULT
イベントで BEFORE-OPEN
イベントの処理を行う必要があります。 また、コントロールまたはダイアログを無効にすると、コンテキストメニューは表示されず、BEFORE-OPEN
イベントも起動されないことにも注意してください。 コンテキストメニュー自体を無効にした場合も同様です。 次に例を示します。
#CONTEXT-MENU-1.ENABLED := FALSE /* DISABLE CONTEXT MENU DISPLAY
この方法で BEFORE-OPEN
イベント中にコンテキストメニューを無効にできます。つまり、コントロール内のマウスカーソルの位置に応じて、コンテキストメニューを選択的に無効にできることに注意してください。 例えば、選択されたリストボックス項目上にマウスカーソルがある場合にのみコンテキストメニューを表示する場合などが該当します。
このケースに該当するかどうかは、以下の 2 つの PROCESS GUI ACTION
呼び出しを使用して判断できます。
INQ-CLICKPOSITION
。このアクションは、コントロール内でマウスの右ボタンがクリックされたときの (X, Y) 位置を返すために、ビットマップとキャンバス以外のコントロールにも拡張されています。 これらのパラメータは現在はオプションパラメータです。また、新しいオプションパラメータが追加されています。このパラメータは、コンテキストメニューがマウスによってアクセスされると
TRUE
に、キーボードによってアクセスされると FALSE
に設定されます。 キーボードの場合、クリック位置は (0, 0) に設定されます。 これらの情報はすべて、BEFORE-OPEN
イベントを送る直前に更新されます。
INQ-ITEM-BY-POSITION
。 このアクションを使用すると、リストボックスに適用される INQ-CLICKPOSITION
によって返される相対座標を、その座標が指す項目に変換できます。
この 2 つの新規アクションを使用する例として、マウスの右ボタンが押されたときに、選択されたリストボックス項目上にカーソルがあるかどうかを確認して、コンテキストメニューを表示するかどうかを判断する場合を考えてみます。 これは、関連付けられているコンテキストメニューの
BEFORE-OPEN
イベントに以下のコードを記述することによって実現できます。
PROCESS GUI ACTION INQ-CLICKPOSITION WITH #LB-1 #X-OFFSET #Y-OFFSET PROCESS GUI ACTION INQ-ITEM-BY-POSITION WITH #LB-1 #X-OFFSET #Y-OFFSET #LBITEM #MENU = *CONTROL IF #LBITEM = NULL-HANDLE /* NO ITEM UNDER (MOUSE) CURSOR */ #MENU.ENABLED := FALSE ELSE IF #LBITEM.SELECTED = FALSE /* ITEM UNDER CURSOR DESELECTED */ #MENU.ENABLED := FALSE ELSE /* ITEM UNDER CURSOR IS SELECTED */ #MENU.ENABLED := TRUE END-IF END-IF
マウスカーソルの下にあるメニュー項目がまだ選択されていなくても、既存の選択をクリアして、この項目を自動的に選択したい場合があります。 リストボックスの場合、直接またはダイアログエディタの[リストボックス属性]ウィンドウ内の新しい[自動選択]チェックボックス経由で、新規属性 AUTOSELECT
を使用することによって、自動選択を実現できます。 この属性に TRUE
を設定した場合、選択されていないリストボックス項目上でコンテキストメニューが起動されると、Natural は BEFORE-OPEN
イベントを送る前に自動的に選択状態を更新します。
テーブルコントロールの場合、選択の変更は BEFORE-OPEN
イベントの中でアプリケーション自体が行う必要があります。 これを実現するために、テーブルコントロールに対して、以下の新規 PROCESS GUI ACTION
が導入されています。
TABLE-INQUIRE-CELL
。 これは、テーブル内の相対位置 (X, Y) に対するセルの行と列の番号(1 から始まる)を返します。 通常、この位置は前の PROCESS GUI ACTION INQ-CLICKPOSITION
呼び出しによって返される位置になります。
COMMAND-STATUS
イベントは、アプリケーションでコマンド(つまり、メニュー項目、ツールバー項目、およびシグナル)の無効化やチェックを実行するためのもう 1 つの場所です。 すでにこのイベントを使用している場合、これらのアクションを BEFORE-OPEN
イベントで実行する必要はありません。
前述のコンテキストメニューの自動起動処理の他に、SHOW-CONTEXT-MENU
アクションを使用して、特定の位置で特定のコンテキストメニューを手動で起動することもできます。
これは基本的には、自動起動処理が常に有効とは限らない ActiveX コントロールを使用するための機能ですが、ActiveX に使用が限定されているわけではありません。 ActiveX コントロールの中には、その内部実装によって、Natural でコンテキストメニューの表示を起動するために使用されているメッセージが送出されないものがあります。 この場合、マウスの第 2 ボタンを押すと ActiveX コントロールでイベントが発生するのであれば、この操作によるイベントのイベントハンドラ内でコンテキストメニューを手動で表示できます。
例えば、コンテキストメニュー #CTXMENU-1
を Microsoft Rich Textbox ActiveX Control の #OCX-1
で表示する場合、コントロールの MouseDown イベントハンドラに以下のコードを記述します。
IF #OCX-1.<<PARAMETER-Button>> = 2 #X := #OCX-1.<<PARAMETER-x>> + 2 #Y := #OCX-1.<<PARAMETER-y>> + 2 PROCESS GUI ACTION SHOW-CONTEXT-MENU WITH #CTXMENU-1 #OCX-1 #X #Y GIVING *ERROR END-IF
ローカルデータの定義は以下のように想定されています。
01 #X (I4) 01 #Y (I4)
上記のコードでは、マウスの第 2 ボタンが押されたかどうかをまず確認してから、コントロールから渡された位置に基づいてコンテキストメニューを手動で起動していることに注意してください。 ただし、コントロールによって渡される位置は ActiveX コントロールとの相対位置(幅 2 ピクセルのくぼんだ境界線を含む)であるのに対し、コンテキストメニューの表示には ActiveX コントロールのコンテナウィンドウ(境界線なし)との相対位置が使用されるため、最初に位置を少し修正しています。
ActiveX コントロールによっては、twip(twentieths of a point)など、ピクセル以外の座標単位で位置を返すものがあることに注意してください。 以下の例は、座標 (#X
, #Y
) を twip からピクセルに変換する方法を示しています。
#CONTROL := *CONTROL /* Convert x-coordinate MULTIPLY #X BY #CONTROL.DPI DIVIDE #X BY 1440 /* Convert y-coordinate MULTIPLY #Y BY #CONTROL.DPI DIVIDE #Y BY 1440
#CONTROL
は HANDLE OF GUI
として定義され、#X
および #Y
はフォーマット I4 を想定しています。
1440 という値は、論理インチごとの twip 数です。一方、ダイアログエレメントに適用されている DPI 属性は論理インチごとのピクセル数を返します。
1 つのコンテキストメニューを複数のオブジェクト(コントロールまたはダイアログ)に関連付けることができます。 次に例を示します。
#LB-1.CONTEXT-MENU := #CTXMENU-1 #LB-2.CONTEXT-MENU := #CTXMENU-1
このような場合、コンテキストメニューがどのコントロールに対して起動されたかを識別する必要があります。 *CONTROL
はコンテキストメニュー自体のハンドルを含むため、BEFORE-OPEN
イベントでは使用できません。 Natural はコンテキストメニューが起動されているコントロールにフォーカスを自動的に位置付けるので、どのコントロールにフォーカスがあるのかを問い合わせる必要があります。 以下の例は、この手法の使用方法を説明した BEFORE-OPEN
イベントコードです。
PROCESS GUI ACTION GET-FOCUS WITH #CONTROL DECIDE ON FIRST VALUE OF #CONTROL VALUE #LB-1 #MITEM-17.ENABLED := FALSE VALUE #LB-2 #MITEM-17.CHECKED := CHECKED NONE IGNORE END-DECIDE
ただし、あらゆる場合に使用できるより優れた方法は、コンテキストメニューの CONTROL
属性をクエリすることです。
#CONTROL := *CONTROL DECIDE ON FIRST VALUE OF #CONTROL.CONTROL ... END-DECIDE