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

ツリービューコントロールの操作

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


はじめに

ツリービューコントロールは、階層形式でデータを表示するために使用します。 階層内の各ノードは、ツリービュー項目として内部的に表されます。 以下の図は、7 つのツリービュー項目を持つ 3 つの階層を表示する、簡単なツリービュー(任意のチェックボックス付き)です。

上記のツリービューコントロールには、[+/- ボタン(b)]、[行(l)]、[ルート行(r)]、および[チェックボックス(c)]の各 STYLE フラグが設定されています。

階層間の各項目の高さとインデントは、それぞれ ITEM-H 属性および SPACING 属性で設定できます。 これらのどちらかがゼロの場合、デフォルト設定が使用されます。

Top of page

項目イメージの設定

ツリービュー項目のイメージは、「イメージリストコントロールの操作」で説明しているように、ツリービューコントロールにイメージリストコントロールを作成および関連付け、(項目ごとに)必要なイメージを、インデックスおよび/またはイメージハンドルを使用してイメージリストから選択することによって定義できます。

ツリービューコントロールでは小さいイメージしか使用されないため、リストビューコントロールと同じイメージリストを使用していない限り、イメージリストコントロールの[大きいイメージ(L)]スタイルを設定する必要はないことに注意してください。

Top of page

項目の選択

リストビューコントロールとは異なり、ツリービューコントロールで選択できる項目は 1 つのみです。 現在の選択項目(存在する場合)は、ツリービューコントロールの(読み取り専用の)SELECTED-SUCCESSOR 属性をクエリすることによって取得できます。

また、ユーザーによる選択の他に、プログラムで項目の SELECTED 属性を設定したりクリアしたりすることにより、項目を選択したり選択を解除したりできます。

項目が選択されると、ツリービューコントロールの CLICK イベントが発生します(抑制されていない場合)。このとき、対応する項目のハンドルがコントロールの ITEM 属性に設定されます。

Top of page

項目の有効化

項目をダブルクリックすると、ツリービューコントロールの ACTIVATE イベントが発生します(抑制されていない場合)。 このイベントをアプリケーションで制御する場合、通常は選択された項目上でユーザー定義のデフォルトのアクションを実行します。この項目のハンドルは、ツリービューコントロールの ITEM 属性または SELECTED-SUCCESSOR 属性で取得できます。 選択された項目に対し、デフォルトでない他のアクションも使用できることに注意してください。ただし、これらのアクションは通常、他のメカニズムによってアクセスされます。 例えば、これらはアプリケーションが表示するコンテキストメニューに(通常、デフォルトのアクションと一緒に)リストされます。

[ダブルクリック展開(d)]STYLE フラグが設定されている場合、子項目を持つツリービュー項目をダブルクリックすると、子項目が展開されアクティブ化されます。

また、ACTIVATE イベントは、キーボードを使用してトリガすることもできます。 このためには、次の 2 つの方法のいずれかを使用します。

  1. ツリービューコントロールの ACCELERATOR 属性に定義されているキーまたはキーの組み合わせを押す。 現時点で、コントロールにフォーカスがなくてもかまいません。

  2. ツリービューコントロールにフォーカスがある場合、Enter キーを押す。 この方法は、ダイアログにデフォルトのプッシュボタンが含まれておらず、プッシュボタンに[OK ボタン (O)]の STYLE フラグが設定されていない場合にのみ機能します。

どちらの場合も、項目が選択されていないと ACTIVATE イベントは発生しません。

Top of page

項目データ

適切にソートできるようにするために(次のセクションを参照)ツリービュー項目のデータを英数字にする必要はありません。デフォルトは英数字ですが、項目の FORMAT 属性でサポートされている定義済みのタイプを使用できます。 また、項目の EDIT-MASK 属性を設定することによって、項目に編集マスクを適用できます。 ユーザーに表示される項目のラベルは、関連付けられている編集マスク(存在する場合)が適用された、項目の内部データの英数字表記になります。 編集マスクが使用されている場合、項目の内部データと表示データ間の変換は、MOVE EDITED ステートメントと互換性があります。 それ以外の場合、内部データと表示されたデータ間およびその逆の変換は、データを Natural スタックにコピーしたり、Natural スタックからコピーする場合に発生する変換と互換性があります(STACK TOP DATA および INPUT ステートメントを参照)。 例えば、数値は、現在の小数点文字を使用して(DC パラメータで定義されたように)表示されます。必要に応じて、負の場合は先頭にマイナスを付けることができます。日付値は、DTFORM パラメータで定義されたフォーマットで表示されます。論理値は、真の場合は "X" として、偽の場合は空白として表示されます。

英文字以外のツリービュー項目の使用例を以下に示します。

