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