1   
28  package demo.view.uml;
29  
30  import y.anim.AnimationFactory;
31  import y.anim.AnimationObject;
32  import y.anim.AnimationPlayer;
33  import y.anim.CompositeAnimationObject;
34  import y.base.Edge;
35  import y.base.GraphEvent;
36  import y.base.GraphListener;
37  import y.base.Node;
38  import y.base.NodeList;
39  import y.layout.router.polyline.EdgeRouter;
40  import y.util.DataProviderAdapter;
41  import y.view.CreateEdgeMode;
42  import y.view.EdgeRealizer;
43  import y.view.EditMode;
44  import y.view.Graph2D;
45  import y.view.Graph2DLayoutExecutor;
46  import y.view.Graph2DTraversal;
47  import y.view.Graph2DView;
48  import y.view.Graph2DViewRepaintManager;
49  import y.view.HitInfo;
50  import y.view.HitInfoFactory;
51  import y.view.HotSpotMode;
52  import y.view.MouseInputMode;
53  import y.view.MoveSelectionMode;
54  import y.view.NodeRealizer;
55  import y.view.ViewMode;
56  import y.view.YLabel;
57  
58  import javax.swing.Timer;
59  import java.awt.Component;
60  import java.awt.event.ActionEvent;
61  import java.awt.event.ActionListener;
62  import java.awt.event.MouseEvent;
63  import java.beans.PropertyChangeEvent;
64  import java.beans.PropertyChangeListener;
65  
66  
74  class UmlEditMode extends EditMode {
75    private static final String ZOOM_PROPERTY = "Zoom";
76  
77    private static final int DELAY = 500;
78  
79    private static final EdgeRealizer ASSOCIATION_REALIZER = UmlRealizerFactory.createAssociationRealizer();
80    private static final EdgeRealizer DEPENDENCY_REALIZER = UmlRealizerFactory.createDependencyRealizer();
81    private static final EdgeRealizer GENERALIZATION_REALIZER = UmlRealizerFactory.createGeneralizationRealizer();
82    private static final EdgeRealizer REALIZATION_REALIZER = UmlRealizerFactory.createRealizationRealizer();
83    private static final EdgeRealizer AGGREGATION_REALIZER = UmlRealizerFactory.createAggregationRealizer();
84    private static final EdgeRealizer COMPOSITION_REALIZER = UmlRealizerFactory.createCompositionRealizer();
85  
86    private final Timer showingTimer;
87    private final Timer hidingTimer;
88    private final AnimationPlayer player;
89    private Graph2DViewRepaintManager repaintManager;
90    private UmlCreateEdgeMode umlCreateEdgeMode;
91  
92    private Node lastNode;
93    private Node currentNode;
94    private UmlEdgeCreationButtons lastButtons;
95    private UmlEdgeCreationButtons currentButtons;
96    private UmlEdgeCreationButtons rollingOutButtons;
97    private UmlEdgeCreationButtons rollingInButtons;
98  
99    private boolean lastPressHitButton;
100 
101   
110   public UmlEditMode(final Graph2DView view, EdgeRouter edgeRouter) {
111         lastNode = null;
113     currentNode = null;
114     lastButtons = null;
115     currentButtons = null;
116     lastPressHitButton = false;
117 
118     player = new AnimationPlayer(false);
119 
120     addListeners(view);
121     addChildModes(edgeRouter);
122 
123         repaintManager = new Graph2DViewRepaintManager(view);
125     player.addAnimationListener(repaintManager);
126 
127             showingTimer = new Timer(0, null);
130     showingTimer.setInitialDelay(DELAY);
131     showingTimer.setRepeats(false);
132     showingTimer.addActionListener(new ActionListener() {
133       public void actionPerformed(ActionEvent event) {
134         if (lastButtons == null || lastButtons.getNode() != currentNode) {
135                     final CompositeAnimationObject animations = AnimationFactory.createConcurrency();
137 
138                     if (currentNode != null && paintDetailed(view.getZoom())) {
140             currentButtons = new UmlEdgeCreationButtons(view, currentNode);
141             animations.addAnimation(
142                 AnimationFactory.createEasedAnimation(new EditingElementsAnimation(currentButtons, true), 1, 1));
143           }
144 
145                     player.animate(animations);
147 
148                     lastButtons = currentButtons;
150         }
151       }
152     });
153 
154             hidingTimer = new Timer(0, null);
157     hidingTimer.setInitialDelay(DELAY);
158     hidingTimer.setRepeats(false);
159     hidingTimer.addActionListener(new ActionListener() {
160       public void actionPerformed(ActionEvent event) {
161         if (lastButtons == null || lastButtons.getNode() != currentNode) {
162                     final CompositeAnimationObject animations = AnimationFactory.createConcurrency();
164 
165                     if (lastButtons != null) {
167             animations.addAnimation(
168                 AnimationFactory.createEasedAnimation(new EditingElementsAnimation(lastButtons, false), 1, 1));
169           }
170 
171                     player.animate(animations);
173 
174                     lastButtons = null;
176         }
177       }
178     });
179   }
180 
181   
184   private boolean paintDetailed(double zoom) {
185     return zoom > view.getPaintDetailThreshold();
186   }
187 
188   private void addListeners(final Graph2DView view) {
189         view.getGraph2D().addGraphListener(createNodeDeletionListener());
191 
192             view.getCanvasComponent().addPropertyChangeListener(new PropertyChangeListener() {
195       public void propertyChange(PropertyChangeEvent evt) {
196         if (ZOOM_PROPERTY.equals(evt.getPropertyName())) {
197           final double zoom = ((Double) evt.getNewValue()).doubleValue();
198           if (!paintDetailed(zoom)) {
199                         currentNode = null;
201             currentButtons = null;
202             if (lastNode != currentNode) {
203               hidingTimer.setInitialDelay(0);
204               hidingTimer.restart();
205               showingTimer.stop();
206               lastNode = currentNode;
207             }
208           } else {
209                                                             MouseEvent lastMouseMotionEvent = getLastMoveEvent();
214             if (lastMouseMotionEvent == null) {
215               lastMouseMotionEvent = getLastDragEvent();
216             } else if (getLastDragEvent() != null && lastMouseMotionEvent.getWhen() < getLastDragEvent().getWhen()) {
217               lastMouseMotionEvent = getLastDragEvent();
218             }
219 
220             if (lastMouseMotionEvent != null) {
221               scheduleButtonAnimations(view.toWorldCoordX(lastMouseMotionEvent.getX()),
222                   view.toWorldCoordY(lastMouseMotionEvent.getY()));
223             }
224           }
225         }
226       }
227     });
228   }
229 
230   
235   private void addChildModes(EdgeRouter edgeRouter) {
236     setMoveSelectionMode(new ReroutingMoveSelectionMode(edgeRouter));
237     final ReroutingCreateEdgeMode createEdgeMode = new ReroutingCreateEdgeMode(edgeRouter);
238     createEdgeMode.setIndicatingTargetNode(true);
239     setCreateEdgeMode(createEdgeMode);
240     setHotSpotMode(new ReroutingHotSpotMode(edgeRouter));
241     setMoveLabelMode(null);
242     umlCreateEdgeMode = new UmlCreateEdgeMode(edgeRouter);
243   }
244 
245   
250   private GraphListener createNodeDeletionListener() {
251     return new GraphListener() {
252       public void onGraphEvent(GraphEvent event) {
253                 if (lastButtons != null) {
255           if ((event.getType()) == GraphEvent.POST_NODE_REMOVAL
256               && event.getData().equals(lastButtons.getNode())) {
257             view.removeDrawable(lastButtons);
258             lastButtons = null;
259             view.updateView();
260           }
261         }
262 
263                 if (rollingOutButtons != null) {
265           if ((event.getType()) == GraphEvent.POST_NODE_REMOVAL
266               && event.getData().equals(rollingOutButtons.getNode())) {
267             view.removeDrawable(rollingOutButtons);
268             rollingOutButtons = null;
269             view.updateView();
270           }
271         }
272 
273                 if (rollingInButtons != null) {
275           if ((event.getType()) == GraphEvent.POST_NODE_REMOVAL
276               && event.getData().equals(rollingInButtons.getNode())) {
277             view.removeDrawable(rollingInButtons);
278             rollingInButtons = null;
279             view.updateView();
280           }
281         }
282       }
283     };
284   }
285 
286   
291   public void setSnappingEnabled(boolean snapping) {
292     super.setSnappingEnabled(snapping);
293 
294     umlCreateEdgeMode.setSnappingEnabled(snapping);
295     umlCreateEdgeMode.getSnapContext().setUsingOrthogonalMovementConstraints(snapping);
296     umlCreateEdgeMode.getSnapContext().setSnappingSegmentsToSnapLines(snapping);
297   }
298 
299   
309   protected void labelClicked(
310       final Graph2D graph,
311       final YLabel label,
312       final boolean wasSelected,
313       final double x,
314       final double y,
315       final boolean modifierSet
316   ) {
317         super.labelClicked(graph, label, wasSelected, x, y, false);
319   }
320 
321   
325   public void mousePressedRight(double x, double y) {
326     final HitInfo hit = getHitInfo(x, y);
327     final boolean isOnNodeWithButtons = (lastButtons != null) && (hit.getHitNode() == lastButtons.getNode());
328     final boolean isOnButtons = (lastButtons != null) && (lastButtons.hasButtonAt(x, y));
329     if (!isOnNodeWithButtons && !isOnButtons) {
330       super.mousePressedRight(x, y);
331     }
332   }
333 
334   
340   public void mousePressedLeft(final double x, final double y) {
341     lastPressHitButton = false;
342 
343     if (hitsEdgeCreationButton(x, y)) {
344       lastPressHitButton = true;
345       lastButtons.selectButtonAt(x, y);
346       final int selectedButtonIndex = lastButtons.getSelectedButtonIndex();
347       switch (selectedButtonIndex) {
348         case UmlEdgeCreationButtons.TYPE_ASSOCIATION:
349           getGraph2D().setDefaultEdgeRealizer(ASSOCIATION_REALIZER);
350           break;
351         case UmlEdgeCreationButtons.TYPE_DEPENDENCY:
352           getGraph2D().setDefaultEdgeRealizer(DEPENDENCY_REALIZER);
353           break;
354         case UmlEdgeCreationButtons.TYPE_GENERALIZATION:
355           getGraph2D().setDefaultEdgeRealizer(GENERALIZATION_REALIZER);
356           break;
357         case UmlEdgeCreationButtons.TYPE_REALIZATION:
358           getGraph2D().setDefaultEdgeRealizer(REALIZATION_REALIZER);
359           break;
360         case UmlEdgeCreationButtons.TYPE_AGGREGATION:
361           getGraph2D().setDefaultEdgeRealizer(AGGREGATION_REALIZER);
362           break;
363         case UmlEdgeCreationButtons.TYPE_COMPOSITION:
364           getGraph2D().setDefaultEdgeRealizer(COMPOSITION_REALIZER);
365           break;
366       }
367     }
368 
369     super.mousePressedLeft(x, y);
370   }
371 
372   
379   public void mouseReleasedLeft(final double x, final double y) {
380     final Graph2D graph = getGraph2D();
381     if (lastPressHitButton) {
382       startCreateEdgeMode(graph);
383     } else {
384       super.mouseReleasedLeft(x, y);
385     }
386   }
387 
388   
395   public void mouseMoved(final double x, final double y) {
396     final Graph2D graph = getGraph2D();
397     if (hitsButtonsDrawable(lastButtons, x, y)) {
398             graph.moveToLast(lastButtons.getNode());
400     } else {
401       final HitInfoFactory hitInfoFactory = view.getHitInfoFactory();
402       final HitInfo hit = hitInfoFactory.createHitInfo(x, y, Graph2DTraversal.NODES, true);
403       if (hit.hasHitNodes()) {
404                 final Node node = hit.getHitNode();
406         graph.moveToLast(node);
407       }
408     }
409 
410         scheduleButtonAnimations(x, y);
412 
413         super.mouseMoved(x, y);
415   }
416 
417   
424   public void mouseDraggedLeft(final double x, final double y) {
425     scheduleButtonAnimations(x, y);
426 
427     final Graph2D graph = getGraph2D();
428     if (lastPressHitButton) {
429             umlCreateEdgeMode.setSourceNode(lastButtons.getNode());
431             setChild(umlCreateEdgeMode, lastPressEvent, lastDragEvent);
433     } else {
434       final HitInfo hit = getHitInfo(lastPressEvent);
435             if (!hit.hasHitNodes() || graph.isSelected(hit.getHitNode())) {
437                 super.mouseDraggedLeft(x, y);
439       }
440     }
441   }
442 
443   
449   private void scheduleButtonAnimations(final double x, final double y) {
450     if (hitsButtonsDrawable(lastButtons, x, y)) {
451             updateButtonSelection(x, y);
453 
454       currentNode = lastButtons.getNode();
455     } else if (paintDetailed(view.getZoom())) {
456       final HitInfo hitInfo = view.getHitInfoFactory().createHitInfo(x, y, Graph2DTraversal.NODES, true);
457       currentNode = hitInfo.getHitNode();
458     }
459 
460         if (lastNode != currentNode) {
462       currentButtons = currentNode != null ? new UmlEdgeCreationButtons(view, currentNode) : null;
463       lastNode = currentNode;
464       hidingTimer.setInitialDelay(DELAY);
465       hidingTimer.start();
466       showingTimer.setInitialDelay(DELAY);
467       showingTimer.restart();
468     }
469   }
470 
471   
474   private void updateButtonSelection(double x, double y) {
475     int lastIndex = lastButtons.getSelectedButtonIndex();
476     lastButtons.selectButtonAt(x, y);
477     int currentIndex = lastButtons.getSelectedButtonIndex();
478     if (currentIndex != lastIndex) {
479       view.updateView();
480     }
481   }
482 
483   
487   private void startCreateEdgeMode(final Graph2D graph) {
488     if (lastButtons != null) {
489                   lastDragEvent = new MouseEvent((Component) lastPressEvent.getSource(), MouseEvent.MOUSE_DRAGGED,
492           lastPressEvent.getWhen(), lastPressEvent.getModifiers(), lastPressEvent.getX(), lastPressEvent.getY(),
493           lastPressEvent.getClickCount(), lastPressEvent.isPopupTrigger());
494 
495                   final Node associatedNode = lastButtons.getNode();
498       final MouseEvent pressEvent = new MouseEvent((Component) lastPressEvent.getSource(), lastPressEvent.getID(),
499           lastPressEvent.getWhen(), lastPressEvent.getModifiers(),
500           view.toViewCoordX(graph.getCenterX(associatedNode)),
501           view.toViewCoordY(graph.getCenterY(associatedNode)),
502           lastPressEvent.getClickCount(), lastPressEvent.isPopupTrigger());
503 
504             setChild(getCreateEdgeMode(), pressEvent, lastDragEvent);
506     }
507   }
508 
509   
512   protected void nodeCreated(Node node) {
513     Graph2D graph = getGraph2D();
514     scheduleButtonAnimations(graph.getCenterX(node), graph.getCenterY(node));
515   }
516 
517   
520   public void setChild(final ViewMode child,
521                        final MouseEvent pressEvent, final MouseEvent dragEvent, final MouseEvent releaseEvent) {
522     if (child != null && !(child instanceof MouseInputMode)) {
523                   currentNode = null;
526       currentButtons = null;
527       if (lastNode != currentNode) {
528         hidingTimer.setInitialDelay(0);
529         hidingTimer.restart();
530         showingTimer.stop();
531         lastNode = currentNode;
532       }
533     }
534 
535     super.setChild(child, pressEvent, dragEvent, releaseEvent);
536   }
537 
538   
547   private boolean hitsEdgeCreationButton(final double x, final double y) {
548     return lastButtons != null && lastButtons.hasButtonAt(x, y);
549   }
550 
551   
557   private boolean hitsButtonsDrawable(final UmlEdgeCreationButtons buttons, final double x, final double y) {
558     return buttons != null && buttons.contains(x, y);
559   }
560 
561   
565   private class EditingElementsAnimation implements AnimationObject {
566     private static final int MOVE_DURATION = 300;
567 
568     private final UmlEdgeCreationButtons drawable;
569     private final boolean in;
570     private double progress;
571 
572     private final NodeRealizer realizer;
573     private double attributeButtonOpacity;
574     private double operationButtonOpacity;
575     private double selectionOpacity;
576 
577 
578     EditingElementsAnimation(final UmlEdgeCreationButtons drawable, final boolean in) {
579       this.drawable = drawable;
580       this.in = in;
581       this.realizer = getGraph2D().getRealizer(drawable.getNode());
582     }
583 
584     public void initAnimation() {
585       if (repaintManager != null) {
586         repaintManager.add(drawable);
587         repaintManager.add(realizer);
588       }
589 
590       if (in) {
591         rollingInButtons = drawable;
592         view.addDrawable(drawable);
593         drawable.setProgress(0);
594 
595         UmlRealizerFactory.setAttributeButtonOpacity(realizer, 0);
596         UmlRealizerFactory.setOperationButtonOpacity(realizer, 0);
597         UmlRealizerFactory.setSelectionOpacity(realizer, 0);
598       } else {
599         rollingOutButtons = drawable;
600         progress = drawable.getProgress();
601 
602         attributeButtonOpacity = UmlRealizerFactory.getAttributeButtonOpacity(realizer);
603         operationButtonOpacity = UmlRealizerFactory.getOperationButtonOpacity(realizer);
604         selectionOpacity = UmlRealizerFactory.getSelectionOpacity(realizer);
605       }
606     }
607 
608     public void calcFrame(final double time) {
609       if (in) {
610         drawable.setProgress(time);
611 
612         UmlRealizerFactory.setAttributeButtonOpacity(realizer, (float) time);
613         UmlRealizerFactory.setOperationButtonOpacity(realizer, (float) time);
614         UmlRealizerFactory.setSelectionOpacity(realizer, (float) time);
615       } else {
616         drawable.setProgress(progress * (1 - time));
617 
618         UmlRealizerFactory.setAttributeButtonOpacity(realizer, (float) (attributeButtonOpacity * (1 - time)));
619         UmlRealizerFactory.setOperationButtonOpacity(realizer, (float) (operationButtonOpacity * (1 - time)));
620         UmlRealizerFactory.setSelectionOpacity(realizer, (float) (selectionOpacity * (1 - time)));
621       }
622     }
623 
624     public void disposeAnimation() {
625       if (repaintManager != null) {
626         repaintManager.remove(drawable);
627         repaintManager.remove(realizer);
628       }
629       if (in) {
630         rollingInButtons = null;
631       } else {
632         view.removeDrawable(drawable);
633         rollingOutButtons = null;
634       }
635     }
636 
637     public long preferredDuration() {
638       if (in) {
639         return MOVE_DURATION;
640       } else {
641         if (progress > 0) {
642           return (long) (MOVE_DURATION * progress);
643         } else {
644           return MOVE_DURATION;
645         }
646       }
647     }
648   }
649 
650   
653   private static class ReroutingMoveSelectionMode extends MoveSelectionMode {
654     private final EdgeRouter edgeRouter;
655 
656     public ReroutingMoveSelectionMode(EdgeRouter edgeRouter) {
657       this.edgeRouter = edgeRouter;
658     }
659 
660     public void mouseShiftPressedLeft(final double x, final double y) {
661       getGraph2D().firePreEvent();
662       super.mouseShiftPressedLeft(x, y);
663     }
664 
665     public void mousePressedLeft(final double x, final double y) {
666       getGraph2D().firePreEvent();
667       super.mousePressedLeft(x, y);
668     }
669 
670     public void mouseShiftReleasedLeft(final double x, final double y) {
671       super.mouseShiftReleasedLeft(x, y);
672       routeEdgesAtMovedNodes();
673       getGraph2D().firePostEvent();
674     }
675 
676     public void mouseReleasedLeft(final double x, final double y) {
677       super.mouseReleasedLeft(x, y);
678       routeEdgesAtMovedNodes();
679       getGraph2D().firePostEvent();
680     }
681 
682     private void routeEdgesAtMovedNodes() {
683       final Graph2D graph = getGraph2D();
684       final NodeList nodesToBeMoved = getNodesToBeMoved();
685       final DataProviderAdapter selectedNodes = new DataProviderAdapter() {
686         public boolean getBool(Object dataHolder) {
687           return nodesToBeMoved.contains(dataHolder);
688         }
689       };
690       edgeRouter.setSphereOfAction(EdgeRouter.ROUTE_EDGES_AT_SELECTED_NODES);
691       graph.addDataProvider(EdgeRouter.SELECTED_NODES, selectedNodes);
692       try {
693         final Graph2DLayoutExecutor executor = new Graph2DLayoutExecutor();
694         executor.getLayoutMorpher().setKeepZoomFactor(true);
695         executor.doLayout(view, edgeRouter);
696       } finally {
697         graph.removeDataProvider(EdgeRouter.SELECTED_NODES);
698       }
699     }
700   }
701 
702   
705   private static class ReroutingCreateEdgeMode extends CreateEdgeMode {
706     private final EdgeRouter edgeRouter;
707 
708     public ReroutingCreateEdgeMode(EdgeRouter edgeRouter) {
709       this.edgeRouter = edgeRouter;
710     }
711 
712     protected Edge createEdge(final Graph2D graph, final Node startNode, final Node targetNode,
713                               final EdgeRealizer realizer) {
714       getGraph2D().firePreEvent();
715       return super.createEdge(graph, startNode, targetNode, realizer);
716     }
717 
718     protected void edgeCreated(final Edge edge) {
719       super.edgeCreated(edge);
720 
721       final Graph2D graph = getGraph2D();
722       final DataProviderAdapter selectedEdges = new DataProviderAdapter() {
723         public boolean getBool(Object dataHolder) {
724           return dataHolder == edge;
725         }
726       };
727       edgeRouter.setSphereOfAction(EdgeRouter.ROUTE_SELECTED_EDGES);
728       graph.addDataProvider(EdgeRouter.SELECTED_EDGES, selectedEdges);
729       try {
730         final Graph2DLayoutExecutor executor = new Graph2DLayoutExecutor();
731         executor.getLayoutMorpher().setKeepZoomFactor(true);
732         executor.doLayout(view, edgeRouter);
733       } finally {
734         graph.removeDataProvider(EdgeRouter.SELECTED_EDGES);
735       }
736       graph.firePostEvent();
737     }
738   }
739 
740   
743   private static final class ReroutingHotSpotMode extends HotSpotMode {
744     private final EdgeRouter edgeRouter;
745 
746     public ReroutingHotSpotMode(EdgeRouter edgeRouter) {
747       this.edgeRouter = edgeRouter;
748     }
749 
750     public void mousePressedLeft(double x, double y) {
751       getGraph2D().firePreEvent();
752       super.mousePressedLeft(x, y);
753     }
754 
755     public void mouseReleasedLeft(double x, double y) {
756       super.mouseReleasedLeft(x, y);
757       edgeRouter.setSphereOfAction(EdgeRouter.ROUTE_EDGES_AT_SELECTED_NODES);
758       getGraph2D().addDataProvider(EdgeRouter.SELECTED_NODES, new DataProviderAdapter() {
759         public boolean getBool(Object dataHolder) {
760           return dataHolder instanceof Node && getGraph2D().isSelected((Node) dataHolder);
761         }
762       });
763       try {
764         final Graph2DLayoutExecutor executor = new Graph2DLayoutExecutor();
765         executor.getLayoutMorpher().setKeepZoomFactor(true);
766         executor.doLayout(view, edgeRouter);
767       } finally {
768         getGraph2D().firePostEvent();
769         edgeRouter.setSphereOfAction(EdgeRouter.ROUTE_SELECTED_EDGES);
770       }
771     }
772   }
773 }
774