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