1 /* 2 * Copyright (c) 2010, 2014, 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 350 scene.sceneListener.mouseEvent(mouseEventType(type), x, y, xAbs, yAbs, 351 mouseEventButton(button), isPopupTrigger, isSynthesized, 352 shiftDown, controlDown, altDown, metaDown, 353 primaryButtonDown, middleButtonDown, secondaryButtonDown); 354 } 355 } finally { 356 if (stage != null) { 357 stage.setInEventHandler(false); 358 } 359 if (PULSE_LOGGING_ENABLED) { 360 PulseLogger.newInput(null); 361 } 362 } 363 return null; 364 } 365 } 366 367 @Override 368 public void handleMouseEvent(View view, long time, int type, int button, 369 int x, int y, int xAbs, int yAbs, 370 int modifiers, boolean isPopupTrigger, boolean isSynthesized) 371 { 372 mouseNotification.view = view; 373 mouseNotification.time = time; 374 mouseNotification.type = type; 375 mouseNotification.button = button; 376 mouseNotification.x = x; 377 mouseNotification.y = y; 378 mouseNotification.xAbs = xAbs; 379 mouseNotification.yAbs = yAbs; 380 mouseNotification.modifiers = modifiers; 381 mouseNotification.isPopupTrigger = isPopupTrigger; 382 mouseNotification.isSynthesized = isSynthesized; 383 384 QuantumToolkit.runWithoutRenderLock(() -> { 385 return AccessController.doPrivileged(mouseNotification, scene.getAccessControlContext()); 386 }); 387 } 388 389 @Override public void handleMenuEvent(final View view, 390 final int x, final int y, final int xAbs, final int yAbs, 391 final boolean isKeyboardTrigger) 392 { 393 if (PULSE_LOGGING_ENABLED) { 394 PulseLogger.newInput("MENU_EVENT"); 395 } 396 WindowStage stage = scene.getWindowStage(); 397 try { 398 if (stage != null) { 399 stage.setInEventHandler(true); 400 } 401 QuantumToolkit.runWithoutRenderLock(() -> { 402 return AccessController.doPrivileged((PrivilegedAction<Void>) () -> { 403 if (scene.sceneListener != null) { 404 scene.sceneListener.menuEvent(x, y, xAbs, yAbs, isKeyboardTrigger); 405 } 406 return null; 407 }, scene.getAccessControlContext()); 408 }); 409 } finally { 410 if (stage != null) { 411 stage.setInEventHandler(false); 412 } 413 if (PULSE_LOGGING_ENABLED) { 414 PulseLogger.newInput(null); 415 } 416 } 417 } 418 419 @Override public void handleScrollEvent(final View view, final long time, 420 final int x, final int y, final int xAbs, final int yAbs, 421 final double deltaX, final double deltaY, final int modifiers, 422 final int lines, final int chars, 423 final int defaultLines, final int defaultChars, 424 final double xMultiplier, final double yMultiplier) 425 { 426 if (PULSE_LOGGING_ENABLED) { 427 PulseLogger.newInput("SCROLL_EVENT"); 428 } 429 WindowStage stage = scene.getWindowStage(); 430 try { 431 if (stage != null) { 432 stage.setInEventHandler(true); 433 } 434 QuantumToolkit.runWithoutRenderLock(() -> { 435 return AccessController.doPrivileged((PrivilegedAction<Void>) () -> { 436 if (scene.sceneListener != null) { 437 scene.sceneListener.scrollEvent(ScrollEvent.SCROLL, 438 deltaX, deltaY, 0, 0, 439 xMultiplier, yMultiplier, 440 0, // touchCount 441 chars, lines, defaultChars, defaultLines, 442 x, y, xAbs, yAbs, 443 (modifiers & KeyEvent.MODIFIER_SHIFT) != 0, 444 (modifiers & KeyEvent.MODIFIER_CONTROL) != 0, 445 (modifiers & KeyEvent.MODIFIER_ALT) != 0, 446 (modifiers & KeyEvent.MODIFIER_WINDOWS) != 0, 447 false, // this is always indirect 448 false); // this has no inertia 449 } 450 return null; 451 }, scene.getAccessControlContext()); 452 }); 453 } finally { 454 if (stage != null) { 455 stage.setInEventHandler(false); 456 } 457 if (PULSE_LOGGING_ENABLED) { 458 PulseLogger.newInput(null); 459 } 460 } 461 } 462 463 // TODO: Define these somewhere else. It is Windows specific for now. 464 private final static byte ATTR_INPUT = 0x00; 465 private final static byte ATTR_TARGET_CONVERTED = 0x01; 466 private final static byte ATTR_CONVERTED = 0x02; 467 private final static byte ATTR_TARGET_NOTCONVERTED = 0x03; 468 private final static byte ATTR_INPUT_ERROR = 0x04; 469 470 private static byte inputMethodEventAttrValue(int pos, int[] attrBoundary, byte[] attrValue) { 471 if (attrBoundary != null) { 472 for (int current = 0; current < attrBoundary.length-1; current++) { 473 if (pos >= attrBoundary[current] && 474 pos < attrBoundary[current+1]) { 475 return attrValue[current]; 476 } 477 } 478 } 479 return ATTR_INPUT_ERROR; 480 } 481 482 private static ObservableList<InputMethodTextRun> inputMethodEventComposed( 483 String text, int commitCount, int[] clauseBoundary, int[] attrBoundary, byte[] attrValue) 484 { 485 ObservableList<InputMethodTextRun> composed = new TrackableObservableList<InputMethodTextRun>() { 486 @Override 487 protected void onChanged(ListChangeListener.Change<InputMethodTextRun> c) { 488 } 489 }; 490 491 if (commitCount < text.length()) { 492 if (clauseBoundary == null) { 493 // Create one single segment as UNSELECTED_RAW 494 composed.add(new InputMethodTextRun( 495 text.substring(commitCount), 496 InputMethodHighlight.UNSELECTED_RAW)); 497 } else { 498 for (int current = 0; current < clauseBoundary.length-1; current++) { 499 if (clauseBoundary[current] < commitCount) { 500 continue; 501 } 502 503 InputMethodHighlight highlight; 504 switch (inputMethodEventAttrValue(clauseBoundary[current], attrBoundary, attrValue)) { 505 case ATTR_TARGET_CONVERTED: 506 highlight = InputMethodHighlight.SELECTED_CONVERTED; 507 break; 508 case ATTR_CONVERTED: 509 highlight = InputMethodHighlight.UNSELECTED_CONVERTED; 510 break; 511 case ATTR_TARGET_NOTCONVERTED: 512 highlight = InputMethodHighlight.SELECTED_RAW; 513 break; 514 case ATTR_INPUT: 515 case ATTR_INPUT_ERROR: 516 default: 517 highlight = InputMethodHighlight.UNSELECTED_RAW; 518 break; 519 } 520 composed.add(new InputMethodTextRun( 521 text.substring(clauseBoundary[current], 522 clauseBoundary[current+1]), 523 highlight)); 524 } 525 } 526 } 527 return composed; 528 } 529 530 @Override public void handleInputMethodEvent(final long time, final String text, 531 final int[] clauseBoundary, 532 final int[] attrBoundary, final byte[] attrValue, 533 final int commitCount, final int cursorPos) 534 { 535 if (PULSE_LOGGING_ENABLED) { 536 PulseLogger.newInput("INPUT_METHOD_EVENT"); 537 } 538 WindowStage stage = scene.getWindowStage(); 539 try { 540 if (stage != null) { 541 stage.setInEventHandler(true); 542 } 543 QuantumToolkit.runWithoutRenderLock(() -> { 544 return AccessController.doPrivileged((PrivilegedAction<Void>) () -> { 545 if (scene.sceneListener != null) { 546 String t = text != null ? text : ""; 547 EventType<InputMethodEvent> eventType = 548 InputMethodEvent.INPUT_METHOD_TEXT_CHANGED; 549 ObservableList<InputMethodTextRun> composed = inputMethodEventComposed( 550 t, commitCount, clauseBoundary, attrBoundary, attrValue); 551 String committed = t.substring(0, commitCount); 552 scene.sceneListener.inputMethodEvent(eventType, composed, committed, cursorPos); 553 } 554 return null; 555 }, scene.getAccessControlContext()); 556 }); 557 } finally { 558 if (stage != null) { 559 stage.setInEventHandler(false); 560 } 561 if (PULSE_LOGGING_ENABLED) { 562 PulseLogger.newInput(null); 563 } 564 } 565 } 566 567 @Override 568 public double[] getInputMethodCandidatePos(int offset) { 569 Point2D p2d = scene.inputMethodRequests.getTextLocation(offset); 570 double[] ret = new double[2]; 571 ret[0] = p2d.getX(); 572 ret[1] = p2d.getY(); 573 return ret; 574 } 575 576 private static TransferMode actionToTransferMode(int dropActions) { 577 if (dropActions == Clipboard.ACTION_NONE) { 578 return null; 579 } else if (dropActions == Clipboard.ACTION_COPY 580 //IE drop action for URL copy; XXX: should be fixed in Glass 581 || dropActions == (Clipboard.ACTION_COPY | Clipboard.ACTION_REFERENCE) ) 582 { 583 return TransferMode.COPY; 584 } else if (dropActions == Clipboard.ACTION_MOVE 585 //IE drop action for URL move; XXX: should be fixed in Glass 586 || dropActions == (Clipboard.ACTION_MOVE | Clipboard.ACTION_REFERENCE) ) 587 { 588 return TransferMode.MOVE; 589 } else if (dropActions == Clipboard.ACTION_REFERENCE) { 590 return TransferMode.LINK; 591 } else if (dropActions == Clipboard.ACTION_COPY_OR_MOVE) { 592 if (QuantumToolkit.verbose) { 593 System.err.println("Ambiguous drop action: " + Integer.toHexString(dropActions)); 594 } 595 } else { 596 if (QuantumToolkit.verbose) { 597 System.err.println("Unknown drop action: " + Integer.toHexString(dropActions)); 598 } 599 } 600 return null; 601 } 602 603 private static int transferModeToAction(TransferMode tm) { 604 if (tm == null) { 605 return Clipboard.ACTION_NONE; 606 } 607 608 switch (tm) { 609 case COPY: 610 return Clipboard.ACTION_COPY; 611 case MOVE: 612 return Clipboard.ACTION_MOVE; 613 case LINK: 614 return Clipboard.ACTION_REFERENCE; 615 default: 616 return Clipboard.ACTION_NONE; 617 } 618 } 619 620 @Override public int handleDragEnter(View view, 621 final int x, final int y, final int xAbs, final int yAbs, 622 final int recommendedDropAction, 623 final ClipboardAssistance dropTargetAssistant) 624 { 625 if (PULSE_LOGGING_ENABLED) { 626 PulseLogger.newInput("DRAG_ENTER"); 627 } 628 TransferMode action; 629 try { 630 action = QuantumToolkit.runWithoutRenderLock(() -> { 631 return dndHandler.handleDragEnter(x, y, xAbs, yAbs, 632 actionToTransferMode(recommendedDropAction), 633 dropTargetAssistant); 634 }); 635 } finally { 636 if (PULSE_LOGGING_ENABLED) { 637 PulseLogger.newInput(null); 638 } 639 } 640 return transferModeToAction(action); 641 } 642 643 @Override public void handleDragLeave(View view, final ClipboardAssistance dropTargetAssistant) { 644 if (PULSE_LOGGING_ENABLED) { 645 PulseLogger.newInput("DRAG_LEAVE"); 646 } 647 try { 648 QuantumToolkit.runWithoutRenderLock(() -> { 649 dndHandler.handleDragLeave(dropTargetAssistant); 650 return null; 651 }); 652 } finally { 653 if (PULSE_LOGGING_ENABLED) { 654 PulseLogger.newInput(null); 655 } 656 } 657 } 658 659 @Override public int handleDragDrop(View view, 660 final int x, final int y, final int xAbs, final int yAbs, 661 final int recommendedDropAction, 662 final ClipboardAssistance dropTargetAssistant) 663 { 664 if (PULSE_LOGGING_ENABLED) { 665 PulseLogger.newInput("DRAG_DROP"); 666 } 667 TransferMode action; 668 try { 669 action = QuantumToolkit.runWithoutRenderLock(() -> { 670 return dndHandler.handleDragDrop(x, y, xAbs, yAbs, 671 actionToTransferMode(recommendedDropAction), 672 dropTargetAssistant); 673 }); 674 } finally { 675 if (PULSE_LOGGING_ENABLED) { 676 PulseLogger.newInput(null); 677 } 678 } 679 return transferModeToAction(action); 680 } 681 682 @Override public int handleDragOver(View view, 683 final int x, final int y, final int xAbs, final int yAbs, 684 final int recommendedDropAction, 685 final ClipboardAssistance dropTargetAssistant) 686 { 687 if (PULSE_LOGGING_ENABLED) { 688 PulseLogger.newInput("DRAG_OVER"); 689 } 690 TransferMode action; 691 try { 692 action = QuantumToolkit.runWithoutRenderLock(() -> { 693 return dndHandler.handleDragOver(x, y, xAbs, yAbs, 694 actionToTransferMode(recommendedDropAction), 695 dropTargetAssistant); 696 }); 697 } finally { 698 if (PULSE_LOGGING_ENABLED) { 699 PulseLogger.newInput(null); 700 } 701 } 702 return transferModeToAction(action); 703 } 704 705 private ClipboardAssistance dropSourceAssistant; 706 707 @Override public void handleDragStart(View view, final int button, 708 final int x, final int y, final int xAbs, final int yAbs, 709 final ClipboardAssistance assistant) 710 { 711 if (PULSE_LOGGING_ENABLED) { 712 PulseLogger.newInput("DRAG_START"); 713 } 714 dropSourceAssistant = assistant; 715 try { 716 QuantumToolkit.runWithoutRenderLock(() -> { 717 dndHandler.handleDragStart(button, x, y, xAbs, yAbs, assistant); 718 return null; 719 }); 720 } finally { 721 if (PULSE_LOGGING_ENABLED) { 722 PulseLogger.newInput(null); 723 } 724 } 725 } 726 727 @Override public void handleDragEnd(View view, final int performedAction) { 728 if (PULSE_LOGGING_ENABLED) { 729 PulseLogger.newInput("DRAG_END"); 730 } 731 try { 732 QuantumToolkit.runWithoutRenderLock(() -> { 733 dndHandler.handleDragEnd(actionToTransferMode(performedAction), dropSourceAssistant); 734 return null; 735 }); 736 } finally { 737 if (PULSE_LOGGING_ENABLED) { 738 PulseLogger.newInput(null); 739 } 740 } 741 } 742 743 // TODO - dropTargetListener.dropActionChanged 744 // TODO - dragSourceListener.dropActionChanged 745 746 private final ViewEventNotification viewNotification = new ViewEventNotification(); 747 private class ViewEventNotification implements PrivilegedAction<Void> { 748 View view; 749 long time; 750 int type; 751 752 @Override 753 public Void run() { 754 if (scene.sceneListener == null) { 755 return null; 756 } 757 switch (type) { 758 case ViewEvent.REPAINT: { 759 Window w = view.getWindow(); 760 if (w != null && w.getMinimumWidth() == view.getWidth() && !w.isVisible()) { 761 // RT-21057 - ignore initial minimum size setting if not visible 762 break; 763 } 764 if (QuantumToolkit.drawInPaint && w != null && w.isVisible()) { 765 WindowStage stage = scene.getWindowStage(); 766 if (stage != null && !stage.isApplet()) { 767 collector.liveRepaintRenderJob(scene); 768 } 769 } 770 scene.entireSceneNeedsRepaint(); 771 break; 772 } 773 case ViewEvent.RESIZE: { 774 Window w = view.getWindow(); 775 scene.sceneListener.changedSize(view.getWidth(), view.getHeight()); 776 scene.entireSceneNeedsRepaint(); 777 QuantumToolkit.runWithRenderLock(() -> { 778 scene.updateSceneState(); 779 return null; 780 }); 781 if (QuantumToolkit.liveResize && w != null && w.isVisible()) { 782 WindowStage stage = scene.getWindowStage(); 783 if (stage != null && !stage.isApplet()) { 784 collector.liveRepaintRenderJob(scene); 785 } 786 } 787 break; 788 } 789 case ViewEvent.MOVE: 790 scene.sceneListener.changedLocation(view.getX(), view.getY()); 791 break; 792 case ViewEvent.FULLSCREEN_ENTER: 793 case ViewEvent.FULLSCREEN_EXIT: 794 if (scene.getWindowStage() != null) { 795 scene.getWindowStage().fullscreenChanged(type == ViewEvent.FULLSCREEN_ENTER); 796 } 797 break; 798 case ViewEvent.ADD: 799 case ViewEvent.REMOVE: 800 // unhandled 801 break; 802 default: 803 throw new RuntimeException("handleViewEvent: unhandled type: " + type); 804 } 805 return null; 806 } 807 } 808 809 @Override public void handleViewEvent(View view, long time, final int type) { 810 if (PULSE_LOGGING_ENABLED) { 811 PulseLogger.newInput("VIEW_EVENT: "+ViewEvent.getTypeString(type)); 812 } 813 viewNotification.view = view; 814 viewNotification.time = time; 815 viewNotification.type = type; 816 try { 817 QuantumToolkit.runWithoutRenderLock(() -> { 818 return AccessController.doPrivileged(viewNotification, scene.getAccessControlContext()); 819 }); 820 } 821 finally { 822 if (PULSE_LOGGING_ENABLED) { 823 PulseLogger.newInput(null); 824 } 825 } 826 } 827 828 @Override public void handleScrollGestureEvent( 829 View view, final long time, final int type, 830 final int modifiers, final boolean isDirect, final boolean isInertia, final int touchCount, 831 final int x, final int y, final int xAbs, final int yAbs, final double dx, final double dy, 832 final double totaldx, final double totaldy, final double multiplierX, final double multiplierY) 833 { 834 if (PULSE_LOGGING_ENABLED) { 835 PulseLogger.newInput("SCROLL_GESTURE_EVENT"); 836 } 837 WindowStage stage = scene.getWindowStage(); 838 try { 839 if (stage != null) { 840 stage.setInEventHandler(true); 841 } 842 QuantumToolkit.runWithoutRenderLock(() -> { 843 return AccessController.doPrivileged((PrivilegedAction<Void>) () -> { 844 if (scene.sceneListener != null) { 845 EventType<ScrollEvent> eventType; 846 switch(type) { 847 case GestureEvent.GESTURE_STARTED: 848 eventType = ScrollEvent.SCROLL_STARTED; 849 break; 850 case GestureEvent.GESTURE_PERFORMED: 851 eventType = ScrollEvent.SCROLL; 852 break; 853 case GestureEvent.GESTURE_FINISHED: 854 eventType = ScrollEvent.SCROLL_FINISHED; 855 break; 856 default: 857 throw new RuntimeException("Unknown scroll event type: " + type); 858 } 859 scene.sceneListener.scrollEvent(eventType, dx, dy, totaldx, totaldy, 860 multiplierX, multiplierY, 861 touchCount, 862 0, 0, 0, 0, 863 x == View.GESTURE_NO_VALUE ? Double.NaN : x, 864 y == View.GESTURE_NO_VALUE ? Double.NaN : y, 865 xAbs == View.GESTURE_NO_VALUE ? Double.NaN : xAbs, 866 yAbs == View.GESTURE_NO_VALUE ? Double.NaN : yAbs, 867 (modifiers & KeyEvent.MODIFIER_SHIFT) != 0, 868 (modifiers & KeyEvent.MODIFIER_CONTROL) != 0, 869 (modifiers & KeyEvent.MODIFIER_ALT) != 0, 870 (modifiers & KeyEvent.MODIFIER_WINDOWS) != 0, 871 isDirect, isInertia); 872 } 873 return null; 874 }, scene.getAccessControlContext()); 875 }); 876 } finally { 877 if (stage != null) { 878 stage.setInEventHandler(false); 879 } 880 if (PULSE_LOGGING_ENABLED) { 881 PulseLogger.newInput(null); 882 } 883 } 884 } 885 886 @Override public void handleZoomGestureEvent( 887 View view, final long time, final int type, 888 final int modifiers, final boolean isDirect, final boolean isInertia, 889 final int originx, final int originy, 890 final int originxAbs, final int originyAbs, 891 final double scale, double expansion, 892 final double totalscale, double totalexpansion) 893 { 894 if (PULSE_LOGGING_ENABLED) { 895 PulseLogger.newInput("ZOOM_GESTURE_EVENT"); 896 } 897 WindowStage stage = scene.getWindowStage(); 898 try { 899 if (stage != null) { 900 stage.setInEventHandler(true); 901 } 902 QuantumToolkit.runWithoutRenderLock(() -> { 903 return AccessController.doPrivileged((PrivilegedAction<Void>) () -> { 904 if (scene.sceneListener != null) { 905 EventType<ZoomEvent> eventType; 906 switch (type) { 907 case GestureEvent.GESTURE_STARTED: 908 eventType = ZoomEvent.ZOOM_STARTED; 909 break; 910 case GestureEvent.GESTURE_PERFORMED: 911 eventType = ZoomEvent.ZOOM; 912 break; 913 case GestureEvent.GESTURE_FINISHED: 914 eventType = ZoomEvent.ZOOM_FINISHED; 915 break; 916 default: 917 throw new RuntimeException("Unknown scroll event type: " + type); 918 } 919 scene.sceneListener.zoomEvent(eventType, scale, totalscale, 920 originx == View.GESTURE_NO_VALUE ? Double.NaN : originx, 921 originy == View.GESTURE_NO_VALUE ? Double.NaN : originy, 922 originxAbs == View.GESTURE_NO_VALUE ? Double.NaN : originxAbs, 923 originyAbs == View.GESTURE_NO_VALUE ? Double.NaN : originyAbs, 924 (modifiers & KeyEvent.MODIFIER_SHIFT) != 0, 925 (modifiers & KeyEvent.MODIFIER_CONTROL) != 0, 926 (modifiers & KeyEvent.MODIFIER_ALT) != 0, 927 (modifiers & KeyEvent.MODIFIER_WINDOWS) != 0, 928 isDirect, isInertia); 929 } 930 return null; 931 }, scene.getAccessControlContext()); 932 }); 933 } finally { 934 if (stage != null) { 935 stage.setInEventHandler(false); 936 } 937 if (PULSE_LOGGING_ENABLED) { 938 PulseLogger.newInput(null); 939 } 940 } 941 } 942 943 @Override public void handleRotateGestureEvent( 944 View view, final long time, final int type, 945 final int modifiers, final boolean isDirect, final boolean isInertia, 946 final int originx, final int originy, 947 final int originxAbs, final int originyAbs, 948 final double dangle, final double totalangle) 949 { 950 if (PULSE_LOGGING_ENABLED) { 951 PulseLogger.newInput("ROTATE_GESTURE_EVENT"); 952 } 953 WindowStage stage = scene.getWindowStage(); 954 try { 955 if (stage != null) { 956 stage.setInEventHandler(true); 957 } 958 QuantumToolkit.runWithoutRenderLock(() -> { 959 return AccessController.doPrivileged((PrivilegedAction<Void>) () -> { 960 if (scene.sceneListener != null) { 961 EventType<RotateEvent> eventType; 962 switch (type) { 963 case GestureEvent.GESTURE_STARTED: 964 eventType = RotateEvent.ROTATION_STARTED; 965 break; 966 case GestureEvent.GESTURE_PERFORMED: 967 eventType = RotateEvent.ROTATE; 968 break; 969 case GestureEvent.GESTURE_FINISHED: 970 eventType = RotateEvent.ROTATION_FINISHED; 971 break; 972 default: 973 throw new RuntimeException("Unknown scroll event type: " + type); 974 } 975 scene.sceneListener.rotateEvent(eventType, dangle, totalangle, 976 originx == View.GESTURE_NO_VALUE ? Double.NaN : originx, 977 originy == View.GESTURE_NO_VALUE ? Double.NaN : originy, 978 originxAbs == View.GESTURE_NO_VALUE ? Double.NaN : originxAbs, 979 originyAbs == View.GESTURE_NO_VALUE ? Double.NaN : originyAbs, 980 (modifiers & KeyEvent.MODIFIER_SHIFT) != 0, 981 (modifiers & KeyEvent.MODIFIER_CONTROL) != 0, 982 (modifiers & KeyEvent.MODIFIER_ALT) != 0, 983 (modifiers & KeyEvent.MODIFIER_WINDOWS) != 0, 984 isDirect, isInertia); 985 } 986 return null; 987 }, scene.getAccessControlContext()); 988 }); 989 } finally { 990 if (stage != null) { 991 stage.setInEventHandler(false); 992 } 993 if (PULSE_LOGGING_ENABLED) { 994 PulseLogger.newInput(null); 995 } 996 } 997 } 998 999 @Override public void handleSwipeGestureEvent( 1000 View view, final long time, int type, 1001 final int modifiers, final boolean isDirect, 1002 boolean isInertia, final int touchCount, 1003 final int dir, final int x, final int y, final int xAbs, final int yAbs) 1004 { 1005 if (PULSE_LOGGING_ENABLED) { 1006 PulseLogger.newInput("SWIPE_EVENT"); 1007 } 1008 WindowStage stage = scene.getWindowStage(); 1009 try { 1010 if (stage != null) { 1011 stage.setInEventHandler(true); 1012 } 1013 QuantumToolkit.runWithoutRenderLock(() -> { 1014 return AccessController.doPrivileged((PrivilegedAction<Void>) () -> { 1015 if (scene.sceneListener != null) { 1016 EventType<SwipeEvent> eventType; 1017 switch (dir) { 1018 case SwipeGesture.DIR_UP: 1019 eventType = SwipeEvent.SWIPE_UP; 1020 break; 1021 case SwipeGesture.DIR_DOWN: 1022 eventType = SwipeEvent.SWIPE_DOWN; 1023 break; 1024 case SwipeGesture.DIR_LEFT: 1025 eventType = SwipeEvent.SWIPE_LEFT; 1026 break; 1027 case SwipeGesture.DIR_RIGHT: 1028 eventType = SwipeEvent.SWIPE_RIGHT; 1029 break; 1030 default: 1031 throw new RuntimeException("Unknown swipe event direction: " + dir); 1032 } 1033 scene.sceneListener.swipeEvent(eventType, touchCount, 1034 x == View.GESTURE_NO_VALUE ? Double.NaN : x, 1035 y == View.GESTURE_NO_VALUE ? Double.NaN : y, 1036 xAbs == View.GESTURE_NO_VALUE ? Double.NaN : xAbs, 1037 yAbs == View.GESTURE_NO_VALUE ? Double.NaN : yAbs, 1038 (modifiers & KeyEvent.MODIFIER_SHIFT) != 0, 1039 (modifiers & KeyEvent.MODIFIER_CONTROL) != 0, 1040 (modifiers & KeyEvent.MODIFIER_ALT) != 0, 1041 (modifiers & KeyEvent.MODIFIER_WINDOWS) != 0, 1042 isDirect); 1043 } 1044 return null; 1045 }, scene.getAccessControlContext()); 1046 }); 1047 } finally { 1048 if (stage != null) { 1049 stage.setInEventHandler(false); 1050 } 1051 if (PULSE_LOGGING_ENABLED) { 1052 PulseLogger.newInput(null); 1053 } 1054 } 1055 } 1056 1057 @Override public void handleBeginTouchEvent( 1058 View view, final long time, final int modifiers, 1059 final boolean isDirect, final int touchEventCount) 1060 { 1061 if (PULSE_LOGGING_ENABLED) { 1062 PulseLogger.newInput("BEGIN_TOUCH_EVENT"); 1063 } 1064 WindowStage stage = scene.getWindowStage(); 1065 try { 1066 if (stage != null) { 1067 stage.setInEventHandler(true); 1068 } 1069 QuantumToolkit.runWithoutRenderLock(() -> { 1070 return AccessController.doPrivileged((PrivilegedAction<Void>) () -> { 1071 if (scene.sceneListener != null) { 1072 scene.sceneListener.touchEventBegin(time, touchEventCount, 1073 isDirect, 1074 (modifiers & KeyEvent.MODIFIER_SHIFT) != 0, 1075 (modifiers & KeyEvent.MODIFIER_CONTROL) != 0, 1076 (modifiers & KeyEvent.MODIFIER_ALT) != 0, 1077 (modifiers & KeyEvent.MODIFIER_WINDOWS) != 0); 1078 } 1079 return null; 1080 }, scene.getAccessControlContext()); 1081 }); 1082 } finally { 1083 if (stage != null) { 1084 stage.setInEventHandler(false); 1085 } 1086 if (PULSE_LOGGING_ENABLED) { 1087 PulseLogger.newInput(null); 1088 } 1089 } 1090 1091 gestures.notifyBeginTouchEvent(time, modifiers, isDirect, touchEventCount); 1092 } 1093 1094 @Override public void handleNextTouchEvent( 1095 View view, final long time, final int type, final long touchId, 1096 final int x, final int y, final int xAbs, final int yAbs) 1097 { 1098 if (PULSE_LOGGING_ENABLED) { 1099 PulseLogger.newInput("NEXT_TOUCH_EVENT"); 1100 } 1101 WindowStage stage = scene.getWindowStage(); 1102 try { 1103 if (stage != null) { 1104 stage.setInEventHandler(true); 1105 } 1106 QuantumToolkit.runWithoutRenderLock(() -> { 1107 return AccessController.doPrivileged((PrivilegedAction<Void>) () -> { 1108 if (scene.sceneListener != null) { 1109 TouchPoint.State state; 1110 switch (type) { 1111 case TouchEvent.TOUCH_PRESSED: 1112 state = TouchPoint.State.PRESSED; 1113 break; 1114 case TouchEvent.TOUCH_MOVED: 1115 state = TouchPoint.State.MOVED; 1116 break; 1117 case TouchEvent.TOUCH_STILL: 1118 state = TouchPoint.State.STATIONARY; 1119 break; 1120 case TouchEvent.TOUCH_RELEASED: 1121 state = TouchPoint.State.RELEASED; 1122 break; 1123 default: 1124 throw new RuntimeException("Unknown touch state: " + type); 1125 } 1126 scene.sceneListener.touchEventNext(state, touchId, x, y, xAbs, yAbs); 1127 } 1128 return null; 1129 }, scene.getAccessControlContext()); 1130 }); 1131 } finally { 1132 if (stage != null) { 1133 stage.setInEventHandler(false); 1134 } 1135 if (PULSE_LOGGING_ENABLED) { 1136 PulseLogger.newInput(null); 1137 } 1138 } 1139 1140 gestures.notifyNextTouchEvent(time, type, touchId, x, y, xAbs, yAbs); 1141 } 1142 1143 @Override public void handleEndTouchEvent(View view, long time) { 1144 if (PULSE_LOGGING_ENABLED) { 1145 PulseLogger.newInput("END_TOUCH_EVENT"); 1146 } 1147 WindowStage stage = scene.getWindowStage(); 1148 try { 1149 if (stage != null) { 1150 stage.setInEventHandler(true); 1151 } 1152 QuantumToolkit.runWithoutRenderLock(() -> { 1153 return AccessController.doPrivileged((PrivilegedAction<Void>) () -> { 1154 if (scene.sceneListener != null) { 1155 scene.sceneListener.touchEventEnd(); 1156 } 1157 return null; 1158 }, scene.getAccessControlContext()); 1159 }); 1160 } finally { 1161 if (stage != null) { 1162 stage.setInEventHandler(false); 1163 } 1164 if (PULSE_LOGGING_ENABLED) { 1165 PulseLogger.newInput(null); 1166 } 1167 } 1168 1169 gestures.notifyEndTouchEvent(time); 1170 } 1171 1172 @Override 1173 public Accessible getSceneAccessible() { 1174 if (scene != null && scene.sceneListener != null) { 1175 return scene.sceneListener.getSceneAccessible(); 1176 } 1177 return null; 1178 } 1179 }