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