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.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
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 private final CyApplicationManager applicationManager;
234
235
236
237
238
239
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
327
328
329
330
331 DiagramView(final BinaryVennNode<CyNode> binaryVennNode, final CyApplicationManager applicationManager)
332 {
333 this(applicationManager);
334
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
346
347
348
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
364
365
366
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
382
383
384
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
401
402
403
404
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
427
428
429
430
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
453
454 private void exportToPNG()
455 {
456 Image image = canvas.getLayer().toImage();
457
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
473 }
474 }
475 }
476
477
478
479
480 private void exportToSVG()
481 {
482
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
504 }
505 finally
506 {
507 try
508 {
509 writer.close();
510 }
511 catch (Exception e)
512 {
513
514 }
515 }
516 }
517 }
518
519
520
521
522
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
539
540
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
557
558
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
575
576
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
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
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
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
652
653 private void zoomIn()
654 {
655 PCamera camera = canvas.getCamera();
656 double scale = 1.0d + 4.0d * SCALE_FACTOR;
657
658 Point2D center = camera.getBoundsReference().getCenter2D();
659 camera.scaleViewAboutPoint(scale, center.getX(), center.getY());
660 }
661
662
663
664
665 private void zoomOut()
666 {
667 PCamera camera = canvas.getCamera();
668 double scale = 1.0d - 2.0d * SCALE_FACTOR;
669
670 Point2D center = camera.getBoundsReference().getCenter2D();
671 camera.scaleViewAboutPoint(scale, center.getX(), center.getY());
672 }
673
674
675
676
677 private class ToolTipTextListener
678 extends PBasicInputEventHandler
679 {
680
681
682
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
695 public void mouseEntered(final PInputEvent event)
696 {
697 PCanvas canvas = (PCanvas) event.getComponent();
698 canvas.setToolTipText(getLabelTextForPickedNode(event.getPath()));
699 }
700
701
702 public void mouseExited(final PInputEvent event)
703 {
704 PCanvas canvas = (PCanvas) event.getComponent();
705 canvas.setToolTipText(null);
706 }
707 }
708
709
710
711
712 private class MousePressedListener
713 extends PBasicInputEventHandler
714 {
715
716
717
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
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
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
767
768 private class ModeEventHandler
769 extends KeyAdapter
770 {
771
772
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
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
795
796 private class PanEventHandler
797 extends PPanEventHandler
798 {
799
800
801
802
803 PanEventHandler()
804 {
805 super();
806
807
808 setEventFilter(new PInputEventFilter(InputEvent.BUTTON1_MASK)
809 {
810
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 }