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;
15  
16  import y.base.DataProvider;
17  import y.io.GMLIOHandler;
18  import y.io.IOHandler;
19  import y.io.YGFIOHandler;
20  import y.option.OptionHandler;
21  import y.util.D;
22  import y.view.AreaZoomMode;
23  import y.view.AutoDragViewMode;
24  import y.view.EditMode;
25  import y.view.Graph2DPrinter;
26  import y.view.Graph2DView;
27  import y.view.Graph2DViewActions;
28  import y.view.Graph2DViewMouseWheelZoomListener;
29  import y.view.Selections;
30  import y.view.ViewMode;
31  
32  import javax.swing.AbstractAction;
33  import javax.swing.Action;
34  import javax.swing.ActionMap;
35  import javax.swing.ImageIcon;
36  import javax.swing.InputMap;
37  import javax.swing.JComponent;
38  import javax.swing.JFileChooser;
39  import javax.swing.JFrame;
40  import javax.swing.JMenu;
41  import javax.swing.JMenuBar;
42  import javax.swing.JPanel;
43  import javax.swing.JRootPane;
44  import javax.swing.JToolBar;
45  import javax.swing.UIManager;
46  import java.awt.BorderLayout;
47  import java.awt.Rectangle;
48  import java.awt.event.ActionEvent;
49  import java.awt.print.PageFormat;
50  import java.awt.print.PrinterJob;
51  import java.io.IOException;
52  import java.net.URL;
53  import java.util.Iterator;
54  
55  /**
56   * Abstract base class for GUI- and <code>Graph2DView</code>-based demos.
57   * Provides useful callback methods.
58   * <p/>
59   * To avoid problems with "calls to overwritten method in constructor", do not initialize the demo
60   * within the constructor of the subclass, use the method {@link #initialize()} instead.
61   */
62  public abstract class DemoBase {
63    /**
64     * Initializes to a "nice" look and feel.
65     */
66    public static void initLnF() {
67      try {
68        if ( !"com.sun.java.swing.plaf.motif.MotifLookAndFeel".equals(
69                UIManager.getSystemLookAndFeelClassName()) &&
70             !"com.sun.java.swing.plaf.gtk.GTKLookAndFeel".equals(
71                UIManager.getSystemLookAndFeelClassName()) &&
72             !UIManager.getSystemLookAndFeelClassName().equals(
73                UIManager.getLookAndFeel().getClass().getName() ) ) {
74          UIManager.setLookAndFeel( UIManager.getSystemLookAndFeelClassName() );
75        }
76      }
77      catch ( Exception e ) {
78        e.printStackTrace();
79      }
80    }
81  
82  
83    /**
84     * The view component of this demo.
85     */
86    protected Graph2DView view;
87    protected final JPanel contentPane;
88  
89    /**
90     * This constructor creates the {@link #view}
91     * and calls,
92     * {@link #createToolBar()}
93     * {@link #registerViewModes()}, {@link #registerViewActions()},
94     * and {@link #registerViewListeners()}
95     */
96    protected DemoBase() {
97      view = new Graph2DView();
98      view.setAntialiasedPainting( true );
99  
100     contentPane = new JPanel();
101     contentPane.setLayout( new BorderLayout() );
102 
103     initialize();
104 
105     registerViewModes();
106     registerViewActions();
107 
108     contentPane.add( view, BorderLayout.CENTER );
109     final JToolBar jtb = createToolBar();
110     if ( jtb != null ) {
111       contentPane.add( jtb, BorderLayout.NORTH );
112     }
113 
114     registerViewListeners();
115   }
116 
117   /**
118    * This method is called before the view modes and actions are registered and the menu and toolbar is build.
119    */
120   protected void initialize() {
121   }
122 
123   public void dispose() {
124   }
125 
126   protected void loadGraph( Class aClass, String resourceString ) {
127     String fqResourceName = aClass.getPackage().getName().replace( '.', '/' ) + '/' + resourceString;
128 
129     URL resource = aClass.getResource( resourceString );
130     if ( resource == null ) {
131       String message = "Resource \"" + fqResourceName + "\" not found in classpath";
132       D.showError( message );
133       throw new RuntimeException( message );
134     }
135 
136     try {
137       IOHandler ioh;
138       if ( resource.getFile().endsWith( "ygf" ) ) {
139         ioh = new YGFIOHandler();
140       } else {
141         ioh = new GMLIOHandler();
142       }
143       ioh.read( view.getGraph2D(), resource );
144     } catch ( Exception e ) {
145       String message = "Unexpected error while loading resource \"" + fqResourceName + "\" due to " + e.getMessage();
146       D.showError( message );
147       throw new RuntimeException( message, e );
148     }
149     view.fitContent();
150   }
151 
152   protected void loadGraph( String resourceString ) {
153     loadGraph( getClass(), resourceString );
154   }
155 
156   /**
157    * Creates an application  frame for this demo
158    * and displays it. The class name is the title of
159    * the displayed frame.
160    */
161   public final void start() {
162     start( getClass().getName() );
163   }
164 
165   /**
166    * Creates an application  frame for this demo
167    * and displays it. The given string is the title of
168    * the displayed frame.
169    */
170   public final void start( String title ) {
171     JFrame frame = new JFrame( title );
172     frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
173     this.addContentTo( frame.getRootPane() );
174     frame.pack();
175     frame.setLocationRelativeTo( null );
176     frame.setVisible( true );
177   }
178 
179   public void addContentTo( final JRootPane rootPane ) {
180     final JMenuBar jmb = createMenuBar();
181     if ( jmb != null ) {
182       rootPane.setJMenuBar( jmb );
183     }
184     rootPane.setContentPane( contentPane );
185   }
186 
187   protected void registerViewActions() {
188     //register keyboard actions
189     Graph2DViewActions actions = new Graph2DViewActions( view );
190     ActionMap amap = actions.createActionMap();
191     InputMap imap = actions.createDefaultInputMap( amap );
192     if ( !isDeletionEnabled() ) {
193       amap.remove( Graph2DViewActions.DELETE_SELECTION );
194     }
195     view.getCanvasComponent().setActionMap( amap );
196     view.getCanvasComponent().setInputMap( JComponent.WHEN_FOCUSED, imap );
197   }
198 
199   /**
200    * Adds the view modes to the view.
201    * This implementation adds a new EditMode created by {@link #createEditMode()}
202    * a new {@link AutoDragViewMode}.
203    */
204   protected void registerViewModes() {
205     //edit mode will show tool tips over nodes
206     EditMode editMode = createEditMode();
207     if ( editMode != null ) {
208       view.addViewMode( editMode );
209     }
210     view.addViewMode( new AutoDragViewMode() );
211   }
212 
213   /**
214    * Callback used by {@link #registerViewModes()} to create the default EditMode
215    *
216    * @return an instance of {@link EditMode} with showNodeTips enabled
217    */
218   protected EditMode createEditMode() {
219     EditMode editMode = new EditMode();
220     editMode.showNodeTips( true );
221     return editMode;
222   }
223 
224   /**
225    * Instantiates and registers the listeners for the view.
226    * (e.g. {@link y.view.Graph2DViewMouseWheelZoomListener}
227    */
228   protected void registerViewListeners() {
229     //Note that mouse wheel support requires J2SE 1.4 or higher.
230     view.getCanvasComponent().addMouseWheelListener( new Graph2DViewMouseWheelZoomListener() );
231   }
232 
233   /**
234    * Determines whether default actions for deletions will be added to the view
235    * and toolbar.
236    */
237   protected boolean isDeletionEnabled() {
238     return true;
239   }
240 
241   /**
242    * Creates a toolbar for this demo.
243    */
244   protected JToolBar createToolBar() {
245     JToolBar toolBar = new JToolBar();
246     if ( isDeletionEnabled() ) {
247       toolBar.add( createDeleteAllAction() );
248       toolBar.add( createDeleteSelectionAction() );
249     }
250     toolBar.add( new Zoom( 1.2 ) );
251     toolBar.add( new Zoom( 0.8 ) );
252     toolBar.add( new ZoomArea() );
253     toolBar.add( new FitContent( view ) );
254 
255     return toolBar;
256   }
257 
258   /**
259    * Create a menu bar for this demo.
260    */
261   protected JMenuBar createMenuBar() {
262     JMenuBar menuBar = new JMenuBar();
263     JMenu menu = new JMenu( "File" );
264     menu.add( createLoadAction() );
265     menu.add( createSaveAction() );
266     menu.add( new SaveSubsetAction() );
267     menu.addSeparator();
268     menu.add( new PrintAction() );
269     menu.addSeparator();
270     menu.add( new ExitAction() );
271     menuBar.add( menu );
272     return menuBar;
273   }
274 
275   protected Action createLoadAction() {
276     return new LoadAction();
277   }
278 
279   protected Action createSaveAction() {
280     return new SaveAction();
281   }
282 
283   protected Action createDeleteAllAction() {
284     return new DeleteAll( view );
285   }
286 
287   protected Action createDeleteSelectionAction() {
288     return new DeleteSelection( view );
289   }
290 
291   public JPanel getContentPane() {
292     return contentPane;
293   }
294 
295   /**
296    * Action that prints the contents of the view
297    */
298   protected class PrintAction extends AbstractAction {
299     PageFormat pageFormat;
300     OptionHandler printOptions;
301 
302     public PrintAction() {
303       super( "Print" );
304 
305       //setup option handler
306       printOptions = new OptionHandler( "Print Options" );
307       printOptions.addInt( "Poster Rows", 1 );
308       printOptions.addInt( "Poster Columns", 1 );
309       printOptions.addBool( "Add Poster Coords", false );
310       final String[] area = {"View", "Graph"};
311       printOptions.addEnum( "Clip Area", area, 1 );
312     }
313 
314     public void actionPerformed( ActionEvent e ) {
315       Graph2DPrinter gprinter = new Graph2DPrinter( view );
316 
317       //show custom print dialog and adopt values
318       if ( !printOptions.showEditor() ) {
319         return;
320       }
321       gprinter.setPosterRows( printOptions.getInt( "Poster Rows" ) );
322       gprinter.setPosterColumns( printOptions.getInt( "Poster Columns" ) );
323       gprinter.setPrintPosterCoords(
324           printOptions.getBool( "Add Poster Coords" ) );
325       if ("Graph".equals( printOptions.get( "Clip Area" ) ) ) {
326         gprinter.setClipType( Graph2DPrinter.CLIP_GRAPH );
327       } else {
328         gprinter.setClipType( Graph2DPrinter.CLIP_VIEW );
329       }
330 
331       //show default print dialogs
332       PrinterJob printJob = PrinterJob.getPrinterJob();
333       if ( pageFormat == null ) {
334         pageFormat = printJob.defaultPage();
335       }
336       PageFormat pf = printJob.pageDialog( pageFormat );
337       if ( pf == pageFormat ) {
338         return;
339       } else {
340         pageFormat = pf;
341       }
342 
343       //setup printjob.
344       //Graph2DPrinter is of type Printable
345       printJob.setPrintable( gprinter, pageFormat );
346 
347       if ( printJob.printDialog() ) {
348         try {
349           printJob.print();
350         } catch ( Exception ex ) {
351           ex.printStackTrace();
352         }
353       }
354     }
355   }
356 
357   /**
358    * Action that terminates the application
359    */
360   protected static class ExitAction extends AbstractAction {
361     public ExitAction() {
362       super( "Exit" );
363     }
364 
365     public void actionPerformed( ActionEvent e ) {
366       System.exit( 0 );
367     }
368   }
369 
370   /**
371    * Action that saves the current graph to a file in YGF format.
372    */
373   protected class SaveAction extends AbstractAction {
374     JFileChooser chooser;
375 
376     public SaveAction() {
377       super( "Save..." );
378       chooser = null;
379     }
380 
381     public void actionPerformed( ActionEvent e ) {
382       if ( chooser == null ) {
383         chooser = new JFileChooser();
384       }
385       if ( chooser.showSaveDialog( contentPane ) == JFileChooser.APPROVE_OPTION ) {
386         String name = chooser.getSelectedFile().toString();
387         if ( name.endsWith( ".gml" ) ) {
388           GMLIOHandler ioh = new GMLIOHandler();
389           try {
390             ioh.write( view.getGraph2D(), name );
391           } catch ( IOException ioe ) {
392             D.show( ioe );
393           }
394         } else {
395           if ( !name.endsWith( ".ygf" ) ) {
396             name = name + ".ygf";
397           }
398           YGFIOHandler ioh = new YGFIOHandler();
399           try {
400             ioh.write( view.getGraph2D(), name );
401           } catch ( IOException ioe ) {
402             D.show( ioe );
403           }
404         }
405       }
406     }
407   }
408 
409   /**
410    * Action that saves the current subset of the graph to a file in YGF format.
411    */
412   protected class SaveSubsetAction extends AbstractAction {
413     JFileChooser chooser;
414 
415     public SaveSubsetAction() {
416       super( "Save selection..." );
417       chooser = null;
418     }
419 
420     public void actionPerformed( ActionEvent e ) {
421       if ( chooser == null ) {
422         chooser = new JFileChooser();
423       }
424       if ( chooser.showSaveDialog( contentPane ) == JFileChooser.APPROVE_OPTION ) {
425         String name = chooser.getSelectedFile().toString();
426         if ( !name.endsWith( ".ygf" ) ) {
427           name = name + ".ygf";
428         }
429         YGFIOHandler ioh = new YGFIOHandler();
430         try {
431           DataProvider dp = Selections.createSelectionDataProvider( view.getGraph2D() );
432           ioh.writeSubset( view.getGraph2D(), dp, name );
433         } catch ( IOException ioe ) {
434           D.show( ioe );
435         }
436       }
437     }
438   }
439 
440   /**
441    * Action that loads the current graph from a file in YGF format.
442    */
443   protected class LoadAction extends AbstractAction {
444     JFileChooser chooser;
445 
446     public LoadAction() {
447       super( "Load..." );
448       chooser = null;
449     }
450 
451     public void actionPerformed( ActionEvent e ) {
452       if ( chooser == null ) {
453         chooser = new JFileChooser();
454       }
455       if ( chooser.showOpenDialog( contentPane ) == JFileChooser.APPROVE_OPTION ) {
456         String name = chooser.getSelectedFile().toString();
457         if ( name.endsWith( ".gml" ) ) {
458           GMLIOHandler ioh = new GMLIOHandler();
459           try {
460             view.getGraph2D().clear();
461             ioh.read( view.getGraph2D(), name );
462           } catch ( IOException ioe ) {
463             D.show( ioe );
464           }
465         } else {
466           if ( !name.endsWith( ".ygf" ) ) {
467             name = name + ".ygf";
468           }
469           YGFIOHandler ioh = new YGFIOHandler();
470           try {
471             view.getGraph2D().clear();
472             ioh.read( view.getGraph2D(), name );
473           } catch ( IOException ioe ) {
474             D.show( ioe );
475           }
476         }
477         //force redisplay of view contents
478         view.fitContent();
479         view.getGraph2D().updateViews();
480       }
481     }
482   }
483 
484   /**
485    * Action that deletes all graph elements.
486    */
487   protected static class DeleteAll extends AbstractAction {
488     private final Graph2DView view;
489 
490     public DeleteAll( final Graph2DView view ) {
491       this.view = view;
492       URL imageURL = ClassLoader.getSystemResource( "demo/view/resource/New16.gif" );
493       if ( imageURL != null ) {
494         putValue( Action.SMALL_ICON, new ImageIcon( imageURL ) );
495       }
496       putValue( Action.SHORT_DESCRIPTION, "Clear Window" );
497     }
498 
499     public void actionPerformed( ActionEvent e ) {
500       view.getGraph2D().clear();
501       view.getGraph2D().updateViews();
502     }
503   }
504 
505   /**
506    * Action that deletes the selected parts of the graph.
507    */
508   protected static class DeleteSelection extends AbstractAction {
509     private final Graph2DView view;
510 
511     public DeleteSelection( final Graph2DView view ) {
512       super( "Delete Selection" );
513       this.view = view;
514       URL imageURL = ClassLoader.getSystemResource( "demo/view/resource/Delete16.gif" );
515       if ( imageURL != null ) {
516         this.putValue( Action.SMALL_ICON, new ImageIcon( imageURL ) );
517       }
518       this.putValue( Action.SHORT_DESCRIPTION, "Delete Selection" );
519     }
520 
521     public void actionPerformed( ActionEvent e ) {
522       view.getGraph2D().removeSelection();
523       view.getGraph2D().updateViews();
524     }
525   }
526 
527   /**
528    * Action that applies a specified zoom level to the view.
529    */
530   protected class Zoom extends AbstractAction {
531     double factor;
532 
533     public Zoom( double factor ) {
534       super( "Zoom " + ( factor > 1.0 ? "In" : "Out" ) );
535       URL imageURL;
536       if ( factor > 1.0d ) {
537         imageURL = ClassLoader.getSystemResource( "demo/view/resource/ZoomIn16.gif" );
538       } else {
539         imageURL = ClassLoader.getSystemResource( "demo/view/resource/ZoomOut16.gif" );
540       }
541       if ( imageURL != null ) {
542         this.putValue( Action.SMALL_ICON, new ImageIcon( imageURL ) );
543       }
544       this.putValue( Action.SHORT_DESCRIPTION, "Zoom " + ( factor > 1.0 ? "In" : "Out" ) );
545       this.factor = factor;
546     }
547 
548     public void actionPerformed( ActionEvent e ) {
549       view.setZoom( view.getZoom() * factor );
550       //optional code that adjusts the size of the
551       //view's world rectangle. The world rectangle
552       //defines the region of the canvas that is
553       //accessible by using the scrollbars of the view.
554       Rectangle box = view.getGraph2D().getBoundingBox();
555       view.setWorldRect( box.x - 20, box.y - 20, box.width + 40, box.height + 40 );
556 
557       view.updateView();
558     }
559   }
560 
561   /**
562    * Action that fits the content nicely inside the view.
563    */
564   protected static class FitContent extends AbstractAction {
565     private final Graph2DView view;
566 
567     public FitContent( final Graph2DView view ) {
568       super( "Fit Content" );
569       this.view = view;
570       URL imageURL = ClassLoader.getSystemResource( "demo/view/resource/FitContent16.gif" );
571       if ( imageURL != null ) {
572         this.putValue( Action.SMALL_ICON, new ImageIcon( imageURL ) );
573       }
574       this.putValue( Action.SHORT_DESCRIPTION, "Fit Content" );
575     }
576 
577     public void actionPerformed( ActionEvent e ) {
578       view.fitContent();
579       view.updateView();
580     }
581   }
582 
583   /**
584    * Action that zooms the view to the bounding box of selected nodes.
585    */
586   public class ZoomArea extends AbstractAction {
587     public ZoomArea() {
588       super( "Zoom Area" );
589       URL imageURL = ClassLoader.getSystemResource( "demo/view/resource/Zoom16.gif" );
590       if ( imageURL != null ) {
591         this.putValue( Action.SMALL_ICON, new ImageIcon( imageURL ) );
592       }
593       this.putValue( Action.SHORT_DESCRIPTION, "Zoom Area" );
594     }
595 
596     public void actionPerformed( ActionEvent e ) {
597       Iterator viewModes = view.getViewModes();
598       while ( viewModes.hasNext() ) {
599         ViewMode viewMode = ( ViewMode ) viewModes.next();
600         if ( viewMode instanceof EditMode ) {
601           EditMode editMode = ( EditMode ) viewMode;
602           editMode.setChild( new AreaZoomMode(), null, null );
603         }
604       }
605     }
606   }
607 }