FileDocCategorySizeDatePackage
RepaintManager.javaAPI DocJava SE 5 API25171Fri Aug 26 14:57:58 BST 2005javax.swing

RepaintManager

public class RepaintManager extends Object
This class manages repaint requests, allowing the number of repaints to be minimized, for example by collapsing multiple requests into a single repaint for members of a component tree.
version
1.60 02/18/04
author
Arnaud Weber

Fields Summary
private Map
volatileMap
Maps from GraphicsConfiguration to VolatileImage.
Hashtable
dirtyComponents
Hashtable
tmpDirtyComponents
Vector
invalidComponents
boolean
doubleBufferingEnabled
private Dimension
doubleBufferMaxSize
DoubleBufferInfo
standardDoubleBuffer
private static final Object
repaintManagerKey
static boolean
volatileImageBufferEnabled
static final int
VOLATILE_LOOP_MAX
Rectangle
tmp
Constructors Summary
public RepaintManager()
Create a new RepaintManager instance. You rarely call this constructor. directly. To get the default RepaintManager, use RepaintManager.currentManager(JComponent) (normally "this").

	Object dbe = java.security.AccessController.doPrivileged(
               new GetPropertyAction("awt.nativeDoubleBuffering"));
        boolean nativeDoubleBuffering = (dbe != null) ?
                      Boolean.valueOf(dbe.toString()).booleanValue() : false;
        // If native doublebuffering is being used, do NOT use
        // Swing doublebuffering.
        doubleBufferingEnabled = !nativeDoubleBuffering;
    
Methods Summary
private java.awt.Image_getOffscreenBuffer(java.awt.Component c, int proposedWidth, int proposedHeight)

	Dimension maxSize = getDoubleBufferMaximumSize();
	DoubleBufferInfo doubleBuffer = null;
        int width, height;

        if (standardDoubleBuffer == null) {
            standardDoubleBuffer = new DoubleBufferInfo();
        }
        doubleBuffer = standardDoubleBuffer;
	    
	width = proposedWidth < 1? 1 : 
	          (proposedWidth > maxSize.width? maxSize.width : proposedWidth);
        height = proposedHeight < 1? 1 : 
                  (proposedHeight > maxSize.height? maxSize.height : proposedHeight);

        if (doubleBuffer.needsReset || (doubleBuffer.image != null &&
                                        (doubleBuffer.size.width < width ||
                                         doubleBuffer.size.height < height))) {
            doubleBuffer.needsReset = false;
            if (doubleBuffer.image != null) {
                doubleBuffer.image.flush();
                doubleBuffer.image = null;
            }
            width = Math.max(doubleBuffer.size.width, width);
            height = Math.max(doubleBuffer.size.height, height);
        }

	Image result = doubleBuffer.image;

	if (doubleBuffer.image == null) {
            result = c.createImage(width , height);
            doubleBuffer.size = new Dimension(width, height);
	    if (c instanceof JComponent) {
		((JComponent)c).setCreatedDoubleBuffer(true);
		doubleBuffer.image = result;
	    }
	    // JComponent will inform us when it is no longer valid
	    // (via removeNotify) we have no such hook to other components,
	    // therefore we don't keep a ref to the Component
	    // (indirectly through the Image) by stashing the image.
	}
        return result;
    
public voidaddDirtyRegion(javax.swing.JComponent c, int x, int y, int w, int h)
Add a component in the list of components that should be refreshed. If c already has a dirty region, the rectangle (x,y,w,h) will be unioned with the region that should be redrawn.

