1
28 package demo.view.advanced;
29
30 import demo.view.DemoBase;
31
32 import java.awt.BorderLayout;
33 import java.awt.Color;
34 import java.awt.Graphics2D;
35 import java.awt.GridLayout;
36 import java.awt.EventQueue;
37 import java.awt.Font;
38 import java.awt.FontMetrics;
39 import java.awt.Rectangle;
40 import java.awt.event.ActionEvent;
41 import java.awt.event.ComponentAdapter;
42 import java.awt.event.ComponentEvent;
43 import java.awt.geom.Rectangle2D;
44 import java.net.URL;
45 import java.util.HashMap;
46 import java.util.HashSet;
47 import java.util.Iterator;
48 import java.util.Locale;
49 import java.util.Set;
50 import javax.swing.AbstractAction;
51 import javax.swing.Action;
52 import javax.swing.ActionMap;
53 import javax.swing.ImageIcon;
54 import javax.swing.InputMap;
55 import javax.swing.JComponent;
56 import javax.swing.JPanel;
57 import javax.swing.JRootPane;
58 import javax.swing.JToolBar;
59
60 import y.base.Edge;
61 import y.base.EdgeCursor;
62 import y.base.Graph;
63 import y.base.GraphEvent;
64 import y.base.GraphListener;
65 import y.base.Node;
66 import y.base.NodeCursor;
67 import y.layout.ComponentLayouter;
68 import y.layout.Layouter;
69 import y.layout.hierarchic.IncrementalHierarchicLayouter;
70 import y.layout.orthogonal.OrthogonalLayouter;
71 import y.layout.router.polyline.EdgeRouter;
72 import y.layout.tree.BalloonLayouter;
73 import y.layout.tree.TreeReductionStage;
74 import y.view.DefaultBackgroundRenderer;
75 import y.view.EditMode;
76 import y.view.Graph2D;
77 import y.view.Graph2DCopyFactory;
78 import y.view.Graph2DEvent;
79 import y.view.Graph2DListener;
80 import y.view.Graph2DUndoManager;
81 import y.view.Graph2DView;
82 import y.view.Graph2DViewActions;
83 import y.view.Graph2DViewMouseWheelZoomListener;
84 import y.view.ModelViewManager;
85 import y.view.NodeLabel;
86 import y.view.NodeRealizer;
87 import y.view.ShapeNodeRealizer;
88 import y.view.Graph2DLayoutExecutor;
89
90
113 public class ModelViewManagerDemo extends DemoBase {
114 private final ModelViewManager manager;
115 private final Graph2DView[] subViews;
116 private HashMap view2undoManager = new HashMap(2);
117
118 public ModelViewManagerDemo() {
119 subViews = new Graph2DView[3];
120
121 initGraph(view.getGraph2D());
122 manager = ModelViewManager.getInstance(view.getGraph2D());
123
124 contentPane.remove(view);
125 contentPane.add(createMultiView(), BorderLayout.CENTER);
126 }
127
128
131 private void initGraph( final Graph2D graph ) {
132 graph.clear();
133 graph.getDefaultNodeRealizer().setFillColor(new Color(73, 147, 255));
134
135 final Node[] nodes = new Node[10];
137 for (int i = 0; i < nodes.length; ++i) {
138 nodes[i] = graph.createNode();
139 }
140
141 graph.createEdge(nodes[1], nodes[8]);
143 graph.createEdge(nodes[1], nodes[2]);
144 graph.createEdge(nodes[1], nodes[6]);
145 graph.createEdge(nodes[1], nodes[0]);
146 graph.createEdge(nodes[2], nodes[0]);
147 graph.createEdge(nodes[3], nodes[5]);
148 graph.createEdge(nodes[3], nodes[6]);
149 graph.createEdge(nodes[3], nodes[1]);
150 graph.createEdge(nodes[4], nodes[2]);
151 graph.createEdge(nodes[4], nodes[7]);
152 graph.createEdge(nodes[5], nodes[0]);
153 graph.createEdge(nodes[6], nodes[5]);
154 graph.createEdge(nodes[6], nodes[0]);
155 graph.createEdge(nodes[7], nodes[2]);
156 graph.createEdge(nodes[7], nodes[8]);
157 graph.createEdge(nodes[8], nodes[4]);
158 graph.createEdge(nodes[9], nodes[8]);
159 graph.createEdge(nodes[9], nodes[7]);
160
161 for (NodeCursor nc = graph.nodes(); nc.ok(); nc.next()) {
163 graph.getRealizer(nc.node()).setLabelText(Integer.toString(nc.node().index()));
164 }
165
166 final IncrementalHierarchicLayouter ihl = new IncrementalHierarchicLayouter();
168 new Graph2DLayoutExecutor().doLayout(graph, ihl);
169 }
170
171
175 private JComponent createMultiView() {
176 final JPanel pane = new JPanel(new GridLayout(2, 2, 1, 1));
177
178 final LabelChangeHandler labelChangeHandler = new LabelChangeHandler();
180 final Graph2DViewMouseWheelZoomListener mwzl =
181 new Graph2DViewMouseWheelZoomListener();
182
183 final JToolBar vtb = createToolBar(view);
185 if (vtb != null) {
186 final JPanel viewAndTools = new JPanel(new BorderLayout());
187 viewAndTools.add(view, BorderLayout.CENTER);
188 viewAndTools.add(vtb, BorderLayout.NORTH);
189 pane.add(viewAndTools);
190 } else {
191 pane.add(view);
192 }
193 view.fitContent();
194 view.getGraph2D().addGraph2DListener(labelChangeHandler);
195 MyBackgroundRenderer.newInstance(view).setText("Editable Model");
196 getUndoManager(view).resetQueue();
197
198
199
200 for (int i = 0; i < subViews.length; ++i) {
203 subViews[i] = new Graph2DView();
204 subViews[i].setFitContentOnResize(true);
205 final JToolBar svitb = createToolBar(subViews[i]);
206 if (svitb != null) {
207 final JPanel viewAndTools = new JPanel(new BorderLayout());
208 viewAndTools.add(subViews[i], BorderLayout.CENTER);
209 viewAndTools.add(svitb, BorderLayout.NORTH);
210 pane.add(viewAndTools);
211 } else {
212 pane.add(subViews[i]);
213 }
214 }
215
216
217
218 final Graph2D graph = subViews[0].getGraph2D();
220 graph.setGraphCopyFactory(new MyCopyFactory(createRedCircle()));
221
222 manager.addViewGraph(graph, null, true);
225 manager.synchronizeModelToViewGraph(graph);
226
227 graph.setDefaultNodeRealizer(createRedCircle());
228 graph.addGraph2DListener(labelChangeHandler);
229
230 final Graph2DViewActions actions = new Graph2DViewActions(subViews[0]);
232 final ActionMap amap = actions.createActionMap();
233 final InputMap imap = actions.createDefaultInputMap(amap);
234 subViews[0].getCanvasComponent().setActionMap(amap);
235 subViews[0].getCanvasComponent().setInputMap(JComponent.WHEN_FOCUSED, imap);
236 subViews[0].getCanvasComponent().addMouseWheelListener(mwzl);
237 subViews[0].addViewMode(new EditMode());
238 MyBackgroundRenderer.newInstance(subViews[0]).setText("Editable View");
239
240 subViews[0].applyLayout(createOrthogonalLayouter());
242 subViews[0].fitContent();
243 getUndoManager(subViews[0]).resetQueue();
244
245
246
247 subViews[1].setGraph2D((Graph2D) manager.createViewGraph(
249 new MyCopyFactory(createOrangeOctagon()), new NoEdgesFilter(), false));
250
251 subViews[1].fitContent();
253 subViews[1].getCanvasComponent().addMouseWheelListener(mwzl);
254 MyBackgroundRenderer.newInstance(subViews[1]).setText("Non-editable View");
255
256
257
258 subViews[2].setGraph2D((Graph2D) manager.createViewGraph(
260 null, new ExcludeFilter(view.getGraph2D()), false));
261
262 subViews[2].fitContent();
264 subViews[2].getCanvasComponent().addMouseWheelListener(mwzl);
265 MyBackgroundRenderer.newInstance(subViews[2]).setText("Non-editable View");
266
267
268
269 manager.getModel().addGraphListener(new UpdateHandler());
272 for (Iterator it = manager.viewGraphs(); it.hasNext();) {
273 ((Graph) it.next()).addGraphListener(new UpdateHandler());
274 }
275
276
277 return pane;
278 }
279
280
284 public void addContentTo( final JRootPane rootPane ) {
285 super.addContentTo(rootPane);
286 final ComponentAdapter handler = new ComponentAdapter() {
287 private int callCount;
288
289 public void componentResized( final ComponentEvent e ) {
290 if (callCount < 2) {
291 configureviews();
292 }
293 ++callCount;
294 if (callCount == 2) {
295 rootPane.removeComponentListener(this);
296 }
297 }
298
299 private void configureviews() {
300 view.fitContent();
301 view.updateView();
302 for (int i = 0; i < subViews.length; ++i) {
303 subViews[i].fitContent();
304 subViews[i].updateView();
305 }
306 }
307 };
308 rootPane.addComponentListener(handler);
309 }
310
311
317 protected JToolBar createToolBar() {
318 return null;
319 }
320
321
324 private JToolBar createToolBar( final Graph2DView view ) {
325 final JToolBar jtb = new JToolBar();
326 jtb.setFloatable(false);
327
328 if (view == this.view || view == this.subViews[0]) {
330 jtb.add(new DeleteSelection(view));
331 }
332
333 jtb.add(new FitContent(view));
335
336 if (view == this.view || view == this.subViews[0]) {
338 jtb.addSeparator();
339 jtb.add(createUndoAction(view));
340 jtb.add(createRedoAction(view));
341 }
342
343 jtb.addSeparator();
344 jtb.add(createActionControl(createLayoutAction(view)));
346
347 if (view == this.subViews[1] || view == this.subViews[2]) {
349 jtb.add(createActionControl(new SynchViewToModel(view)));
350 }
351
352 return jtb;
353 }
354
355 private Action createUndoAction(Graph2DView view) {
356 final Action action = getUndoManager(view).getUndoAction();
357 action.putValue(Action.SMALL_ICON, getIconResource("resource/undo.png"));
358 action.putValue(Action.SHORT_DESCRIPTION, "Undo");
359 return action;
360 }
361
362 private Action createRedoAction(Graph2DView view) {
363 final Action action = getUndoManager(view).getRedoAction();
364 action.putValue(Action.SMALL_ICON, getIconResource("resource/redo.png"));
365 action.putValue(Action.SHORT_DESCRIPTION, "Redo");
366 return action;
367 }
368
369 private Graph2DUndoManager getUndoManager(Graph2DView view) {
370 Graph2DUndoManager undoManager = (Graph2DUndoManager) view2undoManager.get(view);
371 if(undoManager == null) {
372 undoManager = new Graph2DUndoManager(view.getGraph2D());
373 undoManager.setViewContainer(view);
374 view2undoManager.put(view, undoManager);
375 }
376 return undoManager;
377 }
378
379
383 private Action createLayoutAction( final Graph2DView view ) {
384 final Layout layout;
385
386 if (view == this.view) {
387 layout = new Layout(view, new IncrementalHierarchicLayouter());
389 layout.putValue(Action.SHORT_DESCRIPTION, "Layout Hierarchically");
390 } else if (view == subViews[0]) {
391 layout = new Layout(view, createOrthogonalLayouter());
393 layout.putValue(Action.SHORT_DESCRIPTION, "Layout Orthogonally");
394 } else if (view == subViews[1]) {
395 layout = new Layout(view, new ComponentLayouter());
397 layout.putValue(Action.SHORT_DESCRIPTION, "Layout Component Grid");
398 } else if (view == subViews[2]) {
399 layout = new Layout(view, createBalloonLayouter());
401 layout.putValue(Action.SHORT_DESCRIPTION, "Layout Balloon-style Tree");
402 } else {
403 layout = new Layout(view, null);
404 }
405
406 return layout;
407 }
408
409
410 public static void main( String[] args ) {
411 try {
412 EventQueue.invokeAndWait(new Runnable() {
413 public void run() {
414 Locale.setDefault(Locale.ENGLISH);
415 initLnF();
416 (new ModelViewManagerDemo()).start();
417 }
418 });
419 } catch (Throwable t) {
420 t.printStackTrace();
421 }
422 }
423
424
427 private static NodeRealizer createRedCircle() {
428 final ShapeNodeRealizer snr = new ShapeNodeRealizer();
429 snr.setShapeType(ShapeNodeRealizer.ELLIPSE);
430 snr.setFillColor(new Color(196, 0, 64));
431 return snr;
432 }
433
434
437 private static NodeRealizer createOrangeOctagon() {
438 final ShapeNodeRealizer snr = new ShapeNodeRealizer();
439 snr.setWidth(50);
440 snr.setShapeType(ShapeNodeRealizer.OCTAGON);
441 snr.setFillColor(new Color(223, 134, 17));
442 return snr;
443 }
444
445
448 private static Layouter createBalloonLayouter() {
449 final EdgeRouter orthogonal = new EdgeRouter();
450 orthogonal.setReroutingEnabled(true);
451 orthogonal.setSphereOfAction(EdgeRouter.ROUTE_SELECTED_EDGES);
452
453 final TreeReductionStage trs = new TreeReductionStage();
454 trs.setNonTreeEdgeSelectionKey(EdgeRouter.SELECTED_EDGES);
455 trs.setNonTreeEdgeRouter(orthogonal);
456
457 final BalloonLayouter bl = new BalloonLayouter();
458 bl.setRootNodePolicy(BalloonLayouter.DIRECTED_ROOT);
459 bl.setPreferredChildWedge(300);
460 bl.setPreferredRootWedge(360);
461 bl.setMinimalEdgeLength(40);
462 bl.setCompactnessFactor(0.5);
463 bl.setAllowOverlaps(false);
464 bl.setFromSketchModeEnabled(false);
465 bl.appendStage(trs);
466 return bl;
467 }
468
469
472 private static Layouter createOrthogonalLayouter() {
473 final OrthogonalLayouter ol = new OrthogonalLayouter();
474 ol.setLayoutStyle(OrthogonalLayouter.NORMAL_TREE_STYLE);
475 ol.setGrid(25);
476 ol.setUseLengthReduction(true);
477 ol.setUseCrossingPostprocessing(true);
478 ol.setPerceivedBendsOptimizationEnabled(true);
479 ol.setUseRandomization(false);
480 ol.setUseFaceMaximization(true);
481 ol.setUseSketchDrawing(false);
482 return ol;
483 }
484
485
486
491 private static final class NoEdgesFilter implements ModelViewManager.Filter {
492 public boolean acceptInsertion( final Node node ) {
493 return true;
494 }
495
496 public boolean acceptInsertion( final Edge edge ) {
497 return false;
498 }
499
500 public boolean acceptRemoval( final Node node ) {
501 return true;
502 }
503
504 public boolean acceptRemoval( final Edge edge ) {
505 return true;
506 }
507
508 public boolean acceptRetention( final Node node ) {
509 return true;
510 }
511
512 public boolean acceptRetention( final Edge edge ) {
513 return true;
514 }
515 }
516
517
523 private static final class ExcludeFilter implements ModelViewManager.Filter {
524 final Set excludedNodes;
525 final Set excludedEdges;
526
527 ExcludeFilter( final Graph graph ) {
528 excludedNodes = new HashSet();
529 excludedEdges = new HashSet();
530
531 for (NodeCursor nc = graph.nodes(); nc.ok(); nc.next()) {
532 excludedNodes.add(nc.node());
533 }
534 for (EdgeCursor ec = graph.edges(); ec.ok(); ec.next()) {
535 excludedEdges.add(ec.edge());
536 }
537 }
538
539 public boolean acceptInsertion( final Node node ) {
540 return !excludedNodes.contains(node);
541 }
542
543 public boolean acceptInsertion( final Edge edge ) {
544 return !excludedEdges.contains(edge);
545 }
546
547 public boolean acceptRemoval( final Node node ) {
548 return true;
549 }
550
551 public boolean acceptRemoval( final Edge edge ) {
552 return true;
553 }
554
555 public boolean acceptRetention( final Node node ) {
556 return true;
557 }
558
559 public boolean acceptRetention( final Edge edge ) {
560 return true;
561 }
562 }
563
564
569 private static final class MyCopyFactory extends Graph2DCopyFactory {
570 private NodeRealizer template;
571
572 MyCopyFactory( final NodeRealizer template ) {
573 setTemplateImpl(template);
574 }
575
576 protected NodeRealizer copyRealizer( final NodeRealizer nr ) {
577 if (template != null) {
578 final NodeRealizer _nr = template.createCopy();
581 _nr.setCenter(nr.getCenterX(), nr.getCenterY());
582
583 final int lc = nr.labelCount();
585 if (lc > 0) {
586 _nr.setLabel((NodeLabel) nr.getLabel().clone());
587 for (int i = 1; i < lc; ++i) {
588 _nr.addLabel((NodeLabel) nr.getLabel(i).clone());
589 }
590 }
591 return _nr;
592 } else {
593 return nr.createCopy();
594 }
595 }
596
597 NodeRealizer getTemplate() {
598 return template;
599 }
600
601 void setTemplate( final NodeRealizer template ) {
602 setTemplateImpl(template);
603 }
604
605 private void setTemplateImpl( final NodeRealizer template ) {
606 this.template = template != null ? template.createCopy() : null;
607 }
608 }
609
610
613 private static final class MyBackgroundRenderer
614 extends DefaultBackgroundRenderer {
615 private String text;
616 private Color textColor;
617 private final Rectangle r;
618
619 MyBackgroundRenderer( final Graph2DView view ) {
620 super(view);
621 textColor = new Color(192, 192, 192);
622 r = new Rectangle(0, 0, -1, 1);
623 }
624
625 String getText() {
626 return text;
627 }
628
629 void setText( final String text ) {
630 this.text = text;
631 }
632
633 Color getTextColor() {
634 return textColor;
635 }
636
637 void setTextColor( final Color color ) {
638 this.textColor = color;
639 }
640
641 public void paint(
642 final Graphics2D gfx,
643 final int x,
644 final int y,
645 final int w,
646 final int h ) {
647 super.paint(gfx, x, y, w, h);
648 paintText(gfx);
649 }
650
651 private void paintText( final Graphics2D gfx ) {
652 if (text != null && textColor != null) {
653 final Color oldColor = gfx.getColor();
654 final Font oldFont = gfx.getFont();
655
656 undoWorldTransform(gfx);
657
658 gfx.setColor(textColor);
659 gfx.setFont(oldFont.deriveFont(30.0f));
660
661 view.getBounds(r);
662 r.setLocation(0, 0);
663 final FontMetrics fm = gfx.getFontMetrics();
664 final Rectangle2D bnds = fm.getStringBounds(text, gfx);
665 final float textX = (float) (r.x + (r.width - bnds.getWidth()) * 0.5);
666 final float textY = (float) (r.y + (r.height - bnds.getHeight()) * 0.5 + fm.getMaxAscent());
667 gfx.drawString(text, textX, textY);
668
669 redoWorldTransform(gfx);
670
671 gfx.setFont(oldFont);
672 gfx.setColor(oldColor);
673 }
674 }
675
676
677 static MyBackgroundRenderer newInstance( final Graph2DView view ) {
678 final MyBackgroundRenderer mbr = new MyBackgroundRenderer(view);
679 view.setBackgroundRenderer(mbr);
680 return mbr;
681 }
682 }
683
684
688 private static class UpdateHandler implements GraphListener {
689 private int block;
690
691 public void onGraphEvent( final GraphEvent e ) {
692 if (e.getGraph() instanceof Graph2D) {
693 switch (e.getType()) {
694 case GraphEvent.PRE_EVENT:
695 ++block;
696 break;
697 case GraphEvent.POST_EVENT:
698 --block;
699 break;
700 default:
701 break;
702 }
703 if (block == 0) {
704 ((Graph2D) e.getGraph()).updateViews();
705 }
706 }
707 }
708 }
709
710
714 private class LabelChangeHandler implements Graph2DListener {
715 private boolean armed;
716
717 LabelChangeHandler() {
718 armed = true;
719 }
720
721 public void onGraph2DEvent( final Graph2DEvent e ) {
722 if (!armed) {
723 return;
724 }
725
726 if ("text".equals(e.getPropertyName()) &&
727 e.getSubject() instanceof NodeLabel) {
728 final NodeLabel nl = (NodeLabel) e.getSubject();
729 setLabelText(nl.getNode(), nl.getText());
730 }
731 }
732
733 private void setLabelText( final Node node, final String text ) {
734 armed = false;
735
736 final Node mn;
737 final Graph2D model = view.getGraph2D();
738 if (node.getGraph() != model) {
739 mn = manager.getModelNode(node);
741 if (mn != null) {
742 model.getRealizer(mn).setLabelText(text);
744 model.updateViews();
745 }
746 } else {
747 mn = node;
748 }
749
750 if (mn != null) {
751 for (Iterator it = manager.viewGraphs(); it.hasNext();) {
752 final Graph2D graph = ((Graph2D) it.next());
753 final Node vn = manager.getViewNode(mn, graph);
755 if (vn != null && vn != node) {
756 graph.getRealizer(vn).setLabelText(text);
758 graph.updateViews();
759 }
760 }
761 }
762
763 armed = true;
764 }
765 }
766
767
771 private final class SynchViewToModel extends AbstractAction {
772 private final Graph2DView view;
773
774 SynchViewToModel( final Graph2DView view ) {
775 super("Synchronize");
776 this.view = view;
777 final URL imageURL = ClassLoader.getSystemResource(
778 "demo/view/advanced/resource/Export16.gif");
779 if (imageURL != null) {
780 this.putValue(Action.SMALL_ICON, new ImageIcon(imageURL));
781 }
782 this.putValue(Action.SHORT_DESCRIPTION, "Synchronize View to Model");
783 }
784
785 public void actionPerformed( final ActionEvent e ) {
786 manager.synchronizeViewGraphToModel(view.getGraph2D());
787
788 ModelViewManagerDemo.this.view.fitContent();
789 ModelViewManagerDemo.this.view.updateView();
790 for (int i = 0; i < subViews.length; ++i) {
791 subViews[i].fitContent();
792 subViews[i].updateView();
793 }
794 }
795 }
796
797
801 private static final class Layout extends AbstractAction {
802 private final Graph2DView view;
803 private final Layouter layouter;
804
805 Layout( final Graph2DView view, final Layouter layouter ) {
806 super("Layout");
807 this.view = view;
808 this.layouter = layouter;
809 this.putValue(Action.SMALL_ICON, getIconResource("resource/layout.png"));
810 this.putValue(Action.SHORT_DESCRIPTION, "Layout Graph");
811 }
812
813 public void actionPerformed( final ActionEvent e ) {
814 view.applyLayout(layouter);
815
816 view.fitContent();
817 view.updateView();
818 }
819 }
820 }
821