1 /* 2 * Copyright (c) 1996, 2016, 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 package sun.awt.windows; 26 27 import java.awt.*; 28 import java.awt.event.*; 29 import java.awt.image.*; 30 import java.awt.peer.*; 31 32 import java.beans.*; 33 34 import java.util.*; 35 import java.util.List; 36 import sun.util.logging.PlatformLogger; 37 38 import sun.awt.*; 39 40 import sun.java2d.pipe.Region; 41 42 public class WWindowPeer extends WPanelPeer implements WindowPeer, 43 DisplayChangedListener 44 { 45 46 private static final PlatformLogger log = PlatformLogger.getLogger("sun.awt.windows.WWindowPeer"); 47 private static final PlatformLogger screenLog = PlatformLogger.getLogger("sun.awt.windows.screen.WWindowPeer"); 48 49 // we can't use WDialogPeer as blocker may be an instance of WPrintDialogPeer that 50 // extends WWindowPeer, not WDialogPeer 51 private WWindowPeer modalBlocker = null; 52 53 private boolean isOpaque; 54 55 private TranslucentWindowPainter painter; 56 57 /* 58 * A key used for storing a list of active windows in AppContext. The value 59 * is a list of windows, sorted by the time of activation: later a window is 60 * activated, greater its index is in the list. 61 */ 62 private static final StringBuffer ACTIVE_WINDOWS_KEY = 63 new StringBuffer("active_windows_list"); 64 65 /* 66 * Listener for 'activeWindow' KFM property changes. It is added to each 67 * AppContext KFM. See ActiveWindowListener inner class below. 68 */ 69 private static PropertyChangeListener activeWindowListener = 70 new ActiveWindowListener(); 71 72 /* 73 * The object is a listener for the AppContext.GUI_DISPOSED property. 74 */ 75 private static final PropertyChangeListener guiDisposedListener = 76 new GuiDisposedListener(); 77 78 /* 79 * Called (on the Toolkit thread) before the appropriate 80 * WindowStateEvent is posted to the EventQueue. 81 */ 82 private WindowListener windowListener; 83 84 /** 85 * Initialize JNI field IDs 86 */ 87 private static native void initIDs(); 88 static { 89 initIDs(); 90 } 91 92 // WComponentPeer overrides 93 @Override 94 @SuppressWarnings("unchecked") 95 protected void disposeImpl() { 96 AppContext appContext = SunToolkit.targetToAppContext(target); 97 synchronized (appContext) { 98 List<WWindowPeer> l = (List<WWindowPeer>)appContext.get(ACTIVE_WINDOWS_KEY); 99 if (l != null) { 100 l.remove(this); 101 } 102 } 103 104 // Remove ourself from the Map of DisplayChangeListeners 105 GraphicsConfiguration gc = getGraphicsConfiguration(); 106 ((Win32GraphicsDevice)gc.getDevice()).removeDisplayChangedListener(this); 107 108 synchronized (getStateLock()) { 109 TranslucentWindowPainter currentPainter = painter; 110 if (currentPainter != null) { 111 currentPainter.flush(); 112 // don't set the current one to null here; reduces the chances of 113 // MT issues (like NPEs) 114 } 115 } 116 117 super.disposeImpl(); 118 } 119 120 // WindowPeer implementation 121 122 @Override 123 public void toFront() { 124 updateFocusableWindowState(); 125 _toFront(); 126 } 127 private native void _toFront(); 128 129 @Override 130 public native void toBack(); 131 132 private native void setAlwaysOnTopNative(boolean value); 133 134 public void setAlwaysOnTop(boolean value) { 135 if ((value && ((Window)target).isVisible()) || !value) { 136 setAlwaysOnTopNative(value); 137 } 138 } 139 140 @Override 141 public void updateAlwaysOnTopState() { 142 setAlwaysOnTop(((Window)target).isAlwaysOnTop()); 143 } 144 145 @Override 146 public void updateFocusableWindowState() { 147 setFocusableWindow(((Window)target).isFocusableWindow()); 148 } 149 native void setFocusableWindow(boolean value); 150 151 // FramePeer & DialogPeer partial shared implementation 152 153 public void setTitle(String title) { 154 // allow a null title to pass as an empty string. 155 if (title == null) { 156 title = ""; 157 } 158 _setTitle(title); 159 } 160 private native void _setTitle(String title); 161 162 public void setResizable(boolean resizable) { 163 _setResizable(resizable); 164 } 165 166 private native void _setResizable(boolean resizable); 167 168 // Toolkit & peer internals 169 170 WWindowPeer(Window target) { 171 super(target); 172 } 173 174 @Override 175 void initialize() { 176 super.initialize(); 177 178 updateInsets(insets_); 179 180 if (!((Window) target).isFontSet()) { 181 ((Window) target).setFont(defaultFont); 182 setFont(defaultFont); 183 } 184 185 // Express our interest in display changes 186 GraphicsConfiguration gc = getGraphicsConfiguration(); 187 ((Win32GraphicsDevice)gc.getDevice()).addDisplayChangedListener(this); 188 189 initActiveWindowsTracking((Window)target); 190 191 updateIconImages(); 192 193 Shape shape = ((Window)target).getShape(); 194 if (shape != null) { 195 applyShape(Region.getInstance(shape, null)); 196 } 197 198 float opacity = ((Window)target).getOpacity(); 199 if (opacity < 1.0f) { 200 setOpacity(opacity); 201 } 202 203 synchronized (getStateLock()) { 204 // default value of a boolean field is 'false', so set isOpaque to 205 // true here explicitly 206 this.isOpaque = true; 207 setOpaque(((Window)target).isOpaque()); 208 } 209 } 210 211 native void createAwtWindow(WComponentPeer parent); 212 213 private volatile Window.Type windowType = Window.Type.NORMAL; 214 215 // This method must be called for Window, Dialog, and Frame before creating 216 // the hwnd 217 void preCreate(WComponentPeer parent) { 218 windowType = ((Window)target).getType(); 219 } 220 221 @Override 222 void create(WComponentPeer parent) { 223 preCreate(parent); 224 createAwtWindow(parent); 225 } 226 227 @Override 228 final WComponentPeer getNativeParent() { 229 final Container owner = ((Window) target).getOwner(); 230 return (WComponentPeer) WToolkit.targetToPeer(owner); 231 } 232 233 // should be overriden in WDialogPeer 234 protected void realShow() { 235 super.show(); 236 } 237 238 @Override 239 public void show() { 240 updateFocusableWindowState(); 241 242 boolean alwaysOnTop = ((Window)target).isAlwaysOnTop(); 243 244 // Fix for 4868278. 245 // If we create a window with a specific GraphicsConfig, and then move it with 246 // setLocation() or setBounds() to another one before its peer has been created, 247 // then calling Window.getGraphicsConfig() returns wrong config. That may lead 248 // to some problems like wrong-placed tooltips. It is caused by calling 249 // super.displayChanged() in WWindowPeer.displayChanged() regardless of whether 250 // GraphicsDevice was really changed, or not. So we need to track it here. 251 updateGC(); 252 253 realShow(); 254 updateMinimumSize(); 255 256 if (((Window)target).isAlwaysOnTopSupported() && alwaysOnTop) { 257 setAlwaysOnTop(alwaysOnTop); 258 } 259 260 synchronized (getStateLock()) { 261 if (!isOpaque) { 262 updateWindow(true); 263 } 264 } 265 266 // See https://javafx-jira.kenai.com/browse/RT-32570 267 WComponentPeer owner = getNativeParent(); 268 if (owner != null && owner.isLightweightFramePeer()) { 269 Rectangle b = getBounds(); 270 handleExpose(0, 0, b.width, b.height); 271 } 272 } 273 274 // Synchronize the insets members (here & in helper) with actual window 275 // state. 276 native void updateInsets(Insets i); 277 278 static native int getSysMinWidth(); 279 static native int getSysMinHeight(); 280 static native int getSysIconWidth(); 281 static native int getSysIconHeight(); 282 static native int getSysSmIconWidth(); 283 static native int getSysSmIconHeight(); 284 /**windows/classes/sun/awt/windows/ 285 * Creates native icon from specified raster data and updates 286 * icon for window and all descendant windows that inherit icon. 287 * Raster data should be passed in the ARGB form. 288 * Note that raster data format was changed to provide support 289 * for XP icons with alpha-channel 290 */ 291 native void setIconImagesData(int[] iconRaster, int w, int h, 292 int[] smallIconRaster, int smw, int smh); 293 294 synchronized native void reshapeFrame(int x, int y, int width, int height); 295 296 native Dimension getNativeWindowSize(); 297 298 public Dimension getScaledWindowSize() { 299 return getNativeWindowSize(); 300 } 301 302 public boolean requestWindowFocus(CausedFocusEvent.Cause cause) { 303 if (!focusAllowedFor()) { 304 return false; 305 } 306 return requestWindowFocus(cause == CausedFocusEvent.Cause.MOUSE_EVENT); 307 } 308 private native boolean requestWindowFocus(boolean isMouseEventCause); 309 310 public boolean focusAllowedFor() { 311 Window window = (Window)this.target; 312 if (!window.isVisible() || 313 !window.isEnabled() || 314 !window.isFocusableWindow()) 315 { 316 return false; 317 } 318 if (isModalBlocked()) { 319 return false; 320 } 321 return true; 322 } 323 324 @Override 325 void hide() { 326 WindowListener listener = windowListener; 327 if (listener != null) { 328 // We're not getting WINDOW_CLOSING from the native code when hiding 329 // the window programmatically. So, create it and notify the listener. 330 listener.windowClosing(new WindowEvent((Window)target, WindowEvent.WINDOW_CLOSING)); 331 } 332 super.hide(); 333 } 334 335 // WARNING: it's called on the Toolkit thread! 336 @Override 337 void preprocessPostEvent(AWTEvent event) { 338 if (event instanceof WindowEvent) { 339 WindowListener listener = windowListener; 340 if (listener != null) { 341 switch(event.getID()) { 342 case WindowEvent.WINDOW_CLOSING: 343 listener.windowClosing((WindowEvent)event); 344 break; 345 case WindowEvent.WINDOW_ICONIFIED: 346 listener.windowIconified((WindowEvent)event); 347 break; 348 } 349 } 350 } 351 } 352 353 synchronized void addWindowListener(WindowListener l) { 354 windowListener = AWTEventMulticaster.add(windowListener, l); 355 } 356 357 synchronized void removeWindowListener(WindowListener l) { 358 windowListener = AWTEventMulticaster.remove(windowListener, l); 359 } 360 361 @Override 362 public void updateMinimumSize() { 363 Dimension minimumSize = null; 364 if (((Component)target).isMinimumSizeSet()) { 365 minimumSize = ((Component)target).getMinimumSize(); 366 } 367 if (minimumSize != null) { 368 int msw = getSysMinWidth(); 369 int msh = getSysMinHeight(); 370 int w = (minimumSize.width >= msw) ? minimumSize.width : msw; 371 int h = (minimumSize.height >= msh) ? minimumSize.height : msh; 372 setMinSize(w, h); 373 } else { 374 setMinSize(0, 0); 375 } 376 } 377 378 @Override 379 public void updateIconImages() { 380 java.util.List<Image> imageList = ((Window)target).getIconImages(); 381 if (imageList == null || imageList.size() == 0) { 382 setIconImagesData(null, 0, 0, null, 0, 0); 383 } else { 384 int w = getSysIconWidth(); 385 int h = getSysIconHeight(); 386 int smw = getSysSmIconWidth(); 387 int smh = getSysSmIconHeight(); 388 DataBufferInt iconData = SunToolkit.getScaledIconData(imageList, 389 w, h); 390 DataBufferInt iconSmData = SunToolkit.getScaledIconData(imageList, 391 smw, smh); 392 if (iconData != null && iconSmData != null) { 393 setIconImagesData(iconData.getData(), w, h, 394 iconSmData.getData(), smw, smh); 395 } else { 396 setIconImagesData(null, 0, 0, null, 0, 0); 397 } 398 } 399 } 400 401 native void setMinSize(int width, int height); 402 403 /* 404 * ---- MODALITY SUPPORT ---- 405 */ 406 407 /** 408 * Some modality-related code here because WFileDialogPeer, WPrintDialogPeer and 409 * WPageDialogPeer are descendants of WWindowPeer, not WDialogPeer 410 */ 411 412 public boolean isModalBlocked() { 413 return modalBlocker != null; 414 } 415 416 @Override 417 public void setModalBlocked(Dialog dialog, boolean blocked) { 418 synchronized (((Component)getTarget()).getTreeLock()) // State lock should always be after awtLock 419 { 420 // use WWindowPeer instead of WDialogPeer because of FileDialogs and PrintDialogs 421 WWindowPeer blockerPeer = AWTAccessor.getComponentAccessor() 422 .getPeer(dialog); 423 if (blocked) 424 { 425 modalBlocker = blockerPeer; 426 // handle native dialogs separately, as they may have not 427 // got HWND yet; modalEnable/modalDisable is called from 428 // their setHWnd() methods 429 if (blockerPeer instanceof WFileDialogPeer) { 430 ((WFileDialogPeer)blockerPeer).blockWindow(this); 431 } else if (blockerPeer instanceof WPrintDialogPeer) { 432 ((WPrintDialogPeer)blockerPeer).blockWindow(this); 433 } else { 434 modalDisable(dialog, blockerPeer.getHWnd()); 435 } 436 } else { 437 modalBlocker = null; 438 if (blockerPeer instanceof WFileDialogPeer) { 439 ((WFileDialogPeer)blockerPeer).unblockWindow(this); 440 } else if (blockerPeer instanceof WPrintDialogPeer) { 441 ((WPrintDialogPeer)blockerPeer).unblockWindow(this); 442 } else { 443 modalEnable(dialog); 444 } 445 } 446 } 447 } 448 449 native void modalDisable(Dialog blocker, long blockerHWnd); 450 native void modalEnable(Dialog blocker); 451 452 /* 453 * Returns all the ever active windows from the current AppContext. 454 * The list is sorted by the time of activation, so the latest 455 * active window is always at the end. 456 */ 457 @SuppressWarnings("unchecked") 458 public static long[] getActiveWindowHandles(Component target) { 459 AppContext appContext = SunToolkit.targetToAppContext(target); 460 if (appContext == null) return null; 461 synchronized (appContext) { 462 List<WWindowPeer> l = (List<WWindowPeer>)appContext.get(ACTIVE_WINDOWS_KEY); 463 if (l == null) { 464 return null; 465 } 466 long[] result = new long[l.size()]; 467 for (int j = 0; j < l.size(); j++) { 468 result[j] = l.get(j).getHWnd(); 469 } 470 return result; 471 } 472 } 473 474 /* 475 * ----DISPLAY CHANGE SUPPORT---- 476 */ 477 478 /* 479 * Called from native code when we have been dragged onto another screen. 480 */ 481 void draggedToNewScreen() { 482 SunToolkit.executeOnEventHandlerThread((Component)target,new Runnable() 483 { 484 @Override 485 public void run() { 486 displayChanged(); 487 } 488 }); 489 } 490 491 public void updateGC() { 492 int scrn = getScreenImOn(); 493 if (screenLog.isLoggable(PlatformLogger.Level.FINER)) { 494 log.finer("Screen number: " + scrn); 495 } 496 497 // get current GD 498 Win32GraphicsDevice oldDev = winGraphicsConfig.getDevice(); 499 500 Win32GraphicsDevice newDev; 501 GraphicsDevice devs[] = GraphicsEnvironment 502 .getLocalGraphicsEnvironment() 503 .getScreenDevices(); 504 // Occasionally during device addition/removal getScreenImOn can return 505 // a non-existing screen number. Use the default device in this case. 506 if (scrn >= devs.length) { 507 newDev = (Win32GraphicsDevice)GraphicsEnvironment 508 .getLocalGraphicsEnvironment().getDefaultScreenDevice(); 509 } else { 510 newDev = (Win32GraphicsDevice)devs[scrn]; 511 } 512 513 // Set winGraphicsConfig to the default GC for the monitor this Window 514 // is now mostly on. 515 winGraphicsConfig = (Win32GraphicsConfig)newDev 516 .getDefaultConfiguration(); 517 if (screenLog.isLoggable(PlatformLogger.Level.FINE)) { 518 if (winGraphicsConfig == null) { 519 screenLog.fine("Assertion (winGraphicsConfig != null) failed"); 520 } 521 } 522 523 // if on a different display, take off old GD and put on new GD 524 if (oldDev != newDev) { 525 oldDev.removeDisplayChangedListener(this); 526 newDev.addDisplayChangedListener(this); 527 } 528 529 AWTAccessor.getComponentAccessor(). 530 setGraphicsConfiguration((Component)target, winGraphicsConfig); 531 } 532 533 /** 534 * From the DisplayChangedListener interface. 535 * 536 * This method handles a display change - either when the display settings 537 * are changed, or when the window has been dragged onto a different 538 * display. 539 * Called after a change in the display mode. This event 540 * triggers replacing the surfaceData object (since that object 541 * reflects the current display depth information, which has 542 * just changed). 543 */ 544 @Override 545 public void displayChanged() { 546 updateGC(); 547 } 548 549 /** 550 * Part of the DisplayChangedListener interface: components 551 * do not need to react to this event 552 */ 553 @Override 554 public void paletteChanged() { 555 } 556 557 private native int getScreenImOn(); 558 559 // Used in Win32GraphicsDevice. 560 public final native void setFullScreenExclusiveModeState(boolean state); 561 562 /* 563 * ----END DISPLAY CHANGE SUPPORT---- 564 */ 565 566 public void grab() { 567 nativeGrab(); 568 } 569 570 public void ungrab() { 571 nativeUngrab(); 572 } 573 private native void nativeGrab(); 574 private native void nativeUngrab(); 575 576 private final boolean hasWarningWindow() { 577 return ((Window)target).getWarningString() != null; 578 } 579 580 boolean isTargetUndecorated() { 581 return true; 582 } 583 584 // These are the peer bounds. They get updated at: 585 // 1. the WWindowPeer.setBounds() method. 586 // 2. the native code (on WM_SIZE/WM_MOVE) 587 private volatile int sysX = 0; 588 private volatile int sysY = 0; 589 private volatile int sysW = 0; 590 private volatile int sysH = 0; 591 592 @Override 593 public native void repositionSecurityWarning(); 594 595 @Override 596 public void setBounds(int x, int y, int width, int height, int op) { 597 sysX = x; 598 sysY = y; 599 sysW = width; 600 sysH = height; 601 602 super.setBounds(x, y, width, height, op); 603 } 604 605 @Override 606 public void print(Graphics g) { 607 // We assume we print the whole frame, 608 // so we expect no clip was set previously 609 Shape shape = AWTAccessor.getWindowAccessor().getShape((Window)target); 610 if (shape != null) { 611 g.setClip(shape); 612 } 613 super.print(g); 614 } 615 616 private void replaceSurfaceDataRecursively(Component c) { 617 if (c instanceof Container) { 618 for (Component child : ((Container)c).getComponents()) { 619 replaceSurfaceDataRecursively(child); 620 } 621 } 622 final Object cp = AWTAccessor.getComponentAccessor().getPeer(c); 623 if (cp instanceof WComponentPeer) { 624 ((WComponentPeer)cp).replaceSurfaceDataLater(); 625 } 626 } 627 628 public final Graphics getTranslucentGraphics() { 629 synchronized (getStateLock()) { 630 return isOpaque ? null : painter.getBackBuffer(false).getGraphics(); 631 } 632 } 633 634 @Override 635 public void setBackground(Color c) { 636 super.setBackground(c); 637 synchronized (getStateLock()) { 638 if (!isOpaque && ((Window)target).isVisible()) { 639 updateWindow(true); 640 } 641 } 642 } 643 644 private native void setOpacity(int iOpacity); 645 private float opacity = 1.0f; 646 647 @Override 648 public void setOpacity(float opacity) { 649 if (!((SunToolkit)((Window)target).getToolkit()). 650 isWindowOpacitySupported()) 651 { 652 return; 653 } 654 655 if (opacity < 0.0f || opacity > 1.0f) { 656 throw new IllegalArgumentException( 657 "The value of opacity should be in the range [0.0f .. 1.0f]."); 658 } 659 660 if (((this.opacity == 1.0f && opacity < 1.0f) || 661 (this.opacity < 1.0f && opacity == 1.0f)) && 662 !Win32GraphicsEnvironment.isVistaOS()) 663 { 664 // non-Vista OS: only replace the surface data if opacity status 665 // changed (see WComponentPeer.isAccelCapable() for more) 666 replaceSurfaceDataRecursively((Component)getTarget()); 667 } 668 669 this.opacity = opacity; 670 671 final int maxOpacity = 0xff; 672 int iOpacity = (int)(opacity * maxOpacity); 673 if (iOpacity < 0) { 674 iOpacity = 0; 675 } 676 if (iOpacity > maxOpacity) { 677 iOpacity = maxOpacity; 678 } 679 680 setOpacity(iOpacity); 681 682 synchronized (getStateLock()) { 683 if (!isOpaque && ((Window)target).isVisible()) { 684 updateWindow(true); 685 } 686 } 687 } 688 689 private native void setOpaqueImpl(boolean isOpaque); 690 691 @Override 692 public void setOpaque(boolean isOpaque) { 693 synchronized (getStateLock()) { 694 if (this.isOpaque == isOpaque) { 695 return; 696 } 697 } 698 699 Window target = (Window)getTarget(); 700 701 if (!isOpaque) { 702 SunToolkit sunToolkit = (SunToolkit)target.getToolkit(); 703 if (!sunToolkit.isWindowTranslucencySupported() || 704 !sunToolkit.isTranslucencyCapable(target.getGraphicsConfiguration())) 705 { 706 return; 707 } 708 } 709 710 boolean isVistaOS = Win32GraphicsEnvironment.isVistaOS(); 711 712 if (this.isOpaque != isOpaque && !isVistaOS) { 713 // non-Vista OS: only replace the surface data if the opacity 714 // status changed (see WComponentPeer.isAccelCapable() for more) 715 replaceSurfaceDataRecursively(target); 716 } 717 718 synchronized (getStateLock()) { 719 this.isOpaque = isOpaque; 720 setOpaqueImpl(isOpaque); 721 if (isOpaque) { 722 TranslucentWindowPainter currentPainter = painter; 723 if (currentPainter != null) { 724 currentPainter.flush(); 725 painter = null; 726 } 727 } else { 728 painter = TranslucentWindowPainter.createInstance(this); 729 } 730 } 731 732 if (isVistaOS) { 733 // On Vista: setting the window non-opaque makes the window look 734 // rectangular, though still catching the mouse clicks within 735 // its shape only. To restore the correct visual appearance 736 // of the window (i.e. w/ the correct shape) we have to reset 737 // the shape. 738 Shape shape = target.getShape(); 739 if (shape != null) { 740 target.setShape(shape); 741 } 742 } 743 744 if (target.isVisible()) { 745 updateWindow(true); 746 } 747 } 748 749 native void updateWindowImpl(int[] data, int width, int height); 750 751 @Override 752 public void updateWindow() { 753 updateWindow(false); 754 } 755 756 private void updateWindow(boolean repaint) { 757 Window w = (Window)target; 758 synchronized (getStateLock()) { 759 if (isOpaque || !w.isVisible() || 760 (w.getWidth() <= 0) || (w.getHeight() <= 0)) 761 { 762 return; 763 } 764 TranslucentWindowPainter currentPainter = painter; 765 if (currentPainter != null) { 766 currentPainter.updateWindow(repaint); 767 } else if (log.isLoggable(PlatformLogger.Level.FINER)) { 768 log.finer("Translucent window painter is null in updateWindow"); 769 } 770 } 771 } 772 773 /* 774 * The method maps the list of the active windows to the window's AppContext, 775 * then the method registers ActiveWindowListener, GuiDisposedListener listeners; 776 * it executes the initilialization only once per AppContext. 777 */ 778 @SuppressWarnings("unchecked") 779 private static void initActiveWindowsTracking(Window w) { 780 AppContext appContext = AppContext.getAppContext(); 781 synchronized (appContext) { 782 List<WWindowPeer> l = (List<WWindowPeer>)appContext.get(ACTIVE_WINDOWS_KEY); 783 if (l == null) { 784 l = new LinkedList<WWindowPeer>(); 785 appContext.put(ACTIVE_WINDOWS_KEY, l); 786 appContext.addPropertyChangeListener(AppContext.GUI_DISPOSED, guiDisposedListener); 787 788 KeyboardFocusManager kfm = KeyboardFocusManager.getCurrentKeyboardFocusManager(); 789 kfm.addPropertyChangeListener("activeWindow", activeWindowListener); 790 } 791 } 792 } 793 794 /* 795 * The GuiDisposedListener class listens for the AppContext.GUI_DISPOSED property, 796 * it removes the list of the active windows from the disposed AppContext and 797 * unregisters ActiveWindowListener listener. 798 */ 799 private static class GuiDisposedListener implements PropertyChangeListener { 800 @Override 801 public void propertyChange(PropertyChangeEvent e) { 802 boolean isDisposed = (Boolean)e.getNewValue(); 803 if (isDisposed != true) { 804 if (log.isLoggable(PlatformLogger.Level.FINE)) { 805 log.fine(" Assertion (newValue != true) failed for AppContext.GUI_DISPOSED "); 806 } 807 } 808 AppContext appContext = AppContext.getAppContext(); 809 synchronized (appContext) { 810 appContext.remove(ACTIVE_WINDOWS_KEY); 811 appContext.removePropertyChangeListener(AppContext.GUI_DISPOSED, this); 812 813 KeyboardFocusManager kfm = KeyboardFocusManager.getCurrentKeyboardFocusManager(); 814 kfm.removePropertyChangeListener("activeWindow", activeWindowListener); 815 } 816 } 817 } 818 819 /* 820 * Static inner class, listens for 'activeWindow' KFM property changes and 821 * updates the list of active windows per AppContext, so the latest active 822 * window is always at the end of the list. The list is stored in AppContext. 823 */ 824 @SuppressWarnings("unchecked") 825 private static class ActiveWindowListener implements PropertyChangeListener { 826 @Override 827 public void propertyChange(PropertyChangeEvent e) { 828 Window w = (Window)e.getNewValue(); 829 if (w == null) { 830 return; 831 } 832 AppContext appContext = SunToolkit.targetToAppContext(w); 833 synchronized (appContext) { 834 WWindowPeer wp = AWTAccessor.getComponentAccessor().getPeer(w); 835 // add/move wp to the end of the list 836 List<WWindowPeer> l = (List<WWindowPeer>)appContext.get(ACTIVE_WINDOWS_KEY); 837 if (l != null) { 838 l.remove(wp); 839 l.add(wp); 840 } 841 } 842 } 843 } 844 }