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.AWTEvent;
29 import java.awt.AWTException;
30 import java.awt.BufferCapabilities;
31 import java.awt.Color;
32 import java.awt.Component;
33 import java.awt.Container;
34 import java.awt.Cursor;
35 import java.awt.Dimension;
36 import java.awt.Font;
37 import java.awt.FontMetrics;
38 import java.awt.Graphics;
39 import java.awt.GraphicsConfiguration;
40 import java.awt.Image;
41 import java.awt.Point;
42 import java.awt.Rectangle;
43 import java.awt.Toolkit;
44 import java.awt.Window;
45 import java.awt.dnd.DropTarget;
46 import java.awt.dnd.peer.DropTargetPeer;
47 import java.awt.event.AWTEventListener;
48 import java.awt.event.ComponentEvent;
49 import java.awt.event.FocusEvent;
50 import java.awt.event.InputEvent;
51 import java.awt.event.KeyEvent;
52 import java.awt.event.MouseEvent;
53 import java.awt.event.MouseWheelEvent;
54 import java.awt.event.PaintEvent;
55 import java.awt.image.ColorModel;
56 import java.awt.image.ImageObserver;
57 import java.awt.image.ImageProducer;
58 import java.awt.image.VolatileImage;
59 import java.awt.peer.ComponentPeer;
60 import java.awt.peer.ContainerPeer;
61 import java.awt.peer.KeyboardFocusManagerPeer;
62 import java.lang.reflect.Field;
63 import java.security.AccessController;
64 import java.security.PrivilegedAction;
65 import java.util.concurrent.atomic.AtomicBoolean;
66 
67 import javax.swing.JComponent;
68 import javax.swing.RepaintManager;
69 import javax.swing.SwingUtilities;
70 
71 import com.sun.java.swing.SwingUtilities3;
72 import sun.awt.AWTAccessor;
73 import sun.awt.PaintEventDispatcher;
74 import sun.awt.RepaintArea;
75 import sun.awt.SunToolkit;
76 import sun.awt.event.IgnorePaintEvent;
77 import sun.awt.image.SunVolatileImage;
78 import sun.awt.image.ToolkitImage;
79 import sun.java2d.SunGraphics2D;
80 import sun.java2d.opengl.OGLRenderQueue;
81 import sun.java2d.pipe.Region;
82 import sun.util.logging.PlatformLogger;
83 
84 public abstract class LWComponentPeer<T extends Component, D extends JComponent>
85     implements ComponentPeer, DropTargetPeer
86 {
87     private static final PlatformLogger focusLog = PlatformLogger.getLogger("sun.lwawt.focus.LWComponentPeer");
88 
89     /**
90      * State lock is to be used for modifications to this peer's fields (e.g.
91      * bounds, background, font, etc.) It should be the last lock in the lock
92      * chain
93      */
94     private final Object stateLock = new Object();
95 
96     /**
97      * The lock to operate with the peers hierarchy. AWT tree lock is not used
98      * as there are many peers related ops to be done on the toolkit thread, and
99      * we don't want to depend on a public lock on this thread
100      */
101     private static final Object peerTreeLock = new Object();
102 
103     /**
104      * The associated AWT object.
105      */
106     private final T target;
107 
108     /**
109      * Container peer. It may not be the peer of the target's direct parent, for
110      * example, in the case of hw/lw mixing. However, let's skip this scenario
111      * for the time being. We also assume the container peer is not null, which
112      * might also be false if addNotify() is called for a component outside of
113      * the hierarchy. The exception is LWWindowPeers: their containers are
114      * always null
115      */
116     private final LWContainerPeer<?, ?> containerPeer;
117 
118     /**
119      * Handy reference to the top-level window peer. Window peer is borrowed
120      * from the containerPeer in constructor, and should also be updated when
121      * the component is reparented to another container
122      */
123     private final LWWindowPeer windowPeer;
124 
125     private final AtomicBoolean disposed = new AtomicBoolean(false);
126 
127     // Bounds are relative to parent peer
128     private final Rectangle bounds = new Rectangle();
129     private Region region;
130 
131     // Component state. Should be accessed under the state lock
132     private boolean visible = false;
133     private boolean enabled = true;
134 
135     private Color background;
136     private Color foreground;
137     private Font font;
138 
139     /**
140      * Paint area to coalesce all the paint events and store the target dirty
141      * area.
142      */
143     private final RepaintArea targetPaintArea;
144 
145     //   private volatile boolean paintPending;
146     private volatile boolean isLayouting;
147 
148     private final D delegate;
149     private Container delegateContainer;
150     private Component delegateDropTarget;
151     private final Object dropTargetLock = new Object();
152 
153     private int fNumDropTargets = 0;
154     private PlatformDropTarget fDropTarget = null;
155 
156     private final PlatformComponent platformComponent;
157 
158     /**
159      * Character with reasonable value between the minimum width and maximum.
160      */
161     static final char WIDE_CHAR = '0';
162 
163     /**
164      * The back buffer provide user with a BufferStrategy.
165      */
166     private Image backBuffer;
167 
168     /**
169      * All Swing delegates use delegateContainer as a parent. This container
170      * intentionally do not use parent of the peer.
171      */
172     @SuppressWarnings("serial")// Safe: outer class is non-serializable.
173     private final class DelegateContainer extends Container {
174         {
175             enableEvents(0xFFFFFFFF);
176         }
177 
178         // Empty non private constructor was added because access to this
179         // class shouldn't be emulated by a synthetic accessor method.
DelegateContainer()180         DelegateContainer() {
181             super();
182         }
183 
184         @Override
isLightweight()185         public boolean isLightweight() {
186             return false;
187         }
188 
189         @Override
getLocation()190         public Point getLocation() {
191             return getLocationOnScreen();
192         }
193 
194         @Override
getLocationOnScreen()195         public Point getLocationOnScreen() {
196             return LWComponentPeer.this.getLocationOnScreen();
197         }
198 
199         @Override
getX()200         public int getX() {
201             return getLocation().x;
202         }
203 
204         @Override
getY()205         public int getY() {
206             return getLocation().y;
207         }
208     }
209 
LWComponentPeer(final T target, final PlatformComponent platformComponent)210     LWComponentPeer(final T target, final PlatformComponent platformComponent) {
211         targetPaintArea = new LWRepaintArea();
212         this.target = target;
213         this.platformComponent = platformComponent;
214 
215         // Container peer is always null for LWWindowPeers, so
216         // windowPeer is always null for them as well. On the other
217         // hand, LWWindowPeer shouldn't use windowPeer at all
218         final Container container = SunToolkit.getNativeContainer(target);
219         containerPeer = (LWContainerPeer) LWToolkit.targetToPeer(container);
220         windowPeer = containerPeer != null ? containerPeer.getWindowPeerOrSelf()
221                                            : null;
222         // don't bother about z-order here as updateZOrder()
223         // will be called from addNotify() later anyway
224         if (containerPeer != null) {
225             containerPeer.addChildPeer(this);
226         }
227 
228         // the delegate must be created after the target is set
229         AWTEventListener toolkitListener = null;
230         synchronized (Toolkit.getDefaultToolkit()) {
231             try {
232                 toolkitListener = getToolkitAWTEventListener();
233                 setToolkitAWTEventListener(null);
234 
235                 synchronized (getDelegateLock()) {
236                     delegate = createDelegate();
237                     if (delegate != null) {
238                         delegate.setVisible(false);
239                         delegateContainer = new DelegateContainer();
240                         delegateContainer.add(delegate);
241                         delegateContainer.addNotify();
242                         delegate.addNotify();
243                         resetColorsAndFont(delegate);
244                         delegate.setOpaque(true);
245                     } else {
246                         return;
247                     }
248                 }
249 
250             } finally {
251                 setToolkitAWTEventListener(toolkitListener);
252             }
253 
254             // todo swing: later on we will probably have one global RM
255             SwingUtilities3.setDelegateRepaintManager(delegate, new RepaintManager() {
256                 @Override
257                 public void addDirtyRegion(final JComponent c, final int x, final int y, final int w, final int h) {
258                     repaintPeer(SwingUtilities.convertRectangle(
259                             c, new Rectangle(x, y, w, h), getDelegate()));
260                 }
261             });
262         }
263     }
264 
265     /**
266      * This method must be called under Toolkit.getDefaultToolkit() lock
267      * and followed by setToolkitAWTEventListener()
268      */
getToolkitAWTEventListener()269     protected final AWTEventListener getToolkitAWTEventListener() {
270         return AccessController.doPrivileged(new PrivilegedAction<AWTEventListener>() {
271             public AWTEventListener run() {
272                 Toolkit toolkit = Toolkit.getDefaultToolkit();
273                 try {
274                     Field field = Toolkit.class.getDeclaredField("eventListener");
275                     field.setAccessible(true);
276                     return (AWTEventListener) field.get(toolkit);
277                 } catch (Exception e) {
278                     throw new InternalError(e.toString());
279                 }
280             }
281         });
282     }
283 
284     protected final void setToolkitAWTEventListener(final AWTEventListener listener) {
285         AccessController.doPrivileged(new PrivilegedAction<Void>() {
286             public Void run() {
287                 Toolkit toolkit = Toolkit.getDefaultToolkit();
288                 try {
289                     Field field = Toolkit.class.getDeclaredField("eventListener");
290                     field.setAccessible(true);
291                     field.set(toolkit, listener);
292                 } catch (Exception e) {
293                     throw new InternalError(e.toString());
294                 }
295                 return null;
296             }
297         });
298     }
299 
300     /**
301      * This method is called under getDelegateLock().
302      * Overridden in subclasses.
303      */
304     D createDelegate() {
305         return null;
306     }
307 
308     final D getDelegate() {
309         return delegate;
310     }
311 
312     /**
313      * This method should be called under getDelegateLock().
314      */
315     Component getDelegateFocusOwner() {
316         return getDelegate();
317     }
318 
319     /**
320      * Initializes this peer. The call to initialize() is not placed to
321      * LWComponentPeer ctor to let the subclass ctor to finish completely first.
322      * Instead, it's the LWToolkit object who is responsible for initialization.
323      * Note that we call setVisible() at the end of initialization.
324      */
325     public final void initialize() {
326         platformComponent.initialize(getPlatformWindow());
327         initializeImpl();
328         setVisible(target.isVisible());
329     }
330 
331     /**
332      * Fetching general properties from the target. Should be overridden in
333      * subclasses to initialize specific peers properties.
334      */
335     void initializeImpl() {
336         // note that these methods can be overridden by the user and
337         // can return some strange values like null.
338         setBackground(target.getBackground());
339         setForeground(target.getForeground());
340         setFont(target.getFont());
341         setBounds(target.getBounds());
342         setEnabled(target.isEnabled());
343     }
344 
345     private static void resetColorsAndFont(final Container c) {
346         c.setBackground(null);
347         c.setForeground(null);
348         c.setFont(null);
349         for (int i = 0; i < c.getComponentCount(); i++) {
350             resetColorsAndFont((Container) c.getComponent(i));
351         }
352     }
353 
354     final Object getStateLock() {
355         return stateLock;
356     }
357 
358     /**
359      * Synchronize all operations with the Swing delegates under AWT tree lock,
360      * using a new separate lock to synchronize access to delegates may lead
361      * deadlocks. Think of it as a 'virtual EDT'.
362      *
363      * @return DelegateLock
364      */
365     final Object getDelegateLock() {
366         return getTarget().getTreeLock();
367     }
368 
369     protected static final Object getPeerTreeLock() {
370         return peerTreeLock;
371     }
372 
373     public final T getTarget() {
374         return target;
375     }
376 
377     // Just a helper method
378     // Returns the window peer or null if this is a window peer
379     protected final LWWindowPeer getWindowPeer() {
380         return windowPeer;
381     }
382 
383     // Returns the window peer or 'this' if this is a window peer
384     protected LWWindowPeer getWindowPeerOrSelf() {
385         return getWindowPeer();
386     }
387 
388     // Just a helper method
389     protected final LWContainerPeer<?, ?> getContainerPeer() {
390         return containerPeer;
391     }
392 
393     public PlatformWindow getPlatformWindow() {
394         LWWindowPeer windowPeer = getWindowPeer();
395         return windowPeer.getPlatformWindow();
396     }
397 
398     // ---- PEER METHODS ---- //
399 
400     // Just a helper method
401     public LWToolkit getLWToolkit() {
402         return LWToolkit.getLWToolkit();
403     }
404 
405     @Override
406     public final void dispose() {
407         if (disposed.compareAndSet(false, true)) {
408             disposeImpl();
409         }
410     }
411 
412     protected void disposeImpl() {
413         destroyBuffers();
414         LWContainerPeer<?, ?> cp = getContainerPeer();
415         if (cp != null) {
416             cp.removeChildPeer(this);
417         }
418         platformComponent.dispose();
419         LWToolkit.targetDisposedPeer(getTarget(), this);
420     }
421 
422     public final boolean isDisposed() {
423         return disposed.get();
424     }
425 
426     /*
427      * GraphicsConfiguration is borrowed from the parent peer. The
428      * return value must not be null.
429      *
430      * Overridden in LWWindowPeer.
431      */
432     @Override
433     public GraphicsConfiguration getGraphicsConfiguration() {
434         // Don't check windowPeer for null as it can only happen
435         // for windows, but this method is overridden in
436         // LWWindowPeer and doesn't call super()
437         return getWindowPeer().getGraphicsConfiguration();
438     }
439 
440 
441     // Just a helper method
442     public final LWGraphicsConfig getLWGC() {
443         return (LWGraphicsConfig) getGraphicsConfiguration();
444     }
445 
446     /*
447      * Overridden in LWWindowPeer to replace its surface
448      * data and back buffer.
449      */
450     @Override
451     public boolean updateGraphicsData(GraphicsConfiguration gc) {
452         // TODO: not implemented
453 //        throw new RuntimeException("Has not been implemented yet.");
454         return false;
455     }
456 
457     @Override
458     public Graphics getGraphics() {
459         final Graphics g = getOnscreenGraphics();
460         if (g != null) {
461             synchronized (getPeerTreeLock()){
462                 applyConstrain(g);
463             }
464         }
465         return g;
466     }
467 
468     /*
469      * Peer Graphics is borrowed from the parent peer, while
470      * foreground and background colors and font are specific to
471      * this peer.
472      */
473     public final Graphics getOnscreenGraphics() {
474         final LWWindowPeer wp = getWindowPeerOrSelf();
475         return wp.getOnscreenGraphics(getForeground(), getBackground(),
476                                       getFont());
477 
478     }
479 
480     private void applyConstrain(final Graphics g) {
481         final SunGraphics2D sg2d = (SunGraphics2D) g;
482         final Rectangle size = localToWindow(getSize());
483         sg2d.constrain(size.x, size.y, size.width, size.height, getVisibleRegion());
484     }
485 
486     Region getVisibleRegion() {
487         return computeVisibleRect(this, getRegion());
488     }
489 
490     static final Region computeVisibleRect(final LWComponentPeer<?, ?> c,
491                                            Region region) {
492         final LWContainerPeer<?, ?> p = c.getContainerPeer();
493         if (p != null) {
494             final Rectangle r = c.getBounds();
495             region = region.getTranslatedRegion(r.x, r.y);
496             region = region.getIntersection(p.getRegion());
497             region = region.getIntersection(p.getContentSize());
498             region = p.cutChildren(region, c);
499             region = computeVisibleRect(p, region);
500             region = region.getTranslatedRegion(-r.x, -r.y);
501         }
502         return region;
503     }
504 
505     @Override
506     public ColorModel getColorModel() {
507         // Is it a correct implementation?
508         return getGraphicsConfiguration().getColorModel();
509     }
510 
511     public boolean isTranslucent() {
512         // Translucent windows of the top level are supported only
513         return false;
514     }
515 
516     @Override
517     public final void createBuffers(int numBuffers, BufferCapabilities caps)
518             throws AWTException {
519         getLWGC().assertOperationSupported(numBuffers, caps);
520         final Image buffer = getLWGC().createBackBuffer(this);
521         synchronized (getStateLock()) {
522             backBuffer = buffer;
523         }
524     }
525 
526     @Override
527     public final Image getBackBuffer() {
528         synchronized (getStateLock()) {
529             if (backBuffer != null) {
530                 return backBuffer;
531             }
532         }
533         throw new IllegalStateException("Buffers have not been created");
534     }
535 
536     @Override
537     public final void flip(int x1, int y1, int x2, int y2,
538                      BufferCapabilities.FlipContents flipAction) {
539         getLWGC().flip(this, getBackBuffer(), x1, y1, x2, y2, flipAction);
540     }
541 
542     @Override
543     public final void destroyBuffers() {
544         final Image oldBB;
545         synchronized (getStateLock()) {
546             oldBB = backBuffer;
547             backBuffer = null;
548         }
549         getLWGC().destroyBackBuffer(oldBB);
550     }
551 
552     // Helper method
553     public void setBounds(Rectangle r) {
554         setBounds(r.x, r.y, r.width, r.height, SET_BOUNDS);
555     }
556 
557     /**
558      * This method could be called on the toolkit thread.
559      */
560     @Override
561     public void setBounds(int x, int y, int w, int h, int op) {
562         setBounds(x, y, w, h, op, true, false);
563     }
564 
565     protected void setBounds(int x, int y, int w, int h, int op, boolean notify,
566                              final boolean updateTarget) {
567         Rectangle oldBounds;
568         synchronized (getStateLock()) {
569             oldBounds = new Rectangle(bounds);
570             if ((op & (SET_LOCATION | SET_BOUNDS)) != 0) {
571                 bounds.x = x;
572                 bounds.y = y;
573             }
574             if ((op & (SET_SIZE | SET_BOUNDS)) != 0) {
575                 bounds.width = w;
576                 bounds.height = h;
577             }
578         }
579         boolean moved = (oldBounds.x != x) || (oldBounds.y != y);
580         boolean resized = (oldBounds.width != w) || (oldBounds.height != h);
581         if (!moved && !resized) {
582             return;
583         }
584         final D delegate = getDelegate();
585         if (delegate != null) {
586             synchronized (getDelegateLock()) {
587                 delegateContainer.setBounds(0, 0, w, h);
588                 delegate.setBounds(delegateContainer.getBounds());
589                 // TODO: the following means that the delegateContainer NEVER gets validated. That's WRONG!
590                 delegate.validate();
591             }
592         }
593 
594         final Point locationInWindow = localToWindow(0, 0);
595         platformComponent.setBounds(locationInWindow.x, locationInWindow.y, w,
596                                     h);
597         if (notify) {
598             repaintOldNewBounds(oldBounds);
599             if (resized) {
600                 handleResize(w, h, updateTarget);
601             }
602             if (moved) {
603                 handleMove(x, y, updateTarget);
604             }
605         }
606     }
607 
608     public final Rectangle getBounds() {
609         synchronized (getStateLock()) {
610             // Return a copy to prevent subsequent modifications
611             return bounds.getBounds();
612         }
613     }
614 
615     public final Rectangle getSize() {
616         synchronized (getStateLock()) {
617             // Return a copy to prevent subsequent modifications
618             return new Rectangle(bounds.width, bounds.height);
619         }
620     }
621 
622     @Override
623     public Point getLocationOnScreen() {
624         Point windowLocation = getWindowPeer().getLocationOnScreen();
625         Point locationInWindow = localToWindow(0, 0);
626         return new Point(windowLocation.x + locationInWindow.x,
627                 windowLocation.y + locationInWindow.y);
628     }
629 
630     /**
631      * Returns the cursor of the peer, which is cursor of the target by default,
632      * but peer can override this behavior.
633      *
634      * @param p Point relative to the peer.
635      * @return Cursor of the peer or null if default cursor should be used.
636      */
637     Cursor getCursor(final Point p) {
638         return getTarget().getCursor();
639     }
640 
641     @Override
642     public void setBackground(final Color c) {
643         final Color oldBg = getBackground();
644         if (oldBg == c || (oldBg != null && oldBg.equals(c))) {
645             return;
646         }
647         synchronized (getStateLock()) {
648             background = c;
649         }
650         final D delegate = getDelegate();
651         if (delegate != null) {
652             synchronized (getDelegateLock()) {
653                 // delegate will repaint the target
654                 delegate.setBackground(c);
655             }
656         } else {
657             repaintPeer();
658         }
659     }
660 
661     public final Color getBackground() {
662         synchronized (getStateLock()) {
663             return background;
664         }
665     }
666 
667     @Override
668     public void setForeground(final Color c) {
669         final Color oldFg = getForeground();
670         if (oldFg == c || (oldFg != null && oldFg.equals(c))) {
671             return;
672         }
673         synchronized (getStateLock()) {
674             foreground = c;
675         }
676         final D delegate = getDelegate();
677         if (delegate != null) {
678             synchronized (getDelegateLock()) {
679                 // delegate will repaint the target
680                 delegate.setForeground(c);
681             }
682         } else {
683             repaintPeer();
684         }
685     }
686 
687     protected final Color getForeground() {
688         synchronized (getStateLock()) {
689             return foreground;
690         }
691     }
692 
693     @Override
694     public void setFont(final Font f) {
695         final Font oldF = getFont();
696         if (oldF == f || (oldF != null && oldF.equals(f))) {
697             return;
698         }
699         synchronized (getStateLock()) {
700             font = f;
701         }
702         final D delegate = getDelegate();
703         if (delegate != null) {
704             synchronized (getDelegateLock()) {
705                 // delegate will repaint the target
706                 delegate.setFont(f);
707             }
708         } else {
709             repaintPeer();
710         }
711     }
712 
713     protected final Font getFont() {
714         synchronized (getStateLock()) {
715             return font;
716         }
717     }
718 
719     @Override
720     public FontMetrics getFontMetrics(final Font f) {
721         // Borrow the metrics from the top-level window
722 //        return getWindowPeer().getFontMetrics(f);
723         // Obtain the metrics from the offscreen window where this peer is
724         // mostly drawn to.
725         // TODO: check for "use platform metrics" settings
726         final Graphics g = getOnscreenGraphics();
727         if (g != null) {
728             try {
729                 return g.getFontMetrics(f);
730             } finally {
731                 g.dispose();
732             }
733         }
734         synchronized (getDelegateLock()) {
735             return delegateContainer.getFontMetrics(f);
736         }
737     }
738 
739     @Override
740     public void setEnabled(final boolean e) {
741         boolean status = e;
742         final LWComponentPeer<?, ?> cp = getContainerPeer();
743         if (cp != null) {
744             status &= cp.isEnabled();
745         }
746         synchronized (getStateLock()) {
747             if (enabled == status) {
748                 return;
749             }
750             enabled = status;
751         }
752 
753         final D delegate = getDelegate();
754 
755         if (delegate != null) {
756             synchronized (getDelegateLock()) {
757                 delegate.setEnabled(status);
758             }
759         } else {
760             repaintPeer();
761         }
762     }
763 
764     // Helper method
765     public final boolean isEnabled() {
766         synchronized (getStateLock()) {
767             return enabled;
768         }
769     }
770 
771     @Override
772     public void setVisible(final boolean v) {
773         synchronized (getStateLock()) {
774             if (visible == v) {
775                 return;
776             }
777             visible = v;
778         }
779         setVisibleImpl(v);
780     }
781 
782     protected void setVisibleImpl(final boolean v) {
783         final D delegate = getDelegate();
784 
785         if (delegate != null) {
786             synchronized (getDelegateLock()) {
787                 delegate.setVisible(v);
788             }
789         }
790         if (visible) {
791             repaintPeer();
792         } else {
793             repaintParent(getBounds());
794         }
795     }
796 
797     // Helper method
798     public final boolean isVisible() {
799         synchronized (getStateLock()) {
800             return visible;
801         }
802     }
803 
804     @Override
805     public void paint(final Graphics g) {
806         getTarget().paint(g);
807     }
808 
809     @Override
810     public void print(final Graphics g) {
811         getTarget().print(g);
812     }
813 
814     @Override
815     public void reparent(ContainerPeer newContainer) {
816         // TODO: not implemented
817         throw new UnsupportedOperationException("ComponentPeer.reparent()");
818     }
819 
820     @Override
821     public boolean isReparentSupported() {
822         // TODO: not implemented
823         return false;
824     }
825 
826     @Override
827     public void setZOrder(final ComponentPeer above) {
828         LWContainerPeer<?, ?> cp = getContainerPeer();
829         // Don't check containerPeer for null as it can only happen
830         // for windows, but this method is overridden in
831         // LWWindowPeer and doesn't call super()
832         cp.setChildPeerZOrder(this, (LWComponentPeer<?, ?>) above);
833     }
834 
835     @Override
836     public void coalescePaintEvent(PaintEvent e) {
837         if (!(e instanceof IgnorePaintEvent)) {
838             Rectangle r = e.getUpdateRect();
839             if ((r != null) && !r.isEmpty()) {
840                 targetPaintArea.add(r, e.getID());
841             }
842         }
843     }
844 
845     /*
846      * Should be overridden in subclasses which use complex Swing components.
847      */
848     @Override
849     public void layout() {
850         // TODO: not implemented
851     }
852 
853     @Override
854     public boolean isObscured() {
855         // TODO: not implemented
856         return false;
857     }
858 
859     @Override
860     public boolean canDetermineObscurity() {
861         // TODO: not implemented
862         return false;
863     }
864 
865     /**
866      * Determines the preferred size of the component. By default forwards the
867      * request to the Swing helper component. Should be overridden in subclasses
868      * if required.
869      */
870     @Override
871     public Dimension getPreferredSize() {
872         final Dimension size;
873         synchronized (getDelegateLock()) {
874             size = getDelegate().getPreferredSize();
875         }
876         return validateSize(size);
877     }
878 
879     /**
880      * Determines the minimum size of the component. By default forwards the
881      * request to the Swing helper component. Should be overridden in subclasses
882      * if required.
883      */
884     @Override
885     public Dimension getMinimumSize() {
886         final Dimension size;
887         synchronized (getDelegateLock()) {
888             size = getDelegate().getMinimumSize();
889         }
890         return validateSize(size);
891     }
892 
893     /**
894      * In some situations delegates can return empty minimum/preferred size.
895      * (For example: empty JLabel, etc), but awt components never should be
896      * empty. In the XPeers or WPeers we use some magic constants, but here we
897      * try to use something more useful,
898      */
899     private Dimension validateSize(final Dimension size) {
900         if (size.width == 0 || size.height == 0) {
901             final FontMetrics fm = getFontMetrics(getFont());
902             size.width = fm.charWidth(WIDE_CHAR);
903             size.height = fm.getHeight();
904         }
905         return size;
906     }
907 
908     @Override
909     public void updateCursorImmediately() {
910         getLWToolkit().getCursorManager().updateCursor();
911     }
912 
913     @Override
914     public boolean isFocusable() {
915         // Overridden in focusable subclasses like buttons
916         return false;
917     }
918 
919     @Override
920     public boolean requestFocus(Component lightweightChild, boolean temporary,
921                                 boolean focusedWindowChangeAllowed, long time,
922                                 FocusEvent.Cause cause)
923     {
924         if (focusLog.isLoggable(PlatformLogger.Level.FINEST)) {
925             focusLog.finest("lightweightChild=" + lightweightChild + ", temporary=" + temporary +
926                             ", focusedWindowChangeAllowed=" + focusedWindowChangeAllowed +
927                             ", time= " + time + ", cause=" + cause);
928         }
929         if (LWKeyboardFocusManagerPeer.processSynchronousLightweightTransfer(
930                 getTarget(), lightweightChild, temporary,
931                 focusedWindowChangeAllowed, time)) {
932             return true;
933         }
934 
935         int result = LWKeyboardFocusManagerPeer.shouldNativelyFocusHeavyweight(
936                 getTarget(), lightweightChild, temporary,
937                 focusedWindowChangeAllowed, time, cause);
938         switch (result) {
939             case LWKeyboardFocusManagerPeer.SNFH_FAILURE:
940                 return false;
941             case LWKeyboardFocusManagerPeer.SNFH_SUCCESS_PROCEED:
942                 Window parentWindow = SunToolkit.getContainingWindow(getTarget());
943                 if (parentWindow == null) {
944                     focusLog.fine("request rejected, parentWindow is null");
945                     LWKeyboardFocusManagerPeer.removeLastFocusRequest(getTarget());
946                     return false;
947                 }
948                 final LWWindowPeer parentPeer =
949                         AWTAccessor.getComponentAccessor()
950                                    .getPeer(parentWindow);
951                 if (parentPeer == null) {
952                     focusLog.fine("request rejected, parentPeer is null");
953                     LWKeyboardFocusManagerPeer.removeLastFocusRequest(getTarget());
954                     return false;
955                 }
956 
957                 // A fix for 7145768. Ensure the parent window is currently natively focused.
958                 // The more evident place to perform this check is in KFM.shouldNativelyFocusHeavyweight,
959                 // however that is the shared code and this particular problem's reproducibility has
960                 // platform specifics. So, it was decided to narrow down the fix to lwawt (OSX) in
961                 // current release. TODO: consider fixing it in the shared code.
962                 if (!focusedWindowChangeAllowed) {
963                     LWWindowPeer decoratedPeer = parentPeer.isSimpleWindow() ?
964                         LWWindowPeer.getOwnerFrameDialog(parentPeer) : parentPeer;
965 
966                     if (decoratedPeer == null || !decoratedPeer.getPlatformWindow().isActive()) {
967                         if (focusLog.isLoggable(PlatformLogger.Level.FINE)) {
968                             focusLog.fine("request rejected, focusedWindowChangeAllowed==false, " +
969                                           "decoratedPeer is inactive: " + decoratedPeer);
970                         }
971                         LWKeyboardFocusManagerPeer.removeLastFocusRequest(getTarget());
972                         return false;
973                     }
974                 }
975 
976                 boolean res = parentPeer.requestWindowFocus(cause);
977                 // If parent window can be made focused and has been made focused (synchronously)
978                 // then we can proceed with children, otherwise we retreat
979                 if (!res || !parentWindow.isFocused()) {
980                     if (focusLog.isLoggable(PlatformLogger.Level.FINE)) {
981                         focusLog.fine("request rejected, res= " + res + ", parentWindow.isFocused()=" +
982                                       parentWindow.isFocused());
983                     }
984                     LWKeyboardFocusManagerPeer.removeLastFocusRequest(getTarget());
985                     return false;
986                 }
987 
988                 KeyboardFocusManagerPeer kfmPeer = LWKeyboardFocusManagerPeer.getInstance();
989                 Component focusOwner = kfmPeer.getCurrentFocusOwner();
990                 return LWKeyboardFocusManagerPeer.deliverFocus(lightweightChild,
991                         getTarget(), temporary,
992                         focusedWindowChangeAllowed,
993                         time, cause, focusOwner);
994 
995             case LWKeyboardFocusManagerPeer.SNFH_SUCCESS_HANDLED:
996                 return true;
997         }
998 
999         return false;
1000     }
1001 
1002     @Override
1003     public final Image createImage(final ImageProducer producer) {
1004         return new ToolkitImage(producer);
1005     }
1006 
1007     @Override
1008     public final Image createImage(final int width, final int height) {
1009         return getLWGC().createAcceleratedImage(getTarget(), width, height);
1010     }
1011 
1012     @Override
1013     public final VolatileImage createVolatileImage(final int w, final int h) {
1014         return new SunVolatileImage(getTarget(), w, h);
1015     }
1016 
1017     @Override
1018     public boolean prepareImage(Image img, int w, int h, ImageObserver o) {
1019         // TODO: is it a right/complete implementation?
1020         return Toolkit.getDefaultToolkit().prepareImage(img, w, h, o);
1021     }
1022 
1023     @Override
1024     public int checkImage(Image img, int w, int h, ImageObserver o) {
1025         // TODO: is it a right/complete implementation?
1026         return Toolkit.getDefaultToolkit().checkImage(img, w, h, o);
1027     }
1028 
1029     @Override
1030     public boolean handlesWheelScrolling() {
1031         // TODO: not implemented
1032         return false;
1033     }
1034 
1035     @Override
1036     public final void applyShape(final Region shape) {
1037         synchronized (getStateLock()) {
1038             if (region == shape || (region != null && region.equals(shape))) {
1039                 return;
1040             }
1041         }
1042         applyShapeImpl(shape);
1043     }
1044 
1045     void applyShapeImpl(final Region shape) {
1046         synchronized (getStateLock()) {
1047             if (shape != null) {
1048                 region = Region.WHOLE_REGION.getIntersection(shape);
1049             } else {
1050                 region = null;
1051             }
1052         }
1053         repaintParent(getBounds());
1054     }
1055 
1056     protected final Region getRegion() {
1057         synchronized (getStateLock()) {
1058             return isShaped() ? region : Region.getInstance(getSize());
1059         }
1060     }
1061 
1062     public boolean isShaped() {
1063         synchronized (getStateLock()) {
1064             return region != null;
1065         }
1066     }
1067 
1068     // DropTargetPeer Method
1069     @Override
1070     public void addDropTarget(DropTarget dt) {
1071         LWWindowPeer winPeer = getWindowPeerOrSelf();
1072         if (winPeer != null && winPeer != this) {
1073             // We need to register the DropTarget in the
1074             // peer of the window ancestor of the component
1075             winPeer.addDropTarget(dt);
1076         } else {
1077             synchronized (dropTargetLock) {
1078                 // 10-14-02 VL: Windows WComponentPeer would add (or remove) the drop target only
1079                 // if it's the first (or last) one for the component. Otherwise this call is a no-op.
1080                 if (++fNumDropTargets == 1) {
1081                     // Having a non-null drop target would be an error but let's check just in case:
1082                     if (fDropTarget != null) {
1083                         throw new IllegalStateException("Current drop target is not null");
1084                     }
1085                     // Create a new drop target:
1086                     fDropTarget = LWToolkit.getLWToolkit().createDropTarget(dt, target, this);
1087                 }
1088             }
1089         }
1090     }
1091 
1092     // DropTargetPeer Method
1093     @Override
1094     public void removeDropTarget(DropTarget dt) {
1095         LWWindowPeer winPeer = getWindowPeerOrSelf();
1096         if (winPeer != null && winPeer != this) {
1097             // We need to unregister the DropTarget in the
1098             // peer of the window ancestor of the component
1099             winPeer.removeDropTarget(dt);
1100         } else {
1101             synchronized (dropTargetLock){
1102                 // 10-14-02 VL: Windows WComponentPeer would add (or remove) the drop target only
1103                 // if it's the first (or last) one for the component. Otherwise this call is a no-op.
1104                 if (--fNumDropTargets == 0) {
1105                     // Having a null drop target would be an error but let's check just in case:
1106                     if (fDropTarget != null) {
1107                         // Dispose of the drop target:
1108                         fDropTarget.dispose();
1109                         fDropTarget = null;
1110                     } else
1111                         System.err.println("CComponent.removeDropTarget(): current drop target is null.");
1112                 }
1113             }
1114         }
1115     }
1116 
1117     // ---- PEER NOTIFICATIONS ---- //
1118 
1119     /**
1120      * Called when this peer's location has been changed either as a result
1121      * of target.setLocation() or as a result of user actions (window is
1122      * dragged with mouse).
1123      *
1124      * This method could be called on the toolkit thread.
1125      */
1126     protected final void handleMove(final int x, final int y,
1127                                     final boolean updateTarget) {
1128         if (updateTarget) {
1129             AWTAccessor.getComponentAccessor().setLocation(getTarget(), x, y);
1130             postEvent(new ComponentEvent(getTarget(),
1131                                          ComponentEvent.COMPONENT_MOVED));
1132         }
1133     }
1134 
1135     /**
1136      * Called when this peer's size has been changed either as a result of
1137      * target.setSize() or as a result of user actions (window is resized).
1138      *
1139      * This method could be called on the toolkit thread.
1140      */
1141     protected final void handleResize(final int w, final int h,
1142                                       final boolean updateTarget) {
1143         Image oldBB = null;
1144         synchronized (getStateLock()) {
1145             if (backBuffer != null) {
1146                 oldBB = backBuffer;
1147                 backBuffer = getLWGC().createBackBuffer(this);
1148             }
1149         }
1150         getLWGC().destroyBackBuffer(oldBB);
1151 
1152         if (updateTarget) {
1153             AWTAccessor.getComponentAccessor().setSize(getTarget(), w, h);
1154             postEvent(new ComponentEvent(getTarget(),
1155                                          ComponentEvent.COMPONENT_RESIZED));
1156         }
1157     }
1158 
1159     protected final void repaintOldNewBounds(final Rectangle oldB) {
1160         repaintParent(oldB);
1161         repaintPeer(getSize());
1162     }
1163 
1164     protected final void repaintParent(final Rectangle oldB) {
1165         final LWContainerPeer<?, ?> cp = getContainerPeer();
1166         if (cp != null) {
1167             // Repaint unobscured part of the parent
1168             cp.repaintPeer(cp.getContentSize().intersection(oldB));
1169         }
1170     }
1171 
1172     // ---- EVENTS ---- //
1173 
1174     /**
1175      * Post an event to the proper Java EDT.
1176      */
1177     public void postEvent(final AWTEvent event) {
1178         LWToolkit.postEvent(event);
1179     }
1180 
1181     protected void postPaintEvent(int x, int y, int w, int h) {
1182         // TODO: call getIgnoreRepaint() directly with the right ACC
1183         if (AWTAccessor.getComponentAccessor().getIgnoreRepaint(target)) {
1184             return;
1185         }
1186         PaintEvent event = PaintEventDispatcher.getPaintEventDispatcher().
1187                 createPaintEvent(getTarget(), x, y, w, h);
1188         if (event != null) {
1189             postEvent(event);
1190         }
1191     }
1192 
1193     /*
1194      * Gives a chance for the peer to handle the event after it's been
1195      * processed by the target.
1196      */
1197     @Override
1198     public void handleEvent(AWTEvent e) {
1199         if ((e instanceof InputEvent) && ((InputEvent) e).isConsumed()) {
1200             return;
1201         }
1202         switch (e.getID()) {
1203             case FocusEvent.FOCUS_GAINED:
1204             case FocusEvent.FOCUS_LOST:
1205                 handleJavaFocusEvent((FocusEvent) e);
1206                 break;
1207             case PaintEvent.PAINT:
1208                 // Got a native paint event
1209 //                paintPending = false;
1210                 // fall through to the next statement
1211             case PaintEvent.UPDATE:
1212                 handleJavaPaintEvent();
1213                 break;
1214             case MouseEvent.MOUSE_PRESSED:
1215                 handleJavaMouseEvent((MouseEvent)e);
1216         }
1217 
1218         sendEventToDelegate(e);
1219     }
1220 
1221     protected void sendEventToDelegate(final AWTEvent e) {
1222         if (getDelegate() == null || !isShowing() || !isEnabled()) {
1223             return;
1224         }
1225         synchronized (getDelegateLock()) {
1226             AWTEvent delegateEvent = createDelegateEvent(e);
1227             if (delegateEvent != null) {
1228                 AWTAccessor.getComponentAccessor()
1229                         .processEvent((Component) delegateEvent.getSource(),
1230                                 delegateEvent);
1231                 if (delegateEvent instanceof KeyEvent) {
1232                     KeyEvent ke = (KeyEvent) delegateEvent;
1233                     SwingUtilities.processKeyBindings(ke);
1234                 }
1235             }
1236         }
1237     }
1238 
1239     /**
1240      * Changes the target of the AWTEvent from awt component to appropriate
1241      * swing delegate.
1242      */
1243     @SuppressWarnings("deprecation")
1244     private AWTEvent createDelegateEvent(final AWTEvent e) {
1245         // TODO modifiers should be changed to getModifiers()|getModifiersEx()?
1246         AWTEvent delegateEvent = null;
1247         if (e instanceof MouseWheelEvent) {
1248             MouseWheelEvent me = (MouseWheelEvent) e;
1249             delegateEvent = new MouseWheelEvent(
1250                     delegate, me.getID(), me.getWhen(),
1251                     me.getModifiers(),
1252                     me.getX(), me.getY(),
1253                     me.getXOnScreen(), me.getYOnScreen(),
1254                     me.getClickCount(),
1255                     me.isPopupTrigger(),
1256                     me.getScrollType(),
1257                     me.getScrollAmount(),
1258                     me.getWheelRotation(),
1259                     me.getPreciseWheelRotation());
1260         } else if (e instanceof MouseEvent) {
1261             MouseEvent me = (MouseEvent) e;
1262 
1263             Component eventTarget = SwingUtilities.getDeepestComponentAt(delegate, me.getX(), me.getY());
1264 
1265             if (me.getID() == MouseEvent.MOUSE_DRAGGED) {
1266                 if (delegateDropTarget == null) {
1267                     delegateDropTarget = eventTarget;
1268                 } else {
1269                     eventTarget = delegateDropTarget;
1270                 }
1271             }
1272             if (me.getID() == MouseEvent.MOUSE_RELEASED && delegateDropTarget != null) {
1273                 eventTarget = delegateDropTarget;
1274                 delegateDropTarget = null;
1275             }
1276             if (eventTarget == null) {
1277                 eventTarget = delegate;
1278             }
1279             delegateEvent = SwingUtilities.convertMouseEvent(getTarget(), me, eventTarget);
1280         } else if (e instanceof KeyEvent) {
1281             KeyEvent ke = (KeyEvent) e;
1282             delegateEvent = new KeyEvent(getDelegateFocusOwner(), ke.getID(), ke.getWhen(),
1283                     ke.getModifiers(), ke.getKeyCode(), ke.getKeyChar(), ke.getKeyLocation());
1284             AWTAccessor.getKeyEventAccessor().setExtendedKeyCode((KeyEvent) delegateEvent,
1285                     ke.getExtendedKeyCode());
1286         } else if (e instanceof FocusEvent) {
1287             FocusEvent fe = (FocusEvent) e;
1288             delegateEvent = new FocusEvent(getDelegateFocusOwner(), fe.getID(), fe.isTemporary());
1289         }
1290         return delegateEvent;
1291     }
1292 
1293     protected void handleJavaMouseEvent(MouseEvent e) {
1294         Component target = getTarget();
1295         assert (e.getSource() == target);
1296 
1297         if (!target.isFocusOwner() && LWKeyboardFocusManagerPeer.shouldFocusOnClick(target)) {
1298             LWKeyboardFocusManagerPeer.requestFocusFor(target, FocusEvent.Cause.MOUSE_EVENT);
1299         }
1300     }
1301 
1302     /**
1303      * Handler for FocusEvents.
1304      */
1305     void handleJavaFocusEvent(final FocusEvent e) {
1306         // Note that the peer receives all the FocusEvents from
1307         // its lightweight children as well
1308         KeyboardFocusManagerPeer kfmPeer = LWKeyboardFocusManagerPeer.getInstance();
1309         kfmPeer.setCurrentFocusOwner(e.getID() == FocusEvent.FOCUS_GAINED ? getTarget() : null);
1310     }
1311 
1312     /**
1313      * All peers should clear background before paint.
1314      *
1315      * @return false on components that DO NOT require a clearRect() before
1316      *         painting.
1317      */
1318     protected final boolean shouldClearRectBeforePaint() {
1319         // TODO: sun.awt.noerasebackground
1320         return true;
1321     }
1322 
1323     /**
1324      * Handler for PAINT and UPDATE PaintEvents.
1325      */
1326     private void handleJavaPaintEvent() {
1327         // Skip all painting while layouting and all UPDATEs
1328         // while waiting for native paint
1329 //        if (!isLayouting && !paintPending) {
1330         if (!isLayouting()) {
1331             targetPaintArea.paint(getTarget(), shouldClearRectBeforePaint());
1332         }
1333     }
1334 
1335     // ---- UTILITY METHODS ---- //
1336 
1337     /**
1338      * Finds a top-most visible component for the given point. The location is
1339      * specified relative to the peer's parent.
1340      */
1341     LWComponentPeer<?, ?> findPeerAt(final int x, final int y) {
1342         final Rectangle r = getBounds();
1343         final Region sh = getRegion();
1344         final boolean found = isVisible() && sh.contains(x - r.x, y - r.y);
1345         return found ? this : null;
1346     }
1347 
1348     /*
1349      * Translated the given point in Window coordinates to the point in
1350      * coordinates local to this component. The given window peer must be
1351      * the window where this component is in.
1352      */
1353     public Point windowToLocal(int x, int y, LWWindowPeer wp) {
1354         return windowToLocal(new Point(x, y), wp);
1355     }
1356 
1357     public Point windowToLocal(Point p, LWWindowPeer wp) {
1358         LWComponentPeer<?, ?> cp = this;
1359         while (cp != wp) {
1360             Rectangle cpb = cp.getBounds();
1361             p.x -= cpb.x;
1362             p.y -= cpb.y;
1363             cp = cp.getContainerPeer();
1364         }
1365         // Return a copy to prevent subsequent modifications
1366         return new Point(p);
1367     }
1368 
1369     public Rectangle windowToLocal(Rectangle r, LWWindowPeer wp) {
1370         Point p = windowToLocal(r.getLocation(), wp);
1371         return new Rectangle(p, r.getSize());
1372     }
1373 
1374     public Point localToWindow(int x, int y) {
1375         return localToWindow(new Point(x, y));
1376     }
1377 
1378     public Point localToWindow(Point p) {
1379         LWComponentPeer<?, ?> cp = getContainerPeer();
1380         Rectangle r = getBounds();
1381         while (cp != null) {
1382             p.x += r.x;
1383             p.y += r.y;
1384             r = cp.getBounds();
1385             cp = cp.getContainerPeer();
1386         }
1387         // Return a copy to prevent subsequent modifications
1388         return new Point(p);
1389     }
1390 
1391     public Rectangle localToWindow(Rectangle r) {
1392         Point p = localToWindow(r.getLocation());
1393         return new Rectangle(p, r.getSize());
1394     }
1395 
1396     public final void repaintPeer() {
1397         repaintPeer(getSize());
1398     }
1399 
1400     void repaintPeer(final Rectangle r) {
1401         final Rectangle toPaint = getSize().intersection(r);
1402         if (!isShowing() || toPaint.isEmpty()) {
1403             return;
1404         }
1405 
1406         postPaintEvent(toPaint.x, toPaint.y, toPaint.width, toPaint.height);
1407     }
1408 
1409     /**
1410      * Determines whether this peer is showing on screen. This means that the
1411      * peer must be visible, and it must be in a container that is visible and
1412      * showing.
1413      *
1414      * @see #isVisible()
1415      */
1416     protected final boolean isShowing() {
1417         synchronized (getPeerTreeLock()) {
1418             if (isVisible()) {
1419                 final LWContainerPeer<?, ?> container = getContainerPeer();
1420                 return (container == null) || container.isShowing();
1421             }
1422         }
1423         return false;
1424     }
1425 
1426     /**
1427      * Paints the peer. Delegate the actual painting to Swing components.
1428      */
1429     protected final void paintPeer(final Graphics g) {
1430         final D delegate = getDelegate();
1431         if (delegate != null) {
1432             if (!SwingUtilities.isEventDispatchThread()) {
1433                 throw new InternalError("Painting must be done on EDT");
1434             }
1435             synchronized (getDelegateLock()) {
1436                 // JComponent.print() is guaranteed to not affect the double buffer
1437                 getDelegate().print(g);
1438             }
1439         }
1440     }
1441 
1442     protected static final void flushOnscreenGraphics(){
1443         final OGLRenderQueue rq = OGLRenderQueue.getInstance();
1444         rq.lock();
1445         try {
1446             rq.flushNow();
1447         } finally {
1448             rq.unlock();
1449         }
1450     }
1451 
1452     /**
1453      * Used by ContainerPeer to skip all the paint events during layout.
1454      *
1455      * @param isLayouting layouting state.
1456      */
1457     protected final void setLayouting(final boolean isLayouting) {
1458         this.isLayouting = isLayouting;
1459     }
1460 
1461     /**
1462      * Returns layouting state. Used by ComponentPeer to skip all the paint
1463      * events during layout.
1464      *
1465      * @return true during layout, false otherwise.
1466      */
1467     private boolean isLayouting() {
1468         return isLayouting;
1469     }
1470 }
1471