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