X

Geertjan's Blog

  • July 12, 2006

Getting Around isDragImageSupported() on Windows

Geertjan Wielenga
Product Manager
In the comments at the end of New NetBeans Drag And Drop Samples, Stephen writes (among other things): "there is no icon visible while dragging the Simpsons around". That's one of the things that hadn't yet been implemented in the samples. And, in fact, as pointed out in yesterday's blog entry, that is one of the things I've been wanting to implement for the Music NotePad. And today I worked out how to do it, although it isn't very simple. There's a lot of discussion on this topic of drag images, as will be seen a bit later in this blog entry. First, evidence of the fact that I have managed to implement drag images! Here you see a note in the process of being dragged, the cursor has become a hand and the note itself is now the drag image, so that you can see the note that you're moving and so that it seems that the same note that you've started dragging is also the one that's dropped (although, they're different instances of the same image, but that's transparent to the end user):

Where I started trying to implement the drag image is in the same line of code where I changed the cursor yesterday:

dge.startDrag( Cursor.getPredefinedCursor(Cursor.HAND_CURSOR), displayer.getData().getBigImage(), 
dge.getComponent().getLocation(),
new ItemDataTransferable( displayer.getData() ),
dragSourceListener);

However, this doesn't work. Why? Because I'm using Windows! Really, that's the reason. I found this out by chance while reading the Java Drag and Drop FAQ. One of the questions there is "How do I make the drag image work?" And the answer starts like this: "You are probably using Windows if you can't. Ask the guys in Redmond why you can't." Funny FAQ writers. Then I came across Shannon Hickey's Blog where, in the comments at the end, there's a bit of discussion about this problem.

So, I thought I was basically stuck. Windows doesn't support the drawing of drag images (see Shannon Hickey's blog above for details). However, a glimmer of hope shone through the clouds when I came across Java Tip 114: Add ghosted drag images to your JTrees. It turns out that even though Windows doesn't draw the image for you, you can draw it yourself! I extrapolated from some code I found in that article and now my dragOver() method looks like this (note the comments in the code):

private void doDragOver( DropTargetDragEvent dtde ) {
if( dtde.isDataFlavorSupported( Utils.MY_DATA_FLAVOR ) ) {
//only accept object of our type
dtde.acceptDrag( DnDConstants.ACTION_COPY_OR_MOVE );
} else {
//reject everything else
dtde.rejectDrag();
}
if (!DragSource.isDragImageSupported()) {//Step 1: Get the image from the transferable:
MyItemData data = null;
try {
data = (MyItemData) dtde.getTransferable().getTransferData(Utils.MY_DATA_FLAVOR);
} catch (IOException ex) {
ex.printStackTrace();
} catch (UnsupportedFlavorException ex) {
ex.printStackTrace();
}
ItemDataDisplayer displayer = new ItemDataDisplayer( data );
Image dragImage = displayer.getData().getBigImage();//Step 2: Paint the drag image at the right place,
//and paint it immediately, otherwise it'll be drawn over
//and over again, which is fun but not very useful:

Graphics2D g2 = (Graphics2D) getGraphics();
Rectangle visRect = getVisibleRect();
paintImmediately(visRect.x, visRect.y, visRect.width, visRect.height);
g2.drawImage(dragImage,
AffineTransform.getTranslateInstance(dtde.getLocation().getX(),
dtde.getLocation().getY()),
null);
}
}

What's kind of indicative of how entrenched this problem is is the fact that there's an actual isDragImageSupported() method! The method reports whether drag images are supported by the platform (and, as pointed out, most Windows platforms don't).

But, anyway, I now have the drag image working and that makes it much easier to put a note on the desired line in the music sheet.

Join the discussion

Comments ( 7 )
  • Ken Langer Wednesday, July 12, 2006
    This looks interesting. You should put these examples out as WebStart apps. More people would probably play with them.
  • Geertjan Thursday, July 13, 2006
    Hi Ken. Thanks for your interest. I have just open sourced this project, so that is one avenue you can use to access the sources (blogged about it today). I do intend to make some/all samples available via WebStart, just need to work out how to do that.
  • Timothy Wall Thursday, July 13, 2006
    I'm not sure exactly how netbeans encapsulates drag/drop operations, but here is a web-startable demo without any external dependencies which allows the drag source to provide an appropriate image which is drawn wherever it is dragged. The drop handler can concentrate on painting where on the drop target the item will be positioned if dropped.
  • Geertjan Friday, July 14, 2006
    Hi Timothy, thanks a lot for this info and link. What I read in the link is going to be really helpful.
  • Fotis Friday, October 20, 2006
    What will happen when the drag location(mouse) is right up at the border on the right side, the draggin image will not be completely painted.
    Any way to overcome this?
  • Richard Hauswald Monday, September 14, 2009

    Whats about saving the image position into member variables and only draw if the position changed? I noticed that the image "flickers" if the mouse is not moving and doing so solved the problem.


  • Richard Hauswald Monday, September 14, 2009

    public class DragImagePainter {

    private final AlphaComposite ALPHA_COMPOSITE = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.5f);

    private final JComponent rootComponent;

    private final StoryListPanel storyListPanel;

    private final int offsetY;

    private final int imageWidth;

    private Point lastKnownCursorPosition;

    public DragImagePainter(JComponent rootComponent, StoryListPanel storyListPanel) {

    this.rootComponent = rootComponent;

    this.storyListPanel = storyListPanel;

    this.offsetY = -15;

    this.imageWidth = 50;

    }

    public void paintDragImage(int currentCursorPositionX, int currentCursorPositionY, Graphics2D rootPaneGraphics) {

    boolean imagePositionChanged = imagePositionChanged(currentCursorPositionX, currentCursorPositionY);

    if (imagePositionChanged) {

    double scaleFactor = (double) imageWidth / storyListPanel.getWidth();

    int imageHeight = calculateImageHeight(scaleFactor);

    if (!isFirstRun())

    repaintLastKnownImageArea(imageHeight);

    paintDragImage(currentCursorPositionX, currentCursorPositionY, scaleFactor, rootPaneGraphics);

    lastKnownCursorPosition = new Point(currentCursorPositionX, currentCursorPositionY);

    }

    }

    private int calculateImageHeight(double scaleFactor) {

    return (int) Math.ceil(storyListPanel.getHeight() \* scaleFactor);

    }

    boolean imagePositionChanged(int currentCursorPositionX, int currentCursorPositionY) {

    return isFirstRun() || lastKnownCursorPosition.x != currentCursorPositionX

    || lastKnownCursorPosition.y != currentCursorPositionY;

    }

    void repaintLastKnownImageArea(int height) {

    rootComponent.paintImmediately(lastKnownCursorPosition.x, lastKnownCursorPosition.y + offsetY, imageWidth,

    height);

    }

    boolean isFirstRun() {

    return lastKnownCursorPosition == null;

    }

    void paintDragImage(int currentCursorPositionX, int currentCursorPositionY, double scaleFactor,

    Graphics2D rootPaneGraphics) {

    Graphics2D dragPictureGraphics = (Graphics2D) rootPaneGraphics.create();

    dragPictureGraphics.translate(currentCursorPositionX, currentCursorPositionY + offsetY);

    dragPictureGraphics.scale(scaleFactor, scaleFactor);

    dragPictureGraphics.setComposite(ALPHA_COMPOSITE);

    storyListPanel.paint(dragPictureGraphics);

    }

    }


Please enter your name.Please provide a valid email address.Please enter a comment.CAPTCHA challenge response provided was incorrect. Please try again.Captcha