1   /****************************************************************************
2    **
3    ** This file is part of yFiles-2.6. 
4    ** 
5    ** yWorks proprietary/confidential. Use is subject to license terms.
6    **
7    ** Redistribution of this file or of an unauthorized byte-code version
8    ** of this file is strictly forbidden.
9    **
10   ** Copyright (c) 2000-2008 by yWorks GmbH, Vor dem Kreuzberg 28, 
11   ** 72070 Tuebingen, Germany. All rights reserved.
12   **
13   ***************************************************************************/
14  package demo.view.layout.hierarchic;
15  
16  import demo.view.DemoBase;
17  import demo.view.advanced.DragAndDropDemo;
18  import demo.view.realizer.GenericNodeRealizerDemo;
19  import y.anim.AnimationFactory;
20  import y.anim.AnimationPlayer;
21  import y.base.DataMap;
22  import y.base.DataProvider;
23  import y.base.Edge;
24  import y.base.Node;
25  import y.base.NodeCursor;
26  import y.base.NodeList;
27  import y.layout.BufferedLayouter;
28  import y.layout.GraphLayout;
29  import y.layout.PortCandidate;
30  import y.layout.PortCandidateSet;
31  import y.layout.PortConstraint;
32  import y.layout.hierarchic.IncrementalHierarchicLayouter;
33  import y.layout.hierarchic.incremental.IncrementalHintsFactory;
34  import y.layout.hierarchic.incremental.SimplexNodePlacer;
35  import y.util.DataProviderAdapter;
36  import y.util.DataProviders;
37  import y.util.Maps;
38  import y.view.Arrow;
39  import y.view.BridgeCalculator;
40  import y.view.DefaultGraph2DRenderer;
41  import y.view.EdgeRealizer;
42  import y.view.EditMode;
43  import y.view.GenericNodeRealizer;
44  import y.view.Graph2D;
45  import y.view.Graph2DView;
46  import y.view.HitInfo;
47  import y.view.LayoutMorpher;
48  import y.view.NodeRealizer;
49  import y.view.GeneralPathNodePainter;
50  
51  import javax.swing.AbstractAction;
52  import javax.swing.JScrollPane;
53  import javax.swing.JToolBar;
54  import javax.swing.SwingUtilities;
55  import java.awt.BorderLayout;
56  import java.awt.Color;
57  import java.awt.Cursor;
58  import java.awt.event.ActionEvent;
59  import java.awt.geom.Ellipse2D;
60  import java.awt.geom.GeneralPath;
61  import java.awt.geom.RoundRectangle2D;
62  import java.util.ArrayList;
63  import java.util.HashMap;
64  import java.util.Iterator;
65  import java.util.List;
66  import java.util.Map;
67  
68  /**
69   * This demo shows a simple FlowChartEditor. It depicts how to use {@link IncrementalHierarchicLayouter} in both
70   * normal and incremental mode and the use of {@link PortConstraint}s and {@link PortCandidateSet}s.
71   */
72  public class FlowChartDemo extends DemoBase {
73  
74    // the layouter instance to use for the automatic layouts
75    private IncrementalHierarchicLayouter layouter;
76    // the hintmap used for hinting the layouter if a new node should be added incrementally
77    private DataMap hintMap;
78  
79    public FlowChartDemo() {
80      configureView();
81  
82      final List nodeRealizerList = new ArrayList();
83      addNodeRealizerTemplates(nodeRealizerList);
84  
85      // creat a nice customized DragAndDrop List support
86      DragAndDropDemo.DragAndDropSupport dndSupport = new DragAndDropDemo.DragAndDropSupport(nodeRealizerList, view) {
87  
88        // use the configuration string from GenericNodeRealizer for the Transferable
89        protected String getTextValue(NodeRealizer selected) {
90          if (selected instanceof GenericNodeRealizer) {
91            GenericNodeRealizer gnr = (GenericNodeRealizer) selected;
92            return gnr.getConfiguration();
93          } else {
94            return null;
95          }
96        }
97  
98        // use the configuration string from GenericNodeRealizer for the Transferable
99        protected NodeRealizer createNodeRealizerFromTextValue(String s) {
100         try {
101           for (Iterator iterator = nodeRealizerList.iterator(); iterator.hasNext();) {
102             GenericNodeRealizer genericNodeRealizer = (GenericNodeRealizer) iterator.next();
103             if (genericNodeRealizer.getConfiguration().equals(s)) {
104               return genericNodeRealizer;
105             }
106           }
107         } catch (IllegalArgumentException iae){
108         }
109         return null;
110       }
111 
112       // customize the drop operation to invoke a layout algorithm and auto-create an edge
113       protected boolean dropRealizer(Graph2DView view, NodeRealizer r, double worldCoordX, double worldCoordY) {
114         final Graph2D graph = view.getGraph2D();
115         final HitInfo hitInfo = new HitInfo(view, worldCoordX, worldCoordY, true, HitInfo.NODE);
116         r = r.createCopy();
117         r.setCenter(worldCoordX, worldCoordY);
118         final Node node = graph.createNode(r);
119         if (hitInfo.getHitNode() != null) {
120           graph.createEdge(hitInfo.getHitNode(), node);
121           SwingUtilities.invokeLater(new Runnable() {
122             public void run() {
123               runIncrementalLayout(new NodeList(node).nodes());
124             }
125           });
126         } else {
127           view.updateView();
128         }
129         return true;
130       }
131     };
132 
133     // add the list to the UI
134     contentPane.add(new JScrollPane(dndSupport.getList()), BorderLayout.WEST);
135     layouter = createLayouter();
136   }
137 
138   /**
139    * Overwritten to disable node label setting and disallow resizing.
140    */
141   protected EditMode createEditMode() {
142     final EditMode editMode = super.createEditMode();
143     editMode.assignNodeLabel(false);
144     editMode.allowResizeNodes(false);
145     return editMode;
146   }
147 
148   /**
149    * Add a layout button to the ToolBar
150    */
151   protected JToolBar createToolBar() {
152     final JToolBar toolBar = super.createToolBar();
153     toolBar.add(new AbstractAction("Layout") {
154       public void actionPerformed(ActionEvent e) {
155         runLayout();
156       }
157     });
158     return toolBar;
159   }
160 
161   /**
162    * Configures the view.
163    */
164   private void configureView() {
165     final EdgeRealizer defaultEdgeRealizer = view.getGraph2D().getDefaultEdgeRealizer();
166     defaultEdgeRealizer.setTargetArrow(Arrow.STANDARD);
167     ((DefaultGraph2DRenderer) view.getGraph2DRenderer()).setBridgeCalculator(new BridgeCalculator());
168   }
169 
170   /**
171    * Creates the Layouter instance.
172    */
173   private IncrementalHierarchicLayouter createLayouter(){
174     final IncrementalHierarchicLayouter layouter = new IncrementalHierarchicLayouter();
175     ((SimplexNodePlacer) layouter.getNodePlacer()).setBaryCenterModeEnabled(true);
176     layouter.setLayoutMode(IncrementalHierarchicLayouter.LAYOUT_MODE_FROM_SCRATCH);
177     layouter.getEdgeLayoutDescriptor().setOrthogonallyRouted(true);
178     final Graph2D graph = view.getGraph2D();
179 
180     // create the map for the Incremental Hints
181     hintMap = Maps.createDataMap(new HashMap());
182     graph.addDataProvider(IncrementalHierarchicLayouter.INCREMENTAL_HINTS_DPKEY, hintMap);
183 
184     // create an adapter that returns the userdata of the GenericNodeRealizers as the PortCandidateSet
185     graph.addDataProvider(PortCandidateSet.NODE_DP_KEY, new DataProviderAdapter() {
186       public Object get(Object dataHolder) {
187         final Node node = (Node) dataHolder;
188         final NodeRealizer realizer = graph.getRealizer(node);
189         if (realizer instanceof GenericNodeRealizer){
190           return (PortCandidateSet) ((GenericNodeRealizer) realizer).getUserData();
191         } else {
192           return null;
193         }
194       }
195     });
196 
197     // create automatic bus structures for outgoing edges of "start" nodes
198     graph.addDataProvider(PortConstraint.SOURCE_GROUPID_KEY, new DataProviderAdapter() {
199       public Object get(Object dataHolder) {
200         Edge edge = (Edge) dataHolder;
201         Node source = edge.source();
202         GenericNodeRealizer gnr = (GenericNodeRealizer) graph.getRealizer(source);
203         String sourceConfiguration = gnr.getConfiguration();
204         if (sourceConfiguration.equals("start")) {
205           return source;
206         }
207         return null;
208       }
209     });
210 
211     //... and bus structures for incoming edges at "switch" and "branch" nodes
212     graph.addDataProvider(PortConstraint.TARGET_GROUPID_KEY, new DataProviderAdapter() {
213       public Object get(Object dataHolder) {
214         Edge edge = (Edge) dataHolder;
215         Node target = edge.target();
216         GenericNodeRealizer gnr = (GenericNodeRealizer) graph.getRealizer(target);
217         String targetConfiguration = gnr.getConfiguration();
218         if (targetConfiguration.equals("switch") || targetConfiguration.equals("branch")) {
219           return target;
220         }
221         return null;
222       }
223     });
224 
225     // make sure that edges enter at the north side and exit at the south side of the nodes
226     final DataProvider southDP = DataProviders.createConstantDataProvider(PortConstraint.create(PortConstraint.SOUTH));
227     graph.addDataProvider(PortConstraint.SOURCE_PORT_CONSTRAINT_KEY, southDP);
228     final DataProvider northDP = DataProviders.createConstantDataProvider(PortConstraint.create(PortConstraint.NORTH));
229     graph.addDataProvider(PortConstraint.TARGET_PORT_CONSTRAINT_KEY, northDP);
230     return layouter;
231   }
232 
233   /**
234    * Run the layout in normal mode.
235    */
236   private void runLayout() {
237     Cursor oldCursor = view.getCanvasComponent().getCursor();
238     try {
239       view.getCanvasComponent().setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
240       GraphLayout result = new BufferedLayouter(layouter).calcLayout(view.getGraph2D());
241       LayoutMorpher morpher = new LayoutMorpher(view, result);
242       morpher.setSmoothViewTransform(true);
243       morpher.setPreferredDuration(300);
244       final AnimationPlayer player = new AnimationPlayer();
245       player.addAnimationListener(view);
246       player.setFps(120);
247       player.animate(AnimationFactory.createEasedAnimation(morpher));
248     } finally {
249       view.getCanvasComponent().setCursor(oldCursor);
250     }
251   }
252 
253   /**
254    * Run the layout in incremental mode.
255    */
256   private void runIncrementalLayout(NodeCursor incrementalNodes) {
257     Cursor oldCursor = view.getCanvasComponent().getCursor();
258     try {
259       view.getCanvasComponent().setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
260       layouter.setLayoutMode(IncrementalHierarchicLayouter.LAYOUT_MODE_INCREMENTAL);
261       final IncrementalHintsFactory factory = layouter.createIncrementalHintsFactory();
262       for (incrementalNodes.toFirst(); incrementalNodes.ok(); incrementalNodes.next()){
263         hintMap.set(incrementalNodes.node(), factory.createLayerIncrementallyHint(incrementalNodes.node()));
264       }
265       GraphLayout result = new BufferedLayouter(layouter).calcLayout(view.getGraph2D());
266       LayoutMorpher morpher = new LayoutMorpher(view, result);
267       morpher.setSmoothViewTransform(true);
268       morpher.setPreferredDuration(300);
269       final AnimationPlayer player = new AnimationPlayer();
270       player.addAnimationListener(view);
271       player.setFps(120);
272       player.animate(AnimationFactory.createEasedAnimation(morpher));
273     } finally {
274       layouter.setLayoutMode(IncrementalHierarchicLayouter.LAYOUT_MODE_FROM_SCRATCH);
275       for (incrementalNodes.toFirst(); incrementalNodes.ok(); incrementalNodes.next()){
276         hintMap.set(incrementalNodes.node(), null);
277       }
278       view.getCanvasComponent().setCursor(oldCursor);
279     }
280   }
281 
282   /** This method adds the possible NodeRealizer's for this application to the given list.
283    */
284   private void addNodeRealizerTemplates(List nodeRealizerList) {
285     // obtain the factory to register the configurations
286     final GenericNodeRealizer.Factory factory = GenericNodeRealizer.getFactory();
287     {
288       // create a start node configuration
289       final Map map = GenericNodeRealizer.getFactory().createDefaultConfigurationMap();
290       Ellipse2D.Double outer = new Ellipse2D.Double(0,0, 1,1);
291       Ellipse2D.Double inner = new Ellipse2D.Double(0.1, 0.1, 0.8, 0.8);
292       Ellipse2D.Double inner2 = new Ellipse2D.Double(0.2, 0.2, 0.6, 0.6);
293       GeneralPath gp = new GeneralPath(GeneralPath.WIND_EVEN_ODD, 6);
294       gp.append(outer, true);
295       gp.append(inner, false);
296       gp.append(inner2, false);
297       final GeneralPathNodePainter painter = new GeneralPathNodePainter(gp);
298       final GenericNodeRealizerDemo.RectangularShapePainter containsTest = new GenericNodeRealizerDemo.RectangularShapePainter(outer);
299       map.put(GenericNodeRealizer.Painter.class, painter);
300       map.put(GenericNodeRealizer.ContainsTest.class, containsTest);
301       factory.addConfiguration("start", map);
302 
303       // create an configure an instance
304       final GenericNodeRealizer startRealizer = new GenericNodeRealizer("start");
305       startRealizer.setFillColor(Color.black);
306 
307       // configure the PortCandidateSet as the userdata
308       // sophisticated Painter implementations could visualize these ports.
309       PortCandidateSet candidateSet = new PortCandidateSet();
310       candidateSet.add(PortCandidate.createCandidate(0.0d, 0.0d, PortCandidate.ANY, 0), Integer.MAX_VALUE);
311       startRealizer.setUserData(candidateSet);
312 
313       // add it to the list
314       nodeRealizerList.add(startRealizer);
315     }
316     {
317       final Map map = GenericNodeRealizer.getFactory().createDefaultConfigurationMap();
318       RoundRectangle2D.Double rr = new RoundRectangle2D.Double(0,0,100, 100, 20, 20);
319       final GenericNodeRealizerDemo.RectangularShapePainter painter = new GenericNodeRealizerDemo.RectangularShapePainter(rr);
320       map.put(GenericNodeRealizer.Painter.class, painter);
321       map.put(GenericNodeRealizer.ContainsTest.class, painter);
322       factory.addConfiguration("state", map);
323 
324       // create an configure an instance
325       final GenericNodeRealizer stateRealizer = new GenericNodeRealizer("state");
326       stateRealizer.setFillColor(Color.yellow);
327 
328       // configure the PortCandidateSet as the userdata
329       PortCandidateSet candidateSet = new PortCandidateSet();
330       candidateSet.add(PortCandidate.createCandidate(PortCandidate.NORTH, 0), Integer.MAX_VALUE);
331       candidateSet.add(PortCandidate.createCandidate(PortCandidate.SOUTH, 0), Integer.MAX_VALUE);
332       stateRealizer.setUserData(candidateSet);
333 
334       // add it to the list
335       nodeRealizerList.add(stateRealizer);
336     }
337     {
338       final Map map = GenericNodeRealizer.getFactory().createDefaultConfigurationMap();
339       GeneralPath gp = new GeneralPath(GeneralPath.WIND_EVEN_ODD, 6);
340       gp.moveTo(0.5f,0f);
341       gp.lineTo(1.0f, 0.5f);
342       gp.lineTo(0.5f, 1.0f);
343       gp.lineTo(0,0.5f);
344       gp.closePath();
345       final GeneralPathNodePainter painter = new GeneralPathNodePainter(gp);
346       map.put(GenericNodeRealizer.Painter.class, painter);
347       map.put(GenericNodeRealizer.ContainsTest.class, painter);
348       factory.addConfiguration("switch", map);
349 
350       // create an configure an instance
351       final GenericNodeRealizer switchRealizer = new GenericNodeRealizer("switch");
352 
353       // configure the PortCandidateSet as the userdata
354       PortCandidateSet candidateSet = new PortCandidateSet();
355       candidateSet.add(PortCandidate.createCandidate(0, -15, PortCandidate.NORTH, 0), 1);
356       candidateSet.add(PortCandidate.createCandidate(0, 15, PortCandidate.SOUTH, 0), 1);
357       candidateSet.add(PortCandidate.createCandidate(15, 0,PortCandidate.EAST, 0), 1);
358       candidateSet.add(PortCandidate.createCandidate(-15, 0,PortCandidate.WEST, 0), 1);
359       candidateSet.add(PortCandidate.createCandidate(0, -15, PortCandidate.NORTH, 1), Integer.MAX_VALUE);
360       candidateSet.add(PortCandidate.createCandidate(0, 15, PortCandidate.SOUTH, 1), Integer.MAX_VALUE);
361       switchRealizer.setUserData(candidateSet);
362 
363       // add it to the list
364       nodeRealizerList.add(switchRealizer);
365     }
366     {
367       final Map map = GenericNodeRealizer.getFactory().createDefaultConfigurationMap();
368       GeneralPath gp = new GeneralPath(GeneralPath.WIND_EVEN_ODD, 6);
369       gp.moveTo(0.5f,0.3f);
370       gp.lineTo(1, 1);
371       gp.lineTo(0, 1);
372       gp.closePath();
373       final GeneralPathNodePainter painter = new GeneralPathNodePainter(gp);
374       map.put(GenericNodeRealizer.Painter.class, painter);
375       map.put(GenericNodeRealizer.ContainsTest.class, painter);
376       factory.addConfiguration("branch", map);
377 
378       // create an configure an instance
379       final GenericNodeRealizer branch = new GenericNodeRealizer("branch");
380 
381       // configure the PortCandidateSet as the userdata
382       PortCandidateSet candidateSet = new PortCandidateSet();
383       candidateSet.add(PortCandidate.createCandidate(0, 0, PortCandidate.NORTH, 0), Integer.MAX_VALUE);
384       candidateSet.add(PortCandidate.createCandidate(PortCandidate.SOUTH, 0), 3);
385       candidateSet.add(PortCandidate.createCandidate(0, 0,PortCandidate.EAST, 1), Integer.MAX_VALUE);
386       candidateSet.add(PortCandidate.createCandidate(0, 0,PortCandidate.WEST, 1), Integer.MAX_VALUE);
387       branch.setUserData(candidateSet);
388 
389       // add it to the list
390       nodeRealizerList.add(branch);
391     }
392     {
393       final Map map = GenericNodeRealizer.getFactory().createDefaultConfigurationMap();
394       Ellipse2D.Double outer = new Ellipse2D.Double(0.2, 0.2, 0.6, 0.6);
395       GeneralPath gp = new GeneralPath(GeneralPath.WIND_EVEN_ODD, 6);
396       gp.append(outer, true);
397 
398       final GeneralPathNodePainter painter = new GeneralPathNodePainter(gp);
399       map.put(GenericNodeRealizer.Painter.class, painter);
400       map.put(GenericNodeRealizer.ContainsTest.class, painter);
401       factory.addConfiguration("end", map);
402 
403       // create and configure an instance
404       final GenericNodeRealizer endRealizer = new GenericNodeRealizer("end");
405       endRealizer.setFillColor(Color.black);
406 
407       // configure the PortCandidateSet as the userdata
408       PortCandidateSet candidateSet = new PortCandidateSet();
409       candidateSet.add(PortCandidate.createCandidate(0, 0,PortCandidate.NORTH, 0), 1);
410       candidateSet.add(PortCandidate.createCandidate(0, 0,PortCandidate.EAST, 1), 1);
411       candidateSet.add(PortCandidate.createCandidate(0, 0,PortCandidate.WEST, 1), 1);
412       candidateSet.add(PortCandidate.createCandidate(0, 0,PortCandidate.EAST, 2), Integer.MAX_VALUE);
413       candidateSet.add(PortCandidate.createCandidate(0, 0,PortCandidate.WEST, 2), Integer.MAX_VALUE);
414       endRealizer.setUserData(candidateSet);
415 
416       // add it to the list
417       nodeRealizerList.add(endRealizer);
418     }
419   }
420 
421   public static void main(String[] args) {
422     initLnF();
423     new FlowChartDemo().start("Flow Chart Demo");
424   }
425 }
426