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