View Javadoc

1   /*
2   
3       dsh-venn-cytoscape-plugin  Cytoscape plugin for venn diagrams.
4       Copyright (c) 2010-2012 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.cytoscape;
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 cytoscape.CyNode;
71  import cytoscape.CyNetwork;
72  import cytoscape.Cytoscape;
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 
233     /**
234      * Create a new diagram view.
235      */
236     private DiagramView()
237     {
238         canvas = new PCanvas();
239         canvas.setDefaultRenderQuality(PPaintContext.HIGH_QUALITY_RENDERING);
240         canvas.setAnimatingRenderQuality(PPaintContext.HIGH_QUALITY_RENDERING);
241         canvas.setInteractingRenderQuality(PPaintContext.HIGH_QUALITY_RENDERING);
242         canvas.removeInputEventListener(canvas.getPanEventHandler());
243         canvas.removeInputEventListener(canvas.getZoomEventHandler());
244         canvas.addKeyListener(new ModeEventHandler());
245         canvas.addInputEventListener(new PanEventHandler());
246         PMouseWheelZoomEventHandler mouseWheelZoomEventHandler = new PMouseWheelZoomEventHandler();
247         mouseWheelZoomEventHandler.zoomAboutCanvasCenter();
248         mouseWheelZoomEventHandler.setScaleFactor(1.0E-02d);
249         canvas.addInputEventListener(mouseWheelZoomEventHandler);
250 
251         InputMap inputMap = getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
252         int menuKeyMask = Toolkit.getDefaultToolkit().getMenuShortcutKeyMask();
253         KeyStroke ctrlShiftP = KeyStroke.getKeyStroke(KeyEvent.VK_P, menuKeyMask | InputEvent.SHIFT_DOWN_MASK);
254         KeyStroke ctrlShiftS = KeyStroke.getKeyStroke(KeyEvent.VK_S, menuKeyMask | InputEvent.SHIFT_DOWN_MASK);
255         KeyStroke ctrlShiftA = KeyStroke.getKeyStroke(KeyEvent.VK_A, menuKeyMask | InputEvent.SHIFT_DOWN_MASK);
256         KeyStroke ctrlShiftC = KeyStroke.getKeyStroke(KeyEvent.VK_C, menuKeyMask | InputEvent.SHIFT_DOWN_MASK);
257         KeyStroke ctrlShiftPeriod = KeyStroke.getKeyStroke(KeyEvent.VK_COMMA, menuKeyMask | InputEvent.SHIFT_DOWN_MASK);
258         KeyStroke ctrlShiftComma = KeyStroke.getKeyStroke(KeyEvent.VK_PERIOD, menuKeyMask | InputEvent.SHIFT_DOWN_MASK);
259         inputMap.put(ctrlShiftP, "exportToPNG");
260         inputMap.put(ctrlShiftS, "exportToSVG");
261         inputMap.put(ctrlShiftA, "selectAll");
262         inputMap.put(ctrlShiftC, "clearSelection");
263         inputMap.put(ctrlShiftComma, "zoomIn");
264         inputMap.put(ctrlShiftPeriod, "zoomOut");
265         getActionMap().put("exportToPNG", exportToPNG);
266         getActionMap().put("exportToSVG", exportToSVG);
267         getActionMap().put("selectAll", selectAll);
268         getActionMap().put("clearSelection", clearSelection);
269         getActionMap().put("zoomIn", zoomIn);
270         getActionMap().put("zoomOut", zoomOut);
271 
272         JMenuItem exportToPNGMenuItem = new JMenuItem(exportToPNG);
273         exportToPNGMenuItem.setAccelerator(ctrlShiftP);
274         JMenuItem exportToSVGMenuItem = new JMenuItem(exportToSVG);
275         exportToSVGMenuItem.setAccelerator(ctrlShiftS);
276 
277         JCheckBoxMenuItem displayLabelsMenuItem = new JCheckBoxMenuItem(displayLabels);
278         displayLabelsMenuItem.setSelected(true);
279         JCheckBoxMenuItem displaySizesMenuItem = new JCheckBoxMenuItem(displaySizes);
280         displaySizesMenuItem.setSelected(true);
281         JCheckBoxMenuItem displaySizeLabelsMenuItem = new JCheckBoxMenuItem(displaySizeLabels);
282         displaySizeLabelsMenuItem.setSelected(true);
283         JCheckBoxMenuItem displaySizesForEmptyAreasMenuItem = new JCheckBoxMenuItem(displaySizesForEmptyAreas);
284         displaySizesForEmptyAreasMenuItem.setSelected(true);
285         JMenuItem selectAllMenuItem = new JMenuItem(selectAll);
286         selectAllMenuItem.setAccelerator(ctrlShiftA);
287         JMenuItem clearSelectionMenuItem = new JMenuItem(clearSelection);
288         clearSelectionMenuItem.setAccelerator(ctrlShiftC);
289         JMenuItem zoomInMenuItem = new JMenuItem(zoomIn);
290         zoomInMenuItem.setAccelerator(ctrlShiftPeriod);
291         JMenuItem zoomOutMenuItem = new JMenuItem(zoomOut);
292         zoomOutMenuItem.setAccelerator(ctrlShiftComma);
293 
294         JPopupMenu contextMenu = new JPopupMenu();
295         contextMenu.add(exportToPNGMenuItem);
296         contextMenu.add(exportToSVGMenuItem);
297         contextMenu.addSeparator();
298         contextMenu.add(displayLabelsMenuItem);
299         contextMenu.add(displaySizesMenuItem);
300         contextMenu.add(displaySizeLabelsMenuItem);
301         contextMenu.add(displaySizesForEmptyAreasMenuItem);
302         contextMenu.addSeparator();
303         contextMenu.add(selectAllMenuItem);
304         contextMenu.add(clearSelectionMenuItem);
305         contextMenu.addSeparator();
306         contextMenu.add(zoomInMenuItem);
307         contextMenu.add(zoomOutMenuItem);
308         canvas.addMouseListener(new ContextMenuListener(contextMenu));
309 
310         setLayout(new BorderLayout());
311         add("Center", canvas);
312     }
313 
314     /**
315      * Create a new diagram view with the specified binary venn node.
316      *
317      * @param binaryVennNode binary venn node
318      */
319     DiagramView(final BinaryVennNode<CyNode> binaryVennNode)
320     {
321         this();
322         // todo:  use bounding rect provided by layout
323         binaryVennNode.offset(92.0d, 124.0d);
324         for (PNode node : binaryVennNode.nodes())
325         {
326             node.addInputEventListener(new ToolTipTextListener());
327             node.addInputEventListener(new MousePressedListener());
328         }
329         canvas.getLayer().addChild(binaryVennNode);
330     }
331 
332     /**
333      * Create a new diagram view with the specified ternary venn node.
334      *
335      * @param ternaryVennNode ternary venn node
336      */
337     DiagramView(final TernaryVennNode<CyNode> ternaryVennNode)
338     {
339         this();
340         ternaryVennNode.offset(92.0d, 70.0d);
341         for (PNode node : ternaryVennNode.nodes())
342         {
343             node.addInputEventListener(new ToolTipTextListener());
344             node.addInputEventListener(new MousePressedListener());
345         }
346         canvas.getLayer().addChild(ternaryVennNode);
347     }
348 
349     /**
350      * Create a new diagram view with the specified quaternary venn node.
351      *
352      * @param quaternaryVennNode quaternary venn node
353      */
354     DiagramView(final QuaternaryVennNode<CyNode> quaternaryVennNode)
355     {
356         this();
357         quaternaryVennNode.offset(40.0d, 235.0d);
358         for (PNode node : quaternaryVennNode.nodes())
359         {
360             node.addInputEventListener(new ToolTipTextListener());
361             node.addInputEventListener(new MousePressedListener());
362         }
363         canvas.getLayer().addChild(quaternaryVennNode);
364     }
365 
366     /**
367      * Create a new diagram view with the specified venn node.
368      *
369      * @param vennNode venn node
370      */
371     DiagramView(final VennNode<CyNode> vennNode)
372     {
373         this();
374         vennNode.offset(100.0d, 100.0d);
375         for (PNode node : vennNode.nodes())
376         {
377             node.addInputEventListener(new ToolTipTextListener());
378             node.addInputEventListener(new MousePressedListener());
379         }
380         canvas.getLayer().addChild(vennNode);
381     }
382 
383 
384     /**
385      * Return the label text for the picked node for the specified pick path, if any.
386      *
387      * @param path pick path
388      * @return the label text for the picked node for the specified pick path, or <code>null</code>
389      *    if no such label text exists
390      */
391     private String getLabelTextForPickedNode(final PPickPath path)
392     {
393         PNode pickedNode = path.getPickedNode();
394         for (Iterator i = path.getNodeStackReference().iterator(); i.hasNext(); )
395         {
396             PNode node = (PNode) i.next();
397             if (node instanceof AbstractVennNode)
398             {
399                 AbstractVennNode<CyNode> abstractVennNode = (AbstractVennNode<CyNode>) node;
400                 String labelText = abstractVennNode.labelTextForNode(pickedNode);
401                 if (labelText != null)
402                 {
403                     return labelText;
404                 }
405             }
406         }
407         return null;
408     }
409 
410     /**
411      * Return the view for the picked node for the specified pick path, if any.
412      *
413      * @param path pick path
414      * @return the view for the picked node for the specified pick path, or <code>null</code>
415      *    if no such view exists
416      */
417     private Set<CyNode> getViewForPickedNode(final PPickPath path)
418     {
419         PNode pickedNode = path.getPickedNode();
420         for (Iterator i = path.getNodeStackReference().iterator(); i.hasNext(); )
421         {
422             PNode node = (PNode) i.next();
423             if (node instanceof AbstractVennNode)
424             {
425                 AbstractVennNode<CyNode> abstractVennNode = (AbstractVennNode<CyNode>) node;
426                 Set<CyNode> view = abstractVennNode.viewForNode(pickedNode);
427                 if (view != null)
428                 {
429                     return view;
430                 }
431             }
432         }
433         return null;
434     }
435 
436     /**
437      * Export to PNG.
438      */
439     private void exportToPNG()
440     {
441         Image image = canvas.getLayer().toImage();
442         // unsafe cast, if this view isn't rooted in a dialog
443         FileDialog fileDialog = new FileDialog((Dialog) windowForComponent(this), "Export to PNG...", FileDialog.SAVE);
444         fileDialog.setVisible(true);
445 
446         String directory = fileDialog.getDirectory();
447         String fileName = fileDialog.getFile();
448 
449         if (fileName != null && directory != null)
450         {
451             try
452             {
453                 ImageIO.write((RenderedImage) image, "png", new File(directory, fileName));
454             }
455             catch (IOException e)
456             {
457                 // ignore
458             }
459         }
460     }
461 
462     /**
463      * Export to SVG.
464      */
465     private void exportToSVG()
466     {
467         // unsafe cast, if this view isn't rooted in a dialog
468         FileDialog fileDialog = new FileDialog((Dialog) windowForComponent(this), "Export to SVG...", FileDialog.SAVE);
469         fileDialog.setVisible(true);
470 
471         String directory = fileDialog.getDirectory();
472         String fileName = fileDialog.getFile();
473 
474         if (fileName != null && directory != null)
475         {
476             FileWriter writer = null;
477             try
478             {
479                 writer = new FileWriter(new File(directory, fileName));
480                 DOMImplementation domImpl = GenericDOMImplementation.getDOMImplementation();
481                 Document document = domImpl.createDocument(SVG_NS, "svg", null);
482                 SVGGraphics2D svgGenerator = new SVGGraphics2D(document);
483                 canvas.paint(svgGenerator);
484                 svgGenerator.stream(writer, true);
485             }
486             catch (IOException e)
487             {
488                 // ignore
489             }
490             finally
491             {
492                 try
493                 {
494                     writer.close();
495                 }
496                 catch (Exception e)
497                 {
498                     // ignore
499                 }
500             }
501         }
502     }
503 
504     /**
505      * Display set labels.
506      *
507      * @param displayLabels true if labels should display set labels
508      */
509     private void displayLabels(final boolean displayLabels)
510     {
511         for (Iterator i = canvas.getLayer().getChildrenIterator(); i.hasNext(); )
512         {
513             PNode node = (PNode) i.next();
514             if (node instanceof AbstractVennNode)
515             {
516                 AbstractVennNode<CyNode> vennNode = (AbstractVennNode<CyNode>) node;
517                 vennNode.setDisplayLabels(displayLabels);
518             }
519         }
520     }
521 
522     /**
523      * Display sizes.
524      *
525      * @param displaySizes true if labels should display sizes
526      */
527     private void displaySizes(final boolean displaySizes)
528     {
529         for (Iterator i = canvas.getLayer().getChildrenIterator(); i.hasNext(); )
530         {
531             PNode node = (PNode) i.next();
532             if (node instanceof AbstractVennNode)
533             {
534                 AbstractVennNode<CyNode> vennNode = (AbstractVennNode<CyNode>) node;
535                 vennNode.setDisplaySizes(displaySizes);
536             }
537         }
538     }
539 
540     /**
541      * Display size labels.
542      *
543      * @param displaySizeLabels true if labels should display size labels
544      */
545     private void displaySizeLabels(final boolean displaySizeLabels)
546     {
547         for (Iterator i = canvas.getLayer().getChildrenIterator(); i.hasNext(); )
548         {
549             PNode node = (PNode) i.next();
550             if (node instanceof AbstractVennNode)
551             {
552                 AbstractVennNode<CyNode> vennNode = (AbstractVennNode<CyNode>) node;
553                 vennNode.setDisplaySizeLabels(displaySizeLabels);
554             }
555         }
556     }
557 
558     /**
559      * Display sizes for empty areas.
560      *
561      * @param displaySizesForEmptyAreas true if labels should display sizes for empty areas
562      */
563     private void displaySizesForEmptyAreas(final boolean displaySizesForEmptyAreas)
564     {
565         for (Iterator i = canvas.getLayer().getChildrenIterator(); i.hasNext(); )
566         {
567             PNode node = (PNode) i.next();
568             if (node instanceof AbstractVennNode)
569             {
570                 AbstractVennNode<CyNode> vennNode = (AbstractVennNode<CyNode>) node;
571                 vennNode.setDisplaySizesForEmptyAreas(displaySizesForEmptyAreas);
572             }
573         }
574     }
575 
576     /**
577      * Select all.
578      */
579     private void selectAll()
580     {
581         CyNetwork currentNetwork = Cytoscape.getCurrentNetwork();
582         for (Iterator i = canvas.getLayer().getChildrenIterator(); i.hasNext(); )
583         {
584             PNode node = (PNode) i.next();
585             // todo:  getModel (and possibly also setModel, get/setLayout) should be refactored to AbstractVennNode
586             if (node instanceof BinaryVennNode)
587             {
588                 BinaryVennNode<CyNode> binaryVennNode = (BinaryVennNode<CyNode>) node;
589                 currentNetwork.unselectAllNodes();
590                 currentNetwork.setSelectedNodeState(binaryVennNode.getModel().union(), true);
591             }
592             else if (node instanceof TernaryVennNode)
593             {
594                 TernaryVennNode<CyNode> ternaryVennNode = (TernaryVennNode<CyNode>) node;
595                 currentNetwork.unselectAllNodes();
596                 currentNetwork.setSelectedNodeState(ternaryVennNode.getModel().union(), true);
597             }
598             else if (node instanceof QuaternaryVennNode)
599             {
600                 QuaternaryVennNode<CyNode> quaternaryVennNode = (QuaternaryVennNode<CyNode>) node;
601                 currentNetwork.unselectAllNodes();
602                 currentNetwork.setSelectedNodeState(quaternaryVennNode.getModel().union(), true);
603             }
604             else if (node instanceof VennNode)
605             {
606                 VennNode<CyNode> vennNode = (VennNode<CyNode>) node;
607                 currentNetwork.unselectAllNodes();
608                 currentNetwork.setSelectedNodeState(vennNode.getModel().union(), true);
609             }
610         }
611         Cytoscape.getCurrentNetworkView().updateView();
612     }
613 
614     /**
615      * Clear selection.
616      */
617     private void clearSelection()
618     {
619         CyNetwork currentNetwork = Cytoscape.getCurrentNetwork();
620         currentNetwork.unselectAllNodes();
621         Cytoscape.getCurrentNetworkView().updateView();
622     }
623 
624     /**
625      * Zoom in.
626      */
627     private void zoomIn()
628     {
629         PCamera camera = canvas.getCamera();
630         double scale = 1.0d + 4.0d * SCALE_FACTOR;
631         // todo: should limit scale to some reasonable maximum
632         Point2D center = camera.getBoundsReference().getCenter2D();
633         camera.scaleViewAboutPoint(scale, center.getX(), center.getY());
634     }
635 
636     /**
637      * Zoom out.
638      */
639     private void zoomOut()
640     {
641         PCamera camera = canvas.getCamera();
642         double scale = 1.0d - 2.0d * SCALE_FACTOR;
643         // todo: should limit scale to some reasonable minimum
644         Point2D center = camera.getBoundsReference().getCenter2D();
645         camera.scaleViewAboutPoint(scale, center.getX(), center.getY());
646     }
647 
648     /**
649      * Tool tip text listener.
650      */
651     private class ToolTipTextListener
652         extends PBasicInputEventHandler
653     {
654 
655         /**
656          * Create a new tool tip text listener.
657          */
658         ToolTipTextListener()
659         {
660             super();
661             PInputEventFilter eventFilter = new PInputEventFilter();
662             eventFilter.rejectAllEventTypes();
663             eventFilter.setAcceptsMouseEntered(true);
664             eventFilter.setAcceptsMouseExited(true);
665             setEventFilter(eventFilter);
666         }
667 
668         /** {@inheritDoc} */
669         public void mouseEntered(final PInputEvent event)
670         {
671             PCanvas canvas = (PCanvas) event.getComponent();
672             canvas.setToolTipText(getLabelTextForPickedNode(event.getPath()));
673         }
674 
675         /** {@inheritDoc} */
676         public void mouseExited(final PInputEvent event)
677         {
678             PCanvas canvas = (PCanvas) event.getComponent();
679             canvas.setToolTipText(null);
680         }
681     }
682 
683     /**
684      * Mouse pressed listener.
685      */
686     private class MousePressedListener
687         extends PBasicInputEventHandler
688     {
689 
690         /**
691          * Create a new mouse pressed listener.
692          */
693         MousePressedListener()
694         {
695             super();
696             PInputEventFilter eventFilter = new PInputEventFilter();
697             eventFilter.rejectAllEventTypes();
698             eventFilter.setAcceptsMousePressed(true);
699             eventFilter.setAcceptsMouseReleased(true);
700             eventFilter.setAndMask(InputEvent.BUTTON1_MASK);
701             setEventFilter(eventFilter);
702         }
703 
704 
705         /** {@inheritDoc} */
706         public void mousePressed(final PInputEvent event)
707         {
708             if (Mode.PAN == mode)
709             {
710                 return;
711             }
712             PNode pickedNode = event.getPickedNode();
713             pickedNode.setPaint(AREA_PRESSED_PAINT);
714 
715             Set<CyNode> selection = getViewForPickedNode(event.getPath());
716             if (selection != null)
717             {
718                 CyNetwork currentNetwork = Cytoscape.getCurrentNetwork();
719                 currentNetwork.unselectAllNodes();
720                 currentNetwork.setSelectedNodeState(selection, true);
721                 Cytoscape.getCurrentNetworkView().updateView();
722             }
723         }
724 
725         /** {@inheritDoc} */
726         public void mouseReleased(final PInputEvent event)
727         {
728             if (Mode.PAN == mode)
729             {
730                 return;
731             }
732             PNode pickedNode = event.getPickedNode();
733             pickedNode.animateToColor(AREA_COLOR, 250L);
734         }
735     }
736 
737     /**
738      * Mode event listener.
739      */
740     private class ModeEventHandler
741         extends KeyAdapter
742     {
743 
744         /** {@inheritDoc} */
745         public void keyPressed(final KeyEvent event)
746         {
747             if (KeyEvent.VK_SPACE == event.getKeyCode())
748             {
749                 mode = Mode.PAN;
750                 ((PCanvas) event.getComponent()).setCursor(Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR));
751             }
752         }
753 
754         /** {@inheritDoc} */
755         public void keyReleased(final KeyEvent event)
756         {
757             if (KeyEvent.VK_SPACE == event.getKeyCode())
758             {
759                 mode = Mode.EDIT;
760                 ((PCanvas) event.getComponent()).setCursor(Cursor.getDefaultCursor());
761             }
762         }
763     }
764 
765     /**
766      * Pan event handler.
767      */
768     private class PanEventHandler
769         extends PPanEventHandler
770     {
771 
772         /**
773          * Create a new pan event handler.
774          */
775         PanEventHandler()
776         {
777             super();
778             // todo: cytoscape main network view uses middle-click + drag to pan
779             //    adding BUTTON2_MASK didn't seem to help, may need to fully subclass PPanEventHandler
780             setEventFilter(new PInputEventFilter(InputEvent.BUTTON1_MASK)
781                 {
782                     /** {@inheritDoc} */
783                     public boolean acceptsEvent(final PInputEvent event, final int type)
784                     {
785                         return super.acceptsEvent(event, type) && (Mode.PAN == mode);
786                     }
787                 });
788         }
789     }
790 }