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.view.flowchart;
29  
30  import demo.view.DemoBase;
31  import org.w3c.dom.Element;
32  import y.base.Graph;
33  import y.io.GraphMLIOHandler;
34  import y.io.graphml.GraphMLHandler;
35  import y.io.graphml.KeyScope;
36  import y.io.graphml.graph2d.Graph2DGraphMLHandler;
37  import y.io.graphml.graph2d.PostprocessorInputHandler;
38  import y.io.graphml.graph2d.PostprocessorOutputHandler;
39  import y.io.graphml.input.GraphMLParseContext;
40  import y.io.graphml.input.GraphMLParseException;
41  import y.io.graphml.input.GraphMLParser;
42  import y.io.graphml.input.InputHandlerProvider;
43  import y.io.graphml.input.QueryInputHandlersEvent;
44  import y.module.YModule;
45  import y.util.DataProviderAdapter;
46  import y.view.Graph2D;
47  import y.view.Graph2DView;
48  import y.view.Overview;
49  
50  import javax.swing.AbstractAction;
51  import javax.swing.Action;
52  import javax.swing.BorderFactory;
53  import javax.swing.JComponent;
54  import javax.swing.JLabel;
55  import javax.swing.JMenu;
56  import javax.swing.JMenuBar;
57  import javax.swing.JMenuItem;
58  import javax.swing.JPanel;
59  import javax.swing.JSplitPane;
60  import javax.swing.JToolBar;
61  import java.awt.BorderLayout;
62  import java.awt.Color;
63  import java.awt.Dimension;
64  import java.awt.EventQueue;
65  import java.awt.Font;
66  import java.awt.event.ActionEvent;
67  import java.util.Iterator;
68  import java.util.LinkedHashMap;
69  import java.util.Locale;
70  import java.util.Map;
71  
72  
73  /**
74   * A viewer and editor for flowchart diagrams. It shows how to <ul> <li>integrate and configure an adjusted view, the
75   * {@link FlowchartView}</li> <li>add a palette of flowchart symbols, the {@link FlowchartPalette}, to ease the creation
76   * of diagrams</li> <li>add the specific properties panel</li> <li>implement a {@link
77   * y.view.GenericNodeRealizer.Painter} tailored for the drawing of flowchart symbols</li> </ul>
78   */
79  public class FlowchartDemo extends DemoBase {
80  
81    private static final Map EXAMPLES_FILE_NAMES;
82  
83    static {
84      EXAMPLES_FILE_NAMES = new LinkedHashMap();
85      EXAMPLES_FILE_NAMES.put("problemsolving.graphml", "Problem Solving");
86      EXAMPLES_FILE_NAMES.put("studentRegistration.graphml", "Student Registration");
87      EXAMPLES_FILE_NAMES.put("E-commerce.graphml", "E-commerce");
88      EXAMPLES_FILE_NAMES.put("ComputingFactorial.graphml", "Computing the Factorial");
89      EXAMPLES_FILE_NAMES.put("FindingLargestNumber.graphml", "Finding the Largest Number");
90      EXAMPLES_FILE_NAMES.put("FindingLargestNumber2.graphml", "Finding the Largest Number 2");
91      EXAMPLES_FILE_NAMES.put("flowchart.graphml", "Finding a Flow Chart Diagram");
92    }
93  
94    FlowchartLayoutModule layoutModule;                                                                 
95    FlowchartPalette palette;
96  
97    /**
98     * Instantiates this demo. Builds the GUI.
99     */
100   public FlowchartDemo() {
101     super();
102 
103     final JComponent workBench = createWorkBench();
104     if (workBench != null) {
105       contentPane.add(workBench, BorderLayout.CENTER);
106     }
107 
108     EventQueue.invokeLater(new Runnable() {
109       public void run() {
110         loadInitialGraph();
111       }
112     });
113   }
114 
115   protected void loadInitialGraph() {
116     loadGraph("resource/graphs/problemsolving.graphml");
117   }
118 
119   /**
120    * Overwritten to register no view mode at all. {@link demo.view.flowchart.FlowchartView}, the editor component that
121    * is used to edit flowchart diagrams, registers all required view modes upon its instantiation.
122    *
123    * @see demo.view.flowchart.FlowchartView#registerViewModes()
124    */
125   protected void registerViewModes() {
126   }
127 
128   /**
129    * Creates a {@link FlowchartView}.
130    *
131    * @return a <code>FlowchartView</code>
132    */
133   protected Graph2DView createGraphView() {
134     return new FlowchartView();
135   }
136 
137   /**
138    * Initializes the Flowchart palette.
139    */
140   protected void initialize() {
141     palette = new FlowchartPalette(view);
142     palette.setSnapMode(true);
143 
144     layoutModule = new FlowchartLayoutModule();                                                       
145 
146     // Register a DataProvider that returns the layout module. This dataprovider is used by           
147     // PostprocessorOutputHandler to lookup the postprocessors it should serialize.                   
148     view.getGraph2D().addDataProvider(PostprocessorOutputHandler.PROCESSORS_DPKEY,                    
149         new DataProviderAdapter() {                                                                   
150           public Object get(Object dataHolder) {                                                      
151             return layoutModule;                                                                      
152           }                                                                                           
153         });                                                                                           
154   }
155 
156   /**
157    * Adds menu items for example graphs to the default menu bar.
158    *
159    * @return the menu bar for this demo.
160    */
161   protected JMenuBar createMenuBar() {
162     JMenu examplesMenu = new JMenu("Examples");
163     for (Iterator iterator = EXAMPLES_FILE_NAMES.entrySet().iterator(); iterator.hasNext(); ) {
164       final Map.Entry entry = (Map.Entry) iterator.next();
165       examplesMenu.add(new JMenuItem(new AbstractAction((String) entry.getValue()) {
166         public void actionPerformed(ActionEvent e) {
167           loadGraph("resource/graphs/" + entry.getKey());
168         }
169       }));
170     }
171 
172     JMenuBar menuBar = super.createMenuBar();
173     menuBar.add(examplesMenu);
174     return menuBar;
175   }
176 
177   /**
178    * Adds layout actions to the default toolbar.
179    *
180    * @return the toolbar for this demo.
181    */
182   protected JToolBar createToolBar() {
183     JToolBar toolBar = super.createToolBar();
184 
185     toolBar.addSeparator();                                                                           
186     toolBar.add(createActionControl(createLayoutAction()));                                           
187     toolBar.addSeparator(TOOLBAR_SMALL_SEPARATOR);                                                    
188     toolBar.add(createActionControl(createLayoutSettingsAction()));                                   
189 
190     return toolBar;
191   }
192 
193   /**
194    * Creates a GraphMLIOHandler that has additional input and output support for GraphML postprocessors.
195    * <p/>
196    * Note that input support for PostProcessors is registered by default. It is disabled by registering a customized
197    * input handler which additionally sets this demo's layout module to the parsed module.
198    */
199   protected GraphMLIOHandler createGraphMLIOHandler() {
200     final GraphMLIOHandler ioh = new PostprocessorGraphMLIOHandler(new PostprocessorInputHandler() {
201       protected void startModule(YModule module, Graph2D graph, GraphMLParseContext context)
202           throws GraphMLParseException {
203 //        super.startModule(graph, module, context);
204 
205         if (module instanceof FlowchartLayoutModule) {                                                
206           layoutModule = (FlowchartLayoutModule) module;                                              
207         }                                                                                             
208       }
209     });
210     ioh.getGraphMLHandler().addOutputHandlerProvider(new PostprocessorOutputHandler());
211 
212     return ioh;
213   }
214 
215   /**
216    * Creates four panels which form the workbench of this demo: a Flowchart view, an overview, a palette and a property
217    * window.
218    *
219    * @return a JComponent containing the view, overview, palette and property panel.
220    */
221   JComponent createWorkBench() {
222     JPanel titledOverview = createTitledPanel(createOverview(), "Overview");
223     JPanel titledPalette = createTitledPanel(this.palette, "Palette");
224     JSplitPane overviewPaletteSplit = new JSplitPane(JSplitPane.VERTICAL_SPLIT, true, titledOverview, titledPalette);
225     return new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, overviewPaletteSplit, view);
226   }
227 
228   /**
229    * Callback method that creates and configures the layout action.
230    *
231    * @return the layout action.
232    */
233   protected Action createLayoutAction() {                                                             
234     final Action action = new AbstractAction("Layout") {                                              
235       public void actionPerformed(ActionEvent e) {                                                    
236         layoutModule.start(view.getGraph2D());                                                        
237       }                                                                                               
238     };                                                                                                
239     action.putValue(Action.SHORT_DESCRIPTION, "Run the flowchart layout");                            
240     action.putValue(Action.SMALL_ICON, SHARED_LAYOUT_ICON);                                           
241 
242     return action;                                                                                    
243   }                                                                                                   
244 
245   /**
246    * Callback method that creates and configures the layout settings action.
247    *
248    * @return the layout settings action.
249    */
250   protected Action createLayoutSettingsAction() {                                                     
251     final Action action = new AbstractAction("Settings...") {                                         
252       public void actionPerformed(ActionEvent e) {                                                    
253         OptionSupport.showDialog(layoutModule, view.getGraph2D(), false, view.getFrame());            
254       }                                                                                               
255     };                                                                                                
256     action.putValue(Action.SHORT_DESCRIPTION, "Configure and run the flowchart layout");              
257     action.putValue(Action.SMALL_ICON, getIconResource("resource/properties.png"));                   
258 
259     return action;                                                                                    
260   }                                                                                                   
261 
262   /**
263    * Creates a pre-configured {@link y.view.Overview}.
264    *
265    * @return the pre-configured overview.
266    */
267   protected Overview createOverview() {
268     final Overview overview = new Overview(view);
269     //blurs the part of the graph which can currently not be seen
270     overview.putClientProperty("Overview.PaintStyle", "Funky");
271     //allows zooming from within the overview
272     overview.putClientProperty("Overview.AllowZooming", Boolean.TRUE);
273     //provides functionality for navigation via keyboard (zoom in (+), zoom out (-), navigation with arrow keys)
274     overview.putClientProperty("Overview.AllowKeyboardNavigation", Boolean.TRUE);
275     //determines how to differ between the part of the graph that can currently be seen, and the rest
276     overview.putClientProperty("Overview.Inverse", Boolean.TRUE);
277 
278     overview.setPreferredSize(new Dimension((int) (0.2 * (double) contentPane.getWidth()), 200));
279     return overview;
280   }
281 
282   /**
283    * Creates a panel which contains the specified component and a title on top of it.
284    */
285   protected JPanel createTitledPanel(JComponent content, String title) {
286     JLabel label = new JLabel(title);
287     label.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
288     label.setBackground(new Color(231, 219, 182));
289     label.setOpaque(true);
290     label.setForeground(Color.DARK_GRAY);
291     label.setFont(label.getFont().deriveFont(Font.BOLD));
292     label.setFont(label.getFont().deriveFont(13.0f));
293 
294     JPanel panel = new JPanel();
295     panel.setLayout(new BorderLayout());
296     panel.add(label, BorderLayout.NORTH);
297     panel.add(content, BorderLayout.CENTER);
298     return panel;
299   }
300 
301   public static void main(String[] args) {
302     EventQueue.invokeLater(new Runnable() {
303       public void run() {
304         Locale.setDefault(Locale.ENGLISH);
305         initLnF();
306         final FlowchartDemo demo = new FlowchartDemo();
307         demo.start("Flowchart Editor Demo");
308       }
309     });
310   }
311 
312   /**
313    * This GraphMLIOHandler registers the given {@link InputHandlerProvider} for post-processor data before the default
314    * {@link PostprocessorInputHandler} is registered. This disables the default handler.
315    */
316   static class PostprocessorGraphMLIOHandler extends GraphMLIOHandler {
317     private final PostprocessorInputHandler postprocessorInputHandler;
318 
319     PostprocessorGraphMLIOHandler(PostprocessorInputHandler postprocessorInputHandler) {
320       this.postprocessorInputHandler = postprocessorInputHandler;
321     }
322 
323     protected Graph2DGraphMLHandler createGraphMLHandler() {
324       return new Graph2DGraphMLHandler() {
325 
326         protected void configureInputHandlers(Graph graph, GraphMLParser parser) {
327           if (graph instanceof Graph2D) {
328             parser.addInputHandlerProvider(new InputHandlerProvider() {
329 
330               public void onQueryInputHandler(QueryInputHandlersEvent event) throws GraphMLParseException {
331                 final Element keyDefinition = event.getKeyDefinition();
332                 if (GraphMLHandler.matchesScope(keyDefinition, KeyScope.GRAPH)
333                     && postprocessorInputHandler.acceptKey(keyDefinition.getAttributes())) {
334                   event.addInputHandler(postprocessorInputHandler);
335                 }
336               }
337             });
338 
339           }
340           super.configureInputHandlers(graph, parser);
341         }
342       };
343 
344     }
345   }
346 
347 }
348