PROCESS GUI ACTION ADD WITH
PARAMETERS
   HANDLE-VARIABLE = #TVITEM-DATE
   TYPE = TREEVIEWITEM
   PARENT = #TV-1
   STRING = '+123.456'
   FORMAT = FT-DECIMAL
   EDIT-MASK = '+ZZZ,ZZ9.999'
END-PARAMETERS GIVING *ERROR

この場合、指定した項目ラベル(STRING 属性値)は、指定した編集マスク(+ZZZ,ZZ9.999)と互換性のあるフォーマットの有効な数値である必要があります。 ラベルは内部的に、指定したフォーマット FT-DECIMAL(= P10.5)に対応するデータタイプと長さに変換されます。

ツリービュー項目の基となるデータには直接アクセスできないことに注意してください。 項目のデータは、項目のラベル経由でのみ設定または取得できます。

Top of page

ソート

ツリービュー項目は、SORT-ITEMS アクションを呼び出すことにより、ソートされた順序で挿入することも、挿入後に明示的にソートすることもできます。 ツリービューコントロールまたは挿入するツリービュー項目の SORTED 属性が TRUE に設定されている場合、項目はアルファベットの昇順で挿入されます。 挿入後にソートする場合、内部データに応じて、項目は任意で昇順または降順でソートされます(最後のセクションを参照)。 詳細については、SORT-ITEMS アクションのドキュメントを参照してください。 ソート対象の項目の基本となるフォーマット(FORMAT 属性で定義)を比較可能なタイプに設定するのは、プログラマの責任であることに注意してください。 例えば、整数値と浮動小数点値は一緒に使用できますが、整数値と日付値は一緒に使用できません。

Top of page

ラベル編集

ツリービューコントロールのラベル編集プロセスは、リストビューコントロールと同じです。 このため、この内容の詳細については、「ツリービューおよびリストビューコントロールでのラベル編集」を参照してください。

SORTED 属性が設定されている場合でも、ラベル編集の操作が完了した後は、項目の順序付けが自動的に再実行されることはありません。 これが必要な場合は、以下の例に従って操作してください。 まず、後で使用する変数をいくつか定義します。

01 #CONTROL HANDLE OF GUI 01 #ITEM HANDLE OF TREEVIEWITEM 
01 #SORTITEM HANDLE OF TREEVIEWITEM

また、ツリービューコントロールの名前は #TV-1 であるとします。

ツリービューコントロールの AFTER-EDIT イベントでは、(まだ完了していない)編集プロセスに影響があるため、非同期で順序付けを再実行できません。 代わりに、#SORTITEM 変数を設定し、編集プロセスの完了後に順序付けを再実行するよう指定します。 この処理は、ツリービュー項目をソートする場合にのみ実行する必要があります。

#CONTROL := *CONTROL #ITEM := #CONTROL.ITEM IF #CONTROL.SORTED OR #ITEM.SORTED 
#SORTITEM := #ITEM ELSE #SORTITEM := NULL-HANDLE END-IF

この例では、ツリービューコントロール自体が提供しているデフォルトのソート(アルファベットの昇順)を使用することが想定されていることに注意してください。

項目の順序付け再実行の実際の作業は、ダイアログの IDLE イベントで非同期的に行われます。

IF #SORTITEM <> NULL-HANDLE PROCESS GUI ACTION SORT-ITEMS WITH #SORTITEM.PARENT 
GIVING *ERROR RESET #SORTITEM END-IF

Top of page

マルチプルコンテキストメニュー

コントロールに単一のコンテキストメニューのみを使用する場合、表示するコンテキストメニューのハンドルをコントロールの CONTEXT-MENU 属性に設定し、そのままにしておきます。 ただし、ツリービューコントロールに複数のコンテキストメニューを表示する必要があることがよくありますが、このアプローチでは柔軟性が足りません。

上記の問題に対応するために、CONTEXT-MENU イベントが導入されました(上記の同じ名前の属性と混同しないように注意してください)。 このイベント(無効にされていない場合)は、CONTEXT-MENU 属性が評価される直前にターゲットのコントロールで発生します。これによって、アプリケーションは最初に、適切なコンテキストメニューのハンドルにこの属性をダイナミックに設定できます。

例として、ダイアログエディタで 2 つのコンテキストメニューを定義したとします。1 つは項目に関連するコマンドが組み込まれている #CTXMENU-ITEMS で、もう 1 つは汎用コマンド(表示モードの切り替えなど)が組み込まれている #CTXMENU-DEFAULT です。 この場合、次の CONTEXT-MENU イベントを使用することができます。

#CONTROL := *CONTROL IF #CONTROL.SELECTED-SUCCESSOR 
<> NULL-HANDLE #CONTROL.CONTEXT-MENU := #CTXMENU-ITEMS ELSE #CONTROL.CONTEXT-MENU 
:= #CTXMENU-DEFAULT END-IF

