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