Some more stuff that I had trouble with Espial/Espresso. This should fix
the font handling in AWT.
2006-09-20 Roman Kennke <[EMAIL PROTECTED]>
* java/awt/Component.java
(addNotify): Invalidate here. Fetch peer font.
(getFont): Delegate to helper method, to protect from
overriding client code. Lock the tree while fetching the font.
(getFontImpl): New helper method. Moved code from getFont() in
here.
(removeNotify): Nullify peerFont too.
(setFont): Synchronize on tree and component to avoid threading
issues. Update the peerFont correctly.
(validate): Update the peer font if necessary, before validating.
(getGraphics): Revert to recursive graphics fetching.
Set component font on the Graphics object.
(translateEvent): Removed unnecessary cast.
* java/awt/Container.java
(invalidateTree): Made final and private. Made implementation
slightly more efficient.
(setFont): Get old and new font via getFont() to account for
the real font, and only invalidate the tree when they are not
the same and not equal.
(visitChild): Set the font of the child on the component graphics.
* java/awt/Frame.java
(setMenuBar): Create local reference of peer for thread safety.
Only call simple invalidate, not invalidateTree().
/Roman
Index: java/awt/Component.java
===================================================================
RCS file: /cvsroot/classpath/classpath/java/awt/Component.java,v
retrieving revision 1.147
diff -u -1 -5 -r1.147 Component.java
--- java/awt/Component.java 18 Sep 2006 13:22:57 -0000 1.147
+++ java/awt/Component.java 20 Sep 2006 09:11:06 -0000
@@ -1168,55 +1168,102 @@
*/
public boolean isBackgroundSet()
{
return background != null;
}
/**
* Returns the font in use for this component. If not set, this is inherited
* from the parent.
*
* @return the font for this component
* @see #setFont(Font)
*/
public Font getFont()
{
- Font f = font;
- if (f != null)
- return f;
+ Font f;
+ synchronized (getTreeLock())
+ {
+ f = getFontImpl();
+ }
+ return f;
+ }
- Component p = parent;
- if (p != null)
- return p.getFont();
- return null;
+ /**
+ * Implementation of getFont(). This is pulled out of getFont() to prevent
+ * client programs from overriding this. This method is executed within
+ * a tree lock, so we can assume that the hierarchy doesn't change in
+ * between.
+ *
+ * @return the font of this component
+ */
+ private final Font getFontImpl()
+ {
+ Font f = font;
+ if (f == null)
+ {
+ Component p = parent;
+ if (p != null)
+ f = p.getFontImpl();
+ }
+ return f;
}
/**
* Sets the font for this component to the specified font. This is a bound
* property.
*
- * @param newFont the new font for this component
+ * @param f the new font for this component
*
* @see #getFont()
*/
- public void setFont(Font newFont)
+ public void setFont(Font f)
{
- Font oldFont = font;
- font = newFont;
- if (peer != null)
- peer.setFont(font);
+ Font oldFont;
+ Font newFont;
+ // Synchronize on the tree because getFontImpl() relies on the hierarchy
+ // not beeing changed.
+ synchronized (getTreeLock())
+ {
+ // Synchronize on this here to guarantee thread safety wrt to the
+ // property values.
+ synchronized (this)
+ {
+ oldFont = font;
+ font = f;
+ newFont = f;
+ }
+ // Create local variable here for thread safety.
+ ComponentPeer p = peer;
+ if (p != null)
+ {
+ // The peer receives the real font setting, which can depend on
+ // the parent font when this component's font has been set to null.
+ f = getFont();
+ if (f != null)
+ {
+ p.setFont(f);
+ peerFont = f;
+ }
+ }
+ }
+
+ // Fire property change event.
firePropertyChange("font", oldFont, newFont);
+
+ // Invalidate when necessary as font changes can change the size of the
+ // component.
if (valid)
invalidate();
}
/**
* Tests if the font was explicitly set, or just inherited from the parent.
*
* @return true if the font has been set
* @since 1.4
*/
public boolean isFontSet()
{
return font != null;
}
@@ -2024,31 +2071,56 @@
{
// Nothing to do unless we're a container.
}
/**
* Called to ensure that the layout for this component is valid. This is
* usually called on containers.
*
* @see #invalidate()
* @see #doLayout()
* @see LayoutManager
* @see Container#validate()
*/
public void validate()
{
- valid = true;
+ if (! valid)
+ {
+ // Synchronize on the tree here as this might change the layout
+ // of the hierarchy.
+ synchronized (getTreeLock())
+ {
+ // Create local variables for thread safety.
+ ComponentPeer p = peer;
+ if (p != null)
+ {
+ // Possibly update the peer's font.
+ Font newFont = getFont();
+ Font oldFont = peerFont;
+ // Only update when the font really changed.
+ if (newFont != oldFont
+ && (oldFont == null || ! oldFont.equals(newFont)))
+ {
+ p.setFont(newFont);
+ peerFont = newFont;
+ }
+ // Let the peer perform any layout.
+ p.layout();
+ }
+ }
+ valid = true;
+ }
}
/**
* Invalidates this component and all of its parent components. This will
* cause them to have their layout redone. This is called frequently, so
* make it fast.
*/
public void invalidate()
{
// Need to lock here, to avoid races and other ugly stuff when doing
// layout or structure changes in other threads.
synchronized (getTreeLock())
{
// Invalidate.
valid = false;
@@ -2066,51 +2138,50 @@
parent.invalidate();
}
}
/**
* Returns a graphics object for this component. Returns <code>null</code>
* if this component is not currently displayed on the screen.
*
* @return a graphics object for this component
* @see #paint(Graphics)
*/
public Graphics getGraphics()
{
// Only heavyweight peers can handle this.
ComponentPeer p = peer;
- Component comp = this;
- int offsX = 0;
- int offsY = 0;
- while (p instanceof LightweightPeer)
+ Graphics g = null;
+ if (p instanceof LightweightPeer)
{
- offsX += comp.x;
- offsY += comp.y;
- comp = comp.parent;
- p = comp == null ? null : comp.peer;
+ if (parent != null)
+ {
+ g = parent.getGraphics();
+ if (g != null)
+ {
+ g.translate(x, y);
+ g.setClip(0, 0, width, height);
+ g.setFont(getFont());
+ }
+ }
}
-
- Graphics gfx = null;
- if (p != null)
+ else
{
- assert ! (p instanceof LightweightPeer);
- gfx = p.getGraphics();
- gfx.translate(offsX, offsY);
- gfx.clipRect(0, 0, width, height);
- gfx.setFont(font);
+ if (p != null)
+ g = p.getGraphics();
}
- return gfx;
+ return g;
}
/**
* Returns the font metrics for the specified font in this component.
*
* @param font the font to retrieve metrics for
* @return the font metrics for the specified font
* @throws NullPointerException if font is null
* @see #getFont()
* @see Toolkit#getFontMetrics(Font)
*/
public FontMetrics getFontMetrics(Font font)
{
ComponentPeer p = peer;
Component comp = this;
@@ -4067,71 +4138,78 @@
* time. This method is called automatically by the AWT system and
* should not be called by user level code.
*
* @see #isDisplayable()
* @see #removeNotify()
*/
public void addNotify()
{
// We need to lock the tree here to avoid races and inconsistencies.
synchronized (getTreeLock())
{
if (peer == null)
peer = getToolkit().createComponent(this);
else if (parent != null && parent.isLightweight())
new HeavyweightInLightweightListener(parent);
- /* Now that all the children has gotten their peers, we should
- have the event mask needed for this component and its
- lightweight subcomponents. */
+ // Now that all the children has gotten their peers, we should
+ // have the event mask needed for this component and its
+ //lightweight subcomponents.
peer.setEventMask(eventMask);
- /* We do not invalidate here, but rather leave that job up to
- the peer. For efficiency, the peer can choose not to
- invalidate if it is happy with the current dimensions,
- etc. */
- if (dropTarget != null)
- dropTarget.addNotify(peer);
-
- // Notify hierarchy listeners.
- long flags = HierarchyEvent.DISPLAYABILITY_CHANGED;
- if (isHierarchyVisible())
- flags |= HierarchyEvent.SHOWING_CHANGED;
- fireHierarchyEvent(HierarchyEvent.HIERARCHY_CHANGED, this, parent,
- flags);
+
+ // We used to leave the invalidate() to the peer. However, I put it
+ // back here for 2 reasons: 1) The RI does call invalidate() from
+ // addNotify(); 2) The peer shouldn't be bother with validation too
+ // much.
+ invalidate();
+
+ if (dropTarget != null)
+ dropTarget.addNotify(peer);
+
+ // Fetch the peerFont for later installation in validate().
+ peerFont = getFont();
+
+ // Notify hierarchy listeners.
+ long flags = HierarchyEvent.DISPLAYABILITY_CHANGED;
+ if (isHierarchyVisible())
+ flags |= HierarchyEvent.SHOWING_CHANGED;
+ fireHierarchyEvent(HierarchyEvent.HIERARCHY_CHANGED, this, parent,
+ flags);
}
}
/**
* Called to inform this component is has been removed from its
* container. Its native peer - if any - is destroyed at this time.
* This method is called automatically by the AWT system and should
* not be called by user level code.
*
* @see #isDisplayable()
* @see #addNotify()
*/
public void removeNotify()
{
// We need to lock the tree here to avoid races and inconsistencies.
synchronized (getTreeLock())
{
// We null our peer field before disposing of it, such that if we're
// not the event dispatch thread and the dispatch thread is awoken by
// the dispose call, there will be no race checking the peer's null
// status.
ComponentPeer tmp = peer;
peer = null;
+ peerFont = null;
if (tmp != null)
{
tmp.hide();
tmp.dispose();
}
// Notify hierarchy listeners.
long flags = HierarchyEvent.DISPLAYABILITY_CHANGED;
if (isHierarchyVisible())
flags |= HierarchyEvent.SHOWING_CHANGED;
fireHierarchyEvent(HierarchyEvent.HIERARCHY_CHANGED, this, parent,
flags);
}
}
@@ -5562,31 +5640,31 @@
case KeyEvent.VK_RIGHT:
case KeyEvent.VK_KP_RIGHT:
oldKey = Event.RIGHT;
break;
case KeyEvent.VK_SCROLL_LOCK:
oldKey = Event.SCROLL_LOCK;
break;
case KeyEvent.VK_TAB:
oldKey = Event.TAB;
break;
case KeyEvent.VK_UP:
case KeyEvent.VK_KP_UP:
oldKey = Event.UP;
break;
default:
- oldKey = (int) ((KeyEvent) e).getKeyChar();
+ oldKey = ((KeyEvent) e).getKeyChar();
}
translated = new Event (target, when, oldID,
0, 0, oldKey, oldMods);
}
}
else if (e instanceof AdjustmentEvent)
{
AdjustmentEvent ae = (AdjustmentEvent) e;
int type = ae.getAdjustmentType();
int oldType;
if (type == AdjustmentEvent.BLOCK_DECREMENT)
oldType = Event.SCROLL_PAGE_UP;
else if (type == AdjustmentEvent.BLOCK_INCREMENT)
oldType = Event.SCROLL_PAGE_DOWN;
Index: java/awt/Container.java
===================================================================
RCS file: /cvsroot/classpath/classpath/java/awt/Container.java,v
retrieving revision 1.105
diff -u -1 -5 -r1.105 Container.java
--- java/awt/Container.java 18 Sep 2006 13:22:58 -0000 1.105
+++ java/awt/Container.java 20 Sep 2006 09:11:06 -0000
@@ -27,31 +27,30 @@
As a special exception, the copyright holders of this library give you
permission to link this library with independent modules to produce an
executable, regardless of the license terms of these independent
modules, and to copy and distribute the resulting executable under
terms of your choice, provided that you also meet, for each linked
independent module, the terms and conditions of the license of that
module. An independent module is a module which is not derived from
or based on this library. If you modify this library, you may extend
this exception to your version of the library, but you are not
obligated to do so. If you do not wish to do so, delete this
exception statement from your version. */
package java.awt;
-import java.awt.event.ComponentListener;
import java.awt.event.ContainerEvent;
import java.awt.event.ContainerListener;
import java.awt.event.HierarchyEvent;
import java.awt.event.KeyEvent;
import java.awt.peer.ComponentPeer;
import java.awt.peer.ContainerPeer;
import java.awt.peer.LightweightPeer;
import java.beans.PropertyChangeListener;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.io.Serializable;
import java.util.Collections;
@@ -599,48 +598,44 @@
*/
public void validate()
{
synchronized (getTreeLock ())
{
if (! isValid() && peer != null)
{
validateTree();
}
}
}
/**
* Recursively invalidates the container tree.
*/
- void invalidateTree()
+ private final void invalidateTree()
{
synchronized (getTreeLock())
{
- super.invalidate(); // Clean cached layout state.
for (int i = 0; i < ncomponents; i++)
{
Component comp = component[i];
- comp.invalidate();
if (comp instanceof Container)
((Container) comp).invalidateTree();
+ else if (comp.valid)
+ comp.invalidate();
}
-
- if (layoutMgr != null && layoutMgr instanceof LayoutManager2)
- {
- LayoutManager2 lm2 = (LayoutManager2) layoutMgr;
- lm2.invalidateLayout(this);
- }
+ if (valid)
+ invalidate();
}
}
/**
* Recursively validates the container tree, recomputing any invalid
* layouts.
*/
protected void validateTree()
{
if (valid)
return;
ContainerPeer cPeer = null;
if (peer instanceof ContainerPeer)
{
@@ -667,37 +662,35 @@
if (peer instanceof ContainerPeer)
{
cPeer = (ContainerPeer) peer;
cPeer.endValidate();
}
/* children will call invalidate() when they are layed out. It
is therefore important that valid is not set to true
until after the children have been layed out. */
valid = true;
}
public void setFont(Font f)
{
- if( (f != null && (font == null || !font.equals(f)))
- || f == null)
+ Font oldFont = getFont();
+ super.setFont(f);
+ Font newFont = getFont();
+ if (newFont != oldFont && (oldFont == null || ! oldFont.equals(newFont)))
{
- super.setFont(f);
- // FIXME: Although it might make more sense to invalidate only
- // those children whose font == null, Sun invalidates all children.
- // So we'll do the same.
invalidateTree();
}
}
/**
* Returns the preferred size of this container.
*
* @return The preferred size of this container.
*/
public Dimension getPreferredSize()
{
return preferredSize ();
}
/**
@@ -1864,30 +1857,31 @@
* should be performed.
*
* @param comp The child component that should be visited.
*/
private void visitChild(Graphics gfx, GfxVisitor visitor,
Component comp)
{
Rectangle bounds = comp.getBounds();
if(!gfx.hitClip(bounds.x,bounds.y, bounds.width, bounds.height))
return;
Graphics g2 = gfx.create(bounds.x, bounds.y, bounds.width,
bounds.height);
try
{
+ g2.setFont(comp.getFont());
visitor.visit(comp, g2);
}
finally
{
g2.dispose();
}
}
void dispatchEventImpl(AWTEvent e)
{
boolean dispatched =
LightweightDispatcher.getInstance().dispatchEvent(e);
if (! dispatched)
{
if ((e.id <= ContainerEvent.CONTAINER_LAST
Index: java/awt/Frame.java
===================================================================
RCS file: /cvsroot/classpath/classpath/java/awt/Frame.java,v
retrieving revision 1.38
diff -u -1 -5 -r1.38 Frame.java
--- java/awt/Frame.java 7 Apr 2006 18:33:38 -0000 1.38
+++ java/awt/Frame.java 20 Sep 2006 09:11:06 -0000
@@ -328,37 +328,40 @@
* @param menuBar the new menu bar for this frame
*/
public synchronized void setMenuBar(MenuBar menuBar)
{
if (this.menuBar != null)
remove(this.menuBar);
this.menuBar = menuBar;
if (menuBar != null)
{
MenuContainer parent = menuBar.getParent();
if (parent != null)
parent.remove(menuBar);
menuBar.setParent(this);
- if (peer != null)
- {
- if (menuBar != null)
- menuBar.addNotify();
- invalidateTree();
- ((FramePeer) peer).setMenuBar(menuBar);
- }
+ // Create local copy for thread safety.
+ FramePeer p = (FramePeer) peer;
+ if (p != null)
+ {
+ if (menuBar != null)
+ menuBar.addNotify();
+ if (valid)
+ invalidate();
+ p.setMenuBar(menuBar);
+ }
}
}
/**
* Tests whether or not this frame is resizable. This will be
* <code>true</code> by default.
*
* @return <code>true</code> if this frame is resizable, <code>false</code>
* otherwise
*/
public boolean isResizable()
{
return resizable;
}