1 /*
   2  * Copyright (c) 2011, 2017, 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.scene.input;
  27 
  28 import java.util.EnumSet;
  29 import java.util.Set;
  30 
  31 import javafx.beans.NamedArg;
  32 import javafx.event.Event;
  33 import javafx.event.EventTarget;
  34 import javafx.event.EventType;
  35 import javafx.geometry.Point3D;
  36 
  37 import com.sun.javafx.scene.input.InputEventUtils;
  38 import java.io.IOException;
  39 
  40 // PENDING_DOC_REVIEW
  41 /**
  42  * Drag events replace mouse events during drag-and-drop gesture.
  43  * The difference between press-drag-release and drag-and-drop gestures
  44  * is described at {@link javafx.scene.input.MouseEvent MouseEvent}.
  45  * <p>
  46  * Drag and drop gesture can be started by calling {@code startDragAndDrop()}
  47  * (on a node or scene) inside of a {@link MouseEvent#DRAG_DETECTED DRAG_DETECTED} event handler.
  48  * The data to be transfered to drop target are placed to a {@code dragBoard}
  49  * at this moment.
  50  * <p>
  51  * Drag entered/exited events behave similarly to mouse entered/exited
  52  * events, please see {@code MouseEvent} overview.
  53  *
  54  * <h3>Drag sources: initiating a drag and drop gesture</h3>
  55  *
  56  * When a drag gesture is detected, an application can decide whether to
  57  * start a drag and drop gesture or continue with a press-drag-release gesture.
  58  * <p>
  59  * The default drag detection mechanism uses mouse movements with a pressed
  60  * button in combination with hysteresis. This behavior can be
  61  * augmented by the application. Each {@code MOUSE_PRESSED} and
  62  * {@code MOUSE_DRAGGED} event has a {@code dragDetect} flag that determines
  63  * whether a drag gesture has been detected. The default value of this flag
  64  * depends on the default detection mechanism and can be modified by calling
  65  * {@code setDragDetect()} inside of an event handler. When processing of
  66  * one of these events ends with the {@code dragDetect} flag set to true,
  67  * a {@code DRAG_DETECTED} {@code MouseEvent} is sent to the potential gesture
  68  * source (the object on which a mouse button has been pressed). This event
  69  * notifies about the gesture detection.
  70  * <p>
  71  * Inside a {@code DRAG_DETECTED} event handler, if the
  72  * {@code startDragAndDrop()} method is called on a node or scene and a dragged
  73  * data is made available to the returned {@code Dragboard}, the object on which
  74  * {@code startDragAndDrop()} has been called is considred a gesture source
  75  * and the drag and drop gesture is started. The {@code Dragboard} has system
  76  * clipboard functionality but is specifically used for drag and drop data
  77  * transfer.
  78  * <p>
  79  * The {@code startDragAndDrop()} method takes a set of {@code TransferMode}s
  80  * supported by the gesture source. For instance passing only
  81  * {@code TransferMode.COPY} indicates that the gesture source allows only
  82  * copying of the data, not moving or referencing.
  83  * <p>
  84  * Following example shows a simple drag and drop source:
  85  * <pre>
  86 Rectangle rect = new Rectangle(100, 100);
  87 rect.setOnDragDetected(new EventHandler&lt;MouseEvent&gt;() {
  88     @Override public void handle(MouseEvent event) {
  89         Dragboard db = startDragAndDrop(TransferMode.ANY);
  90         ClipboardContent content = new ClipboardContent();
  91         content.putString("Hello!");
  92         db.setContent(content);
  93         event.consume();
  94     }
  95 });
  96  * </pre>
  97  *
  98  * <br><h3>Potential drop targets</h3>
  99  *
 100  * <p>
 101  * After the drag and drop gesture has been started, any object
 102  * ({@code Node}, {@code Scene}) over which the mouse is dragged is
 103  * a potential drop target.
 104  * <p>
 105  * When the mouse is dragged into the boundaries of potential drop target,
 106  * the potential target gets a {@code DRAG_ENTERED} event. When the mouse is
 107  * dragged outside of the potential target's bounds, it gets a
 108  * {@code DRAG_EXITED} event. There are also the bubbling
 109  * {@code DRAG_ENTERED_TARGET} and {@code DRAG_EXITED_TARGET} variants. They
 110  * behave similarly to mouse entered/exited events, please see
 111  * {@code MouseEvent} overview.
 112  * <p>
 113  * A potential drop target can decide to change its appearance to
 114  * let the user know that the dragged data can be dropped on it. This can be
 115  * done in a {@code DRAG_OVER} event handler, based on the position of the
 116  * mouse. Another option is to change the potential target's appearance in
 117  * a {@code DRAG_ENTERED} and {@code DRAG_EXITED} handlers.
 118  * <p>
 119  * In {@code DRAG_OVER} event handler a potential drop target has the ability
 120  * to make it known that it is an actual target. This is done by calling
 121  * {@code acceptTransferModes(TransferMode...)} on the event,
 122  * passing transfer modes it is willing to accept.
 123  * If it <i>is not called</i> during the event delivery or if none of the
 124  * passed transfer modes is supported by gesture source, then the potential
 125  * drop target <i>is not considered to be an actual drop target</i>.
 126  * <p>
 127  * When deciding weather to accept the event by calling {@code acceptTransferModes(TransferMode...)},
 128  * the type of data available on the {@code Dragboard} should be considered.
 129  * Access to the {@code Dragboard} is provided by the {@code getDragboard()}
 130  * method.
 131  * <p>
 132  * When accepting an event, the potential gesture target decides which
 133  * {@code TransferMode} is accepted for the operation. To make the decision,
 134  * {@code DragBoard.getTransferModes()} (set of transfer modes supported by
 135  * the gesture source) and {@code DragEvent.getTransferMode()} (default
 136  * transfer mode issued by platform, driven by key modifiers) can be used.
 137  * It is poosible to pass more transfer modes into the
 138  * {@code acceptTransferModes(TransferMode...)} method. In this case
 139  * it makes the decision in behalf of the
 140  * application (it chooses the default mode if it's supported by gesture source
 141  * and accepted by gesture target, otherwise it chooses the most common mode
 142  * of the supported and accepted ones).
 143  * The {@code DRAG_DROPPED} event's {@code getTransferMode()} later reports the
 144  * transfer mode accepted by the {@code DRAG_OVER} event handler.
 145  * <p>
 146  * A drag and drop gesture ends when the mouse button is released.
 147  * If this happens over a gesture target that accepted previous {@code DRAG_OVER}
 148  * events with a transfer mode supported by gesture source,
 149  * a {@code DRAG_DROPPED} event is sent to the gesture target.
 150  * In its handler, the gesture target can access the data on the dragboard.
 151  * After data has been transferred (or decided not to transfer), the gesture
 152  * needs to be completed by calling {@code setDropCompleted(Boolean)} on the event.
 153  * The {@code Boolean} argument indicates if the data has been transferred
 154  * successfully or not. If it is not called, the gesture is considered
 155  * unsuccessful.
 156  *
 157  * <p>
 158  * Following example shows a simple drag and drop target for text data:
 159  * <pre>
 160 Rectangle rect = new Rectangle(100, 100);
 161 
 162 rect.setOnDragOver(new EventHandler&lt;DragEvent&gt;() {
 163     @Override public void handle(DragEvent event) {
 164         Dragboard db = event.getDragboard();
 165         if (db.hasString()) {
 166             event.acceptTransferModes(TransferMode.COPY_OR_MOVE);
 167         }
 168         event.consume();
 169     }
 170 });
 171 
 172 rect.setOnDragDropped(new EventHandler&lt;DragEvent&gt;() {
 173     @Override public void handle(DragEvent event) {
 174         Dragboard db = event.getDragboard();
 175         boolean success = false;
 176         if (db.hasString()) {
 177             System.out.println("Dropped: " + db.getString());
 178             success = true;
 179         }
 180         event.setDropCompleted(success);
 181         event.consume();
 182     }
 183 });
 184  * </pre>
 185  *
 186  * <h3>Drag sources: finalizing drag and drop gesture</h3>
 187  *
 188  * <p>
 189  * After the gesture has been finished, whether by successful or unsuccessful
 190  * data transfer or being canceled, the {@code DRAG_DONE} event is sent to
 191  * the gesture source. The {@code getTransferMode()} method of the event
 192  * indicates to the gesture source how the transfer of data was completed.
 193  * If the transfer mode has the value {@code MOVE}, then this allows the source
 194  * to clear out its data. Clearing the source's data gives the appropriate
 195  * appearance to a user that the data has been moved by the drag and drop
 196  * gesture. If it has the value {@code null}, then the drag and drop gesture
 197  * ended without any data being transferred.  This could happen as a result of
 198  * a mouse release event over a node that is not a drop target, or the user
 199  * pressing the ESC key to cancel the drag and drop gesture, or by
 200  * the gesture target reporting an unsuccessful data transfer.
 201  * </p>
 202  * @since JavaFX 2.0
 203  */
 204 public final class DragEvent extends InputEvent {
 205 
 206     private static final long serialVersionUID = 20121107L;
 207 
 208     /**
 209      * Common supertype for all drag event types.
 210      */
 211     public static final EventType<DragEvent> ANY =
 212             new EventType<DragEvent>(InputEvent.ANY, "DRAG");
 213 
 214     /**
 215      * This event occurs when drag gesture enters a node. It's the
 216      * bubbling variant, which is delivered also to all parents of the
 217      * entered node (unless it was consumed). When notifications about
 218      * entering some of node's children are not desired,
 219      * {@code DRAG_ENTERED} event handler should be used.
 220      *
 221      * @see MouseEvent MouseEvent for more information about mouse entered/exited handling
 222      * which is similar
 223      */
 224     public static final EventType<DragEvent> DRAG_ENTERED_TARGET =
 225             new EventType<DragEvent>(DragEvent.ANY, "DRAG_ENTERED_TARGET");
 226 
 227     /**
 228      * This event occurs when drag gesture enters a node.
 229      * This event type is delivered only to the entered node,
 230      * if parents want to filter it or get the bubbling event,
 231      * they need to use {@code DRAG_ENTERED_TARGET}.
 232      *
 233      * @see MouseEvent MouseEvent for more information about mouse entered/exited handling
 234      * which is similar
 235      */
 236     public static final EventType<DragEvent> DRAG_ENTERED =
 237             new EventType<DragEvent>(DragEvent.DRAG_ENTERED_TARGET, "DRAG_ENTERED");
 238 
 239     /**
 240      * This event occurs when drag gesture exits a node. It's the
 241      * bubbling variant, which is delivered also to all parents of the
 242      * eixited node (unless it was consumed). When notifications about
 243      * exiting some of node's children are not desired,
 244      * {@code DRAG_EXITED} event handler should be used.
 245      *
 246      * @see MouseEvent MouseEvent for more information about mouse entered/exited handling
 247      * which is similar
 248      */
 249     public static final EventType<DragEvent> DRAG_EXITED_TARGET =
 250             new EventType<DragEvent>(DragEvent.ANY, "DRAG_EXITED_TARGET");
 251 
 252     /**
 253      * This event occurs when drag gesture exits a node.
 254      * This event type is delivered only to the exited node,
 255      * if parents want to filter it or get the bubbling event,
 256      * they need to use {@code DRAG_EXITED_TARGET}.
 257      *
 258      * @see MouseEvent MouseEvent for more information about mouse entered/exited handling
 259      * which is similar
 260      */
 261     public static final EventType<DragEvent> DRAG_EXITED =
 262             new EventType<DragEvent>(DragEvent.DRAG_EXITED_TARGET, "DRAG_EXITED");
 263 
 264     /**
 265      * This event occurs when drag gesture progresses within this node.
 266      */
 267     public static final EventType<DragEvent> DRAG_OVER =
 268             new EventType<DragEvent>(DragEvent.ANY, "DRAG_OVER");
 269 
 270     // Do we want DRAG_TRANSFER_MODE_CHANGED event?
 271 //    /**
 272 //     * This event occurs on a potential drag-and-drop target when the user
 273 //     * takes action to change the intended {@code TransferMode}.
 274 //     * The user can change the intended {@link TransferMode} by holding down
 275 //     * or releasing key modifiers.
 276 //     */
 277 //    public static final EventType<DragEvent> DRAG_TRANSFER_MODE_CHANGED =
 278 //            new EventType<DragEvent>(DragEvent.ANY, "DRAG_TRANSFER_MODE_CHANGED");
 279 
 280     /**
 281      * This event occurs when the mouse button is released during drag and drop
 282      * gesture on a drop target. Transfer of data from the
 283      * {@link DragEvent}'s {@link DragEvent#getDragboard() dragboard} should happen
 284      * in handler of this event.
 285      */
 286     public static final EventType<DragEvent> DRAG_DROPPED =
 287             new EventType<DragEvent>(DragEvent.ANY, "DRAG_DROPPED");
 288 
 289     /**
 290      * This event occurs on drag-and-drop gesture source after its data has
 291      * been dropped on a drop target. The {@code transferMode} of the
 292      * event shows what just happened at the drop target.
 293      * If {@code transferMode} has the value {@code MOVE}, then the source can
 294      * clear out its data. Clearing the source's data gives the appropriate
 295      * appearance to a user that the data has been moved by the drag and drop
 296      * gesture. A {@code transferMode} that has the value {@code NONE}
 297      * indicates that no data was transferred during the drag and drop gesture.
 298      */
 299     public static final EventType<DragEvent> DRAG_DONE =
 300             new EventType<DragEvent>(DragEvent.ANY, "DRAG_DONE");
 301 
 302     /**
 303      * Creates a copy of the given drag event with the given fields substituted.
 304      * @param source the new source of the copied event
 305      * @param target the new target of the copied event
 306      * @param gestureSource the new gesture source.
 307      * @param gestureTarget the new gesture target.
 308      * @param eventType the new eventType
 309      * @return the event copy with the fields
 310      * @since JavaFX 8.0
 311      */
 312     public DragEvent copyFor(Object source, EventTarget target,
 313             Object gestureSource, Object gestureTarget,
 314             EventType<DragEvent> eventType) {
 315 
 316         DragEvent copyEvent = copyFor(source, target, eventType);
 317         recomputeCoordinatesToSource(copyEvent, source);
 318         copyEvent.gestureSource = gestureSource;
 319         copyEvent.gestureTarget = gestureTarget;
 320         return copyEvent;
 321     }
 322 
 323     /**
 324      * Constructs new DragEvent event.
 325      * For DRAG_DROPPED and DRAG_DONE event types, the {@code accepted} state
 326      * and {@code acceptedTransferMode} are set according to the passed
 327      * {@code transferMode}.
 328      * @param source the source of the event. Can be null.
 329      * @param target the target of the event. Can be null.
 330      * @param eventType The type of the event.
 331      * @param dragboard the dragboard of the event.
 332      * @param x The x with respect to the scene.
 333      * @param y The y with respect to the scene.
 334      * @param screenX The x coordinate relative to screen.
 335      * @param screenY The y coordinate relative to screen.
 336      * @param transferMode the transfer mode of the event.
 337      * @param gestureSource the source of the DnD gesture of the event.
 338      * @param gestureTarget the target of the DnD gesture of the event.
 339      * @param pickResult pick result. Can be null, in this case a 2D pick result
 340      *                   without any further values is constructed
 341      *                   based on the scene coordinates and the target
 342      * @since JavaFX 8.0
 343      */
 344     public DragEvent(@NamedArg("source") Object source, @NamedArg("target") EventTarget target, @NamedArg("eventType") EventType<DragEvent> eventType, @NamedArg("dragboard") Dragboard dragboard,
 345             @NamedArg("x") double x, @NamedArg("y") double y,
 346             @NamedArg("screenX") double screenX, @NamedArg("screenY") double screenY, @NamedArg("transferMode") TransferMode transferMode,
 347             @NamedArg("gestureSource") Object gestureSource, @NamedArg("gestureTarget") Object gestureTarget, @NamedArg("pickResult") PickResult pickResult) {
 348         super(source, target, eventType);
 349         this.gestureSource = gestureSource;
 350         this.gestureTarget = gestureTarget;
 351         this.x = x;
 352         this.y = y;
 353         this.screenX = screenX;
 354         this.screenY = screenY;
 355         this.sceneX = x;
 356         this.sceneY = y;
 357         this.transferMode = transferMode;
 358         this.dragboard = dragboard;
 359 
 360         if (eventType == DragEvent.DRAG_DROPPED
 361                 || eventType == DragEvent.DRAG_DONE) {
 362             state.accepted = transferMode != null;
 363             state.acceptedTransferMode = transferMode;
 364             state.acceptingObject = state.accepted ? source : null;
 365         }
 366 
 367         this.pickResult = pickResult != null ? pickResult : new PickResult(
 368                 eventType == DRAG_DONE ? null : target, x, y);
 369         final Point3D p = InputEventUtils.recomputeCoordinates(this.pickResult, null);
 370         this.x = p.getX();
 371         this.y = p.getY();
 372         this.z = p.getZ();
 373     }
 374 
 375     /**
 376      * Constructs new DragEvent event with empty source and target.
 377      * @param eventType The type of the event.
 378      * @param dragboard the dragboard of the event.
 379      * @param x The x with respect to the scene.
 380      * @param y The y with respect to the scene.
 381      * @param screenX The x coordinate relative to screen.
 382      * @param screenY The y coordinate relative to screen.
 383      * @param transferMode the transfer mode of the event.
 384      * @param gestureSource the source of the DnD gesture of the event.
 385      * @param gestureTarget the target of the DnD gesture of the event.
 386      * @param pickResult pick result. Can be null, in this case a 2D pick result
 387      *                   without any further values is constructed
 388      *                   based on the scene coordinates
 389      * @since JavaFX 8.0
 390      */
 391     public DragEvent(@NamedArg("eventType") EventType<DragEvent> eventType, @NamedArg("dragboard") Dragboard dragboard,
 392             @NamedArg("x") double x, @NamedArg("y") double y,
 393             @NamedArg("screenX") double screenX, @NamedArg("screenY") double screenY, @NamedArg("transferMode") TransferMode transferMode,
 394             @NamedArg("gestureSource") Object gestureSource, @NamedArg("gestureTarget") Object gestureTarget, @NamedArg("pickResult") PickResult pickResult) {
 395         this(null, null, eventType, dragboard, x, y, screenX, screenY, transferMode,
 396                 gestureSource, gestureTarget, pickResult);
 397     }
 398 
 399     /**
 400      * Fills the given event by this event's coordinates recomputed to the given
 401      * source object
 402      * @param newEvent Event whose coordinates are to be filled
 403      * @param newSource Source object to compute coordinates for
 404      */
 405     private void recomputeCoordinatesToSource(DragEvent newEvent, Object newSource) {
 406 
 407         if (newEvent.getEventType() == DRAG_DONE) {
 408             // DRAG_DONE contains all zeros, doesn't make sense to recompute it
 409             return;
 410         }
 411 
 412         final Point3D newCoordinates = InputEventUtils.recomputeCoordinates(
 413                 pickResult, newSource);
 414 
 415         newEvent.x = newCoordinates.getX();
 416         newEvent.y = newCoordinates.getY();
 417         newEvent.z = newCoordinates.getZ();
 418     }
 419 
 420     @Override
 421     public DragEvent copyFor(Object newSource, EventTarget newTarget) {
 422         DragEvent e = (DragEvent) super.copyFor(newSource, newTarget);
 423         recomputeCoordinatesToSource(e, newSource);
 424         return e;
 425     }
 426 
 427     /**
 428      * Creates a copy of the given drag event with the given fields substituted.
 429      * @param source source of the copied event
 430      * @param target target of the copied event
 431      * @param type type of event
 432      * @return the event copy with the fields
 433      * @since JavaFX 8.0
 434      */
 435     public DragEvent copyFor(Object source, EventTarget target, EventType<DragEvent> type) {
 436         DragEvent e = (DragEvent) copyFor(source, target);
 437         e.eventType = type;
 438         return e;
 439     }
 440 
 441     @Override
 442     public EventType<DragEvent> getEventType() {
 443         return (EventType<DragEvent>) super.getEventType();
 444     }
 445 
 446     /**
 447      * Horizontal x position of the event relative to the
 448      * origin of the MouseEvent's node.
 449      */
 450     private transient double x;
 451 
 452     /**
 453      * Horizontal position of the event relative to the
 454      * origin of the DragEvent's source.
 455      *
 456      * @return horizontal position of the event relative to the
 457      * origin of the DragEvent's source
 458      */
 459     public final double getX() {
 460         return x;
 461     }
 462 
 463     /**
 464      * Vertical y position of the event relative to the
 465      * origin of the MouseEvent's node.
 466      */
 467     private transient double y;
 468 
 469     /**
 470      * Vertical position of the event relative to the
 471      * origin of the DragEvent's source.
 472      *
 473      * @return vertical position of the event relative to the
 474      * origin of the DragEvent's source
 475      */
 476     public final double getY() {
 477         return y;
 478     }
 479 
 480     /**
 481      * Depth z position of the event relative to the
 482      * origin of the MouseEvent's node.
 483      */
 484     private transient double z;
 485 
 486     /**
 487      * Depth position of the event relative to the
 488      * origin of the MouseEvent's source.
 489      *
 490      * @return depth position of the event relative to the
 491      * origin of the MouseEvent's source
 492      * @since JavaFX 8.0
 493      */
 494     public final double getZ() {
 495         return z;
 496     }
 497 
 498     /**
 499      * Absolute horizontal x position of the event.
 500      */
 501     private final double screenX;
 502 
 503     /**
 504      * Returns absolute horizontal position of the event.
 505      * @return absolute horizontal position of the event
 506      */
 507     public final double getScreenX() {
 508         return screenX;
 509     }
 510 
 511     /**
 512      * Absolute vertical y position of the event.
 513      */
 514     private final double screenY;
 515 
 516     /**
 517      * Returns absolute vertical position of the event.
 518      * @return absolute vertical position of the event
 519      */
 520     public final double getScreenY() {
 521         return screenY;
 522     }
 523 
 524     /**
 525      * Horizontal x position of the event relative to the
 526      * origin of the {@code Scene} that contains the DragEvent's node.
 527      * If the node is not in a {@code Scene}, then the value is relative to
 528      * the boundsInParent of the root-most parent of the DragEvent's node.
 529      */
 530     private final double sceneX;
 531 
 532     /**
 533      * Returns horizontal position of the event relative to the
 534      * origin of the {@code Scene} that contains the DragEvent's source.
 535      * If the node is not in a {@code Scene}, then the value is relative to
 536      * the boundsInParent of the root-most parent of the DragEvent's node.
 537      * Note that in 3D scene, this represents the flat coordinates after
 538      * applying the projection transformations.
 539      *
 540      * @return horizontal position of the event relative to the
 541      * origin of the {@code Scene} that contains the DragEvent's source
 542      */
 543     public final double getSceneX() {
 544         return sceneX;
 545     }
 546 
 547     /**
 548      * Vertical y position of the event relative to the
 549      * origin of the {@code Scene} that contains the DragEvent's node.
 550      * If the node is not in a {@code Scene}, then the value is relative to
 551      * the boundsInParent of the root-most parent of the DragEvent's node.
 552      */
 553     private final double sceneY;
 554 
 555     /**
 556      * Returns vertical position of the event relative to the
 557      * origin of the {@code Scene} that contains the DragEvent's source.
 558      * If the node is not in a {@code Scene}, then the value is relative to
 559      * the boundsInParent of the root-most parent of the DragEvent's node.
 560      * Note that in 3D scene, this represents the flat coordinates after
 561      * applying the projection transformations.
 562      *
 563      * @return vertical position of the event relative to the
 564      * origin of the {@code Scene} that contains the DragEvent's source
 565      */
 566     public final double getSceneY() {
 567         return sceneY;
 568     }
 569 
 570     /**
 571      * Information about the pick if the picked {@code Node} is a
 572      * {@code Shape3D} node and its pickOnBounds is false.
 573      */
 574     private PickResult pickResult;
 575 
 576     /**
 577      * Returns information about the pick.
 578      *
 579      * @return new PickResult object that contains information about the pick
 580      * @since JavaFX 8.0
 581      */
 582     public final PickResult getPickResult() {
 583         return pickResult;
 584     }
 585 
 586     /**
 587      * The source object of the drag and drop gesture.
 588      * Gesture source is the object that started drag and drop operation.
 589      * The value {@code null} is valid in the case that the gesture comes
 590      * from another application.
 591      * @return the source object of the drag and drop gesture
 592      */
 593     public final Object getGestureSource() { return gestureSource; }
 594     private Object gestureSource;
 595 
 596     /**
 597      * The target object of the drag and drop gesture.
 598      * Gesture target is the object that accepts drag events.
 599      * The value {@code null} is valid in the case that the drag and drop
 600      * gesture has been canceled or completed without a transfer taking place
 601      * or there is currently no event target accepting the drag events.
 602      * @return the target object of the drag and drop gesture
 603      */
 604     public final Object getGestureTarget() { return gestureTarget; }
 605     private Object gestureTarget;
 606 
 607     /**
 608      * Data transfer mode. Before the data transfer is is performed,
 609      * this is the default transfer mode set by system according to
 610      * input events such as the user holding some modifiers.
 611      * In time of data transfer (in DRAG_DROPPED event) it determines
 612      * the transfer mode accepted by previous DRAG_OVER handler.
 613      * After the data transfer (in DRAG_DONE event)
 614      * it determines the actual mode of the transfer done.
 615      * @return the data transfer mode
 616      */
 617     public final TransferMode getTransferMode() { return transferMode; }
 618     private TransferMode transferMode;
 619 
 620     private final State state = new State();
 621 
 622     /**
 623      * Indicates if this event has been accepted.
 624      * @return is this event has been accepted
 625      * @see #acceptTransferModes
 626      * @defaultValue false
 627      */
 628     public final boolean isAccepted() { return state.accepted; }
 629 
 630     /**
 631      * Gets transfer mode accepted by potential target.
 632      * @return transfer mode accepted by potential target
 633      */
 634     public final TransferMode getAcceptedTransferMode() {
 635         return state.acceptedTransferMode;
 636     }
 637 
 638     /**
 639      * The object that accepted the drag.
 640      * @return the object that accepted the drag
 641      * @since JavaFX 8.0
 642      */
 643     public final Object getAcceptingObject() {
 644         return state.acceptingObject;
 645     }
 646 
 647     /**
 648      * A dragboard that is available to transfer data.
 649      * Data can be placed onto this dragboard in handler of the
 650      * {@code DRAG_DETECTED} mouse event. Data can be copied from this
 651      * dragboard in handler of the {@code DRAG_DROPPED} event.
 652      * @return a dragboard that is available to transfer data
 653      */
 654     public final Dragboard getDragboard() {
 655         return dragboard;
 656     }
 657     private transient Dragboard dragboard;
 658 
 659     /**
 660      * Chooses a transfer mode for the operation
 661      * @param supported Transfer modes supported by gesture source
 662      * @param accepted Transfer modes accepted by gesture
 663      * @param proposed Transfer mode proposed by platform
 664      * @return The chosen transfer mode, null if none would work
 665      */
 666     private static TransferMode chooseTransferMode(Set<TransferMode> supported,
 667             TransferMode[] accepted, TransferMode proposed) {
 668 
 669         TransferMode result = null;
 670         Set<TransferMode> intersect = EnumSet.noneOf(TransferMode.class);
 671 
 672         for (TransferMode tm : InputEventUtils.safeTransferModes(accepted)) {
 673             if (supported.contains(tm)) {
 674                 intersect.add(tm);
 675             }
 676         }
 677 
 678         if (intersect.contains(proposed)) {
 679             result = proposed;
 680         } else {
 681             if (intersect.contains(TransferMode.MOVE)) {
 682                 result = TransferMode.MOVE;
 683             } else if (intersect.contains(TransferMode.COPY)) {
 684                 result = TransferMode.COPY;
 685             } else if (intersect.contains(TransferMode.LINK)) {
 686                 result = TransferMode.LINK;
 687             }
 688         }
 689 
 690         return result;
 691     }
 692 
 693     /**
 694      * Accepts this {@code DragEvent}, choosing the transfer mode for the
 695      * drop operation.
 696      * Used to indicate that the potential drop target
 697      * that receives this event is a drop target from {@code DRAG_OVER}
 698      * event handler.
 699      * <p>
 700      * It accepts one of the transfer modes that are both passed into this
 701      * method and supported by the gesture source. It accepts the default
 702      * transfer mode if possible, otherwise the most common one of the
 703      * acceptable modes.
 704      * @param transferModes the transfer mode for the drop operation.
 705      */
 706     public void acceptTransferModes(TransferMode... transferModes) {
 707 
 708         if (dragboard == null || dragboard.getTransferModes() == null ||
 709                 transferMode == null) {
 710             state.accepted = false;
 711             return;
 712         }
 713 
 714         TransferMode tm = chooseTransferMode(dragboard.getTransferModes(),
 715                 transferModes, transferMode);
 716 
 717         if (tm == null && getEventType() == DRAG_DROPPED) {
 718             throw new IllegalStateException("Accepting unsupported transfer "
 719                     + "modes inside DRAG_DROPPED handler");
 720         }
 721 
 722         state.accepted = tm != null;
 723         state.acceptedTransferMode = tm;
 724         state.acceptingObject = state.accepted ? source : null;
 725     }
 726 
 727     /**
 728      * Indicates that transfer handling of this {@code DragEvent} was completed
 729      * successfully during a {@code DRAG_DROPPED} event handler.
 730      * No {@link #getDragboard() dragboard} access can happen after this call.
 731      *
 732      * @param isTransferDone {@code true} indicates that the transfer was successful.
 733      * @throws IllegalStateException if this is not a DRAG_DROPPED event
 734      */
 735     public void setDropCompleted(boolean isTransferDone) {
 736         if (getEventType() != DRAG_DROPPED) {
 737             throw new IllegalStateException("setDropCompleted can be called " +
 738                     "only from DRAG_DROPPED handler");
 739         }
 740 
 741         state.dropCompleted = isTransferDone;
 742     }
 743 
 744     /**
 745      * Whether {@code setDropCompleted(true)} has been called on this event.
 746      * @return true if {@code setDropCompleted(true)} has been called
 747      */
 748     public boolean isDropCompleted() {
 749         return state.dropCompleted;
 750     }
 751 
 752     private void readObject(java.io.ObjectInputStream in)
 753             throws IOException, ClassNotFoundException {
 754         in.defaultReadObject();
 755         x = sceneX;
 756         y = sceneY;
 757     }
 758 
 759     /**
 760      * These properties need to live in a separate object shared among all the
 761      * copied events to make sure that the values are propagated to the
 762      * original event.
 763      */
 764     private static class State {
 765         /**
 766          * Whether this event has been accepted.
 767          */
 768         boolean accepted = false;
 769 
 770         /**
 771          * Whether drop completed successfully.
 772          */
 773         boolean dropCompleted = false;
 774 
 775         /**
 776          * Transfer mode accepted by the potential gesture target.
 777          */
 778         TransferMode acceptedTransferMode = null;
 779 
 780         /**
 781          * Object that accepted this event.
 782          */
 783         Object acceptingObject = null;
 784     }
 785 
 786 }