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 }