Home > Articles > Programming > Java

This chapter is from the book

This chapter is from the book

The valueForPathChanged Method

The only other method of the data model that still needs to be described is the one that this section opened with—valueForPathChanged. This method is called with a TreePath for the node that has been edited and the new value which will have come from the combo box and which is therefore known to be a String, because the combo box was created using the get-NamesAsCombo method, which populates it with Strings. What value-ForPathChanged needs to do is find the correct user object corresponding to the String returned from the editor and install it in the node. Finding the user object is easy—the data model has a hashtable that maps from the name to the object itself, so all that is necessary is to invoke the getObjectFor-Name method, which accesses the hashtable to locate the object. This method only returns an appropriate answer if the String returned by the combo box was the key for one of the objects that are in the hashtable. Given the way that this example has been written, this will always be true, but if you take the code here and use it in another application which allows the combo box to be editable (probably a mistake!), the user may type a meaningless value and you won't find a corresponding object in the hashtable. To defend against this possibility, if this happens, getObjectForName will return null, and the exact value that the user typed is stored as the user object. Whether or not this is of any use depends on the application: It does at least stop the program getting a NullPointerException.

Having got the correct object from the getObjectForName method, the next step is to find the node to install it in. You already know how to do this—you just apply getLastPathComponent to the TreePath passed to val-ueForPathChanged to find the node and then call setUserObject to install the user object. Finally, because the content of the model has changed, the nodeChanged method must be called to have the data model send events to registered TreeModelListeners. This will cause the tree's visual state to be updated.

That takes care of the data model. However, this example also needs three other things:

  1. A JTreeto display the model.

  2. An editor that provides a combo box with the legal hardware types installed.

  3. A renderer that will display the hardware object in each of the tree's leaf nodes.

We'll cover each of these items separately. For reference, the complete implementation, apart from the data model, which was covered earlier, is shown in Listing 10–8.

Listing 10–8 Rendering and editing nodes with custom icons

package JFCBook.Chapter10; 

import javax.swing.*; 
import javax.swing.tree.*; 
import javax.swing.event.*; 
import java.util.*; 
import java.awt.Component; 
import java.awt.event.*; 

public class CustomIcons {
  public static void main(String[] args) {
    JFrame f = new JFrame("Custom Icons"); 

    // Create the model with just the root node
    DefaultMutableTreeNode rootNode = 
                  new DefaultMutableTreeNode("Apollo"); 

    CustomTreeModel m = new CustomTreeModel(rootNode);
    JTree t = new JTree(m) {
      public boolean isPathEditable(TreePath path) {
        // Only allow editing of leaf nodes
        return isEditable() && getModel().isLeaf(path.getLastPathComponent());
      }
    }; 
  t.putClientProperty("JTree.lineStyle", "Angled");
  t.setEditable(true);
  t.setRowHeight(0);// Row height is not fixed

  // Define a replacement renderer
  final TreeCellRenderer oldRenderer = t.getCellRenderer();
  DefaultTreeCellRenderer r = new DefaultTreeCellRenderer()
{
    private Object lastValue; 

    public Component getTreeCellRendererComponent(
                  JTree tree, Object value,
                  boolean selected, boolean expanded,
                  boolean leaf, int row,
                  boolean hasFocus) {
      // Store the value for getLeafIcon
      lastValue = value; 

      // Allow the original renderer to set up the label
      Component c =
        oldRenderer.getTreeCellRendererComponent(
                      tree, value, selected,
                      expanded, leaf,
                      row, hasFocus); 
      // Now change the icon if necessary
      if (leaf && c instanceof JLabel) {
        JLabel l = (JLabel)c;
        Icon icon = getLeafIcon();
        if (icon != null) { 

        l.setIcon(icon);
        }
      } 

      return c; } 

      public Icon getLeafIcon() {
        Object o = 
        ((DefaultMutableTreeNode)lastValue).getUserObject();
        if (o instanceof NodeWithIcon) {
          return ((NodeWithIcon)o).getIcon(); 
      } else if (oldRenderer
              instanceof DefaultTreeCellRenderer) {
        return ((DefaultTreeCellRenderer)oldRenderer).
                      getLeafIcon();
      }
      return null; 
    } 
  };
  t.setCellRenderer(r); 

  // Define a replacement editor 
  JComboBox combo = m.getNamesAsCombo();
  DefaultCellEditor comboEditor =
         new DefaultCellEditor(combo);
  DefaultTreeCellEditor editor =
         new DefaultTreeCellEditor(t, r, comboEditor);
  t.setCellEditor(editor); 

  // Populate the model with a small amount of data
  DefaultMutableTreeNode hardwareNode =
              new DefaultMutableTreeNode("Hardware");
  rootNode.add(hardwareNode);
  String itemName = (String)combo.getItemAt(0); 
  DefaultMutableTreeNode itemNode =
      new DefaultMutableTreeNode(
                   m.getObjectForName(itemName));
      hardwareNode.add(itemNode); 

      t.expandRow(0);
      t.expandRow(1); 

      f.getContentPane().add(new JScrollPane(t));
      f.setSize(300, 300);
      f.setVisible(true);
  } 
} 
  • + Share This
  • 🔖 Save To Your Account