comparison 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
comparison
equal deleted inserted replaced
-1:000000000000 0:9418ab7b7f3f
1 /*
2 CCmI Editor - A Collaborative Cross-Modal Diagram Editing Tool
3
4 Copyright (C) 2002 Cay S. Horstmann (http://horstmann.com)
5 Copyright (C) 2011 Queen Mary University of London (http://ccmi.eecs.qmul.ac.uk/)
6
7 This program is free software: you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation, either version 3 of the License, or
10 (at your option) any later version.
11
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with this program. If not, see <http://www.gnu.org/licenses/>.
19 */
20
21 package uk.ac.qmul.eecs.ccmi.gui;
22
23 import java.awt.Color;
24 import java.awt.Dimension;
25 import java.awt.Graphics;
26 import java.awt.Graphics2D;
27 import java.awt.event.ActionEvent;
28 import java.awt.event.InputEvent;
29 import java.awt.event.KeyEvent;
30 import java.awt.event.MouseAdapter;
31 import java.awt.event.MouseEvent;
32 import java.awt.event.MouseMotionAdapter;
33 import java.awt.geom.Point2D;
34 import java.awt.geom.Rectangle2D;
35 import java.util.ArrayList;
36 import java.util.HashSet;
37 import java.util.Iterator;
38 import java.util.LinkedList;
39 import java.util.List;
40 import java.util.ResourceBundle;
41 import java.util.Set;
42
43 import javax.swing.AbstractAction;
44 import javax.swing.JOptionPane;
45 import javax.swing.JPanel;
46 import javax.swing.KeyStroke;
47
48 import uk.ac.qmul.eecs.ccmi.diagrammodel.CollectionEvent;
49 import uk.ac.qmul.eecs.ccmi.diagrammodel.CollectionListener;
50 import uk.ac.qmul.eecs.ccmi.diagrammodel.CollectionModel;
51 import uk.ac.qmul.eecs.ccmi.diagrammodel.ConnectNodesException;
52 import uk.ac.qmul.eecs.ccmi.diagrammodel.DiagramElement;
53 import uk.ac.qmul.eecs.ccmi.diagrammodel.DiagramModelTreeNode;
54 import uk.ac.qmul.eecs.ccmi.diagrammodel.DiagramNode;
55 import uk.ac.qmul.eecs.ccmi.diagrammodel.ElementChangedEvent;
56 import uk.ac.qmul.eecs.ccmi.utils.InteractionLog;
57
58 /**
59 * A panel to draw a graph
60 */
61 @SuppressWarnings("serial")
62 public class GraphPanel extends JPanel{
63 /**
64 * Constructs a graph.
65 * @param aDiagram a diagram to paint in the graph
66 * @param a aToolbar a toolbar containing the node and edges prototypes for creating
67 * elements in the graph.
68 */
69
70 public GraphPanel(Diagram aDiagram, GraphToolbar aToolbar) {
71 grid = new Grid();
72 gridSize = GRID;
73 grid.setGrid((int) gridSize, (int) gridSize);
74 zoom = 1;
75 toolbar = aToolbar;
76 setBackground(Color.WHITE);
77 wasMoving = false;
78 minBounds = null;
79
80 this.model = aDiagram.getCollectionModel();
81 synchronized(model.getMonitor()){
82 edges = new LinkedList<Edge>(model.getEdges());
83 nodes = new LinkedList<Node>(model.getNodes());
84 }
85 setModelUpdater(aDiagram.getModelUpdater());
86
87 selectedElements = new HashSet<DiagramElement>();
88 moveLockedElements = new HashSet<Object>();
89
90 toolbar.addEdgeCreatedListener(new innerEdgeListener());
91
92 getInputMap(WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_DELETE,0),"delete");
93 getActionMap().put("delete", new AbstractAction(){
94 @Override
95 public void actionPerformed(ActionEvent evt) {
96 /* nothing selected DELETE key has no effect */
97 if(selectedElements.isEmpty())
98 return;
99 /* create a new Set to maintain iterator consistency as elementTakenOut will change selectedItems */
100 HashSet<DiagramElement> iterationSet = new HashSet<DiagramElement>(selectedElements);
101 HashSet<DiagramElement>alreadyLockedElements = new HashSet<DiagramElement>();
102 /* check which, of the selected elements, can be deleted and which ones are currently held by *
103 * other clients. If an element is locked it's removed from the list and put into a separated set */
104 for(Iterator<DiagramElement> itr=iterationSet.iterator(); itr.hasNext();){
105 DiagramElement selected = itr.next();
106 if(!modelUpdater.getLock(selected, Lock.DELETE)){
107 itr.remove();
108 alreadyLockedElements.add(selected);
109 }
110 }
111 ResourceBundle resources = ResourceBundle.getBundle(EditorFrame.class.getName());
112 /* all the elements are locked by other clients */
113 if(iterationSet.isEmpty()){
114 iLog("Could not get lock on any selected element for deletion","");
115 JOptionPane.showMessageDialog(
116 JOptionPane.getFrameForComponent(GraphPanel.this),
117 alreadyLockedElements.size() == 1 ? // singular vs plural
118 resources.getString("dialog.lock_failure.delete") :
119 resources.getString("dialog.lock_failure.deletes"));
120 return;
121 }
122
123 String warning = "";
124 if(!alreadyLockedElements.isEmpty()){
125 StringBuilder builder = new StringBuilder(resources.getString("dialog.lock_failure.deletes_warning"));
126 for(DiagramElement alreadyLocked : alreadyLockedElements)
127 builder.append(alreadyLocked.getName()).append(' ');
128 warning = builder.append('\n').toString();
129 iLog("Could not get lock on some selected element for deletion",warning);
130 }
131
132 iLog("open delete dialog",warning);
133 int answer = JOptionPane.showConfirmDialog(
134 JOptionPane.getFrameForComponent(GraphPanel.this),
135 warning+resources.getString("dialog.confirm.deletions"),
136 resources.getString("dialog.confirm.title"),
137 SpeechOptionPane.YES_NO_OPTION);
138 if(answer == JOptionPane.YES_OPTION){
139 /* the user chose to delete the elements, proceed (locks *
140 * will be automatically removed upon deletion by the server ) */
141 for(DiagramElement selected : iterationSet)
142 modelUpdater.takeOutFromCollection(selected);
143 }else{
144 /* the user chose not to delete the elements, release the acquired locks */
145 for(DiagramElement selected : iterationSet){
146 /* if it's a node all its attached edges were locked as well */
147 /*if(selected instanceof Node){ DONE IN THE SERVER
148 Node n = (Node)selected;
149 for(int i=0; i<n.getEdgesNum();i++){
150 modelUpdater.yieldLock(n.getEdgeAt(i), Lock.DELETE);
151 }
152 }*/
153 modelUpdater.yieldLock(selected, Lock.DELETE);
154 }
155 iLog("cancel delete node dialog","");
156 }
157 }});
158
159 /* ---- COLLECTION LISTENER ----
160 * Adding a collection listener. This listener reacts at changes in the model
161 * by any source, and thus the graph itself. Basically it refreshes the graph
162 * and paints again all the nodes and edges.
163 */
164 model.addCollectionListener(new CollectionListener(){
165 @Override
166 public void elementInserted(final CollectionEvent e) {
167 DiagramElement element = e.getDiagramElement();
168 if(element instanceof Node)
169 nodes.add((Node)element);
170 else
171 edges.add((Edge)element);
172 checkBounds(element,false);
173 if(e.getDiagramElement() instanceof Node && e.getSource().equals(model) ){ //FIXME change model into this model source changes
174 setElementSelected(e.getDiagramElement());
175 dragMode = DRAG_NODE;
176 }
177 revalidate();
178 repaint();
179 }
180 @Override
181 public void elementTakenOut(final CollectionEvent e) {
182 DiagramElement element = e.getDiagramElement();
183 if(element instanceof Node){
184 if(nodePopup != null && nodePopup.nodeRef.equals(element))
185 nodePopup.setVisible(false);
186 nodes.remove(element);
187 }
188 else{
189 if(edgePopup != null && edgePopup.edgeRef.equals(element))
190 edgePopup.setVisible(false);
191 edges.remove(element);
192 }
193 checkBounds(e.getDiagramElement(),true);
194 removeElementFromSelection(e.getDiagramElement());
195 revalidate();
196 repaint();
197 }
198 @Override
199 public void elementChanged(final ElementChangedEvent e) {
200 /* we changed the position of an element and might need to update the boundaries */
201 if(e.getChangeType().equals("stop_move")){
202 checkBounds(e.getDiagramElement(),false);
203 }
204 revalidate();
205 repaint();
206 }
207 });
208 /* --------------------------------------------------------------------------- */
209
210 /* ------------- MOUSE LISTENERS --------------------------------------------
211 * For pressed and released mouse click and moved mouse
212 */
213 addMouseListener(new MouseAdapter(){
214 @Override
215 public void mousePressed(MouseEvent event){
216 requestFocusInWindow();
217 final Point2D mousePoint = new Point2D.Double(
218 (event.getX()+minX)/zoom,
219 (event.getY()+minY)/zoom
220 );
221 boolean isCtrl = (event.getModifiersEx() & InputEvent.CTRL_DOWN_MASK) != 0;
222 Node n = Finder.findNode(mousePoint,nodes);
223 Edge e = Finder.findEdge(mousePoint,edges);
224
225 Object tool = toolbar.getSelectedTool();
226 /* - right click - */
227 if((event.getModifiers() & InputEvent.BUTTON1_MASK) == 0) {
228 if(e != null){
229 if( e.contains(mousePoint)){
230 Node extremityNode = e.getClosestNode(mousePoint,EDGE_END_MIN_CLICK_DIST);
231 if(extremityNode == null){ // click far from the attached nodes, only prompt with set name item
232 EdgePopupMenu pop = new EdgePopupMenu(e,GraphPanel.this,modelUpdater);
233 edgePopup = pop;
234 pop.show(GraphPanel.this, event.getX(), event.getY());
235 }else{ // click near an attached nodes, prompt for name change, set end label and select arrow head
236 EdgePopupMenu pop = new EdgePopupMenu(e,extremityNode,GraphPanel.this,modelUpdater);
237 edgePopup = pop;
238 pop.show(GraphPanel.this, event.getX(), event.getY());
239 }
240 }
241 }else if(n != null){
242 NodePopupMenu pop = new NodePopupMenu(n,GraphPanel.this,modelUpdater);
243 nodePopup = pop;
244 pop.show(GraphPanel.this, event.getX(), event.getY());
245 }else
246 return;
247 }
248
249 /* - one click && palette == select - */
250 else if (tool == null){
251 if(n != null){ // node selected
252 if (isCtrl)
253 addElementToSelection(n,false);
254 else
255 setElementSelected(n);
256 dragMode = DRAG_NODE;
257 }else if (e != null){ // edge selected
258 if (isCtrl){
259 addElementToSelection(e,false);
260 dragMode = DRAG_NODE;
261 }else{
262 setElementSelected(e);
263 modelUpdater.startMove(e, mousePoint);
264 dragMode = DRAG_EDGE;
265 }
266 }else{ // nothing selected : make selection lasso
267 if (!isCtrl)
268 clearSelection();
269 dragMode = DRAG_LASSO;
270 }
271 }
272 /* - one click && palette == node - */
273 else {
274 /* click on an already existing node = select it*/
275 if (n != null){
276 if (isCtrl)
277 addElementToSelection(n,false);
278 else
279 setElementSelected(n);
280 dragMode = DRAG_NODE;
281 }else{
282 Node prototype = (Node) tool;
283 Node newNode = (Node) prototype.clone();
284 Rectangle2D bounds = newNode.getBounds();
285 /* perform the translation from the origin */
286 newNode.translate(new Point2D.Double(), mousePoint.getX() - bounds.getX(),
287 mousePoint.getY() - bounds.getY());
288 /* log stuff */
289 iLog("insert node",""+((newNode.getId() == DiagramElement.NO_ID) ? "(no id)" : newNode.getId()));
290 /* insert the node into the model (no lock needed) */
291 modelUpdater.insertInCollection(newNode);
292 }
293 }
294
295 lastMousePoint = mousePoint;
296 mouseDownPoint = mousePoint;
297 repaint();
298 }
299
300 @Override
301 public void mouseReleased(MouseEvent event){
302 final Point2D mousePoint = new Point2D.Double(
303 (event.getX()+minX)/zoom,
304 (event.getY()+minY)/zoom
305 );
306 if(lastSelected != null){
307 if(lastSelected instanceof Node){
308 if(wasMoving){
309 iLog("move selected stop",mousePoint.getX()+" "+ mousePoint.getY());
310 for(Object element : moveLockedElements){
311 modelUpdater.stopMove((GraphElement)element);
312 modelUpdater.yieldLock((DiagramModelTreeNode)element, Lock.MOVE);
313 }
314 moveLockedElements.clear();
315 }
316 }else{ // instanceof Edge
317 if(wasMoving){
318 iLog("bend edge stop",mousePoint.getX()+" "+ mousePoint.getY());
319 if(moveLockedEdge != null){
320 modelUpdater.stopMove(moveLockedEdge);
321 modelUpdater.yieldLock(moveLockedEdge, Lock.MOVE);
322 moveLockedEdge = null;
323 }
324 }
325 }
326 }
327 dragMode = DRAG_NONE;
328 wasMoving = false;
329 repaint();
330 }
331 });
332
333 addMouseMotionListener(new MouseMotionAdapter(){
334 public void mouseDragged(MouseEvent event){
335 Point2D mousePoint = new Point2D.Double(
336 (event.getX()+minX)/zoom,
337 (event.getY()+minY)/zoom
338 );
339 boolean isCtrl = (event.getModifiersEx() & InputEvent.CTRL_DOWN_MASK) != 0;
340
341 if (dragMode == DRAG_NODE){
342 /* translate selected nodes (edges as well) */
343 double dx = mousePoint.getX() - lastMousePoint.getX();
344 double dy = mousePoint.getY() - lastMousePoint.getY();
345 if(!wasMoving){
346 wasMoving = true;
347 /* when the motion starts, we need to get the move-lock from the server */
348 Iterator<DiagramElement> iterator = selectedElements.iterator();
349 while(iterator.hasNext()){
350 DiagramElement element = iterator.next();
351 if(modelUpdater.getLock(element, Lock.MOVE)){
352 moveLockedElements.add(element);
353 }else{
354 iLog("Could not get move lock for element",DiagramElement.toLogString(element));
355 iterator.remove();
356 }
357 }
358 iLog("move selected start",mousePoint.getX()+" "+ mousePoint.getY());
359 }
360
361 for (DiagramElement selected : selectedElements){
362 if(selected instanceof Node)
363 modelUpdater.translate((Node)selected, lastMousePoint, dx, dy);
364 else
365 modelUpdater.translate((Edge)selected, lastMousePoint, dx, dy);
366 }
367 } else if(dragMode == DRAG_EDGE){
368 if(!wasMoving){
369 wasMoving = true;
370 if(modelUpdater.getLock(lastSelected, Lock.MOVE))
371 moveLockedEdge = (Edge)lastSelected;
372 else
373 iLog("Could not get move lock for element",DiagramElement.toLogString(lastSelected));
374 iLog("bend edge start",mousePoint.getX()+" "+ mousePoint.getY());
375 }
376 if(moveLockedEdge != null)
377 modelUpdater.bend(moveLockedEdge, new Point2D.Double(mousePoint.getX(), mousePoint.getY()));
378 } else if (dragMode == DRAG_LASSO){
379 double x1 = mouseDownPoint.getX();
380 double y1 = mouseDownPoint.getY();
381 double x2 = mousePoint.getX();
382 double y2 = mousePoint.getY();
383 Rectangle2D.Double lasso = new Rectangle2D.Double(Math.min(x1, x2),
384 Math.min(y1, y2), Math.abs(x1 - x2), Math.abs(y1 - y2));
385 for (Node n : GraphPanel.this.nodes){
386 Rectangle2D bounds = n.getBounds();
387 if(!isCtrl && !lasso.contains(bounds)){
388 removeElementFromSelection(n);
389 }
390 else if (lasso.contains(bounds)){
391 addElementToSelection(n,true);
392 }
393 }
394 if(selectedElements.size() != oldLazoSelectedNum){
395 StringBuilder builder = new StringBuilder();
396 for(DiagramElement de : selectedElements)
397 builder.append(DiagramElement.toLogString(de)).append(' ');
398 iLog("added by lazo",builder.toString());
399 }
400 oldLazoSelectedNum = selectedElements.size();
401 }
402 lastMousePoint = mousePoint;
403 }
404 });
405 }
406 /* --------------------------------------------------------------------------- */
407
408 @Override
409 public void paintComponent(Graphics g){
410 super.paintComponent(g);
411 paintGraph(g);
412 }
413
414 public void paintGraph(Graphics g){
415 Graphics2D g2 = (Graphics2D) g;
416 g2.translate(-minX, -minY);
417 g2.scale(zoom, zoom);
418 Rectangle2D bounds = getBounds();
419 Rectangle2D graphBounds = getGraphBounds();
420 if (!hideGrid) grid.draw(g2, new Rectangle2D.Double(minX, minY,
421 Math.max(bounds.getMaxX() / zoom, graphBounds.getMaxX()),
422 Math.max(bounds.getMaxY() / zoom, graphBounds.getMaxY())));
423
424 /* draw nodes and edges */
425 for (Edge e : edges)
426 e.draw(g2);
427 for (Node n : nodes)
428 n.draw(g2);
429
430 for(DiagramElement selected : selectedElements){
431 if (selected instanceof Node){
432 Rectangle2D grabberBounds = ((Node) selected).getBounds();
433 drawGrabber(g2, grabberBounds.getMinX(), grabberBounds.getMinY());
434 drawGrabber(g2, grabberBounds.getMinX(), grabberBounds.getMaxY());
435 drawGrabber(g2, grabberBounds.getMaxX(), grabberBounds.getMinY());
436 drawGrabber(g2, grabberBounds.getMaxX(), grabberBounds.getMaxY());
437 }
438 else if (selected instanceof Edge){
439 for(Point2D p : ((Edge)selected).getConnectionPoints())
440 drawGrabber(g2, p.getX(), p.getY());
441 }
442 }
443
444 if (dragMode == DRAG_LASSO){
445 Color oldColor = g2.getColor();
446 g2.setColor(GRABBER_COLOR);
447 double x1 = mouseDownPoint.getX();
448 double y1 = mouseDownPoint.getY();
449 double x2 = lastMousePoint.getX();
450 double y2 = lastMousePoint.getY();
451 Rectangle2D.Double lasso = new Rectangle2D.Double(Math.min(x1, x2),
452 Math.min(y1, y2), Math.abs(x1 - x2) , Math.abs(y1 - y2));
453 g2.draw(lasso);
454 g2.setColor(oldColor);
455 repaint();
456 }
457 }
458
459 /**
460 * Draws a single "grabber", a filled square
461 * @param g2 the graphics context
462 * @param x the x coordinate of the center of the grabber
463 * @param y the y coordinate of the center of the grabber
464 */
465 static void drawGrabber(Graphics2D g2, double x, double y){
466 final int SIZE = 5;
467 Color oldColor = g2.getColor();
468 g2.setColor(GRABBER_COLOR);
469 g2.fill(new Rectangle2D.Double(x - SIZE / 2, y - SIZE / 2, SIZE, SIZE));
470 g2.setColor(oldColor);
471 }
472
473 @Override
474 public Dimension getPreferredSize(){
475 Rectangle2D graphBounds = getGraphBounds();
476 return new Dimension((int) (zoom * graphBounds.getMaxX()),
477 (int) (zoom * graphBounds.getMaxY()));
478 }
479
480 /**
481 * Changes the zoom of this panel. The zoom is 1 by default and is multiplied
482 * by sqrt(2) for each positive stem or divided by sqrt(2) for each negative
483 * step.
484 * @param steps the number of steps by which to change the zoom. A positive
485 * value zooms in, a negative value zooms out.
486 */
487 public void changeZoom(int steps){
488 final double FACTOR = Math.sqrt(2);
489 for (int i = 1; i <= steps; i++)
490 zoom *= FACTOR;
491 for (int i = 1; i <= -steps; i++)
492 zoom /= FACTOR;
493 revalidate();
494 repaint();
495 }
496
497 /**
498 * Changes the grid size of this panel. The zoom is 10 by default and is
499 * multiplied by sqrt(2) for each positive stem or divided by sqrt(2) for
500 * each negative step.
501 * @param steps the number of steps by which to change the zoom. A positive
502 * value zooms in, a negative value zooms out.
503 */
504 public void changeGridSize(int steps){
505 final double FACTOR = Math.sqrt(2);
506 for (int i = 1; i <= steps; i++)
507 gridSize *= FACTOR;
508 for (int i = 1; i <= -steps; i++)
509 gridSize /= FACTOR;
510 grid.setGrid((int) gridSize, (int) gridSize);
511 repaint();
512 }
513
514 private void addElementToSelection(DiagramElement element, boolean byLasso){
515 /* if not added to selected elements by including it in the lasso, the element is moved *
516 * to the back of the collection so that it will be painted on the top on the next refresh */
517 if(!byLasso)
518 if(element instanceof Node){
519 /* put the node in the last position so that it will be drawn on the top */
520 nodes.remove(element);
521 nodes.add((Node)element);
522 iLog("addeded node to selected",DiagramElement.toLogString(element));
523 }else{
524 /* put the edge in the last position so that it will be drawn on the top */
525 edges.remove(element);
526 edges.add((Edge)element);
527 iLog("addeded edge to selected",DiagramElement.toLogString(element));
528 }
529 if(selectedElements.contains(element)){
530 lastSelected = element;
531 return;
532 }
533 lastSelected = element;
534 selectedElements.add(element);
535 return;
536 }
537
538 private void removeElementFromSelection(DiagramElement element){
539 if (element == lastSelected){
540 lastSelected = null;
541 }
542 if(selectedElements.contains(element)){
543 selectedElements.remove(element);
544 }
545 }
546
547 private void setElementSelected(DiagramElement element){
548 /* clear the selection */
549 selectedElements.clear();
550 lastSelected = element;
551 selectedElements.add(element);
552 if(element instanceof Node){
553 nodes.remove(element);
554 nodes.add((Node)element);
555 iLog("node selected",DiagramElement.toLogString(element));
556 }else{
557 edges.remove(element);
558 edges.add((Edge)element);
559 iLog("edge selected",DiagramElement.toLogString(element));
560 }
561 }
562
563 private void clearSelection(){
564 iLog("selection cleared","");
565 selectedElements.clear();
566 lastSelected = null;
567 }
568
569 /**
570 * Sets the value of the hideGrid property
571 * @param newValue true if the grid is being hidden
572 */
573 public void setHideGrid(boolean newValue){
574 hideGrid = newValue;
575 repaint();
576 }
577
578 /**
579 * Gets the value of the hideGrid property
580 * @return true if the grid is being hidden
581 */
582 public boolean getHideGrid(){
583 return hideGrid;
584 }
585
586 /**
587 Gets the smallest rectangle enclosing the graph
588 @return the bounding rectangle
589 */
590 public Rectangle2D getMinBounds() { return minBounds; }
591 public void setMinBounds(Rectangle2D newValue) { minBounds = newValue; }
592
593 public Rectangle2D getGraphBounds(){
594 Rectangle2D r = minBounds;
595 for (Node n : nodes){
596 Rectangle2D b = n.getBounds();
597 if (r == null) r = b;
598 else r.add(b);
599 }
600 for (Edge e : edges){
601 r.add(e.getBounds());
602 }
603 return r == null ? new Rectangle2D.Double() : new Rectangle2D.Double(r.getX(), r.getY(),
604 r.getWidth() + Node.SHADOW_GAP + Math.abs(minX), r.getHeight() + Node.SHADOW_GAP + Math.abs(minY));
605 }
606
607 public void setModelUpdater(DiagramModelUpdater modelUpdater){
608 this.modelUpdater = modelUpdater;
609 }
610
611 private void iLog(String action,String args){
612 InteractionLog.log("GRAPH",action,args);
613 }
614
615 private void checkBounds(DiagramElement de, boolean wasRemoved){
616 GraphElement ge;
617 if(de instanceof Node)
618 ge = (Node)de;
619 else
620 ge = (Edge)de;
621 if(wasRemoved){
622 if(ge == top){
623 top = null;
624 minY = 0;
625 Rectangle2D bounds;
626 for(Edge e : edges){
627 bounds = e.getBounds();
628 if(bounds.getY() < minY){
629 top = e;
630 minY = bounds.getY();
631 }
632 }
633 for(Node n : nodes){
634 bounds = n.getBounds();
635 if(bounds.getY() < minY){
636 top = n;
637 minY = bounds.getY();
638 }
639 }
640 }
641 if(ge == left){
642 minX = 0;
643 left = null;
644 synchronized(model.getMonitor()){
645 Rectangle2D bounds;
646 for(Edge e : model.getEdges()){
647 bounds = e.getBounds();
648 if(bounds.getX() < minX){
649 left = e;
650 minX = bounds.getX();
651 }
652 }
653 for(Node n : model.getNodes()){
654 bounds = n.getBounds();
655 if(bounds.getX() < minX){
656 left = n;
657 minX = bounds.getX();
658 }
659 }
660 }
661 }
662 }else{ // was added or translated
663 Rectangle2D bounds = ge.getBounds();
664 if(top == null){
665 if(bounds.getY() < 0){
666 top = ge;
667 minY = bounds.getY();
668 }
669 }else if(ge == top){ //the top-most has been translated recalculate the new top-most, as itf it were deleted
670 checkBounds(de, true);
671 }else if(bounds.getY() < top.getBounds().getY()){
672 top = ge;
673 minY = bounds.getY();
674 }
675
676 if(left == null){
677 if(bounds.getX() < 0){
678 left = ge;
679 minX = bounds.getX();
680 }
681 }else if(ge == left){
682 checkBounds(de,true);//the left-most has been translated recalculate the new left-most, as if it were deleted
683 }
684 else if(bounds.getX() < left.getBounds().getX()){
685 left = ge;
686 minX = bounds.getX();
687 }
688 }
689 }
690
691 private class innerEdgeListener implements GraphToolbar.EdgeCreatedListener {
692 @Override
693 public void edgeCreated(Edge e) {
694 ArrayList<DiagramNode> nodesToConnect = new ArrayList<DiagramNode>(selectedElements.size());
695 for(DiagramElement element : selectedElements){
696 if(element instanceof Node)
697 nodesToConnect.add((Node)element);
698 }
699 try {
700 e.connect(nodesToConnect);
701 modelUpdater.insertInCollection(e);
702 } catch (ConnectNodesException cnEx) {
703 JOptionPane.showMessageDialog(GraphPanel.this,
704 cnEx.getLocalizedMessage(),
705 ResourceBundle.getBundle(EditorFrame.class.getName()).getString("dialog.error.title"),
706 JOptionPane.ERROR_MESSAGE);
707 iLog("insert edge error",cnEx.getMessage());
708 }
709 }
710 }
711
712 private List<Edge> edges;
713 private List<Node> nodes;
714 private DiagramModelUpdater modelUpdater;
715 private CollectionModel<Node,Edge> model;
716
717 private Grid grid;
718 private GraphToolbar toolbar;
719 private NodePopupMenu nodePopup;
720 private EdgePopupMenu edgePopup;
721
722 private double zoom;
723 private double gridSize;
724 private boolean hideGrid;
725 private boolean wasMoving;
726
727 private GraphElement top;
728 private GraphElement left;
729 private double minX;
730 private double minY;
731
732 private DiagramElement lastSelected;
733 private Edge moveLockedEdge;
734 private Set<DiagramElement> selectedElements;
735 private Set<Object> moveLockedElements;
736
737 private Point2D lastMousePoint;
738 private Point2D mouseDownPoint;
739 private Rectangle2D minBounds;
740 private int dragMode;
741
742 private int oldLazoSelectedNum;
743
744 /* button is not down, mouse motion will habe no effects */
745 private static final int DRAG_NONE = 0;
746 /* one or more nodes (and eventually some edges) have been selected, mouse motion will result in a translation */
747 private static final int DRAG_NODE = 1;
748 /* one edge has been selected, mouse motion will result in an edge bending */
749 private static final int DRAG_EDGE = 2;
750 /* mouse button down but nothing selected, mouse motion will result in a lasso */
751 private static final int DRAG_LASSO = 3; // multiple selection
752
753 private static final int GRID = 10;
754 private static final double EDGE_END_MIN_CLICK_DIST = 10;
755
756 public static final Color GRABBER_COLOR = new Color(0,128,255);
757 }