package com.jedics.graph.visual;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.util.Hashtable;
import javax.swing.JComponent;
import javax.swing.JLayeredPane;
import javax.swing.Scrollable;
import javax.swing.SwingConstants;
import javax.swing.SwingUtilities;
import com.jedics.graph.Edge;
import com.jedics.graph.GraphHandler;
import com.jedics.graph.Vertex;
@SuppressWarnings("serial")
public class GraphCanvas extends JLayeredPane implements Scrollable {
private GraphHandler handler;
private EdgePanel edgePanel;
Hashtable<Integer, VertexComponent> vertices;
Hashtable<Integer, EdgeComponent> edges;
private boolean flowVisible;
private boolean weightVisible;
private VertexMouseHandler vmh;
private GraphCanvasSizeHandler sizeHandler;
private int maxUnitIncrement;
public GraphCanvas(GraphHandler handler) {
this.handler = handler;
setFocusable(false);
setOpaque(true);
setBackground(Color.white);
maxUnitIncrement = 10;
vertices = new Hashtable<Integer, VertexComponent>();
edges = new Hashtable<Integer, EdgeComponent>();
vmh = new VertexMouseHandler(handler, this);
sizeHandler = new GraphCanvasSizeHandler(this);
weightVisible = true;
flowVisible = false;
edgePanel = new EdgePanel();
edgePanel.addMouseListener(new GraphCanvasMouseHandler(this));
addComponentListener(new ComponentAdapter() {
public void componentResized(ComponentEvent e) {
edgePanel.resize();
repaint();
}
});
add(edgePanel, 998);
setBackground(Color.white);
}
/**
* Return the edge that lies at this point.
*
* @param p
* Point to query for the <code>Edge</code>
* @return the key of the Edge at that lies < 3 pixels away from this point.
* <code>null</code> otherwise.
*/
public int getEdgeAtPoint(Point p) {
double dist = 3.0;
int least = -1;
EdgeComponent e;
for (Integer i : edges.keySet()) {
e = edges.get(i);
if (e.distance(p) < dist) {
dist = e.distance(p);
least = i;
}
}
return least;
}
/**
* Select the given Edge. Typically this would entail showing information
* about <code>Edge</code> in the <code>InformationPanel</code>
*
* @param p the point where to check for an Edge.
*/
public boolean selectEdgeAtPoint(Point p) {
int i = getEdgeAtPoint(p);
if (i == -1)
return false;
handler.fireEdgeSelected(i);
return true;
}
public void removeEdgeAtPoint(Point p) {
Integer i = getEdgeAtPoint(p);
if (i == null)
return;
handler.removeEdge(i);
}
public void removeEdge(int id) {
edges.remove(id);
edgePanel.repaint();
}
public void addEdge(Edge edge) {
if(edge == null)
return;
EdgeComponent ec = createEdgeComponent(edge);
ec.setWeightVisible(weightVisible);
ec.setFlowVisible(flowVisible);
edges.put(edge.getId(), ec);
edge.setEdgeComponent(ec);
edgePanel.repaint();
}
int createEdge(int u, int v) {
Edge edge = handler.createEdge(u, v);
if(edge == null)
return -1;
addEdge(edge);
return edge.getId();
}
private EdgeComponent createEdgeComponent(Edge edge) {
return new LineEdgeComponent(edge, vertices.get(edge.getStart().getId()), vertices.get(edge.getEnd().getId()), this);
}
public int createVertex(int x, int y) {
return addVertex(handler.createVertex(), x, y);
}
public int addVertex(Vertex vertex, int x, int y) {
VertexComponent v = new VertexComponent(vertex);
vertex.setVertexComponent(v);
vertices.put(v.getId(), v);
v.addMouseListener(vmh);
v.addMouseMotionListener(vmh);
v.addVertexChangeListener(sizeHandler);
v.positionCentered(x, y);
add(v, 0);
repaint();
return vertex.getId();
}
public void removeVertex(Integer i) {
remove(vertices.remove(i));
repaint();
}
public void clear() {
setIgnoreRepaint(true);
revalidate();
for(Integer i : vertices.keySet()) {
remove(vertices.get(i));
}
vertices.clear();
edges.clear();
setIgnoreRepaint(false);
repaint();
}
public void reset() {
setIgnoreRepaint(true);
for (Integer i : vertices.keySet()) {
vertices.get(i).setBackground(Color.white);
vertices.get(i).setForeground(Color.black);
vertices.get(i).setBorderColor(Color.black);
vertices.get(i).setEastText("");
vertices.get(i).setSouthText("");
}
for (Integer i : edges.keySet()) {
edges.get(i).setBackground(Color.white);
edges.get(i).setForeground(Color.black);
}
setIgnoreRepaint(false);
SwingUtilities.invokeLater(new Runnable() {
public void run() {
repaint();
}
});
fireSelected();
}
void fireSelected() {
handler.fireCanvasSelected();
}
public void resetColors() {
setIgnoreRepaint(true);
for (Integer i : vertices.keySet()) {
vertices.get(i).setBackground(Color.white);
vertices.get(i).setForeground(Color.black);
vertices.get(i).setBorderColor(Color.black);
}
for (Integer i : edges.keySet()) {
edges.get(i).setBackground(Color.white);
edges.get(i).setForeground(Color.black);
}
setIgnoreRepaint(false);
repaint();
}
public void resetData() {
setIgnoreRepaint(true);
for (Integer i : vertices.keySet()) {
vertices.get(i).setEastText("");
vertices.get(i).setSouthText("");
}
setIgnoreRepaint(false);
repaint();
}
boolean isEdgeWeightVisible() {
return weightVisible;
}
boolean isEdgeFlowVisible() {
return flowVisible;
}
void setEdgeWeightVisible(boolean b) {
if(b == weightVisible) return;
weightVisible = b;
for (Integer i : edges.keySet()) {
edges.get(i).setWeightVisible(b);
}
edgePanel.repaint();
}
void setEdgeFlowVisible(boolean b) {
if(b == flowVisible) return;
flowVisible = b;
for (Integer i : edges.keySet()) {
edges.get(i).setFlowVisible(b);
}
edgePanel.repaint();
}
public void paintComponent(Graphics _g) {
Graphics2D g = (Graphics2D)_g;
g.translate(-g.getTransform().getTranslateX(), -g.getTransform().getTranslateY());
for (Integer i : edges.keySet()) {
edges.get(i).draw(g);
}
}
private class EdgePanel extends JComponent {
private EdgePanel() {
setOpaque(false);
}
private void resize() {
setLocation(0, 0);
setSize(GraphCanvas.this.getSize());
repaint();
}
}
@Override
public Dimension getPreferredScrollableViewportSize() {
return getPreferredSize();
}
@Override
public int getScrollableBlockIncrement(Rectangle visibleRect,
int orientation, int direction) {
int currentPosition = 0;
if (orientation == SwingConstants.HORIZONTAL)
currentPosition = visibleRect.x;
else
currentPosition = visibleRect.y;
if (direction < 0) {
int newPosition = currentPosition - (currentPosition / maxUnitIncrement) * maxUnitIncrement;
return (newPosition == 0) ? maxUnitIncrement : newPosition;
} else {
return ((currentPosition / maxUnitIncrement) + 1) * maxUnitIncrement - currentPosition;
}
}
@Override
public boolean getScrollableTracksViewportHeight() {
return false;
}
@Override
public boolean getScrollableTracksViewportWidth() {
return false;
}
@Override
public int getScrollableUnitIncrement(Rectangle visibleRect,
int orientation, int direction) {
if (orientation == SwingConstants.HORIZONTAL)
return visibleRect.width - maxUnitIncrement;
else
return visibleRect.height - maxUnitIncrement;
}
}