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