ローカルデータの定義は以下のように想定されています。

01 #CONTROL HANDLE OF GUI

この例では、項目が占めている位置をユーザーが右クリックすると、コンテキストメニュー #CTXMENU-ITEMS が表示されます。それ以外の場合は、#CTXMENU-DEFAULT が表示されます。

この手法をさらに活用して、選択された項目のタイプに固有のコンテキストメニューを表示することもできます。

Top of page

項目のダイナミックな作成

ツリービュー項目が展開または圧縮されると、イベントが抑制されていなければ、コントロールの EXPAND イベントまたは COLLAPSE イベントがそれぞれ発生します。 多数あるイベントの中のこれらのイベントにより、ツリービュー項目をオンデマンドでダイナミックに追加したり削除したりできます。

例えば、以下のコードは、EXPAND イベントに応答して 3 つの項目をダイナミックに作成する方法を示しています。 このコードでは、項目を入力する位置に、空の STRING 属性値を持つダミーのプレースホルダ項目がすでに配置されているものとしています。 プレースホルダは、ダイアログエディタでスタティックに定義することも、ツリービュー項目を最初に作成するときにダイナミックに定義することもできます。 プレースホルダの目的は、"+" ボタン([+/- ボタン(b)]STYLE によってボタン表示が有効に設定されている場合)が親ノードの隣に確実に表示されるようにすることです。

以下の EXPAND イベントコードでは、変数 #TGT-ITEM にツリービュー項目のハンドルが設定されているものとしています。このツリービュー項目の下に、ツリービュー項目がダイナミックに作成されます。

#CONTROL := *CONTROL
#ITEM := #CONTROL.ITEM
IF #ITEM = #TGT-ITEM
  #TVITEM-DYN := #ITEM.FIRST-CHILD
  IF #TVITEM-DYN <> NULL-HANDLE AND
     #TVITEM-DYN.STRING = ' '
    PROCESS GUI ACTION DELETE WITH #TVITEM-DYN GIVING *ERROR
    FOR #I 1 3
      COMPRESS 'Dynamic Item' #I INTO #A
      PROCESS GUI ACTION ADD WITH PARAMETERS
        HANDLE-VARIABLE = #TVITEM-DYN
        TYPE = TREEVIEWITEM
        PARENT = #ITEM
        STRING = #A
      END-PARAMETERS GIVING *ERROR
    END-FOR
  END-IF
END-IF

ローカルデータの定義は以下のように想定されています。

01 #CONTROL HANDLE OF GUI
01 #ITEM HANDLE OF TREEVIEWITEM
01 #TVITEM-DYN HANDLE OF TREEVIEWITEM
01 #TGT-ITEM HANDLE OF TREEVIEWITEM
01 #I (I4)
01 #A (A) DYNAMIC

上記のコードではまず、展開する項目がターゲット項目かどうかを確認します。 ターゲット項目の場合、最初の子の STRING 属性をクエリして、プレースホルダが存在するかどうかを確認します。 プレースホルダが存在する場合、そのプレースホルダは削除され、3 つのツリービュー項目がダイナミックに作成されます。 挿入される項目の STRING 属性値は、SORTED 属性が設定されたツリービューでもコードが正しく動作するように、後で変更するのではなく、作成時に設定します。

リソースを節約するために、ダイナミックに作成した項目を COLLAPSE イベントハンドラ内で削除して、別のプレースホルダ項目で置き換えることもできます。

