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