diff options
author | Roman Kennke <roman@kennke.org> | 2006-08-23 22:03:22 +0000 |
---|---|---|
committer | Roman Kennke <roman@kennke.org> | 2006-08-23 22:03:22 +0000 |
commit | 16cbf77124e2ee77b17ff95816777f805c61d511 (patch) | |
tree | 32b5fb0384cd12a08a2b9ad9ffe5b4b3f42c687b | |
parent | 5586796c5ca3abc7f1248682f61700f983a8601b (diff) | |
download | classpath-16cbf77124e2ee77b17ff95816777f805c61d511.tar.gz |
2006-08-23 Roman Kennke <kennke@aicas.com>
* javax/swing/JComponent.java
(isRepainting): Made package private.
(paintChild): New field.
(findOpaqueParent): Removed method. This is now in
paintImmediately().
(findOverlapFreeParent): Removed method. This is now
in paintImmediately2().
(findPaintRoot): Removed method. This is now
in paintImmediately2().
(isCompletelyObscured): Changed to take rectangle as single
ints as argument.
(isPaintingDoubleBuffered): Removed method. This is now
in paintImmediately2().
(isPartiallyObscured): New helper method.
(onTop): New helper method for optimization.
(paintChildren): Paint only to specific child when
requested like this from paintImmediately2().
(paintDoubleBuffered): Changed to take rectangle as single int
arguments.
(paintImmediately2): Changed to take rectangle as single int
arguments. Optimized determination of paint root.
(paintImmediately(Rectangle)): Change to delegate to
paintImmediately(int,int,int,int).
(paintImmediately(int,int,int,int)): Look for opaque ancestor
and start painting there.
(paint): Call paintDoubleBuffered() with int arguments. Only
paint component, when not completely occupied by opaque child.
(processKeyBinding): Removed unnecessary cast.
(isOccupiedByChild): New helper method.
* javax/swing/RepaintManager.java
(repaintUnderway): Removed obsolete field.
(commitRequests): Removed obsolete field.
(RepaintManager): Removed initialization of obsolete fields.
(addDirtyRegion): Removed unused statement.
(commitBuffer): Changed to take plain ints as argument.
(compileRepaintRoots): Optimized to avoid use of Rectangle.
Compute offsets in place, rather than using SwingUtilities.
(paintDirtyRegions): Removed unused field.
* javax/swing/JMenuItem.java
(onTop): Return true when not descendant of JInternalFrame.
* javax/swing/JPopupMenu.java
(onTop): Return true.
* javax/swing/JToolTip.java
(onTop): Return true.
* javax/swing/JViewport.java
(paintImmediately2): Change signature to match the
corresponding JComponent method.
-rw-r--r-- | ChangeLog | 50 | ||||
-rw-r--r-- | javax/swing/JComponent.java | 469 | ||||
-rw-r--r-- | javax/swing/JMenuItem.java | 16 | ||||
-rw-r--r-- | javax/swing/JPopupMenu.java | 14 | ||||
-rw-r--r-- | javax/swing/JToolTip.java | 14 | ||||
-rw-r--r-- | javax/swing/JViewport.java | 4 | ||||
-rw-r--r-- | javax/swing/RepaintManager.java | 91 |
7 files changed, 447 insertions, 211 deletions
@@ -1,3 +1,53 @@ +2006-08-23 Roman Kennke <kennke@aicas.com> + + * javax/swing/JComponent.java + (isRepainting): Made package private. + (paintChild): New field. + (findOpaqueParent): Removed method. This is now in + paintImmediately(). + (findOverlapFreeParent): Removed method. This is now + in paintImmediately2(). + (findPaintRoot): Removed method. This is now + in paintImmediately2(). + (isCompletelyObscured): Changed to take rectangle as single + ints as argument. + (isPaintingDoubleBuffered): Removed method. This is now + in paintImmediately2(). + (isPartiallyObscured): New helper method. + (onTop): New helper method for optimization. + (paintChildren): Paint only to specific child when + requested like this from paintImmediately2(). + (paintDoubleBuffered): Changed to take rectangle as single int + arguments. + (paintImmediately2): Changed to take rectangle as single int + arguments. Optimized determination of paint root. + (paintImmediately(Rectangle)): Change to delegate to + paintImmediately(int,int,int,int). + (paintImmediately(int,int,int,int)): Look for opaque ancestor + and start painting there. + (paint): Call paintDoubleBuffered() with int arguments. Only + paint component, when not completely occupied by opaque child. + (processKeyBinding): Removed unnecessary cast. + (isOccupiedByChild): New helper method. + * javax/swing/RepaintManager.java + (repaintUnderway): Removed obsolete field. + (commitRequests): Removed obsolete field. + (RepaintManager): Removed initialization of obsolete fields. + (addDirtyRegion): Removed unused statement. + (commitBuffer): Changed to take plain ints as argument. + (compileRepaintRoots): Optimized to avoid use of Rectangle. + Compute offsets in place, rather than using SwingUtilities. + (paintDirtyRegions): Removed unused field. + * javax/swing/JMenuItem.java + (onTop): Return true when not descendant of JInternalFrame. + * javax/swing/JPopupMenu.java + (onTop): Return true. + * javax/swing/JToolTip.java + (onTop): Return true. + * javax/swing/JViewport.java + (paintImmediately2): Change signature to match the + corresponding JComponent method. + 2006-08-23 Tania Bento <tbento@redhat.com> * java/awt/Color.java diff --git a/javax/swing/JComponent.java b/javax/swing/JComponent.java index fc9353e06..91bb5428f 100644 --- a/javax/swing/JComponent.java +++ b/javax/swing/JComponent.java @@ -69,6 +69,7 @@ import java.beans.PropertyVetoException; import java.beans.VetoableChangeListener; import java.beans.VetoableChangeSupport; import java.io.Serializable; +import java.util.ArrayList; import java.util.EventListener; import java.util.Hashtable; import java.util.Locale; @@ -666,7 +667,7 @@ public abstract class JComponent extends Container implements Serializable * Indicates whether we are calling paintDoubleBuffered() from * paintImmadiately (RepaintManager) or from paint() (AWT refresh). */ - static private boolean isRepainting = false; + static boolean isRepainting = false; /** * Listeners for events other than {@link PropertyChangeEvent} are @@ -762,6 +763,13 @@ public abstract class JComponent extends Container implements Serializable */ public static final int WHEN_IN_FOCUSED_WINDOW = 2; + + /** + * Used to optimize painting. This is set in paintImmediately2() to specify + * the exact component path to be painted by paintChildren. + */ + Component paintChild; + /** * Indicates if the opaque property has been set by a client program or by * the UI. @@ -1790,7 +1798,7 @@ public abstract class JComponent extends Container implements Serializable && rm.isDoubleBufferingEnabled()) { Rectangle clip = g.getClipBounds(); - paintDoubleBuffered(clip); + paintDoubleBuffered(clip.x, clip.y, clip.width, clip.height); } else { @@ -1805,8 +1813,22 @@ public abstract class JComponent extends Container implements Serializable dragBuffer = null; } - if (g.getClip() == null) - g.setClip(0, 0, getWidth(), getHeight()); + Rectangle clip = g.getClipBounds(); + int clipX, clipY, clipW, clipH; + if (clip == null) + { + clipX = 0; + clipY = 0; + clipW = getWidth(); + clipH = getHeight(); + } + else + { + clipX = clip.x; + clipY = clip.y; + clipW = clip.width; + clipH = clip.height; + } if (dragBuffer != null && dragBufferInitialized) { g.drawImage(dragBuffer, 0, 0, this); @@ -1814,14 +1836,51 @@ public abstract class JComponent extends Container implements Serializable else { Graphics g2 = getComponentGraphics(g); - paintComponent(g2); - paintBorder(g2); + if (! isOccupiedByChild(clipX, clipY, clipW, clipH)) + { + paintComponent(g2); + paintBorder(g2); + } paintChildren(g2); } } } /** + * Determines if a region of this component is completely occupied by + * an opaque child component, in which case we don't need to bother + * painting this component at all. + * + * @param x the area, x coordinate + * @param y the area, y coordinate + * @param w the area, width + * @param h the area, height + * + * @return <code>true</code> if the specified area is completely covered + * by a child component, <code>false</code> otherwise + */ + private boolean isOccupiedByChild(int x, int y, int w, int h) + { + boolean occupied = false; + int count = getComponentCount(); + for (int i = 0; i < count; i++) + { + Component child = getComponent(i); + int cx = child.getX(); + int cy = child.getY(); + int cw = child.getWidth(); + int ch = child.getHeight(); + if (child.isVisible() && x >= cx && x + w <= cx + cw && y >= cy + && y + h <= cy + ch) + { + occupied = child.isOpaque(); + break; + } + } + return occupied; + } + + /** * Initializes the drag buffer by creating a new image and painting this * component into it. */ @@ -1880,7 +1939,14 @@ public abstract class JComponent extends Container implements Serializable // Need to lock the tree to avoid problems with AWT and concurrency. synchronized (getTreeLock()) { - for (int i = getComponentCount() - 1; i >= 0; i--) + // Fast forward to the child to paint, if set by + // paintImmediately2() + int i = getComponentCount() - 1; + if (paintChild != null && paintChild.isOpaque()) + { + for (; i >= 0 && getComponent(i) != paintChild; i--); + } + for (; i >= 0; i--) { Component child = getComponent(i); if (child != null && child.isLightweight() @@ -1898,7 +1964,8 @@ public abstract class JComponent extends Container implements Serializable Rectangle clip = g.getClipBounds(); // A copy. SwingUtilities.computeIntersection(cx, cy, cw, ch, clip); - if (isCompletelyObscured(i, clip)) + if (isCompletelyObscured(i, clip.x, clip.y, + clip.width, clip.height)) continue; // Continues the for-loop. } Graphics cg = g.create(cx, cy, cw, ch); @@ -1924,12 +1991,15 @@ public abstract class JComponent extends Container implements Serializable * of its siblings. * * @param index the index of the child component - * @param rect the region to check + * @param x the region to check, x coordinate + * @param y the region to check, y coordinate + * @param w the region to check, width + * @param h the region to check, height * * @return <code>true</code> if the region is completely obscured by a * sibling, <code>false</code> otherwise */ - private boolean isCompletelyObscured(int index, Rectangle rect) + private boolean isCompletelyObscured(int index, int x, int y, int w, int h) { boolean obscured = false; for (int i = index - 1; i >= 0 && obscured == false; i--) @@ -1938,10 +2008,10 @@ public abstract class JComponent extends Container implements Serializable if (sib.isVisible()) { Rectangle sibRect = sib.getBounds(rectCache); - if (sib.isOpaque() && rect.x >= sibRect.x - && (rect.x + rect.width) <= (sibRect.x + sibRect.width) - && rect.y >= sibRect.y - && (rect.y + rect.height) <= (sibRect.y + sibRect.height)) + if (sib.isOpaque() && x >= sibRect.x + && (x + w) <= (sibRect.x + sibRect.width) + && y >= sibRect.y + && (y + h) <= (sibRect.y + sibRect.height)) { obscured = true; } @@ -1951,6 +2021,39 @@ public abstract class JComponent extends Container implements Serializable } /** + * Checks if a component/rectangle is partially obscured by one of its + * siblings. + * Note that this doesn't check for completely obscured, this is + * done by isCompletelyObscured() and should probably also be checked. + * + * @param i the component index from which to start searching + * @param x the x coordinate of the rectangle to check + * @param y the y coordinate of the rectangle to check + * @param w the width of the rectangle to check + * @param h the height of the rectangle to check + * + * @return <code>true</code> if the rectangle is partially obscured + */ + private boolean isPartiallyObscured(int i, int x, int y, int w, int h) + { + boolean obscured = false; + for (int j = i - 1; j >= 0 && ! obscured; j--) + { + Component sibl = getComponent(j); + if (sibl.isVisible()) + { + Rectangle rect = sibl.getBounds(rectCache); + if (!(x + w <= rect.x) + || (y + h <= rect.y) + || (x >= rect.x + rect.width) + || (y >= rect.y + rect.height)) + obscured = true; + } + } + return obscured; + } + + /** * Paint the component's body. This usually means calling {@link * ComponentUI#update} on the {@link #ui} property of the component, if * it is non-<code>null</code>. You may override this if you wish to @@ -1990,7 +2093,26 @@ public abstract class JComponent extends Container implements Serializable */ public void paintImmediately(int x, int y, int w, int h) { - paintImmediately(new Rectangle(x, y, w, h)); + // Find opaque parent and call paintImmediately2() on it. + if (isShowing()) + { + Component c = this; + Component p; + while (c != null && ! c.isOpaque()) + { + p = c.getParent(); + if (p != null) + { + x += c.getX(); + y += c.getY(); + c = p; + } + } + if (c instanceof JComponent) + ((JComponent) c).paintImmediately2(x, y, w, h); + else + c.repaint(x, y, w, h); + } } /** @@ -2013,60 +2135,177 @@ public abstract class JComponent extends Container implements Serializable */ public void paintImmediately(Rectangle r) { - // Try to find a root pane for 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.isShowing()) - return; - SwingUtilities.convertRectangleToAncestor(this, r, root); - if (root instanceof JComponent) - ((JComponent) root).paintImmediately2(r); - else - root.repaint(r.x, r.y, r.width, r.height); + paintImmediately(r.x, r.y, r.width, r.height); } /** * Performs the actual work of paintImmediatly on the repaint root. * - * @param r the area to be repainted + * @param x the area to be repainted, X coordinate + * @param y the area to be repainted, Y coordinate */ - void paintImmediately2(Rectangle r) + void paintImmediately2(int x, int y, int w, int h) { - isRepainting = true; + // Optimization for components that are always painted on top. + boolean onTop = onTop() && isOpaque(); + + // Fetch the RepaintManager. RepaintManager rm = RepaintManager.currentManager(this); - if (rm.isDoubleBufferingEnabled() && isPaintingDoubleBuffered()) - paintDoubleBuffered(r); - else - paintSimple(r); - isRepainting = false; + + // The painting clip; + int paintX = x; + int paintY = y; + int paintW = w; + int paintH = h; + + // If we should paint buffered or not. + boolean haveBuffer = false; + + // The component that is finally triggered for painting. + JComponent paintRoot = this; + + // Stores the component and all its parents. This will be used to limit + // the actually painted components in paintChildren by setting + // the field paintChild. + int pIndex = -1; + int pCount = 0; + ArrayList components = new ArrayList(); + + // Offset to subtract from the paintRoot rectangle when painting. + int offsX = 0; + int offsY = 0; + + // The current component and its child. + Component child; + Container c; + + // Find appropriate paint root. + for (c = this, child = null; + c != null && ! (c instanceof Window) && ! (c instanceof Applet); + child = c, c = c.getParent()) + { + JComponent jc = c instanceof JComponent ? (JComponent) c : null; + components.add(c); + if (! onTop && jc != null && ! jc.isOptimizedDrawingEnabled()) + { + // Check obscured state of the child. + // Generally, we have 3 cases here: + // 1. Not obscured. No need to paint from the parent. + // 2. Partially obscured. Paint from the parent. + // 3. Completely obscured. No need to paint anything. + if (c != this) + { + int count = c.getComponentCount(); + int i = 0; + for (; i < count && c.getComponent(i) != child; i++); + + if (jc.isCompletelyObscured(i, paintX, paintY, paintW, paintH)) + return; // No need to paint anything. + else if (jc.isPartiallyObscured(i, paintX, paintY, paintW, + paintH)) + { + // Paint from parent. + paintRoot = jc; + pIndex = pCount; + offsX = 0; + offsY = 0; + haveBuffer = false; + } + } + } + pCount++; + // Check if component is double buffered. + if (rm.isDoubleBufferingEnabled() && jc != null + && jc.isDoubleBuffered()) + { + haveBuffer = true; + } + + // Clip the paint region with the parent. + if (! onTop) + { + paintX = Math.max(0, paintX); + paintY = Math.max(0, paintY); + paintW = Math.min(c.getWidth(), paintW + paintX) - paintX; + paintH = Math.min(c.getHeight(), paintH + paintY) - paintY; + int dx = c.getX(); + int dy = c.getY(); + paintX += dx; + paintY += dy; + offsX += dx; + offsY += dy; + } + } + if (c != null && c.getPeer() != null && paintW > 0 && paintH > 0) + { + isRepainting = true; + paintX -= offsX; + paintY -= offsY; + + // Set the painting path so that paintChildren paints only what we + // want. + if (paintRoot != this) + { + for (int i = pIndex; i > 0; i--) + { + Component paintParent = (Component) components.get(i); + if (paintParent instanceof JComponent) + ((JComponent) paintParent).paintChild = + (Component) components.get(i - 1); + } + } + + // Actually trigger painting. + if (haveBuffer) + paintRoot.paintDoubleBuffered(paintX, paintY, paintW, + paintH); + else + { + Graphics g = paintRoot.getGraphics(); + try + { + g.setClip(paintX, paintY, paintW, paintH); + paintRoot.paint(g); + } + finally + { + g.dispose(); + } + } + + // Reset the painting path. + if (paintRoot != this) + { + for (int i = pIndex; i > 0; i--) + { + Component paintParent = (Component) components.get(i); + if (paintParent instanceof JComponent) + ((JComponent) paintParent).paintChild = null; + } + } + + isRepainting = false; + } } /** - * Returns true if we must paint double buffered, that is, when this - * component or any of it's ancestors are double buffered. + * Returns <code>true</code> if the component is guaranteed to be painted + * on top of others. This returns false by default and is overridden by + * components like JMenuItem, JPopupMenu and JToolTip to return true for + * added efficiency. * - * @return true if we must paint double buffered, that is, when this - * component or any of it's ancestors are double buffered + * @return <code>true</code> if the component is guaranteed to be painted + * on top of others */ - private boolean isPaintingDoubleBuffered() + boolean onTop() { - boolean doubleBuffered = isDoubleBuffered(); - Component parent = getParent(); - while (! doubleBuffered && parent != null) - { - doubleBuffered = parent instanceof JComponent - && ((JComponent) parent).isDoubleBuffered(); - parent = parent.getParent(); - } - return doubleBuffered; + return false; } /** * Performs double buffered repainting. */ - private void paintDoubleBuffered(Rectangle r) + private void paintDoubleBuffered(int x, int y, int w, int h) { RepaintManager rm = RepaintManager.currentManager(this); @@ -2083,7 +2322,7 @@ public abstract class JComponent extends Container implements Serializable //Rectangle targetClip = SwingUtilities.convertRectangle(this, r, root); Graphics g2 = buffer.getGraphics(); clipAndTranslateGraphics(root, this, g2); - g2.clipRect(r.x, r.y, r.width, r.height); + g2.clipRect(x, y, w, h); g2 = getComponentGraphics(g2); paintingDoubleBuffered = true; try @@ -2104,7 +2343,7 @@ public abstract class JComponent extends Container implements Serializable } // Paint the buffer contents on screen. - rm.commitBuffer(this, r); + rm.commitBuffer(this, x, y, w, h); } /** @@ -2531,7 +2770,7 @@ public abstract class JComponent extends Container implements Serializable if (cmd instanceof ActionListenerProxy) act = (Action) cmd; else - act = (Action) getActionMap().get(cmd); + act = getActionMap().get(cmd); } } if (act != null && act.isEnabled()) @@ -3476,130 +3715,6 @@ public abstract class JComponent extends Container implements Serializable jc.fireAncestorEvent(ancestor, id); } } - - /** - * Finds a suitable paint root for painting this component. This method first - * checks if this component is overlapped using - * {@link #findOverlapFreeParent(Rectangle)}. The returned paint root is then - * feeded to {@link #findOpaqueParent(Component)} to find the nearest opaque - * component for this paint root. If no paint is necessary, then we return - * <code>null</code>. - * - * @param c the clip of this component - * - * @return the paint root or <code>null</code> 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 - * <code>this</code>, if no component overlaps this component. It returns - * <code>null</code> 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 - * {@link #isOptimizedDrawingEnabled()} is used. - * - * @param clip the clip of this component - * - * @return the paint root, or <code>null</code> if no paint is necessary - */ - private Component findOverlapFreeParent(Rectangle clip) - { - Rectangle currentClip = clip; - Component found = this; - Container parent = this; - - while (parent != null && !(parent instanceof Window)) - { - Container newParent = parent.getParent(); - if (newParent == null || newParent instanceof Window) - 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; - } - - // If the parent is not optimizedDrawingEnabled, we must check if the - // parent or some neighbor overlaps the current clip. - - // This is the current clip converted to the parent's coordinate - // system. TODO: We can do this more efficiently by succesively - // cumulating the parent-child translations. - Rectangle target = SwingUtilities.convertRectangle(found, - currentClip, - newParent); - - // We have an overlap if either: - // - The new parent itself doesn't completely cover the clip - // (this can be the case with viewports). - // - If some higher-level (than the current) children of the new parent - // intersect the target rectangle. - Rectangle parentRect = SwingUtilities.getLocalBounds(newParent); - boolean haveOverlap = - ! SwingUtilities.isRectangleContainingRectangle(parentRect, target); - if (! haveOverlap) - { - Component child; - for (int i = 0; (child = newParent.getComponent(i)) != parent && !haveOverlap; i++) - { - Rectangle childRect = child.getBounds(); - haveOverlap = target.intersects(childRect); - } - } - if (haveOverlap) - { - found = newParent; - currentClip = target; - } - parent = newParent; - } - //System.err.println("overlapfree parent: " + found); - return found; - } - - /** - * Finds the nearest component to <code>c</code> (upwards in the containment - * hierarchy), that is opaque. If <code>c</code> itself is opaque, - * this returns <code>c</code> itself. - * - * @param c the start component for the search - * @return the nearest component to <code>c</code> (upwards in the containment - * hierarchy), that is opaque; If <code>c</code> itself is opaque, - * this returns <code>c</code> itself - */ - private Component findOpaqueParent(Component c) - { - Component found = c; - while (true) - { - if ((found instanceof JComponent) && ((JComponent) found).isOpaque()) - break; - else if (!(found instanceof JComponent) && !found.isLightweight()) - break; - Container p = found.getParent(); - if (p == null) - break; - else - found = p; - } - return found; - } /** * This is the method that gets called when the WHEN_IN_FOCUSED_WINDOW map diff --git a/javax/swing/JMenuItem.java b/javax/swing/JMenuItem.java index f7f93bf00..324d61cd4 100644 --- a/javax/swing/JMenuItem.java +++ b/javax/swing/JMenuItem.java @@ -833,4 +833,20 @@ public class JMenuItem extends AbstractButton implements Accessible, return AccessibleRole.MENU_ITEM; } } + + /** + * Returns <code>true</code> if the component is guaranteed to be painted + * on top of others. This returns false by default and is overridden by + * components like JMenuItem, JPopupMenu and JToolTip to return true for + * added efficiency. + * + * @return <code>true</code> if the component is guaranteed to be painted + * on top of others + */ + boolean onTop() + { + return SwingUtilities.getAncestorOfClass(JInternalFrame.class, this) + == null; + } + } diff --git a/javax/swing/JPopupMenu.java b/javax/swing/JPopupMenu.java index 2e59d4767..1ae8adad0 100644 --- a/javax/swing/JPopupMenu.java +++ b/javax/swing/JPopupMenu.java @@ -902,6 +902,20 @@ public class JPopupMenu extends JComponent implements Accessible, MenuElement } } + /** + * Returns <code>true</code> if the component is guaranteed to be painted + * on top of others. This returns false by default and is overridden by + * components like JMenuItem, JPopupMenu and JToolTip to return true for + * added efficiency. + * + * @return <code>true</code> if the component is guaranteed to be painted + * on top of others + */ + boolean onTop() + { + return true; + } + protected class AccessibleJPopupMenu extends AccessibleJComponent { private static final long serialVersionUID = 7423261328879849768L; diff --git a/javax/swing/JToolTip.java b/javax/swing/JToolTip.java index 836c122c6..3153894da 100644 --- a/javax/swing/JToolTip.java +++ b/javax/swing/JToolTip.java @@ -225,4 +225,18 @@ public class JToolTip extends JComponent implements Accessible { setUI((ToolTipUI) UIManager.getUI(this)); } + + /** + * Returns <code>true</code> if the component is guaranteed to be painted + * on top of others. This returns false by default and is overridden by + * components like JMenuItem, JPopupMenu and JToolTip to return true for + * added efficiency. + * + * @return <code>true</code> if the component is guaranteed to be painted + * on top of others + */ + boolean onTop() + { + return true; + } } diff --git a/javax/swing/JViewport.java b/javax/swing/JViewport.java index 7cf393996..babffb7ce 100644 --- a/javax/swing/JViewport.java +++ b/javax/swing/JViewport.java @@ -942,10 +942,10 @@ public class JViewport extends JComponent implements Accessible * * @param r the rectangle to paint */ - void paintImmediately2(Rectangle r) + void paintImmediately2(int x, int y, int w, int h) { isPaintRoot = true; - super.paintImmediately2(r); + super.paintImmediately2(x, y, w, h); isPaintRoot = false; } } diff --git a/javax/swing/RepaintManager.java b/javax/swing/RepaintManager.java index 51f0f9b6a..99a733a5a 100644 --- a/javax/swing/RepaintManager.java +++ b/javax/swing/RepaintManager.java @@ -197,19 +197,6 @@ public class RepaintManager private WeakHashMap offscreenBuffers; /** - * Indicates if the RepaintManager is currently repainting an area. - */ - private boolean repaintUnderway; - - /** - * This holds buffer commit requests when the RepaintManager is working. - * This maps Component objects (the top level components) to Rectangle - * objects (the area of the corresponding buffer that must be blitted on - * the component). - */ - private HashMap commitRequests; - - /** * The maximum width and height to allocate as a double buffer. Requests * beyond this size are ignored. * @@ -232,8 +219,6 @@ public class RepaintManager doubleBufferMaximumSize = new Dimension(2000,2000); doubleBufferingEnabled = true; offscreenBuffers = new WeakHashMap(); - repaintUnderway = false; - commitRequests = new HashMap(); } /** @@ -398,8 +383,6 @@ public class RepaintManager if (w <= 0 || h <= 0 || !component.isShowing()) return; - Component parent = component.getParent(); - component.computeVisibleRect(rectCache); SwingUtilities.computeIntersection(x, y, w, h, rectCache); @@ -557,7 +540,6 @@ public class RepaintManager compileRepaintRoots(dirtyComponentsWork, dirty, repaintRoots); } - repaintUnderway = true; for (Iterator i = repaintRoots.iterator(); i.hasNext();) { JComponent comp = (JComponent) i.next(); @@ -567,12 +549,11 @@ public class RepaintManager comp.paintImmediately(damaged); } dirtyComponentsWork.clear(); - repaintUnderway = false; } - + /** * Compiles a list of components that really get repainted. This is called - * once for each component in the dirtyComponents HashMap, each time with + * once for each component in the dirtyRegions HashMap, each time with * another <code>dirty</code> parameter. This searches up the component * hierarchy of <code>dirty</code> to find the highest parent that is also * marked dirty and merges the dirty regions. @@ -587,6 +568,29 @@ public class RepaintManager Component current = dirty; Component root = dirty; + // This will contain the dirty region in the root coordinate system, + // possibly clipped by ancestor's bounds. + Rectangle originalDirtyRect = (Rectangle) dirtyRegions.get(dirty); + rectCache.setBounds(originalDirtyRect); + + // The bounds of the current component. + int x = dirty.getX(); + int y = dirty.getY(); + int w = dirty.getWidth(); + int h = dirty.getHeight(); + + // Do nothing if dirty region is clipped away by the component's bounds. + rectCache = SwingUtilities.computeIntersection(0, 0, w, h, rectCache); + if (rectCache.isEmpty()) + return; + + // The cumulated offsets. + int dx = 0; + int dy = 0; + // The actual offset for the found root. + int rootDx = 0; + int rootDy = 0; + // Search the highest component that is also marked dirty. Component parent; while (true) @@ -596,10 +600,29 @@ public class RepaintManager break; current = parent; + // Update the offset. + dx += x; + dy += y; + rectCache.x += x; + rectCache.y += y; + + x = current.getX(); + y = current.getY(); + w = current.getWidth(); + h = current.getHeight(); + rectCache = SwingUtilities.computeIntersection(0, 0, w, h, rectCache); + + // Don't paint if the dirty regions is clipped away by any of + // its ancestors. + if (rectCache.isEmpty()) + return; + // We can skip to the next up when this parent is not dirty. if (dirtyRegions.containsKey(parent)) { root = current; + rootDx = dx; + rootDy = dy; } } @@ -607,15 +630,16 @@ public class RepaintManager // the are different. if (root != dirty) { - Rectangle dirtyRect = (Rectangle) dirtyRegions.get(dirty); - dirtyRect = SwingUtilities.convertRectangle(dirty, dirtyRect, root); - Rectangle rootRect = (Rectangle) dirtyRegions.get(root); - SwingUtilities.computeUnion(dirtyRect.x, dirtyRect.y, dirtyRect.width, - dirtyRect.height, rootRect); + rectCache.x += rootDx - dx; + rectCache.y += rootDy - dy; + Rectangle dirtyRect = (Rectangle) dirtyRegions.get(root); + SwingUtilities.computeUnion(rectCache.x, rectCache.y, rectCache.width, + rectCache.height, dirtyRect); } // Adds the root to the roots set. - roots.add(root); + if (! roots.contains(root)) + roots.add(root); } /** @@ -653,16 +677,19 @@ public class RepaintManager * This is package private because it must get called by JComponent. * * @param comp the component to be painted - * @param area the area to paint on screen, in comp coordinates + * @param x the area to paint on screen, in comp coordinates + * @param y the area to paint on screen, in comp coordinates + * @param w the area to paint on screen, in comp coordinates + * @param h the area to paint on screen, in comp coordinates */ - void commitBuffer(Component comp, Rectangle area) + void commitBuffer(Component comp, int x, int y, int w, int h) { Component root = comp; while (root != null && ! (root instanceof Window || root instanceof Applet)) { - area.x += root.getX(); - area.y += root.getY(); + x += root.getX(); + y += root.getY(); root = root.getParent(); } @@ -670,7 +697,7 @@ public class RepaintManager Image buffer = (Image) offscreenBuffers.get(root); // Make sure we have a sane clip at this point. - g.clipRect(area.x, area.y, area.width, area.height); + g.clipRect(x, y, w, h); g.drawImage(buffer, 0, 0, root); g.dispose(); } |