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