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