Newsletter sign-up
View all newsletters

Enterprise Java Newsletter
Stay up to date on the latest tutorials and Java community news posted on JavaWorld

Java Tip 114: Add ghosted drag images to your JTrees

What to do when your platform doesn't support drag images

  • Digg
  • Reddit
  • SlashDot
  • Stumble
  • del.icio.us
  • Technorati
  • dzone
Adding drag-and-drop support to JTrees has been covered already in Java Tip 97: Add Drag and Drop to Your JTrees. I encourage you to read that tip first to get a good understanding of the drag-and-drop paradigm.

The code I present in this tip has the following design goals:

  • Ensures a drag image displays even when the underlying platform does not do so (but lets the platform draw the image if it can)
  • Draws a "cue line" under the drop target to indicate where the drop will occur
  • Draws the drag image as a "ghosted" version of the JTree node being dragged
  • Automatically expands or collapses the drop target when the user mouses over the target
  • Recognizes some mouse gestures (right flick and left flick), which the model could then use to shift JTree nodes right and left without keyboard interaction


As a bonus, I have included support for the Autoscroll interface so that the JTree content is automatically scrolled when the mouse nears the viewport's edge during a drag operation. This is implemented as described in Eckstein, Loy, and Wood's Java Swing, which explains the autoscrolling process adequately, so I won't elaborate here.

Arguably, these capabilities should all be part of a Swing look-and-feel implementation, but I found them easier to implement as part of a JTree class extension. In spite of the number of goals, the actual code involved is quite minimal.

Figure 1 is an example of what you would see during a drag operation after implementing this tip -- the food folder is about to be dropped after the football node.

Figure 1. Screenshot of CTree drag image in action

To emulate this JTree behavior, you must change both the DragGestureListener and the DropTargetListener classes.

DragGestureListener

You add code to the DragGestureListener's dragGestureRecognized() method to figure out which JTree node is being dragged, and then build a BufferedImage to represent the node during the drag operation.

First, find out where the mouse is clicked relative to the selected tree node's bounding rectangle. You'll need this later to keep the drag image positioned at the same distance from the mouse pointer as the node is being dragged.

    Point ptDragOrigin = e.getDragOrigin();
    TreePath path = getPathForLocation(ptDragOrigin.x, ptDragOrigin.y);
    Rectangle raPath = getPathBounds(path);
    m_ptOffset.setLocation(ptDragOrigin.x-raPath.x, ptDragOrigin.y-raPath.y);


Now, ask the tree cell renderer (if you are using the DefaultTreeCellRenderer, the renderer is a JLabel) to render itself into a BufferedImage, using a Graphics2D graphics context set up to create a semi-transparent image. The result is a ghosted version of the original tree node that won't interfere with the ability to see the underlying JTree nodes as they are dragged over.

    // Get the tree cell renderer
    JLabel lbl = (JLabel) getCellRenderer().getTreeCellRendererComponent
    (
        this,                                           // tree
        path.getLastPathComponent(),                    // value
        false,                                          // isSelected
        isExpanded(path),                               // isExpanded
        getModel().isLeaf(path.getLastPathComponent()), // isLeaf
        0,                                              // row
        false                                           // hasFocus
    );
    // The layout manager normally does this...
    lbl.setSize((int)raPath.getWidth(), (int)raPath.getHeight()); 
    // Get a buffered image of the selection for dragging a ghost image
    _imgGhost = new BufferedImage
    (
        (int)raPath.getWidth(), 
        (int)raPath.getHeight(), 
        BufferedImage.TYPE_INT_ARGB_PRE
    );
    // Get a graphics context for this image    
    Graphics2D g2 = _imgGhost.createGraphics();
    // Make the image ghostlike
    g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC, 0.5f));
    // Ask the cell renderer to paint itself into the BufferedImage
    lbl.paint(g2);


To make the ghosted JLabel look more flash, you also paint under the text with a GradientPaint that fades from left to right:

  • Digg
  • Reddit
  • SlashDot
  • Stumble
  • del.icio.us
  • Technorati
  • dzone
Comment
Login
Forgot your account info?
Add comment
Anonymous comments subject to approval. Register here for member benefits.
Have a JavaWorld account? Log in here. Register now for a free account.
Resources