drag-n-drop: why it doesn't work

Firstly, a warning: Sun’s official tutorial on 1.4 dnd is wrong, and presents source code that I know the author never tested because it could never have worked. In particular, the code in this section: http://java.sun.com/docs/books/tutorial/uiswing/misc/dnd.html#customComp won’t work because it includes some methods from MouseListener, but the component neither implements that interface nor does it invoke addMouseListener. And the whole algorithm presented requires the MouseListener in order to execute.

Secondly, if you get to the point where you’re thinking “I can’t seem to implement the method they want me to implement that will encode the exported data as a Transferable” then you are correct: Some god-forsaken person designed this part so that it doesn’t tell you what x and y the mouse was at when it initiated the drag…so, any source Component with multiple internal pieces of data has no way of knowing what item in the list/tree/etc was dragged (hence JTree has to wait until something is selected, and then just re-use the last selection - this is one of the highly-voted-for bugs on the bug parade, and instead of faffing with selection mechanisms and “handler precedence” (the official reasons for taking 3 years not to fix it), it looks like they could just fix the API and it would then become relatively trivial to fix JTree).

It’s so stupid an omission you have to laugh (or else you’d cry :(),

[emote]wonders how long it’ll be before java has real drag-n-drop support[/emote]

Anyway… the best workaround I’ve seen is to be a bit non-object-oriented and have backdoors between your components so that they can sneakily share the information that the clever dnd API refuses to let them have. When you’re initiating DND in your source component, using a MouseMotionListener.mouseDragged method, calculate the item currently pointed to by the MouseEvent that started the drag, and save it into an instance variable. Then, when your TransferHandler is asked to generate a Transferable, it reads the aforementioned variable (either directly or indirectly via your component) to get a handle to the data which it can then wrap into the Transferable.

(if anyone’s got a better one, please let me know - and CC: it to the swing team, and just maybe they might use it to fix up JTree etc).

FYI, I’ve only belatedly sorted all this out because it’s only on my N-th attempt to use D-n-D that I got around to fully exercising the API and finally fully understanding what was really going on. All previous apps where I’ve used it I’ve beaten about the bush, and just hacked until it worked. I’m hoping this advice might help some people (since it looks like Sun won’t have the bugs sorted for some time to come yet; they need to re-think the API really. It’s also kind of tragic that one of their tutorials is a blank page, and the other wouldn’t execute if you tried).

PS another thing that will have you scratching your head…

How do I find the (x,y) point in the dropped-on component where the user let go of the mouse?

(e.g. you are dropping objects onto an image, and want to position the new objects where the mouse was when the user let go).

Answer: you can’t, because they “forgot” to include this option. Duh.

You can see at what point Sun fixes this by checking:

http://developer.java.sun.com/developer/bugParade/bugs/4468566.html

(sorry - login required!)

EDIT: I mean “can’t…without more hacks”. It’s actually rahter complicated to get arond this one…if you’d like to know how, message me and I’ll write a tutorial (it’s worthy of a tutorial!)

[quote]How do I find the (x,y) point in the dropped-on component where the user let go of the mouse?
[/quote]
I may be misunderstanding you here but I didn’t think that this was a problem? DropTargetDropEvent.getLocation does this doesn’t it?

Although its a long time since I was working with DnD and I remember it being a complete bitch so I’m probably wrong here…

The new API design doesn’t include DTDE and friends; appropriately, Sun’s tutorial on DND doesn’t even mention their existence.

I basically did a load of debugging and tracking of when references become valid / invalid to infer how the DTD classes and interfaces currently work in co-operation with the TransferHandler and friends, and what the flow of control and data is. It’s obvious once you know how, but it’s also not documented within the API, so…

No DTDE? So what, its just earmarked as depreciated and going to be phased out? My Swing book on 1.4 describes and uses it so I assumed it was the ‘proper’ way of doing things…

[quote]No DTDE? So what, its just earmarked as depreciated and going to be phased out? My Swing book on 1.4 describes and uses it so I assumed it was the ‘proper’ way of doing things…
[/quote]
Not deprecated, I believe it’s still essential to the way it’s all implemented (but I haven’t checked).

It’s @since 1.2, and the less-painful stuff that 1.4 introduced appears to be an attempt to slowly phase it out guesswork.

But, who knows? The API docs are sparse (and don’t contain an overall explanation of WTF is going on), the long-promised trail has been cancelled by Sun, and their own tutorial doesn’t work :).

I just happen to have got it all to work successfully on a number of occasions, usually different subsets at a time, and only recently understood the whole picture - and thought my experiences could be useful to other people scratching their heads :). There are a few stupid design decisions in there (like with Jtree), which make it hard to come up with good overall design patterns for using the API’s (if you only need a small part, e.g. “drop only” or “drag only”, then it’s easier, but still not particularly easy IME). Once you’ve got the patterns, it all works fine.