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(
|
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(
|