1 /*
   2  * Copyright (c) 2011, 2013, 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  * <p>
  54  *
  55  * <br><h4>Drag sources: initiating a drag and drop gesture</h4>
  56  *
  57  * When a drag gesture is detected, an application can decide whether to
  58  * start a drag and drop gesture or continue with a press-drag-release gesture.
  59  * <p>
  60  * The default drag detection mechanism uses mouse movements with a pressed
  61  * button in combination with hysteresis. This behavior can be
  62  * augmented by the application. Each {@code MOUSE_PRESSED} and
  63  * {@code MOUSE_DRAGGED} event has a {@code dragDetect} flag that determines
  64  * whether a drag gesture has been detected. The default value of this flag
  65  * depends on the default detection mechanism and can be modified by calling
  66  * {@code setDragDetect()} inside of an event handler. When processing of
  67  * one of these events ends with the {@code dragDetect} flag set to true,
  68  * a {@code DRAG_DETECTED} {@code MouseEvent} is sent to the potential gesture
  69  * source (the object on which a mouse button has been pressed). This event
  70  * notifies about the gesture detection.
  71  * <p>
  72  * Inside a {@code DRAG_DETECTED} event handler, if the
  73  * {@code startDragAndDrop()} method is called on a node or scene and a dragged
  74  * data is made available to the returned {@code Dragboard}, the object on which
  75  * {@code startDragAndDrop()} has been called is considred a gesture source
  76  * and the drag and drop gesture is started. The {@code Dragboard} has system
  77  * clipboard functionality but is specifically used for drag and drop data
  78  * transfer.
  79  * <p>
  80  * The {@code startDragAndDrop()} method takes a set of {@code TransferMode}s
  81  * supported by the gesture source. For instance passing only
  82  * {@code TransferMode.COPY} indicates that the gesture source allows only
  83  * copying of the data, not moving or referencing.
  84  * <p>
  85  * Following example shows a simple drag and drop source:
  86  * <code><pre>
  87 Rectangle rect = new Rectangle(100, 100);
  88 rect.setOnDragDetected(new EventHandler<MouseEvent>() {
  89     &#64;Override public void handle(MouseEvent event) {
  90         Dragboard db = startDragAndDrop(TransferMode.ANY);
  91         ClipboardContent content = new ClipboardContent();
  92         content.putString("Hello!");
  93         db.setContent(content);
  94         event.consume();
  95     }
  96 });
  97  * </pre></code>
  98  *
  99  * <br><h4>Potential drop targets</h4>
 100  *
 101  * <p>
 102  * After the drag and drop gesture has been started, any object
 103  * ({@code Node}, {@code Scene}) over which the mouse is dragged is
 104  * a potential drop target.
 105  * <p>
 106  * When the mouse is dragged into the boundaries of potential drop target,
 107  * the potential target gets a {@code DRAG_ENTERED} event. When the mouse is
 108  * dragged outside of the potential target's bounds, it gets a
 109  * {@code DRAG_EXITED} event. There are also the bubbling
 110  * {@code DRAG_ENTERED_TARGET} and {@code DRAG_EXITED_TARGET} variants. They
 111  * behave similarly to mouse entered/exited events, please see
 112  * {@code MouseEvent} overview.
 113  * <p>
 114  * A potential drop target can decide to change its appearance to
 115  * let the user know that the dragged data can be dropped on it. This can be
 116  * done in a {@code DRAG_OVER} event handler, based on the position of the
 117  * mouse. Another option is to change the potential target's appearance in
 118  * a {@code DRAG_ENTERED} and {@code DRAG_EXITED} handlers.
 119  * <p>
 120  * In {@code DRAG_OVER} event handler a potential drop target has the ability
 121  * to make it known that it is an actual target. This is done by calling
 122  * {@code acceptTransferModes(TransferMode...)} on the event,
 123  * passing transfer modes it is willing to accept.
 124  * If it <i>is not called</i> during the event delivery or if none of the
 125  * passed transfer modes is supported by gesture source, then the potential
 126  * drop target <i>is not considered to be an actual drop target</i>.
 127  * <p>
 128  * When deciding weather to accept the event by calling {@code acceptTransferModes(TransferMode...)},
 129  * the type of data available on the {@code Dragboard} should be considered.
 130  * Access to the {@code Dragboard} is provided by the {@code getDragboard()}
 131  * method.
 132  * <p>
 133  * When accepting an event, the potential gesture target decides which
 134  * {@code TransferMode} is accepted for the operation. To make the decision,
 135  * {@code DragBoard.getTransferModes()} (set of transfer modes supported by
 136  * the gesture source) and {@code DragEvent.getTransferMode()} (default
 137  * transfer mode issued by platform, driven by key modifiers) can be used.
 138  * It is poosible to pass more transfer modes into the
 139  * {@code acceptTransferModes(TransferMode...)} method. In this case
 140  * it makes the decision in behalf of the
 141  * application (it chooses the default mode if it's supported by gesture source
 142  * and accepted by gesture target, otherwise it chooses the most common mode
 143  * of the supported and accepted ones).
 144  * The {@code DRAG_DROPPED} event's {@code getTransferMode()} later reports the
 145  * transfer mode accepted by the {@code DRAG_OVER} event handler.
 146  * <p>
 147  * A drag and drop gesture ends when the mouse button is released.
 148  * If this happens over a gesture target that accepted previous {@code DRAG_OVER}
 149  * events with a transfer mode supported by gesture source,
 150  * a {@code DRAG_DROPPED} event is sent to the gesture target.
 151  * In its handler, the gesture target can access the data on the dragboard.
 152  * After data has been transferred (or decided not to transfer), the gesture
 153  * needs to be completed by calling {@code setDropCompleted(Boolean)} on the event.
 154  * The {@code Boolean} argument indicates if the data has been transferred
 155  * successfully or not. If it is not called, the gesture is considered
 156  * unsuccessful.
 157  *
 158  * <p>
 159  * Following example shows a simple drag and drop target for text data:
 160  * <code><pre>
 161 Rectangle rect = new Rectangle(100, 100);
 162 
 163 rect.setOnDragOver(new EventHandler<DragEvent>() {
 164     &#64;Override public void handle(DragEvent event) {
 165         Dragboard db = event.getDragboard();
 166         if (db.hasString()) {
 167             event.acceptTransferModes(TransferMode.COPY_OR_MOVE);
 168         }
 169         event.consume();
 170     }
 171 });
 172 
 173 rect.setOnDragDropped(new EventHandler<DragEvent>() {
 174     &#64;Override public void handle(DragEvent event) {
 175         Dragboard db = event.getDragboard();
 176         boolean success = false;
 177         if (db.hasString()) {
 178             System.out.println("Dropped: " + db.getString());
 179             success = true;
 180         }
 181         event.setDropCompleted(success);
 182         event.consume();
 183     }
 184 });
 185  * </pre></code>
 186  *
 187  * <br><h4>Drag sources: finalizing drag and drop gesture</h4>
 188  *
 189  * <p>
 190  * After the gesture has been finished, whether by successful or unsuccessful
 191  * data transfer or being canceled, the {@code DRAG_DONE} event is sent to 
 192  * the gesture source. The {@code getTransferMode()} method of the event
 193  * indicates to the gesture source how the transfer of data was completed.
 194  * If the transfer mode has the value {@code MOVE}, then this allows the source
 195  * to clear out its data. Clearing the source's data gives the appropriate
 196  * appearance to a user that the data has been moved by the drag and drop 
 197  * gesture. If it has the value {@code null}, then the drag and drop gesture
 198  * ended without any data being transferred.  This could happen as a result of
 199  * a mouse release event over a node that is not a drop target, or the user
 200  * pressing the ESC key to cancel the drag and drop gesture, or by
 201  * the gesture target reporting an unsuccessful data transfer.
 202  * </p>
 203  * @since JavaFX 2.0
 204  */
 205 public final class DragEvent extends InputEvent {
 206 
 207     private static final long serialVersionUID = 20121107L;
 208     
 209     /**
 210      * Common supertype for all drag event types.
 211      */
 212     public static final EventType<DragEvent> ANY =
 213             new EventType<DragEvent>(InputEvent.ANY, "DRAG");
 214 
 215     /**
 216      * This event occurs when drag gesture enters a node. It's the
 217      * bubbling variant, which is delivered also to all parents of the
 218      * entered node (unless it was consumed). When notifications about
 219      * entering some of node's children are not desired,
 220      * {@code DRAG_ENTERED} event handler should be used.
 221      *
 222      * @see MouseEvent MouseEvent for more information about mouse entered/exited handling
 223      * which is similar
 224      */
 225     public static final EventType<DragEvent> DRAG_ENTERED_TARGET =
 226             new EventType<DragEvent>(DragEvent.ANY, "DRAG_ENTERED_TARGET");
 227 
 228     /**
 229      * This event occurs when drag gesture enters a node.
 230      * This event type is delivered only to the entered node,
 231      * if parents want to filter it or get the bubbling event,
 232      * they need to use {@code DRAG_ENTERED_TARGET}.
 233      *
 234      * @see MouseEvent MouseEvent for more information about mouse entered/exited handling
 235      * which is similar
 236      */
 237     public static final EventType<DragEvent> DRAG_ENTERED =
 238             new EventType<DragEvent>(DragEvent.DRAG_ENTERED_TARGET, "DRAG_ENTERED");
 239 
 240     /**
 241      * This event occurs when drag gesture exits a node. It's the
 242      * bubbling variant, which is delivered also to all parents of the
 243      * eixited node (unless it was consumed). When notifications about
 244      * exiting some of node's children are not desired,
 245      * {@code DRAG_EXITED} event handler should be used.
 246      *
 247      * @see MouseEvent MouseEvent for more information about mouse entered/exited handling
 248      * which is similar
 249      */
 250     public static final EventType<DragEvent> DRAG_EXITED_TARGET =
 251             new EventType<DragEvent>(DragEvent.ANY, "DRAG_EXITED_TARGET");
 252 
 253     /**
 254      * This event occurs when drag gesture exits a node.
 255      * This event type is delivered only to the exited node,
 256      * if parents want to filter it or get the bubbling event,
 257      * they need to use {@code DRAG_EXITED_TARGET}.
 258      *
 259      * @see MouseEvent MouseEvent for more information about mouse entered/exited handling
 260      * which is similar
 261      */
 262     public static final EventType<DragEvent> DRAG_EXITED =
 263             new EventType<DragEvent>(DragEvent.DRAG_EXITED_TARGET, "DRAG_EXITED");
 264 
 265     /**
 266      * This event occurs when drag gesture progresses within this node.
 267      */
 268     public static final EventType<DragEvent> DRAG_OVER =
 269             new EventType<DragEvent>(DragEvent.ANY, "DRAG_OVER");
 270 
 271     // Do we want DRAG_TRANSFER_MODE_CHANGED event?
 272 //    /**
 273 //     * This event occurs on a potential drag-and-drop target when the user
 274 //     * takes action to change the intended {@code TransferMode}.
 275 //     * The user can change the intended {@link TransferMode} by holding down
 276 //     * or releasing key modifiers.
 277 //     */
 278 //    public static final EventType<DragEvent> DRAG_TRANSFER_MODE_CHANGED =
 279 //            new EventType<DragEvent>(DragEvent.ANY, "DRAG_TRANSFER_MODE_CHANGED");
 280 
 281     /**
 282      * This event occurs when the mouse button is released during drag and drop
 283      * gesture on a drop target. Transfer of data from the
 284      * {@link DragEvent}'s {@link DragEvent#dragboard dragboard} should happen
 285      * in handler of this event.
 286      */
 287     public static final EventType<DragEvent> DRAG_DROPPED =
 288             new EventType<DragEvent>(DragEvent.ANY, "DRAG_DROPPED");
 289 
 290     /**
 291      * This event occurs on drag-and-drop gesture source after its data has
 292      * been dropped on a drop target. The {@code transferMode} of the
 293      * event shows what just happened at the drop target.
 294      * If {@code transferMode} has the value {@code MOVE}, then the source can
 295      * clear out its data. Clearing the source's data gives the appropriate
 296      * appearance to a user that the data has been moved by the drag and drop
 297      * gesture. A {@code transferMode} that has the value {@code NONE}
 298      * indicates that no data was transferred during the drag and drop gesture.
 299      */
 300     public static final EventType<DragEvent> DRAG_DONE =
 301             new EventType<DragEvent>(DragEvent.ANY, "DRAG_DONE");
 302 
 303     /**
 304      * Creates a copy of the given drag event with the given fields substituted.
 305      * @param source the new source of the copied event
 306      * @param target the new target of the copied event
 307      * @param gestureSource the new gesture source.
 308      * @param gestureTarget the new gesture target.
 309      * @param eventType the new eventType
 310      * @return the event copy with the fields
 311      * @since JavaFX 8.0
 312      */
 313     public DragEvent copyFor(Object source, EventTarget target,
 314             Object gestureSource, Object gestureTarget,
 315             EventType<DragEvent> eventType) {
 316 
 317         DragEvent copyEvent = copyFor(source, target, eventType);
 318         recomputeCoordinatesToSource(copyEvent, source);
 319         copyEvent.gestureSource = gestureSource;
 320         copyEvent.gestureTarget = gestureTarget;
 321         return copyEvent;
 322     }
 323 
 324     /**
 325      * Constructs new DragEvent event.
 326      * For DRAG_DROPPED and DRAG_DONE event types, the {@code accepted} state
 327      * and {@code acceptedTransferMode} are set according to the passed
 328      * {@code transferMode}.
 329      * @param source the source of the event. Can be null.
 330      * @param target the target of the event. Can be null.
 331      * @param eventType The type of the event.
 332      * @param dragboard the dragboard of the event.
 333      * @param x The x with respect to the scene.
 334      * @param y The y with respect to the scene.
 335      * @param screenX The x coordinate relative to screen.
 336      * @param screenY The y coordinate relative to screen.
 337      * @param transferMode the transfer mode of the event.
 338      * @param gestureSource the source of the DnD gesture of the event.
 339      * @param gestureTarget the target of the DnD gesture of the event.
 340      * @param pickResult pick result. Can be null, in this case a 2D pick result
 341      *                   without any further values is constructed
 342      *                   based on the scene coordinates and the target
 343      * @since JavaFX 8.0
 344      */
 345     public DragEvent(@NamedArg("source") Object source, @NamedArg("target") EventTarget target, @NamedArg("eventType") EventType<DragEvent> eventType, @NamedArg("dragboard") Dragboard dragboard,
 346             @NamedArg("x") double x, @NamedArg("y") double y,
 347             @NamedArg("screenX") double screenX, @NamedArg("screenY") double screenY, @NamedArg("transferMode") TransferMode transferMode,
 348             @NamedArg("gestureSource") Object gestureSource, @NamedArg("gestureTarget") Object gestureTarget, @NamedArg("pickResult") PickResult pickResult) {
 349         super(source, target, eventType);
 350         this.gestureSource = gestureSource;
 351         this.gestureTarget = gestureTarget;
 352         this.x = x;
 353         this.y = y;
 354         this.screenX = screenX;
 355         this.screenY = screenY;
 356         this.sceneX = x;
 357         this.sceneY = y;
 358         this.transferMode = transferMode;
 359         this.dragboard = dragboard;
 360 
 361         if (eventType == DragEvent.DRAG_DROPPED
 362                 || eventType == DragEvent.DRAG_DONE) {
 363             state.accepted = transferMode != null;
 364             state.acceptedTrasferMode = transferMode;
 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 the new source of the copied event
 430      * @param target the new target of the copied event
 431      * @param eventType the new eventType
 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      */
 592     public final Object getGestureSource() { return gestureSource; }
 593     private Object gestureSource;
 594 
 595     /**
 596      * The target object of the drag and drop gesture.
 597      * Gesture target is the object that accepts drag events.
 598      * The value {@code null} is valid in the case that the drag and drop
 599      * gesture has been canceled or completed without a transfer taking place
 600      * or there is currently no event target accepting the drag events.
 601      */
 602     public final Object getGestureTarget() { return gestureTarget; }
 603     private Object gestureTarget;
 604 
 605     /**
 606      * Data transfer mode. Before the data transfer is is performed,
 607      * this is the default transfer mode set by system according to
 608      * input events such as the user holding some modifiers.
 609      * In time of data transfer (in DRAG_DROPPED event) it determines
 610      * the transfer mode accepted by previous DRAG_OVER handler.
 611      * After the data transfer (in DRAG_DONE event)
 612      * it determines the actual mode of the transfer done.
 613      */
 614     public final TransferMode getTransferMode() { return transferMode; }
 615     private TransferMode transferMode;
 616 
 617     private final State state = new State();
 618 
 619     /**
 620      * Indicates if this event has been accepted.
 621      * @see #acceptTransferModes
 622      * @defaultValue false
 623      */
 624     public final boolean isAccepted() { return state.accepted; }
 625 
 626     /**
 627      * Gets transfer mode accepted by potential target.
 628      * @return transfer mode accepted by potential target.
 629      */
 630     public final TransferMode getAcceptedTransferMode() {
 631         return state.acceptedTrasferMode;
 632     }
 633 
 634     /**
 635      * The object that accepted the drag.
 636      * @return the object that accepted the drag.
 637      * @since JavaFX 8.0
 638      */
 639     public final Object getAcceptingObject() {
 640         return state.acceptingObject;
 641     }
 642 
 643     /**
 644      * A dragboard that is available to transfer data.
 645      * Data can be placed onto this dragboard in handler of the
 646      * {@code DRAG_DETECTED} mouse event. Data can be copied from this
 647      * dragboard in handler of the {@code DRAG_DROPPED} event.
 648      */
 649     public final Dragboard getDragboard() {
 650         return dragboard;
 651     }
 652     private transient Dragboard dragboard;
 653 
 654     /**
 655      * Chooses a transfer mode for the operation
 656      * @param supported Transfer modes supported by gesture source
 657      * @param accepted Transfer modes accepted by gesture
 658      * @param proposed Transfer mode proposed by platform
 659      * @return The chosen transfer mode, null if none would work
 660      */
 661     private static TransferMode chooseTransferMode(Set<TransferMode> supported,
 662             TransferMode[] accepted, TransferMode proposed) {
 663 
 664         TransferMode result = null;
 665         Set<TransferMode> intersect = EnumSet.noneOf(TransferMode.class);
 666 
 667         for (TransferMode tm : InputEventUtils.safeTransferModes(accepted)) {
 668             if (supported.contains(tm)) {
 669                 intersect.add(tm);
 670             }
 671         }
 672 
 673         if (intersect.contains(proposed)) {
 674             result = proposed;
 675         } else {
 676             if (intersect.contains(TransferMode.MOVE)) {
 677                 result = TransferMode.MOVE;
 678             } else if (intersect.contains(TransferMode.COPY)) {
 679                 result = TransferMode.COPY;
 680             } else if (intersect.contains(TransferMode.LINK)) {
 681                 result = TransferMode.LINK;
 682             }
 683         }
 684 
 685         return result;
 686     }
 687 
 688     /**
 689      * Accepts this {@code DragEvent}, choosing the transfer mode for the
 690      * drop operation.
 691      * Used to indicate that the potential drop target
 692      * that receives this event is a drop target from {@code DRAG_OVER}
 693      * event handler.
 694      * <p>
 695      * It accepts one of the transfer modes that are both passed into this
 696      * method and supported by the gesture source. It accepts the default
 697      * transfer mode if possible, otherwise the most common one of the
 698      * acceptable modes.
 699      */
 700     public void acceptTransferModes(TransferMode... transferModes) {
 701 
 702         if (dragboard == null || dragboard.getTransferModes() == null ||
 703                 transferMode == null) {
 704             state.accepted = false;
 705             return;
 706         }
 707 
 708         TransferMode tm = chooseTransferMode(dragboard.getTransferModes(),
 709                 transferModes, transferMode);
 710 
 711         if (tm == null && getEventType() == DRAG_DROPPED) {
 712             throw new IllegalStateException("Accepting unsupported transfer "
 713                     + "modes inside DRAG_DROPPED handler");
 714         }
 715 
 716         state.accepted = tm != null;
 717         state.acceptedTrasferMode = tm;
 718         state.acceptingObject = state.accepted ? source : null;
 719     }
 720 
 721     /**
 722      * Indicates that transfer handling of this {@code DragEvent} was completed
 723      * successfully during a {@code DRAG_DROPPED} event handler.
 724      * No {@link #dragboard} access can happen after this call.
 725      *
 726      * @param isTransferDone {@code true} indicates that the transfer was successful.
 727      * @throws IllegalStateException if this is not a DRAG_DROPPED event
 728      */
 729     public void setDropCompleted(boolean isTransferDone) {
 730         if (getEventType() != DRAG_DROPPED) {
 731             throw new IllegalStateException("setDropCompleted can be called " +
 732                     "only from DRAG_DROPPED handler");
 733         }
 734 
 735         state.dropCompleted = isTransferDone;
 736     }
 737 
 738     /**
 739      * Whether {@code setDropCompleted(true)} has been called on this event.
 740      * @return true if {@code setDropCompleted(true)} has been called
 741      */
 742     public boolean isDropCompleted() {
 743         return state.dropCompleted;
 744     }
 745 
 746     private void readObject(java.io.ObjectInputStream in)
 747             throws IOException, ClassNotFoundException {
 748         in.defaultReadObject();
 749         x = sceneX;
 750         y = sceneY;
 751     }
 752 
 753     /**
 754      * These properties need to live in a separate object shared among all the
 755      * copied events to make sure that the values are propagated to the
 756      * original event.
 757      */
 758     private static class State {
 759         /**
 760          * Whether this event has been accepted.
 761          */
 762         boolean accepted = false;
 763 
 764         /**
 765          * Whether drop completed successfully.
 766          */
 767         boolean dropCompleted = false;
 768 
 769         /**
 770          * Transfer mode accepted by the potential gesture target.
 771          */
 772         TransferMode acceptedTrasferMode = null;
 773 
 774         /**
 775          * Object that accepted this event.
 776          */
 777         Object acceptingObject = null;
 778     }
 779 
 780 }