This should improve Swing's painting performance:
- JComponent.paint() only paints the component, if the clip region of that component isn't completely occupied by an opaque child. - JComponent.paintChildren() can paint directly to a child that is specified in paintImmediately2() (via the new field paintChild). - JComponent.paintImmediately() has been reworked to determine the paint root more efficiently. - RepaintManager.compileRepaintRoot() now avoids use of Rectangle and SwingUtilities.convertRectangle() and computes the offsets on the fly. - An optimization has been added for components that are never obscured when painted, because they are always on top, because if they aren't, then they are made invisible. These are JToolTip, JMenuItem (and thus, JMenu), and JPopupMenu.
- Some old cruft is cleaned up, that was pointed out by Eclipse.

I tested a couple of apps, that have frequently been affected by painting optimizations, I have made sure that clipping still works, etc. If there are any complaints, please email me and I'll repair it asap.

2006-08-23  Roman Kennke  <[EMAIL PROTECTED]>

        * 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.

/Roman
Index: javax/swing/JComponent.java
===================================================================
RCS file: /cvsroot/classpath/classpath/javax/swing/JComponent.java,v
retrieving revision 1.144
diff -u -1 -2 -r1.144 JComponent.java
--- javax/swing/JComponent.java	22 Aug 2006 13:41:24 -0000	1.144
+++ javax/swing/JComponent.java	23 Aug 2006 22:02:23 -0000
@@ -60,24 +60,25 @@
 import java.awt.event.ContainerListener;
 import java.awt.event.FocusEvent;
 import java.awt.event.FocusListener;
 import java.awt.event.KeyEvent;
 import java.awt.event.MouseEvent;
 import java.awt.peer.LightweightPeer;
 import java.beans.PropertyChangeEvent;
 import java.beans.PropertyChangeListener;
 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;
 import java.util.Set;
 
 import javax.accessibility.Accessible;
 import javax.accessibility.AccessibleContext;
 import javax.accessibility.AccessibleExtendedComponent;
 import javax.accessibility.AccessibleKeyBinding;
 import javax.accessibility.AccessibleRole;
 import javax.accessibility.AccessibleState;
 import javax.accessibility.AccessibleStateSet;
@@ -657,25 +658,25 @@
   boolean autoscrolls = false;
 
   /**
    * Indicates whether the current paint call is already double buffered or
    * not. 
    */
   static boolean paintingDoubleBuffered = false;
 
   /**
    * 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 [EMAIL PROTECTED] PropertyChangeEvent} are
    * handled by this listener list. PropertyChangeEvents are handled in
    * [EMAIL PROTECTED] #changeSupport}.
    */
   protected EventListenerList listenerList = new EventListenerList();
 
   /**
    * Handles VetoableChangeEvents.
    */
   private VetoableChangeSupport vetoableChangeSupport;
@@ -753,24 +754,31 @@
    * @see #registerKeyboardAction(ActionListener, KeyStroke, int)
    */
   public static final int WHEN_ANCESTOR_OF_FOCUSED_COMPONENT = 1;
 
   /**
    * Constant used to indicate that an action should be performed only when 
    * the component is in the window which has focus.
    *
    * @see #registerKeyboardAction(ActionListener, KeyStroke, int)
    */
   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.
    *
    * @see #setUIProperty(String, Object)
    * @see LookAndFeel#installProperty(JComponent, String, Object)
    */
   private boolean clientOpaqueSet = false;
 
   /**
    * Indicates if the autoscrolls property has been set by a client program or
    * by the UI.
@@ -1781,56 +1789,107 @@
     // We do a little stunt act here to switch on double buffering if it's
     // not already on. If we are not already doublebuffered, then we jump
     // into the method paintDoubleBuffered, which turns on the double buffer
     // and then calls paint(g) again. In the second call we go into the else
     // branch of this if statement and actually paint things to the double
     // buffer. When this method completes, the call stack unwinds back to
     // paintDoubleBuffered, where the buffer contents is finally drawn to the
     // screen.
     if (!paintingDoubleBuffered && isDoubleBuffered()
         && rm.isDoubleBufferingEnabled())
       {
         Rectangle clip = g.getClipBounds();
-        paintDoubleBuffered(clip);
+        paintDoubleBuffered(clip.x, clip.y, clip.width, clip.height);
       }
     else
       {
         if (getClientProperty("bufferedDragging") != null
             && dragBuffer == null)
           {
             initializeDragBuffer();
           }
         else if (getClientProperty("bufferedDragging") == null
             && dragBuffer != null)
           {
             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);
           }
         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.
    */
   private void initializeDragBuffer()
   {
     dragBufferInitialized = false;
     // Allocate new dragBuffer if the current one is too small.
     if (dragBuffer == null || dragBuffer.getWidth(this) < getWidth()
         || dragBuffer.getHeight(this) < getHeight())
       {
         dragBuffer = createImage(getWidth(), getHeight());
       }
@@ -1871,43 +1930,51 @@
    *
    * @see #paint
    * @see #paintBorder
    * @see #paintComponent
    */
   protected void paintChildren(Graphics g)
   {
     if (getComponentCount() > 0)
       {
         // 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()
                     && child.isVisible())
                   {
                     int cx = child.getX();
                     int cy = child.getY();
                     int cw = child.getWidth();
                     int ch = child.getHeight();
                     if (g.hitClip(cx, cy, cw, ch))
                       {
                         if ((! isOptimizedDrawingEnabled()) && i > 0)
                           {
                             // Check if the child is completely obscured.
                             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);
                         cg.setColor(child.getForeground());
                         cg.setFont(child.getFont());
                         try
                           {
                             child.paint(cg);
                           }
                         finally
                           {
                             cg.dispose();
@@ -1915,51 +1982,87 @@
                       }
                   }
               }
           }
       }
   }
 
   /**
    * Determines if a region of a child component is completely obscured by one
    * 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--)
       {
         Component sib = getComponent(i);
         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;
               }
           }
       }
     return obscured;
   }
 
   /**
+   * 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 [EMAIL PROTECTED]
    * ComponentUI#update} on the [EMAIL PROTECTED] #ui} property of the component, if
    * it is non-<code>null</code>. You may override this if you wish to
    * customize the component's body-painting behavior. The component's body
    * is painted first, before the border and children.
    *
    * @param g The graphics context with which to paint the body
    *
    * @see #paint
    * @see #paintBorder
    * @see #paintChildren
    */
