JTreeTable

A combination tree/table component has never been a part of Swing, but has been available to Swing programmers for almost as long as Swing itself. The original was written by Philip Milne and Scott Violet before the release of JDK 1.2, when Swing had to be downloaded separately and still used the com.sun.java.swing package name. Read the article and check out the code yourself if you like.

Essentially, it was a clever hack that used a JTree as both a TableCellRenderer (to paint the tree column one cell at a time) and a TableCellEditor (to allow the tree to react to mouse and keyboard events). The code was actually pretty simple, and it did work, but it was limited. After a couple years, some of the limitations had been overcome. A subsequent article provided event handling, and a third supported editing of the tree column. JTreeTable had become usable, but it was still a hack, and it still had rough edges.

JTreeTable image, July 2000

Over the years since then, many people have taken JTreeTable and adapted it to their needs. Some of these adaptations have been released for others to download and use. If you look, there are a lot of almost-but-not-quite-compatible versions of JTreeTable out there. Some are commercial, some are free, but they all seem to be based on the Milne/Violet approach. They also all tend to have plenty of pending bugs in their bug-tracking systems.

I’m not familiar with most of the commercial implementations, but of the free ones I’ve seen I like JXTreeTable the best. It’s part of the SwingX project from SwingLabs. It works pretty well and it is actively developed, which means there are regularly-updated releases and that bugs do tend to get fixed, though plenty of bugs remain. A down side is that the implementation is huge. It is built on top of SwingX’s custom JXTable and JXTree components, not directly on Swing’s JTree and JTable.


The interesting news is that there is a new implementation of a Swing tree table component that is not based on the Milne/Violet code. They call it the Outline component. (The name is evidently taken from a tree table component in OSX.) I haven’t had time to take a serious look at it, but it does seem to do a lot of things right. But not quite everything right—at least not yet.

I’m looking forward to keeping an eye on this new implementation. Things I’m hoping for: (1) fewer bug reports than a typical Swing tree table, (2) improved UI consistency, and (3) better and/or simpler handling of fireXxxChanged() events.

LazyViewport

disclaimer: This entry isn’t intended for a general audience. If you’re not a Swing programmer, you’ll probably want to skip it.

When the user flits about in a JScrollPane, the viewport’s view repeatedly repaints to maintain the illusion that the view component is sliding. This is usually a good thing, but sometimes the view is complicated and takes a long time to paint. This can freeze the GUI or chew up too many cycles.

In those situations one might prefer a ‘lazy’ scroll pane that doesn’t repaint the view while the user is dragging the thumb of the scroll bar, but does a single repaint when the user releases the thumb. Here’s an implementation:

import javax.swing.JViewport;
import javax.swing.JScrollPane;
import java.awt.Component;
import java.awt.Point;

/**
 * a subclass of JViewport that overrides a method in order not
 * to repaint its component many times during a vertical drag
 *
 * @author Brian Cole
 */
public class LazyViewport extends JViewport {

    /**
     * equivalent to <b>new JScrollPane(view)</b> except uses a LazyViewport
     */
    public static JScrollPane createLazyScrollPaneFor(Component view) {
        LazyViewport vp = new LazyViewport();
        vp.setView(view);
        JScrollPane scrollpane = new JScrollPane();
        scrollpane.setViewport(vp);
        return scrollpane;
    }

    /**
     * overridden to not repaint during during a vertical drag
     */
    public void setViewPosition(Point p) {
        Component parent = getParent();
        if ( parent instanceof JScrollPane &&
             ((JScrollPane)parent).getVerticalScrollBar().getValueIsAdjusting() ) {
            // value is adjusting, skip repaint
            return;
        }
        super.setViewPosition(p);
    }

    private static final long serialVersionUID = 2006L;
}

This has been implentated to be lazy only when scrolling up and down, which is usually what I want. It would be simple to extend it to make horizontal scrolling lazy also.

 

Swing single threading rule and JSR-296

I didn’t make it to the JavaOne conference this year, but I notice that the slides and video (registration required) have been posted for the session on JSR-296: Swing Application Framework.

The Swing Application Framework has a number of goals, but one of them is to provide an Application class that has lifecycle methods for the programmer to implement, sort of like the existing Applet class. Application makes it easier not to violate the more-stringent Swing threading rule by invoking its startup method on the event thread. [I originally wrote by automatically invoking but it’s not automatic. One must call launch(MyClass.class, argv) from main, which then calls startup(argv) via reflection.]

The idea is that JSR-296 will eventually be part of the JDK7 or JDK8. It’s possible to use it now as an external library, but the API is subject to change. It’s also possible to write code inspired by JSR-296 without actually using the library. Like other attempts to comply with the Swing threading rule, this is ugly but I throw it out there for discussion. Continue reading

Will the real Swing Single Threading Rule please stand up?

disclaimer: This entry isn’t intended for a general audience. If you’re not a Swing programmer, you’ll probably want to skip it.

This is what a Swing “hello world” program used to look like.

import javax.swing.*;

public class HelloWorld2002 {

	public static void main(String[] argv) {
		JLabel bulletin = new JLabel("Hello, world!", JLabel.CENTER);

		JFrame frame = new JFrame("Bulletin");
		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		frame.getContentPane().add(bulletin); // adds to CENTER
		frame.setSize(200, 150);
		frame.setVisible(true);
	}
}

This is pretty much the same as the code on page 85 of my book. In fact, just about all the examples in the book kick things off this way. They create JFrames and other JComponents in the main thread, either directly in main() or indirectly. Swing is not thread-safe (by design) so we have always had to be careful. For example, if we want to call bulletin.setForeground(Color.RED) after the frame is visible, we have to make sure it executes in the event-dispatching thread. If it executes in the main thread or some other thread it’s likely to work just fine, but there are no guarantees and it is possible that bad things (such as deadlock) may happen. Even if it seems to work on your development machine, it may fail intermittently, or it may even deterministically fail on some platform on which you haven’t tested or on some future release of the JDK.

Swing Single Threading Rule (through 2003)
Once a Swing component has been realized*, only the event-dispatching thread should affect or query the component.
*Realized means that the component has been painted onscreen, or is ready to be painted. The methods setVisible(true) and pack cause a window to be realized, which in turn causes the components it contains to be realized.

Several years ago I heard rumors that intermittent errors on the Solaris operating system could be worked-around by doing everything on the event-dispatching thread. By everything I mean not only the manipulation of realized components, but also of unrealized ones, and even the instantiation of the components themselves.

I never heard anything official in this regard, but it often pays to be cautious so I started doing this in most of my production code. I noticed that other Swing programmers started doing it in their code too.

Flash forward to last week. I was at a job interview. I had submitted a code sample that set up the GUI in the main thread, and now I was being asked about it. As a Swing expert, how could I have made such a common error?
Continue reading