1   /****************************************************************************
2    **
3    ** This file is part of yFiles-2.9. 
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-2011 by yWorks GmbH, Vor dem Kreuzberg 28, 
11   ** 72070 Tuebingen, Germany. All rights reserved.
12   **
13   ***************************************************************************/
14  package demo.view.flowchart;
15  
16  import demo.view.DemoBase;
17  import org.w3c.dom.Element;
18  import y.base.Graph;
19  import y.io.GraphMLIOHandler;
20  import y.io.graphml.GraphMLHandler;
21  import y.io.graphml.KeyScope;
22  import y.io.graphml.graph2d.Graph2DGraphMLHandler;
23  import y.io.graphml.graph2d.PostprocessorInputHandler;
24  import y.io.graphml.graph2d.PostprocessorOutputHandler;
25  import y.io.graphml.input.GraphMLParseContext;
26  import y.io.graphml.input.GraphMLParseException;
27  import y.io.graphml.input.GraphMLParser;
28  import y.io.graphml.input.InputHandlerProvider;
29  import y.io.graphml.input.QueryInputHandlersEvent;
30  import y.module.YModule;
31  import y.util.DataProviderAdapter;
32  import y.view.Graph2D;
33  import y.view.Graph2DClipboard;
34  import y.view.Graph2DUndoManager;
35  import y.view.Graph2DView;
36  import y.view.Overview;
37  
38  import javax.swing.AbstractAction;
39  import javax.swing.Action;
40  import javax.swing.BorderFactory;
41  import javax.swing.JComponent;
42  import javax.swing.JLabel;
43  import javax.swing.JMenu;
44  import javax.swing.JMenuBar;
45  import javax.swing.JMenuItem;
46  import javax.swing.JPanel;
47  import javax.swing.JSplitPane;
48  import javax.swing.JToolBar;
49  import javax.swing.KeyStroke;
50  import java.awt.BorderLayout;
51  import java.awt.Color;
52  import java.awt.Dimension;
53  import java.awt.EventQueue;
54  import java.awt.Font;
55  import java.awt.event.ActionEvent;
56  import java.awt.event.InputEvent;
57  import java.awt.event.KeyEvent;
58  import java.net.URL;
59  import java.util.Iterator;
60  import java.util.LinkedHashMap;
61  import java.util.Locale;
62  import java.util.Map;
63  
64  
65  /**
66   * A viewer and editor for flowchart diagrams. It shows how to <ul> <li>integrate and configure an adjusted view, the
67   * {@link FlowchartView}</li> <li>add a palette of flowchart symbols, the {@link FlowchartPalette}, to ease the creation
68   * of diagrams</li> <li>add the specific properties panel</li> <li>implement a {@link
69   * y.view.GenericNodeRealizer.Painter} tailored for the drawing of flowchart symbols</li> </ul>
70   */
71  public class FlowchartDemo extends DemoBase {
72  
73    private static final Map EXAMPLES_FILE_NAMES;
74  
75    static {
76      EXAMPLES_FILE_NAMES = new LinkedHashMap();
77      EXAMPLES_FILE_NAMES.put("problemsolving.graphml", "Problem Solving");
78      EXAMPLES_FILE_NAMES.put("studentRegistration.graphml", "Student Registration");
79      EXAMPLES_FILE_NAMES.put("E-commerce.graphml", "E-commerce");
80      EXAMPLES_FILE_NAMES.put("Procrastination.graphml", "The Procrastination Flowchart");
81      EXAMPLES_FILE_NAMES.put("DecisionMaking.graphml", "Decision Making");
82      EXAMPLES_FILE_NAMES.put("ComputingFactorial.graphml", "Computing the Factorial");
83      EXAMPLES_FILE_NAMES.put("FindingLargestNumber.graphml", "Finding the Largest Number");
84      EXAMPLES_FILE_NAMES.put("FindingLargestNumber2.graphml", "Finding the Largest Number 2");
85    }
86  
87    FlowchartLayoutModule layoutModule;                                                                 
88    FlowchartPalette palette;
89    Graph2DUndoManager undoManager;
90    Graph2DClipboard clipboard;
91  
92    /**
93     * Instantiates this demo. Builds the GUI.
94     */
95    public FlowchartDemo() {
96      super();
97  
98      final JComponent workBench = createWorkBench();
99      if (workBench != null) {
100       contentPane.add(workBench, BorderLayout.CENTER);
101     }
102 
103     EventQueue.invokeLater(new Runnable() {
104       public void run() {
105         loadInitialGraph();
106       }
107     });
108   }
109 
110   protected void loadInitialGraph() {
111     loadGraph("resource/graphs/problemsolving.graphml");
112   }
113 
114   /**
115    * Overwritten to register no view mode at all. {@link demo.view.flowchart.FlowchartView}, the editor component that
116    * is used to edit flowchart diagrams, registers all required view modes upon its instantiation.
117    *
118    * @see demo.view.flowchart.FlowchartView#registerViewModes()
119    */
120   protected void registerViewModes() {
121   }
122 
123   /**
124    * Creates a {@link FlowchartView}.
125    *
126    * @return a <code>FlowchartView</code>
127    */
128   protected Graph2DView createGraphView() {
129     return new FlowchartView();
130   }
131 
132   /**
133    * Initializes the Flowchart palette and the undo manager.
134    */
135   protected void initialize() {
136     palette = new FlowchartPalette(view);
137     palette.setSnapMode(true);
138 
139     undoManager = new Graph2DUndoManager(view.getGraph2D());
140     undoManager.setViewContainer(view);
141 
142     clipboard = new Graph2DClipboard(view);
143     clipboard.setCopyFactory(view.getGraph2D().getGraphCopyFactory());
144 
145     layoutModule = new FlowchartLayoutModule();                                                       
146 
147     // Register a DataProvider that returns the layout module. This dataprovider is used by           
148     // PostprocessorOutputHandler to lookup the postprocessors it should serialize.                   
149     view.getGraph2D().addDataProvider(PostprocessorOutputHandler.PROCESSORS_DPKEY,                    
150         new DataProviderAdapter() {                                                                   
151           public Object get(Object dataHolder) {                                                      
152             return layoutModule;                                                                      
153           }                                                                                           
154         });                                                                                           
155   }
156 
157   /**
158    * Adds menu items for example graphs to the default menu bar.
159    *
160    * @return the menu bar for this demo.
161    */
162   protected JMenuBar createMenuBar() {
163     JMenu examplesMenu = new JMenu("Examples");
164     for (Iterator iterator = EXAMPLES_FILE_NAMES.entrySet().iterator(); iterator.hasNext(); ) {
165       final Map.Entry entry = (Map.Entry) iterator.next();
166       examplesMenu.add(new JMenuItem(new AbstractAction((String) entry.getValue()) {
167         public void actionPerformed(ActionEvent e) {
168           loadGraph("resource/graphs/" + entry.getKey());
169         }
170       }));
171     }
172 
173     JMenuBar menuBar = super.createMenuBar();
174     menuBar.add(examplesMenu);
175     return menuBar;
176   }
177 
178   /**
179    * Adds undo/redo actions and cut/copy/paste actions to the default toolbar.
180    *
181    * @return the toolbar for this demo.
182    */
183   protected JToolBar createToolBar() {
184     JToolBar toolBar = super.createToolBar();
185 
186     toolBar.addSeparator();
187     toolBar.add(createUndoAction());
188     toolBar.add(createRedoAction());
189 
190     toolBar.addSeparator();
191     toolBar.add(createCutAction());
192     toolBar.add(createCopyAction());
193     toolBar.add(createPasteAction());
194 
195     toolBar.addSeparator();                                                                           
196     toolBar.add(createActionControl(createLayoutAction()));                                           
197     toolBar.addSeparator(TOOLBAR_SMALL_SEPARATOR);                                                    
198     toolBar.add(createActionControl(createLayoutSettingsAction()));                                   
199 
200     return toolBar;
201   }
202 
203   /**
204    * Creates a GraphMLIOHandler that has additional input and output support for GraphML postprocessors.
205    * <p/>
206    * Note that input support for PostProcessors is registered by default. It is disabled by registering a customized
207    * input handler which additionally sets this demo's layout module to the parsed module.
208    */
209   protected GraphMLIOHandler createGraphMLIOHandler() {
210     final GraphMLIOHandler ioh = new PostprocessorGraphMLIOHandler(new PostprocessorInputHandler() {
211       protected void startModule(YModule module, Graph2D graph, GraphMLParseContext context)
212           throws GraphMLParseException {
213 //        super.startModule(graph, module, context);
214 
215         if (module instanceof FlowchartLayoutModule) {                                                
216           layoutModule = (FlowchartLayoutModule) module;                                              
217         }                                                                                             
218       }
219     });
220     ioh.getGraphMLHandler().addOutputHandlerProvider(new PostprocessorOutputHandler());
221 
222     return ioh;
223   }
224 
225   /**
226    * Callback for loading a graph. Overwritten to allow an empty URL and to reset the undo queue.
227    */
228   protected void loadGraph(URL resource) {
229     final Graph2D graph = view.getGraph2D();
230     graph.firePreEvent();
231     super.loadGraph(resource);
232     graph.firePostEvent();
233     undoManager.resetQueue();
234 
235     EventQueue.invokeLater(new Runnable() {                                                           
236       public void run() {                                                                             
237         layoutModule.start(graph);                                                                    
238       }                                                                                               
239     });                                                                                               
240   }
241 
242   /**
243    * Creates four panels which form the workbench of this demo: a Flowchart view, an overview, a palette and a property
244    * window.
245    *
246    * @return a JComponent containing the view, overview, palette and property panel.
247    */
248   JComponent createWorkBench() {
249     JPanel titledOverview = createTitledPanel(createOverview(), "Overview");
250     JPanel titledPalette = createTitledPanel(this.palette, "Palette");
251     JSplitPane overviewPaletteSplit = new JSplitPane(JSplitPane.VERTICAL_SPLIT, true, titledOverview, titledPalette);
252     return new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, overviewPaletteSplit, view);
253   }
254 
255   /**
256    * Callback method that creates and configures the layout action.
257    *
258    * @return the layout action.
259    */
260   protected Action createLayoutAction() {                                                             
261     final Action action = new AbstractAction("Layout") {                                              
262       public void actionPerformed(ActionEvent e) {                                                    
263         layoutModule.start(view.getGraph2D());                                                        
264       }                                                                                               
265     };                                                                                                
266     action.putValue(Action.SHORT_DESCRIPTION, "Run the flowchart layout");                            
267     action.putValue(Action.SMALL_ICON, SHARED_LAYOUT_ICON);                                           
268 
269     return action;                                                                                    
270   }                                                                                                   
271 
272   /**
273    * Callback method that creates and configures the layout settings action.
274    *
275    * @return the layout settings action.
276    */
277   protected Action createLayoutSettingsAction() {                                                     
278     final Action action = new AbstractAction("Settings...") {                                         
279       public void actionPerformed(ActionEvent e) {                                                    
280         OptionSupport.showDialog(layoutModule, view.getGraph2D(), false, view.getFrame());            
281       }                                                                                               
282     };                                                                                                
283     action.putValue(Action.SHORT_DESCRIPTION, "Configure and run the flowchart layout");              
284     action.putValue(Action.SMALL_ICON, getIconResource("resource/properties.png"));                   
285 
286     return action;                                                                                    
287   }                                                                                                   
288 
289   /**
290    * Callback method that creates and configures the undo action.
291    *
292    * @return the undo action.
293    */
294   protected Action createUndoAction() {
295     Action undoAction = undoManager.getUndoAction();
296     undoAction.putValue(Action.SMALL_ICON, getIconResource("resource/undo.png"));
297     undoAction.putValue(Action.SHORT_DESCRIPTION, "Undo");
298 
299     return undoAction;
300   }
301 
302   /**
303    * Callback method that creates and configures the redo action.
304    *
305    * @return the redo action.
306    */
307   protected Action createRedoAction() {
308     Action redoAction = undoManager.getRedoAction();
309     redoAction.putValue(Action.SMALL_ICON, getIconResource("resource/redo.png"));
310     redoAction.putValue(Action.SHORT_DESCRIPTION, "Redo");
311     return redoAction;
312   }
313 
314   /**
315    * Callback method that creates and configures the cut action.
316    *
317    * @return the cut action.
318    */
319   protected Action createCutAction() {
320     Action cutAction = clipboard.getCutAction();
321     cutAction.putValue(Action.SMALL_ICON, getIconResource("resource/cut.png"));
322     cutAction.putValue(Action.SHORT_DESCRIPTION, "Cut");
323 
324     view.getCanvasComponent().getActionMap().put("CUT", cutAction);
325     view.getCanvasComponent().getInputMap().put(
326         KeyStroke.getKeyStroke(KeyEvent.VK_X, InputEvent.CTRL_MASK), "CUT");
327 
328     return cutAction;
329   }
330 
331   /**
332    * Callback method that creates and configures the copy action.
333    *
334    * @return the copy action.
335    */
336   protected Action createCopyAction() {
337     Action copyAction = clipboard.getCopyAction();
338     copyAction.putValue(Action.SMALL_ICON, getIconResource("resource/copy.png"));
339     copyAction.putValue(Action.SHORT_DESCRIPTION, "Copy");
340 
341     view.getCanvasComponent().getActionMap().put("COPY", copyAction);
342     view.getCanvasComponent().getInputMap().put(
343         KeyStroke.getKeyStroke(KeyEvent.VK_C, InputEvent.CTRL_MASK), "COPY");
344 
345     return copyAction;
346   }
347 
348   /**
349    * Callback method that creates and configures the paste action.
350    *
351    * @return the paste action.
352    */
353   protected Action createPasteAction() {
354     Action pasteAction = clipboard.getPasteAction();
355     pasteAction.putValue(Action.SMALL_ICON, getIconResource("resource/paste.png"));
356     pasteAction.putValue(Action.SHORT_DESCRIPTION, "Paste");
357 
358     view.getCanvasComponent().getActionMap().put("PASTE", pasteAction);
359     view.getCanvasComponent().getInputMap().put(
360         KeyStroke.getKeyStroke(KeyEvent.VK_V, InputEvent.CTRL_MASK), "PASTE");
361 
362     return pasteAction;
363   }
364 
365   /**
366    * Creates a pre-configured {@link y.view.Overview}.
367    *
368    * @return the pre-configured overview.
369    */
370   protected Overview createOverview() {
371     final Overview overview = new Overview(view);
372     //blurs the part of the graph which can currently not be seen
373     overview.putClientProperty("Overview.PaintStyle", "Funky");
374     //allows zooming from within the overview
375     overview.putClientProperty("Overview.AllowZooming", Boolean.TRUE);
376     //provides functionality for navigation via keyboard (zoom in (+), zoom out (-), navigation with arrow keys)
377     overview.putClientProperty("Overview.AllowKeyboardNavigation", Boolean.TRUE);
378     //determines how to differ between the part of the graph that can currently be seen, and the rest
379     overview.putClientProperty("Overview.Inverse", Boolean.TRUE);
380 
381     overview.setPreferredSize(new Dimension((int) (0.2 * (double) contentPane.getWidth()), 200));
382     return overview;
383   }
384 
385   /**
386    * Creates a panel which contains the specified component and a title on top of it.
387    */
388   protected JPanel createTitledPanel(JComponent content, String title) {
389     JLabel label = new JLabel(title);
390     label.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
391     label.setBackground(new Color(231, 219, 182));
392     label.setOpaque(true);
393     label.setForeground(Color.DARK_GRAY);
394     label.setFont(label.getFont().deriveFont(Font.BOLD));
395     label.setFont(label.getFont().deriveFont(13.0f));
396 
397     JPanel panel = new JPanel();
398     panel.setLayout(new BorderLayout());
399     panel.add(label, BorderLayout.NORTH);
400     panel.add(content, BorderLayout.CENTER);
401     return panel;
402   }
403 
404   public static void main(String[] args) {
405     EventQueue.invokeLater(new Runnable() {
406       public void run() {
407         Locale.setDefault(Locale.ENGLISH);
408         initLnF();
409         final FlowchartDemo demo = new FlowchartDemo();
410         demo.start("Flowchart Editor Demo");
411       }
412     });
413   }
414 
415   /**
416    * This GraphMLIOHandler registers the given {@link InputHandlerProvider} for post-processor data before the default
417    * {@link PostprocessorInputHandler} is registered. This disables the default handler.
418    */
419   static class PostprocessorGraphMLIOHandler extends GraphMLIOHandler {
420     private final PostprocessorInputHandler postprocessorInputHandler;
421 
422     PostprocessorGraphMLIOHandler(PostprocessorInputHandler postprocessorInputHandler) {
423       this.postprocessorInputHandler = postprocessorInputHandler;
424     }
425 
426     protected Graph2DGraphMLHandler createGraphMLHandler() {
427       return new Graph2DGraphMLHandler() {
428 
429         protected void configureInputHandlers(Graph graph, GraphMLParser parser) {
430           if (graph instanceof Graph2D) {
431             parser.addInputHandlerProvider(new InputHandlerProvider() {
432 
433               public void onQueryInputHandler(QueryInputHandlersEvent event) throws GraphMLParseException {
434                 final Element keyDefinition = event.getKeyDefinition();
435                 if (GraphMLHandler.matchesScope(keyDefinition, KeyScope.GRAPH)
436                     && postprocessorInputHandler.acceptKey(keyDefinition.getAttributes())) {
437                   event.addInputHandler(postprocessorInputHandler);
438                 }
439               }
440             });
441 
442           }
443           super.configureInputHandlers(graph, parser);
444         }
445       };
446 
447     }
448   }
449 
450 }