Skip to main content

Enable Dropping into Empty JTables

Posted by shan_man on January 19, 2006 at 1:46 PM PST

So you've created an empty JTable, you've given it a TransferHandler to accept drops,
and you've added it to a JScrollPane. But when you launch your application and drag some completely valid
data into the table, it rejects you! I'm not kidding; go ahead and try it yourself with this demo
(requires build 76 or greater of Mustang). Drag from the component
labelled "Drag from here" into the JTable, and you'll see the following:

What's going on? Without peeking at the demo's "Options" menu (which would likely give it away), can you
figure it out? How about a hint: The background color of a JTable in the Ocean Look and Feel
is supposed to be white. In this demo, I haven't changed it, and yet you aren't seeing that white color. Perhaps
there's a connection...Are you ready for a second hint? Using the demo, double click a couple of times on the
drag source to have some rows added to the table. Now try dragging again and you'll see that this time you
can indeed drop into the table. But, the drop line is only shown when the mouse is over an existing row; if
you drag below the existing rows, you'll see something like the following:

After seeing this, have you yet started to wonder if maybe that gray "no-drop" area is not actually a part of the table?
If so, you've figured out what's going on! You see, unlike JList and JTree, for some reason
JTable is not implemented to be automatically stretched to fill the height of a JScrollPane's viewport;
it only takes up as much vertical room as needed for the rows that it contains. As such, when you're dragging
over that gray area, you're actually not over the table. This issue has been known for some time under
bug number 4310721
(JTable is not stretched to fill a viewport's height). Let's have a look at how this has been resolved in Mustang,
and how you can easily work around it in earlier releases.

The behavior of a component in this capacity is controlled by the component's implementation
of the getScrollableTracksViewportHeight method, declared in the Scrollable interface.
This method tells the viewport whether or not it should force the height of the contained component to
match the height of the viewport. JList and JTree, which implement Scrollable,
do something intelligent in this method and tell the viewport that they want to be stretched any time their preferred
height is smaller than the viewport height. JTable on the other hand, prior to Mustang,
always returns false.

Determining this to be something that developers often want to change,
we've made it easier to do so in Mustang; it is now very simple to configure JTable to
act like the other two components in this regard. Striving to maintain backward compatibility and
to not break the previous clearly specified behavior (however poor), we've added a new property to JTable
to enable solving this problem: "fillsViewportHeight". By default the property is false,
so that JTable acts exactly as it did previously. By setting the property to true,
with the method JTable.setFillsViewportHeight(boolean), JTable behaves
like lists and trees and is stretched to fill the viewport height when appropriate.

Using the demo again, let's see what this looks like. If you've closed the demo, please launch it
again. If you still have it running, please select the "Reset"
item from the "Options" menu to reset the state. In the demo, the new "fillsViewportHeight" property can
be changed via the "Fill Viewport Height" item in the "Options" menu. Please enable this option now. As you'll
see, and as indicated in the following screen shots, the table is now stretched to fill the viewport. This
results in two positive things: 1) The white background is now visible; 2) You can now drop anywhere within the viewport.


Incidentally, since it's already apparent in the demo and screen shots, let me quickly draw your attention to
the solid line indicating the drop location in the JTable. This is also new in Mustang, and the result of using
JTable in the INSERT_ROWS drop mode, a subject which I began talking about in my earlier
blog entry. There's
actually a lot more to say about the new drop modes with respect to JTable, and for that reason
I'll leave further discussion on the subject to a future blog entry.

For now, let's conclude with me fulfilling my promise of demonstrating how to make JTable stretch to
fill the viewport in pre-Mustang releases. It's extremely simple, with a variation of the following override in a
JTable subclass being all that is needed :

public boolean getScrollableTracksViewportHeight() {
    // fetch the table's parent
    Container viewport = getParent();

    // if the parent is not a viewport, calling this isn't useful
    if (!viewport instanceof JViewport) {
        return false;

    // return true if the table's preferred height is smaller
    // than the viewport height, else false
    return getPreferredSize().height < viewport.getHeight();

With this code you'll acheive identical results to turning the new property on in Mustang. And there's no need
to wait since it's most likely the behavior you want, is extremely easy, and will not interfere when you upgrade
in the future. That's the best kind of work-around in my opinion!

That's all I have for today, but please stay tuned.
I'm finding it extremely rewarding to communicate with you via this blog and I'm on a roll, with the words just flowing.
Potential upcoming topics include the new drop modes as they pertain to JTable, and fancy customization of drop mode
indication in JTree - something that was a big hit, and received audience applause at the last JavaOne conference.
In fact, I may just have to write that one first, as I'm getting quite excited about it. Until next time, take care!

Related Topics >>