package javafx.scene.input;

import com.sun.javafx.geom.Point2D;
import com.sun.javafx.scene.input.InputEventUtils;
import javafx.event.Event;
import javafx.event.EventTarget;
import javafx.event.EventType;

/**
 * When mouse event occurs, the top-most node under cursor is picked and
 * the event is delivered to it through capturing and bubbling phases
 * described at {@link javafx.event.EventDispatcher EventDispatcher}.
 * <p>
 * The mouse (pointer's) location is available relative to several
 * coordinate systems: x,y - relative to the origin of the
 * MouseEvent's node, sceneX,sceneY - relative to to the
 * origin of the {@code Scene} that contains the node,
 * screenX,screenY - relative to origin of the screen that
 * contains the mouse pointer.
 *
 * <h4>Dragging gestures</h4>
 * <p>
 * There are three types of dragging gestures. They are all initiated by
 * a mouse press event and terminated as a result of a mouse released
 * event, the source node decides which gesture will take place.
 * <p>
 * The simple press-drag-release gesture is default. It's best used to allow
 * changing size of a shape, dragging it around and so on. Whole
 * press-drag-release gesture is delivered to one node. When mouse
 * button is pressed, the top-most node is picked and all subsequent
 * mouse events are delivered to the same node until the button is released.
 * If a mouse clicked event is generated from these events, it is still
 * delivered to the same node.
 * <p>
 * During simple press-drag-release gesture, the other nodes are not involved
 * and don't get any events. If these nodes need to be involved in the gesture,
 * full press-drag-release gesture has to be activated. This gesture is
 * best used for connecting nodes by "wires", dragging nodes to other nodes etc.
 * This gesture type is more closely described at
 * {@link javafx.scene.input.MouseDragEvent MouseDragEvent} which contains
 * the events delivered to the gesture targets.
 * <p>
 * The third gesture type is platform-supported drag-and-drop gesture. It serves
 * best to transfer data and works also between (not necessarily FX)
 * applications. This gesture type is more closely described
 * at {@link javafx.scene.input.DragEvent DragEvent}.
 * <p>
 * In a short summary, simple press-drag-release gesture is activated
 * automatically when a mouse button is pressed and delivers all
 * {@code MouseEvent}s to the gesture source. When you start dragging,
 * eventually the {@code DRAG_DETECTED} event arrives. In its handler
 * you can either start full press-drag-release gesture by calling
 * {@code startFullDrag} method on a node or scene - the {@code MouseDragEvent}s
 * start to be delivered to gesture targets, or you can start drag and drop
 * gesture by calling {@code startDragAndDrop} method on a node or scene -
 * the system switches into the drag and drop mode and {@code DragEvent}s start
 * to be delivered instead of {@code MouseEvent}s. If you don't call any of
 * those methods, the simple press-drag-release gesture continues.
 * <p>
 * Note that dragging a finger over touch screen produces mouse dragging events,
 * but also scroll gesture events. If it means a conflict in an application
 * (the physical dragging action is handled by two different handlers), the
 * {@code isSynthesized()} method may be used to detect the problem and make the
 * dragging handlers behave accordingly.
 *
 * <h4>Mouse enter/exit handling</h4>
 * <p>
 * When mouse enters a node, the node gets {@code MOUSE_ENTERED} event, when
 * it leaves, it gets {@code MOUSE_EXITED} event. These events are delivered
 * only to the entered/exited node and seemingly don't go through the
 * capturing/bubbling phases. This is the most common use-case.
 * <p>
 * When the capturing or bubbling is desired, there are
 * {@code MOUSE_ENTERED_TARGET}/{@code MOUSE_EXITED_TARGET} events. These events
 * go through capturing/bubbling phases normally. This means that parent may
 * receive the {@code MOUSE_ENTERED_TARGET} event when mouse entered
 * either the parent itself or some of its children. To distinguish between
 * these two cases event target can be tested on equality with the node.
 * <p>
 * These two types are closely connected:
 * {@code MOUSE_ENTERED}/{@code MOUSE_EXITED} are subtypes
 * of {@code MOUSE_ENTERED_TARGET}/{@code MOUSE_EXITED_TARGET}.
 * During capturing phase,
 * {@code MOUSE_ENTERED_TARGET} is delivered to the
 * parents. When the event is delivered to the event target (the node that
 * has actually been entered), its type is switched to
 * {@code MOUSE_ENTERED}. Then the type is switched back to
 * {@code MOUSE_ENTERED_TARGET} for the bubbling phase.
 * It's still one event just switching types, so if it's filtered or consumed,
 * it affects both event variants. Thanks to the subtype-relationship, a
 * {@code MOUSE_ENTERED_TARGET} event handler will receive the
 * {@code MOUSE_ENTERED} event on target.
 *
 * <h4>Notes</h4>
 * <ul>
 *   <li>For triggering context menus see the {@link ContextMenuEvent}.</li>
 * </ul>
 * @since JavaFX 2.0
 */
