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