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