public class MouseEvent extends InputEvent {

    //private static final long serialVersionUID = 20121107L;

    /**
     * Common supertype for all mouse event types.
     */
    public static final EventType<MouseEvent> ANY = new EventType<>(InputEvent.ANY, "MOUSE");

    /**
     * This event occurs when mouse button is pressed. This activates a
     * press-drag-release gesture, so all subsequent mouse events until
     * the button is released are delivered to the same node.
     */
    public static final EventType<MouseEvent> MOUSE_PRESSED = new EventType<>(MouseEvent.ANY, "MOUSE_PRESSED");

    /**
     * This event occurs when mouse button is released. It is delivered
     * to the same node where the button has been pressed which activated
     * a press-drag-release gesture.
     */
    public static final EventType<MouseEvent> MOUSE_RELEASED = new EventType<>(MouseEvent.ANY, "MOUSE_RELEASED");

    /**
     * This event occurs when mouse button has been clicked (pressed and
     * released on the same node). This event provides a button-like behavior
     * to any node. Note that even long drags can generate click event (it
     * is delivered to the top-most node on which the mouse was both
     * pressed and released).
     */
    public static final EventType<MouseEvent> MOUSE_CLICKED = new EventType<>(MouseEvent.ANY, "MOUSE_CLICKED");

    /**
     * This event occurs when mouse enters a node. It's the bubbling variant,
     * which is delivered also to all parents of the entered node (unless it
     * was consumed). When notifications about mouse entering some of node's
     * children are not desired, {@code MOUSE_ENTERED} event handler should
     * be used.
     *
     * @see MouseEvent MouseEvent for more information about mouse entered/exited handling
     */
    public static final EventType<MouseEvent> MOUSE_ENTERED_TARGET = new EventType<>(MouseEvent.ANY, "MOUSE_ENTERED_TARGET");

    /**
     * This event occurs when mouse enters a node. This event type is delivered
     * only to the entered node, if parents want to filter it or get the
     * bubbling event, they need to use {@code MOUSE_ENTERED_TARGET}.
     *
     * @see MouseEvent MouseEvent for more information about mouse entered/exited handling
     */
    public static final EventType<MouseEvent> MOUSE_ENTERED = new EventType<>(MouseEvent.MOUSE_ENTERED_TARGET, "MOUSE_ENTERED");

    /**
     * This event occurs when mouse exits a node. It's the bubbling variant,
     * which is delivered also to all parents of the exited node (unless it
     * was consumed). When notifications about mouse exiting some of node's
     * children are not desired, {@code MOUSE_EXITED} event handler should
     * be used.
     *
     * @see MouseEvent MouseEvent for more information about mouse entered/exited handling
     */
    public static final EventType<MouseEvent> MOUSE_EXITED_TARGET = new EventType<>(MouseEvent.ANY, "MOUSE_EXITED_TARGET");

    /**
     * This event occurs when mouse exits a node. This event type is delivered
     * only to the exited node, if parents want to filter it or get the
     * bubbling event, they need to use {@code MOUSE_EXITED_TARGET}.
     *
     * @see MouseEvent MouseEvent for more information about mouse entered/exited handling
     */
    public static final EventType<MouseEvent> MOUSE_EXITED = new EventType<>(MouseEvent.MOUSE_EXITED_TARGET, "MOUSE_EXITED");

