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