バージョン 6.3.3
 —  プログラミングガイド  —

コンテキストメニューの定義および使用

このドキュメントでは、次のトピックについて説明します。


はじめに

Natural v4.1.1 から、Natural アプリケーション内で使用するコンテキストメニューを作成できるようになりました。 メニュー内容があらかじめ判明しており、ダイアログエディタで作成できる場合は、コンテキストメニューを完全にスタティックにすることができます。一方、メニュー内容や状態がランタイム時に決まるため設計時に判明していない場合は、コンテキストメニューの全体または一部をダイナミックにすることができます。

Top of page

構築

コンテキストメニューはサブメニューの概念に非常に似ています。 そこで、コンテキストメニューの編集には、ダイアログのメニューバーの編集に使用するのと同じメニューエディタを使用します。 メニューバーのサブメニューとまったく同じ方法で、メニュー項目をコンテキストメニューに追加し、イベントと関連付けることができます。 [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

コンテキストメニューまたはコンテキストメニュー項目をユーザー記述コード内でダイナミックに作成すると、コンテキストメニューまたはコンテキストメニュー項目がダイアログエディタで表示されないことに注意してください。 例えば、ダイナミックに作成したメニューは、コンテキストメニューリストボックスに表示されません。また、ダイナミックに作成されたメニュー項目はコンテキストメニューエディタに表示されません。

Top of page

アソシエーション

コンテキストメニューの作成後に、コンテキストメニューを 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

Top of page

起動

Natural のコンテキストメニュー起動処理は、次の手順で実行されます。

  1. マウスを使用してコンテキストメニューにアクセスした場合(つまり、マウスの第 2 ボタンをクリックした場合)、ターゲットコントロールはマウスカーソルのすぐ下にあるコントロールであるとみなされます。 そうでない場合、すなわち、キーボードによってコンテキストメニューにアクセスした場合(つまり、コンテキストメニューキーがあればそのキー、またはキーの組み合わせの Shift + F10 を押した場合)、ターゲットコントロールはその時点でキーボードのフォーカスが当たっているコントロールだとみなされます。

  2. コントロールのクリック位置は、ターゲットコントロールのクライアントエリアに対する相対位置で設定されます。 キーボードからコンテキストメニューにアクセスした場合、クリック位置は (0, 0) に設定されます。

  3. イベントが抑制されていない場合、SUPPRESS-CONTEXT-MENU-EVENT 属性に基づいて、ターゲットコントロールに対する CONTEXT-MENU イベントが発生します。

  4. ターゲットコントロールの CONTEXT-MENU 属性がクエリされます。 ターゲットコントロールの値とタイプによって、以下のアクションが実行されます。

  5. イベントが抑制されていない場合、ターゲットコンテキストメニューに対する BEFORE-OPEN イベントが発生します。

  6. ターゲットコンテキストメニューの ENABLED 属性がクエリされます。 ENABLED 属性が FALSE に設定されている場合、コンテキストメニューは表示されません。

  7. そうでない場合、抑制されていなければ、ターゲットダイアログに対する COMMAND-STATUS イベントが発生します。 ターゲットダイアログとは、ターゲットコントロールがダイアログエレメントの場合はそのコントロールが組み込まれているダイアログを指し、ターゲットコントロールがダイアログの場合はターゲットコントロール自体を指します。

  8. コンテキストメニューは、上記の手順 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 呼び出しを使用して判断できます。

この 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 が導入されています。

COMMAND-STATUS イベントは、アプリケーションでコマンド(つまり、メニュー項目、ツールバー項目、およびシグナル)の無効化やチェックを実行するためのもう 1 つの場所です。 すでにこのイベントを使用している場合、これらのアクションを BEFORE-OPEN イベントで実行する必要はありません。

Top of page

手動による起動

前述のコンテキストメニューの自動起動処理の他に、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

#CONTROLHANDLE OF GUI として定義され、#X および #Y はフォーマット I4 を想定しています。

1440 という値は、論理インチごとの twip 数です。一方、ダイアログエレメントに適用されている DPI 属性は論理インチごとのピクセル数を返します。

Top of page

コンテキストメニューの共有

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

Top of page