    /**
     * This event occurs when mouse moves within a node and no buttons
     * are pressed. If any mouse button is pressed, MOUSE_DRAGGED event
     * occurs instead.
     */
    public static final EventType<MouseEvent> MOUSE_MOVED = new EventType<>(MouseEvent.ANY, "MOUSE_MOVED");

    /**
     * This event occurs when mouse moves with a pressed button.
     * It is delivered to the same node where the button has been pressed
     * which activated a press-drag-release gesture. It is delivered
     * regardless of the mouse being within bounds of the node.
     */
    public static final EventType<MouseEvent> MOUSE_DRAGGED = new EventType<>(MouseEvent.ANY, "MOUSE_DRAGGED");

    /**
     * This event is delivered to a node that is identified as a source of a
     * dragging gesture. Handler of this event is the only place where
     * full press-drag-release gesture or a drag and drop gesture can be
     * started (by calling {@link javafx.scene.Node#startFullDrag startFullDrag()}
     * of {@link javafx.scene.Node#startDragAndDrop startDragAndDrop()} method).
     * If none of them is called, simple press-drag-release gesture will continue.
     * <p>
     * Note that his event is generated based on dragging the mouse over a
     * platform-specific distance threshold. You can modify this behavior
     * by calling {@code setDragDetect} method on any MOUSE_PRESSED or
     * MOUSE_DRAGGED event.
     *
     * @see MouseEvent MouseEvent for more details about simple press-drag-release gestures
     * @see MouseDragEvent MouseDragEvent for more details about full press-drag-release gestures
     * @see DragEvent DragEvent for more details about drag and drop gestures
     */
    public static final EventType<MouseEvent> DRAG_DETECTED = new EventType<>(MouseEvent.ANY, "DRAG_DETECTED");

    /**
     * Fills the given event by this event's coordinates recomputed to the given
     * source object
     * @param oldEvent Event whose coordinates are to be filled
     * @param newSource Source object to compute coordinates for
     */
    void recomputeCoordinatesToSource(MouseEvent oldEvent, Object newSource) {
/*
        Point3D newCoordinates = InputEventUtils.recomputeCoordinates(pickResult, newSource);
        x = newCoordinates.getX();
        y = newCoordinates.getY();
        z = newCoordinates.getZ();
*/
        Point2D newCoordinates = InputEventUtils.recomputeCoordinates(pickResult, newSource);
        x = newCoordinates.x;
        y = newCoordinates.y;

    }

    @Override
    public EventType<? extends MouseEvent> getEventType() {
        return (EventType<? extends MouseEvent>) super.getEventType();
    }

    /**
     * Copies this event for a different source and target.
     * In most cases you don't need to use this method, it's called
     * automatically when you fire the event.
     * @param newSource New event source
     * @param newTarget New event target
     * @return copy of this event for a different source and target
     */
    @Override
    public MouseEvent copyFor(Object newSource, EventTarget newTarget) {
        MouseEvent e = (MouseEvent) super.copyFor(newSource, newTarget);
        e.recomputeCoordinatesToSource(this, newSource);
        return e;
    }

    /**
     * Creates a copy of the given event with the given fields substituted.
     * @param newSource the new source of the copied event
     * @param newTarget the new target of the copied event
     * @param eventType the new eventType
     * @return the event copy with the fields substituted
     * @since JavaFX 8.0
     */
    public MouseEvent copyFor(Object newSource, EventTarget newTarget, EventType<? extends MouseEvent> eventType) {
        MouseEvent e = copyFor(newSource, newTarget);
        e.eventType = eventType;
        return e;
    }

