Index: javax/swing/JComponent.java
===================================================================
RCS file: /cvsroot/classpath/classpath/javax/swing/JComponent.java,v
retrieving revision 1.68
diff -u -r1.68 JComponent.java
--- javax/swing/JComponent.java 12 Oct 2005 12:41:27 -0000 1.68
+++ javax/swing/JComponent.java 14 Oct 2005 20:54:56 -0000
@@ -258,7 +258,6 @@
*
* @see #setDoubleBuffered
* @see #isDoubleBuffered
- * @see #paintLock
* @see #paint
*/
boolean doubleBuffered = true;
@@ -387,17 +386,6 @@
*/
private static transient Rectangle rectCache;
- /**
- * A lock held during recursive painting; this is used to serialize
- * access to the double buffer, and also to select the "top level"
- * object which should acquire the double buffer in a given widget
- * tree (which may have multiple double buffered children).
- *
- * @see #doubleBuffered
- * @see #paint
- */
- private static final Object paintLock = new Object();
-
/**
* The default locale of the component.
*
@@ -441,6 +429,13 @@
public static final int WHEN_IN_FOCUSED_WINDOW = 2;
/**
+ * Indicates if this component is completely dirty or not. This is used
+ * by the RepaintManager's
+ * address@hidden RepaintManager#isCompletelyDirty(JComponent)} method.
+ */
+ boolean isCompletelyDirty = false;
+
+ /**
* Creates a new JComponent
instance.
*/
public JComponent()
@@ -874,9 +869,13 @@
*/
public void setBorder(Border newBorder)
{
- Border oldBorder = border;
+ Border oldBorder = getBorder();
+ if (oldBorder == newBorder)
+ return;
+
border = newBorder;
firePropertyChange("border", oldBorder, newBorder);
+ repaint();
}
/**
@@ -1482,6 +1481,10 @@
paintComponent(g);
paintBorder(g);
paintChildren(g);
+ Rectangle clip = g.getClipBounds();
+ if (clip.x == 0 && clip.y == 0 && clip.width == getWidth()
+ && clip.height == getHeight())
+ RepaintManager.currentManager(this).markCompletelyClean(this);
}
}
@@ -1616,11 +1619,12 @@
public void paintImmediately(Rectangle r)
{
// Try to find a root pane for this component.
- Component root = SwingUtilities.getRootPane(this);
- // If no root pane can be found, then try to find the Window that contains
- // this component.
+ //Component root = findPaintRoot(r);
+ Component root = findPaintRoot(r);
+ // If no paint root is found, then this component is completely overlapped
+ // by another component and we don't need repainting.
if (root == null)
- root = SwingUtilities.getRoot(this);
+ return;
if (root == null || !root.isShowing())
return;
@@ -1646,6 +1650,7 @@
else
paintSimple(g);
g.dispose();
+ rm.markCompletelyClean(this);
}
/**
@@ -1662,20 +1667,17 @@
RepaintManager rm = RepaintManager.currentManager(this);
// Paint on the offscreen buffer.
- synchronized (paintLock)
- {
- Image buffer = rm.getOffscreenBuffer(this, getWidth(), getHeight());
- Graphics g2 = buffer.getGraphics();
- g2 = getComponentGraphics(g2);
- g2.setClip(r.x, r.y, r.width, r.height);
- isPaintingDoubleBuffered = true;
- paint(g2);
- isPaintingDoubleBuffered = false;
- g2.dispose();
-
- // Paint the buffer contents on screen.
- g.drawImage(buffer, 0, 0, this);
- }
+ Image buffer = rm.getOffscreenBuffer(this, getWidth(), getHeight());
+ Graphics g2 = buffer.getGraphics();
+ g2 = getComponentGraphics(g2);
+ g2.setClip(r.x, r.y, r.width, r.height);
+ isPaintingDoubleBuffered = true;
+ paint(g2);
+ isPaintingDoubleBuffered = false;
+ g2.dispose();
+
+ // Paint the buffer contents on screen.
+ g.drawImage(buffer, 0, 0, this);
}
/**
@@ -2188,9 +2190,11 @@
*/
public void setEnabled(boolean enable)
{
- boolean oldEnabled = isEnabled();
+ if (enable == isEnabled())
+ return;
super.setEnabled(enable);
- firePropertyChange("enabled", oldEnabled, enable);
+ firePropertyChange("enabled", !enable, enable);
+ repaint();
}
/**
@@ -2200,14 +2204,11 @@
*/
public void setFont(Font f)
{
- if (f == null && getFont() == null)
+ if (f == getFont())
return;
-
- if (f == null || !f.equals(getFont()))
- {
- super.setFont(f);
- revalidate();
- }
+ super.setFont(f);
+ revalidate();
+ repaint();
}
/**
@@ -2217,7 +2218,10 @@
*/
public void setBackground(Color bg)
{
+ if (bg == getBackground())
+ return;
super.setBackground(bg);
+ repaint();
}
/**
@@ -2227,7 +2231,10 @@
*/
public void setForeground(Color fg)
{
+ if (fg == getForeground())
+ return;
super.setForeground(fg);
+ repaint();
}
/**
@@ -2403,6 +2410,7 @@
firePropertyChange("UI", oldUI, newUI);
revalidate();
+ repaint();
}
/**
@@ -2935,5 +2943,136 @@
JComponent jc = (JComponent) children[i];
jc.fireAncestorEvent(ancestor, id);
}
+ }
+
+ /**
+ * Finds a suitable paint root for painting this component. This method first
+ * checks if this component is overlapped using
+ * address@hidden #findOverlapFreeParent(Rectangle)}. The returned paint root is then
+ * feeded to address@hidden #findOpaqueParent(Component)} to find the nearest opaque
+ * component for this paint root. If no paint is necessary, then we return
+ * null
.
+ *
+ * @param c the clip of this component
+ *
+ * @return the paint root or null
if no painting is necessary
+ */
+ private Component findPaintRoot(Rectangle c)
+ {
+ Component p = findOverlapFreeParent(c);
+ if (p == null)
+ return null;
+ Component root = findOpaqueParent(p);
+ return root;
+ }
+
+ /**
+ * Scans the containment hierarchy upwards for components that overlap the
+ * this component in the specified clip. This method returns
+ * this
, if no component overlaps this component. It returns
+ * null
if another component completely covers this component
+ * in the specified clip (no repaint necessary). If another component partly
+ * overlaps this component in the specified clip, then the parent of this
+ * component is returned (this is the component that must be used as repaint
+ * root). For efficient lookup, the method
+ * address@hidden #isOptimizedDrawingEnabled()} is used.
+ *
+ * @param clip the clip of this component
+ *
+ * @return the paint root, or null
if no paint is necessary
+ */
+ private Component findOverlapFreeParent(Rectangle clip)
+ {
+ Rectangle currentClip = clip;
+ Component found = this;
+ Container parent = this;
+ while (parent != null)
+ {
+ Container newParent = parent.getParent();
+ if (newParent == null)
+ break;
+ // If the parent is optimizedDrawingEnabled, then its children are
+ // tiled and cannot have an overlapping child. Go directly to next
+ // parent.
+ if (newParent instanceof JComponent
+ && ((JComponent) newParent).isOptimizedDrawingEnabled())
+ {
+ parent = newParent;
+ continue;
+ }
+
+ // First we must check if the new parent itself somehow clips the
+ // target rectangle. This can happen in JViewports.
+ Rectangle parRect = new Rectangle(0, 0, newParent.getWidth(),
+ newParent.getHeight());
+ Rectangle target = SwingUtilities.convertRectangle(found,
+ currentClip,
+ newParent);
+ if (target.contains(parRect) || target.intersects(parRect))
+ {
+ found = newParent;
+ currentClip = target;
+ parent = newParent;
+ continue;
+ }
+
+ // Otherwise we must check if one of the children of this parent
+ // overlaps with the current component.
+ Component[] children = newParent.getComponents();
+ // This flag is used to skip components that are 'below' the component
+ // in question.
+ boolean skip = true;
+ for (int i = children.length - 1; i >= 0; i--)
+ {
+ if (children[i] == parent)
+ skip = false;
+ if (skip)
+ continue;
+ Component c = children[i];
+ Rectangle compBounds = c.getBounds();
+ // If the component completely overlaps the clip in question, we
+ // don't need to repaint. Return null.
+ if (compBounds.contains(target))
+ return null;
+ if (compBounds.intersects(target))
+ {
+ // We found a parent whose children overlap with our current
+ // component. Make this the current component.
+ found = newParent;
+ currentClip = target;
+ break;
+ }
+ }
+ parent = newParent;
+ }
+ return found;
+ }
+
+ /**
+ * Finds the nearest component to c
(upwards in the containment
+ * hierarchy), that is opaque. If c
itself is opaque,
+ * this returns c
itself.
+ *
+ * @param c the start component for the search
+ * @return the nearest component to c
(upwards in the containment
+ * hierarchy), that is opaque; If c
itself is opaque,
+ * this returns c
itself
+ */
+ private Component findOpaqueParent(Component c)
+ {
+ Component found = c;
+ while (true)
+ {
+ if ((found instanceof JComponent) && ((JComponent) found).isOpaque())
+ break;
+ else if (!(found instanceof JComponent))
+ break;
+ Container p = found.getParent();
+ if (p == null)
+ break;
+ else
+ found = p;
+ }
+ return found;
}
}
Index: javax/swing/RepaintManager.java
===================================================================
RCS file: /cvsroot/classpath/classpath/javax/swing/RepaintManager.java,v
retrieving revision 1.14
diff -u -r1.14 RepaintManager.java
--- javax/swing/RepaintManager.java 13 Sep 2005 09:17:21 -0000 1.14
+++ javax/swing/RepaintManager.java 14 Oct 2005 20:54:57 -0000
@@ -43,6 +43,9 @@
import java.awt.Image;
import java.awt.Rectangle;
import java.awt.image.VolatileImage;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Hashtable;
@@ -111,6 +114,62 @@
}
+ /**
+ * Compares two components using their depths in the component hierarchy.
+ * A component with a lesser depth (higher level components) are sorted
+ * before components with a deeper depth (low level components). This is used
+ * to order paint requests, so that the higher level components are painted
+ * before the low level components get painted.
+ *
+ * @author Roman Kennke (address@hidden)
+ */
+ private class ComponentComparator implements Comparator
+ {
+
+ /**
+ * Compares two components.
+ *
+ * @param o1 the first component
+ * @param o2 the second component
+ *
+ * @return a negative integer, if o1
is higher in the
+ * hierarchy than o2
, zero, if both are at the same
+ * level and a positive integer, if o1
is deeper in
+ * the hierarchy than o2
+ */
+ public int compare(Object o1, Object o2)
+ {
+ if (o1 instanceof JComponent && o2 instanceof JComponent)
+ {
+ JComponent c1 = (JComponent) o1;
+ JComponent c2 = (JComponent) o2;
+ return getDepth(c1) - getDepth(c2);
+ }
+ else
+ throw new ClassCastException("This comparator can only be used with "
+ + "JComponents");
+ }
+
+ /**
+ * Computes the depth for a given JComponent.
+ *
+ * @param c the component to compute the depth for
+ *
+ * @return the depth of the component
+ */
+ private int getDepth(JComponent c)
+ {
+ Component comp = c;
+ int depth = 0;
+ while (comp != null)
+ {
+ comp = comp.getParent();
+ depth++;
+ }
+ return depth;
+ }
+ }
+
/**
* A table storing the dirty regions of components. The keys of this
* table are components, the values are rectangles. Each component maps
@@ -123,7 +182,20 @@
* @see #markCompletelyClean
* @see #markCompletelyDirty
*/
- Hashtable dirtyComponents;
+ HashMap dirtyComponents;
+
+ HashMap workDirtyComponents;
+
+ /**
+ * Stores the order in which the components get repainted.
+ */
+ ArrayList repaintOrder;
+ ArrayList workRepaintOrder;
+
+ /**
+ * The comparator used for ordered inserting into the repaintOrder list.
+ */
+ Comparator comparator;
/**
* A single, shared instance of the helper class. Any methods which mark
@@ -146,7 +218,8 @@
* @see #removeInvalidComponent
* @see #validateInvalidComponents
*/
- Vector invalidComponents;
+ ArrayList invalidComponents;
+ ArrayList workInvalidComponents;
/**
* Whether or not double buffering is enabled on this repaint
@@ -194,8 +267,12 @@
*/
public RepaintManager()
{
- dirtyComponents = new Hashtable();
- invalidComponents = new Vector();
+ dirtyComponents = new HashMap();
+ workDirtyComponents = new HashMap();
+ repaintOrder = new ArrayList();
+ workRepaintOrder = new ArrayList();
+ invalidComponents = new ArrayList();
+ workInvalidComponents = new ArrayList();
repaintWorker = new RepaintWorker();
doubleBufferMaximumSize = new Dimension(2000,2000);
doubleBufferingEnabled = true;
@@ -291,7 +368,7 @@
*/
public synchronized void removeInvalidComponent(JComponent component)
{
- invalidComponents.removeElement(component);
+ invalidComponents.remove(component);
}
/**
@@ -315,12 +392,13 @@
public synchronized void addDirtyRegion(JComponent component, int x, int y,
int w, int h)
{
- if (w == 0 || h == 0)
+ if (w == 0 || h == 0 || !component.isShowing())
return;
-
Rectangle r = new Rectangle(x, y, w, h);
if (dirtyComponents.containsKey(component))
r = r.union((Rectangle)dirtyComponents.get(component));
+ else
+ insertInRepaintOrder(component);
dirtyComponents.put(component, r);
if (! repaintWorker.isLive())
{
@@ -328,7 +406,23 @@
SwingUtilities.invokeLater(repaintWorker);
}
}
-
+
+ /**
+ * Inserts a component into the repaintOrder list in an ordered fashion,
+ * using a binary search.
+ *
+ * @param c the component to be inserted
+ */
+ private void insertInRepaintOrder(JComponent c)
+ {
+ if (comparator == null)
+ comparator = new ComponentComparator();
+ int insertIndex = Collections.binarySearch(repaintOrder, c, comparator);
+ if (insertIndex < 0)
+ insertIndex = -(insertIndex + 1);
+ repaintOrder.add(insertIndex, c);
+ }
+
/**
* Get the dirty region associated with a component, or null
* if the component has no dirty region.
@@ -345,7 +439,10 @@
*/
public Rectangle getDirtyRegion(JComponent component)
{
- return (Rectangle) dirtyComponents.get(component);
+ Rectangle dirty = (Rectangle) dirtyComponents.get(component);
+ if (dirty == null)
+ dirty = new Rectangle();
+ return dirty;
}
/**
@@ -363,6 +460,7 @@
{
Rectangle r = component.getBounds();
addDirtyRegion(component, r.x, r.y, r.width, r.height);
+ component.isCompletelyDirty = true;
}
/**
@@ -378,7 +476,11 @@
*/
public void markCompletelyClean(JComponent component)
{
- dirtyComponents.remove(component);
+ synchronized (this)
+ {
+ dirtyComponents.remove(component);
+ }
+ component.isCompletelyDirty = false;
}
/**
@@ -397,13 +499,9 @@
*/
public boolean isCompletelyDirty(JComponent component)
{
- Rectangle dirty = (Rectangle) dirtyComponents.get(component);
- if (dirty == null)
+ if (! dirtyComponents.containsKey(component))
return false;
- Rectangle r = component.getBounds();
- if (r == null)
- return true;
- return dirty.contains(r);
+ return component.isCompletelyDirty;
}
/**
@@ -412,58 +510,56 @@
*/
public void validateInvalidComponents()
{
- for (Enumeration e = invalidComponents.elements(); e.hasMoreElements(); )
+ // In order to keep the blocking of application threads minimal, we switch
+ // the invalidComponents field with the workInvalidComponents field and
+ // work wíth the workInvalidComponents field.
+ synchronized(this)
+ {
+ ArrayList swap = invalidComponents;
+ invalidComponents = workInvalidComponents;
+ workInvalidComponents = swap;
+ }
+ for (Iterator i = workInvalidComponents.iterator(); i.hasNext(); )
{
- JComponent comp = (JComponent) e.nextElement();
+ JComponent comp = (JComponent) i.next();
if (! (comp.isVisible() && comp.isShowing()))
continue;
comp.validate();
}
- invalidComponents.clear();
+ workInvalidComponents.clear();
}
/**
* Repaint all regions of all components which have been marked dirty in
* the address@hidden #dirtyComponents} table.
*/
- public void paintDirtyRegions()
+ public synchronized void paintDirtyRegions()
{
- // step 1: pull out roots and calculate spanning damage
-
- HashMap roots = new HashMap();
- for (Enumeration e = dirtyComponents.keys(); e.hasMoreElements(); )
+ // In order to keep the blocking of application threads minimal, we switch
+ // the dirtyComponents field with the workdirtyComponents field and the
+ // repaintOrder field with the workRepaintOrder field and work with the
+ // work* fields.
+ synchronized(this)
+ {
+ ArrayList swap = workRepaintOrder;
+ workRepaintOrder = repaintOrder;
+ repaintOrder = swap;
+ HashMap swap2 = workDirtyComponents;
+ workDirtyComponents = dirtyComponents;
+ dirtyComponents = swap2;
+ }
+ for (Iterator i = workRepaintOrder.iterator(); i.hasNext();)
{
- JComponent comp = (JComponent) e.nextElement();
- if (! (comp.isVisible() && comp.isShowing()))
+ JComponent comp = (JComponent) i.next();
+ // If a component is marked completely clean in the meantime, then skip
+ // it.
+ Rectangle damaged = (Rectangle) workDirtyComponents.get(comp);
+ if (damaged == null || damaged.isEmpty())
continue;
- Rectangle damaged = getDirtyRegion(comp);
- if (damaged.width == 0 || damaged.height == 0)
- continue;
- JRootPane root = comp.getRootPane();
- // If the component has no root, no repainting will occur.
- if (root == null)
- continue;
- Rectangle rootDamage = SwingUtilities.convertRectangle(comp, damaged, root);
- if (! roots.containsKey(root))
- {
- roots.put(root, rootDamage);
- }
- else
- {
- roots.put(root, ((Rectangle)roots.get(root)).union(rootDamage));
- }
- }
- dirtyComponents.clear();
-
- // step 2: paint those roots
- Iterator i = roots.entrySet().iterator();
- while(i.hasNext())
- {
- Map.Entry ent = (Map.Entry) i.next();
- JRootPane root = (JRootPane) ent.getKey();
- Rectangle rect = (Rectangle) ent.getValue();
- root.paintImmediately(rect);
+ comp.paintImmediately(damaged);
}
+ workRepaintOrder.clear();
+ workDirtyComponents.clear();
}
/**
Index: javax/swing/JLayeredPane.java
===================================================================
RCS file: /cvsroot/classpath/classpath/javax/swing/JLayeredPane.java,v
retrieving revision 1.28
diff -u -r1.28 JLayeredPane.java
--- javax/swing/JLayeredPane.java 20 Sep 2005 19:16:03 -0000 1.28
+++ javax/swing/JLayeredPane.java 14 Oct 2005 20:54:57 -0000
@@ -272,7 +272,7 @@
* Increments the recorded size of a given layer.
*
* @param layer the layer number to increment.
- * @see #incrLayer()
+ * @see #incrLayer
*/
private void incrLayer(Integer layer)
{
@@ -286,7 +286,7 @@
* Decrements the recorded size of a given layer.
*
* @param layer the layer number to decrement.
- * @see #decrLayer()
+ * @see #incrLayer
*/
private void decrLayer(Integer layer)
{
@@ -685,5 +685,16 @@
g.setColor(getBackground());
g.fillRect(0, 0, getWidth(), getHeight());
super.paint(g);
+ }
+
+ /**
+ * Overridden to return false
, since JLayeredPane
+ * cannot guarantee that its children don't overlap.
+ *
+ * @return false
+ */
+ public boolean isOptimizedDrawingEnabled()
+ {
+ return false;
}
}