- 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
Programmatic Control of Editors
Most tree editing will be initiated by the user. The user interface gestures that initiate and end the editing process are detected by the tree's UI delegate class which listens to mouse events and installs keyboard mappings for the F2 key and any other keys that a custom look-and-feel might want to use for editing. The UI class makes use of a relatively small API provided by JTree that actually initiates and terminates edits. The same API can be used by application code to start or end an editing session in response to different user gestures or as a result of some state change in the application. The methods that you can use are defined in Table 108.
Rendering and Editing Custom Objects
The replacement editor developed in the previous section had an implicit assumption that doesn't hold for some trees. The assumption was that the user objects associated with the tree's nodes were all strings, which was (conveniently) true in that particular example. There is no reason for this always to be true and in general it is useful to be able to associate an arbitrary object with a node.
When the user object is not a string, the default renderer will display it properly provided that it provides a suitable toString method because, as you already know, the renderer applies this method to the node, which will in turn apply it to the user object. However, editing such an object is not so simple. Let's return to the combo box editor to see what the issue is. When the tree was built for this case, all the user objects were strings and the combo box was populated with all of the possible string values that would be meaningful for the nodes that could be edited. When the user selects a new value, the editor applies the value to the tree. Because the editor was derived from the DefaultTreeCellEditor class, there was no need to supply the code
Table 108 Tree Editing Methods
Method |
Description |
public void startEditingAtPath(TreePath path) |
Initiates an edit of the given path. The tree is scrolled so that the node corresponding to path is visible, if necessary and checks are made to ensure that the tree and node are both editable, as described earlier. If these conditions are not met, or there is no editor installed in the tree, the edit will not start. The isEditing method can be used to determine whether the edit was started. |
public boolean stopEditing() |
This method is used to request a tidy end to the editing session, in which the new value supplied by the user, if any, is applied to the tree. If no edit is in progress, this method simply returns true. Otherwise, the editor's stop-CellEditing method is called. If the editor returns false, then the edit will continue and stopEditing itself will return false. If the editor returns true, its getCellEditorValue method is called to get the value that the user supplied and this value is stored in the node being edited using the TreeModel valueForPath-Changed method, which will be described in the next section. Finally, the editor is removed from the tree and stopEditing returns true to indicate success. |
public void cancelEditing() |
By contrast to stopEditing, this method requests that the current edit be forcibly terminated. Its operation is very similar to that of stopEditing, with the following differences: |
|
1. The editor is not asked to stop editing via its stopCellEditing method; instead, its cancel-CellEditing method is called. This requires it to unconditionally abandon its editing session. |
|
2. The value in the editor is not written to the tree. Thus, any changes made by the user in the editor are lost. |
|
This method is typically called when the user makes a gesture that signals he or she does not not want to continue with the edit, such as pressing the ESCAPE key or selecting a different node. |
public TreePath getEditingPath() |
Returns the path that is currently being edited or nullif there is no edit in progress. |
public boolean isEditing() |
Returns trueif there is an edit in progress, falseif there is not. |
public boolean isPathEditable(TreePath path) |
Returns trueif the path given by the argument is editable. The default implementation of this method returns trueif the tree itself is editable and falseif it is not. Jtreesubclasses can override this method to return different values for different nodes, as shown earlier in this section. |
that carried out this step. What happens is that the editor's stopCellEdit-ing method is called when the edit is finished and this method informs any registered CellEditorListeners that editing is complete. The tree's UI class registers as a CellEditorListener and, when it receives this event, it gets the new value from the editor by calling its getCellEditorValue method, which, in the case of our example, will return the value selected from the combo box, courtesy of code provided by DefaultTreeCellEdi-tor.
Core Note
CellEditorListener is not a very useful interface unless you are implementing a tree UI or an editor. It consists of two methods:
public interface CellEditorListener extends EventListener { public abstract void editingStopped(ChangeEvent evt); public abstract void editingCanceled( ChangeEvent evt); }
The editingStopped method is called when editing completes normally, while editingCanceled is invoked when editing is aborted because, for example, the user selected a different node on the tree or collapsed the branch that contained the node being edited. This interface doesn't have an event of its ownit just supplies a ChangeEvent, an event that has no state, but just indicates that something about the event source may have changed.
The tree's UI class stores the edited value in the model by invoking the DefaultTreeModel valueForPathChanged method, which is defined as follows:
public void valueForPathChanged(TreePath path, Object value);
The default implementation of this method locates the DefaultMu-tableTreeNode for the affected path and stores the new value by invoking setUserObject(value). In our earlier example, the combo box returned a String value, so the type of the value argument to this method will be String, which means that the user object will be replaced by its own String representation. Unless the user object in the node that the editor was editing was originally a String (which, fortunately, it was in our example), this would be a fatal mistake.
To get around this problem, you can do one of two things:
Populate the combo box with the user objects themselves instead of strings. This works as long as the user objects provide a toStringmethod that returns a Stringthat is meaningful to the user, because the combo box displays the result of invoking toString on each of its displayed items, but the selected item would be an instance of the user object type. Therefore, valueForPathChanged will be passed an object of the correct type.
Continue to populate the combo box with strings, but override the valueForPathChangedmethod to substitute an appropriate object for the Stringwhen the edit is complete.
The first method is the much simpler of the two and really requires no further explanation so, for the purposes of illustration, let's look at how to implement the second method.