    /**
     * Constructs new MouseEvent event with null source and target.
     * @param eventType The type of the event.
     * @param x The x with respect to the scene.
     * @param y The y with respect to the scene.
     * @param screenX The x coordinate relative to screen.
     * @param screenY The y coordinate relative to screen.
     * @param button the mouse button used
     * @param clickCount number of click counts
     * @param shiftDown true if shift modifier was pressed.
     * @param controlDown true if control modifier was pressed.
     * @param altDown true if alt modifier was pressed.
     * @param metaDown true if meta modifier was pressed.
     * @param primaryButtonDown true if primary button was pressed.
     * @param middleButtonDown true if middle button was pressed.
     * @param secondaryButtonDown true if secondary button was pressed.
     * @param synthesized if this event was synthesized
     * @param popupTrigger whether this event denotes a popup trigger for current platform
     * @param stillSincePress see {@link #isStillSincePress() }
     * @param pickResult pick result. Can be null, in this case a 2D pick result
     *                   without any further values is constructed
     *                   based on the scene coordinates
     * @since JavaFX 8.0
     */
    public MouseEvent(
            EventType<? extends MouseEvent> eventType,
            double x, double y,
            double screenX, double screenY,
            MouseButton button,
            int clickCount,
            boolean shiftDown,
            boolean controlDown,
            boolean altDown,
            boolean metaDown,
            boolean primaryButtonDown,
            boolean middleButtonDown,
            boolean secondaryButtonDown,
            boolean synthesized,
            boolean popupTrigger,
            boolean stillSincePress,
            PickResult pickResult) {
        this(null, null, eventType, x, y, screenX, screenY, button, clickCount,
                shiftDown, controlDown, altDown, metaDown,
                primaryButtonDown, middleButtonDown, secondaryButtonDown,
                synthesized, popupTrigger, stillSincePress, pickResult);
    }

    /**
     * Constructs new MouseEvent event.
     * @param source the source of the event. Can be null.
     * @param target the target of the event. Can be null.
     * @param eventType The type of the event.
     * @param x The x with respect to the source. Should be in scene coordinates if source == null or source is not a Node.
     * @param y The y with respect to the source. Should be in scene coordinates if source == null or source is not a Node.
     * @param screenX The x coordinate relative to screen.
     * @param screenY The y coordinate relative to screen.
     * @param button the mouse button used
     * @param clickCount number of click counts
     * @param shiftDown true if shift modifier was pressed.
     * @param controlDown true if control modifier was pressed.
     * @param altDown true if alt modifier was pressed.
     * @param metaDown true if meta modifier was pressed.
     * @param primaryButtonDown true if primary button was pressed.
     * @param middleButtonDown true if middle button was pressed.
     * @param secondaryButtonDown true if secondary button was pressed.
     * @param synthesized if this event was synthesized
     * @param popupTrigger whether this event denotes a popup trigger for current platform
     * @param stillSincePress see {@link #isStillSincePress() }
     * @param pickResult pick result. Can be null, in this case a 2D pick result
     *                   without any further values is constructed
     *                   based on the scene coordinates and target
     * @since JavaFX 8.0
     */
    public MouseEvent(Object source, EventTarget target,
                      EventType<? extends MouseEvent> eventType,
                      double x, double y,
                      double screenX, double screenY,
                      MouseButton button,
                      int clickCount,
                      boolean shiftDown,
                      boolean controlDown,
                      boolean altDown,
                      boolean metaDown,
                      boolean primaryButtonDown,
                      boolean middleButtonDown,
                      boolean secondaryButtonDown,
                      boolean synthesized,
                      boolean popupTrigger,
                      boolean stillSincePress,
                      PickResult pickResult) {
        super(source, target, eventType);
        this.x = x;
        this.y = y;
        this.screenX = screenX;
        this.screenY = screenY;
        this.sceneX = x;
        this.sceneY = y;
        this.button = button;
        this.clickCount = clickCount;
        this.shiftDown = shiftDown;
        this.controlDown = controlDown;
        this.altDown = altDown;
        this.metaDown = metaDown;
        this.primaryButtonDown = primaryButtonDown;
        this.middleButtonDown = middleButtonDown;
        this.secondaryButtonDown = secondaryButtonDown;
        this.synthesized = synthesized;
        this.stillSincePress = stillSincePress;
        this.popupTrigger = popupTrigger;
        this.pickResult = pickResult != null ? pickResult : new PickResult(target, x, y);
/*
        final Point3D p = InputEventUtils.recomputeCoordinates(this.pickResult, null);
        this.x = p.getX();
        this.y = p.getY();
        this.z = p.getZ();
*/
        final Point2D p = InputEventUtils.recomputeCoordinates(this.pickResult, null);
        this.x = p.x;
        this.y = p.y;
    }

