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