#CONTROL := *CONTROL
#ITEM := #CONTROL.ITEM
IF #ITEM = #TGT-ITEM
  PROCESS GUI ACTION DELETE-CHILDREN WITH #ITEM GIVING *ERROR 
  PROCESS GUI ACTION ADD WITH PARAMETERS /* placeholder
    TYPE = TREEVIEWITEM
    PARENT = #ITEM
  END-PARAMETERS GIVING *ERROR
END-IF

Top of page

ドラッグ & ドロップ

ドラッグ & ドロップをサポートするための基本的な手法については、「クリップボードおよびドラッグ & ドロップの使用」に記載されています。

マウスカーソルの下にある項目(存在する場合)の SELECTED 属性を(必要に応じて)設定して強調表示し、後で元の選択状態(設定されていた場合)に戻すのは、プログラマの責任であることに注意してください。

以下の例は、ラベルを変更するための、別のアプリケーション(ワードパッドなど)からツリービュー項目へのテキストのドラッグ & ドロップをサポートする、ドロップターゲットとして動作するツリービューコントロールのコードを示しています。

まず最初に、ドロップモードをツリービューコントロールに適切に設定します。 ダイアログエディタの[ツリービューコントロール属性]ウィンドウで、[ドロップモード]選択ボックスを[コピー + 移動]に設定します。 これにより、ダイアログの生成ソースコード内で、コントロールの DROP-MODE 属性が DM-COPYMOVE に設定されます。

次に、以下で使用される必要なローカルの変数を定義する必要があります。

01 #CONTROL HANDLE OF GUI
01 #DROP-ITEM HANDLE OF GUI
01 #ITEM HANDLE OF GUI
01 #SELECTED HANDLE OF GUI
01 #AVAIL (L)
01 #X (I4)
01 #Y (I4)

これを行った後、必要なイベントハンドラを記述することができます。 処理を開始する論理的な場所は、DRAG-ENTER イベントです。

#CONTROL := *CONTROL
#CONTROL.CLIENT-HANDLE := #CONTROL.SELECTED-SUCCESSOR
PROCESS GUI ACTION INQ-FORMAT-AVAILABLE WITH CF-TEXT #AVAIL GIVING *ERROR
IF #AVAIL
  #CONTROL.SUPPRESS-DRAG-DROP-EVENT := NOT-SUPPRESSED
ELSE
  #CONTROL.SUPPRESS-DRAG-DROP-EVENT := SUPPRESSED
END-IF

上記のコードではまず、後で項目の選択状態を元に戻すために、現在選択されている項目のハンドルをコントロールの CLIENT-HANDLE 属性に保存します。 その後、イベントハンドラはドラッグドロップクリップボードでテキストを使用できるかどうかを確認します。 使用できる場合、ドロップを可能にするために DRAG-DROP イベントの抑制が解除されます。 それ以外の場合、このイベントを無効にしてドロップを禁止し、ドロップアンドドラッグカーソルが表示され"ない"ようにします。

外部データをツリービューコントロール上にドラッグしているときにドロップの強調表示を行うには、DRAG-OVER イベントハンドラを使用します。

#CONTROL := *CONTROL
IF #CONTROL.SUPPRESS-DRAG-DROP-EVENT = NOT-SUPPRESSED
  PROCESS GUI ACTION INQ-DRAG-DROP WITH
    3X #X #Y GIVING *ERROR
  PROCESS GUI ACTION INQ-ITEM-BY-POSITION WITH
    #CONTROL #X #Y #ITEM GIVING *ERROR
  IF #ITEM <> #DROP-ITEM
    #DROP-ITEM := #ITEM
    IF #DROP-ITEM <> NULL-HANDLE
      #DROP-ITEM.SELECTED := TRUE
    END-IF
  END-IF
END-IF

上述したコードにより、次の処理が実行されます。

  1. DRAG-ENTER イベントでドロップが許可されていない場合、ドロップの強調表示は不要なため、このイベントは無視されます。

  2. ドロップが許可されている場合、INQ-DRAG-DROP アクションを呼び出して現在のドロップ位置を把握します。

  3. INQ-ITEM-BY-POSITION アクションを使用して、現在のドロップ位置にあるツリービュー項目(存在する場合)を把握します。 この項目を #DROP-ITEM に保存します。

  4. カーソルの下にあるツリービュー項目(存在する場合)の SELECTED 属性を TRUE に設定して、この項目を選択し、ドロップの強調表示を行います。

実際のドロップを実行するために、DRAG-DROP イベントハンドラが提供されています。

IF #DROP-ITEM <> NULL-HANDLE
  PROCESS GUI ACTION GET-CLIPBOARD-DATA WITH CF-TEXT #DROP-ITEM.STRING
    GIVING *ERROR
END-IF
#CONTROL := CONTROL  /* "parameter" for subroutine below
PERFORM RESET-SELECTION

ドロップターゲットのツリービュー項目が存在する場合、上記のコードはドラッグドロップクリップボードからテキストを取得して項目のキャプションに直接設定します。 その後、ツリービューコントロールの選択状態を元に戻します。 このためには、以下に記述するコードのように、RESET-SELECTION サブルーチンを使用します。

#ITEM := #CONTROL.CLIENT-HANDLE
IF #ITEM <> NULL-HANDLE
  /* Restore original selection:
  #ITEM.SELECTED := TRUE
ELSE
  /* Nothing was originally selected,
  /* so clear any existing selection:
  #ITEM := #CONTROL.SELECTED-SUCCESSOR
  #ITEM.SELECTED := FALSE
END-IF
RESET #DROP-ITEM

他のイベントハンドラのロジックと合わせるために、#DROP-ITEM もリセットして、ドロップ項目がないことを示します。

最後に、ユーザーがドラッグ操作をキャンセルした場合、またはドロップを実行せずにツリービューコントロールの境界線の外に出た場合は、DRAG-LEAVE イベントハンドラを使用します。

#CONTROL := CONTROL  /* "parameter" for subroutine below
PERFORM RESET-SELECTION

上記のコードは、ドロップの強調表示(行われている場合)をクリアして元の選択状態に戻すために、前述のインラインサブルーチンを呼び出しているだけです。

Top of page