/* Copyright 2003-2005, University of Colorado */
/*
* CVS Info -
* Filename : $Source: /fs/cvs/piccolo/piccolo/extras/edu/umd/cs/piccolox/pswing/PSwingRepaintManager.java,v $
* Branch : $Name: $
* Modified by : $Author: jesse $
* Revision : $Revision: 1.1 $
* Date modified : $Date: 2006/01/05 16:54:26 $
*/
package edu.umd.cs.piccolox.pswing;
import edu.umd.cs.piccolo.util.PBounds;
import javax.swing.*;
import java.awt.*;
import java.util.ArrayList;
import java.util.Vector;
/**
* This RepaintManager replaces the default Swing implementation, and
* is used to intercept and repaint dirty regions of PSwing components.
* <p/>
* This is an internal class used by Piccolo to support Swing components
* in Piccolo. This should not be instantiated, though all the public
* methods of javax.swing.RepaintManager may still be called and
* perform in the expected manner.
* <p/>
* PBasicRepaint Manager is an extension of RepaintManager that traps
* those repaints called by the Swing components that have been added
* to the PCanvas and passes these repaints to the
* SwingVisualComponent rather than up the component hierarchy as
* usually happens.
* <p/>
* Also traps revalidate calls made by the Swing components added
* to the PCanvas to reshape the applicable Visual Component.
* <p/>
* Also keeps a list of PSwings that are painting. This
* disables repaint until the component has finished painting. This is
* to address a problem introduced by Swing's CellRendererPane which is
* itself a work-around. The problem is that JTable's, JTree's, and
* JList's cell renderers need to be validated before repaint. Since
* we have to repaint the entire Swing component hierarchy (in the case
* of a Swing component group used as a Piccolo visual component). This
* causes an infinite loop. So we introduce the restriction that no
* repaints can be triggered by a call to paint.
*
* @author Benjamin B. Bederson
* @author Lance E. Good
* @author Sam R. Reid
*/
public class PSwingRepaintManager extends RepaintManager {
private ArrayList swingWrappers = new ArrayList();
// The components that are currently painting
// This needs to be a vector for thread safety
private Vector paintingComponents = new Vector();
/**
* Locks repaint for a particular (Swing) component displayed by
* PCanvas
*
* @param c The component for which the repaint is to be locked
*/
public void lockRepaint( JComponent c ) {
paintingComponents.addElement( c );
}
/**
* Unlocks repaint for a particular (Swing) component displayed by
* PCanvas
*
* @param c The component for which the repaint is to be unlocked
*/
public void unlockRepaint( JComponent c ) {
synchronized( paintingComponents ) {
paintingComponents.removeElementAt( paintingComponents.lastIndexOf( c ) );
}
}
/**
* Returns true if repaint is currently locked for a component and
* false otherwise
*
* @param c The component for which the repaint status is desired
* @return Whether the component is currently painting
*/
public boolean isPainting( JComponent c ) {
return paintingComponents.contains( c );
}
/**
* This is the method "repaint" now calls in the Swing components.
* Overridden to capture repaint calls from those Swing components
* which are being used as Piccolo visual components and to call the Piccolo
* repaint mechanism rather than the traditional Component hierarchy
* repaint mechanism. Otherwise, behaves like the superclass.
*
* @param c Component to be repainted
* @param x X coordinate of the dirty region in the component
* @param y Y coordinate of the dirty region in the component
* @param w Width of the dirty region in the component
* @param h Height of the dirty region in the component
*/
public synchronized void addDirtyRegion( JComponent c, int x, int y, final int w, final int h ) {
// System.out.println( "PSwingCanvas$PBasicRepaintManager.addDirtyRegion, c="+c.getClass()+", rect=["+x+", "+y+", "+w+", " +", "+h+"], c="+c);
boolean captureRepaint = false;
JComponent capturedComponent = null;
int captureX = x, captureY = y;
// We have to check to see if the PCanvas
// (ie. the SwingWrapper) is in the components ancestry. If so,
// we will want to capture that repaint. However, we also will
// need to translate the repaint request since the component may
// be offset inside another component.
for( Component comp = c; comp != null && comp.isLightweight() && !captureRepaint; comp = comp.getParent() ) {
if( swingWrappers.contains( comp.getParent() ) ) {
if( comp instanceof JComponent ) {
captureRepaint = true;
capturedComponent = (JComponent)comp;
}
}
else {
// Adds to the offset since the component is nested
captureX += comp.getLocation().getX();
captureY += comp.getLocation().getY();
}
}
// Now we check to see if we should capture the repaint and act
// accordingly
if( captureRepaint ) {
if( !isPainting( capturedComponent ) ) {
final PSwing vis = (PSwing)capturedComponent.getClientProperty( PSwing.PSWING_PROPERTY );
if( vis != null ) {
final int repaintX = captureX;
final int repaintY = captureY;
Runnable repainter = new Runnable() {
public void run() {
vis.repaint( new PBounds( (double)repaintX, (double)repaintY, (double)w, (double)h ) );
}
};
SwingUtilities.invokeLater( repainter );
}
}
}
else {
super.addDirtyRegion( c, x, y, w, h );
}
}
/**
* This is the method "revalidate" calls in the Swing components.
* Overridden to capture revalidate calls from those Swing components
* being used as Piccolo visual components and to update Piccolo's visual
* component wrapper bounds (these are stored separately from the
* Swing component). Otherwise, behaves like the superclass.
*
* @param invalidComponent The Swing component that needs validation
*/
public synchronized void addInvalidComponent( JComponent invalidComponent ) {
final JComponent capturedComponent = invalidComponent;
if( capturedComponent.getParent() != null &&
capturedComponent.getParent() instanceof JComponent &&
( (JComponent)capturedComponent.getParent() ).getClientProperty( PSwingCanvas.SWING_WRAPPER_KEY ) != null )
{
Runnable validater = new Runnable() {
public void run() {
capturedComponent.validate();
PSwing swing = (PSwing)capturedComponent.getClientProperty( PSwing.PSWING_PROPERTY );
swing.reshape();
}
};
SwingUtilities.invokeLater( validater );
}
else {
super.addInvalidComponent( invalidComponent );
}
}
void addPSwingCanvas( PSwingCanvas swingWrapper ) {
swingWrappers.add( swingWrapper.getSwingWrapper() );
}
}