Programming Tutorials Browser Tutorials Articles Struts Tutorials Hibernate Tutorials

  Tutorial: Add ghosted drag images to your JTrees - JavaWorld Tips

Add ghosted drag images to your JTrees - JavaWorld Tips

Tutorial Details:

Java Tip 114: Add ghosted drag images to your JTrees
Java Tip 114: Add ghosted drag images to your JTrees
By: By Andrew J. Armstrong
What to do when your platform doesn't support drag images
dding drag-and-drop support to JTree s 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:
// Locate the JLabel's icon so you don't paint under it
Icon icon = lbl.getIcon();
int nStartOfText =
(icon == null) ? 0 : icon.getIconWidth()+lbl.getIconTextGap();
// Use DST_OVER to cause under-painting to occur
g2.setComposite(AlphaComposite.getInstance(AlphaComposite.DST_OVER, 0.5f));
// Use system colors to match the existing decor
g2.setPaint(new GradientPaint(nStartOfText, 0, SystemColor.controlShadow,
getWidth(), 0, new Color(255,255,255,0)));
// Paint under the JLabel's text
g2.fillRect(nStartOfText, 0, getWidth(), _imgGhost.getHeight());
// Finished with the graphics context now
g2.dispose();
Finally, wrap the TreeNode about to be dragged in a Transferable class and invoke startDrag() . Note that you still pass the drag image to the startDrag() method because the underlying platform uses it if it can.
// Wrap the path being transferred into a Transferable object
Transferable transferable = new CTransferableTreePath(path);
// Remember the path being dragged (you may want to delete it later)
_pathSource = path;
// Pass the drag image just in case the platform IS supports it
e.startDrag(null, _imgGhost, new Point(5,5), transferable, this);
DropTargetListener
All the drag-image painting happens in the DropTargetListener' s dragOver() method. You only need to draw the image if the platform does not already do it for you, which you find out by first calling the DragSource.isDragImageSupported() static method.
To draw the drag image, you must do at least two things: First, repaint the real estate the drag image last occupied. Note that simply calling repaint() won't work because it effectively delays the repainting, possibly until after you have drawn the new drag image, and therefore, erases all or part of it. You really must paint the area immediately, using the, you guessed it, paintImmediately() method.
Second, you draw the ghost image in its new location. Note that you draw the image the same distance away from the mouse pointer as when the node was first clicked.
Graphics2D g2 = (Graphics2D) getGraphics();
if (!DragSource.isDragImageSupported())
{
// Erase the last ghost image and cue line
paintImmediately(_raGhost.getBounds());
// Remember where you are about to draw the new ghost image
_raGhost.setRect(pt.x - _ptOffset.x, pt.y - _ptOffset.y,
_imgGhost.getWidth(), _imgGhost.getHeight());
// Draw the ghost image
g2.drawImage(_imgGhost,
AffineTransform.getTranslateInstance(_raGhost.getX(),
_raGhost.getY()),
null);
}
In addition, you create a ghosted cue line. A cue line is a line temporarily drawn under each prospective drop target, giving the user a better idea of which node is the current drop target.
// Get the drop target's bounding rectangle
Rectangle raPath = getPathBounds(path);
// Cue line bounds (2 pixels beneath the drop target)
_raCueLine.setRect(0, raPath.y+(int)raPath.getHeight(), getWidth(), 2);
g2.setColor(_colorCueLine); // The cue line color
g2.fill(_raCueLine); // Draw the cue line
To enable automatic expanding and collapsing of tree nodes as the user's mouse
hovers over them, you need to restart a timer each time the tree selection changes.
TreePath path = getClosestPathForLocation(pt.x, pt.y);
if (!(path == _pathLast))
{
_pathLast = path;
_timerHover.restart();
}
If the timer pops, then you toggle the expanded/collapsed state of the last selected tree path. The timer is set up in the DropTargetListener constructor. The following code kicks in after about 1,000 milliseconds (1 second) of loitering.
_timerHover = new Timer(1000, new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
_nLeftRight = 0; // Reset left/right movement trend
if (isRootPath(_pathLast))
return; // Do nothing if you are hovering over the root node
if (isExpanded(_pathLast))
collapsePath(_pathLast);
else
expandPath(_pathLast);
}
});
Our next design goal is to recognize some mouse gestures. If the user drags the mouse sufficiently far to the right, then the dragged tree node must be inserted under rather than after the drop target tree node. Similar, but reverse, logic applies to a sufficiently long drag to the left.
Your TreeModel must actually insert the node in the appropriate place. Our task here is to recognize the gesture and pass the hint to the model. You can do this in the dragOver() method after you determine that the mouse pointer has actually moved.
int nDeltaLeftRight = pt.x - _ptLast.x;
if ((_nLeftRight > 0 && nDeltaLeftRight < 0)
|| (_nLeftRight < 0 && nDeltaLeftRight > 0) )
_nLeftRight = 0;
_nLeftRight += nDeltaLeftRight;
I have found that on my system a 20-pixel horizontal movement equates to a "flick." The flick gesture sends a visual signal to the user by overlaying the ghost image with an arrow pointing to either the left or right as required. (Arrow images are built by the CArrowImage class, which saves having to ship image files with your application.)
if (_nLeftRight > 20)
{
g2.drawImage(_imgRight,
AffineTransform.getTranslateInstance(pt.x - _ptOffset.x,
pt.y - _ptOffset.y), null);
_nShift = +1;
}
else if (_nLeftRight < -20)
{
g2.drawImage(_imgLeft,
AffineTransform.getTranslateInstance(pt.x - _ptOffset.x,
pt.y - _ptOffset.y), null);
_nShift = -1;
}
else
_nShift = 0;
Your one-stop shop for drag and drop
This tip resolves an annoying inconsistency in the cross-platform implementation of drag and drop. Users increasingly expect strong visual feedback when performing drag-and-drop operations, especially when they doubt what will happen when they release the mouse button!
The code presented here is a one-stop shop for the following drag-and-drop features:
Ghosted drag images
Ghosted cue lines
Automatic node expand/collapse when hovering
Mouse gesture recognition
There is plenty of room for enhancing this code. For example, you can graphically represent a multiple-selection drag, animated drag images, snail trails, and other eye candy; I shall leave those to readers with more time on


 

