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