This fixes a problem with Swing painting and the JViewport. When running
in backingstore scroll mode (the default now), sometimes it fails to
update the backingstore correctly. That happens (for instance) when you
select something in a list inside a JViewport. Then the selection gets
painted into the Swing backbuffer, but not into the JViewport's
backbuffer. Now, when you scroll the list, the selection seems to
disappear (because JViewport blits the not-updated buffer on the
screen). But when you force a general repaint, then the selection is
back there.

I fixed this by adding a package private method to JComponent
isPaintRoot(), which by default returns false and in JViewport this is
overridden to return true when running in backing store mode. When this
method returns true, the component is forced to be the paint root for
all paints regarding the JViewport's children and descendents.

2006-10-12  Roman Kennke  <[EMAIL PROTECTED]>

        * javax/swing/JComponent.java
        (paintImmediately2): Added support for components which need
        to force themselves as paint root.
        (isPaintRoot): New method. This should be overridden by components
        which need to force themselves as paint root.
        * javax/swing/JViewport.java
        (isPaintRoot): Overridden to force the viewport as paint root
        when running in backingstore mode.


/Roman

Index: javax/swing/JComponent.java
===================================================================
RCS file: /cvsroot/classpath/classpath/javax/swing/JComponent.java,v
retrieving revision 1.145
diff -u -1 -5 -r1.145 JComponent.java
--- javax/swing/JComponent.java	23 Aug 2006 22:03:22 -0000	1.145
+++ javax/swing/JComponent.java	12 Oct 2006 10:20:17 -0000
@@ -2176,54 +2176,67 @@
     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())
           {
+            // Indicates whether we reset the paint root to be the current
+            // component.
+            boolean updatePaintRoot = false;
+
             // 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))
+                if (jc.isPaintRoot())
+                  updatePaintRoot = true;
+                else
                   {
-                    // Paint from parent.
-                    paintRoot = jc;
-                    pIndex = pCount;
-                    offsX = 0;
-                    offsY = 0;
-                    haveBuffer = false;
+                    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))
+                      updatePaintRoot = true;
+                      
                   }
               }
+            if (updatePaintRoot)
+              {
+                // 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;
@@ -2245,32 +2258,31 @@
         // 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);
+          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.
@@ -2291,30 +2303,43 @@
   /**
    * 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 false;
   }
 
   /**
+   * This returns true when a component needs to force itself as a paint
+   * origin. This is used for example in JViewport to make sure that it
+   * gets to update its backbuffer.
+   *
+   * @return true when a component needs to force itself as a paint
+   *         origin
+   */
+  boolean isPaintRoot()
+  {
+    return false;
+  }
+
+  /**
    * Performs double buffered repainting.
    */
   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());
Index: javax/swing/JViewport.java
===================================================================
RCS file: /cvsroot/classpath/classpath/javax/swing/JViewport.java,v
retrieving revision 1.49
diff -u -1 -5 -r1.49 JViewport.java
--- javax/swing/JViewport.java	29 Sep 2006 14:36:54 -0000	1.49
+++ javax/swing/JViewport.java	12 Oct 2006 10:20:17 -0000
@@ -914,16 +914,25 @@
 
   /**
    * Overridden from JComponent to set the [EMAIL PROTECTED] #isPaintRoot} flag.
    *
    * @param x the rectangle to paint, X coordinate
    * @param y the rectangle to paint, Y coordinate
    * @param w the rectangle to paint, width
    * @param h the rectangle to paint, height
    */
   void paintImmediately2(int x, int y, int w, int h)
   {
     isPaintRoot = true;
     super.paintImmediately2(x, y, w, h);
     isPaintRoot = false;
   }
+
+  /**
+   * Returns true when the JViewport is using a backbuffer, so that we
+   * can update our backbuffer correctly.
+   */
+  boolean isPaintRoot()
+  {
+    return scrollMode == BACKINGSTORE_SCROLL_MODE;
+  }
 }

Reply via email to