The Rhino TreeViewPane Component

Introduction

The Rhino TreeViewPane is a Component which displays a set of hierarchical data in an outline form. Multiple trees can be displayed, one at a time. The individual nodes in each tree are Items; each level in each tree contains Items within a Category or Association. The structure of the trees are specified almost entirely in Properties Files.

About the TreeViewPane

The TreeViewPane extends the JScrollPane class, and can thus be displayed within any Frame. The tree in the TreeViewPane is a JTree. Each node in the tree is associated with an Item in a particular Category or Association. Each node in the tree has an Icon and a name. The Icon can be a FtrIcon and thus can visually respond to changes in the state of its associated Item. It is possible to customize the display of the name of the Item by specifying an ItemNameRendererFormat.

The JTree in the TreeViewPane is also accessible so that one can take advantage of all of its capabilities (including listening for selections).

Creating a TreeViewPane

As with most Rhino UI classes, there are two basic steps in the creation of a TreeViewPane. First, one adds properties to the properties file which define the structure of the tree. Second, one writes the code which creates a new TreeViewPane which is defined by those properties. The correlation between the properties and the TreeViewPane is a name, a String, which is used as a prefix to the various property names. Pass this string to the TreeViewPane constructor as the prefix argument.

The TreeViewPane Properties

The various properties which define the structure of the tree are shown below. Default values, if any, are shown in parentheses.

General Appearance:

