1 /*
   2  * Copyright (c) 2008, 2013, 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 import java.util.concurrent.atomic.AtomicReference;
  40 
  41 import javafx.scene.input.KeyCombination;
  42 import javafx.stage.Modality;
  43 import javafx.stage.Stage;
  44 import javafx.stage.StageStyle;
  45 
  46 import com.sun.glass.events.WindowEvent;
  47 import com.sun.glass.ui.*;
  48 import com.sun.glass.ui.accessible.AccessibleBaseProvider;
  49 import com.sun.glass.ui.accessible.AccessibleRoot;
  50 import com.sun.javafx.PlatformUtil;
  51 import com.sun.javafx.iio.common.PushbroomScaler;
  52 import com.sun.javafx.iio.common.ScalerFactory;
  53 import com.sun.javafx.tk.FocusCause;
  54 import com.sun.javafx.tk.TKScene;
  55 import com.sun.javafx.tk.TKStage;
  56 import com.sun.prism.Image;
  57 import com.sun.prism.PixelFormat;
  58 import com.sun.javafx.accessible.providers.AccessibleProvider;
  59 import com.sun.javafx.accessible.providers.AccessibleStageProvider;
  60 
  61 class WindowStage extends GlassStage {
  62 
  63     protected Window platformWindow;
  64 
  65     protected javafx.stage.Stage fxStage;
  66 
  67     private StageStyle style;
  68     private GlassStage owner = null;
  69     private Modality modality = Modality.NONE;
  70 
  71     private OverlayWarning warning = null;
  72     private boolean rtl = false;
  73     private boolean transparent = false;
  74     private boolean isPrimaryStage = false;
  75     private boolean isAppletStage = false; // true if this is an embedded applet window
  76     private boolean isPopupStage = false;
  77     private boolean isInFullScreen = false;
  78 
  79     // A flag to indicate whether a call was generated from
  80     // an input event handler.
  81     private boolean inEventHandler = false;
  82 
  83     private static final AtomicReference<WindowStage> activeFSWindow = new AtomicReference<>();
  84 
  85     // An active window is visible && enabled && focusable.
  86     // The list is maintained in the z-order, so that the last element
  87     // represents the topmost window (or more accurately, the last
  88     // focused window, which we assume is very close to the last topmost one).
  89     private static List<WindowStage> activeWindows = new LinkedList<>();
  90 
  91     private static Map<Window, WindowStage> platformWindows = new HashMap<>();
  92 
  93     private static GlassAppletWindow appletWindow = null;
  94     static void setAppletWindow(GlassAppletWindow aw) {
  95         appletWindow = aw;
  96     }
  97     static GlassAppletWindow getAppletWindow() {
  98         return appletWindow;
  99     }
 100 
 101     public WindowStage(javafx.stage.Window peerWindow, final StageStyle stageStyle, Modality modality, TKStage owner) {
 102         this.style = stageStyle;
 103         this.owner = (GlassStage)owner;
 104         this.modality = modality;
 105 
 106         if (peerWindow instanceof javafx.stage.Stage) {
 107             fxStage = (Stage)peerWindow;
 108         } else {
 109             fxStage = null;
 110         }
 111 
 112         transparent = stageStyle == StageStyle.TRANSPARENT;
 113         if (owner == null) {
 114             if (this.modality == Modality.WINDOW_MODAL) {
 115                 this.modality = Modality.NONE;
 116             }
 117         }
 118     }
 119 
 120     final void setIsPrimary() {
 121         isPrimaryStage = true;
 122         if (appletWindow != null) {
 123             // this is an embedded applet stage
 124             isAppletStage = true;
 125         }
 126     }
 127 
 128     final void setIsPopup() {
 129         isPopupStage = true;
 130     }
 131 
 132     // Called by QuantumToolkit, so we can override initPlatformWindow in subclasses
 133     public final WindowStage init(GlassSystemMenu sysmenu) {
 134         initPlatformWindow();
 135         platformWindow.setEventHandler(new GlassWindowEventHandler(this));
 136         if (sysmenu.isSupported()) {
 137             sysmenu.createMenuBar();
 138             platformWindow.setMenuBar(sysmenu.getMenuBar());
 139         }
 140         return this;
 141     }
 142 
 143     private void initPlatformWindow() {
 144         if (platformWindow == null) {
 145             Application app = Application.GetApplication();
 146             if (isPrimaryStage && (null != appletWindow)) {
 147                 platformWindow = app.createWindow(appletWindow.getGlassWindow().getNativeWindow());
 148             } else {
 149                 Window ownerWindow = null;
 150                 if (owner instanceof WindowStage) {
 151                     ownerWindow = ((WindowStage)owner).platformWindow;
 152                 }
 153                 boolean resizable = false;
 154                 boolean focusable = true;
 155                 int windowMask = rtl ? Window.RIGHT_TO_LEFT : 0;
 156                 if (isPopupStage) { // TODO: make it a stage style?
 157                     windowMask |= Window.TRANSPARENT | Window.POPUP;
 158                     focusable = false;
 159                 } else {
 160                     switch (style) {
 161                         case UNIFIED:
 162                             if (app.supportsUnifiedWindows()) {
 163                                 windowMask |= Window.UNIFIED;
 164                             }
 165                             // fall through
 166                         case DECORATED:
 167                             windowMask |=
 168                                     Window.TITLED | Window.CLOSABLE | Window.MINIMIZABLE | Window.MAXIMIZABLE;
 169                             resizable = true;
 170                             break;
 171                         case UTILITY:
 172                             windowMask |=  Window.TITLED | Window.UTILITY | Window.CLOSABLE;
 173                             break;
 174                         default:
 175                             windowMask |=
 176                                     (transparent ? Window.TRANSPARENT : Window.UNTITLED) | Window.CLOSABLE;
 177                             break;
 178                     }
 179                 }
 180                 platformWindow =
 181                         app.createWindow(ownerWindow, Screen.getMainScreen(), windowMask);
 182                 platformWindow.setResizable(resizable);
 183                 platformWindow.setFocusable(focusable);
 184             }
 185         }
 186         platformWindows.put(platformWindow, this);
 187     }
 188 
 189     final Window getPlatformWindow() {
 190         return platformWindow;
 191     }
 192 
 193     static WindowStage findWindowStage(Window platformWindow) {
 194         return platformWindows.get(platformWindow);
 195     }
 196 
 197     protected GlassStage getOwner() {
 198         return owner;
 199     }
 200     
 201     protected ViewScene getViewScene() {
 202         return (ViewScene)getScene();
 203     }
 204     
 205     StageStyle getStyle() {
 206         return style;
 207     }
 208 
 209     @Override public TKScene createTKScene(boolean depthBuffer, boolean antiAliasing, AccessControlContext acc) {
 210         ViewScene scene = new ViewScene(depthBuffer, antiAliasing);
 211         scene.setSecurityContext(acc);
 212         return scene;
 213     }
 214     
 215     /**
 216      * Set the scene to be displayed in this stage
 217      *
 218      * @param scene The peer of the scene to be displayed
 219      */
 220     @Override public void setScene(TKScene scene) {
 221         GlassScene oldScene = getScene();
 222         super.setScene(scene);
 223         if (scene != null) {
 224             GlassScene newScene = getViewScene();
 225             View view = newScene.getPlatformView();
 226             ViewPainter.renderLock.lock();
 227             try {
 228                 platformWindow.setView(view);
 229                 if (oldScene != null) oldScene.updateSceneState();
 230                 newScene.updateSceneState();
 231             } finally {
 232                 ViewPainter.renderLock.unlock();
 233             }
 234             requestFocus();
 235             applyFullScreen();
 236         } else {
 237             ViewPainter.renderLock.lock();
 238             try {
 239                 // platformWindow can be null here, if this window is owned,
 240                 // and its owner is being closed.
 241                 if (platformWindow != null) {
 242                     platformWindow.setView(null);
 243                 }
 244                 if (oldScene != null) {
 245                     oldScene.updateSceneState();
 246                 }
 247             } finally {
 248                 ViewPainter.renderLock.unlock();
 249             }
 250         }
 251         if (oldScene != null) {
 252             ViewPainter painter = ((ViewScene)oldScene).getPainter();
 253             QuantumRenderer.getInstance().disposePresentable(painter.presentable);   // latched on RT
 254         }
 255     }
 256     
 257     @Override public void setBounds(float x, float y, boolean xSet, boolean ySet,
 258                                     float w, float h, float cw, float ch,
 259                                     float xGravity, float yGravity)
 260     {
 261         if (isAppletStage) {
 262             xSet = ySet = false;
 263         }
 264         platformWindow.setBounds((int)x, (int)y, xSet, ySet, 
 265                                  (int)w, (int)h, (int)cw, (int)ch, 
 266                                  xGravity, yGravity);
 267     }
 268 
 269     @Override public void setMinimumSize(int minWidth, int minHeight) {
 270         platformWindow.setMinimumSize(minWidth, minHeight);
 271     }
 272 
 273     @Override public void setMaximumSize(int maxWidth, int maxHeight) {
 274         platformWindow.setMaximumSize(maxWidth, maxHeight);
 275     }
 276 
 277     @Override public void setIcons(java.util.List icons) {
 278 
 279         int SMALL_ICON_HEIGHT = 32;
 280         int SMALL_ICON_WIDTH = 32;
 281         if (PlatformUtil.isMac()) { //Mac Sized Icons
 282             SMALL_ICON_HEIGHT = 128;
 283             SMALL_ICON_WIDTH = 128;
 284         } else if (PlatformUtil.isWindows()) { //Windows Sized Icons
 285             SMALL_ICON_HEIGHT = 32;
 286             SMALL_ICON_WIDTH = 32;
 287         } else if (PlatformUtil.isLinux()) { //Linux icons
 288             SMALL_ICON_HEIGHT = 128;
 289             SMALL_ICON_WIDTH = 128;
 290         }
 291 
 292         if (icons == null || icons.size() < 1) { //no icons passed in
 293             return;
 294         }
 295 
 296         int width = platformWindow.getWidth();
 297         int height = platformWindow.getHeight();
 298 
 299         Image image = null;
 300         double bestSimilarity = 3; //Impossibly high value
 301         for (int i = 0; i < icons.size(); i++) {
 302             //Iterate imageList looking for best matching image.
 303             //'Similarity' measure is defined as good scale factor and small insets.
 304             //best possible similarity is 0 (no scale, no insets).
 305             //It's found by experimentation that good-looking results are achieved
 306             //with scale factors x1, x3/4, x2/3, xN, x1/N.
 307             //Check to make sure the image/image format is correct.
 308             Image im = (Image)icons.get(i);
 309             if (im == null || !(im.getPixelFormat() == PixelFormat.BYTE_RGB ||
 310                 im.getPixelFormat() == PixelFormat.BYTE_BGRA_PRE ||
 311                 im.getPixelFormat() == PixelFormat.BYTE_GRAY))
 312             {
 313                 continue;
 314             }
 315 
 316             int iw = im.getWidth();
 317             int ih = im.getHeight();
 318 
 319             if (iw > 0 && ih > 0) {
 320                 //Calc scale factor
 321                 double scaleFactor = Math.min((double)SMALL_ICON_WIDTH / (double)iw,
 322                                               (double)SMALL_ICON_HEIGHT / (double)ih);
 323                 //Calculate scaled image dimensions
 324                 //adjusting scale factor to nearest "good" value
 325                 int adjw;
 326                 int adjh;
 327                 double scaleMeasure = 1; //0 - best (no) scale, 1 - impossibly bad
 328                 if (scaleFactor >= 2) {
 329                     //Need to enlarge image more than twice
 330                     //Round down scale factor to multiply by integer value
 331                     scaleFactor = Math.floor(scaleFactor);
 332                     adjw = iw * (int)scaleFactor;
 333                     adjh = ih * (int)scaleFactor;
 334                     scaleMeasure = 1.0 - 0.5 / scaleFactor;
 335                 } else if (scaleFactor >= 1) {
 336                     //Don't scale
 337                     scaleFactor = 1.0;
 338                     adjw = iw;
 339                     adjh = ih;
 340                     scaleMeasure = 0;
 341                 } else if (scaleFactor >= 0.75) {
 342                     //Multiply by 3/4
 343                     scaleFactor = 0.75;
 344                     adjw = iw * 3 / 4;
 345                     adjh = ih * 3 / 4;
 346                     scaleMeasure = 0.3;
 347                 } else if (scaleFactor >= 0.6666) {
 348                     //Multiply by 2/3
 349                     scaleFactor = 0.6666;
 350                     adjw = iw * 2 / 3;
 351                     adjh = ih * 2 / 3;
 352                     scaleMeasure = 0.33;
 353                 } else {
 354                     //Multiply size by 1/scaleDivider
 355                     //where scaleDivider is minimum possible integer
 356                     //larger than 1/scaleFactor
 357                     double scaleDivider = Math.ceil(1.0 / scaleFactor);
 358                     scaleFactor = 1.0 / scaleDivider;
 359                     adjw = (int)Math.round((double)iw / scaleDivider);
 360                     adjh = (int)Math.round((double)ih / scaleDivider);
 361                     scaleMeasure = 1.0 - 1.0 / scaleDivider;
 362                 }
 363                 double similarity = ((double)width - (double)adjw) / (double)width +
 364                     ((double)height - (double)adjh) / (double)height + //Large padding is bad
 365                     scaleMeasure; //Large rescale is bad
 366                 if (similarity < bestSimilarity) {
 367                     image = im;
 368                 }
 369                 if (similarity == 0) break;
 370             }
 371         }
 372 
 373         if (image == null) {
 374             //No images were found, possibly all are broken
 375             return;
 376         }
 377 
 378         PushbroomScaler scaler = ScalerFactory.createScaler(image.getWidth(), image.getHeight(),
 379                                                             image.getBytesPerPixelUnit(),
 380                                                             SMALL_ICON_WIDTH, SMALL_ICON_HEIGHT, true);
 381 
 382         //shrink the image and convert the format to INT_ARGB_PRE
 383         ByteBuffer buf = (ByteBuffer) image.getPixelBuffer();
 384         byte bytes[] = new byte[buf.limit()];
 385 
 386         int iheight = image.getHeight();
 387 
 388         //Iterate through each scanline of the image
 389         //and pass it one at a time to the scaling object
 390         for (int z = 0; z < iheight; z++) {
 391             buf.position(z*image.getScanlineStride());
 392             buf.get(bytes, 0, image.getScanlineStride());
 393             scaler.putSourceScanline(bytes, 0);
 394         }
 395 
 396         buf.rewind();
 397 
 398         final Image img = image.iconify(scaler.getDestination(), SMALL_ICON_WIDTH, SMALL_ICON_HEIGHT);
 399         platformWindow.setIcon(PixelUtils.imageToPixels(img));
 400     }
 401 
 402     @Override public void setTitle(String title) {
 403         platformWindow.setTitle(title);
 404     }
 405 
 406     @Override public void setUpdatesCursor(boolean updatesCursor) {
 407         platformWindow.setUpdatesCursor(updatesCursor);
 408     }
 409 
 410     @Override public void setVisible(final boolean visible) {
 411         // Before setting visible to false on the native window, we unblock
 412         // other windows.
 413         if (!visible) {
 414             removeActiveWindow(this);
 415             if (modality == Modality.WINDOW_MODAL) {
 416                 if (owner != null && owner instanceof WindowStage) {
 417                     ((WindowStage) owner).setEnabled(true);
 418                 }
 419             } else if (modality == Modality.APPLICATION_MODAL) {
 420                 windowsSetEnabled(true);
 421             } else {
 422                 // Note: This method is required to workaround a glass issue
 423                 // mentioned in RT-12607
 424                 if (owner != null && owner instanceof WindowStage) {
 425                     WindowStage ownerStage = (WindowStage)owner;
 426                     ownerStage.requestToFront();
 427                 }
 428             }
 429         }
 430         try {
 431             ViewPainter.renderLock.lock();
 432             // platformWindow can be null here, if this window is owned,
 433             // and its owner is being closed.
 434             if (platformWindow != null) {
 435                 platformWindow.setVisible(visible);
 436             }
 437             super.setVisible(visible);
 438         } finally {
 439             ViewPainter.renderLock.unlock();
 440         }
 441         // After setting visible to true on the native window, we block
 442         // other windows.
 443         if (visible) {
 444             if (modality == Modality.WINDOW_MODAL) {
 445                 if (owner != null && owner instanceof WindowStage) {
 446                     ((WindowStage) owner).setEnabled(false);
 447                 }
 448             } else if (modality == Modality.APPLICATION_MODAL) {
 449                 windowsSetEnabled(false);
 450             }
 451             if (isAppletStage && null != appletWindow) {
 452                 appletWindow.assertStageOrder();
 453             }
 454         }
 455         
 456         applyFullScreen();
 457     }
 458     
 459     @Override boolean isVisible() {
 460         return platformWindow.isVisible();
 461     }
 462     
 463     @Override public void setOpacity(float opacity) {
 464         platformWindow.setAlpha(opacity);
 465     }
 466 
 467     public boolean needsUpdateWindow() {
 468         return transparent && (Application.GetApplication().shouldUpdateWindow());
 469     }
 470 
 471     @Override public void setIconified(boolean iconified) {
 472         if (platformWindow.isMinimized() == iconified) {
 473             return;
 474         }
 475         platformWindow.minimize(iconified);
 476     }
 477 
 478     @Override public void setMaximized(boolean maximized) {
 479         if (platformWindow.isMaximized() == maximized) {
 480             return;
 481         }
 482         platformWindow.maximize(maximized);
 483     }
 484 
 485     @Override public void setResizable(boolean resizable) {
 486         platformWindow.setResizable(resizable);
 487         // note: for child windows this is ignored and we fail silently
 488     }
 489 
 490     // Return true if this stage is trusted for full screen - doesn't have a
 491     // security manager, or a permission check doesn't result in a security
 492     // exeception.
 493     boolean isTrustedFullScreen() {
 494         return hasPermission(fullScreenPermission);
 495     }
 496 
 497     // Safely exit full screen
 498     void exitFullScreen() {
 499         setFullScreen(false);
 500     }
 501     
 502     boolean isApplet() {
 503         return isPrimaryStage && null != appletWindow;
 504     }
 505 
 506     private boolean hasPermission(Permission perm) {
 507         try {
 508             if (System.getSecurityManager() != null) {
 509                 getAccessControlContext().checkPermission(perm);
 510             }
 511             return true;
 512         } catch (AccessControlException ae) {
 513             return false;
 514         }
 515     }
 516 
 517     // We may need finer-grained permissions in the future, but for
 518     // now AllPermission is good enough to do the job we need, such
 519     // as fullscreen support for signed/unsigned application.
 520     private static final Permission fullScreenPermission = new AllPermission();
 521 
 522     private boolean fullScreenFromUserEvent = false;
 523 
 524     private KeyCombination savedFullScreenExitKey = null;
 525 
 526     public final KeyCombination getSavedFullScreenExitKey() {
 527         return savedFullScreenExitKey;
 528     }
 529 
 530     private void applyFullScreen() {
 531         if (platformWindow == null) {
 532             // applyFullScreen() can be called from setVisible(false), while the
 533             // platformWindow has already been destroyed.
 534             return;
 535         }
 536         View v = platformWindow.getView();
 537         if (isVisible() && v != null && v.isInFullscreen() != isInFullScreen) {
 538             if (isInFullScreen) {
 539                 // Check whether app is full screen trusted or flag is set
 540                 // indicating that the fullscreen request came from an input
 541                 // event handler.
 542                 // If not notify the stageListener to reset fullscreen to false.
 543                 final boolean isTrusted = isTrustedFullScreen();
 544                 if (!isTrusted && !fullScreenFromUserEvent) {
 545                     exitFullScreen();
 546                 } else {
 547                     v.enterFullscreen(false, false, false);
 548                     if (warning != null && warning.inWarningTransition()) {
 549                         warning.setView(getViewScene());
 550                     } else {                        
 551                         boolean showWarning = true;
 552 
 553                         KeyCombination key = null;
 554                         String exitMessage = null;
 555 
 556                         if (isTrusted && (fxStage != null)) {
 557                             // copy the user set definitions for later use.
 558                             key = fxStage.getFullScreenExitKeyCombination();
 559 
 560                             exitMessage = fxStage.getFullScreenExitHint();
 561                         }
 562                         
 563                         savedFullScreenExitKey =
 564                                 key == null
 565                                 ? defaultFullScreenExitKeycombo
 566                                 : key;
 567 
 568                         // if the hint is "" dont show
 569                         if (exitMessage != null &&
 570                                 (exitMessage.equals(""))) {
 571                             showWarning = false;
 572                         }
 573 
 574                         // if the key is NO_MATCH dont show
 575                         if (savedFullScreenExitKey.equals(KeyCombination.NO_MATCH)) {
 576                             showWarning = false;
 577                         }
 578                         
 579                         if (showWarning && warning == null) {
 580                             warning = new OverlayWarning(getViewScene());
 581                         }
 582 
 583                         if (showWarning && warning != null) {
 584                             warning.warn(exitMessage);
 585                         }
 586                     }
 587                 }
 588             } else {
 589                 if (warning != null) {
 590                     warning.cancel();
 591                     warning = null;
 592                 }
 593                 v.exitFullscreen(false);
 594             }
 595             // Reset flag once we are done process fullscreen
 596             fullScreenFromUserEvent = false;
 597         } else if (!isVisible() && warning != null) {
 598             // if the window is closed - re-open with fresh warning
 599             warning.cancel();
 600             warning = null;
 601         }
 602     }
 603 
 604     @Override public void setFullScreen(boolean fullScreen) {
 605         if (isInFullScreen == fullScreen) {
 606             return;
 607         }
 608 
 609        // Set a flag indicating whether this method was called from
 610         // an input event handler.
 611         if (isInEventHandler()) {
 612             fullScreenFromUserEvent = true;
 613         }
 614 
 615         WindowStage fsWindow = activeFSWindow.get();
 616         if (fullScreen && (fsWindow != null)) {
 617             fsWindow.setFullScreen(false);
 618         }
 619         isInFullScreen = fullScreen;
 620         applyFullScreen();
 621         if (fullScreen) {
 622             activeFSWindow.set(this);
 623         }
 624     }
 625 
 626     void fullscreenChanged(final boolean fs) {
 627         if (!fs) {
 628             activeFSWindow.compareAndSet(this, null);
 629         }
 630         AccessController.doPrivileged(new PrivilegedAction<Void>() {
 631             @Override
 632             public Void run() {
 633                 if (stageListener != null) {
 634                     stageListener.changedFullscreen(fs);
 635                 }
 636                 return null;
 637             }
 638         }, getAccessControlContext());
 639     }
 640 
 641     @Override public void toBack() {
 642         platformWindow.toBack();
 643         if (isAppletStage && null != appletWindow) {
 644             appletWindow.assertStageOrder();
 645         }
 646     }
 647 
 648     @Override public void toFront() {
 649         platformWindow.requestFocus(); // RT-17836
 650         platformWindow.toFront();
 651         if (isAppletStage && null != appletWindow) {
 652             appletWindow.assertStageOrder();
 653         }
 654     }
 655     
 656     @Override public void close() {
 657         super.close();
 658         ViewPainter.renderLock.lock();
 659         try {
 660             // prevents closing a closed platform window
 661             if (platformWindow != null) {
 662                 platformWindows.remove(platformWindow);
 663                 platformWindow.close();
 664                 platformWindow = null;
 665             }
 666             GlassScene oldScene = getViewScene();
 667             if (oldScene != null) {
 668                 oldScene.updateSceneState();
 669             }
 670         } finally {
 671             ViewPainter.renderLock.unlock();
 672         }
 673     }
 674 
 675     // setPlatformWindowClosed is only set upon receiving platform window has
 676     // closed notification. This state is necessary to prevent the platform
 677     // window from being closed more than once.
 678     void setPlatformWindowClosed() {
 679         platformWindow = null;
 680     }
 681 
 682     static void addActiveWindow(WindowStage window) {
 683         activeWindows.remove(window);
 684         activeWindows.add(window);
 685     }
 686 
 687     static void removeActiveWindow(WindowStage window) {
 688         activeWindows.remove(window);
 689     }
 690 
 691     final void handleFocusDisabled() {
 692         if (activeWindows.isEmpty()) {
 693             return;
 694         }
 695         WindowStage window = activeWindows.get(activeWindows.size() - 1);
 696         window.setIconified(false);
 697         window.requestToFront();
 698         window.requestFocus();
 699     }
 700 
 701     @Override public boolean grabFocus() {
 702         return platformWindow.grabFocus();
 703     }
 704 
 705     @Override public void ungrabFocus() {
 706         platformWindow.ungrabFocus();
 707     }
 708 
 709     @Override public void requestFocus() {
 710         platformWindow.requestFocus();
 711     }
 712     
 713     @Override public void requestFocus(FocusCause cause) {
 714         switch (cause) {
 715             case TRAVERSED_FORWARD:
 716                 platformWindow.requestFocus(WindowEvent.FOCUS_GAINED_FORWARD);
 717                 break;
 718             case TRAVERSED_BACKWARD:
 719                 platformWindow.requestFocus(WindowEvent.FOCUS_GAINED_BACKWARD);
 720                 break;
 721             case ACTIVATED:
 722                 platformWindow.requestFocus(WindowEvent.FOCUS_GAINED);
 723                 break;
 724             case DEACTIVATED:
 725                 platformWindow.requestFocus(WindowEvent.FOCUS_LOST);
 726                 break;
 727         }
 728     }
 729 
 730     @Override
 731     protected void setPlatformEnabled(boolean enabled) {
 732         super.setPlatformEnabled(enabled);
 733         platformWindow.setEnabled(enabled);
 734         if (enabled) {
 735             requestToFront();
 736         } else {
 737             removeActiveWindow(this);
 738         }
 739     }
 740 
 741     void setEnabled(boolean enabled) {
 742         if ((owner != null) && (owner instanceof WindowStage)) {
 743             ((WindowStage) owner).setEnabled(enabled);
 744         }
 745         /*
 746          * RT-17588 - exit if stage is closed from under us as 
 747          *            any further access to the Glass layer 
 748          *            will throw an exception
 749          */
 750         if (enabled && platformWindow.isClosed()) {
 751             return;
 752         }
 753         setPlatformEnabled(enabled);
 754         if (enabled) {
 755             if (isAppletStage && null != appletWindow) {
 756                 appletWindow.assertStageOrder();
 757             }
 758         }
 759     }
 760 
 761     // Note: This method is required to workaround a glass issue mentioned in RT-12607
 762     protected void requestToFront() {
 763         platformWindow.toFront();
 764         platformWindow.requestFocus();
 765     }
 766 
 767     public void setInEventHandler(boolean inEventHandler) {
 768         this.inEventHandler = inEventHandler;
 769     }
 770 
 771     public boolean isInEventHandler() {
 772         return inEventHandler;
 773     }
 774 
 775     @Override
 776     public void requestInput(String text, int type, double width, double height, 
 777                         double Mxx, double Mxy, double Mxz, double Mxt,
 778                         double Myx, double Myy, double Myz, double Myt, 
 779                         double Mzx, double Mzy, double Mzz, double Mzt) {
 780         platformWindow.requestInput(text, type, width, height, 
 781                                     Mxx, Mxy, Mxz, Mxt, 
 782                                     Myx, Myy, Myz, Myt, 
 783                                     Mzx, Mzy, Mzz, Mzt);
 784     }
 785 
 786     @Override
 787     public void releaseInput() {
 788         platformWindow.releaseInput();
 789     }
 790 
 791     @Override public void setRTL(boolean b) {
 792         rtl = b;
 793     }
 794 
 795     /**
 796      * 
 797      * Accessibility glue for native
 798      * 
 799      */
 800     
 801     /**
 802      * Initialize Accessibility
 803      *
 804      * @param ac    the Glass accessible root object.
 805      */
 806     @Override public void setAccessibilityInitIsComplete(Object ac) {
 807         if (ac instanceof AccessibleRoot) {
 808             platformWindow.setAccessibilityInitIsComplete((AccessibleRoot)ac);
 809         } else {
 810             platformWindow.setAccessibilityInitIsComplete(null);
 811         }
 812     } 
 813 
 814     /**
 815      * Create accessible Glass object corresponding to stage
 816      * 
 817      * @param ac    the FX accessible root/stage node.
 818      * 
 819      * @return the Glass AccessibleRoot object.
 820      */
 821     @Override
 822     public Object accessibleCreateStageProvider(AccessibleStageProvider ac) {
 823         return AccessibleRoot.createAccessible(ac, platformWindow);
 824     }
 825 
 826     /**
 827      * Create Glass accessible object corresponding to controls
 828      * 
 829      * @param ac    the FX accessible node
 830      * 
 831      * @return the Glass accessible Object
 832      */
 833     @Override public Object accessibleCreateBasicProvider(AccessibleProvider ac) {
 834         return AccessibleBaseProvider.createProvider(ac);
 835     }
 836 
 837     /**
 838      * Delete Glass accessible object corresponding to controls
 839      *
 840      * @param glassAcc the Glass accessible
 841      */
 842     @Override public void accessibleDestroyBasicProvider(Object glassAcc) {
 843         if (glassAcc instanceof AccessibleBaseProvider) {
 844             ((AccessibleBaseProvider)glassAcc).destroyAccessible();
 845         }
 846     }
 847 
 848     /**
 849      * Fire accessible event
 850      *
 851      * @param glassAcc  the Glass accessible
 852      * @param eventID   identifies the event.
 853      */
 854     @Override public void accessibleFireEvent(Object glassAcc, int eventID) {
 855         if (glassAcc instanceof AccessibleBaseProvider) {
 856             ((AccessibleBaseProvider)glassAcc).fireEvent(eventID);
 857         }
 858     }
 859     
 860     /**
 861      * Fire accessible property change event
 862      * 
 863      * @param glassAcc      the Glass accessible 
 864      * @param propertyId    identifies the property
 865      * @param oldProperty   the old value of the property
 866      * @param newProperty   the new value of the property
 867      */
 868     @Override public void accessibleFirePropertyChange( Object glassAcc, int propertyId,
 869                                                         int oldProperty, int newProperty ) {
 870         if (glassAcc instanceof AccessibleBaseProvider) {
 871             ((AccessibleBaseProvider)glassAcc).
 872                 firePropertyChange(propertyId, oldProperty, newProperty);
 873         }
 874     }
 875     
 876     /**
 877      * Fire accessible property change event
 878      * 
 879      * @param glassAcc      the Glass accessible
 880      * @param propertyId    identifies the property
 881      * @param oldProperty   the old value of the property
 882      * @param newProperty   the new value of the property
 883      */
 884     @Override public void accessibleFirePropertyChange( Object glassAcc, int propertyId,
 885                                                         boolean oldProperty,
 886                                                         boolean newProperty ) {
 887         if (glassAcc instanceof AccessibleBaseProvider) {
 888             ((AccessibleBaseProvider)glassAcc).
 889                 firePropertyChange(propertyId, oldProperty, newProperty);
 890         }
 891     }
 892 }