@@ -1981,139 +2084,275 @@
 
   /**
    * A variant of [EMAIL PROTECTED] #paintImmediately(Rectangle)} which takes
    * integer parameters.
    *
    * @param x The left x coordinate of the dirty region
    * @param y The top y coordinate of the dirty region
    * @param w The width of the dirty region
    * @param h The height of the dirty region
    */
   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);
+      }
   }
 
   /**
    * Transform the provided dirty rectangle for this component into the
    * appropriate ancestral [EMAIL PROTECTED] JRootPane} and call [EMAIL PROTECTED] #paint} on
    * that root pane. This method is called from the [EMAIL PROTECTED] RepaintManager}
    * and should always be called within the painting thread.
    *
    * <p>This method will acquire a double buffer from the [EMAIL PROTECTED]
    * RepaintManager} if the component's [EMAIL PROTECTED] #doubleBuffered} property is
    * <code>true</code> and the <code>paint</code> call is the
    * <em>first</em> recursive <code>paint</code> call inside swing.</p>
    *
    * <p>The method will also modify the provided [EMAIL PROTECTED] Graphics} context
    * via the [EMAIL PROTECTED] #getComponentGraphics} method. If you want to customize
    * the graphics object used for painting, you should override that method
    * rather than <code>paint</code>.</p>
    *
    * @param r The dirty rectangle to paint
    */
   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);
 
     // Paint on the offscreen buffer.
     Component root = SwingUtilities.getRoot(this);
     Image buffer = rm.getVolatileOffscreenBuffer(this, root.getWidth(),
                                                  root.getHeight());
 
     // The volatile offscreen buffer may be null when that's not supported
     // by the AWT backend. Fall back to normal backbuffer in this case.
     if (buffer == null)
       buffer = rm.getOffscreenBuffer(this, root.getWidth(), root.getHeight());
 
     //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
       {
         if (isRepainting) // Called from paintImmediately, go through paint().
           paint(g2);
         else // Called from paint() (AWT refresh), don't call it again.
           {
             paintComponent(g2);
             paintBorder(g2);
             paintChildren(g2);
           }
       }
     finally
       {
         paintingDoubleBuffered = false;
         g2.dispose();
       }
 
     // Paint the buffer contents on screen.
