1   
28  package demo.view.mindmap;
29  
30  import demo.view.DemoBase;
31  import y.algo.GraphConnectivity;
32  import y.algo.Trees;
33  import y.base.Command;
34  import y.base.Edge;
35  import y.base.EdgeCursor;
36  import y.base.EdgeMap;
37  import y.base.Graph;
38  import y.base.Node;
39  import y.base.NodeMap;
40  import y.base.WrongGraphStructure;
41  import y.io.GraphMLIOHandler;
42  import y.io.IOHandler;
43  import y.io.graphml.KeyScope;
44  import y.io.graphml.KeyType;
45  import y.view.ArcEdgeRealizer;
46  import y.view.Arrow;
47  import y.view.BezierPathCalculator;
48  import y.view.DefaultBackgroundRenderer;
49  import y.view.EdgeLabel;
50  import y.view.EditMode;
51  import y.view.GenericEdgeRealizer;
52  import y.view.GenericNodeRealizer;
53  import y.view.Graph2D;
54  import y.view.Graph2DView;
55  import y.view.Graph2DViewMouseWheelZoomListener;
56  import y.view.LineType;
57  import y.view.ShapeNodeRealizer;
58  import y.view.SmartEdgeLabelModel;
59  import y.view.YRenderingHints;
60  
61  import javax.swing.AbstractAction;
62  import javax.swing.Action;
63  import javax.swing.JFileChooser;
64  import javax.swing.JMenu;
65  import javax.swing.JMenuBar;
66  import javax.swing.JMenuItem;
67  import javax.swing.filechooser.FileFilter;
68  import java.awt.Color;
69  import java.awt.EventQueue;
70  import java.awt.Graphics2D;
71  import java.awt.Rectangle;
72  import java.awt.event.ActionEvent;
73  import java.awt.event.MouseWheelListener;
74  import java.io.File;
75  import java.io.IOException;
76  import java.net.MalformedURLException;
77  import java.net.URL;
78  import java.util.ArrayList;
79  import java.util.Iterator;
80  import java.util.Locale;
81  import java.util.Map;
82  
83  
150 public class MindMapDemo extends DemoBase {
151   public static void main(String[] args) {
152     EventQueue.invokeLater(new Runnable() {
153       public void run() {
154         Locale.setDefault(Locale.ENGLISH);
155         initLnF();
156         (new MindMapDemo("resource/mindmaphelp.html")).start("MindMapDemo");
157       }
158     });
159   }
160 
161   public MindMapDemo() {
162     this(null);
163   }
164 
165   public MindMapDemo(final String helpFilePath) {
166     new CollapseButton.Handler(view);
167 
168     loadFile(getResource("resource/hobbies.graphml"));
169 
170     KeyboardHandling.setKeyActions(view);
171     configureRendering();
172         final Graph2DViewMouseWheelZoomListener zl = new Graph2DViewMouseWheelZoomListener();
174     zl.setMaximumZoom(2.5);
175     zl.setMinimumZoom(0.08);
176     zl.setCenterZooming(false);
177         MouseWheelListener[] mouseWheelListeners = view.getCanvasComponent().getMouseWheelListeners();
179     view.getCanvasComponent().removeMouseWheelListener(mouseWheelListeners[0]);
180     zl.addToCanvas(view);
181 
182     addHelpPane(helpFilePath);
183 
184         configureBackgroundRenderer();
186     getUndoManager().resetQueue();
187   }
188 
189   protected Graph2DView createGraphView() {
190     final Graph2DView view = new Graph2DView(ViewModel.instance.graph);
191     view.setFitContentOnResize(true);
192     return view;
193   }
194 
195   
198   private void configureBackgroundRenderer() {
199     view.setBackgroundRenderer(new DefaultBackgroundRenderer(view) {
200       public void paint( Graphics2D gfx, int x, int y, int w, int h ) {
201         super.paint(gfx, x, y, w, h);
202         final int SQUARE_SIZE = 25;
203         final int HOLE_SIZE = 30;
204         final Rectangle bounds = view.getBounds();
205 
206         gfx.setColor(new Color(174, 174, 174));
207 
208         undoWorldTransform(gfx);
209         final int width = 5;
210         gfx.fillRect(7 * SQUARE_SIZE - (width / 2), 0, width, bounds.y + bounds.height);
211         for (int i = 0; i < bounds.height; i += SQUARE_SIZE) {
212           gfx.drawLine(0, i, bounds.width + bounds.x, i);
213         }
214         for (int i = 0; i < bounds.width; i += SQUARE_SIZE) {
215           gfx.drawLine(i, 0, i, bounds.y + bounds.height);
216         }
217         double height = bounds.height / 8;
218 
219         gfx.setColor(Color.WHITE);
220 
221         gfx.fillOval((int) (1.5 * SQUARE_SIZE), (int) height, HOLE_SIZE, HOLE_SIZE);
222         gfx.fillOval((int) (1.5 * SQUARE_SIZE), (int) (height * 3), HOLE_SIZE, HOLE_SIZE);
223         gfx.fillOval((int) (1.5 * SQUARE_SIZE), (int) (height * 5), HOLE_SIZE, HOLE_SIZE);
224         gfx.fillOval((int) (1.5 * SQUARE_SIZE), (int) (height * 7), HOLE_SIZE, HOLE_SIZE);
225 
226         gfx.setColor(new Color(167, 167, 167));
227         gfx.drawOval((int) (1.5 * SQUARE_SIZE), (int) height, HOLE_SIZE, HOLE_SIZE);
228         gfx.drawOval((int) (1.5 * SQUARE_SIZE), (int) (height * 3), HOLE_SIZE, HOLE_SIZE);
229         gfx.drawOval((int) (1.5 * SQUARE_SIZE), (int) (height * 5), HOLE_SIZE, HOLE_SIZE);
230         gfx.drawOval((int) (1.5 * SQUARE_SIZE), (int) (height * 7), HOLE_SIZE, HOLE_SIZE);
231 
232         redoWorldTransform(gfx);
233       }
234     });
235   }
236 
237   
248   private void configureRendering() {
249     view.setGraph2DRenderer(new MindMapRenderer());
250 
251             view.getRenderingHints().put(
254         ShapeNodeRealizer.KEY_SLOPPY_RECT_PAINTING,
255         ShapeNodeRealizer.VALUE_SLOPPY_RECT_PAINTING_OFF);
256 
257             view.getRenderingHints().put(
260             YRenderingHints.KEY_NODE_LABEL_PAINTING,
261             YRenderingHints.VALUE_NODE_LABEL_PAINTING_OFF);
262   }
263 
264   
267   protected void configureDefaultRealizers() {
268     final GenericNodeRealizer.Factory nodeFactory = GenericNodeRealizer.getFactory();
269     final Map nodeMap = nodeFactory.createDefaultConfigurationMap();
270     nodeMap.put(GenericNodeRealizer.Painter.class, new MindMapNodePainter());
271     nodeFactory.addConfiguration("MindMapUnderline", nodeMap);
272     final GenericNodeRealizer gnr = new GenericNodeRealizer();
273     gnr.setConfiguration("MindMapUnderline");
274     gnr.setHeight(24);
275     view.getGraph2D().setDefaultNodeRealizer(gnr);
276 
277     final GenericEdgeRealizer.Factory edgeFactory = GenericEdgeRealizer.getFactory();
278     final Map edgeMap = edgeFactory.createDefaultConfigurationMap();
279     edgeMap.put(GenericEdgeRealizer.PathCalculator.class, new BezierPathCalculator());
280     edgeMap.put(GenericEdgeRealizer.Painter.class,new GradientEdgePainter());
281         edgeFactory.addConfiguration("BezierGradientEdge", edgeMap);
282     final GenericEdgeRealizer edgeRealizer = new GenericEdgeRealizer();
283     edgeRealizer.setConfiguration("BezierGradientEdge");
284     final ArcEdgeRealizer realizer = new ArcEdgeRealizer(ArcEdgeRealizer.FIXED_RATIO);
285 
286     final EdgeLabel label = realizer.getLabel();
287     final SmartEdgeLabelModel model = new SmartEdgeLabelModel();
288     final SmartEdgeLabelModel.ModelParameter modelParameter = new SmartEdgeLabelModel.ModelParameter(0, 1, 0, false,
289         SmartEdgeLabelModel.ModelParameter.LEFT, 0);
290     label.setLabelModel(model,modelParameter);
291     label.setBackgroundColor(Color.WHITE);
292     realizer.setTargetArrow(Arrow.DELTA);
293     realizer.setLineColor(MindMapUtil.CROSS_EDGE_COLOR);
294     realizer.setLineType(LineType.LINE_7);
295     view.getGraph2D().setDefaultEdgeRealizer(realizer);
296   }
297 
298   
301   protected void registerViewModes() {
302                 new HoverButton(view);
306 
307     final EditMode editMode = new MoveNodeMode(getUndoManager());
308     editMode.getMouseInputMode().setDrawableSearchingEnabled(true);
309     editMode.allowMouseInput(true);
310     editMode.allowBendCreation(false);
311     editMode.allowMovePorts(false);
312     editMode.allowNodeCreation(false);
313     editMode.allowEdgeCreation(false);
314     editMode.allowResizeNodes(false);
315     editMode.allowMoveLabels(false);
316     view.addViewMode(editMode);
317     view.addViewMode(new KeyboardHandling.LabelChangeViewMode());
318   }
319 
320   
324   protected Action createDeleteSelectionAction() {
325     return KeyboardHandling.createDeleteSelectionAction(view);
326   }
327 
328   
331   public class SaveFileAction extends AbstractAction {
332     JFileChooser chooser;
333 
334     public SaveFileAction() {
335       super("Save...");
336       putValue(Action.SHORT_DESCRIPTION, "Save...");
337     }
338 
339     public void actionPerformed(final ActionEvent e) {
340       if (chooser == null) {
341         chooser = new JFileChooser();
342         chooser.setDialogTitle("Save Mindmap...");
343                 chooser.setAcceptAllFileFilterUsed(false);
345         chooser.addChoosableFileFilter(new FileFilter() {
346           public boolean accept(File f) {
347             return f.isDirectory() || f.getName().endsWith(".mm");
348           }
349 
350           public String getDescription() {
351             return "FreeMind Mindmap (.mm)";
352           }
353         });
354         chooser.addChoosableFileFilter(new FileFilter() {
355           public boolean accept(File f) {
356             return f.isDirectory() || f.getName().endsWith(".graphml");
357           }
358 
359           public String getDescription() {
360             return "GraphML (.graphml)";
361           }
362         });
363       }
364 
365       if (chooser.showSaveDialog(contentPane) == JFileChooser.APPROVE_OPTION) {
366         final boolean useNativeFormat =
367                 chooser.getFileFilter().getDescription().startsWith("GraphML");
368 
369         String fileName = chooser.getSelectedFile().getAbsolutePath();
370                 if (useNativeFormat) {
372           final String suffix = ".graphml";
373           if (!fileName.toLowerCase().endsWith(suffix)) {
374             fileName += suffix;
375           }
376         } else {
377           final String suffix = ".mm";
378           if (!fileName.toLowerCase().endsWith(suffix)) {
379             fileName += suffix;
380           }
381         }
382 
383                 final IOHandler writer =
385                 useNativeFormat
386                 ? (IOHandler) createGraphMLIOHandler()
387                 : new FreeMindIOHandler();
388         try {
389           writer.write(view.getGraph2D(), fileName);
390         } catch (IOException ioe) {
391           ioe.printStackTrace();
392         }
393       }
394     }
395   }
396 
397   
400   public class OpenFileAction extends AbstractAction {
401     JFileChooser chooser;
402 
403     public OpenFileAction() {
404       super("Load...");
405       putValue(Action.SHORT_DESCRIPTION, "Load...");
406     }
407 
408     public void actionPerformed(final ActionEvent e) {
409       if (chooser == null) {
410         chooser = new JFileChooser();
411         chooser.setAcceptAllFileFilterUsed(false);
412         chooser.setDialogTitle("Open Mindmap...");
413         chooser.addChoosableFileFilter(new FileFilter() {
414           public boolean accept( final File f ) {
415             return f.isDirectory() ||
416                    f.getName().endsWith(".mm") ||
417                    f.getName().endsWith(".graphml");
418           }
419 
420           public String getDescription() {
421             return "Mindmap (.graphml, .mm)";
422           }
423         });
424       }
425 
426       if (chooser.showOpenDialog(contentPane) == JFileChooser.APPROVE_OPTION) {
427         try {
428           final URL url = chooser.getSelectedFile().toURI().toURL();
429           loadFile(url);
430         } catch (MalformedURLException mue) {
431           mue.printStackTrace();
432         }
433       }
434     }
435   }
436 
437   private void loadFile( final URL resource ) {
438     final ArrayList exceptions = new ArrayList();
439     final ViewModel model = ViewModel.instance;
440 
441     final Graph2D graph = view.getGraph2D();
442     graph.firePreEvent();
443 
444     graph.clear();
445 
446     final boolean useNativeFormat = resource.getFile().toLowerCase().endsWith(".graphml");
447     if (useNativeFormat) {
448       try {
449         createGraphMLIOHandler().read(graph, resource);
450       } catch (IOException ioe) {
451         exceptions.add(ioe);
452       }
453     } else {
454       try {
455         (new FreeMindIOHandler()).read(graph, resource);
456       } catch (IOException ioe) {
457         exceptions.add(ioe);
458       }
459     }
460 
461     if (exceptions.isEmpty()) {
462       try {
463         final Node root = findRoot(graph);
464         model.setRoot(root);
465 
466         if (!useNativeFormat) {
467           MindMapUtil.setRootRealizer(graph, root, graph.getLabelText(root));
468           LayoutUtil.layout(graph);
469         }
470       } catch (WrongGraphStructure wgse) {
471         exceptions.add(wgse);
472       }
473     }
474 
475     if (!exceptions.isEmpty()) {
476       graph.clear();
477       final Node root = graph.createNode();
478       MindMapUtil.setRootRealizer(graph, root, "Mind Map");
479       model.setRoot(root);
480 
481       for (Iterator it = exceptions.iterator(); it.hasNext(); ) {
482         ((Exception) it.next()).printStackTrace();
483       }
484 
485       view.fitContent();
486       view.updateView();
487     } else if (useNativeFormat) {
488       view.fitContent();
489       view.updateView();
490     }
491 
492     graph.firePostEvent();
493     model.clearHiddenCrossReferences();
494     getUndoManager().resetQueue();
495   }
496 
497   
508   private Node findRoot( final Graph2D graph ) {
509     if (GraphConnectivity.isConnected(graph)) {
510       final Graph copy = new Graph();
511       final Node[] cNodes = new Node[graph.nodeCount()];
512       for (int i = 0; i < cNodes.length; ++i) {
513         cNodes[i] = copy.createNode();
514       }
515 
516       final ViewModel model = ViewModel.instance;
517       for (EdgeCursor ec = graph.edges(); ec.ok(); ec.next()) {
518         final Edge edge = ec.edge();
519         if (!model.isCrossReference(edge)) {
520           copy.createEdge(
521                   cNodes[edge.source().index()],
522                   cNodes[edge.target().index()]);
523         }
524       }
525 
526       if (Trees.isRootedTree(copy)) {
527         final Node cRoot = Trees.getRoot(copy);
528         return graph.getNodeArray()[cRoot.index()];
529       }
530     }
531     throw new WrongGraphStructure("Graph is not a mind map");
532   }
533 
534   protected Action createLoadAction() {
535     return new OpenFileAction();
536   }
537 
538   protected Action createSaveAction() {
539     return new SaveFileAction();
540   }
541 
542   private class OpenExampleAction extends AbstractAction {
543     private String filename;
544 
545     public OpenExampleAction(String filename) {
546       super(filename);
547       this.filename = filename;
548     }
549 
550     public void actionPerformed(ActionEvent e) {
551       loadFile(getResource("resource/" + filename + ".graphml"));
552     }
553   }
554 
555   protected JMenuBar createMenuBar() {
556     JMenuBar menuBar = super.createMenuBar();
557     JMenu menu = menuBar.getMenu(0);
558     JMenuItem item = new JMenuItem(new NewFileAction());
559     menu.add(item,0);
560     menu = new JMenu("Example Mind Maps");
561     menuBar.add(menu);
562     menu.add(new OpenExampleAction("hobbies"));
563     menu.add(new OpenExampleAction("yFiles"));
564     menu.add(new OpenExampleAction("packages"));
565     return menuBar;
566   }
567 
568   private class NewFileAction extends AbstractAction {
569     public NewFileAction() {
570       super("New");
571     }
572 
573     public void actionPerformed(ActionEvent e) {
574       final Graph2D graph = view.getGraph2D();
575       graph.firePreEvent();
576       graph.backupRealizers();
577       graph.clear();
578 
579       final Node root = graph.createNode();
580       MindMapUtil.setRootRealizer(graph, root, "Mind Map");
581       final ViewModel model = ViewModel.instance;
582       getUndoManager().push(new SetRoot(model.getRoot(), root));
583       model.setRoot(root);
584       graph.firePostEvent();
585 
586       view.fitContent();
587       view.updateView();
588     }
589   }
590 
591   private static final class SetRoot implements Command {
592     private final Node oldRoot;
593     private final Node newRoot;
594 
595     SetRoot( final Node oldRoot, final Node newRoot ) {
596       this.oldRoot = oldRoot;
597       this.newRoot = newRoot;
598     }
599 
600     public void execute() {
601     }
602 
603     public void undo() {
604       ViewModel.instance.setRoot(oldRoot);
605     }
606 
607     public void redo() {
608       ViewModel.instance.setRoot(newRoot);
609     }
610   }
611 
612 
613   protected boolean isClipboardEnabled() {
614     return false;
615   }
616 
617 
618   protected GraphMLIOHandler createGraphMLIOHandler() {
619     final GraphMLIOHandler ioHandler = super.createGraphMLIOHandler();
620 
621     final NodeMap leftRightMap = LayoutUtil.getLeftRightMap(ViewModel.instance.graph);
622     ioHandler.getGraphMLHandler().addOutputDataProvider("Mindmap.LeftSide", leftRightMap, KeyScope.NODE, KeyType.BOOLEAN);
623     ioHandler.getGraphMLHandler().addInputDataAcceptor("Mindmap.LeftSide", leftRightMap, KeyScope.NODE, KeyType.BOOLEAN);
624 
625     final EdgeMap crossReferences = LayoutUtil.getCrossReferencesMap(ViewModel.instance.graph);
626     ioHandler.getGraphMLHandler().addOutputDataProvider("Mindmap.CrossReference", crossReferences, KeyScope.EDGE, KeyType.BOOLEAN);
627     ioHandler.getGraphMLHandler().addInputDataAcceptor("Mindmap.CrossReference", crossReferences, KeyScope.EDGE, KeyType.BOOLEAN);
628 
629     return ioHandler;
630   }
631 }
632