<prefix>.background
Specifies the color to be used as the background for the TreeViewPane (#99cccc).
<prefix>.width
Specifies the default width, in points, of the tree pane (160).
<prefix>.height
Specifies the default height, in points, of the tree pane (200).
<prefix>.toolTipText
Specifies the string to be displayed as the ToolTip text for each node in the tree.
<prefix>.textColor
Specifies the color to be used to display the name of the Item at each node of the tree (#0033cc).
<prefix>.selectColor
Specifies the color to be used as the background of the selected Item in the tree (#ffff66)
<prefix>.rootFont
Specifies the name of the font to be used to display the name of the item at the root node of the tree (SansSerif-bold-12).
<prefix>.childFont
Specifies the name of the font to be used to display the name of all items in the tree (except for the item at the root of the tree) (SansSerif-12).
<prefix>.cellBorderWidth
Specifies the height, in points, of the border around each item in the tree (2).
<prefix>.cellBorderHeight
Specifies the width, in points, of the border around each item in the tree (2).
<prefix>.iconWidth
Specifies the width, in points, of the icon to be displayed at each node of the tree (17).
<prefix>.iconHeight
Specifies the height, in points, of the icon to be displayed at each node of the tree (17).
<prefix>.iconBlinkOnTime
Specifies the time, in milliseconds, that a blinking icon will be visible before it blinks off again (750).
<prefix>.iconBlinkOffTime
Specifies the time, in milliseconds, that a blinking icon will not be visible before it blinks on again (750).
<prefix>.openedIcon
Specifies the package-qualified name of the icon to display when a node in the tree has children and those children are visible, that is, when the node is open (com.sgi.sysadm.ftr.OpenArrow).
<prefix>.closedIcon
Specifies the package-qualified name of the icon to display when a node in the tree has children and those children are not visible, that is, when the node is closed (com.sgi.sysadm.ftr.CloseArrow).

Tree Structure:

<prefix>.tree<n>
A string array that specifies the names of the trees to be displayed in the TreeViewPane. One tree can be displayed at a time.
<prefix>.<treename>.level<n>
The package-qualified name of the Category of Item at each level of the named tree. By default each level of the tree is actually an Association between the Item at the root of the particular subtree and the Category of its children. The Category at the first level of each tree must be the same, and must match the Category of the Item passed to the TreeViewPane constructor.
<prefix>.<treename>.level<n>.useAssoc
Specify whether or not to use an Association as the Category for the children of this level (true).
<prefix>.<treename>.level<n>.rootFilterAttr
If ".useAssoc" is false, specify an Attribute of the rootItem. If the value of that Attribute of the root Item of the tree matches the value of that Attribute in each item in the Category, then the item is added to the tree.

Item Rendering:

<prefix>.<categoryName>.displayAttr
Specify this to override the default rendering of the name of each Item in this Category. There are two ways to override the default rendering:
  1. Specify an Attribute name; the value of the Attribute will be displayed as the name of the Item (the node); and
  2. Specify a format string (see java.text.MessageFormat). The arguments are specified as a .displayAttrArg string array, as below.
<prefix>.<categoryName>.displayAttrArg<n>
Each .displayAttrArg (numbered from 0) is an Attribute name. The values of the Item Attributes are passed as arguments to java.text.MessageFormat.
For example, suppose the Properties file contains the following entries:
      <prefix>.com.shoon.MyCategory.displayAttr = {0}: {1}
      <prefix>.com.shoon.MyCategory.displayAttrArg0 = ITEM_TYPE
      <prefix>.com.shoon.MyCategory.displayAttrArg1 = ITEM_NAME
And let's suppose the Item corresponding to a given node of the tree has the following Attributes:
      ITEM_TYPE = Personal Name
      ITEM_NAME = Howard
Then the following call will be made to render the name of the node (using the Attributes of its Item item):
      java.text.MessageFormat("{0}: {1}",
                              new Object {
                                  item.getValueString("ITEM_TYPE"),
                                  item.getValueString("ITEM_NAME")
                              });
Thus the name of the Item (and the node in the tree) will be rendered as:
      Personal Name: Howard
<prefix>.<categoryName>.stateAttr
The name of the Attribute of the Item to use to determine the state of the Item. The values of this Item Attribute are used to change the rendering of the icon.
<prefix>.<categoryName>.<state>.blink
Set to "true" if the icon should blink when the value of the .stateAttr Attribute of the Item matches "state".
<prefix>.<categoryName>.itemComparator
The fully-qualified name of a Class which is used to compare two Items in this Category. The Class must implement the ItemComparator interface.

Code to Implement a TreeViewPane

To create a new TreeViewPane, you must specify the Item which will serve as the root of the tree and a name which will be used to find the Properties. Note that the type of the Item must match the type of Category specified in the Properties for level0 of the tree. Here is a simple example which creates a TreeViewPane and adds it to the Frame (The <prefix> is "MyTree"):

      TreeViewPane treeViewPane =
          new TreeViewPane(uic, hc, rs, rootItem, "MyTree");
      add(treeViewPane);

See the description of TreeViewPane for a full description of the Class and its constructor arguments.

The tree displayed by default is tree 0 (see Tree Structure above).

To change trees programmatically, for example, using a menu, call TreeViewPane.setTree(int) or TreeViewPane.setTree(java.lang.String).

To listen for user selection of a node in the tree, use the standard JTree calls. For example, use TreeViewPane.addTreeSelectionListener(TreeSelectionListener) to add a listener which fires when a node in the tree is selected. You can also use TreeViewPane.addActionListener(ActionListener) to listen for the user performing an action upon a node in the tree.

Examples:

Here is a portion of the Properties file which defines the structure of four tree (example adapted from the FailSafe Manager 2.0 product):

      MyTree.tree0 = groupsResources
      MyTree.tree1 = resources
      MyTree.tree2 = groups
      MyTree.tree3 = policies

      MyTree.groupsResources.level0 = com.sgi.fsmgr.category.ClusterCategory
      MyTree.groupsResources.level1 = com.sgi.fsmgr.category.ResourceGroupCategory
      MyTree.groupsResources.level2 = com.sgi.fsmgr.category.ResourceCategory

      MyTree.resources.level0 = com.sgi.fsmgr.category.ClusterCategory
      MyTree.resources.level1 = com.sgi.fsmgr.category.ResourceCategory

      MyTree.groups.level0 = com.sgi.fsmgr.category.ClusterCategory
      MyTree.groups.level1 = com.sgi.fsmgr.category.ResourceGroupCategory

      MyTree.policies.level0 = com.sgi.fsmgr.category.ClusterCategory
      MyTree.policies.level1 = com.sgi.fsmgr.category.FailoverPolicyCategory
      MyTree.policies.level1.useAssoc = false

      MyTree.com.sgi.fsmgr.category.ResourceCategory.displayAttr = {0}: {1}
      MyTree.com.sgi.fsmgr.category.ResourceCategory.displayAttrArg0 = _RESOURCE_TYPE
      MyTree.com.sgi.fsmgr.category.ResourceCategory.displayAttrArg1 = _RESOURCE
      MyTree.com.sgi.fsmgr.category.ResourceCategory.stateAttr = CAM_STATUS
      MyTree.com.sgi.fsmgr.category.ResourceCategory.ONLINE_PENDING.blink = true
      MyTree.com.sgi.fsmgr.category.ResourceCategory.OFFLINE_PENDING.blink = true
      MyTree.com.sgi.fsmgr.category.ResourceCategory.ERROR.blink = true
      MyTree.com.sgi.fsmgr.category.ResourceCategory.itemCompare = \
	      com.sgi.fsmgr.detailView.CategoryItemCompare

      MyTree.com.sgi.fsmgr.category.ResourceGroupCategory.stateAttr = CAM_STATUS
      MyTree.com.sgi.fsmgr.category.ResourceGroupCategory.ERROR.blink = true
      MyTree.com.sgi.fsmgr.category.ResourceGroupCategory.ONLINE_PENDING.blink = true
      MyTree.com.sgi.fsmgr.category.ResourceGroupCategory.OFFLINE_PENDING.blink = true
      MyTree.com.sgi.fsmgr.category.ResourceGroupCategory.itemCompare = \
	      com.sgi.fsmgr.detailView.CategoryItemCompare

      MyTree.com.sgi.fsmgr.category.ClusterCategory.stateAttr = CAM_STATUS
      MyTree.com.sgi.fsmgr.category.ClusterCategory.INACTIVE.blink = true

These example Properties define four (4) trees, one of which is displayed in the TreeViewPane at any given time. The Item at the root of the tree must be in the "Cluster" Category. Any given tree can be dynamically chosen for display. To select the "resources" tree, for example, to be displayed in the TreeViewPane, the following calls are equivalent:

      treeViewPane.setTree(1);
      treeViewPane.setTree("resources");
The four trees which can be displayed are as follows:
  1. groupsResources
    This tree is three (3) levels deep. The second level of the tree is populated with Items in an Association between the root Cluster Item and Items in the "ResourceGroup" Category. The third level of the tree is populated with Items in an Association between each ResourceGroup Item at the second level of the tree and Items in the "Resource" Category.
  2. resources
    This tree is two (2) levels deep. The second level of the tree is populated with Items in an Association between the root Cluster Item and Items in the "Resource" Category.
  3. groups
    This tree is two (2) levels deep. The second level of the tree is populated with Items in an Association between the root Cluster Item and Items in the "ResourceGroup" Category.
  4. policies
    This tree is two (2) levels deep. The second level of the tree is populated with Items in the "FailoverPolicy" Category (no Association is used).

Four (4) Categories of Items can be displayed in the tree, as follows:

  1. com.sgi.fsmgr.category.ClusterCategory
    If the "CAM_STATUS" Attribute of any Item in this Category has the value "INACTIVE", its icon will blink. Names of Items in this Category will be rendered using the default rendering.
  2. com.sgi.fsmgr.category.ResourceGroupCategory
    If the "CAM_STATUS" Attribute of any Item in this Category has the value "ONLINE_PENDING" "ERROR", or "OFFLINE_PENDING", its icon will blink. Items in this Category will be compared (for sorting purposes) by using the ItemComparator com.sgi.fsmgr.detailView.CategoryItemCompare. Names of Items in this Category will be rendered using the default rendering.
  3. com.sgi.fsmgr.category.ResourceCategory
    If the "CAM_STATUS" Attribute of any Item in this Category has the value "ONLINE_PENDING" "ERROR", or "OFFLINE_PENDING", its icon will blink. The name of the Item will be rendered using the java.text.MessageFormat string "{0}: {1}" with the arguments being the "_RESOURCE_TYPE" and "_RESOURCE" Attributes of that Item, respectively. Items in this Category will be compared (for sorting purposes) by using the ItemComparator com.sgi.fsmgr.detailView.CategoryItemCompare.
  4. com.sgi.fsmgr.category.FailoverPolicyCategory
    Names of Items in this Category will be rendered using the default rendering.

To listen for a node in the tree being acted upon (double-clicked) by the user, the following code is used:

      treeViewPane.addActionListener(new ActionListener() {
	  public void actionPerformed(ActionEvent event) {

	      // get the node of the tree that has been selected
	      //
	      DefaultMutableTreeNode node = (DefaultMutableTreeNode)
		  (((TreePath)event.getSource()).getLastPathComponent());

	      // ... node actions go here ...

	      try {

		  // get the Item that belongs to the selected node
		  //
		  ItemUserObject nodeInfo
		      = (ItemUserObject)node.getUserObject();
		  Item item = nodeInfo.getItem();

		  // ... actions upon the Item go here ...

	      } catch (ClassCastException ex) {
	      }
	  }
      });

To listen for a node in the tree being selected by the user, the following code is used:

      treeViewPane.addTreeSelectionListener(new TreeSelectionListener() {
	  public void valueChanged(TreeSelectionEvent event) {

	      // get the node of the tree that has been selected
	      //
	      DefaultMutableTreeNode node = (DefaultMutableTreeNode)
		  (event.getPath().getLastPathComponent());

	      // ... node actions go here ...

	      try {

		  // get the Item that belongs to the selected node
		  //
		  ItemUserObject nodeInfo
		      = (ItemUserObject)node.getUserObject();
		  Item item = nodeInfo.getItem();

		  // ... actions upon the Item go here ...

	      } catch (ClassCastException ex) {
	      }
	  }
      });


$Revision: 1.2 $ $Date: 2000/09/10 08:16:57 $