Creating TreeTables:
Part 2
Completing the Example
Program
This article builds on the JTreeTable
example that was introduced in "Creating
TreeTables in Swing,"
the previous article in this section. Before reading this article,
please read the previous one.
By Scott Violet
and Kathy Walrath
For
the sake of simplicity, the example program presented in "Creating
TreeTables in Swing" was
a bare-bones program with a few shortcomings. This article presents
an expanded and enhanced example program, renamed TreeTable II,
that fixes the shortcomings of the original example and adds a few
new features of its own.
In the Source
Code section of this article, you´ll get
a chance to view the rest of the program´s code. You´ll also get
an opportunity to download a zipped version of the complete program
and its resources.
This article has three major sections:
What´s
New in TreeTable II
In last month´s issue of The Swing Connection, the "Creating
TreeTables in Swing" article showed how to create a
combination of a Tree and a Table -- a component capable of both
expanding and contracting rows, as well as showing multiple columns
of data. The article concluded with a example application, named
TreeTableExample0, which displayed a working TreeTable browser
that you can use to browse a local file system
As you can see by examining the JTreeTable.java
file, this article´s version of the JTreeTable
class is much improved over the version of the same class presented
in "Creating
TreeTables in Swing." Before,
colors and row heights weren't consistent between the tree and the
table. Also, keyboard navigation was awkward. Two other shortcomings,
which weren't noticeable due to the previous example's simplicity,
were that the tree's selection model didn't track changes properly
and the GUI wouldn't be updated if the tree data changed.
We substantially changed the behavior of
the example to show off the capabilities of new, improved JTreeTable.
TreeTable II behaves rather like the UNIX command du,
or the NEXTSTEP example DarkForest. The basic functionality changes
are that the size of a directory is now the total size of all its
children, and you can update the size data and inspect multiple
directory hierarchies. We also added the ability to change the look
and feel while TreeTable II is running.
Another difference from the previous source
code is that we now use the new Swing package structure (javax.swing,
rather than com.sun.java.swing). You can run the example
with either JDK 1.2 (RC1 or a compatible release) or with JDK 1.1
+ JFC 1.1 (Swing 1.1 Beta 3 or a compatible version).
Here are the fixes we added to overcome shortcomings
of the previous example:
Here are the features we added to implement
new functionality:
Coordinating
colors and row height
To coordinate colors and row height, we overrode
two methods (updateUI
and setRowHeight)
in both the JTree subclass (JTreeTable.TreeTableCellRenderer) and
the JTable subclass (JTreeTable). The code in the updateUI
methods makes the table use the tree's default foreground and background
colors, and the tree use the table's selection colors. The setRowHeight
methods ensure that when the row height of the tree or table is
changed, the row height of the other object (table or tree) is changed
to be exactly the same.
Improving
keyboard navigation
In the previous version, the tree could get
the keyboard focus. Once it did, the tree handled all key events.
This gave an odd feeling in that you could not tab out of the tree
to select other cells in the table, nor use the left/right arrow
keys as you normally could in a table.
In the new version, the TableCellEditor's
isCellEditable
method always returns false,
so the tree never gets the focus. If the argument to isCellEditable
is a MouseEvent, then isCellEditable
forwards the mouse event to the tree. This lets the tree handle
the mouse event as it normally would, without ever having focus.
This change fixes the focus problem, and as a side effect enables
autoscrolling to the new selection.
Updating
the table in response to tree events
As the previous article explained, TreeTableModelAdapter
is used to share the TreeTableModel (the table's data model) between
the JTree and JTable. The previous version of TreeTableModelAdapter
did not implement TreeModelListener, which meant that changes in
the TreeTableModel would not be propagated to the UI. This didn't
matter in the previous example, since the tree's contents didn't
change after the tree was visible.
The current version of TreeTableModelAdapter
registers a TreeModelListener on the TreeTableModel. The listener's
methods fire TableModelEvents so that the UI will update when the
tree is updated. This enables TreeTable II's Reload feature.
Making
the tree track the visible selection
In the previous version, the
JTable referenced the ListSelectionModel from the DefaultTreeSelectionModel.
As DefaultTreeSelectionModel does not listen for changes in its
ListSelectionModel, this could result in an inconsistent state.
The ListSelectionModel would have one thing selected and the TreeSelectionModel
would reference another set of paths as being selected.
This version defines a new subclass of DefaultTreeSelectionModel,
called JTreeTable.ListToTreeSelectionModelWrapper, that listens
for changes in its ListSelectionModel. When the ListSelectionModel
changes, JTreeTable.ListToTreeSelectionModelWrapper updates its
paths so that the two models remain in sync. This didn't matter
in the previous example, since no code queried the tree for its
selection state. Implementing this feature enables detecting the
selected tree paths, which is necessary for commands such as Reload
that work on the current selection.
Reloading
data
By selecting a directory and then choosing
the File > Reload menu item, you cause that directory
to be rechecked.
Links, by default, aren't initially descended.
To load the contents of a link, select the link and then choose
the Reload menu item.
Inspecting
a new root directory
With the File > Open command, you
bring up a file chooser from which you can choose a directory. A
new frame appears that shows a tree-table that has the specified
directory as its root.
Dynamically
changing the look and feel
By choosing any of the menu items in the
Options menu, you can dynamically change the look and feel.
You have the choice of three look-and-feel options: Metal (the Java
Look and Feel), CDE/Motif Look and Feel, and Windows Look and Feel
(which works only on computers that normally use the corresonding
look and feel).
Note that if you're using Swing with JDK
1.1, you must be sure that the appropriate look-and-feel archive
files are in the class path. For example, if you might use the CDE/Motif
Look and Feel, then the class path must include
motif.jar.
For the Windows Look and Feel, the class path must include windows.jar.
The Java Look and Feel (Metal) is already included in swing.jar.
Dynamically
sorting rows
When all the children of the topmost directory
have been loaded, they are sorted again by total size. This sorting
generates a TreeStructureChange
event, which has the effect of causing any expanded children of
the topmost directory to collapse.
Using
a background thread
To avoid freezing the UI, calculating the
total size is done in a background thread. Because Swing is not
thread safe, data-ready notifications (accomplished by firing TreeModelEvents)
can't be done in the background thread. Instead, TreeTable II uses
the SwingUtilities.invokeLater()
method to make the event-firing code be executed in the event dispatch
thread.
A new background thread is used for each
data-loading cycle. First, all of the children of the tree's root
are loaded and made available. Then a new thread is spawned that
recursively loads the descendants of each child of the tree's root
directory. Each time all the descendants of one of the tree root's
children have been loaded, a TreeModelEvent
is generated.
Source Code
The list of files for TreeTable II is almost
the same as for the old
example. The differences are that
FileSystemModel.java
has been renamed to FileSystemModel2.java
to reflect its drastically different implementation, and TreeTableExample0.java
has been renamed to TreeTableExample2.java.
Here are the source files for the new example:
src.zip
AbstractCellEditor.java
No significant changes. A subclass of CellEditorapi
that handles the list of listeners, providing a base class for
cell editors.
AbstractTreeTableModel.java
FileSystemModel2.java
Now implements sorting, getting total size of directories, and
background thread. FileSystemModel2 extends AbstractTreeTableModel.
JTreeTable.java
Changed to improve selection coordination between tree and table,
use a better row height, use better colors, support switching
the look and feel, and improve navigability. Includes these classes:
MergeSort.java
No significant changes. Implements a sorting algorithm.
TreeTableExample2.java
Modified as necessary to create the new GUI and hook everything
together. Contains the main method that
creates and runs the TreeTable II example.
TreeTableModel.java
No significant changes. A TreeModelapi
subinterface that describes the kind of data that can be drawn
by a TreeTable.
TreeTableModelAdapter.java
Now installs a TreeModelListenerapi
to fire events to the table after the tree has been updated. TreeTableModelAdapter
implements the TableModelapi
interface, given both a TreeTableModel and a JTree.
Compiling
and Running the Example
You can compile and run TreeTable
II using either JDK 1.2 or JDK 1.1 plus JFC 1.1 (with Swing 1.1
Beta 3 or a compatible release). As long as the release uses the
javax.swing
names for the Swing packages (as opposed to com.sun.java.swing),
it should work with this example.
When running
the TreeTable II, you can provide a single command-line argument.
This argument specifies the topmost directory for which size calculations
should be generated. This directory is the root of the tree displayed
by TreeTable II. If you don't specify an argument, then TreeTable
II uses the user's home directory as the root of the tree.
The following examples show how to run TreeTable
II, both with JDK 1.1 and JDK 1.2, and on UNIX and Win32 platforms.
JDK 1.1 + JFC 1.1 (Swing 1.1 Beta 3 or
a compatible version):
/java/jdk1.1.7/bin/java
-classpath.:/java/jdk1.1.7/lib/classes.zip:/java/
swing-1.1beta3/swing.jar:/java/swing-1.1beta3/motif.jar
TreeTableExample2 /somedir (UNIX)
c:\java\jdk1.1.7\bin\java -classpath
.;c:\java\jdk1.1.7\lib\classes.zip;
c:\java\swing-1.1beta3\swing.jar;
c:\java\swing-1.1beta3\motif.jar
TreeTableExample2 c:\somedir (Win32)
JDK
1.2 (RC1 or a compatible release):
java TreeTableExample2 /somedir (UNIX)
java TreeTableExample2 c:\somedir (Win32)
|