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