1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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
100
101 final class DiagramView
102 extends JPanel
103 {
104
105 private static final String SVG_NS = "http://www.w3.org/2000/svg";
106
107
108 private final PCanvas canvas;
109
110
111 private final Action exportToPNG = new AbstractAction("Export to PNG...")
112 {
113
114 public void actionPerformed(final ActionEvent event)
115 {
116 exportToPNG();
117 }
118 };
119
120
121 private final Action exportToSVG = new AbstractAction("Export to SVG...")
122 {
123
124 public void actionPerformed(final ActionEvent event)
125 {
126 exportToSVG();
127 }
128 };
129
130
131 private final Action selectAll = new AbstractAction("Select all")
132 {
133
134 public void actionPerformed(final ActionEvent event)
135 {
136 selectAll();
137 }
138 };
139
140
141 private final Action clearSelection = new AbstractAction("Clear selection")
142 {
143
144 public void actionPerformed(final ActionEvent event)
145 {
146 clearSelection();
147 }
148 };
149
150
151 private final Action zoomIn = new AbstractAction("Zoom in")
152 {
153
154 public void actionPerformed(final ActionEvent event)
155 {
156 zoomIn();
157 }
158 };
159
160
161 private final Action zoomOut = new AbstractAction("Zoom out")
162 {
163
164 public void actionPerformed(final ActionEvent event)
165 {
166 zoomOut();
167 }
168 };
169
170
171 private final Action displayLabels = new AbstractAction("Display set labels")
172 {
173
174 public void actionPerformed(final ActionEvent event)
175 {
176 displayLabels(((AbstractButton) event.getSource()).isSelected());
177 }
178 };
179
180
181 private final Action displaySizeLabels = new AbstractAction("Display size labels")
182 {
183
184 public void actionPerformed(final ActionEvent event)
185 {
186 displaySizeLabels(((AbstractButton) event.getSource()).isSelected());
187 }
188 };
189
190
191 private final Action displaySizes = new AbstractAction("Display sizes in set labels")
192 {
193
194 public void actionPerformed(final ActionEvent event)
195 {
196 displaySizes(((AbstractButton) event.getSource()).isSelected());
197 }
198 };
199
200
201 private final Action displaySizesForEmptyAreas = new AbstractAction("Display sizes for empty areas")
202 {
203
204 public void actionPerformed(final ActionEvent event)
205 {
206 displaySizesForEmptyAreas(((AbstractButton) event.getSource()).isSelected());
207 }
208 };
209
210
211 private static final Color AREA_COLOR = new Color(0, 0, 0, 0);
212
213
214 private static final Paint AREA_PRESSED_PAINT = new Color(20, 20, 20, 80);
215
216
217 private static final double SCALE_FACTOR = 0.1d;
218
219
220 private Mode mode = Mode.EDIT;
221
222
223 private enum Mode
224 {
225
226 EDIT,
227
228
229 PAN
230 };
231
232
233
234
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
316
317
318
319 DiagramView(final BinaryVennNode<CyNode> binaryVennNode)
320 {
321 this();
322
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
334
335
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
351
352
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
368
369
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
386
387
388
389
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
412
413
414
415
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
438
439 private void exportToPNG()
440 {
441 Image image = canvas.getLayer().toImage();
442
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
458 }
459 }
460 }
461
462
463
464
465 private void exportToSVG()
466 {
467
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
489 }
490 finally
491 {
492 try
493 {
494 writer.close();
495 }
496 catch (Exception e)
497 {
498
499 }
500 }
501 }
502 }
503
504
505
506
507
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
524
525
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
542
543
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
560
561
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
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
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
616
617 private void clearSelection()
618 {
619 CyNetwork currentNetwork = Cytoscape.getCurrentNetwork();
620 currentNetwork.unselectAllNodes();
621 Cytoscape.getCurrentNetworkView().updateView();
622 }
623
624
625
626
627 private void zoomIn()
628 {
629 PCamera camera = canvas.getCamera();
630 double scale = 1.0d + 4.0d * SCALE_FACTOR;
631
632 Point2D center = camera.getBoundsReference().getCenter2D();
633 camera.scaleViewAboutPoint(scale, center.getX(), center.getY());
634 }
635
636
637
638
639 private void zoomOut()
640 {
641 PCamera camera = canvas.getCamera();
642 double scale = 1.0d - 2.0d * SCALE_FACTOR;
643
644 Point2D center = camera.getBoundsReference().getCenter2D();
645 camera.scaleViewAboutPoint(scale, center.getX(), center.getY());
646 }
647
648
649
650
651 private class ToolTipTextListener
652 extends PBasicInputEventHandler
653 {
654
655
656
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
669 public void mouseEntered(final PInputEvent event)
670 {
671 PCanvas canvas = (PCanvas) event.getComponent();
672 canvas.setToolTipText(getLabelTextForPickedNode(event.getPath()));
673 }
674
675
676 public void mouseExited(final PInputEvent event)
677 {
678 PCanvas canvas = (PCanvas) event.getComponent();
679 canvas.setToolTipText(null);
680 }
681 }
682
683
684
685
686 private class MousePressedListener
687 extends PBasicInputEventHandler
688 {
689
690
691
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
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
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
739
740 private class ModeEventHandler
741 extends KeyAdapter
742 {
743
744
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
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
767
768 private class PanEventHandler
769 extends PPanEventHandler
770 {
771
772
773
774
775 PanEventHandler()
776 {
777 super();
778
779
780 setEventFilter(new PInputEventFilter(InputEvent.BUTTON1_MASK)
781 {
782
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 }