blob: 956281b84731050a9522890aa49abaea397ddd2f [file] [log] [blame]
/*
* 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
*/
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 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;
/**
* 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,
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;
}
}
return parentPeer.requestWindowFocus(cause, () -> {
if (focusLog.isLoggable(PlatformLogger.Level.FINE)) {
focusLog.fine("request rejected, parentWindow.isFocused() = " +
parentWindow.isFocused());
}
LWKeyboardFocusManagerPeer.removeLastFocusRequest(getTarget());
}, () -> {
KeyboardFocusManagerPeer kfmPeer = LWKeyboardFocusManagerPeer.getInstance();
Component focusOwner = kfmPeer.getCurrentFocusOwner();
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.
*/
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.
*/
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.
*/
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;
}
}