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