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