/* * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package javafx.scene.input; import com.sun.javafx.tk.Toolkit; import javafx.beans.NamedArg; import javafx.event.Event; import javafx.event.EventTarget; import javafx.event.EventType; import javafx.geometry.Point3D; import javafx.scene.Node; import com.sun.javafx.scene.input.InputEventUtils; import java.io.IOException; // PENDING_DOC_REVIEW /** * 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}. *
* 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. * *
* 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. *
* 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. *
* 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. *
* 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}. *
* 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. *
* 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. * *
* 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. *
* 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. *
* 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. * *
* 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
* 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.
* Note: 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.
* Note: 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() {
final 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 implements Cloneable {
/**
* Whether dragDetected event is going to be sent after this event.
* Applies only to MOUSE_PRESSED and MOUSE_MOVED event types.
*/
boolean dragDetect = true;
@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;
}
}