1 /*
   2  * Copyright (c) 2010, 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 com.sun.javafx.tk.Toolkit;
  29 import javafx.beans.NamedArg;
  30 import javafx.event.Event;
  31 import javafx.event.EventTarget;
  32 import javafx.event.EventType;
  33 import javafx.geometry.Point3D;
  34 import javafx.scene.Node;
  35 
  36 import com.sun.javafx.scene.input.InputEventUtils;
  37 import java.io.IOException;
  38 
  39 // PENDING_DOC_REVIEW
  40 /**
  41  * When mouse event occurs, the top-most node under cursor is picked and
  42  * the event is delivered to it through capturing and bubbling phases
  43  * described at {@link javafx.event.EventDispatcher EventDispatcher}.
  44  * <p>
  45  * The mouse (pointer's) location is available relative to several
  46  * coordinate systems: x,y - relative to the origin of the
  47  * MouseEvent's node, sceneX,sceneY - relative to to the
  48  * origin of the {@code Scene} that contains the node,
  49  * screenX,screenY - relative to origin of the screen that
  50  * contains the mouse pointer.
  51  *
  52  * <h4>Dragging gestures</h4>
  53  * <p>
  54  * There are three types of dragging gestures. They are all initiated by
  55  * a mouse press event and terminated as a result of a mouse released
  56  * event, the source node decides which gesture will take place.
  57  * <p>
  58  * The simple press-drag-release gesture is default. It's best used to allow
  59  * changing size of a shape, dragging it around and so on. Whole
  60  * press-drag-release gesture is delivered to one node. When mouse
  61  * button is pressed, the top-most node is picked and all subsequent
  62  * mouse events are delivered to the same node until the button is released.
  63  * If a mouse clicked event is generated from these events, it is still
  64  * delivered to the same node.
  65  * <p>
  66  * During simple press-drag-release gesture, the other nodes are not involved
  67  * and don't get any events. If these nodes need to be involved in the gesture,
  68  * full press-drag-release gesture has to be activated. This gesture is
  69  * best used for connecting nodes by "wires", dragging nodes to other nodes etc.
  70  * This gesture type is more closely described at
  71  * {@link javafx.scene.input.MouseDragEvent MouseDragEvent} which contains
  72  * the events delivered to the gesture targets.
  73  * <p>
  74  * The third gesture type is platform-supported drag-and-drop gesture. It serves
  75  * best to transfer data and works also between (not necessarily FX)
  76  * applications. This gesture type is more closely described
  77  * at {@link javafx.scene.input.DragEvent DragEvent}.
  78  * <p>
  79  * In a short summary, simple press-drag-release gesture is activated
  80  * automatically when a mouse button is pressed and delivers all
  81  * {@code MouseEvent}s to the gesture source. When you start dragging,
  82  * eventually the {@code DRAG_DETECTED} event arrives. In its handler
  83  * you can either start full press-drag-release gesture by calling
  84  * {@code startFullDrag} method on a node or scene - the {@code MouseDragEvent}s
  85  * start to be delivered to gesture targets, or you can start drag and drop
  86  * gesture by calling {@code startDragAndDrop} method on a node or scene -
  87  * the system switches into the drag and drop mode and {@code DragEvent}s start
  88  * to be delivered instead of {@code MouseEvent}s. If you don't call any of
  89  * those methods, the simple press-drag-release gesture continues.
  90  * <p>
  91  * Note that dragging a finger over touch screen produces mouse dragging events,
  92  * but also scroll gesture events. If it means a conflict in an application
  93  * (the physical dragging action is handled by two different handlers), the
  94  * {@code isSynthesized()} method may be used to detect the problem and make the
  95  * dragging handlers behave accordingly.
  96  *
  97  * <h4>Mouse enter/exit handling</h4>
  98  * <p>
  99  * When mouse enters a node, the node gets {@code MOUSE_ENTERED} event, when
 100  * it leaves, it gets {@code MOUSE_EXITED} event. These events are delivered
 101  * only to the entered/exited node and seemingly don't go through the
 102  * capturing/bubbling phases. This is the most common use-case.
 103  * <p>
 104  * When the capturing or bubbling is desired, there are
 105  * {@code MOUSE_ENTERED_TARGET}/{@code MOUSE_EXITED_TARGET} events. These events
 106  * go through capturing/bubbling phases normally. This means that parent may
 107  * receive the {@code MOUSE_ENTERED_TARGET} event when mouse entered
 108  * either the parent itself or some of its children. To distinguish between
 109  * these two cases event target can be tested on equality with the node.
 110  * <p>
 111  * These two types are closely connected:
 112  * {@code MOUSE_ENTERED}/{@code MOUSE_EXITED} are subtypes
 113  * of {@code MOUSE_ENTERED_TARGET}/{@code MOUSE_EXITED_TARGET}.
 114  * During capturing phase,
 115  * {@code MOUSE_ENTERED_TARGET} is delivered to the
 116  * parents. When the event is delivered to the event target (the node that
 117  * has actually been entered), its type is switched to
 118  * {@code MOUSE_ENTERED}. Then the type is switched back to
 119  * {@code MOUSE_ENTERED_TARGET} for the bubbling phase.
 120  * It's still one event just switching types, so if it's filtered or consumed,
 121  * it affects both event variants. Thanks to the subtype-relationship, a
 122  * {@code MOUSE_ENTERED_TARGET} event handler will receive the
 123  * {@code MOUSE_ENTERED} event on target.
 124  *
 125  * <h4>Notes</h4>
 126  * <ul>
 127  *   <li>For triggering context menus see the {@link ContextMenuEvent}.</li>
 128  * </ul>
 129  * @since JavaFX 2.0
 130  */
 131 public class MouseEvent extends InputEvent {
 132 
 133     private static final long serialVersionUID = 20121107L;
 134 
 135     /**
 136      * Common supertype for all mouse event types.
 137      */
 138     public static final EventType<MouseEvent> ANY =
 139             new EventType<MouseEvent>(InputEvent.ANY, "MOUSE");
 140 
 141     /**
 142      * This event occurs when mouse button is pressed. This activates a
 143      * press-drag-release gesture, so all subsequent mouse events until
 144      * the button is released are delivered to the same node.
 145      */
 146     public static final EventType<MouseEvent> MOUSE_PRESSED =
 147             new EventType<MouseEvent>(MouseEvent.ANY, "MOUSE_PRESSED");
 148 
 149     /**
 150      * This event occurs when mouse button is released. It is delivered
 151      * to the same node where the button has been pressed which activated
 152      * a press-drag-release gesture.
 153      */
 154     public static final EventType<MouseEvent> MOUSE_RELEASED =
 155             new EventType<MouseEvent>(MouseEvent.ANY, "MOUSE_RELEASED");
 156 
 157     /**
 158      * This event occurs when mouse button has been clicked (pressed and
 159      * released on the same node). This event provides a button-like behavior
 160      * to any node. Note that even long drags can generate click event (it
 161      * is delivered to the top-most node on which the mouse was both
 162      * pressed and released).
 163      */
 164     public static final EventType<MouseEvent> MOUSE_CLICKED =
 165             new EventType<MouseEvent>(MouseEvent.ANY, "MOUSE_CLICKED");
 166 
 167     /**
 168      * This event occurs when mouse enters a node. It's the bubbling variant,
 169      * which is delivered also to all parents of the entered node (unless it
 170      * was consumed). When notifications about mouse entering some of node's
 171      * children are not desired, {@code MOUSE_ENTERED} event handler should
 172      * be used.
 173      *
 174      * @see MouseEvent MouseEvent for more information about mouse entered/exited handling
 175      */
 176     public static final EventType<MouseEvent> MOUSE_ENTERED_TARGET =
 177             new EventType<MouseEvent>(MouseEvent.ANY, "MOUSE_ENTERED_TARGET");
 178 
 179     /**
 180      * This event occurs when mouse enters a node. This event type is delivered
 181      * only to the entered node, if parents want to filter it or get the
 182      * bubbling event, they need to use {@code MOUSE_ENTERED_TARGET}.
 183      *
 184      * @see MouseEvent MouseEvent for more information about mouse entered/exited handling
 185      */
 186     public static final EventType<MouseEvent> MOUSE_ENTERED =
 187             new EventType<MouseEvent>(MouseEvent.MOUSE_ENTERED_TARGET, "MOUSE_ENTERED");
 188 
 189     /**
 190      * This event occurs when mouse exits a node. It's the bubbling variant,
 191      * which is delivered also to all parents of the exited node (unless it
 192      * was consumed). When notifications about mouse exiting some of node's
 193      * children are not desired, {@code MOUSE_EXITED} event handler should
 194      * be used.
 195      *
 196      * @see MouseEvent MouseEvent for more information about mouse entered/exited handling
 197      */
 198     public static final EventType<MouseEvent> MOUSE_EXITED_TARGET =
 199             new EventType<MouseEvent>(MouseEvent.ANY, "MOUSE_EXITED_TARGET");
 200 
 201     /**
 202      * This event occurs when mouse exits a node. This event type is delivered
 203      * only to the exited node, if parents want to filter it or get the
 204      * bubbling event, they need to use {@code MOUSE_EXITED_TARGET}.
 205      *
 206      * @see MouseEvent MouseEvent for more information about mouse entered/exited handling
 207      */
 208     public static final EventType<MouseEvent> MOUSE_EXITED =
 209             new EventType<MouseEvent>(MouseEvent.MOUSE_EXITED_TARGET, "MOUSE_EXITED");
 210 
 211     /**
 212      * This event occurs when mouse moves within a node and no buttons
 213      * are pressed. If any mouse button is pressed, MOUSE_DRAGGED event
 214      * occurs instead.
 215      */
 216     public static final EventType<MouseEvent> MOUSE_MOVED =
 217             new EventType<MouseEvent>(MouseEvent.ANY, "MOUSE_MOVED");
 218 
 219     /**
 220      * This event occurs when mouse moves with a pressed button.
 221      * It is delivered to the same node where the button has been pressed
 222      * which activated a press-drag-release gesture. It is delivered
 223      * regardless of the mouse being within bounds of the node.
 224      */
 225     public static final EventType<MouseEvent> MOUSE_DRAGGED =
 226             new EventType<MouseEvent>(MouseEvent.ANY, "MOUSE_DRAGGED");
 227 
 228     /**
 229      * This event is delivered to a node that is identified as a source of a
 230      * dragging gesture. Handler of this event is the only place where
 231      * full press-drag-release gesture or a drag and drop gesture can be
 232      * started (by calling {@link javafx.scene.Node#startFullDrag startFullDrag()}
 233      * of {@link javafx.scene.Node#startDragAndDrop startDragAndDrop()} method).
 234      * If none of them is called, simple press-drag-release gesture will continue.
 235      * <p>
 236      * Note that his event is generated based on dragging the mouse over a
 237      * platform-specific distance threshold. You can modify this behavior
 238      * by calling {@code setDragDetect} method on any MOUSE_PRESSED or
 239      * MOUSE_DRAGGED event.
 240      *
 241      * @see MouseEvent MouseEvent for more details about simple press-drag-release gestures
 242      * @see MouseDragEvent MouseDragEvent for more details about full press-drag-release gestures
 243      * @see DragEvent DragEvent for more details about drag and drop gestures
 244      */
 245     public static final EventType<MouseEvent> DRAG_DETECTED =
 246             new EventType<MouseEvent>(MouseEvent.ANY, "DRAG_DETECTED");
 247 
 248     /**
 249      * Fills the given event by this event's coordinates recomputed to the given
 250      * source object
 251      * @param newEvent Event whose coordinates are to be filled
 252      * @param newSource Source object to compute coordinates for
 253      */
 254     void recomputeCoordinatesToSource(MouseEvent oldEvent, Object newSource) {
 255 
 256         final Point3D newCoordinates = InputEventUtils.recomputeCoordinates(
 257                 pickResult, newSource);
 258 
 259         x = newCoordinates.getX();
 260         y = newCoordinates.getY();
 261         z = newCoordinates.getZ();
 262     }
 263 
 264     @Override
 265     public EventType<? extends MouseEvent> getEventType() {
 266         return (EventType<? extends MouseEvent>) super.getEventType();
 267     }
 268 
 269     /**
 270      * Copies this event for a different source and target.
 271      * In most cases you don't need to use this method, it's called
 272      * automatically when you fire the event.
 273      * @param newSource New event source
 274      * @param newTarget New event target
 275      * @return copy of this event for a different source and target
 276      */
 277     @Override
 278     public MouseEvent copyFor(Object newSource, EventTarget newTarget) {
 279         MouseEvent e = (MouseEvent) super.copyFor(newSource, newTarget);
 280         e.recomputeCoordinatesToSource(this, newSource);
 281         return e;
 282     }
 283 
 284     /**
 285      * Creates a copy of the given event with the given fields substituted.
 286      * @param newSource the new source of the copied event
 287      * @param newTarget the new target of the copied event
 288      * @param eventType the new eventType
 289      * @return the event copy with the fields substituted
 290      * @since JavaFX 8.0
 291      */
 292     public MouseEvent copyFor(Object newSource, EventTarget newTarget, EventType<? extends MouseEvent> eventType) {
 293         MouseEvent e = copyFor(newSource, newTarget);
 294         e.eventType = eventType;
 295         return e;
 296     }
 297 
 298     /**
 299      * Constructs new MouseEvent event with null source and target.
 300      * @param eventType The type of the event.
 301      * @param x The x with respect to the scene.
 302      * @param y The y with respect to the scene.
 303      * @param screenX The x coordinate relative to screen.
 304      * @param screenY The y coordinate relative to screen.
 305      * @param button the mouse button used
 306      * @param clickCount number of click counts
 307      * @param shiftDown true if shift modifier was pressed.
 308      * @param controlDown true if control modifier was pressed.
 309      * @param altDown true if alt modifier was pressed.
 310      * @param metaDown true if meta modifier was pressed.
 311      * @param primaryButtonDown true if primary button was pressed.
 312      * @param middleButtonDown true if middle button was pressed.
 313      * @param secondaryButtonDown true if secondary button was pressed.
 314      * @param synthesized if this event was synthesized
 315      * @param popupTrigger whether this event denotes a popup trigger for current platform
 316      * @param stillSincePress see {@link #isStillSincePress() }
 317      * @param pickResult pick result. Can be null, in this case a 2D pick result
 318      *                   without any further values is constructed
 319      *                   based on the scene coordinates
 320      * @since JavaFX 8.0
 321      */
 322     public MouseEvent(
 323             @NamedArg("eventType") EventType<? extends MouseEvent> eventType,
 324             @NamedArg("x") double x, @NamedArg("y") double y,
 325             @NamedArg("screenX") double screenX, @NamedArg("screenY") double screenY,
 326             @NamedArg("button") MouseButton button,
 327             @NamedArg("clickCount") int clickCount,
 328             @NamedArg("shiftDown") boolean shiftDown,
 329             @NamedArg("controlDown") boolean controlDown,
 330             @NamedArg("altDown") boolean altDown,
 331             @NamedArg("metaDown") boolean metaDown,
 332             @NamedArg("primaryButtonDown") boolean primaryButtonDown,
 333             @NamedArg("middleButtonDown") boolean middleButtonDown,
 334             @NamedArg("secondaryButtonDown") boolean secondaryButtonDown,
 335             @NamedArg("synthesized") boolean synthesized,
 336             @NamedArg("popupTrigger") boolean popupTrigger,
 337             @NamedArg("stillSincePress") boolean stillSincePress,
 338             @NamedArg("pickResult") PickResult pickResult) {
 339         this(null, null, eventType, x, y, screenX, screenY, button, clickCount,
 340                 shiftDown, controlDown, altDown, metaDown,
 341                 primaryButtonDown, middleButtonDown, secondaryButtonDown,
 342                 synthesized, popupTrigger, stillSincePress, pickResult);
 343     }
 344 
 345     /**
 346      * Constructs new MouseEvent event.
 347      * @param source the source of the event. Can be null.
 348      * @param target the target of the event. Can be null.
 349      * @param eventType The type of the event.
 350      * @param x The x with respect to the source. Should be in scene coordinates if source == null or source is not a Node.
 351      * @param y The y with respect to the source. Should be in scene coordinates if source == null or source is not a Node.
 352      * @param screenX The x coordinate relative to screen.
 353      * @param screenY The y coordinate relative to screen.
 354      * @param button the mouse button used
 355      * @param clickCount number of click counts
 356      * @param shiftDown true if shift modifier was pressed.
 357      * @param controlDown true if control modifier was pressed.
 358      * @param altDown true if alt modifier was pressed.
 359      * @param metaDown true if meta modifier was pressed.
 360      * @param primaryButtonDown true if primary button was pressed.
 361      * @param middleButtonDown true if middle button was pressed.
 362      * @param secondaryButtonDown true if secondary button was pressed.
 363      * @param synthesized if this event was synthesized
 364      * @param popupTrigger whether this event denotes a popup trigger for current platform
 365      * @param stillSincePress see {@link #isStillSincePress() }
 366      * @param pickResult pick result. Can be null, in this case a 2D pick result
 367      *                   without any further values is constructed
 368      *                   based on the scene coordinates and target
 369      * @since JavaFX 8.0
 370      */
 371     public MouseEvent(@NamedArg("source") Object source, @NamedArg("target") EventTarget target,
 372             @NamedArg("eventType") EventType<? extends MouseEvent> eventType,
 373             @NamedArg("x") double x, @NamedArg("y") double y,
 374             @NamedArg("screenX") double screenX, @NamedArg("screenY") double screenY,
 375             @NamedArg("button") MouseButton button,
 376             @NamedArg("clickCount") int clickCount,
 377             @NamedArg("shiftDown") boolean shiftDown,
 378             @NamedArg("controlDown") boolean controlDown,
 379             @NamedArg("altDown") boolean altDown,
 380             @NamedArg("metaDown") boolean metaDown,
 381             @NamedArg("primaryButtonDown") boolean primaryButtonDown,
 382             @NamedArg("middleButtonDown") boolean middleButtonDown,
 383             @NamedArg("secondaryButtonDown") boolean secondaryButtonDown,
 384             @NamedArg("synthesized") boolean synthesized,
 385             @NamedArg("popupTrigger") boolean popupTrigger,
 386             @NamedArg("stillSincePress") boolean stillSincePress,
 387             @NamedArg("pickResult") PickResult pickResult) {
 388         super(source, target, eventType);
 389         this.x = x;
 390         this.y = y;
 391         this.screenX = screenX;
 392         this.screenY = screenY;
 393         this.sceneX = x;
 394         this.sceneY = y;
 395         this.button = button;
 396         this.clickCount = clickCount;
 397         this.shiftDown = shiftDown;
 398         this.controlDown = controlDown;
 399         this.altDown = altDown;
 400         this.metaDown = metaDown;
 401         this.primaryButtonDown = primaryButtonDown;
 402         this.middleButtonDown = middleButtonDown;
 403         this.secondaryButtonDown = secondaryButtonDown;
 404         this.synthesized = synthesized;
 405         this.stillSincePress = stillSincePress;
 406         this.popupTrigger = popupTrigger;
 407         this.pickResult = pickResult;
 408         this.pickResult = pickResult != null ? pickResult : new PickResult(target, x, y);
 409         final Point3D p = InputEventUtils.recomputeCoordinates(this.pickResult, null);
 410         this.x = p.getX();
 411         this.y = p.getY();
 412         this.z = p.getZ();
 413     }
 414 
 415     /**
 416      * Creates a copy of this mouse event of MouseDragEvent type
 417      * @param e the mouse event to copy
 418      * @param source the new source of the copied event
 419      * @param target the new target of the copied event
 420      * @param type the new MouseDragEvent type
 421      * @param gestureSource the new source of the gesture
 422      * @param pickResult pick result. Can be null, in this case a 2D pick result
 423      *                   without any further values is constructed
 424      *                   based on the scene coordinates
 425      * @return new MouseDragEvent that was created from MouseEvent
 426      * @since JavaFX 8.0
 427      */
 428     public static MouseDragEvent copyForMouseDragEvent(
 429             MouseEvent e,
 430             Object source, EventTarget target,
 431             EventType<MouseDragEvent> type,
 432             Object gestureSource, PickResult pickResult) {
 433         MouseDragEvent ev = new MouseDragEvent(source, target,
 434                 type, e.sceneX, e.sceneY, e.screenX, e.screenY,
 435                 e.button, e.clickCount, e.shiftDown, e.controlDown,
 436                 e.altDown, e.metaDown, e.primaryButtonDown, e.middleButtonDown,
 437                 e.secondaryButtonDown, e.synthesized, e.popupTrigger,
 438                 pickResult, gestureSource);
 439         ev.recomputeCoordinatesToSource(e, source);
 440         return ev;
 441     }
 442     private final Flags flags = new Flags();
 443 
 444     /**
 445      * Determines whether this event will be followed by {@code DRAG_DETECTED}
 446      * event. It has effect only with  {@code MOUSE_PRESSED} and
 447      * {@code MOUSE_DRAGGED} events.
 448      *
 449      * @return true if the {@code DRAG_DETECTED} event will follow
 450      */
 451     public boolean isDragDetect() {
 452         return flags.dragDetect;
 453     }
 454 
 455     /**
 456      * Augments drag detection behavior. The value says whether this event
 457      * will be followed by {@code DRAG_DETECTED} event. It has effect only
 458      * with  {@code MOUSE_PRESSED} and  {@code MOUSE_DRAGGED} events.
 459      *
 460      * @param dragDetect Whether {@code DRAG_DETECTED} event will follow
 461      */
 462     public void setDragDetect(boolean dragDetect) {
 463         flags.dragDetect = dragDetect;
 464     }
 465 
 466     /**
 467      * Horizontal x position of the event relative to the
 468      * origin of the MouseEvent's node.
 469      */
 470     private transient double x;
 471 
 472     /**
 473      * Horizontal position of the event relative to the
 474      * origin of the MouseEvent's source.
 475      *
 476      * @return horizontal position of the event relative to the
 477      * origin of the MouseEvent's source.
 478      */
 479     public final double getX() {
 480         return x;
 481     }
 482 
 483     /**
 484      * Vertical y position of the event relative to the
 485      * origin of the MouseEvent's node.
 486      */
 487     private transient double y;
 488 
 489     /**
 490      * Vertical position of the event relative to the
 491      * origin of the MouseEvent's source.
 492      *
 493      * @return vertical position of the event relative to the
 494      * origin of the MouseEvent's source.
 495      */
 496     public final double getY() {
 497         return y;
 498     }
 499 
 500     /**
 501      * Depth z position of the event relative to the
 502      * origin of the MouseEvent's node.
 503      */
 504     private transient double z;
 505 
 506     /**
 507      * Depth position of the event relative to the
 508      * origin of the MouseEvent's source.
 509      *
 510      * @return depth position of the event relative to the
 511      * origin of the MouseEvent's source.
 512      * @since JavaFX 8.0
 513      */
 514     public final double getZ() {
 515         return z;
 516     }
 517 
 518     /**
 519      * Absolute horizontal x position of the event.
 520      */
 521     private final double screenX;
 522 
 523     /**
 524      * Returns absolute horizontal position of the event.
 525      * @return absolute horizontal position of the event
 526      */
 527     public final double getScreenX() {
 528         return screenX;
 529     }
 530 
 531     /**
 532      * Absolute vertical y position of the event.
 533      */
 534     private final double screenY;
 535 
 536     /**
 537      * Returns absolute vertical position of the event.
 538      * @return absolute vertical position of the event
 539      */
 540     public final double getScreenY() {
 541         return screenY;
 542     }
 543 
 544     /**
 545      * Horizontal x position of the event relative to the
 546      * origin of the {@code Scene} that contains the MouseEvent's node.
 547      * If the node is not in a {@code Scene}, then the value is relative to
 548      * the boundsInParent of the root-most parent of the MouseEvent's node.
 549      */
 550     private final double sceneX;
 551 
 552     /**
 553      * Returns horizontal position of the event relative to the
 554      * origin of the {@code Scene} that contains the MouseEvent's source.
 555      * If the node is not in a {@code Scene}, then the value is relative to
 556      * the boundsInParent of the root-most parent of the MouseEvent's node.
 557      * Note that in 3D scene, this represents the flat coordinates after
 558      * applying the projection transformations.
 559      *
 560      * @return horizontal position of the event relative to the
 561      * origin of the {@code Scene} that contains the MouseEvent's source
 562      */
 563     public final double getSceneX() {
 564         return sceneX;
 565     }
 566 
 567     /**
 568      * Vertical y position of the event relative to the
 569      * origin of the {@code Scene} that contains the MouseEvent's node.
 570      * If the node is not in a {@code Scene}, then the value is relative to
 571      * the boundsInParent of the root-most parent of the MouseEvent's node.
 572      */
 573     private final double sceneY;
 574 
 575     /**
 576      * Returns vertical position of the event relative to the
 577      * origin of the {@code Scene} that contains the MouseEvent's source.
 578      * If the node is not in a {@code Scene}, then the value is relative to
 579      * the boundsInParent of the root-most parent of the MouseEvent's node.
 580      * Note that in 3D scene, this represents the flat coordinates after
 581      * applying the projection transformations.
 582      *
 583      * @return vertical position of the event relative to the
 584      * origin of the {@code Scene} that contains the MouseEvent's source
 585      */
 586     public final double getSceneY() {
 587         return sceneY;
 588     }
 589 
 590     /**
 591      * Which, if any, of the mouse buttons is responsible for this event.
 592      */
 593     private final MouseButton button;
 594 
 595     /**
 596      * Which, if any, of the mouse buttons is responsible for this event.
 597      *
 598      * @return mouse button whose state change caused this event
 599      */
 600     public final MouseButton getButton() {
 601         return button;
 602     }
 603 
 604     /**
 605      * Number of mouse clicks associated with this event.
 606      * All MOUSE_MOVED events have the clickCount value equal to 0. The
 607      * value is increased with MOUSE_PRESSED event and stays like
 608      * that for all subsequent events till MOUSE_RELEASED, including the
 609      * afterwards generated MOUSE_CLICKED event. The value is increased
 610      * to numbers higher than one if all the events between two subsequent
 611      * presses happen on a small region and in a small time (according
 612      * to native operating system configuration).
 613      */
 614     private final int clickCount;
 615 
 616     /**
 617      * Returns number of mouse clicks associated with this event.
 618      * All MOUSE_MOVED events have the clickCount value equal to 0. The
 619      * value is increased with MOUSE_PRESSED event and stays like
 620      * that for all subsequent events till MOUSE_RELEASED, including the
 621      * afterwards generated MOUSE_CLICKED event. The value is increased
 622      * to numbers higher than one if all the events between two subsequent
 623      * presses happen on a small region and in a small time (according
 624      * to native operating system configuration).
 625      *
 626      * @return number of mouse clicks associated with this event
 627      */
 628     public final int getClickCount() {
 629         return clickCount;
 630     }
 631 
 632     /**
 633      * Whether the mouse cursor left the hysteresis region since the previous
 634      * press.
 635      */
 636     private final boolean stillSincePress;
 637 
 638     /**
 639      * Indicates whether the mouse cursor stayed in the system-provided
 640      * hysteresis area since last pressed event that occurred before this event.
 641      * <p>
 642      * Click event is generated for a node if mouse was both pressed and
 643      * released over the node, regardless of mouse movements between the press
 644      * and release. If a node wants to react differently on a simple click and
 645      * on a mouse drag, it should use a system-supplied short distance
 646      * threshold to decide between click and drag (users often perform
 647      * inadvertent tiny movements during a click). It can be easily achieved
 648      * by ignoring all drags with this method returning {@code true} and
 649      * ignoring all clicks with this method returning {@code false}.
 650      *
 651      * @return true if there were no significant mouse movements (out of
 652      * system hysteresis area) since the last pressed event that occurred
 653      * before this event.
 654      */
 655     public final boolean isStillSincePress() {
 656         return stillSincePress;
 657     }
 658 
 659     /**
 660      * Whether or not the Shift modifier is down on this event.
 661      */
 662     private final boolean shiftDown;
 663 
 664     /**
 665      * Whether or not the Shift modifier is down on this event.
 666      * @return true if the Shift modifier is down on this event
 667      */
 668     public final boolean isShiftDown() {
 669         return shiftDown;
 670     }
 671 
 672     /**
 673      * Whether or not the Control modifier is down on this event.
 674      */
 675     private final boolean controlDown;
 676 
 677     /**
 678      * Whether or not the Control modifier is down on this event.
 679      * @return true if the Control modifier is down on this event
 680      */
 681     public final boolean isControlDown() {
 682         return controlDown;
 683     }
 684 
 685     /**
 686      * Whether or not the Alt modifier is down on this event.
 687      */
 688     private final boolean altDown;
 689 
 690     /**
 691      * Whether or not the Alt modifier is down on this event.
 692      * @return true if the Alt modifier is down on this event
 693      */
 694     public final boolean isAltDown() {
 695         return altDown;
 696     }
 697 
 698     /**
 699      * Whether or not the Meta modifier is down on this event.
 700      */
 701     private final boolean metaDown;
 702 
 703     /**
 704      * Whether or not the Meta modifier is down on this event.
 705      * @return true if the Meta modifier is down on this event
 706      */
 707     public final boolean isMetaDown() {
 708         return metaDown;
 709     }
 710 
 711     private final boolean synthesized;
 712 
 713     /**
 714      * Indicates whether this event is synthesized from using a touch screen
 715      * instead of usual mouse event source devices like mouse or track pad.
 716      * When a finger is dragged over a touch screen, both scrolling gesture
 717      * and mouse dragging are produced. If it causes a conflict in an
 718      * application, this flag can be used to tell apart the usual mouse dragging
 719      * from the touch screen dragging already handled as scroll events.
 720      * @return true if this event is synthesized from using a touch screen
 721      * @since JavaFX 2.2
 722      */
 723     public boolean isSynthesized() {
 724         return synthesized;
 725     }
 726 
 727     /**
 728      * Returns whether or not the host platform common shortcut modifier is
 729      * down on this event. This common shortcut modifier is a modifier key which
 730      * is used commonly in shortcuts on the host platform. It is for example
 731      * {@code control} on Windows and {@code meta} (command key) on Mac.
 732      *
 733      * @return {@code true} if the shortcut modifier is down, {@code false}
 734      *      otherwise
 735      */
 736     public final boolean isShortcutDown() {
 737         switch (Toolkit.getToolkit().getPlatformShortcutKey()) {
 738             case SHIFT:
 739                 return shiftDown;
 740 
 741             case CONTROL:
 742                 return controlDown;
 743 
 744             case ALT:
 745                 return altDown;
 746 
 747             case META:
 748                 return metaDown;
 749 
 750             default:
 751                 return false;
 752         }
 753     }
 754 
 755     /**
 756      * Whether or not this mouse event is the popup menu
 757      * trigger event for the platform.
 758      * <p><b>Note</b>: Popup menus are triggered differently
 759      * on different systems. Therefore, {@code popupTrigger}
 760      * should be checked in both {@code onMousePressed}
 761      * and {@code mouseReleased} for proper cross-platform functionality.
 762      */
 763     private final boolean popupTrigger;
 764 
 765     /**
 766      * Returns {@code true} if this mouse event is the popup menu
 767      * trigger event for the platform.
 768      * <p><b>Note</b>: Popup menus are triggered differently
 769      * on different systems. Therefore, {@code popupTrigger}
 770      * should be checked in both {@code onMousePressed}
 771      * and {@code mouseReleased} for proper cross-platform functionality.
 772      *
 773      * @return {@code true} if this mouse event is the popup menu
 774      * trigger event for the platform
 775      * @since JavaFX 8.0
 776      */
 777     public final boolean isPopupTrigger() {
 778         return popupTrigger;
 779     }
 780 
 781     /**
 782      * {@code true} if primary button (button 1, usually the left) is currently
 783      * pressed. Note that this is different from the {@link #getButton() button}
 784      * variable in that the {@code button} variable indicates which button press was
 785      * responsible for this event while this variable indicates whether the
 786      * primary button is depressed.
 787      */
 788     private final boolean primaryButtonDown;
 789 
 790     /**
 791      * Returns {@code true} if primary button (button 1, usually the left)
 792      * is currently pressed. Note that this is different from the
 793      * {@code getButton()} method that indicates which button press was
 794      * responsible for this event while this method indicates whether the
 795      * primary button is depressed.
 796      *
 797      * @return {@code true} if primary button (button 1, usually the left)
 798      * is currently pressed
 799      */
 800     public final boolean isPrimaryButtonDown() {
 801         return primaryButtonDown;
 802     }
 803 
 804     /**
 805      * {@code true} if secondary button (button 3, usually the right) is currently
 806      * pressed. Note that this is different from the {@link #getButton() button}
 807      * variable in that the {@code button} variable indicates which button press was
 808      * responsible for this event while this variable indicates whether the
 809      * primary button is depressed.
 810      */
 811     private final boolean secondaryButtonDown;
 812 
 813     /**
 814      * Returns {@code true} if secondary button (button 1, usually the right)
 815      * is currently pressed. Note that this is different from the
 816      * {@code getButton()} method that indicates which button press was
 817      * responsible for this event while this method indicates whether the
 818      * secondary button is depressed.
 819      *
 820      * @return {@code true} if secondary button (button 3, usually the right)
 821      * is currently pressed
 822      */
 823     public final boolean isSecondaryButtonDown() {
 824         return secondaryButtonDown;
 825     }
 826 
 827     /**
 828      * {@code true} if middle button (button 2) is currently pressed.
 829      * Note that this is different from the {@link #getButton() button} variable in
 830      * that the {@code button} variable indicates which button press was
 831      * responsible for this event while this variable indicates whether the
 832      * middle button is depressed.
 833      */
 834     private final boolean middleButtonDown;
 835 
 836     /**
 837      * Returns {@code true} if middle button (button 2)
 838      * is currently pressed. Note that this is different from the
 839      * {@code getButton()} method that indicates which button press was
 840      * responsible for this event while this method indicates whether the
 841      * middle button is depressed.
 842      *
 843      * @return {@code true} if middle button (button 2) is currently pressed
 844      */
 845     public final boolean isMiddleButtonDown() {
 846         return middleButtonDown;
 847     }
 848 
 849     /**
 850      * Returns a string representation of this {@code MouseEvent} object.
 851      * @return a string representation of this {@code MouseEvent} object.
 852      */
 853     @Override public String toString() {
 854         final StringBuilder sb = new StringBuilder("MouseEvent [");
 855 
 856         sb.append("source = ").append(getSource());
 857         sb.append(", target = ").append(getTarget());
 858         sb.append(", eventType = ").append(getEventType());
 859         sb.append(", consumed = ").append(isConsumed());
 860 
 861         sb.append(", x = ").append(getX()).append(", y = ").append(getY())
 862                 .append(", z = ").append(getZ());
 863 
 864         if (getButton() != null) {
 865             sb.append(", button = ").append(getButton());
 866         }
 867         if (getClickCount() > 1) {
 868             sb.append(", clickCount = ").append(getClickCount());
 869         }
 870         if (isPrimaryButtonDown()) {
 871             sb.append(", primaryButtonDown");
 872         }
 873         if (isMiddleButtonDown()) {
 874             sb.append(", middleButtonDown");
 875         }
 876         if (isSecondaryButtonDown()) {
 877             sb.append(", secondaryButtonDown");
 878         }
 879         if (isShiftDown()) {
 880             sb.append(", shiftDown");
 881         }
 882         if (isControlDown()) {
 883             sb.append(", controlDown");
 884         }
 885         if (isAltDown()) {
 886             sb.append(", altDown");
 887         }
 888         if (isMetaDown()) {
 889             sb.append(", metaDown");
 890         }
 891         if (isShortcutDown()) {
 892             sb.append(", shortcutDown");
 893         }
 894         if (isSynthesized()) {
 895             sb.append(", synthesized");
 896         }
 897         sb.append(", pickResult = ").append(getPickResult());
 898 
 899         return sb.append("]").toString();
 900     }
 901 
 902     /**
 903      * Information about the pick if the picked {@code Node} is a
 904      * {@code Shape3D} node and its pickOnBounds is false.
 905      */
 906     private PickResult pickResult;
 907 
 908     /**
 909      * Returns information about the pick.
 910      *
 911      * @return new PickResult object that contains information about the pick
 912      * @since JavaFX 8.0
 913      */
 914     public final PickResult getPickResult() {
 915         return pickResult;
 916     }
 917 
 918     /**
 919      * These properties need to live in a separate object shared among all the
 920      * copied events to make sure that the values are propagated to the
 921      * original event.
 922      */
 923     private static class Flags implements Cloneable {
 924         /**
 925          * Whether dragDetected event is going to be sent after this event.
 926          * Applies only to MOUSE_PRESSED and MOUSE_MOVED event types.
 927          */
 928         boolean dragDetect = true;
 929 
 930         @Override
 931         public Flags clone() {
 932             try {
 933                 return (Flags) super.clone();
 934             } catch (CloneNotSupportedException e) {
 935                 /* won't happen */
 936                 return null;
 937             }
 938         }
 939     }
 940 
 941     private void readObject(java.io.ObjectInputStream in)
 942             throws IOException, ClassNotFoundException {
 943         in.defaultReadObject();
 944         x = sceneX;
 945         y = sceneY;
 946     }
 947 }