/*
* Created on Apr 19, 2005
*/
package org.mindswap.swoop.utils.graph;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Font;
import java.awt.Paint;
import java.awt.Shape;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.geom.Point2D;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComboBox;
import javax.swing.JLabel;
import javax.swing.JPanel;
import org.mindswap.swoop.SwoopModel;
import org.semanticweb.owl.model.OWLOntology;
import edu.uci.ics.jung.graph.Edge;
import edu.uci.ics.jung.graph.Graph;
import edu.uci.ics.jung.graph.Vertex;
import edu.uci.ics.jung.graph.decorators.ConstantVertexAspectRatioFunction;
import edu.uci.ics.jung.graph.decorators.ConstantVertexStringer;
import edu.uci.ics.jung.graph.decorators.EdgeShape;
import edu.uci.ics.jung.graph.decorators.EdgeShapeFunction;
import edu.uci.ics.jung.graph.decorators.EllipseVertexShapeFunction;
import edu.uci.ics.jung.graph.decorators.VertexAspectRatioFunction;
import edu.uci.ics.jung.graph.decorators.VertexFontFunction;
import edu.uci.ics.jung.graph.decorators.VertexPaintFunction;
import edu.uci.ics.jung.graph.decorators.VertexShapeFunction;
import edu.uci.ics.jung.graph.decorators.VertexSizeFunction;
import edu.uci.ics.jung.graph.decorators.VertexStringer;
import edu.uci.ics.jung.graph.impl.DirectedSparseEdge;
import edu.uci.ics.jung.graph.impl.DirectedSparseGraph;
import edu.uci.ics.jung.graph.impl.DirectedSparseVertex;
import edu.uci.ics.jung.utils.Pair;
import edu.uci.ics.jung.utils.UserDataContainer;
import edu.uci.ics.jung.visualization.FRLayout;
import edu.uci.ics.jung.visualization.GraphZoomScrollPane;
import edu.uci.ics.jung.visualization.ISOMLayout;
import edu.uci.ics.jung.visualization.Layout;
import edu.uci.ics.jung.visualization.PickSupport;
import edu.uci.ics.jung.visualization.PickedInfo;
import edu.uci.ics.jung.visualization.PluggableRenderer;
import edu.uci.ics.jung.visualization.ShapePickSupport;
import edu.uci.ics.jung.visualization.SpringLayout;
import edu.uci.ics.jung.visualization.VisualizationViewer;
import edu.uci.ics.jung.visualization.ZoomPanGraphMouse;
import edu.uci.ics.jung.visualization.contrib.CircleLayout;
import edu.uci.ics.jung.visualization.contrib.DAGLayout;
import edu.uci.ics.jung.visualization.contrib.KKLayout;
/**
* @author Evren Sirin
*
*/
public class GenericGraph extends JPanel implements ActionListener {
// just a dummy variable used a key
private static String DATA = "DATA";
private static int MIN = 20;
private static int MAX = 100;
private static final EdgeShapeFunction LINE = new EdgeShape.Line();
private static final EdgeShapeFunction CURVE = new EdgeShape.QuadCurve();
private final EdgeShapeFunction CURVED_LINE = new EdgeShapeFn();
private final UserDataContainer.CopyAction SHARE = new UserDataContainer.CopyAction.Shared();
private final VertexStringer SHORT_LABEL = new VertexLabel( true );
private final VertexStringer LONG_LABEL = new VertexLabel( false );
private final VertexStringer NO_LABEL = new ConstantVertexStringer("");
protected SwoopModel model;
protected DirectedSparseGraph graph ;
protected VisualizationViewer vv;
private PluggableRenderer pr;
private VertexSize vSize = new VertexSize();
private VertexFont vFont = new VertexFont();
private VertexAspectRatioFunction vAspect = new ConstantVertexAspectRatioFunction( 1.0f );
private VertexShapeFunction vShape = new EllipseVertexShapeFunction( vSize, vAspect );
private EdgeShapeFunction eShape = CURVED_LINE;
private GraphProperties props;
public GenericGraph(SwoopModel model, Object obj, GraphProperties props) {
this( model, Collections.singleton( obj ), props );
}
public GenericGraph(SwoopModel model, Collection partitions, GraphProperties props) {
this.model = model;
this.props = props;
graph = new DirectedSparseGraph();
for(Iterator i = partitions.iterator(); i.hasNext();)
{
Object obj = i.next();
addToGraph(graph, obj);
}
setupUI();
}
protected void setupUI() {
pr = new PluggableRenderer();
Layout layout = new FRLayout( graph );
vv = new VisualizationViewer(layout, pr);
vv.setPickSupport(new ShapePickSupport(vv));
pr.setVertexPaintFunction( new VertexColor( vv ) );
pr.setVertexShapeFunction( vShape );
pr.setVertexStringer( NO_LABEL );
pr.setVertexLabelCentering( true );
pr.setVertexFontFunction( vFont );
pr.setEdgeShapeFunction( eShape );
vSize.setGraph( graph );
GraphZoomScrollPane scrollPane = new GraphZoomScrollPane(vv);
ZoomPanGraphMouse gm = new ZoomPanGraphMouse(vv);
vv.setGraphMouse( gm );
vv.setToolTipListener(new VertexTips());
vv.setBackground(Color.white);
setLayout( new BorderLayout() );
add( scrollPane );
add( getControlPanel(), BorderLayout.SOUTH);
}
protected JPanel getControlPanel() {
JPanel controlPanel = new JPanel();
controlPanel.setLayout(new BoxLayout(controlPanel, BoxLayout.Y_AXIS));
String[] layouts = {
"KK Layout", "FR Layout", "Circle Layout",
"Spring Layout", "ISOM Layout", "DAG Layout"};
JComboBox jcb = new JComboBox( layouts );
jcb.setActionCommand("layout");
jcb.addActionListener(this);
jcb.setSelectedItem( props.getPreferredLayout() );
Box layoutPanel = Box.createHorizontalBox();
layoutPanel.add(new JLabel("Graph Layout"));
layoutPanel.add(jcb);
Box labelPanel = Box.createHorizontalBox();
JCheckBox v_labels = new JCheckBox("Show partition labels");
v_labels.setAlignmentX(Component.LEFT_ALIGNMENT);
v_labels.setActionCommand("label");
v_labels.addActionListener(this);
v_labels.setSelected( false );
JCheckBox v_font = new JCheckBox("Use bold font");
v_font.setActionCommand("font");
v_font.addActionListener(this);
v_font.setSelected( false );
labelPanel.add(v_labels);
labelPanel.add(v_font);
JCheckBox v_size = new JCheckBox("Scale nodes with respect to number of entities in the partition");
v_size.setAlignmentX(Component.LEFT_ALIGNMENT);
v_size.addActionListener(this);
v_size.setActionCommand("scale");
v_size.setSelected( true );
JCheckBox eShape = new JCheckBox("Do not overlap inverse edges");
eShape.setAlignmentX(Component.LEFT_ALIGNMENT);
eShape.addActionListener(this);
eShape.setActionCommand("inverseEdge");
eShape.setSelected( true );
JButton plus = new JButton("+");
plus.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
vv.scale(1.1, 1.1);
}
});
JButton minus = new JButton("-");
minus.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
vv.scale(0.9,0.9);
}
});
JLabel zoomLabel = new JLabel("Zoom");
zoomLabel.setAlignmentX(Component.LEFT_ALIGNMENT);
Box zoomPanel = Box.createHorizontalBox();
zoomPanel.add(zoomLabel);
zoomPanel.add(plus);
zoomPanel.add(minus);
controlPanel.add(labelPanel);
controlPanel.add(v_size);
controlPanel.add(eShape);
controlPanel.add(zoomPanel);
controlPanel.add(layoutPanel);
return controlPanel;
}
public void actionPerformed(ActionEvent e)
{
String cmd = e.getActionCommand();
if( cmd.equals("label") ) {
VertexStringer vs = ((JCheckBox) e.getSource()).isSelected()
? SHORT_LABEL
: NO_LABEL;
pr.setVertexStringer( vs );
}
else if( cmd.equals("inverseEdge") ) {
EdgeShapeFunction es = ((JCheckBox) e.getSource()).isSelected()
? CURVED_LINE
: LINE;
pr.setEdgeShapeFunction( es );
}
else if( cmd.equals("scale") ) {
vSize.setScaling( ((JCheckBox) e.getSource()).isSelected() );
}
else if( cmd.equals("font") ) {
vFont.setBold( ((JCheckBox) e.getSource()).isSelected() );
}
else if( cmd.equals("layout") ) {
String layoutName = ((JComboBox) e.getSource()).getSelectedItem().toString();
if( layoutName.startsWith("KK") )
vv.setGraphLayout( new KKLayout( graph ) );
else if( layoutName.startsWith("Spring") )
vv.setGraphLayout( new SpringLayout( graph ) );
else if( layoutName.startsWith("Circle") )
vv.setGraphLayout( new CircleLayout( graph ) );
else if( layoutName.startsWith("FR") )
vv.setGraphLayout( new FRLayout( graph ) );
else if( layoutName.startsWith("ISOM") )
vv.setGraphLayout( new ISOMLayout( graph ) );
else if( layoutName.startsWith("DAG") )
vv.setGraphLayout( new DAGLayout( graph ) );
else
throw new RuntimeException("Unknown layout");
}
vv.repaint();
}
protected DirectedSparseVertex addToGraph(DirectedSparseGraph graph, Object obj)
{
DirectedSparseVertex node = (DirectedSparseVertex) graph.getUserDatum( obj );
if(node == null)
{
node = new DirectedSparseVertex();
node.setUserDatum( DATA, obj, SHARE );
graph.setUserDatum( obj, node, SHARE );
graph.addVertex( node );
Collection linkedOnts = props.getLinkedElements( obj );
for(Iterator i = linkedOnts.iterator(); i.hasNext();) {
Object linkedObj = i.next();
DirectedSparseVertex linkedNode = addToGraph(graph, linkedObj);
DirectedSparseEdge edge = new DirectedSparseEdge(node, linkedNode);
graph.addEdge(edge);
}
}
return node;
}
private final class VertexSize implements VertexSizeFunction {
boolean scale = true;
int maxSize = Integer.MIN_VALUE;
int minSize = Integer.MAX_VALUE;
double factor = 1.0;
public VertexSize() {
}
public void setGraph( Graph g ) {
for(Iterator i = g.getVertices().iterator(); i.hasNext();) {
Vertex vertex = (Vertex) i.next();
int size = props.getSize( vertex.getUserDatum( DATA ) );
maxSize = Math.max( maxSize, size );
minSize = Math.min( minSize, size );
}
if( maxSize == minSize )
factor = 0.0;
else
factor = (double) (MAX - MIN) / (maxSize - minSize);
}
public void setScaling(boolean scale) {
this.scale = scale;
}
public boolean getScaling() {
return scale;
}
public int getSize( Vertex vertex ) {
if( scale ) {
int size = props.getSize( vertex.getUserDatum( DATA ) );
return ((int) ((size - minSize) * factor)) + MIN;
}
else
return MIN;
}
}
private class VertexLabel implements VertexStringer {
private boolean shortLabel;
public VertexLabel(boolean qname) {
this.shortLabel = qname;
}
public String getLabel(Vertex vertex) {
Object obj = vertex.getUserDatum( DATA );
if( shortLabel )
return props.getShortName( obj );
else
return props.getLongName( obj );
}
}
public class VertexTips implements VisualizationViewer.ToolTipListener {
public VertexTips() {
}
public String getToolTipText(MouseEvent e) {
PickSupport pickSupport = vv.getPickSupport();
Point2D p = vv.transform(e.getPoint());
Vertex v = pickSupport.getVertex(p.getX(), p.getY());
if (v != null) {
return LONG_LABEL.getLabel( v );
} else {
Edge edge = pickSupport.getEdge(p.getX(), p.getY());
if(edge != null) {
return edge.toString();
}
return "<html><center>Use the mouse wheel to zoom<p>Click and Drag the mouse to pan</center></html>";
}
}
}
private final class VertexColor implements VertexPaintFunction {
protected PickedInfo pi;
public VertexColor(VisualizationViewer vv) {
this.pi = vv.getPickedState();
}
public Paint getDrawPaint(Vertex v) {
return pi.isPicked(v)
? Color.YELLOW
: Color.BLACK;
}
public Paint getFillPaint(Vertex v) {
if( v.getOutEdges().isEmpty() ) {
if( v.getInEdges().isEmpty() )
return Color.GREEN;
else
return Color.BLUE;
}
else
return Color.RED;
}
}
private final static class VertexFont implements VertexFontFunction {
protected boolean bold = false;
Font f = new Font("Helvetica", Font.PLAIN, 12);
Font b = new Font("Helvetica", Font.BOLD, 12);
public void setBold(boolean bold) {
this.bold = bold;
}
public Font getFont(Vertex v) {
return bold ? b : f;
}
}
private class EdgeShapeFn implements EdgeShapeFunction {
public Shape getShape(Edge edge) {
Pair pair = edge.getEndpoints();
Vertex from = (Vertex) pair.getFirst();
Vertex to = (Vertex) pair.getSecond();
if( to.findEdge( from ) == null )
return LINE.getShape( edge );
else
return CURVE.getShape( edge );
}
public void setControlOffsetIncrement(float inc) {
LINE.setControlOffsetIncrement( inc );
CURVE.setControlOffsetIncrement( inc );
}
}
}