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