- The Tree Control
- Tree Appearance
- The TreeNode Interface
- The MutableTreeNode Interface
- The DefaultMutableTreeNode Class
- The TreePath Class
- What is a Leaf?
- Tree Expansion and Traversal
- Expanding and Collapsing Nodes under Program Control
- Tree Expansion Events
- Making Nodes Visible
- Controlling Node Expansion and Collapse
- Tree Model Events
- Implementation Plan for the File System Control
- File System Tree Control Implementation
- Using the File System Tree Control
- Custom Tree Rendering and Editing
- Customizing the Default Tree Cell Renderer
- ToolTips and Renderers
- Custom Cell Editors
- Controlling Which Nodes Can Be Edited
- Controlling Editability by Subclassing JTree
- Programmatic Control of Editors
- Editing Trees with Custom User Objects
- The valueForPathChanged Method
- The Tree Implementation
- The Cell Editor
- The Cell Renderer
- Summary
Controlling Editability by Subclassing JTree
When the user makes one of the gestures that initiates editing, the following JTree method is called:
public boolean isPathEditable(TreePath path);
where the TreePath corresponds to the node to be edited. The default implementation of this method looks like this:
public boolean isPathEditable(TreePath path) { return isEditable(); }
As a result of this, any cell can be edited provided that the tree itself is editable (although the editor can still refuse to allow the edit as you'll see below). One way to gain control of the editing mechanism is to override this method and apply your own criterion to determine whether the given cell should be edited. As an example, to arrange that only leaf nodes can be edited, you could implement the isPathEditable method as follows:
public boolean isPathEditable(TreePath path) { if (isEditable()) { return getModel().isLeaf( path.getLastPathComponent()); } return false; }
Note that it is necessary to ensure that the tree is editable by calling the isEditable method before applying a more specific test to the TreePath itself.
Controlling Editability in the Tree Cell Editor
Even if the tree is editable and the selected path is editable according to the isPathEditable method, it is still possible to stop the user editing a node by overriding the isCellEditable method of the CellEditor implementation. If this method returns false, no editor will be displayed. The isCellEditable method is defined as follows:
public boolean isCellEditable(EventObject evt)
The only argument that this method receives is an EventObject. The meaning of this argument depends on how this method is invoked:
When the user clicks on the node with the mouse, this argument is the MouseEventthat was delivered to the tree. The coordinates of the event correspond to some location within the rendered area of the node.
If the user attempts to start an edit using the F2key or programmatically (see below), this argument is null.
If the edit is started because the user clicked in a cell that was already selected, this argument is again passed as null.
Usually, the isCellEditable method is used to allow or disallow edits based on the way in which the edit is startedfor example, the editor may allow any edit started via the keyboard (provided the isPathEditable method has already allowed it), but only allow an edit initiated using the mouse on a triple click, to determine which it would inspect the Mou-seEvent. This method is not usually used to make a decision based on which node is being edited and it is difficult to do so, because the affected node is not passed as a method argument. Granted, you can locate the affected node by calling the JTree getPathForLocation method using the coordinates in the MouseEvent, but this is only possible in one of the three possible cases for which this method might be called.
Nevertheless, it is sometimes useful to make node-dependent decisions in the editor. To do this, you have to store a reference to the node being edited when the getTreeCellEditorComponent method is called, which happens just before isCellEditable is invoked. An example implementation for our tree example is shown below.
DefaultCellEditor comboEditor = new DefaultCellEditor(combo); DefaultTreeCellEditor editor = new DefaultTreeCellEditor( t, dtcr, comboEditor) { private Object lastValue; public Component getTreeCellEditorComponent(JTree tree, Object value, boolean isSelected, boolean expanded, boolean leaf, int row) { // Store the path lastValue = value; return super.getTreeCellEditorComponent(tree, value, isSelected, expanded, leaf, row); } public boolean isCellEditable(EventObject evt) { if (super.isCellEditable(evt) && lastValue instanceof DefaultMutableTreeNode) { Object userObject = ((DefaultMutableTreeNode)lastValue). getUserObject(); for (int i = 0; i < values.length; i++) { if (userObject.equals(values[i])) { return true; } } } return false; } }; t.setCellEditor(editor);
This example can be run with the command:
java JFCBook.Chapter10.ComboTree2
If you select several nodes in the tree in turn and try to edit them, you'll find that you are prevented from doing so, unless you select one of the spacecraft nodes (initially labeled "Apollo" and "Skylab"). The code itself is straightforward given the discussion that preceded it. The getTreeCellEd-itorComponent simply stores the value in the tree that it is given to edit and then calls the superclass implementation to get the actual editor. The value argument passed to this method is actually the last component of the Tree-Path for the node being edited, which will be a DefaultMutableTreeNode in this case.
The overridden isEditable method first ensures that the tree itself agrees to the current node being edited by calling the superclass method that it overrides. Assuming that this is the case, it retrieves the DefaultMu-tableTreeNode stored by the getTreeCellEditorComponent method and extracts its user object. The problem is how to determine that the node that the user wants to edit corresponds to a spacecraft. In this case, the approach taken is to compare the user object with all of the values stored in the combo box, which represent all of the legal values that this node could hold. This will work for this specific example. Another possible approach would be to define the tree nodes in such a way that they include an attribute that indicates whether they can be edited, or to implement specific tree node classes that represent spacecraft and astronauts and perform the check by inspecting the runtime type of the node.