/*
* Copyright (c) 2013, 2014, 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 com.sun.glass.ui.monocle;
import com.sun.glass.events.MouseEvent;
import com.sun.glass.ui.Application;
import com.sun.glass.ui.Clipboard;
import java.util.BitSet;
Processes mouse input events based on changes to mouse state. Not
thread-safe and can only be used on the JavaFX application thread.
/**
* Processes mouse input events based on changes to mouse state. Not
* thread-safe and can only be used on the JavaFX application thread.
*/
class MouseInput {
private static MouseInput instance = new MouseInput();
private MouseState state = new MouseState();
private IntSet buttons = new IntSet();
Are we currently processing drag and drop? /** Are we currently processing drag and drop? */
private boolean dragInProgress = false;
What button started the drag operation? /** What button started the drag operation? */
private int dragButton = MouseEvent.BUTTON_NONE;
On what View is the drag operation currently over? /** On what View is the drag operation currently over? */
private MonocleView dragView = null;
What drag actions have been performed? /** What drag actions have been performed? */
private BitSet dragActions = new BitSet();
private static final int DRAG_ENTER = 1;
private static final int DRAG_LEAVE = 2;
private static final int DRAG_OVER = 3;
private static final int DRAG_DROP = 4;
static MouseInput getInstance() {
return instance;
}
Retrieves the current state of mouse buttons and of the cursor.
Params: - result – a MouseState to which to copy data on the current mouse
buttons and coordinates.
/** Retrieves the current state of mouse buttons and of the cursor.
*
* @param result a MouseState to which to copy data on the current mouse
* buttons and coordinates.
*/
void getState(MouseState result) {
state.copyTo(result);
}
Sets a new state for mouse buttons and coordinates, generating input
events where appropriate.
Params: - newState – the new state
- synthesized – true if this state change is synthesized from a change
in touch state; false if this state change comes from
an actual relative pointing devices or from the Glass
robot.
/**
* Sets a new state for mouse buttons and coordinates, generating input
* events where appropriate.
*
* @param newState the new state
* @param synthesized true if this state change is synthesized from a change
* in touch state; false if this state change comes from
* an actual relative pointing devices or from the Glass
* robot.
*/
void setState(MouseState newState, boolean synthesized) {
if (MonocleSettings.settings.traceEvents) {
MonocleTrace.traceEvent("Set %s", newState);
}
// Restrict new state coordinates to screen bounds
NativeScreen screen = NativePlatformFactory.getNativePlatform().getScreen();
int x = Math.max(0, Math.min(newState.getX(), screen.getWidth() - 1));
int y = Math.max(0, Math.min(newState.getY(), screen.getHeight() - 1));
newState.setX(x);
newState.setY(y);
// Get the cached window for the old state and compute the window for
// the new state
MonocleWindow oldWindow = state.getWindow(false);
boolean recalculateWindow = state.getButtonsPressed().isEmpty();
MonocleWindow window = newState.getWindow(recalculateWindow);
MonocleView view = (window == null) ? null : (MonocleView) window.getView();
// send exit event
if (oldWindow != window && oldWindow != null) {
if (!oldWindow.isEnabled()) {
//send focus disabled event
RunnableProcessor.runLater(() -> {
MonocleWindowManager.getInstance().notifyFocusDisabled(oldWindow);
});
} else {
MonocleView oldView = (MonocleView) oldWindow.getView();
if (oldView != null) {
// send exit event
int modifiers = state.getModifiers(); // TODO: include key modifiers
int button = state.getButton();
boolean isPopupTrigger = false; // TODO
int oldX = state.getX();
int oldY = state.getY();
int oldRelX = oldX - oldWindow.getX();
int oldRelY = oldY - oldWindow.getY();
try {
postMouseEvent(oldView, MouseEvent.EXIT, button,
oldRelX, oldRelY, oldX, oldY,
modifiers, isPopupTrigger, synthesized);
} catch (RuntimeException e) {
Application.reportException(e);
}
}
}
}
boolean newAbsoluteLocation = state.getX() != x || state.getY() != y;
if (newAbsoluteLocation) {
NativePlatformFactory.getNativePlatform()
.getCursor().setLocation(x, y);
}
if (view == null) {
newState.copyTo(state);
return;
}
if (window != null && !window.isEnabled()) {
newState.copyTo(state);
//send focus disabled event
RunnableProcessor.runLater(() -> {
MonocleWindowManager.getInstance().notifyFocusDisabled(window);
});
return;
}
int relX = x - window.getX();
int relY = y - window.getY();
// send enter event
if (oldWindow != window && view != null) {
int modifiers = state.getModifiers(); // TODO: include key modifiers
int button = state.getButton();
boolean isPopupTrigger = false; // TODO
postMouseEvent(view, MouseEvent.ENTER, button,
relX, relY, x, y,
modifiers, isPopupTrigger, synthesized);
}
// send motion events
if (oldWindow != window | newAbsoluteLocation) {
boolean isDrag = !state.getButtonsPressed().isEmpty();
int eventType = isDrag ? MouseEvent.DRAG : MouseEvent.MOVE;
int modifiers = state.getModifiers(); // TODO: include key modifiers
int button = state.getButton();
boolean isPopupTrigger = false; // TODO
postMouseEvent(view, eventType, button,
relX, relY, x, y,
modifiers, isPopupTrigger, synthesized);
}
// send press events
newState.getButtonsPressed().difference(buttons, state.getButtonsPressed());
if (!buttons.isEmpty()) {
MouseState pressState = new MouseState();
state.copyTo(pressState);
for (int i = 0; i < buttons.size(); i++) {
int button = buttons.get(i);
pressState.pressButton(button);
// send press event
boolean isPopupTrigger = false; // TODO
postMouseEvent(view, MouseEvent.DOWN, button,
relX, relY, x, y,
pressState.getModifiers(), isPopupTrigger,
synthesized);
}
}
buttons.clear();
// send release events
state.getButtonsPressed().difference(buttons,
newState.getButtonsPressed());
if (!buttons.isEmpty()) {
MouseState releaseState = new MouseState();
state.copyTo(releaseState);
for (int i = 0; i < buttons.size(); i++) {
int button = buttons.get(i);
releaseState.releaseButton(button);
// send release event
boolean isPopupTrigger = false; // TODO
postMouseEvent(view, MouseEvent.UP, button,
relX, relY, x, y,
releaseState.getModifiers(), isPopupTrigger,
synthesized);
}
}
buttons.clear();
// send scroll events
if (newState.getWheel() != state.getWheel()) {
double dY;
switch (newState.getWheel()) {
case MouseState.WHEEL_DOWN: dY = -1.0; break;
case MouseState.WHEEL_UP: dY = 1.0; break;
default: dY = 0.0; break;
}
if (dY != 0.0) {
int modifiers = newState.getModifiers();
RunnableProcessor.runLater(() -> {
view.notifyScroll(relX, relY, x, y, 0.0, dY,
modifiers, 1, 0, 0, 0, 1.0, 1.0);
});
}
newState.setWheel(MouseState.WHEEL_NONE);
}
newState.copyTo(state);
}
private void postMouseEvent(MonocleView view, int eventType, int button,
int relX, int relY, int x, int y,
int modifiers, boolean isPopupTrigger, boolean synthesized) {
RunnableProcessor.runLater(() -> {
notifyMouse(view, eventType, button,
relX, relY, x, y,
modifiers, isPopupTrigger, synthesized);
});
}
private void notifyMouse(MonocleView view, int eventType, int button,
int relX, int relY, int x, int y,
int modifiers, boolean isPopupTrigger, boolean synthesized) {
switch (eventType) {
case MouseEvent.DOWN: {
if (dragButton == MouseEvent.BUTTON_NONE) {
dragButton = button;
}
break;
}
case MouseEvent.UP: {
if (dragButton == button) {
dragButton = MouseEvent.BUTTON_NONE;
if (dragInProgress) {
try {
view.notifyDragDrop(relX, relY, x, y,
Clipboard.ACTION_MOVE);
} catch (RuntimeException e) {
Application.reportException(e);
}
try {
view.notifyDragEnd(Clipboard.ACTION_MOVE);
} catch (RuntimeException e) {
Application.reportException(e);
}
((MonocleApplication) Application.GetApplication()).leaveDndEventLoop();
dragActions.clear();
dragView = null;
dragInProgress = false;
}
}
break;
}
case MouseEvent.DRAG: {
if (dragButton != MouseEvent.BUTTON_NONE) {
if (dragInProgress) {
if (dragView == view && dragActions.isEmpty()) {
// first drag notification
try {
view.notifyDragEnter(relX, relY, x, y,
Clipboard.ACTION_MOVE);
} catch (RuntimeException e) {
Application.reportException(e);
}
dragActions.set(DRAG_ENTER);
} else if (dragView == view && dragActions.get(DRAG_ENTER)) {
try {
view.notifyDragOver(relX, relY, x, y,
Clipboard.ACTION_MOVE);
} catch (RuntimeException e) {
Application.reportException(e);
}
dragActions.set(DRAG_OVER);
} else if (dragView != view) {
if (dragView != null) {
try {
dragView.notifyDragLeave();
} catch (RuntimeException e) {
Application.reportException(e);
}
}
try {
view.notifyDragEnter(relX, relY, x, y,
Clipboard.ACTION_MOVE);
} catch (RuntimeException e) {
Application.reportException(e);
}
dragActions.clear();
dragActions.set(DRAG_ENTER);
dragView = view;
}
return; // consume event
} else {
if (dragView == null) {
dragView = view;
}
}
}
break;
}
}
view.notifyMouse(eventType, button,
relX, relY, x, y,
modifiers, isPopupTrigger,
synthesized);
}
void notifyDragStart() {
dragInProgress = true;
}
}