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