JDK-8094457 : [SwingNode, DND] : drag-and-drop does not work
  • Type: Bug
  • Component: javafx
  • Sub-Component: swing
  • Affected Version: 8u5
  • Priority: P3
  • Status: Resolved
  • Resolution: Fixed
  • Submitted: 2014-05-15
  • Updated: 2017-09-21
  • Resolved: 2014-08-12
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
JDK 8
8u40Fixed
Related Reports
Relates :  
Relates :  
Relates :  
Relates :  
Description
Drag-and-drop within a SwingNode does not work. First drag has no effect, second drag creates this exception:

Exception in thread "AWT-EventQueue-0" java.awt.dnd.InvalidDnDOperationException: Drag and drop in progress
	at sun.awt.dnd.SunDragSourceContextPeer.setDragDropInProgress(Unknown Source)
	at java.awt.dnd.DragSource.startDrag(Unknown Source)
	at java.awt.dnd.DragSource.startDrag(Unknown Source)
	at java.awt.dnd.DragGestureEvent.startDrag(Unknown Source)
	at mint.demo.SwingFx$2$1.dragGestureRecognized(SwingFx.java:73)
	at java.awt.dnd.DragGestureRecognizer.fireDragGestureRecognized(Unknown Source)
	at sun.awt.windows.WMouseDragGestureRecognizer.mouseDragged(Unknown Source)
	at java.awt.Component.processMouseMotionEvent(Unknown Source)
	at javax.swing.JComponent.processMouseMotionEvent(Unknown Source)
	at java.awt.Component.processEvent(Unknown Source)
	at java.awt.Container.processEvent(Unknown Source)
	at java.awt.Component.dispatchEventImpl(Unknown Source)
	at java.awt.Container.dispatchEventImpl(Unknown Source)
	at java.awt.Component.dispatchEvent(Unknown Source)
	at java.awt.LightweightDispatcher.retargetMouseEvent(Unknown Source)
	at java.awt.LightweightDispatcher.processMouseEvent(Unknown Source)
	at java.awt.LightweightDispatcher.dispatchEvent(Unknown Source)
	at java.awt.Container.dispatchEventImpl(Unknown Source)
	at java.awt.Window.dispatchEventImpl(Unknown Source)
	at java.awt.Component.dispatchEvent(Unknown Source)
	at java.awt.EventQueue.dispatchEventImpl(Unknown Source)
	at java.awt.EventQueue.access$400(Unknown Source)
	at java.awt.EventQueue$3.run(Unknown Source)
	at java.awt.EventQueue$3.run(Unknown Source)
	at java.security.AccessController.doPrivileged(Native Method)
	at java.security.ProtectionDomain$1.doIntersectionPrivilege(Unknown Source)
	at java.security.ProtectionDomain$1.doIntersectionPrivilege(Unknown Source)
	at java.awt.EventQueue$4.run(Unknown Source)
	at java.awt.EventQueue$4.run(Unknown Source)
	at java.security.AccessController.doPrivileged(Native Method)
	at java.security.ProtectionDomain$1.doIntersectionPrivilege(Unknown Source)
	at java.awt.EventQueue.dispatchEvent(Unknown Source)
	at java.awt.EventDispatchThread.pumpOneEventForFilters(Unknown Source)
	at java.awt.EventDispatchThread.pumpEventsForFilter(Unknown Source)
	at java.awt.EventDispatchThread.pumpEventsForHierarchy(Unknown Source)
	at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
	at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
	at java.awt.EventDispatchThread.run(Unknown Source)

See example in first comment. The problem can be reproduced by dragging the label two times.
Comments
The JDK part of the fix is likely to be delivered to 8u40b02.
14-08-2014

The fix is now also in JDK 8u-dev: http://hg.openjdk.java.net/jdk8u/jdk8u-dev/jdk/rev/9f242c8434b0
12-08-2014

[SQE] The fix delegates certain DnD-related operations to JavaFX when a DnD operation is started on a Swing component that is added to a SwingNode JavaFX node. It also provides support for a DropTarget so that you can drop things onto a Swing component within a SwingNode node. In order to test this fix, please use the manual test from the description of this issue. Note that you will need to use a JDK build that contains a fix for JDK-8049065. However, it is also worth testing a JDK w/o the fix + FX with the fix, and JDK w/ the fix + FX w/o the fix. In either combinations tests should not crash or produce any unexpected results. Obviously, the DnD will only work if both JDK and FX contain the fix. [/SQE]
12-08-2014