    @Override
    public Event duplicate() {
        return new MouseEvent(source, target, getEventType(), x, y, screenX, screenY, button, clickCount, shiftDown, controlDown, altDown, metaDown, primaryButtonDown, middleButtonDown, secondaryButtonDown, synthesized, popupTrigger, stillSincePress, pickResult);
    }


    /**
     * Creates a copy of this mouse event of MouseDragEvent type
     * @param e the mouse event to copy
     * @param source the new source of the copied event
     * @param target the new target of the copied event
     * @param type the new MouseDragEvent type
     * @param gestureSource the new source of the gesture
     * @param pickResult pick result. Can be null, in this case a 2D pick result
     *                   without any further values is constructed
     *                   based on the scene coordinates
     * @return new MouseDragEvent that was created from MouseEvent
     * @since JavaFX 8.0
     */
    public static MouseDragEvent copyForMouseDragEvent(
            MouseEvent e,
            Object source, EventTarget target,
            EventType<MouseDragEvent> type,
            Object gestureSource, PickResult pickResult) {
        MouseDragEvent ev = new MouseDragEvent(source, target,
                type, e.sceneX, e.sceneY, e.screenX, e.screenY,
                e.button, e.clickCount, e.shiftDown, e.controlDown,
                e.altDown, e.metaDown, e.primaryButtonDown, e.middleButtonDown,
                e.secondaryButtonDown, e.synthesized, e.popupTrigger,
                pickResult, gestureSource);
        ev.recomputeCoordinatesToSource(e, source);
        return ev;
    }
    private final Flags flags = new Flags();

    /**
     * Determines whether this event will be followed by {@code DRAG_DETECTED}
     * event. It has effect only with  {@code MOUSE_PRESSED} and
     * {@code MOUSE_DRAGGED} events.
     *
     * @return true if the {@code DRAG_DETECTED} event will follow
     */
    public boolean isDragDetect() {
        return flags.dragDetect;
    }

    /**
     * Augments drag detection behavior. The value says whether this event
     * will be followed by {@code DRAG_DETECTED} event. It has effect only
     * with  {@code MOUSE_PRESSED} and  {@code MOUSE_DRAGGED} events.
     *
     * @param dragDetect Whether {@code DRAG_DETECTED} event will follow
     */
    public void setDragDetect(boolean dragDetect) {
        flags.dragDetect = dragDetect;
    }

    /**
     * Horizontal x position of the event relative to the
     * origin of the MouseEvent's node.
     */
    private transient double x;

    /**
     * Horizontal position of the event relative to the
     * origin of the MouseEvent's source.
     *
     * @return horizontal position of the event relative to the
     * origin of the MouseEvent's source.
     */
    public final double getX() {
        return x;
    }

    /**
     * Vertical y position of the event relative to the
     * origin of the MouseEvent's node.
     */
    private transient double y;

    /**
     * Vertical position of the event relative to the
     * origin of the MouseEvent's source.
     *
     * @return vertical position of the event relative to the
     * origin of the MouseEvent's source.
     */
    public final double getY() {
        return y;
    }

    /**
     * Depth z position of the event relative to the
     * origin of the MouseEvent's node.
     */
    private transient double z;

    /**
     * Depth position of the event relative to the
     * origin of the MouseEvent's source.
     *
     * @return depth position of the event relative to the
     * origin of the MouseEvent's source.
     * @since JavaFX 8.0
     */
    public final double getZ() {
        return z;
    }

    /**
     * Absolute horizontal x position of the event.
     */
    private final double screenX;

    /**
     * Returns absolute horizontal position of the event.
     * @return absolute horizontal position of the event
     */
    public final double getScreenX() {
        return screenX;
    }

    /**
     * Absolute vertical y position of the event.
     */
    private final double screenY;

