gTreeAddNodes
| Definition: |
include xpGUI.e
gTreeAddNodes(object treenode, sequence tree_nodes) |
||||||||||||||||
| Description: |
Populates a tree from a nested sequence of branches and leaves.
treenode: As provided to the BRANCHOPEN handler, or a gdx from gTreeView(). tree_nodes: a nested collection of nodes (see examples below). If a plain gdx is specified as the treenode, the entire treeview contents are replaced, whereas the more opaque values fed through from BRANCHOPEN permit deferrred loading of sub-trees. In both cases any previous children of that [sub-]tree are implicitly deleted. This routine does not return a value. It will rudely terminate in error should tree_nodes not conform to the required format, or unique userids cannot be found, etc. |
||||||||||||||||
| pwa/p2js: | Supported. | ||||||||||||||||
| See Also: | gSetHandler, gTreeView | ||||||||||||||||
| Example: |
-- demo\xpGUI\gTreeView2.exw
include xpGUI.e
constant tree_nodes = {"Figures", -- (branch with 5 children)
{"Other", -- (leaf)
{"triangle", -- (branch with 3 children)
{"STATE","COLLAPSED"},
{"equilateral",
{"isoceles", -- (branch with 2 children)
{"STATE","COLLAPSED","USERDATA",3},
{}}, -- ([we don’t get a ’+’])
"scalenus",
}},
{"triangle2", -- (branch with 3 children)
{"STATE","COLLAPSED","USERDATA",1},
{"dummy"}}, -- (ensures we get a ’+’)
{"parallelogram", -- (branch with 2 children)
{"square",
"diamond"
}},
"2D", -- (leaf)
{"3D",
{"STATE","COLLAPSED"},
{}}, -- (branch, empty)
{"4D",
{"STATE","COLLAPSED","USERDATA",2},
{"dummy"}} -- (branch, deferred load)
}
}
sequence tree_data = {{"equilateral", -- 1 (for Figures/triangle2)
{"isoceles", -- (branch with 2 children)
{"STATE","COLLAPSED","USERDATA",4}, -- see tree_data[4]
{"dummy"}}, -- (ensures we get a ’+’)
"scalenus"},
{"some", -- 2 (for Figures/4D)
"other",
"branch"},
{}, -- 3 (kills + w/o opening, for Figures/triangle/isoceles)
{"acute", -- 4 (for Figures/triangle2/isoceles [a nested deferral])
"obtuse"}
}
procedure branchopen(object treenode)
integer userid = gTreeGetUserId(treenode)
if userid!=0
and tree_data[userid]!=0 then -- (deferred load (still) rqd for this node)
sequence children = tree_data[userid]
tree_data[userid] = 0
gTreeAddNodes(treenode, children)
end if
end procedure
gdx tree_view = gTreeView(tree_nodes,branchopen),
dlg = gDialog(tree_view,"Tree View2",`SIZE=350x300`)
gShow(dlg)
gMainLoop()
See also gTreeView for a simpler example without deferred loading. |
||||||||||||||||
| Notes: |
A tree is represented by a nested collection of nodes (see example below). A node is a string (leaf node) or a sequence of length 2 or 3:
The rules to remember are: if you have attributes on a leaf node, it must have a third element of 0, and: if a branch has no children it must still have a {} to represent that. There cannot be any (ignored) application-specifc data on shorthand forms, ie 4 out of 6 entries in the table below. Should you see leaf nodes such as "STATE" and "COLLAPSED", or similar attribute names/values, suspect a missing 0 or {}. The following table lists the permitted nodes. Note that the entire tree is always represented by a single node, albeit an arbitrarily nested one.
DEV The next paragraph in particular has not yet been properly updated... (attributes on treenodes in xpGUI are pretty low down on my priorities list right now). Every odd element in any even-length set of attributes should be an attribute that accepts an id, ie one from the Nodes or Images section of the Attributes table as shown in the documentation for IupFlatTree. The even elements are strings, except for "USERDATA" which requires an integer, intended to be an index into some application-specific table. In practice, a node can also be a sequence of length 1, which represents a leaf node in the same way that a plain string does. The fact that a leaf node with no attributes can be represented in three different ways is a simple consequence of making the recursive structure easier to use, rather than a deliberate design decision to complicate matters. While concrete examples of the node structure might be a little eye-watering to look at, they should however be reasonably straightforward to (recursively) construct from almost any data, and of course it makes no odds should there be say {"string",{},{}} where a plain "string" would suffice. Performance note: For large trees it would normally be prudent to create all branches collapsed and empty, and populate them when the branch is expanded. If you have seen Windows Explorer take forever to load a single directory (such as the Recycle Bin), imagine what it would be like if it pre-loaded every directory on your entire system before displaying anything. Obvously you might also want to auto-expand the top-level node, and have that auto-populated, and perhaps repeat that down to some target. To defer load, you must specify {"STATE","COLLAPSED","USERDATA",4} where 4 is some unique userid for the branch, and create a "dummy" entry (to get a '+' shown), and lastly invoke gTreeAddNodes() inside the branchopen as above. Note that USERDATA is generally useful for other purposes, and would normally be allocated sequentially on that basis, hence the "plucked out of the air" aspect of that 4: should you need to defer-load three nodes, there is no requirement to use {1,2,3} over {38,5,17}, other than your branchopen code coping with the latter from gTreeGetUserId(). |
||||||||||||||||
| Additional: |
integer treeIdx = gTreeGetUserId(object treenode)
I trust that is self explanatory, at least after reading the branchopen() example code as shown above. A deeply nested tree_nodes as passed directly to gTreeView() on creation could be not only very slow (eg should it contain the full recursive contents of your root drive) but somewhat tricky to later navigate. In contrast, splitting the structure in two as shown above, with both the original tree_nodes and tree_data itself referencing deeper nested/deferred nodes via application-defined USERDATA indexes solves both issues. You could in theory use negative indexes to reference tree_nodes and positive ones to reference tree_data, and/or you might want to reserve special a value such as -1 or -9999 to mean "go and fetch it". Of course there should be no problem dynamically extending tree_data as needed, since that firmly belongs to the application and xpGUI only gets to see the bits of it the application deigns to show it, and it really won’t care should any of those get deleted or reused, that is after gTreeAddNodes() returns. You could equally store the contents of tree_data in a dictionary or database or anything else you can think of. The treenode value is backend-specific and should normally be considered opaque. |