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             state.acceptingObject = state.accepted ? source : null;
 366         }
 367 
 368         this.pickResult = pickResult != null ? pickResult : new PickResult(
 369                 eventType == DRAG_DONE ? null : target, x, y);
 370         final Point3D p = InputEventUtils.recomputeCoordinates(this.pickResult, null);
 371         this.x = p.getX();
 372         this.y = p.getY();
 373         this.z = p.getZ();
 374     }
 375 
 376     /**
 377      * Constructs new DragEvent event with empty source and target.
 378      * @param eventType The type of the event.
 379      * @param dragboard the dragboard of the event.
 380      * @param x The x with respect to the scene.
 381      * @param y The y with respect to the scene.
 382      * @param screenX The x coordinate relative to screen.
 383      * @param screenY The y coordinate relative to screen.
 384      * @param transferMode the transfer mode of the event.
 385      * @param gestureSource the source of the DnD gesture of the event.
 386      * @param gestureTarget the target of the DnD gesture of the event.
 387      * @param pickResult pick result. Can be null, in this case a 2D pick result
 388      *                   without any further values is constructed
 389      *                   based on the scene coordinates
 390      * @since JavaFX 8.0
 391      */
 392     public DragEvent(@NamedArg("eventType") EventType<DragEvent> eventType, @NamedArg("dragboard") Dragboard dragboard,
 393             @NamedArg("x") double x, @NamedArg("y") double y,
 394             @NamedArg("screenX") double screenX, @NamedArg("screenY") double screenY, @NamedArg("transferMode") TransferMode transferMode,
 395             @NamedArg("gestureSource") Object gestureSource, @NamedArg("gestureTarget") Object gestureTarget, @NamedArg("pickResult") PickResult pickResult) {
 396         this(null, null, eventType, dragboard, x, y, screenX, screenY, transferMode,
 397                 gestureSource, gestureTarget, pickResult);
 398     }
 399 
 400     /**
 401      * Fills the given event by this event's coordinates recomputed to the given
 402      * source object
 403      * @param newEvent Event whose coordinates are to be filled
 404      * @param newSource Source object to compute coordinates for
 405      */
 406     private void recomputeCoordinatesToSource(DragEvent newEvent, Object newSource) {
 407 
 408         if (newEvent.getEventType() == DRAG_DONE) {
 409             // DRAG_DONE contains all zeros, doesn't make sense to recompute it
 410             return;
 411         }
 412 
 413         final Point3D newCoordinates = InputEventUtils.recomputeCoordinates(
 414                 pickResult, newSource);
 415 
 416         newEvent.x = newCoordinates.getX();
 417         newEvent.y = newCoordinates.getY();
 418         newEvent.z = newCoordinates.getZ();
 419     }
 420     
 421     @Override
 422     public DragEvent copyFor(Object newSource, EventTarget newTarget) {
 423         DragEvent e = (DragEvent) super.copyFor(newSource, newTarget);
 424         recomputeCoordinatesToSource(e, newSource);
 425         return e;
 426     }
 427 
 428     /**
 429      * Creates a copy of the given drag event with the given fields substituted.
 430      * @param source the new source of the copied event
 431      * @param target the new target of the copied event
 432      * @param eventType the new eventType
 433      * @return the event copy with the fields
 434      * @since JavaFX 8.0
 435      */
 436     public DragEvent copyFor(Object source, EventTarget target, EventType<DragEvent> type) {
 437         DragEvent e = (DragEvent) copyFor(source, target);
 438         e.eventType = type;
 439         return e;
 440     }
 441 
 442     @Override
 443     public EventType<DragEvent> getEventType() {
 444         return (EventType<DragEvent>) super.getEventType();
 445     }
 446 
 447     /**
 448      * Horizontal x position of the event relative to the
 449      * origin of the MouseEvent's node.
 450      */
 451     private transient double x;
 452 
 453     /**
 454      * Horizontal position of the event relative to the
 455      * origin of the DragEvent's source.
 456      * 
 457      * @return horizontal position of the event relative to the
 458      * origin of the DragEvent's source.
 459      */
 460     public final double getX() {
 461         return x;
 462     }
 463 
 464     /**
 465      * Vertical y position of the event relative to the
 466      * origin of the MouseEvent's node.
 467      */
 468     private transient double y;
 469 
 470     /**
 471      * Vertical position of the event relative to the
 472      * origin of the DragEvent's source.
 473      * 
 474      * @return vertical position of the event relative to the
 475      * origin of the DragEvent's source.
 476      */
 477     public final double getY() {
 478         return y;
 479     }
 480 
 481     /**
 482      * Depth z position of the event relative to the
 483      * origin of the MouseEvent's node.
 484      */
 485     private transient double z;
 486 
 487     /**
 488      * Depth position of the event relative to the
 489      * origin of the MouseEvent's source.
 490      *
 491      * @return depth position of the event relative to the
 492      * origin of the MouseEvent's source.
 493      * @since JavaFX 8.0
 494      */
 495     public final double getZ() {
 496         return z;
 497     }
 498 
 499     /**
 500      * Absolute horizontal x position of the event.
 501      */
 502     private final double screenX;
 503 
 504     /**
 505      * Returns absolute horizontal position of the event.
 506      * @return absolute horizontal position of the event
 507      */
 508     public final double getScreenX() {
 509         return screenX;
 510     }
 511 
 512     /**
 513      * Absolute vertical y position of the event.
 514      */
 515     private final double screenY;
 516 
 517     /**
 518      * Returns absolute vertical position of the event.
 519      * @return absolute vertical position of the event
 520      */
 521     public final double getScreenY() {
 522         return screenY;
 523     }
 524 
 525     /**
 526      * Horizontal x position of the event relative to the
 527      * origin of the {@code Scene} that contains the DragEvent's node.
 528      * If the node is not in a {@code Scene}, then the value is relative to
 529      * the boundsInParent of the root-most parent of the DragEvent's node.
 530      */
 531     private final double sceneX;
 532 
 533     /**
 534      * Returns horizontal position of the event relative to the
 535      * origin of the {@code Scene} that contains the DragEvent's source.
 536      * If the node is not in a {@code Scene}, then the value is relative to
 537      * the boundsInParent of the root-most parent of the DragEvent's node.
 538      * Note that in 3D scene, this represents the flat coordinates after
 539      * applying the projection transformations.
 540      * 
 541      * @return horizontal position of the event relative to the
 542      * origin of the {@code Scene} that contains the DragEvent's source
 543      */
 544     public final double getSceneX() {
 545         return sceneX;
 546     }
 547 
 548     /**
 549      * Vertical y position of the event relative to the
 550      * origin of the {@code Scene} that contains the DragEvent's node.
 551      * If the node is not in a {@code Scene}, then the value is relative to
 552      * the boundsInParent of the root-most parent of the DragEvent's node.
 553      */
 554     private final double sceneY;
 555 
 556     /**
 557      * Returns vertical position of the event relative to the
 558      * origin of the {@code Scene} that contains the DragEvent's source.
 559      * If the node is not in a {@code Scene}, then the value is relative to
 560      * the boundsInParent of the root-most parent of the DragEvent's node.
 561      * Note that in 3D scene, this represents the flat coordinates after
 562      * applying the projection transformations.
 563      * 
 564      * @return vertical position of the event relative to the
 565      * origin of the {@code Scene} that contains the DragEvent's source
 566      */
 567     public final double getSceneY() {
 568         return sceneY;
 569     }
 570 
 571     /**
 572      * Information about the pick if the picked {@code Node} is a
 573      * {@code Shape3D} node and its pickOnBounds is false.
 574      */
 575     private PickResult pickResult;
 576 
 577     /**
 578      * Returns information about the pick.
 579      *
 580      * @return new PickResult object that contains information about the pick
 581      * @since JavaFX 8.0
 582      */
 583     public final PickResult getPickResult() {
 584         return pickResult;
 585     }
 586 
 587     /**
 588      * The source object of the drag and drop gesture.
 589      * Gesture source is the object that started drag and drop operation.
 590      * The value {@code null} is valid in the case that the gesture comes
 591      * from another application.
 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      */
 603     public final Object getGestureTarget() { return gestureTarget; }
 604     private Object gestureTarget;
 605 
 606     /**
 607      * Data transfer mode. Before the data transfer is is performed,
 608      * this is the default transfer mode set by system according to
 609      * input events such as the user holding some modifiers.
 610      * In time of data transfer (in DRAG_DROPPED event) it determines
 611      * the transfer mode accepted by previous DRAG_OVER handler.
 612      * After the data transfer (in DRAG_DONE event)
 613      * it determines the actual mode of the transfer done.
 614      */
 615     public final TransferMode getTransferMode() { return transferMode; }
 616     private TransferMode transferMode;
 617 
 618     private final State state = new State();
 619 
 620     /**
 621      * Indicates if this event has been accepted.
 622      * @see #acceptTransferModes
 623      * @defaultValue false
 624      */
 625     public final boolean isAccepted() { return state.accepted; }
 626 
 627     /**
 628      * Gets transfer mode accepted by potential target.
 629      * @return transfer mode accepted by potential target.
 630      */
 631     public final TransferMode getAcceptedTransferMode() {
 632         return state.acceptedTrasferMode;
 633     }
 634 
 635     /**
 636      * The object that accepted the drag.
 637      * @return the object that accepted the drag.
 638      * @since JavaFX 8.0
 639      */
 640     public final Object getAcceptingObject() {
 641         return state.acceptingObject;
 642     }
 643 
 644     /**
 645      * A dragboard that is available to transfer data.
 646      * Data can be placed onto this dragboard in handler of the
 647      * {@code DRAG_DETECTED} mouse event. Data can be copied from this
 648      * dragboard in handler of the {@code DRAG_DROPPED} event.
 649      */
 650     public final Dragboard getDragboard() {
 651         return dragboard;
 652     }
 653     private transient Dragboard dragboard;
 654 
 655     /**
 656      * Chooses a transfer mode for the operation
 657      * @param supported Transfer modes supported by gesture source
 658      * @param accepted Transfer modes accepted by gesture
 659      * @param proposed Transfer mode proposed by platform
 660      * @return The chosen transfer mode, null if none would work
 661      */
 662     private static TransferMode chooseTransferMode(Set<TransferMode> supported,
 663             TransferMode[] accepted, TransferMode proposed) {
 664 
 665         TransferMode result = null;
 666         Set<TransferMode> intersect = EnumSet.noneOf(TransferMode.class);
 667 
 668         for (TransferMode tm : InputEventUtils.safeTransferModes(accepted)) {
 669             if (supported.contains(tm)) {
 670                 intersect.add(tm);
 671             }
 672         }
 673 
 674         if (intersect.contains(proposed)) {
 675             result = proposed;
 676         } else {
 677             if (intersect.contains(TransferMode.MOVE)) {
 678                 result = TransferMode.MOVE;
 679             } else if (intersect.contains(TransferMode.COPY)) {
 680                 result = TransferMode.COPY;
 681             } else if (intersect.contains(TransferMode.LINK)) {
 682                 result = TransferMode.LINK;
 683             }
 684         }
 685 
 686         return result;
 687     }
 688 
 689     /**
 690      * Accepts this {@code DragEvent}, choosing the transfer mode for the
 691      * drop operation.
 692      * Used to indicate that the potential drop target
 693      * that receives this event is a drop target from {@code DRAG_OVER}
 694      * event handler.
 695      * <p>
 696      * It accepts one of the transfer modes that are both passed into this
 697      * method and supported by the gesture source. It accepts the default
 698      * transfer mode if possible, otherwise the most common one of the
 699      * acceptable modes.
 700      */
 701     public void acceptTransferModes(TransferMode... transferModes) {
 702 
 703         if (dragboard == null || dragboard.getTransferModes() == null ||
 704                 transferMode == null) {
 705             state.accepted = false;
 706             return;
 707         }
 708 
 709         TransferMode tm = chooseTransferMode(dragboard.getTransferModes(),
 710                 transferModes, transferMode);
 711 
 712         if (tm == null && getEventType() == DRAG_DROPPED) {
 713             throw new IllegalStateException("Accepting unsupported transfer "
 714                     + "modes inside DRAG_DROPPED handler");
 715         }
 716 
 717         state.accepted = tm != null;
 718         state.acceptedTrasferMode = tm;
 719         state.acceptingObject = state.accepted ? source : null;
 720     }
 721 
 722     /**
 723      * Indicates that transfer handling of this {@code DragEvent} was completed
 724      * successfully during a {@code DRAG_DROPPED} event handler.
 725      * No {@link #dragboard} access can happen after this call.
 726      *
 727      * @param isTransferDone {@code true} indicates that the transfer was successful.
 728      * @throws IllegalStateException if this is not a DRAG_DROPPED event
 729      */
 730     public void setDropCompleted(boolean isTransferDone) {
 731         if (getEventType() != DRAG_DROPPED) {
 732             throw new IllegalStateException("setDropCompleted can be called " +
 733                     "only from DRAG_DROPPED handler");
 734         }
 735 
 736         state.dropCompleted = isTransferDone;
 737     }
 738 
 739     /**
 740      * Whether {@code setDropCompleted(true)} has been called on this event.
 741      * @return true if {@code setDropCompleted(true)} has been called
 742      */
 743     public boolean isDropCompleted() {
 744         return state.dropCompleted;
 745     }
 746 
 747     private void readObject(java.io.ObjectInputStream in)
 748             throws IOException, ClassNotFoundException {
 749         in.defaultReadObject();
 750         x = sceneX;
 751         y = sceneY;
 752     }
 753 
 754     /**
 755      * These properties need to live in a separate object shared among all the
 756      * copied events to make sure that the values are propagated to the
 757      * original event.
 758      */
 759     private static class State {
 760         /**
 761          * Whether this event has been accepted.
 762          */
 763         boolean accepted = false;
 764 
 765         /**
 766          * Whether drop completed successfully.
 767          */
 768         boolean dropCompleted = false;
 769 
 770         /**
 771          * Transfer mode accepted by the potential gesture target.
 772          */
 773         TransferMode acceptedTrasferMode = null;
 774 
 775         /**
 776          * Object that accepted this event.
 777          */
 778         Object acceptingObject = null;
 779     }
 780 
 781 }