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