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; } } }