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