1 /* 2 * Copyright (c) 2011, 2013, 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 sun.lwawt.macosx; 27 28 import java.awt.*; 29 import java.awt.datatransfer.Clipboard; 30 import java.awt.dnd.*; 31 import java.awt.dnd.peer.DragSourceContextPeer; 32 import java.awt.event.InputEvent; 33 import java.awt.event.InvocationEvent; 34 import java.awt.event.KeyEvent; 35 import java.awt.im.InputMethodHighlight; 36 import java.awt.peer.*; 37 import java.lang.reflect.*; 38 import java.io.File; 39 import java.io.InputStream; 40 import java.net.URL; 41 import java.security.*; 42 import java.util.*; 43 import java.util.concurrent.Callable; 44 45 import sun.awt.*; 46 import sun.awt.image.ToolkitImage; 47 import sun.lwawt.*; 48 import sun.lwawt.LWWindowPeer.PeerType; 49 import sun.security.action.GetBooleanAction; 50 import com.sun.awt.MultiResolutionImage; 51 52 import sun.util.CoreResourceBundleControl; 53 54 class NamedCursor extends Cursor { 55 NamedCursor(String name) { 56 super(name); 57 } 58 } 59 60 /** 61 * Mac OS X Cocoa-based AWT Toolkit. 62 */ 63 public final class LWCToolkit extends LWToolkit { 64 // While it is possible to enumerate all mouse devices 65 // and query them for the number of buttons, the code 66 // that does it is rather complex. Instead, we opt for 67 // the easy way and just support up to 5 mouse buttons, 68 // like Windows. 69 private static final int BUTTONS = 5; 70 71 private static native void initIDs(); 72 73 private static CInputMethodDescriptor sInputMethodDescriptor; 74 75 static { 76 System.err.flush(); 77 78 ResourceBundle platformResources = java.security.AccessController.doPrivileged( 79 new java.security.PrivilegedAction<ResourceBundle>() { 80 public ResourceBundle run() { 81 ResourceBundle platformResources = null; 82 try { 83 platformResources = 84 ResourceBundle.getBundle("sun.awt.resources.awtosx", 85 CoreResourceBundleControl.getRBControlInstance()); 86 } catch (MissingResourceException e) { 87 // No resource file; defaults will be used. 88 } 89 90 System.loadLibrary("awt"); 91 System.loadLibrary("fontmanager"); 92 93 return platformResources; 94 } 95 }); 96 97 AWTAccessor.getToolkitAccessor().setPlatformResources(platformResources); 98 99 if (!GraphicsEnvironment.isHeadless()) { 100 initIDs(); 101 } 102 inAWT = AccessController.doPrivileged(new PrivilegedAction<Boolean>() { 103 @Override 104 public Boolean run() { 105 return !Boolean.parseBoolean(System.getProperty("javafx.embed.singleThread", "false")); 106 } 107 }); 108 } 109 110 /* 111 * If true we operate in normal mode and nested runloop is executed in JavaRunLoopMode 112 * If false we operate in singleThreaded FX/AWT interop mode and nested loop uses NSDefaultRunLoopMode 113 */ 114 private static final boolean inAWT; 115 116 public LWCToolkit() { 117 SunToolkit.setDataTransfererClassName("sun.lwawt.macosx.CDataTransferer"); 118 119 areExtraMouseButtonsEnabled = Boolean.parseBoolean(System.getProperty("sun.awt.enableExtraMouseButtons", "true")); 120 //set system property if not yet assigned 121 System.setProperty("sun.awt.enableExtraMouseButtons", ""+areExtraMouseButtonsEnabled); 122 } 123 124 /* 125 * System colors with default initial values, overwritten by toolkit if system values differ and are available. 126 */ 127 private final static int NUM_APPLE_COLORS = 3; 128 public final static int KEYBOARD_FOCUS_COLOR = 0; 129 public final static int INACTIVE_SELECTION_BACKGROUND_COLOR = 1; 130 public final static int INACTIVE_SELECTION_FOREGROUND_COLOR = 2; 131 private static int[] appleColors = { 132 0xFF808080, // keyboardFocusColor = Color.gray; 133 0xFFC0C0C0, // secondarySelectedControlColor 134 0xFF303030, // controlDarkShadowColor 135 }; 136 137 private native void loadNativeColors(final int[] systemColors, final int[] appleColors); 138 139 protected void loadSystemColors(final int[] systemColors) { 140 if (systemColors == null) return; 141 loadNativeColors(systemColors, appleColors); 142 } 143 144 private static class AppleSpecificColor extends Color { 145 int index; 146 public AppleSpecificColor(int index) { 147 super(appleColors[index]); 148 this.index = index; 149 } 150 151 public int getRGB() { 152 return appleColors[index]; 153 } 154 } 155 156 /** 157 * Returns Apple specific colors that we may expose going forward. 158 * 159 */ 160 public static Color getAppleColor(int color) { 161 return new AppleSpecificColor(color); 162 } 163 164 static void systemColorsChanged() { 165 // This is only called from native code. 166 EventQueue.invokeLater(new Runnable() { 167 public void run() { 168 AccessController.doPrivileged (new PrivilegedAction<Object>() { 169 public Object run() { 170 try { 171 final Method updateColorsMethod = SystemColor.class.getDeclaredMethod("updateSystemColors", new Class[0]); 172 updateColorsMethod.setAccessible(true); 173 updateColorsMethod.invoke(null, new Object[0]); 174 } catch (final Throwable e) { 175 e.printStackTrace(); 176 // swallow this if something goes horribly wrong 177 } 178 return null; 179 } 180 }); 181 } 182 }); 183 } 184 185 public static LWCToolkit getLWCToolkit() { 186 return (LWCToolkit)Toolkit.getDefaultToolkit(); 187 } 188 189 @Override 190 protected PlatformWindow createPlatformWindow(PeerType peerType) { 191 if (peerType == PeerType.EMBEDDED_FRAME) { 192 return new CPlatformEmbeddedFrame(); 193 } else if (peerType == PeerType.VIEW_EMBEDDED_FRAME) { 194 return new CViewPlatformEmbeddedFrame(); 195 } else if (peerType == PeerType.LW_FRAME) { 196 return new CPlatformLWWindow(); 197 } else { 198 assert (peerType == PeerType.SIMPLEWINDOW || peerType == PeerType.DIALOG || peerType == PeerType.FRAME); 199 return new CPlatformWindow(); 200 } 201 } 202 203 @Override 204 protected SecurityWarningWindow createSecurityWarning(Window ownerWindow, LWWindowPeer ownerPeer) { 205 return new CWarningWindow(ownerWindow, ownerPeer); 206 } 207 208 @Override 209 protected PlatformComponent createPlatformComponent() { 210 return new CPlatformComponent(); 211 } 212 213 @Override 214 protected PlatformComponent createLwPlatformComponent() { 215 return new CPlatformLWComponent(); 216 } 217 218 @Override 219 protected FileDialogPeer createFileDialogPeer(FileDialog target) { 220 return new CFileDialog(target); 221 } 222 223 @Override 224 public MenuPeer createMenu(Menu target) { 225 MenuPeer peer = new CMenu(target); 226 targetCreatedPeer(target, peer); 227 return peer; 228 } 229 230 @Override 231 public MenuBarPeer createMenuBar(MenuBar target) { 232 MenuBarPeer peer = new CMenuBar(target); 233 targetCreatedPeer(target, peer); 234 return peer; 235 } 236 237 @Override 238 public MenuItemPeer createMenuItem(MenuItem target) { 239 MenuItemPeer peer = new CMenuItem(target); 240 targetCreatedPeer(target, peer); 241 return peer; 242 } 243 244 @Override 245 public CheckboxMenuItemPeer createCheckboxMenuItem(CheckboxMenuItem target) { 246 CheckboxMenuItemPeer peer = new CCheckboxMenuItem(target); 247 targetCreatedPeer(target, peer); 248 return peer; 249 } 250 251 @Override 252 public PopupMenuPeer createPopupMenu(PopupMenu target) { 253 PopupMenuPeer peer = new CPopupMenu(target); 254 targetCreatedPeer(target, peer); 255 return peer; 256 257 } 258 259 @Override 260 public SystemTrayPeer createSystemTray(SystemTray target) { 261 SystemTrayPeer peer = new CSystemTray(); 262 return peer; 263 } 264 265 @Override 266 public TrayIconPeer createTrayIcon(TrayIcon target) { 267 TrayIconPeer peer = new CTrayIcon(target); 268 targetCreatedPeer(target, peer); 269 return peer; 270 } 271 272 @Override 273 public LWCursorManager getCursorManager() { 274 return CCursorManager.getInstance(); 275 } 276 277 @Override 278 public Cursor createCustomCursor(final Image cursor, final Point hotSpot, final String name) throws IndexOutOfBoundsException, HeadlessException { 279 return new CCustomCursor(cursor, hotSpot, name); 280 } 281 282 @Override 283 public Dimension getBestCursorSize(final int preferredWidth, final int preferredHeight) throws HeadlessException { 284 return CCustomCursor.getBestCursorSize(preferredWidth, preferredHeight); 285 } 286 287 @Override 288 protected void platformCleanup() { 289 // TODO Auto-generated method stub 290 291 } 292 293 @Override 294 protected void platformInit() { 295 // TODO Auto-generated method stub 296 297 } 298 299 @Override 300 protected void platformRunMessage() { 301 // TODO Auto-generated method stub 302 303 } 304 305 @Override 306 protected void platformShutdown() { 307 // TODO Auto-generated method stub 308 309 } 310 311 class OSXPlatformFont extends sun.awt.PlatformFont 312 { 313 public OSXPlatformFont(String name, int style) 314 { 315 super(name, style); 316 } 317 protected char getMissingGlyphCharacter() 318 { 319 // Follow up for real implementation 320 return (char)0xfff8; // see http://developer.apple.com/fonts/LastResortFont/ 321 } 322 } 323 public FontPeer getFontPeer(String name, int style) { 324 return new OSXPlatformFont(name, style); 325 } 326 327 @Override 328 protected int getScreenHeight() { 329 return GraphicsEnvironment.getLocalGraphicsEnvironment() 330 .getDefaultScreenDevice().getDefaultConfiguration().getBounds().height; 331 } 332 333 @Override 334 protected int getScreenWidth() { 335 return GraphicsEnvironment.getLocalGraphicsEnvironment() 336 .getDefaultScreenDevice().getDefaultConfiguration().getBounds().width; 337 } 338 339 @Override 340 protected void initializeDesktopProperties() { 341 super.initializeDesktopProperties(); 342 Map <Object, Object> fontHints = new HashMap<Object, Object>(); 343 fontHints.put(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); 344 fontHints.put(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON); 345 desktopProperties.put(SunToolkit.DESKTOPFONTHINTS, fontHints); 346 desktopProperties.put("awt.mouse.numButtons", BUTTONS); 347 348 // These DnD properties must be set, otherwise Swing ends up spewing NPEs 349 // all over the place. The values came straight off of MToolkit. 350 desktopProperties.put("DnD.Autoscroll.initialDelay", new Integer(50)); 351 desktopProperties.put("DnD.Autoscroll.interval", new Integer(50)); 352 desktopProperties.put("DnD.Autoscroll.cursorHysteresis", new Integer(5)); 353 354 desktopProperties.put("DnD.isDragImageSupported", new Boolean(true)); 355 356 // Register DnD cursors 357 desktopProperties.put("DnD.Cursor.CopyDrop", new NamedCursor("DnD.Cursor.CopyDrop")); 358 desktopProperties.put("DnD.Cursor.MoveDrop", new NamedCursor("DnD.Cursor.MoveDrop")); 359 desktopProperties.put("DnD.Cursor.LinkDrop", new NamedCursor("DnD.Cursor.LinkDrop")); 360 desktopProperties.put("DnD.Cursor.CopyNoDrop", new NamedCursor("DnD.Cursor.CopyNoDrop")); 361 desktopProperties.put("DnD.Cursor.MoveNoDrop", new NamedCursor("DnD.Cursor.MoveNoDrop")); 362 desktopProperties.put("DnD.Cursor.LinkNoDrop", new NamedCursor("DnD.Cursor.LinkNoDrop")); 363 364 } 365 366 367 /* 368 * The method returns true if some events were processed during that timeout. 369 * @see sun.awt.SunToolkit#syncNativeQueue(long) 370 */ 371 @Override 372 protected boolean syncNativeQueue(long timeout) { 373 return nativeSyncQueue(timeout); 374 } 375 376 @Override 377 public native void beep(); 378 379 @Override 380 public int getScreenResolution() throws HeadlessException { 381 return (int) ((CGraphicsDevice) GraphicsEnvironment 382 .getLocalGraphicsEnvironment().getDefaultScreenDevice()) 383 .getXResolution(); 384 } 385 386 @Override 387 public Insets getScreenInsets(final GraphicsConfiguration gc) { 388 return ((CGraphicsConfig) gc).getDevice().getScreenInsets(); 389 } 390 391 @Override 392 public void sync() { 393 // TODO Auto-generated method stub 394 395 } 396 397 @Override 398 public RobotPeer createRobot(Robot target, GraphicsDevice screen) { 399 return new CRobot(target, (CGraphicsDevice)screen); 400 } 401 402 private native boolean isCapsLockOn(); 403 404 /* 405 * NOTE: Among the keys this method is supposed to check, 406 * only Caps Lock works as a true locking key with OS X. 407 * There is no Scroll Lock key on modern Apple keyboards, 408 * and with a PC keyboard plugged in Scroll Lock is simply 409 * ignored: no LED lights up if you press it. 410 * The key located at the same position on Apple keyboards 411 * as Num Lock on PC keyboards is called Clear, doesn't lock 412 * anything and is used for entirely different purpose. 413 */ 414 public boolean getLockingKeyState(int keyCode) throws UnsupportedOperationException { 415 switch (keyCode) { 416 case KeyEvent.VK_NUM_LOCK: 417 case KeyEvent.VK_SCROLL_LOCK: 418 case KeyEvent.VK_KANA_LOCK: 419 throw new UnsupportedOperationException("Toolkit.getLockingKeyState"); 420 421 case KeyEvent.VK_CAPS_LOCK: 422 return isCapsLockOn(); 423 424 default: 425 throw new IllegalArgumentException("invalid key for Toolkit.getLockingKeyState"); 426 } 427 } 428 429 //Is it allowed to generate events assigned to extra mouse buttons. 430 //Set to true by default. 431 private static boolean areExtraMouseButtonsEnabled = true; 432 433 public boolean areExtraMouseButtonsEnabled() throws HeadlessException { 434 return areExtraMouseButtonsEnabled; 435 } 436 437 public int getNumberOfButtons(){ 438 return BUTTONS; 439 } 440 441 @Override 442 public boolean isTraySupported() { 443 return true; 444 } 445 446 @Override 447 public boolean isAlwaysOnTopSupported() { 448 return true; 449 } 450 451 // Intended to be called from the LWCToolkit.m only. 452 private static void installToolkitThreadNameInJava() { 453 Thread.currentThread().setName(CThreading.APPKIT_THREAD_NAME); 454 } 455 456 @Override 457 public boolean isWindowOpacitySupported() { 458 return true; 459 } 460 461 @Override 462 public boolean isFrameStateSupported(int state) throws HeadlessException { 463 switch (state) { 464 case Frame.NORMAL: 465 case Frame.ICONIFIED: 466 case Frame.MAXIMIZED_BOTH: 467 return true; 468 default: 469 return false; 470 } 471 } 472 473 /** 474 * Determines which modifier key is the appropriate accelerator 475 * key for menu shortcuts. 476 * <p> 477 * Menu shortcuts, which are embodied in the 478 * <code>MenuShortcut</code> class, are handled by the 479 * <code>MenuBar</code> class. 480 * <p> 481 * By default, this method returns <code>Event.CTRL_MASK</code>. 482 * Toolkit implementations should override this method if the 483 * <b>Control</b> key isn't the correct key for accelerators. 484 * @return the modifier mask on the <code>Event</code> class 485 * that is used for menu shortcuts on this toolkit. 486 * @see java.awt.MenuBar 487 * @see java.awt.MenuShortcut 488 * @since JDK1.1 489 */ 490 public int getMenuShortcutKeyMask() { 491 return Event.META_MASK; 492 } 493 494 @Override 495 public Image getImage(final String filename) { 496 final Image nsImage = checkForNSImage(filename); 497 if (nsImage != null) { 498 return nsImage; 499 } 500 501 Image image = super.getImage(filename); 502 Image scalableImage = ScalableToolkitImage.toScalableImage(image, filename); 503 if (image != scalableImage) { 504 putImageToCache(filename, (ToolkitImage) scalableImage); 505 } 506 return scalableImage; 507 } 508 509 @Override 510 public Image getImage(URL url) { 511 Image image = super.getImage(url); 512 Image scalableImage = ScalableToolkitImage.toScalableImage(image, url); 513 if (image != scalableImage) { 514 putImageToCache(url, (ToolkitImage) scalableImage); 515 } 516 return scalableImage; 517 } 518 519 @Override 520 public Image createImage(String filename) { 521 return ScalableToolkitImage.toScalableImage( 522 super.createImage(filename), filename); 523 } 524 525 @Override 526 public Image createImage(URL url) { 527 return ScalableToolkitImage.toScalableImage(super.createImage(url), url); 528 } 529 530 static final String nsImagePrefix = "NSImage://"; 531 protected Image checkForNSImage(final String imageName) { 532 if (imageName == null) return null; 533 if (!imageName.startsWith(nsImagePrefix)) return null; 534 return CImage.getCreator().createImageFromName(imageName.substring(nsImagePrefix.length())); 535 } 536 537 // Thread-safe Object.equals() called from native 538 public static boolean doEquals(final Object a, final Object b, Component c) { 539 if (a == b) return true; 540 541 final boolean[] ret = new boolean[1]; 542 543 try { invokeAndWait(new Runnable() { public void run() { synchronized(ret) { 544 ret[0] = a.equals(b); 545 }}}, c); } catch (Exception e) { e.printStackTrace(); } 546 547 synchronized(ret) { return ret[0]; } 548 } 549 550 public static <T> T invokeAndWait(final Callable<T> callable, Component component) throws Exception { 551 final CallableWrapper<T> wrapper = new CallableWrapper<T>(callable); 552 invokeAndWait(wrapper, component); 553 return wrapper.getResult(); 554 } 555 556 static final class CallableWrapper<T> implements Runnable { 557 final Callable<T> callable; 558 T object; 559 Exception e; 560 561 public CallableWrapper(final Callable<T> callable) { 562 this.callable = callable; 563 } 564 565 public void run() { 566 try { 567 object = callable.call(); 568 } catch (final Exception e) { 569 this.e = e; 570 } 571 } 572 573 public T getResult() throws Exception { 574 if (e != null) throw e; 575 return object; 576 } 577 } 578 579 // Kicks an event over to the appropriate eventqueue and waits for it to finish 580 // To avoid deadlocking, we manually run the NSRunLoop while waiting 581 // Any selector invoked using ThreadUtilities performOnMainThread will be processed in doAWTRunLoop 582 // The InvocationEvent will call LWCToolkit.stopAWTRunLoop() when finished, which will stop our manual runloop 583 // Does not dispatch native events while in the loop 584 public static void invokeAndWait(Runnable runnable, Component component) throws InvocationTargetException { 585 final long mediator = createAWTRunLoopMediator(); 586 587 InvocationEvent invocationEvent = 588 new InvocationEvent(component != null ? component : Toolkit.getDefaultToolkit(), 589 runnable, 590 () -> { 591 if (mediator != 0) { 592 stopAWTRunLoop(mediator); 593 } 594 }, 595 true); 596 597 if (component != null) { 598 AppContext appContext = SunToolkit.targetToAppContext(component); 599 SunToolkit.postEvent(appContext, invocationEvent); 600 601 // 3746956 - flush events from PostEventQueue to prevent them from getting stuck and causing a deadlock 602 SunToolkit.flushPendingEvents(appContext); 603 } else { 604 // This should be the equivalent to EventQueue.invokeAndWait 605 ((LWCToolkit)Toolkit.getDefaultToolkit()).getSystemEventQueueForInvokeAndWait().postEvent(invocationEvent); 606 } 607 608 doAWTRunLoop(mediator, false); 609 610 Throwable eventException = invocationEvent.getException(); 611 if (eventException != null) { 612 if (eventException instanceof UndeclaredThrowableException) { 613 eventException = ((UndeclaredThrowableException)eventException).getUndeclaredThrowable(); 614 } 615 throw new InvocationTargetException(eventException); 616 } 617 } 618 619 public static void invokeLater(Runnable event, Component component) throws InvocationTargetException { 620 final InvocationEvent invocationEvent = 621 new InvocationEvent(component != null ? component : Toolkit.getDefaultToolkit(), event); 622 623 if (component != null) { 624 final AppContext appContext = SunToolkit.targetToAppContext(component); 625 SunToolkit.postEvent(appContext, invocationEvent); 626 627 // 3746956 - flush events from PostEventQueue to prevent them from getting stuck and causing a deadlock 628 SunToolkit.flushPendingEvents(appContext); 629 } else { 630 // This should be the equivalent to EventQueue.invokeAndWait 631 ((LWCToolkit)Toolkit.getDefaultToolkit()).getSystemEventQueueForInvokeAndWait().postEvent(invocationEvent); 632 } 633 634 final Throwable eventException = invocationEvent.getException(); 635 if (eventException == null) return; 636 637 if (eventException instanceof UndeclaredThrowableException) { 638 throw new InvocationTargetException(((UndeclaredThrowableException)eventException).getUndeclaredThrowable()); 639 } 640 throw new InvocationTargetException(eventException); 641 } 642 643 // This exists purely to get around permissions issues with getSystemEventQueueImpl 644 EventQueue getSystemEventQueueForInvokeAndWait() { 645 return getSystemEventQueueImpl(); 646 } 647 648 649 // DnD support 650 651 public DragSourceContextPeer createDragSourceContextPeer(DragGestureEvent dge) throws InvalidDnDOperationException { 652 DragSourceContextPeer dscp = CDragSourceContextPeer.createDragSourceContextPeer(dge); 653 654 return dscp; 655 } 656 657 public <T extends DragGestureRecognizer> T createDragGestureRecognizer(Class<T> abstractRecognizerClass, DragSource ds, Component c, int srcActions, DragGestureListener dgl) { 658 DragGestureRecognizer dgr = null; 659 660 // Create a new mouse drag gesture recognizer if we have a class match: 661 if (MouseDragGestureRecognizer.class.equals(abstractRecognizerClass)) 662 dgr = new CMouseDragGestureRecognizer(ds, c, srcActions, dgl); 663 664 return (T)dgr; 665 } 666 667 // InputMethodSupport Method 668 /** 669 * Returns the default keyboard locale of the underlying operating system 670 */ 671 public Locale getDefaultKeyboardLocale() { 672 Locale locale = CInputMethod.getNativeLocale(); 673 674 if (locale == null) { 675 return super.getDefaultKeyboardLocale(); 676 } 677 678 return locale; 679 } 680 681 public java.awt.im.spi.InputMethodDescriptor getInputMethodAdapterDescriptor() { 682 if (sInputMethodDescriptor == null) 683 sInputMethodDescriptor = new CInputMethodDescriptor(); 684 685 return sInputMethodDescriptor; 686 } 687 688 /** 689 * Returns a map of visual attributes for thelevel description 690 * of the given input method highlight, or null if no mapping is found. 691 * The style field of the input method highlight is ignored. The map 692 * returned is unmodifiable. 693 * @param highlight input method highlight 694 * @return style attribute map, or null 695 * @since 1.3 696 */ 697 public Map mapInputMethodHighlight(InputMethodHighlight highlight) { 698 return CInputMethod.mapInputMethodHighlight(highlight); 699 } 700 701 /** 702 * Returns key modifiers used by Swing to set up a focus accelerator key stroke. 703 */ 704 @Override 705 public int getFocusAcceleratorKeyMask() { 706 return InputEvent.CTRL_MASK | InputEvent.ALT_MASK; 707 } 708 709 /** 710 * Tests whether specified key modifiers mask can be used to enter a printable 711 * character. 712 */ 713 @Override 714 public boolean isPrintableCharacterModifiersMask(int mods) { 715 return ((mods & (InputEvent.META_MASK | InputEvent.CTRL_MASK)) == 0); 716 } 717 718 /** 719 * Returns whether popup is allowed to be shown above the task bar. 720 */ 721 @Override 722 public boolean canPopupOverlapTaskBar() { 723 return false; 724 } 725 726 private static Boolean sunAwtDisableCALayers = null; 727 728 /** 729 * Returns the value of "sun.awt.disableCALayers" property. Default 730 * value is {@code false}. 731 */ 732 public synchronized static boolean getSunAwtDisableCALayers() { 733 if (sunAwtDisableCALayers == null) { 734 sunAwtDisableCALayers = AccessController.doPrivileged( 735 new GetBooleanAction("sun.awt.disableCALayers")); 736 } 737 return sunAwtDisableCALayers.booleanValue(); 738 } 739 740 741 /* 742 * Returns true if the application (one of its windows) owns keyboard focus. 743 */ 744 public native boolean isApplicationActive(); 745 746 /************************ 747 * Native methods section 748 ************************/ 749 750 static native long createAWTRunLoopMediator(); 751 /** 752 * Method to run a nested run-loop. The nested loop is spinned in the javaRunLoop mode, so selectors sent 753 * by [JNFRunLoop performOnMainThreadWaiting] are processed. 754 * @param mediator a native pointer to the mediator object created by createAWTRunLoopMediator 755 * @param processEvents if true - dispatches event while in the nested loop. Used in DnD. 756 * Additional attention is needed when using this feature as we short-circuit normal event 757 * processing which could break Appkit. 758 * (One known example is when the window is resized with the mouse) 759 * 760 * if false - all events come after exit form the nested loop 761 */ 762 static void doAWTRunLoop(long mediator, boolean processEvents) { 763 doAWTRunLoopImpl(mediator, processEvents, inAWT); 764 } 765 static private native void doAWTRunLoopImpl(long mediator, boolean processEvents, boolean inAWT); 766 static native void stopAWTRunLoop(long mediator); 767 768 private native boolean nativeSyncQueue(long timeout); 769 770 @Override 771 public Clipboard createPlatformClipboard() { 772 return new CClipboard("System"); 773 } 774 775 @Override 776 public boolean isModalExclusionTypeSupported(Dialog.ModalExclusionType exclusionType) { 777 return (exclusionType == null) || 778 (exclusionType == Dialog.ModalExclusionType.NO_EXCLUDE) || 779 (exclusionType == Dialog.ModalExclusionType.APPLICATION_EXCLUDE) || 780 (exclusionType == Dialog.ModalExclusionType.TOOLKIT_EXCLUDE); 781 } 782 783 @Override 784 public boolean isModalityTypeSupported(Dialog.ModalityType modalityType) { 785 //TODO: FileDialog blocks excluded windows... 786 //TODO: Test: 2 file dialogs, separate AppContexts: a) Dialog 1 blocked, shouldn't be. Frame 4 blocked (shouldn't be). 787 return (modalityType == null) || 788 (modalityType == Dialog.ModalityType.MODELESS) || 789 (modalityType == Dialog.ModalityType.DOCUMENT_MODAL) || 790 (modalityType == Dialog.ModalityType.APPLICATION_MODAL) || 791 (modalityType == Dialog.ModalityType.TOOLKIT_MODAL); 792 } 793 794 @Override 795 public boolean isWindowShapingSupported() { 796 return true; 797 } 798 799 @Override 800 public boolean isWindowTranslucencySupported() { 801 return true; 802 } 803 804 @Override 805 public boolean isTranslucencyCapable(GraphicsConfiguration gc) { 806 return true; 807 } 808 809 public boolean isSwingBackbufferTranslucencySupported() { 810 return true; 811 } 812 813 @Override 814 public boolean enableInputMethodsForTextComponent() { 815 return true; 816 } 817 818 public static final class ScalableToolkitImage extends ToolkitImage implements MultiResolutionImage { 819 820 Image highResolutionImage; 821 822 public ScalableToolkitImage(Image lowResolutionImage, Image highResolutionImage) { 823 super(lowResolutionImage.getSource()); 824 this.highResolutionImage = highResolutionImage; 825 } 826 827 @Override 828 public Image getResolutionVariant(int width, int height) { 829 return ((width <= getWidth() && height <= getHeight())) 830 ? this : highResolutionImage; 831 } 832 833 static Image toScalableImage(Image image, String fileName) { 834 835 if (fileName != null && !fileName.contains("@2x") 836 && !(image instanceof ScalableToolkitImage)) { 837 String filename2x = getScaledImageName(fileName); 838 if (filename2x != null && new File(filename2x).exists()) { 839 return new ScalableToolkitImage(image, getDefaultToolkit().getImage(filename2x)); 840 } 841 } 842 return image; 843 } 844 845 @SuppressWarnings("try") 846 static Image toScalableImage(Image image, URL url) { 847 848 if (url != null && !url.toString().contains("@2x") 849 && !(image instanceof ScalableToolkitImage)) { 850 try { 851 URL url2x = getScaledImageURL(url); 852 853 if (url2x != null) { 854 try (InputStream is = url2x.openStream()) { 855 return new ScalableToolkitImage(image, 856 getDefaultToolkit().getImage(url2x)); 857 } 858 } 859 } catch (Exception ignore) { 860 } 861 } 862 return image; 863 } 864 865 private static URL getScaledImageURL(URL url) throws Exception { 866 String scaledImagePath = getScaledImageName(url.getPath()); 867 return scaledImagePath == null ? null : new URL(url.getProtocol(), 868 url.getHost(), url.getPort(), scaledImagePath); 869 } 870 871 private static String getScaledImageName(String path) { 872 if (!isValidPath(path)) { 873 return null; 874 } 875 int slash = path.lastIndexOf('/'); 876 String name = (slash < 0) ? path : path.substring(slash + 1); 877 878 int dot = name.lastIndexOf('.'); 879 String name2x = (dot < 0) ? name + "@2x" 880 : name.substring(0, dot) + "@2x" + name.substring(dot); 881 return (slash < 0) ? name2x : path.substring(0, slash + 1) + name2x; 882 } 883 884 private static boolean isValidPath(String path) { 885 return !path.isEmpty() && !path.endsWith("/") && !path.endsWith("."); 886 } 887 888 } 889 }