| /* |
| * Copyright (c) 2011, 2016, Oracle and/or its affiliates. All rights reserved. |
| * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
| * |
| * This code is free software; you can redistribute it and/or modify it |
| * under the terms of the GNU General Public License version 2 only, as |
| * published by the Free Software Foundation. Oracle designates this |
| * particular file as subject to the "Classpath" exception as provided |
| * by Oracle in the LICENSE file that accompanied this code. |
| * |
| * This code is distributed in the hope that it will be useful, but WITHOUT |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
| * version 2 for more details (a copy is included in the LICENSE file that |
| * accompanied this code). |
| * |
| * You should have received a copy of the GNU General Public License version |
| * 2 along with this work; if not, write to the Free Software Foundation, |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
| * |
| * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
| * or visit www.oracle.com if you need additional information or have any |
| * questions. |
| */ |
| |
| |
| package sun.lwawt; |
| |
| import java.awt.*; |
| |
| import java.awt.dnd.DropTarget; |
| import java.awt.dnd.peer.DropTargetPeer; |
| import java.awt.event.*; |
| |
| import java.awt.image.ColorModel; |
| import java.awt.image.ImageObserver; |
| import java.awt.image.ImageProducer; |
| import java.awt.image.VolatileImage; |
| |
| import java.awt.peer.ComponentPeer; |
| import java.awt.peer.ContainerPeer; |
| |
| import java.awt.peer.KeyboardFocusManagerPeer; |
| import java.util.concurrent.atomic.AtomicBoolean; |
| import java.lang.reflect.Field; |
| import java.security.AccessController; |
| import java.security.PrivilegedAction; |
| |
| import sun.awt.*; |
| |
| import sun.awt.event.IgnorePaintEvent; |
| |
| import sun.awt.image.SunVolatileImage; |
| import sun.awt.image.ToolkitImage; |
| |
| import sun.java2d.SunGraphics2D; |
| import sun.java2d.opengl.OGLRenderQueue; |
| import sun.java2d.pipe.Region; |
| |
| import sun.util.logging.PlatformLogger; |
| |
| import javax.swing.JComponent; |
| import javax.swing.SwingUtilities; |
| import javax.swing.RepaintManager; |
| |
| import com.sun.java.swing.SwingUtilities3; |
| |
| public abstract class LWComponentPeer<T extends Component, D extends JComponent> |
| implements ComponentPeer, DropTargetPeer |
| { |
| private static final PlatformLogger focusLog = PlatformLogger.getLogger("sun.lwawt.focus.LWComponentPeer"); |
| |
| /** |
| * State lock is to be used for modifications to this peer's fields (e.g. |
| * bounds, background, font, etc.) It should be the last lock in the lock |
| * chain |
| */ |
| private final Object stateLock = new Object(); |
| |
| /** |
| * The lock to operate with the peers hierarchy. AWT tree lock is not used |
| * as there are many peers related ops to be done on the toolkit thread, and |
| * we don't want to depend on a public lock on this thread |
| */ |
| private static final Object peerTreeLock = new Object(); |
| |
| /** |
| * The associated AWT object. |
| */ |
| private final T target; |
| |
| /** |
| * Container peer. It may not be the peer of the target's direct parent, for |
| * example, in the case of hw/lw mixing. However, let's skip this scenario |
| * for the time being. We also assume the container peer is not null, which |
| * might also be false if addNotify() is called for a component outside of |
| * the hierarchy. The exception is LWWindowPeers: their containers are |
| * always null |
| */ |
| private final LWContainerPeer<?, ?> containerPeer; |
| |
| /** |
| * Handy reference to the top-level window peer. Window peer is borrowed |
| * from the containerPeer in constructor, and should also be updated when |
| * the component is reparented to another container |
| */ |
| private final LWWindowPeer windowPeer; |
| |
| private final AtomicBoolean disposed = new AtomicBoolean(false); |
| |
| // Bounds are relative to parent peer |
| private final Rectangle bounds = new Rectangle(); |
| private Region region; |
| |
| // Component state. Should be accessed under the state lock |
| private boolean visible = false; |
| private boolean enabled = true; |
| |
| private Color background; |
| private Color foreground; |
| private Font font; |
| |
| /** |
| * Paint area to coalesce all the paint events and store the target dirty |
| * area. |
| */ |
| private final RepaintArea targetPaintArea; |
| |
| // private volatile boolean paintPending; |
| private volatile boolean isLayouting; |
| |
| private final D delegate; |
| private Container delegateContainer; |
| private Component delegateDropTarget; |
| private final Object dropTargetLock = new Object(); |
| |
| private int fNumDropTargets = 0; |
| private PlatformDropTarget fDropTarget = null; |
| |
| private final PlatformComponent platformComponent; |
| |
| /** |
| * Character with reasonable value between the minimum width and maximum. |
| */ |
| static final char WIDE_CHAR = '0'; |
| |
| /** |
| * The back buffer provide user with a BufferStrategy. |
| */ |
| private Image backBuffer; |
| |
| /** |
| * All Swing delegates use delegateContainer as a parent. This container |
| * intentionally do not use parent of the peer. |
| */ |
| @SuppressWarnings("serial")// Safe: outer class is non-serializable. |
| private final class DelegateContainer extends Container { |
| { |
| enableEvents(0xFFFFFFFF); |
| } |
| |
| // Empty non private constructor was added because access to this |
| // class shouldn't be emulated by a synthetic accessor method. |
| DelegateContainer() { |
| super(); |
| } |
| |
| @Override |
| public boolean isLightweight() { |
| return false; |
| } |
| |
| @Override |
| public Point getLocation() { |
| return getLocationOnScreen(); |
| } |
| |
| @Override |
| public Point getLocationOnScreen() { |
| return LWComponentPeer.this.getLocationOnScreen(); |
| } |
| |
| @Override |
| public int getX() { |
| return getLocation().x; |
| } |
| |
| @Override |
| public int getY() { |
| return getLocation().y; |
| } |
| } |
| |
| LWComponentPeer(final T target, final PlatformComponent platformComponent) { |
| targetPaintArea = new LWRepaintArea(); |
| this.target = target; |
| this.platformComponent = platformComponent; |
| |
| // Container peer is always null for LWWindowPeers, so |
| // windowPeer is always null for them as well. On the other |
| // hand, LWWindowPeer shouldn't use windowPeer at all |
| final Container container = SunToolkit.getNativeContainer(target); |
| containerPeer = (LWContainerPeer) LWToolkit.targetToPeer(container); |
| windowPeer = containerPeer != null ? containerPeer.getWindowPeerOrSelf() |
| : null; |
| // don't bother about z-order here as updateZOrder() |
| // will be called from addNotify() later anyway |
| if (containerPeer != null) { |
| containerPeer.addChildPeer(this); |
| } |
| |
| // the delegate must be created after the target is set |
| AWTEventListener toolkitListener = null; |
| synchronized (Toolkit.getDefaultToolkit()) { |
| try { |
| toolkitListener = getToolkitAWTEventListener(); |
| setToolkitAWTEventListener(null); |
| |
| synchronized (getDelegateLock()) { |
| delegate = createDelegate(); |
| if (delegate != null) { |
| delegate.setVisible(false); |
| delegateContainer = new DelegateContainer(); |
| delegateContainer.add(delegate); |
| delegateContainer.addNotify(); |
| delegate.addNotify(); |
| resetColorsAndFont(delegate); |
| delegate.setOpaque(true); |
| } else { |
| return; |
| } |
| } |
| |
| } finally { |
| setToolkitAWTEventListener(toolkitListener); |
| } |
| |
| // todo swing: later on we will probably have one global RM |
| SwingUtilities3.setDelegateRepaintManager(delegate, new RepaintManager() { |
| @Override |
| public void addDirtyRegion(final JComponent c, final int x, final int y, final int w, final int h) { |
| repaintPeer(SwingUtilities.convertRectangle( |
| c, new Rectangle(x, y, w, h), getDelegate())); |
| } |
| }); |
| } |
| } |
| |
| /** |
| * This method must be called under Toolkit.getDefaultToolkit() lock |
| * and followed by setToolkitAWTEventListener() |
| */ |
| protected final AWTEventListener getToolkitAWTEventListener() { |
| return AccessController.doPrivileged(new PrivilegedAction<AWTEventListener>() { |
| public AWTEventListener run() { |
| Toolkit toolkit = Toolkit.getDefaultToolkit(); |
| try { |
| Field field = Toolkit.class.getDeclaredField("eventListener"); |
| field.setAccessible(true); |
| return (AWTEventListener) field.get(toolkit); |
| } catch (Exception e) { |
| throw new InternalError(e.toString()); |
| } |
| } |
| }); |
| } |
| |
| protected final void setToolkitAWTEventListener(final AWTEventListener listener) { |
| AccessController.doPrivileged(new PrivilegedAction<Void>() { |
| public Void run() { |
| Toolkit toolkit = Toolkit.getDefaultToolkit(); |
| try { |
| Field field = Toolkit.class.getDeclaredField("eventListener"); |
| field.setAccessible(true); |
| field.set(toolkit, listener); |
| } catch (Exception e) { |
| throw new InternalError(e.toString()); |
| } |
| return null; |
| } |
| }); |
| } |
| |
| /** |
| * This method is called under getDelegateLock(). |
| * Overridden in subclasses. |
| */ |
| D createDelegate() { |
| return null; |
| } |
| |
| final D getDelegate() { |
| return delegate; |
| } |
| |
| /** |
| * This method should be called under getDelegateLock(). |
| */ |
| Component getDelegateFocusOwner() { |
| return getDelegate(); |
| } |
| |
| /** |
| * Initializes this peer. The call to initialize() is not placed to |
| * LWComponentPeer ctor to let the subclass ctor to finish completely first. |
| * Instead, it's the LWToolkit object who is responsible for initialization. |
| * Note that we call setVisible() at the end of initialization. |
| */ |
| public final void initialize() { |
| platformComponent.initialize(getPlatformWindow()); |
| initializeImpl(); |
| setVisible(target.isVisible()); |
| } |
| |
| /** |
| * Fetching general properties from the target. Should be overridden in |
| * subclasses to initialize specific peers properties. |
| */ |
| void initializeImpl() { |
| // note that these methods can be overridden by the user and |
| // can return some strange values like null. |
| setBackground(target.getBackground()); |
| setForeground(target.getForeground()); |
| setFont(target.getFont()); |
| setBounds(target.getBounds()); |
| setEnabled(target.isEnabled()); |
| } |
| |
| private static void resetColorsAndFont(final Container c) { |
| c.setBackground(null); |
| c.setForeground(null); |
| c.setFont(null); |
| for (int i = 0; i < c.getComponentCount(); i++) { |
| resetColorsAndFont((Container) c.getComponent(i)); |
| } |
| } |
| |
| final Object getStateLock() { |
| return stateLock; |
| } |
| |
| /** |
| * Synchronize all operations with the Swing delegates under AWT tree lock, |
| * using a new separate lock to synchronize access to delegates may lead |
| * deadlocks. Think of it as a 'virtual EDT'. |
| * |
| * @return DelegateLock |
| */ |
| final Object getDelegateLock() { |
| return getTarget().getTreeLock(); |
| } |
| |
| protected static final Object getPeerTreeLock() { |
| return peerTreeLock; |
| } |
| |
| public final T getTarget() { |
| return target; |
| } |
| |
| // Just a helper method |
| // Returns the window peer or null if this is a window peer |
| protected final LWWindowPeer getWindowPeer() { |
| return windowPeer; |
| } |
| |
| // Returns the window peer or 'this' if this is a window peer |
| protected LWWindowPeer getWindowPeerOrSelf() { |
| return getWindowPeer(); |
| } |
| |
| // Just a helper method |
| protected final LWContainerPeer<?, ?> getContainerPeer() { |
| return containerPeer; |
| } |
| |
| public PlatformWindow getPlatformWindow() { |
| LWWindowPeer windowPeer = getWindowPeer(); |
| return windowPeer.getPlatformWindow(); |
| } |
| |
| // ---- PEER METHODS ---- // |
| |
| // Just a helper method |
| public LWToolkit getLWToolkit() { |
| return LWToolkit.getLWToolkit(); |
| } |
| |
| @Override |
| public final void dispose() { |
| if (disposed.compareAndSet(false, true)) { |
| disposeImpl(); |
| } |
| } |
| |
| protected void disposeImpl() { |
| destroyBuffers(); |
| LWContainerPeer<?, ?> cp = getContainerPeer(); |
| if (cp != null) { |
| cp.removeChildPeer(this); |
| } |
| platformComponent.dispose(); |
| LWToolkit.targetDisposedPeer(getTarget(), this); |
| } |
| |
| public final boolean isDisposed() { |
| return disposed.get(); |
| } |
| |
| /* |
| * GraphicsConfiguration is borrowed from the parent peer. The |
| * return value must not be null. |
| * |
| * Overridden in LWWindowPeer. |
| */ |
| @Override |
| public GraphicsConfiguration getGraphicsConfiguration() { |
| // Don't check windowPeer for null as it can only happen |
| // for windows, but this method is overridden in |
| // LWWindowPeer and doesn't call super() |
| return getWindowPeer().getGraphicsConfiguration(); |
| } |
| |
| |
| // Just a helper method |
| public final LWGraphicsConfig getLWGC() { |
| return (LWGraphicsConfig) getGraphicsConfiguration(); |
| } |
| |
| /* |
| * Overridden in LWWindowPeer to replace its surface |
| * data and back buffer. |
| */ |
| @Override |
| public boolean updateGraphicsData(GraphicsConfiguration gc) { |
| // TODO: not implemented |
| // throw new RuntimeException("Has not been implemented yet."); |
| return false; |
| } |
| |
| @Override |
| public Graphics getGraphics() { |
| final Graphics g = getOnscreenGraphics(); |
| if (g != null) { |
| synchronized (getPeerTreeLock()){ |
| applyConstrain(g); |
| } |
| } |
| return g; |
| } |
| |
| /* |
| * Peer Graphics is borrowed from the parent peer, while |
| * foreground and background colors and font are specific to |
| * this peer. |
| */ |
| public final Graphics getOnscreenGraphics() { |
| final LWWindowPeer wp = getWindowPeerOrSelf(); |
| return wp.getOnscreenGraphics(getForeground(), getBackground(), |
| getFont()); |
| |
| } |
| |
| private void applyConstrain(final Graphics g) { |
| final SunGraphics2D sg2d = (SunGraphics2D) g; |
| final Rectangle size = localToWindow(getSize()); |
| sg2d.constrain(size.x, size.y, size.width, size.height, getVisibleRegion()); |
| } |
| |
| Region getVisibleRegion() { |
| return computeVisibleRect(this, getRegion()); |
| } |
| |
| static final Region computeVisibleRect(final LWComponentPeer<?, ?> c, |
| Region region) { |
| final LWContainerPeer<?, ?> p = c.getContainerPeer(); |
| if (p != null) { |
| final Rectangle r = c.getBounds(); |
| region = region.getTranslatedRegion(r.x, r.y); |
| region = region.getIntersection(p.getRegion()); |
| region = region.getIntersection(p.getContentSize()); |
| region = p.cutChildren(region, c); |
| region = computeVisibleRect(p, region); |
| region = region.getTranslatedRegion(-r.x, -r.y); |
| } |
| return region; |
| } |
| |
| @Override |
| public ColorModel getColorModel() { |
| // Is it a correct implementation? |
| return getGraphicsConfiguration().getColorModel(); |
| } |
| |
| public boolean isTranslucent() { |
| // Translucent windows of the top level are supported only |
| return false; |
| } |
| |
| @Override |
| public final void createBuffers(int numBuffers, BufferCapabilities caps) |
| throws AWTException { |
| getLWGC().assertOperationSupported(numBuffers, caps); |
| final Image buffer = getLWGC().createBackBuffer(this); |
| synchronized (getStateLock()) { |
| backBuffer = buffer; |
| } |
| } |
| |
| @Override |
| public final Image getBackBuffer() { |
| synchronized (getStateLock()) { |
| if (backBuffer != null) { |
| return backBuffer; |
| } |
| } |
| throw new IllegalStateException("Buffers have not been created"); |
| } |
| |
| @Override |
| public final void flip(int x1, int y1, int x2, int y2, |
| BufferCapabilities.FlipContents flipAction) { |
| getLWGC().flip(this, getBackBuffer(), x1, y1, x2, y2, flipAction); |
| } |
| |
| @Override |
| public final void destroyBuffers() { |
| final Image oldBB; |
| synchronized (getStateLock()) { |
| oldBB = backBuffer; |
| backBuffer = null; |
| } |
| getLWGC().destroyBackBuffer(oldBB); |
| } |
| |
| // Helper method |
| public void setBounds(Rectangle r) { |
| setBounds(r.x, r.y, r.width, r.height, SET_BOUNDS); |
| } |
| |
| /** |
| * This method could be called on the toolkit thread. |
| */ |
| @Override |
| public void setBounds(int x, int y, int w, int h, int op) { |
| setBounds(x, y, w, h, op, true, false); |
| } |
| |
| protected void setBounds(int x, int y, int w, int h, int op, boolean notify, |
| final boolean updateTarget) { |
| Rectangle oldBounds; |
| synchronized (getStateLock()) { |
| oldBounds = new Rectangle(bounds); |
| if ((op & (SET_LOCATION | SET_BOUNDS)) != 0) { |
| bounds.x = x; |
| bounds.y = y; |
| } |
| if ((op & (SET_SIZE | SET_BOUNDS)) != 0) { |
| bounds.width = w; |
| bounds.height = h; |
| } |
| } |
| boolean moved = (oldBounds.x != x) || (oldBounds.y != y); |
| boolean resized = (oldBounds.width != w) || (oldBounds.height != h); |
| if (!moved && !resized) { |
| return; |
| } |
| final D delegate = getDelegate(); |
| if (delegate != null) { |
| synchronized (getDelegateLock()) { |
| delegateContainer.setBounds(0, 0, w, h); |
| delegate.setBounds(delegateContainer.getBounds()); |
| // TODO: the following means that the delegateContainer NEVER gets validated. That's WRONG! |
| delegate.validate(); |
| } |
| } |
| |
| final Point locationInWindow = localToWindow(0, 0); |
| platformComponent.setBounds(locationInWindow.x, locationInWindow.y, w, |
| h); |
| if (notify) { |
| repaintOldNewBounds(oldBounds); |
| if (resized) { |
| handleResize(w, h, updateTarget); |
| } |
| if (moved) { |
| handleMove(x, y, updateTarget); |
| } |
| } |
| } |
| |
| public final Rectangle getBounds() { |
| synchronized (getStateLock()) { |
| // Return a copy to prevent subsequent modifications |
| return bounds.getBounds(); |
| } |
| } |
| |
| public final Rectangle getSize() { |
| synchronized (getStateLock()) { |
| // Return a copy to prevent subsequent modifications |
| return new Rectangle(bounds.width, bounds.height); |
| } |
| } |
| |
| @Override |
| public Point getLocationOnScreen() { |
| Point windowLocation = getWindowPeer().getLocationOnScreen(); |
| Point locationInWindow = localToWindow(0, 0); |
| return new Point(windowLocation.x + locationInWindow.x, |
| windowLocation.y + locationInWindow.y); |
| } |
| |
| /** |
| * Returns the cursor of the peer, which is cursor of the target by default, |
| * but peer can override this behavior. |
| * |
| * @param p Point relative to the peer. |
| * @return Cursor of the peer or null if default cursor should be used. |
| */ |
| Cursor getCursor(final Point p) { |
| return getTarget().getCursor(); |
| } |
| |
| @Override |
| public void setBackground(final Color c) { |
| final Color oldBg = getBackground(); |
| if (oldBg == c || (oldBg != null && oldBg.equals(c))) { |
| return; |
| } |
| synchronized (getStateLock()) { |
| background = c; |
| } |
| final D delegate = getDelegate(); |
| if (delegate != null) { |
| synchronized (getDelegateLock()) { |
| // delegate will repaint the target |
| delegate.setBackground(c); |
| } |
| } else { |
| repaintPeer(); |
| } |
| } |
| |
| public final Color getBackground() { |
| synchronized (getStateLock()) { |
| return background; |
| } |
| } |
| |
| @Override |
| public void setForeground(final Color c) { |
| final Color oldFg = getForeground(); |
| if (oldFg == c || (oldFg != null && oldFg.equals(c))) { |
| return; |
| } |
| synchronized (getStateLock()) { |
| foreground = c; |
| } |
| final D delegate = getDelegate(); |
| if (delegate != null) { |
| synchronized (getDelegateLock()) { |
| // delegate will repaint the target |
| delegate.setForeground(c); |
| } |
| } else { |
| repaintPeer(); |
| } |
| } |
| |
| protected final Color getForeground() { |
| synchronized (getStateLock()) { |
| return foreground; |
| } |
| } |
| |
| @Override |
| public void setFont(final Font f) { |
| final Font oldF = getFont(); |
| if (oldF == f || (oldF != null && oldF.equals(f))) { |
| return; |
| } |
| synchronized (getStateLock()) { |
| font = f; |
| } |
| final D delegate = getDelegate(); |
| if (delegate != null) { |
| synchronized (getDelegateLock()) { |
| // delegate will repaint the target |
| delegate.setFont(f); |
| } |
| } else { |
| repaintPeer(); |
| } |
| } |
| |
| protected final Font getFont() { |
| synchronized (getStateLock()) { |
| return font; |
| } |
| } |
| |
| @Override |
| public FontMetrics getFontMetrics(final Font f) { |
| // Borrow the metrics from the top-level window |
| // return getWindowPeer().getFontMetrics(f); |
| // Obtain the metrics from the offscreen window where this peer is |
| // mostly drawn to. |
| // TODO: check for "use platform metrics" settings |
| final Graphics g = getOnscreenGraphics(); |
| if (g != null) { |
| try { |
| return g.getFontMetrics(f); |
| } finally { |
| g.dispose(); |
| } |
| } |
| synchronized (getDelegateLock()) { |
| return delegateContainer.getFontMetrics(f); |
| } |
| } |
| |
| @Override |
| public void setEnabled(final boolean e) { |
| boolean status = e; |
| final LWComponentPeer<?, ?> cp = getContainerPeer(); |
| if (cp != null) { |
| status &= cp.isEnabled(); |
| } |
| synchronized (getStateLock()) { |
| if (enabled == status) { |
| return; |
| } |
| enabled = status; |
| } |
| |
| final D delegate = getDelegate(); |
| |
| if (delegate != null) { |
| synchronized (getDelegateLock()) { |
| delegate.setEnabled(status); |
| } |
| } else { |
| repaintPeer(); |
| } |
| } |
| |
| // Helper method |
| public final boolean isEnabled() { |
| synchronized (getStateLock()) { |
| return enabled; |
| } |
| } |
| |
| @Override |
| public void setVisible(final boolean v) { |
| synchronized (getStateLock()) { |
| if (visible == v) { |
| return; |
| } |
| visible = v; |
| } |
| setVisibleImpl(v); |
| } |
| |
| protected void setVisibleImpl(final boolean v) { |
| final D delegate = getDelegate(); |
| |
| if (delegate != null) { |
| synchronized (getDelegateLock()) { |
| delegate.setVisible(v); |
| } |
| } |
| if (visible) { |
| repaintPeer(); |
| } else { |
| repaintParent(getBounds()); |
| } |
| } |
| |
| // Helper method |
| public final boolean isVisible() { |
| synchronized (getStateLock()) { |
| return visible; |
| } |
| } |
| |
| @Override |
| public void paint(final Graphics g) { |
| getTarget().paint(g); |
| } |
| |
| @Override |
| public void print(final Graphics g) { |
| getTarget().print(g); |
| } |
| |
| @Override |
| public void reparent(ContainerPeer newContainer) { |
| // TODO: not implemented |
| throw new UnsupportedOperationException("ComponentPeer.reparent()"); |
| } |
| |
| @Override |
| public boolean isReparentSupported() { |
| // TODO: not implemented |
| return false; |
| } |
| |
| @Override |
| public void setZOrder(final ComponentPeer above) { |
| LWContainerPeer<?, ?> cp = getContainerPeer(); |
| // Don't check containerPeer for null as it can only happen |
| // for windows, but this method is overridden in |
| // LWWindowPeer and doesn't call super() |
| cp.setChildPeerZOrder(this, (LWComponentPeer<?, ?>) above); |
| } |
| |
| @Override |
| public void coalescePaintEvent(PaintEvent e) { |
| if (!(e instanceof IgnorePaintEvent)) { |
| Rectangle r = e.getUpdateRect(); |
| if ((r != null) && !r.isEmpty()) { |
| targetPaintArea.add(r, e.getID()); |
| } |
| } |
| } |
| |
| /* |
| * Should be overridden in subclasses which use complex Swing components. |
| */ |
| @Override |
| public void layout() { |
| // TODO: not implemented |
| } |
| |
| @Override |
| public boolean isObscured() { |
| // TODO: not implemented |
| return false; |
| } |
| |
| @Override |
| public boolean canDetermineObscurity() { |
| // TODO: not implemented |
| return false; |
| } |
| |
| /** |
| * Determines the preferred size of the component. By default forwards the |
| * request to the Swing helper component. Should be overridden in subclasses |
| * if required. |
| */ |
| @Override |
| public Dimension getPreferredSize() { |
| final Dimension size; |
| synchronized (getDelegateLock()) { |
| size = getDelegate().getPreferredSize(); |
| } |
| return validateSize(size); |
| } |
| |
| /** |
| * Determines the minimum size of the component. By default forwards the |
| * request to the Swing helper component. Should be overridden in subclasses |
| * if required. |
| */ |
| @Override |
| public Dimension getMinimumSize() { |
| final Dimension size; |
| synchronized (getDelegateLock()) { |
| size = getDelegate().getMinimumSize(); |
| } |
| return validateSize(size); |
| } |
| |
| /** |
| * In some situations delegates can return empty minimum/preferred size. |
| * (For example: empty JLabel, etc), but awt components never should be |
| * empty. In the XPeers or WPeers we use some magic constants, but here we |
| * try to use something more useful, |
| */ |
| private Dimension validateSize(final Dimension size) { |
| if (size.width == 0 || size.height == 0) { |
| final FontMetrics fm = getFontMetrics(getFont()); |
| size.width = fm.charWidth(WIDE_CHAR); |
| size.height = fm.getHeight(); |
| } |
| return size; |
| } |
| |
| @Override |
| public void updateCursorImmediately() { |
| getLWToolkit().getCursorManager().updateCursor(); |
| } |
| |
| @Override |
| public boolean isFocusable() { |
| // Overridden in focusable subclasses like buttons |
| return false; |
| } |
| |
| @Override |
| public boolean requestFocus(Component lightweightChild, boolean temporary, |
| boolean focusedWindowChangeAllowed, long time, |
| FocusEvent.Cause cause) |
| { |
| if (focusLog.isLoggable(PlatformLogger.Level.FINEST)) { |
| focusLog.finest("lightweightChild=" + lightweightChild + ", temporary=" + temporary + |
| ", focusedWindowChangeAllowed=" + focusedWindowChangeAllowed + |
| ", time= " + time + ", cause=" + cause); |
| } |
| if (LWKeyboardFocusManagerPeer.processSynchronousLightweightTransfer( |
| getTarget(), lightweightChild, temporary, |
| focusedWindowChangeAllowed, time)) { |
| return true; |
| } |
| |
| int result = LWKeyboardFocusManagerPeer.shouldNativelyFocusHeavyweight( |
| getTarget(), lightweightChild, temporary, |
| focusedWindowChangeAllowed, time, cause); |
| switch (result) { |
| case LWKeyboardFocusManagerPeer.SNFH_FAILURE: |
| return false; |
| case LWKeyboardFocusManagerPeer.SNFH_SUCCESS_PROCEED: |
| Window parentWindow = SunToolkit.getContainingWindow(getTarget()); |
| if (parentWindow == null) { |
| focusLog.fine("request rejected, parentWindow is null"); |
| LWKeyboardFocusManagerPeer.removeLastFocusRequest(getTarget()); |
| return false; |
| } |
| final LWWindowPeer parentPeer = |
| AWTAccessor.getComponentAccessor() |
| .getPeer(parentWindow); |
| if (parentPeer == null) { |
| focusLog.fine("request rejected, parentPeer is null"); |
| LWKeyboardFocusManagerPeer.removeLastFocusRequest(getTarget()); |
| return false; |
| } |
| |
| // A fix for 7145768. Ensure the parent window is currently natively focused. |
| // The more evident place to perform this check is in KFM.shouldNativelyFocusHeavyweight, |
| // however that is the shared code and this particular problem's reproducibility has |
| // platform specifics. So, it was decided to narrow down the fix to lwawt (OSX) in |
| // current release. TODO: consider fixing it in the shared code. |
| if (!focusedWindowChangeAllowed) { |
| LWWindowPeer decoratedPeer = parentPeer.isSimpleWindow() ? |
| LWWindowPeer.getOwnerFrameDialog(parentPeer) : parentPeer; |
| |
| if (decoratedPeer == null || !decoratedPeer.getPlatformWindow().isActive()) { |
| if (focusLog.isLoggable(PlatformLogger.Level.FINE)) { |
| focusLog.fine("request rejected, focusedWindowChangeAllowed==false, " + |
| "decoratedPeer is inactive: " + decoratedPeer); |
| } |
| LWKeyboardFocusManagerPeer.removeLastFocusRequest(getTarget()); |
| return false; |
| } |
| } |
| |
| boolean res = parentPeer.requestWindowFocus(cause); |
| // If parent window can be made focused and has been made focused (synchronously) |
| // then we can proceed with children, otherwise we retreat |
| if (!res || !parentWindow.isFocused()) { |
| if (focusLog.isLoggable(PlatformLogger.Level.FINE)) { |
| focusLog.fine("request rejected, res= " + res + ", parentWindow.isFocused()=" + |
| parentWindow.isFocused()); |
| } |
| LWKeyboardFocusManagerPeer.removeLastFocusRequest(getTarget()); |
| return false; |
| } |
| |
| KeyboardFocusManagerPeer kfmPeer = LWKeyboardFocusManagerPeer.getInstance(); |
| Component focusOwner = kfmPeer.getCurrentFocusOwner(); |
| return LWKeyboardFocusManagerPeer.deliverFocus(lightweightChild, |
| getTarget(), temporary, |
| focusedWindowChangeAllowed, |
| time, cause, focusOwner); |
| |
| case LWKeyboardFocusManagerPeer.SNFH_SUCCESS_HANDLED: |
| return true; |
| } |
| |
| return false; |
| } |
| |
| @Override |
| public final Image createImage(final ImageProducer producer) { |
| return new ToolkitImage(producer); |
| } |
| |
| @Override |
| public final Image createImage(final int width, final int height) { |
| return getLWGC().createAcceleratedImage(getTarget(), width, height); |
| } |
| |
| @Override |
| public final VolatileImage createVolatileImage(final int w, final int h) { |
| return new SunVolatileImage(getTarget(), w, h); |
| } |
| |
| @Override |
| public boolean prepareImage(Image img, int w, int h, ImageObserver o) { |
| // TODO: is it a right/complete implementation? |
| return Toolkit.getDefaultToolkit().prepareImage(img, w, h, o); |
| } |
| |
| @Override |
| public int checkImage(Image img, int w, int h, ImageObserver o) { |
| // TODO: is it a right/complete implementation? |
| return Toolkit.getDefaultToolkit().checkImage(img, w, h, o); |
| } |
| |
| @Override |
| public boolean handlesWheelScrolling() { |
| // TODO: not implemented |
| return false; |
| } |
| |
| @Override |
| public final void applyShape(final Region shape) { |
| synchronized (getStateLock()) { |
| if (region == shape || (region != null && region.equals(shape))) { |
| return; |
| } |
| } |
| applyShapeImpl(shape); |
| } |
| |
| void applyShapeImpl(final Region shape) { |
| synchronized (getStateLock()) { |
| if (shape != null) { |
| region = Region.WHOLE_REGION.getIntersection(shape); |
| } else { |
| region = null; |
| } |
| } |
| repaintParent(getBounds()); |
| } |
| |
| protected final Region getRegion() { |
| synchronized (getStateLock()) { |
| return isShaped() ? region : Region.getInstance(getSize()); |
| } |
| } |
| |
| public boolean isShaped() { |
| synchronized (getStateLock()) { |
| return region != null; |
| } |
| } |
| |
| // DropTargetPeer Method |
| @Override |
| public void addDropTarget(DropTarget dt) { |
| LWWindowPeer winPeer = getWindowPeerOrSelf(); |
| if (winPeer != null && winPeer != this) { |
| // We need to register the DropTarget in the |
| // peer of the window ancestor of the component |
| winPeer.addDropTarget(dt); |
| } else { |
| synchronized (dropTargetLock) { |
| // 10-14-02 VL: Windows WComponentPeer would add (or remove) the drop target only |
| // if it's the first (or last) one for the component. Otherwise this call is a no-op. |
| if (++fNumDropTargets == 1) { |
| // Having a non-null drop target would be an error but let's check just in case: |
| if (fDropTarget != null) { |
| throw new IllegalStateException("Current drop target is not null"); |
| } |
| // Create a new drop target: |
| fDropTarget = LWToolkit.getLWToolkit().createDropTarget(dt, target, this); |
| } |
| } |
| } |
| } |
| |
| // DropTargetPeer Method |
| @Override |
| public void removeDropTarget(DropTarget dt) { |
| LWWindowPeer winPeer = getWindowPeerOrSelf(); |
| if (winPeer != null && winPeer != this) { |
| // We need to unregister the DropTarget in the |
| // peer of the window ancestor of the component |
| winPeer.removeDropTarget(dt); |
| } else { |
| synchronized (dropTargetLock){ |
| // 10-14-02 VL: Windows WComponentPeer would add (or remove) the drop target only |
| // if it's the first (or last) one for the component. Otherwise this call is a no-op. |
| if (--fNumDropTargets == 0) { |
| // Having a null drop target would be an error but let's check just in case: |
| if (fDropTarget != null) { |
| // Dispose of the drop target: |
| fDropTarget.dispose(); |
| fDropTarget = null; |
| } else |
| System.err.println("CComponent.removeDropTarget(): current drop target is null."); |
| } |
| } |
| } |
| } |
| |
| // ---- PEER NOTIFICATIONS ---- // |
| |
| /** |
| * Called when this peer's location has been changed either as a result |
| * of target.setLocation() or as a result of user actions (window is |
| * dragged with mouse). |
| * |
| * This method could be called on the toolkit thread. |
| */ |
| protected final void handleMove(final int x, final int y, |
| final boolean updateTarget) { |
| if (updateTarget) { |
| AWTAccessor.getComponentAccessor().setLocation(getTarget(), x, y); |
| } |
| postEvent(new ComponentEvent(getTarget(), |
| ComponentEvent.COMPONENT_MOVED)); |
| } |
| |
| /** |
| * Called when this peer's size has been changed either as a result of |
| * target.setSize() or as a result of user actions (window is resized). |
| * |
| * This method could be called on the toolkit thread. |
| */ |
| protected final void handleResize(final int w, final int h, |
| final boolean updateTarget) { |
| Image oldBB = null; |
| synchronized (getStateLock()) { |
| if (backBuffer != null) { |
| oldBB = backBuffer; |
| backBuffer = getLWGC().createBackBuffer(this); |
| } |
| } |
| getLWGC().destroyBackBuffer(oldBB); |
| |
| if (updateTarget) { |
| AWTAccessor.getComponentAccessor().setSize(getTarget(), w, h); |
| } |
| postEvent(new ComponentEvent(getTarget(), |
| ComponentEvent.COMPONENT_RESIZED)); |
| } |
| |
| protected final void repaintOldNewBounds(final Rectangle oldB) { |
| repaintParent(oldB); |
| repaintPeer(getSize()); |
| } |
| |
| protected final void repaintParent(final Rectangle oldB) { |
| final LWContainerPeer<?, ?> cp = getContainerPeer(); |
| if (cp != null) { |
| // Repaint unobscured part of the parent |
| cp.repaintPeer(cp.getContentSize().intersection(oldB)); |
| } |
| } |
| |
| // ---- EVENTS ---- // |
| |
| /** |
| * Post an event to the proper Java EDT. |
| */ |
| public void postEvent(final AWTEvent event) { |
| LWToolkit.postEvent(event); |
| } |
| |
| protected void postPaintEvent(int x, int y, int w, int h) { |
| // TODO: call getIgnoreRepaint() directly with the right ACC |
| if (AWTAccessor.getComponentAccessor().getIgnoreRepaint(target)) { |
| return; |
| } |
| PaintEvent event = PaintEventDispatcher.getPaintEventDispatcher(). |
| createPaintEvent(getTarget(), x, y, w, h); |
| if (event != null) { |
| postEvent(event); |
| } |
| } |
| |
| /* |
| * Gives a chance for the peer to handle the event after it's been |
| * processed by the target. |
| */ |
| @Override |
| public void handleEvent(AWTEvent e) { |
| if ((e instanceof InputEvent) && ((InputEvent) e).isConsumed()) { |
| return; |
| } |
| switch (e.getID()) { |
| case FocusEvent.FOCUS_GAINED: |
| case FocusEvent.FOCUS_LOST: |
| handleJavaFocusEvent((FocusEvent) e); |
| break; |
| case PaintEvent.PAINT: |
| // Got a native paint event |
| // paintPending = false; |
| // fall through to the next statement |
| case PaintEvent.UPDATE: |
| handleJavaPaintEvent(); |
| break; |
| case MouseEvent.MOUSE_PRESSED: |
| handleJavaMouseEvent((MouseEvent)e); |
| } |
| |
| sendEventToDelegate(e); |
| } |
| |
| protected void sendEventToDelegate(final AWTEvent e) { |
| if (getDelegate() == null || !isShowing() || !isEnabled()) { |
| return; |
| } |
| synchronized (getDelegateLock()) { |
| AWTEvent delegateEvent = createDelegateEvent(e); |
| if (delegateEvent != null) { |
| AWTAccessor.getComponentAccessor() |
| .processEvent((Component) delegateEvent.getSource(), |
| delegateEvent); |
| if (delegateEvent instanceof KeyEvent) { |
| KeyEvent ke = (KeyEvent) delegateEvent; |
| SwingUtilities.processKeyBindings(ke); |
| } |
| } |
| } |
| } |
| |
| /** |
| * Changes the target of the AWTEvent from awt component to appropriate |
| * swing delegate. |
| */ |
| @SuppressWarnings("deprecation") |
| private AWTEvent createDelegateEvent(final AWTEvent e) { |
| // TODO modifiers should be changed to getModifiers()|getModifiersEx()? |
| AWTEvent delegateEvent = null; |
| if (e instanceof MouseWheelEvent) { |
| MouseWheelEvent me = (MouseWheelEvent) e; |
| delegateEvent = new MouseWheelEvent( |
| delegate, me.getID(), me.getWhen(), |
| me.getModifiers(), |
| me.getX(), me.getY(), |
| me.getXOnScreen(), me.getYOnScreen(), |
| me.getClickCount(), |
| me.isPopupTrigger(), |
| me.getScrollType(), |
| me.getScrollAmount(), |
| me.getWheelRotation(), |
| me.getPreciseWheelRotation()); |
| } else if (e instanceof MouseEvent) { |
| MouseEvent me = (MouseEvent) e; |
| |
| Component eventTarget = SwingUtilities.getDeepestComponentAt(delegate, me.getX(), me.getY()); |
| |
| if (me.getID() == MouseEvent.MOUSE_DRAGGED) { |
| if (delegateDropTarget == null) { |
| delegateDropTarget = eventTarget; |
| } else { |
| eventTarget = delegateDropTarget; |
| } |
| } |
| if (me.getID() == MouseEvent.MOUSE_RELEASED && delegateDropTarget != null) { |
| eventTarget = delegateDropTarget; |
| delegateDropTarget = null; |
| } |
| if (eventTarget == null) { |
| eventTarget = delegate; |
| } |
| delegateEvent = SwingUtilities.convertMouseEvent(getTarget(), me, eventTarget); |
| } else if (e instanceof KeyEvent) { |
| KeyEvent ke = (KeyEvent) e; |
| delegateEvent = new KeyEvent(getDelegateFocusOwner(), ke.getID(), ke.getWhen(), |
| ke.getModifiers(), ke.getKeyCode(), ke.getKeyChar(), ke.getKeyLocation()); |
| AWTAccessor.getKeyEventAccessor().setExtendedKeyCode((KeyEvent) delegateEvent, |
| ke.getExtendedKeyCode()); |
| } else if (e instanceof FocusEvent) { |
| FocusEvent fe = (FocusEvent) e; |
| delegateEvent = new FocusEvent(getDelegateFocusOwner(), fe.getID(), fe.isTemporary()); |
| } |
| return delegateEvent; |
| } |
| |
| protected void handleJavaMouseEvent(MouseEvent e) { |
| Component target = getTarget(); |
| assert (e.getSource() == target); |
| |
| if (!target.isFocusOwner() && LWKeyboardFocusManagerPeer.shouldFocusOnClick(target)) { |
| LWKeyboardFocusManagerPeer.requestFocusFor(target, FocusEvent.Cause.MOUSE_EVENT); |
| } |
| } |
| |
| /** |
| * Handler for FocusEvents. |
| */ |
| void handleJavaFocusEvent(final FocusEvent e) { |
| // Note that the peer receives all the FocusEvents from |
| // its lightweight children as well |
| KeyboardFocusManagerPeer kfmPeer = LWKeyboardFocusManagerPeer.getInstance(); |
| kfmPeer.setCurrentFocusOwner(e.getID() == FocusEvent.FOCUS_GAINED ? getTarget() : null); |
| } |
| |
| /** |
| * All peers should clear background before paint. |
| * |
| * @return false on components that DO NOT require a clearRect() before |
| * painting. |
| */ |
| protected final boolean shouldClearRectBeforePaint() { |
| // TODO: sun.awt.noerasebackground |
| return true; |
| } |
| |
| /** |
| * Handler for PAINT and UPDATE PaintEvents. |
| */ |
| private void handleJavaPaintEvent() { |
| // Skip all painting while layouting and all UPDATEs |
| // while waiting for native paint |
| // if (!isLayouting && !paintPending) { |
| if (!isLayouting()) { |
| targetPaintArea.paint(getTarget(), shouldClearRectBeforePaint()); |
| } |
| } |
| |
| // ---- UTILITY METHODS ---- // |
| |
| /** |
| * Finds a top-most visible component for the given point. The location is |
| * specified relative to the peer's parent. |
| */ |
| LWComponentPeer<?, ?> findPeerAt(final int x, final int y) { |
| final Rectangle r = getBounds(); |
| final Region sh = getRegion(); |
| final boolean found = isVisible() && sh.contains(x - r.x, y - r.y); |
| return found ? this : null; |
| } |
| |
| /* |
| * Translated the given point in Window coordinates to the point in |
| * coordinates local to this component. The given window peer must be |
| * the window where this component is in. |
| */ |
| public Point windowToLocal(int x, int y, LWWindowPeer wp) { |
| return windowToLocal(new Point(x, y), wp); |
| } |
| |
| public Point windowToLocal(Point p, LWWindowPeer wp) { |
| LWComponentPeer<?, ?> cp = this; |
| while (cp != wp) { |
| Rectangle cpb = cp.getBounds(); |
| p.x -= cpb.x; |
| p.y -= cpb.y; |
| cp = cp.getContainerPeer(); |
| } |
| // Return a copy to prevent subsequent modifications |
| return new Point(p); |
| } |
| |
| public Rectangle windowToLocal(Rectangle r, LWWindowPeer wp) { |
| Point p = windowToLocal(r.getLocation(), wp); |
| return new Rectangle(p, r.getSize()); |
| } |
| |
| public Point localToWindow(int x, int y) { |
| return localToWindow(new Point(x, y)); |
| } |
| |
| public Point localToWindow(Point p) { |
| LWComponentPeer<?, ?> cp = getContainerPeer(); |
| Rectangle r = getBounds(); |
| while (cp != null) { |
| p.x += r.x; |
| p.y += r.y; |
| r = cp.getBounds(); |
| cp = cp.getContainerPeer(); |
| } |
| // Return a copy to prevent subsequent modifications |
| return new Point(p); |
| } |
| |
| public Rectangle localToWindow(Rectangle r) { |
| Point p = localToWindow(r.getLocation()); |
| return new Rectangle(p, r.getSize()); |
| } |
| |
| public final void repaintPeer() { |
| repaintPeer(getSize()); |
| } |
| |
| void repaintPeer(final Rectangle r) { |
| final Rectangle toPaint = getSize().intersection(r); |
| if (!isShowing() || toPaint.isEmpty()) { |
| return; |
| } |
| |
| postPaintEvent(toPaint.x, toPaint.y, toPaint.width, toPaint.height); |
| } |
| |
| /** |
| * Determines whether this peer is showing on screen. This means that the |
| * peer must be visible, and it must be in a container that is visible and |
| * showing. |
| * |
| * @see #isVisible() |
| */ |
| protected final boolean isShowing() { |
| synchronized (getPeerTreeLock()) { |
| if (isVisible()) { |
| final LWContainerPeer<?, ?> container = getContainerPeer(); |
| return (container == null) || container.isShowing(); |
| } |
| } |
| return false; |
| } |
| |
| /** |
| * Paints the peer. Delegate the actual painting to Swing components. |
| */ |
| protected final void paintPeer(final Graphics g) { |
| final D delegate = getDelegate(); |
| if (delegate != null) { |
| if (!SwingUtilities.isEventDispatchThread()) { |
| throw new InternalError("Painting must be done on EDT"); |
| } |
| synchronized (getDelegateLock()) { |
| // JComponent.print() is guaranteed to not affect the double buffer |
| getDelegate().print(g); |
| } |
| } |
| } |
| |
| protected static final void flushOnscreenGraphics(){ |
| final OGLRenderQueue rq = OGLRenderQueue.getInstance(); |
| rq.lock(); |
| try { |
| rq.flushNow(); |
| } finally { |
| rq.unlock(); |
| } |
| } |
| |
| /** |
| * Used by ContainerPeer to skip all the paint events during layout. |
| * |
| * @param isLayouting layouting state. |
| */ |
| protected final void setLayouting(final boolean isLayouting) { |
| this.isLayouting = isLayouting; |
| } |
| |
| /** |
| * Returns layouting state. Used by ComponentPeer to skip all the paint |
| * events during layout. |
| * |
| * @return true during layout, false otherwise. |
| */ |
| private final boolean isLayouting() { |
| return isLayouting; |
| } |
| } |