diff --git a/.idea/misc.xml b/.idea/misc.xml deleted file mode 100644 index ac8fa666b5..0000000000 --- a/.idea/misc.xml +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - - - - diff --git a/modules/javafx.controls/src/test/java/test/com/sun/javafx/scene/control/infrastructure/MouseEventFirer.java b/modules/javafx.controls/src/test/java/test/com/sun/javafx/scene/control/infrastructure/MouseEventFirer.java index 8c1f1b78db..da98c4a486 100644 --- a/modules/javafx.controls/src/test/java/test/com/sun/javafx/scene/control/infrastructure/MouseEventFirer.java +++ b/modules/javafx.controls/src/test/java/test/com/sun/javafx/scene/control/infrastructure/MouseEventFirer.java @@ -194,6 +194,8 @@ private void fireMouseEvent(EventType evtType, MouseButton button, i button == MouseButton.PRIMARY, // primary button button == MouseButton.MIDDLE, // middle button button == MouseButton.SECONDARY, // secondary button + button == MouseButton.BACK, // back button + button == MouseButton.FORWARD, // forward button false, // synthesized button == MouseButton.SECONDARY, // is popup trigger true, // still since pick diff --git a/modules/javafx.graphics/src/main/java/com/sun/glass/events/KeyEvent.java b/modules/javafx.graphics/src/main/java/com/sun/glass/events/KeyEvent.java index f41111fa34..bf979e8a94 100644 --- a/modules/javafx.graphics/src/main/java/com/sun/glass/events/KeyEvent.java +++ b/modules/javafx.graphics/src/main/java/com/sun/glass/events/KeyEvent.java @@ -65,6 +65,8 @@ public class KeyEvent { @Native public final static int MODIFIER_BUTTON_PRIMARY = 1 << 5; @Native public final static int MODIFIER_BUTTON_SECONDARY = 1 << 6; @Native public final static int MODIFIER_BUTTON_MIDDLE = 1 << 7; + @Native public final static int MODIFIER_BUTTON_BACK = 1 << 8; + @Native public final static int MODIFIER_BUTTON_FORWARD = 1 << 9; /* * Key event key codes. diff --git a/modules/javafx.graphics/src/main/java/com/sun/glass/events/MouseEvent.java b/modules/javafx.graphics/src/main/java/com/sun/glass/events/MouseEvent.java index 4409573071..26cf121443 100644 --- a/modules/javafx.graphics/src/main/java/com/sun/glass/events/MouseEvent.java +++ b/modules/javafx.graphics/src/main/java/com/sun/glass/events/MouseEvent.java @@ -31,6 +31,8 @@ public class MouseEvent { @Native final static public int BUTTON_LEFT = 212; @Native final static public int BUTTON_RIGHT = 213; @Native final static public int BUTTON_OTHER = 214; + @Native final static public int BUTTON_BACK = 215; + @Native final static public int BUTTON_FORWARD = 216; @Native final static public int DOWN = 221; @Native final static public int UP = 222; diff --git a/modules/javafx.graphics/src/main/java/com/sun/glass/ui/GlassRobot.java b/modules/javafx.graphics/src/main/java/com/sun/glass/ui/GlassRobot.java index 3170e4e46d..d2a06d7e8a 100644 --- a/modules/javafx.graphics/src/main/java/com/sun/glass/ui/GlassRobot.java +++ b/modules/javafx.graphics/src/main/java/com/sun/glass/ui/GlassRobot.java @@ -44,6 +44,8 @@ public abstract class GlassRobot { @Native public static final int MOUSE_LEFT_BTN = 1 << 0; @Native public static final int MOUSE_RIGHT_BTN = 1 << 1; @Native public static final int MOUSE_MIDDLE_BTN = 1 << 2; + @Native public static final int MOUSE_BACK_BTN = 1 << 3; + @Native public static final int MOUSE_FORWARD_BTN = 1 << 4; /** * Initializes any state necessary for this {@code Robot}. Called by @@ -247,6 +249,8 @@ public static int convertToRobotMouseButton(MouseButton[] buttons) { case PRIMARY: ret |= MOUSE_LEFT_BTN; break; case SECONDARY: ret |= MOUSE_RIGHT_BTN; break; case MIDDLE: ret |= MOUSE_MIDDLE_BTN; break; + case BACK: ret |= MOUSE_BACK_BTN; break; + case FORWARD: ret |= MOUSE_FORWARD_BTN; break; default: throw new IllegalArgumentException("MouseButton: " + button + " not supported by Robot"); } } diff --git a/modules/javafx.graphics/src/main/java/com/sun/glass/ui/monocle/LinuxMouseProcessor.java b/modules/javafx.graphics/src/main/java/com/sun/glass/ui/monocle/LinuxMouseProcessor.java index b88dc7a0df..3446e9fd53 100644 --- a/modules/javafx.graphics/src/main/java/com/sun/glass/ui/monocle/LinuxMouseProcessor.java +++ b/modules/javafx.graphics/src/main/java/com/sun/glass/ui/monocle/LinuxMouseProcessor.java @@ -127,6 +127,10 @@ private static int mouseButtonForKeyCode(int keyCode) { return MouseEvent.BUTTON_OTHER; case LinuxInput.BTN_RIGHT: return MouseEvent.BUTTON_RIGHT; + case LinuxInput.BTN_BACK: + return MouseEvent.BUTTON_BACK; + case LinuxInput.BTN_FORWARD: + return MouseEvent.BUTTON_FORWARD; default: return -1; } diff --git a/modules/javafx.graphics/src/main/java/com/sun/glass/ui/monocle/MonocleRobot.java b/modules/javafx.graphics/src/main/java/com/sun/glass/ui/monocle/MonocleRobot.java index 4c17f1c56e..b055e4c8f3 100644 --- a/modules/javafx.graphics/src/main/java/com/sun/glass/ui/monocle/MonocleRobot.java +++ b/modules/javafx.graphics/src/main/java/com/sun/glass/ui/monocle/MonocleRobot.java @@ -100,6 +100,20 @@ private static MouseState convertToMouseState(boolean press, MouseState state, M state.releaseButton(MouseEvent.BUTTON_OTHER); } break; + case BACK: + if (press) { + state.pressButton(MouseEvent.BUTTON_BACK); + } else { + state.releaseButton(MouseEvent.BUTTON_BACK); + } + break; + case FORWARD: + if (press) { + state.pressButton(MouseEvent.BUTTON_FORWARD); + } else { + state.releaseButton(MouseEvent.BUTTON_FORWARD); + } + break; default: throw new IllegalArgumentException("MouseButton: " + button + " not supported by Monocle Robot"); } diff --git a/modules/javafx.graphics/src/main/java/com/sun/glass/ui/monocle/MouseState.java b/modules/javafx.graphics/src/main/java/com/sun/glass/ui/monocle/MouseState.java index 2358277faf..3c21a601b0 100644 --- a/modules/javafx.graphics/src/main/java/com/sun/glass/ui/monocle/MouseState.java +++ b/modules/javafx.graphics/src/main/java/com/sun/glass/ui/monocle/MouseState.java @@ -115,6 +115,12 @@ int getModifiers() { case MouseEvent.BUTTON_RIGHT: modifiers |= KeyEvent.MODIFIER_BUTTON_SECONDARY; break; + case MouseEvent.BUTTON_BACK: + modifiers |= KeyEvent.MODIFIER_BUTTON_BACK; + break; + case MouseEvent.BUTTON_FORWARD: + modifiers |= KeyEvent.MODIFIER_BUTTON_FORWARD; + break; } } return modifiers; diff --git a/modules/javafx.graphics/src/main/java/com/sun/glass/ui/monocle/VNCScreen.java b/modules/javafx.graphics/src/main/java/com/sun/glass/ui/monocle/VNCScreen.java index b4ff6769b8..10335a7ee8 100644 --- a/modules/javafx.graphics/src/main/java/com/sun/glass/ui/monocle/VNCScreen.java +++ b/modules/javafx.graphics/src/main/java/com/sun/glass/ui/monocle/VNCScreen.java @@ -275,6 +275,7 @@ public void run() { final MouseState state = new MouseState(); state.setX(x); state.setY(y); + // These values are from RFC 6143 Section 7.5.5. if (buttons.get(0)) { state.pressButton(MouseEvent.BUTTON_LEFT); } @@ -284,6 +285,20 @@ public void run() { if (buttons.get(2)) { state.pressButton(MouseEvent.BUTTON_RIGHT); } + if (buttons.get(3)) { + state.setWheel(MouseState.WHEEL_UP); + } + if (buttons.get(4)) { + state.setWheel(MouseState.WHEEL_DOWN); + } + // TODO: Buttons from here on are not officially mentioned in the docs, can someone confirm + // on a real device? + if (buttons.get(5)) { + state.pressButton(MouseEvent.BUTTON_BACK); + } + if (buttons.get(6)) { + state.pressButton(MouseEvent.BUTTON_FORWARD); + } Platform.runLater(() -> MouseInput.getInstance().setState(state, false)); break; } diff --git a/modules/javafx.graphics/src/main/java/com/sun/glass/ui/monocle/X.java b/modules/javafx.graphics/src/main/java/com/sun/glass/ui/monocle/X.java index f291b1ba36..06057b5951 100644 --- a/modules/javafx.graphics/src/main/java/com/sun/glass/ui/monocle/X.java +++ b/modules/javafx.graphics/src/main/java/com/sun/glass/ui/monocle/X.java @@ -85,6 +85,10 @@ private static void checkPermissions() { static final int Button3 = 3; static final int Button4 = 4; static final int Button5 = 5; + // 4th button (aka browser backward button). + static final int Button8 = 8; + // 5th button (aka browser forward button). + static final int Button9 = 9; static final long _NET_WM_STATE_REMOVE = 0; static final long _NET_WM_STATE_ADD = 1; diff --git a/modules/javafx.graphics/src/main/java/com/sun/glass/ui/monocle/X11InputDeviceRegistry.java b/modules/javafx.graphics/src/main/java/com/sun/glass/ui/monocle/X11InputDeviceRegistry.java index 0ff89e6ad8..a456f1a34b 100644 --- a/modules/javafx.graphics/src/main/java/com/sun/glass/ui/monocle/X11InputDeviceRegistry.java +++ b/modules/javafx.graphics/src/main/java/com/sun/glass/ui/monocle/X11InputDeviceRegistry.java @@ -178,6 +178,8 @@ private static int buttonToGlassButton(int button) { case X.Button1: return MouseEvent.BUTTON_LEFT; case X.Button2: return MouseEvent.BUTTON_OTHER; case X.Button3: return MouseEvent.BUTTON_RIGHT; + case X.Button8: return MouseEvent.BUTTON_BACK; + case X.Button9: return MouseEvent.BUTTON_FORWARD; default: return MouseEvent.BUTTON_NONE; } } diff --git a/modules/javafx.graphics/src/main/java/com/sun/javafx/embed/AbstractEvents.java b/modules/javafx.graphics/src/main/java/com/sun/javafx/embed/AbstractEvents.java index 448968e112..54e05b6289 100644 --- a/modules/javafx.graphics/src/main/java/com/sun/javafx/embed/AbstractEvents.java +++ b/modules/javafx.graphics/src/main/java/com/sun/javafx/embed/AbstractEvents.java @@ -57,6 +57,8 @@ public class AbstractEvents { public final static int MOUSEEVENT_PRIMARY_BUTTON = 1; public final static int MOUSEEVENT_SECONDARY_BUTTON = 2; public final static int MOUSEEVENT_MIDDLE_BUTTON = 4; + public final static int MOUSEEVENT_BACK_BUTTON = 8; + public final static int MOUSEEVENT_FORWARD_BUTTON = 16; public final static int KEYEVENT_PRESSED = 0; public final static int KEYEVENT_RELEASED = 1; @@ -118,6 +120,10 @@ public static MouseButton mouseButtonToFXMouseButton(int embedButton) { return MouseButton.SECONDARY; case MOUSEEVENT_MIDDLE_BUTTON: return MouseButton.MIDDLE; + case MOUSEEVENT_BACK_BUTTON: + return MouseButton.BACK; + case MOUSEEVENT_FORWARD_BUTTON: + return MouseButton.FORWARD; } // Should never reach here return MouseButton.NONE; diff --git a/modules/javafx.graphics/src/main/java/com/sun/javafx/embed/EmbeddedSceneInterface.java b/modules/javafx.graphics/src/main/java/com/sun/javafx/embed/EmbeddedSceneInterface.java index fb936109de..9833403ef2 100644 --- a/modules/javafx.graphics/src/main/java/com/sun/javafx/embed/EmbeddedSceneInterface.java +++ b/modules/javafx.graphics/src/main/java/com/sun/javafx/embed/EmbeddedSceneInterface.java @@ -69,6 +69,7 @@ public interface EmbeddedSceneInterface { */ public void mouseEvent(int type, int button, boolean primaryBtnDown, boolean middleBtnDown, boolean secondaryBtnDown, + boolean backBtnDown, boolean forwardBtnDown, int x, int y, int xAbs, int yAbs, boolean shift, boolean ctrl, boolean alt, boolean meta, boolean popupTrigger); diff --git a/modules/javafx.graphics/src/main/java/com/sun/javafx/tk/TKSceneListener.java b/modules/javafx.graphics/src/main/java/com/sun/javafx/tk/TKSceneListener.java index da9692a641..d68207285e 100644 --- a/modules/javafx.graphics/src/main/java/com/sun/javafx/tk/TKSceneListener.java +++ b/modules/javafx.graphics/src/main/java/com/sun/javafx/tk/TKSceneListener.java @@ -58,7 +58,8 @@ public interface TKSceneListener { public void mouseEvent(EventType type, double x, double y, double screenX, double screenY, MouseButton button, boolean popupTrigger, boolean synthesized, boolean shiftDown, boolean controlDown, boolean altDown, boolean metaDown, - boolean primaryDown, boolean middleDown, boolean secondaryDown); + boolean primaryDown, boolean middleDown, boolean secondaryDown, + boolean backDown, boolean forwardDown); /** * Pass a key event to the scene to handle diff --git a/modules/javafx.graphics/src/main/java/com/sun/javafx/tk/quantum/EmbeddedScene.java b/modules/javafx.graphics/src/main/java/com/sun/javafx/tk/quantum/EmbeddedScene.java index 4f451fa4e0..b00a3900e1 100644 --- a/modules/javafx.graphics/src/main/java/com/sun/javafx/tk/quantum/EmbeddedScene.java +++ b/modules/javafx.graphics/src/main/java/com/sun/javafx/tk/quantum/EmbeddedScene.java @@ -271,6 +271,7 @@ protected Color getClearColor() { @Override public void mouseEvent(final int type, final int button, final boolean primaryBtnDown, final boolean middleBtnDown, final boolean secondaryBtnDown, + final boolean backBtnDown, final boolean forwardBtnDown, final int x, final int y, final int xAbs, final int yAbs, final boolean shift, final boolean ctrl, final boolean alt, final boolean meta, final boolean popupTrigger) @@ -287,7 +288,9 @@ public void mouseEvent(final int type, final int button, AbstractEvents.mouseButtonToFXMouseButton(button), popupTrigger, false, // do we know if it's synthesized? RT-20142 shift, ctrl, alt, meta, - primaryBtnDown, middleBtnDown, secondaryBtnDown); + primaryBtnDown, middleBtnDown, secondaryBtnDown, + backBtnDown, forwardBtnDown + ); return null; }, getAccessControlContext()); }); diff --git a/modules/javafx.graphics/src/main/java/com/sun/javafx/tk/quantum/GlassEventUtils.java b/modules/javafx.graphics/src/main/java/com/sun/javafx/tk/quantum/GlassEventUtils.java index a528876b71..d95abdf518 100644 --- a/modules/javafx.graphics/src/main/java/com/sun/javafx/tk/quantum/GlassEventUtils.java +++ b/modules/javafx.graphics/src/main/java/com/sun/javafx/tk/quantum/GlassEventUtils.java @@ -45,6 +45,10 @@ public static String getMouseEventString(int type) { return "BUTTON_RIGHT"; case MouseEvent.BUTTON_OTHER: return "BUTTON_OTHER"; + case MouseEvent.BUTTON_BACK: + return "BUTTON_BACK"; + case MouseEvent.BUTTON_FORWARD: + return "BUTTON_FORWARD"; case MouseEvent.DOWN: return "DOWN"; case MouseEvent.UP: diff --git a/modules/javafx.graphics/src/main/java/com/sun/javafx/tk/quantum/GlassViewEventHandler.java b/modules/javafx.graphics/src/main/java/com/sun/javafx/tk/quantum/GlassViewEventHandler.java index 8ef2a91ee3..3771aaab88 100644 --- a/modules/javafx.graphics/src/main/java/com/sun/javafx/tk/quantum/GlassViewEventHandler.java +++ b/modules/javafx.graphics/src/main/java/com/sun/javafx/tk/quantum/GlassViewEventHandler.java @@ -283,6 +283,10 @@ private static MouseButton mouseEventButton(int glassButton) { return MouseButton.SECONDARY; case com.sun.glass.events.MouseEvent.BUTTON_OTHER: return MouseButton.MIDDLE; + case com.sun.glass.events.MouseEvent.BUTTON_BACK: + return MouseButton.BACK; + case com.sun.glass.events.MouseEvent.BUTTON_FORWARD: + return MouseButton.FORWARD; default: return MouseButton.NONE; } @@ -319,6 +323,12 @@ public Void run() { case MouseEvent.BUTTON_RIGHT: buttonMask = KeyEvent.MODIFIER_BUTTON_SECONDARY; break; + case MouseEvent.BUTTON_BACK: + buttonMask = KeyEvent.MODIFIER_BUTTON_BACK; + break; + case MouseEvent.BUTTON_FORWARD: + buttonMask = KeyEvent.MODIFIER_BUTTON_FORWARD; + break; default: buttonMask = 0; break; @@ -375,6 +385,8 @@ public Void run() { boolean primaryButtonDown = (modifiers & KeyEvent.MODIFIER_BUTTON_PRIMARY) != 0; boolean middleButtonDown = (modifiers & KeyEvent.MODIFIER_BUTTON_MIDDLE) != 0; boolean secondaryButtonDown = (modifiers & KeyEvent.MODIFIER_BUTTON_SECONDARY) != 0; + boolean backButtonDown = (modifiers & KeyEvent.MODIFIER_BUTTON_BACK) != 0; + boolean forwardButtonDown = (modifiers & KeyEvent.MODIFIER_BUTTON_FORWARD) != 0; final Window w = view.getWindow(); double pScaleX, pScaleY, spx, spy, sx, sy; if (w != null) { @@ -399,7 +411,8 @@ public Void run() { sx + (xAbs - spx) / pScaleX, sy + (yAbs - spy) / pScaleY, mouseEventButton(button), isPopupTrigger, isSynthesized, shiftDown, controlDown, altDown, metaDown, - primaryButtonDown, middleButtonDown, secondaryButtonDown); + primaryButtonDown, middleButtonDown, secondaryButtonDown, + backButtonDown, forwardButtonDown); } } finally { if (stage != null) { diff --git a/modules/javafx.graphics/src/main/java/javafx/scene/Scene.java b/modules/javafx.graphics/src/main/java/javafx/scene/Scene.java index 231af2781f..f152650748 100644 --- a/modules/javafx.graphics/src/main/java/javafx/scene/Scene.java +++ b/modules/javafx.graphics/src/main/java/javafx/scene/Scene.java @@ -2579,12 +2579,14 @@ public void changedSize(float w, float h) { public void mouseEvent(EventType type, double x, double y, double screenX, double screenY, MouseButton button, boolean popupTrigger, boolean synthesized, boolean shiftDown, boolean controlDown, boolean altDown, boolean metaDown, - boolean primaryDown, boolean middleDown, boolean secondaryDown) + boolean primaryDown, boolean middleDown, boolean secondaryDown, + boolean backDown, boolean forwardDown) { MouseEvent mouseEvent = new MouseEvent(type, x, y, screenX, screenY, button, 0, // click count will be adjusted by clickGenerator later anyway shiftDown, controlDown, altDown, metaDown, - primaryDown, middleDown, secondaryDown, synthesized, popupTrigger, false, null); + primaryDown, middleDown, secondaryDown, backDown, forwardDown, + synthesized, popupTrigger, false, null); processMouseEvent(mouseEvent); } @@ -3518,7 +3520,8 @@ private MouseEvent preProcess(MouseEvent e) { if (! e.isPrimaryButtonDown()) { counters.get(MouseButton.PRIMARY).clear(); } if (! e.isSecondaryButtonDown()) { counters.get(MouseButton.SECONDARY).clear(); } if (! e.isMiddleButtonDown()) { counters.get(MouseButton.MIDDLE).clear(); } - + if (! e.isBackButtonDown()) { counters.get(MouseButton.BACK).clear(); } + if (! e.isForwardButtonDown()) { counters.get(MouseButton.FORWARD).clear(); } cc.applyOut(); cc.inc(); cc.start(e.getSceneX(), e.getSceneY()); @@ -3530,6 +3533,7 @@ private MouseEvent preProcess(MouseEvent e) { cc != null && e.getEventType() != MouseEvent.MOUSE_MOVED ? cc.get() : 0, e.isShiftDown(), e.isControlDown(), e.isAltDown(), e.isMetaDown(), e.isPrimaryButtonDown(), e.isMiddleButtonDown(), e.isSecondaryButtonDown(), + e.isBackButtonDown(), e.isForwardButtonDown(), e.isSynthesized(), e.isPopupTrigger(), still, e.getPickResult()); } @@ -3560,6 +3564,7 @@ private void postProcess(MouseEvent e, TargetWrapper target, TargetWrapper picke cc.get(), e.isShiftDown(), e.isControlDown(), e.isAltDown(), e.isMetaDown(), e.isPrimaryButtonDown(), e.isMiddleButtonDown(), e.isSecondaryButtonDown(), + e.isBackButtonDown(), e.isForwardButtonDown(), e.isSynthesized(), e.isPopupTrigger(), lastPress.isStill(), e.getPickResult()); Event.fireEvent(clickedTarget, click); } @@ -3588,6 +3593,8 @@ class MouseHandler { private boolean primaryButtonDown = false; private boolean secondaryButtonDown = false; private boolean middleButtonDown = false; + private boolean backButtonDown = false; + private boolean forwardButtonDown = false; private EventTarget fullPDRSource = null; private TargetWrapper fullPDRTmpTargetWrapper = new TargetWrapper(); @@ -3780,7 +3787,8 @@ private void process(MouseEvent e, boolean onPulse) { boolean gestureStarted = false; if (!onPulse) { if (e.getEventType() == MouseEvent.MOUSE_PRESSED) { - if (!(primaryButtonDown || secondaryButtonDown || middleButtonDown)) { + if (!(primaryButtonDown || secondaryButtonDown || middleButtonDown || + backButtonDown || forwardButtonDown)) { //old gesture ended and new one started gestureStarted = true; if (!PLATFORM_DRAG_GESTURE_INITIATION) { @@ -3800,6 +3808,8 @@ private void process(MouseEvent e, boolean onPulse) { primaryButtonDown = e.isPrimaryButtonDown(); secondaryButtonDown = e.isSecondaryButtonDown(); middleButtonDown = e.isMiddleButtonDown(); + backButtonDown = e.isBackButtonDown(); + forwardButtonDown = e.isForwardButtonDown(); } pick(tmpTargetWrapper, e.getSceneX(), e.getSceneY()); @@ -3809,6 +3819,7 @@ private void process(MouseEvent e, boolean onPulse) { e.getScreenX(), e.getScreenY(), e.getButton(), e.getClickCount(), e.isShiftDown(), e.isControlDown(), e.isAltDown(), e.isMetaDown(), e.isPrimaryButtonDown(), e.isMiddleButtonDown(), e.isSecondaryButtonDown(), + e.isBackButtonDown(), e.isForwardButtonDown(), e.isSynthesized(), e.isPopupTrigger(), e.isStillSincePress(), res); } @@ -3885,7 +3896,8 @@ private void process(MouseEvent e, boolean onPulse) { } if (pdrInProgress && - !(primaryButtonDown || secondaryButtonDown || middleButtonDown)) { + !(primaryButtonDown || secondaryButtonDown || middleButtonDown || + backButtonDown || forwardButtonDown)) { clearPDREventTargets(); exitFullPDR(e); // we need to do new picking in case the originally picked node diff --git a/modules/javafx.graphics/src/main/java/javafx/scene/input/MouseButton.java b/modules/javafx.graphics/src/main/java/javafx/scene/input/MouseButton.java index 0fdb218349..c06fcf9c87 100644 --- a/modules/javafx.graphics/src/main/java/javafx/scene/input/MouseButton.java +++ b/modules/javafx.graphics/src/main/java/javafx/scene/input/MouseButton.java @@ -47,7 +47,21 @@ public enum MouseButton { MIDDLE, /** - * Represents seconday (button 3, usually the right) mouse button. + * Represents secondary (button 3, usually the right) mouse button. */ SECONDARY, + + /** + * Represents back (button 4) mouse button. + * + * @since 12 + */ + BACK, + + /** + * Represents forward (button 5) mouse button. + * + * @since 12 + */ + FORWARD, } diff --git a/modules/javafx.graphics/src/main/java/javafx/scene/input/MouseDragEvent.java b/modules/javafx.graphics/src/main/java/javafx/scene/input/MouseDragEvent.java index 588b65a20d..06b102417b 100644 --- a/modules/javafx.graphics/src/main/java/javafx/scene/input/MouseDragEvent.java +++ b/modules/javafx.graphics/src/main/java/javafx/scene/input/MouseDragEvent.java @@ -161,9 +161,53 @@ public MouseDragEvent(@NamedArg("source") Object source, @NamedArg("target") Eve @NamedArg("primaryButtonDown") boolean primaryButtonDown, @NamedArg("middleButtonDown") boolean middleButtonDown, @NamedArg("secondaryButtonDown") boolean secondaryButtonDown, @NamedArg("synthesized") boolean synthesized, @NamedArg("popupTrigger") boolean popupTrigger, @NamedArg("pickResult") PickResult pickResult, @NamedArg("gestureSource") Object gestureSource) { + this(source, target, eventType, x, y, screenX, screenY, button, + clickCount, shiftDown, controlDown, altDown, metaDown, + primaryButtonDown, middleButtonDown, secondaryButtonDown, + false, false, + synthesized, popupTrigger, pickResult, gestureSource); + } + + /** + * Constructs new MouseDragEvent 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 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 backButtonDown true if back button was pressed. + * @param forwardButtonDown true if forward button was pressed. + * @param synthesized if this event was synthesized + * @param popupTrigger whether this event denotes a popup trigger for current platform + * @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 + * @param gestureSource source object of the ongoing gesture. + * @since 12 + */ + public MouseDragEvent(@NamedArg("source") Object source, @NamedArg("target") EventTarget target, @NamedArg("eventType") EventType eventType, + @NamedArg("x") double x, @NamedArg("y") double y, @NamedArg("screenX") double screenX, @NamedArg("screenY") double screenY, + @NamedArg("button") MouseButton button, @NamedArg("clickCount") int clickCount, + @NamedArg("shiftDown") boolean shiftDown, @NamedArg("controlDown") boolean controlDown, @NamedArg("altDown") boolean altDown, @NamedArg("metaDown") boolean metaDown, + @NamedArg("primaryButtonDown") boolean primaryButtonDown, @NamedArg("middleButtonDown") boolean middleButtonDown, @NamedArg("secondaryButtonDown") boolean secondaryButtonDown, + @NamedArg("backButtonDown") boolean backButtonDown, @NamedArg("forwardButtonDown") boolean forwardButtonDown, + @NamedArg("synthesized") boolean synthesized, @NamedArg("popupTrigger") boolean popupTrigger, @NamedArg("pickResult") PickResult pickResult, + @NamedArg("gestureSource") Object gestureSource) { super(source, target, eventType, x, y, screenX, screenY, button, clickCount, shiftDown, controlDown, altDown, metaDown, primaryButtonDown, middleButtonDown, secondaryButtonDown, + backButtonDown, forwardButtonDown, synthesized, popupTrigger, false, pickResult); this.gestureSource = gestureSource; } @@ -250,6 +294,12 @@ public Object getGestureSource() { if (isSecondaryButtonDown()) { sb.append(", secondaryButtonDown"); } + if (isBackButtonDown()) { + sb.append(", backButtonDown"); + } + if (isForwardButtonDown()) { + sb.append(", forwardButtonDown"); + } if (isShiftDown()) { sb.append(", shiftDown"); } diff --git a/modules/javafx.graphics/src/main/java/javafx/scene/input/MouseEvent.java b/modules/javafx.graphics/src/main/java/javafx/scene/input/MouseEvent.java index 5b5c0b610e..be89206506 100644 --- a/modules/javafx.graphics/src/main/java/javafx/scene/input/MouseEvent.java +++ b/modules/javafx.graphics/src/main/java/javafx/scene/input/MouseEvent.java @@ -342,6 +342,58 @@ public MouseEvent( synthesized, popupTrigger, stillSincePress, pickResult); } + /** + * 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 backButtonDown true if back button was pressed. + * @param forwardButtonDown true if forward 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 12 + */ + public MouseEvent( + @NamedArg("eventType") EventType eventType, + @NamedArg("x") double x, @NamedArg("y") double y, + @NamedArg("screenX") double screenX, @NamedArg("screenY") double screenY, + @NamedArg("button") MouseButton button, + @NamedArg("clickCount") int clickCount, + @NamedArg("shiftDown") boolean shiftDown, + @NamedArg("controlDown") boolean controlDown, + @NamedArg("altDown") boolean altDown, + @NamedArg("metaDown") boolean metaDown, + @NamedArg("primaryButtonDown") boolean primaryButtonDown, + @NamedArg("middleButtonDown") boolean middleButtonDown, + @NamedArg("secondaryButtonDown") boolean secondaryButtonDown, + @NamedArg("backButtonDown") boolean backButtonDown, + @NamedArg("forwardButtonDown") boolean forwardButtonDown, + @NamedArg("synthesized") boolean synthesized, + @NamedArg("popupTrigger") boolean popupTrigger, + @NamedArg("stillSincePress") boolean stillSincePress, + @NamedArg("pickResult") PickResult pickResult) { + this(null, null, eventType, x, y, screenX, screenY, button, clickCount, + shiftDown, controlDown, altDown, metaDown, + primaryButtonDown, middleButtonDown, secondaryButtonDown, + backButtonDown, forwardButtonDown, + synthesized, popupTrigger, stillSincePress, pickResult); + } + /** * Constructs new MouseEvent event. * @param source the source of the event. Can be null. @@ -385,6 +437,59 @@ public MouseEvent(@NamedArg("source") Object source, @NamedArg("target") EventTa @NamedArg("popupTrigger") boolean popupTrigger, @NamedArg("stillSincePress") boolean stillSincePress, @NamedArg("pickResult") PickResult pickResult) { + this(source, target, eventType, x, y, screenX, screenY, button, clickCount, + shiftDown, controlDown, altDown, metaDown, + primaryButtonDown, middleButtonDown, secondaryButtonDown, false, false, + 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 backButtonDown true if the back button was pressed + * @param forwardButtonDown true if the forward 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 12 + */ + public MouseEvent(@NamedArg("source") Object source, @NamedArg("target") EventTarget target, + @NamedArg("eventType") EventType eventType, + @NamedArg("x") double x, @NamedArg("y") double y, + @NamedArg("screenX") double screenX, @NamedArg("screenY") double screenY, + @NamedArg("button") MouseButton button, + @NamedArg("clickCount") int clickCount, + @NamedArg("shiftDown") boolean shiftDown, + @NamedArg("controlDown") boolean controlDown, + @NamedArg("altDown") boolean altDown, + @NamedArg("metaDown") boolean metaDown, + @NamedArg("primaryButtonDown") boolean primaryButtonDown, + @NamedArg("middleButtonDown") boolean middleButtonDown, + @NamedArg("secondaryButtonDown") boolean secondaryButtonDown, + @NamedArg("backButtonDown") boolean backButtonDown, + @NamedArg("forwardButtonDown") boolean forwardButtonDown, + @NamedArg("synthesized") boolean synthesized, + @NamedArg("popupTrigger") boolean popupTrigger, + @NamedArg("stillSincePress") boolean stillSincePress, + @NamedArg("pickResult") PickResult pickResult) { super(source, target, eventType); this.x = x; this.y = y; @@ -401,6 +506,8 @@ public MouseEvent(@NamedArg("source") Object source, @NamedArg("target") EventTa this.primaryButtonDown = primaryButtonDown; this.middleButtonDown = middleButtonDown; this.secondaryButtonDown = secondaryButtonDown; + this.backButtonDown = backButtonDown; + this.forwardButtonDown = forwardButtonDown; this.synthesized = synthesized; this.stillSincePress = stillSincePress; this.popupTrigger = popupTrigger; @@ -434,7 +541,7 @@ public static MouseDragEvent copyForMouseDragEvent( 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, + e.secondaryButtonDown, e.backButtonDown, e.forwardButtonDown, e.synthesized, e.popupTrigger, pickResult, gestureSource); ev.recomputeCoordinatesToSource(e, source); return ev; @@ -846,6 +953,52 @@ public final boolean isMiddleButtonDown() { return middleButtonDown; } + /** + * {@code true} if back button (button 4) 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 + * back button is depressed. + */ + private final boolean backButtonDown; + + /** + * Returns {@code true} if back button (button 4) + * 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 + * back button is depressed. + * + * @return {@code true} if back button (button 4) is currently pressed + * @since 12 + */ + public final boolean isBackButtonDown() { + return backButtonDown; + } + + /** + * {@code true} if forward button (button 5) 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 + * forward button is depressed. + */ + private final boolean forwardButtonDown; + + /** + * Returns {@code true} if forward button (button 5) + * 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 + * back button is depressed. + * + * @return {@code true} if forward button (button 5) is currently pressed + * @since 12 + */ + public final boolean isForwardButtonDown() { + return forwardButtonDown; + } + /** * Returns a string representation of this {@code MouseEvent} object. * @return a string representation of this {@code MouseEvent} object. @@ -876,6 +1029,12 @@ public final boolean isMiddleButtonDown() { if (isSecondaryButtonDown()) { sb.append(", secondaryButtonDown"); } + if (isBackButtonDown()) { + sb.append(", backButtonDown"); + } + if (isForwardButtonDown()) { + sb.append(", forwardButtonDown"); + } if (isShiftDown()) { sb.append(", shiftDown"); } diff --git a/modules/javafx.graphics/src/main/native-glass/gtk/GlassRobot.cpp b/modules/javafx.graphics/src/main/native-glass/gtk/GlassRobot.cpp index 66589f2e3b..72bfb311ba 100644 --- a/modules/javafx.graphics/src/main/native-glass/gtk/GlassRobot.cpp +++ b/modules/javafx.graphics/src/main/native-glass/gtk/GlassRobot.cpp @@ -38,6 +38,8 @@ #include "glass_key.h" #include "glass_screen.h" +#define MOUSE_BACK_BTN 8 +#define MOUSE_FORWARD_BTN 9 static void checkXTest(JNIEnv* env) { int32_t major_opcode, first_event, first_error; @@ -152,6 +154,12 @@ static void mouseButtons(jint buttons, gboolean press) if (buttons & com_sun_glass_ui_GlassRobot_MOUSE_RIGHT_BTN) { XTestFakeButtonEvent(xdisplay, 3, press, CurrentTime); } + if (buttons & com_sun_glass_ui_GlassRobot_MOUSE_BACK_BTN) { + XTestFakeButtonEvent(xdisplay, MOUSE_BACK_BTN, press, CurrentTime); + } + if (buttons & com_sun_glass_ui_GlassRobot_MOUSE_FORWARD_BTN) { + XTestFakeButtonEvent(xdisplay, MOUSE_FORWARD_BTN, press, CurrentTime); + } XSync(xdisplay, False); } diff --git a/modules/javafx.graphics/src/main/native-glass/gtk/glass_key.cpp b/modules/javafx.graphics/src/main/native-glass/gtk/glass_key.cpp index 4ae67f1bf9..14656ee78f 100644 --- a/modules/javafx.graphics/src/main/native-glass/gtk/glass_key.cpp +++ b/modules/javafx.graphics/src/main/native-glass/gtk/glass_key.cpp @@ -293,6 +293,8 @@ jint gdk_modifier_mask_to_glass(guint mask) glass_mask |= (mask & GDK_BUTTON1_MASK) ? com_sun_glass_events_KeyEvent_MODIFIER_BUTTON_PRIMARY : 0; glass_mask |= (mask & GDK_BUTTON2_MASK) ? com_sun_glass_events_KeyEvent_MODIFIER_BUTTON_MIDDLE : 0; glass_mask |= (mask & GDK_BUTTON3_MASK) ? com_sun_glass_events_KeyEvent_MODIFIER_BUTTON_SECONDARY : 0; + glass_mask |= (mask & GDK_BUTTON4_MASK) ? com_sun_glass_events_KeyEvent_MODIFIER_BUTTON_BACK : 0; + glass_mask |= (mask & GDK_BUTTON5_MASK) ? com_sun_glass_events_KeyEvent_MODIFIER_BUTTON_FORWARD : 0; glass_mask |= (mask & GDK_SUPER_MASK) ? com_sun_glass_events_KeyEvent_MODIFIER_WINDOWS : 0; // XXX: is this OK? return glass_mask; diff --git a/modules/javafx.graphics/src/main/native-glass/gtk/glass_window.cpp b/modules/javafx.graphics/src/main/native-glass/gtk/glass_window.cpp index 27f645058e..aa51391b97 100644 --- a/modules/javafx.graphics/src/main/native-glass/gtk/glass_window.cpp +++ b/modules/javafx.graphics/src/main/native-glass/gtk/glass_window.cpp @@ -48,6 +48,9 @@ #include +#define MOUSE_BACK_BTN 8 +#define MOUSE_FORWARD_BTN 9 + WindowContext * WindowContextBase::sm_grab_window = NULL; WindowContext * WindowContextBase::sm_mouse_drag_window = NULL; @@ -241,6 +244,10 @@ static inline jint gtk_button_number_to_mouse_button(guint button) { return com_sun_glass_events_MouseEvent_BUTTON_OTHER; case 3: return com_sun_glass_events_MouseEvent_BUTTON_RIGHT; + case MOUSE_BACK_BTN: + return com_sun_glass_events_MouseEvent_BUTTON_BACK; + case MOUSE_FORWARD_BTN: + return com_sun_glass_events_MouseEvent_BUTTON_FORWARD; default: // Other buttons are not supported by quantum and are not reported by other platforms return com_sun_glass_events_MouseEvent_BUTTON_NONE; @@ -265,6 +272,12 @@ void WindowContextBase::process_mouse_button(GdkEventButton* event) { case 3: mask = GDK_BUTTON3_MASK; break; + case MOUSE_BACK_BTN: + mask = GDK_BUTTON4_MASK; + break; + case MOUSE_FORWARD_BTN: + mask = GDK_BUTTON5_MASK; + break; } if (press) { @@ -290,9 +303,17 @@ void WindowContextBase::process_mouse_button(GdkEventButton* event) { // We can grab mouse pointer for these needs. if (press) { grab_mouse_drag_focus(); - } else if ((event->state & MOUSE_BUTTONS_MASK) + } else { + if ((event->state & MOUSE_BUTTONS_MASK) && !(state & MOUSE_BUTTONS_MASK)) { // all buttons released - ungrab_mouse_drag_focus(); + ungrab_mouse_drag_focus(); + } else if (event->button == 8 || event->button == 9) { + // GDK X backend interprets button press events for buttons 4-7 as + // scroll events so GDK_BUTTON4_MASK and GDK_BUTTON5_MASK will never + // be set on the event->state from GDK. Thus we cannot check if all + // buttons have been released in the usual way (as above). + ungrab_mouse_drag_focus(); + } } jint button = gtk_button_number_to_mouse_button(event->button); @@ -323,7 +344,9 @@ void WindowContextBase::process_mouse_motion(GdkEventMotion* event) { jint isDrag = glass_modifier & ( com_sun_glass_events_KeyEvent_MODIFIER_BUTTON_PRIMARY | com_sun_glass_events_KeyEvent_MODIFIER_BUTTON_MIDDLE | - com_sun_glass_events_KeyEvent_MODIFIER_BUTTON_SECONDARY); + com_sun_glass_events_KeyEvent_MODIFIER_BUTTON_SECONDARY | + com_sun_glass_events_KeyEvent_MODIFIER_BUTTON_BACK | + com_sun_glass_events_KeyEvent_MODIFIER_BUTTON_FORWARD); jint button = com_sun_glass_events_MouseEvent_BUTTON_NONE; if (glass_modifier & com_sun_glass_events_KeyEvent_MODIFIER_BUTTON_PRIMARY) { @@ -332,6 +355,10 @@ void WindowContextBase::process_mouse_motion(GdkEventMotion* event) { button = com_sun_glass_events_MouseEvent_BUTTON_OTHER; } else if (glass_modifier & com_sun_glass_events_KeyEvent_MODIFIER_BUTTON_SECONDARY) { button = com_sun_glass_events_MouseEvent_BUTTON_RIGHT; + } else if (glass_modifier & com_sun_glass_events_KeyEvent_MODIFIER_BUTTON_BACK) { + button = com_sun_glass_events_MouseEvent_BUTTON_BACK; + } else if (glass_modifier & com_sun_glass_events_KeyEvent_MODIFIER_BUTTON_FORWARD) { + button = com_sun_glass_events_MouseEvent_BUTTON_FORWARD; } if (jview) { diff --git a/modules/javafx.graphics/src/main/native-glass/ios/GlassViewDelegate.m b/modules/javafx.graphics/src/main/native-glass/ios/GlassViewDelegate.m index 0b3b8a187c..098f17245e 100644 --- a/modules/javafx.graphics/src/main/native-glass/ios/GlassViewDelegate.m +++ b/modules/javafx.graphics/src/main/native-glass/ios/GlassViewDelegate.m @@ -501,6 +501,12 @@ - (void)sendJavaMouseEvent:(CGPoint)viewPoint type:(int)type button:(int)button case com_sun_glass_events_MouseEvent_BUTTON_OTHER: modifiers |= com_sun_glass_events_KeyEvent_MODIFIER_BUTTON_MIDDLE; break; + case com_sun_glass_events_MouseEvent_BUTTON_BACK: + modifiers |= com_sun_glass_events_KeyEvent_MODIFIER_BUTTON_BACK; + break; + case com_sun_glass_events_MouseEvent_BUTTON_FORWARD: + modifiers |= com_sun_glass_events_KeyEvent_MODIFIER_BUTTON_FORWARD; + break; } } diff --git a/modules/javafx.graphics/src/main/native-glass/mac/GlassKey.m b/modules/javafx.graphics/src/main/native-glass/mac/GlassKey.m index f36bcb1ceb..c11852bc4b 100644 --- a/modules/javafx.graphics/src/main/native-glass/mac/GlassKey.m +++ b/modules/javafx.graphics/src/main/native-glass/mac/GlassKey.m @@ -218,6 +218,12 @@ jint GetJavaMouseModifiers(NSUInteger buttons) if (buttons & (1 << 2)) { jModifiers |= com_sun_glass_events_KeyEvent_MODIFIER_BUTTON_MIDDLE; } + if (buttons & (1 << 3)) { + jModifiers |= com_sun_glass_events_KeyEvent_MODIFIER_BUTTON_BACK; + } + if (buttons & (1 << 4)) { + jModifiers |= com_sun_glass_events_KeyEvent_MODIFIER_BUTTON_FORWARD; + } return jModifiers; } diff --git a/modules/javafx.graphics/src/main/native-glass/mac/GlassViewDelegate.m b/modules/javafx.graphics/src/main/native-glass/mac/GlassViewDelegate.m index 81b5524575..334e657010 100644 --- a/modules/javafx.graphics/src/main/native-glass/mac/GlassViewDelegate.m +++ b/modules/javafx.graphics/src/main/native-glass/mac/GlassViewDelegate.m @@ -339,7 +339,17 @@ - (void)sendJavaMouseEvent:(NSEvent *)theEvent break; case NSOtherMouseDown: type = com_sun_glass_events_MouseEvent_DOWN; - button = com_sun_glass_events_MouseEvent_BUTTON_OTHER; + switch ([theEvent buttonNumber]) { + case 2: + button = com_sun_glass_events_MouseEvent_BUTTON_OTHER; + break; + case 3: + button = com_sun_glass_events_MouseEvent_BUTTON_BACK; + break; + case 4: + button = com_sun_glass_events_MouseEvent_BUTTON_FORWARD; + break; + } break; case NSLeftMouseUp: @@ -352,7 +362,17 @@ - (void)sendJavaMouseEvent:(NSEvent *)theEvent break; case NSOtherMouseUp: type = com_sun_glass_events_MouseEvent_UP; - button = com_sun_glass_events_MouseEvent_BUTTON_OTHER; + switch ([theEvent buttonNumber]) { + case 2: + button = com_sun_glass_events_MouseEvent_BUTTON_OTHER; + break; + case 3: + button = com_sun_glass_events_MouseEvent_BUTTON_BACK; + break; + case 4: + button = com_sun_glass_events_MouseEvent_BUTTON_FORWARD; + break; + } break; case NSLeftMouseDragged: @@ -365,7 +385,17 @@ - (void)sendJavaMouseEvent:(NSEvent *)theEvent break; case NSOtherMouseDragged: type = com_sun_glass_events_MouseEvent_DRAG; - button = com_sun_glass_events_MouseEvent_BUTTON_OTHER; + switch ([theEvent buttonNumber]) { + case 2: + button = com_sun_glass_events_MouseEvent_BUTTON_OTHER; + break; + case 3: + button = com_sun_glass_events_MouseEvent_BUTTON_BACK; + break; + case 4: + button = com_sun_glass_events_MouseEvent_BUTTON_FORWARD; + break; + } break; case NSMouseMoved: @@ -491,6 +521,12 @@ - (void)sendJavaMouseEvent:(NSEvent *)theEvent case com_sun_glass_events_MouseEvent_BUTTON_OTHER: modifiers |= com_sun_glass_events_KeyEvent_MODIFIER_BUTTON_MIDDLE; break; + case com_sun_glass_events_MouseEvent_BUTTON_BACK: + modifiers |= com_sun_glass_events_KeyEvent_MODIFIER_BUTTON_BACK; + break; + case com_sun_glass_events_MouseEvent_BUTTON_FORWARD: + modifiers |= com_sun_glass_events_KeyEvent_MODIFIER_BUTTON_FORWARD; + break; } } } @@ -515,9 +551,11 @@ - (void)sendJavaMouseEvent:(NSEvent *)theEvent // prepare GlassDragSource for possible drag, case com_sun_glass_events_MouseEvent_DOWN: switch (button) { - case com_sun_glass_events_MouseEvent_BUTTON_LEFT: self->mouseDownMask |= 1 << 0; break; - case com_sun_glass_events_MouseEvent_BUTTON_RIGHT: self->mouseDownMask |= 1 << 1; break; - case com_sun_glass_events_MouseEvent_BUTTON_OTHER: self->mouseDownMask |= 1 << 2; break; + case com_sun_glass_events_MouseEvent_BUTTON_LEFT: self->mouseDownMask |= 1 << 0; break; + case com_sun_glass_events_MouseEvent_BUTTON_RIGHT: self->mouseDownMask |= 1 << 1; break; + case com_sun_glass_events_MouseEvent_BUTTON_OTHER: self->mouseDownMask |= 1 << 2; break; + case com_sun_glass_events_MouseEvent_BUTTON_BACK: self->mouseDownMask |= 1 << 3; break; + case com_sun_glass_events_MouseEvent_BUTTON_FORWARD: self->mouseDownMask |= 1 << 4; break; } //fall through case com_sun_glass_events_MouseEvent_DRAG: @@ -529,9 +567,11 @@ - (void)sendJavaMouseEvent:(NSEvent *)theEvent break; case com_sun_glass_events_MouseEvent_UP: switch (button) { - case com_sun_glass_events_MouseEvent_BUTTON_LEFT: self->mouseDownMask &= ~(1 << 0); break; - case com_sun_glass_events_MouseEvent_BUTTON_RIGHT: self->mouseDownMask &= ~(1 << 1); break; - case com_sun_glass_events_MouseEvent_BUTTON_OTHER: self->mouseDownMask &= ~(1 << 2); break; + case com_sun_glass_events_MouseEvent_BUTTON_LEFT: self->mouseDownMask &= ~(1 << 0); break; + case com_sun_glass_events_MouseEvent_BUTTON_RIGHT: self->mouseDownMask &= ~(1 << 1); break; + case com_sun_glass_events_MouseEvent_BUTTON_OTHER: self->mouseDownMask &= ~(1 << 2); break; + case com_sun_glass_events_MouseEvent_BUTTON_BACK: self->mouseDownMask &= ~(1 << 3); break; + case com_sun_glass_events_MouseEvent_BUTTON_FORWARD: self->mouseDownMask &= ~(1 << 4); break; } break; @@ -988,7 +1028,7 @@ - (BOOL)suppressMouseEnterExitOnMouseDown static jstring convertNSStringToJString(id aString, int length) { GET_MAIN_JENV; - + jstring jStr; if ([aString isKindOfClass:[NSAttributedString class]]) { NSData *data = [[aString string] dataUsingEncoding:NSUTF16LittleEndianStringEncoding]; @@ -1001,7 +1041,7 @@ static jstring convertNSStringToJString(id aString, int length) } else { jStr = (*env)->NewStringUTF(env, [aString UTF8String]); } - + GLASS_CHECK_EXCEPTION(env); return jStr; diff --git a/modules/javafx.graphics/src/main/native-glass/win/FullScreenWindow.cpp b/modules/javafx.graphics/src/main/native-glass/win/FullScreenWindow.cpp index 91e604ed2b..c7d8304daa 100644 --- a/modules/javafx.graphics/src/main/native-glass/win/FullScreenWindow.cpp +++ b/modules/javafx.graphics/src/main/native-glass/win/FullScreenWindow.cpp @@ -323,6 +323,9 @@ LRESULT FullScreenWindow::WindowProc(UINT msg, WPARAM wParam, LPARAM lParam) case WM_MBUTTONDOWN: case WM_MBUTTONUP: case WM_MBUTTONDBLCLK: + case WM_XBUTTONDOWN: + case WM_XBUTTONUP: + case WM_XBUTTONDBLCLK: case WM_MOUSEWHEEL: case WM_MOUSEHWHEEL: case WM_MOUSELEAVE: { diff --git a/modules/javafx.graphics/src/main/native-glass/win/GlassDnD.cpp b/modules/javafx.graphics/src/main/native-glass/win/GlassDnD.cpp index acfa329783..2a40c0d588 100644 --- a/modules/javafx.graphics/src/main/native-glass/win/GlassDnD.cpp +++ b/modules/javafx.graphics/src/main/native-glass/win/GlassDnD.cpp @@ -316,6 +316,12 @@ GlassDropSource::GlassDropSource(jobject jDnDClipboard) case com_sun_glass_events_MouseEvent_BUTTON_OTHER: m_button = MK_MBUTTON; break; + case com_sun_glass_events_MouseEvent_BUTTON_BACK: + m_button = MK_XBUTTON1; + break; + case com_sun_glass_events_MouseEvent_BUTTON_FORWARD: + m_button = MK_XBUTTON2; + break; default: m_button = 0; break; diff --git a/modules/javafx.graphics/src/main/native-glass/win/GlassWindow.cpp b/modules/javafx.graphics/src/main/native-glass/win/GlassWindow.cpp index 8d9a7f5e92..968af8da21 100644 --- a/modules/javafx.graphics/src/main/native-glass/win/GlassWindow.cpp +++ b/modules/javafx.graphics/src/main/native-glass/win/GlassWindow.cpp @@ -510,6 +510,9 @@ LRESULT GlassWindow::WindowProc(UINT msg, WPARAM wParam, LPARAM lParam) case WM_RBUTTONDBLCLK: case WM_MBUTTONUP: case WM_MBUTTONDBLCLK: + case WM_XBUTTONDOWN: + case WM_XBUTTONUP: + case WM_XBUTTONDBLCLK: case WM_MOUSEWHEEL: case WM_MOUSEHWHEEL: case WM_MOUSELEAVE: diff --git a/modules/javafx.graphics/src/main/native-glass/win/Robot.cpp b/modules/javafx.graphics/src/main/native-glass/win/Robot.cpp index 493ddf51ca..ba5cffe808 100644 --- a/modules/javafx.graphics/src/main/native-glass/win/Robot.cpp +++ b/modules/javafx.graphics/src/main/native-glass/win/Robot.cpp @@ -168,6 +168,12 @@ JNIEXPORT void JNICALL Java_com_sun_glass_ui_win_WinRobot__1mousePress if (buttons & (1 << 2)) { dwFlags |= MOUSEEVENTF_MIDDLEDOWN; } + if (buttons & (1 << 3)) { + dwFlags |= MOUSEEVENTF_XDOWN; + } + if (buttons & (1 << 4)) { + dwFlags |= MOUSEEVENTF_XDOWN; + } INPUT mouseInput = {0}; mouseInput.type = INPUT_MOUSE; @@ -176,11 +182,9 @@ JNIEXPORT void JNICALL Java_com_sun_glass_ui_win_WinRobot__1mousePress // Support for extra buttons if (buttons & (1 << 3)) { - mouseInput.mi.dwFlags |= MOUSEEVENTF_XDOWN; mouseInput.mi.mouseData = XBUTTON1; } if (buttons & (1 << 4)) { - mouseInput.mi.dwFlags |= MOUSEEVENTF_XDOWN; mouseInput.mi.mouseData = XBUTTON2; } @@ -210,6 +214,12 @@ JNIEXPORT void JNICALL Java_com_sun_glass_ui_win_WinRobot__1mouseRelease if (buttons & (1 << 2)) { dwFlags |= MOUSEEVENTF_MIDDLEUP; } + if (buttons & (1 << 3)) { + dwFlags |= MOUSEEVENTF_XUP; + } + if (buttons & (1 << 4)) { + dwFlags |= MOUSEEVENTF_XUP; + } INPUT mouseInput = {0}; mouseInput.type = INPUT_MOUSE; @@ -218,11 +228,9 @@ JNIEXPORT void JNICALL Java_com_sun_glass_ui_win_WinRobot__1mouseRelease // Support for extra buttons if (buttons & (1 << 3)) { - mouseInput.mi.dwFlags |= MOUSEEVENTF_XUP; mouseInput.mi.mouseData = XBUTTON1; } if (buttons & (1 << 4)) { - mouseInput.mi.dwFlags |= MOUSEEVENTF_XUP; mouseInput.mi.mouseData = XBUTTON2; } diff --git a/modules/javafx.graphics/src/main/native-glass/win/Utils.cpp b/modules/javafx.graphics/src/main/native-glass/win/Utils.cpp index 1b2886f1ef..44c67d9176 100644 --- a/modules/javafx.graphics/src/main/native-glass/win/Utils.cpp +++ b/modules/javafx.graphics/src/main/native-glass/win/Utils.cpp @@ -97,6 +97,12 @@ jint GetModifiers() if (HIBYTE(::GetKeyState(VK_LBUTTON)) != 0) { modifiers |= com_sun_glass_events_KeyEvent_MODIFIER_BUTTON_PRIMARY; } + if (HIBYTE(::GetKeyState(VK_XBUTTON1)) != 0) { + modifiers |= com_sun_glass_events_KeyEvent_MODIFIER_BUTTON_BACK; + } + if (HIBYTE(::GetKeyState(VK_XBUTTON2)) != 0) { + modifiers |= com_sun_glass_events_KeyEvent_MODIFIER_BUTTON_FORWARD; + } return modifiers; } diff --git a/modules/javafx.graphics/src/main/native-glass/win/ViewContainer.cpp b/modules/javafx.graphics/src/main/native-glass/win/ViewContainer.cpp index 891cee7d83..8b14868126 100644 --- a/modules/javafx.graphics/src/main/native-glass/win/ViewContainer.cpp +++ b/modules/javafx.graphics/src/main/native-glass/win/ViewContainer.cpp @@ -781,7 +781,7 @@ BOOL ViewContainer::HandleViewMouseEvent(HWND hwnd, UINT msg, WPARAM wParam, LPA m_lastMouseMovePosition = lParam; } // See RT-11305 regarding the GetCapture() check - if ((wParam & (MK_LBUTTON | MK_RBUTTON | MK_MBUTTON)) != 0 && ::GetCapture() == hwnd) { + if ((wParam & (MK_LBUTTON | MK_RBUTTON | MK_MBUTTON | MK_XBUTTON1 | MK_XBUTTON2)) != 0 && ::GetCapture() == hwnd) { type = com_sun_glass_events_MouseEvent_DRAG; } else { type = com_sun_glass_events_MouseEvent_MOVE; @@ -795,6 +795,10 @@ BOOL ViewContainer::HandleViewMouseEvent(HWND hwnd, UINT msg, WPARAM wParam, LPA button = com_sun_glass_events_MouseEvent_BUTTON_LEFT; } else if (wParam & MK_MBUTTON) { button = com_sun_glass_events_MouseEvent_BUTTON_OTHER; + } else if (wParam & MK_XBUTTON1) { + button = com_sun_glass_events_MouseEvent_BUTTON_BACK; + } else if (wParam & MK_XBUTTON2) { + button = com_sun_glass_events_MouseEvent_BUTTON_FORWARD; } break; case WM_LBUTTONDOWN: @@ -821,6 +825,16 @@ BOOL ViewContainer::HandleViewMouseEvent(HWND hwnd, UINT msg, WPARAM wParam, LPA type = com_sun_glass_events_MouseEvent_UP; button = com_sun_glass_events_MouseEvent_BUTTON_OTHER; break; + case WM_XBUTTONDOWN: + type = com_sun_glass_events_MouseEvent_DOWN; + button = GET_XBUTTON_WPARAM(wParam) == XBUTTON1 ? com_sun_glass_events_MouseEvent_BUTTON_BACK : + com_sun_glass_events_MouseEvent_BUTTON_FORWARD; + break; + case WM_XBUTTONUP: + type = com_sun_glass_events_MouseEvent_UP; + button = GET_XBUTTON_WPARAM(wParam) == XBUTTON1 ? com_sun_glass_events_MouseEvent_BUTTON_BACK : + com_sun_glass_events_MouseEvent_BUTTON_FORWARD; + break; case WM_MOUSEWHEEL: case WM_MOUSEHWHEEL: { diff --git a/modules/javafx.graphics/src/test/java/test/javafx/scene/input/MouseDragEventTest.java b/modules/javafx.graphics/src/test/java/test/javafx/scene/input/MouseDragEventTest.java index cb31ac6500..59e0e35521 100644 --- a/modules/javafx.graphics/src/test/java/test/javafx/scene/input/MouseDragEventTest.java +++ b/modules/javafx.graphics/src/test/java/test/javafx/scene/input/MouseDragEventTest.java @@ -79,6 +79,8 @@ public class MouseDragEventTest { assertFalse(e.isPrimaryButtonDown()); assertTrue(e.isMiddleButtonDown()); assertFalse(e.isSecondaryButtonDown()); + assertFalse(e.isBackButtonDown()); + assertFalse(e.isForwardButtonDown()); assertTrue(e.isSynthesized()); assertFalse(e.isPopupTrigger()); assertSame(gsrc, e.getGestureSource()); @@ -160,6 +162,8 @@ public class MouseDragEventTest { assertFalse(e.isPrimaryButtonDown()); assertTrue(e.isMiddleButtonDown()); assertFalse(e.isSecondaryButtonDown()); + assertFalse(e.isBackButtonDown()); + assertFalse(e.isForwardButtonDown()); assertTrue(e.isSynthesized()); assertFalse(e.isPopupTrigger()); assertSame(gsrc, e.getGestureSource()); @@ -187,7 +191,6 @@ public class MouseDragEventTest { assertTrue(e.isPopupTrigger()); } - @Test public void testLongConstructorWithoutPickResult() { Rectangle n1 = new Rectangle(10, 10); Rectangle n2 = new Rectangle(10, 10); @@ -212,6 +215,135 @@ public class MouseDragEventTest { assertEquals(0, e.getPickResult().getIntersectedPoint().getZ(), 10e-20); } + @Test public void testFullConstructor() { + Rectangle n1 = new Rectangle(10, 10); + Rectangle n2 = new Rectangle(10, 10); + Rectangle node = new Rectangle(); + node.setTranslateX(3); + node.setTranslateY(2); + node.setTranslateZ(50); + Rectangle gsrc = new Rectangle(); + + PickResult pickRes = new PickResult(node, new Point3D(15, 25, 100), 33); + + MouseDragEvent e = new MouseDragEvent(n1, n2, MouseDragEvent.MOUSE_DRAG_OVER, + 10, 20, 30, 40, MouseButton.MIDDLE, 3, + true, false, false, true, + false, true, false, true, true, + true, false, pickRes, gsrc); + + assertSame(MouseDragEvent.MOUSE_DRAG_OVER, e.getEventType()); + assertEquals(18, e.getX(), 10e-20); + assertEquals(27, e.getY(), 10e-20); + assertEquals(150, e.getZ(), 10e-20); + assertEquals(10, e.getSceneX(), 10e-20); + assertEquals(20, e.getSceneY(), 10e-20); + assertEquals(30, e.getScreenX(), 10e-20); + assertEquals(40, e.getScreenY(), 10e-20); + assertSame(MouseButton.MIDDLE, e.getButton()); + assertEquals(3, e.getClickCount()); + assertTrue(e.isShiftDown()); + assertFalse(e.isControlDown()); + assertFalse(e.isAltDown()); + assertTrue(e.isMetaDown()); + assertFalse(e.isPrimaryButtonDown()); + assertTrue(e.isMiddleButtonDown()); + assertFalse(e.isSecondaryButtonDown()); + assertTrue(e.isBackButtonDown()); + assertTrue(e.isForwardButtonDown()); + assertTrue(e.isSynthesized()); + assertFalse(e.isPopupTrigger()); + assertSame(gsrc, e.getGestureSource()); + assertFalse(e.isConsumed()); + assertSame(pickRes, e.getPickResult()); + assertSame(n1, e.getSource()); + assertSame(n2, e.getTarget()); + + e = new MouseDragEvent(n1, n2, MouseDragEvent.MOUSE_DRAG_OVER, + 10, 20, 30, 40, MouseButton.MIDDLE, 3, + false, true, true, false, + true, false, true, true, false, + false, true, pickRes, gsrc); + + assertSame(n1, e.getSource()); + assertSame(n2, e.getTarget()); + assertFalse(e.isShiftDown()); + assertTrue(e.isControlDown()); + assertTrue(e.isAltDown()); + assertFalse(e.isMetaDown()); + assertTrue(e.isPrimaryButtonDown()); + assertFalse(e.isMiddleButtonDown()); + assertTrue(e.isSecondaryButtonDown()); + assertTrue(e.isBackButtonDown()); + assertFalse(e.isForwardButtonDown()); + assertFalse(e.isSynthesized()); + assertTrue(e.isPopupTrigger()); + + e = new MouseDragEvent(n1, n2, MouseDragEvent.MOUSE_DRAG_OVER, + 10, 20, 30, 40, MouseButton.MIDDLE, 3, + false, true, true, false, + true, false, true, false, true, + false, true, pickRes, gsrc); + + assertSame(n1, e.getSource()); + assertSame(n2, e.getTarget()); + assertFalse(e.isShiftDown()); + assertTrue(e.isControlDown()); + assertTrue(e.isAltDown()); + assertFalse(e.isMetaDown()); + assertTrue(e.isPrimaryButtonDown()); + assertFalse(e.isMiddleButtonDown()); + assertTrue(e.isSecondaryButtonDown()); + assertFalse(e.isBackButtonDown()); + assertTrue(e.isForwardButtonDown()); + assertFalse(e.isSynthesized()); + assertTrue(e.isPopupTrigger()); + + e = new MouseDragEvent(n1, n2, MouseDragEvent.MOUSE_DRAG_OVER, + 10, 20, 30, 40, MouseButton.MIDDLE, 3, + false, true, true, false, + true, false, true, false, false, + false, true, pickRes, gsrc); + + assertSame(n1, e.getSource()); + assertSame(n2, e.getTarget()); + assertFalse(e.isShiftDown()); + assertTrue(e.isControlDown()); + assertTrue(e.isAltDown()); + assertFalse(e.isMetaDown()); + assertTrue(e.isPrimaryButtonDown()); + assertFalse(e.isMiddleButtonDown()); + assertTrue(e.isSecondaryButtonDown()); + assertFalse(e.isBackButtonDown()); + assertFalse(e.isForwardButtonDown()); + assertFalse(e.isSynthesized()); + assertTrue(e.isPopupTrigger()); + } + + @Test public void testFullConstructorWithoutPickResult() { + Rectangle n1 = new Rectangle(10, 10); + Rectangle n2 = new Rectangle(10, 10); + MouseDragEvent e = new MouseDragEvent(n1, n2, MouseDragEvent.MOUSE_DRAG_OVER, + 10, 20, 30, 40, MouseButton.MIDDLE, 3, + true, false, false, true, + false, true, false, true, true, + true, false, null, new Rectangle()); + assertSame(n1, e.getSource()); + assertSame(n2, e.getTarget()); + assertEquals(10, e.getX(), 10e-20); + assertEquals(20, e.getY(), 10e-20); + assertEquals(0, e.getZ(), 10e-20); + assertEquals(10, e.getSceneX(), 10e-20); + assertEquals(20, e.getSceneY(), 10e-20); + assertEquals(30, e.getScreenX(), 10e-20); + assertEquals(40, e.getScreenY(), 10e-20); + assertNotNull(e.getPickResult()); + assertNotNull(e.getPickResult().getIntersectedPoint()); + assertEquals(10, e.getPickResult().getIntersectedPoint().getX(), 10e-20); + assertEquals(20, e.getPickResult().getIntersectedPoint().getY(), 10e-20); + assertEquals(0, e.getPickResult().getIntersectedPoint().getZ(), 10e-20); + } + @Test public void fullPDRShouldNotStartAutomatically() { World w = new World(false, false); diff --git a/modules/javafx.graphics/src/test/java/test/javafx/scene/input/MouseEventTest.java b/modules/javafx.graphics/src/test/java/test/javafx/scene/input/MouseEventTest.java index 125518c701..51fe8b7a57 100644 --- a/modules/javafx.graphics/src/test/java/test/javafx/scene/input/MouseEventTest.java +++ b/modules/javafx.graphics/src/test/java/test/javafx/scene/input/MouseEventTest.java @@ -81,6 +81,8 @@ public class MouseEventTest { assertFalse(e.isPrimaryButtonDown()); assertTrue(e.isMiddleButtonDown()); assertFalse(e.isSecondaryButtonDown()); + assertFalse(e.isBackButtonDown()); + assertFalse(e.isForwardButtonDown()); assertTrue(e.isSynthesized()); assertFalse(e.isPopupTrigger()); assertTrue(e.isStillSincePress()); @@ -102,6 +104,8 @@ public class MouseEventTest { assertTrue(e.isPrimaryButtonDown()); assertFalse(e.isMiddleButtonDown()); assertTrue(e.isSecondaryButtonDown()); + assertFalse(e.isBackButtonDown()); + assertFalse(e.isForwardButtonDown()); assertFalse(e.isSynthesized()); assertTrue(e.isPopupTrigger()); assertFalse(e.isStillSincePress()); @@ -164,6 +168,8 @@ public class MouseEventTest { assertFalse(e.isPrimaryButtonDown()); assertTrue(e.isMiddleButtonDown()); assertFalse(e.isSecondaryButtonDown()); + assertFalse(e.isBackButtonDown()); + assertFalse(e.isForwardButtonDown()); assertTrue(e.isSynthesized()); assertFalse(e.isPopupTrigger()); assertTrue(e.isStillSincePress()); @@ -185,12 +191,13 @@ public class MouseEventTest { assertTrue(e.isPrimaryButtonDown()); assertFalse(e.isMiddleButtonDown()); assertTrue(e.isSecondaryButtonDown()); + assertFalse(e.isBackButtonDown()); + assertFalse(e.isForwardButtonDown()); assertFalse(e.isSynthesized()); assertTrue(e.isPopupTrigger()); assertFalse(e.isStillSincePress()); } - @Test public void testLongConstructorWithoutPickResult() { Rectangle n1 = new Rectangle(10, 10); Rectangle n2 = new Rectangle(10, 10); @@ -215,6 +222,31 @@ public class MouseEventTest { assertEquals(0, e.getPickResult().getIntersectedPoint().getZ(), 10e-20); } + @Test public void testFullConstructorWithoutPickResult() { + Rectangle n1 = new Rectangle(10, 10); + Rectangle n2 = new Rectangle(10, 10); + MouseEvent e = new MouseEvent(n1, n2, MouseEvent.MOUSE_DRAGGED, + 10, 20, 30, 40, MouseButton.MIDDLE, 3, + true, false, false, true, + false, true, false, + false, false, true, + false, true, null); + assertSame(n1, e.getSource()); + assertSame(n2, e.getTarget()); + assertEquals(10, e.getX(), 10e-20); + assertEquals(20, e.getY(), 10e-20); + assertEquals(0, e.getZ(), 10e-20); + assertEquals(10, e.getSceneX(), 10e-20); + assertEquals(20, e.getSceneY(), 10e-20); + assertEquals(30, e.getScreenX(), 10e-20); + assertEquals(40, e.getScreenY(), 10e-20); + assertNotNull(e.getPickResult()); + assertNotNull(e.getPickResult().getIntersectedPoint()); + assertEquals(10, e.getPickResult().getIntersectedPoint().getX(), 10e-20); + assertEquals(20, e.getPickResult().getIntersectedPoint().getY(), 10e-20); + assertEquals(0, e.getPickResult().getIntersectedPoint().getZ(), 10e-20); + } + @Test public void shouldCreateDoubleClickMouseEvent() { /* constructor called during initialization */ @@ -234,6 +266,8 @@ public void shouldCreateDoubleClickMouseEvent() { assertFalse(doubleclick.isPrimaryButtonDown()); assertTrue(doubleclick.isMiddleButtonDown()); assertFalse(doubleclick.isSecondaryButtonDown()); + assertFalse(doubleclick.isBackButtonDown()); + assertFalse(doubleclick.isForwardButtonDown()); assertSame(MouseEvent.MOUSE_CLICKED, doubleclick.getEventType()); assertSame(MouseEvent.NULL_SOURCE_TARGET, doubleclick.getSource()); } @@ -259,6 +293,8 @@ public void shouldCopyMouseEvent() { assertEquals(doubleclick.isPrimaryButtonDown(), copy.isPrimaryButtonDown()); assertEquals(doubleclick.isMiddleButtonDown(), copy.isMiddleButtonDown()); assertEquals(doubleclick.isSecondaryButtonDown(), copy.isSecondaryButtonDown()); + assertEquals(doubleclick.isBackButtonDown(), copy.isBackButtonDown()); + assertEquals(doubleclick.isForwardButtonDown(), copy.isForwardButtonDown()); assertSame(doubleclick.getEventType(), copy.getEventType()); assertSame(node1, copy.getSource()); assertSame(node2, copy.getTarget()); @@ -286,6 +322,8 @@ public void shouldCopyMouseEventWithEventId() { assertEquals(doubleclick.isPrimaryButtonDown(), copy.isPrimaryButtonDown()); assertEquals(doubleclick.isMiddleButtonDown(), copy.isMiddleButtonDown()); assertEquals(doubleclick.isSecondaryButtonDown(), copy.isSecondaryButtonDown()); + assertEquals(doubleclick.isBackButtonDown(), copy.isBackButtonDown()); + assertEquals(doubleclick.isForwardButtonDown(), copy.isForwardButtonDown()); assertSame(MouseEvent.MOUSE_ENTERED, copy.getEventType()); assertSame(node1, copy.getSource()); assertSame(node2, copy.getTarget()); @@ -313,6 +351,8 @@ public void shouldCopyMouseEventWithNode() { assertEquals(doubleclick.isPrimaryButtonDown(), copy.isPrimaryButtonDown()); assertEquals(doubleclick.isMiddleButtonDown(), copy.isMiddleButtonDown()); assertEquals(doubleclick.isSecondaryButtonDown(), copy.isSecondaryButtonDown()); + assertEquals(doubleclick.isBackButtonDown(), copy.isBackButtonDown()); + assertEquals(doubleclick.isForwardButtonDown(), copy.isForwardButtonDown()); assertSame(doubleclick.getEventType(), copy.getEventType()); assertSame(node2, copy.getSource()); assertSame(node1, copy.getTarget()); @@ -339,6 +379,8 @@ public void shouldCopyMouseEventWithDrag() { assertEquals(doubleclick.isPrimaryButtonDown(), copy.isPrimaryButtonDown()); assertEquals(doubleclick.isMiddleButtonDown(), copy.isMiddleButtonDown()); assertEquals(doubleclick.isSecondaryButtonDown(), copy.isSecondaryButtonDown()); + assertEquals(doubleclick.isBackButtonDown(), copy.isBackButtonDown()); + assertEquals(doubleclick.isForwardButtonDown(), copy.isForwardButtonDown()); assertSame(doubleclick.getEventType(), copy.getEventType()); assertSame(node1, copy.getSource()); assertSame(node2, copy.getTarget()); diff --git a/modules/javafx.swing/src/main/java/com/sun/javafx/embed/swing/SwingEvents.java b/modules/javafx.swing/src/main/java/com/sun/javafx/embed/swing/SwingEvents.java index 8a7cf11f35..7921c8b036 100644 --- a/modules/javafx.swing/src/main/java/com/sun/javafx/embed/swing/SwingEvents.java +++ b/modules/javafx.swing/src/main/java/com/sun/javafx/embed/swing/SwingEvents.java @@ -81,6 +81,10 @@ public static int mouseButtonToEmbedMouseButton(int button, int extModifiers) { abstractButton = AbstractEvents.MOUSEEVENT_MIDDLE_BUTTON; } else if ((extModifiers & MouseEvent.BUTTON3_DOWN_MASK) != 0) { abstractButton = AbstractEvents.MOUSEEVENT_SECONDARY_BUTTON; + } else if ((extModifiers & MouseEvent.getMaskForButton(4)) != 0) { + abstractButton = AbstractEvents.MOUSEEVENT_BACK_BUTTON; + } else if ((extModifiers & MouseEvent.getMaskForButton(5)) != 0) { + abstractButton = AbstractEvents.MOUSEEVENT_FORWARD_BUTTON; } return abstractButton; } @@ -180,6 +184,12 @@ public static int fxMouseModsToMouseMods(javafx.scene.input.MouseEvent event) { if (event.isMiddleButtonDown()) { mods |= MouseEvent.BUTTON2_DOWN_MASK; } + if (event.isBackButtonDown()) { + mods |= MouseEvent.getMaskForButton(4); + } + if (event.isForwardButtonDown()) { + mods |= MouseEvent.getMaskForButton(5); + } return mods; } diff --git a/modules/javafx.swing/src/main/java/javafx/embed/swing/JFXPanel.java b/modules/javafx.swing/src/main/java/javafx/embed/swing/JFXPanel.java index c2cf538846..0cf076e5a0 100644 --- a/modules/javafx.swing/src/main/java/javafx/embed/swing/JFXPanel.java +++ b/modules/javafx.swing/src/main/java/javafx/embed/swing/JFXPanel.java @@ -388,6 +388,9 @@ private void sendMouseEventToFX(MouseEvent e) { boolean primaryBtnDown = (extModifiers & MouseEvent.BUTTON1_DOWN_MASK) != 0; boolean middleBtnDown = (extModifiers & MouseEvent.BUTTON2_DOWN_MASK) != 0; boolean secondaryBtnDown = (extModifiers & MouseEvent.BUTTON3_DOWN_MASK) != 0; + boolean backBtnDown = (extModifiers & MouseEvent.getMaskForButton(4)) != 0; + boolean forwardBtnDown = (extModifiers & MouseEvent.getMaskForButton(5)) != 0; + // Fix for RT-16558: if a PRESSED event is consumed, e.g. by a Swing Popup, // subsequent DRAGGED and RELEASED events should not be sent to FX as well if (e.getID() == MouseEvent.MOUSE_DRAGGED) { @@ -400,7 +403,7 @@ private void sendMouseEventToFX(MouseEvent e) { if (!isCapturingMouse) { return; } - isCapturingMouse = primaryBtnDown || middleBtnDown || secondaryBtnDown; + isCapturingMouse = primaryBtnDown || middleBtnDown || secondaryBtnDown || backBtnDown || forwardBtnDown; } else if (e.getID() == MouseEvent.MOUSE_CLICKED) { // Don't send click events to FX, as they are generated in Scene return; @@ -427,6 +430,7 @@ private void sendMouseEventToFX(MouseEvent e) { SwingEvents.mouseIDToEmbedMouseType(e.getID()), SwingEvents.mouseButtonToEmbedMouseButton(e.getButton(), extModifiers), primaryBtnDown, middleBtnDown, secondaryBtnDown, + backBtnDown, forwardBtnDown, e.getX(), e.getY(), e.getXOnScreen(), e.getYOnScreen(), (extModifiers & MouseEvent.SHIFT_DOWN_MASK) != 0, (extModifiers & MouseEvent.CTRL_DOWN_MASK) != 0, diff --git a/modules/javafx.swt/src/main/java/javafx/embed/swt/FXCanvas.java b/modules/javafx.swt/src/main/java/javafx/embed/swt/FXCanvas.java index 778864f00e..59fbed05fc 100644 --- a/modules/javafx.swt/src/main/java/javafx/embed/swt/FXCanvas.java +++ b/modules/javafx.swt/src/main/java/javafx/embed/swt/FXCanvas.java @@ -689,6 +689,8 @@ private void sendMouseEventToFX(MouseEvent me, int embedMouseType) { boolean primaryBtnDown = (me.stateMask & SWT.BUTTON1) != 0; boolean middleBtnDown = (me.stateMask & SWT.BUTTON2) != 0; boolean secondaryBtnDown = (me.stateMask & SWT.BUTTON3) != 0; + boolean backBtnDown = (me.stateMask & SWT.BUTTON4) != 0; + boolean forwardBtnDown = (me.stateMask & SWT.BUTTON5) != 0; boolean shift = (me.stateMask & SWT.SHIFT) != 0; boolean control = (me.stateMask & SWT.CONTROL) != 0; boolean alt = (me.stateMask & SWT.ALT) != 0; @@ -699,11 +701,15 @@ private void sendMouseEventToFX(MouseEvent me, int embedMouseType) { primaryBtnDown |= me.button == 1; middleBtnDown |= me.button == 2; secondaryBtnDown |= me.button == 3; + backBtnDown |= me.button == 4; + forwardBtnDown |= me.button == 5; break; case AbstractEvents.MOUSEEVENT_RELEASED: primaryBtnDown &= me.button != 1; middleBtnDown &= me.button != 2; secondaryBtnDown &= me.button != 3; + backBtnDown &= me.button == 4; + forwardBtnDown &= me.button == 5; break; case AbstractEvents.MOUSEEVENT_CLICKED: // Don't send click events to FX, as they are generated in Scene @@ -723,6 +729,10 @@ private void sendMouseEventToFX(MouseEvent me, int embedMouseType) { button = 2; } else if ((me.stateMask & SWT.BUTTON3) != 0) { button = 3; + } else if ((me.stateMask & SWT.BUTTON4) != 0) { + button = 4; + } else if ((me.stateMask & SWT.BUTTON5) != 0) { + button = 5; } } break; @@ -735,6 +745,7 @@ private void sendMouseEventToFX(MouseEvent me, int embedMouseType) { embedMouseType, SWTEvents.mouseButtonToEmbedMouseButton(button, me.stateMask), primaryBtnDown, middleBtnDown, secondaryBtnDown, + backBtnDown, forwardBtnDown, me.x, me.y, los.x, los.y, shift, control, alt, meta, diff --git a/modules/javafx.swt/src/main/java/javafx/embed/swt/SWTEvents.java b/modules/javafx.swt/src/main/java/javafx/embed/swt/SWTEvents.java index f0ae6fe226..7e7468e5dc 100644 --- a/modules/javafx.swt/src/main/java/javafx/embed/swt/SWTEvents.java +++ b/modules/javafx.swt/src/main/java/javafx/embed/swt/SWTEvents.java @@ -69,6 +69,8 @@ static int mouseButtonToEmbedMouseButton(int button, int extModifiers) { case 1: return AbstractEvents.MOUSEEVENT_PRIMARY_BUTTON; case 2: return AbstractEvents.MOUSEEVENT_MIDDLE_BUTTON; case 3: return AbstractEvents.MOUSEEVENT_SECONDARY_BUTTON; + case 4: return AbstractEvents.MOUSEEVENT_BACK_BUTTON; + case 5: return AbstractEvents.MOUSEEVENT_FORWARD_BUTTON; } return AbstractEvents.MOUSEEVENT_NONE_BUTTON; } diff --git a/tests/manual/swt/FXCanvasMouseButtonEventsTest.java b/tests/manual/swt/FXCanvasMouseButtonEventsTest.java new file mode 100644 index 0000000000..f8e6081aaf --- /dev/null +++ b/tests/manual/swt/FXCanvasMouseButtonEventsTest.java @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2018, 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. + */ + +import javafx.embed.swt.FXCanvas; +import javafx.scene.Group; +import javafx.scene.Scene; +import javafx.scene.control.Label; +import javafx.scene.control.TextArea; +import javafx.scene.layout.VBox; +import org.eclipse.swt.SWT; +import org.eclipse.swt.layout.FillLayout; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Shell; + +public class FXCanvasMouseButtonEventsTest { + + static final String instructions = + "This tests that SWT mouse button events (press, release, and click) are properly transferred to SWT. " + + "It passes if all mouse button events for each of the 5 buttons are recognized properly."; + + private static TextArea createInfo(String msg) { + TextArea t = new TextArea(msg); + t.setWrapText(true); + t.setEditable(false); + t.setMaxWidth(400); + t.setMaxHeight(100); + return t; + } + + public static void main(String[] args) { + final Display display = new Display(); + final Shell shell = new Shell(display); + shell.setText("FXCanvasMouseButtonEventsTest"); + shell.setSize(400, 400); + shell.setLayout(new FillLayout()); + final FXCanvas canvas = new FXCanvas(shell, SWT.NONE); + shell.open(); + + // create and hook scene + Group root = new Group(); + + TextArea info = createInfo(instructions); + Label clickOutput = new Label("No click events yet..."); + Label pressOutput = new Label("No press events yet..."); + Label releaseOutput = new Label("No release events yet..."); + + VBox vbox = new VBox(); + vbox.getChildren().addAll(info, clickOutput, pressOutput, releaseOutput); + root.getChildren().add(vbox); + + final Scene scene = new Scene(root, 400, 400); + + final int[] clickEventCount = {0}; + root.setOnMouseClicked(clickEvent -> { + clickOutput.setText("Mouse CLICK #" + clickEventCount[0]++ + ": button: " + clickEvent.getButton()); + }); + final int[] pressEventCount = {0}; + root.setOnMousePressed(pressEvent -> { + pressOutput.setText("Mouse PRESS #" + pressEventCount[0]++ + ": button: " + pressEvent.getButton()); + }); + final int[] releaseEventCount = {0}; + root.setOnMouseReleased(releaseEvent -> { + releaseOutput.setText("Mouse RELEASE #" + releaseEventCount[0]++ + ": button: " + releaseEvent.getButton()); + }); + + canvas.setScene(scene); + + while (!shell.isDisposed()) { + // run SWT event loop + if (!display.readAndDispatch()) { + display.sleep(); + } + } + display.dispose(); + } +} diff --git a/tests/system/src/test/java/test/robot/com/sun/glass/ui/monocle/RobotTest.java b/tests/system/src/test/java/test/robot/com/sun/glass/ui/monocle/RobotTest.java index b29b9a9ee7..97afb01929 100644 --- a/tests/system/src/test/java/test/robot/com/sun/glass/ui/monocle/RobotTest.java +++ b/tests/system/src/test/java/test/robot/com/sun/glass/ui/monocle/RobotTest.java @@ -79,6 +79,16 @@ public void clickTest() throws Exception { assertEquals(400, (int) robot.getMouseY()); }); TestLogShim.waitForLog("Clicked at 300, 400"); + + Platform.runLater(() -> { + Robot robot = new Robot(); + robot.mouseMove(new Point2D(300, 400)); + robot.mouseClick(MouseButton.BACK); + assertEquals(new Point2D(300, 400), robot.getMousePosition()); + assertEquals(300, (int) robot.getMouseX()); + assertEquals(400, (int) robot.getMouseY()); + }); + TestLogShim.waitForLog("Clicked at 300, 400"); } @Test diff --git a/tests/system/src/test/java/test/robot/javafx/scene/RobotTest.java b/tests/system/src/test/java/test/robot/javafx/scene/RobotTest.java index 2bf0455371..99dd80705f 100644 --- a/tests/system/src/test/java/test/robot/javafx/scene/RobotTest.java +++ b/tests/system/src/test/java/test/robot/javafx/scene/RobotTest.java @@ -37,6 +37,7 @@ import javafx.geometry.Point2D; import javafx.scene.Scene; import javafx.scene.control.Button; +import javafx.scene.control.Label; import javafx.scene.control.TextField; import javafx.scene.image.WritableImage; import javafx.scene.input.KeyCode; @@ -98,12 +99,19 @@ public static void main(String[] args) throws Exception { test.testMouseClickPrimary(); test.testMouseClickSecondary(); test.testMouseClickMiddle(); + test.testMouseClickForward(); + test.testMouseClickBack(); test.testMousePressThrowsISEOnWrongThread(); test.testMousePressThrowsNPEForNullArgument(); test.testMouseReleaseThrowsISEOnWrongThread(); test.testMouseReleaseThrowsNPEForNullArgument(); test.testMouseClickThrowsISEOnWrongThread(); test.testMouseClickThrowsNPEForNullArgument(); + test.testMouseDragPrimary(); + test.testMouseDragSecondary(); + test.testMouseDragMiddle(); + test.testMouseDragForward(); + test.testMouseDragBack(); test.testMouseWheelPositiveAmount(); test.testMouseWheelNegativeAmount(); test.testMouseWheelThrowsISEOnWrongThread(); @@ -312,6 +320,16 @@ public void testMouseClickMiddle() { testMouseAction(MouseAction.CLICKED, MouseButton.MIDDLE); } + @Test + public void testMouseClickForward() { + testMouseAction(MouseAction.CLICKED, MouseButton.FORWARD); + } + + @Test + public void testMouseClickBack() { + testMouseAction(MouseAction.CLICKED, MouseButton.BACK); + } + private enum MouseAction { PRESSED, CLICKED @@ -459,6 +477,65 @@ public void testMouseClickThrowsNPEForNullArgument() { }); } + @Test + public void testMouseDragPrimary() { + testMouseDrag(MouseButton.PRIMARY); + } + + @Test + public void testMouseDragSecondary() { + testMouseDrag(MouseButton.SECONDARY); + } + + @Test + public void testMouseDragMiddle() { + testMouseDrag(MouseButton.MIDDLE); + } + + @Test + public void testMouseDragForward() { + testMouseDrag(MouseButton.FORWARD); + } + + @Test + public void testMouseDragBack() { + testMouseDrag(MouseButton.BACK); + } + + public void testMouseDrag(MouseButton mouseButton) { + CountDownLatch mouseDragLatch = new CountDownLatch(1); + CountDownLatch setSceneLatch = new CountDownLatch(1); + Label label = new Label("Source"); + InvalidationListener invalidationListener = observable -> setSceneLatch.countDown(); + Util.runAndWait(() -> { + label.setOnMouseDragged(event -> { + if (event.getButton() == mouseButton) { + mouseDragLatch.countDown(); + } + }); + scene = new Scene(new HBox(label)); + stage.sceneProperty().addListener(observable -> { + setSceneLatch.countDown(); + stage.sceneProperty().removeListener(invalidationListener); + }); + stage.setScene(scene); + }); + waitForLatch(setSceneLatch, 5, "Timeout while waiting for scene to be set on stage."); + Util.runAndWait(() -> { + int mouseX = (int) (scene.getWindow().getX() + scene.getX() + + label.getLayoutX() + label.getLayoutBounds().getWidth() / 2); + int mouseY = (int) (scene.getWindow().getY() + scene.getY() + + label.getLayoutY() + label.getLayoutBounds().getHeight() / 2); + robot.mouseMove(mouseX, mouseY); + robot.mousePress(mouseButton); + for (int i = 1; i <= 50; i++) { + robot.mouseMove(mouseX + i, mouseY); + } + robot.mouseRelease(mouseButton); + }); + waitForLatch(mouseDragLatch, 5, "Timeout while waiting for button.onMouseDragged()."); + } + @Test public void testMouseWheelPositiveAmount() { testMouseWheel(5);