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