1   /****************************************************************************
2    * This demo file is part of yFiles for Java 2.14.
3    * Copyright (c) 2000-2017 by yWorks GmbH, Vor dem Kreuzberg 28,
4    * 72070 Tuebingen, Germany. All rights reserved.
5    * 
6    * yFiles demo files exhibit yFiles for Java functionalities. Any redistribution
7    * of demo files in source code or binary form, with or without
8    * modification, is not permitted.
9    * 
10   * Owners of a valid software license for a yFiles for Java version that this
11   * demo is shipped with are allowed to use the demo source code as basis
12   * for their own yFiles for Java powered applications. Use of such programs is
13   * governed by the rights and conditions as set out in the yFiles for Java
14   * license agreement.
15   * 
16   * THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY EXPRESS OR IMPLIED
17   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
18   * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
19   * NO EVENT SHALL yWorks BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
21   * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
22   * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
23   * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
24   * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25   * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26   *
27   ***************************************************************************/
28  package demo.layout.organic;
29  
30  import demo.view.DemoBase;
31  import demo.view.DemoDefaults;
32  
33  import y.base.Edge;
34  import y.base.EdgeCursor;
35  import y.base.EdgeMap;
36  import y.layout.organic.OrganicLayouter;
37  import y.module.GRIPModule;
38  import demo.layout.module.OrganicEdgeRouterModule;
39  import demo.layout.module.OrganicLayoutModule;
40  import demo.layout.module.SmartOrganicLayoutModule;
41  import y.module.YModule;
42  import y.view.EdgeRealizer;
43  import y.view.EditMode;
44  import y.view.Graph2D;
45  import y.view.NodeRealizer;
46  import y.view.PopupMode;
47  import y.view.YLabel;
48  
49  import javax.swing.AbstractAction;
50  import javax.swing.Action;
51  import javax.swing.ButtonGroup;
52  import javax.swing.JMenu;
53  import javax.swing.JMenuBar;
54  import javax.swing.JPopupMenu;
55  import javax.swing.JRadioButtonMenuItem;
56  import javax.swing.JToolBar;
57  import java.awt.event.ActionEvent;
58  import java.awt.event.ActionListener;
59  import java.awt.EventQueue;
60  import java.util.Locale;
61  
62  /**
63   * Demonstrates different organic layout algorithms and 
64   * how to specify individual preferred edge lengths 
65   * for OrganicLayouter.  
66   * <br>
67   * In this demo the edge lengths can be specified by right clicking 
68   * on an edge or applying the current node distances using the button from the 
69   * toolbar.
70   * <br>
71   * Choose the item "Edit Preferred Edge Length" from the context menu to open up 
72   * a label editor that allows for entering a value for the edge length in pixels.
73   * Note that the entered value must be numeric. Otherwise 
74   * a default length will be chosen.
75   *
76   * @see <a href="http://docs.yworks.com/yfiles/doc/api/index.html#/dguide/smart_organic_layouter" target="_blank">Section Organic Layout Style</a> in the yFiles for Java Developer's Guide
77   * @see <a href="http://docs.yworks.com/yfiles/doc/api/index.html#/dguide/organic_edge_router" target="_blank">Section Organic Edge Routing</a> in the yFiles for Java Developer's Guide
78   */
79  public class OrganicLayouterDemo extends DemoBase {
80    EdgeMap preferredEdgeLengthMap;
81    YModule module;
82  
83    public OrganicLayouterDemo() {
84      preferredEdgeLengthMap = view.getGraph2D().createEdgeMap();
85      view.getGraph2D().addDataProvider(OrganicLayouter.PREFERRED_EDGE_LENGTH_DATA, preferredEdgeLengthMap);
86      module = new SmartOrganicLayoutModule();
87      loadGraph("resource/organic.graphml");
88      DemoDefaults.applyRealizerDefaults(view.getGraph2D(), true, true);
89    }
90  
91    protected void registerViewModes() {
92      EditMode editMode = new EditMode();
93      view.addViewMode(editMode);
94  
95      editMode.setPopupMode(new PopupMode() {
96        public JPopupMenu getEdgePopup(Edge e) {
97          JPopupMenu pm = new JPopupMenu();
98          pm.add(new EditLabel(e));
99          return pm;
100       }
101     });
102   }
103 
104   /**
105    * Returns ViewActionDemo toolbar plus actions to trigger some layout algorithms 
106    */
107   protected JToolBar createToolBar() {
108     JToolBar bar = super.createToolBar();
109 
110     bar.addSeparator();
111     bar.add(createActionControl(new LayoutAction()));
112     bar.add(createActionControl(new OptionAction()));
113     bar.addSeparator();
114     bar.add(new AssignLengthsAction());
115     return bar;
116   }
117 
118   protected JMenuBar createMenuBar() {
119     JMenuBar mb = super.createMenuBar();
120     JMenu layoutMenu = new JMenu("Style");
121     ButtonGroup bg = new ButtonGroup();
122     ActionListener listener = new ActionListener() {
123       public void actionPerformed(ActionEvent ae) {
124         module = new OrganicLayoutModule();
125       }
126     };
127     JRadioButtonMenuItem item = new JRadioButtonMenuItem("Classic");
128     item.addActionListener(listener);
129     bg.add(item);
130     layoutMenu.add(item);
131     listener = new ActionListener() {
132       public void actionPerformed(ActionEvent ae) {
133         module = new SmartOrganicLayoutModule();
134       }
135     };
136     item = new JRadioButtonMenuItem("Smart");
137     item.addActionListener(listener);
138     item.setSelected(true);
139     bg.add(item);
140     layoutMenu.add(item);
141 
142     listener = new ActionListener() {
143       public void actionPerformed(ActionEvent ae) {
144         module = new GRIPModule();
145       }
146     };
147     item = new JRadioButtonMenuItem("GRIP");
148     item.addActionListener(listener);
149     bg.add(item);
150     layoutMenu.add(item);
151     listener = new ActionListener() {
152       public void actionPerformed(ActionEvent ae) {
153         module = new OrganicEdgeRouterModule();
154       }
155     };
156     item = new JRadioButtonMenuItem("EdgeRouting");
157     item.addActionListener(listener);
158     bg.add(item);
159     layoutMenu.add(item);
160     mb.add(layoutMenu);
161     return mb;
162   }
163 
164   /**
165    * Displays the layout options for organic layouter
166    */
167   class OptionAction extends AbstractAction {
168     OptionAction() {
169       super("Settings...", getIconResource("resource/properties.png"));
170     }
171 
172     public void actionPerformed(ActionEvent e) {
173       OptionSupport.showDialog(module, view.getGraph2D(), false, view.getFrame());
174     }
175   }
176 
177   /**
178    *  Launches the OrganicLayouter.
179    */
180   class LayoutAction extends AbstractAction {
181     LayoutAction() {
182       super("Layout", SHARED_LAYOUT_ICON);
183     }
184 
185     public void actionPerformed(ActionEvent e) {
186       //update preferredEdgeLengthData before launching the module
187       Graph2D graph = view.getGraph2D();
188       for (EdgeCursor ec = graph.edges(); ec.ok(); ec.next()) {
189         Edge edge = ec.edge();
190         String eLabel = graph.getLabelText(edge);
191         preferredEdgeLengthMap.set(edge, null);
192         try {
193           preferredEdgeLengthMap.setDouble(edge, Double.parseDouble(eLabel));
194         }
195         catch (Exception ex) {
196         }
197       }
198 
199       //start the module
200       module.start(view.getGraph2D());
201     }
202   }
203 
204   /**
205    * Action that opens a text editor for the label of an edge
206    * <p>
207    * The inlined label editor allows to enter a single line of
208    * label text for an edge. To terminate the label editor 
209    * press "Enter".
210    */
211   class EditLabel extends AbstractAction {
212     Edge e;
213 
214     EditLabel(Edge e) {
215       super("Edit Preferred Length");
216       this.e = e;
217     }
218 
219     public void actionPerformed(ActionEvent ev) {
220 
221       final EdgeRealizer r = view.getGraph2D().getRealizer(e);
222       final YLabel label = r.getLabel();
223 
224       view.openLabelEditor(label,
225           label.getBox().getX(),
226           label.getBox().getY(),
227           null, true);
228     }
229   }
230 
231   class AssignLengthsAction extends AbstractAction {
232     AssignLengthsAction() {
233       super("Assign Preferred Length");
234       putValue(Action.SHORT_DESCRIPTION, "Set the preferred length of each edge to its current geometric length");
235     }
236 
237     public void actionPerformed(ActionEvent e) {
238       Graph2D g = view.getGraph2D();
239       g.firePreEvent();
240       g.backupRealizers(g.edges());
241       try {
242         for (EdgeCursor ec = g.edges(); ec.ok(); ec.next()) {
243           NodeRealizer snr = g.getRealizer(ec.edge().source());
244           NodeRealizer tnr = g.getRealizer(ec.edge().target());
245           double deltaX = snr.getCenterX() - tnr.getCenterX();
246           double deltaY = snr.getCenterY() - tnr.getCenterY();
247           double dist = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
248           EdgeRealizer er = g.getRealizer(ec.edge());
249           er.getLabel().setText(Integer.toString((int) dist));
250         }
251         g.updateViews();
252       } finally {
253         g.firePostEvent();
254       }
255     }
256   }
257 
258   public static void main(String[] args) {
259     EventQueue.invokeLater(new Runnable() {
260       public void run() {
261         Locale.setDefault(Locale.ENGLISH);
262         initLnF();
263         new OrganicLayouterDemo().start("Organic Layouter Demo");
264       }
265     });
266   }
267 }
268 
269 
270       
271