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