/* 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() ); } }