see
JComponent#repaint

	/* Special cases we don't have to bother with.
	 */
        if ((w <= 0) || (h <= 0) || (c == null)) {
            return;
        }

	if ((c.getWidth() <= 0) || (c.getHeight() <= 0)) {
	    return;
	}

        if (extendDirtyRegion(c, x, y, w, h)) {
            // Component was already marked as dirty, region has been
            // extended, no need to continue.
            return;
        }

	/* Make sure that c and all it ancestors (up to an Applet or
	 * Window) are visible.  This loop has the same effect as 
	 * checking c.isShowing() (and note that it's still possible 
	 * that c is completely obscured by an opaque ancestor in 
	 * the specified rectangle).
	 */
	Component root = null;

        // Note: We can't synchronize around this, Frame.getExtendedState
        // is synchronized so that if we were to synchronize around this
        // it could lead to the possibility of getting locks out
        // of order and deadlocking.
	for (Container p = c; p != null; p = p.getParent()) {
	    if (!p.isVisible() || (p.getPeer() == null)) {
		return;
	    }
	    if ((p instanceof Window) || (p instanceof Applet)) {
                // Iconified frames are still visible!
                if (p instanceof Frame &&
                        (((Frame)p).getExtendedState() & Frame.ICONIFIED) ==
                                    Frame.ICONIFIED) {
                    return;
                }
		root = p;
		break;
	    }
	}

	if (root == null) return;

        synchronized(this) {
            if (extendDirtyRegion(c, x, y, w, h)) {
                // In between last check and this check another thread
                // queued up runnable, can bail here.
                return;
            }
            dirtyComponents.put(c, new Rectangle(x, y, w, h));
        }

	/* Queues a Runnable that calls validateInvalidComponents() and
	 * rm.paintDirtyRegions() with SwingUtilities.invokeLater().
	 */
	SystemEventQueueUtilities.queueComponentWorkRequest(root);
    
public synchronized voidaddInvalidComponent(javax.swing.JComponent invalidComponent)
Mark the component as in need of layout and queue a runnable for the event dispatching thread that will validate the components first isValidateRoot() ancestor.

see
JComponent#isValidateRoot
see
#removeInvalidComponent

        Component validateRoot = null;

	/* Find the first JComponent ancestor of this component whose
	 * isValidateRoot() method returns true.  
	 */
        for(Component c = invalidComponent; c != null; c = c.getParent()) {
	    if ((c instanceof CellRendererPane) || (c.getPeer() == null)) {
		return;
	    }
	    if ((c instanceof JComponent) && (((JComponent)c).isValidateRoot())) {
		validateRoot = c;
		break;
	    }
	}
        
	/* There's no validateRoot to apply validate to, so we're done.
	 */
	if (validateRoot == null) {
	    return;
	}

	/* If the validateRoot and all of its ancestors aren't visible
	 * then we don't do anything.  While we're walking up the tree
	 * we find the root Window or Applet.
	 */
	Component root = null;
	
	for(Component c = validateRoot; c != null; c = c.getParent()) {
	    if (!c.isVisible() || (c.getPeer() == null)) {
		return;
	    }
	    if ((c instanceof Window) || (c instanceof Applet)) {
		root = c;
		break;
	    }
	}

	if (root == null) {
	    return;
	}
	   
	/* Lazily create the invalidateComponents vector and add the
	 * validateRoot if it's not there already.  If this validateRoot
	 * is already in the vector, we're done.
	 */
	if (invalidComponents == null) {
	    invalidComponents = new Vector();
	}
	else {
	    int n = invalidComponents.size();
	    for(int i = 0; i < n; i++) {
		if(validateRoot == (Component)(invalidComponents.elementAt(i))) {
		    return;
		}
	    }
	}
	invalidComponents.addElement(validateRoot);

	/* Queues a Runnable that calls RepaintManager.validateInvalidComponents() 
	 * and RepaintManager.paintDirtyRegions() with SwingUtilities.invokeLater().
	 */
	SystemEventQueueUtilities.queueComponentWorkRequest(root);
    
