1 package jalview.ext.archaeopteryx;
3 import jalview.analysis.Conservation;
4 import jalview.api.AlignViewportI;
5 import jalview.datamodel.ColumnSelection;
6 import jalview.datamodel.HiddenColumns;
7 import jalview.datamodel.SequenceGroup;
8 import jalview.datamodel.SequenceI;
9 import jalview.ext.treeviewer.ExternalTreeViewerBindingI;
10 import jalview.gui.AlignViewport;
11 import jalview.gui.AlignmentPanel;
12 import jalview.gui.Desktop;
13 import jalview.gui.JvOptionPane;
14 import jalview.gui.PaintRefresher;
15 import jalview.schemes.ColourSchemeI;
16 import jalview.schemes.ColourSchemeProperty;
17 import jalview.schemes.UserColourScheme;
18 import jalview.structure.SelectionSource;
19 import jalview.structure.StructureSelectionManager;
20 import jalview.util.MappingUtils;
21 import jalview.util.MessageManager;
22 import jalview.viewmodel.AlignmentViewport;
24 import java.awt.Color;
25 import java.awt.event.ActionEvent;
26 import java.awt.event.InputEvent;
27 import java.awt.event.MouseEvent;
28 import java.util.ArrayList;
29 import java.util.HashSet;
30 import java.util.List;
34 import javax.swing.JTabbedPane;
35 import javax.swing.SwingUtilities;
36 import javax.swing.event.ChangeEvent;
37 import javax.swing.event.ChangeListener;
38 import javax.swing.event.InternalFrameAdapter;
39 import javax.swing.event.InternalFrameEvent;
41 import org.forester.archaeopteryx.MainFrame;
42 import org.forester.phylogeny.Phylogeny;
43 import org.forester.phylogeny.PhylogenyMethods;
44 import org.forester.phylogeny.PhylogenyNode;
45 import org.forester.phylogeny.data.BranchColor;
48 * Class for binding the Archaeopteryx tree viewer to the Jalview alignment that
49 * it originates from, meaning that selecting sequences in the tree viewer also
50 * selects them in the alignment view and vice versa.
52 * @author kjvanderheide
55 public final class JalviewBinding
56 implements ExternalTreeViewerBindingI<PhylogenyNode>
58 private org.forester.archaeopteryx.TreePanel treeView;
60 private AlignmentViewport parentAvport;
62 private final JTabbedPane treeTabs;
64 private final StructureSelectionManager ssm;
66 private AlignmentPanel[] associatedPanels;
68 private Map<SequenceI, PhylogenyNode> sequencesBoundToNodes;
70 private Map<PhylogenyNode, SequenceI> nodesBoundToSequences;
74 private float furthestNodeX;
76 private int nrTreeGroups = 0;
78 private boolean applyToAllViews = false;
82 * @param archaeopteryx
84 * @param jalviewAlignmentViewport
85 * alignment viewport from which the tree was calculated.
87 * @param alignMappedToNodes
88 * map with sequences used to calculate the tree and matching tree
89 * nodes as key, value pair respectively.
91 * @param nodesMappedToAlign
92 * map with tree nodes and matching sequences used to calculate the
93 * tree as key, value pair respectively.
95 public JalviewBinding(final MainFrame archaeopteryx,
96 final AlignmentViewport jalviewAlignmentViewport,
97 final Map<SequenceI, PhylogenyNode> alignMappedToNodes,
98 final Map<PhylogenyNode, SequenceI> nodesMappedToAlign)
101 if (archaeopteryx.getMainPanel().getTabbedPane().getTabCount() > 1)
103 JvOptionPane.showMessageDialog(Desktop.desktop,
104 MessageManager.getString("label.tabs_detected_archaeopteryx"),
105 MessageManager.getString("label.problem_reading_tree_file"),
106 JvOptionPane.WARNING_MESSAGE);
110 // deal with/prohibit null values here as that will cause problems
111 parentAvport = jalviewAlignmentViewport;
112 sequencesBoundToNodes = alignMappedToNodes;
113 nodesBoundToSequences = nodesMappedToAlign;
115 treeView = archaeopteryx.getMainPanel().getCurrentTreePanel();
116 treeTabs = archaeopteryx.getMainPanel().getTabbedPane();
117 ssm = parentAvport.getStructureSelectionManager();
119 ssm.addSelectionListener(this);
120 treeView.addMouseListener(this);
121 PaintRefresher.Register(treeView, parentAvport.getSequenceSetId());
122 associatedPanels = PaintRefresher
123 .getAssociatedPanels(parentAvport.getSequenceSetId());
125 archaeopteryx.addInternalFrameListener(new InternalFrameAdapter()
129 public void internalFrameClosed(InternalFrameEvent e)
131 AptxInit.getAllAptxFrames().remove(archaeopteryx);
136 treeTabs.addChangeListener(new ChangeListener()
140 public void stateChanged(ChangeEvent e)
143 SwingUtilities.invokeLater(new Runnable()
148 * Resend the selection to the tree view when tabs get switched, this
149 * has to be buried in invokeLater as Forester first resets the tree
150 * view on switching tabs, without invokeLater this would get called
151 * before Forester resets which would nullify the selection.
155 treeView = archaeopteryx.getMainPanel().getCurrentTreePanel();
156 parentAvport.sendSelection();
157 // PaintRefresher.Refresh(treeView,
158 // parentAvport.getSequenceSetId());
170 public void actionPerformed(ActionEvent e)
175 public void mouseClicked(MouseEvent e)
177 SwingUtilities.invokeLater(new Runnable() {
181 * invokeLater so that this always runs after Forester's mouseClicked
185 final PhylogenyNode node = treeView.findNode(e.getX(), e.getY());
188 if ((e.getModifiers() & InputEvent.SHIFT_MASK) == 0) // clear previous
189 // selection if shift
192 parentAvport.setSelectionGroup(null);
195 showNodeSelectionOnAlign(node);
200 partitionTree(e.getX());
202 PaintRefresher.Refresh(treeView, parentAvport.getSequenceSetId());
214 public void mousePressed(final MouseEvent e)
219 public void mouseReleased(MouseEvent e)
224 public void mouseEntered(MouseEvent e)
229 public void mouseExited(MouseEvent e)
235 public void selection(final SequenceGroup seqsel,
236 final ColumnSelection colsel, final HiddenColumns hidden,
237 final SelectionSource source)
239 if (source == parentAvport) // check if source is alignment from where the
242 treeView.setFoundNodes0(
243 new HashSet<Long>(seqsel.getSequences().size()));
245 for (SequenceI selectedSequence : seqsel.getSequences())
247 PhylogenyNode matchingNode = sequencesBoundToNodes.get(selectedSequence);
248 if (matchingNode != null)
250 treeView.getFoundNodes0().add(matchingNode.getId());
253 if (!matchingNode.getBranchData().isHasBranchColor())
255 Color foundNodesColour = treeView.getTreeColorSet()
257 matchingNode.getBranchData()
258 .setBranchColor(new BranchColor(foundNodesColour));
273 * Partially refactored from TreeCanvas
275 public void partitionTree(final int x)
277 Phylogeny tree = treeView.getPhylogeny();
281 // should be calculated on each partition as the tree can theoretically
282 // change in the meantime
283 PhylogenyNode furthestNode = PhylogenyMethods
284 .calculateNodeWithMaxDistanceToRoot(tree);
285 furthestNodeX = furthestNode.getXcoord();
286 rootX = tree.getRoot().getXcoord();
288 if (furthestNodeX != rootX && !(x < rootX || x > furthestNodeX)) // don't
292 // clicked x lies outside
296 float threshold = (x - rootX) / (furthestNodeX - rootX);
297 List<PhylogenyNode> foundNodes = getNodesAboveThreshold(threshold,
306 public List<PhylogenyNode> getNodesAboveThreshold(double threshold,
310 List<PhylogenyNode> nodesAboveThreshold = new ArrayList<>();
312 parentAvport.setSelectionGroup(null);
313 parentAvport.getAlignment().deleteAllGroups();
314 parentAvport.clearSequenceColours();
315 if (parentAvport.getCodingComplement() != null)
317 parentAvport.getCodingComplement().setSelectionGroup(null);
318 parentAvport.getCodingComplement().getAlignment().deleteAllGroups();
319 parentAvport.getCodingComplement().clearSequenceColours();
323 colourNodesAboveThreshold(nodesAboveThreshold, threshold,
325 return nodesAboveThreshold;
330 * Partially refactored from TreeCanvas colourGroups (can be made nicer).
338 private List<PhylogenyNode> colourNodesAboveThreshold(
339 List<PhylogenyNode> nodeList, double threshold,
343 for (PhylogenyNode childNode : node.getDescendants())
345 childNode.getBranchData()
346 .setBranchColor(new BranchColor(Color.black));
347 float nodeCutoff = (childNode.getXcoord() - rootX)
348 / (furthestNodeX - rootX);
350 if (nodeCutoff > threshold)
352 nodeList.add(childNode);
354 Color randomColour = new Color((int) (Math.random() * 255),
355 (int) (Math.random() * 255), (int) (Math.random() * 255));
356 childNode.getBranchData()
357 .setBranchColor(new BranchColor(randomColour));
359 List<SequenceI> groupSeqs = new ArrayList<>();
360 SequenceI seq = nodesBoundToSequences.get(childNode);
364 parentAvport.setSequenceColour(seq, randomColour);
367 List<PhylogenyNode> descendantNodes = PhylogenyMethods
368 .getAllDescendants(childNode);
370 for (PhylogenyNode descNode : descendantNodes)
372 seq = nodesBoundToSequences.get(descNode);
376 parentAvport.setSequenceColour(seq, randomColour);
379 descNode.getBranchData()
380 .setBranchColor(new BranchColor(randomColour));
383 if (groupSeqs != null)
386 groupThresholdSequences(groupSeqs, randomColour);
391 colourNodesAboveThreshold(nodeList, threshold, childNode);
396 for (AlignmentPanel associatedPanel : associatedPanels) {
398 associatedPanel.updateAnnotation();
400 final AlignViewportI codingComplement = associatedPanel.getAlignViewport()
401 .getCodingComplement();
402 if (codingComplement != null)
405 ((AlignViewport) codingComplement).getAlignPanel()
414 public void groupThresholdSequences(List<SequenceI> groupedSeqs,
417 SequenceGroup treeGroup = new SequenceGroup(groupedSeqs, null, null,
418 true, true, false, 0,
419 parentAvport.getAlignment().getWidth() - 1);
421 ColourSchemeI cs = null;
422 if (parentAvport.getGlobalColourScheme() != null)
424 if (parentAvport.getGlobalColourScheme() instanceof UserColourScheme)
426 cs = new UserColourScheme(
427 ((UserColourScheme) parentAvport.getGlobalColourScheme())
432 cs = ColourSchemeProperty.getColourScheme(treeGroup,
433 ColourSchemeProperty.getColourName(
434 parentAvport.getGlobalColourScheme()));
438 treeGroup.setColourScheme(cs);
439 treeGroup.getGroupColourScheme().setThreshold(
440 parentAvport.getResidueShading().getThreshold(),
441 parentAvport.isIgnoreGapsConsensus());
443 treeGroup.setName("Tree Group " + nrTreeGroups);
444 treeGroup.setIdColour(groupColour);
446 for (AlignmentPanel associatedPanel : associatedPanels)
448 AlignViewportI altViewport = associatedPanel
451 if (altViewport.getGlobalColourScheme() != null
452 && altViewport.getResidueShading()
453 .conservationApplied())
455 Conservation conserv = new Conservation(treeGroup.getName(),
456 treeGroup.getSequences(null), treeGroup.getStartRes(),
457 treeGroup.getEndRes());
459 conserv.verdict(false, altViewport.getConsPercGaps());
460 treeGroup.getGroupColourScheme().setConservation(conserv);
463 altViewport.getAlignment().addGroup(treeGroup);
464 // TODO can we push all of the below into AlignViewportI?
465 final AlignViewportI codingComplement = altViewport
466 .getCodingComplement();
467 if (codingComplement != null)
469 SequenceGroup mappedGroup = MappingUtils.mapSequenceGroup(treeGroup,
470 parentAvport, codingComplement);
471 if (mappedGroup.getSequences().size() > 0)
473 codingComplement.getAlignment().addGroup(mappedGroup);
474 for (SequenceI seq : mappedGroup.getSequences())
476 codingComplement.setSequenceColour(seq, groupColour.brighter());
486 * may or may not need an extra repaint on the alignment view (check what kira
490 public void showNodeSelectionOnAlign(final PhylogenyNode node)
493 if (node.isInternal())
495 showMatchingChildSequences(node);
500 showMatchingSequence(node);
511 public void showMatchingSequence(final PhylogenyNode nodeToMatch)
513 SequenceI matchingSequence = nodesBoundToSequences.get(nodeToMatch);
514 if (matchingSequence != null)
516 long nodeId = nodeToMatch.getId();
517 addOrRemoveInSet(treeView.getFoundNodes0(), nodeId);
518 treeSelectionChanged(matchingSequence);
519 parentAvport.sendSelection();
525 public void showMatchingChildSequences(final PhylogenyNode parentNode)
527 List<PhylogenyNode> childNodes = PhylogenyMethods
528 .getAllDescendants(parentNode);
531 for (PhylogenyNode childNode : childNodes)
533 // childNode.getBranchData().setBranchColor(new BranchColor(Color.BLUE));
535 SequenceI matchingSequence = nodesBoundToSequences.get(childNode);
536 if (matchingSequence != null)
538 long nodeId = childNode.getId();
539 addOrRemoveInSet(treeView.getFoundNodes0(), nodeId);
541 treeSelectionChanged(matchingSequence);
546 parentAvport.sendSelection();
552 * Refactored from TreeCanvas.
555 * of the node selected in the tree viewer.
558 public void treeSelectionChanged(final SequenceI sequence)
560 if (!parentAvport.isClosed()) // alignment view could be closed
562 SequenceGroup selected = parentAvport.getSelectionGroup();
564 if (selected == null)
566 selected = new SequenceGroup();
567 parentAvport.setSelectionGroup(selected);
570 selected.setEndRes(parentAvport.getAlignment().getWidth() - 1);
571 selected.addOrRemove(sequence, true);
575 public void sortByTree_actionPerformed() {
585 * @param objectToCheck
587 public static <E> void addOrRemoveInSet(Set<E> set, E objectToCheck)
589 if (set.contains(objectToCheck))
591 set.remove(objectToCheck);
595 set.add(objectToCheck);
600 public AlignmentViewport getParentAvport()
605 public void setParentAvport(final AlignmentViewport parentAvport)
607 this.parentAvport = parentAvport;
610 public AlignmentPanel[] getAssociatedPanels()
612 return associatedPanels;
615 public void setAssociatedPanels(AlignmentPanel[] associatedPanels)
617 this.associatedPanels = associatedPanels;