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