voidcollectDirtyComponents(java.util.Hashtable dirtyComponents, javax.swing.JComponent dirtyComponent, java.util.Vector roots)


      
				 
				  
        int dx, dy, rootDx, rootDy;
        Component component, rootDirtyComponent,parent;
	//Rectangle tmp;
        Rectangle cBounds;

        // Find the highest parent which is dirty.  When we get out of this
        // rootDx and rootDy will contain the translation from the
        // rootDirtyComponent's coordinate system to the coordinates of the
        // original dirty component.  The tmp Rect is also used to compute the
        // visible portion of the dirtyRect.

        component = rootDirtyComponent = dirtyComponent;

        int x = dirtyComponent.getX();
        int y = dirtyComponent.getY();
        int w = dirtyComponent.getWidth();
        int h = dirtyComponent.getHeight();

        dx = rootDx = 0;
        dy = rootDy = 0;
        tmp.setBounds((Rectangle) dirtyComponents.get(dirtyComponent));

        // System.out.println("Collect dirty component for bound " + tmp + 
        //                                   "component bounds is " + cBounds);;
        SwingUtilities.computeIntersection(0,0,w,h,tmp);

        if (tmp.isEmpty()) {
            // System.out.println("Empty 1");
            return;
        } 

        for(;;) {
            parent = component.getParent();
            if(parent == null) 
                break;

            if(!(parent instanceof JComponent))
                break;

            component = parent;

            dx += x;
            dy += y;
            tmp.setLocation(tmp.x + x, tmp.y + y);

            x = component.getX();
            y = component.getY();
            w = component.getWidth();
            h = component.getHeight();
            tmp = SwingUtilities.computeIntersection(0,0,w,h,tmp);

            if (tmp.isEmpty()) {
                // System.out.println("Empty 2");
                return;
            }

            if (dirtyComponents.get(component) != null) {
                rootDirtyComponent = component;
                rootDx = dx;
                rootDy = dy;
            }
        } 

        if (dirtyComponent != rootDirtyComponent) {
	    Rectangle r;
            tmp.setLocation(tmp.x + rootDx - dx,
			    tmp.y + rootDy - dy);
	    r = (Rectangle)dirtyComponents.get(rootDirtyComponent);
	    SwingUtilities.computeUnion(tmp.x,tmp.y,tmp.width,tmp.height,r);
        }

        // If we haven't seen this root before, then we need to add it to the
        // list of root dirty Views.

        if (!roots.contains(rootDirtyComponent)) 
            roots.addElement(rootDirtyComponent);	
    
public static javax.swing.RepaintManagercurrentManager(java.awt.Component c)
Return the RepaintManager for the calling thread given a Component.

param
c a Component -- unused in the default implementation, but could be used by an overridden version to return a different RepaintManager depending on the Component
return
the RepaintManager object


     
	String vib = (String) java.security.AccessController.doPrivileged(
               new GetPropertyAction("swing.volatileImageBufferEnabled"));
	volatileImageBufferEnabled = (vib == null || vib.equals("true"));
    
        // Note: SystemEventQueueUtilities.ComponentWorkRequest passes
        // in null as the component, so if component is ever used to 
        // determine the current RepaintManager, SystemEventQueueUtilities
        // will need to be modified accordingly.
        RepaintManager result = (RepaintManager) SwingUtilities.appContextGet(repaintManagerKey);
        if(result == null) {
            result = new RepaintManager();
            SwingUtilities.appContextPut(repaintManagerKey, result);
        }
	return result;
    
public static javax.swing.RepaintManagercurrentManager(javax.swing.JComponent c)
Return the RepaintManager for the calling thread given a JComponent.

Note: This method exists for backward binary compatibility with earlier versions of the Swing library. It simply returns the result returned by {@link #currentManager(Component)}.

param
c a JComponent -- unused
return
the RepaintManager object

	return currentManager((Component)c);
    
private synchronized booleanextendDirtyRegion(java.awt.Component c, int x, int y, int w, int h)
Extends the dirty region for the specified component to include the new region.

return
false if c is not yet marked dirty.

        Rectangle r = (Rectangle)dirtyComponents.get(c);
        if (r != null) {
            // A non-null r implies c is already marked as dirty,
            // and that the parent is valid. Therefore we can
            // just union the rect and bail.
            SwingUtilities.computeUnion(x, y, w, h, r);
            return true;
        }
        return false;
    
public java.awt.RectanglegetDirtyRegion(javax.swing.JComponent aComponent)
Return the current dirty region for a component. Return an empty rectangle if the component is not dirty.

	Rectangle r = null;
	synchronized(this) {
	    r = (Rectangle)dirtyComponents.get(aComponent);
	}
	if(r == null)
	    return new Rectangle(0,0,0,0);
	else
	    return new Rectangle(r);
    
public java.awt.DimensiongetDoubleBufferMaximumSize()
Returns the maximum double buffer size.

return
a Dimension object representing the maximum size

	if (doubleBufferMaxSize == null) {
	    try {
	        doubleBufferMaxSize = Toolkit.getDefaultToolkit().getScreenSize();
	    } catch (HeadlessException e) {
		doubleBufferMaxSize = new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE);
	    }
	}
        return doubleBufferMaxSize;
    
public java.awt.ImagegetOffscreenBuffer(java.awt.Component c, int proposedWidth, int proposedHeight)
Return the offscreen buffer that should be used as a double buffer with the component c. By default there is a double buffer per RepaintManager. The buffer might be smaller than (proposedWidth,proposedHeight) This happens when the maximum double buffer size as been set for the receiving repaint manager.

	return _getOffscreenBuffer(c, proposedWidth, proposedHeight);
    
