Starting a system drag on a mouseExited event

Exit Drag

Swing’s drag-and-drop implementation is pretty straightforward for the common cases—if you’re working with standard components, you might not have to do any work at all to get basic drag-and-drop support. However, once you get to the point of wanting to do interesting nonstandard things, it’s easy to get tangled up in the web of interrelated UI classes, many of which can’t just be constructed.

Case in point: in certain parts of my application, I’d like item dragging to be rendered by my component up until the point at which the cursor crosses the component’s boundary. It’s simple to start a system drag when at the point at which the user clicks the cursor on an item—but I want the system drag to start only when the component gets a mouseExited() during an in-component drag. After a bit of experimentation, I found that I could accomplish this using a custom, somewhat corrupt DragGestureRecognizer (application-specific code indicated by $dollar signs$):

private class ExitDragGestureRecognizer 
    extends DragGestureRecognizer {
    public ExitDragGestureRecognizer(Component comp) {
        super(DragSource.getDefaultDragSource(), comp);
    }

    public synchronized int getSourceActions() {
        return DnDConstants.ACTION_COPY_OR_MOVE;
    }

    protected void registerListeners() {}

    protected void unregisterListeners() {}

    public void setEvent(InputEvent e) {
        resetRecognizer();
        appendEvent(e);
    }
}

private ExitDragGestureRecognizer dgr = 
    new ExitDragGestureRecognizer($myPanel$);

Then, in the component’s attached MouseListener, the following code transfers from a local to a system drag when the cursor hits the component boundary:

public void mouseExited(MouseEvent e) {
    if ($inLocalDrag()$) {
        Transferable trans = $getDragTransferable()$;
        DragSource source = DragSource.getDefaultDragSource();
        DragGestureEvent event = 
            new DragGestureEvent(dgr, DnDConstants.ACTION_COPY,
            $localDragStartPoint$, Collections.singletonList(e))
        dgr.setEvent(e);
        source.startDrag(event, DragSource.DefaultCopyDrop, 
            trans, null);

        $endLocalDrag()$;
    }
}

In my testing, this seemed to work under J2SE 1.4+ on Windows XP and Mac OS X 10.3.7. It’s really not all that difficult, but the only way I was able to figure it out was just by guessing at calls and seeing where they failed. If anyone knows of a good in-depth Swing reference that talks addresses this sort of thing, please let me know.

Leave a Reply

Your email address will not be published.

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>