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