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