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 setVisible(final boolean visible) {
 407         // Before setting visible to false on the native window, we unblock
 408         // other windows.
 409         if (!visible) {
 410             removeActiveWindow(this);
 411             if (modality == Modality.WINDOW_MODAL) {
 412                 if (owner != null && owner instanceof WindowStage) {
 413                     ((WindowStage) owner).setEnabled(true);
 414                 }
 415             } else if (modality == Modality.APPLICATION_MODAL) {
 416                 windowsSetEnabled(true);
 417             } else {
 418                 // Note: This method is required to workaround a glass issue
 419                 // mentioned in RT-12607
 420                 if (owner != null && owner instanceof WindowStage) {
 421                     WindowStage ownerStage = (WindowStage)owner;
 422                     ownerStage.requestToFront();
 423                 }
 424             }
 425         }
 426         try {
 427             ViewPainter.renderLock.lock();
 428             // platformWindow can be null here, if this window is owned,
 429             // and its owner is being closed.
 430             if (platformWindow != null) {
 431                 platformWindow.setVisible(visible);
 432             }
 433             super.setVisible(visible);
 434         } finally {
 435             ViewPainter.renderLock.unlock();
 436         }
 437         // After setting visible to true on the native window, we block
 438         // other windows.
 439         if (visible) {
 440             if (modality == Modality.WINDOW_MODAL) {
 441                 if (owner != null && owner instanceof WindowStage) {
 442                     ((WindowStage) owner).setEnabled(false);
 443                 }
 444             } else if (modality == Modality.APPLICATION_MODAL) {
 445                 windowsSetEnabled(false);
 446             }
 447             if (isAppletStage && null != appletWindow) {
 448                 appletWindow.assertStageOrder();
 449             }
 450         }
 451         
 452         applyFullScreen();
 453     }
 454     
 455     @Override boolean isVisible() {
 456         return platformWindow.isVisible();
 457     }
 458     
 459     @Override public void setOpacity(float opacity) {
 460         platformWindow.setAlpha(opacity);
 461     }
 462 
 463     public boolean needsUpdateWindow() {
 464         return transparent && (Application.GetApplication().shouldUpdateWindow());
 465     }
 466 
 467     @Override public void setIconified(boolean iconified) {
 468         if (platformWindow.isMinimized() == iconified) {
 469             return;
 470         }
 471         platformWindow.minimize(iconified);
 472     }
 473 
 474     @Override public void setMaximized(boolean maximized) {
 475         if (platformWindow.isMaximized() == maximized) {
 476             return;
 477         }
 478         platformWindow.maximize(maximized);
 479     }
 480 
 481     @Override public void setResizable(boolean resizable) {
 482         platformWindow.setResizable(resizable);
 483         // note: for child windows this is ignored and we fail silently
 484     }
 485 
 486     // Return true if this stage is trusted for full screen - doesn't have a
 487     // security manager, or a permission check doesn't result in a security
 488     // exeception.
 489     boolean isTrustedFullScreen() {
 490         return hasPermission(fullScreenPermission);
 491     }
 492 
 493     // Safely exit full screen
 494     void exitFullScreen() {
 495         setFullScreen(false);
 496     }
 497     
 498     boolean isApplet() {
 499         return isPrimaryStage && null != appletWindow;
 500     }
 501 
 502     private boolean hasPermission(Permission perm) {
 503         try {
 504             if (System.getSecurityManager() != null) {
 505                 getAccessControlContext().checkPermission(perm);
 506             }
 507             return true;
 508         } catch (AccessControlException ae) {
 509             return false;
 510         }
 511     }
 512 
 513     // We may need finer-grained permissions in the future, but for
 514     // now AllPermission is good enough to do the job we need, such
 515     // as fullscreen support for signed/unsigned application.
 516     private static final Permission fullScreenPermission = new AllPermission();
 517 
 518     private boolean fullScreenFromUserEvent = false;
 519 
 520     private KeyCombination savedFullScreenExitKey = null;
 521 
 522     public final KeyCombination getSavedFullScreenExitKey() {
 523         return savedFullScreenExitKey;
 524     }
 525 
 526     private void applyFullScreen() {
 527         if (platformWindow == null) {
 528             // applyFullScreen() can be called from setVisible(false), while the
 529             // platformWindow has already been destroyed.
 530             return;
 531         }
 532         View v = platformWindow.getView();
 533         if (isVisible() && v != null && v.isInFullscreen() != isInFullScreen) {
 534             if (isInFullScreen) {
 535                 // Check whether app is full screen trusted or flag is set
 536                 // indicating that the fullscreen request came from an input
 537                 // event handler.
 538                 // If not notify the stageListener to reset fullscreen to false.
 539                 final boolean isTrusted = isTrustedFullScreen();
 540                 if (!isTrusted && !fullScreenFromUserEvent) {
 541                     exitFullScreen();
 542                 } else {
 543                     v.enterFullscreen(false, false, false);
 544                     if (warning != null && warning.inWarningTransition()) {
 545                         warning.setView(getViewScene());
 546                     } else {                        
 547                         boolean showWarning = true;
 548 
 549                         KeyCombination key = null;
 550                         String exitMessage = null;
 551 
 552                         if (isTrusted && (fxStage != null)) {
 553                             // copy the user set definitions for later use.
 554                             key = fxStage.getFullScreenExitKeyCombination();
 555 
 556                             exitMessage = fxStage.getFullScreenExitHint();
 557                         }
 558                         
 559                         savedFullScreenExitKey =
 560                                 key == null
 561                                 ? defaultFullScreenExitKeycombo
 562                                 : key;
 563 
 564                         // if the hint is "" dont show
 565                         if (exitMessage != null &&
 566                                 (exitMessage.equals(""))) {
 567                             showWarning = false;
 568                         }
 569 
 570                         // if the key is NO_MATCH dont show
 571                         if (savedFullScreenExitKey.equals(KeyCombination.NO_MATCH)) {
 572                             showWarning = false;
 573                         }
 574                         
 575                         if (showWarning && warning == null) {
 576                             warning = new OverlayWarning(getViewScene());
 577                         }
 578 
 579                         if (showWarning && warning != null) {
 580                             warning.warn(exitMessage);
 581                         }
 582                     }
 583                 }
 584             } else {
 585                 if (warning != null) {
 586                     warning.cancel();
 587                     warning = null;
 588                 }
 589                 v.exitFullscreen(false);
 590             }
 591             // Reset flag once we are done process fullscreen
 592             fullScreenFromUserEvent = false;
 593         } else if (!isVisible() && warning != null) {
 594             // if the window is closed - re-open with fresh warning
 595             warning.cancel();
 596             warning = null;
 597         }
 598     }
 599 
 600     @Override public void setFullScreen(boolean fullScreen) {
 601         if (isInFullScreen == fullScreen) {
 602             return;
 603         }
 604 
 605        // Set a flag indicating whether this method was called from
 606         // an input event handler.
 607         if (isInEventHandler()) {
 608             fullScreenFromUserEvent = true;
 609         }
 610 
 611         WindowStage fsWindow = activeFSWindow.get();
 612         if (fullScreen && (fsWindow != null)) {
 613             fsWindow.setFullScreen(false);
 614         }
 615         isInFullScreen = fullScreen;
 616         applyFullScreen();
 617         if (fullScreen) {
 618             activeFSWindow.set(this);
 619         }
 620     }
 621 
 622     void fullscreenChanged(final boolean fs) {
 623         if (!fs) {
 624             activeFSWindow.compareAndSet(this, null);
 625         }
 626         AccessController.doPrivileged(new PrivilegedAction<Void>() {
 627             @Override
 628             public Void run() {
 629                 if (stageListener != null) {
 630                     stageListener.changedFullscreen(fs);
 631                 }
 632                 return null;
 633             }
 634         }, getAccessControlContext());
 635     }
 636 
 637     @Override public void toBack() {
 638         platformWindow.toBack();
 639         if (isAppletStage && null != appletWindow) {
 640             appletWindow.assertStageOrder();
 641         }
 642     }
 643 
 644     @Override public void toFront() {
 645         platformWindow.requestFocus(); // RT-17836
 646         platformWindow.toFront();
 647         if (isAppletStage && null != appletWindow) {
 648             appletWindow.assertStageOrder();
 649         }
 650     }
 651     
 652     @Override public void close() {
 653         super.close();
 654         ViewPainter.renderLock.lock();
 655         try {
 656             // prevents closing a closed platform window
 657             if (platformWindow != null) {
 658                 platformWindows.remove(platformWindow);
 659                 platformWindow.close();
 660                 platformWindow = null;
 661             }
 662             GlassScene oldScene = getViewScene();
 663             if (oldScene != null) {
 664                 oldScene.updateSceneState();
 665             }
 666         } finally {
 667             ViewPainter.renderLock.unlock();
 668         }
 669     }
 670 
 671     // setPlatformWindowClosed is only set upon receiving platform window has
 672     // closed notification. This state is necessary to prevent the platform
 673     // window from being closed more than once.
 674     void setPlatformWindowClosed() {
 675         platformWindow = null;
 676     }
 677 
 678     static void addActiveWindow(WindowStage window) {
 679         activeWindows.remove(window);
 680         activeWindows.add(window);
 681     }
 682 
 683     static void removeActiveWindow(WindowStage window) {
 684         activeWindows.remove(window);
 685     }
 686 
 687     final void handleFocusDisabled() {
 688         if (activeWindows.isEmpty()) {
 689             return;
 690         }
 691         WindowStage window = activeWindows.get(activeWindows.size() - 1);
 692         window.setIconified(false);
 693         window.requestToFront();
 694         window.requestFocus();
 695     }
 696 
 697     @Override public boolean grabFocus() {
 698         return platformWindow.grabFocus();
 699     }
 700 
 701     @Override public void ungrabFocus() {
 702         platformWindow.ungrabFocus();
 703     }
 704 
 705     @Override public void requestFocus() {
 706         platformWindow.requestFocus();
 707     }
 708     
 709     @Override public void requestFocus(FocusCause cause) {
 710         switch (cause) {
 711             case TRAVERSED_FORWARD:
 712                 platformWindow.requestFocus(WindowEvent.FOCUS_GAINED_FORWARD);
 713                 break;
 714             case TRAVERSED_BACKWARD:
 715                 platformWindow.requestFocus(WindowEvent.FOCUS_GAINED_BACKWARD);
 716                 break;
 717             case ACTIVATED:
 718                 platformWindow.requestFocus(WindowEvent.FOCUS_GAINED);
 719                 break;
 720             case DEACTIVATED:
 721                 platformWindow.requestFocus(WindowEvent.FOCUS_LOST);
 722                 break;
 723         }
 724     }
 725 
 726     @Override
 727     protected void setPlatformEnabled(boolean enabled) {
 728         super.setPlatformEnabled(enabled);
 729         platformWindow.setEnabled(enabled);
 730         if (enabled) {
 731             requestToFront();
 732         } else {
 733             removeActiveWindow(this);
 734         }
 735     }
 736 
 737     void setEnabled(boolean enabled) {
 738         if ((owner != null) && (owner instanceof WindowStage)) {
 739             ((WindowStage) owner).setEnabled(enabled);
 740         }
 741         /*
 742          * RT-17588 - exit if stage is closed from under us as 
 743          *            any further access to the Glass layer 
 744          *            will throw an exception
 745          */
 746         if (enabled && platformWindow.isClosed()) {
 747             return;
 748         }
 749         setPlatformEnabled(enabled);
 750         if (enabled) {
 751             if (isAppletStage && null != appletWindow) {
 752                 appletWindow.assertStageOrder();
 753             }
 754         }
 755     }
 756 
 757     // Note: This method is required to workaround a glass issue mentioned in RT-12607
 758     protected void requestToFront() {
 759         platformWindow.toFront();
 760         platformWindow.requestFocus();
 761     }
 762 
 763     public void setInEventHandler(boolean inEventHandler) {
 764         this.inEventHandler = inEventHandler;
 765     }
 766 
 767     public boolean isInEventHandler() {
 768         return inEventHandler;
 769     }
 770 
 771     @Override
 772     public void requestInput(String text, int type, double width, double height, 
 773                         double Mxx, double Mxy, double Mxz, double Mxt,
 774                         double Myx, double Myy, double Myz, double Myt, 
 775                         double Mzx, double Mzy, double Mzz, double Mzt) {
 776         platformWindow.requestInput(text, type, width, height, 
 777                                     Mxx, Mxy, Mxz, Mxt, 
 778                                     Myx, Myy, Myz, Myt, 
 779                                     Mzx, Mzy, Mzz, Mzt);
 780     }
 781 
 782     @Override
 783     public void releaseInput() {
 784         platformWindow.releaseInput();
 785     }
 786 
 787     @Override public void setRTL(boolean b) {
 788         rtl = b;
 789     }
 790 
 791     /**
 792      * 
 793      * Accessibility glue for native
 794      * 
 795      */
 796     
 797     /**
 798      * Initialize Accessibility
 799      *
 800      * @param ac    the Glass accessible root object.
 801      */
 802     @Override public void setAccessibilityInitIsComplete(Object ac) {
 803         if (ac instanceof AccessibleRoot) {
 804             platformWindow.setAccessibilityInitIsComplete((AccessibleRoot)ac);
 805         } else {
 806             platformWindow.setAccessibilityInitIsComplete(null);
 807         }
 808     } 
 809 
 810     /**
 811      * Create accessible Glass object corresponding to stage
 812      * 
 813      * @param ac    the FX accessible root/stage node.
 814      * 
 815      * @return the Glass AccessibleRoot object.
 816      */
 817     @Override
 818     public Object accessibleCreateStageProvider(AccessibleStageProvider ac) {
 819         return AccessibleRoot.createAccessible(ac, platformWindow);
 820     }
 821 
 822     /**
 823      * Create Glass accessible object corresponding to controls
 824      * 
 825      * @param ac    the FX accessible node
 826      * 
 827      * @return the Glass accessible Object
 828      */
 829     @Override public Object accessibleCreateBasicProvider(AccessibleProvider ac) {
 830         return AccessibleBaseProvider.createProvider(ac);
 831     }
 832 
 833     /**
 834      * Delete Glass accessible object corresponding to controls
 835      *
 836      * @param glassAcc the Glass accessible
 837      */
 838     @Override public void accessibleDestroyBasicProvider(Object glassAcc) {
 839         if (glassAcc instanceof AccessibleBaseProvider) {
 840             ((AccessibleBaseProvider)glassAcc).destroyAccessible();
 841         }
 842     }
 843 
 844     /**
 845      * Fire accessible event
 846      *
 847      * @param glassAcc  the Glass accessible
 848      * @param eventID   identifies the event.
 849      */
 850     @Override public void accessibleFireEvent(Object glassAcc, int eventID) {
 851         if (glassAcc instanceof AccessibleBaseProvider) {
 852             ((AccessibleBaseProvider)glassAcc).fireEvent(eventID);
 853         }
 854     }
 855     
 856     /**
 857      * Fire accessible property change event
 858      * 
 859      * @param glassAcc      the Glass accessible 
 860      * @param propertyId    identifies the property
 861      * @param oldProperty   the old value of the property
 862      * @param newProperty   the new value of the property
 863      */
 864     @Override public void accessibleFirePropertyChange( Object glassAcc, int propertyId,
 865                                                         int oldProperty, int newProperty ) {
 866         if (glassAcc instanceof AccessibleBaseProvider) {
 867             ((AccessibleBaseProvider)glassAcc).
 868                 firePropertyChange(propertyId, oldProperty, newProperty);
 869         }
 870     }
 871     
 872     /**
 873      * Fire accessible property change event
 874      * 
 875      * @param glassAcc      the Glass accessible
 876      * @param propertyId    identifies the property
 877      * @param oldProperty   the old value of the property
 878      * @param newProperty   the new value of the property
 879      */
 880     @Override public void accessibleFirePropertyChange( Object glassAcc, int propertyId,
 881                                                         boolean oldProperty,
 882                                                         boolean newProperty ) {
 883         if (glassAcc instanceof AccessibleBaseProvider) {
 884             ((AccessibleBaseProvider)glassAcc).
 885                 firePropertyChange(propertyId, oldProperty, newProperty);
 886         }
 887     }
 888 }