View Javadoc

1   /*
2   
3       dsh-venn-cytoscape3-app  Cytoscape3 app for venn and euler diagrams.
4       Copyright (c) 2012-2013 held jointly by the individual authors.
5   
6       This library is free software; you can redistribute it and/or modify it
7       under the terms of the GNU Lesser General Public License as published
8       by the Free Software Foundation; either version 3 of the License, or (at
9       your option) any later version.
10  
11      This library is distributed in the hope that it will be useful, but WITHOUT
12      ANY WARRANTY; with out even the implied warranty of MERCHANTABILITY or
13      FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
14      License for more details.
15  
16      You should have received a copy of the GNU Lesser General Public License
17      along with this library;  if not, write to the Free Software Foundation,
18      Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA.
19  
20      > http://www.fsf.org/licensing/licenses/lgpl.html
21      > http://www.opensource.org/licenses/lgpl-license.php
22  
23  */
24  package org.dishevelled.venn.cytoscape3.internal;
25  
26  import java.awt.BorderLayout;
27  import java.awt.Color;
28  import java.awt.Cursor;
29  import java.awt.Dialog;
30  import java.awt.FileDialog;
31  import java.awt.Image;
32  import java.awt.Paint;
33  import java.awt.Toolkit;
34  
35  import java.awt.event.ActionEvent;
36  import java.awt.event.InputEvent;
37  import java.awt.event.KeyAdapter;
38  import java.awt.event.KeyEvent;
39  
40  import java.awt.geom.Point2D;
41  
42  import java.awt.image.RenderedImage;
43  
44  import java.io.File;
45  import java.io.FileWriter;
46  import java.io.IOException;
47  
48  import java.util.Iterator;
49  import java.util.Set;
50  
51  import javax.imageio.ImageIO;
52  
53  import javax.swing.AbstractAction;
54  import javax.swing.AbstractButton;
55  import javax.swing.Action;
56  import javax.swing.InputMap;
57  import javax.swing.KeyStroke;
58  import javax.swing.JCheckBoxMenuItem;
59  import javax.swing.JComponent;
60  import javax.swing.JMenuItem;
61  import javax.swing.JPanel;
62  import javax.swing.JPopupMenu;
63  
64  import static javax.swing.SwingUtilities.windowForComponent;
65  
66  import org.apache.batik.dom.GenericDOMImplementation;
67  
68  import org.apache.batik.svggen.SVGGraphics2D;
69  
70  import org.cytoscape.application.CyApplicationManager;
71  import org.cytoscape.model.CyNetwork;
72  import org.cytoscape.model.CyNode;
73  
74  import org.dishevelled.identify.ContextMenuListener;
75  
76  import org.dishevelled.piccolo.venn.AbstractVennNode;
77  import org.dishevelled.piccolo.venn.BinaryVennNode;
78  import org.dishevelled.piccolo.venn.TernaryVennNode;
79  import org.dishevelled.piccolo.venn.QuaternaryVennNode;
80  import org.dishevelled.piccolo.venn.VennNode;
81  
82  import org.piccolo2d.PCamera;
83  import org.piccolo2d.PCanvas;
84  import org.piccolo2d.PNode;
85  
86  import org.piccolo2d.event.PBasicInputEventHandler;
87  import org.piccolo2d.event.PInputEvent;
88  import org.piccolo2d.event.PInputEventFilter;
89  import org.piccolo2d.event.PMouseWheelZoomEventHandler;
90  import org.piccolo2d.event.PPanEventHandler;
91  
92  import org.piccolo2d.util.PPaintContext;
93  import org.piccolo2d.util.PPickPath;
94  
95  import org.w3c.dom.Document;
96  import org.w3c.dom.DOMImplementation;
97  
98  /**
99   * Diagram view.
100  */
101 final class DiagramView
102     extends JPanel
103 {
104     /** SVG namespace. */
105     private static final String SVG_NS = "http://www.w3.org/2000/svg";
106 
107     /** Canvas. */
108     private final PCanvas canvas;
109 
110     /** Export to PNG image action. */
111     private final Action exportToPNG = new AbstractAction("Export to PNG...") // i18n
112         {
113             /** {@inheritDoc} */
114             public void actionPerformed(final ActionEvent event)
115             {
116                 exportToPNG();
117             }
118         };
119 
120     /** Export to SNG image action. */
121     private final Action exportToSVG = new AbstractAction("Export to SVG...") // i18n
122         {
123             /** {@inheritDoc} */
124             public void actionPerformed(final ActionEvent event)
125             {
126                 exportToSVG();
127             }
128         };
129 
130     /** Select all action. */
131     private final Action selectAll = new AbstractAction("Select all") // i18n
132         {
133             /** {@inheritDoc} */
134             public void actionPerformed(final ActionEvent event)
135             {
136                 selectAll();
137             }
138         };
139 
140     /** Clear selection action. */
141     private final Action clearSelection = new AbstractAction("Clear selection") // i18n
142         {
143             /** {@inheritDoc} */
144             public void actionPerformed(final ActionEvent event)
145             {
146                 clearSelection();
147             }
148         };
149 
150     /** Zoom in action. */
151     private final Action zoomIn = new AbstractAction("Zoom in") // i18n
152         {
153             /** {@inheritDoc} */
154             public void actionPerformed(final ActionEvent event)
155             {
156                 zoomIn();
157             }
158         };
159 
160     /** Zoom out action. */
161     private final Action zoomOut = new AbstractAction("Zoom out") // i18n
162         {
163             /** {@inheritDoc} */
164             public void actionPerformed(final ActionEvent event)
165             {
166                 zoomOut();
167             }
168         };
169 
170     /** Toggle display labels. */
171     private final Action displayLabels = new AbstractAction("Display set labels") // i18n
172         {
173             /** {@inheritDoc} */
174             public void actionPerformed(final ActionEvent event)
175             {
176                 displayLabels(((AbstractButton) event.getSource()).isSelected());
177             }
178         };
179 
180     /** Toggle display size labels. */
181     private final Action displaySizeLabels = new AbstractAction("Display size labels") // i18n
182         {
183             /** {@inheritDoc} */
184             public void actionPerformed(final ActionEvent event)
185             {
186                 displaySizeLabels(((AbstractButton) event.getSource()).isSelected());
187             }
188         };
189 
190     /** Toggle display sizes in set labels. */
191     private final Action displaySizes = new AbstractAction("Display sizes in set labels") // i18n
192         {
193             /** {@inheritDoc} */
194             public void actionPerformed(final ActionEvent event)
195             {
196                 displaySizes(((AbstractButton) event.getSource()).isSelected());
197             }
198         };
199 
200     /** Toggle display sizes for empty areas. */
201     private final Action displaySizesForEmptyAreas = new AbstractAction("Display sizes for empty areas") // i18n
202         {
203             /** {@inheritDoc} */
204             public void actionPerformed(final ActionEvent event)
205             {
206                 displaySizesForEmptyAreas(((AbstractButton) event.getSource()).isSelected());
207             }
208         };
209 
210     /** Area color. */
211     private static final Color AREA_COLOR = new Color(0, 0, 0, 0);
212 
213     /** Area pressed paint. */
214     private static final Paint AREA_PRESSED_PAINT = new Color(20, 20, 20, 80);
215 
216     /** Scale factor. */
217     private static final double SCALE_FACTOR = 0.1d;
218 
219     /** Current mode. */
220     private Mode mode = Mode.EDIT;
221 
222     /** Edit or pan modes, toggled by the space bar. */
223     private enum Mode
224     {
225         /** Edit mode. */
226         EDIT,
227 
228         /** Pan mode. */
229         PAN
230     };
231 
232     /** Application manager. */
233     private final CyApplicationManager applicationManager;
234 
235 
236     /**
237      * Create a new diagram view.
238      *
239      * @param applicationManager application manager, must not be null
240      */
241     private DiagramView(final CyApplicationManager applicationManager)
242     {
243         if (applicationManager == null)
244         {
245             throw new IllegalArgumentException("applicationManager must not be null");
246         }
247         this.applicationManager = applicationManager;
248 
249         canvas = new PCanvas();
250         canvas.setDefaultRenderQuality(PPaintContext.HIGH_QUALITY_RENDERING);
251         canvas.setAnimatingRenderQuality(PPaintContext.HIGH_QUALITY_RENDERING);
252         canvas.setInteractingRenderQuality(PPaintContext.HIGH_QUALITY_RENDERING);
253         canvas.removeInputEventListener(canvas.getPanEventHandler());
254         canvas.removeInputEventListener(canvas.getZoomEventHandler());
255         canvas.addKeyListener(new ModeEventHandler());
256         canvas.addInputEventListener(new PanEventHandler());
257         PMouseWheelZoomEventHandler mouseWheelZoomEventHandler = new PMouseWheelZoomEventHandler();
258         mouseWheelZoomEventHandler.zoomAboutCanvasCenter();
259         mouseWheelZoomEventHandler.setScaleFactor(1.0E-02d);
260         canvas.addInputEventListener(mouseWheelZoomEventHandler);
261 
262         InputMap inputMap = getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
263         int menuKeyMask = Toolkit.getDefaultToolkit().getMenuShortcutKeyMask();
264         KeyStroke ctrlShiftP = KeyStroke.getKeyStroke(KeyEvent.VK_P, menuKeyMask | InputEvent.SHIFT_DOWN_MASK);
265         KeyStroke ctrlShiftS = KeyStroke.getKeyStroke(KeyEvent.VK_S, menuKeyMask | InputEvent.SHIFT_DOWN_MASK);
266         KeyStroke ctrlShiftA = KeyStroke.getKeyStroke(KeyEvent.VK_A, menuKeyMask | InputEvent.SHIFT_DOWN_MASK);
267         KeyStroke ctrlShiftC = KeyStroke.getKeyStroke(KeyEvent.VK_C, menuKeyMask | InputEvent.SHIFT_DOWN_MASK);
268         KeyStroke ctrlShiftPeriod = KeyStroke.getKeyStroke(KeyEvent.VK_COMMA, menuKeyMask | InputEvent.SHIFT_DOWN_MASK);
269         KeyStroke ctrlShiftComma = KeyStroke.getKeyStroke(KeyEvent.VK_PERIOD, menuKeyMask | InputEvent.SHIFT_DOWN_MASK);
270         inputMap.put(ctrlShiftP, "exportToPNG");
271         inputMap.put(ctrlShiftS, "exportToSVG");
272         inputMap.put(ctrlShiftA, "selectAll");
273         inputMap.put(ctrlShiftC, "clearSelection");
274         inputMap.put(ctrlShiftComma, "zoomIn");
275         inputMap.put(ctrlShiftPeriod, "zoomOut");
276         getActionMap().put("exportToPNG", exportToPNG);
277         getActionMap().put("exportToSVG", exportToSVG);
278         getActionMap().put("selectAll", selectAll);
279         getActionMap().put("clearSelection", clearSelection);
280         getActionMap().put("zoomIn", zoomIn);
281         getActionMap().put("zoomOut", zoomOut);
282 
283         JMenuItem exportToPNGMenuItem = new JMenuItem(exportToPNG);
284         exportToPNGMenuItem.setAccelerator(ctrlShiftP);
285         JMenuItem exportToSVGMenuItem = new JMenuItem(exportToSVG);
286         exportToSVGMenuItem.setAccelerator(ctrlShiftS);
287 
288         JCheckBoxMenuItem displayLabelsMenuItem = new JCheckBoxMenuItem(displayLabels);
289         displayLabelsMenuItem.setSelected(true);
290         JCheckBoxMenuItem displaySizesMenuItem = new JCheckBoxMenuItem(displaySizes);
291         displaySizesMenuItem.setSelected(true);
292         JCheckBoxMenuItem displaySizeLabelsMenuItem = new JCheckBoxMenuItem(displaySizeLabels);
293         displaySizeLabelsMenuItem.setSelected(true);
294         JCheckBoxMenuItem displaySizesForEmptyAreasMenuItem = new JCheckBoxMenuItem(displaySizesForEmptyAreas);
295         displaySizesForEmptyAreasMenuItem.setSelected(true);
296         JMenuItem selectAllMenuItem = new JMenuItem(selectAll);
297         selectAllMenuItem.setAccelerator(ctrlShiftA);
298         JMenuItem clearSelectionMenuItem = new JMenuItem(clearSelection);
299         clearSelectionMenuItem.setAccelerator(ctrlShiftC);
300         JMenuItem zoomInMenuItem = new JMenuItem(zoomIn);
301         zoomInMenuItem.setAccelerator(ctrlShiftPeriod);
302         JMenuItem zoomOutMenuItem = new JMenuItem(zoomOut);
303         zoomOutMenuItem.setAccelerator(ctrlShiftComma);
304 
305         JPopupMenu contextMenu = new JPopupMenu();
306         contextMenu.add(exportToPNGMenuItem);
307         contextMenu.add(exportToSVGMenuItem);
308         contextMenu.addSeparator();
309         contextMenu.add(displayLabelsMenuItem);
310         contextMenu.add(displaySizesMenuItem);
311         contextMenu.add(displaySizeLabelsMenuItem);
312         contextMenu.add(displaySizesForEmptyAreasMenuItem);
313         contextMenu.addSeparator();
314         contextMenu.add(selectAllMenuItem);
315         contextMenu.add(clearSelectionMenuItem);
316         contextMenu.addSeparator();
317         contextMenu.add(zoomInMenuItem);
318         contextMenu.add(zoomOutMenuItem);
319         canvas.addMouseListener(new ContextMenuListener(contextMenu));
320 
321         setLayout(new BorderLayout());
322         add("Center", canvas);
323     }
324 
325     /**
326      * Create a new diagram view with the specified binary venn node.
327      *
328      * @param binaryVennNode binary venn node
329      * @param applicationManager application manager, must not be null
330      */
331     DiagramView(final BinaryVennNode<CyNode> binaryVennNode, final CyApplicationManager applicationManager)
332     {
333         this(applicationManager);
334         // todo:  use bounding rect provided by layout
335         binaryVennNode.offset(92.0d, 124.0d);
336         for (PNode node : binaryVennNode.nodes())
337         {
338             node.addInputEventListener(new ToolTipTextListener());
339             node.addInputEventListener(new MousePressedListener());
340         }
341         canvas.getLayer().addChild(binaryVennNode);
342     }
343 
344     /**
345      * Create a new diagram view with the specified ternary venn node.
346      *
347      * @param ternaryVennNode ternary venn node
348      * @param applicationManager application manager, must not be null
349      */
350     DiagramView(final TernaryVennNode<CyNode> ternaryVennNode, final CyApplicationManager applicationManager)
351     {
352         this(applicationManager);
353         ternaryVennNode.offset(92.0d, 70.0d);
354         for (PNode node : ternaryVennNode.nodes())
355         {
356             node.addInputEventListener(new ToolTipTextListener());
357             node.addInputEventListener(new MousePressedListener());
358         }
359         canvas.getLayer().addChild(ternaryVennNode);
360     }
361 
362     /**
363      * Create a new diagram view with the specified quaternary venn node.
364      *
365      * @param quaternaryVennNode quaternary venn node
366      * @param applicationManager application manager, must not be null
367      */
368     DiagramView(final QuaternaryVennNode<CyNode> quaternaryVennNode, final CyApplicationManager applicationManager)
369     {
370         this(applicationManager);
371         quaternaryVennNode.offset(40.0d, 235.0d);
372         for (PNode node : quaternaryVennNode.nodes())
373         {
374             node.addInputEventListener(new ToolTipTextListener());
375             node.addInputEventListener(new MousePressedListener());
376         }
377         canvas.getLayer().addChild(quaternaryVennNode);
378     }
379 
380     /**
381      * Create a new diagram view with the specified venn node.
382      *
383      * @param vennNode venn node
384      * @param applicationManager application manager, must not be null
385      */
386     DiagramView(final VennNode<CyNode> vennNode, final CyApplicationManager applicationManager)
387     {
388         this(applicationManager);
389         vennNode.offset(100.0d, 100.0d);
390         for (PNode node : vennNode.nodes())
391         {
392             node.addInputEventListener(new ToolTipTextListener());
393             node.addInputEventListener(new MousePressedListener());
394         }
395         canvas.getLayer().addChild(vennNode);
396     }
397 
398 
399     /**
400      * Return the label text for the picked node for the specified pick path, if any.
401      *
402      * @param path pick path
403      * @return the label text for the picked node for the specified pick path, or <code>null</code>
404      *    if no such label text exists
405      */
406     private String getLabelTextForPickedNode(final PPickPath path)
407     {
408         PNode pickedNode = path.getPickedNode();
409         for (Iterator i = path.getNodeStackReference().iterator(); i.hasNext(); )
410         {
411             PNode node = (PNode) i.next();
412             if (node instanceof AbstractVennNode)
413             {
414                 AbstractVennNode<CyNode> abstractVennNode = (AbstractVennNode<CyNode>) node;
415                 String labelText = abstractVennNode.labelTextForNode(pickedNode);
416                 if (labelText != null)
417                 {
418                     return labelText;
419                 }
420             }
421         }
422         return null;
423     }
424 
425     /**
426      * Return the view for the picked node for the specified pick path, if any.
427      *
428      * @param path pick path
429      * @return the view for the picked node for the specified pick path, or <code>null</code>
430      *    if no such view exists
431      */
432     private Set<CyNode> getViewForPickedNode(final PPickPath path)
433     {
434         PNode pickedNode = path.getPickedNode();
435         for (Iterator i = path.getNodeStackReference().iterator(); i.hasNext(); )
436         {
437             PNode node = (PNode) i.next();
438             if (node instanceof AbstractVennNode)
439             {
440                 AbstractVennNode<CyNode> abstractVennNode = (AbstractVennNode<CyNode>) node;
441                 Set<CyNode> view = abstractVennNode.viewForNode(pickedNode);
442                 if (view != null)
443                 {
444                     return view;
445                 }
446             }
447         }
448         return null;
449     }
450 
451     /**
452      * Export to PNG.
453      */
454     private void exportToPNG()
455     {
456         Image image = canvas.getLayer().toImage();
457         // unsafe cast, if this view isn't rooted in a dialog
458         FileDialog fileDialog = new FileDialog((Dialog) windowForComponent(this), "Export to PNG...", FileDialog.SAVE);
459         fileDialog.setVisible(true);
460 
461         String directory = fileDialog.getDirectory();
462         String fileName = fileDialog.getFile();
463 
464         if (fileName != null && directory != null)
465         {
466             try
467             {
468                 ImageIO.write((RenderedImage) image, "png", new File(directory, fileName));
469             }
470             catch (IOException e)
471             {
472                 // ignore
473             }
474         }
475     }
476 
477     /**
478      * Export to SVG.
479      */
480     private void exportToSVG()
481     {
482         // unsafe cast, if this view isn't rooted in a dialog
483         FileDialog fileDialog = new FileDialog((Dialog) windowForComponent(this), "Export to SVG...", FileDialog.SAVE);
484         fileDialog.setVisible(true);
485 
486         String directory = fileDialog.getDirectory();
487         String fileName = fileDialog.getFile();
488 
489         if (fileName != null && directory != null)
490         {
491             FileWriter writer = null;
492             try
493             {
494                 writer = new FileWriter(new File(directory, fileName));
495                 DOMImplementation domImpl = GenericDOMImplementation.getDOMImplementation();
496                 Document document = domImpl.createDocument(SVG_NS, "svg", null);
497                 SVGGraphics2D svgGenerator = new SVGGraphics2D(document);
498                 canvas.paint(svgGenerator);
499                 svgGenerator.stream(writer, true);
500             }
501             catch (IOException e)
502             {
503                 // ignore
504             }
505             finally
506             {
507                 try
508                 {
509                     writer.close();
510                 }
511                 catch (Exception e)
512                 {
513                     // ignore
514                 }
515             }
516         }
517     }
518 
519     /**
520      * Display set labels.
521      *
522      * @param displayLabels true if labels should display set labels
523      */
524     private void displayLabels(final boolean displayLabels)
525     {
526         for (Iterator i = canvas.getLayer().getChildrenIterator(); i.hasNext(); )
527         {
528             PNode node = (PNode) i.next();
529             if (node instanceof AbstractVennNode)
530             {
531                 AbstractVennNode<CyNode> vennNode = (AbstractVennNode<CyNode>) node;
532                 vennNode.setDisplayLabels(displayLabels);
533             }
534         }
535     }
536 
537     /**
538      * Display sizes.
539      *
540      * @param displaySizes true if labels should display sizes
541      */
542     private void displaySizes(final boolean displaySizes)
543     {
544         for (Iterator i = canvas.getLayer().getChildrenIterator(); i.hasNext(); )
545         {
546             PNode node = (PNode) i.next();
547             if (node instanceof AbstractVennNode)
548             {
549                 AbstractVennNode<CyNode> vennNode = (AbstractVennNode<CyNode>) node;
550                 vennNode.setDisplaySizes(displaySizes);
551             }
552         }
553     }
554 
555     /**
556      * Display size labels.
557      *
558      * @param displaySizeLabels true if labels should display size labels
559      */
560     private void displaySizeLabels(final boolean displaySizeLabels)
561     {
562         for (Iterator i = canvas.getLayer().getChildrenIterator(); i.hasNext(); )
563         {
564             PNode node = (PNode) i.next();
565             if (node instanceof AbstractVennNode)
566             {
567                 AbstractVennNode<CyNode> vennNode = (AbstractVennNode<CyNode>) node;
568                 vennNode.setDisplaySizeLabels(displaySizeLabels);
569             }
570         }
571     }
572 
573     /**
574      * Display sizes for empty areas.
575      *
576      * @param displaySizesForEmptyAreas true if labels should display sizes for empty areas
577      */
578     private void displaySizesForEmptyAreas(final boolean displaySizesForEmptyAreas)
579     {
580         for (Iterator i = canvas.getLayer().getChildrenIterator(); i.hasNext(); )
581         {
582             PNode node = (PNode) i.next();
583             if (node instanceof AbstractVennNode)
584             {
585                 AbstractVennNode<CyNode> vennNode = (AbstractVennNode<CyNode>) node;
586                 vennNode.setDisplaySizesForEmptyAreas(displaySizesForEmptyAreas);
587             }
588         }
589     }
590 
591     /**
592      * Select all.
593      */
594     private void selectAll()
595     {
596         CyNetwork currentNetwork = applicationManager.getCurrentNetwork();
597         for (Iterator i = canvas.getLayer().getChildrenIterator(); i.hasNext(); )
598         {
599             PNode node = (PNode) i.next();
600             // todo:  getModel (and possibly also setModel, get/setLayout) should be refactored to AbstractVennNode
601             if (node instanceof BinaryVennNode)
602             {
603                 BinaryVennNode<CyNode> binaryVennNode = (BinaryVennNode<CyNode>) node;
604                 for (CyNode cyNode : currentNetwork.getNodeList())
605                 {
606                     currentNetwork.getRow(cyNode).set(CyNetwork.SELECTED, binaryVennNode.getModel().union().contains(cyNode));
607                 }
608             }
609             else if (node instanceof TernaryVennNode)
610             {
611                 TernaryVennNode<CyNode> ternaryVennNode = (TernaryVennNode<CyNode>) node;
612                 for (CyNode cyNode : currentNetwork.getNodeList())
613                 {
614                     currentNetwork.getRow(cyNode).set(CyNetwork.SELECTED, ternaryVennNode.getModel().union().contains(cyNode));
615                 }
616             }
617             else if (node instanceof QuaternaryVennNode)
618             {
619                 QuaternaryVennNode<CyNode> quaternaryVennNode = (QuaternaryVennNode<CyNode>) node;
620                 for (CyNode cyNode : currentNetwork.getNodeList())
621                 {
622                     currentNetwork.getRow(cyNode).set(CyNetwork.SELECTED, quaternaryVennNode.getModel().union().contains(cyNode));
623                 }
624             }
625             else if (node instanceof VennNode)
626             {
627                 VennNode<CyNode> vennNode = (VennNode<CyNode>) node;
628                 for (CyNode cyNode : currentNetwork.getNodeList())
629                 {
630                     currentNetwork.getRow(cyNode).set(CyNetwork.SELECTED, vennNode.getModel().union().contains(cyNode));
631                 }
632             }
633         }
634         applicationManager.getCurrentNetworkView().updateView();
635     }
636 
637     /**
638      * Clear selection.
639      */
640     private void clearSelection()
641     {
642         CyNetwork currentNetwork = applicationManager.getCurrentNetwork();
643         for (CyNode node : currentNetwork.getNodeList())
644         {
645             currentNetwork.getRow(node).set(CyNetwork.SELECTED, false);
646         }
647         applicationManager.getCurrentNetworkView().updateView();
648     }
649 
650     /**
651      * Zoom in.
652      */
653     private void zoomIn()
654     {
655         PCamera camera = canvas.getCamera();
656         double scale = 1.0d + 4.0d * SCALE_FACTOR;
657         // todo: should limit scale to some reasonable maximum
658         Point2D center = camera.getBoundsReference().getCenter2D();
659         camera.scaleViewAboutPoint(scale, center.getX(), center.getY());
660     }
661 
662     /**
663      * Zoom out.
664      */
665     private void zoomOut()
666     {
667         PCamera camera = canvas.getCamera();
668         double scale = 1.0d - 2.0d * SCALE_FACTOR;
669         // todo: should limit scale to some reasonable minimum
670         Point2D center = camera.getBoundsReference().getCenter2D();
671         camera.scaleViewAboutPoint(scale, center.getX(), center.getY());
672     }
673 
674     /**
675      * Tool tip text listener.
676      */
677     private class ToolTipTextListener
678         extends PBasicInputEventHandler
679     {
680 
681         /**
682          * Create a new tool tip text listener.
683          */
684         ToolTipTextListener()
685         {
686             super();
687             PInputEventFilter eventFilter = new PInputEventFilter();
688             eventFilter.rejectAllEventTypes();
689             eventFilter.setAcceptsMouseEntered(true);
690             eventFilter.setAcceptsMouseExited(true);
691             setEventFilter(eventFilter);
692         }
693 
694         /** {@inheritDoc} */
695         public void mouseEntered(final PInputEvent event)
696         {
697             PCanvas canvas = (PCanvas) event.getComponent();
698             canvas.setToolTipText(getLabelTextForPickedNode(event.getPath()));
699         }
700 
701         /** {@inheritDoc} */
702         public void mouseExited(final PInputEvent event)
703         {
704             PCanvas canvas = (PCanvas) event.getComponent();
705             canvas.setToolTipText(null);
706         }
707     }
708 
709     /**
710      * Mouse pressed listener.
711      */
712     private class MousePressedListener
713         extends PBasicInputEventHandler
714     {
715 
716         /**
717          * Create a new mouse pressed listener.
718          */
719         MousePressedListener()
720         {
721             super();
722             PInputEventFilter eventFilter = new PInputEventFilter();
723             eventFilter.rejectAllEventTypes();
724             eventFilter.setAcceptsMousePressed(true);
725             eventFilter.setAcceptsMouseReleased(true);
726             eventFilter.setAndMask(InputEvent.BUTTON1_MASK);
727             setEventFilter(eventFilter);
728         }
729 
730 
731         /** {@inheritDoc} */
732         public void mousePressed(final PInputEvent event)
733         {
734             if (Mode.PAN == mode)
735             {
736                 return;
737             }
738             PNode pickedNode = event.getPickedNode();
739             pickedNode.setPaint(AREA_PRESSED_PAINT);
740 
741             Set<CyNode> selection = getViewForPickedNode(event.getPath());
742             if (selection != null)
743             {
744                 CyNetwork currentNetwork = applicationManager.getCurrentNetwork();
745                 for (CyNode node : currentNetwork.getNodeList())
746                 {
747                     currentNetwork.getRow(node).set(CyNetwork.SELECTED, selection.contains(node));
748                 }
749                 applicationManager.getCurrentNetworkView().updateView();
750             }
751         }
752 
753         /** {@inheritDoc} */
754         public void mouseReleased(final PInputEvent event)
755         {
756             if (Mode.PAN == mode)
757             {
758                 return;
759             }
760             PNode pickedNode = event.getPickedNode();
761             pickedNode.animateToColor(AREA_COLOR, 250L);
762         }
763     }
764 
765     /**
766      * Mode event listener.
767      */
768     private class ModeEventHandler
769         extends KeyAdapter
770     {
771 
772         /** {@inheritDoc} */
773         public void keyPressed(final KeyEvent event)
774         {
775             if (KeyEvent.VK_SPACE == event.getKeyCode())
776             {
777                 mode = Mode.PAN;
778                 ((PCanvas) event.getComponent()).setCursor(Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR));
779             }
780         }
781 
782         /** {@inheritDoc} */
783         public void keyReleased(final KeyEvent event)
784         {
785             if (KeyEvent.VK_SPACE == event.getKeyCode())
786             {
787                 mode = Mode.EDIT;
788                 ((PCanvas) event.getComponent()).setCursor(Cursor.getDefaultCursor());
789             }
790         }
791     }
792 
793     /**
794      * Pan event handler.
795      */
796     private class PanEventHandler
797         extends PPanEventHandler
798     {
799 
800         /**
801          * Create a new pan event handler.
802          */
803         PanEventHandler()
804         {
805             super();
806             // todo: cytoscape main network view uses middle-click + drag to pan
807             //    adding BUTTON2_MASK didn't seem to help, may need to fully subclass PPanEventHandler
808             setEventFilter(new PInputEventFilter(InputEvent.BUTTON1_MASK)
809                 {
810                     /** {@inheritDoc} */
811                     public boolean acceptsEvent(final PInputEvent event, final int type)
812                     {
813                         return super.acceptsEvent(event, type) && (Mode.PAN == mode);
814                     }
815                 });
816         }
817     }
818 }