package gve.calc.graph;
import awt.*;
import awt.Button;
import java.awt.*;
import java.awt.event.*;
import gve.calc.formula.*;
/* This View handles the mouse on itself when dragging. Strange though:
* when we are MouseMotionListener, our FormulaView does not receive any
* Mouse events anymore, so we have to listen to them ourselves. */
public class GraphView extends Component
implements MouseMotionListener,MouseListener,ActionListener,View,MouseSensitive,CreatesPopup,ActivationListener {
public final static byte ADD_VERTEX = 1;
public final static byte REMOVE_VERTEX = 2;
public final static byte TRACE_MOUSE = 3; // drag: move a vertex; move: draw arrow
public final static byte MOVE_VERTEX = 4; // user is dragging vertex
public final static byte DRAW_ARROW = 5;
public final static byte RESIZE = 6; // resize in lower right corner
public static final int VERTEXDIAM = 10;
int mouseint; // TRACE_MOUSE -> idx of indicated vertex
int mouse2,mouse3; // DRAW_ARROW -> position of arrow
int width = 100, height = 100;
private byte mode;
private Graph model;
public GraphView(Graph gr) {
model = gr;
MVC.registerView(model,this);
}
public Object getModel() { return model; }
public void updateView(Object with) {
repaint();
}
public void deactivate() {
removeMouseMotionListener(this);
removeMouseListener(this);
mode = 0;
repaint();
}
public void activate() { repaint(); }
private void drawRubberArrow(int x,int y) {
Graphics g = getGraphics();
g.setXORMode(Color.white);
g.drawLine(model.getXcoord(mouseint),model.getYcoord(mouseint),x,y);
g.setPaintMode();
}
public boolean mousePressed(int flags,int x,int y) {
//System.out.println("Graph mousePressed");
if ((flags & Part.Mouse_RIGHT) != 0) return false;
switch (mode) {
case 0: {
if (x>=width-VERTEXDIAM && y>=height-VERTEXDIAM) {
// Click in resize area
mode = GraphView.RESIZE;
addMouseMotionListener(this);
addMouseListener(this);
setCursor(Cursor.getPredefinedCursor(Cursor.SE_RESIZE_CURSOR));
return true; // we will handle mouse drag on our own
}
int idx = model.vertexAt(x,y);
if (idx >= 0) {
// Click on a vertex => move it or draw an arrow originating from it
mouseint = idx;
mode = GraphView.TRACE_MOUSE;
addMouseMotionListener(this);
addMouseListener(this);
return true;
}
break;
}
}
return false; // we don't want to drag mouse.
}
public void mouseDown(int flags,int x,int y) {
//System.out.println("GraphView mousedn mode="+mode);
switch (mode) {
case ADD_VERTEX:
if (model.vertexAt(x,y) >= 0) break;
model.enlarge(1);
int size = model.getSize();
model.setXcoord(size-1,x);
model.setYcoord(size-1,y);
break;
case REMOVE_VERTEX: {
int idx = model.vertexAt(x,y);
if (idx < 0) return;
if (model.getSize() == 0) mode = 0;
model.removeVertex(idx);
break;
}
case 0: {
// Rectangle r = (Rectangle)f.positions.get(this);
// if (x>=r.width-VERTEXDIAM && y>=r.height-VERTEXDIAM) {
// view.mode = GraphView.RESIZE;
// f.comp.addMouseMotionListener(view);
// f.comp.setCursor(Cursor.getPredefinedCursor(Cursor.SE_RESIZE_CURSOR));
// return;
// }
// uncommented 24-may-2000
int idx = model.vertexAt(x,y);
if (idx >= 0) {
mouseint = idx;
mode = TRACE_MOUSE;
addMouseMotionListener(this);
}
break;
}
case TRACE_MOUSE:
// uncomment 24-may-2000
// f.comp.removeMouseMotionListener(view);
break;
case DRAW_ARROW: {
// draw arrow
int idx = model.vertexAt(x,y);
if (idx >= 0)
model.setAdjacent(mouseint,idx,!model.isAdjacent(mouseint,idx));
else repaint(); // remove rubber-line
mode = 0;
removeMouseMotionListener(this);
removeMouseListener(this);
break;
}
}
}
public void mouseMoved(MouseEvent evt) {
//System.out.println("MouseMoved "+mode);
if (mode == MOVE_VERTEX) { // Stop dragging this vertex
removeMouseMotionListener(this);
removeMouseListener(this);
mode = 0;
return;
} else if (mode == TRACE_MOUSE) {
mode = DRAW_ARROW;
mouse2 = mouse3 = -1;
} else if (mode == DRAW_ARROW) {
}
else return;
// drag a vertex
int x = evt.getX();
int y = evt.getY();
if (mouse2 >= 0) drawRubberArrow(mouse2,mouse3); // erase old arrow
drawRubberArrow(mouse2=x,mouse3=y);
}
public void mouseDragged(MouseEvent evt) {
System.out.println("MouseDragged "+mode);
int x = evt.getX();
int y = evt.getY();
switch (mode) {
case TRACE_MOUSE:
// BUG: never gets here!
// If dragged far enough, the user wants to move a vertex
if (Math.abs(model.getXcoord(mouseint)-x) + Math.abs(model.getYcoord(mouseint)-y) >= 4) {
mode = MOVE_VERTEX;
model.setXcoord(mouseint,x);
model.setYcoord(mouseint,y);
}
break;
case MOVE_VERTEX:
model.setXcoord(mouseint,x);
model.setYcoord(mouseint,y);
break;
case RESIZE:
width = x; height = y;
invalidate();
FormulaView.get(this).validate();
break;
}
}
public Dimension getPreferredSize() {
if (width < VERTEXDIAM*2) width = VERTEXDIAM*2;
if (height < VERTEXDIAM*2) height = VERTEXDIAM*2;
return new Dimension(width,height);
}
public Dimension getMinimumSize() { return getPreferredSize(); }
public void paint(Graphics g) {
g.drawRect(0,0,width-1,height-1);
FormulaView view = FormulaView.get(this);
if (this == view.getCursorComp()) {
g.drawLine(1,1,width-2,1);
g.drawLine(1,1,1,height-2);
g.drawLine(width-2,1,width-2,height-10);
g.drawLine(1,height-2,width-10,height-2);
g.drawLine(width-2,height-10,width-10,height-2);
}
String str = null;
if (mode == GraphView.ADD_VERTEX)
str = "Add vertex";
else if (mode == GraphView.REMOVE_VERTEX)
str = "Remove vertex";
if (str != null) {
FontMetrics fm = g.getFontMetrics();
g.drawString(str,2,3+fm.getAscent());
}
paintGraph(g);
}
Utility method, e.g. for GraphMorphismView |
public void paintGraph(Graphics g) {
int size = model.getSize();
for (int i = 0; i < size; i++) { // Paint vertices
g.drawOval(model.getXcoord(i)-VERTEXDIAM/2,model.getYcoord(i)-VERTEXDIAM/2,VERTEXDIAM,VERTEXDIAM);
}
for (int i = 0; i < size; i++) { // Paint arrows
for (int j = 0; j < size; j++) {
if (!model.isAdjacent(i,j)) continue;
drawArrow(g,i,j);
}
}
}
public Component createPopup(int x,int y) {
Panel pop = new Panel();
pop.setLayout(new GridPackLayout(0,1));
Button b;
pop.add(b = new Button(mode==GraphView.ADD_VERTEX ? "Stop adding vertices" : "Add vertex"));
b.addActionListener(this);
if (model.getSize() > 0) {
pop.add(b = new Button(mode==GraphView.REMOVE_VERTEX ? "Stop removing vertices" : "Remove vertex"));
b.addActionListener(this);
}
pop.add(b = new Button(model.isDirected() ? "Make undirected" : "Make directed"));
b.addActionListener(this);
return pop;
}
public void actionPerformed(ActionEvent e) {
String cmd = e.getActionCommand();
if (cmd.equals("Add vertex")) {
if (mode == ADD_VERTEX) mode = 0;
else mode = ADD_VERTEX;
repaint();
} else if (cmd.equals("Remove vertex")) {
if (mode == REMOVE_VERTEX) mode = 0;
else mode = REMOVE_VERTEX;
repaint();
} else if (cmd.equals("Make undirected")) {
model.setDirected(false);
} else if (cmd.equals("Make directed")) {
model.setDirected(true);
} else if (cmd.equals("Stop adding vertices") || cmd.equals("Stop removing vertices")) {
mode = 0;
repaint();
}
}
public void drawArrow(Graphics g,int v1,int v2) {
int x1 = model.getXcoord(v1), y1 = model.getYcoord(v1);
if (v1 == v2) {
// PENDING
if (!model.isDirected()) {
return;
}
return;
}
int x2 = model.getXcoord(v2), y2 = model.getYcoord(v2);
if (!model.isDirected()) {
g.drawLine(x1,y1,x2,y2);
return;
}
float len = (float)Math.sqrt((x1-x2)*(x1-x2) + (y1-y2)*(y1-y2));
// (dirX,dirY) is richvector van p1->p2
float dirX = (x2-x1)/len, dirY = (y2-y1)/len;
// (dir3X,dir3Y) is vector p1->p2 maar 1/3 zo lang
float dir3X = (x2-x1)/3f, dir3Y = (y2-y1)/3f;
// Teken de boog
int px,py;
px = x1 + (int)(dir3X+5*dirY);
py = y1 + (int)(dir3Y-5*dirX);
g.drawLine(x1,y1,px,py);
int px2,py2;
px2 = x1 + (int)(dir3X*2+5*dirY);
py2 = y1 + (int)(dir3Y*2-5*dirX);
g.drawLine(px,py,px2,py2);
g.drawLine(px2,py2,x2,y2);
// Teken een pijltje
px = x1 + (int)((x2-x1)/2+5*dirY); // (px,py): middelpunt pijltje
py = y1 + (int)((y2-y1)/2-5*dirX);
g.drawLine(px+(int)(-6*dirX+3*dirY),py+(int)(-6*dirY-3*dirX),px,py);
g.drawLine(px+(int)(-6*dirX-3*dirY),py+(int)(-6*dirY+3*dirX),px,py);
}
public void mouseClicked(MouseEvent evt) {mouseDown(0,evt.getX(),evt.getY()); }
public void mouseEntered(MouseEvent evt) {}
public void mouseExited(MouseEvent evt) {}
public void mousePressed(MouseEvent evt) {}
public void mouseReleased(MouseEvent evt) {
if (mode == RESIZE) {
removeMouseMotionListener(this);
removeMouseListener(this);
setCursor(Cursor.getDefaultCursor());
////formula.comp.repaint();
mode = 0;
}
}
}