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