package iiuf.swing.graph;
/*
TODO
- addContext menu doesn't work after setEditable(true)
*/
import java.io.File;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.ArrayList;
import java.util.Collection;
import java.util.StringTokenizer;
import java.awt.Point;
import java.awt.Container;
import java.awt.LayoutManager;
import java.awt.LayoutManager2;
import java.awt.BorderLayout;
import java.awt.GridBagLayout;
import java.awt.Dimension;
import java.awt.Component;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.AWTEvent;
import java.awt.Cursor;
import java.awt.Color;
import java.awt.Rectangle;
import java.awt.Stroke;
import java.awt.geom.AffineTransform;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
import java.awt.event.ItemListener;
import java.awt.event.ItemEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionAdapter;
import java.awt.event.MouseMotionListener;
import java.awt.event.MouseEvent;
import java.awt.dnd.DropTargetListener;
import java.awt.dnd.DropTargetDragEvent;
import java.awt.dnd.DropTargetDropEvent;
import java.awt.dnd.DropTargetEvent;
import java.awt.dnd.DropTarget;
import java.lang.reflect.Array;
import javax.swing.JFrame;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JPanel;
import javax.swing.JLabel;
import javax.swing.JTextField;
import javax.swing.JScrollPane;
import javax.swing.JComboBox;
import javax.swing.JCheckBox;
import javax.swing.JViewport;
import javax.swing.Scrollable;
import javax.swing.SwingConstants;
import javax.swing.JFileChooser;
import javax.swing.UIManager;
import javax.swing.ToolTipManager;
import javax.swing.event.ChangeListener;
import javax.swing.event.ChangeEvent;
import iiuf.awt.Awt;
import iiuf.swing.Swing;
import iiuf.swing.ContextMenuEnabled;
import iiuf.swing.ContextMenuManager;
import iiuf.swing.SetSelectionModel;
import iiuf.swing.HexagonalBorder;
import iiuf.util.AttributeFactory;
import iiuf.util.Attributable;
import iiuf.util.Util;
import iiuf.util.EventListenerList;
import iiuf.util.graph.GraphNode;
import iiuf.util.graph.GraphModel;
import iiuf.util.graph.DefaultGraphModel;
import iiuf.util.graph.GraphPort;
import iiuf.util.graph.DefaultGraphNode;
import iiuf.util.graph.DefaultGraphEdge;
import iiuf.util.graph.GraphModelListener;
/**
Graph visualizer class.<p>
(c) 2000, 2001, IIUF, DIUF<p>
@author $Author: ohitz $
@version $Name: $ $Revision: 1.1 $
*/
public class GraphPanel
extends
JPanel
implements
Scrollable,
ContextMenuEnabled,
GraphModelListener,
DropTargetListener
{
public static final String IS_EDITABLE = "IS_EDITABLE";
public static final String SELECTION_BOUNDS_CHANGED = "SELECTION_BOUNDS_CHANGED";
static final int SELECTION_MARK_SIZE = 6;
static final int SELECTION_MARK_CENTER = SELECTION_MARK_SIZE / 2;
static final String COMPONENT_TAG = "__component__";
static final String GRAPH_NODE_PORT_TAG = "__graph_node_port__";
static final String EDGE_TAG = "__edge__";
public int COMPONENT = -1;
public int GRAPH_EDGE = -1;
public int NODE_PROPERTIES = -1;
public int GRAPH_NODE_PORT = -1;
protected GraphPanelEditor editor;
private GraphRouter router;
private GraphModel graph;
private AbstractNodeComponentFactory nodeFactory;
private AbstractGraphEdgeFactory edgeFactory;
private AbstractPortFactory portFactory;
private AbstractPropertiesFactory propertiesFactory;
private GraphLayout layout;
private boolean edgeAfter;
private HashMap cmpToModelMap = new HashMap();
private HashMap edgeToModelMap = new HashMap();
private GraphNode showPorts;
private GraphPort preferredPort;
private SelectionModel selectionModel = new SelectionModel();
private Rectangle selection = new Rectangle();
private Point dot = new Point();
private Point mark = new Point();
private int tolerance = 5;
private Color selectionColor;
private Rectangle tmpRect = new Rectangle();
private ToolTipManager toolTipManager;
private Rectangle selectionBoundingBox;
private Rectangle oldSelectionBoundingBox;
int layoutBlock;
int settingModel;
boolean edit;
public GraphPanel(AbstractNodeComponentFactory nodeFactory,
AbstractPortFactory portFactory,
AbstractGraphEdgeFactory edgeFactory,
NodeLayouter layouter,
GraphRouter router) {
this();
setNodeComponentFactory(nodeFactory);
nodeFactory.gp = this;
setPortFactory(portFactory);
setGraphEdgeFactory(edgeFactory);
setRouter(router);
setLayouter(layouter);
}
public GraphPanel() {
toolTipManager = ToolTipManager.sharedInstance();
setNodeComponentFactory(new AbstractNodeComponentFactory(GraphPanel.this) {
protected Component newNodeComponent(GraphNode node, Object[] args) {
return new JButton("Node");
}
});
setPortFactory(new AbstractPortFactory() {
protected GraphNodePort newPort(GraphPort port, Object[] args) {
return null;
}
});
setGraphEdgeFactory(new AbstractGraphEdgeFactory() {
protected GraphEdge newGraphEdge(iiuf.util.graph.GraphEdge edge,
GraphNode fromNode, GraphPort fromPort,
GraphNode toNode, GraphPort toPort, Object[] args) {
return new GraphEdge((Component)fromNode.get(COMPONENT), (GraphNodePort)fromPort.get(GRAPH_NODE_PORT),
(Component)toNode.get(COMPONENT), (GraphNodePort)toPort.get(GRAPH_NODE_PORT));
}
});
setPropertiesFactory(new AbstractPropertiesFactory() {
protected Component newPropertiesWindow(GraphNode node, Object[] args) {
return null;
}
});
setRouter(new StraightLineRouter());
editor = new GraphPanelEditor(this);
editor.setAutoscrolls(true);
editor.setSize(0 ,0);
add(editor);
layout = new GraphLayout(new DefaultNL());
setLayout(layout);
selectionModel.addChangeListener(new ChangeListener() {
public void stateChanged(ChangeEvent e) {
repaint();
}
});
setAutoscrolls(true);
editor.addMouseMotionListener(new Scroller());
addMouseMotionListener(new Scroller());
setSelectionColor(UIManager.getColor("Tree.selectionBackground"));
}
static class Scroller extends MouseMotionAdapter {
public void mouseDragged(MouseEvent e) {
Rectangle r = new Rectangle(e.getX(), e.getY(), 1, 1);
((JComponent)e.getSource()).scrollRectToVisible(r);
}
}
public Dimension getPreferredScrollableViewportSize() {return getPreferredSize(); }
public boolean getScrollableTracksViewportHeight() {
return (getParent() instanceof JViewport) ? (((JViewport)getParent()).getHeight() > getPreferredSize().height) : false;
}
public boolean getScrollableTracksViewportWidth() {
return (getParent() instanceof JViewport) ? (((JViewport)getParent()).getWidth() > getPreferredSize().width) : false;
}
public int getScrollableUnitIncrement(Rectangle visibleRect, int orientation, int direction) {return 8;}
public int getScrollableBlockIncrement(Rectangle visibleRect, int orientation, int direction) {
int result = orientation == SwingConstants.VERTICAL ? visibleRect.height : visibleRect.width;
if(result > 32) result -= 16;
return result;
}
public void setSelectionColor(Color newColor) {
selectionColor = newColor;
}
public Color getSelectionColor() {
return selectionColor;
}
public void setEdgeSelectionTolerance(int tolerance_) {
tolerance = tolerance_;
}
public int getEdgeSelectionTolerance() {
return tolerance;
}
public GraphPanelEditor getEditor() {
return editor;
}
public SetSelectionModel getSelectionModel() {
return selectionModel;
}
public void setNodeComponentFactory(AbstractNodeComponentFactory nodeFactory_) {
nodeFactory = nodeFactory_;
}
public void setPortFactory(AbstractPortFactory portFactory_) {
portFactory = portFactory_;
}
public void setPropertiesFactory(AbstractPropertiesFactory propertiesFactory_) {
propertiesFactory = propertiesFactory_;
}
public void setGraphEdgeFactory(AbstractGraphEdgeFactory edgeFactory_) {
edgeFactory = edgeFactory_;
}
private GraphNode showPortsNodeCache;
private GraphPort showPortsPrefferedPortCache;
public void setShowPorts(GraphNode node, GraphPort preferredPort_) {
if(showPortsNodeCache == node && showPortsPrefferedPortCache == preferredPort_)
return;
showPortsNodeCache = node;
showPortsPrefferedPortCache = preferredPort_;
if(showPorts != null)
repaint(((Component)showPorts.get(COMPONENT)).getBounds(tmpRect));
showPorts = node;
preferredPort = preferredPort_;
if(showPorts != null)
repaint(((Component)showPorts.get(COMPONENT)).getBounds(tmpRect));
}
public GraphModel getModel() {
return graph;
}
public void setModel(GraphModel graph_) {
settingModel++;
setShowPorts(null, null);
selectionModel.clearSelection();
if(graph != null)
graph.removeGraphModelListener(this);
graph = graph_;
graph.addGraphModelListener(this);
COMPONENT = graph.nodeAttribute(COMPONENT_TAG, nodeFactory);
NODE_PROPERTIES = graph.nodeAttribute("properties", propertiesFactory);
GRAPH_NODE_PORT = graph.portAttribute(GRAPH_NODE_PORT_TAG, portFactory);
GRAPH_EDGE = graph.edgeAttribute(EDGE_TAG, edgeFactory);
Component[] cs = getComponents();
for(int i = 0; i < cs.length; i++)
if(cs[i] instanceof JComponent)
toolTipManager.unregisterComponent((JComponent)cs[i]);
removeAll();
add(editor);
GraphNode[] nodes = graph.nodesArray();
for(int i = 0; i < nodes.length; i++)
addNode(nodes[i]);
edgeca = null;
settingModel--;
validate();
repaint(0, 0, getWidth(), getHeight());
}
public Object viewToModel(Point p) {
return viewToModel(p.x, p.y);
}
public Object viewToModel(int x, int y) {
Object result = locationToObject(this, x, y);
if(result instanceof GraphEdge)
return viewToModel((GraphEdge)result);
else
return viewToModel((Component)result);
}
public GraphNode viewToModel(Component c) {
return (GraphNode)cmpToModelMap.get(c);
}
public iiuf.util.graph.GraphEdge viewToModel(GraphEdge e) {
return (iiuf.util.graph.GraphEdge)edgeToModelMap.get(e);
}
public void nodesAdded(GraphModel model, GraphNode[] nodes) {
layoutBlock++;
for(int i = 0; i < nodes.length; i++)
addNode(nodes[i]);
layoutBlock--;
doLayout();
repaint();
}
public void nodesRemoved(GraphModel model, GraphNode[] nodes) {
layoutBlock++;
for(int i = 0; i < nodes.length; i++)
removeNode(nodes[i]);
layoutBlock--;
setShowPorts(null, null);
doLayout();
repaint();
}
public void edgesAdded(GraphModel model, iiuf.util.graph.GraphEdge[] edges) {
layoutBlock++;
edgeca = null;
for(int i = 0; i < edges.length; i++) {
GraphNode node = edges[i].getToNode();
if(node instanceof ConnectingNode &&
editor.connectingNode != node) {
Rectangle bounds = ((Component)edges[i].getFromNode().get(COMPONENT)).getBounds(tmpRect);
((Component)node.get(COMPONENT)).setLocation(bounds.x + bounds.width / 2, bounds.y + bounds.height / 2);
break;
}
}
layoutBlock--;
doLayout();
repaint();
}
public void edgesRemoved(GraphModel model, iiuf.util.graph.GraphEdge[] edges) {
layoutBlock++;
SetSelectionModel sm = getSelectionModel();
for(int i = 0; i < edges.length; i++)
sm.remove(edges[i]);
edgeca = null;
layoutBlock--;
setShowPorts(null, null);
doLayout();
repaint();
}
public void setEditable(boolean state) {
if(edit != state) {
edit = state;
if(edit) {
editor.setSize(getSize());
editor.start();
if(ctxmgr != null)
ctxmgr.setComponent(editor);
}
else {
selectionModel.clearSelection();
editor.stop();
editor.setSize(0, 0);
if(ctxmgr != null)
ctxmgr.setComponent(this);
}
repaint();
firePropertyChange(IS_EDITABLE, !state, state);
}
}
public boolean isEditable() {
return edit;
}
public void setLayouter(NodeLayouter layouter) {
layout.setLayouter(layouter);
doLayout();
repaint();
}
public NodeLayouter getLayouter() {
return layout.getLayouter();
}
public void setRouter(GraphRouter router_) {
router = router_;
router.init();
repaint();
}
public void setPaintEdgesAfterNodes(boolean state) {
if(state != edgeAfter) {
edgeAfter = state;
repaint();
}
}
private void addNode(GraphNode node) {
Component cmp = (Component)node.get(COMPONENT);
if(cmp instanceof GraphNodeComponent)
((GraphNodeComponent)cmp).addComponent(this, 1);
else
add(cmp, 1);
cmpToModelMap.put(node.get(COMPONENT), node);
nodeca = null;
}
private void removeNode(GraphNode node) {
Component c = (Component)node.get(COMPONENT);
if(c instanceof JComponent)
toolTipManager.unregisterComponent((JComponent)c);
if(c instanceof GraphNodeComponent)
((GraphNodeComponent)c).dispose();
remove(c);
cmpToModelMap.remove(node.get(COMPONENT));
getSelectionModel().remove(node);
nodeca = null;
}
public GraphNode findNodeAt(int x, int y) {
return (GraphNode)cmpToModelMap.get(locationToComponent(x, y));
}
public GraphPort findPortAt(int x, int y) {
GraphNode n = findNodeAt(x, y);
return n == null ? null : findPortAt(n, x, y);
}
public GraphPort findPortAt(GraphNode node, int mx, int my) {
GraphPort[] ports = node.getPorts();
Component cmp = (Component)node.get(COMPONENT);
int mind = Integer.MAX_VALUE;
int min = 0;
int cw = cmp.getWidth();
int ch = cmp.getHeight();
int cx = cmp.getX();
int cy = cmp.getY();
for(int i = 0; i < ports.length; i++) {
GraphNodePort port = (GraphNodePort)ports[i].get(GRAPH_NODE_PORT);
if(port == null) return null;
int x = mx - (int)(port.x * cw + cx);
int y = my - (int)(port.y * ch + cy);
int d = x * x + y * y;
if(d < mind) {
min = i;
mind = d;
}
}
return ports[min];
}
GraphEdge[] edgeca;
private GraphEdge[] getEdges() {
if(graph == null) return new GraphEdge[0];
if(edgeca == null) {
edgeToModelMap = new HashMap();
iiuf.util.graph.GraphEdge[] tmp = graph.edgesArray();
edgeca = new GraphEdge[tmp.length];
for(int i = 0; i < tmp.length; i++) {
edgeca[i] = (GraphEdge)tmp[i].get(GRAPH_EDGE);
edgeToModelMap.put(edgeca[i], tmp[i]);
}
}
return edgeca;
}
Component[] nodeca;
private Component[] getNodes() {
if(graph == null) return new Component[0];
if(nodeca == null) {
GraphNode[] tmp = graph.nodesArray();
nodeca = new Component[tmp.length];
for(int i = 0; i < tmp.length; i++)
nodeca[i] = (Component)tmp[i].get(COMPONENT);
}
return nodeca;
}
public Object locationToObject(Component component, Point location) {
return locationToObject(component, location.x, location.y);
}
private Component locationToComponent(int x, int y) {
synchronized (getTreeLock()) {
Component[] components = getComponents();
for(int i = 0; i < components.length; i++) {
Component comp = components[i];
if(comp != null && comp != editor)
if (comp.contains(x - comp.getX(), y - comp.getY()))
return comp;
}
}
return null;
}
public Object locationToObject(Component component, int x, int y) {
if(!contains(x, y)) return null;
Component cmp = locationToComponent(x, y);
if(cmp != null) return cmp;
GraphEdge[] edges = getEdges();
for(int i = 0; i < edges.length; i++)
if(edges[i].pointIsNear(x, y, tolerance))
return edges[i];
return this;
}
public Component getComponent() {
return this;
}
private ContextMenuManager ctxmgr;
public void setContextMenuManager(ContextMenuManager manager) {
ctxmgr = manager;
}
public ContextMenuManager getContextMenuManager() {
return ctxmgr;
}
public ContextMenuManager addContextMenu() {
if(ctxmgr == null)
ctxmgr = new ContextMenuManager(this);
return ctxmgr;
}
public Rectangle getDotMarkRectangle() {
return selection;
}
public boolean isDragging(GraphNode node) {
return editor.dragable == node || node instanceof ConnectingNode;
}
public void setDot(int x, int y) {
dot.setLocation(x, y);
setSelection(x, y);
}
public void setMark(int x, int y) {
mark.setLocation(x, y);
setSelection(x, y);
}
private void setSelection(int x, int y) {
tmpRect = (Rectangle)selection.clone();
int w = selection.width;
int h = selection.height;
selection.setBounds(x, y, 0, 0);
selection.add(dot);
if(selection.width != w || selection.height != h) {
tmpRect.add(selection);
tmpRect.width++;
tmpRect.height++;
repaint(tmpRect);
}
}
public void repaint(int x, int y, int w, int h) {
super.repaint(x - SELECTION_MARK_CENTER, y - SELECTION_MARK_CENTER, w + SELECTION_MARK_SIZE, h + SELECTION_MARK_SIZE);
}
public void paintChildren(Graphics g) {
if(settingModel != 0) {
repaint(100);
return;
}
oldSelectionBoundingBox = selectionBoundingBox;
if(!edgeAfter)
paintEdges(g);
super.paintChildren(g);
if(edgeAfter)
paintEdges(g);
g.setColor(selectionColor);
Graphics2D g2 = (Graphics2D)g;
if(!selectionModel.isEmpty()) {
selectionBoundingBox = null;
Object[] sel = selectionModel.getSelection();
for(int i = 0; i < sel.length; i++) {
if(!(sel[i] instanceof GraphNode)) continue;
Rectangle bounds = ((Component)((GraphNode)sel[i]).get(COMPONENT)).getBounds(tmpRect);
g2.draw(bounds);
if(selectionBoundingBox == null)
selectionBoundingBox = (Rectangle)bounds.clone();
else
selectionBoundingBox.add(bounds);
}
if(selectionBoundingBox != null) {
g2.draw(selectionBoundingBox);
if(getLayouter().allowsNodeLocationChange()) {
int x = selectionBoundingBox.x;
int y = selectionBoundingBox.y;
int w = selectionBoundingBox.width;
int wh = w / 2;
int h = selectionBoundingBox.height;
int hh = h / 2;
drawHandle(g, x, y);
drawHandle(g, x + wh, y);
drawHandle(g, x + w, y);
drawHandle(g, x, y + hh);
drawHandle(g, x + w, y + hh);
drawHandle(g, x, y + h);
drawHandle(g, x + wh, y + h);
drawHandle(g, x + w, y + h);
}
}
} else
selectionBoundingBox = null;
if(selection.width != 0 && selection.height != 0)
g2.draw(selection);
if(showPorts != null) {
GraphPort[] ports = showPorts.getPorts();
Component cmp = (Component)showPorts.get(COMPONENT);
g.setColor(Color.red);
for(int i = 0; i < ports.length; i++) {
GraphNodePort port = (GraphNodePort)ports[i].get(GRAPH_NODE_PORT);
if(port != null)
port.paint(cmp, g, ports[i] == preferredPort);
}
}
if((oldSelectionBoundingBox == null && selectionBoundingBox != null) ||
(oldSelectionBoundingBox != null && !oldSelectionBoundingBox.equals(selectionBoundingBox)))
firePropertyChange(SELECTION_BOUNDS_CHANGED, oldSelectionBoundingBox, selectionBoundingBox);
}
private void paintEdges(Graphics g) {
Graphics2D g2 = (Graphics2D)g;
Stroke svStroke = g2.getStroke();
GraphEdge[] edges = getEdges();
router.setupEdges(this, edges, getNodes());
for(int i = 0; i < edges.length; i++) {
GraphEdge edge = edges[i];
Color svColor = edge.color;
edge.color = selectionModel.isSelected(edgeToModelMap.get(edges[i])) ? selectionColor : edges[i].color;
edge.paint(g);
edge.color = svColor;
}
((Graphics2D)g).setStroke(svStroke);
for(int i = 0; i < edges.length; i++)
edges[i].paintMarkers(g);
}
private transient Component targetLastEntered;
private void trackMouseEnterExit(Component targetOver, MouseEvent e) {
if(targetLastEntered == targetOver) return;
if(targetLastEntered == null)
retargetMouseEvent(targetOver, MouseEvent.MOUSE_ENTERED, e.getModifiers(), e);
if(targetLastEntered != null) {
retargetMouseEvent(targetLastEntered, MouseEvent.MOUSE_EXITED, e.getModifiers(), e);
retargetMouseEvent(targetOver, MouseEvent.MOUSE_ENTERED, e.getModifiers(), e);
}
targetLastEntered = targetOver;
}
static void retargetMouseEvent(Component target, int id, int modifiers, MouseEvent e) {
if(target == null) return;
target.dispatchEvent(new MouseEvent(target,
id,
e.getWhen(),
modifiers,
e.getX() - target.getX(),
e.getY() - target.getY(),
e.getClickCount(),
e.isPopupTrigger()));
}
void handleMouseMoved(MouseEvent e) {
int x = e.getX();
int y = e.getY();
Component[] c = getComponents();
Component targetOver = null;
for(int i = 0; i < c.length; i++) {
if(c[i] == editor) continue;
if(c[i].getBounds(tmpRect).contains(x, y)) {
targetOver = c[i];
break;
}
}
trackMouseEnterExit(targetOver, e);
retargetMouseEvent(targetOver, e.getID(), e.getModifiers(), e);
}
public Rectangle getSelectionBounds() {
return selectionBoundingBox;
}
int pointToSelectionArea(int px, int py) {
if(selectionBoundingBox == null) return -1;
int x = selectionBoundingBox.x;
int y = selectionBoundingBox.y;
int w = selectionBoundingBox.width;
int wh = w / 2;
int h = selectionBoundingBox.height;
int hh = h / 2;
if(px >= x - SELECTION_MARK_CENTER && px <= x + SELECTION_MARK_CENTER) {
if(py >= y - SELECTION_MARK_CENTER && py <= y + SELECTION_MARK_CENTER)
return SwingConstants.NORTH_WEST;
if(py >= y - SELECTION_MARK_CENTER + hh && py <= y + SELECTION_MARK_CENTER + hh)
return SwingConstants.WEST;
if(py >= y - SELECTION_MARK_CENTER + h && py <= y + SELECTION_MARK_CENTER + h)
return SwingConstants.SOUTH_WEST;
}
if(px >= x - SELECTION_MARK_CENTER + wh && px <= x + SELECTION_MARK_CENTER + wh) {
if(py >= y - SELECTION_MARK_CENTER && py <= y + SELECTION_MARK_CENTER)
return SwingConstants.NORTH;
if(py >= y - SELECTION_MARK_CENTER + h && py <= y + SELECTION_MARK_CENTER + h)
return SwingConstants.SOUTH;
}
if(px >= x - SELECTION_MARK_CENTER + w && px <= x + SELECTION_MARK_CENTER + w) {
if(py >= y - SELECTION_MARK_CENTER && py <= y + SELECTION_MARK_CENTER)
return SwingConstants.NORTH_EAST;
if(py >= y - SELECTION_MARK_CENTER + hh && py <= y + SELECTION_MARK_CENTER + hh)
return SwingConstants.EAST;
if(py >= y - SELECTION_MARK_CENTER + h && py <= y + SELECTION_MARK_CENTER + h)
return SwingConstants.SOUTH_EAST;
}
return selectionBoundingBox.contains(px, py) ? SwingConstants.CENTER : -1;
}
private void drawHandle(Graphics g, int x, int y) {
Color svColor = g.getColor();
g.setColor(Color.white);
g.fillRect(x - SELECTION_MARK_CENTER,
y - SELECTION_MARK_CENTER,
SELECTION_MARK_SIZE, SELECTION_MARK_SIZE);
g.setColor(svColor);
g.drawRect(x - SELECTION_MARK_CENTER,
y - SELECTION_MARK_CENTER,
SELECTION_MARK_SIZE, SELECTION_MARK_SIZE);
}
//--- empty dnd implementation ---
public void dragEnter(DropTargetDragEvent e) {}
public void dragExit(DropTargetEvent e) {}
public void dragOver(DropTargetDragEvent e) {}
public void dropActionChanged(DropTargetDragEvent e) {}
public void drop(DropTargetDropEvent e) {}
//----------------------- test stuff -------------
static int nodecnt = 0;
static int MAXNODES = 3;
static int MAXEDGES = 1;
static int NAME;
static GraphNode[] gnodes;
static String[] NLNAMES = {"Tree",
"Force Directed",
"Distance Ordered",
"Free Move"};
static String[] ROUTERNAMES = {"Straight Line", "Orthogonal"};
static String[] GNAMES = {"Random",
"Circle",
"FS Tree",
"Example 1",
"Example 2",
"Example 3",
"Example 4"};
static String[] GS = {"",
"",
"",
"joe-food,joe-dog,joe-tea,joe-cat,joe-table,table-plate/50,plate-food/30,food-mouse/100,food-dog/100,mouse-cat/150,table-cup/30,cup-tea/30,dog-cat/80,cup-spoon/50,plate-fork,dog-flea1,dog-flea2,flea1-flea2/20,plate-knife",
"zero-one,one-two,two-three,three-four,four-five,five-six,six-seven,seven-zero",
"zero-one,zero-two,zero-three,zero-four,zero-five,zero-six,zero-seven,zero-eight,zero-nine,one-ten,two-twenty,three-thirty,four-fourty,five-fifty,six-sixty,seven-seventy,eight-eighty,nine-ninety,ten-twenty/80,twenty-thirty/80,thirty-fourty/80,fourty-fifty/80,fifty-sixty/80,sixty-seventy/80,seventy-eighty/80,eighty-ninety/80,ninety-ten/80,one-two/30,two-three/30,three-four/30,four-five/30,five-six/30,six-seven/30,seven-eight/30,eight-nine/30,nine-one/30",
"a1-a2,a2-a3,a3-a4,a4-a5,a5-a6,b1-b2,b2-b3,b3-b4,b4-b5,b5-b6,c1-c2,c2-c3,c3-c4,c4-c5,c5-c6,x-a1,x-b1,x-c1,x-a6,x-b6,x-c6"};
private static ArrayList views = new ArrayList();
private static GraphNode findNode(GraphModel g, String lbl) {
Object nodes[] = g.nodes().toArray();
for (int i = 0 ; i < nodes.length; i++)
if(((DefaultGraphNode)nodes[i]).get(NAME).equals(lbl))
return (DefaultGraphNode)nodes[i];
DefaultGraphNode result = new DefaultGraphNode();
result.set(NAME, lbl);
g.add(result);
return result;
}
private static GraphNode buildTree(GraphModel g, File f) {
DefaultGraphNode node = new DefaultGraphNode();
node.set(NAME, f.getName());
g.add(node);
if(f.isDirectory()) {
File[] fs = f.listFiles();
for(int i = 0; i < fs.length; i++) {
GraphNode n = buildTree(g, fs[i]);
iiuf.util.graph.DefaultGraphEdge e = new iiuf.util.graph.DefaultGraphEdge(node, n);
e.setWeight(100);
g.add(e);
}
}
return node;
}
private static GraphNode startnode;
private static GraphModel filetree(File root) {
GraphModel result = new DefaultGraphModel();
NAME = result.nodeAttribute("name", new AttributeFactory() {
public Object newAttribute(Attributable attributable, Object[] args) {
return "";
}
});
startnode = buildTree(result, root);
GraphView[] va = getViews();
for(int j = 0; j < va.length; j++)
for(int i = 0; i < va[j].NLS.length; i++)
if(va[j].NLS[i] instanceof TreeNL)
((TreeNL)va[j].NLS[i]).setStart(startnode);
return result;
}
private static GraphModel parse(String edges) {
DefaultGraphModel result = new DefaultGraphModel();
NAME = result.nodeAttribute("name", new AttributeFactory() {
public Object newAttribute(Attributable attributable, Object[] args) {
return new String("" + nodecnt++);
}
});
for(StringTokenizer t = new StringTokenizer(edges, ",") ; t.hasMoreTokens() ; ) {
String str = t.nextToken();
int i = str.indexOf('-');
if (i > 0) {
int len = 50;
int j = str.indexOf('/');
if (j > 0) {
len = Integer.valueOf(str.substring(j+1)).intValue();
str = str.substring(0, j);
}
iiuf.util.graph.DefaultGraphEdge edge =
new iiuf.util.graph.DefaultGraphEdge(findNode(result, str.substring(0,i)), findNode(result, str.substring(i+1)));
edge.setWeight(len);
result.add(edge);
}
}
return result;
}
private static GraphModel circle() {
gnodes = new DefaultGraphNode[MAXNODES];
DefaultGraphModel g = new DefaultGraphModel();
NAME = g.nodeAttribute("name", new AttributeFactory() {
public Object newAttribute(Attributable attributable, Object[] args) {
return new String("" + nodecnt++);
}
});
for(int i = 0; i < MAXNODES; i++) {
gnodes[i] = new DefaultGraphNode();
g.add(gnodes[i]);
}
iiuf.util.graph.DefaultGraphEdge edge;
for(int i = 0; i < MAXNODES - 1; i++) {
edge = new iiuf.util.graph.DefaultGraphEdge(gnodes[i], gnodes[i + 1]);
edge.setWeight(50);
g.add(edge);
}
edge = new iiuf.util.graph.DefaultGraphEdge(gnodes[MAXNODES - 1], gnodes[0]);
edge.setWeight(50);
g.add(edge);
return g;
}
private static GraphModel randomize() {
gnodes = new DefaultGraphNode[MAXNODES];
DefaultGraphModel g = new DefaultGraphModel();
NAME = g.nodeAttribute("name", new AttributeFactory() {
public Object newAttribute(Attributable attributable, Object[] args) {
return new String("" + nodecnt++);
}
});
for(int i = 0; i < MAXNODES; i++) {
gnodes[i] = new DefaultGraphNode();
g.add(gnodes[i]);
}
for(int i = 0; i < MAXEDGES; i++) {
iiuf.util.graph.DefaultGraphEdge edge =
new iiuf.util.graph.DefaultGraphEdge(gnodes[Util.intRandom(MAXNODES)], gnodes[Util.intRandom(MAXNODES)]);
edge.setWeight(50);
g.add(edge);
}
return g;
}
static class GraphView
extends
JFrame
{
JTextField nodestf = new JTextField("" + MAXNODES);
JTextField edgestf = new JTextField("" + MAXEDGES);
NodeLayouter[] NLS = {new TreeNL(),
new ForceDirectedNL(),
new DistanceOrderedNL(),
new DefaultNL()};
GraphRouter[] ROUTERS = {new StraightLineRouter(), new OrthogonalRouter()};
EdgeMarker[][] CAPTIONS = {{new EquilateralTriangleMarker(10, Math.PI / 6.0, true, false)},
{new EquilateralTriangleMarker(10, Math.PI / 6.0, false, false)},
{new EquilateralTriangleMarker(10, Math.PI / 6.0, false, false, Color.blue, Color.green)},
{new EquilateralTriangleMarker(10, Math.PI / 6.0, false, false),
new EquilateralTriangleMarker(10, Math.PI / 6.0, false, true)},
{new EquilateralTriangleMarker(12, Math.PI / 4.0, false, false, Color.black, Color.white)},
{new LabelMarker("Hello World")},
{new LabelMarker("Hello World", HexagonalBorder.newBlackBorder())}};
double[][] CAPTIONSPOS = {{1.0},
{1.0},
{1.0},
{1.0, 0.0},
{0.5},
{0.5},
{0.5}};
GraphPanel gp;
GraphView(GraphModel model) {
super("GraphView");
gp =
new GraphPanel(
new AbstractNodeComponentFactory(gp) {
protected Component newNodeComponent(GraphNode node, Object[] args) {
return new JButton((String)node.get(NAME));
}
},
new AbstractPortFactory() {
protected GraphNodePort newPort(GraphPort port, Object[] args) {
return new GraphNodePort(0.5, 0.5, 0);
}
},
new AbstractGraphEdgeFactory() {
protected GraphEdge newGraphEdge(iiuf.util.graph.GraphEdge edge,
GraphNode fromNode, GraphPort fromPort,
GraphNode toNode, GraphPort toPort,
Object[] args) {
return new GraphEdge((Component)fromNode.get(gp.COMPONENT),
(GraphNodePort)fromPort.get(gp.GRAPH_NODE_PORT),
(Component)toNode.get(gp.COMPONENT),
(GraphNodePort)toPort.get(gp.GRAPH_NODE_PORT));
}
},
NLS[0],
ROUTERS[0]);
gp.setModel(model);
gp.addContextMenu();
JPanel params = new JPanel();
JPanel controls = new JPanel();
JFrame frame = new JFrame("Graph Test");
frame.setSize(800, 500);
frame.setLocation(Awt.centerOnScreen(frame.getSize()));
frame.getContentPane().setLayout(new BorderLayout());
frame.getContentPane().add(new JScrollPane(gp), BorderLayout.CENTER);
params.setLayout(new GridBagLayout());
params.add(new JLabel("Nodes:"), Awt.constraints(false));
nodestf = new JTextField("" + MAXNODES, 5);
params.add(nodestf, Awt.constraints(false));
JComboBox nodenl = new JComboBox(NLNAMES);
nodenl.addItemListener(new ItemListener() {
public void itemStateChanged(ItemEvent e) {
for(int i = 0; i < NLNAMES.length; i++)
if(NLNAMES[i].equals(e.getItem())) {
if(startnode != null && NLS[i] instanceof TreeNL)
((TreeNL)NLS[i]).setStart(startnode);
gp.setLayouter(NLS[i]);
break;
}
}
});
params.add(nodenl, Awt.constraints(true));
params.add(new JLabel("Edges:"), Awt.constraints(false));
edgestf = new JTextField("" + MAXEDGES, 5);
params.add(edgestf, Awt.constraints(false));
JComboBox edgert = new JComboBox(ROUTERNAMES);
edgert.addItemListener(new ItemListener() {
public void itemStateChanged(ItemEvent e) {
if(e.getStateChange() != e.SELECTED) return;
for(int i = 0; i < ROUTERNAMES.length; i++)
if(ROUTERNAMES[i].equals(e.getItem())) {
gp.setRouter(ROUTERS[i]);
break;
}
}
});
params.add(edgert, Awt.constraints(true));
params.add(new GeometryEditor(gp));
params.add(new StyleEditor(gp));
EdgeEditor ee = new EdgeEditor(gp);
for(int i = 0; i < CAPTIONS.length; i++)
ee.addMarkers(CAPTIONS[i], CAPTIONSPOS[i]);
params.add(ee);
controls.add(Swing.newCheckBox("Edit",
new ItemListener() {
public void itemStateChanged(ItemEvent e) {
gp.setEditable(((JCheckBox)e.getItemSelectable()).isSelected());
}
}));
controls.add(Swing.newCheckBox("Edge Over",
new ItemListener() {
public void itemStateChanged(ItemEvent e) {
gp.setPaintEdgesAfterNodes(((JCheckBox)e.getItemSelectable()).isSelected());
}
}));
JComboBox gshape = new JComboBox(GNAMES);
gshape.addItemListener(new ItemListener() {
public void itemStateChanged(ItemEvent e) {
if(e.getStateChange() != e.SELECTED) return;
if(GNAMES[0].equals(e.getItem())) {
MAXNODES = Integer.parseInt(nodestf.getText());
MAXEDGES = Integer.parseInt(edgestf.getText());
setModel(randomize());
} else if(GNAMES[1].equals(e.getItem())) {
MAXNODES = Integer.parseInt(nodestf.getText());
setModel(circle());
} else if(GNAMES[2].equals(e.getItem())) {
fc.setFileSelectionMode(fc.DIRECTORIES_ONLY);
if(fc.showOpenDialog((Component)e.getItemSelectable()) == fc.APPROVE_OPTION)
setModel(filetree(fc.getSelectedFile()));
} else {
for(int i = 2; i < GNAMES.length; i++)
if(GNAMES[i].equals(e.getItem())) {
setModel(parse(GS[i]));
break;
}
}
}
});
controls.add(gshape);
controls.add(Swing.newButton("New View",
new ActionListener() {
public void actionPerformed(ActionEvent e) {
views.add(new GraphView(gp.getModel()));
}
}));
controls.add(Swing.newButton("Quit",
new ActionListener() {
public void actionPerformed(ActionEvent e) {
System.exit(0);
}
}));
frame.getContentPane().add(params, BorderLayout.NORTH);
frame.getContentPane().add(controls, BorderLayout.SOUTH);
frame.setVisible(true);
}
private void setModel(GraphModel model) {
GraphView[] va = getViews();
for(int i = 0; i < va.length; i++)
va[i].gp.setModel(model);
}
}
private static GraphView[] getViews() {
return (GraphView[])views.toArray(new GraphView[views.size()]);
}
static JFileChooser fc = new JFileChooser();
public static void main(String[] argv) {
views.add(new GraphView(randomize()));
}
}
class GraphLayout
implements
LayoutManager2
{
private Dimension dimension = new Dimension();
private NodeLayouter layouter;
GraphLayout(NodeLayouter layouter) {
setLayouter(layouter);
}
void setLayouter(NodeLayouter layouter_) {
if(layouter != null) layouter.deactivate();
layouter = layouter_;
layouter.activate();
}
NodeLayouter getLayouter() {
return layouter;
}
public void addLayoutComponent(Component comp, Object constraints) {
// System.out.println("addLayoutComponent");
}
public float getLayoutAlignmentX(Container target) {
// System.out.println("getLayoutAlignmentX");
return 0;
}
public float getLayoutAlignmentY(Container target) {
// System.out.println("getLayoutAlignmentY");
return 0;
}
public void invalidateLayout(Container target) {
// System.out.println("invalidateLayout");
}
public Dimension maximumLayoutSize(Container target) {
return dimension;
}
public void addLayoutComponent(String name, Component comp) {
layoutContainer(comp.getParent());
}
public void layoutContainer(Container parent_) {
GraphPanel parent = (GraphPanel)parent_;
if(parent.layoutBlock != 0) return;
Dimension oldDim = parent.getSize();
dimension = layouter.layout(parent, parent.getModel());
dimension = new Dimension(Math.max(dimension.width, parent.getWidth()), Math.max(dimension.height, parent.getHeight()));
if(parent.edit)
parent.editor.setSize(dimension);
if(oldDim.width != dimension.width || oldDim.height != dimension.height)
parent.revalidate();
}
public Dimension minimumLayoutSize(Container parent) {
return dimension;
}
public Dimension preferredLayoutSize(Container parent) {
return dimension;
}
public void removeLayoutComponent(Component comp) {
layoutContainer(comp.getParent());
}
}
class SelectionModel
implements
SetSelectionModel
{
private HashSet selection = new HashSet();
private EventListenerList listeners = new EventListenerList();
private ChangeEvent EVENT;
SelectionModel() {
EVENT = new ChangeEvent(this);
}
public void add(Object node) {
selection.add(node);
fireChangeEvent();
}
public void addAll(Collection nodes) {
selection.addAll(nodes);
fireChangeEvent();
}
public void remove(Object node) {
selection.remove(node);
fireChangeEvent();
}
public void removeAll(Collection nodes) {
selection.removeAll(nodes);
fireChangeEvent();
}
public void clearSelection() {
selection = new HashSet();
fireChangeEvent();
}
public boolean isEmpty() {
return selection.isEmpty();
}
public Object[] getSelection() {
return selection.toArray();
}
public Object[] getSelection(Class cls) {
HashSet subsel = new HashSet();
Object[] sel = selection.toArray();
for(int i = 0; i < sel.length; i++)
if(cls.isAssignableFrom(sel[i].getClass()))
subsel.add(sel[i]);
Object[] result = (Object[])Array.newInstance(cls, subsel.size());
int j = 0;
for(Iterator i = subsel.iterator(); i.hasNext(); j++)
result[j] = i.next();
return result;
}
public boolean isSelected(Object o) {
return selection.contains(o);
}
private synchronized void fireChangeEvent() {
ChangeListener[] l = (ChangeListener[])listeners.getListeners(ChangeListener.class);
for(int i = 0; i < l.length; i++)
l[i].stateChanged(EVENT);
}
public synchronized void addChangeListener(ChangeListener listener) {
listeners.add(ChangeListener.class, listener);
}
public synchronized void addChangeListener(ChangeListener listener, boolean weak) {
listeners.add(ChangeListener.class, listener, weak);
}
public synchronized void removeChangeListener(ChangeListener listener) {
listeners.remove(ChangeListener.class, listener);
}
public int size() {
return selection.size();
}
public int size(Class cls) {
return getSelection(cls).length;
}
}
/*
$Log: GraphPanel.java,v $
Revision 1.1 2002/07/11 12:09:52 ohitz
Initial checkin
Revision 1.15 2001/04/30 07:33:17 schubige
added webcom to cvstree
Revision 1.14 2001/03/21 19:34:06 schubige
started with dom stuff
Revision 1.13 2001/03/20 14:28:30 schubige
enhanced sample soundlet, added format popup
Revision 1.12 2001/03/19 16:13:26 schubige
soundium without drag cursor
Revision 1.11 2001/03/16 18:08:20 schubige
improved orthogonal router
Revision 1.10 2001/03/13 13:41:05 schubige
Fixed some graph panel and soundium bugs
Revision 1.9 2001/03/12 17:52:00 schubige
Added version support to sourcewatch and enhanced soundium
Revision 1.8 2001/03/11 17:59:38 schubige
Fixed various soundium and iiuf.swing.graph bugs
Revision 1.7 2001/03/09 21:24:58 schubige
Added preferences to edge editor
Revision 1.6 2001/03/09 15:30:51 schubige
Added markers to graph panel
Revision 1.5 2001/03/07 17:36:28 schubige
soundium properties panel beta
Revision 1.4 2001/03/05 17:55:07 schubige
Still working on soundium properties panel
Revision 1.3 2001/02/26 15:57:22 schubige
Again changes in SoundEngine.x, added some todos to graph panel & co
Revision 1.2 2001/02/23 17:23:11 schubige
Added loop source to soundium and fxed some bugs along
Revision 1.1 2001/02/17 09:54:21 schubige
moved graph stuff to iiuf.swing.graph, started work on rotatable GraphNodeComponents
Revision 1.18 2001/02/16 13:47:38 schubige
Adapted soundium to new rtsp version
Revision 1.17 2001/02/15 16:00:43 schubige
Improved graph panel, fixed some soundium bugs
Revision 1.16 2001/02/14 17:25:37 schubige
implemented resizing, select all and key-shortcuts for graph panel
Revision 1.15 2001/02/13 14:49:06 schubige
started work on gui - engine connection
Revision 1.14 2001/02/12 17:50:05 schubige
still working on soundium gui
Revision 1.13 2001/02/11 16:25:39 schubige
working on soundium
Revision 1.12 2001/01/12 08:26:20 schubige
TJGUI update and some TreeView bug fixes
Revision 1.11 2001/01/04 16:28:38 schubige
Header update for 2001 and DIUF
Revision 1.10 2001/01/04 12:12:36 schubige
fixed bugs reported by iiuf.dev.java.Verify
Revision 1.9 2001/01/04 09:58:49 schubige
fixed bugs reported by iiuf.dev.java.Verify
Revision 1.8 2001/01/03 15:23:50 schubige
graph stuff beta
Revision 1.7 2000/12/29 08:03:55 schubige
SourceWatch beta debug iter 1
Revision 1.6 2000/12/28 09:29:10 schubige
SourceWatch beta
Revision 1.5 2000/12/20 09:46:39 schubige
TJGUI update
Revision 1.4 2000/12/18 12:39:09 schubige
Added ports to iiuf.util.graph
Revision 1.3 2000/11/10 10:46:53 schubige
iiuf tree cleanup iter 3
Revision 1.2 2000/10/09 06:47:57 schubige
Updated logger stuff
Revision 1.1 2000/08/17 16:22:14 schubige
Swing cleanup & TreeView added
Revision 1.2 2000/07/28 12:06:54 schubige
Graph stuff update
Revision 1.1 2000/07/14 13:56:20 schubige
Added graph view stuff
*/