1 /* 2 * Copyright (c) 2012, 2014, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package javafx.embed.swing; 27 28 import java.io.UnsupportedEncodingException; 29 30 import java.util.Collections; 31 import java.util.ArrayList; 32 import java.util.EnumSet; 33 import java.util.Arrays; 34 import java.util.List; 35 import java.util.Set; 36 37 import com.sun.javafx.embed.EmbeddedSceneDSInterface; 38 import com.sun.javafx.embed.HostDragStartListener; 39 import javafx.scene.input.TransferMode; 40 41 import com.sun.javafx.embed.EmbeddedSceneInterface; 42 import com.sun.javafx.embed.EmbeddedSceneDTInterface; 43 import com.sun.javafx.tk.Toolkit; 44 45 import javax.swing.JComponent; 46 import javax.swing.SwingUtilities; 55 import java.awt.dnd.DragSource; 56 import java.awt.dnd.DragSourceAdapter; 57 import java.awt.dnd.DragSourceListener; 58 import java.awt.dnd.DragSourceDropEvent; 59 import java.awt.dnd.DropTarget; 60 import java.awt.dnd.DropTargetAdapter; 61 import java.awt.dnd.DropTargetDragEvent; 62 import java.awt.dnd.DropTargetDropEvent; 63 import java.awt.dnd.DropTargetEvent; 64 import java.awt.dnd.DropTargetListener; 65 import java.awt.dnd.InvalidDnDOperationException; 66 67 import java.awt.event.InputEvent; 68 import java.awt.event.MouseAdapter; 69 import java.awt.event.MouseEvent; 70 71 /** 72 * An utility class to connect DnD mechanism of Swing and FX. 73 * It allows FX content to use the AWT machinery for performing DnD. 74 */ 75 final class SwingDnD { 76 77 private final Transferable dndTransferable = new DnDTransferable(); 78 79 private final DragSource dragSource; 80 private final DragSourceListener dragSourceListener; 81 82 // swingDragSource and fxDropTarget are used when DnD is initiated from 83 // Swing or external process, i.e. this SwingDnD is used as a drop target 84 private SwingDragSource swingDragSource; 85 private EmbeddedSceneDTInterface fxDropTarget; 86 87 // fxDragSource is used when DnD is initiated from FX, i.e. this 88 // SwingDnD acts as a drag source 89 private EmbeddedSceneDSInterface fxDragSource; 90 91 private MouseEvent me; 92 93 SwingDnD(final JComponent comp, final EmbeddedSceneInterface embeddedScene) { 94 95 comp.addMouseListener(new MouseAdapter() { 96 @Override 97 public void mouseClicked(MouseEvent me) { 98 storeMouseEvent(me); 99 } 100 @Override 101 public void mouseDragged(MouseEvent me) { 102 storeMouseEvent(me); 103 } 104 @Override 105 public void mousePressed(MouseEvent me) { 106 storeMouseEvent(me); 107 } 108 @Override 109 public void mouseReleased(MouseEvent me) { 110 storeMouseEvent(me); 111 } 112 }); 113 203 applyDropResult(lastTransferMode, e); 204 } catch (InvalidDnDOperationException ignore) { 205 // This means the JDK doesn't contain a fix for 8029979 yet. 206 // DnD still works, but a drag source won't know about 207 // the actual drop result reported by the FX app from 208 // its drop() handler. It will use the dropResult from 209 // the last call to dragOver() instead. 210 } 211 } finally { 212 e.dropComplete(lastTransferMode != null); 213 endDnD(); 214 lastTransferMode = null; 215 } 216 } 217 }; 218 comp.setDropTarget(new DropTarget(comp, 219 DnDConstants.ACTION_COPY | DnDConstants.ACTION_MOVE | DnDConstants.ACTION_LINK, dtl)); 220 221 } 222 223 void addNotify() { 224 dragSource.addDragSourceListener(dragSourceListener); 225 } 226 227 void removeNotify() { 228 // RT-22049: Multi-JFrame/JFXPanel app leaks JFXPanels 229 // Don't forget to unregister drag source listener! 230 dragSource.removeDragSourceListener(dragSourceListener); 231 } 232 233 HostDragStartListener getDragStartListener() { 234 return (dragSource, dragAction) -> { 235 assert Toolkit.getToolkit().isFxUserThread(); 236 assert dragSource != null; 237 238 // The method is called from FX Scene just before entering 239 // nested event loop servicing DnD events. 240 // It should initialize DnD in AWT EDT. 241 SwingUtilities.invokeLater(() -> { 242 assert fxDragSource == null; 243 assert swingDragSource == null; 244 assert fxDropTarget == null; 245 246 fxDragSource = dragSource; 247 startDrag(me, dndTransferable, dragSource. 248 getSupportedActions(), dragAction); 249 }); 250 }; 251 } 252 253 private void startDrag(final MouseEvent e, final Transferable t, 294 private void applyDragResult(final TransferMode dragResult, 295 final DropTargetDragEvent e) 296 { 297 if (dragResult == null) { 298 e.rejectDrag(); 299 } else { 300 e.acceptDrag(transferModeToDropAction(dragResult)); 301 } 302 } 303 304 private void applyDropResult(final TransferMode dropResult, 305 final DropTargetDropEvent e) 306 { 307 if (dropResult == null) { 308 e.rejectDrop(); 309 } else { 310 e.acceptDrop(transferModeToDropAction(dropResult)); 311 } 312 } 313 314 static TransferMode dropActionToTransferMode(final int dropAction) { 315 switch (dropAction) { 316 case DnDConstants.ACTION_COPY: 317 return TransferMode.COPY; 318 case DnDConstants.ACTION_MOVE: 319 return TransferMode.MOVE; 320 case DnDConstants.ACTION_LINK: 321 return TransferMode.LINK; 322 case DnDConstants.ACTION_NONE: 323 return null; 324 default: 325 throw new IllegalArgumentException(); 326 } 327 } 328 329 static int transferModeToDropAction(final TransferMode tm) { 330 switch (tm) { 331 case COPY: 332 return DnDConstants.ACTION_COPY; 333 case MOVE: 334 return DnDConstants.ACTION_MOVE; 335 case LINK: 336 return DnDConstants.ACTION_LINK; 337 default: 338 throw new IllegalArgumentException(); 339 } 340 } 341 342 static Set<TransferMode> dropActionsToTransferModes( 343 final int dropActions) 344 { 345 final Set<TransferMode> tms = EnumSet.noneOf(TransferMode.class); 346 if ((dropActions & DnDConstants.ACTION_COPY) != 0) { 347 tms.add(TransferMode.COPY); 348 } 349 if ((dropActions & DnDConstants.ACTION_MOVE) != 0) { 350 tms.add(TransferMode.MOVE); 351 } 352 if ((dropActions & DnDConstants.ACTION_LINK) != 0) { 353 tms.add(TransferMode.LINK); 354 } 355 return Collections.unmodifiableSet(tms); 356 } 357 358 static int transferModesToDropActions(final Set<TransferMode> tms) { 359 int dropActions = DnDConstants.ACTION_NONE; 360 for (TransferMode tm : tms) { 361 dropActions |= transferModeToDropAction(tm); 362 } 363 return dropActions; 364 } 365 366 // Transferable wrapper over FX dragboard. All the calls are 367 // forwarded to FX and executed on the FX event thread. 368 private final class DnDTransferable implements Transferable { 369 370 @Override 371 public Object getTransferData(final DataFlavor flavor) 372 throws UnsupportedEncodingException 373 { 374 assert fxDragSource != null; 375 assert SwingUtilities.isEventDispatchThread(); 376 377 String mimeType = DataFlavorUtils.getFxMimeType(flavor); 378 return DataFlavorUtils.adjustFxData( | 1 /* 2 * Copyright (c) 2012, 2018, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package com.sun.javafx.embed.swing; 27 28 import java.io.UnsupportedEncodingException; 29 30 import java.util.Collections; 31 import java.util.ArrayList; 32 import java.util.EnumSet; 33 import java.util.Arrays; 34 import java.util.List; 35 import java.util.Set; 36 37 import com.sun.javafx.embed.EmbeddedSceneDSInterface; 38 import com.sun.javafx.embed.HostDragStartListener; 39 import javafx.scene.input.TransferMode; 40 41 import com.sun.javafx.embed.EmbeddedSceneInterface; 42 import com.sun.javafx.embed.EmbeddedSceneDTInterface; 43 import com.sun.javafx.tk.Toolkit; 44 45 import javax.swing.JComponent; 46 import javax.swing.SwingUtilities; 55 import java.awt.dnd.DragSource; 56 import java.awt.dnd.DragSourceAdapter; 57 import java.awt.dnd.DragSourceListener; 58 import java.awt.dnd.DragSourceDropEvent; 59 import java.awt.dnd.DropTarget; 60 import java.awt.dnd.DropTargetAdapter; 61 import java.awt.dnd.DropTargetDragEvent; 62 import java.awt.dnd.DropTargetDropEvent; 63 import java.awt.dnd.DropTargetEvent; 64 import java.awt.dnd.DropTargetListener; 65 import java.awt.dnd.InvalidDnDOperationException; 66 67 import java.awt.event.InputEvent; 68 import java.awt.event.MouseAdapter; 69 import java.awt.event.MouseEvent; 70 71 /** 72 * An utility class to connect DnD mechanism of Swing and FX. 73 * It allows FX content to use the AWT machinery for performing DnD. 74 */ 75 final public class SwingDnD { 76 77 private final Transferable dndTransferable = new DnDTransferable(); 78 79 private final DragSource dragSource; 80 private final DragSourceListener dragSourceListener; 81 82 // swingDragSource and fxDropTarget are used when DnD is initiated from 83 // Swing or external process, i.e. this SwingDnD is used as a drop target 84 private SwingDragSource swingDragSource; 85 private EmbeddedSceneDTInterface fxDropTarget; 86 87 // fxDragSource is used when DnD is initiated from FX, i.e. this 88 // SwingDnD acts as a drag source 89 private EmbeddedSceneDSInterface fxDragSource; 90 91 private MouseEvent me; 92 93 public SwingDnD(final JComponent comp, final EmbeddedSceneInterface embeddedScene) { 94 95 comp.addMouseListener(new MouseAdapter() { 96 @Override 97 public void mouseClicked(MouseEvent me) { 98 storeMouseEvent(me); 99 } 100 @Override 101 public void mouseDragged(MouseEvent me) { 102 storeMouseEvent(me); 103 } 104 @Override 105 public void mousePressed(MouseEvent me) { 106 storeMouseEvent(me); 107 } 108 @Override 109 public void mouseReleased(MouseEvent me) { 110 storeMouseEvent(me); 111 } 112 }); 113 203 applyDropResult(lastTransferMode, e); 204 } catch (InvalidDnDOperationException ignore) { 205 // This means the JDK doesn't contain a fix for 8029979 yet. 206 // DnD still works, but a drag source won't know about 207 // the actual drop result reported by the FX app from 208 // its drop() handler. It will use the dropResult from 209 // the last call to dragOver() instead. 210 } 211 } finally { 212 e.dropComplete(lastTransferMode != null); 213 endDnD(); 214 lastTransferMode = null; 215 } 216 } 217 }; 218 comp.setDropTarget(new DropTarget(comp, 219 DnDConstants.ACTION_COPY | DnDConstants.ACTION_MOVE | DnDConstants.ACTION_LINK, dtl)); 220 221 } 222 223 public void addNotify() { 224 dragSource.addDragSourceListener(dragSourceListener); 225 } 226 227 public void removeNotify() { 228 // RT-22049: Multi-JFrame/JFXPanel app leaks JFXPanels 229 // Don't forget to unregister drag source listener! 230 dragSource.removeDragSourceListener(dragSourceListener); 231 } 232 233 public HostDragStartListener getDragStartListener() { 234 return (dragSource, dragAction) -> { 235 assert Toolkit.getToolkit().isFxUserThread(); 236 assert dragSource != null; 237 238 // The method is called from FX Scene just before entering 239 // nested event loop servicing DnD events. 240 // It should initialize DnD in AWT EDT. 241 SwingUtilities.invokeLater(() -> { 242 assert fxDragSource == null; 243 assert swingDragSource == null; 244 assert fxDropTarget == null; 245 246 fxDragSource = dragSource; 247 startDrag(me, dndTransferable, dragSource. 248 getSupportedActions(), dragAction); 249 }); 250 }; 251 } 252 253 private void startDrag(final MouseEvent e, final Transferable t, 294 private void applyDragResult(final TransferMode dragResult, 295 final DropTargetDragEvent e) 296 { 297 if (dragResult == null) { 298 e.rejectDrag(); 299 } else { 300 e.acceptDrag(transferModeToDropAction(dragResult)); 301 } 302 } 303 304 private void applyDropResult(final TransferMode dropResult, 305 final DropTargetDropEvent e) 306 { 307 if (dropResult == null) { 308 e.rejectDrop(); 309 } else { 310 e.acceptDrop(transferModeToDropAction(dropResult)); 311 } 312 } 313 314 public static TransferMode dropActionToTransferMode(final int dropAction) { 315 switch (dropAction) { 316 case DnDConstants.ACTION_COPY: 317 return TransferMode.COPY; 318 case DnDConstants.ACTION_MOVE: 319 return TransferMode.MOVE; 320 case DnDConstants.ACTION_LINK: 321 return TransferMode.LINK; 322 case DnDConstants.ACTION_NONE: 323 return null; 324 default: 325 throw new IllegalArgumentException(); 326 } 327 } 328 329 public static int transferModeToDropAction(final TransferMode tm) { 330 switch (tm) { 331 case COPY: 332 return DnDConstants.ACTION_COPY; 333 case MOVE: 334 return DnDConstants.ACTION_MOVE; 335 case LINK: 336 return DnDConstants.ACTION_LINK; 337 default: 338 throw new IllegalArgumentException(); 339 } 340 } 341 342 public static Set<TransferMode> dropActionsToTransferModes( 343 final int dropActions) 344 { 345 final Set<TransferMode> tms = EnumSet.noneOf(TransferMode.class); 346 if ((dropActions & DnDConstants.ACTION_COPY) != 0) { 347 tms.add(TransferMode.COPY); 348 } 349 if ((dropActions & DnDConstants.ACTION_MOVE) != 0) { 350 tms.add(TransferMode.MOVE); 351 } 352 if ((dropActions & DnDConstants.ACTION_LINK) != 0) { 353 tms.add(TransferMode.LINK); 354 } 355 return Collections.unmodifiableSet(tms); 356 } 357 358 public static int transferModesToDropActions(final Set<TransferMode> tms) { 359 int dropActions = DnDConstants.ACTION_NONE; 360 for (TransferMode tm : tms) { 361 dropActions |= transferModeToDropAction(tm); 362 } 363 return dropActions; 364 } 365 366 // Transferable wrapper over FX dragboard. All the calls are 367 // forwarded to FX and executed on the FX event thread. 368 private final class DnDTransferable implements Transferable { 369 370 @Override 371 public Object getTransferData(final DataFlavor flavor) 372 throws UnsupportedEncodingException 373 { 374 assert fxDragSource != null; 375 assert SwingUtilities.isEventDispatchThread(); 376 377 String mimeType = DataFlavorUtils.getFxMimeType(flavor); 378 return DataFlavorUtils.adjustFxData( |