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