    /**
     * Returns absolute vertical position of the event.
     * @return absolute vertical position of the event
     */
    public final double getScreenY() {
        return screenY;
    }

    /**
     * Horizontal x position of the event relative to the
     * origin of the {@code Scene} that contains the MouseEvent's node.
     * If the node is not in a {@code Scene}, then the value is relative to
     * the boundsInParent of the root-most parent of the MouseEvent's node.
     */
    private final double sceneX;

    /**
     * Returns horizontal position of the event relative to the
     * origin of the {@code Scene} that contains the MouseEvent's source.
     * If the node is not in a {@code Scene}, then the value is relative to
     * the boundsInParent of the root-most parent of the MouseEvent's node.
     * Note that in 3D scene, this represents the flat coordinates after
     * applying the projection transformations.
     *
     * @return horizontal position of the event relative to the
     * origin of the {@code Scene} that contains the MouseEvent's source
     */
    public final double getSceneX() {
        return sceneX;
    }

    /**
     * Vertical y position of the event relative to the
     * origin of the {@code Scene} that contains the MouseEvent's node.
     * If the node is not in a {@code Scene}, then the value is relative to
     * the boundsInParent of the root-most parent of the MouseEvent's node.
     */
    private final double sceneY;

    /**
     * Returns vertical position of the event relative to the
     * origin of the {@code Scene} that contains the MouseEvent's source.
     * If the node is not in a {@code Scene}, then the value is relative to
     * the boundsInParent of the root-most parent of the MouseEvent's node.
     * Note that in 3D scene, this represents the flat coordinates after
     * applying the projection transformations.
     *
     * @return vertical position of the event relative to the
     * origin of the {@code Scene} that contains the MouseEvent's source
     */
    public final double getSceneY() {
        return sceneY;
    }

    /**
     * Which, if any, of the mouse buttons is responsible for this event.
     */
    private final MouseButton button;

    /**
     * Which, if any, of the mouse buttons is responsible for this event.
     *
     * @return mouse button whose state change caused this event
     */
    public final MouseButton getButton() {
        return button;
    }

    /**
     * Number of mouse clicks associated with this event.
     * All MOUSE_MOVED events have the clickCount value equal to 0. The
     * value is increased with MOUSE_PRESSED event and stays like
     * that for all subsequent events till MOUSE_RELEASED, including the
     * afterwards generated MOUSE_CLICKED event. The value is increased
     * to numbers higher than one if all the events between two subsequent
     * presses happen on a small region and in a small time (according
     * to native operating system configuration).
     */
    private final int clickCount;

    /**
     * Returns number of mouse clicks associated with this event.
     * All MOUSE_MOVED events have the clickCount value equal to 0. The
     * value is increased with MOUSE_PRESSED event and stays like
     * that for all subsequent events till MOUSE_RELEASED, including the
     * afterwards generated MOUSE_CLICKED event. The value is increased
     * to numbers higher than one if all the events between two subsequent
     * presses happen on a small region and in a small time (according
     * to native operating system configuration).
     *
     * @return number of mouse clicks associated with this event
     */
    public final int getClickCount() {
        return clickCount;
    }

    /**
     * Whether the mouse cursor left the hysteresis region since the previous
     * press.
     */
    private final boolean stillSincePress;

    /**
     * Indicates whether the mouse cursor stayed in the system-provided
     * hysteresis area since last pressed event that occurred before this event.
     * <p>
     * Click event is generated for a node if mouse was both pressed and
     * released over the node, regardless of mouse movements between the press
     * and release. If a node wants to react differently on a simple click and
     * on a mouse drag, it should use a system-supplied short distance
     * threshold to decide between click and drag (users often perform
     * inadvertent tiny movements during a click). It can be easily achieved
     * by ignoring all drags with this method returning {@code true} and
     * ignoring all clicks with this method returning {@code false}.
     *
     * @return true if there were no significant mouse movements (out of
     * system hysteresis area) since the last pressed event that occurred
     * before this event.
     */
    public final boolean isStillSincePress() {
        return stillSincePress;
    }