JDK changeset: http://hg.openjdk.java.net/jdk9/client/jdk/rev/750ef1bf8b2c (will be back-ported to 8u-dev soon) FX: Changeset: 14063a5dd1e1 Author: Anthony Petrov <anthony.petrov@oracle.com> Date: 2014-08-12 14:36 +0400 URL: http://hg.openjdk.java.net/openjfx/8u-dev/rt/rev/14063a5dd1e1
12-08-2014

Anthony, the fix looks fine for me in general, however I can't judge on the details of DnD as I'm not an expert in it.
06-08-2014

Petr, Anton, Artem, Steve, Please review the fix: JDK: http://cr.openjdk.java.net/~anthony/9-5.2/ FX: http://cr.openjdk.java.net/~anthony/g-522-swingNodeDnD-RT-37149.3/ JavaFX implements the DragSourceContextPeer and DragGestureRecognizer so that SwingNode content could pose as a drag source. In order to support a drop target, the DropTargetContextPeer is implemented in SwingNode and the add/removeDropTarget() methods register/unregister the drop target listeners. The changes in JDK are mostly technical. We simply delegate the Toolkit.createDragSourceContextPeer() and Toolkit.createDragGestureRecognizer() factory methods to SwingNode. Similarly, we delegate the DropTargetPeer.add/removeDropTarget() operations to SwingNode as well. In FX I've factored out the CachingTransferable class from the SwingDragSource class so as to share the implementation with the SwingNode DnD support. Also I've added a few utility methods to DataFlavorUtils and SwingFXUtils. The main fix is the new code in FXDnD.java which actually implements the AWT interfaces, installs appropriate event handlers on the SwingNode node in FX, and handles all the DnD events. Note that the JDK <-> FX interface is loose because I use default methods in the LightweightContent interface, so that you can run new FX with the old JDK, or old FX with the new JDK, and nothing should break. Obviously, the DnD in SwingNode will only work if both JDK and FX are patched. I've tested these changes on Windows and Mac with the sample code from this JIRA as well as a JFXPanel DnD test application from RT-34283. The DnD works fine for me in both intra- and inter-process modes.
18-07-2014

A draft, PoC version of the Drop Target support: JDK: http://cr.openjdk.java.net/~anthony/9-5.1/ FX: http://cr.openjdk.java.net/~anthony/g-522-swingNodeDnD-RT-37149.2/ Unfinished: 1. Mapping between FX's DataFormat <-> long <-> AWT's DataFlavor objects. Currently no data is being transferred at all since I hard-code "new long[0]". 2. Proper handling of supported/accepted drop actions when processing drop target events.
16-07-2014

Up-to-date fix: http://cr.openjdk.java.net/~anthony/g-522-swingNodeDnD-RT-37149.1/ Support for Drag Source is complete and the code is more or less clean. The drag cursor ("drag image") isn't supported yet. Note that JFXPanel doesn't support it either (see RT-35812). So currently we are not going to implement it for SwingNode, too. Now I'll be implementing support for Drop Target and testing the changes on other platforms, after which I'll be sending the fix for review.
15-07-2014

Here's a work-in-progress patches: JDK: http://cr.openjdk.java.net/~anthony/9-5.0/ FX: http://cr.openjdk.java.net/~anthony/g-522-swingNodeDnD-RT-37149.0/ The code is extremely raw. Even the dragged content is hard-coded. However, with these patches I can successfully drag a string from SwingNode and drop it to a Cygwin console window, and the text gets transferred correctly. So the code is good as a proof of concept. What remains to be done: 1. Actually take into account the data put by the app code to the dragboard. 2. Implement support for DropTarget. 3. Support platforms other than Windows (merely a technical change needed in JDK. The 99,(9)% of the code is platform-independent anyway) 4. Clean up the code.
10-07-2014

Apparently the fix will require changes in JDK in order to allow the SwingNode/JLightweitFrame to create a custom DragGestureRecognizer object and custom DragSourceContext/DragSourceContextPeer objects. I've filed a JDK bug to track the necessary changes: https://bugs.openjdk.java.net/browse/JDK-8049065
02-07-2014

