/* * Copyright (c) 2011, 2012, 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 sun.lwawt.macosx.CDropTarget; import com.sun.java.swing.SwingUtilities3; public abstract class LWComponentPeer 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 StringBuilder("LWComponentPeer.stateLock"); // 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 StringBuilder("LWComponentPeer.peerTreeLock"); 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 CDropTarget 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; private final class DelegateContainer extends Container { { enableEvents(0xFFFFFFFF); } 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; } } public LWComponentPeer(T target, 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() { 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() { 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. */ protected D createDelegate() { return null; } protected final D getDelegate() { return delegate; } protected 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() { 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(); } protected AppContext getAppContext() { return SunToolkit.targetToAppContext(getTarget()); } // ---- PEER METHODS ---- // @Override public Toolkit getToolkit() { return LWToolkit.getLWToolkit(); } // 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()); } public Region getVisibleRegion() { return computeVisibleRect(this, getRegion()); } static final Region computeVisibleRect(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. */ protected 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(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, CausedFocusEvent.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; } LWWindowPeer parentPeer = (LWWindowPeer) parentWindow.getPeer(); 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 getToolkit().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 getToolkit().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) System.err.println("CComponent.addDropTarget(): current drop target is non-null."); // Create a new drop target: fDropTarget = CDropTarget.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(AWTEvent event) { SunToolkit.postEvent(getAppContext(), 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. */ 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.getClickCount(), me.isPopupTrigger(), me.getScrollType(), me.getScrollAmount(), me.getWheelRotation()); } 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, CausedFocusEvent.Cause.MOUSE_EVENT); } } /** * Handler for FocusEvents. */ protected void handleJavaFocusEvent(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. */ public 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()); } public 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. Overridden in subclasses to 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; } }