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 itself.
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.