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 float renderScaleX, float renderScaleY) 282 { 283 if (renderScaleX > 0.0 || renderScaleY > 0.0) { 284 // We set the render scale first since the call to setBounds() 285 // below can induce a recursive update on the scales if it moves 286 // the window to a new screen and we will then end up being called 287 // back with a new scale. We do not want to set these old scale 288 // values after that recursion happens. 289 if (renderScaleX > 0.0) { 290 platformWindow.setRenderScaleX(renderScaleX); 291 } 292 if (renderScaleY > 0.0) { 293 platformWindow.setRenderScaleY(renderScaleY); 294 } 295 ViewScene vscene = getViewScene(); 296 if (vscene != null) { 297 vscene.updateSceneState(); 298 vscene.entireSceneNeedsRepaint(); 299 } 300 } 301 if (isAppletStage) { 302 xSet = ySet = false; 303 } 304 float pScaleX = platformWindow.getPlatformScaleX(); 305 float pScaleY = platformWindow.getPlatformScaleY(); 306 int px, py; 307 if (xSet || ySet) { 308 Screen screen = platformWindow.getScreen(); 309 List<Screen> screens = Screen.getScreens(); 310 if (screens.size() > 1) { 311 float winfxW = (w > 0) ? w : (platformWindow.getWidth() / pScaleX); 312 float winfxH = (h > 0) ? h : (platformWindow.getHeight()/ pScaleY); 313 float winfxX = xSet ? x : 314 screen.getX() + (platformWindow.getX() - screen.getPlatformX()) / pScaleX; 315 float winfxY = ySet ? y : 316 screen.getY() + (platformWindow.getY() - screen.getPlatformY()) / pScaleY; 317 float winfxCX = winfxX + winfxW/2f; 318 float winfxCY = winfxY + winfxH/2f; 319 // If the center point of the window (winfxCX,Y) is on any 320 // screen, then use that screen. Otherwise, use the screen 321 // whose center is closest to the window center. 322 int scrX = screen.getX(); 323 int scrY = screen.getY(); 324 int scrW = screen.getWidth(); 325 int scrH = screen.getHeight(); 326 if (winfxCX < scrX || 327 winfxCY < scrY || 328 winfxCX >= scrX + scrW || 329 winfxCY >= scrY + scrH) 330 { 331 float relx = scrX + scrW / 2.0f - winfxCX; 332 float rely = scrY + scrH / 2.0f - winfxCY; 333 float distsq = relx * relx + rely * rely; 334 for (Screen s : Screen.getScreens()) { 335 scrX = s.getX(); 336 scrY = s.getY(); 337 scrW = s.getWidth(); 338 scrH = s.getHeight(); 339 if (winfxCX >= scrX && 340 winfxCY >= scrY && 341 winfxCX < scrX + scrW && 342 winfxCY < scrY + scrH) 343 { 344 screen = s; 345 break; 346 } 347 relx = scrX + scrW / 2.0f - winfxCX; 348 rely = scrY + scrH / 2.0f - winfxCY; 349 float distsq2 = relx * relx + rely * rely; 350 if (distsq2 < distsq) { 351 screen = s; 352 distsq = distsq2; 353 } 354 } 355 } 356 } 357 float sx = screen == null ? 0 : screen.getX(); 358 float sy = screen == null ? 0 : screen.getY(); 359 float sScaleX = screen.getPlatformScaleX(); 360 float sScaleY = screen.getPlatformScaleY(); 361 px = xSet ? Math.round(screen.getPlatformX() + (x - sx) * sScaleX) : 0; 362 py = ySet ? Math.round(screen.getPlatformY() + (y - sy) * sScaleY) : 0; 363 } else { 364 px = py = 0; 365 } 366 int pw = (int) (w > 0 ? Math.ceil(w * pScaleX) : w); 367 int ph = (int) (h > 0 ? Math.ceil(h * pScaleY) : h); 368 int pcw = (int) (cw > 0 ? Math.ceil(cw * pScaleX) : cw); 369 int pch = (int) (ch > 0 ? Math.ceil(ch * pScaleY) : ch); 370 platformWindow.setBounds(px, py, xSet, ySet, 371 pw, ph, pcw, pch, 372 xGravity, yGravity); 373 } 374 375 @Override 376 public float getPlatformScaleX() { 377 return platformWindow.getPlatformScaleX(); 378 } 379 380 @Override 381 public float getPlatformScaleY() { 382 return platformWindow.getPlatformScaleY(); 383 } 384 385 @Override 386 public float getOutputScaleX() { 387 return platformWindow.getOutputScaleX(); 388 } 389 390 @Override 391 public float getOutputScaleY() { 392 return platformWindow.getOutputScaleY(); 393 } 394 395 @Override public void setMinimumSize(int minWidth, int minHeight) { 396 minWidth = (int) Math.ceil(minWidth * getPlatformScaleX()); 397 minHeight = (int) Math.ceil(minHeight * getPlatformScaleY()); 398 platformWindow.setMinimumSize(minWidth, minHeight); 399 } 400 401 @Override public void setMaximumSize(int maxWidth, int maxHeight) { 402 maxWidth = (int) Math.ceil(maxWidth * getPlatformScaleX()); 403 maxHeight = (int) Math.ceil(maxHeight * getPlatformScaleY()); 404 platformWindow.setMaximumSize(maxWidth, maxHeight); 405 } 406 407 static Image findBestImage(java.util.List icons, int width, int height) { 408 Image image = null; 409 double bestSimilarity = 3; //Impossibly high value 410 for (Object icon : icons) { 411 //Iterate imageList looking for best matching image. 412 //'Similarity' measure is defined as good scale factor and small insets. 413 //best possible similarity is 0 (no scale, no insets). 414 //It's found by experimentation that good-looking results are achieved 415 //with scale factors x1, x3/4, x2/3, xN, x1/N. 416 //Check to make sure the image/image format is correct. 417 Image im = (Image)icon; 418 if (im == null || !(im.getPixelFormat() == PixelFormat.BYTE_RGB || 419 im.getPixelFormat() == PixelFormat.BYTE_BGRA_PRE || 420 im.getPixelFormat() == PixelFormat.BYTE_GRAY)) 421 { 422 continue; 423 } 424 425 int iw = im.getWidth(); 426 int ih = im.getHeight(); 427 428 if (iw > 0 && ih > 0) { 429 //Calc scale factor 430 double scaleFactor = Math.min((double)width / (double)iw, 431 (double)height / (double)ih); 432 //Calculate scaled image dimensions 433 //adjusting scale factor to nearest "good" value 434 int adjw; 435 int adjh; 436 double scaleMeasure = 1; //0 - best (no) scale, 1 - impossibly bad 437 if (scaleFactor >= 2) { 438 //Need to enlarge image more than twice 439 //Round down scale factor to multiply by integer value 440 scaleFactor = Math.floor(scaleFactor); 441 adjw = iw * (int)scaleFactor; 442 adjh = ih * (int)scaleFactor; 443 scaleMeasure = 1.0 - 0.5 / scaleFactor; 444 } else if (scaleFactor >= 1) { 445 //Don't scale 446 scaleFactor = 1.0; 447 adjw = iw; 448 adjh = ih; 449 scaleMeasure = 0; 450 } else if (scaleFactor >= 0.75) { 451 //Multiply by 3/4 452 scaleFactor = 0.75; 453 adjw = iw * 3 / 4; 454 adjh = ih * 3 / 4; 455 scaleMeasure = 0.3; 456 } else if (scaleFactor >= 0.6666) { 457 //Multiply by 2/3 458 scaleFactor = 0.6666; 459 adjw = iw * 2 / 3; 460 adjh = ih * 2 / 3; 461 scaleMeasure = 0.33; 462 } else { 463 //Multiply size by 1/scaleDivider 464 //where scaleDivider is minimum possible integer 465 //larger than 1/scaleFactor 466 double scaleDivider = Math.ceil(1.0 / scaleFactor); 467 scaleFactor = 1.0 / scaleDivider; 468 adjw = (int)Math.round((double)iw / scaleDivider); 469 adjh = (int)Math.round((double)ih / scaleDivider); 470 scaleMeasure = 1.0 - 1.0 / scaleDivider; 471 } 472 double similarity = ((double)width - (double)adjw) / (double)width + 473 ((double)height - (double)adjh) / (double)height + //Large padding is bad 474 scaleMeasure; //Large rescale is bad 475 if (similarity < bestSimilarity) { 476 bestSimilarity = similarity; 477 image = im; 478 } 479 if (similarity == 0) break; 480 } 481 } 482 return image; 483 } 484 485 @Override public void setIcons(java.util.List icons) { 486 487 int SMALL_ICON_HEIGHT = 32; 488 int SMALL_ICON_WIDTH = 32; 489 if (PlatformUtil.isMac()) { //Mac Sized Icons 490 SMALL_ICON_HEIGHT = 128; 491 SMALL_ICON_WIDTH = 128; 492 } else if (PlatformUtil.isWindows()) { //Windows Sized Icons 493 SMALL_ICON_HEIGHT = 32; 494 SMALL_ICON_WIDTH = 32; 495 } else if (PlatformUtil.isLinux()) { //Linux icons 496 SMALL_ICON_HEIGHT = 128; 497 SMALL_ICON_WIDTH = 128; 498 } 499 500 if (icons == null || icons.size() < 1) { //no icons passed in 501 platformWindow.setIcon(null); 502 return; 503 } 504 505 Image image = findBestImage(icons, SMALL_ICON_WIDTH, SMALL_ICON_HEIGHT); 506 if (image == null) { 507 //No images were found, possibly all are broken 508 return; 509 } 510 511 PushbroomScaler scaler = ScalerFactory.createScaler(image.getWidth(), image.getHeight(), 512 image.getBytesPerPixelUnit(), 513 SMALL_ICON_WIDTH, SMALL_ICON_HEIGHT, true); 514 515 //shrink the image and convert the format to INT_ARGB_PRE 516 ByteBuffer buf = (ByteBuffer) image.getPixelBuffer(); 517 byte bytes[] = new byte[buf.limit()]; 518 519 int iheight = image.getHeight(); 520 521 //Iterate through each scanline of the image 522 //and pass it one at a time to the scaling object 523 for (int z = 0; z < iheight; z++) { 524 buf.position(z*image.getScanlineStride()); 525 buf.get(bytes, 0, image.getScanlineStride()); 526 scaler.putSourceScanline(bytes, 0); 527 } 528 529 buf.rewind(); 530 531 final Image img = image.iconify(scaler.getDestination(), SMALL_ICON_WIDTH, SMALL_ICON_HEIGHT); 532 platformWindow.setIcon(PixelUtils.imageToPixels(img)); 533 } 534 535 @Override public void setTitle(String title) { 536 platformWindow.setTitle(title); 537 } 538 539 @Override public void setVisible(final boolean visible) { 540 // Before setting visible to false on the native window, we unblock 541 // other windows. 542 if (!visible) { 543 removeActiveWindow(this); 544 if (modality == Modality.WINDOW_MODAL) { 545 if (owner != null && owner instanceof WindowStage) { 546 ((WindowStage) owner).setEnabled(true); 547 } 548 } else if (modality == Modality.APPLICATION_MODAL) { 549 windowsSetEnabled(true); 550 } else { 551 // Note: This method is required to workaround a glass issue 552 // mentioned in RT-12607 553 // If the hiding stage is unfocusable (i.e. it's a PopupStage), 554 // then we don't do this to avoid stealing the focus. 555 if (!isPopupStage && owner != null && owner instanceof WindowStage) { 556 WindowStage ownerStage = (WindowStage)owner; 557 ownerStage.requestToFront(); 558 } 559 } 560 } 561 QuantumToolkit.runWithRenderLock(() -> { 562 // platformWindow can be null here, if this window is owned, 563 // and its owner is being closed. 564 if (platformWindow != null) { 565 platformWindow.setVisible(visible); 566 } 567 super.setVisible(visible); 568 return null; 569 }); 570 // After setting visible to true on the native window, we block 571 // other windows. 572 if (visible) { 573 if (modality == Modality.WINDOW_MODAL) { 574 if (owner != null && owner instanceof WindowStage) { 575 ((WindowStage) owner).setEnabled(false); 576 } 577 } else if (modality == Modality.APPLICATION_MODAL) { 578 windowsSetEnabled(false); 579 } 580 if (isAppletStage && null != appletWindow) { 581 appletWindow.assertStageOrder(); 582 } 583 } 584 585 applyFullScreen(); 586 } 587 588 @Override boolean isVisible() { 589 return platformWindow.isVisible(); 590 } 591 592 @Override public void setOpacity(float opacity) { 593 platformWindow.setAlpha(opacity); 594 GlassScene gs = getScene(); 595 if (gs != null) { 596 gs.entireSceneNeedsRepaint(); 597 } 598 } 599 600 public boolean needsUpdateWindow() { 601 return transparent && (Application.GetApplication().shouldUpdateWindow()); 602 } 603 604 @Override public void setIconified(boolean iconified) { 605 if (platformWindow.isMinimized() == iconified) { 606 return; 607 } 608 platformWindow.minimize(iconified); 609 } 610 611 @Override public void setMaximized(boolean maximized) { 612 if (platformWindow.isMaximized() == maximized) { 613 return; 614 } 615 platformWindow.maximize(maximized); 616 } 617 618 @Override 619 public void setAlwaysOnTop(boolean alwaysOnTop) { 620 // The securityDialog flag takes precedence over alwaysOnTop 621 if (securityDialog) return; 622 623 if (alwaysOnTop) { 624 if (hasPermission(alwaysOnTopPermission)) { 625 platformWindow.setLevel(Level.FLOATING); 626 } 627 } else { 628 platformWindow.setLevel(Level.NORMAL); 629 } 630 631 } 632 633 @Override public void setResizable(boolean resizable) { 634 platformWindow.setResizable(resizable); 635 // note: for child windows this is ignored and we fail silently 636 } 637 638 // Return true if this stage is trusted for full screen - doesn't have a 639 // security manager, or a permission check doesn't result in a security 640 // exeception. 641 boolean isTrustedFullScreen() { 642 return hasPermission(fullScreenPermission); 643 } 644 645 // Safely exit full screen 646 void exitFullScreen() { 647 setFullScreen(false); 648 } 649 650 boolean isApplet() { 651 return isPrimaryStage && null != appletWindow; 652 } 653 654 private boolean hasPermission(Permission perm) { 655 try { 656 final SecurityManager sm = System.getSecurityManager(); 657 if (sm != null) { 658 sm.checkPermission(perm, getAccessControlContext()); 659 } 660 return true; 661 } catch (SecurityException se) { 662 return false; 663 } 664 } 665 666 // We may need finer-grained permissions in the future, but for 667 // now AllPermission is good enough to do the job we need, such 668 // as fullscreen support for signed/unsigned application. 669 private static final Permission fullScreenPermission = new AllPermission(); 670 private static final Permission alwaysOnTopPermission = new AllPermission(); 671 672 private boolean fullScreenFromUserEvent = false; 673 674 private KeyCombination savedFullScreenExitKey = null; 675 676 public final KeyCombination getSavedFullScreenExitKey() { 677 return savedFullScreenExitKey; 678 } 679 680 private void applyFullScreen() { 681 if (platformWindow == null) { 682 // applyFullScreen() can be called from setVisible(false), while the 683 // platformWindow has already been destroyed. 684 return; 685 } 686 View v = platformWindow.getView(); 687 if (isVisible() && v != null && v.isInFullscreen() != isInFullScreen) { 688 if (isInFullScreen) { 689 // Check whether app is full screen trusted or flag is set 690 // indicating that the fullscreen request came from an input 691 // event handler. 692 // If not notify the stageListener to reset fullscreen to false. 693 final boolean isTrusted = isTrustedFullScreen(); 694 if (!isTrusted && !fullScreenFromUserEvent) { 695 exitFullScreen(); 696 fullscreenChanged(false); 697 } else { 698 v.enterFullscreen(false, false, false); 699 if (warning != null && warning.inWarningTransition()) { 700 warning.setView(getViewScene()); 701 } else { 702 boolean showWarning = true; 703 704 KeyCombination key = null; 705 String exitMessage = null; 706 707 if (isTrusted && (fxStage != null)) { 708 // copy the user set definitions for later use. 709 key = fxStage.getFullScreenExitKeyCombination(); 710 711 exitMessage = fxStage.getFullScreenExitHint(); 712 } 713 714 savedFullScreenExitKey = 715 key == null 716 ? defaultFullScreenExitKeycombo 717 : key; 718 719 if ( 720 // the hint is "" 721 "".equals(exitMessage) || 722 // if the key is NO_MATCH 723 (savedFullScreenExitKey.equals(KeyCombination.NO_MATCH)) 724 ) { 725 showWarning = false; 726 } 727 728 // the hint is not set, use the key for the message 729 if (showWarning && exitMessage == null) { 730 if (key == null) { 731 exitMessage = RESOURCES.getString("OverlayWarningESC"); 732 } else { 733 String f = RESOURCES.getString("OverlayWarningKey"); 734 exitMessage = f.format(f, savedFullScreenExitKey.toString()); 735 } 736 } 737 738 if (showWarning && warning == null) { 739 setWarning(new OverlayWarning(getViewScene())); 740 } 741 742 if (showWarning && warning != null) { 743 warning.warn(exitMessage); 744 } 745 } 746 } 747 } else { 748 if (warning != null) { 749 warning.cancel(); 750 setWarning(null); 751 } 752 v.exitFullscreen(false); 753 } 754 // Reset flag once we are done process fullscreen 755 fullScreenFromUserEvent = false; 756 } else if (!isVisible() && warning != null) { 757 // if the window is closed - re-open with fresh warning 758 warning.cancel(); 759 setWarning(null); 760 } 761 } 762 763 void setWarning(OverlayWarning newWarning) { 764 this.warning = newWarning; 765 getViewScene().synchroniseOverlayWarning(); 766 } 767 768 OverlayWarning getWarning() { 769 return warning; 770 } 771 772 @Override public void setFullScreen(boolean fullScreen) { 773 if (isInFullScreen == fullScreen) { 774 return; 775 } 776 777 // Set a flag indicating whether this method was called from 778 // an input event handler. 779 if (isInEventHandler()) { 780 fullScreenFromUserEvent = true; 781 } 782 783 GlassStage fsWindow = activeFSWindow.get(); 784 if (fullScreen && (fsWindow != null)) { 785 fsWindow.setFullScreen(false); 786 } 787 isInFullScreen = fullScreen; 788 applyFullScreen(); 789 if (fullScreen) { 790 activeFSWindow.set(this); 791 } 792 } 793 794 void fullscreenChanged(final boolean fs) { 795 if (!fs) { 796 if (activeFSWindow.compareAndSet(this, null)) { 797 isInFullScreen = false; 798 } 799 } else { 800 isInFullScreen = true; 801 activeFSWindow.set(this); 802 } 803 AccessController.doPrivileged((PrivilegedAction<Void>) () -> { 804 if (stageListener != null) { 805 stageListener.changedFullscreen(fs); 806 } 807 return null; 808 }, getAccessControlContext()); 809 } 810 811 @Override public void toBack() { 812 platformWindow.toBack(); 813 if (isAppletStage && null != appletWindow) { 814 appletWindow.assertStageOrder(); 815 } 816 } 817 818 @Override public void toFront() { 819 platformWindow.requestFocus(); // RT-17836 820 platformWindow.toFront(); 821 if (isAppletStage && null != appletWindow) { 822 appletWindow.assertStageOrder(); 823 } 824 } 825 826 @Override public void close() { 827 super.close(); 828 QuantumToolkit.runWithRenderLock(() -> { 829 // prevents closing a closed platform window 830 if (platformWindow != null) { 831 platformWindows.remove(platformWindow); 832 platformWindow.close(); 833 platformWindow = null; 834 } 835 GlassScene oldScene = getViewScene(); 836 if (oldScene != null) { 837 oldScene.updateSceneState(); 838 } 839 return null; 840 }); 841 } 842 843 // setPlatformWindowClosed is only set upon receiving platform window has 844 // closed notification. This state is necessary to prevent the platform 845 // window from being closed more than once. 846 void setPlatformWindowClosed() { 847 platformWindow = null; 848 } 849 850 static void addActiveWindow(WindowStage window) { 851 activeWindows.remove(window); 852 activeWindows.add(window); 853 } 854 855 static void removeActiveWindow(WindowStage window) { 856 activeWindows.remove(window); 857 } 858 859 final void handleFocusDisabled() { 860 if (activeWindows.isEmpty()) { 861 return; 862 } 863 WindowStage window = activeWindows.get(activeWindows.size() - 1); 864 window.setIconified(false); 865 window.requestToFront(); 866 window.requestFocus(); 867 } 868 869 @Override public boolean grabFocus() { 870 return platformWindow.grabFocus(); 871 } 872 873 @Override public void ungrabFocus() { 874 platformWindow.ungrabFocus(); 875 } 876 877 @Override public void requestFocus() { 878 platformWindow.requestFocus(); 879 } 880 881 @Override public void requestFocus(FocusCause cause) { 882 switch (cause) { 883 case TRAVERSED_FORWARD: 884 platformWindow.requestFocus(WindowEvent.FOCUS_GAINED_FORWARD); 885 break; 886 case TRAVERSED_BACKWARD: 887 platformWindow.requestFocus(WindowEvent.FOCUS_GAINED_BACKWARD); 888 break; 889 case ACTIVATED: 890 platformWindow.requestFocus(WindowEvent.FOCUS_GAINED); 891 break; 892 case DEACTIVATED: 893 platformWindow.requestFocus(WindowEvent.FOCUS_LOST); 894 break; 895 } 896 } 897 898 @Override 899 protected void setPlatformEnabled(boolean enabled) { 900 super.setPlatformEnabled(enabled); 901 platformWindow.setEnabled(enabled); 902 if (enabled) { 903 // Check if window is really enabled - to handle nested case 904 if (platformWindow.isEnabled()) { 905 requestToFront(); 906 } 907 } else { 908 removeActiveWindow(this); 909 } 910 } 911 912 void setEnabled(boolean enabled) { 913 if ((owner != null) && (owner instanceof WindowStage)) { 914 ((WindowStage) owner).setEnabled(enabled); 915 } 916 /* 917 * RT-17588 - exit if stage is closed from under us as 918 * any further access to the Glass layer 919 * will throw an exception 920 */ 921 if (enabled && (platformWindow == null || platformWindow.isClosed())) { 922 return; 923 } 924 setPlatformEnabled(enabled); 925 if (enabled) { 926 if (isAppletStage && null != appletWindow) { 927 appletWindow.assertStageOrder(); 928 } 929 } 930 } 931 932 // Note: This method is required to workaround a glass issue mentioned in RT-12607 933 protected void requestToFront() { 934 if (platformWindow != null) { 935 platformWindow.toFront(); 936 platformWindow.requestFocus(); 937 } 938 } 939 940 public void setInEventHandler(boolean inEventHandler) { 941 this.inEventHandler = inEventHandler; 942 } 943 944 public boolean isInEventHandler() { 945 return inEventHandler; 946 } 947 948 @Override 949 public void requestInput(String text, int type, double width, double height, 950 double Mxx, double Mxy, double Mxz, double Mxt, 951 double Myx, double Myy, double Myz, double Myt, 952 double Mzx, double Mzy, double Mzz, double Mzt) { 953 platformWindow.requestInput(text, type, width, height, 954 Mxx, Mxy, Mxz, Mxt, 955 Myx, Myy, Myz, Myt, 956 Mzx, Mzy, Mzz, Mzt); 957 } 958 959 @Override 960 public void releaseInput() { 961 platformWindow.releaseInput(); 962 } 963 964 @Override public void setRTL(boolean b) { 965 rtl = b; 966 } 967 968 }