Tipswip   >   Windows   >   Treelists
TreeList: a hierachical list with expandable and collapsable nodes.
TreeLists are among the last hold outs in my Omnis Studio learning experience. I remember attending a class on TreeLists at my first Omnis conference. After that session on TreeLists my thoughts were "TreeLists are too much work, I'll stick with headed lists, they are a lot easier".
TreeLists are more work for the programmer, but in the right situation they are a great GUI.
Imagine trying to navigate StudioTips without TreeLists! For presenting lots of information in a format that allows the user to explore the layers on their own, TreeLists are a great GUI.
This section of StudioTips covers all the TreeList methods and demonstrates what each one does.
Some of the methods, such as $countall, pertain the TreeList only. Other methods, such as $nodeparent, pertain the tree node only. Most methods, pertain to both the TreeList or a tree node.
The distinction between "Treelist methods" and "Treelist Node Methods" in the F1 Help is fuzzy at times. (It took me quite a while to figure out the correct syntax for $addbefore and $addafter.)
In this section if a method I try to make the distinction between treelist methods and treelist "node" methods. Methods pertaining only to the "kTreeColList" are appended with "_kTreeColList".
The naming conventions used in this section are as follows:
irTree ;; Item reference to the TreeList object. (instance variable)
irNode ;; Item reference to a node in the TreeList. (instance variable)
rNode ;; Item reference to a node in the TreeList. (local variable)
You do not need to issue a "redraw" after using a TreeList method. Each TreeList method automatically redraws the TreeList window object. (where applicable)
TREELIST DEMOS
For most of the demos, you can play around clicking on different nodes in the TreeList and then clicking the pushbutton to test the method on different nodes.
Holding down the shift key while clicking the TreeList method button in the demo window will take you to a breakpoint in the method code allowing you to step through the actual code so you can see what is happening behind the scenes.$add(cName[,iIdent,lRowForColumns])
Adds a node with the specified iIdent, and returns a tree node item reference to the new node. If the TreeList has multiple columns, you can supply the data for columns 2 and greater in the row variable lRowForColumns.
Add a new root level node to the TreeList.
Do irTree.$add(NodeName) Returns rNode
Do irTree.$add(NodeName,NodeIdent)
Do irTree.$add(NodeName,NodeIdent,ColumnsRow)
Add a new child node to "irNode". If any child node(s) already existing the new child node will be added to the end.
Do irNode.$add(NodeName)$addafter(rItem,cName[,iIdent,lRowForColumns])
Adds a node with the specified iIdent, after the node rItem, and returns a tree node item reference to the new node.
Add a new root level node after "irNode".
"irNode" can be a child node, but the new node will be added at the root level.
Do irTree.$addafter(irNode,NodeName) Returns rNewNode
Do irTree.$addafter(irNode,NodeName,NodeIdent) Returns rNewNode
Do irTree.$addafter(irNode,NodeName,NodeIdent,ColumnsRow) Returns rNewNode
Add a new child node to the parent "irNode" after the existing child node "rChildNode".
Do irNode.$addafter(rChildNode,NodeName) Returns rNewChildNode$addbefore(rItem,cName[,iIdent,lRowForColumns])
Adds a node with the specified iIdent, before the node rItem, and returns a tree node item reference to the new node.
Add a new root level node before "irNode".
"irNode" can be a child node, but new node will be added at the root level.
Do irTree.$addbefore(irNode,NodeName) Returns rNewNode
Do irTree.$addbefore(irNode,NodeName,NodeIdent) Returns rNewNode
Do irTree.$addbefore(irNode,NodeName,NodeIdent,ColumnsRow) Returns rNewNode
Add a new child node to the parent "irNode" after the existing child node "rChildNode"
Do irNode.$addbefore(rChildNode,NodeName) Returns rNewChildNode$clearallnodes()
Clears all nodes in the entire tree.
Do irTree.$clearallnodes()
Clears all nodes beneath the current node.
Do irNode.$clearallnodes()$collapse()
Closes all nodes in the entire TreeList.
Do irTree.$collapse()
Collapse "irNode".
Do irNode.$collapse()$count()
Return the number of root nodes in the tree
Do irTree.$count() Returns RootNodeNum
Return the number of nodes immediately below the current node
Do irNode.$count() Returns NodeNum$countall()
Returns the total number of nodes in the tree.
Do irTree.$countall() Returns NodeNum$currentnode()
Returns a tree node item reference to the currently selected node in the tree.
Do irTree.$currentnode() Returns irNode$edittext(iColumnNumber)
Lets the user edit the text for a column in the currently selected line, when the tree is the current field.
Do irTree.$edittext(ColNum)
The node (column 1) can only be entered if its $enterable property is kTrue. You can control whether other columns can be entered using the evTreeListEditStarting event.
Note: $edittext is only applicable the "kTreeColList" type TreeList. (I think?)
To stop columns from being enterable add this behind the tree:
-> On evTreeListEditStarting
-> Quit event handler (Discard event)
$edittext had me stumped for a while. The following is a question I asked to the list server and the kind response I received from David Barnett.
DOUG'S QUESTION:
I am currently working a TreeList section and demos for the next release of StudioTips. I'm stuck on the $edittext method.
According to the F1 help:
EDIT TEXT
$edittext(iColumnNumber) lets the user edit the text for a column in the currently selected line, when the tree is the current field. The node (column 1) can only be entered if its $enterable property is kTrue. You can control whether other columns can be entered using the evTreeListEditStarting event.
1. I don't fully understand the F1 Help explanation. Can anyone expand on this for me to make it a
bit clearer?
2. I can't find the $enterable property for the kTreeList window object. What tab is it hiding under, or do I have to set it some other way?
DAVID BARNETT'S RESPONSE:
If I remember correctly, controlling editing of Node Names is different than controlling editing of the other columns of a multi-column tree widget. Now I call it that because a tree object is NOT A LIST in any way that we normally think of an Omnis list or a window object which presents a list (like a complex grid).
The editing of Node names is not something you control in the Properties manager. It's a flag you set in the FlatList style list which you 'turn into' a tree (or add to a node). The flags in the flat list let you turn editing of node names on/off on a node by node basis.
As far as I could see, there's no property to 'turn off' the editing of the other tree columns.
All you can do is intercept the event which says you're starting to edit a tree column (not the Node name) and discard the event.
So, did I make that clearer or more obtuse?
David Barnett
Thanks David for clearing things up. :-)
So ... if you want to be able to enter data for any nodes in the TreeList, you must $setnodelist using kFlatList (or kTreeColList). In the column for $enterable, you set the node to kTrue in the list.
Run the $getnodelist demos to see the $enterable column in the data grid list views. See the Omnis Programming Manual page 155-156 for more information.$expand()
Open all nodes in the entire TreeList.
Do irTree.$expand()
Expand the current node.
Do irNode.$expand()$findident(iIdent)
Returns a tree node item reference to the node specified by iIdent
Do irTree.$findident(NodeIdent) Returns irNode
NOTE: I suspect $findident ONLY works for root level nodes.
DEMO: There is no demo for this TreeList method because I did not assign node idents to the TreeList when I built it.$findname(cName)
Returns a tree node item reference to the node specified by cName.
Do irTree.$findname(NodeName) Returns irNode
NOTE: $findname ONLY works for root level nodes.$findnodeident(rNodeRef,iIdent[,bRecursive=kFalse])
Searches the tree for a node which has the ident iIdent. If it finds such a node, it returns a tree node item reference to the node. Otherwise, it returns NULL. You can use the isnull function to test the returned item reference.
If you pass a tree node item reference in rNodeRef, then the method searches the nodes beneath the referenced node. To search the root nodes, pass zero as the rNodeRef parameter.
If you pass bRecursive as kTrue, then the search is recursive, searching the children of the nodes identified by rNodeRef, and so on.
Do irTree.$findnodename(0,NodeIdent) Returns rNode ;; Search the root nodes
Do irTree.$findnodename(irNode,NodeIdent) Returns rNode ;; Search the nodes below irNode
Search the root nodes. Recursive (search each branch).
Do irTree.$findnodename(0,NodeIdent,kTrue) Returns rNode ;; Search the root nodes
$findnodename(rNodeRef,cName[,bRecursive=kFalse,bIgnoreCase=kTrue])
Searches the tree for a node which has the name cName. If it finds such a node, it returns a tree node item reference to the node. Otherwise, it returns NULL. You can use the isnull function to test the returned item reference.
If you pass a tree node item reference in rNodeRef, then the method searches the nodes beneath the referenced node. To search the root nodes, pass zero as the rNodeRef parameter.
If you pass bRecursive as kTrue, then the search is recursive, searching the children of the nodes identified by rNodeRef, and so on.
The Boolean bIgnoreCase determines whether the search is case-sensitive.
Do irTree.$findnodename(0,NodeName) Returns rNode ;; Search the root nodes
Do irTree.$findnodename(irNode,NodeName) Returns rNode ;; Search the nodes below irNode
$first()
Return a tree node item reference to the first root node.
Do irTree.$first() Returns rNode
Return a tree node item reference to the first node immediately below the current node.
Do irNode.$first() Returns rNode$getcolumnalign()
Returns the alignment of the specified column. This only applies to trees with multiple columns.
Note: I believe "multiple columns" means only the "kTreeColList" type TreeList. See $setnodelist.
Do irTree.$getcolumnalign(ColNum) Returns kConstantAlign$getnodelist(iListmode,rNodeRef,lListname)
Returns the list data under the specified node or for the entire tree; iListmode can be kRelationalList, kFlatList or kTreeColList; rNoderef can be a tree node item reference or zero to retrieve the entire tree, lListname is the list variable to receive the list data.
Do irTree.$getnodelist(kRelationalList,irNode,List)
Do irTree.$getnodelist(kFlatList,irNode,List)
Do irTree.$getnodelist(kTreeColList,irNode,List)
66$getvisibleline(rItem)
Returns the line number of the node identified by the tree node item reference rItem, if the node is visible. If the node is not visible, it returns zero.
Do irTree.$getvisibleline(irNode) Returns LineNum$getvisiblenode(iVisLine)
Returns a tree node item reference to the node for the visible line iVisLine.
Do irTree.$getvisiblenode(3) Returns irNodeirNode().$level
$level is a node property which tells the indent level of the referenced node. See Node Properties for other properties.
Calculate NodeLevel as irNode().$level$multipleselect
If the $multipleselect property has been set to kTrue, the $multipleselect property of a selected node will be kTrue.
Setting the treelist property $multipleselect property to kTrue allows the user to select multiple nodes in the treelist.
The problem I ran into was trying to figure out which nodes the user has selected in a treelist. In a drag and drop operation the pDragValue give you a reference to the treelist...not much help in telling you which nodes are selected. I looked for a $selected node property but none existed. (At least not in v3.3) I turned to tech support who pointed me to the (undocumented) $multipleselect node property which returns kTrue if the node is selected.
The solution is to lobject-oriented programming through the visible nodes in the treelist and check their $multipleselect property. Any selected nodes will return kTrue.$nextnode(rItem,bRecursive)
Returns the node in the tree after the node identified by tree node
item reference rItem, or the first root node if rItem is zero; if bRecursive is kTrue the method
steps into any child nodes.
$nodeparent()
Returns a tree node item reference to the parent node of the current node. If the return value is empty, then the current node is a root node.
Do irNode.$nodeparent() Returns rNodeParent$prevnode(rItem,bRecursive)
Returns the node in the tree before the node identified by tree node item reference rItem; if bRecursive is kTrue, the method steps back into node parents.
Do irTree.$prevnode(irNode) Returns rNode$remove()
Delete the child node identified by tree node item reference rItem.
Do irTree.$remove(irNode)
Delete the child node identified by tree node item reference rItem. rItem must be a child of the current node.
Do irNode.$remove(rNode)Do irNode.$rowdata.$assign(Row)
You can assign the row variable value to the column using the TreeList node method $rowdata. Getting the data from the row is a bit tricky since you can only use $rowdata to assign the values.$setcolumnalign(iColumnNumber,Alignment)
Sets the alignment of the specified column. You can specify Alignment as kLeftJst, kRightJst, or kCenterJst. The property $columnalignmode controls how $setcolumnalign affects the field. This only applies to trees with multiple columns.
Note: I believe "multiple columns" means only the "kTreeColList" type TreeList. See $setnodelist.
Do irTree.$setcolumnalign(ColNum,kConstantAlign)$setcurrentnode(rItem)
Sets the currently selected node to the node identified by tree node item reference rItem. Note that this will only work for visible nodes, and that it will generate a click event on the node after making it current.
Do irTree.$setcurrentnode(irNode)
Do irTree.$setcurrentnode() ;; Leave blank to unselect node.$setnodelist(iListmode,rNodeRef,lListname)
Lets you populate a specified node or the entire tree with the data in lListname; iListmode can be kRelationalList, kFlatList or kTreeColList; rNoderef can be a tree node item reference or zero to populate the entire tree.
Do irTree.$setnodelist(kRelationalList,irNode,List)
Do irTree.$setnodelist(kFlatList,irNode,List)
Do irTree.$setnodelist(kTreeColList,irNode,List)
Do irTree.$setnodelist(kRelationalList,0,List) ;; Set the entire TreeList.
Do rNode.$tag.$assign(ANYTHING)
Omnis introduced a $tag treelist node property which can be assigned by the developer. The $tag property is a binary, so you can assign any datatype to the $tag node property.
I have found the $tag node property great for building treelists on the fly. For example if I build a treelist of all the classes in a library, as the list is built, I assign a class item reference to each node's $tag property. When the user clicks to expand a class node, I simply use $tag property to get the item reference to the class. With the class reference in hand, a list of methods for that class can be very quickly built and the method name nodes added. As each method node is added, the method item reference is assigned to the node's $tag property.
Use your imagination. The $tag property can help with building treelists on the fly and cut a lot of code.Omnis added the ability to display columnar list type data with the TreeList object.
The "kTreeColList" list is the same as the "kFlatList" list with the exception of an extra column. The extra column is a row variable and is the 5th last column in the kTreeColList. You can have as many columns in the row as you like.
See "$rowdata_kTreeColList" for more information.
COLUMN TREELIST DEMO
The column TreeList "Run Demo" with this tip was provided by Tech Support. I just modified some of the naming conventions and added the Get Row Data code to it. Feel free to look behind the TreeList at the kTreeColList code.Each node has a number of properties which are helpful when writing TreeList methods. Some can be assigned, others are read ony.
irNode().$PROPERTY
$name ;; Visible name of the node
$level ;; Indent level of the node
$nodeparent ;; Reference to the node's parent
$first ;; Reference to the nodes first child node
$iconid ;; Icon ID for the node.
$ident ;; Ident for the node.
$seedid ;; Unique number assigned to each node by the tree
$textcolor ;; color the node name is drawn in, if kColorDefault, the tree control's $textcolor is used.
$enterable ;; if true, the node name can be edited in the tree.
$isexpanded ;; true for nodes which are in the expanded state.
$checked ;; if true node icon is drawn in the checked state. Also depends on $treenodeiconmode.
$showexpandalways ;; if true, the node will always draw an expand/collapse box
;; useful for populating a node on evTreeExpand event
$rowdata ;; can be used to assign the row variable for the kTreeColList type TreeList
$multipleselect ;; if the $multipleselect property has been set to kTrue, the $multipleselect property of a selected node will be true.
Calculate NodeLevel as irNode().$PROPERTYYou can populate the TreeList from a list variable using the $setnodelist method. You can either populate the entire TreeList, or just a node in the TreeList.
The 3 ways of populating a treelist from a list are:
kRelationalList ;; Simplest form.
kFlatList ;; Lets you specify the node properties.
kTreeColList ;; Lets you specify the node properties and display column(s).
See the Omnis Programming manual page 155-156 for more details.
See $setnodelist and $getnodelist in the TreeList methods below for more details.
POPULATING ON-THE-FLY
After working for a while with populating treelists from lists I switched to building treelists on-the-fly. Typically I just build the root level nodes with the expand node icons. When the clicks to expand a node I trap the evTreeExpand and using pNodeItem find out which node is being expanded, clear the child nodes and add the child nodes one at a time in a For lobject-oriented programming. As each node is added you use the node reference returned to set the node properties. Populating treelists on the fly is fast, flexible, and in my opinion less work than populating them from lists.In StudioTips I needed to be able to store and restore the exact "state" of each TreeList which you look at in the StudioTips Browser. That way if you close the Browser window or even close the TIPS library, when you reopen the window all the TreeLists will be reopened in the same "state" that you last looked at them.
Tech Support gave me the solution. You simply use the parameter #NULL in the $getnodelist and $setnodelist methods.
The code I use is as follows:There are a number of events related to TreeLists. You can trap them in the $event method of the TreeList.
evTreeCollapse ;; indicates a node is about to be collapsed and provides the reference in "pNodeItem"
evTreeExpand ;; indicates a node is about to be expanded and provides the reference in "pNodeItem"
evTreeExpandCollapseFinished
evTreeNodeIconClicked ;; indicates a node has been clicked and provides the reference in "pNodeItem".
evTreeListEditStarting ;; pColumnNumber,pNodeItem
evTreeListEditFinishing ;; pColumnNumber,pNodeItem,pNewText
evTreeListEditFinished ;; pColumnNumber,pNodeItem
evTreeNodeNameFinished ;; pNodeItem,pNewText
evTreeNodeNameFinishing ;; pNodeItem,pNewText