1 /*
2  * Copyright (c) 2011, 2019, 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.lwawt;
27 
28 import java.awt.AlphaComposite;
29 import java.awt.Color;
30 import java.awt.Component;
31 import java.awt.Dialog;
32 import java.awt.Dimension;
33 import java.awt.Font;
34 import java.awt.FontMetrics;
35 import java.awt.Frame;
36 import java.awt.Graphics;
37 import java.awt.Graphics2D;
38 import java.awt.GraphicsConfiguration;
39 import java.awt.GraphicsDevice;
40 import java.awt.GraphicsEnvironment;
41 import java.awt.Insets;
42 import java.awt.KeyboardFocusManager;
43 import java.awt.MenuBar;
44 import java.awt.Point;
45 import java.awt.Rectangle;
46 import java.awt.Shape;
47 import java.awt.SystemColor;
48 import java.awt.Toolkit;
49 import java.awt.Window;
50 import java.awt.event.FocusEvent;
51 import java.awt.event.KeyEvent;
52 import java.awt.event.MouseEvent;
53 import java.awt.event.MouseWheelEvent;
54 import java.awt.event.WindowEvent;
55 import java.awt.peer.ComponentPeer;
56 import java.awt.peer.DialogPeer;
57 import java.awt.peer.FramePeer;
58 import java.awt.peer.KeyboardFocusManagerPeer;
59 import java.awt.peer.WindowPeer;
60 import java.util.List;
61 
62 import javax.swing.JComponent;
63 
64 import sun.awt.AWTAccessor;
65 import sun.awt.AWTAccessor.ComponentAccessor;
66 import sun.awt.AppContext;
67 import sun.awt.CGraphicsDevice;
68 import sun.awt.DisplayChangedListener;
69 import sun.awt.ExtendedKeyCodes;
70 import sun.awt.FullScreenCapable;
71 import sun.awt.SunToolkit;
72 import sun.awt.TimedWindowEvent;
73 import sun.awt.UngrabEvent;
74 import sun.java2d.NullSurfaceData;
75 import sun.java2d.SunGraphics2D;
76 import sun.java2d.SunGraphicsEnvironment;
77 import sun.java2d.SurfaceData;
78 import sun.java2d.loops.Blit;
79 import sun.java2d.loops.CompositeType;
80 import sun.java2d.pipe.Region;
81 import sun.util.logging.PlatformLogger;
82 
83 public class LWWindowPeer
84     extends LWContainerPeer<Window, JComponent>
85     implements FramePeer, DialogPeer, FullScreenCapable, DisplayChangedListener, PlatformEventNotifier
86 {
87     public enum PeerType {
88         SIMPLEWINDOW,
89         FRAME,
90         DIALOG,
91         EMBEDDED_FRAME,
92         VIEW_EMBEDDED_FRAME,
93         LW_FRAME
94     }
95 
96     private static final PlatformLogger focusLog = PlatformLogger.getLogger("sun.lwawt.focus.LWWindowPeer");
97 
98     private final PlatformWindow platformWindow;
99 
100     private static final int MINIMUM_WIDTH = 1;
101     private static final int MINIMUM_HEIGHT = 1;
102 
103     private Insets insets = new Insets(0, 0, 0, 0);
104     private Rectangle maximizedBounds;
105 
106     private GraphicsDevice graphicsDevice;
107     private GraphicsConfiguration graphicsConfig;
108 
109     private SurfaceData surfaceData;
110     private final Object surfaceDataLock = new Object();
111 
112     private volatile int windowState = Frame.NORMAL;
113 
114     // check that the mouse is over the window
115     private volatile boolean isMouseOver = false;
116 
117     // A peer where the last mouse event came to. Used by cursor manager to
118     // find the component under cursor
119     private static volatile LWComponentPeer<?, ?> lastCommonMouseEventPeer;
120 
121     // A peer where the last mouse event came to. Used to generate
122     // MOUSE_ENTERED/EXITED notifications
123     private volatile LWComponentPeer<?, ?> lastMouseEventPeer;
124 
125     // Peers where all dragged/released events should come to,
126     // depending on what mouse button is being dragged according to Cocoa
127     private static final LWComponentPeer<?, ?>[] mouseDownTarget = new LWComponentPeer<?, ?>[3];
128 
129     // A bitmask that indicates what mouse buttons produce MOUSE_CLICKED events
130     // on MOUSE_RELEASE. Click events are only generated if there were no drag
131     // events between MOUSE_PRESSED and MOUSE_RELEASED for particular button
132     private static int mouseClickButtons = 0;
133 
134     private volatile boolean isOpaque = true;
135 
136     private static final Font DEFAULT_FONT = new Font("Lucida Grande", Font.PLAIN, 13);
137 
138     private static LWWindowPeer grabbingWindow;
139 
140     private volatile boolean skipNextFocusChange;
141 
142     private static final Color nonOpaqueBackground = new Color(0, 0, 0, 0);
143 
144     private volatile boolean textured;
145 
146     private final PeerType peerType;
147 
148     private final SecurityWarningWindow warningWindow;
149 
150     private volatile boolean targetFocusable;
151 
152     /**
153      * Current modal blocker or null.
154      *
155      * Synchronization: peerTreeLock.
156      */
157     private LWWindowPeer blocker;
158 
LWWindowPeer(Window target, PlatformComponent platformComponent, PlatformWindow platformWindow, PeerType peerType)159     public LWWindowPeer(Window target, PlatformComponent platformComponent,
160                         PlatformWindow platformWindow, PeerType peerType)
161     {
162         super(target, platformComponent);
163         this.platformWindow = platformWindow;
164         this.peerType = peerType;
165 
166         Window owner = target.getOwner();
167         LWWindowPeer ownerPeer = owner == null ? null :
168              (LWWindowPeer) AWTAccessor.getComponentAccessor().getPeer(owner);
169         PlatformWindow ownerDelegate = (ownerPeer != null) ? ownerPeer.getPlatformWindow() : null;
170 
171         // The delegate.initialize() needs a non-null GC on X11.
172         GraphicsConfiguration gc = getTarget().getGraphicsConfiguration();
173         synchronized (getStateLock()) {
174             // graphicsConfig should be updated according to the real window
175             // bounds when the window is shown, see 4868278
176             this.graphicsConfig = gc;
177         }
178 
179         if (!target.isFontSet()) {
180             target.setFont(DEFAULT_FONT);
181         }
182 
183         if (!target.isBackgroundSet()) {
184             target.setBackground(SystemColor.window);
185         } else {
186             // first we check if user provided alpha for background. This is
187             // similar to what Apple's Java do.
188             // Since JDK7 we should rely on setOpacity() only.
189             // this.opacity = c.getAlpha();
190         }
191 
192         if (!target.isForegroundSet()) {
193             target.setForeground(SystemColor.windowText);
194             // we should not call setForeground because it will call a repaint
195             // which the peer may not be ready to do yet.
196         }
197 
198         platformWindow.initialize(target, this, ownerDelegate);
199 
200         // Init warning window(for applets)
201         SecurityWarningWindow warn = null;
202         if (target.getWarningString() != null) {
203             // accessSystemTray permission allows to display TrayIcon, TrayIcon tooltip
204             // and TrayIcon balloon windows without a warning window.
205             if (!AWTAccessor.getWindowAccessor().isTrayIconWindow(target)) {
206                 LWToolkit toolkit = (LWToolkit)Toolkit.getDefaultToolkit();
207                 warn = toolkit.createSecurityWarning(target, this);
208             }
209         }
210 
211         warningWindow = warn;
212     }
213 
214     @Override
initializeImpl()215     void initializeImpl() {
216         super.initializeImpl();
217 
218 
219         if (getTarget() instanceof Frame) {
220             Frame frame = (Frame) getTarget();
221             setTitle(frame.getTitle());
222             setState(frame.getExtendedState());
223             setMaximizedBounds(frame.getMaximizedBounds());
224         } else if (getTarget() instanceof Dialog) {
225             setTitle(((Dialog) getTarget()).getTitle());
226         }
227 
228         updateAlwaysOnTopState();
229         updateMinimumSize();
230         updateFocusableWindowState();
231 
232         final Shape shape = getTarget().getShape();
233         if (shape != null) {
234             applyShape(Region.getInstance(shape, null));
235         }
236 
237         final float opacity = getTarget().getOpacity();
238         if (opacity < 1.0f) {
239             setOpacity(opacity);
240         }
241 
242         setOpaque(getTarget().isOpaque());
243 
244         updateInsets(platformWindow.getInsets());
245         if (getSurfaceData() == null) {
246             replaceSurfaceData(false);
247         }
248         activateDisplayListener();
249     }
250 
251     // Just a helper method
252     @Override
getPlatformWindow()253     public PlatformWindow getPlatformWindow() {
254         return platformWindow;
255     }
256 
257     @Override
getWindowPeerOrSelf()258     protected LWWindowPeer getWindowPeerOrSelf() {
259         return this;
260     }
261 
262     // ---- PEER METHODS ---- //
263 
264     @Override
disposeImpl()265     protected void disposeImpl() {
266         deactivateDisplayListener();
267         SurfaceData oldData = getSurfaceData();
268         synchronized (surfaceDataLock){
269             surfaceData = null;
270         }
271         if (oldData != null) {
272             oldData.invalidate();
273         }
274         if (isGrabbing()) {
275             ungrab();
276         }
277         if (warningWindow != null) {
278             warningWindow.dispose();
279         }
280 
281         platformWindow.dispose();
282         super.disposeImpl();
283     }
284 
285     @Override
setVisibleImpl(final boolean visible)286     protected void setVisibleImpl(final boolean visible) {
287         if (!visible && warningWindow != null) {
288             warningWindow.setVisible(false, false);
289         }
290         updateFocusableWindowState();
291         super.setVisibleImpl(visible);
292         // TODO: update graphicsConfig, see 4868278
293         platformWindow.setVisible(visible);
294         if (isSimpleWindow()) {
295             KeyboardFocusManagerPeer kfmPeer = LWKeyboardFocusManagerPeer.getInstance();
296             if (visible) {
297                 if (!getTarget().isAutoRequestFocus()) {
298                     return;
299                 } else {
300                     requestWindowFocus(FocusEvent.Cause.ACTIVATION);
301                 }
302             // Focus the owner in case this window is focused.
303             } else if (kfmPeer.getCurrentFocusedWindow() == getTarget()) {
304                 // Transfer focus to the owner.
305                 LWWindowPeer owner = getOwnerFrameDialog(LWWindowPeer.this);
306                 if (owner != null) {
307                     owner.requestWindowFocus(FocusEvent.Cause.ACTIVATION);
308                 }
309             }
310         }
311     }
312 
313     @Override
getGraphicsConfiguration()314     public final GraphicsConfiguration getGraphicsConfiguration() {
315         synchronized (getStateLock()) {
316             return graphicsConfig;
317         }
318     }
319 
320     @Override
updateGraphicsData(GraphicsConfiguration gc)321     public boolean updateGraphicsData(GraphicsConfiguration gc) {
322         setGraphicsConfig(gc);
323         return false;
324     }
325 
getOnscreenGraphics(Color fg, Color bg, Font f)326     protected final Graphics getOnscreenGraphics(Color fg, Color bg, Font f) {
327         SurfaceData surfaceData = getSurfaceData();
328         if (surfaceData == null) {
329             return null;
330         }
331         if (fg == null) {
332             fg = SystemColor.windowText;
333         }
334         if (bg == null) {
335             bg = SystemColor.window;
336         }
337         if (f == null) {
338             f = DEFAULT_FONT;
339         }
340         return new SunGraphics2D(surfaceData, fg, bg, f);
341     }
342 
343     @Override
setBounds(int x, int y, int w, int h, int op)344     public void setBounds(int x, int y, int w, int h, int op) {
345 
346         if((op & NO_EMBEDDED_CHECK) == 0 && getPeerType() == PeerType.VIEW_EMBEDDED_FRAME) {
347             return;
348         }
349 
350         if ((op & SET_CLIENT_SIZE) != 0) {
351             // SET_CLIENT_SIZE is only applicable to window peers, so handle it here
352             // instead of pulling 'insets' field up to LWComponentPeer
353             // no need to add insets since Window's notion of width and height includes insets.
354             op &= ~SET_CLIENT_SIZE;
355             op |= SET_SIZE;
356         }
357 
358         // Don't post ComponentMoved/Resized and Paint events
359         // until we've got a notification from the delegate
360         Rectangle cb = constrainBounds(x, y, w, h);
361 
362         Rectangle newBounds = new Rectangle(getBounds());
363         if ((op & (SET_LOCATION | SET_BOUNDS)) != 0) {
364             newBounds.x = cb.x;
365             newBounds.y = cb.y;
366         }
367         if ((op & (SET_SIZE | SET_BOUNDS)) != 0) {
368             newBounds.width = cb.width;
369             newBounds.height = cb.height;
370         }
371         // Native system could constraint bounds, so the peer wold be updated in the callback
372         platformWindow.setBounds(newBounds.x, newBounds.y, newBounds.width, newBounds.height);
373     }
374 
constrainBounds(Rectangle bounds)375     public Rectangle constrainBounds(Rectangle bounds) {
376         return constrainBounds(bounds.x, bounds.y, bounds.width, bounds.height);
377     }
378 
constrainBounds(int x, int y, int w, int h)379     public Rectangle constrainBounds(int x, int y, int w, int h) {
380 
381         if (w < MINIMUM_WIDTH) {
382             w = MINIMUM_WIDTH;
383         }
384 
385         if (h < MINIMUM_HEIGHT) {
386             h = MINIMUM_HEIGHT;
387         }
388 
389         final int maxW = getLWGC().getMaxTextureWidth();
390         final int maxH = getLWGC().getMaxTextureHeight();
391 
392         if (w > maxW) {
393             w = maxW;
394         }
395         if (h > maxH) {
396             h = maxH;
397         }
398 
399         return new Rectangle(x, y, w, h);
400     }
401 
402     @Override
getLocationOnScreen()403     public Point getLocationOnScreen() {
404         return platformWindow.getLocationOnScreen();
405     }
406 
407     /**
408      * Overridden from LWContainerPeer to return the correct insets.
409      * Insets are queried from the delegate and are kept up to date by
410      * requiering when needed (i.e. when the window geometry is changed).
411      */
412     @Override
getInsets()413     public Insets getInsets() {
414         synchronized (getStateLock()) {
415             return insets;
416         }
417     }
418 
419     @Override
getFontMetrics(Font f)420     public FontMetrics getFontMetrics(Font f) {
421         // TODO: check for "use platform metrics" settings
422         return platformWindow.getFontMetrics(f);
423     }
424 
425     @Override
toFront()426     public void toFront() {
427         platformWindow.toFront();
428     }
429 
430     @Override
toBack()431     public void toBack() {
432         platformWindow.toBack();
433     }
434 
435     @Override
setZOrder(ComponentPeer above)436     public void setZOrder(ComponentPeer above) {
437         throw new RuntimeException("not implemented");
438     }
439 
440     @Override
updateAlwaysOnTopState()441     public void updateAlwaysOnTopState() {
442         platformWindow.setAlwaysOnTop(getTarget().isAlwaysOnTop());
443     }
444 
445     @Override
updateFocusableWindowState()446     public void updateFocusableWindowState() {
447         targetFocusable = getTarget().isFocusableWindow();
448         platformWindow.updateFocusableWindowState();
449     }
450 
451     @Override
setModalBlocked(Dialog blocker, boolean blocked)452     public void setModalBlocked(Dialog blocker, boolean blocked) {
453         synchronized (getPeerTreeLock()) {
454             ComponentPeer peer =  AWTAccessor.getComponentAccessor().getPeer(blocker);
455             if (blocked && (peer instanceof LWWindowPeer)) {
456                 this.blocker = (LWWindowPeer) peer;
457             } else {
458                 this.blocker = null;
459             }
460         }
461 
462         platformWindow.setModalBlocked(blocked);
463     }
464 
465     @Override
updateMinimumSize()466     public void updateMinimumSize() {
467         final Dimension min;
468         if (getTarget().isMinimumSizeSet()) {
469             min = getTarget().getMinimumSize();
470             min.width = Math.max(min.width, MINIMUM_WIDTH);
471             min.height = Math.max(min.height, MINIMUM_HEIGHT);
472         } else {
473             min = new Dimension(MINIMUM_WIDTH, MINIMUM_HEIGHT);
474         }
475 
476         final Dimension max;
477         if (getTarget().isMaximumSizeSet()) {
478             max = getTarget().getMaximumSize();
479             max.width = Math.min(max.width, getLWGC().getMaxTextureWidth());
480             max.height = Math.min(max.height, getLWGC().getMaxTextureHeight());
481         } else {
482             max = new Dimension(getLWGC().getMaxTextureWidth(),
483                                 getLWGC().getMaxTextureHeight());
484         }
485 
486         platformWindow.setSizeConstraints(min.width, min.height, max.width, max.height);
487     }
488 
489     @Override
updateIconImages()490     public void updateIconImages() {
491         getPlatformWindow().updateIconImages();
492     }
493 
494     @Override
setBackground(final Color c)495     public void setBackground(final Color c) {
496         super.setBackground(c);
497         updateOpaque();
498     }
499 
500     @Override
setOpacity(float opacity)501     public void setOpacity(float opacity) {
502         getPlatformWindow().setOpacity(opacity);
503         repaintPeer();
504     }
505 
506     @Override
setOpaque(final boolean isOpaque)507     public final void setOpaque(final boolean isOpaque) {
508         if (this.isOpaque != isOpaque) {
509             this.isOpaque = isOpaque;
510             updateOpaque();
511         }
512     }
513 
updateOpaque()514     private void updateOpaque() {
515         getPlatformWindow().setOpaque(!isTranslucent());
516         replaceSurfaceData(false);
517         repaintPeer();
518     }
519 
520     @Override
updateWindow()521     public void updateWindow() {
522     }
523 
isTextured()524     public final boolean isTextured() {
525         return textured;
526     }
527 
setTextured(final boolean isTextured)528     public final void setTextured(final boolean isTextured) {
529         textured = isTextured;
530     }
531 
532     @Override
isTranslucent()533     public final boolean isTranslucent() {
534         synchronized (getStateLock()) {
535             /*
536              * Textured window is a special case of translucent window.
537              * The difference is only in nswindow background. So when we set
538              * texture property our peer became fully translucent. It doesn't
539              * fill background, create non opaque backbuffers and layer etc.
540              */
541             return !isOpaque || isShaped() || isTextured();
542         }
543     }
544 
545     @Override
applyShapeImpl(final Region shape)546     final void applyShapeImpl(final Region shape) {
547         super.applyShapeImpl(shape);
548         updateOpaque();
549     }
550 
551     @Override
repositionSecurityWarning()552     public void repositionSecurityWarning() {
553         if (warningWindow != null) {
554             ComponentAccessor compAccessor = AWTAccessor.getComponentAccessor();
555             Window target = getTarget();
556             int x = compAccessor.getX(target);
557             int y = compAccessor.getY(target);
558             int width = compAccessor.getWidth(target);
559             int height = compAccessor.getHeight(target);
560             warningWindow.reposition(x, y, width, height);
561         }
562     }
563 
564     // ---- FRAME PEER METHODS ---- //
565 
566     @Override // FramePeer and DialogPeer
setTitle(String title)567     public void setTitle(String title) {
568         platformWindow.setTitle(title == null ? "" : title);
569     }
570 
571     @Override
setMenuBar(MenuBar mb)572     public void setMenuBar(MenuBar mb) {
573          platformWindow.setMenuBar(mb);
574     }
575 
576     @Override // FramePeer and DialogPeer
setResizable(boolean resizable)577     public void setResizable(boolean resizable) {
578         platformWindow.setResizable(resizable);
579     }
580 
581     @Override
setState(int state)582     public void setState(int state) {
583         platformWindow.setWindowState(state);
584     }
585 
586     @Override
getState()587     public int getState() {
588         return windowState;
589     }
590 
isMaximizedBoundsSet()591     private boolean isMaximizedBoundsSet() {
592         synchronized (getStateLock()) {
593             return maximizedBounds != null;
594         }
595     }
596 
getDefaultMaximizedBounds()597     private Rectangle getDefaultMaximizedBounds() {
598         GraphicsConfiguration config = getGraphicsConfiguration();
599         Insets screenInsets = ((CGraphicsDevice) config.getDevice())
600                 .getScreenInsets();
601         Rectangle gcBounds = config.getBounds();
602         return new Rectangle(
603                 gcBounds.x + screenInsets.left,
604                 gcBounds.y + screenInsets.top,
605                 gcBounds.width - screenInsets.left - screenInsets.right,
606                 gcBounds.height - screenInsets.top - screenInsets.bottom);
607     }
608 
609     @Override
setMaximizedBounds(Rectangle bounds)610     public void setMaximizedBounds(Rectangle bounds) {
611         boolean isMaximizedBoundsSet;
612         synchronized (getStateLock()) {
613             this.maximizedBounds = (isMaximizedBoundsSet = (bounds != null))
614                     ? constrainBounds(bounds) : null;
615         }
616 
617         setPlatformMaximizedBounds(isMaximizedBoundsSet ? maximizedBounds
618                 : getDefaultMaximizedBounds());
619     }
620 
getMaximizedBounds()621     public Rectangle getMaximizedBounds() {
622         synchronized (getStateLock()) {
623             return (maximizedBounds == null)
624                     ? getDefaultMaximizedBounds()
625                     : maximizedBounds;
626         }
627     }
628 
setPlatformMaximizedBounds(Rectangle bounds)629     private void setPlatformMaximizedBounds(Rectangle bounds) {
630         platformWindow.setMaximizedBounds(
631                 bounds.x, bounds.y,
632                 bounds.width, bounds.height);
633     }
634 
635     @Override
setBoundsPrivate(int x, int y, int width, int height)636     public void setBoundsPrivate(int x, int y, int width, int height) {
637         setBounds(x, y, width, height, SET_BOUNDS | NO_EMBEDDED_CHECK);
638     }
639 
640     @Override
getBoundsPrivate()641     public Rectangle getBoundsPrivate() {
642         throw new RuntimeException("not implemented");
643     }
644 
645     // ---- DIALOG PEER METHODS ---- //
646 
647     @Override
blockWindows(List<Window> windows)648     public void blockWindows(List<Window> windows) {
649         //TODO: LWX will probably need some collectJavaToplevels to speed this up
650         for (Window w : windows) {
651             WindowPeer wp = AWTAccessor.getComponentAccessor().getPeer(w);
652             if (wp != null) {
653                 wp.setModalBlocked((Dialog)getTarget(), true);
654             }
655         }
656     }
657 
658     // ---- PEER NOTIFICATIONS ---- //
659 
660     @Override
notifyIconify(boolean iconify)661     public void notifyIconify(boolean iconify) {
662         //The toplevel target is Frame and states are applicable to it.
663         //Otherwise, the target is Window and it don't have state property.
664         //Hopefully, no such events are posted in the queue so consider the
665         //target as Frame in all cases.
666 
667         // REMIND: should we send it anyway if the state not changed since last
668         // time?
669         WindowEvent iconifyEvent = new WindowEvent(getTarget(),
670                 iconify ? WindowEvent.WINDOW_ICONIFIED
671                         : WindowEvent.WINDOW_DEICONIFIED);
672         postEvent(iconifyEvent);
673 
674         int newWindowState = iconify ? Frame.ICONIFIED : Frame.NORMAL;
675         postWindowStateChangedEvent(newWindowState);
676 
677         // REMIND: RepaintManager doesn't repaint iconified windows and
678         // hence ignores any repaint request during deiconification.
679         // So, we need to repaint window explicitly when it becomes normal.
680         if (!iconify) {
681             repaintPeer();
682         }
683     }
684 
685     @Override
notifyZoom(boolean isZoomed)686     public void notifyZoom(boolean isZoomed) {
687         int newWindowState = isZoomed ? Frame.MAXIMIZED_BOTH : Frame.NORMAL;
688         postWindowStateChangedEvent(newWindowState);
689     }
690 
691     /**
692      * Called by the {@code PlatformWindow} when any part of the window should
693      * be repainted.
694      */
695     @Override
notifyExpose(final Rectangle r)696     public void notifyExpose(final Rectangle r) {
697         repaintPeer(r);
698     }
699 
700     /**
701      * Called by the {@code PlatformWindow} when this window is moved/resized by
702      * user or window insets are changed. There's no notifyReshape() in
703      * LWComponentPeer as the only components which could be resized by user are
704      * top-level windows.
705      * <p>
706      * We need to update the target and post the events, if the peer was moved
707      * or resized, or if the target is out of sync with this peer.
708      */
709     @Override
notifyReshape(int x, int y, int w, int h)710     public void notifyReshape(int x, int y, int w, int h) {
711         final Rectangle pBounds = getBounds();
712         final boolean invalid = updateInsets(platformWindow.getInsets());
713         final boolean pMoved = (x != pBounds.x) || (y != pBounds.y);
714         final boolean pResized = (w != pBounds.width) || (h != pBounds.height);
715 
716         final ComponentAccessor accessor = AWTAccessor.getComponentAccessor();
717         final Rectangle tBounds = accessor.getBounds(getTarget());
718         final boolean tMoved = (x != tBounds.x) || (y != tBounds.y);
719         final boolean tResized = (w != tBounds.width) || (h != tBounds.height);
720 
721         // Check if anything changed
722         if (!tMoved && !tResized && !pMoved && !pResized && !invalid) {
723             // Native window(NSWindow)/LWWindowPeer/Target are in sync
724             return;
725         }
726         // First, update peer's bounds
727         setBounds(x, y, w, h, SET_BOUNDS, false, false);
728 
729         // Second, update the graphics config and surface data
730         final boolean isNewDevice = updateGraphicsDevice();
731         if (isNewDevice && !isMaximizedBoundsSet()) {
732             setPlatformMaximizedBounds(getDefaultMaximizedBounds());
733         }
734 
735         if (pResized || isNewDevice) {
736             replaceSurfaceData();
737             updateMinimumSize();
738         }
739 
740         // Third, COMPONENT_MOVED/COMPONENT_RESIZED/PAINT events
741         if (tMoved || pMoved || invalid) {
742             handleMove(x, y, true);
743         }
744         if (tResized || pResized || invalid || isNewDevice) {
745             handleResize(w, h, true);
746             repaintPeer();
747         }
748 
749         repositionSecurityWarning();
750     }
751 
clearBackground(final int w, final int h)752     private void clearBackground(final int w, final int h) {
753         final Graphics g = getOnscreenGraphics(getForeground(), getBackground(),
754                                                getFont());
755         if (g != null) {
756             try {
757                 if (g instanceof Graphics2D) {
758                     ((Graphics2D) g).setComposite(AlphaComposite.Src);
759                 }
760                 if (isTranslucent()) {
761                     g.setColor(nonOpaqueBackground);
762                     g.fillRect(0, 0, w, h);
763                 }
764                 if (!isTextured()) {
765                     if (g instanceof SunGraphics2D) {
766                         ((SunGraphics2D) g).constrain(0, 0, w, h, getRegion());
767                     }
768                     g.setColor(getBackground());
769                     g.fillRect(0, 0, w, h);
770                 }
771             } finally {
772                 g.dispose();
773             }
774         }
775     }
776 
777     @Override
notifyUpdateCursor()778     public void notifyUpdateCursor() {
779         getLWToolkit().getCursorManager().updateCursorLater(this);
780     }
781 
782     @Override
notifyActivation(boolean activation, LWWindowPeer opposite)783     public void notifyActivation(boolean activation, LWWindowPeer opposite) {
784         Window oppositeWindow = (opposite == null)? null : opposite.getTarget();
785         changeFocusedWindow(activation, oppositeWindow);
786     }
787 
788     // MouseDown in non-client area
789     @Override
notifyNCMouseDown()790     public void notifyNCMouseDown() {
791         // Ungrab except for a click on a Dialog with the grabbing owner
792         if (grabbingWindow != null &&
793             !grabbingWindow.isOneOfOwnersOf(this))
794         {
795             grabbingWindow.ungrab();
796         }
797     }
798 
799     // ---- EVENTS ---- //
800 
801     /*
802      * Called by the delegate to dispatch the event to Java. Event
803      * coordinates are relative to non-client window are, i.e. the top-left
804      * point of the client area is (insets.top, insets.left).
805      */
806     @Override
notifyMouseEvent(int id, long when, int button, int x, int y, int absX, int absY, int modifiers, int clickCount, boolean popupTrigger, byte[] bdata)807     public void notifyMouseEvent(int id, long when, int button,
808                                  int x, int y, int absX, int absY,
809                                  int modifiers, int clickCount, boolean popupTrigger,
810                                  byte[] bdata)
811     {
812         // TODO: fill "bdata" member of AWTEvent
813         Rectangle r = getBounds();
814         // findPeerAt() expects parent coordinates
815         LWComponentPeer<?, ?> targetPeer = findPeerAt(r.x + x, r.y + y);
816 
817         if (id == MouseEvent.MOUSE_EXITED) {
818             isMouseOver = false;
819             if (lastMouseEventPeer != null) {
820                 if (lastMouseEventPeer.isEnabled()) {
821                     Point lp = lastMouseEventPeer.windowToLocal(x, y,
822                             this);
823                     Component target = lastMouseEventPeer.getTarget();
824                     postMouseExitedEvent(target, when, modifiers, lp,
825                             absX, absY, clickCount, popupTrigger, button);
826                 }
827 
828                 // Sometimes we may get MOUSE_EXITED after lastCommonMouseEventPeer is switched
829                 // to a peer from another window. So we must first check if this peer is
830                 // the same as lastWindowPeer
831                 if (lastCommonMouseEventPeer != null && lastCommonMouseEventPeer.getWindowPeerOrSelf() == this) {
832                     lastCommonMouseEventPeer = null;
833                 }
834                 lastMouseEventPeer = null;
835             }
836         } else if(id == MouseEvent.MOUSE_ENTERED) {
837             isMouseOver = true;
838             if (targetPeer != null) {
839                 if (targetPeer.isEnabled()) {
840                     Point lp = targetPeer.windowToLocal(x, y, this);
841                     Component target = targetPeer.getTarget();
842                     postMouseEnteredEvent(target, when, modifiers, lp,
843                             absX, absY, clickCount, popupTrigger, button);
844                 }
845                 lastCommonMouseEventPeer = targetPeer;
846                 lastMouseEventPeer = targetPeer;
847             }
848         } else {
849             PlatformWindow topmostPlatformWindow = LWToolkit.getLWToolkit().getPlatformWindowUnderMouse();
850 
851             LWWindowPeer topmostWindowPeer =
852                     topmostPlatformWindow != null ? topmostPlatformWindow.getPeer() : null;
853 
854             // topmostWindowPeer == null condition is added for the backward
855             // compatibility with applets. It can be removed when the
856             // getTopmostPlatformWindowUnderMouse() method will be properly
857             // implemented in CPlatformEmbeddedFrame class
858             if (topmostWindowPeer == this || topmostWindowPeer == null) {
859                 generateMouseEnterExitEventsForComponents(when, button, x, y,
860                         absX, absY, modifiers, clickCount, popupTrigger,
861                         targetPeer);
862             } else {
863                 LWComponentPeer<?, ?> topmostTargetPeer = topmostWindowPeer.findPeerAt(r.x + x, r.y + y);
864                 topmostWindowPeer.generateMouseEnterExitEventsForComponents(when, button, x, y,
865                         absX, absY, modifiers, clickCount, popupTrigger,
866                         topmostTargetPeer);
867             }
868 
869             // TODO: fill "bdata" member of AWTEvent
870 
871             int eventButtonMask = (button > 0)? MouseEvent.getMaskForButton(button) : 0;
872             int otherButtonsPressed = modifiers & ~eventButtonMask;
873 
874             // For pressed/dragged/released events OS X treats other
875             // mouse buttons as if they were BUTTON2, so we do the same
876             int targetIdx = (button > 3) ? MouseEvent.BUTTON2 - 1 : button - 1;
877 
878             // MOUSE_ENTERED/EXITED are generated for the components strictly under
879             // mouse even when dragging. That's why we first update lastMouseEventPeer
880             // based on initial targetPeer value and only then recalculate targetPeer
881             // for MOUSE_DRAGGED/RELEASED events
882             if (id == MouseEvent.MOUSE_PRESSED) {
883 
884                 // Ungrab only if this window is not an owned window of the grabbing one.
885                 if (!isGrabbing() && grabbingWindow != null &&
886                     !grabbingWindow.isOneOfOwnersOf(this))
887                 {
888                     grabbingWindow.ungrab();
889                 }
890                 if (otherButtonsPressed == 0) {
891                     mouseClickButtons = eventButtonMask;
892                 } else {
893                     mouseClickButtons |= eventButtonMask;
894                 }
895 
896                 // The window should be focused on mouse click. If it gets activated by the native platform,
897                 // this request will be no op. It will take effect when:
898                 // 1. A simple not focused window is clicked.
899                 // 2. An active but not focused owner frame/dialog is clicked.
900                 // The mouse event then will trigger a focus request "in window" to the component, so the window
901                 // should gain focus before.
902                 requestWindowFocus(FocusEvent.Cause.MOUSE_EVENT);
903 
904                 mouseDownTarget[targetIdx] = targetPeer;
905             } else if (id == MouseEvent.MOUSE_DRAGGED) {
906                 // Cocoa dragged event has the information about which mouse
907                 // button is being dragged. Use it to determine the peer that
908                 // should receive the dragged event.
909                 targetPeer = mouseDownTarget[targetIdx];
910                 mouseClickButtons &= ~modifiers;
911             } else if (id == MouseEvent.MOUSE_RELEASED) {
912                 // TODO: currently, mouse released event goes to the same component
913                 // that received corresponding mouse pressed event. For most cases,
914                 // it's OK, however, we need to make sure that our behavior is consistent
915                 // with 1.6 for cases where component in question have been
916                 // hidden/removed in between of mouse pressed/released events.
917                 targetPeer = mouseDownTarget[targetIdx];
918 
919                 if ((modifiers & eventButtonMask) == 0) {
920                     mouseDownTarget[targetIdx] = null;
921                 }
922 
923                 // mouseClickButtons is updated below, after MOUSE_CLICK is sent
924             }
925 
926             if (targetPeer == null) {
927                 //TODO This can happen if this window is invisible. this is correct behavior in this case?
928                 targetPeer = this;
929             }
930 
931 
932             Point lp = targetPeer.windowToLocal(x, y, this);
933             if (targetPeer.isEnabled()) {
934                 MouseEvent event = new MouseEvent(targetPeer.getTarget(), id,
935                                                   when, modifiers, lp.x, lp.y,
936                                                   absX, absY, clickCount,
937                                                   popupTrigger, button);
938                 postEvent(event);
939             }
940 
941             if (id == MouseEvent.MOUSE_RELEASED) {
942                 if ((mouseClickButtons & eventButtonMask) != 0
943                     && targetPeer.isEnabled()) {
944                     postEvent(new MouseEvent(targetPeer.getTarget(),
945                                              MouseEvent.MOUSE_CLICKED,
946                                              when, modifiers,
947                                              lp.x, lp.y, absX, absY,
948                                              clickCount, popupTrigger, button));
949                 }
950                 mouseClickButtons &= ~eventButtonMask;
951             }
952         }
953         notifyUpdateCursor();
954     }
955 
generateMouseEnterExitEventsForComponents(long when, int button, int x, int y, int screenX, int screenY, int modifiers, int clickCount, boolean popupTrigger, final LWComponentPeer<?, ?> targetPeer)956     private void generateMouseEnterExitEventsForComponents(long when,
957             int button, int x, int y, int screenX, int screenY,
958             int modifiers, int clickCount, boolean popupTrigger,
959             final LWComponentPeer<?, ?> targetPeer) {
960 
961         if (!isMouseOver || targetPeer == lastMouseEventPeer) {
962             return;
963         }
964 
965         // Generate Mouse Exit for components
966         if (lastMouseEventPeer != null && lastMouseEventPeer.isEnabled()) {
967             Point oldp = lastMouseEventPeer.windowToLocal(x, y, this);
968             Component target = lastMouseEventPeer.getTarget();
969             postMouseExitedEvent(target, when, modifiers, oldp, screenX, screenY,
970                     clickCount, popupTrigger, button);
971         }
972         lastCommonMouseEventPeer = targetPeer;
973         lastMouseEventPeer = targetPeer;
974 
975         // Generate Mouse Enter for components
976         if (targetPeer != null && targetPeer.isEnabled()) {
977             Point newp = targetPeer.windowToLocal(x, y, this);
978             Component target = targetPeer.getTarget();
979             postMouseEnteredEvent(target, when, modifiers, newp, screenX, screenY, clickCount, popupTrigger, button);
980         }
981     }
982 
postMouseEnteredEvent(Component target, long when, int modifiers, Point loc, int xAbs, int yAbs, int clickCount, boolean popupTrigger, int button)983     private void postMouseEnteredEvent(Component target, long when, int modifiers,
984                                        Point loc, int xAbs, int yAbs,
985                                        int clickCount, boolean popupTrigger, int button) {
986 
987         updateSecurityWarningVisibility();
988 
989         postEvent(new MouseEvent(target,
990                 MouseEvent.MOUSE_ENTERED,
991                 when, modifiers,
992                 loc.x, loc.y, xAbs, yAbs,
993                 clickCount, popupTrigger, button));
994     }
995 
postMouseExitedEvent(Component target, long when, int modifiers, Point loc, int xAbs, int yAbs, int clickCount, boolean popupTrigger, int button)996     private void postMouseExitedEvent(Component target, long when, int modifiers,
997                                       Point loc, int xAbs, int yAbs,
998                                       int clickCount, boolean popupTrigger, int button) {
999 
1000         updateSecurityWarningVisibility();
1001 
1002         postEvent(new MouseEvent(target,
1003                 MouseEvent.MOUSE_EXITED,
1004                 when, modifiers,
1005                 loc.x, loc.y, xAbs, yAbs,
1006                 clickCount, popupTrigger, button));
1007     }
1008 
1009     @Override
notifyMouseWheelEvent(long when, int x, int y, int absX, int absY, int modifiers, int scrollType, int scrollAmount, int wheelRotation, double preciseWheelRotation, byte[] bdata)1010     public void notifyMouseWheelEvent(long when, int x, int y, int absX,
1011                                       int absY, int modifiers, int scrollType,
1012                                       int scrollAmount, int wheelRotation,
1013                                       double preciseWheelRotation, byte[] bdata)
1014     {
1015         // TODO: could we just use the last mouse event target here?
1016         Rectangle r = getBounds();
1017         // findPeerAt() expects parent coordinates
1018         final LWComponentPeer<?, ?> targetPeer = findPeerAt(r.x + x, r.y + y);
1019         if (targetPeer == null || !targetPeer.isEnabled()) {
1020             return;
1021         }
1022 
1023         Point lp = targetPeer.windowToLocal(x, y, this);
1024         // TODO: fill "bdata" member of AWTEvent
1025         postEvent(new MouseWheelEvent(targetPeer.getTarget(),
1026                                       MouseEvent.MOUSE_WHEEL,
1027                                       when, modifiers,
1028                                       lp.x, lp.y,
1029                                       absX, absY, /* absX, absY */
1030                                       0 /* clickCount */, false /* popupTrigger */,
1031                                       scrollType, scrollAmount,
1032                                       wheelRotation, preciseWheelRotation));
1033     }
1034 
1035     /*
1036      * Called by the delegate when a key is pressed.
1037      */
1038     @Override
notifyKeyEvent(int id, long when, int modifiers, int keyCode, char keyChar, int keyLocation)1039     public void notifyKeyEvent(int id, long when, int modifiers,
1040                                int keyCode, char keyChar, int keyLocation)
1041     {
1042         LWKeyboardFocusManagerPeer kfmPeer = LWKeyboardFocusManagerPeer.getInstance();
1043         Component focusOwner = kfmPeer.getCurrentFocusOwner();
1044 
1045         if (focusOwner == null) {
1046             focusOwner = kfmPeer.getCurrentFocusedWindow();
1047             if (focusOwner == null) {
1048                 focusOwner = this.getTarget();
1049             }
1050         }
1051 
1052         KeyEvent keyEvent = new KeyEvent(focusOwner, id, when, modifiers,
1053             keyCode, keyChar, keyLocation);
1054         AWTAccessor.getKeyEventAccessor().setExtendedKeyCode(keyEvent,
1055                 (keyChar == KeyEvent.CHAR_UNDEFINED) ? keyCode
1056                 : ExtendedKeyCodes.getExtendedKeyCodeForChar(keyChar));
1057         postEvent(keyEvent);
1058     }
1059 
1060     // ---- UTILITY METHODS ---- //
1061 
activateDisplayListener()1062     private void activateDisplayListener() {
1063         final GraphicsEnvironment ge =
1064                 GraphicsEnvironment.getLocalGraphicsEnvironment();
1065         ((SunGraphicsEnvironment) ge).addDisplayChangedListener(this);
1066     }
1067 
deactivateDisplayListener()1068     private void deactivateDisplayListener() {
1069         final GraphicsEnvironment ge =
1070                 GraphicsEnvironment.getLocalGraphicsEnvironment();
1071         ((SunGraphicsEnvironment) ge).removeDisplayChangedListener(this);
1072     }
1073 
postWindowStateChangedEvent(int newWindowState)1074     private void postWindowStateChangedEvent(int newWindowState) {
1075         if (getTarget() instanceof Frame) {
1076             AWTAccessor.getFrameAccessor().setExtendedState(
1077                     (Frame)getTarget(), newWindowState);
1078         }
1079 
1080         WindowEvent stateChangedEvent = new WindowEvent(getTarget(),
1081                 WindowEvent.WINDOW_STATE_CHANGED,
1082                 windowState, newWindowState);
1083         postEvent(stateChangedEvent);
1084         windowState = newWindowState;
1085 
1086         updateSecurityWarningVisibility();
1087     }
1088 
getGraphicsConfigScreen(GraphicsConfiguration gc)1089     private static int getGraphicsConfigScreen(GraphicsConfiguration gc) {
1090         // TODO: this method can be implemented in a more
1091         // efficient way by forwarding to the delegate
1092         GraphicsDevice gd = gc.getDevice();
1093         GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
1094         GraphicsDevice[] gds = ge.getScreenDevices();
1095         for (int i = 0; i < gds.length; i++) {
1096             if (gds[i] == gd) {
1097                 return i;
1098             }
1099         }
1100         // Should never happen if gc is a screen device config
1101         return 0;
1102     }
1103 
1104     /*
1105      * This method is called when window's graphics config is changed from
1106      * the app code (e.g. when the window is made non-opaque) or when
1107      * the window is moved to another screen by user.
1108      *
1109      * Returns true if the graphics config has been changed, false otherwise.
1110      */
setGraphicsConfig(GraphicsConfiguration gc)1111     private boolean setGraphicsConfig(GraphicsConfiguration gc) {
1112         synchronized (getStateLock()) {
1113             if (graphicsConfig == gc) {
1114                 return false;
1115             }
1116             // If window's graphics config is changed from the app code, the
1117             // config correspond to the same device as before; when the window
1118             // is moved by user, graphicsDevice is updated in notifyReshape().
1119             // In either case, there's nothing to do with screenOn here
1120             graphicsConfig = gc;
1121         }
1122         // SurfaceData is replaced later in updateGraphicsData()
1123         return true;
1124     }
1125 
1126     /**
1127      * Returns true if the GraphicsDevice has been changed, false otherwise.
1128      */
updateGraphicsDevice()1129     public boolean updateGraphicsDevice() {
1130         GraphicsDevice newGraphicsDevice = platformWindow.getGraphicsDevice();
1131         synchronized (getStateLock()) {
1132             if (graphicsDevice == newGraphicsDevice) {
1133                 return false;
1134             }
1135             graphicsDevice = newGraphicsDevice;
1136         }
1137 
1138         final GraphicsConfiguration newGC = newGraphicsDevice.getDefaultConfiguration();
1139 
1140         if (!setGraphicsConfig(newGC)) return false;
1141 
1142         SunToolkit.executeOnEventHandlerThread(getTarget(), new Runnable() {
1143             public void run() {
1144                 AWTAccessor.getComponentAccessor().setGraphicsConfiguration(getTarget(), newGC);
1145             }
1146         });
1147         return true;
1148     }
1149 
1150     @Override
displayChanged()1151     public final void displayChanged() {
1152         if (updateGraphicsDevice()) {
1153             updateMinimumSize();
1154             if (!isMaximizedBoundsSet()) {
1155                 setPlatformMaximizedBounds(getDefaultMaximizedBounds());
1156             }
1157         }
1158         // Replace surface unconditionally, because internal state of the
1159         // GraphicsDevice could be changed.
1160         replaceSurfaceData();
1161         repaintPeer();
1162     }
1163 
1164     @Override
paletteChanged()1165     public final void paletteChanged() {
1166         // components do not need to react to this event.
1167     }
1168 
1169     /*
1170      * May be called by delegate to provide SD to Java2D code.
1171      */
getSurfaceData()1172     public SurfaceData getSurfaceData() {
1173         synchronized (surfaceDataLock) {
1174             return surfaceData;
1175         }
1176     }
1177 
replaceSurfaceData()1178     private void replaceSurfaceData() {
1179         replaceSurfaceData(true);
1180     }
1181 
replaceSurfaceData(final boolean blit)1182     private void replaceSurfaceData(final boolean blit) {
1183         synchronized (surfaceDataLock) {
1184             final SurfaceData oldData = getSurfaceData();
1185             surfaceData = platformWindow.replaceSurfaceData();
1186             final Rectangle size = getSize();
1187             if (getSurfaceData() != null && oldData != getSurfaceData()) {
1188                 clearBackground(size.width, size.height);
1189             }
1190 
1191             if (blit) {
1192                 blitSurfaceData(oldData, getSurfaceData());
1193             }
1194 
1195             if (oldData != null && oldData != getSurfaceData()) {
1196                 // TODO: drop oldData for D3D/WGL pipelines
1197                 // This can only happen when this peer is being created
1198                 oldData.flush();
1199             }
1200         }
1201         flushOnscreenGraphics();
1202     }
1203 
blitSurfaceData(final SurfaceData src, final SurfaceData dst)1204     private void blitSurfaceData(final SurfaceData src, final SurfaceData dst) {
1205         //TODO blit. proof-of-concept
1206         if (src != dst && src != null && dst != null
1207             && !(dst instanceof NullSurfaceData)
1208             && !(src instanceof NullSurfaceData)
1209             && src.getSurfaceType().equals(dst.getSurfaceType())
1210             && src.getDefaultScaleX() == dst.getDefaultScaleX()
1211             && src.getDefaultScaleY() == dst.getDefaultScaleY())
1212         {
1213             final Rectangle size = src.getBounds();
1214             final Blit blit = Blit.locate(src.getSurfaceType(),
1215                                           CompositeType.Src,
1216                                           dst.getSurfaceType());
1217             if (blit != null) {
1218                 blit.Blit(src, dst, AlphaComposite.Src, null, 0, 0, 0, 0,
1219                           size.width, size.height);
1220             }
1221         }
1222     }
1223 
1224     /**
1225      * Request the window insets from the delegate and compares it with the
1226      * current one. This method is mostly called by the delegate, e.g. when the
1227      * window state is changed and insets should be recalculated.
1228      * <p/>
1229      * This method may be called on the toolkit thread.
1230      */
updateInsets(final Insets newInsets)1231     public final boolean updateInsets(final Insets newInsets) {
1232         synchronized (getStateLock()) {
1233             if (insets.equals(newInsets)) {
1234                 return false;
1235             }
1236             insets = newInsets;
1237         }
1238         return true;
1239     }
1240 
getWindowUnderCursor()1241     public static LWWindowPeer getWindowUnderCursor() {
1242         return lastCommonMouseEventPeer != null ? lastCommonMouseEventPeer.getWindowPeerOrSelf() : null;
1243     }
1244 
getPeerUnderCursor()1245     public static LWComponentPeer<?, ?> getPeerUnderCursor() {
1246         return lastCommonMouseEventPeer;
1247     }
1248 
1249     /*
1250      * Requests platform to set native focus on a frame/dialog.
1251      * In case of a simple window, triggers appropriate java focus change.
1252      */
requestWindowFocus(FocusEvent.Cause cause)1253     public boolean requestWindowFocus(FocusEvent.Cause cause) {
1254         if (focusLog.isLoggable(PlatformLogger.Level.FINE)) {
1255             focusLog.fine("requesting native focus to " + this);
1256         }
1257 
1258         if (!focusAllowedFor()) {
1259             focusLog.fine("focus is not allowed");
1260             return false;
1261         }
1262 
1263         if (platformWindow.rejectFocusRequest(cause)) {
1264             return false;
1265         }
1266 
1267         AppContext targetAppContext = AWTAccessor.getComponentAccessor().getAppContext(getTarget());
1268         KeyboardFocusManager kfm = AWTAccessor.getKeyboardFocusManagerAccessor()
1269                 .getCurrentKeyboardFocusManager(targetAppContext);
1270         Window currentActive = kfm.getActiveWindow();
1271 
1272 
1273         Window opposite = LWKeyboardFocusManagerPeer.getInstance().
1274             getCurrentFocusedWindow();
1275 
1276         // Make the owner active window.
1277         if (isSimpleWindow()) {
1278             LWWindowPeer owner = getOwnerFrameDialog(this);
1279 
1280             // If owner is not natively active, request native
1281             // activation on it w/o sending events up to java.
1282             if (owner != null && !owner.platformWindow.isActive()) {
1283                 if (focusLog.isLoggable(PlatformLogger.Level.FINE)) {
1284                     focusLog.fine("requesting native focus to the owner " + owner);
1285                 }
1286                 LWWindowPeer currentActivePeer = currentActive == null ? null :
1287                 (LWWindowPeer) AWTAccessor.getComponentAccessor().getPeer(
1288                         currentActive);
1289 
1290                 // Ensure the opposite is natively active and suppress sending events.
1291                 if (currentActivePeer != null && currentActivePeer.platformWindow.isActive()) {
1292                     if (focusLog.isLoggable(PlatformLogger.Level.FINE)) {
1293                         focusLog.fine("the opposite is " + currentActivePeer);
1294                     }
1295                     currentActivePeer.skipNextFocusChange = true;
1296                 }
1297                 owner.skipNextFocusChange = true;
1298 
1299                 owner.platformWindow.requestWindowFocus();
1300             }
1301 
1302             // DKFM will synthesize all the focus/activation events correctly.
1303             changeFocusedWindow(true, opposite);
1304             return true;
1305 
1306         // In case the toplevel is active but not focused, change focus directly,
1307         // as requesting native focus on it will not have effect.
1308         } else if (getTarget() == currentActive && !getTarget().hasFocus()) {
1309 
1310             changeFocusedWindow(true, opposite);
1311             return true;
1312         }
1313 
1314         return platformWindow.requestWindowFocus();
1315     }
1316 
focusAllowedFor()1317     protected boolean focusAllowedFor() {
1318         Window window = getTarget();
1319         // TODO: check if modal blocked
1320         return window.isVisible() && window.isEnabled() && isFocusableWindow();
1321     }
1322 
isFocusableWindow()1323     private boolean isFocusableWindow() {
1324         boolean focusable  = targetFocusable;
1325         if (isSimpleWindow()) {
1326             LWWindowPeer ownerPeer = getOwnerFrameDialog(this);
1327             if (ownerPeer == null) {
1328                 return false;
1329             }
1330             return focusable && ownerPeer.targetFocusable;
1331         }
1332         return focusable;
1333     }
1334 
isSimpleWindow()1335     public boolean isSimpleWindow() {
1336         Window window = getTarget();
1337         return !(window instanceof Dialog || window instanceof Frame);
1338     }
1339 
1340     @Override
emulateActivation(boolean activate)1341     public void emulateActivation(boolean activate) {
1342         changeFocusedWindow(activate, null);
1343     }
1344 
1345     @SuppressWarnings("deprecation")
isOneOfOwnersOf(LWWindowPeer peer)1346     private boolean isOneOfOwnersOf(LWWindowPeer peer) {
1347         Window owner = (peer != null ? peer.getTarget().getOwner() : null);
1348         while (owner != null) {
1349             final ComponentAccessor acc = AWTAccessor.getComponentAccessor();
1350             if (acc.getPeer(owner) == this) {
1351                 return true;
1352             }
1353             owner = owner.getOwner();
1354         }
1355         return false;
1356     }
1357 
1358     /*
1359      * Changes focused window on java level.
1360      */
changeFocusedWindow(boolean becomesFocused, Window opposite)1361     protected void changeFocusedWindow(boolean becomesFocused, Window opposite) {
1362         if (focusLog.isLoggable(PlatformLogger.Level.FINE)) {
1363             focusLog.fine((becomesFocused?"gaining":"loosing") + " focus window: " + this);
1364         }
1365         if (skipNextFocusChange) {
1366             focusLog.fine("skipping focus change");
1367             skipNextFocusChange = false;
1368             return;
1369         }
1370         if (!isFocusableWindow() && becomesFocused) {
1371             focusLog.fine("the window is not focusable");
1372             return;
1373         }
1374         if (becomesFocused) {
1375             synchronized (getPeerTreeLock()) {
1376                 if (blocker != null) {
1377                     if (focusLog.isLoggable(PlatformLogger.Level.FINEST)) {
1378                         focusLog.finest("the window is blocked by " + blocker);
1379                     }
1380                     return;
1381                 }
1382             }
1383         }
1384 
1385         // Note, the method is not called:
1386         // - when the opposite (gaining focus) window is an owned/owner window.
1387         // - for a simple window in any case.
1388         if (!becomesFocused &&
1389             (isGrabbing() || this.isOneOfOwnersOf(grabbingWindow)))
1390         {
1391             if (focusLog.isLoggable(PlatformLogger.Level.FINE)) {
1392                 focusLog.fine("ungrabbing on " + grabbingWindow);
1393             }
1394             // ungrab a simple window if its owner looses activation.
1395             grabbingWindow.ungrab();
1396         }
1397 
1398         KeyboardFocusManagerPeer kfmPeer = LWKeyboardFocusManagerPeer.getInstance();
1399 
1400         if (!becomesFocused && kfmPeer.getCurrentFocusedWindow() != getTarget()) {
1401             // late window focus lost event - ingoring
1402             return;
1403         }
1404 
1405         kfmPeer.setCurrentFocusedWindow(becomesFocused ? getTarget() : null);
1406 
1407         int eventID = becomesFocused ? WindowEvent.WINDOW_GAINED_FOCUS : WindowEvent.WINDOW_LOST_FOCUS;
1408         WindowEvent windowEvent = new TimedWindowEvent(getTarget(), eventID, opposite, System.currentTimeMillis());
1409 
1410         // TODO: wrap in SequencedEvent
1411         postEvent(windowEvent);
1412     }
1413 
1414     /*
1415      * Retrieves the owner of the peer.
1416      * Note: this method returns the owner which can be activated, (i.e. the instance
1417      * of Frame or Dialog may be returned).
1418      */
getOwnerFrameDialog(LWWindowPeer peer)1419     static LWWindowPeer getOwnerFrameDialog(LWWindowPeer peer) {
1420         Window owner = (peer != null ? peer.getTarget().getOwner() : null);
1421         while (owner != null && !(owner instanceof Frame || owner instanceof Dialog)) {
1422             owner = owner.getOwner();
1423         }
1424         return owner == null ? null : AWTAccessor.getComponentAccessor()
1425                                                  .getPeer(owner);
1426     }
1427 
1428     /**
1429      * Returns the foremost modal blocker of this window, or null.
1430      */
getBlocker()1431     public LWWindowPeer getBlocker() {
1432         synchronized (getPeerTreeLock()) {
1433             LWWindowPeer blocker = this.blocker;
1434             if (blocker == null) {
1435                 return null;
1436             }
1437             while (blocker.blocker != null) {
1438                 blocker = blocker.blocker;
1439             }
1440             return blocker;
1441         }
1442     }
1443 
1444     @Override
enterFullScreenMode()1445     public void enterFullScreenMode() {
1446         platformWindow.enterFullScreenMode();
1447         updateSecurityWarningVisibility();
1448     }
1449 
1450     @Override
exitFullScreenMode()1451     public void exitFullScreenMode() {
1452         platformWindow.exitFullScreenMode();
1453         updateSecurityWarningVisibility();
1454     }
1455 
getLayerPtr()1456     public long getLayerPtr() {
1457         return getPlatformWindow().getLayerPtr();
1458     }
1459 
grab()1460     void grab() {
1461         if (grabbingWindow != null && !isGrabbing()) {
1462             grabbingWindow.ungrab();
1463         }
1464         grabbingWindow = this;
1465     }
1466 
ungrab(boolean doPost)1467     final void ungrab(boolean doPost) {
1468         if (isGrabbing()) {
1469             grabbingWindow = null;
1470             if (doPost) {
1471                 postEvent(new UngrabEvent(getTarget()));
1472             }
1473         }
1474     }
1475 
ungrab()1476     void ungrab() {
1477         ungrab(true);
1478     }
1479 
isGrabbing()1480     private boolean isGrabbing() {
1481         return this == grabbingWindow;
1482     }
1483 
getPeerType()1484     public PeerType getPeerType() {
1485         return peerType;
1486     }
1487 
updateSecurityWarningVisibility()1488     public void updateSecurityWarningVisibility() {
1489         if (warningWindow == null) {
1490             return;
1491         }
1492 
1493         if (!isVisible()) {
1494             return; // The warning window should already be hidden.
1495         }
1496 
1497         boolean show = false;
1498 
1499         if (!platformWindow.isFullScreenMode()) {
1500             if (isVisible()) {
1501                 if (LWKeyboardFocusManagerPeer.getInstance().getCurrentFocusedWindow() ==
1502                         getTarget()) {
1503                     show = true;
1504                 }
1505 
1506                 if (platformWindow.isUnderMouse() || warningWindow.isUnderMouse()) {
1507                     show = true;
1508                 }
1509             }
1510         }
1511 
1512         warningWindow.setVisible(show, true);
1513     }
1514 
1515     @Override
toString()1516     public String toString() {
1517         return super.toString() + " [target is " + getTarget() + "]";
1518     }
1519 }
1520