1:/* 2: * Programming graphicxal user interfaces 3: * Example: DiagramView 4: * Jarkko Leponiemi 2002 5: */ 6: 7:import java.awt.*; 8:import java.awt.event.*; 9:import java.util.*; 10:import javax.swing.*; 11: 12:/** 13: * A class representing the combined view and controller 14: * of the diagram application. 15: */ 16:public class DiagramView extends JPanel implements Observer 17:{ 18: // the diagram model 19: private Diagram diagram; 20: // the cached bounding rectangles for nodes 21: private Map rcache; 22: 23: public DiagramView(Diagram d) { 24: diagram = d; 25: d.addObserver(this); 26: rcache = new HashMap(); 27: // create the controller add register as listener 28: Controller c = new Controller(); 29: this.addMouseListener(c); 30: this.addMouseMotionListener(c); 31: } 32: 33: // the Observer function 34: // called when model has been changed 35: public void update(Observable o, Object arg) { 36: if (arg instanceof Diagram.Node) { 37: rcache.remove(arg); 38: } 39: repaint(); 40: } 41: 42: public void paint(Graphics graphics) { 43: super.paint(graphics); 44: Graphics2D g = (Graphics2D) graphics; 45: g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); 46: 47: // draw the connectors 48: Iterator i = diagram.getConnectors(); 49: while (i.hasNext()) { 50: Diagram.Connector c = (Diagram.Connector) i.next(); 51: Diagram.Node n1 = c.getNode1(); 52: Diagram.Node n2 = c.getNode2(); 53: g.drawLine(n1.getX(), n1.getY(), n2.getX(), n2.getY()); 54: } 55: 56: // draw the nodes 57: FontMetrics fm = g.getFontMetrics(); 58: int h = fm.getAscent(); 59: i = diagram.getNodes(); 60: while (i.hasNext()) { 61: Diagram.Node n = (Diagram.Node) i.next(); 62: // get the bounding rectangle 63: Rectangle r = (Rectangle) rcache.get(n); 64: if (r == null) 65: r = calcNodeBounds(n, fm); 66: // draw the node 67: g.setColor(Color.white); 68: g.fill(r); 69: g.setColor(Color.black); 70: g.draw(r); 71: g.drawString(n.getTitle(), r.x + 3, r.y + h + 3); 72: } 73: } 74: 75: // calculate the bounds of a node in the view 76: private Rectangle calcNodeBounds(Diagram.Node n, FontMetrics fm) { 77: int sw = fm.stringWidth(n.getTitle()) + 6; 78: int sh = fm.getAscent() + fm.getDescent() + 6; 79: Rectangle r = new Rectangle(n.getX() - sw / 2, n.getY() - sh / 2, sw, sh); 80: rcache.put(n, r); 81: return r; 82: } 83: 84: // find the topmost node under the given point 85: private Diagram.Node findNodeInPoint(int x, int y) { 86: Diagram.Node last = null; 87: Iterator i = diagram.getNodes(); 88: while (i.hasNext()) { 89: Diagram.Node n = (Diagram.Node) i.next(); 90: Rectangle r = (Rectangle) rcache.get(n); 91: if (r != null && r.contains(x, y)) 92: last = n; 93: } 94: return last; 95: } 96: 97: // an inner class representing the controller 98: private class Controller implements MouseListener, MouseMotionListener 99: { 100: // the modes of the controller 101: private static final int NODE = 0; 102: private static final int CONNECTOR = 1; 103: private static final int SELECT = 2; 104: 105: private boolean dragging = false; 106: private Diagram.Node n = null; 107: private int offsx, offsy; 108: private int curx, cury; 109: private int mode = SELECT; 110: private int idcnt = 1; 111: 112: Controller() { 113: // add entries to the inputmap and the action map 114: // for changing the mode with the keyboard 115: InputMap im = getInputMap(); 116: im.put(KeyStroke.getKeyStroke(KeyEvent.VK_N, 0), "node"); 117: im.put(KeyStroke.getKeyStroke(KeyEvent.VK_C, 0), "connector"); 118: im.put(KeyStroke.getKeyStroke(KeyEvent.VK_S, 0), "select"); 119: ActionMap am = getActionMap(); 120: am.put("node", new ModeAction(NODE)); 121: am.put("connector", new ModeAction(CONNECTOR)); 122: am.put("select", new ModeAction(SELECT)); 123: } 124: 125: // create a new node or 126: // start moving a node or 127: // start creating a connector 128: public void mousePressed(MouseEvent e) { 129: // create node 130: if (mode == NODE) { 131: diagram.createNode("Untitled" + idcnt++, e.getX(), e.getY()); 132: } else { 133: n = findNodeInPoint(e.getX(), e.getY()); 134: if (n != null) { 135: dragging = true; 136: // move node 137: if (mode == SELECT) { 138: offsx = n.getX() - e.getX(); 139: offsy = n.getY() - e.getY(); 140: } 141: // create connector 142: else if (mode == CONNECTOR) { 143: curx = e.getX(); 144: cury = e.getY(); 145: drawTempConn(); 146: } 147: } 148: } 149: } 150: 151: // draw or erase the temporary connector 152: private void drawTempConn() { 153: Graphics g = getGraphics(); 154: g.setXORMode(getBackground()); 155: g.setColor(Color.black); 156: g.drawLine(n.getX(), n.getY(), curx, cury); 157: g.dispose(); 158: } 159: 160: public void mouseDragged(MouseEvent e) { 161: if (dragging) { 162: // drag the node 163: if (mode == SELECT) 164: n.move(e.getX() + offsx, e.getY() + offsy); 165: // or an image of a connector 166: else if (mode == CONNECTOR) { 167: drawTempConn(); 168: curx = e.getX(); 169: cury = e.getY(); 170: drawTempConn(); 171: } 172: } 173: } 174: 175: public void mouseReleased(MouseEvent e) { 176: if (dragging) { 177: dragging = false; 178: // try to create a new connector 179: if (mode == CONNECTOR) { 180: drawTempConn(); 181: Diagram.Node n2 = findNodeInPoint(e.getX(), e.getY()); 182: if (n2 != null) 183: diagram.createConnector(n, n2); 184: } 185: n = null; 186: } 187: } 188: 189: public void mouseClicked(MouseEvent e) {} 190: public void mouseEntered(MouseEvent e) {} 191: public void mouseExited(MouseEvent e) {} 192: public void mouseMoved(MouseEvent e) {} 193: 194: // an inner class forn changing the mode 195: private class ModeAction extends AbstractAction 196: { 197: private int m; 198: ModeAction(int m) { 199: this.m = m; 200: } 201: 202: public void actionPerformed(ActionEvent e) { 203: mode = this.m; 204: } 205: } 206: } 207:}