public java.awt.ImagegetVolatileOffscreenBuffer(java.awt.Component c, int proposedWidth, int proposedHeight)
Return a volatile offscreen buffer that should be used as a double buffer with the specified component c. The image returned will be an instance of VolatileImage, or null if a VolatileImage object could not be instantiated. This buffer might be smaller than (proposedWidth,proposedHeight). This happens when the maximum double buffer size has been set for this repaint manager.

see
java.awt.image.VolatileImage
since
1.4

        GraphicsConfiguration config = c.getGraphicsConfiguration();
        if (config == null) {
            config = GraphicsEnvironment.getLocalGraphicsEnvironment().
                            getDefaultScreenDevice().getDefaultConfiguration();
        }
	Dimension maxSize = getDoubleBufferMaximumSize();
	int width = proposedWidth < 1 ? 1 :
            (proposedWidth > maxSize.width? maxSize.width : proposedWidth);
        int height = proposedHeight < 1 ? 1 :
            (proposedHeight > maxSize.height? maxSize.height : proposedHeight);
        VolatileImage image = (VolatileImage)volatileMap.get(config);
        if (image == null || image.getWidth() < width ||
                             image.getHeight() < height) {
            if (image != null) {
                image.flush();
            }
            image = config.createCompatibleVolatileImage(width, height);
            volatileMap.put(config, image);
        }
	return image;
    
public booleanisCompletelyDirty(javax.swing.JComponent aComponent)
Convenience method that returns true if aComponent will be completely painted during the next paintDirtyRegions(). If computing dirty regions is expensive for your component, use this method and avoid computing dirty region if it return true.

	Rectangle r;
	
	r = getDirtyRegion(aComponent);
	if(r.width == Integer.MAX_VALUE &&
	   r.height == Integer.MAX_VALUE)
	    return true;
	else
	    return false;
    
public booleanisDoubleBufferingEnabled()
Returns true if this RepaintManager is double buffered. The default value for this property may vary from platform to platform. On platforms where native double buffering is supported in the AWT, the default value will be false to avoid unnecessary buffering in Swing. On platforms where native double buffering is not supported, the default value will be true.

return
true if this object is double buffered

        return doubleBufferingEnabled;
    
public voidmarkCompletelyClean(javax.swing.JComponent aComponent)
Mark a component completely clean. aComponent will not get painted during the next paintDirtyRegions() call.

	synchronized(this) {
		dirtyComponents.remove(aComponent);
	}
    
public voidmarkCompletelyDirty(javax.swing.JComponent aComponent)
Mark a component completely dirty. aComponent will be completely painted during the next paintDirtyRegions() call.

	addDirtyRegion(aComponent,0,0,Integer.MAX_VALUE,Integer.MAX_VALUE);
    
public voidpaintDirtyRegions()
Paint all of the components that have been marked dirty.

see
#addDirtyRegion

        int i, count;
	Vector roots;
        JComponent dirtyComponent;

	synchronized(this) {  // swap for thread safety
	    Hashtable tmp = tmpDirtyComponents;
	    tmpDirtyComponents = dirtyComponents;
	    dirtyComponents = tmp;
	    dirtyComponents.clear();
	}

        count = tmpDirtyComponents.size();
        if (count == 0) {
            return;
        } 

        Rectangle rect;
        int localBoundsX = 0;
        int localBoundsY = 0;
        int localBoundsH = 0;
        int localBoundsW = 0;	
        Enumeration keys;

        roots = new Vector(count);
        keys = tmpDirtyComponents.keys();

        while(keys.hasMoreElements()) {
            dirtyComponent = (JComponent) keys.nextElement();
            collectDirtyComponents(tmpDirtyComponents, dirtyComponent, roots);
        }

        count = roots.size();
        //        System.out.println("roots size is " + count);
        for(i=0 ; i < count ; i++) {
            dirtyComponent = (JComponent) roots.elementAt(i);
            rect = (Rectangle) tmpDirtyComponents.get(dirtyComponent);
            //            System.out.println("Should refresh :" + rect);
            localBoundsH = dirtyComponent.getHeight();
            localBoundsW = dirtyComponent.getWidth();

            SwingUtilities.computeIntersection(localBoundsX, 
					       localBoundsY,
					       localBoundsW,
					       localBoundsH,
					       rect);
            // System.out.println("** paint of " + dirtyComponent + rect);
            if (rect.x == 0 && rect.y == 0 &&
                         rect.width == dirtyComponent.getWidth() &&
                         rect.height == dirtyComponent.getHeight()) {
                Container parent = dirtyComponent.getParent();
                ComponentPeer parentPeer;
                if (parent != null && !parent.isLightweight() &&
                    (parentPeer = parent.getPeer()) != null) {
                    // Cancel any pending paints on the heavy weight peer.
                    // This avoid duplicate painting.
                    ((ContainerPeer)parentPeer).cancelPendingPaint(
                                    dirtyComponent.getX(),
                                    dirtyComponent.getY(),
                                    rect.width, rect.height);
                }
            }
            dirtyComponent.paintImmediately(rect.x,rect.y,rect.width,rect.height);
        }
	tmpDirtyComponents.clear();
    