(There's no hw component underneath.) Well, you may be right (would be fine if someone tries it). I just wanted to convey that we didn't specially implement dnd for SwingNode, unlike JFXPanel.
22-05-2014

SwingNode is just an FX node. When the mouse is pressed, the mouse event must be delivered from FX to AWT/Swing code running somewhere. If the mouse event was to trigger a drag and drops, this should just be started in the normal way inside AWT/Swing. I guess that there is no heavyweight AWT component underneath and this is needed for drag and drop? I should just go look at the implementation. I am not suggesting that it will work for free or thinking that we should not defer this bug because no work is required.
21-05-2014

Steve, what do you mean? SwingNode, as an FX node, can't tell the outer world what (swing component) the user is going to drag from it. This is just not implemented...
21-05-2014

Should not the outer world just see this as DND from another process?
21-05-2014

Steve, DnD inside SwingNode should work, as we forward all the "raw" events to Swing which makes DnD events out of them. DnD b/w SwingNode and the outer world is not yet supported.
21-05-2014

Anthony, could we at least get rid of the exception (if easy)? Anton, what can you tell me about DND in SwingNode? For JFXPanel and FXCanvas, DND needed to be implemented by the host widget toolkit. For SwingNode, isn't everything also implemented by the host toolkit (Swing) and we are essentially telling Swing to render to an image. Are we also forwarding events from FX to Swing? It seems like it.
20-05-2014

I'm afraid it's too late for 8u20 to implement support for drag and drop in SwingNode. For one, we're already past the feature freeze for that release, and for another, this is a big task and we don't have enough resources to implement it in time. We'll have to re-target this issue to a future release.
20-05-2014

It looks like support for DnD is simply not implemented in SwingNode at all, so currently this is not supposed to work.
20-05-2014

I can reproduce the bug. It looks as if the DnD gesture never really finishes/gets canceled, and so when the second gesture is started, AWT rightfully complains.
20-05-2014

import java.awt.Color; import java.awt.datatransfer.StringSelection; import java.awt.dnd.DnDConstants; import java.awt.dnd.DragGestureEvent; import java.awt.dnd.DragGestureListener; import java.awt.dnd.DragGestureRecognizer; import java.awt.dnd.DragSource; import java.awt.dnd.DropTarget; import java.awt.dnd.DropTargetAdapter; import java.awt.dnd.DropTargetDropEvent; import java.awt.dnd.DropTargetListener; import javafx.application.Application; import javafx.beans.InvalidationListener; import javafx.beans.Observable; import javafx.embed.swing.SwingNode; import javafx.scene.Scene; import javafx.scene.layout.BorderPane; import javafx.stage.Stage; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.SwingUtilities; public class SwingFx extends Application { private DragSource dragSource; private DropTarget dropTarget; private DropTargetListener dropListener; private DragGestureListener dgListener; private DragGestureRecognizer dragRecognizer; public static void main(String[] args) { Application.launch(args); } @Override public void start(Stage stage) { final SwingNode swingNode = new SwingNode(); createSwingContent(swingNode); BorderPane pane = new BorderPane(); pane.setCenter(swingNode); stage.setTitle("Swing in JavaFX"); stage.setScene(new Scene(pane, 250, 150)); stage.onCloseRequestProperty().addListener(new InvalidationListener(){ @Override public void invalidated(Observable observable) { System.exit(0); } }); stage.show(); } private void createSwingContent(final SwingNode swingNode) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { JPanel panel = new JPanel(); panel.setBackground(Color.RED); panel.add(new JLabel("Drag me!")); swingNode.setContent(panel); dgListener = new DragGestureListener() { @Override public void dragGestureRecognized(DragGestureEvent dge) { System.out.println("drag recognized..."); dge.startDrag(java.awt.Cursor.getPredefinedCursor(java.awt.Cursor.HAND_CURSOR), new StringSelection("some string data")); } }; dropListener = new DropTargetAdapter() { @Override public void drop(DropTargetDropEvent dtde) { //this is never called =( System.out.println("drop... " + dtde.getTransferable()); } }; dragSource = new DragSource(); dragRecognizer = dragSource.createDefaultDragGestureRecognizer(panel, DnDConstants.ACTION_COPY_OR_MOVE, dgListener); dropTarget = new DropTarget(panel, dropListener); System.out.println(dragRecognizer); System.out.println(dropTarget); } }); } }
15-05-2014