    /**
     * Whether or not the Shift modifier is down on this event.
     */
    private final boolean shiftDown;

    /**
     * Whether or not the Shift modifier is down on this event.
     * @return true if the Shift modifier is down on this event
     */
    public final boolean isShiftDown() {
        return shiftDown;
    }

    /**
     * Whether or not the Control modifier is down on this event.
     */
    private final boolean controlDown;

    /**
     * Whether or not the Control modifier is down on this event.
     * @return true if the Control modifier is down on this event
     */
    public final boolean isControlDown() {
        return controlDown;
    }

    /**
     * Whether or not the Alt modifier is down on this event.
     */
    private final boolean altDown;

    /**
     * Whether or not the Alt modifier is down on this event.
     * @return true if the Alt modifier is down on this event
     */
    public final boolean isAltDown() {
        return altDown;
    }

    /**
     * Whether or not the Meta modifier is down on this event.
     */
    private final boolean metaDown;

    /**
     * Whether or not the Meta modifier is down on this event.
     * @return true if the Meta modifier is down on this event
     */
    public final boolean isMetaDown() {
        return metaDown;
    }

    private final boolean synthesized;

    /**
     * Indicates whether this event is synthesized from using a touch screen
     * instead of usual mouse event source devices like mouse or track pad.
     * When a finger is dragged over a touch screen, both scrolling gesture
     * and mouse dragging are produced. If it causes a conflict in an
     * application, this flag can be used to tell apart the usual mouse dragging
     * from the touch screen dragging already handled as scroll events.
     * @return true if this event is synthesized from using a touch screen
     * @since JavaFX 2.2
     */
    public boolean isSynthesized() {
        return synthesized;
    }

    /**
     * Returns whether or not the host platform common shortcut modifier is
     * down on this event. This common shortcut modifier is a modifier key which
     * is used commonly in shortcuts on the host platform. It is for example
     * {@code control} on Windows and {@code meta} (command key) on Mac.
     *
     * @return {@code true} if the shortcut modifier is down, {@code false}
     *      otherwise
     */
/*
    public final boolean isShortcutDown() {
        switch (Toolkit.getToolkit().getPlatformShortcutKey()) {
            case SHIFT:
                return shiftDown;

            case CONTROL:
                return controlDown;

            case ALT:
                return altDown;

            case META:
                return metaDown;

            default:
                return false;
        }
    }
*/

    /**
     * Whether or not this mouse event is the popup menu
     * trigger event for the platform.
     * <p><b>Note</b>: Popup menus are triggered differently
     * on different systems. Therefore, {@code popupTrigger}
     * should be checked in both {@code onMousePressed}
     * and {@code mouseReleased} for proper cross-platform functionality.
     */
    private final boolean popupTrigger;

    /**
     * Returns {@code true} if this mouse event is the popup menu
     * trigger event for the platform.
     * <p><b>Note</b>: Popup menus are triggered differently
     * on different systems. Therefore, {@code popupTrigger}
     * should be checked in both {@code onMousePressed}
     * and {@code mouseReleased} for proper cross-platform functionality.
     *
     * @return {@code true} if this mouse event is the popup menu
     * trigger event for the platform
     * @since JavaFX 8.0
     */
    public final boolean isPopupTrigger() {
        return popupTrigger;
    }

    /**
     * {@code true} if primary button (button 1, usually the left) is currently
     * pressed. Note that this is different from the {@link #getButton() button}
     * variable in that the {@code button} variable indicates which button press was
     * responsible for this event while this variable indicates whether the
     * primary button is depressed.
     */
    private final boolean primaryButtonDown;

    /**
     * Returns {@code true} if primary button (button 1, usually the left)
     * is currently pressed. Note that this is different from the
     * {@code getButton()} method that indicates which button press was
     * responsible for this event while this method indicates whether the
     * primary button is depressed.
     *
     * @return {@code true} if primary button (button 1, usually the left)
     * is currently pressed
     */
    public final boolean isPrimaryButtonDown() {
        return primaryButtonDown;
    }

