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