Mercurial > hg > ccmieditor
diff java/src/uk/ac/qmul/eecs/ccmi/gui/GraphPanel.java @ 0:9418ab7b7f3f
Initial import
author | Fiore Martin <fiore@eecs.qmul.ac.uk> |
---|---|
date | Fri, 16 Dec 2011 17:35:51 +0000 |
parents | |
children | 9e67171477bc |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/java/src/uk/ac/qmul/eecs/ccmi/gui/GraphPanel.java Fri Dec 16 17:35:51 2011 +0000 @@ -0,0 +1,757 @@ +/* + CCmI Editor - A Collaborative Cross-Modal Diagram Editing Tool + + Copyright (C) 2002 Cay S. Horstmann (http://horstmann.com) + Copyright (C) 2011 Queen Mary University of London (http://ccmi.eecs.qmul.ac.uk/) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package uk.ac.qmul.eecs.ccmi.gui; + +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.event.ActionEvent; +import java.awt.event.InputEvent; +import java.awt.event.KeyEvent; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.awt.event.MouseMotionAdapter; +import java.awt.geom.Point2D; +import java.awt.geom.Rectangle2D; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.ResourceBundle; +import java.util.Set; + +import javax.swing.AbstractAction; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.KeyStroke; + +import uk.ac.qmul.eecs.ccmi.diagrammodel.CollectionEvent; +import uk.ac.qmul.eecs.ccmi.diagrammodel.CollectionListener; +import uk.ac.qmul.eecs.ccmi.diagrammodel.CollectionModel; +import uk.ac.qmul.eecs.ccmi.diagrammodel.ConnectNodesException; +import uk.ac.qmul.eecs.ccmi.diagrammodel.DiagramElement; +import uk.ac.qmul.eecs.ccmi.diagrammodel.DiagramModelTreeNode; +import uk.ac.qmul.eecs.ccmi.diagrammodel.DiagramNode; +import uk.ac.qmul.eecs.ccmi.diagrammodel.ElementChangedEvent; +import uk.ac.qmul.eecs.ccmi.utils.InteractionLog; + +/** + * A panel to draw a graph + */ +@SuppressWarnings("serial") +public class GraphPanel extends JPanel{ + /** + * Constructs a graph. + * @param aDiagram a diagram to paint in the graph + * @param a aToolbar a toolbar containing the node and edges prototypes for creating + * elements in the graph. + */ + + public GraphPanel(Diagram aDiagram, GraphToolbar aToolbar) { + grid = new Grid(); + gridSize = GRID; + grid.setGrid((int) gridSize, (int) gridSize); + zoom = 1; + toolbar = aToolbar; + setBackground(Color.WHITE); + wasMoving = false; + minBounds = null; + + this.model = aDiagram.getCollectionModel(); + synchronized(model.getMonitor()){ + edges = new LinkedList<Edge>(model.getEdges()); + nodes = new LinkedList<Node>(model.getNodes()); + } + setModelUpdater(aDiagram.getModelUpdater()); + + selectedElements = new HashSet<DiagramElement>(); + moveLockedElements = new HashSet<Object>(); + + toolbar.addEdgeCreatedListener(new innerEdgeListener()); + + getInputMap(WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_DELETE,0),"delete"); + getActionMap().put("delete", new AbstractAction(){ + @Override + public void actionPerformed(ActionEvent evt) { + /* nothing selected DELETE key has no effect */ + if(selectedElements.isEmpty()) + return; + /* create a new Set to maintain iterator consistency as elementTakenOut will change selectedItems */ + HashSet<DiagramElement> iterationSet = new HashSet<DiagramElement>(selectedElements); + HashSet<DiagramElement>alreadyLockedElements = new HashSet<DiagramElement>(); + /* check which, of the selected elements, can be deleted and which ones are currently held by * + * other clients. If an element is locked it's removed from the list and put into a separated set */ + for(Iterator<DiagramElement> itr=iterationSet.iterator(); itr.hasNext();){ + DiagramElement selected = itr.next(); + if(!modelUpdater.getLock(selected, Lock.DELETE)){ + itr.remove(); + alreadyLockedElements.add(selected); + } + } + ResourceBundle resources = ResourceBundle.getBundle(EditorFrame.class.getName()); + /* all the elements are locked by other clients */ + if(iterationSet.isEmpty()){ + iLog("Could not get lock on any selected element for deletion",""); + JOptionPane.showMessageDialog( + JOptionPane.getFrameForComponent(GraphPanel.this), + alreadyLockedElements.size() == 1 ? // singular vs plural + resources.getString("dialog.lock_failure.delete") : + resources.getString("dialog.lock_failure.deletes")); + return; + } + + String warning = ""; + if(!alreadyLockedElements.isEmpty()){ + StringBuilder builder = new StringBuilder(resources.getString("dialog.lock_failure.deletes_warning")); + for(DiagramElement alreadyLocked : alreadyLockedElements) + builder.append(alreadyLocked.getName()).append(' '); + warning = builder.append('\n').toString(); + iLog("Could not get lock on some selected element for deletion",warning); + } + + iLog("open delete dialog",warning); + int answer = JOptionPane.showConfirmDialog( + JOptionPane.getFrameForComponent(GraphPanel.this), + warning+resources.getString("dialog.confirm.deletions"), + resources.getString("dialog.confirm.title"), + SpeechOptionPane.YES_NO_OPTION); + if(answer == JOptionPane.YES_OPTION){ + /* the user chose to delete the elements, proceed (locks * + * will be automatically removed upon deletion by the server ) */ + for(DiagramElement selected : iterationSet) + modelUpdater.takeOutFromCollection(selected); + }else{ + /* the user chose not to delete the elements, release the acquired locks */ + for(DiagramElement selected : iterationSet){ + /* if it's a node all its attached edges were locked as well */ + /*if(selected instanceof Node){ DONE IN THE SERVER + Node n = (Node)selected; + for(int i=0; i<n.getEdgesNum();i++){ + modelUpdater.yieldLock(n.getEdgeAt(i), Lock.DELETE); + } + }*/ + modelUpdater.yieldLock(selected, Lock.DELETE); + } + iLog("cancel delete node dialog",""); + } + }}); + + /* ---- COLLECTION LISTENER ---- + * Adding a collection listener. This listener reacts at changes in the model + * by any source, and thus the graph itself. Basically it refreshes the graph + * and paints again all the nodes and edges. + */ + model.addCollectionListener(new CollectionListener(){ + @Override + public void elementInserted(final CollectionEvent e) { + DiagramElement element = e.getDiagramElement(); + if(element instanceof Node) + nodes.add((Node)element); + else + edges.add((Edge)element); + checkBounds(element,false); + if(e.getDiagramElement() instanceof Node && e.getSource().equals(model) ){ //FIXME change model into this model source changes + setElementSelected(e.getDiagramElement()); + dragMode = DRAG_NODE; + } + revalidate(); + repaint(); + } + @Override + public void elementTakenOut(final CollectionEvent e) { + DiagramElement element = e.getDiagramElement(); + if(element instanceof Node){ + if(nodePopup != null && nodePopup.nodeRef.equals(element)) + nodePopup.setVisible(false); + nodes.remove(element); + } + else{ + if(edgePopup != null && edgePopup.edgeRef.equals(element)) + edgePopup.setVisible(false); + edges.remove(element); + } + checkBounds(e.getDiagramElement(),true); + removeElementFromSelection(e.getDiagramElement()); + revalidate(); + repaint(); + } + @Override + public void elementChanged(final ElementChangedEvent e) { + /* we changed the position of an element and might need to update the boundaries */ + if(e.getChangeType().equals("stop_move")){ + checkBounds(e.getDiagramElement(),false); + } + revalidate(); + repaint(); + } + }); + /* --------------------------------------------------------------------------- */ + + /* ------------- MOUSE LISTENERS -------------------------------------------- + * For pressed and released mouse click and moved mouse + */ + addMouseListener(new MouseAdapter(){ + @Override + public void mousePressed(MouseEvent event){ + requestFocusInWindow(); + final Point2D mousePoint = new Point2D.Double( + (event.getX()+minX)/zoom, + (event.getY()+minY)/zoom + ); + boolean isCtrl = (event.getModifiersEx() & InputEvent.CTRL_DOWN_MASK) != 0; + Node n = Finder.findNode(mousePoint,nodes); + Edge e = Finder.findEdge(mousePoint,edges); + + Object tool = toolbar.getSelectedTool(); + /* - right click - */ + if((event.getModifiers() & InputEvent.BUTTON1_MASK) == 0) { + if(e != null){ + if( e.contains(mousePoint)){ + Node extremityNode = e.getClosestNode(mousePoint,EDGE_END_MIN_CLICK_DIST); + if(extremityNode == null){ // click far from the attached nodes, only prompt with set name item + EdgePopupMenu pop = new EdgePopupMenu(e,GraphPanel.this,modelUpdater); + edgePopup = pop; + pop.show(GraphPanel.this, event.getX(), event.getY()); + }else{ // click near an attached nodes, prompt for name change, set end label and select arrow head + EdgePopupMenu pop = new EdgePopupMenu(e,extremityNode,GraphPanel.this,modelUpdater); + edgePopup = pop; + pop.show(GraphPanel.this, event.getX(), event.getY()); + } + } + }else if(n != null){ + NodePopupMenu pop = new NodePopupMenu(n,GraphPanel.this,modelUpdater); + nodePopup = pop; + pop.show(GraphPanel.this, event.getX(), event.getY()); + }else + return; + } + + /* - one click && palette == select - */ + else if (tool == null){ + if(n != null){ // node selected + if (isCtrl) + addElementToSelection(n,false); + else + setElementSelected(n); + dragMode = DRAG_NODE; + }else if (e != null){ // edge selected + if (isCtrl){ + addElementToSelection(e,false); + dragMode = DRAG_NODE; + }else{ + setElementSelected(e); + modelUpdater.startMove(e, mousePoint); + dragMode = DRAG_EDGE; + } + }else{ // nothing selected : make selection lasso + if (!isCtrl) + clearSelection(); + dragMode = DRAG_LASSO; + } + } + /* - one click && palette == node - */ + else { + /* click on an already existing node = select it*/ + if (n != null){ + if (isCtrl) + addElementToSelection(n,false); + else + setElementSelected(n); + dragMode = DRAG_NODE; + }else{ + Node prototype = (Node) tool; + Node newNode = (Node) prototype.clone(); + Rectangle2D bounds = newNode.getBounds(); + /* perform the translation from the origin */ + newNode.translate(new Point2D.Double(), mousePoint.getX() - bounds.getX(), + mousePoint.getY() - bounds.getY()); + /* log stuff */ + iLog("insert node",""+((newNode.getId() == DiagramElement.NO_ID) ? "(no id)" : newNode.getId())); + /* insert the node into the model (no lock needed) */ + modelUpdater.insertInCollection(newNode); + } + } + + lastMousePoint = mousePoint; + mouseDownPoint = mousePoint; + repaint(); + } + + @Override + public void mouseReleased(MouseEvent event){ + final Point2D mousePoint = new Point2D.Double( + (event.getX()+minX)/zoom, + (event.getY()+minY)/zoom + ); + if(lastSelected != null){ + if(lastSelected instanceof Node){ + if(wasMoving){ + iLog("move selected stop",mousePoint.getX()+" "+ mousePoint.getY()); + for(Object element : moveLockedElements){ + modelUpdater.stopMove((GraphElement)element); + modelUpdater.yieldLock((DiagramModelTreeNode)element, Lock.MOVE); + } + moveLockedElements.clear(); + } + }else{ // instanceof Edge + if(wasMoving){ + iLog("bend edge stop",mousePoint.getX()+" "+ mousePoint.getY()); + if(moveLockedEdge != null){ + modelUpdater.stopMove(moveLockedEdge); + modelUpdater.yieldLock(moveLockedEdge, Lock.MOVE); + moveLockedEdge = null; + } + } + } + } + dragMode = DRAG_NONE; + wasMoving = false; + repaint(); + } + }); + + addMouseMotionListener(new MouseMotionAdapter(){ + public void mouseDragged(MouseEvent event){ + Point2D mousePoint = new Point2D.Double( + (event.getX()+minX)/zoom, + (event.getY()+minY)/zoom + ); + boolean isCtrl = (event.getModifiersEx() & InputEvent.CTRL_DOWN_MASK) != 0; + + if (dragMode == DRAG_NODE){ + /* translate selected nodes (edges as well) */ + double dx = mousePoint.getX() - lastMousePoint.getX(); + double dy = mousePoint.getY() - lastMousePoint.getY(); + if(!wasMoving){ + wasMoving = true; + /* when the motion starts, we need to get the move-lock from the server */ + Iterator<DiagramElement> iterator = selectedElements.iterator(); + while(iterator.hasNext()){ + DiagramElement element = iterator.next(); + if(modelUpdater.getLock(element, Lock.MOVE)){ + moveLockedElements.add(element); + }else{ + iLog("Could not get move lock for element",DiagramElement.toLogString(element)); + iterator.remove(); + } + } + iLog("move selected start",mousePoint.getX()+" "+ mousePoint.getY()); + } + + for (DiagramElement selected : selectedElements){ + if(selected instanceof Node) + modelUpdater.translate((Node)selected, lastMousePoint, dx, dy); + else + modelUpdater.translate((Edge)selected, lastMousePoint, dx, dy); + } + } else if(dragMode == DRAG_EDGE){ + if(!wasMoving){ + wasMoving = true; + if(modelUpdater.getLock(lastSelected, Lock.MOVE)) + moveLockedEdge = (Edge)lastSelected; + else + iLog("Could not get move lock for element",DiagramElement.toLogString(lastSelected)); + iLog("bend edge start",mousePoint.getX()+" "+ mousePoint.getY()); + } + if(moveLockedEdge != null) + modelUpdater.bend(moveLockedEdge, new Point2D.Double(mousePoint.getX(), mousePoint.getY())); + } else if (dragMode == DRAG_LASSO){ + double x1 = mouseDownPoint.getX(); + double y1 = mouseDownPoint.getY(); + double x2 = mousePoint.getX(); + double y2 = mousePoint.getY(); + Rectangle2D.Double lasso = new Rectangle2D.Double(Math.min(x1, x2), + Math.min(y1, y2), Math.abs(x1 - x2), Math.abs(y1 - y2)); + for (Node n : GraphPanel.this.nodes){ + Rectangle2D bounds = n.getBounds(); + if(!isCtrl && !lasso.contains(bounds)){ + removeElementFromSelection(n); + } + else if (lasso.contains(bounds)){ + addElementToSelection(n,true); + } + } + if(selectedElements.size() != oldLazoSelectedNum){ + StringBuilder builder = new StringBuilder(); + for(DiagramElement de : selectedElements) + builder.append(DiagramElement.toLogString(de)).append(' '); + iLog("added by lazo",builder.toString()); + } + oldLazoSelectedNum = selectedElements.size(); + } + lastMousePoint = mousePoint; + } + }); + } + /* --------------------------------------------------------------------------- */ + + @Override + public void paintComponent(Graphics g){ + super.paintComponent(g); + paintGraph(g); + } + + public void paintGraph(Graphics g){ + Graphics2D g2 = (Graphics2D) g; + g2.translate(-minX, -minY); + g2.scale(zoom, zoom); + Rectangle2D bounds = getBounds(); + Rectangle2D graphBounds = getGraphBounds(); + if (!hideGrid) grid.draw(g2, new Rectangle2D.Double(minX, minY, + Math.max(bounds.getMaxX() / zoom, graphBounds.getMaxX()), + Math.max(bounds.getMaxY() / zoom, graphBounds.getMaxY()))); + + /* draw nodes and edges */ + for (Edge e : edges) + e.draw(g2); + for (Node n : nodes) + n.draw(g2); + + for(DiagramElement selected : selectedElements){ + if (selected instanceof Node){ + Rectangle2D grabberBounds = ((Node) selected).getBounds(); + drawGrabber(g2, grabberBounds.getMinX(), grabberBounds.getMinY()); + drawGrabber(g2, grabberBounds.getMinX(), grabberBounds.getMaxY()); + drawGrabber(g2, grabberBounds.getMaxX(), grabberBounds.getMinY()); + drawGrabber(g2, grabberBounds.getMaxX(), grabberBounds.getMaxY()); + } + else if (selected instanceof Edge){ + for(Point2D p : ((Edge)selected).getConnectionPoints()) + drawGrabber(g2, p.getX(), p.getY()); + } + } + + if (dragMode == DRAG_LASSO){ + Color oldColor = g2.getColor(); + g2.setColor(GRABBER_COLOR); + double x1 = mouseDownPoint.getX(); + double y1 = mouseDownPoint.getY(); + double x2 = lastMousePoint.getX(); + double y2 = lastMousePoint.getY(); + Rectangle2D.Double lasso = new Rectangle2D.Double(Math.min(x1, x2), + Math.min(y1, y2), Math.abs(x1 - x2) , Math.abs(y1 - y2)); + g2.draw(lasso); + g2.setColor(oldColor); + repaint(); + } + } + + /** + * Draws a single "grabber", a filled square + * @param g2 the graphics context + * @param x the x coordinate of the center of the grabber + * @param y the y coordinate of the center of the grabber + */ + static void drawGrabber(Graphics2D g2, double x, double y){ + final int SIZE = 5; + Color oldColor = g2.getColor(); + g2.setColor(GRABBER_COLOR); + g2.fill(new Rectangle2D.Double(x - SIZE / 2, y - SIZE / 2, SIZE, SIZE)); + g2.setColor(oldColor); + } + + @Override + public Dimension getPreferredSize(){ + Rectangle2D graphBounds = getGraphBounds(); + return new Dimension((int) (zoom * graphBounds.getMaxX()), + (int) (zoom * graphBounds.getMaxY())); + } + + /** + * Changes the zoom of this panel. The zoom is 1 by default and is multiplied + * by sqrt(2) for each positive stem or divided by sqrt(2) for each negative + * step. + * @param steps the number of steps by which to change the zoom. A positive + * value zooms in, a negative value zooms out. + */ + public void changeZoom(int steps){ + final double FACTOR = Math.sqrt(2); + for (int i = 1; i <= steps; i++) + zoom *= FACTOR; + for (int i = 1; i <= -steps; i++) + zoom /= FACTOR; + revalidate(); + repaint(); + } + + /** + * Changes the grid size of this panel. The zoom is 10 by default and is + * multiplied by sqrt(2) for each positive stem or divided by sqrt(2) for + * each negative step. + * @param steps the number of steps by which to change the zoom. A positive + * value zooms in, a negative value zooms out. + */ + public void changeGridSize(int steps){ + final double FACTOR = Math.sqrt(2); + for (int i = 1; i <= steps; i++) + gridSize *= FACTOR; + for (int i = 1; i <= -steps; i++) + gridSize /= FACTOR; + grid.setGrid((int) gridSize, (int) gridSize); + repaint(); + } + + private void addElementToSelection(DiagramElement element, boolean byLasso){ + /* if not added to selected elements by including it in the lasso, the element is moved * + * to the back of the collection so that it will be painted on the top on the next refresh */ + if(!byLasso) + if(element instanceof Node){ + /* put the node in the last position so that it will be drawn on the top */ + nodes.remove(element); + nodes.add((Node)element); + iLog("addeded node to selected",DiagramElement.toLogString(element)); + }else{ + /* put the edge in the last position so that it will be drawn on the top */ + edges.remove(element); + edges.add((Edge)element); + iLog("addeded edge to selected",DiagramElement.toLogString(element)); + } + if(selectedElements.contains(element)){ + lastSelected = element; + return; + } + lastSelected = element; + selectedElements.add(element); + return; + } + + private void removeElementFromSelection(DiagramElement element){ + if (element == lastSelected){ + lastSelected = null; + } + if(selectedElements.contains(element)){ + selectedElements.remove(element); + } + } + + private void setElementSelected(DiagramElement element){ + /* clear the selection */ + selectedElements.clear(); + lastSelected = element; + selectedElements.add(element); + if(element instanceof Node){ + nodes.remove(element); + nodes.add((Node)element); + iLog("node selected",DiagramElement.toLogString(element)); + }else{ + edges.remove(element); + edges.add((Edge)element); + iLog("edge selected",DiagramElement.toLogString(element)); + } + } + + private void clearSelection(){ + iLog("selection cleared",""); + selectedElements.clear(); + lastSelected = null; + } + + /** + * Sets the value of the hideGrid property + * @param newValue true if the grid is being hidden + */ + public void setHideGrid(boolean newValue){ + hideGrid = newValue; + repaint(); + } + + /** + * Gets the value of the hideGrid property + * @return true if the grid is being hidden + */ + public boolean getHideGrid(){ + return hideGrid; + } + + /** + Gets the smallest rectangle enclosing the graph + @return the bounding rectangle + */ + public Rectangle2D getMinBounds() { return minBounds; } + public void setMinBounds(Rectangle2D newValue) { minBounds = newValue; } + + public Rectangle2D getGraphBounds(){ + Rectangle2D r = minBounds; + for (Node n : nodes){ + Rectangle2D b = n.getBounds(); + if (r == null) r = b; + else r.add(b); + } + for (Edge e : edges){ + r.add(e.getBounds()); + } + return r == null ? new Rectangle2D.Double() : new Rectangle2D.Double(r.getX(), r.getY(), + r.getWidth() + Node.SHADOW_GAP + Math.abs(minX), r.getHeight() + Node.SHADOW_GAP + Math.abs(minY)); + } + + public void setModelUpdater(DiagramModelUpdater modelUpdater){ + this.modelUpdater = modelUpdater; + } + + private void iLog(String action,String args){ + InteractionLog.log("GRAPH",action,args); + } + + private void checkBounds(DiagramElement de, boolean wasRemoved){ + GraphElement ge; + if(de instanceof Node) + ge = (Node)de; + else + ge = (Edge)de; + if(wasRemoved){ + if(ge == top){ + top = null; + minY = 0; + Rectangle2D bounds; + for(Edge e : edges){ + bounds = e.getBounds(); + if(bounds.getY() < minY){ + top = e; + minY = bounds.getY(); + } + } + for(Node n : nodes){ + bounds = n.getBounds(); + if(bounds.getY() < minY){ + top = n; + minY = bounds.getY(); + } + } + } + if(ge == left){ + minX = 0; + left = null; + synchronized(model.getMonitor()){ + Rectangle2D bounds; + for(Edge e : model.getEdges()){ + bounds = e.getBounds(); + if(bounds.getX() < minX){ + left = e; + minX = bounds.getX(); + } + } + for(Node n : model.getNodes()){ + bounds = n.getBounds(); + if(bounds.getX() < minX){ + left = n; + minX = bounds.getX(); + } + } + } + } + }else{ // was added or translated + Rectangle2D bounds = ge.getBounds(); + if(top == null){ + if(bounds.getY() < 0){ + top = ge; + minY = bounds.getY(); + } + }else if(ge == top){ //the top-most has been translated recalculate the new top-most, as itf it were deleted + checkBounds(de, true); + }else if(bounds.getY() < top.getBounds().getY()){ + top = ge; + minY = bounds.getY(); + } + + if(left == null){ + if(bounds.getX() < 0){ + left = ge; + minX = bounds.getX(); + } + }else if(ge == left){ + checkBounds(de,true);//the left-most has been translated recalculate the new left-most, as if it were deleted + } + else if(bounds.getX() < left.getBounds().getX()){ + left = ge; + minX = bounds.getX(); + } + } + } + + private class innerEdgeListener implements GraphToolbar.EdgeCreatedListener { + @Override + public void edgeCreated(Edge e) { + ArrayList<DiagramNode> nodesToConnect = new ArrayList<DiagramNode>(selectedElements.size()); + for(DiagramElement element : selectedElements){ + if(element instanceof Node) + nodesToConnect.add((Node)element); + } + try { + e.connect(nodesToConnect); + modelUpdater.insertInCollection(e); + } catch (ConnectNodesException cnEx) { + JOptionPane.showMessageDialog(GraphPanel.this, + cnEx.getLocalizedMessage(), + ResourceBundle.getBundle(EditorFrame.class.getName()).getString("dialog.error.title"), + JOptionPane.ERROR_MESSAGE); + iLog("insert edge error",cnEx.getMessage()); + } + } + } + + private List<Edge> edges; + private List<Node> nodes; + private DiagramModelUpdater modelUpdater; + private CollectionModel<Node,Edge> model; + + private Grid grid; + private GraphToolbar toolbar; + private NodePopupMenu nodePopup; + private EdgePopupMenu edgePopup; + + private double zoom; + private double gridSize; + private boolean hideGrid; + private boolean wasMoving; + + private GraphElement top; + private GraphElement left; + private double minX; + private double minY; + + private DiagramElement lastSelected; + private Edge moveLockedEdge; + private Set<DiagramElement> selectedElements; + private Set<Object> moveLockedElements; + + private Point2D lastMousePoint; + private Point2D mouseDownPoint; + private Rectangle2D minBounds; + private int dragMode; + + private int oldLazoSelectedNum; + + /* button is not down, mouse motion will habe no effects */ + private static final int DRAG_NONE = 0; + /* one or more nodes (and eventually some edges) have been selected, mouse motion will result in a translation */ + private static final int DRAG_NODE = 1; + /* one edge has been selected, mouse motion will result in an edge bending */ + private static final int DRAG_EDGE = 2; + /* mouse button down but nothing selected, mouse motion will result in a lasso */ + private static final int DRAG_LASSO = 3; // multiple selection + + private static final int GRID = 10; + private static final double EDGE_END_MIN_CLICK_DIST = 10; + + public static final Color GRABBER_COLOR = new Color(0,128,255); +} \ No newline at end of file