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 static javax.swing.SwingUtilities.windowForComponent;
27  import static org.dishevelled.venn.cytoscape.VennDiagramsUtils.installCloseKeyBinding;
28  import static org.dishevelled.venn.cytoscape.VennDiagramsUtils.nameOf;
29  
30  import java.awt.BorderLayout;
31  import java.awt.Toolkit;
32  
33  import java.awt.event.ActionEvent;
34  import java.awt.event.InputEvent;
35  import java.awt.event.KeyEvent;
36  
37  import java.awt.geom.Rectangle2D;
38  
39  import java.util.ArrayList;
40  import java.util.HashSet;
41  import java.util.List;
42  import java.util.Set;
43  
44  import javax.swing.AbstractAction;
45  import javax.swing.Action;
46  import javax.swing.InputMap;
47  import javax.swing.KeyStroke;
48  import javax.swing.JCheckBoxMenuItem;
49  import javax.swing.JComponent;
50  import javax.swing.JDialog;
51  import javax.swing.JList;
52  import javax.swing.JPanel;
53  import javax.swing.JPopupMenu;
54  import javax.swing.JScrollPane;
55  import javax.swing.SwingUtilities;
56  
57  import javax.swing.border.EmptyBorder;
58  
59  import javax.swing.event.ListSelectionEvent;
60  import javax.swing.event.ListSelectionListener;
61  
62  import ca.odell.glazedlists.EventList;
63  import ca.odell.glazedlists.GlazedLists;
64  
65  import ca.odell.glazedlists.swing.EventListModel;
66  import ca.odell.glazedlists.swing.EventSelectionModel;
67  
68  import com.google.common.base.Joiner;
69  
70  import cytoscape.CyNode;
71  
72  import cytoscape.groups.CyGroup;
73  import cytoscape.groups.CyGroupChangeListener;
74  import cytoscape.groups.CyGroupManager;
75  import cytoscape.groups.CyGroupViewer;
76  
77  import cytoscape.view.CyNetworkView;
78  
79  import org.dishevelled.iconbundle.IconBundle;
80  import org.dishevelled.iconbundle.IconSize;
81  
82  import org.dishevelled.iconbundle.impl.CachingIconBundle;
83  import org.dishevelled.iconbundle.impl.PNGIconBundle;
84  
85  import org.dishevelled.identify.ContextMenuListener;
86  import org.dishevelled.identify.IdentifiableAction;
87  import org.dishevelled.identify.IdButton;
88  import org.dishevelled.identify.IdMenuItem;
89  import org.dishevelled.identify.IdToolBar;
90  
91  import org.dishevelled.layout.ButtonPanel;
92  import org.dishevelled.layout.LabelFieldPanel;
93  
94  import org.dishevelled.piccolo.venn.BinaryVennNode;
95  import org.dishevelled.piccolo.venn.TernaryVennNode;
96  import org.dishevelled.piccolo.venn.QuaternaryVennNode;
97  import org.dishevelled.piccolo.venn.VennNode;
98  
99  import org.dishevelled.venn.VennModel;
100 import org.dishevelled.venn.VennLayout;
101 import org.dishevelled.venn.VennLayouter;
102 import org.dishevelled.venn.VennLayouter.PerformanceHint;
103 
104 import org.dishevelled.venn.cytoscape.CyGroupListCellRenderer;
105 import org.dishevelled.venn.model.VennModels;
106 
107 import org.dishevelled.venn.swing.BinaryVennList;
108 import org.dishevelled.venn.swing.TernaryVennList;
109 import org.dishevelled.venn.swing.QuaternaryVennList;
110 
111 import org.cytoscape.venneuler.VennEulerLayouter;
112 
113 /**
114  * Groups view.
115  */
116 final class GroupsView
117     extends JPanel
118     implements CyGroupChangeListener, CyGroupViewer
119 {
120     /** List of groups. */
121     private final EventList<CyGroup> groups;
122 
123     /** List selection. */
124     private final EventList<CyGroup> selected;
125 
126     /** List of groups. */
127     private final JList groupList;
128 
129     /** Context menu. */
130     private final JPopupMenu contextMenu;
131 
132     /** Euler diagram action icon bundle. */
133     private final IconBundle eulerDiagramIconBundle = new CachingIconBundle(new PNGIconBundle("/org/dishevelled/venn/cytoscape/eulerDiagram"));
134 
135     /** Euler diagram action. */
136     private final IdentifiableAction eulerDiagram = new IdentifiableAction("Euler Diagram...", eulerDiagramIconBundle) // i18n
137         {
138             /** {@inheritDoc} */
139             public void actionPerformed(final ActionEvent event)
140             {
141                 if (selected.size() > 1)
142                 {
143                     eulerDiagram();
144                 }
145             }
146         };
147 
148     /** Venn diagram action icon bundle. */
149     private final IconBundle vennDiagramIconBundle = new CachingIconBundle(new PNGIconBundle("/org/dishevelled/venn/cytoscape/vennDiagram"));
150 
151     /** Venn diagram action. */
152     private final IdentifiableAction vennDiagram = new IdentifiableAction("Venn Diagram...", vennDiagramIconBundle) // i18n
153         {
154             /** {@inheritDoc} */
155             public void actionPerformed(final ActionEvent event)
156             {
157                 switch (selected.size())
158                 {
159                     case 2:
160                         binaryDiagram();
161                         break;
162                     case 3:
163                         ternaryDiagram();
164                         break;
165                     case 4:
166                         quaternaryDiagram();
167                         break;
168                     default:
169                         break;
170                 }
171             }
172         };
173 
174     /** Details action icon bundle. */
175     private final IconBundle detailsIconBundle = new CachingIconBundle(new PNGIconBundle("/org/dishevelled/venn/cytoscape/details"));
176 
177     /** Details action. */
178     private final IdentifiableAction details = new IdentifiableAction("Details...", detailsIconBundle) // i18n
179         {
180             /** {@inheritDoc} */
181             public void actionPerformed(final ActionEvent event)
182             {
183                 switch (selected.size())
184                 {
185                     case 2:
186                         binaryDetails();
187                         break;
188                     case 3:
189                         ternaryDetails();
190                         break;
191                     case 4:
192                         quaternaryDetails();
193                         break;
194                     default:
195                         break;
196                 }
197             }
198         };
199 
200     /** Done action. */
201     private final Action done = new AbstractAction("Done") // i18n
202         {
203             /** {@inheritDoc} */
204             public void actionPerformed(final ActionEvent event)
205             {
206                 done();
207             }
208         };
209 
210     /** List selection listener. */
211     private final ListSelectionListener listSelectionListener = new ListSelectionListener()
212         {
213             /** {@inheritDoc} */
214             public void valueChanged(final ListSelectionEvent event)
215             {
216                 int size = selected.size();
217                 eulerDiagram.setEnabled(size > 1);
218                 vennDiagram.setEnabled(size > 1 && size < 5);
219                 details.setEnabled(size > 1 && size < 5);
220             }
221         };
222 
223     /** Venn euler layouter. */
224     private final VennLayouter<CyNode> vennLayouter = new VennEulerLayouter<CyNode>();
225 
226 
227     /**
228      * Create a new groups view.
229      */
230     GroupsView()
231     {
232         super();
233 
234         groups = GlazedLists.eventList(CyGroupManager.getGroupList());
235         EventListModel<CyGroup> listModel = new EventListModel<CyGroup>(groups);
236         EventSelectionModel<CyGroup> selectionModel = new EventSelectionModel<CyGroup>(groups);
237         selected = selectionModel.getSelected();
238         selectionModel.addListSelectionListener(listSelectionListener); // or use event list listener
239         groupList = new JList(listModel);
240         groupList.setSelectionModel(selectionModel);
241         groupList.setCellRenderer(new CyGroupListCellRenderer());
242 
243         InputMap inputMap = getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
244         int menuKeyMask = Toolkit.getDefaultToolkit().getMenuShortcutKeyMask();
245         KeyStroke ctrlShiftE = KeyStroke.getKeyStroke(KeyEvent.VK_E, menuKeyMask | InputEvent.SHIFT_DOWN_MASK);
246         KeyStroke ctrlShiftV = KeyStroke.getKeyStroke(KeyEvent.VK_V, menuKeyMask | InputEvent.SHIFT_DOWN_MASK);
247         KeyStroke ctrlShiftD = KeyStroke.getKeyStroke(KeyEvent.VK_D, menuKeyMask | InputEvent.SHIFT_DOWN_MASK);
248         inputMap.put(ctrlShiftE, "eulerDiagram");
249         inputMap.put(ctrlShiftV, "vennDiagram");
250         inputMap.put(ctrlShiftD, "details");
251         getActionMap().put("eulerDiagram", eulerDiagram);
252         getActionMap().put("vennDiagram", vennDiagram);
253         getActionMap().put("details", details);
254 
255         IdMenuItem eulerDiagramMenuItem = new IdMenuItem(eulerDiagram);
256         eulerDiagramMenuItem.setAccelerator(ctrlShiftE);
257         IdMenuItem vennDiagramMenuItem = new IdMenuItem(vennDiagram);
258         vennDiagramMenuItem.setAccelerator(ctrlShiftV);
259         IdMenuItem detailsMenuItem = new IdMenuItem(details);
260         detailsMenuItem.setAccelerator(ctrlShiftD);
261 
262         contextMenu = new JPopupMenu();
263         contextMenu.add(eulerDiagramMenuItem);
264         contextMenu.add(vennDiagramMenuItem);
265         contextMenu.add(detailsMenuItem);
266         groupList.addMouseListener(new ContextMenuListener(contextMenu));
267 
268         eulerDiagram.setEnabled(false);
269         vennDiagram.setEnabled(false);
270         details.setEnabled(false);
271 
272         layoutComponents();
273         CyGroupManager.registerGroupViewer(this);
274         CyGroupManager.addGroupChangeListener(this);
275     }
276 
277     /**
278      * Layout components.
279      */
280     private void layoutComponents()
281     {
282         LabelFieldPanel mainPanel = new LabelFieldPanel();
283         mainPanel.setBorder(new EmptyBorder(12, 12, 0, 12));
284         mainPanel.addLabel("Groups:"); // i18n
285         mainPanel.addFinalField(new JScrollPane(groupList));
286 
287         IdToolBar toolBar = new IdToolBar();
288         IdButton eulerDiagramButton = toolBar.add(eulerDiagram);
289         eulerDiagramButton.setBorderPainted(false);
290         eulerDiagramButton.setFocusPainted(false);
291         IdButton vennDiagramButton = toolBar.add(vennDiagram);
292         vennDiagramButton.setBorderPainted(false);
293         vennDiagramButton.setFocusPainted(false);
294         IdButton detailsButton = toolBar.add(details);
295         detailsButton.setBorderPainted(false);
296         detailsButton.setFocusPainted(false);
297 
298         toolBar.displayIcons();
299         toolBar.setIconSize(IconSize.DEFAULT_24X24);
300 
301         JPopupMenu toolBarContextMenu = new JPopupMenu();
302         for (Object menuItem : toolBar.getDisplayMenuItems())
303         {
304             toolBarContextMenu.add((JCheckBoxMenuItem) menuItem);
305         }
306         toolBar.addMouseListener(new ContextMenuListener(toolBarContextMenu));
307 
308         ButtonPanel buttonPanel = new ButtonPanel();
309         buttonPanel.setBorder(new EmptyBorder(24, 12, 12, 12));
310         buttonPanel.add(done);
311 
312         setLayout(new BorderLayout());
313         add("North", toolBar);
314         add("Center", mainPanel);
315         add("South", buttonPanel);
316     }
317 
318     /**
319      * Done.
320      */
321     private void done()
322     {
323         windowForComponent(this).setVisible(false);
324         // todo:  unregister listeners and dispose?
325     }
326 
327     /**
328      * Show a euler diagram.
329      */
330     private void eulerDiagram()
331     {
332         List<String> labels = new ArrayList<String>(selected.size());
333         List<Set<CyNode>> sets = new ArrayList<Set<CyNode>>(selected.size());
334         for (CyGroup selectedGroup : selected)
335         {
336             labels.add(nameOf(selectedGroup));
337             sets.add(new HashSet<CyNode>(selectedGroup.getNodes()));
338         }
339         final VennModel<CyNode> model = VennModels.createVennModel(sets);
340         final VennNode<CyNode> vennNode = new VennNode<CyNode>(model);
341         // add ctr that takes List<String> labels as parameter?
342         for (int i = 0, size = labels.size(); i < size; i++)
343         {
344             vennNode.setLabelText(i, labels.get(i));
345         }
346 
347         JDialog dialog = new JDialog(windowForComponent(this), Joiner.on(", ").join(labels) + " Euler Diagram");
348         dialog.setContentPane(new DiagramView(vennNode));
349         dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
350         installCloseKeyBinding(dialog);
351 
352         // todo: offset per parent dialog
353         dialog.setBounds(100, 100, 600, 600);
354         if (model.size() > 4) {
355             dialog.setBounds(100, 100, 800, 800);
356         }
357         dialog.setVisible(true);
358 
359         // run in a cytoscape task?
360         SwingUtilities.invokeLater(new Runnable()
361             {
362                 /** {@inheritDoc} */
363                 public void run()
364                 {
365                     Rectangle2D.Double boundingRectangle = new Rectangle2D.Double(0.0d, 0.0d, 400.0d, 400.0d);
366                     if (model.size() > 4) {
367                         boundingRectangle.setRect(0.0d, 0.0d, 600.0d, 600.0d);
368                     }
369                     VennLayout layout = vennLayouter.layout(model, boundingRectangle, PerformanceHint.OPTIMIZE_FOR_SPEED);
370                     vennNode.setLayout(layout);
371                 }
372             });
373     }
374 
375     /**
376      * Show a binary venn diagram.
377      */
378     private void binaryDiagram()
379     {
380         String firstLabel = selected.get(0).getGroupName();
381         String secondLabel = selected.get(1).getGroupName();
382         Set<CyNode> first = new HashSet<CyNode>(selected.get(0).getNodes());
383         Set<CyNode> second = new HashSet<CyNode>(selected.get(1).getNodes());
384         BinaryVennNode<CyNode> binaryVennNode = new BinaryVennNode<CyNode>(firstLabel, first, secondLabel, second);
385 
386         JDialog dialog = new JDialog(windowForComponent(this), firstLabel + ", " + secondLabel + " Venn Diagram");
387         dialog.setContentPane(new DiagramView(binaryVennNode));
388         dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
389         installCloseKeyBinding(dialog);
390 
391         // todo: offset per parent dialog
392         dialog.setBounds(100, 100, 400, 400);
393         dialog.setVisible(true);
394     }
395 
396     /**
397      * Show a ternary venn diagram.
398      */
399     private void ternaryDiagram()
400     {
401         String firstLabel = selected.get(0).getGroupName();
402         String secondLabel = selected.get(1).getGroupName();
403         String thirdLabel = selected.get(2).getGroupName();
404         Set<CyNode> first = new HashSet<CyNode>(selected.get(0).getNodes());
405         Set<CyNode> second = new HashSet<CyNode>(selected.get(1).getNodes());
406         Set<CyNode> third = new HashSet<CyNode>(selected.get(2).getNodes());
407         TernaryVennNode<CyNode> ternaryVennNode = new TernaryVennNode<CyNode>(firstLabel, first, secondLabel, second, thirdLabel, third);
408 
409         JDialog dialog = new JDialog(windowForComponent(this), firstLabel + ", " + secondLabel + ", " + thirdLabel + " Venn Diagram");
410         dialog.setContentPane(new DiagramView(ternaryVennNode));
411         dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
412         installCloseKeyBinding(dialog);
413 
414         // todo: offset per parent dialog
415         dialog.setBounds(100, 100, 400, 400);
416         dialog.setVisible(true);
417     }
418 
419     /**
420      * Show a quaternary venn diagram.
421      */
422     private void quaternaryDiagram()
423     {
424         String firstLabel = selected.get(0).getGroupName();
425         String secondLabel = selected.get(1).getGroupName();
426         String thirdLabel = selected.get(2).getGroupName();
427         String fourthLabel = selected.get(3).getGroupName();
428         Set<CyNode> first = new HashSet<CyNode>(selected.get(0).getNodes());
429         Set<CyNode> second = new HashSet<CyNode>(selected.get(1).getNodes());
430         Set<CyNode> third = new HashSet<CyNode>(selected.get(2).getNodes());
431         Set<CyNode> fourth = new HashSet<CyNode>(selected.get(3).getNodes());
432         QuaternaryVennNode<CyNode> quaternaryVennNode = new QuaternaryVennNode<CyNode>(firstLabel, first, secondLabel, second, thirdLabel, third, fourthLabel, fourth);
433 
434         JDialog dialog = new JDialog(windowForComponent(this), firstLabel + ", " + secondLabel + ", " + thirdLabel + ", " + fourthLabel + " Venn Diagram");
435         dialog.setContentPane(new DiagramView(quaternaryVennNode));
436         dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
437         installCloseKeyBinding(dialog);
438 
439         // todo: offset per parent dialog
440         dialog.setBounds(100, 100, 600, 600);
441         dialog.setVisible(true);
442     }
443 
444     /**
445      * Show binary venn details.
446      */
447     private void binaryDetails()
448     {
449         String firstLabel = selected.get(0).getGroupName();
450         String secondLabel = selected.get(1).getGroupName();
451         Set<CyNode> first = new HashSet<CyNode>(selected.get(0).getNodes());
452         Set<CyNode> second = new HashSet<CyNode>(selected.get(1).getNodes());
453         final BinaryVennList<CyNode> binaryVennList = new BinaryVennList<CyNode>(firstLabel, first, secondLabel, second);
454 
455         JDialog dialog = new JDialog(windowForComponent(this), firstLabel + ", " + secondLabel + " Details");
456         dialog.setContentPane(new DetailsView(binaryVennList));
457         dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
458         installCloseKeyBinding(dialog);
459 
460         // todo: offset per parent dialog
461         dialog.setBounds(100, 100, 600, 450);
462         dialog.setVisible(true);
463     }
464 
465     /**
466      * Show ternary venn details.
467      */
468     private void ternaryDetails()
469     {
470         String firstLabel = selected.get(0).getGroupName();
471         String secondLabel = selected.get(1).getGroupName();
472         String thirdLabel = selected.get(2).getGroupName();
473         Set<CyNode> first = new HashSet<CyNode>(selected.get(0).getNodes());
474         Set<CyNode> second = new HashSet<CyNode>(selected.get(1).getNodes());
475         Set<CyNode> third = new HashSet<CyNode>(selected.get(2).getNodes());
476         final TernaryVennList<CyNode> ternaryVennList = new TernaryVennList<CyNode>(firstLabel, first, secondLabel, second, thirdLabel, third);
477 
478         JDialog dialog = new JDialog(windowForComponent(this), firstLabel + ", " + secondLabel + ", " + thirdLabel + " Details");
479         dialog.setContentPane(new DetailsView(ternaryVennList));
480         dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
481         installCloseKeyBinding(dialog);
482 
483         // todo: offset per parent dialog
484         dialog.setBounds(100, 100, 747, 669);
485         dialog.setVisible(true);
486     }
487 
488     /**
489      * Show quaternary venn details.
490      */
491     private void quaternaryDetails()
492     {
493         String firstLabel = selected.get(0).getGroupName();
494         String secondLabel = selected.get(1).getGroupName();
495         String thirdLabel = selected.get(2).getGroupName();
496         String fourthLabel = selected.get(3).getGroupName();
497         Set<CyNode> first = new HashSet<CyNode>(selected.get(0).getNodes());
498         Set<CyNode> second = new HashSet<CyNode>(selected.get(1).getNodes());
499         Set<CyNode> third = new HashSet<CyNode>(selected.get(2).getNodes());
500         Set<CyNode> fourth = new HashSet<CyNode>(selected.get(3).getNodes());
501         final QuaternaryVennList<CyNode> quaternaryVennList = new QuaternaryVennList<CyNode>(firstLabel, first, secondLabel, second, thirdLabel, third, fourthLabel, fourth);
502 
503         JDialog dialog = new JDialog(windowForComponent(this), firstLabel + ", " + secondLabel + ", " + thirdLabel + ", " + fourthLabel + " Details");
504         dialog.setContentPane(new DetailsView(quaternaryVennList));
505         dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
506         installCloseKeyBinding(dialog);
507 
508         // todo: offset per parent dialog
509         dialog.setBounds(100, 100, 894, 888);
510         dialog.setVisible(true);
511     }
512 
513     /** {@inheritDoc} */
514     public String getViewerName()
515     {
516         return "Venn/Euler Diagrams";
517     }
518 
519     /** {@inheritDoc} */
520     public void groupChanged(final CyGroup group, final CyNode changedNode, CyGroupViewer.ChangeType change)
521     {
522         // ignore for now
523     }
524 
525     /** {@inheritDoc} */
526     public void groupCreated(final CyGroup group)
527     {
528         if (!groups.contains(group))
529         {
530             groups.add(group);
531         }
532     }
533 
534     /** {@inheritDoc} */
535     public void groupCreated(final CyGroup group, final CyNetworkView networkView)
536     {
537         if (!groups.contains(group))
538         {
539             groups.add(group);
540         }
541     }
542 
543     /** {@inheritDoc} */
544     public void groupWillBeRemoved(final CyGroup group)
545     {
546         if (groups.contains(group))
547         {
548             groups.remove(group);
549         }
550     }
551 
552     /** {@inheritDoc} */
553     public void groupChanged(final CyGroup group, final CyGroupChangeListener.ChangeType change)
554     {
555         switch (change)
556         {
557             case GROUP_CREATED:
558                 if (!groups.contains(group))
559                 {
560                     groups.add(group);
561                 }
562                 break;
563             case GROUP_DELETED:
564                 if (groups.contains(group))
565                 {
566                     groups.remove(group);
567                 }
568                 break;
569             case GROUP_MODIFIED:
570             default:
571                 break;
572         }
573     }
574 }