-    rm.commitBuffer(this, r);
+    rm.commitBuffer(this, x, y, w, h);
   }
 
   /**
    * Clips and translates the Graphics instance for painting on the double
    * buffer. This has to be done, so that it reflects the component clip of the
    * target component.
    *
    * @param root the root component (top-level container usually)
    * @param target the component to be painted
    * @param g the Graphics instance
    */
   private void clipAndTranslateGraphics(Component root, Component target,
@@ -2522,25 +2761,25 @@
       {
         Action act = null;
         Object cmd = null;
         InputMap map = getInputMap(condition);
         if (map != null)
           {
             cmd = map.get(ks);
             if (cmd != null)
               {
                 if (cmd instanceof ActionListenerProxy)
                   act = (Action) cmd;
                 else 
-                  act = (Action) getActionMap().get(cmd);
+                  act = getActionMap().get(cmd);
               }
           }
         if (act != null && act.isEnabled())
           {
             // Need to synchronize here so we don't get in trouble with
             // our __command__ hack.
             synchronized (act)
               {
                 // We add the command as value to the action, so that
                 // the action can later determine the command with which it
                 // was called. This is undocumented, but shouldn't affect
                 // compatibility. It allows us to use only one Action instance
@@ -3467,148 +3706,24 @@
       }
     // Dispatch event to all children.
     int numChildren = getComponentCount();
     for (int i = 0; i < numChildren; i++)
       {
         Component child = getComponent(i);
         if (! (child instanceof JComponent))
           continue;
         JComponent jc = (JComponent) child;
         jc.fireAncestorEvent(ancestor, id);
       }
   }
-
-  /**
-   * Finds a suitable paint root for painting this component. This method first
-   * checks if this component is overlapped using
-   * [EMAIL PROTECTED] #findOverlapFreeParent(Rectangle)}. The returned paint root is then
-   * feeded to [EMAIL PROTECTED] #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
-   * [EMAIL PROTECTED] #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
    * is changed.
    *
    * @param changed the JComponent associated with the WHEN_IN_FOCUSED_WINDOW
    *        map
    */
   void updateComponentInputMap(ComponentInputMap changed)
   {
     // Since you can change a component's input map via
     // setInputMap, we have to check if <code>changed</code>
Index: javax/swing/JMenuItem.java
===================================================================
RCS file: /cvsroot/classpath/classpath/javax/swing/JMenuItem.java,v
retrieving revision 1.32
diff -u -1 -2 -r1.32 JMenuItem.java
--- javax/swing/JMenuItem.java	14 Jun 2006 14:00:05 -0000	1.32
+++ javax/swing/JMenuItem.java	23 Aug 2006 22:02:23 -0000
@@ -824,13 +824,29 @@
     }
 
     /**
      * Returns the accessible role for the <code>JMenuItem</code> component.
      *
      * @return [EMAIL PROTECTED] AccessibleRole#MENU_ITEM}.
      */
     public AccessibleRole getAccessibleRole()
     {
       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;
+  }
+
 }
Index: javax/swing/JPopupMenu.java
===================================================================
RCS file: /cvsroot/classpath/classpath/javax/swing/JPopupMenu.java,v
retrieving revision 1.38
diff -u -1 -2 -r1.38 JPopupMenu.java
--- javax/swing/JPopupMenu.java	2 Aug 2006 23:05:59 -0000	1.38
+++ javax/swing/JPopupMenu.java	23 Aug 2006 22:02:23 -0000
@@ -893,24 +893,38 @@
   {
     public Separator()
     {
       super();
     }
 
     public String getUIClassID()
     {
       return "PopupMenuSeparatorUI";
     }
   }
 
+  /**
+   * 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;
 
     protected AccessibleJPopupMenu()
     {
       // Nothing to do here.
     }
 
     public AccessibleRole getAccessibleRole()
     {
       return AccessibleRole.POPUP_MENU;
Index: javax/swing/JToolTip.java
===================================================================
RCS file: /cvsroot/classpath/classpath/javax/swing/JToolTip.java,v
retrieving revision 1.14
diff -u -1 -2 -r1.14 JToolTip.java
--- javax/swing/JToolTip.java	20 Apr 2006 13:09:20 -0000	1.14
+++ javax/swing/JToolTip.java	23 Aug 2006 22:02:23 -0000
@@ -216,13 +216,27 @@
     String oldValue = text;
     text = tipText;
     firePropertyChange("tiptext", oldValue, tipText);
   }
 
   /**
    * This method resets the UI used to the Look and Feel default.
    */
   public void updateUI()
   {
     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;
+  }
 }
