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