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