Index: javax/swing/JViewport.java
===================================================================
RCS file: /cvsroot/classpath/classpath/javax/swing/JViewport.java,v
retrieving revision 1.46
diff -u -1 -2 -r1.46 JViewport.java
--- javax/swing/JViewport.java	1 Jun 2006 04:38:49 -0000	1.46
+++ javax/swing/JViewport.java	23 Aug 2006 22:02:24 -0000
@@ -933,19 +933,19 @@
     // If blitting is not possible for some reason, fall back to repainting
     // everything.
     else
       paintSimple(g);
     lastPaintPosition.setLocation(getViewPosition());
   }
 
   /**
    * Overridden from JComponent to set the [EMAIL PROTECTED] #isPaintRoot} flag.
    *
    * @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;
   }
 }
Index: javax/swing/RepaintManager.java
===================================================================
RCS file: /cvsroot/classpath/classpath/javax/swing/RepaintManager.java,v
retrieving revision 1.44
diff -u -1 -2 -r1.44 RepaintManager.java
--- javax/swing/RepaintManager.java	17 Aug 2006 15:03:35 -0000	1.44
+++ javax/swing/RepaintManager.java	23 Aug 2006 22:02:24 -0000
@@ -188,61 +188,46 @@
    * @see #setDoubleBufferingEnabled
    */
   private boolean doubleBufferingEnabled;
 
   /**
    * The offscreen buffers. This map holds one offscreen buffer per
    * Window/Applet and releases them as soon as the Window/Applet gets garbage
    * collected.
    */
   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.
    *
    * @see #paintDirtyRegions
    * @see #getDoubleBufferMaximumSize
    * @see #setDoubleBufferMaximumSize
    */
   private Dimension doubleBufferMaximumSize;
 
 
   /**
    * Create a new RepaintManager object.
    */
   public RepaintManager()
   {
     dirtyComponents = new HashMap();
     dirtyComponentsWork = new HashMap();
     invalidComponents = new ArrayList();
     repaintWorker = new RepaintWorker();
     doubleBufferMaximumSize = new Dimension(2000,2000);
     doubleBufferingEnabled = true;
     offscreenBuffers = new WeakHashMap();
-    repaintUnderway = false;
-    commitRequests = new HashMap();
   }
 
   /**
    * Returns the <code>RepaintManager</code> for the current thread's
    * thread group. The default implementation ignores the
    * <code>component</code> parameter and returns the same repaint manager
    * for all components.
    *
    * @param component a component to look up the manager of
    *
    * @return the current repaint manager for the calling thread's thread group
    *         and the specified component
@@ -389,26 +374,24 @@
    * @see #addDirtyRegion
    * @see #getDirtyRegion
    * @see #isCompletelyDirty
    * @see #markCompletelyClean
    * @see #markCompletelyDirty
    */
   public void addDirtyRegion(JComponent component, int x, int y,
                              int w, int h)
   {
     if (w <= 0 || h <= 0 || !component.isShowing())
       return;
 
-    Component parent = component.getParent();
-    
     component.computeVisibleRect(rectCache);
     SwingUtilities.computeIntersection(x, y, w, h, rectCache);
 
     if (! rectCache.isEmpty())
       {
         if (dirtyComponents.containsKey(component))
           {
             SwingUtilities.computeUnion(rectCache.x, rectCache.y,
                                         rectCache.width, rectCache.height,
                                    (Rectangle) dirtyComponents.get(component));
           }
         else
@@ -548,83 +531,124 @@
         dirtyComponentsWork = swap;
       }
 
     // Compile a set of repaint roots.
     HashSet repaintRoots = new HashSet();
     Set components = dirtyComponentsWork.keySet();
     for (Iterator i = components.iterator(); i.hasNext();)
       {
         JComponent dirty = (JComponent) i.next();
         compileRepaintRoots(dirtyComponentsWork, dirty, repaintRoots);
       }
 
-    repaintUnderway = true;
     for (Iterator i = repaintRoots.iterator(); i.hasNext();)
       {
         JComponent comp = (JComponent) i.next();
         Rectangle damaged = (Rectangle) dirtyComponentsWork.remove(comp);
         if (damaged == null || damaged.isEmpty())
           continue;
         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.
    *
    * @param dirtyRegions the dirty regions 
    * @param dirty the component for which to find the repaint root
    * @param roots the list to which new repaint roots get appended
    */
   private void compileRepaintRoots(HashMap dirtyRegions, JComponent dirty,
                                    HashSet roots)
   {
     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)
       {
         parent = current.getParent();
         if (parent == null || !(parent instanceof JComponent))
           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;
           }
       }
 
     // Merge the rectangles of the root and the requested component if
     // 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);
   }
 
   /**
    * Get an offscreen buffer for painting a component's image. This image
    * may be smaller than the proposed dimensions, depending on the value of
    * the [EMAIL PROTECTED] #doubleBufferMaximumSize} property.
    *
    * @param component The component to return an offscreen buffer for
    * @param proposedWidth The proposed width of the offscreen buffer
    * @param proposedHeight The proposed height of the offscreen buffer
    *
    * @return A shared offscreen buffer for painting
@@ -644,42 +668,45 @@
         height = Math.min(doubleBufferMaximumSize.height, height);
         buffer = component.createImage(width, height);
         offscreenBuffers.put(root, buffer);
       }
     return buffer;
   }
 
   /**
    * Blits the back buffer of the specified root component to the screen.
    * 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();
       }
 
     Graphics g = root.getGraphics();
     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();
   }
 
   /**
    * Creates and returns a volatile offscreen buffer for the specified
    * component that can be used as a double buffer. The returned image
    * is a [EMAIL PROTECTED] VolatileImage}. Its size will be <code>(proposedWidth,
    * proposedHeight)</code> except when the maximum double buffer size
    * has been set in this RepaintManager.
    *
    * @param comp the Component for which to create a volatile buffer

Reply via email to