This document covers the following topics:
A tree view control is used to display data in hierarchical form. Each node in the hierarchy is represented internally as a tree view item. The following shows a simple tree view (with optional check boxes) displaying a 3-level hierarchy containing seven tree view items:
The tree view control shown above is specified with the
"+/- buttons (b)", "Lines
(l)", "Lines at root (r)" and
"Check boxes (c)"
STYLE
flags.
The height of each item and the indentation between levels can be set
via the ITEM-H
and SPACING
attributes, respectively. If either of these are zero, the default setting is
used.
Images for the tree view items may be defined by creating and associating an image list control with the tree view control, then (for each item) selecting the required image from the image list via its index and/or image handle, as described in the section Working with Image List Controls.
Please note that it is not necessary to set the image list control's "Large images (L)" style, unless you are additionally using the same image list for a list view control, because the tree view control only uses small images.
Unlike the list view control, the tree view control only supports one
selected item. The current selection (if any) may be retrieved by querying the
tree view control's (read-only) SELECTED-SUCCESSOR
attribute.
In addition to being selected by the user, items may be selected or
deselected programmatically by setting or clearing their
SELECTED
attribute.
When an item is selected, a
CLICK
event is raised for the list view control (if not
suppressed), with the
handle of the corresponding item being set in the control's
ITEM
attribute.
When a user double-clicks on an item, an
ACTIVATE
event is raised (unless suppressed) for the tree view
control. The application, if it decides to handle this event, normally performs
a user-defined default action on the selected item, the handle to which may be
obtained via either the ITEM
or
SELECTED-SUCCESSOR
attributes of the tree view control. Note that there may be other, non-default,
actions applicable to the selected item, but these are typically accessed via
other mechanisms. For example, they may be listed (typically along with the
default action) in a context menu displayed by the application.
If the "Dbl. click expand (d)"
STYLE
flag
is set, double clicking a tree view item that has child items expands the item
in addition to activating it.
The ACTIVATE
event can also be triggered via the keyboard. This can be done in either of two
ways:
By pressing the key or key combination defined for the tree view
control's ACCELERATOR
attribute. The control need not currently have the focus.
By pressing the ENTER key, if the tree view control
currently has the focus. This method only works if the dialog neither contains
a default pushbutton, nor a
pushbutton with the "OK Button (O)"
STYLE
flag
set.
In either case, no ACTIVATE
event is raised if no item is currently selected.
In order to be able to support sorting correctly (see next section), a
tree view item's data does not need to be alphanumeric, as it is per default,
but can be one of any of the pre-defined types supported by the column's
FORMAT
attribute. In addition, an edit mask can be applied to the item by setting its
EDIT-MASK
attribute. The item's label, as seen by the user for the item, is the
alphanumeric representation of the item's internal data, using the associated
edit mask (if any). The conversion between the item's internal data and the
displayed data is compatible with the MOVE
EDITED
statement if an edit mask is supplied. Otherwise, the
conversion between the internal and displayed data, and vice-versa, is
compatible with the conversion involved in copying the data to and from the
Natural stack (see the STACK TOP
DATA
and INPUT
statements). For example,
numeric values are displayed using the current decimal character (as defined by
the DC
parameter) if necessary, with a leading minus character if negative, date
values are displayed in the format defined by the
DTFORM
parameter setting, logical values are displayed as an
"X" if true and as a blank if false, and so on.
An example of use of a non-alpha tree view item is shown below:
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
In this case, the specified item label (STRING
attribute value) must of course be a valid number of a form compatible with the
specified edit mask (+ZZZ,ZZ9.999). Internally, the label is converted to the
data type and length corresponding to the specified format, FT-DECIMAL (=
P10.5).
Note that it is not possible to access the underlying data for a tree view item directly. The item's data can only be set or retrieved via the item's label.
Tree view items can either be inserted in sorted sequence or explicitly
sorted after insertion, by calling the
SORT-ITEMS
action. Items are inserted in ascending alphabetical sequence if either the
tree view control or the tree view item being inserted have their
SORTED
attribute set to TRUE
. In the latter case, the items are
optionally sorted in ascending or descending sequence according to their
internal data (see last section). See the documentation for the
SORT-ITEMS
action for more information. Note that it is the responsibility of the
programmer to ensure that the underlying item formats of the items concerned
(as defined by the FORMAT
attribute) are of comparable types. For example, it is possible to mix integers
and floating point values, but not integers and dates.
The process of label editing for tree view controls is the same as for list view controls. Therefore, for more information on this subject, please refer to the section Label Editing in Tree View and List View Controls.
Note that even if the SORTED
attribute is set, the items are not automatically re-sequenced after a label
editing operation has been completed. If this is required, this can be done as
shown in the example below. Firstly, we define some variables for later
use:
01 #CONTROL HANDLE OF GUI 01 #ITEM HANDLE OF TREEVIEWITEM 01 #SORTITEM HANDLE OF TREEVIEWITEM
In addition, it is assumed that the tree view control is named
#TV-1
.
In the tree view control's
AFTER-EDIT
event, we cannot do the re-sequencing asynchronously, as this would interfere
with the (as yet incomplete) editing process. Instead, we simply set the
#SORTITEM
variable to indicate that the re-sequencing should occur
at a later time, after the editing process has been completed. This only needs
to be done if the tree view items are sorted:
#CONTROL := *CONTROL #ITEM := #CONTROL.ITEM IF #CONTROL.SORTED OR #ITEM.SORTED #SORTITEM := #ITEM ELSE #SORTITEM := NULL-HANDLE END-IF
Note that this examples assumes the use of the default (ascending alphabetical) sorting provided by the tree view control iteself.
The actual work of re-sequencing the items is done asynchronously in the
dialog's IDLE
event:
IF #SORTITEM <> NULL-HANDLE PROCESS GUI ACTION SORT-ITEMS WITH #SORTITEM.PARENT GIVING *ERROR RESET #SORTITEM END-IF
If you wish to support just a single context menu for the control, you
can simply set the control's CONTEXT-MENU
attribute to the handle of the context menu you wish to display, and leave it
set to this value. However, it is often required to be able to display more
than one context menu for list view controls, whereby this approach is too
inflexible.
To address the above problem, the
CONTEXT-MENU
event was introduced (not to be confused with the attribute of the
same name as mentioned above!). This event (if not suppressed) is raised for
the target control immediately before its
CONTEXT-MENU
attribute is evaluated, allowing the application to dynamically set this
attribute to the handle of the appropriate context menu first.
As an example, assume that we have defined two context menus in the
dialog editor: one containing item-related commands,
#CTXMENU-ITEMS
, and one containing generic commands (e.g., for
switching the view mode), #CTXMENU-DEFAULT
. In this case, the
following CONTEXT-MENU
event could be used:
#CONTROL := *CONTROL IF #CONTROL.SELECTED-SUCCESSOR <> NULL-HANDLE #CONTROL.CONTEXT-MENU := #CTXMENU-ITEMS ELSE #CONTROL.CONTEXT-MENU := #CTXMENU-DEFAULT END-IF
where the following local data definition is assumed:
01 #CONTROL HANDLE OF GUI
In this example, the context menu #CTXMENU-ITEMS
will be
displayed if the user right clicks at a position occupied by an item, or
#CTXMENU-DEFAULT
otherwise.
Of course, this technique can be refined further to display context menus specific to the type(s) of the selected item(s).
When a tree view item is expanded or collapsed, an
EXPAND
event or COLLAPSE
event (respectively) is raised for the control, if the event is not
suppressed. Amongst other things, these events allow tree-view items to be
dynamically created and deleted on demand.
For example, the following code demonstrates the dynamic creation of
three items in response to the
EXPAND
event. It assumes that a dummy placeholder item with an empty
STRING
attribute value is already in place at the position where the items should be
inserted. The placeholder can either have been statically defined in the dialog
editor, or dynamically-defined during the initial tree view item creation. The
purpose of the placeholder is to ensure that a "+"
button (if button display enabled via the "+/- buttons
(b)" STYLE
)
appears next to the parent node.
The following EXPAND
event
code assumes that the variable #TGT-ITEM
contains the handle of
the tree view item under which the dynamic tree view items are to be
created:
#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
where the following local data definitions are assumed:
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
The above code first looks to see whether the item being expanded is the
target item. If so, it queries the STRING
attribute of the first child, to find out whether a placeholder is present. If
this is the case, the placeholder is deleted, and three dynamic tree view items
are created. The STRING
attribute value for the inserted items is specified on creation, rather than
modified afterwards, to ensure that the code also works correctly for
SORTED
tree views.
To save resources, the dynamically-created items could optionally be
deleted again in the COLLAPSE
event handler, being replaced by a placeholder item:
#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
The basic technique for providing drag and drop support is described in the section Using the Clipboard and Drag and Drop.
Note that it is the responsibility of the programmer to (if required)
highlight the item (if any) under the mouse cursor by setting its
SELECTED
attribute, and to restore the original selection (if any) afterwards.
The following example provides some code for demonstrating a tree view control acting as a drop target, in order to support dragging and dropping text from another application (e.g. WordPad) onto a tree view item in order to change its label.
The first step is to ensure that the drop mode is set correctly for the
tree view control. In the Tree View Control
Attributes window in the dialog editor, set the Drop
mode selection box to Copy+Move. This
causes the control's DROP-MODE
attribute to be set to DM-COPYMOVE
in the generated source code
for the dialog.
Next, the required local variables that are going to be used below must be defined:
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)
Having done this, we can write the necessary event handlers. The logical
place to start is with the
DRAG-ENTER
event:
#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
The above code first saves the handle of the currently selected item in
the control's CLIENT-HANDLE
attribute for the
purposes of restoring the selection to this item later. The event handler then
checks whether text data is available on the drag-drop clipboard. If it is, the
DRAG-DROP
event is unsuppressed in order to allow a drop. Otherwise, we
suppress this event in order to prohibit a drop such that the "no
drop" drag cursor appears.
To provide drop emphasis during the dragging of external data across the
tree view control, a DRAG-OVER
event handler is supplied:
#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
The above code performs the following:
If a drop was disallowed in the
DRAG-ENTER
event, the event is ignored, as no drag emphasis is required in this
case.
Otherwise, the INQ-DRAG-DROP
action is called to determine the current drop position.
The INQ-ITEM-BY-POSITION
action is used to find the tree view item (if any) at the current drop
position. This item is tracked in #DROP-ITEM
.
The tree view item (if any) under the cursor is selected to provide
the drop emphasis by setting its SELECTED
attribute to TRUE
.
To perform the actual drop, a
DRAG-DROP
event handler is supplied:
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
If there is a tree view item representing the drop target, the above
code retrieves the text from the drag-drop clipboard directly into the item's
caption. Afterwards, the original selection state of the tree view control is
restored. The RESET-SELECTION
subroutine used for this purpose can
be written as follows:
#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
To satisfy the logic provided for the other event handlers,
#DROP-ITEM
is also reset, indicating the absence of a drop
item.
Lastly, in the case that the user cancels the drag operation, or exits
the bounds of the tree view control without having performed a drop, a
DRAG-LEAVE
event handler is supplied:
#CONTROL := CONTROL /* "parameter" for subroutine below PERFORM RESET-SELECTION
The above code simply invokes the inline subroutine listed above in order to clear the drop emphasis (if any), and to restore the original selection state.