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 com.sun.javafx.tk.Toolkit; 29 import java.awt.Component; 30 import java.awt.Cursor; 31 import java.awt.Point; 32 import java.awt.SecondaryLoop; 33 import java.awt.datatransfer.DataFlavor; 34 import java.awt.datatransfer.Transferable; 35 import java.awt.dnd.DnDConstants; 36 import java.awt.dnd.DragGestureEvent; 37 import java.awt.dnd.DragGestureListener; 38 import java.awt.dnd.DragGestureRecognizer; 39 import java.awt.dnd.DragSource; 40 import java.awt.dnd.DropTarget; 41 import java.awt.dnd.DropTargetContext; 42 import java.awt.dnd.DropTargetDragEvent; 43 import java.awt.dnd.DropTargetDropEvent; 44 import java.awt.dnd.DropTargetListener; 45 import java.awt.dnd.InvalidDnDOperationException; 46 import java.awt.dnd.MouseDragGestureRecognizer; 47 import java.awt.dnd.peer.DragSourceContextPeer; 48 import java.awt.dnd.peer.DropTargetContextPeer; 49 import java.security.AccessController; 50 import java.security.PrivilegedAction; 51 import java.util.HashMap; 52 import java.util.Map; 53 import javafx.application.Platform; 54 import javafx.event.EventHandler; 55 import javafx.event.EventType; 56 import javafx.scene.input.DataFormat; 57 import javafx.scene.input.DragEvent; 58 import javafx.scene.input.Dragboard; 59 import javafx.scene.input.MouseEvent; 60 import javafx.scene.input.TransferMode; 61 import sun.awt.AWTAccessor; 62 import sun.awt.dnd.SunDragSourceContextPeer; 63 import sun.swing.JLightweightFrame; 64 65 66 /** 67 * A utility class to connect DnD mechanism of Swing and FX. 68 * It allows Swing content to use the FX machinery for performing DnD. 69 */ 70 final class FXDnD { 71 private final SwingNode node; 72 private static boolean fxAppThreadIsDispatchThread; 73 private SwingNode getNode() { return node; } 74 75 static { 76 AccessController.doPrivileged(new PrivilegedAction<Object>() { 77 public Object run() { 78 fxAppThreadIsDispatchThread = 79 "true".equals(System.getProperty("javafx.embed.singleThread")); 80 return null; 81 } 82 }); 83 } 84 85 FXDnD(SwingNode node) { 86 this.node = node; 87 } 88 89 /** 90 * Utility class that operates on Maps with Components as keys. 91 * Useful when processing mouse events to choose an object from the map 92 * based on the component located at the given coordinates. 93 */ 94 private class ComponentMapper<T> { 95 private int x, y; 96 private T object = null; 97 98 private ComponentMapper(Map<Component, T> map, int xArg, int yArg) { 99 this.x = xArg; 100 this.y = yArg; 101 102 final JLightweightFrame lwFrame = node.getLightweightFrame(); 103 Component c = AWTAccessor.getContainerAccessor().findComponentAt( 104 lwFrame, x, y, false); 105 if (c == null) return; 106 107 synchronized (c.getTreeLock()) { 108 do { 109 object = map.get(c); 110 } while (object == null && (c = c.getParent()) != null); 111 112 if (object != null) { 113 // The object is either a DropTarget or a DragSource, so: 114 //assert c == object.getComponent(); 115 116 // Translate x, y from lwFrame to component coordinates 117 while (c != lwFrame && c != null) { 118 x -= c.getX(); 119 y -= c.getY(); 120 c = c.getParent(); 121 } 122 } 123 } 124 } 125 } 126 public <T> ComponentMapper<T> mapComponent(Map<Component, T> map, int x, int y) { 127 return new ComponentMapper<T>(map, x, y); 128 } 129 130 131 132 133 134 /////////////////////////////////////////////////////////////////////////// 135 // DRAG SOURCE IMPLEMENTATION 136 /////////////////////////////////////////////////////////////////////////// 137 138 139 private boolean isDragSourceListenerInstalled = false; 140 141 // To keep track of where the DnD gesture actually started 142 private MouseEvent pressEvent = null; 143 private long pressTime = 0; 144 145 private volatile SecondaryLoop loop; 146 147 private final Map<Component, FXDragGestureRecognizer> recognizers = new HashMap<>(); 148 149 // Note that we don't really use the MouseDragGestureRecognizer facilities, 150 // however some code in JDK may expect a descendant of this class rather 151 // than a generic DragGestureRecognizer. So we inherit from it. 152 private class FXDragGestureRecognizer extends MouseDragGestureRecognizer { 153 FXDragGestureRecognizer(DragSource ds, Component c, int srcActions, 154 DragGestureListener dgl) 155 { 156 super(ds, c, srcActions, dgl); 157 158 if (c != null) recognizers.put(c, this); 159 } 160 161 @Override public void setComponent(Component c) { 162 final Component old = getComponent(); 163 if (old != null) recognizers.remove(old); 164 super.setComponent(c); 165 if (c != null) recognizers.put(c, this); 166 } 167 168 protected void registerListeners() { 169 SwingFXUtils.runOnFxThread(() -> { 170 if (!isDragSourceListenerInstalled) { 171 node.addEventHandler(MouseEvent.MOUSE_PRESSED, onMousePressHandler); 172 node.addEventHandler(MouseEvent.DRAG_DETECTED, onDragStartHandler); 173 node.addEventHandler(DragEvent.DRAG_DONE, onDragDoneHandler); 174 175 isDragSourceListenerInstalled = true; 176 } 177 }); 178 } 179 180 protected void unregisterListeners() { 181 SwingFXUtils.runOnFxThread(() -> { 182 if (isDragSourceListenerInstalled) { 183 node.removeEventHandler(MouseEvent.MOUSE_PRESSED, onMousePressHandler); 184 node.removeEventHandler(MouseEvent.DRAG_DETECTED, onDragStartHandler); 185 node.removeEventHandler(DragEvent.DRAG_DONE, onDragDoneHandler); 186 187 isDragSourceListenerInstalled = false; 188 } 189 }); 190 } 191 192 private void fireEvent(int x, int y, long evTime, int modifiers) { 193 // In theory we should register all the events that trigger the gesture (like PRESS, DRAG, DRAG, BINGO!) 194 // But we can live with this hack for now. 195 appendEvent(new java.awt.event.MouseEvent(getComponent(), java.awt.event.MouseEvent.MOUSE_PRESSED, 196 evTime, modifiers, x, y, 0, false)); 197 198 // Also, the modifiers here should've actually come from the last known mouse event (last MOVE or DRAG). 199 // But we're OK with using the initial PRESS modifiers for now 200 int initialAction = SunDragSourceContextPeer.convertModifiersToDropAction( 201 modifiers, getSourceActions()); 202 203 fireDragGestureRecognized(initialAction, new java.awt.Point(x, y)); 204 } 205 } 206 207 // Invoked on EDT 208 private void fireEvent(int x, int y, long evTime, int modifiers) { 209 ComponentMapper<FXDragGestureRecognizer> mapper = mapComponent(recognizers, x, y); 210 211 final FXDragGestureRecognizer r = mapper.object; 212 if (r != null) { 213 r.fireEvent(mapper.x, mapper.y, evTime, modifiers); 214 } else { 215 // No recognizer, no DnD, no startDrag, so release the FX loop now 216 SwingFXUtils.leaveFXNestedLoop(FXDnD.this); 217 } 218 } 219 220 private MouseEvent getInitialGestureEvent() { 221 return pressEvent; 222 } 223 224 private final EventHandler<MouseEvent> onMousePressHandler = (event) -> { 225 // It would be nice to maintain a list of all the events that initiate 226 // a DnD gesture (see a comment in FXDragGestureRecognizer.fireEvent(). 227 // For now, we simply use the initial PRESS event for this purpose. 228 pressEvent = event; 229 pressTime = System.currentTimeMillis(); 230 }; 231 232 233 private volatile FXDragSourceContextPeer activeDSContextPeer; 234 235 private final EventHandler<MouseEvent> onDragStartHandler = (event) -> { 236 // Call to AWT and determine the active DragSourceContextPeer 237 activeDSContextPeer = null; 238 final MouseEvent firstEv = getInitialGestureEvent(); 239 SwingFXUtils.runOnEDTAndWait(FXDnD.this, () -> fireEvent( 240 (int)firstEv.getX(), (int)firstEv.getY(), pressTime, 241 SwingEvents.fxMouseModsToMouseMods(firstEv))); 242 if (activeDSContextPeer == null) return; 243 244 // Since we're going to start DnD, consume the event. 245 event.consume(); 246 247 Dragboard db = getNode().startDragAndDrop(SwingDnD.dropActionsToTransferModes( 248 activeDSContextPeer.sourceActions).toArray(new TransferMode[1])); 249 250 // At this point the activeDSContextPeer.transferable contains all the data from AWT 251 Map<DataFormat, Object> fxData = new HashMap<>(); 252 for (String mt : activeDSContextPeer.transferable.getMimeTypes()) { 253 DataFormat f = DataFormat.lookupMimeType(mt); 254 //TODO: what to do if f == null? 255 if (f != null) fxData.put(f, activeDSContextPeer.transferable.getData(mt)); 256 } 257 258 final boolean hasContent = db.setContent(fxData); 259 if (!hasContent) { 260 // No data, no DnD, no onDragDoneHandler, so release the AWT loop now 261 if (!fxAppThreadIsDispatchThread) { 262 loop.exit(); 263 } 264 } 265 }; 266 267 private final EventHandler<DragEvent> onDragDoneHandler = (event) -> { 268 event.consume(); 269 270 // Release FXDragSourceContextPeer.startDrag() 271 if (!fxAppThreadIsDispatchThread) { 272 loop.exit(); 273 } 274 275 if (activeDSContextPeer != null) { 276 final TransferMode mode = event.getTransferMode(); 277 activeDSContextPeer.dragDone( 278 mode == null ? 0 : SwingDnD.transferModeToDropAction(mode), 279 (int)event.getX(), (int)event.getY()); 280 } 281 }; 282 283 284 private final class FXDragSourceContextPeer extends SunDragSourceContextPeer { 285 private volatile int sourceActions = 0; 286 287 private final CachingTransferable transferable = new CachingTransferable(); 288 289 @Override public void startSecondaryEventLoop(){ 290 Toolkit.getToolkit().enterNestedEventLoop(this); 291 } 292 @Override public void quitSecondaryEventLoop(){ 293 assert !Platform.isFxApplicationThread(); 294 Platform.runLater(() -> Toolkit.getToolkit().exitNestedEventLoop(FXDragSourceContextPeer.this, null)); 295 } 296 297 @Override protected void setNativeCursor(long nativeCtxt, Cursor c, int cType) { 298 //TODO 299 } 300 301 302 private void dragDone(int operation, int x, int y) { 303 dragDropFinished(operation != 0, operation, x, y); 304 } 305 306 FXDragSourceContextPeer(DragGestureEvent dge) { 307 super(dge); 308 } 309 310 311 // It's Map<Long, DataFlavor> actually, but javac complains if the type isn't erased... 312 @Override protected void startDrag(Transferable trans, long[] formats, Map formatMap) 313 { 314 activeDSContextPeer = this; 315 316 // NOTE: we ignore the formats[] and the formatMap altogether. 317 // AWT provides those to allow for more flexible representations of 318 // e.g. text data (in various formats, encodings, etc.) However, FX 319 // code isn't ready to handle those (e.g. it can't digest a 320 // StringReader as data, etc.) So instead we perform our internal 321 // translation. 322 // Note that fetchData == true. FX doesn't support delayed data 323 // callbacks yet anyway, so we have to fetch all the data from AWT upfront. 324 transferable.updateData(trans, true); 325 326 sourceActions = getDragSourceContext().getSourceActions(); 327 328 // Release the FX nested loop to allow onDragDetected to start the actual DnD operation, 329 // and then start an AWT nested loop to wait until DnD finishes. 330 if (!fxAppThreadIsDispatchThread) { 331 loop = java.awt.Toolkit.getDefaultToolkit().getSystemEventQueue().createSecondaryLoop(); 332 SwingFXUtils.leaveFXNestedLoop(FXDnD.this); 333 if (!loop.enter()) { 334 // An error occured, but there's little we can do here... 335 } 336 } 337 } 338 }; 339 340 public <T extends DragGestureRecognizer> T createDragGestureRecognizer( 341 Class<T> abstractRecognizerClass, 342 DragSource ds, Component c, int srcActions, 343 DragGestureListener dgl) 344 { 345 return (T) new FXDragGestureRecognizer(ds, c, srcActions, dgl); 346 } 347 348 public DragSourceContextPeer createDragSourceContextPeer(DragGestureEvent dge) throws InvalidDnDOperationException 349 { 350 return new FXDragSourceContextPeer(dge); 351 } 352 353 354 355 356 357 /////////////////////////////////////////////////////////////////////////// 358 // DROP TARGET IMPLEMENTATION 359 /////////////////////////////////////////////////////////////////////////// 360 361 362 private boolean isDropTargetListenerInstalled = false; 363 private volatile FXDropTargetContextPeer activeDTContextPeer = null; 364 private final Map<Component, DropTarget> dropTargets = new HashMap<>(); 365 366 private final EventHandler<DragEvent> onDragEnteredHandler = (event) -> { 367 if (activeDTContextPeer == null) activeDTContextPeer = new FXDropTargetContextPeer(); 368 369 int action = activeDTContextPeer.postDropTargetEvent(event); 370 371 // If AWT doesn't accept anything, let parent nodes handle the event 372 if (action != 0) event.consume(); 373 }; 374 375 private final EventHandler<DragEvent> onDragExitedHandler = (event) -> { 376 if (activeDTContextPeer == null) activeDTContextPeer = new FXDropTargetContextPeer(); 377 378 activeDTContextPeer.postDropTargetEvent(event); 379 380 activeDTContextPeer = null; 381 }; 382 383 private final EventHandler<DragEvent> onDragOverHandler = (event) -> { 384 if (activeDTContextPeer == null) activeDTContextPeer = new FXDropTargetContextPeer(); 385 386 int action = activeDTContextPeer.postDropTargetEvent(event); 387 388 // If AWT doesn't accept anything, let parent nodes handle the event 389 if (action != 0) { 390 // NOTE: in FX the acceptTransferModes() may ONLY be called from DRAG_OVER. 391 // If the AWT app always reports NONE and suddenly decides to accept the 392 // data in its DRAG_DROPPED handler, this just won't work. There's no way 393 // to workaround this other than by modifing the AWT application code. 394 event.acceptTransferModes(SwingDnD.dropActionsToTransferModes(action).toArray(new TransferMode[1])); 395 event.consume(); 396 } 397 }; 398 399 private final EventHandler<DragEvent> onDragDroppedHandler = (event) -> { 400 if (activeDTContextPeer == null) activeDTContextPeer = new FXDropTargetContextPeer(); 401 402 int action = activeDTContextPeer.postDropTargetEvent(event); 403 404 if (action != 0) { 405 // NOTE: the dropAction is ignored since we use the action last 406 // reported from the DRAG_OVER handler. 407 // 408 // We might want to: 409 // 410 // assert activeDTContextPeer.dropAction == onDragDroppedHandler.currentAction; 411 // 412 // and maybe print a diagnostic message if they differ. 413 event.setDropCompleted(activeDTContextPeer.success); 414 415 event.consume(); 416 } 417 418 activeDTContextPeer = null; 419 }; 420 421 private final class FXDropTargetContextPeer implements DropTargetContextPeer { 422 423 private int targetActions = DnDConstants.ACTION_NONE; 424 private int currentAction = DnDConstants.ACTION_NONE; 425 private DropTarget dt = null; 426 private DropTargetContext ctx = null; 427 428 private final CachingTransferable transferable = new CachingTransferable(); 429 430 // Drop result 431 private boolean success = false; 432 private int dropAction = 0; 433 434 @Override public synchronized void setTargetActions(int actions) { targetActions = actions; } 435 @Override public synchronized int getTargetActions() { return targetActions; } 436 437 @Override public synchronized DropTarget getDropTarget() { return dt; } 438 439 @Override public synchronized boolean isTransferableJVMLocal() { return false; } 440 441 @Override public synchronized DataFlavor[] getTransferDataFlavors() { return transferable.getTransferDataFlavors(); } 442 @Override public synchronized Transferable getTransferable() { return transferable; } 443 444 @Override public synchronized void acceptDrag(int dragAction) { currentAction = dragAction; } 445 @Override public synchronized void rejectDrag() { currentAction = DnDConstants.ACTION_NONE; } 446 447 @Override public synchronized void acceptDrop(int dropAction) { this.dropAction = dropAction; } 448 @Override public synchronized void rejectDrop() { dropAction = DnDConstants.ACTION_NONE; } 449 450 @Override public synchronized void dropComplete(boolean success) { this.success = success; } 451 452 453 private int postDropTargetEvent(DragEvent event) 454 { 455 ComponentMapper<DropTarget> mapper = mapComponent(dropTargets, (int)event.getX(), (int)event.getY()); 456 457 final EventType<?> fxEvType = event.getEventType(); 458 459 Dragboard db = event.getDragboard(); 460 transferable.updateData(db, DragEvent.DRAG_DROPPED.equals(fxEvType)); 461 462 final int sourceActions = SwingDnD.transferModesToDropActions(db.getTransferModes()); 463 final int userAction = event.getTransferMode() == null ? DnDConstants.ACTION_NONE 464 : SwingDnD.transferModeToDropAction(event.getTransferMode()); 465 466 // A target for the AWT DnD event 467 DropTarget target = mapper.object != null ? mapper.object : dt; 468 469 SwingFXUtils.runOnEDTAndWait(FXDnD.this, () -> { 470 if (target != dt) { 471 if (ctx != null) { 472 AWTAccessor.getDropTargetContextAccessor().reset(ctx); 473 } 474 ctx = null; 475 476 currentAction = dropAction = DnDConstants.ACTION_NONE; 477 } 478 479 if (target != null) { 480 if (ctx == null) { 481 ctx = target.getDropTargetContext(); 482 AWTAccessor.getDropTargetContextAccessor() 483 .setDropTargetContextPeer(ctx, FXDropTargetContextPeer.this); 484 } 485 486 DropTargetListener dtl = (DropTargetListener)target; 487 488 if (DragEvent.DRAG_DROPPED.equals(fxEvType)) { 489 DropTargetDropEvent awtEvent = new DropTargetDropEvent( 490 ctx, new Point(mapper.x, mapper.y), userAction, sourceActions); 491 492 dtl.drop(awtEvent); 493 } else { 494 DropTargetDragEvent awtEvent = new DropTargetDragEvent( 495 ctx, new Point(mapper.x, mapper.y), userAction, sourceActions); 496 497 if (DragEvent.DRAG_OVER.equals(fxEvType)) dtl.dragOver(awtEvent); 498 else if (DragEvent.DRAG_ENTERED.equals(fxEvType)) dtl.dragEnter(awtEvent); 499 else if (DragEvent.DRAG_EXITED.equals(fxEvType)) dtl.dragExit(awtEvent); 500 } 501 } 502 503 dt = mapper.object; 504 if (dt == null) { 505 // FIXME: once we switch to JDK 9 as the boot JDK 506 // we need to re-implement the following using 507 // available API. 508 /* 509 if (ctx != null) ctx.removeNotify(); 510 */ 511 ctx = null; 512 513 currentAction = dropAction = DnDConstants.ACTION_NONE; 514 } 515 if (DragEvent.DRAG_DROPPED.equals(fxEvType) || DragEvent.DRAG_EXITED.equals(fxEvType)) { 516 // This must be done to ensure that the data isn't being 517 // cached in AWT. Otherwise subsequent DnD operations will 518 // see the old data only. 519 // FIXME: once we switch to JDK 9 as the boot JDK 520 // we need to re-implement the following using 521 // available API. 522 /* 523 if (ctx != null) ctx.removeNotify(); 524 */ 525 ctx = null; 526 } 527 528 SwingFXUtils.leaveFXNestedLoop(FXDnD.this); 529 }); 530 531 if (DragEvent.DRAG_DROPPED.equals(fxEvType)) return dropAction; 532 533 return currentAction; 534 } 535 } 536 537 public void addDropTarget(DropTarget dt) { 538 dropTargets.put(dt.getComponent(), dt); 539 Platform.runLater(() -> { 540 if (!isDropTargetListenerInstalled) { 541 node.addEventHandler(DragEvent.DRAG_ENTERED, onDragEnteredHandler); 542 node.addEventHandler(DragEvent.DRAG_EXITED, onDragExitedHandler); 543 node.addEventHandler(DragEvent.DRAG_OVER, onDragOverHandler); 544 node.addEventHandler(DragEvent.DRAG_DROPPED, onDragDroppedHandler); 545 546 isDropTargetListenerInstalled = true; 547 } 548 }); 549 } 550 551 public void removeDropTarget(DropTarget dt) { 552 dropTargets.remove(dt.getComponent()); 553 Platform.runLater(() -> { 554 if (isDropTargetListenerInstalled && dropTargets.isEmpty()) { 555 node.removeEventHandler(DragEvent.DRAG_ENTERED, onDragEnteredHandler); 556 node.removeEventHandler(DragEvent.DRAG_EXITED, onDragExitedHandler); 557 node.removeEventHandler(DragEvent.DRAG_OVER, onDragOverHandler); 558 node.removeEventHandler(DragEvent.DRAG_DROPPED, onDragDroppedHandler); 559 560 isDropTargetListenerInstalled = false; 561 } 562 }); 563 } 564 } | 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 com.sun.javafx.tk.Toolkit; 29 import java.awt.Component; 30 import java.awt.Cursor; 31 import java.awt.Point; 32 import java.awt.SecondaryLoop; 33 import java.awt.datatransfer.DataFlavor; 34 import java.awt.datatransfer.Transferable; 35 import java.awt.dnd.DnDConstants; 36 import java.awt.dnd.DragGestureEvent; 37 import java.awt.dnd.DragGestureListener; 38 import java.awt.dnd.DragGestureRecognizer; 39 import java.awt.dnd.DragSource; 40 import java.awt.dnd.DropTarget; 41 import java.awt.dnd.DropTargetContext; 42 import java.awt.dnd.DropTargetDragEvent; 43 import java.awt.dnd.DropTargetDropEvent; 44 import java.awt.dnd.DropTargetListener; 45 import java.awt.dnd.InvalidDnDOperationException; 46 import java.awt.dnd.MouseDragGestureRecognizer; 47 import java.security.AccessController; 48 import java.security.PrivilegedAction; 49 import java.util.HashMap; 50 import java.util.Map; 51 import javafx.application.Platform; 52 import javafx.event.EventHandler; 53 import javafx.event.EventType; 54 import javafx.scene.input.DataFormat; 55 import javafx.scene.input.DragEvent; 56 import javafx.scene.input.Dragboard; 57 import javafx.scene.input.MouseEvent; 58 import javafx.scene.input.TransferMode; 59 60 import javafx.embed.swing.SwingNode; 61 62 /** 63 * A utility class to connect DnD mechanism of Swing and FX. 64 * It allows Swing content to use the FX machinery for performing DnD. 65 */ 66 final public class FXDnD { 67 private static SwingNode node; 68 public static boolean fxAppThreadIsDispatchThread; 69 private SwingNode getNode() { return node; } 70 private static FXDnDInterop fxdndiop; 71 72 static { 73 AccessController.doPrivileged(new PrivilegedAction<Object>() { 74 public Object run() { 75 fxAppThreadIsDispatchThread = 76 "true".equals(System.getProperty("javafx.embed.singleThread")); 77 return null; 78 } 79 }); 80 81 InteropFactory instance = null; 82 try { 83 instance = InteropFactory.getInstance(); 84 } catch (Exception e) { 85 throw new ExceptionInInitializerError(e); 86 } 87 fxdndiop = instance.createFXDnDImpl(); 88 } 89 90 public FXDnD(SwingNode node) { 91 this.node = node; 92 fxdndiop.setNode(node); 93 } 94 95 public Object createDragSourceContext(DragGestureEvent dge) 96 throws InvalidDnDOperationException { 97 return fxdndiop.createDragSourceContext(dge); 98 } 99 100 public <T extends DragGestureRecognizer> T createDragGestureRecognizer( 101 Class<T> abstractRecognizerClass, 102 DragSource ds, Component c, int srcActions, 103 DragGestureListener dgl) 104 { 105 return fxdndiop.createDragGestureRecognizer(ds, c, srcActions, dgl); 106 } 107 108 public void addDropTarget(DropTarget dt) { 109 fxdndiop.addDropTarget(dt, node); 110 } 111 112 public void removeDropTarget(DropTarget dt) { 113 fxdndiop.removeDropTarget(dt, node); 114 } 115 } |