1 /* 2 * Copyright (c) 2010, 2015, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package com.sun.javafx.tk.quantum; 27 28 import com.sun.glass.events.GestureEvent; 29 import com.sun.glass.events.KeyEvent; 30 import com.sun.glass.events.MouseEvent; 31 import com.sun.glass.events.ViewEvent; 32 import com.sun.glass.events.TouchEvent; 33 import com.sun.glass.events.SwipeGesture; 34 import com.sun.glass.ui.Accessible; 35 import com.sun.glass.ui.Clipboard; 36 import com.sun.glass.ui.ClipboardAssistance; 37 import com.sun.glass.ui.View; 38 import com.sun.glass.ui.Window; 39 import com.sun.javafx.PlatformUtil; 40 import com.sun.javafx.collections.TrackableObservableList; 41 import com.sun.javafx.logging.PulseLogger; 42 import static com.sun.javafx.logging.PulseLogger.PULSE_LOGGING_ENABLED; 43 import com.sun.javafx.scene.input.KeyCodeMap; 44 45 import javafx.collections.ListChangeListener; 46 import javafx.collections.ObservableList; 47 48 import javafx.event.EventType; 49 import javafx.geometry.Point2D; 50 import javafx.scene.input.InputMethodEvent; 51 import javafx.scene.input.InputMethodHighlight; 52 import javafx.scene.input.InputMethodTextRun; 53 import javafx.scene.input.MouseButton; 54 import javafx.scene.input.RotateEvent; 55 import javafx.scene.input.ScrollEvent; 56 import javafx.scene.input.SwipeEvent; 57 import javafx.scene.input.TouchPoint; 58 import javafx.scene.input.TransferMode; 59 import javafx.scene.input.ZoomEvent; 60 61 import java.security.AccessController; 62 import java.security.PrivilegedAction; 63 64 class GlassViewEventHandler extends View.EventHandler { 65 66 static boolean zoomGestureEnabled; 67 static boolean rotateGestureEnabled; 68 static boolean scrollGestureEnabled; 69 static { 70 AccessController.doPrivileged((PrivilegedAction<Void>) () -> { 71 zoomGestureEnabled = Boolean.valueOf(System.getProperty("com.sun.javafx.gestures.zoom", "false")); 72 rotateGestureEnabled = Boolean.valueOf(System.getProperty("com.sun.javafx.gestures.rotate", "false")); 73 scrollGestureEnabled = Boolean.valueOf(System.getProperty("com.sun.javafx.gestures.scroll", "false")); 74 return null; 75 }); 76 } 77 78 private ViewScene scene; 79 private final GlassSceneDnDEventHandler dndHandler; 80 private final GestureRecognizers gestures; 81 82 public GlassViewEventHandler(final ViewScene scene) { 83 this.scene = scene; 84 85 dndHandler = new GlassSceneDnDEventHandler(scene); 86 87 gestures = new GestureRecognizers(); 88 if (PlatformUtil.isWindows() || PlatformUtil.isIOS() || PlatformUtil.isEmbedded()) { 89 gestures.add(new SwipeGestureRecognizer(scene)); 90 } 91 if (zoomGestureEnabled) { 92 gestures.add(new ZoomGestureRecognizer(scene)); 93 } 94 if (rotateGestureEnabled) { 95 gestures.add(new RotateGestureRecognizer(scene)); 96 } 97 if (scrollGestureEnabled) { 98 gestures.add(new ScrollGestureRecognizer(scene)); 99 } 100 } 101 102 // Default fullscreen allows limited keyboard input. 103 // It will only receive events from the following keys: 104 // DOWN, UP, LEFT, RIGHT, SPACE, TAB, PAGE_UP, PAGE_DOWN, 105 // HOME, END, ENTER. 106 private static boolean allowableFullScreenKeys(int key) { 107 switch (key) { 108 case KeyEvent.VK_DOWN: 109 case KeyEvent.VK_UP: 110 case KeyEvent.VK_LEFT: 111 case KeyEvent.VK_RIGHT: 112 case KeyEvent.VK_SPACE: 113 case KeyEvent.VK_TAB: 114 case KeyEvent.VK_PAGE_DOWN: 115 case KeyEvent.VK_PAGE_UP: 116 case KeyEvent.VK_HOME: 117 case KeyEvent.VK_END: 118 case KeyEvent.VK_ENTER: 119 return true; 120 } 121 return false; 122 } 123 124 private boolean checkFullScreenKeyEvent(int type, int key, char chars[], int modifiers) { 125 return scene.getWindowStage().isTrustedFullScreen() || allowableFullScreenKeys(key); 126 } 127 128 private final PaintCollector collector = PaintCollector.getInstance(); 129 130 private static EventType<javafx.scene.input.KeyEvent> keyEventType(int glassType) { 131 switch (glassType) { 132 case com.sun.glass.events.KeyEvent.PRESS: 133 return javafx.scene.input.KeyEvent.KEY_PRESSED; 134 case com.sun.glass.events.KeyEvent.RELEASE: 135 return javafx.scene.input.KeyEvent.KEY_RELEASED; 136 case com.sun.glass.events.KeyEvent.TYPED: 137 return javafx.scene.input.KeyEvent.KEY_TYPED; 138 default: 139 if (QuantumToolkit.verbose) { 140 System.err.println("Unknown Glass key event type: " + glassType); 141 } 142 return null; 143 } 144 } 145 146 private final KeyEventNotification keyNotification = new KeyEventNotification(); 147 private class KeyEventNotification implements PrivilegedAction<Void> { 148 View view; 149 long time; 150 int type; 151 int key; 152 char[] chars; 153 int modifiers; 154 155 @Override 156 public Void run() { 157 if (PULSE_LOGGING_ENABLED) { 158 PulseLogger.newInput(keyEventType(type).toString()); 159 } 160 WindowStage stage = scene.getWindowStage(); 161 try { 162 if (stage != null) { 163 stage.setInEventHandler(true); 164 } 165 166 boolean shiftDown = (modifiers & KeyEvent.MODIFIER_SHIFT) != 0; 167 boolean controlDown = (modifiers & KeyEvent.MODIFIER_CONTROL) != 0; 168 boolean altDown = (modifiers & KeyEvent.MODIFIER_ALT) != 0; 169 boolean metaDown = (modifiers & KeyEvent.MODIFIER_WINDOWS) != 0; 170 171 String str = new String(chars); 172 String text = str; // TODO: this must be a text like "HOME", "F1", or "A" 173 174 javafx.scene.input.KeyEvent keyEvent = new javafx.scene.input.KeyEvent( 175 keyEventType(type), 176 str, text, 177 KeyCodeMap.valueOf(key) , 178 shiftDown, controlDown, altDown, metaDown); 179 180 switch (type) { 181 case com.sun.glass.events.KeyEvent.PRESS: 182 if (view.isInFullscreen() && stage != null) { 183 if (stage.getSavedFullScreenExitKey() != null 184 && stage.getSavedFullScreenExitKey().match(keyEvent)) { 185 stage.exitFullScreen(); 186 } 187 } 188 /* NOBREAK */ 189 case com.sun.glass.events.KeyEvent.RELEASE: 190 case com.sun.glass.events.KeyEvent.TYPED: 191 if (view.isInFullscreen()) { 192 if (!checkFullScreenKeyEvent(type, key, chars, modifiers)) { 193 break; 194 } 195 } 196 if (scene.sceneListener != null) { 197 scene.sceneListener.keyEvent(keyEvent); 198 } 199 break; 200 default: 201 if (QuantumToolkit.verbose) { 202 System.out.println("handleKeyEvent: unhandled type: " + type); 203 } 204 } 205 } finally { 206 if (stage != null) { 207 stage.setInEventHandler(false); 208 } 209 if (PULSE_LOGGING_ENABLED) { 210 PulseLogger.newInput(null); 211 } 212 } 213 return null; 214 } 215 } 216 217 @Override public void handleKeyEvent(View view, long time, int type, int key, 218 char[] chars, int modifiers) 219 { 220 keyNotification.view = view; 221 keyNotification.time = time; 222 keyNotification.type = type; 223 keyNotification.key = key; 224 keyNotification.chars = chars; 225 keyNotification.modifiers = modifiers; 226 227 QuantumToolkit.runWithoutRenderLock(() -> { 228 return AccessController.doPrivileged(keyNotification, scene.getAccessControlContext()); 229 }); 230 } 231 232 private static EventType<javafx.scene.input.MouseEvent> mouseEventType(int glassType) { 233 switch (glassType) { 234 case com.sun.glass.events.MouseEvent.DOWN: 235 return javafx.scene.input.MouseEvent.MOUSE_PRESSED; 236 case com.sun.glass.events.MouseEvent.UP: 237 return javafx.scene.input.MouseEvent.MOUSE_RELEASED; 238 case com.sun.glass.events.MouseEvent.ENTER: 239 return javafx.scene.input.MouseEvent.MOUSE_ENTERED; 240 case com.sun.glass.events.MouseEvent.EXIT: 241 return javafx.scene.input.MouseEvent.MOUSE_EXITED; 242 case com.sun.glass.events.MouseEvent.MOVE: 243 return javafx.scene.input.MouseEvent.MOUSE_MOVED; 244 case com.sun.glass.events.MouseEvent.DRAG: 245 return javafx.scene.input.MouseEvent.MOUSE_DRAGGED; 246 case com.sun.glass.events.MouseEvent.WHEEL: 247 throw new IllegalArgumentException("WHEEL event cannot " 248 + "be translated to MouseEvent, must be translated to " 249 + "ScrollEvent"); 250 default: 251 if (QuantumToolkit.verbose) { 252 System.err.println("Unknown Glass mouse event type: " + glassType); 253 } 254 return null; 255 } 256 } 257 258 private static MouseButton mouseEventButton(int glassButton) { 259 switch (glassButton) { 260 case com.sun.glass.events.MouseEvent.BUTTON_LEFT: 261 return MouseButton.PRIMARY; 262 case com.sun.glass.events.MouseEvent.BUTTON_RIGHT: 263 return MouseButton.SECONDARY; 264 case com.sun.glass.events.MouseEvent.BUTTON_OTHER: 265 return MouseButton.MIDDLE; 266 default: 267 return MouseButton.NONE; 268 } 269 } 270 271 // Used to determine whether a DRAG operation has been initiated on this window 272 private int mouseButtonPressedMask = 0; 273 274 private final MouseEventNotification mouseNotification = new MouseEventNotification(); 275 private class MouseEventNotification implements PrivilegedAction<Void> { 276 View view; 277 long time; 278 int type; 279 int button; 280 int x, y, xAbs, yAbs; 281 int modifiers; 282 boolean isPopupTrigger; 283 boolean isSynthesized; 284 285 @Override 286 public Void run() { 287 if (PULSE_LOGGING_ENABLED) { 288 PulseLogger.newInput(mouseEventType(type).toString()); 289 } 290 291 int buttonMask; 292 switch (button) { 293 case MouseEvent.BUTTON_LEFT: 294 buttonMask = KeyEvent.MODIFIER_BUTTON_PRIMARY; 295 break; 296 case MouseEvent.BUTTON_OTHER: 297 buttonMask = KeyEvent.MODIFIER_BUTTON_MIDDLE; 298 break; 299 case MouseEvent.BUTTON_RIGHT: 300 buttonMask = KeyEvent.MODIFIER_BUTTON_SECONDARY; 301 break; 302 default: 303 buttonMask = 0; 304 break; 305 } 306 307 switch (type) { 308 case MouseEvent.MOVE: 309 if (button != MouseEvent.BUTTON_NONE) { 310 //RT-11305: the drag hasn't been started on this window -- ignore the event 311 return null; 312 } 313 break; 314 case MouseEvent.UP: 315 if ((mouseButtonPressedMask & buttonMask) == 0) { 316 //RT-11305: the mouse button hasn't been pressed on this window -- ignore the event 317 return null; 318 } 319 mouseButtonPressedMask &= ~buttonMask; 320 break; 321 case MouseEvent.DOWN: 322 mouseButtonPressedMask |= buttonMask; 323 break; 324 case MouseEvent.ENTER: 325 case MouseEvent.EXIT: 326 break; 327 case MouseEvent.CLICK: 328 // Don't send click events to FX, as they are generated in Scene 329 return null; 330 default: 331 if (QuantumToolkit.verbose) { 332 System.out.println("handleMouseEvent: unhandled type: " + type); 333 } 334 } 335 336 WindowStage stage = scene.getWindowStage(); 337 try { 338 if (stage != null) { 339 stage.setInEventHandler(true); 340 } 341 if (scene.sceneListener != null) { 342 boolean shiftDown = (modifiers & KeyEvent.MODIFIER_SHIFT) != 0; 343 boolean controlDown = (modifiers & KeyEvent.MODIFIER_CONTROL) != 0; 344 boolean altDown = (modifiers & KeyEvent.MODIFIER_ALT) != 0; 345 boolean metaDown = (modifiers & KeyEvent.MODIFIER_WINDOWS) != 0; 346 boolean primaryButtonDown = (modifiers & KeyEvent.MODIFIER_BUTTON_PRIMARY) != 0; 347 boolean middleButtonDown = (modifiers & KeyEvent.MODIFIER_BUTTON_MIDDLE) != 0; 348 boolean secondaryButtonDown = (modifiers & KeyEvent.MODIFIER_BUTTON_SECONDARY) != 0; 349 final Window w = view.getWindow(); 350 double pScaleX = (w == null) ? 1.0 : w.getPlatformScaleX(); 351 double pScaleY = (w == null) ? 1.0 : w.getPlatformScaleY(); 352 353 scene.sceneListener.mouseEvent(mouseEventType(type), 354 x / pScaleX, y / pScaleY, xAbs / pScaleX, yAbs / pScaleY, 355 mouseEventButton(button), isPopupTrigger, isSynthesized, 356 shiftDown, controlDown, altDown, metaDown, 357 primaryButtonDown, middleButtonDown, secondaryButtonDown); 358 } 359 } finally { 360 if (stage != null) { 361 stage.setInEventHandler(false); 362 } 363 if (PULSE_LOGGING_ENABLED) { 364 PulseLogger.newInput(null); 365 } 366 } 367 return null; 368 } 369 } 370 371 @Override 372 public void handleMouseEvent(View view, long time, int type, int button, 373 int x, int y, int xAbs, int yAbs, 374 int modifiers, boolean isPopupTrigger, boolean isSynthesized) 375 { 376 mouseNotification.view = view; 377 mouseNotification.time = time; 378 mouseNotification.type = type; 379 mouseNotification.button = button; 380 mouseNotification.x = x; 381 mouseNotification.y = y; 382 mouseNotification.xAbs = xAbs; 383 mouseNotification.yAbs = yAbs; 384 mouseNotification.modifiers = modifiers; 385 mouseNotification.isPopupTrigger = isPopupTrigger; 386 mouseNotification.isSynthesized = isSynthesized; 387 388 QuantumToolkit.runWithoutRenderLock(() -> { 389 return AccessController.doPrivileged(mouseNotification, scene.getAccessControlContext()); 390 }); 391 } 392 393 @Override public void handleMenuEvent(final View view, 394 final int x, final int y, final int xAbs, final int yAbs, 395 final boolean isKeyboardTrigger) 396 { 397 if (PULSE_LOGGING_ENABLED) { 398 PulseLogger.newInput("MENU_EVENT"); 399 } 400 WindowStage stage = scene.getWindowStage(); 401 try { 402 if (stage != null) { 403 stage.setInEventHandler(true); 404 } 405 QuantumToolkit.runWithoutRenderLock(() -> { 406 return AccessController.doPrivileged((PrivilegedAction<Void>) () -> { 407 if (scene.sceneListener != null) { 408 final Window w = view.getWindow(); 409 double pScaleX = (w == null) ? 1.0 : w.getPlatformScaleX(); 410 double pScaleY = (w == null) ? 1.0 : w.getPlatformScaleY(); 411 scene.sceneListener.menuEvent(x / pScaleX, y / pScaleY, 412 xAbs / pScaleX, yAbs / pScaleY, 413 isKeyboardTrigger); 414 } 415 return null; 416 }, scene.getAccessControlContext()); 417 }); 418 } finally { 419 if (stage != null) { 420 stage.setInEventHandler(false); 421 } 422 if (PULSE_LOGGING_ENABLED) { 423 PulseLogger.newInput(null); 424 } 425 } 426 } 427 428 @Override public void handleScrollEvent(final View view, final long time, 429 final int x, final int y, final int xAbs, final int yAbs, 430 final double deltaX, final double deltaY, final int modifiers, 431 final int lines, final int chars, 432 final int defaultLines, final int defaultChars, 433 final double xMultiplier, final double yMultiplier) 434 { 435 if (PULSE_LOGGING_ENABLED) { 436 PulseLogger.newInput("SCROLL_EVENT"); 437 } 438 WindowStage stage = scene.getWindowStage(); 439 try { 440 if (stage != null) { 441 stage.setInEventHandler(true); 442 } 443 QuantumToolkit.runWithoutRenderLock(() -> { 444 return AccessController.doPrivileged((PrivilegedAction<Void>) () -> { 445 if (scene.sceneListener != null) { 446 final Window w = view.getWindow(); 447 double pScaleX = (w == null) ? 1.0 : w.getPlatformScaleX(); 448 double pScaleY = (w == null) ? 1.0 : w.getPlatformScaleY(); 449 scene.sceneListener.scrollEvent(ScrollEvent.SCROLL, 450 deltaX / pScaleX, deltaY / pScaleY, 0, 0, 451 xMultiplier, yMultiplier, 452 0, // touchCount 453 chars, lines, defaultChars, defaultLines, 454 x / pScaleX, y / pScaleY, xAbs / pScaleX, yAbs / pScaleY, 455 (modifiers & KeyEvent.MODIFIER_SHIFT) != 0, 456 (modifiers & KeyEvent.MODIFIER_CONTROL) != 0, 457 (modifiers & KeyEvent.MODIFIER_ALT) != 0, 458 (modifiers & KeyEvent.MODIFIER_WINDOWS) != 0, 459 false, // this is always indirect 460 false); // this has no inertia 461 } 462 return null; 463 }, scene.getAccessControlContext()); 464 }); 465 } finally { 466 if (stage != null) { 467 stage.setInEventHandler(false); 468 } 469 if (PULSE_LOGGING_ENABLED) { 470 PulseLogger.newInput(null); 471 } 472 } 473 } 474 475 private static byte inputMethodEventAttrValue(int pos, int[] attrBoundary, byte[] attrValue) { 476 if (attrBoundary != null) { 477 for (int current = 0; current < attrBoundary.length-1; current++) { 478 if (pos >= attrBoundary[current] && 479 pos < attrBoundary[current+1]) { 480 return attrValue[current]; 481 } 482 } 483 } 484 return View.IME_ATTR_INPUT_ERROR; 485 } 486 487 private static ObservableList<InputMethodTextRun> inputMethodEventComposed( 488 String text, int commitCount, int[] clauseBoundary, int[] attrBoundary, byte[] attrValue) 489 { 490 ObservableList<InputMethodTextRun> composed = new TrackableObservableList<InputMethodTextRun>() { 491 @Override 492 protected void onChanged(ListChangeListener.Change<InputMethodTextRun> c) { 493 } 494 }; 495 496 if (commitCount < text.length()) { 497 if (clauseBoundary == null) { 498 // Create one single segment as UNSELECTED_RAW 499 composed.add(new InputMethodTextRun( 500 text.substring(commitCount), 501 InputMethodHighlight.UNSELECTED_RAW)); 502 } else { 503 for (int current = 0; current < clauseBoundary.length-1; current++) { 504 if (clauseBoundary[current] < commitCount) { 505 continue; 506 } 507 508 InputMethodHighlight highlight; 509 switch (inputMethodEventAttrValue(clauseBoundary[current], attrBoundary, attrValue)) { 510 case View.IME_ATTR_TARGET_CONVERTED: 511 highlight = InputMethodHighlight.SELECTED_CONVERTED; 512 break; 513 case View.IME_ATTR_CONVERTED: 514 highlight = InputMethodHighlight.UNSELECTED_CONVERTED; 515 break; 516 case View.IME_ATTR_TARGET_NOTCONVERTED: 517 highlight = InputMethodHighlight.SELECTED_RAW; 518 break; 519 case View.IME_ATTR_INPUT: 520 case View.IME_ATTR_INPUT_ERROR: 521 default: 522 highlight = InputMethodHighlight.UNSELECTED_RAW; 523 break; 524 } 525 composed.add(new InputMethodTextRun( 526 text.substring(clauseBoundary[current], 527 clauseBoundary[current+1]), 528 highlight)); 529 } 530 } 531 } 532 return composed; 533 } 534 535 @Override public void handleInputMethodEvent(final long time, final String text, 536 final int[] clauseBoundary, 537 final int[] attrBoundary, final byte[] attrValue, 538 final int commitCount, final int cursorPos) 539 { 540 if (PULSE_LOGGING_ENABLED) { 541 PulseLogger.newInput("INPUT_METHOD_EVENT"); 542 } 543 WindowStage stage = scene.getWindowStage(); 544 try { 545 if (stage != null) { 546 stage.setInEventHandler(true); 547 } 548 QuantumToolkit.runWithoutRenderLock(() -> { 549 return AccessController.doPrivileged((PrivilegedAction<Void>) () -> { 550 if (scene.sceneListener != null) { 551 String t = text != null ? text : ""; 552 EventType<InputMethodEvent> eventType = 553 InputMethodEvent.INPUT_METHOD_TEXT_CHANGED; 554 ObservableList<InputMethodTextRun> composed = inputMethodEventComposed( 555 t, commitCount, clauseBoundary, attrBoundary, attrValue); 556 String committed = t.substring(0, commitCount); 557 scene.sceneListener.inputMethodEvent(eventType, composed, committed, cursorPos); 558 } 559 return null; 560 }, scene.getAccessControlContext()); 561 }); 562 } finally { 563 if (stage != null) { 564 stage.setInEventHandler(false); 565 } 566 if (PULSE_LOGGING_ENABLED) { 567 PulseLogger.newInput(null); 568 } 569 } 570 } 571 572 @Override 573 public double[] getInputMethodCandidatePos(int offset) { 574 Point2D p2d = scene.inputMethodRequests.getTextLocation(offset); 575 double[] ret = new double[2]; 576 ret[0] = p2d.getX(); 577 ret[1] = p2d.getY(); 578 return ret; 579 } 580 581 private static TransferMode actionToTransferMode(int dropActions) { 582 if (dropActions == Clipboard.ACTION_NONE) { 583 return null; 584 } else if (dropActions == Clipboard.ACTION_COPY 585 //IE drop action for URL copy; XXX: should be fixed in Glass 586 || dropActions == (Clipboard.ACTION_COPY | Clipboard.ACTION_REFERENCE) ) 587 { 588 return TransferMode.COPY; 589 } else if (dropActions == Clipboard.ACTION_MOVE 590 //IE drop action for URL move; XXX: should be fixed in Glass 591 || dropActions == (Clipboard.ACTION_MOVE | Clipboard.ACTION_REFERENCE) ) 592 { 593 return TransferMode.MOVE; 594 } else if (dropActions == Clipboard.ACTION_REFERENCE) { 595 return TransferMode.LINK; 596 } else if (dropActions == Clipboard.ACTION_COPY_OR_MOVE) { 597 if (QuantumToolkit.verbose) { 598 System.err.println("Ambiguous drop action: " + Integer.toHexString(dropActions)); 599 } 600 } else { 601 if (QuantumToolkit.verbose) { 602 System.err.println("Unknown drop action: " + Integer.toHexString(dropActions)); 603 } 604 } 605 return null; 606 } 607 608 private static int transferModeToAction(TransferMode tm) { 609 if (tm == null) { 610 return Clipboard.ACTION_NONE; 611 } 612 613 switch (tm) { 614 case COPY: 615 return Clipboard.ACTION_COPY; 616 case MOVE: 617 return Clipboard.ACTION_MOVE; 618 case LINK: 619 return Clipboard.ACTION_REFERENCE; 620 default: 621 return Clipboard.ACTION_NONE; 622 } 623 } 624 625 @Override public int handleDragEnter(View view, 626 final int x, final int y, final int xAbs, final int yAbs, 627 final int recommendedDropAction, 628 final ClipboardAssistance dropTargetAssistant) 629 { 630 if (PULSE_LOGGING_ENABLED) { 631 PulseLogger.newInput("DRAG_ENTER"); 632 } 633 TransferMode action; 634 try { 635 action = QuantumToolkit.runWithoutRenderLock(() -> { 636 return dndHandler.handleDragEnter(x, y, xAbs, yAbs, 637 actionToTransferMode(recommendedDropAction), 638 dropTargetAssistant); 639 }); 640 } finally { 641 if (PULSE_LOGGING_ENABLED) { 642 PulseLogger.newInput(null); 643 } 644 } 645 return transferModeToAction(action); 646 } 647 648 @Override public void handleDragLeave(View view, final ClipboardAssistance dropTargetAssistant) { 649 if (PULSE_LOGGING_ENABLED) { 650 PulseLogger.newInput("DRAG_LEAVE"); 651 } 652 try { 653 QuantumToolkit.runWithoutRenderLock(() -> { 654 dndHandler.handleDragLeave(dropTargetAssistant); 655 return null; 656 }); 657 } finally { 658 if (PULSE_LOGGING_ENABLED) { 659 PulseLogger.newInput(null); 660 } 661 } 662 } 663 664 @Override public int handleDragDrop(View view, 665 final int x, final int y, final int xAbs, final int yAbs, 666 final int recommendedDropAction, 667 final ClipboardAssistance dropTargetAssistant) 668 { 669 if (PULSE_LOGGING_ENABLED) { 670 PulseLogger.newInput("DRAG_DROP"); 671 } 672 TransferMode action; 673 try { 674 action = QuantumToolkit.runWithoutRenderLock(() -> { 675 return dndHandler.handleDragDrop(x, y, xAbs, yAbs, 676 actionToTransferMode(recommendedDropAction), 677 dropTargetAssistant); 678 }); 679 } finally { 680 if (PULSE_LOGGING_ENABLED) { 681 PulseLogger.newInput(null); 682 } 683 } 684 return transferModeToAction(action); 685 } 686 687 @Override public int handleDragOver(View view, 688 final int x, final int y, final int xAbs, final int yAbs, 689 final int recommendedDropAction, 690 final ClipboardAssistance dropTargetAssistant) 691 { 692 if (PULSE_LOGGING_ENABLED) { 693 PulseLogger.newInput("DRAG_OVER"); 694 } 695 TransferMode action; 696 try { 697 action = QuantumToolkit.runWithoutRenderLock(() -> { 698 return dndHandler.handleDragOver(x, y, xAbs, yAbs, 699 actionToTransferMode(recommendedDropAction), 700 dropTargetAssistant); 701 }); 702 } finally { 703 if (PULSE_LOGGING_ENABLED) { 704 PulseLogger.newInput(null); 705 } 706 } 707 return transferModeToAction(action); 708 } 709 710 private ClipboardAssistance dropSourceAssistant; 711 712 @Override public void handleDragStart(View view, final int button, 713 final int x, final int y, final int xAbs, final int yAbs, 714 final ClipboardAssistance assistant) 715 { 716 if (PULSE_LOGGING_ENABLED) { 717 PulseLogger.newInput("DRAG_START"); 718 } 719 dropSourceAssistant = assistant; 720 try { 721 QuantumToolkit.runWithoutRenderLock(() -> { 722 dndHandler.handleDragStart(button, x, y, xAbs, yAbs, assistant); 723 return null; 724 }); 725 } finally { 726 if (PULSE_LOGGING_ENABLED) { 727 PulseLogger.newInput(null); 728 } 729 } 730 } 731 732 @Override public void handleDragEnd(View view, final int performedAction) { 733 if (PULSE_LOGGING_ENABLED) { 734 PulseLogger.newInput("DRAG_END"); 735 } 736 try { 737 QuantumToolkit.runWithoutRenderLock(() -> { 738 dndHandler.handleDragEnd(actionToTransferMode(performedAction), dropSourceAssistant); 739 return null; 740 }); 741 } finally { 742 if (PULSE_LOGGING_ENABLED) { 743 PulseLogger.newInput(null); 744 } 745 } 746 } 747 748 // TODO - dropTargetListener.dropActionChanged 749 // TODO - dragSourceListener.dropActionChanged 750 751 private final ViewEventNotification viewNotification = new ViewEventNotification(); 752 private class ViewEventNotification implements PrivilegedAction<Void> { 753 View view; 754 long time; 755 int type; 756 757 @Override 758 public Void run() { 759 if (scene.sceneListener == null) { 760 return null; 761 } 762 switch (type) { 763 case ViewEvent.REPAINT: { 764 Window w = view.getWindow(); 765 if (w != null && w.getMinimumWidth() == view.getWidth() && !w.isVisible()) { 766 // RT-21057 - ignore initial minimum size setting if not visible 767 break; 768 } 769 if (QuantumToolkit.drawInPaint && w != null && w.isVisible()) { 770 WindowStage stage = scene.getWindowStage(); 771 if (stage != null && !stage.isApplet()) { 772 collector.liveRepaintRenderJob(scene); 773 } 774 } 775 scene.entireSceneNeedsRepaint(); 776 break; 777 } 778 case ViewEvent.RESIZE: { 779 final Window w = view.getWindow(); 780 float pScaleX = (w == null) ? 1.0f : w.getPlatformScaleX(); 781 float pScaleY = (w == null) ? 1.0f : w.getPlatformScaleY(); 782 scene.sceneListener.changedSize(view.getWidth() / pScaleX, 783 view.getHeight() / pScaleY); 784 scene.entireSceneNeedsRepaint(); 785 QuantumToolkit.runWithRenderLock(() -> { 786 scene.updateSceneState(); 787 return null; 788 }); 789 if (QuantumToolkit.liveResize && w != null && w.isVisible()) { 790 WindowStage stage = scene.getWindowStage(); 791 if (stage != null && !stage.isApplet()) { 792 collector.liveRepaintRenderJob(scene); 793 } 794 } 795 break; 796 } 797 case ViewEvent.MOVE: { 798 // MOVE events can be "synthesized" and the window will 799 // be null if this is synthesized during a "REMOVE" event 800 final Window w = view.getWindow(); 801 float pScaleX = (w == null) ? 1.0f : w.getPlatformScaleX(); 802 float pScaleY = (w == null) ? 1.0f : w.getPlatformScaleY(); 803 scene.sceneListener.changedLocation(view.getX() / pScaleX, 804 view.getY() / pScaleY); 805 break; 806 } 807 case ViewEvent.FULLSCREEN_ENTER: 808 case ViewEvent.FULLSCREEN_EXIT: 809 if (scene.getWindowStage() != null) { 810 scene.getWindowStage().fullscreenChanged(type == ViewEvent.FULLSCREEN_ENTER); 811 } 812 break; 813 case ViewEvent.ADD: 814 case ViewEvent.REMOVE: 815 // unhandled 816 break; 817 default: 818 throw new RuntimeException("handleViewEvent: unhandled type: " + type); 819 } 820 return null; 821 } 822 } 823 824 @Override public void handleViewEvent(View view, long time, final int type) { 825 if (PULSE_LOGGING_ENABLED) { 826 PulseLogger.newInput("VIEW_EVENT: "+ViewEvent.getTypeString(type)); 827 } 828 viewNotification.view = view; 829 viewNotification.time = time; 830 viewNotification.type = type; 831 try { 832 QuantumToolkit.runWithoutRenderLock(() -> { 833 return AccessController.doPrivileged(viewNotification, scene.getAccessControlContext()); 834 }); 835 } 836 finally { 837 if (PULSE_LOGGING_ENABLED) { 838 PulseLogger.newInput(null); 839 } 840 } 841 } 842 843 @Override public void handleScrollGestureEvent( 844 View view, final long time, final int type, 845 final int modifiers, final boolean isDirect, final boolean isInertia, final int touchCount, 846 final int x, final int y, final int xAbs, final int yAbs, final double dx, final double dy, 847 final double totaldx, final double totaldy, final double multiplierX, final double multiplierY) 848 { 849 if (PULSE_LOGGING_ENABLED) { 850 PulseLogger.newInput("SCROLL_GESTURE_EVENT"); 851 } 852 WindowStage stage = scene.getWindowStage(); 853 try { 854 if (stage != null) { 855 stage.setInEventHandler(true); 856 } 857 QuantumToolkit.runWithoutRenderLock(() -> { 858 return AccessController.doPrivileged((PrivilegedAction<Void>) () -> { 859 if (scene.sceneListener != null) { 860 EventType<ScrollEvent> eventType; 861 switch(type) { 862 case GestureEvent.GESTURE_STARTED: 863 eventType = ScrollEvent.SCROLL_STARTED; 864 break; 865 case GestureEvent.GESTURE_PERFORMED: 866 eventType = ScrollEvent.SCROLL; 867 break; 868 case GestureEvent.GESTURE_FINISHED: 869 eventType = ScrollEvent.SCROLL_FINISHED; 870 break; 871 default: 872 throw new RuntimeException("Unknown scroll event type: " + type); 873 } 874 final Window w = view.getWindow(); 875 double pScaleX = (w == null) ? 1.0 : w.getPlatformScaleX(); 876 double pScaleY = (w == null) ? 1.0 : w.getPlatformScaleY(); 877 scene.sceneListener.scrollEvent(eventType, 878 dx / pScaleX, dy / pScaleY, totaldx / pScaleX, totaldy / pScaleY, 879 multiplierX, multiplierY, 880 touchCount, 881 0, 0, 0, 0, 882 x == View.GESTURE_NO_VALUE ? Double.NaN : x / pScaleX, 883 y == View.GESTURE_NO_VALUE ? Double.NaN : y / pScaleY, 884 xAbs == View.GESTURE_NO_VALUE ? Double.NaN : xAbs / pScaleX, 885 yAbs == View.GESTURE_NO_VALUE ? Double.NaN : yAbs / pScaleY, 886 (modifiers & KeyEvent.MODIFIER_SHIFT) != 0, 887 (modifiers & KeyEvent.MODIFIER_CONTROL) != 0, 888 (modifiers & KeyEvent.MODIFIER_ALT) != 0, 889 (modifiers & KeyEvent.MODIFIER_WINDOWS) != 0, 890 isDirect, isInertia); 891 } 892 return null; 893 }, scene.getAccessControlContext()); 894 }); 895 } finally { 896 if (stage != null) { 897 stage.setInEventHandler(false); 898 } 899 if (PULSE_LOGGING_ENABLED) { 900 PulseLogger.newInput(null); 901 } 902 } 903 } 904 905 @Override public void handleZoomGestureEvent( 906 View view, final long time, final int type, 907 final int modifiers, final boolean isDirect, final boolean isInertia, 908 final int originx, final int originy, 909 final int originxAbs, final int originyAbs, 910 final double scale, double expansion, 911 final double totalscale, double totalexpansion) 912 { 913 if (PULSE_LOGGING_ENABLED) { 914 PulseLogger.newInput("ZOOM_GESTURE_EVENT"); 915 } 916 WindowStage stage = scene.getWindowStage(); 917 try { 918 if (stage != null) { 919 stage.setInEventHandler(true); 920 } 921 QuantumToolkit.runWithoutRenderLock(() -> { 922 return AccessController.doPrivileged((PrivilegedAction<Void>) () -> { 923 if (scene.sceneListener != null) { 924 EventType<ZoomEvent> eventType; 925 switch (type) { 926 case GestureEvent.GESTURE_STARTED: 927 eventType = ZoomEvent.ZOOM_STARTED; 928 break; 929 case GestureEvent.GESTURE_PERFORMED: 930 eventType = ZoomEvent.ZOOM; 931 break; 932 case GestureEvent.GESTURE_FINISHED: 933 eventType = ZoomEvent.ZOOM_FINISHED; 934 break; 935 default: 936 throw new RuntimeException("Unknown scroll event type: " + type); 937 } 938 final Window w = view.getWindow(); 939 double pScaleX = (w == null) ? 1.0 : w.getPlatformScaleX(); 940 double pScaleY = (w == null) ? 1.0 : w.getPlatformScaleY(); 941 // REMIND: Scale the [total]scale params too? 942 scene.sceneListener.zoomEvent(eventType, scale, totalscale, 943 originx == View.GESTURE_NO_VALUE ? Double.NaN : originx / pScaleX, 944 originy == View.GESTURE_NO_VALUE ? Double.NaN : originy / pScaleY, 945 originxAbs == View.GESTURE_NO_VALUE ? Double.NaN : originxAbs / pScaleX, 946 originyAbs == View.GESTURE_NO_VALUE ? Double.NaN : originyAbs / pScaleY, 947 (modifiers & KeyEvent.MODIFIER_SHIFT) != 0, 948 (modifiers & KeyEvent.MODIFIER_CONTROL) != 0, 949 (modifiers & KeyEvent.MODIFIER_ALT) != 0, 950 (modifiers & KeyEvent.MODIFIER_WINDOWS) != 0, 951 isDirect, isInertia); 952 } 953 return null; 954 }, scene.getAccessControlContext()); 955 }); 956 } finally { 957 if (stage != null) { 958 stage.setInEventHandler(false); 959 } 960 if (PULSE_LOGGING_ENABLED) { 961 PulseLogger.newInput(null); 962 } 963 } 964 } 965 966 @Override public void handleRotateGestureEvent( 967 View view, final long time, final int type, 968 final int modifiers, final boolean isDirect, final boolean isInertia, 969 final int originx, final int originy, 970 final int originxAbs, final int originyAbs, 971 final double dangle, final double totalangle) 972 { 973 if (PULSE_LOGGING_ENABLED) { 974 PulseLogger.newInput("ROTATE_GESTURE_EVENT"); 975 } 976 WindowStage stage = scene.getWindowStage(); 977 try { 978 if (stage != null) { 979 stage.setInEventHandler(true); 980 } 981 QuantumToolkit.runWithoutRenderLock(() -> { 982 return AccessController.doPrivileged((PrivilegedAction<Void>) () -> { 983 if (scene.sceneListener != null) { 984 EventType<RotateEvent> eventType; 985 switch (type) { 986 case GestureEvent.GESTURE_STARTED: 987 eventType = RotateEvent.ROTATION_STARTED; 988 break; 989 case GestureEvent.GESTURE_PERFORMED: 990 eventType = RotateEvent.ROTATE; 991 break; 992 case GestureEvent.GESTURE_FINISHED: 993 eventType = RotateEvent.ROTATION_FINISHED; 994 break; 995 default: 996 throw new RuntimeException("Unknown scroll event type: " + type); 997 } 998 final Window w = view.getWindow(); 999 double pScaleX = (w == null) ? 1.0 : w.getPlatformScaleX(); 1000 double pScaleY = (w == null) ? 1.0 : w.getPlatformScaleY(); 1001 scene.sceneListener.rotateEvent(eventType, dangle, totalangle, 1002 originx == View.GESTURE_NO_VALUE ? Double.NaN : originx / pScaleX, 1003 originy == View.GESTURE_NO_VALUE ? Double.NaN : originy / pScaleY, 1004 originxAbs == View.GESTURE_NO_VALUE ? Double.NaN : originxAbs / pScaleX, 1005 originyAbs == View.GESTURE_NO_VALUE ? Double.NaN : originyAbs / pScaleY, 1006 (modifiers & KeyEvent.MODIFIER_SHIFT) != 0, 1007 (modifiers & KeyEvent.MODIFIER_CONTROL) != 0, 1008 (modifiers & KeyEvent.MODIFIER_ALT) != 0, 1009 (modifiers & KeyEvent.MODIFIER_WINDOWS) != 0, 1010 isDirect, isInertia); 1011 } 1012 return null; 1013 }, scene.getAccessControlContext()); 1014 }); 1015 } finally { 1016 if (stage != null) { 1017 stage.setInEventHandler(false); 1018 } 1019 if (PULSE_LOGGING_ENABLED) { 1020 PulseLogger.newInput(null); 1021 } 1022 } 1023 } 1024 1025 @Override public void handleSwipeGestureEvent( 1026 View view, final long time, int type, 1027 final int modifiers, final boolean isDirect, 1028 boolean isInertia, final int touchCount, 1029 final int dir, final int x, final int y, final int xAbs, final int yAbs) 1030 { 1031 if (PULSE_LOGGING_ENABLED) { 1032 PulseLogger.newInput("SWIPE_EVENT"); 1033 } 1034 WindowStage stage = scene.getWindowStage(); 1035 try { 1036 if (stage != null) { 1037 stage.setInEventHandler(true); 1038 } 1039 QuantumToolkit.runWithoutRenderLock(() -> { 1040 return AccessController.doPrivileged((PrivilegedAction<Void>) () -> { 1041 if (scene.sceneListener != null) { 1042 EventType<SwipeEvent> eventType; 1043 switch (dir) { 1044 case SwipeGesture.DIR_UP: 1045 eventType = SwipeEvent.SWIPE_UP; 1046 break; 1047 case SwipeGesture.DIR_DOWN: 1048 eventType = SwipeEvent.SWIPE_DOWN; 1049 break; 1050 case SwipeGesture.DIR_LEFT: 1051 eventType = SwipeEvent.SWIPE_LEFT; 1052 break; 1053 case SwipeGesture.DIR_RIGHT: 1054 eventType = SwipeEvent.SWIPE_RIGHT; 1055 break; 1056 default: 1057 throw new RuntimeException("Unknown swipe event direction: " + dir); 1058 } 1059 final Window w = view.getWindow(); 1060 double pScaleX = (w == null) ? 1.0 : w.getPlatformScaleX(); 1061 double pScaleY = (w == null) ? 1.0 : w.getPlatformScaleY(); 1062 scene.sceneListener.swipeEvent(eventType, touchCount, 1063 x == View.GESTURE_NO_VALUE ? Double.NaN : x / pScaleX, 1064 y == View.GESTURE_NO_VALUE ? Double.NaN : y / pScaleY, 1065 xAbs == View.GESTURE_NO_VALUE ? Double.NaN : xAbs / pScaleX, 1066 yAbs == View.GESTURE_NO_VALUE ? Double.NaN : yAbs / pScaleY, 1067 (modifiers & KeyEvent.MODIFIER_SHIFT) != 0, 1068 (modifiers & KeyEvent.MODIFIER_CONTROL) != 0, 1069 (modifiers & KeyEvent.MODIFIER_ALT) != 0, 1070 (modifiers & KeyEvent.MODIFIER_WINDOWS) != 0, 1071 isDirect); 1072 } 1073 return null; 1074 }, scene.getAccessControlContext()); 1075 }); 1076 } finally { 1077 if (stage != null) { 1078 stage.setInEventHandler(false); 1079 } 1080 if (PULSE_LOGGING_ENABLED) { 1081 PulseLogger.newInput(null); 1082 } 1083 } 1084 } 1085 1086 @Override public void handleBeginTouchEvent( 1087 View view, final long time, final int modifiers, 1088 final boolean isDirect, final int touchEventCount) 1089 { 1090 if (PULSE_LOGGING_ENABLED) { 1091 PulseLogger.newInput("BEGIN_TOUCH_EVENT"); 1092 } 1093 WindowStage stage = scene.getWindowStage(); 1094 try { 1095 if (stage != null) { 1096 stage.setInEventHandler(true); 1097 } 1098 QuantumToolkit.runWithoutRenderLock(() -> { 1099 return AccessController.doPrivileged((PrivilegedAction<Void>) () -> { 1100 if (scene.sceneListener != null) { 1101 scene.sceneListener.touchEventBegin(time, touchEventCount, 1102 isDirect, 1103 (modifiers & KeyEvent.MODIFIER_SHIFT) != 0, 1104 (modifiers & KeyEvent.MODIFIER_CONTROL) != 0, 1105 (modifiers & KeyEvent.MODIFIER_ALT) != 0, 1106 (modifiers & KeyEvent.MODIFIER_WINDOWS) != 0); 1107 } 1108 return null; 1109 }, scene.getAccessControlContext()); 1110 }); 1111 } finally { 1112 if (stage != null) { 1113 stage.setInEventHandler(false); 1114 } 1115 if (PULSE_LOGGING_ENABLED) { 1116 PulseLogger.newInput(null); 1117 } 1118 } 1119 1120 gestures.notifyBeginTouchEvent(time, modifiers, isDirect, touchEventCount); 1121 } 1122 1123 @Override public void handleNextTouchEvent( 1124 View view, final long time, final int type, final long touchId, 1125 final int x, final int y, final int xAbs, final int yAbs) 1126 { 1127 if (PULSE_LOGGING_ENABLED) { 1128 PulseLogger.newInput("NEXT_TOUCH_EVENT"); 1129 } 1130 WindowStage stage = scene.getWindowStage(); 1131 try { 1132 if (stage != null) { 1133 stage.setInEventHandler(true); 1134 } 1135 QuantumToolkit.runWithoutRenderLock(() -> { 1136 return AccessController.doPrivileged((PrivilegedAction<Void>) () -> { 1137 if (scene.sceneListener != null) { 1138 TouchPoint.State state; 1139 switch (type) { 1140 case TouchEvent.TOUCH_PRESSED: 1141 state = TouchPoint.State.PRESSED; 1142 break; 1143 case TouchEvent.TOUCH_MOVED: 1144 state = TouchPoint.State.MOVED; 1145 break; 1146 case TouchEvent.TOUCH_STILL: 1147 state = TouchPoint.State.STATIONARY; 1148 break; 1149 case TouchEvent.TOUCH_RELEASED: 1150 state = TouchPoint.State.RELEASED; 1151 break; 1152 default: 1153 throw new RuntimeException("Unknown touch state: " + type); 1154 } 1155 final Window w = view.getWindow(); 1156 double pScaleX = (w == null) ? 1.0 : w.getPlatformScaleX(); 1157 double pScaleY = (w == null) ? 1.0 : w.getPlatformScaleY(); 1158 scene.sceneListener.touchEventNext(state, touchId, 1159 x / pScaleX, y / pScaleY, xAbs / pScaleX, yAbs / pScaleY); 1160 } 1161 return null; 1162 }, scene.getAccessControlContext()); 1163 }); 1164 } finally { 1165 if (stage != null) { 1166 stage.setInEventHandler(false); 1167 } 1168 if (PULSE_LOGGING_ENABLED) { 1169 PulseLogger.newInput(null); 1170 } 1171 } 1172 1173 gestures.notifyNextTouchEvent(time, type, touchId, x, y, xAbs, yAbs); 1174 } 1175 1176 @Override public void handleEndTouchEvent(View view, long time) { 1177 if (PULSE_LOGGING_ENABLED) { 1178 PulseLogger.newInput("END_TOUCH_EVENT"); 1179 } 1180 WindowStage stage = scene.getWindowStage(); 1181 try { 1182 if (stage != null) { 1183 stage.setInEventHandler(true); 1184 } 1185 QuantumToolkit.runWithoutRenderLock(() -> { 1186 return AccessController.doPrivileged((PrivilegedAction<Void>) () -> { 1187 if (scene.sceneListener != null) { 1188 scene.sceneListener.touchEventEnd(); 1189 } 1190 return null; 1191 }, scene.getAccessControlContext()); 1192 }); 1193 } finally { 1194 if (stage != null) { 1195 stage.setInEventHandler(false); 1196 } 1197 if (PULSE_LOGGING_ENABLED) { 1198 PulseLogger.newInput(null); 1199 } 1200 } 1201 1202 gestures.notifyEndTouchEvent(time); 1203 } 1204 1205 @Override 1206 public Accessible getSceneAccessible() { 1207 if (scene != null && scene.sceneListener != null) { 1208 return scene.sceneListener.getSceneAccessible(); 1209 } 1210 return null; 1211 } 1212 }