Hi, this should fix the Swing painting code to handle (heavyweight) AWT components properly. Basically it changes two things:
1. Heavyweights are no longer triggered with paint() during JComponent.paintChildren(). Heavyweight components must take care of their painting themselves. 2. When a Swing component hierarchy is contained inside a (non-window) heavyweight component, the Swing backbuffer needs to be blitted onto that heavyweight instead of the Window. The reason for this is that heavyweights always paint themselves over lightweight components. So when we blit the backbuffer to the window instead, the heavyweight would be painted over the Swing GUI that is contained inside the heavyweight, which is rather strange. This effect has been observed in that applet: http://www.dartmouth.edu/~erikh/hyperbolic/hyperbolic.html and is fixed by this patch (in fact, lillian already fixed this, but not completely. this fix had problems with Swing GUIs that contain applets). That said, I think it's a very bad thing to have heavyweights in Swing GUIs, or even Swing GUIs inside heavyweights. In particular, our Applet impl is heavyweight ATM, due to Panel (GtkPanelPeer) having a native representation. I would rather make it act like a lightweight (as it is the case in Sun's impl). I don't know if that's feasible though. We only need to change the GtkPanelPeer's signature to extend GLightweightPeer and implement PanelPeer and comment out the whole body of it. 2006-06-14 Roman Kennke <[EMAIL PROTECTED]> * javax/swing/JComponent.java (isRepainting): New flag. (paintImmediately2): Set isRepainting flag. (getRoot): Removed obsolete method. (paintDoubleBuffered): Differenciate between paint calls from RepaintManager and from AWT refresh. Call RepaintManager.commitBuffer with this and local coordinates. (findOpaqueParent): Stop searching at heavyweight component. These are always opaque. (paintChildrenOptimized): Don't paint heavyweight children. These should care for themselves. (paintChildrenWithOverlap): Don't paint heavyweight children. These should care for themselves. * javax/swing/RepaintManager.java (getOffscreenBuffer): Associate offscreen buffer with toplevel windows only. (getVolatileOffscreenBuffer): Associate offscreen buffer with toplevel windows only. (getRoot): Removed obsolete method. (commitBuffer): Blit buffer on nearest heavyweight. (blitBuffer): New helper method. (getHeavyweightParent): New helper method. (commitRemainingBuffers): Call blitBuffer instead of commitBuffer. * javax/swing/SwingUtilities.java (convertRectangleToAncestor): New helper method. /Roman -- “Improvement makes straight roads, but the crooked roads, without Improvement, are roads of Genius.” - William Blake
Index: javax/swing/JComponent.java
===================================================================
RCS file: /cvsroot/classpath/classpath/javax/swing/JComponent.java,v
retrieving revision 1.126
diff -u -1 -0 -r1.126 JComponent.java
--- javax/swing/JComponent.java 12 Jun 2006 21:14:48 -0000 1.126
+++ javax/swing/JComponent.java 14 Jun 2006 11:00:25 -0000
@@ -664,20 +664,26 @@
*/
boolean autoscrolls = false;
/**
* Indicates whether the current paint call is already double buffered or
* not.
*/
static boolean isPaintingDoubleBuffered = false;
/**
+ * Indicates whether we are calling paintDoubleBuffered() from
+ * paintImmadiately (RepaintManager) or from paint() (AWT refresh).
+ */
+ static private 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();
/**
* Storage for "client properties", which are key/value pairs associated
* with this component by a "client", such as a user application or a
* layout manager. This is lazily constructed when the component gets its
@@ -1848,21 +1854,21 @@
ArrayList newPaintRects = new ArrayList();
paintRectangles.add(g.getClipBounds());
ArrayList componentRectangles = new ArrayList();
// Go through children from top to bottom and find out their paint
// rectangles.
for (int index = 0; paintRectangles.size() > 0 &&
index < children.length; index++)
{
Component comp = children[index];
- if (! comp.isVisible())
+ if (! comp.isVisible() || ! comp.isLightweight())
continue;
Rectangle compBounds = comp.getBounds();
boolean isOpaque = comp instanceof JComponent
&& ((JComponent) comp).isOpaque();
// Add all the current paint rectangles that intersect with the
// component to the component's paint rectangle array.
for (int i = paintRectangles.size() - 1; i >= 0; i--)
{
@@ -2020,21 +2026,21 @@
// paintingTile becomes true just before we start painting the component's
// children.
paintingTile = true;
for (int i = children.length - 1; i >= 0; i--) //children.length; i++)
{
// paintingTile must be set to false before we begin to start painting
// the last tile.
if (i == children.length - 1)
paintingTile = false;
- if (!children[i].isVisible())
+ if (!children[i].isVisible() || ! children[i].isLightweight())
continue;
Rectangle bounds = children[i].getBounds(rectCache);
Rectangle oldClip = g.getClipBounds();
if (oldClip == null)
oldClip = bounds;
if (!g.hitClip(bounds.x, bounds.y, bounds.width, bounds.height))
continue;
@@ -2124,92 +2130,71 @@
root.repaint(rootClip.x, rootClip.y, rootClip.width, rootClip.height);
}
/**
* Performs the actual work of paintImmediatly on the repaint root.
*
* @param r the area to be repainted
*/
void paintImmediately2(Rectangle r)
{
+ isRepainting = true;
RepaintManager rm = RepaintManager.currentManager(this);
if (rm.isDoubleBufferingEnabled() && isDoubleBuffered())
paintDoubleBuffered(r);
else
paintSimple(r);
+ isRepainting = false;
}
/**
- * Gets the root of the component given. If a parent of the
- * component is an instance of Applet, then the applet is
- * returned. The applet is considered the root for painting
- * and adding/removing components. Otherwise, the root Window
- * is returned if it exists.
- *
- * @param comp - The component to get the root for.
- * @return the parent root. An applet if it is a parent,
- * or the root window. If neither exist, null is returned.
- */
- private Component getRoot(Component comp)
- {
- Applet app = null;
-
- while (comp != null)
- {
- if (app == null && comp instanceof Window)
- return comp;
- else if (comp instanceof Applet)
- app = (Applet) comp;
- comp = comp.getParent();
- }
-
- return app;
- }
-
- /**
* Performs double buffered repainting.
*/
private void paintDoubleBuffered(Rectangle r)
{
RepaintManager rm = RepaintManager.currentManager(this);
// Paint on the offscreen buffer.
- Component root = getRoot(this);
+ 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);
- Point translation = SwingUtilities.convertPoint(this, 0, 0, root);
Graphics g2 = buffer.getGraphics();
clipAndTranslateGraphics(root, this, g2);
g2.clipRect(r.x, r.y, r.width, r.height);
g2 = getComponentGraphics(g2);
isPaintingDoubleBuffered = true;
try
{
- paint(g2);
+ 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
{
isPaintingDoubleBuffered = false;
g2.dispose();
}
// Paint the buffer contents on screen.
- rm.commitBuffer(root, new Rectangle(translation.x + r.x,
- translation.y + r.y, r.width,
- r.height));
+ rm.commitBuffer(this, r);
}
/**
* 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
@@ -3647,21 +3632,21 @@
* 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))
+ else if (!(found instanceof JComponent) && !found.isLightweight())
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.37
diff -u -1 -0 -r1.37 RepaintManager.java
--- javax/swing/RepaintManager.java 10 Jun 2006 08:01:27 -0000 1.37
+++ javax/swing/RepaintManager.java 14 Jun 2006 11:00:25 -0000
@@ -611,146 +611,148 @@
*
* @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
*/
public Image getOffscreenBuffer(Component component, int proposedWidth,
int proposedHeight)
{
- Component root = getRoot(component);
+ Component root = SwingUtilities.getWindowAncestor(component);
Image buffer = (Image) offscreenBuffers.get(root);
if (buffer == null
|| buffer.getWidth(null) < proposedWidth
|| buffer.getHeight(null) < proposedHeight)
{
int width = Math.max(proposedWidth, root.getWidth());
width = Math.min(doubleBufferMaximumSize.width, width);
int height = Math.max(proposedHeight, root.getHeight());
height = Math.min(doubleBufferMaximumSize.height, height);
buffer = component.createImage(width, height);
offscreenBuffers.put(root, buffer);
}
return buffer;
}
/**
- * Gets the root of the component given. If a parent of the
- * component is an instance of Applet, then the applet is
- * returned. The applet is considered the root for painting.
- * Otherwise, the root Window is returned if it exists.
- *
- * @param comp - The component to get the root for.
- * @return the parent root. An applet if it is a parent,
- * or the root window. If neither exist, null is returned.
- */
- private Component getRoot(Component comp)
- {
- Applet app = null;
-
- while (comp != null)
- {
- if (app == null && comp instanceof Window)
- return comp;
- else if (comp instanceof Applet)
- app = (Applet) comp;
- comp = comp.getParent();
- }
-
- return app;
- }
-
- /**
* Blits the back buffer of the specified root component to the screen. If
* the RepaintManager is currently working on a paint request, the commit
* requests are queued up and committed at once when the paint request is
* done (by [EMAIL PROTECTED] #commitRemainingBuffers}). This is package private because
* it must get called by JComponent.
*
- * @param root the component, either a Window or an Applet instance
- * @param area the area to paint on screen
+ * @param comp the component to be painted
+ * @param area the area to paint on screen, in comp coordinates
*/
- void commitBuffer(Component root, Rectangle area)
+ void commitBuffer(Component comp, Rectangle area)
{
+ // Determine the component that we finally paint the buffer upon.
+ // We need to paint on the nearest heavyweight component, so that Swing
+ // hierarchies inside (non-window) heavyweights get painted correctly.
+ // Otherwise we would end up blitting the backbuffer behind the heavyweight
+ // which is wrong.
+ Component root = getHeavyweightParent(comp);
+ // FIXME: Optimize this.
+ Rectangle rootRect = SwingUtilities.convertRectangle(comp, area, root);
+
// We synchronize on dirtyComponents here because that is what
// paintDirtyRegions also synchronizes on while painting.
synchronized (dirtyComponents)
{
// If the RepaintManager is not currently painting, then directly
// blit the requested buffer on the screen.
if (! repaintUnderway)
{
- Graphics g = root.getGraphics();
- Image buffer = (Image) offscreenBuffers.get(root);
- Rectangle clip = g.getClipBounds();
- if (clip != null)
- area = SwingUtilities.computeIntersection(clip.x, clip.y,
- clip.width, clip.height,
- area);
- int dx1 = area.x;
- int dy1 = area.y;
- int dx2 = area.x + area.width;
- int dy2 = area.y + area.height;
- // Make sure we have a sane clip at this point.
- g.clipRect(area.x, area.y, area.width, area.height);
-
- // Make sure the coordinates are inside the buffer, everything else
- // might lead to problems.
- // TODO: This code should not really be necessary, however, in fact
- // we have two issues here:
- // 1. We shouldn't get repaint requests in areas outside the buffer
- // region in the first place. This still happens for example
- // when a component is inside a JViewport, and the component has
- // a size that would reach beyond the window size.
- // 2. Graphics.drawImage() should not behave strange when trying
- // to draw regions outside the image.
- int bufferWidth = buffer.getWidth(root);
- int bufferHeight = buffer.getHeight(root);
- dx1 = Math.min(bufferWidth, dx1);
- dy1 = Math.min(bufferHeight, dy1);
- dx2 = Math.min(bufferWidth, dx2);
- dy2 = Math.min(bufferHeight, dy2);
- g.drawImage(buffer, 0, 0, root);
- g.dispose();
+ blitBuffer(root, rootRect);
}
+
// Otherwise queue this request up, until all the RepaintManager work
// is done.
else
{
if (commitRequests.containsKey(root))
- SwingUtilities.computeUnion(area.x, area.y, area.width,
- area.height,
+ SwingUtilities.computeUnion(rootRect.x, rootRect.y,
+ rootRect.width, rootRect.height,
(Rectangle) commitRequests.get(root));
else
- commitRequests.put(root, area);
+ commitRequests.put(root, rootRect);
}
}
}
/**
+ * Copies the buffer to the screen. Note that the root component here is
+ * not necessarily the component with which the offscreen buffer is
+ * associated. The offscreen buffers are always allocated for the toplevel
+ * windows. However, painted is performed on lower-level heavyweight
+ * components too, if they contain Swing components.
+ *
+ * @param root the heavyweight component to blit upon
+ * @param rootRect the rectangle in the root component's coordinate space
+ */
+ private void blitBuffer(Component root, Rectangle rootRect)
+ {
+ // Find the Window from which we use the backbuffer.
+ Component bufferRoot = root;
+ Rectangle bufferRect = rootRect.getBounds();
+ if (!(bufferRoot instanceof Window))
+ {
+ bufferRoot = SwingUtilities.getWindowAncestor(bufferRoot);
+ SwingUtilities.convertRectangleToAncestor(root, rootRect, bufferRoot);
+ }
+
+ Graphics g = root.getGraphics();
+ Image buffer = (Image) offscreenBuffers.get(bufferRoot);
+
+ // Make sure we have a sane clip at this point.
+ g.clipRect(rootRect.x, rootRect.y, rootRect.width, rootRect.height);
+ g.drawImage(buffer, rootRect.x - bufferRect.x, rootRect.y - bufferRect.y,
+ root);
+ g.dispose();
+
+ }
+
+ /**
+ * Finds and returns the nearest heavyweight parent for the specified
+ * component. If the component isn't contained inside a heavyweight parent,
+ * this returns null.
+ *
+ * @param comp the component
+ *
+ * @return the nearest heavyweight parent for the specified component or
+ * null if the component has no heavyweight ancestor
+ */
+ private Component getHeavyweightParent(Component comp)
+ {
+ while (comp != null && comp.isLightweight())
+ comp = comp.getParent();
+ return comp;
+ }
+
+ /**
* Commits the queued up back buffers to screen all at once.
*/
private void commitRemainingBuffers()
{
// We synchronize on dirtyComponents here because that is what
// paintDirtyRegions also synchronizes on while painting.
synchronized (dirtyComponents)
{
Set entrySet = commitRequests.entrySet();
Iterator i = entrySet.iterator();
while (i.hasNext())
{
Map.Entry entry = (Map.Entry) i.next();
Component root = (Component) entry.getKey();
Rectangle area = (Rectangle) entry.getValue();
- commitBuffer(root, area);
+ blitBuffer(root, area);
i.remove();
}
}
}
/**
* 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
@@ -760,21 +762,21 @@
* @param proposedWidth the proposed width of the buffer
* @param proposedHeight the proposed height of the buffer
*
* @since 1.4
*
* @see VolatileImage
*/
public Image getVolatileOffscreenBuffer(Component comp, int proposedWidth,
int proposedHeight)
{
- Component root = getRoot(comp);
+ Component root = SwingUtilities.getWindowAncestor(comp);
Image buffer = (Image) offscreenBuffers.get(root);
if (buffer == null
|| buffer.getWidth(null) < proposedWidth
|| buffer.getHeight(null) < proposedHeight
|| !(buffer instanceof VolatileImage))
{
int width = Math.max(proposedWidth, root.getWidth());
width = Math.min(doubleBufferMaximumSize.width, width);
int height = Math.max(proposedHeight, root.getHeight());
height = Math.min(doubleBufferMaximumSize.height, height);
Index: javax/swing/SwingUtilities.java
===================================================================
RCS file: /cvsroot/classpath/classpath/javax/swing/SwingUtilities.java,v
retrieving revision 1.53
diff -u -1 -0 -r1.53 SwingUtilities.java
--- javax/swing/SwingUtilities.java 11 May 2006 10:51:03 -0000 1.53
+++ javax/swing/SwingUtilities.java 14 Jun 2006 11:00:25 -0000
@@ -1592,11 +1592,34 @@
case WindowConstants.HIDE_ON_CLOSE:
return "HIDE_ON_CLOSE";
case WindowConstants.DISPOSE_ON_CLOSE:
return "DISPOSE_ON_CLOSE";
case WindowConstants.EXIT_ON_CLOSE:
return "EXIT_ON_CLOSE";
default:
throw new IllegalArgumentException("Unrecognised code: " + code);
}
}
+
+ /**
+ * Converts a rectangle in the coordinate system of a child component into
+ * a rectangle of one of it's Ancestors. The result is stored in the input
+ * rectangle.
+ *
+ * @param comp the child component
+ * @param r the rectangle to convert
+ * @param ancestor the ancestor component
+ */
+ static void convertRectangleToAncestor(Component comp, Rectangle r,
+ Component ancestor)
+ {
+ if (comp == ancestor)
+ return;
+
+ r.x += comp.getX();
+ r.y += comp.getY();
+
+ Component parent = comp.getParent();
+ if (parent != null && parent != ancestor)
+ convertRectangleToAncestor(parent, r, ancestor);
+ }
}
signature.asc
Description: Dies ist ein digital signierter Nachrichtenteil