public synchronized voidremoveInvalidComponent(javax.swing.JComponent component)
Remove a component from the list of invalid components.

see
#addInvalidComponent

        if(invalidComponents != null) {
            int index = invalidComponents.indexOf(component);
            if(index != -1) {
                invalidComponents.removeElementAt(index);
            }
        }
    
voidresetDoubleBuffer()
This resets the double buffer. Actually, it marks the double buffer as invalid, the double buffer will then be recreated on the next invocation of getOffscreenBuffer.

	if (standardDoubleBuffer != null) {
	    standardDoubleBuffer.needsReset = true;
	}
    
voidresetVolatileDoubleBuffer(java.awt.GraphicsConfiguration gc)
This resets the volatile double buffer.

        Image image = (Image)volatileMap.remove(gc);
        if (image != null) {
            image.flush();
        }
    
public static voidsetCurrentManager(javax.swing.RepaintManager aRepaintManager)
Set the RepaintManager that should be used for the calling thread. aRepaintManager will become the current RepaintManager for the calling thread's thread group.

param
aRepaintManager the RepaintManager object to use

        if (aRepaintManager != null) {
            SwingUtilities.appContextPut(repaintManagerKey, aRepaintManager);
        } else {
            SwingUtilities.appContextRemove(repaintManagerKey);
        }
    
public voidsetDoubleBufferMaximumSize(java.awt.Dimension d)
Set the maximum double buffer size.

        doubleBufferMaxSize = d;
        if (standardDoubleBuffer != null && standardDoubleBuffer.image != null) {
            if (standardDoubleBuffer.image.getWidth(null) > d.width || 
		standardDoubleBuffer.image.getHeight(null) > d.height) {
		standardDoubleBuffer.image = null;
	    }
        }
        // Clear out the VolatileImages
        Iterator gcs = volatileMap.keySet().iterator();
        while (gcs.hasNext()) {
            GraphicsConfiguration gc = (GraphicsConfiguration)gcs.next();
            VolatileImage image = (VolatileImage)volatileMap.get(gc);
            if (image.getWidth() > d.width || image.getHeight() > d.height) {
                image.flush();
                gcs.remove();
	    }
	}	    
    
public voidsetDoubleBufferingEnabled(boolean aFlag)
Enables or disables double buffering in this RepaintManager. CAUTION: The default value for this property is set for optimal paint performance on the given platform and it is not recommended that programs modify this property directly.

param
aFlag true to activate double buffering
see
#isDoubleBufferingEnabled

        doubleBufferingEnabled = aFlag;
    
public synchronized java.lang.StringtoString()
Returns a string that displays and identifies this object's properties.

return
a String representation of this object

	StringBuffer sb = new StringBuffer();
	if(dirtyComponents != null) 
	    sb.append("" + dirtyComponents);
        return sb.toString();
    
booleanuseVolatileDoubleBuffer()
Returns true if we should use the Image returned from getVolatileOffscreenBuffer to do double buffering.

        return volatileImageBufferEnabled;
    
public voidvalidateInvalidComponents()
Validate all of the components that have been marked invalid.

see
#addInvalidComponent

        Vector ic;
        synchronized(this) {
            if(invalidComponents == null) {
                return;
	    }
            ic = invalidComponents;
            invalidComponents = null;
        }
	int n = ic.size();
        for(int i = 0; i < n; i++) {
            ((Component)ic.elementAt(i)).validate();
        }