1 /* 2 * Copyright (c) 2008, 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 java.nio.ByteBuffer; 29 import java.security.AccessControlException; 30 import java.security.AccessController; 31 import java.security.AllPermission; 32 import java.security.Permission; 33 import java.security.PrivilegedAction; 34 import java.security.AccessControlContext; 35 import java.util.HashMap; 36 import java.util.LinkedList; 37 import java.util.List; 38 import java.util.Map; 39 40 import javafx.scene.input.KeyCombination; 41 import javafx.stage.Modality; 42 import javafx.stage.Stage; 43 import javafx.stage.StageStyle; 44 45 import com.sun.glass.events.WindowEvent; 46 import com.sun.glass.ui.*; 47 import com.sun.glass.ui.Window.Level; 48 import com.sun.javafx.PlatformUtil; 49 import com.sun.javafx.iio.common.PushbroomScaler; 50 import com.sun.javafx.iio.common.ScalerFactory; 51 import com.sun.javafx.tk.FocusCause; 52 import com.sun.javafx.tk.TKScene; 53 import com.sun.javafx.tk.TKStage; 54 import com.sun.prism.Image; 55 import com.sun.prism.PixelFormat; 56 import java.util.Locale; 57 import java.util.ResourceBundle; 58 59 class WindowStage extends GlassStage { 60 61 protected Window platformWindow; 62 63 protected javafx.stage.Stage fxStage; 64 65 private StageStyle style; 66 private GlassStage owner = null; 67 private Modality modality = Modality.NONE; 68 private final boolean securityDialog; 69 70 private OverlayWarning warning = null; 71 private boolean rtl = false; 72 private boolean transparent = false; 73 private boolean isPrimaryStage = false; 74 private boolean isAppletStage = false; // true if this is an embedded applet window 75 private boolean isPopupStage = false; 76 private boolean isInFullScreen = false; 77 78 // A flag to indicate whether a call was generated from 79 // an input event handler. 80 private boolean inEventHandler = false; 81 82 // An active window is visible && enabled && focusable. 83 // The list is maintained in the z-order, so that the last element 84 // represents the topmost window (or more accurately, the last 85 // focused window, which we assume is very close to the last topmost one). 86 private static List<WindowStage> activeWindows = new LinkedList<>(); 87 88 private static Map<Window, WindowStage> platformWindows = new HashMap<>(); 89 90 private static GlassAppletWindow appletWindow = null; 91 static void setAppletWindow(GlassAppletWindow aw) { 92 appletWindow = aw; 93 } 94 static GlassAppletWindow getAppletWindow() { 95 return appletWindow; 96 } 97 98 private static final Locale LOCALE = Locale.getDefault(); 99 100 private static final ResourceBundle RESOURCES = 101 ResourceBundle.getBundle(WindowStage.class.getPackage().getName() + 102 ".QuantumMessagesBundle", LOCALE); 103 104 105 public WindowStage(javafx.stage.Window peerWindow, boolean securityDialog, final StageStyle stageStyle, Modality modality, TKStage owner) { 106 this.style = stageStyle; 107 this.owner = (GlassStage)owner; 108 this.modality = modality; 109 this.securityDialog = securityDialog; 110 111 if (peerWindow instanceof javafx.stage.Stage) { 112 fxStage = (Stage)peerWindow; 113 } else { 114 fxStage = null; 115 } 116 117 transparent = stageStyle == StageStyle.TRANSPARENT; 118 if (owner == null) { 119 if (this.modality == Modality.WINDOW_MODAL) { 120 this.modality = Modality.NONE; 121 } 122 } 123 } 124 125 final void setIsPrimary() { 126 isPrimaryStage = true; 127 if (appletWindow != null) { 128 // this is an embedded applet stage 129 isAppletStage = true; 130 } 131 } 132 133 final void setIsPopup() { 134 isPopupStage = true; 135 } 136 137 final boolean isSecurityDialog() { 138 return securityDialog; 139 } 140 141 // Called by QuantumToolkit, so we can override initPlatformWindow in subclasses 142 public final WindowStage init(GlassSystemMenu sysmenu) { 143 initPlatformWindow(); 144 platformWindow.setEventHandler(new GlassWindowEventHandler(this)); 145 if (sysmenu.isSupported()) { 146 sysmenu.createMenuBar(); 147 platformWindow.setMenuBar(sysmenu.getMenuBar()); 148 } 149 return this; 150 } 151 152 private void initPlatformWindow() { 153 if (platformWindow == null) { 154 Application app = Application.GetApplication(); 155 if (isPrimaryStage && (null != appletWindow)) { 156 platformWindow = app.createWindow(appletWindow.getGlassWindow().getNativeWindow()); 157 } else { 158 Window ownerWindow = null; 159 if (owner instanceof WindowStage) { 160 ownerWindow = ((WindowStage)owner).platformWindow; 161 } 162 boolean resizable = false; 163 boolean focusable = true; 164 int windowMask = rtl ? Window.RIGHT_TO_LEFT : 0; 165 if (isPopupStage) { // TODO: make it a stage style? 166 windowMask |= Window.POPUP; 167 if (style == StageStyle.TRANSPARENT) { 168 windowMask |= Window.TRANSPARENT; 169 } 170 focusable = false; 171 } else { 172 switch (style) { 173 case UNIFIED: 174 if (app.supportsUnifiedWindows()) { 175 windowMask |= Window.UNIFIED; 176 } 177 // fall through 178 case DECORATED: 179 windowMask |= 180 Window.TITLED | Window.CLOSABLE | 181 Window.MINIMIZABLE | Window.MAXIMIZABLE; 182 if (ownerWindow != null || modality != Modality.NONE) { 183 windowMask &= 184 ~(Window.MINIMIZABLE | Window.MAXIMIZABLE); 185 } 186 resizable = true; 187 break; 188 case UTILITY: 189 windowMask |= Window.TITLED | Window.UTILITY | Window.CLOSABLE; 190 break; 191 default: 192 windowMask |= 193 (transparent ? Window.TRANSPARENT : Window.UNTITLED) | Window.CLOSABLE; 194 break; 195 } 196 } 197 platformWindow = 198 app.createWindow(ownerWindow, Screen.getMainScreen(), windowMask); 199 platformWindow.setResizable(resizable); 200 platformWindow.setFocusable(focusable); 201 if (securityDialog) { 202 platformWindow.setLevel(Window.Level.FLOATING); 203 } 204 } 205 } 206 platformWindows.put(platformWindow, this); 207 } 208 209 final Window getPlatformWindow() { 210 return platformWindow; 211 } 212 213 static WindowStage findWindowStage(Window platformWindow) { 214 return platformWindows.get(platformWindow); 215 } 216 217 protected GlassStage getOwner() { 218 return owner; 219 } 220 221 protected ViewScene getViewScene() { 222 return (ViewScene)getScene(); 223 } 224 225 StageStyle getStyle() { 226 return style; 227 } 228 229 @Override public TKScene createTKScene(boolean depthBuffer, boolean msaa, AccessControlContext acc) { 230 ViewScene scene = new ViewScene(depthBuffer, msaa); 231 scene.setSecurityContext(acc); 232 return scene; 233 } 234 235 /** 236 * Set the scene to be displayed in this stage 237 * 238 * @param scene The peer of the scene to be displayed 239 */ 240 @Override public void setScene(TKScene scene) { 241 GlassScene oldScene = getScene(); 242 if (oldScene == scene) { 243 // Nothing to do 244 return; 245 } 246 // RT-21465, RT-28490 247 // We don't support scene changes in full-screen mode. 248 exitFullScreen(); 249 super.setScene(scene); 250 if (scene != null) { 251 GlassScene newScene = getViewScene(); 252 View view = newScene.getPlatformView(); 253 QuantumToolkit.runWithRenderLock(() -> { 254 platformWindow.setView(view); 255 if (oldScene != null) oldScene.updateSceneState(); 256 newScene.updateSceneState(); 257 return null; 258 }); 259 requestFocus(); 260 } else { 261 QuantumToolkit.runWithRenderLock(() -> { 262 // platformWindow can be null here, if this window is owned, 263 // and its owner is being closed. 264 if (platformWindow != null) { 265 platformWindow.setView(null); 266 } 267 if (oldScene != null) { 268 oldScene.updateSceneState(); 269 } 270 return null; 271 }); 272 } 273 if (oldScene != null) { 274 ViewPainter painter = ((ViewScene)oldScene).getPainter(); 275 QuantumRenderer.getInstance().disposePresentable(painter.presentable); // latched on RT 276 } 277 } 278 279 @Override public void setBounds(float x, float y, boolean xSet, boolean ySet, 280 float w, float h, float cw, float ch, 281 float xGravity, float yGravity) 282 { 283 if (isAppletStage) { 284 xSet = ySet = false; 285 } 286 platformWindow.setBounds((int)x, (int)y, xSet, ySet, 287 (int)w, (int)h, (int)cw, (int)ch, 288 xGravity, yGravity); 289 } 290 291 @Override public void setMinimumSize(int minWidth, int minHeight) { 292 platformWindow.setMinimumSize(minWidth, minHeight); 293 } 294 295 @Override public void setMaximumSize(int maxWidth, int maxHeight) { 296 platformWindow.setMaximumSize(maxWidth, maxHeight); 297 } 298 299 static Image findBestImage(java.util.List icons, int width, int height) { 300 Image image = null; 301 double bestSimilarity = 3; //Impossibly high value 302 for (Object icon : icons) { 303 //Iterate imageList looking for best matching image. 304 //'Similarity' measure is defined as good scale factor and small insets. 305 //best possible similarity is 0 (no scale, no insets). 306 //It's found by experimentation that good-looking results are achieved 307 //with scale factors x1, x3/4, x2/3, xN, x1/N. 308 //Check to make sure the image/image format is correct. 309 Image im = (Image)icon; 310 if (im == null || !(im.getPixelFormat() == PixelFormat.BYTE_RGB || 311 im.getPixelFormat() == PixelFormat.BYTE_BGRA_PRE || 312 im.getPixelFormat() == PixelFormat.BYTE_GRAY)) 313 { 314 continue; 315 } 316 317 int iw = im.getWidth(); 318 int ih = im.getHeight(); 319 320 if (iw > 0 && ih > 0) { 321 //Calc scale factor 322 double scaleFactor = Math.min((double)width / (double)iw, 323 (double)height / (double)ih); 324 //Calculate scaled image dimensions 325 //adjusting scale factor to nearest "good" value 326 int adjw; 327 int adjh; 328 double scaleMeasure = 1; //0 - best (no) scale, 1 - impossibly bad 329 if (scaleFactor >= 2) { 330 //Need to enlarge image more than twice 331 //Round down scale factor to multiply by integer value 332 scaleFactor = Math.floor(scaleFactor); 333 adjw = iw * (int)scaleFactor; 334 adjh = ih * (int)scaleFactor; 335 scaleMeasure = 1.0 - 0.5 / scaleFactor; 336 } else if (scaleFactor >= 1) { 337 //Don't scale 338 scaleFactor = 1.0; 339 adjw = iw; 340 adjh = ih; 341 scaleMeasure = 0; 342 } else if (scaleFactor >= 0.75) { 343 //Multiply by 3/4 344 scaleFactor = 0.75; 345 adjw = iw * 3 / 4; 346 adjh = ih * 3 / 4; 347 scaleMeasure = 0.3; 348 } else if (scaleFactor >= 0.6666) { 349 //Multiply by 2/3 350 scaleFactor = 0.6666; 351 adjw = iw * 2 / 3; 352 adjh = ih * 2 / 3; 353 scaleMeasure = 0.33; 354 } else { 355 //Multiply size by 1/scaleDivider 356 //where scaleDivider is minimum possible integer 357 //larger than 1/scaleFactor 358 double scaleDivider = Math.ceil(1.0 / scaleFactor); 359 scaleFactor = 1.0 / scaleDivider; 360 adjw = (int)Math.round((double)iw / scaleDivider); 361 adjh = (int)Math.round((double)ih / scaleDivider); 362 scaleMeasure = 1.0 - 1.0 / scaleDivider; 363 } 364 double similarity = ((double)width - (double)adjw) / (double)width + 365 ((double)height - (double)adjh) / (double)height + //Large padding is bad 366 scaleMeasure; //Large rescale is bad 367 if (similarity < bestSimilarity) { 368 bestSimilarity = similarity; 369 image = im; 370 } 371 if (similarity == 0) break; 372 } 373 } 374 return image; 375 } 376 377 @Override public void setIcons(java.util.List icons) { 378 379 int SMALL_ICON_HEIGHT = 32; 380 int SMALL_ICON_WIDTH = 32; 381 if (PlatformUtil.isMac()) { //Mac Sized Icons 382 SMALL_ICON_HEIGHT = 128; 383 SMALL_ICON_WIDTH = 128; 384 } else if (PlatformUtil.isWindows()) { //Windows Sized Icons 385 SMALL_ICON_HEIGHT = 32; 386 SMALL_ICON_WIDTH = 32; 387 } else if (PlatformUtil.isLinux()) { //Linux icons 388 SMALL_ICON_HEIGHT = 128; 389 SMALL_ICON_WIDTH = 128; 390 } 391 392 if (icons == null || icons.size() < 1) { //no icons passed in 393 platformWindow.setIcon(null); 394 return; 395 } 396 397 Image image = findBestImage(icons, SMALL_ICON_WIDTH, SMALL_ICON_HEIGHT); 398 if (image == null) { 399 //No images were found, possibly all are broken 400 return; 401 } 402 403 PushbroomScaler scaler = ScalerFactory.createScaler(image.getWidth(), image.getHeight(), 404 image.getBytesPerPixelUnit(), 405 SMALL_ICON_WIDTH, SMALL_ICON_HEIGHT, true); 406 407 //shrink the image and convert the format to INT_ARGB_PRE 408 ByteBuffer buf = (ByteBuffer) image.getPixelBuffer(); 409 byte bytes[] = new byte[buf.limit()]; 410 411 int iheight = image.getHeight(); 412 413 //Iterate through each scanline of the image 414 //and pass it one at a time to the scaling object 415 for (int z = 0; z < iheight; z++) { 416 buf.position(z*image.getScanlineStride()); 417 buf.get(bytes, 0, image.getScanlineStride()); 418 scaler.putSourceScanline(bytes, 0); 419 } 420 421 buf.rewind(); 422 423 final Image img = image.iconify(scaler.getDestination(), SMALL_ICON_WIDTH, SMALL_ICON_HEIGHT); 424 platformWindow.setIcon(PixelUtils.imageToPixels(img)); 425 } 426 427 @Override public void setTitle(String title) { 428 platformWindow.setTitle(title); 429 } 430 431 @Override public void setVisible(final boolean visible) { 432 // Before setting visible to false on the native window, we unblock 433 // other windows. 434 if (!visible) { 435 removeActiveWindow(this); 436 if (modality == Modality.WINDOW_MODAL) { 437 if (owner != null && owner instanceof WindowStage) { 438 ((WindowStage) owner).setEnabled(true); 439 } 440 } else if (modality == Modality.APPLICATION_MODAL) { 441 windowsSetEnabled(true); 442 } else { 443 // Note: This method is required to workaround a glass issue 444 // mentioned in RT-12607 445 // If the hiding stage is unfocusable (i.e. it's a PopupStage), 446 // then we don't do this to avoid stealing the focus. 447 if (!isPopupStage && owner != null && owner instanceof WindowStage) { 448 WindowStage ownerStage = (WindowStage)owner; 449 ownerStage.requestToFront(); 450 } 451 } 452 } 453 QuantumToolkit.runWithRenderLock(() -> { 454 // platformWindow can be null here, if this window is owned, 455 // and its owner is being closed. 456 if (platformWindow != null) { 457 platformWindow.setVisible(visible); 458 } 459 super.setVisible(visible); 460 return null; 461 }); 462 // After setting visible to true on the native window, we block 463 // other windows. 464 if (visible) { 465 if (modality == Modality.WINDOW_MODAL) { 466 if (owner != null && owner instanceof WindowStage) { 467 ((WindowStage) owner).setEnabled(false); 468 } 469 } else if (modality == Modality.APPLICATION_MODAL) { 470 windowsSetEnabled(false); 471 } 472 if (isAppletStage && null != appletWindow) { 473 appletWindow.assertStageOrder(); 474 } 475 } 476 477 applyFullScreen(); 478 } 479 480 @Override boolean isVisible() { 481 return platformWindow.isVisible(); 482 } 483 484 @Override public void setOpacity(float opacity) { 485 platformWindow.setAlpha(opacity); 486 GlassScene gs = getScene(); 487 if (gs != null) { 488 gs.entireSceneNeedsRepaint(); 489 } 490 } 491 492 public boolean needsUpdateWindow() { 493 return transparent && (Application.GetApplication().shouldUpdateWindow()); 494 } 495 496 @Override public void setIconified(boolean iconified) { 497 if (platformWindow.isMinimized() == iconified) { 498 return; 499 } 500 platformWindow.minimize(iconified); 501 } 502 503 @Override public void setMaximized(boolean maximized) { 504 if (platformWindow.isMaximized() == maximized) { 505 return; 506 } 507 platformWindow.maximize(maximized); 508 } 509 510 @Override 511 public void setAlwaysOnTop(boolean alwaysOnTop) { 512 // The securityDialog flag takes precedence over alwaysOnTop 513 if (securityDialog) return; 514 515 if (alwaysOnTop) { 516 if (hasPermission(alwaysOnTopPermission)) { 517 platformWindow.setLevel(Level.FLOATING); 518 } 519 } else { 520 platformWindow.setLevel(Level.NORMAL); 521 } 522 523 } 524 525 @Override public void setResizable(boolean resizable) { 526 platformWindow.setResizable(resizable); 527 // note: for child windows this is ignored and we fail silently 528 } 529 530 // Return true if this stage is trusted for full screen - doesn't have a 531 // security manager, or a permission check doesn't result in a security 532 // exeception. 533 boolean isTrustedFullScreen() { 534 return hasPermission(fullScreenPermission); 535 } 536 537 // Safely exit full screen 538 void exitFullScreen() { 539 setFullScreen(false); 540 } 541 542 boolean isApplet() { 543 return isPrimaryStage && null != appletWindow; 544 } 545 546 private boolean hasPermission(Permission perm) { 547 try { 548 if (System.getSecurityManager() != null) { 549 getAccessControlContext().checkPermission(perm); 550 } 551 return true; 552 } catch (AccessControlException ae) { 553 return false; 554 } 555 } 556 557 // We may need finer-grained permissions in the future, but for 558 // now AllPermission is good enough to do the job we need, such 559 // as fullscreen support for signed/unsigned application. 560 private static final Permission fullScreenPermission = new AllPermission(); 561 private static final Permission alwaysOnTopPermission = new AllPermission(); 562 563 private boolean fullScreenFromUserEvent = false; 564 565 private KeyCombination savedFullScreenExitKey = null; 566 567 public final KeyCombination getSavedFullScreenExitKey() { 568 return savedFullScreenExitKey; 569 } 570 571 private void applyFullScreen() { 572 if (platformWindow == null) { 573 // applyFullScreen() can be called from setVisible(false), while the 574 // platformWindow has already been destroyed. 575 return; 576 } 577 View v = platformWindow.getView(); 578 if (isVisible() && v != null && v.isInFullscreen() != isInFullScreen) { 579 if (isInFullScreen) { 580 // Check whether app is full screen trusted or flag is set 581 // indicating that the fullscreen request came from an input 582 // event handler. 583 // If not notify the stageListener to reset fullscreen to false. 584 final boolean isTrusted = isTrustedFullScreen(); 585 if (!isTrusted && !fullScreenFromUserEvent) { 586 exitFullScreen(); 587 } else { 588 v.enterFullscreen(false, false, false); 589 if (warning != null && warning.inWarningTransition()) { 590 warning.setView(getViewScene()); 591 } else { 592 boolean showWarning = true; 593 594 KeyCombination key = null; 595 String exitMessage = null; 596 597 if (isTrusted && (fxStage != null)) { 598 // copy the user set definitions for later use. 599 key = fxStage.getFullScreenExitKeyCombination(); 600 601 exitMessage = fxStage.getFullScreenExitHint(); 602 } 603 604 savedFullScreenExitKey = 605 key == null 606 ? defaultFullScreenExitKeycombo 607 : key; 608 609 if ( 610 // the hint is "" 611 "".equals(exitMessage) || 612 // if the key is NO_MATCH 613 (savedFullScreenExitKey.equals(KeyCombination.NO_MATCH)) 614 ) { 615 showWarning = false; 616 } 617 618 // the hint is not set, use the key for the message 619 if (showWarning && exitMessage == null) { 620 if (key == null) { 621 exitMessage = RESOURCES.getString("OverlayWarningESC"); 622 } else { 623 String f = RESOURCES.getString("OverlayWarningKey"); 624 exitMessage = f.format(f, savedFullScreenExitKey.toString()); 625 } 626 } 627 628 if (showWarning && warning == null) { 629 setWarning(new OverlayWarning(getViewScene())); 630 } 631 632 if (showWarning && warning != null) { 633 warning.warn(exitMessage); 634 } 635 } 636 } 637 } else { 638 if (warning != null) { 639 warning.cancel(); 640 setWarning(null); 641 } 642 v.exitFullscreen(false); 643 } 644 // Reset flag once we are done process fullscreen 645 fullScreenFromUserEvent = false; 646 } else if (!isVisible() && warning != null) { 647 // if the window is closed - re-open with fresh warning 648 warning.cancel(); 649 setWarning(null); 650 } 651 } 652 653 void setWarning(OverlayWarning newWarning) { 654 this.warning = newWarning; 655 getViewScene().synchroniseOverlayWarning(); 656 } 657 658 OverlayWarning getWarning() { 659 return warning; 660 } 661 662 @Override public void setFullScreen(boolean fullScreen) { 663 if (isInFullScreen == fullScreen) { 664 return; 665 } 666 667 // Set a flag indicating whether this method was called from 668 // an input event handler. 669 if (isInEventHandler()) { 670 fullScreenFromUserEvent = true; 671 } 672 673 GlassStage fsWindow = activeFSWindow.get(); 674 if (fullScreen && (fsWindow != null)) { 675 fsWindow.setFullScreen(false); 676 } 677 isInFullScreen = fullScreen; 678 applyFullScreen(); 679 if (fullScreen) { 680 activeFSWindow.set(this); 681 } 682 } 683 684 void fullscreenChanged(final boolean fs) { 685 if (!fs) { 686 if (activeFSWindow.compareAndSet(this, null)) { 687 isInFullScreen = false; 688 } 689 } else { 690 isInFullScreen = true; 691 activeFSWindow.set(this); 692 } 693 AccessController.doPrivileged((PrivilegedAction<Void>) () -> { 694 if (stageListener != null) { 695 stageListener.changedFullscreen(fs); 696 } 697 return null; 698 }, getAccessControlContext()); 699 } 700 701 @Override public void toBack() { 702 platformWindow.toBack(); 703 if (isAppletStage && null != appletWindow) { 704 appletWindow.assertStageOrder(); 705 } 706 } 707 708 @Override public void toFront() { 709 platformWindow.requestFocus(); // RT-17836 710 platformWindow.toFront(); 711 if (isAppletStage && null != appletWindow) { 712 appletWindow.assertStageOrder(); 713 } 714 } 715 716 @Override public void close() { 717 super.close(); 718 QuantumToolkit.runWithRenderLock(() -> { 719 // prevents closing a closed platform window 720 if (platformWindow != null) { 721 platformWindows.remove(platformWindow); 722 platformWindow.close(); 723 platformWindow = null; 724 } 725 GlassScene oldScene = getViewScene(); 726 if (oldScene != null) { 727 oldScene.updateSceneState(); 728 } 729 return null; 730 }); 731 } 732 733 // setPlatformWindowClosed is only set upon receiving platform window has 734 // closed notification. This state is necessary to prevent the platform 735 // window from being closed more than once. 736 void setPlatformWindowClosed() { 737 platformWindow = null; 738 } 739 740 static void addActiveWindow(WindowStage window) { 741 activeWindows.remove(window); 742 activeWindows.add(window); 743 } 744 745 static void removeActiveWindow(WindowStage window) { 746 activeWindows.remove(window); 747 } 748 749 final void handleFocusDisabled() { 750 if (activeWindows.isEmpty()) { 751 return; 752 } 753 WindowStage window = activeWindows.get(activeWindows.size() - 1); 754 window.setIconified(false); 755 window.requestToFront(); 756 window.requestFocus(); 757 } 758 759 @Override public boolean grabFocus() { 760 return platformWindow.grabFocus(); 761 } 762 763 @Override public void ungrabFocus() { 764 platformWindow.ungrabFocus(); 765 } 766 767 @Override public void requestFocus() { 768 platformWindow.requestFocus(); 769 } 770 771 @Override public void requestFocus(FocusCause cause) { 772 switch (cause) { 773 case TRAVERSED_FORWARD: 774 platformWindow.requestFocus(WindowEvent.FOCUS_GAINED_FORWARD); 775 break; 776 case TRAVERSED_BACKWARD: 777 platformWindow.requestFocus(WindowEvent.FOCUS_GAINED_BACKWARD); 778 break; 779 case ACTIVATED: 780 platformWindow.requestFocus(WindowEvent.FOCUS_GAINED); 781 break; 782 case DEACTIVATED: 783 platformWindow.requestFocus(WindowEvent.FOCUS_LOST); 784 break; 785 } 786 } 787 788 @Override 789 protected void setPlatformEnabled(boolean enabled) { 790 super.setPlatformEnabled(enabled); 791 platformWindow.setEnabled(enabled); 792 if (enabled) { 793 // Check if window is really enabled - to handle nested case 794 if (platformWindow.isEnabled()) { 795 requestToFront(); 796 } 797 } else { 798 removeActiveWindow(this); 799 } 800 } 801 802 void setEnabled(boolean enabled) { 803 if ((owner != null) && (owner instanceof WindowStage)) { 804 ((WindowStage) owner).setEnabled(enabled); 805 } 806 /* 807 * RT-17588 - exit if stage is closed from under us as 808 * any further access to the Glass layer 809 * will throw an exception 810 */ 811 if (enabled && platformWindow.isClosed()) { 812 return; 813 } 814 setPlatformEnabled(enabled); 815 if (enabled) { 816 if (isAppletStage && null != appletWindow) { 817 appletWindow.assertStageOrder(); 818 } 819 } 820 } 821 822 // Note: This method is required to workaround a glass issue mentioned in RT-12607 823 protected void requestToFront() { 824 platformWindow.toFront(); 825 platformWindow.requestFocus(); 826 } 827 828 public void setInEventHandler(boolean inEventHandler) { 829 this.inEventHandler = inEventHandler; 830 } 831 832 public boolean isInEventHandler() { 833 return inEventHandler; 834 } 835 836 @Override 837 public void requestInput(String text, int type, double width, double height, 838 double Mxx, double Mxy, double Mxz, double Mxt, 839 double Myx, double Myy, double Myz, double Myt, 840 double Mzx, double Mzy, double Mzz, double Mzt) { 841 platformWindow.requestInput(text, type, width, height, 842 Mxx, Mxy, Mxz, Mxt, 843 Myx, Myy, Myz, Myt, 844 Mzx, Mzy, Mzz, Mzt); 845 } 846 847 @Override 848 public void releaseInput() { 849 platformWindow.releaseInput(); 850 } 851 852 @Override public void setRTL(boolean b) { 853 rtl = b; 854 } 855 856 }