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.orthogonal;
15  
16  import demo.view.DemoBase;
17  import y.base.DataProvider;
18  import y.base.Edge;
19  import y.base.EdgeCursor;
20  import y.base.Node;
21  import y.layout.BufferedLayouter;
22  import y.layout.GraphLayout;
23  import y.layout.LayoutOrientation;
24  import y.layout.Layouter;
25  import y.layout.PortConstraintKeys;
26  import y.layout.orthogonal.DirectedOrthogonalLayouter;
27  import y.layout.router.EdgeGroupRouterStage;
28  import y.layout.router.OrthogonalEdgeRouter;
29  import y.util.DataProviderAdapter;
30  import y.view.Arrow;
31  import y.view.BridgeCalculator;
32  import y.view.DefaultGraph2DRenderer;
33  import y.view.EdgeRealizer;
34  import y.view.Graph2D;
35  import y.view.LayoutMorpher;
36  import y.view.LineType;
37  import y.view.NodeRealizer;
38  import y.view.PolyLineEdgeRealizer;
39  
40  import javax.swing.AbstractAction;
41  import javax.swing.JComboBox;
42  import javax.swing.JComponent;
43  import javax.swing.JList;
44  import javax.swing.JToolBar;
45  import javax.swing.ListCellRenderer;
46  import java.awt.Color;
47  import java.awt.Component;
48  import java.awt.Cursor;
49  import java.awt.Dimension;
50  import java.awt.Graphics;
51  import java.awt.Graphics2D;
52  import java.awt.event.ActionEvent;
53  import java.awt.event.ItemEvent;
54  import java.awt.event.ItemListener;
55  
56  /**
57   * <p>
58   * This demo shows how {@link DirectedOrthogonalLayouter} and {@link OrthogonalEdgeRouter} can be used to
59   * nicely layout UML Class Diagrams in an orthogonal layout style.
60   * <p>
61   * Usually, there are different kind of relationships between the classes of an UML diagram.
62   * Some of the relationships are undirected (e.g. associations) while others are directed
63   * (e.g. generalizations and realizations). This demo arranges a diagram in a way that
64   * directed relationships point in a main layout direction(here bottom-to-top), while the
65   * undirected relationships will be arranged without such a direction constraint.
66   * Furthermore, all directed relationships of the same type sharing a common target node
67   * will be routed in a bus-like style. For this special task
68   * OrthogonalEdgeRouter will be used in combination with {@link EdgeGroupRouterStage}.
69   * <p>
70   * The toolbar of this demo offers four additional items:
71   * <ol>
72   * <li>A combobox that selects the type of relationship to be used: association (no arrow),
73   * generalization (arrow and solid line), and realization (arrow and dashed line).
74   * </li>
75   * <li>Layout button - to layout the diagram</li>
76   * <li>Layout From Sketch button - to layout the diagram, while obeying the layout of the given diagam</li>
77   * <li>Route Edges button - to route all edges of the diagram, while preserving the coordinates of the nodes</li>
78   * </ol>
79   */
80  public class UMLClassDiagramLayouterDemo extends DemoBase {
81  
82    DirectedOrthogonalLayouter doLayouter;
83    OrthogonalEdgeRouter oeRouter;
84    Layouter layouter, router;
85  
86    public UMLClassDiagramLayouterDemo()
87    {
88      final Graph2D graph = view.getGraph2D();
89  
90      //configure default node realizer
91      NodeRealizer nr = graph.getDefaultNodeRealizer();
92      nr.setSize(80,50);
93      nr.setLabelText("<html><b>Class</b><br><hr>doit():void");
94      nr.setFillColor(new Color(189,185,146));
95  
96      //activate grid mode
97      view.setGridMode(true);
98      view.setGridResolution(15);
99  
100     //activate bridge style painting of edge crossings
101     DefaultGraph2DRenderer r = (DefaultGraph2DRenderer) view.getGraph2DRenderer();
102     BridgeCalculator bc = new BridgeCalculator();
103     bc.setCrossingMode(BridgeCalculator.CROSSING_MODE_VERTICAL_CROSSES_HORIZONTAL);
104     r.setBridgeCalculator(bc);
105 
106     configureLayout();
107 
108     loadGraph(getClass(), "resource/classdiagram01.gml");
109   }
110 
111 
112   /**
113    * Configures layout algorithm and adds layout-specific data providers to the graph
114    */
115   private void configureLayout() {
116     final Graph2D graph = view.getGraph2D();
117 
118     doLayouter = new DirectedOrthogonalLayouter();
119     doLayouter.setLayoutOrientation(LayoutOrientation.BOTTOM_TO_TOP);
120     doLayouter.setGrid(30);
121     layouter = doLayouter;
122 
123     DataProvider directedDP = new DataProviderAdapter() {
124       public boolean getBool(Object obj) {
125         return graph.getRealizer((Edge) obj).getTargetArrow() != Arrow.NONE;
126       }
127     };
128     graph.addDataProvider(DirectedOrthogonalLayouter.DIRECTED_EDGE_DPKEY, directedDP);
129 
130     DataProvider targetGroupDP = new DataProviderAdapter() {
131       public Object get(Object obj) {
132         EdgeRealizer er = graph.getRealizer((Edge) obj);
133         if (er.getTargetArrow() == Arrow.NONE)
134           return null;
135         else
136           return er.getLineType();
137       }
138     };
139     graph.addDataProvider(PortConstraintKeys.TARGET_GROUPID_KEY, targetGroupDP);
140 
141     oeRouter = new OrthogonalEdgeRouter();
142     oeRouter.setGridSpacing(10);
143     oeRouter.setGridRoutingEnabled(true);
144     oeRouter.setCrossingCost(2.0);
145     oeRouter.setReroutingEnabled(true);
146     router = new EdgeGroupRouterStage(oeRouter);
147   }
148 
149   /**
150    * Run a layout algorithm
151    */
152   private void runLayout(Layouter layouter) {
153     Cursor oldCursor = view.getCanvasComponent().getCursor();
154     try {
155       view.getCanvasComponent().setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
156       GraphLayout result = new BufferedLayouter(layouter).calcLayout(view.getGraph2D());
157       LayoutMorpher morpher = new LayoutMorpher(view, result);
158       morpher.execute();
159     } finally {
160       view.getCanvasComponent().setCursor(oldCursor);
161     }
162   }
163 
164   /**
165    * Add a layout button and a combobox for edge realizer selection to the ToolBar
166    */
167   protected JToolBar createToolBar() {
168     final JToolBar toolBar = super.createToolBar();
169 
170     toolBar.add(createEdgeRealizerComboBox());
171 
172     toolBar.addSeparator();
173     toolBar.add(new AbstractAction("Layout") {
174       public void actionPerformed(ActionEvent e) {
175         doLayouter.setUseSketchDrawing(false);
176         runLayout(doLayouter);
177       }
178     });
179     toolBar.addSeparator();
180     toolBar.add(new AbstractAction("Layout From Sketch") {
181       public void actionPerformed(ActionEvent e) {
182         doLayouter.setUseSketchDrawing(true);
183         runLayout(doLayouter);
184       }
185     });
186     toolBar.addSeparator();
187     toolBar.add(new AbstractAction("Route Edges") {
188       public void actionPerformed(ActionEvent e) {
189         runLayout(router);
190       }
191     });
192 
193     return toolBar;
194   }
195 
196   JComboBox createEdgeRealizerComboBox()
197   {
198     final EdgeRealizer association = new PolyLineEdgeRealizer();
199     final EdgeRealizer generalization = new PolyLineEdgeRealizer();
200     generalization.setTargetArrow(Arrow.WHITE_DELTA);
201     generalization.setLineType(LineType.LINE_2);
202     generalization.setLineColor(new Color(51, 51, 153));
203     final EdgeRealizer realization = new PolyLineEdgeRealizer();
204     realization.setReversedPathRenderingEnabled(true);
205     realization.setTargetArrow(Arrow.WHITE_DELTA);
206     realization.setLineType(LineType.DASHED_2);
207     realization.setLineColor(new Color(51,51,153));
208 
209     final Object[] items = {
210         association,
211         generalization,
212         realization
213     };
214 
215     final JComboBox box = new JComboBox(items);
216     box.setRenderer(new EdgeRealizerCellRenderer());
217     box.setMaximumSize(new Dimension(box.getMinimumSize().width, box.getMaximumSize().height));
218     box.addItemListener(new ItemListener() {
219       public void itemStateChanged(ItemEvent ev) {
220         if (ev.getStateChange() == ItemEvent.SELECTED) {
221           EdgeRealizer r = (EdgeRealizer) box.getSelectedItem();
222           if (r != null) {
223             for(EdgeCursor ec = view.getGraph2D().selectedEdges(); ec.ok(); ec.next()) {
224               EdgeRealizer ser = view.getGraph2D().getRealizer(ec.edge());
225               ser.setLineColor(r.getLineColor());
226               ser.setTargetArrow(r.getTargetArrow());
227               ser.setLineType(r.getLineType());
228             }
229             view.getGraph2D().setDefaultEdgeRealizer(r.createCopy());
230           }
231         }
232       }
233     });
234     box.setSelectedIndex(0);
235 
236     return box;
237   }
238 
239    static class EdgeRealizerCellRenderer extends JComponent implements ListCellRenderer {
240     private Graph2D graph;
241     private EdgeRealizer er;
242 
243     public EdgeRealizerCellRenderer() {
244       graph = new Graph2D();
245       Node s = graph.createNode(0, 12, 1, 1, "");
246       Node t = graph.createNode(60, 12, 1, 1, "");
247       graph.createEdge(s, t);
248     }
249 
250     public Component getListCellRendererComponent(
251         JList list,
252         Object value,
253         int index,
254         boolean isSelected,
255         boolean cellHasFocus) {
256 
257       setPreferredSize(new Dimension(60, 24));
258 
259       er = (EdgeRealizer)value;
260       graph.setRealizer(graph.firstEdge(), er);
261       return this;
262     }
263 
264     public void paint(Graphics g) {
265       Graphics2D gfx = (Graphics2D) g;
266       er.paint(gfx);
267     }
268   }
269 
270   public static void main(String[] args) {
271     initLnF();
272     DemoBase demo = new UMLClassDiagramLayouterDemo();
273     demo.start(demo.getClass().getName());
274   }
275 }
276