    /**
     * {@code true} if secondary button (button 3, usually the right) is currently
     * pressed. Note that this is different from the {@link #getButton() button}
     * variable in that the {@code button} variable indicates which button press was
     * responsible for this event while this variable indicates whether the
     * primary button is depressed.
     */
    private final boolean secondaryButtonDown;

    /**
     * Returns {@code true} if secondary button (button 1, usually the right)
     * is currently pressed. Note that this is different from the
     * {@code getButton()} method that indicates which button press was
     * responsible for this event while this method indicates whether the
     * secondary button is depressed.
     *
     * @return {@code true} if secondary button (button 3, usually the right)
     * is currently pressed
     */
    public final boolean isSecondaryButtonDown() {
        return secondaryButtonDown;
    }

    /**
     * {@code true} if middle button (button 2) is currently pressed.
     * Note that this is different from the {@link #getButton() button} variable in
     * that the {@code button} variable indicates which button press was
     * responsible for this event while this variable indicates whether the
     * middle button is depressed.
     */
    private final boolean middleButtonDown;

    /**
     * Returns {@code true} if middle button (button 2)
     * is currently pressed. Note that this is different from the
     * {@code getButton()} method that indicates which button press was
     * responsible for this event while this method indicates whether the
     * middle button is depressed.
     *
     * @return {@code true} if middle button (button 2) is currently pressed
     */
    public final boolean isMiddleButtonDown() {
        return middleButtonDown;
    }

    /**
     * Returns a string representation of this {@code MouseEvent} object.
     * @return a string representation of this {@code MouseEvent} object.
     */
    @Override public String toString() {
        StringBuilder sb = new StringBuilder("MouseEvent [");
        sb.append("source = ").append(getSource());
        sb.append(", target = ").append(getTarget());
        sb.append(", eventType = ").append(getEventType());
        sb.append(", consumed = ").append(isConsumed());
        sb.append(", x = ").append(getX()).append(", y = ").append(getY())
                .append(", z = ").append(getZ());
        if (getButton() != null)
            sb.append(", button = ").append(getButton());
        if (getClickCount() > 1)
            sb.append(", clickCount = ").append(getClickCount());
        if (isPrimaryButtonDown())
            sb.append(", primaryButtonDown");
        if (isMiddleButtonDown())
            sb.append(", middleButtonDown");
        if (isSecondaryButtonDown())
            sb.append(", secondaryButtonDown");
        if (isShiftDown())
            sb.append(", shiftDown");
        if (isControlDown())
            sb.append(", controlDown");
        if (isAltDown())
            sb.append(", altDown");
        if (isMetaDown())
            sb.append(", metaDown");
/*
        if (isShortcutDown())
            sb.append(", shortcutDown");
*/
        if (isSynthesized())
            sb.append(", synthesized");
        sb.append(", pickResult = ").append(getPickResult());
        return sb.append("]").toString();
    }

    /**
     * Information about the pick if the picked {@code Node} is a
     * {@code Shape3D} node and its pickOnBounds is false.
     */
    private PickResult pickResult;

    /**
     * Returns information about the pick.
     *
     * @return new PickResult object that contains information about the pick
     * @since JavaFX 8.0
     */
    public final PickResult getPickResult() {
        return pickResult;
    }

    /**
     * These properties need to live in a separate object shared among all the
     * copied events to make sure that the values are propagated to the
     * original event.
     */
    private static class Flags {
        /**
         * Whether dragDetected event is going to be sent after this event.
         * Applies only to MOUSE_PRESSED and MOUSE_MOVED event types.
         */
        boolean dragDetect = true;

        public Flags() {
        }

        public Flags(boolean dragDetect) {
            this.dragDetect = dragDetect;
        }

        Flags duplicate() {
            return new Flags(dragDetect);
        }

        /*
        @Override
        public Flags clone() {
            try {
                return (Flags) super.clone();
            } catch (CloneNotSupportedException e) {
                */
/* won't happen *//*

                return null;
            }
        }
*/
    }

/*
    private void readObject(java.io.ObjectInputStream in)
            throws IOException, ClassNotFoundException {
        in.defaultReadObject();
        x = sceneX;
        y = sceneY;
    }
*/
}
