- 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
Implementation Plan for the File System Control
In order to create a tree that shows a file system, a representation of the file system's directory structure must be loaded into a DefaultTreeModel. On the face of it, this is a relatively simple proposition: All you need is a recursive method that is given a directory, reads its contents and, each time it comes across a subdirectory, adds a node to the tree model, then invokes itself to process the content of that subdirectory. Using the methods of the java.io.File class, it is easy to get a list of the objects in a directory and to determine which of these objects are themselves directories. The problem with this simplistic approach is that it would scan the entire file system when the control is created. This has two obvious drawbacksit may take a long time and it will probably take up a large amount of memory to hold all of the nodes. Most of this time and memory is probably wasted, because the odds are that the user will find what he/she is looking for fairly quickly and not look at most of what has been generated.
The ideal approach would be to start by doing only as much work as necessary to show the user the content of the directory that the control starts in. Then, whenever a directory is clicked and opened, the nodes for that directory should be created and displayed, and so on. If this can be achieved, it should be possible to create the control with only the relatively short delay required to read the top-level directory, at the cost of a small delay each time a new directory is opened. This approach is, however, too simplistic.
To see why this won't work, suppose you have a small file system with a directory structure that looks like this:
C:\java C:\java\bin C:\java\demo C:\java\demo\Animator C:\java\demo\ArcTest C:\java\demo\awt-1.1 C:\java\demo\BarChart C:\java\docs C:\java\docs\api C:\java\docs\guide C:\java\docs\images C:\java\docs\relnotes C:\java\docs\tooldocs C:\java\include C:\java\include\win32 C:\java\lib C:\java\src C:\java\src\java C:\java\src\java\applet C:\java\src\java\awt C:\java\src\java\bean C:\java\src\java\io C:\java\src\sun C:\java\src\sunw
To create a tree that allows the user to navigate the directory C:\java following the algorithm outlined earlier, the first step would be to build a node for C:\java itself and then child nodes would be added for the subdirectories C:\java\bin, C:\java\demo, C:\java\docs, C:\java\include, C:\java\lib and C:\java\src. If the process stopped here, the tree would indeed show the top-level directory and all of these subdirectories. However, you can see that the demo, docs, include, and src directories themselves have subdirectories, so you would expect the tree to show them with an expansion icon. But, this icon is only shown if the node that it relates to has child nodes attached to it. So far, no tree nodes have been added for these subdirectories, because the idea is to minimize the work that is needed to get the control created. To get the right effect, this approach will have to be modified in some way.
The only way to make the expansion icons appear is to add a child node to each directory that has subdirectories. To do this, it is necessary to scan not only the directory that the control starts in, but also the directories that are found in that directory. With this modification, nodes would be added for the subdirectories Animator, ArcTest, awt-1.1, BarChart, api, guide, and so on. It isn't necessary to go any lower at this stage, because directories further down can't influence the appearance of the control when the user is only looking at the contents of the C:\java directory.
Now suppose the user clicked on the icon for C:\java\src. This would open up and reveal the nodes representing the java, sun and sunw subdirectories, but now there is the same problem again: The C:\java\src\java subdirectory and (although this hasn't been shown in detail in the file system map shown above) all the other directories have subdirectories of their own, but the expansion icons for their nodes don't appear because those directories haven't been scanned to build their child nodes. Fortunately, as you know, it is possible to get a TreeExpansionEvent when the user opens a node. This event can be used to find out which directory the user wants to look at and then all of its subdirectories can be populated, as was done with the initial directory. In fact, as you'll see, the same code can be used to handle both cases.
The approach just outlined will work, but there is a small improvement that can be made to minimize the impact of having to scan for subdirectories. The only reason to look for directories that are not going to be visible straight away is to make the expansion icon appear for a directory that will be visible. All you have to do to make the expansion icon appear is to place one child under the node for a directory that has subdirectories. In other words, when scanning for hidden subdirectories, it is possible to stop as soon as we have found one. This can save a lot of time if the file system has big directories, because it avoids the overhead of reading through lots of files to find subdirectories that may not exist, and also minimizes the effect of looking in directories that the user doesn't end up opening anyway. The flip side of this is, of course, that if the user does open a directory that has had a single entry placed in it, it is necessary to get rid of it before fully populating the directory, or the first entry wold appear in the directory twice.