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