/*
* Copyright (c) 2011, 2013, 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<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
/**
* 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
/**
* 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.
/**
* 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
/**
* 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
/**
* 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.
/**
* 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.
/**
* Character with reasonable value between the minimum width and maximum.
*/
static final char WIDE_CHAR = '0';
The back buffer provide user with a BufferStrategy.
/**
* 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.
/**
* 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()
/**
* 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.
/**
* 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().
/**
* 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.
/**
* 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.
/**
* 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'.
Returns: DelegateLock
/**
* 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.
/**
* 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.
Params: - p – Point relative to the peer.
Returns: Cursor of the peer or null if default cursor should be used.
/**
* 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.
/**
* 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.
/**
* 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,
/**
* 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;
}
final LWWindowPeer parentPeer =
(LWWindowPeer) 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)
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.
/**
* 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.
/**
* 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.
/**
* 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.
/**
* 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.
/**
* 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.
Returns: false on components that DO NOT require a clearRect() before
painting.
/**
* 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.
/**
* 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.
/**
* 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 Also: - isVisible()
/**
* 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.
/**
* 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.
Params: - isLayouting – layouting state.
/**
* 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.
Returns: true during layout, false otherwise.
/**
* 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;
}
}