/*************************************************** * * cismet GmbH, Saarbruecken, Germany * * ... and it just works. * ****************************************************/ /* Copyright 2004, Sam Reid */ package pswing; import edu.umd.cs.piccolo.PCanvas; import edu.umd.cs.piccolo.util.PBounds; import org.jdesktop.swingx.RepaintManagerX; import org.jdesktop.swingx.TranslucentRepaintManager; import java.awt.*; import java.util.Vector; import javax.swing.*; /** * User: Sam Reid Date: Jul 12, 2005 Time: 8:47:08 AM Copyright (c) Jul 12, 2005 by Sam Reid * * @version $Revision$, $Date$ */ public class PSwingCanvas extends PCanvas { //~ Static fields/initializers --------------------------------------------- public static final String SWING_WRAPPER_KEY = "Swing Wrapper"; // NOI18N //~ Instance fields -------------------------------------------------------- private JComponent swingWrapper = new SwingWrapper(); private ZBasicRepaintManager zBasicRepaintManager = new ZBasicRepaintManager(); private PSwingEventHandler swingEventHandler; //~ Constructors ----------------------------------------------------------- /** * Creates a new PSwingCanvas object. */ public PSwingCanvas() { add(swingWrapper); RepaintManager.setCurrentManager(zBasicRepaintManager); swingEventHandler = new PSwingEventHandler(this, getCamera()); // todo or maybe getCameraLayer()? swingEventHandler.setActive(true); } //~ Methods ---------------------------------------------------------------- /** * DOCUMENT ME! * * @return DOCUMENT ME! */ public JComponent getSwingWrapper() { return swingWrapper; } //~ Inner Classes ---------------------------------------------------------- /** * DOCUMENT ME! * * @version $Revision$, $Date$ */ public static class SwingWrapper extends JComponent { //~ Constructors ------------------------------------------------------- /** * Creates a new SwingWrapper object. */ public SwingWrapper() { setSize(new Dimension(0, 0)); setPreferredSize(new Dimension(0, 0)); putClientProperty(SWING_WRAPPER_KEY, SWING_WRAPPER_KEY); } } /** * This is an internal class used by Jazz to support Swing components in Jazz. 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/>ZBasicRepaint Manager is an extension of RepaintManager that traps those repaints called by the Swing * components that have been added to the ZCanvas and passes these repaints to the SwingVisualComponent rather than * up the component hierarchy as usually happens.</p> * * <p>Also traps revalidate calls made by the Swing components added to the ZCanvas to reshape the applicable Visual * Component.</p> * * <p>Also keeps a list of ZSwings 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 Jazz visual * component). This causes an infinite loop. So we introduce the restriction that no repaints can be triggered by a * call to paint.</p> * * @version $Revision$, $Date$ */ @TranslucentRepaintManager public class ZBasicRepaintManager extends RepaintManagerX { //~ Instance fields ---------------------------------------------------- // The components that are currently painting // This needs to be a vector for thread safety Vector paintingComponents = new Vector(); //~ Constructors ------------------------------------------------------- /** * Creates a new ZBasicRepaintManager object. */ public ZBasicRepaintManager() { super(new RepaintManager()); } /** * Creates a new ZBasicRepaintManager object. * * @param delegate DOCUMENT ME! */ public ZBasicRepaintManager(final RepaintManager delegate) { super(delegate); } //~ Methods ------------------------------------------------------------ /** * Locks repaint for a particular (Swing) component displayed by ZCanvas. * * @param c The component for which the repaint is to be locked */ public void lockRepaint(final JComponent c) { paintingComponents.addElement(c); } /** * Unlocks repaint for a particular (Swing) component displayed by ZCanvas. * * @param c The component for which the repaint is to be unlocked */ public void unlockRepaint(final JComponent c) { synchronized (paintingComponents) { if (paintingComponents.contains(c)) { 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(final 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 Jazz visual components and to call the Jazz 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 */ @Override public synchronized void addDirtyRegion(final JComponent c, final int x, final int y, final int w, final int h) { boolean captureRepaint = false; JComponent capturedComponent = null; int captureX = x; int captureY = y; // We have to check to see if the ZCanvas // (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) && isLightweight(comp) && !captureRepaint; comp = comp.getParent()) { if (comp.getParent() == swingWrapper) { 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.VISUAL_COMPONENT_KEY); if (vis != null) { final int repaintX = captureX; final int repaintY = captureY; final Runnable repainter = new Runnable() { @Override 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); } } /** * DOCUMENT ME! * * @param comp DOCUMENT ME! * * @return DOCUMENT ME! */ private boolean isLightweight(final Component comp) { return (comp.getPeer() == null) || comp.isLightweight(); } /** * This is the method "revalidate" calls in the Swing components. Overridden to capture revalidate calls from * those Swing components being used as Jazz visual components and to update Jazz'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 */ @Override public synchronized void addInvalidComponent(final JComponent invalidComponent) { final JComponent capturedComponent = invalidComponent; if ((capturedComponent.getParent() != null) && (capturedComponent.getParent() instanceof JComponent) && (((JComponent)capturedComponent.getParent()).getClientProperty(SWING_WRAPPER_KEY) != null)) { final Runnable validater = new Runnable() { @Override public void run() { capturedComponent.validate(); final PSwing swing = (PSwing)capturedComponent.getClientProperty( PSwing.VISUAL_COMPONENT_KEY); swing.reshape(); } }; SwingUtilities.invokeLater(validater); } else { super.addInvalidComponent(invalidComponent); } } } }