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