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.viewmode;
15  
16  import demo.view.DemoBase;
17  import y.view.Arrow;
18  import y.view.Graph2D;
19  import y.view.Graph2DView;
20  import y.view.Graph2DViewMouseWheelZoomListener;
21  import y.view.ImageNodeRealizer;
22  import y.view.NavigationComponent;
23  import y.view.NavigationMode;
24  import y.view.NodeRealizer;
25  import y.view.Overview;
26  
27  import javax.swing.AbstractAction;
28  import javax.swing.Action;
29  import javax.swing.BorderFactory;
30  import javax.swing.Icon;
31  import javax.swing.ImageIcon;
32  import javax.swing.JButton;
33  import javax.swing.JComponent;
34  import javax.swing.JFrame;
35  import javax.swing.JPanel;
36  import javax.swing.JToggleButton;
37  import javax.swing.JToolBar;
38  import javax.swing.JViewport;
39  import javax.swing.KeyStroke;
40  import javax.swing.ScrollPaneConstants;
41  import java.awt.BorderLayout;
42  import java.awt.Color;
43  import java.awt.Component;
44  import java.awt.Dimension;
45  import java.awt.GraphicsDevice;
46  import java.awt.GraphicsEnvironment;
47  import java.awt.GridBagConstraints;
48  import java.awt.GridBagLayout;
49  import java.awt.Insets;
50  import java.awt.DisplayMode;
51  import java.awt.EventQueue;
52  import java.awt.event.ActionEvent;
53  import java.awt.event.ActionListener;
54  import java.awt.event.KeyEvent;
55  import java.awt.event.MouseAdapter;
56  import java.awt.event.MouseEvent;
57  import java.awt.geom.Point2D;
58  import java.net.URL;
59  import java.util.Locale;
60  
61  /**
62   * This demo demonstrates the usage of {@link y.view.NavigationComponent} and {@link y.view.Overview}. Both controls
63   * will be added to a glass pane the view provides {@link y.view.Graph2DView#getGlassPane()} and can be toggled during
64   * runtime.
65   * <p/>
66   * Besides one can switch to a full screen mode and navigate through the graph view.
67   *
68   * @see <a href="http://docs.yworks.com/yfiles/doc/developers-guide/mvc_view.html#cls_NavigationComponent">Section Class NavigationComponents</a> in the yFiles for Java Developer's Guide
69   * @see <a href="http://docs.yworks.com/yfiles/doc/developers-guide/mvc_view.html#cls_OverView">Section Class Overview</a> in the yFiles for Java Developer's Guide
70   */
71  public class FullScreenNavigationDemo extends DemoBase {
72    protected Icon overviewIcon, navigationIcon;
73    private JComponent overview, navigationComponent;
74    private JToolBar toolBar;
75  
76    public FullScreenNavigationDemo() {
77      overviewIcon = createIcon("demo/view/viewmode/resource/overview_tool.png");
78      navigationIcon = createIcon("demo/view/viewmode/resource/navigation_tool.png");
79  
80      //add some controls to the glass pane
81      addGlassPaneComponents();
82  
83      //fill the toolbar
84      fillToolBar();
85  
86      //load an initial graph
87      loadGraph("resource/peopleNav_small.graphml");
88  
89      //set default edge arrow
90      Graph2D graph = view.getGraph2D();
91      graph.getDefaultEdgeRealizer().setArrow(Arrow.DELTA);
92  
93      //set a default node realizer (from the loaded graph
94      NodeRealizer realizer = graph.getRealizer(graph.getNodeArray()[10]);
95      if (realizer instanceof ImageNodeRealizer) {
96        ImageNodeRealizer inr = new ImageNodeRealizer();
97        inr.setImage(((ImageNodeRealizer) realizer).getImage());
98        inr.setSize(48, 48);
99        graph.setDefaultNodeRealizer(inr);
100     }
101 
102     //focus some nodes in the graph
103     view.focusView(1.1, new Point2D.Double(150, 750), false);
104   }
105 
106   protected Action createLoadAction() {
107     //Overridden method to disable the Load menu in the demo
108     //The load action for other graphs makes no sense, because the overview window is wrong for other graphs.
109     return null;
110   }
111 
112   protected Action createSaveAction() {
113     //Overridden method to disable the Save menu in the demo
114     return null;
115   }
116 
117   protected void registerViewModes() {
118     view.addViewMode(new NavigationMode());
119   }
120 
121   protected JToolBar createToolBar() {
122     toolBar = new JToolBar();
123     return toolBar;
124   }
125 
126   protected Icon createIcon(String resourceName) {
127     URL imageURL = ClassLoader.getSystemResource(resourceName);
128     if (imageURL != null) {
129       return new ImageIcon(imageURL);
130     } else {
131       throw new IllegalArgumentException(resourceName + " not found");
132     }
133   }
134 
135   private void addGlassPaneComponents() {
136     //get the glass pane
137     JPanel glassPane = view.getGlassPane();
138     //set an according layout manager
139     glassPane.setLayout(new BorderLayout());
140 
141     JPanel toolsPanel = new JPanel(new GridBagLayout());
142     toolsPanel.setOpaque(false);
143     toolsPanel.setBackground(null);
144     toolsPanel.setBorder(BorderFactory.createEmptyBorder(16, 16, 0, 0));
145 
146     //create and add the overview to the tools panel
147     GridBagConstraints gbc = new GridBagConstraints();
148     gbc.gridx = 0;
149     gbc.anchor = GridBagConstraints.LINE_START;
150     gbc.insets = new Insets(0, 0, 16, 0);
151     overview = createOverview(view);
152     toolsPanel.add(overview, gbc);
153 
154     //create and add the navigation component to the tools panel
155     navigationComponent = createNavigationComponent(view, 20, 30);
156     toolsPanel.add(navigationComponent, gbc);
157 
158     //add the toolspanel to the glass pane
159     gbc.gridx = 0;
160     gbc.gridy = 0;
161     gbc.weightx = 1;
162     gbc.weighty = 1;
163     gbc.anchor = GridBagConstraints.FIRST_LINE_START;
164     JViewport viewport = new JViewport();
165     viewport.add(toolsPanel);
166     viewport.setOpaque(false);
167     viewport.setBackground(null);
168     JPanel westPanel = new JPanel(new BorderLayout());
169     westPanel.setOpaque(false);
170     westPanel.setBackground(null);
171     westPanel.add(viewport, BorderLayout.NORTH);
172     glassPane.add(westPanel, BorderLayout.WEST);
173   }
174 
175   private NavigationComponent createNavigationComponent(Graph2DView view, double scrollStepSize, int scrollTimerDelay) {
176     //create the NavigationComponent itself
177     final NavigationComponent navigation = new NavigationComponent(view);
178     navigation.setScrollStepSize(scrollStepSize);
179     //set the duration between scroll ticks
180     navigation.putClientProperty("NavigationComponent.ScrollTimerDelay", new Integer(scrollTimerDelay));
181     //set the initial duration until the first scroll tick is triggered
182     navigation.putClientProperty("NavigationComponent.ScrollTimerInitialDelay", new Integer(scrollTimerDelay));
183     //set a flag so that the fit content button will adjust the viewports in an animated fashion
184     navigation.putClientProperty("NavigationComponent.AnimateFitContent", Boolean.TRUE);
185 
186     //add a mouse listener that will make a semi transparent background, as soon as the mouse enters this component
187     navigation.setBackground(new Color(255, 255, 255, 0));
188     MouseAdapter navigationToolListener = new MouseAdapter() {
189       public void mouseEntered(MouseEvent e) {
190         super.mouseEntered(e);
191         Color background = navigation.getBackground();
192         //add some semi transparent background
193         navigation.setBackground(new Color(background.getRed(), background.getGreen(), background.getBlue(), 196));
194       }
195 
196       public void mouseExited(MouseEvent e) {
197         super.mouseExited(e);
198         Color background = navigation.getBackground();
199         //make the background completely transparent
200         navigation.setBackground(new Color(background.getRed(), background.getGreen(), background.getBlue(), 0));
201       }
202     };
203     navigation.addMouseListener(navigationToolListener);
204 
205     //add mouse listener to all sub components of the navigationComponent
206     for (int i = 0; i < navigation.getComponents().length; i++) {
207       Component component = navigation.getComponents()[i];
208       component.addMouseListener(navigationToolListener);
209     }
210 
211     return navigation;
212   }
213 
214   private Overview createOverview(Graph2DView view) {
215     Overview ov = new Overview(view);
216     /* customize the overview */
217     //animates the scrolling
218     ov.putClientProperty("Overview.AnimateScrollTo", Boolean.TRUE);
219     //blurs the part of the graph which can currently not be seen
220     ov.putClientProperty("Overview.PaintStyle", "Funky");
221     //allows zooming from within the overview
222     ov.putClientProperty("Overview.AllowZooming", Boolean.TRUE);
223     //provides functionality for navigation via keybord (zoom in (+), zoom out (-), navigation with arrow keys)
224     ov.putClientProperty("Overview.AllowKeyboardNavigation", Boolean.TRUE);
225     //determines how to differ between the part of the graph that can currently be seen, and the rest
226     ov.putClientProperty("Overview.Inverse", Boolean.TRUE);
227     ov.setPreferredSize(new Dimension(150, 150));
228     ov.setMinimumSize(new Dimension(150, 150));
229 
230     ov.setBorder(BorderFactory.createEtchedBorder());
231     return ov;
232   }
233 
234   private void fillToolBar() {
235     //create and add the overview button to the toolbar
236     AbstractAction overviewAction = new ToggleComponentVisibilityAction(overview);
237     overviewAction.putValue(Action.SMALL_ICON, overviewIcon);
238     overviewAction.putValue(Action.SHORT_DESCRIPTION, "Toggle Overview");
239     JToggleButton overviewButton = new JToggleButton(overviewAction);
240     overviewButton.setSelected(true);
241     toolBar.add(overviewButton);
242 
243     AbstractAction navigationControlsAction = new ToggleComponentVisibilityAction(navigationComponent);
244     navigationControlsAction.putValue(Action.SMALL_ICON, navigationIcon);
245     navigationControlsAction.putValue(Action.SHORT_DESCRIPTION, "Toggle Navigation Controls");
246     JToggleButton navigationButton = new JToggleButton(navigationControlsAction);
247     navigationButton.setSelected(true);
248     toolBar.add(navigationButton);
249 
250     toolBar.addSeparator();
251 
252     //add the fullscreen action to the toolbar
253     toolBar.add(new FullScreenAction(view.getGraph2D()));
254   }
255 
256 
257   /**
258    * Launches this demo.
259    *
260    * @param args args
261    */
262   public static void main(String[] args) {
263     EventQueue.invokeLater(new Runnable() {
264       public void run() {
265         Locale.setDefault(Locale.ENGLISH);
266         initLnF();
267         (new FullScreenNavigationDemo()).start("Full Screen Navigation Demo");
268       }
269     });
270   }
271 
272   /** An action that will toggle the visibility of the given component. */
273   static class ToggleComponentVisibilityAction extends AbstractAction {
274     private final Component component;
275 
276     public ToggleComponentVisibilityAction(Component component) {
277       super();
278       this.component = component;
279     }
280 
281     public void actionPerformed(ActionEvent e) {
282       component.setVisible(!component.isVisible());
283     }
284   }
285 
286   /** displays the current graph in full screen mode. */
287   class FullScreenAction extends AbstractAction {
288     private Graph2DView view;
289     private JFrame frame;
290     private int scrollStepSize = 15;
291     private int scrollTimerDelay = 5;
292     private final Graph2D graph2D;
293     private Icon closeIcon;
294 
295     /**
296      * creates an instance.
297      *
298      * @param graph2D the graph this action is created for
299      */
300     public FullScreenAction(Graph2D graph2D) {
301       super("Full Screen");
302       closeIcon = createIcon("demo/view/viewmode/resource/close.png");
303 
304       this.putValue(Action.SMALL_ICON, createIcon("demo/view/viewmode/resource/fullscreen.png"));
305       this.putValue(Action.SHORT_DESCRIPTION, "Fullscreen Mode");
306       this.graph2D = graph2D;
307     }
308 
309     /** @return the current step size for scrolling the graph with the navigation component in the full screen view. */
310     public int getScrollStepSize() {
311       return scrollStepSize;
312     }
313 
314     /**
315      * sets the step size for scrolling the graph with the navigation component in the full screen view.
316      *
317      * @param scrollStepSize the step size for scrolling
318      */
319     public void setScrollStepSize(int scrollStepSize) {
320       this.scrollStepSize = scrollStepSize;
321     }
322 
323     /** @return the current delay between two scroll events of the navigation component in the full screen view. */
324     public int getScrollTimerDelay() {
325       return scrollTimerDelay;
326     }
327 
328     /**
329      * sets the delay between two scroll events of the navigation component in the full screen view.
330      *
331      * @param scrollTimerDelay the delay in ms
332      */
333     public void setScrollTimerDelay(int scrollTimerDelay) {
334       this.scrollTimerDelay = scrollTimerDelay;
335     }
336 
337     private void showFrame() {
338       frame.setVisible(true);
339       view.fitContent();
340     }
341 
342     private boolean addGraphView() {
343       createGraphView();
344       if (view != null) {
345         frame.getRootPane().setContentPane(view);
346         return true;
347       } else {
348         return false;
349       }
350     }
351 
352     private void addGlassPaneComponents() {
353       //get the glass pane
354       JPanel glassPane = view.getGlassPane();
355       //set n according layout
356       glassPane.setLayout(new GridBagLayout());
357 
358       JPanel toolsPanel = new JPanel(new GridBagLayout());
359       toolsPanel.setOpaque(false);
360       toolsPanel.setBackground(null);
361 
362       //creat the overview
363       Overview overview = createOverview(view);
364       //create the navigation component
365       NavigationComponent navigationComponent = createNavigationComponent(view, scrollStepSize, scrollTimerDelay);
366 
367       //create the inner toolbar and add
368       JPanel innerToolbar = createInnerToolbar(overview, navigationComponent);
369       GridBagConstraints gbc = new GridBagConstraints();
370       gbc.gridx = 0;
371       gbc.anchor = GridBagConstraints.LINE_END;
372       toolsPanel.add(innerToolbar, gbc);
373 
374       //add the overview
375       gbc.gridy = 1;
376       gbc.insets = new Insets(16, 0, 0, 0);
377       toolsPanel.add(overview, gbc);
378 
379       //add the navigation component
380       gbc.gridx = 0;
381       gbc.gridy = 0;
382       gbc.anchor = GridBagConstraints.FIRST_LINE_START;
383       gbc.insets = new Insets(11, 11, 0, 0);
384       navigationComponent.setPreferredSize(new Dimension((int) navigationComponent.getPreferredSize().getWidth(), 300));
385       glassPane.add(navigationComponent, gbc);
386 
387       gbc.gridx = 0;
388       gbc.gridy = 0;
389       gbc.weightx = 1;
390       gbc.weighty = 1;
391       gbc.anchor = GridBagConstraints.FIRST_LINE_END;
392       gbc.insets = new Insets(16, 0, 0, 16);
393       glassPane.add(toolsPanel, gbc);
394     }
395 
396     private JPanel createInnerToolbar(final Overview overview, final NavigationComponent navigationComponent) {
397       GridBagConstraints gbc = new GridBagConstraints();
398       Insets margin = new Insets(2, 2, 2, 2);
399 
400       JPanel headPanel = new JPanel(new GridBagLayout());
401       headPanel.setBackground(Color.WHITE);
402 
403       //overview toggle
404       final JToggleButton overviewButton = new JToggleButton(new ToggleComponentVisibilityAction(overview));
405       overviewButton.setMargin(margin);
406       overviewButton.setIcon(overviewIcon);
407       overviewButton.setToolTipText("Toggle Overview");
408       gbc.weightx = 0;
409       gbc.anchor = GridBagConstraints.LINE_END;
410       gbc.insets = new Insets(0, 1, 0, 1);
411       headPanel.add(overviewButton, gbc);
412 
413       //navigation controls toggle
414       final JToggleButton navigateButton = new JToggleButton(new ToggleComponentVisibilityAction(navigationComponent));
415       navigateButton.setMargin(margin);
416       navigateButton.setToolTipText("Toggle Navigation Controls");
417       navigateButton.setIcon(navigationIcon);
418       headPanel.add(navigateButton, gbc);
419 
420       //close button to leave fullscreen mode
421       JButton closeButton = new JButton(new AbstractAction() {
422         public void actionPerformed(ActionEvent e) {
423           closeFrame();
424         }
425       });
426       closeButton.setToolTipText("Leave Fullscreen Mode (ESC)");
427       closeButton.setIcon(closeIcon);
428       closeButton.setMargin(margin);
429       headPanel.add(closeButton, gbc);
430 
431       return headPanel;
432     }
433 
434     private void createFrame() {
435       frame = new JFrame();
436       frame.setResizable(false);
437       if (!frame.isDisplayable()) {
438         frame.setUndecorated(true);
439       }
440       frame.getContentPane().setLayout(new BorderLayout());
441       GraphicsDevice gd = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice();
442       DisplayMode mode = gd.getDisplayMode();
443       frame.setSize(mode.getWidth(), mode.getHeight());
444     }
445 
446     private void createGraphView() {
447       view = new Graph2DView(graph2D);
448       view.setAntialiasedPainting(true);
449       view.getCanvasComponent().addMouseWheelListener(new Graph2DViewMouseWheelZoomListener());
450       view.addViewMode(new NavigationMode());
451       //view.addViewMode(new NavigationMode());
452       view.setScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_NEVER,
453           ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
454     }
455 
456     private void addEscapeListener() {
457       KeyStroke stroke = KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0);
458       view.registerKeyboardAction(new ActionListener() {
459         public void actionPerformed(ActionEvent e) {
460           closeFrame();
461         }
462       }, stroke, JComponent.WHEN_IN_FOCUSED_WINDOW);
463     }
464 
465     private void closeFrame() {
466       if (frame != null) {
467         frame.setVisible(false);
468         frame.dispose();
469       }
470       if (view != null) {
471         view.getGraph2D().removeView(view);
472       }
473       view = null;
474       frame = null;
475     }
476 
477     public void actionPerformed(ActionEvent e) {
478       // Close former full screen, if it is still open.
479       closeFrame();
480       // Create new full screen.
481       createFrame();
482       // Add view for current graph.
483       if (addGraphView()) {
484         // If adding the view was successful, decorate it and show the full screen.
485         addEscapeListener();
486         addGlassPaneComponents();
487         showFrame();
488       }
489     }
490   }
491 }
492