Read Tutorial at: Click here to view the tutorial

Rate Tutorial:
Add ghosted drag images to your JTrees - JavaWorld Tips

View Tutorial:
Add ghosted drag images to your JTrees - JavaWorld Tips

Related Tutorials:

Enhance your Java application with Java Native Interface (JNI)
Enhance your Java application with Java Native Interface (JNI)
 
Create a scrollable virtual desktop in Swing
Create a scrollable virtual desktop in Swing
 
JSP best practices
Follow these tips for reusable and easily maintainable JavaServer Pages
 
Study guide Achieve strong performance with threads Part 1
Study guide Achieve strong performance with threads Part 1
 
Bridge the gap between Java and Twain
Bridge the gap between Java and Twain
 
Quickly access files and directories you use repeatedly
Quickly access files and directories you use repeatedly
 
Get the inside track on J2EE architect certification
Get the inside track on J2EE architect certification
 
Picture this
Picture this
 
Filtering and Transforming Digital Images
Filtering and Transforming Digital Images In this Issue Welcome to the Core Java Technologies Tech Tips for April 7, 2004. Here you\'ll get tips on using core Java technologies and APIs, such as those in Java 2 Platform, Standard Edition (J2SE).
 
Hermes JMS Browser
Hermes JMS Browser Hermes is a Swing application that allows you to interact with JMS providers.
 
JXMLPad 2.3
JXMLPad 2.3 JXMLPad is a pure Swing java component/framework for editing XML/XHTML document.
 
G (2D graphic library)
G is a generic graphics library built on top of Java 2D in order to make scene graph oriented 2D graphics available to client applications in a high level, easy to use way
 
Ruling Out: Rule Engines and Declarative Programming Come to Java
What practical gain can be found in researching rule engines? Is this just another round in the hype cycle, where writers like me talk up the newest "geegaw" technology and try to pawn it to the masses?
 
JXMLPad 3.1 FC
JXMLPad is a pure Swing java component/framework for editing XML/XHTML document.
 
Java Tech: Acquire Images with TWAIN and SANE, Part 1
Scanners, digital cameras, and other image-acquisition devices are part of the computing landscape. Despite their ubiquity, however, Java does not provide a standard API for interacting with these devices. And yet there certainly is a desire to have a sta
 
JTwain
JTwain will implement a Java interface to the the Win32 C DLL TWAIN acquire methods.
 
Portlet Community
Portlet Community If J2EE based portals, JSR 168 or WSRP mean anything to you, you have come to the right place.
 
Tech Tip: Using the Varargs Language Feature
Have you ever needed to pass in many instances of the same object type to a method, but you don't know at compile time how many instances there will be? Find out how the new varargs language feature makes it easy to handle situations like this.
 
Submit project to get developed
Submit project to get developed T his page contains answers to common questions handled by our support staff, along with some tips and tricks that we have found useful and presented here as questions. How do I Submit my Project ? Who can submit a
 
VolatileBufferedToolkitImage Strategies
Ever wondered what kind of image to use in your application? Or what method to use in creating it? This article attempts to address this challenging topic.
 
Site navigation
 

 

Send your comments, Suggestions or Queries regarding this site at roseindia_net@yahoo.com.

Copyright © 2006. All rights reserved.