changeset 6491:c7f043d40430 8u20-b06

RT-34283: [Swing] Drag&Drop with interop on Mac OS X between two processes does not transfer data Summary: Cache the drop result in dragOver() handler, and use it in drop() to fetch data Reviewed-by: pchelko
author Anthony Petrov <anthony.petrov@oracle.com>
date Mon, 17 Mar 2014 16:56:40 +0400
parents 73f0beec1171
children f7a135396a19 c9411d50a822 7339a5bb9864
files modules/swing/src/main/java/javafx/embed/swing/DataFlavorUtils.java modules/swing/src/main/java/javafx/embed/swing/SwingDnD.java modules/swing/src/main/java/javafx/embed/swing/SwingDragSource.java
diffstat 3 files changed, 59 insertions(+), 50 deletions(-) [+]
line wrap: on
line diff
--- a/modules/swing/src/main/java/javafx/embed/swing/DataFlavorUtils.java	Mon Mar 17 05:28:19 2014 -0700
+++ b/modules/swing/src/main/java/javafx/embed/swing/DataFlavorUtils.java	Mon Mar 17 16:56:40 2014 +0400
@@ -74,6 +74,10 @@
                                   final String mimeType,
                                   final Object swingData)
     {
+        if (swingData == null) {
+            return swingData;
+        }
+
         if (flavor.isFlavorJavaFileListType()) {
             // RT-12663
             final List<File> fileList = (List<File>)swingData;
@@ -174,13 +178,20 @@
         return obj;
     }
 
+    /**
+     * Returns a Map populated with keys corresponding to all the MIME types
+     * available in the provided Transferable object. If fetchData is true,
+     * then the data is fetched as well, otherwise all the values are set to
+     * null.
+     */
     static Map<String, Object> readAllData(final Transferable t,
-                                           final Map<String, DataFlavor> fxMimeType2DataFlavor)
+                                           final Map<String, DataFlavor> fxMimeType2DataFlavor,
+                                           final boolean fetchData)
     {
         final Map<String, Object> fxMimeType2Data = new HashMap<>();
         for (DataFlavor flavor : t.getTransferDataFlavors()) {
-            Object obj = readData(t, flavor);
-            if (obj != null) {
+            Object obj = fetchData ? readData(t, flavor) : null;
+            if (obj != null || !fetchData) {
                 String mimeType = getFxMimeType(flavor);
                 obj = adjustSwingData(flavor, mimeType, obj);
                 fxMimeType2Data.put(mimeType, obj);
@@ -189,8 +200,8 @@
         for (Map.Entry<String, DataFlavor> e: fxMimeType2DataFlavor.entrySet()) {
             String mimeType = e.getKey();
             DataFlavor flavor = e.getValue();
-            Object obj = readData(t, flavor);
-            if (obj != null) {
+            Object obj = fetchData ? readData(t, flavor) : null;
+            if (obj != null || !fetchData) {
                 obj = adjustSwingData(flavor, mimeType, obj);
                 fxMimeType2Data.put(e.getKey(), obj);
             }
--- a/modules/swing/src/main/java/javafx/embed/swing/SwingDnD.java	Mon Mar 17 05:28:19 2014 -0700
+++ b/modules/swing/src/main/java/javafx/embed/swing/SwingDnD.java	Mon Mar 17 16:56:40 2014 +0400
@@ -62,6 +62,7 @@
 import java.awt.dnd.DropTargetDropEvent;
 import java.awt.dnd.DropTargetEvent;
 import java.awt.dnd.DropTargetListener;
+import java.awt.dnd.InvalidDnDOperationException;
 
 import java.awt.event.InputEvent;
 import java.awt.event.MouseAdapter;
@@ -123,6 +124,8 @@
         };
 
         DropTargetListener dtl = new DropTargetAdapter() {
+            private TransferMode lastTransferMode;
+
             @Override
             public void dragEnter(final DropTargetDragEvent e) {
                 // This is a temporary workaround for JDK-8027913
@@ -132,7 +135,7 @@
 
                 assert swingDragSource == null;
                 swingDragSource = new SwingDragSource();
-                swingDragSource.updateContents(e);
+                swingDragSource.updateContents(e, false);
 
                 assert fxDropTarget == null;
                 // Cache the Transferable data in advance, as it cannot be
@@ -143,10 +146,10 @@
                 final Point orig = e.getLocation();
                 final Point screen = new Point(orig);
                 SwingUtilities.convertPointToScreen(screen, comp);
-                final TransferMode dr = fxDropTarget.handleDragEnter(
+                lastTransferMode = fxDropTarget.handleDragEnter(
                         orig.x, orig.y, screen.x, screen.y,
                         dropActionToTransferMode(e.getDropAction()), swingDragSource);
-                applyDragResult(dr, e);
+                applyDragResult(lastTransferMode, e);
             }
 
             @Override
@@ -157,43 +160,34 @@
                     fxDropTarget.handleDragLeave();
                 } finally {
                     endDnD();
+                    lastTransferMode = null;
                 }
             }
 
             @Override
             public void dragOver(final DropTargetDragEvent e) {
                 assert swingDragSource != null;
-                // We cache Transferable data in advance, as we can't fetch
-                // it from drop(), see comments in drop() below. However,
-                // caching in every dragOver() is too expensive and also for
-                // some reason has a weird side-effect: e.acceptDrag() is
-                // ignored, and no drops are possible. So the workaround is
-                // to cache all the data in dragEnter() only
-                //
-                // swingDragSource.updateContents(e);
+                swingDragSource.updateContents(e, false);
 
                 assert fxDropTarget != null;
                 final Point orig = e.getLocation();
                 final Point screen = new Point(orig);
                 SwingUtilities.convertPointToScreen(screen, comp);
-                final TransferMode dr = fxDropTarget.handleDragOver(
+                lastTransferMode = fxDropTarget.handleDragOver(
                         orig.x, orig.y, screen.x, screen.y,
                         dropActionToTransferMode(e.getDropAction()));
-                applyDragResult(dr, e);
+                applyDragResult(lastTransferMode, e);
             }
 
             @Override
             public void drop(final DropTargetDropEvent e) {
                 assert swingDragSource != null;
-                // Don't call updateContents() from drop(). In AWT, it is possible to
-                // get data from the Transferable object in drop() only after the drop
-                // has been accepted. Here we first let FX handle drop(), then accept
-                // or reject AWT drop based the result. So instead of querying the
-                // Transferable object, we use data from swingDragSource, which was
-                // cached in dragEnter(), but not in dragOver(), see comments in
-                // dragOver() above
-                //
-                // swingDragSource.updateContents(e);
+
+                // This allows the subsequent call to updateContents() to
+                // actually fetch the data from a drag source. The actual
+                // and final drop result may be redefined later.
+                applyDropResult(lastTransferMode, e);
+                swingDragSource.updateContents(e, true);
 
                 final Point orig = e.getLocation();
                 final Point screen = new Point(orig);
@@ -201,13 +195,22 @@
 
                 assert fxDropTarget != null;
                 try {
-                    final TransferMode dropResult = fxDropTarget.handleDragDrop(
+                    lastTransferMode = fxDropTarget.handleDragDrop(
                             orig.x, orig.y, screen.x, screen.y,
                             dropActionToTransferMode(e.getDropAction()));
-                    applyDropResult(dropResult, e);
-                    e.dropComplete(dropResult != null);
+                    try {
+                        applyDropResult(lastTransferMode, e);
+                    } catch (InvalidDnDOperationException ignore) {
+                        // This means the JDK doesn't contain a fix for 8029979 yet.
+                        // DnD still works, but a drag source won't know about
+                        // the actual drop result reported by the FX app from
+                        // its drop() handler. It will use the dropResult from
+                        // the last call to dragOver() instead.
+                    }
                 } finally {
+                    e.dropComplete(lastTransferMode != null);
                     endDnD();
+                    lastTransferMode = null;
                 }
             }
         };
--- a/modules/swing/src/main/java/javafx/embed/swing/SwingDragSource.java	Mon Mar 17 05:28:19 2014 -0700
+++ b/modules/swing/src/main/java/javafx/embed/swing/SwingDragSource.java	Mon Mar 17 16:56:40 2014 +0400
@@ -48,34 +48,21 @@
     SwingDragSource() {
     }
     
-    void updateContents(final DropTargetDragEvent e) {
+    void updateContents(final DropTargetDragEvent e, boolean fetchData) {
         sourceActions = e.getSourceActions();
-        updateData(e.getTransferable());
+        updateData(e.getTransferable(), fetchData);
     }
 
-    void updateContents(final DropTargetDropEvent e) {
+    void updateContents(final DropTargetDropEvent e, boolean fetchData) {
         sourceActions = e.getSourceActions();
-        updateData(e.getTransferable());
+        updateData(e.getTransferable(), fetchData);
     }
 
-    private void updateData(Transferable t) {
+    private void updateData(Transferable t, boolean fetchData) {
         final Map<String, DataFlavor> mimeType2DataFlavor =
                 DataFlavorUtils.adjustSwingDataFlavors(
                 t.getTransferDataFlavors());
 
-        // Read data from the given Transferable in advance. Need to do this
-        // because we don't want Transferable#getTransferData() to be called
-        // from DropTargetListener#drop().
-        //
-        // When Transferable#getTransferData() is called from
-        // DropTargetListener#drop() it may fail with
-        // "java.awt.dnd.InvalidDnDOperationException: No drop current"
-        // error if the call takes place prior to
-        // DropTargetDropEvent#acceptDrop() call.
-        // But if Transferable#getTransferData() is called from
-        // DropTargetListener#dragEnter() and DropTargetListener#dragExit()
-        // it works flawlessly without any extra calls.
-        //
         // If we keep reference to source Transferable in SwingDragSource and
         // call Transferable#getTransferData() on it from
         // SwingDragSource#getData() we may run into
@@ -91,9 +78,17 @@
         //
         // This observation is true for standard AWT Transferable-s. 
         // Things may be totally broken for custom Transferable-s though.
-        //
+
+        // For performance reasons, the DRAG_ENTERED and DRAG_OVER event
+        // handlers pass fetchData == false so as to update the set of
+        // available MIME types only. The DRAG_DROPPED handler passes
+        // fetchData == true which also fetches all the data.
+        // NOTE: Due to JDK-8028585 this code won't be able to fetch data
+        // when invoked from handlers other than DROPPED in any case.
+
         try {
-            mimeType2Data = DataFlavorUtils.readAllData(t, mimeType2DataFlavor);
+            mimeType2Data = DataFlavorUtils.readAllData(t, mimeType2DataFlavor,
+                    fetchData);
         } catch (Exception e) {
             mimeType2Data = Collections.EMPTY_MAP;
         }