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