1
14 package demo.view.advanced;
15
16 import demo.view.DemoBase;
17
18 import java.awt.BorderLayout;
19 import java.awt.Color;
20 import java.awt.Graphics2D;
21 import java.awt.GridLayout;
22 import java.awt.EventQueue;
23 import java.awt.Font;
24 import java.awt.FontMetrics;
25 import java.awt.Rectangle;
26 import java.awt.event.ActionEvent;
27 import java.awt.event.ComponentAdapter;
28 import java.awt.event.ComponentEvent;
29 import java.awt.geom.Rectangle2D;
30 import java.net.URL;
31 import java.util.HashSet;
32 import java.util.Iterator;
33 import java.util.Locale;
34 import java.util.Set;
35 import javax.swing.AbstractAction;
36 import javax.swing.Action;
37 import javax.swing.ActionMap;
38 import javax.swing.ImageIcon;
39 import javax.swing.InputMap;
40 import javax.swing.JComponent;
41 import javax.swing.JPanel;
42 import javax.swing.JRootPane;
43 import javax.swing.JToolBar;
44
45 import y.base.Edge;
46 import y.base.EdgeCursor;
47 import y.base.Graph;
48 import y.base.GraphEvent;
49 import y.base.GraphListener;
50 import y.base.Node;
51 import y.base.NodeCursor;
52 import y.layout.ComponentLayouter;
53 import y.layout.Layouter;
54 import y.layout.hierarchic.IncrementalHierarchicLayouter;
55 import y.layout.orthogonal.OrthogonalLayouter;
56 import y.layout.router.OrthogonalEdgeRouter;
57 import y.layout.tree.BalloonLayouter;
58 import y.layout.tree.TreeReductionStage;
59 import y.view.DefaultBackgroundRenderer;
60 import y.view.EditMode;
61 import y.view.Graph2D;
62 import y.view.Graph2DCopyFactory;
63 import y.view.Graph2DEvent;
64 import y.view.Graph2DListener;
65 import y.view.Graph2DView;
66 import y.view.Graph2DViewActions;
67 import y.view.Graph2DViewMouseWheelZoomListener;
68 import y.view.ModelViewManager;
69 import y.view.NodeLabel;
70 import y.view.NodeRealizer;
71 import y.view.ShapeNodeRealizer;
72 import y.view.Graph2DLayoutExecutor;
73
74
97 public class ModelViewManagerDemo extends DemoBase {
98 private final ModelViewManager manager;
99 private final Graph2DView[] subViews;
100
101 public ModelViewManagerDemo() {
102 subViews = new Graph2DView[3];
103
104 initGraph(view.getGraph2D());
105 manager = ModelViewManager.getInstance(view.getGraph2D());
106
107 contentPane.remove(view);
108 contentPane.add(createMultiView(), BorderLayout.CENTER);
109 }
110
111
114 private void initGraph( final Graph2D graph ) {
115 graph.clear();
116 graph.getDefaultNodeRealizer().setFillColor(new Color(73, 147, 255));
117
118 final Node[] nodes = new Node[10];
120 for (int i = 0; i < nodes.length; ++i) {
121 nodes[i] = graph.createNode();
122 }
123
124 graph.createEdge(nodes[1], nodes[8]);
126 graph.createEdge(nodes[1], nodes[2]);
127 graph.createEdge(nodes[1], nodes[6]);
128 graph.createEdge(nodes[1], nodes[0]);
129 graph.createEdge(nodes[2], nodes[0]);
130 graph.createEdge(nodes[3], nodes[5]);
131 graph.createEdge(nodes[3], nodes[6]);
132 graph.createEdge(nodes[3], nodes[1]);
133 graph.createEdge(nodes[4], nodes[2]);
134 graph.createEdge(nodes[4], nodes[7]);
135 graph.createEdge(nodes[5], nodes[0]);
136 graph.createEdge(nodes[6], nodes[5]);
137 graph.createEdge(nodes[6], nodes[0]);
138 graph.createEdge(nodes[7], nodes[2]);
139 graph.createEdge(nodes[7], nodes[8]);
140 graph.createEdge(nodes[8], nodes[4]);
141 graph.createEdge(nodes[9], nodes[8]);
142 graph.createEdge(nodes[9], nodes[7]);
143
144 for (NodeCursor nc = graph.nodes(); nc.ok(); nc.next()) {
146 graph.getRealizer(nc.node()).setLabelText(Integer.toString(nc.node().index()));
147 }
148
149 final IncrementalHierarchicLayouter ihl = new IncrementalHierarchicLayouter();
151 new Graph2DLayoutExecutor().doLayout(graph, ihl);
152 }
153
154
158 private JComponent createMultiView() {
159 final JPanel pane = new JPanel(new GridLayout(2, 2, 1, 1));
160
161 final LabelChangeHandler labelChangeHandler = new LabelChangeHandler();
163 final Graph2DViewMouseWheelZoomListener mwzl =
164 new Graph2DViewMouseWheelZoomListener();
165
166 final JToolBar vtb = createToolBar(view);
168 if (vtb != null) {
169 final JPanel viewAndTools = new JPanel(new BorderLayout());
170 viewAndTools.add(view, BorderLayout.CENTER);
171 viewAndTools.add(vtb, BorderLayout.NORTH);
172 pane.add(viewAndTools);
173 } else {
174 pane.add(view);
175 }
176 view.fitContent();
177 view.getGraph2D().addGraph2DListener(labelChangeHandler);
178 MyBackgroundRenderer.newInstance(view).setText("Editable Model");
179
180
181
182 for (int i = 0; i < subViews.length; ++i) {
185 subViews[i] = new Graph2DView();
186 subViews[i].setFitContentOnResize(true);
187 final JToolBar svitb = createToolBar(subViews[i]);
188 if (svitb != null) {
189 final JPanel viewAndTools = new JPanel(new BorderLayout());
190 viewAndTools.add(subViews[i], BorderLayout.CENTER);
191 viewAndTools.add(svitb, BorderLayout.NORTH);
192 pane.add(viewAndTools);
193 } else {
194 pane.add(subViews[i]);
195 }
196 }
197
198
199
200 final Graph2D graph = subViews[0].getGraph2D();
202 graph.setGraphCopyFactory(new MyCopyFactory(createRedCircle()));
203
204 manager.addViewGraph(graph, null, true);
207 manager.synchronizeModelToViewGraph(graph);
208
209 graph.setDefaultNodeRealizer(createRedCircle());
210 graph.addGraph2DListener(labelChangeHandler);
211
212 final Graph2DViewActions actions = new Graph2DViewActions(subViews[0]);
214 final ActionMap amap = actions.createActionMap();
215 final InputMap imap = actions.createDefaultInputMap(amap);
216 subViews[0].getCanvasComponent().setActionMap(amap);
217 subViews[0].getCanvasComponent().setInputMap(JComponent.WHEN_FOCUSED, imap);
218 subViews[0].getCanvasComponent().addMouseWheelListener(mwzl);
219 subViews[0].addViewMode(new EditMode());
220 MyBackgroundRenderer.newInstance(subViews[0]).setText("Editable View");
221
222 subViews[0].applyLayout(createOrthogonalLayouter());
224 subViews[0].fitContent();
225
226
227
228 subViews[1].setGraph2D((Graph2D) manager.createViewGraph(
230 new MyCopyFactory(createOrangeOctagon()), new NoEdgesFilter(), false));
231
232 subViews[1].fitContent();
234 subViews[1].getCanvasComponent().addMouseWheelListener(mwzl);
235 MyBackgroundRenderer.newInstance(subViews[1]).setText("Non-editable View");
236
237
238
239 subViews[2].setGraph2D((Graph2D) manager.createViewGraph(
241 null, new ExcludeFilter(view.getGraph2D()), false));
242
243 subViews[2].fitContent();
245 subViews[2].getCanvasComponent().addMouseWheelListener(mwzl);
246 MyBackgroundRenderer.newInstance(subViews[2]).setText("Non-editable View");
247
248
249
250 manager.getModel().addGraphListener(new UpdateHandler());
253 for (Iterator it = manager.viewGraphs(); it.hasNext();) {
254 ((Graph) it.next()).addGraphListener(new UpdateHandler());
255 }
256
257
258 return pane;
259 }
260
261
265 public void addContentTo( final JRootPane rootPane ) {
266 super.addContentTo(rootPane);
267 final ComponentAdapter handler = new ComponentAdapter() {
268 private int callCount;
269
270 public void componentResized( final ComponentEvent e ) {
271 if (callCount < 2) {
272 configureviews();
273 }
274 ++callCount;
275 if (callCount == 2) {
276 rootPane.removeComponentListener(this);
277 }
278 }
279
280 private void configureviews() {
281 view.fitContent();
282 view.updateView();
283 for (int i = 0; i < subViews.length; ++i) {
284 subViews[i].fitContent();
285 subViews[i].updateView();
286 }
287 }
288 };
289 rootPane.addComponentListener(handler);
290 }
291
292
298 protected JToolBar createToolBar() {
299 return null;
300 }
301
302
305 private JToolBar createToolBar( final Graph2DView view ) {
306 final JToolBar jtb = new JToolBar();
307 jtb.setFloatable(false);
308
309 if (view == this.view || view == this.subViews[0]) {
311 jtb.add(new DeleteSelection(view));
312 }
313
314 jtb.add(new FitContent(view));
316
317 jtb.addSeparator();
318 jtb.add(createActionControl(createLayoutAction(view)));
320
321 if (view == this.subViews[1] || view == this.subViews[2]) {
323 jtb.add(createActionControl(new SynchViewToModel(view)));
324 }
325
326 return jtb;
327 }
328
329
333 private Action createLayoutAction( final Graph2DView view ) {
334 final Layout layout;
335
336 if (view == this.view) {
337 layout = new Layout(view, new IncrementalHierarchicLayouter());
339 layout.putValue(Action.SHORT_DESCRIPTION, "Layout Hierarchically");
340 } else if (view == subViews[0]) {
341 layout = new Layout(view, createOrthogonalLayouter());
343 layout.putValue(Action.SHORT_DESCRIPTION, "Layout Orthogonally");
344 } else if (view == subViews[1]) {
345 layout = new Layout(view, new ComponentLayouter());
347 layout.putValue(Action.SHORT_DESCRIPTION, "Layout Component Grid");
348 } else if (view == subViews[2]) {
349 layout = new Layout(view, createBalloonLayouter());
351 layout.putValue(Action.SHORT_DESCRIPTION, "Layout Balloon-style Tree");
352 } else {
353 layout = new Layout(view, null);
354 }
355
356 return layout;
357 }
358
359
360 public static void main( String[] args ) {
361 try {
362 EventQueue.invokeAndWait(new Runnable() {
363 public void run() {
364 Locale.setDefault(Locale.ENGLISH);
365 initLnF();
366 (new ModelViewManagerDemo()).start();
367 }
368 });
369 } catch (Throwable t) {
370 t.printStackTrace();
371 }
372 }
373
374
377 private static NodeRealizer createRedCircle() {
378 final ShapeNodeRealizer snr = new ShapeNodeRealizer();
379 snr.setShapeType(ShapeNodeRealizer.ELLIPSE);
380 snr.setFillColor(new Color(196, 0, 64));
381 return snr;
382 }
383
384
387 private static NodeRealizer createOrangeOctagon() {
388 final ShapeNodeRealizer snr = new ShapeNodeRealizer();
389 snr.setWidth(50);
390 snr.setShapeType(ShapeNodeRealizer.OCTAGON);
391 snr.setFillColor(new Color(223, 134, 17));
392 return snr;
393 }
394
395
398 private static Layouter createBalloonLayouter() {
399 final OrthogonalEdgeRouter orthogonal = new OrthogonalEdgeRouter();
400 orthogonal.setCrossingCost(1.0);
401 orthogonal.setReroutingEnabled(true);
402 orthogonal.setSphereOfAction(OrthogonalEdgeRouter.ROUTE_SELECTED_EDGES);
403
404 final TreeReductionStage trs = new TreeReductionStage();
405 trs.setNonTreeEdgeSelectionKey(OrthogonalEdgeRouter.SELECTED_EDGES);
406 trs.setNonTreeEdgeRouter(orthogonal);
407
408 final BalloonLayouter bl = new BalloonLayouter();
409 bl.setRootNodePolicy(BalloonLayouter.DIRECTED_ROOT);
410 bl.setPreferredChildWedge(300);
411 bl.setPreferredRootWedge(360);
412 bl.setMinimalEdgeLength(40);
413 bl.setCompactnessFactor(0.5);
414 bl.setAllowOverlaps(false);
415 bl.setFromSketchModeEnabled(false);
416 bl.appendStage(trs);
417 return bl;
418 }
419
420
423 private static Layouter createOrthogonalLayouter() {
424 final OrthogonalLayouter ol = new OrthogonalLayouter();
425 ol.setLayoutStyle(OrthogonalLayouter.NORMAL_TREE_STYLE);
426 ol.setGrid(25);
427 ol.setUseLengthReduction(true);
428 ol.setUseCrossingPostprocessing(true);
429 ol.setPerceivedBendsOptimizationEnabled(true);
430 ol.setUseRandomization(false);
431 ol.setUseFaceMaximization(true);
432 ol.setUseSketchDrawing(false);
433 return ol;
434 }
435
436
437
442 private static final class NoEdgesFilter implements ModelViewManager.Filter {
443 public boolean acceptInsertion( final Node node ) {
444 return true;
445 }
446
447 public boolean acceptInsertion( final Edge edge ) {
448 return false;
449 }
450
451 public boolean acceptRemoval( final Node node ) {
452 return true;
453 }
454
455 public boolean acceptRemoval( final Edge edge ) {
456 return true;
457 }
458
459 public boolean acceptRetention( final Node node ) {
460 return true;
461 }
462
463 public boolean acceptRetention( final Edge edge ) {
464 return true;
465 }
466 }
467
468
474 private static final class ExcludeFilter implements ModelViewManager.Filter {
475 final Set excludedNodes;
476 final Set excludedEdges;
477
478 ExcludeFilter( final Graph graph ) {
479 excludedNodes = new HashSet();
480 excludedEdges = new HashSet();
481
482 for (NodeCursor nc = graph.nodes(); nc.ok(); nc.next()) {
483 excludedNodes.add(nc.node());
484 }
485 for (EdgeCursor ec = graph.edges(); ec.ok(); ec.next()) {
486 excludedEdges.add(ec.edge());
487 }
488 }
489
490 public boolean acceptInsertion( final Node node ) {
491 return !excludedNodes.contains(node);
492 }
493
494 public boolean acceptInsertion( final Edge edge ) {
495 return !excludedEdges.contains(edge);
496 }
497
498 public boolean acceptRemoval( final Node node ) {
499 return true;
500 }
501
502 public boolean acceptRemoval( final Edge edge ) {
503 return true;
504 }
505
506 public boolean acceptRetention( final Node node ) {
507 return true;
508 }
509
510 public boolean acceptRetention( final Edge edge ) {
511 return true;
512 }
513 }
514
515
520 private static final class MyCopyFactory extends Graph2DCopyFactory {
521 private NodeRealizer template;
522
523 MyCopyFactory( final NodeRealizer template ) {
524 setTemplateImpl(template);
525 }
526
527 protected NodeRealizer copyRealizer( final NodeRealizer nr ) {
528 if (template != null) {
529 final NodeRealizer _nr = template.createCopy();
532 _nr.setCenter(nr.getCenterX(), nr.getCenterY());
533
534 final int lc = nr.labelCount();
536 if (lc > 0) {
537 _nr.setLabel((NodeLabel) nr.getLabel().clone());
538 for (int i = 1; i < lc; ++i) {
539 _nr.addLabel((NodeLabel) nr.getLabel(i).clone());
540 }
541 }
542 return _nr;
543 } else {
544 return nr.createCopy();
545 }
546 }
547
548 NodeRealizer getTemplate() {
549 return template;
550 }
551
552 void setTemplate( final NodeRealizer template ) {
553 setTemplateImpl(template);
554 }
555
556 private void setTemplateImpl( final NodeRealizer template ) {
557 this.template = template != null ? template.createCopy() : null;
558 }
559 }
560
561
564 private static final class MyBackgroundRenderer
565 extends DefaultBackgroundRenderer {
566 private String text;
567 private Color textColor;
568 private final Rectangle r;
569
570 MyBackgroundRenderer( final Graph2DView view ) {
571 super(view);
572 textColor = new Color(192, 192, 192);
573 r = new Rectangle(0, 0, -1, 1);
574 }
575
576 String getText() {
577 return text;
578 }
579
580 void setText( final String text ) {
581 this.text = text;
582 }
583
584 Color getTextColor() {
585 return textColor;
586 }
587
588 void setTextColor( final Color color ) {
589 this.textColor = color;
590 }
591
592 public void paint(
593 final Graphics2D gfx,
594 final int x,
595 final int y,
596 final int w,
597 final int h ) {
598 super.paint(gfx, x, y, w, h);
599 paintText(gfx);
600 }
601
602 private void paintText( final Graphics2D gfx ) {
603 if (text != null && textColor != null) {
604 final Color oldColor = gfx.getColor();
605 final Font oldFont = gfx.getFont();
606
607 undoWorldTransform(gfx);
608
609 gfx.setColor(textColor);
610 gfx.setFont(oldFont.deriveFont(30.0f));
611
612 view.getBounds(r);
613 r.setLocation(0, 0);
614 final FontMetrics fm = gfx.getFontMetrics();
615 final Rectangle2D bnds = fm.getStringBounds(text, gfx);
616 final float textX = (float) (r.x + (r.width - bnds.getWidth()) * 0.5);
617 final float textY = (float) (r.y + (r.height - bnds.getHeight()) * 0.5 + fm.getMaxAscent());
618 gfx.drawString(text, textX, textY);
619
620 redoWorldTransform(gfx);
621
622 gfx.setFont(oldFont);
623 gfx.setColor(oldColor);
624 }
625 }
626
627
628 static MyBackgroundRenderer newInstance( final Graph2DView view ) {
629 final MyBackgroundRenderer mbr = new MyBackgroundRenderer(view);
630 view.setBackgroundRenderer(mbr);
631 return mbr;
632 }
633 }
634
635
639 private static class UpdateHandler implements GraphListener {
640 private int block;
641
642 public void onGraphEvent( final GraphEvent e ) {
643 if (e.getGraph() instanceof Graph2D) {
644 switch (e.getType()) {
645 case GraphEvent.PRE_EVENT:
646 ++block;
647 break;
648 case GraphEvent.POST_EVENT:
649 --block;
650 break;
651 default:
652 break;
653 }
654 if (block == 0) {
655 ((Graph2D) e.getGraph()).updateViews();
656 }
657 }
658 }
659 }
660
661
665 private class LabelChangeHandler implements Graph2DListener {
666 private boolean armed;
667
668 LabelChangeHandler() {
669 armed = true;
670 }
671
672 public void onGraph2DEvent( final Graph2DEvent e ) {
673 if (!armed) {
674 return;
675 }
676
677 if ("text".equals(e.getPropertyName()) &&
678 e.getSubject() instanceof NodeLabel) {
679 final NodeLabel nl = (NodeLabel) e.getSubject();
680 setLabelText(nl.getNode(), nl.getText());
681 }
682 }
683
684 private void setLabelText( final Node node, final String text ) {
685 armed = false;
686
687 final Node mn;
688 final Graph2D model = view.getGraph2D();
689 if (node.getGraph() != model) {
690 mn = manager.getModelNode(node);
692 if (mn != null) {
693 model.getRealizer(mn).setLabelText(text);
695 model.updateViews();
696 }
697 } else {
698 mn = node;
699 }
700
701 if (mn != null) {
702 for (Iterator it = manager.viewGraphs(); it.hasNext();) {
703 final Graph2D graph = ((Graph2D) it.next());
704 final Node vn = manager.getViewNode(mn, graph);
706 if (vn != null && vn != node) {
707 graph.getRealizer(vn).setLabelText(text);
709 graph.updateViews();
710 }
711 }
712 }
713
714 armed = true;
715 }
716 }
717
718
722 private final class SynchViewToModel extends AbstractAction {
723 private final Graph2DView view;
724
725 SynchViewToModel( final Graph2DView view ) {
726 super("Synchronize");
727 this.view = view;
728 final URL imageURL = ClassLoader.getSystemResource(
729 "demo/view/advanced/resource/Export16.gif");
730 if (imageURL != null) {
731 this.putValue(Action.SMALL_ICON, new ImageIcon(imageURL));
732 }
733 this.putValue(Action.SHORT_DESCRIPTION, "Synchronize View to Model");
734 }
735
736 public void actionPerformed( final ActionEvent e ) {
737 manager.synchronizeViewGraphToModel(view.getGraph2D());
738
739 ModelViewManagerDemo.this.view.fitContent();
740 ModelViewManagerDemo.this.view.updateView();
741 for (int i = 0; i < subViews.length; ++i) {
742 subViews[i].fitContent();
743 subViews[i].updateView();
744 }
745 }
746 }
747
748
752 private static final class Layout extends AbstractAction {
753 private final Graph2DView view;
754 private final Layouter layouter;
755
756 Layout( final Graph2DView view, final Layouter layouter ) {
757 super("Layout");
758 this.view = view;
759 this.layouter = layouter;
760 this.putValue(Action.SMALL_ICON, getIconResource("resource/layout.png"));
761 this.putValue(Action.SHORT_DESCRIPTION, "Layout Graph");
762 }
763
764 public void actionPerformed( final ActionEvent e ) {
765 view.applyLayout(layouter);
766
767 view.fitContent();
768 view.updateView();
769 }
770 }
771 }
772