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.Set;
34 import javax.swing.AbstractAction;
35 import javax.swing.Action;
36 import javax.swing.ActionMap;
37 import javax.swing.ImageIcon;
38 import javax.swing.InputMap;
39 import javax.swing.JComponent;
40 import javax.swing.JPanel;
41 import javax.swing.JRootPane;
42 import javax.swing.JToolBar;
43
44 import y.base.Edge;
45 import y.base.EdgeCursor;
46 import y.base.Graph;
47 import y.base.GraphEvent;
48 import y.base.GraphListener;
49 import y.base.Node;
50 import y.base.NodeCursor;
51 import y.layout.BufferedLayouter;
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.Arrow;
60 import y.view.DefaultBackgroundRenderer;
61 import y.view.EditMode;
62 import y.view.Graph2D;
63 import y.view.Graph2DCopyFactory;
64 import y.view.Graph2DEvent;
65 import y.view.Graph2DListener;
66 import y.view.Graph2DView;
67 import y.view.Graph2DViewActions;
68 import y.view.Graph2DViewMouseWheelZoomListener;
69 import y.view.ModelViewManager;
70 import y.view.NodeLabel;
71 import y.view.NodeRealizer;
72 import y.view.ShapeNodeRealizer;
73
74
79 public class ModelViewManagerDemo extends DemoBase {
80 private final ModelViewManager manager;
81 private final Graph2DView[] subViews;
82
83 public ModelViewManagerDemo() {
84 subViews = new Graph2DView[3];
85
86 initGraph(view.getGraph2D());
87 manager = ModelViewManager.getInstance(view.getGraph2D());
88
89 contentPane.remove(view);
90 contentPane.add(createMultiView(), BorderLayout.CENTER);
91 }
92
93
96 private void initGraph( final Graph2D graph ) {
97 graph.clear();
98 graph.getDefaultNodeRealizer().setFillColor(new Color(73, 147, 255));
99 graph.getDefaultEdgeRealizer().setTargetArrow(Arrow.STANDARD);
100
101 final Node[] nodes = new Node[10];
103 for (int i = 0; i < nodes.length; ++i) {
104 nodes[i] = graph.createNode();
105 }
106
107 graph.createEdge(nodes[1], nodes[8]);
109 graph.createEdge(nodes[1], nodes[2]);
110 graph.createEdge(nodes[1], nodes[6]);
111 graph.createEdge(nodes[1], nodes[0]);
112 graph.createEdge(nodes[2], nodes[0]);
113 graph.createEdge(nodes[3], nodes[5]);
114 graph.createEdge(nodes[3], nodes[6]);
115 graph.createEdge(nodes[3], nodes[1]);
116 graph.createEdge(nodes[4], nodes[2]);
117 graph.createEdge(nodes[4], nodes[7]);
118 graph.createEdge(nodes[5], nodes[0]);
119 graph.createEdge(nodes[6], nodes[5]);
120 graph.createEdge(nodes[6], nodes[0]);
121 graph.createEdge(nodes[7], nodes[2]);
122 graph.createEdge(nodes[7], nodes[8]);
123 graph.createEdge(nodes[8], nodes[4]);
124 graph.createEdge(nodes[9], nodes[8]);
125 graph.createEdge(nodes[9], nodes[7]);
126
127 for (NodeCursor nc = graph.nodes(); nc.ok(); nc.next()) {
129 graph.getRealizer(nc.node()).setLabelText(Integer.toString(nc.node().index()));
130 }
131
132 final IncrementalHierarchicLayouter ihl = new IncrementalHierarchicLayouter();
134 (new BufferedLayouter(ihl)).doLayout(graph);
135 }
136
137
141 private JComponent createMultiView() {
142 final JPanel pane = new JPanel(new GridLayout(2, 2, 1, 1));
143
144 final UpdateHandler updateHandler = new UpdateHandler();
146 final LabelChangeHandler labelChangeHandler = new LabelChangeHandler();
147 final Graph2DViewMouseWheelZoomListener mwzl =
148 new Graph2DViewMouseWheelZoomListener();
149
150 final JToolBar vtb = createToolBar(view);
152 if (vtb != null) {
153 final JPanel viewAndTools = new JPanel(new BorderLayout());
154 viewAndTools.add(view, BorderLayout.CENTER);
155 viewAndTools.add(vtb, BorderLayout.NORTH);
156 pane.add(viewAndTools);
157 } else {
158 pane.add(view);
159 }
160 view.fitContent();
161 view.getGraph2D().addGraph2DListener(labelChangeHandler);
162 MyBackgroundRenderer.newInstance(view).setText("Editable Model");
163
164
165
166 for (int i = 0; i < subViews.length; ++i) {
169 subViews[i] = new Graph2DView();
170 final JToolBar svitb = createToolBar(subViews[i]);
171 if (svitb != null) {
172 final JPanel viewAndTools = new JPanel(new BorderLayout());
173 viewAndTools.add(subViews[i], BorderLayout.CENTER);
174 viewAndTools.add(svitb, BorderLayout.NORTH);
175 pane.add(viewAndTools);
176 } else {
177 pane.add(subViews[i]);
178 }
179 }
180
181
182
183 final Graph2D graph = subViews[0].getGraph2D();
185 graph.setGraphCopyFactory(new MyCopyFactory(createRedCircle()));
186
187 manager.addViewGraph(graph, null, true);
190 manager.synchronizeModelToViewGraph(graph);
191
192 graph.setDefaultNodeRealizer(createRedCircle());
193 graph.addGraph2DListener(labelChangeHandler);
194
195 final Graph2DViewActions actions = new Graph2DViewActions(subViews[0]);
197 final ActionMap amap = actions.createActionMap();
198 final InputMap imap = actions.createDefaultInputMap(amap);
199 subViews[0].getCanvasComponent().setActionMap(amap);
200 subViews[0].getCanvasComponent().setInputMap(JComponent.WHEN_FOCUSED, imap);
201 subViews[0].getCanvasComponent().addMouseWheelListener(mwzl);
202 subViews[0].addViewMode(new EditMode());
203 MyBackgroundRenderer.newInstance(subViews[0]).setText("Editable View");
204
205 (new BufferedLayouter(createOrthogonalLayouter())).doLayout(graph);
207 subViews[0].fitContent();
208
209
210
211 subViews[1].setGraph2D((Graph2D) manager.createViewGraph(
213 new MyCopyFactory(createOrangeOctagon()), new NoEdgesFilter(), false));
214
215 subViews[1].fitContent();
217 subViews[1].getCanvasComponent().addMouseWheelListener(mwzl);
218 MyBackgroundRenderer.newInstance(subViews[1]).setText("Non-editable View");
219
220
221
222 subViews[2].setGraph2D((Graph2D) manager.createViewGraph(
224 null, new ExcludeFilter(view.getGraph2D()), false));
225
226 subViews[2].fitContent();
228 subViews[2].getCanvasComponent().addMouseWheelListener(mwzl);
229 MyBackgroundRenderer.newInstance(subViews[2]).setText("Non-editable View");
230
231
232
233 manager.getModel().addGraphListener(updateHandler);
236 for (Iterator it = manager.viewGraphs(); it.hasNext();) {
237 ((Graph) it.next()).addGraphListener(updateHandler);
238 }
239
240
241 return pane;
242 }
243
244
248 public void addContentTo( final JRootPane rootPane ) {
249 super.addContentTo(rootPane);
250 final ComponentAdapter handler = new ComponentAdapter() {
251 private int callCount;
252
253 public void componentResized( final ComponentEvent e ) {
254 if (callCount < 2) {
255 configureviews();
256 }
257 ++callCount;
258 if (callCount == 2) {
259 rootPane.removeComponentListener(this);
260 }
261 }
262
263 private void configureviews() {
264 view.fitContent();
265 view.updateView();
266 for (int i = 0; i < subViews.length; ++i) {
267 subViews[i].fitContent();
268 subViews[i].updateView();
269 }
270 }
271 };
272 rootPane.addComponentListener(handler);
273 }
274
275
281 protected JToolBar createToolBar() {
282 return null;
283 }
284
285
288 private JToolBar createToolBar( final Graph2DView view ) {
289 final JToolBar jtb = new JToolBar();
290 jtb.setFloatable(false);
291
292 if (view == this.view || view == this.subViews[0]) {
294 jtb.add(new DeleteAll(view));
295 jtb.add(new DeleteSelection(view));
296 }
297
298 jtb.add(new FitContent(view));
300
301 jtb.addSeparator();
302 jtb.add(createLayoutAction(view));
304
305 if (view == this.subViews[1] || view == this.subViews[2]) {
307 jtb.add(new SynchViewToModel(view));
308 }
309
310 return jtb;
311 }
312
313
317 private Action createLayoutAction( final Graph2DView view ) {
318 final Layout layout;
319
320 if (view == this.view) {
321 layout = new Layout(view, new IncrementalHierarchicLayouter());
323 layout.putValue(Action.SHORT_DESCRIPTION, "Layout Hierarchically");
324 } else if (view == subViews[0]) {
325 layout = new Layout(view, createOrthogonalLayouter());
327 layout.putValue(Action.SHORT_DESCRIPTION, "Layout Orthogonally");
328 } else if (view == subViews[1]) {
329 layout = new Layout(view, new ComponentLayouter());
331 layout.putValue(Action.SHORT_DESCRIPTION, "Layout Component Grid");
332 } else if (view == subViews[2]) {
333 layout = new Layout(view, createBalloonLayouter());
335 layout.putValue(Action.SHORT_DESCRIPTION, "Layout Balloon-style Tree");
336 } else {
337 layout = new Layout(view, null);
338 }
339
340 return layout;
341 }
342
343
344 public static void main( String[] args ) {
345 initLnF();
346 try {
347 EventQueue.invokeAndWait(new Runnable() {
348 public void run() {
349 (new ModelViewManagerDemo()).start();
350 }
351 });
352 } catch (Throwable t) {
353 t.printStackTrace();
354 }
355 }
356
357
360 private static NodeRealizer createRedCircle() {
361 final ShapeNodeRealizer snr = new ShapeNodeRealizer();
362 snr.setShapeType(ShapeNodeRealizer.ELLIPSE);
363 snr.setFillColor(new Color(196, 0, 64));
364 return snr;
365 }
366
367
370 private static NodeRealizer createOrangeOctagon() {
371 final ShapeNodeRealizer snr = new ShapeNodeRealizer();
372 snr.setWidth(50);
373 snr.setShapeType(ShapeNodeRealizer.OCTAGON);
374 snr.setFillColor(new Color(223, 134, 17));
375 return snr;
376 }
377
378
381 private static Layouter createBalloonLayouter() {
382 final OrthogonalEdgeRouter orthogonal = new OrthogonalEdgeRouter();
383 orthogonal.setCrossingCost(1.0);
384 orthogonal.setReroutingEnabled(true);
385 orthogonal.setSphereOfAction(OrthogonalEdgeRouter.ROUTE_SELECTED_EDGES);
386
387 final TreeReductionStage trs = new TreeReductionStage();
388 trs.setNonTreeEdgeSelectionKey(OrthogonalEdgeRouter.SELECTED_EDGES);
389 trs.setNonTreeEdgeRouter(orthogonal);
390
391 final BalloonLayouter bl = new BalloonLayouter();
392 bl.setRootNodePolicy(BalloonLayouter.DIRECTED_ROOT);
393 bl.setPreferredChildWedge(300);
394 bl.setPreferredRootWedge(360);
395 bl.setMinimalEdgeLength(40);
396 bl.setCompactnessFactor(0.5);
397 bl.setAllowOverlaps(false);
398 bl.setFromSketchModeEnabled(false);
399 bl.appendStage(trs);
400 return bl;
401 }
402
403
406 private static Layouter createOrthogonalLayouter() {
407 final OrthogonalLayouter ol = new OrthogonalLayouter();
408 ol.setLayoutStyle(OrthogonalLayouter.NORMAL_TREE_STYLE);
409 ol.setGrid(25);
410 ol.setUseLengthReduction(true);
411 ol.setUseCrossingPostprocessing(true);
412 ol.setPerceivedBendsOptimizationEnabled(true);
413 ol.setUseRandomization(false);
414 ol.setUseFaceMaximization(true);
415 ol.setUseSketchDrawing(false);
416 return ol;
417 }
418
419
420
425 private static final class NoEdgesFilter implements ModelViewManager.Filter {
426 public boolean acceptInsertion( final Node node ) {
427 return true;
428 }
429
430 public boolean acceptInsertion( final Edge edge ) {
431 return false;
432 }
433
434 public boolean acceptRemoval( final Node model ) {
435 return true;
436 }
437
438 public boolean acceptRemoval( final Edge model ) {
439 return true;
440 }
441 }
442
443
449 private static final class ExcludeFilter implements ModelViewManager.Filter {
450 final Set excludedNodes;
451 final Set excludedEdges;
452
453 ExcludeFilter( final Graph graph ) {
454 excludedNodes = new HashSet();
455 excludedEdges = new HashSet();
456
457 for (NodeCursor nc = graph.nodes(); nc.ok(); nc.next()) {
458 excludedNodes.add(nc.node());
459 }
460 for (EdgeCursor ec = graph.edges(); ec.ok(); ec.next()) {
461 excludedEdges.add(ec.edge());
462 }
463 }
464
465 public boolean acceptInsertion( final Node node ) {
466 return !excludedNodes.contains(node);
467 }
468
469 public boolean acceptInsertion( final Edge edge ) {
470 return !excludedEdges.contains(edge);
471 }
472
473 public boolean acceptRemoval( final Node model ) {
474 return true;
475 }
476
477 public boolean acceptRemoval( final Edge model ) {
478 return true;
479 }
480 }
481
482
487 private static final class MyCopyFactory extends Graph2DCopyFactory {
488 private NodeRealizer template;
489
490 MyCopyFactory( final NodeRealizer template ) {
491 setTemplateImpl(template);
492 }
493
494 protected NodeRealizer copyRealizer( final NodeRealizer nr ) {
495 if (template != null) {
496 final NodeRealizer _nr = template.createCopy();
499 _nr.setCenter(nr.getCenterX(), nr.getCenterY());
500
501 final int lc = nr.labelCount();
503 if (lc > 0) {
504 _nr.setLabel((NodeLabel) nr.getLabel().clone());
505 for (int i = 1; i < lc; ++i) {
506 _nr.addLabel((NodeLabel) nr.getLabel(i).clone());
507 }
508 }
509 return _nr;
510 } else {
511 return nr.createCopy();
512 }
513 }
514
515 NodeRealizer getTemplate() {
516 return template;
517 }
518
519 void setTemplate( final NodeRealizer template ) {
520 setTemplateImpl(template);
521 }
522
523 private void setTemplateImpl( final NodeRealizer template ) {
524 this.template = template != null ? template.createCopy() : null;
525 }
526 }
527
528
531 private static final class MyBackgroundRenderer
532 extends DefaultBackgroundRenderer {
533 private String text;
534 private Color textColor;
535 private final Rectangle r;
536
537 MyBackgroundRenderer( final Graph2DView view ) {
538 super(view);
539 textColor = new Color(192, 192, 192);
540 r = new Rectangle(0, 0, -1, 1);
541 }
542
543 String getText() {
544 return text;
545 }
546
547 void setText( final String text ) {
548 this.text = text;
549 }
550
551 Color getTextColor() {
552 return textColor;
553 }
554
555 void setTextColor( final Color color ) {
556 this.textColor = color;
557 }
558
559 public void paint(
560 final Graphics2D gfx,
561 final int x,
562 final int y,
563 final int w,
564 final int h ) {
565 super.paint(gfx, x, y, w, h);
566 paintText(gfx);
567 }
568
569 private void paintText( final Graphics2D gfx ) {
570 if (text != null && textColor != null) {
571 final Color oldColor = gfx.getColor();
572 final Font oldFont = gfx.getFont();
573
574 undoWorldTransform(gfx);
575
576 gfx.setColor(textColor);
577 gfx.setFont(oldFont.deriveFont(30.0f));
578
579 view.getBounds(r);
580 r.setLocation(0, 0);
581 final FontMetrics fm = gfx.getFontMetrics();
582 final Rectangle2D bnds = fm.getStringBounds(text, gfx);
583 final float textX = (float) (r.x + (r.width - bnds.getWidth()) * 0.5);
584 final float textY = (float) (r.y + (r.height - bnds.getHeight()) * 0.5 + fm.getMaxAscent());
585 gfx.drawString(text, textX, textY);
586
587 redoWorldTransform(gfx);
588
589 gfx.setFont(oldFont);
590 gfx.setColor(oldColor);
591 }
592 }
593
594
595 static MyBackgroundRenderer newInstance( final Graph2DView view ) {
596 final MyBackgroundRenderer mbr = new MyBackgroundRenderer(view);
597 view.setBackgroundRenderer(mbr);
598 return mbr;
599 }
600 }
601
602
606 private static class UpdateHandler implements GraphListener {
607 private int block;
608
609 public void onGraphEvent( final GraphEvent e ) {
610 if (e.getGraph() instanceof Graph2D) {
611 switch (e.getType()) {
612 case GraphEvent.PRE_EVENT:
613 ++block;
614 break;
615 case GraphEvent.POST_EVENT:
616 --block;
617 break;
618 default:
619 break;
620 }
621 if (block == 0) {
622 ((Graph2D) e.getGraph()).updateViews();
623 }
624 }
625 }
626 }
627
628
632 private class LabelChangeHandler implements Graph2DListener {
633 private boolean armed;
634
635 LabelChangeHandler() {
636 armed = true;
637 }
638
639 public void onGraph2DEvent( final Graph2DEvent e ) {
640 if (!armed) {
641 return;
642 }
643
644 if ("text".equals(e.getPropertyName()) &&
645 e.getSubject() instanceof NodeLabel) {
646 final NodeLabel nl = (NodeLabel) e.getSubject();
647 setLabelText(nl.getNode(), nl.getText());
648 }
649 }
650
651 private void setLabelText( final Node node, final String text ) {
652 armed = false;
653
654 final Node mn;
655 final Graph2D model = view.getGraph2D();
656 if (node.getGraph() != model) {
657 mn = manager.getModelNode(node);
659 if (mn != null) {
660 model.getRealizer(mn).setLabelText(text);
662 model.updateViews();
663 }
664 } else {
665 mn = node;
666 }
667
668 if (mn != null) {
669 for (Iterator it = manager.viewGraphs(); it.hasNext();) {
670 final Graph2D graph = ((Graph2D) it.next());
671 final Node vn = manager.getViewNode(mn, graph);
673 if (vn != null && vn != node) {
674 graph.getRealizer(vn).setLabelText(text);
676 graph.updateViews();
677 }
678 }
679 }
680
681 armed = true;
682 }
683 }
684
685
689 private final class SynchViewToModel extends AbstractAction {
690 private final Graph2DView view;
691
692 SynchViewToModel( final Graph2DView view ) {
693 super("Synchronize");
694 this.view = view;
695 final URL imageURL = ClassLoader.getSystemResource(
696 "demo/view/advanced/resource/Export16.gif");
697 if (imageURL != null) {
698 this.putValue(Action.SMALL_ICON, new ImageIcon(imageURL));
699 }
700 this.putValue(Action.SHORT_DESCRIPTION, "Synchronize View to Model");
701 }
702
703 public void actionPerformed( final ActionEvent e ) {
704 manager.synchronizeViewGraphToModel(view.getGraph2D());
705
706 ModelViewManagerDemo.this.view.fitContent();
707 ModelViewManagerDemo.this.view.updateView();
708 for (int i = 0; i < subViews.length; ++i) {
709 subViews[i].fitContent();
710 subViews[i].updateView();
711 }
712 }
713 }
714
715
719 private static final class Layout extends AbstractAction {
720 private final Graph2DView view;
721 private final Layouter layouter;
722
723 Layout( final Graph2DView view, final Layouter layouter ) {
724 super("Layout");
725 this.view = view;
726 this.layouter = layouter;
727 final URL imageURL = ClassLoader.getSystemResource(
728 "demo/view/advanced/resource/Layout16.gif");
729 if (imageURL != null) {
730 this.putValue(Action.SMALL_ICON, new ImageIcon(imageURL));
731 }
732 this.putValue(Action.SHORT_DESCRIPTION, "Layout Graph");
733 }
734
735 public void actionPerformed( final ActionEvent e ) {
736 (new BufferedLayouter(layouter)).doLayout(view.getGraph2D());
737
738 view.fitContent();
739 view.updateView();
740 }
741 }
742 }
743