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