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 D delegate = null; 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 private final class DelegateContainer extends Container { 137 { 138 enableEvents(0xFFFFFFFF); 139 } 140 141 DelegateContainer() { 142 super(); 143 } 144 145 @Override 146 public boolean isLightweight() { 147 return false; 148 } 149 150 @Override 151 public Point getLocation() { 152 return getLocationOnScreen(); 153 } 154 155 @Override 156 public Point getLocationOnScreen() { 157 return LWComponentPeer.this.getLocationOnScreen(); 158 } 159 160 @Override 161 public int getX() { 162 return getLocation().x; 163 } 164 165 @Override 166 public int getY() { 167 return getLocation().y; 168 } 169 } 170 171 public LWComponentPeer(T target, PlatformComponent platformComponent) { 172 targetPaintArea = new LWRepaintArea(); 173 this.target = target; 174 this.platformComponent = platformComponent; 175 176 initializeContainerPeer(); 177 // Container peer is always null for LWWindowPeers, so 178 // windowPeer is always null for them as well. On the other 179 // hand, LWWindowPeer shouldn't use windowPeer at all 180 if (containerPeer != null) { 181 windowPeer = containerPeer.getWindowPeerOrSelf(); 182 } 183 // don't bother about z-order here as updateZOrder() 184 // will be called from addNotify() later anyway 185 if (containerPeer != null) { 186 containerPeer.addChildPeer(this); 187 } 188 189 // the delegate must be created after the target is set 190 AWTEventListener toolkitListener = null; 191 synchronized (Toolkit.getDefaultToolkit()) { 192 try { 193 toolkitListener = getToolkitAWTEventListener(); 194 setToolkitAWTEventListener(null); 195 196 synchronized (getDelegateLock()) { 197 delegate = createDelegate(); 198 if (delegate != null) { 199 delegate.setVisible(false); 200 delegateContainer = new DelegateContainer(); 201 delegateContainer.add(delegate); 202 delegateContainer.addNotify(); 203 delegate.addNotify(); 204 resetColorsAndFont(delegate); 205 delegate.setOpaque(true); 206 } else { 207 return; 208 } 209 } 210 211 } finally { 212 setToolkitAWTEventListener(toolkitListener); 213 } 214 215 // todo swing: later on we will probably have one global RM 216 SwingUtilities3.setDelegateRepaintManager(delegate, new RepaintManager() { 217 @Override 218 public void addDirtyRegion(final JComponent c, final int x, final int y, final int w, final int h) { 219 repaintPeer(SwingUtilities.convertRectangle( 220 c, new Rectangle(x, y, w, h), getDelegate())); 221 } 222 }); 223 } 224 } 225 226 /** 227 * This method must be called under Toolkit.getDefaultToolkit() lock 228 * and followed by setToolkitAWTEventListener() 229 */ 230 protected final AWTEventListener getToolkitAWTEventListener() { 231 return AccessController.doPrivileged(new PrivilegedAction<AWTEventListener>() { 232 public AWTEventListener run() { 233 Toolkit toolkit = Toolkit.getDefaultToolkit(); 234 try { 235 Field field = Toolkit.class.getDeclaredField("eventListener"); 236 field.setAccessible(true); 237 return (AWTEventListener) field.get(toolkit); 238 } catch (Exception e) { 239 throw new InternalError(e.toString()); 240 } 241 } 242 }); 243 } 244 245 protected final void setToolkitAWTEventListener(final AWTEventListener listener) { 246 AccessController.doPrivileged(new PrivilegedAction<Void>() { 247 public Void run() { 248 Toolkit toolkit = Toolkit.getDefaultToolkit(); 249 try { 250 Field field = Toolkit.class.getDeclaredField("eventListener"); 251 field.setAccessible(true); 252 field.set(toolkit, listener); 253 } catch (Exception e) { 254 throw new InternalError(e.toString()); 255 } 256 return null; 257 } 258 }); 259 } 260 261 /** 262 * This method is called under getDelegateLock(). 263 * Overridden in subclasses. 264 */ 265 protected D createDelegate() { 266 return null; 267 } 268 269 protected final D getDelegate() { 270 synchronized (getStateLock()) { 271 return delegate; 272 } 273 } 274 275 protected Component getDelegateFocusOwner() { 276 return getDelegate(); 277 } 278 279 /** 280 * Initializes this peer. The call to initialize() is not placed to 281 * LWComponentPeer ctor to let the subclass ctor to finish completely first. 282 * Instead, it's the LWToolkit object who is responsible for initialization. 283 * Note that we call setVisible() at the end of initialization. 284 */ 285 public final void initialize() { 286 platformComponent.initialize(target, this, getPlatformWindow()); 287 initializeImpl(); 288 setVisible(target.isVisible()); 289 } 290 291 /** 292 * Fetching general properties from the target. Should be overridden in 293 * subclasses to initialize specific peers properties. 294 */ 295 void initializeImpl() { 296 setBackground(target.getBackground()); 297 setForeground(target.getForeground()); 298 setFont(target.getFont()); 299 setBounds(target.getBounds()); 300 setEnabled(target.isEnabled()); 301 } 302 303 private static void resetColorsAndFont(final Container c) { 304 c.setBackground(null); 305 c.setForeground(null); 306 c.setFont(null); 307 for (int i = 0; i < c.getComponentCount(); i++) { 308 resetColorsAndFont((Container) c.getComponent(i)); 309 } 310 } 311 312 final Object getStateLock() { 313 return stateLock; 314 } 315 316 /** 317 * Synchronize all operations with the Swing delegates under AWT tree lock, 318 * using a new separate lock to synchronize access to delegates may lead 319 * deadlocks. Think of it as a 'virtual EDT'. 320 * 321 * @return DelegateLock 322 */ 323 final Object getDelegateLock() { 324 return getTarget().getTreeLock(); 325 } 326 327 protected static final Object getPeerTreeLock() { 328 return peerTreeLock; 329 } 330 331 final T getTarget() { 332 return target; 333 } 334 335 // Just a helper method 336 // Returns the window peer or null if this is a window peer 337 protected final LWWindowPeer getWindowPeer() { 338 return windowPeer; 339 } 340 341 // Returns the window peer or 'this' if this is a window peer 342 protected LWWindowPeer getWindowPeerOrSelf() { 343 return getWindowPeer(); 344 } 345 346 // Just a helper method 347 protected final LWContainerPeer getContainerPeer() { 348 return containerPeer; 349 } 350 351 // Just a helper method 352 // Overridden in LWWindowPeer to skip containerPeer initialization 353 protected void initializeContainerPeer() { 354 Container parent = LWToolkit.getNativeContainer(target); 355 if (parent != null) { 356 containerPeer = (LWContainerPeer) LWToolkit.targetToPeer(parent); 357 } 358 } 359 360 public PlatformWindow getPlatformWindow() { 361 LWWindowPeer windowPeer = getWindowPeer(); 362 return windowPeer.getPlatformWindow(); 363 } 364 365 protected AppContext getAppContext() { 366 return SunToolkit.targetToAppContext(getTarget()); 367 } 368 369 // ---- PEER METHODS ---- // 370 371 @Override 372 public Toolkit getToolkit() { 373 return LWToolkit.getLWToolkit(); 374 } 375 376 // Just a helper method 377 public LWToolkit getLWToolkit() { 378 return LWToolkit.getLWToolkit(); 379 } 380 381 @Override 382 public final void dispose() { 383 if (disposed.compareAndSet(false, true)) { 384 disposeImpl(); 385 } 386 } 387 388 protected void disposeImpl() { 389 LWContainerPeer cp = getContainerPeer(); 390 if (cp != null) { 391 cp.removeChildPeer(this); 392 } 393 platformComponent.dispose(); 394 LWToolkit.targetDisposedPeer(getTarget(), this); 395 } 396 397 public final boolean isDisposed() { 398 return disposed.get(); 399 } 400 401 /* 402 * GraphicsConfiguration is borrowed from the parent peer. The 403 * return value must not be null. 404 * 405 * Overridden in LWWindowPeer. 406 */ 407 @Override 408 public GraphicsConfiguration getGraphicsConfiguration() { 409 // Don't check windowPeer for null as it can only happen 410 // for windows, but this method is overridden in 411 // LWWindowPeer and doesn't call super() 412 return getWindowPeer().getGraphicsConfiguration(); 413 } 414 415 /* 416 * Overridden in LWWindowPeer to replace its surface 417 * data and back buffer. 418 */ 419 @Override 420 public boolean updateGraphicsData(GraphicsConfiguration gc) { 421 // TODO: not implemented 422 // throw new RuntimeException("Has not been implemented yet."); 423 return false; 424 } 425 426 @Override 427 public final Graphics getGraphics() { 428 final Graphics g = getOnscreenGraphics(); 429 if (g != null) { 430 synchronized (getPeerTreeLock()){ 431 applyConstrain(g); 432 } 433 } 434 return g; 435 } 436 437 /* 438 * Peer Graphics is borrowed from the parent peer, while 439 * foreground and background colors and font are specific to 440 * this peer. 441 */ 442 public final Graphics getOnscreenGraphics() { 443 final LWWindowPeer wp = getWindowPeerOrSelf(); 444 return wp.getOnscreenGraphics(getForeground(), getBackground(), 445 getFont()); 446 447 } 448 449 private void applyConstrain(final Graphics g) { 450 final SunGraphics2D sg2d = (SunGraphics2D) g; 451 final Rectangle constr = localToWindow(getSize()); 452 // translate and set rectangle constrain. 453 sg2d.constrain(constr.x, constr.y, constr.width, constr.height); 454 // set region constrain. 455 //sg2d.constrain(getVisibleRegion()); 456 SG2DConstraint(sg2d, getVisibleRegion()); 457 } 458 459 //TODO Move this method to SG2D? 460 void SG2DConstraint(final SunGraphics2D sg2d, Region r) { 461 sg2d.constrainX = sg2d.transX; 462 sg2d.constrainY = sg2d.transY; 463 464 Region c = sg2d.constrainClip; 465 if ((sg2d.constrainX | sg2d.constrainY) != 0) { 466 r = r.getTranslatedRegion(sg2d.constrainX, sg2d.constrainY); 467 } 468 if (c == null) { 469 c = r; 470 } else { 471 c = c.getIntersection(r); 472 if (c == sg2d.constrainClip) { 473 // Common case to ignore 474 return; 475 } 476 } 477 sg2d.constrainClip = c; 478 //validateCompClip() forced call. 479 sg2d.setDevClip(r.getLoX(), r.getLoY(), r.getWidth(), r.getHeight()); 480 } 481 482 public Region getVisibleRegion() { 483 return computeVisibleRect(this, getRegion()); 484 } 485 486 static final Region computeVisibleRect(LWComponentPeer c, Region region) { 487 final LWContainerPeer p = c.getContainerPeer(); 488 if (p != null) { 489 final Rectangle r = c.getBounds(); 490 region = region.getTranslatedRegion(r.x, r.y); 491 region = region.getIntersection(p.getRegion()); 492 region = region.getIntersection(p.getContentSize()); 493 region = p.cutChildren(region, c); 494 region = computeVisibleRect(p, region); 495 region = region.getTranslatedRegion(-r.x, -r.y); 496 } 497 return region; 498 } 499 500 @Override 501 public ColorModel getColorModel() { 502 // Is it a correct implementation? 503 return getGraphicsConfiguration().getColorModel(); 504 } 505 506 @Override 507 public void createBuffers(int numBuffers, BufferCapabilities caps) 508 throws AWTException { 509 throw new AWTException("Back buffers are only supported for " + 510 "Window or Canvas components."); 511 } 512 513 /* 514 * To be overridden in LWWindowPeer and LWCanvasPeer. 515 */ 516 @Override 517 public Image getBackBuffer() { 518 // Return null or throw AWTException? 519 return null; 520 } 521 522 @Override 523 public void flip(int x1, int y1, int x2, int y2, 524 BufferCapabilities.FlipContents flipAction) { 525 // Skip silently or throw AWTException? 526 } 527 528 @Override 529 public void destroyBuffers() { 530 // Do nothing 531 } 532 533 // Helper method 534 public void setBounds(Rectangle r) { 535 setBounds(r.x, r.y, r.width, r.height, SET_BOUNDS); 536 } 537 538 /** 539 * This method could be called on the toolkit thread. 540 */ 541 @Override 542 public void setBounds(int x, int y, int w, int h, int op) { 543 setBounds(x, y, w, h, op, true, false); 544 } 545 546 protected void setBounds(int x, int y, int w, int h, int op, boolean notify, 547 final boolean updateTarget) { 548 Rectangle oldBounds; 549 synchronized (getStateLock()) { 550 oldBounds = new Rectangle(bounds); 551 if ((op & (SET_LOCATION | SET_BOUNDS)) != 0) { 552 bounds.x = x; 553 bounds.y = y; 554 } 555 if ((op & (SET_SIZE | SET_BOUNDS)) != 0) { 556 bounds.width = w; 557 bounds.height = h; 558 } 559 } 560 boolean moved = (oldBounds.x != x) || (oldBounds.y != y); 561 boolean resized = (oldBounds.width != w) || (oldBounds.height != h); 562 if (!moved && !resized) { 563 return; 564 } 565 final D delegate = getDelegate(); 566 if (delegate != null) { 567 synchronized (getDelegateLock()) { 568 delegateContainer.setBounds(0, 0, w, h); 569 delegate.setBounds(delegateContainer.getBounds()); 570 // TODO: the following means that the delegateContainer NEVER gets validated. That's WRONG! 571 delegate.validate(); 572 } 573 } 574 575 final Point locationInWindow = localToWindow(0, 0); 576 platformComponent.setBounds(locationInWindow.x, locationInWindow.y, w, 577 h); 578 if (notify) { 579 repaintOldNewBounds(oldBounds); 580 if (resized) { 581 handleResize(w, h, updateTarget); 582 } 583 if (moved) { 584 handleMove(x, y, updateTarget); 585 } 586 } 587 } 588 589 public final Rectangle getBounds() { 590 synchronized (getStateLock()) { 591 // Return a copy to prevent subsequent modifications 592 return bounds.getBounds(); 593 } 594 } 595 596 public final Rectangle getSize() { 597 synchronized (getStateLock()) { 598 // Return a copy to prevent subsequent modifications 599 return new Rectangle(bounds.width, bounds.height); 600 } 601 } 602 603 @Override 604 public Point getLocationOnScreen() { 605 Point windowLocation = getWindowPeer().getLocationOnScreen(); 606 Point locationInWindow = localToWindow(0, 0); 607 return new Point(windowLocation.x + locationInWindow.x, 608 windowLocation.y + locationInWindow.y); 609 } 610 611 /** 612 * Returns the cursor of the peer, which is cursor of the target by default, 613 * but peer can override this behavior. 614 * 615 * @param p Point relative to the peer. 616 * @return Cursor of the peer or null if default cursor should be used. 617 */ 618 protected Cursor getCursor(final Point p) { 619 return getTarget().getCursor(); 620 } 621 622 @Override 623 public void setBackground(final Color c) { 624 final Color oldBg = getBackground(); 625 if (oldBg == c || (oldBg != null && oldBg.equals(c))) { 626 return; 627 } 628 synchronized (getStateLock()) { 629 background = c; 630 } 631 final D delegate = getDelegate(); 632 if (delegate != null) { 633 synchronized (getDelegateLock()) { 634 // delegate will repaint the target 635 delegate.setBackground(c); 636 } 637 } else { 638 repaintPeer(); 639 } 640 } 641 642 protected final Color getBackground() { 643 synchronized (getStateLock()) { 644 return background; 645 } 646 } 647 648 @Override 649 public void setForeground(final Color c) { 650 final Color oldFg = getForeground(); 651 if (oldFg == c || (oldFg != null && oldFg.equals(c))) { 652 return; 653 } 654 synchronized (getStateLock()) { 655 foreground = c; 656 } 657 final D delegate = getDelegate(); 658 if (delegate != null) { 659 synchronized (getDelegateLock()) { 660 // delegate will repaint the target 661 delegate.setForeground(c); 662 } 663 } else { 664 repaintPeer(); 665 } 666 } 667 668 protected final Color getForeground() { 669 synchronized (getStateLock()) { 670 return foreground; 671 } 672 } 673 674 @Override 675 public void setFont(final Font f) { 676 final Font oldF = getFont(); 677 if (oldF == f || (oldF != null && oldF.equals(f))) { 678 return; 679 } 680 synchronized (getStateLock()) { 681 font = f; 682 } 683 final D delegate = getDelegate(); 684 if (delegate != null) { 685 synchronized (getDelegateLock()) { 686 // delegate will repaint the target 687 delegate.setFont(f); 688 } 689 } else { 690 repaintPeer(); 691 } 692 } 693 694 protected final Font getFont() { 695 synchronized (getStateLock()) { 696 return font; 697 } 698 } 699 700 @Override 701 public FontMetrics getFontMetrics(Font f) { 702 // Borrow the metrics from the top-level window 703 // return getWindowPeer().getFontMetrics(f); 704 // Obtain the metrics from the offscreen window where this peer is 705 // mostly drawn to. 706 // TODO: check for "use platform metrics" settings 707 Graphics g = getWindowPeer().getGraphics(); 708 try { 709 if (g != null) { 710 return g.getFontMetrics(f); 711 } else { 712 synchronized (getDelegateLock()) { 713 return delegateContainer.getFontMetrics(f); 714 } 715 } 716 } finally { 717 if (g != null) { 718 g.dispose(); 719 } 720 } 721 } 722 723 @Override 724 public void setEnabled(final boolean e) { 725 boolean status = e; 726 final LWComponentPeer cp = getContainerPeer(); 727 if (cp != null) { 728 status &= cp.isEnabled(); 729 } 730 synchronized (getStateLock()) { 731 if (enabled == status) { 732 return; 733 } 734 enabled = status; 735 } 736 737 final D delegate = getDelegate(); 738 739 if (delegate != null) { 740 synchronized (getDelegateLock()) { 741 delegate.setEnabled(status); 742 } 743 } else { 744 repaintPeer(); 745 } 746 } 747 748 // Helper method 749 public final boolean isEnabled() { 750 synchronized (getStateLock()) { 751 return enabled; 752 } 753 } 754 755 @Override 756 public void setVisible(final boolean v) { 757 synchronized (getStateLock()) { 758 if (visible == v) { 759 return; 760 } 761 visible = v; 762 } 763 setVisibleImpl(v); 764 } 765 766 protected void setVisibleImpl(final boolean v) { 767 final D delegate = getDelegate(); 768 769 if (delegate != null) { 770 synchronized (getDelegateLock()) { 771 delegate.setVisible(v); 772 } 773 } 774 if (visible) { 775 repaintPeer(); 776 } else { 777 repaintParent(getBounds()); 778 } 779 } 780 781 // Helper method 782 public final boolean isVisible() { 783 synchronized (getStateLock()) { 784 return visible; 785 } 786 } 787 788 @Override 789 public void paint(final Graphics g) { 790 getTarget().paint(g); 791 } 792 793 @Override 794 public void print(final Graphics g) { 795 getTarget().print(g); 796 } 797 798 @Override 799 public void reparent(ContainerPeer newContainer) { 800 // TODO: not implemented 801 throw new UnsupportedOperationException("ComponentPeer.reparent()"); 802 } 803 804 @Override 805 public boolean isReparentSupported() { 806 // TODO: not implemented 807 return false; 808 } 809 810 @Override 811 public void setZOrder(ComponentPeer above) { 812 LWContainerPeer cp = getContainerPeer(); 813 // Don't check containerPeer for null as it can only happen 814 // for windows, but this method is overridden in 815 // LWWindowPeer and doesn't call super() 816 cp.setChildPeerZOrder(this, (LWComponentPeer) above); 817 } 818 819 @Override 820 public void coalescePaintEvent(PaintEvent e) { 821 if (!(e instanceof IgnorePaintEvent)) { 822 Rectangle r = e.getUpdateRect(); 823 if ((r != null) && !r.isEmpty()) { 824 targetPaintArea.add(r, e.getID()); 825 } 826 } 827 } 828 829 /* 830 * Should be overridden in subclasses which use complex Swing components. 831 */ 832 @Override 833 public void layout() { 834 // TODO: not implemented 835 } 836 837 @Override 838 public boolean isObscured() { 839 // TODO: not implemented 840 return false; 841 } 842 843 @Override 844 public boolean canDetermineObscurity() { 845 // TODO: not implemented 846 return false; 847 } 848 849 /** 850 * Should be overridden in subclasses to forward the request 851 * to the Swing helper component, if required. 852 */ 853 @Override 854 public Dimension getPreferredSize() { 855 // It looks like a default implementation for all toolkits 856 return getMinimumSize(); 857 } 858 859 /* 860 * Should be overridden in subclasses to forward the request 861 * to the Swing helper component. 862 */ 863 @Override 864 public Dimension getMinimumSize() { 865 D delegate = getDelegate(); 866 867 if (delegate == null) { 868 // Is it a correct default value? 869 return getBounds().getSize(); 870 } else { 871 synchronized (getDelegateLock()) { 872 return delegate.getMinimumSize(); 873 } 874 } 875 } 876 877 @Override 878 public void updateCursorImmediately() { 879 getLWToolkit().getCursorManager().updateCursor(); 880 } 881 882 @Override 883 public boolean isFocusable() { 884 // Overridden in focusable subclasses like buttons 885 return false; 886 } 887 888 @Override 889 public boolean requestFocus(Component lightweightChild, boolean temporary, 890 boolean focusedWindowChangeAllowed, long time, 891 CausedFocusEvent.Cause cause) 892 { 893 if (focusLog.isLoggable(PlatformLogger.FINEST)) { 894 focusLog.finest("lightweightChild=" + lightweightChild + ", temporary=" + temporary + 895 ", focusedWindowChangeAllowed=" + focusedWindowChangeAllowed + 896 ", time= " + time + ", cause=" + cause); 897 } 898 if (LWKeyboardFocusManagerPeer.processSynchronousLightweightTransfer( 899 getTarget(), lightweightChild, temporary, 900 focusedWindowChangeAllowed, time)) { 901 return true; 902 } 903 904 int result = LWKeyboardFocusManagerPeer.shouldNativelyFocusHeavyweight( 905 getTarget(), lightweightChild, temporary, 906 focusedWindowChangeAllowed, time, cause); 907 switch (result) { 908 case LWKeyboardFocusManagerPeer.SNFH_FAILURE: 909 return false; 910 case LWKeyboardFocusManagerPeer.SNFH_SUCCESS_PROCEED: 911 Window parentWindow = SunToolkit.getContainingWindow(getTarget()); 912 if (parentWindow == null) { 913 focusLog.fine("request rejected, parentWindow is null"); 914 LWKeyboardFocusManagerPeer.removeLastFocusRequest(getTarget()); 915 return false; 916 } 917 LWWindowPeer parentPeer = (LWWindowPeer) parentWindow.getPeer(); 918 if (parentPeer == null) { 919 focusLog.fine("request rejected, parentPeer is null"); 920 LWKeyboardFocusManagerPeer.removeLastFocusRequest(getTarget()); 921 return false; 922 } 923 924 // A fix for 7145768. Ensure the parent window is currently natively focused. 925 // The more evident place to perform this check is in KFM.shouldNativelyFocusHeavyweight, 926 // however that is the shared code and this particular problem's reproducibility has 927 // platform specifics. So, it was decided to narrow down the fix to lwawt (OSX) in 928 // current release. TODO: consider fixing it in the shared code. 929 if (!focusedWindowChangeAllowed) { 930 LWWindowPeer decoratedPeer = parentPeer.isSimpleWindow() ? 931 LWWindowPeer.getOwnerFrameDialog(parentPeer) : parentPeer; 932 933 if (decoratedPeer == null || !decoratedPeer.getPlatformWindow().isActive()) { 934 if (focusLog.isLoggable(PlatformLogger.FINE)) { 935 focusLog.fine("request rejected, focusedWindowChangeAllowed==false, " + 936 "decoratedPeer is inactive: " + decoratedPeer); 937 } 938 LWKeyboardFocusManagerPeer.removeLastFocusRequest(getTarget()); 939 return false; 940 } 941 } 942 943 boolean res = parentPeer.requestWindowFocus(cause); 944 // If parent window can be made focused and has been made focused (synchronously) 945 // then we can proceed with children, otherwise we retreat 946 if (!res || !parentWindow.isFocused()) { 947 if (focusLog.isLoggable(PlatformLogger.FINE)) { 948 focusLog.fine("request rejected, res= " + res + ", parentWindow.isFocused()=" + 949 parentWindow.isFocused()); 950 } 951 LWKeyboardFocusManagerPeer.removeLastFocusRequest(getTarget()); 952 return false; 953 } 954 955 KeyboardFocusManagerPeer kfmPeer = LWKeyboardFocusManagerPeer.getInstance(); 956 Component focusOwner = kfmPeer.getCurrentFocusOwner(); 957 return LWKeyboardFocusManagerPeer.deliverFocus(lightweightChild, 958 getTarget(), temporary, 959 focusedWindowChangeAllowed, 960 time, cause, focusOwner); 961 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 protected 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 KeyboardFocusManagerPeer kfmPeer = LWKeyboardFocusManagerPeer.getInstance(); 1267 kfmPeer.setCurrentFocusOwner(e.getID() == FocusEvent.FOCUS_GAINED ? getTarget() : 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 }