/* * $Id$ * * Copyright (c) 1998-2005 The Regents of the University of California. * All rights reserved. See the file COPYRIGHT for details. */ package ptolemy.apps.vergil.graph; import ptolemy.kernel.*; import ptolemy.kernel.util.*; import diva.canvas.CanvasComponent; import diva.canvas.CanvasUtilities; import diva.canvas.CompositeFigure; import diva.canvas.Figure; import diva.canvas.FigureLayer; import diva.canvas.GraphicsPane; import diva.canvas.Site; import diva.canvas.connector.AutonomousSite; import diva.canvas.connector.CenterSite; import diva.canvas.connector.Connector; import diva.canvas.connector.ConnectorAdapter; import diva.canvas.connector.ConnectorEvent; import diva.canvas.connector.ConnectorListener; import diva.canvas.connector.ConnectorManipulator; import diva.canvas.connector.ConnectorTarget; import diva.canvas.connector.PerimeterSite; import diva.canvas.connector.PerimeterTarget; import diva.canvas.event.LayerAdapter; import diva.canvas.event.LayerEvent; import diva.canvas.event.MouseFilter; import diva.canvas.interactor.AbstractInteractor; import diva.canvas.interactor.GrabHandle; import diva.canvas.interactor.Interactor; import diva.canvas.interactor.SelectionDragger; import diva.canvas.interactor.SelectionInteractor; import diva.canvas.interactor.SelectionModel; import diva.graph.*; import diva.util.Filter; import java.awt.event.InputEvent; import java.awt.geom.*; import java.util.HashMap; import java.util.Iterator; import java.util.List; /** * * @author Steve Neuendorffer * @version $Revision$ * @Pt.AcceptedRating Red */ public class MetaNodeController extends CompositeEntity implements NodeController { /** The selection interactor for drag-selecting nodes */ private SelectionDragger _selectionDragger; /** The filter for control operations */ private MouseFilter _controlFilter = new MouseFilter(InputEvent.BUTTON1_MASK, InputEvent.CTRL_MASK); /** * Create a new basic controller with default node and edge interactors. */ public MetaNodeController(CompositeEntity container, String name) throws IllegalActionException, NameDuplicationException { super(container, name); // SelectionModel sm = getController().getSelectionModel(); // setNodeInteractor(new NodeInteractor(getController(), sm)); setNodeInteractor(new MetaInteractor(this, "_interactor")); setNodeRenderer(new MetaNodeRenderer(this, "_renderer")); } /** Given a node, add it to this graph editor and perform a layout * on the new node. */ public void addNode(Object node) { // FIXME this may cause a classcast exception. MutableGraphModel model = (MutableGraphModel) getController() .getGraphModel(); model.addNode(getController(), node, model.getRoot()); drawNode(node); } /** Add the node to this graph editor and render it * at the given location. */ public void addNode(Object node, double x, double y) { MutableGraphModel model = (MutableGraphModel) getController() .getGraphModel(); model.addNode(getController(), node, model.getRoot()); Figure nf = drawNode(node); CanvasUtilities.translateTo(nf, x, y); } /** * Add the node to this graph editor, inside the given parent node * and place it where convenient */ public void addNode(Object node, Object parent) { MutableGraphModel model = (MutableGraphModel) getController() .getGraphModel(); model.addNode(getController(), node, parent); drawNode(node, parent); } /** * Add the node to this graph editor, inside the given parent node * and render it at the given location relative to its parent. */ public void addNode(Object node, Object parent, double x, double y) { MutableGraphModel model = (MutableGraphModel) getController() .getGraphModel(); model.addNode(getController(), node, parent); Figure nf = drawNode(node, parent); CanvasUtilities.translateTo(nf, x, y); } /** * Remove the figure for the given node. */ public void clearNode(Object node) { GraphModel model = getController().getGraphModel(); for (Iterator i = model.outEdges(node); i.hasNext();) { Object edge = i.next(); getController().clearEdge(edge); } for (Iterator i = model.inEdges(node); i.hasNext();) { Object edge = i.next(); getController().clearEdge(edge); } Figure f = getController().getFigure(node); if (f != null) { CanvasComponent container = f.getParent(); f.setUserObject(null); getController().setFigure(node, null); if (container instanceof FigureLayer) { ((FigureLayer) container).remove(f); } else if (container instanceof CompositeFigure) { ((CompositeFigure) container).remove(f); } } } /** * Render the given node and add the resulting figure to the foreground * layer of the graph pane. If the node was previously rendered, then * infer the new location of the figure from the old. */ public Figure drawNode(Object node) { Figure oldFigure = getController().getFigure(node); // Infer the location for the new node. Point2D center; if (oldFigure != null) { center = CanvasUtilities.getCenterPoint(oldFigure.getBounds()); clearNode(node); } else { // no previous figure. which means that we are probably // rendering for the first time. center = null; //FIXME: layout? } Figure newFigure = _renderNode(node); System.out.println("renderedFigure = " + newFigure); getController().getGraphPane().getForegroundLayer().add(newFigure); // Now draw the contained nodes, letting them go where they want to. _drawChildren(node); if (center != null) { // place the new figure where the old one was, if there // was an old figure. CanvasUtilities.translateTo(newFigure, center.getX(), center.getY()); } getController().dispatch(new GraphViewEvent(this, GraphViewEvent.NODE_DRAWN, node)); return newFigure; } /** * Render the given node and add the resulting figure to the given * node's figure, which is assumed to be a CompositeFigure * in the controller's graph pane. */ public Figure drawNode(Object node, Object parent) { // FIXME what if node was previously rendered? Figure newFigure = _renderNode(node); CompositeFigure cf = (CompositeFigure) getController().getFigure(parent); cf.add(newFigure); // Now draw the contained nodes, letting them go where they want to. _drawChildren(node); getController().dispatch(new GraphViewEvent(this, GraphViewEvent.NODE_DRAWN, node)); return newFigure; } /** * Return the graph controller containing this controller. */ public GraphController getController() { return (GraphController) getContainer(); } /** * Return the node interactor associated with this controller. */ public Interactor getNodeInteractor() { List list = entityList(Interactor.class); if (list.size() > 0) { return (Interactor) list.get(0); } return null; } /** * Return the node renderer associated with this controller. */ public NodeRenderer getNodeRenderer() { List list = entityList(NodeRenderer.class); if (list.size() > 0) { System.out.println("NodeRenderer = " + list.get(0)); return (NodeRenderer) list.get(0); } return null; } /** * Initialize all interaction on the graph pane. This method * is called by the setGraphPane() method of the superclass. * This initialization cannot be done in the constructor because * the controller does not yet have a reference to its pane * at that time. */ protected void initializeInteraction() { GraphPane pane = getController().getGraphPane(); // Create and set up the selection dragger _selectionDragger = new SelectionDragger(pane); _selectionDragger.addSelectionInteractor((NodeInteractor) getNodeInteractor()); } /** * Remove the node. */ public void removeNode(Object node) { // FIXME why isn't this symmetric with addNode? MutableGraphModel model = (MutableGraphModel) getController() .getGraphModel(); // clearing the nodes is responsible for clearing any edges that are // connected if (model.isComposite(node)) { for (Iterator i = model.nodes(node); i.hasNext();) { Object insideNode = i.next(); getController().clearNode(insideNode); } } clearNode(node); // we assume that the model will remove any edges that are connected. model.removeNode(getController(), node); getController().getGraphPane().repaint(); } /** * Set the node interactor for this controller */ public void setNodeInteractor(Interactor interactor) { if (!(interactor instanceof ComponentEntity)) { throw new RuntimeException("Interactor " + interactor + " is not a component entity"); } try { Iterator iterator = entityList(Interactor.class).iterator(); while (iterator.hasNext()) { ((ComponentEntity) iterator.next()).setContainer(null); } ((ComponentEntity) interactor).setContainer(this); } catch (KernelException ex) { throw new RuntimeException("Interactor " + interactor + " could not be set"); } } /** * Set the node renderer for this controller */ public void setNodeRenderer(NodeRenderer renderer) { if (!(renderer instanceof ComponentEntity)) { throw new RuntimeException("Renderer " + renderer + " is not a component entity"); } try { Iterator iterator = entityList(NodeRenderer.class).iterator(); while (iterator.hasNext()) { ((ComponentEntity) iterator.next()).setContainer(null); } ((ComponentEntity) renderer).setContainer(this); } catch (KernelException ex) { throw new RuntimeException("Renderer " + renderer + " could not be set"); } } /** * Render the given node using the node renderer. Set the interactor * of the resulting figure to the node interactor. */ protected Figure _renderNode(Object node) { Figure newFigure = getNodeRenderer().render(node); newFigure.setInteractor(getNodeInteractor()); newFigure.setUserObject(node); getController().setFigure(node, newFigure); return newFigure; } /** * Draw the children of the given node. */ protected void _drawChildren(Object node) { GraphModel model = getController().getGraphModel(); if (model.isComposite(node)) { Iterator children = model.nodes(node); while (children.hasNext()) { Object child = children.next(); getController().drawNode(child, node); } } } }