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