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;
39 import org.forester.archaeopteryx.MainFrame;
40 import org.forester.phylogeny.Phylogeny;
41 import org.forester.phylogeny.PhylogenyMethods;
42 import org.forester.phylogeny.PhylogenyNode;
43 import org.forester.phylogeny.data.BranchColor;
46 * Class for binding the Archaeopteryx tree viewer to the Jalview alignment that
47 * it originates from, meaning that selecting sequences in the tree viewer also
48 * selects them in the alignment view and vice versa.
50 * @author kjvanderheide
53 public final class JalviewBinding
54 implements ExternalTreeViewerBindingI<PhylogenyNode>
56 private org.forester.archaeopteryx.TreePanel treeView;
58 private AlignmentViewport parentAvport;
60 private final JTabbedPane treeTabs;
62 private final StructureSelectionManager ssm;
64 private AlignmentPanel[] associatedPanels;
66 private Map<SequenceI, PhylogenyNode> sequencesBoundToNodes;
68 private Map<PhylogenyNode, SequenceI> nodesBoundToSequences;
72 private float furthestNodeX;
74 private int nrTreeGroups = 0;
78 * @param archaeopteryx
80 * @param jalviewAlignmentViewport
81 * alignment viewport from which the tree was calculated.
83 * @param alignMappedToNodes
84 * map with sequences used to calculate the tree and matching tree
85 * nodes as key, value pair respectively.
87 * @param nodesMappedToAlign
88 * map with tree nodes and matching sequences used to calculate the
89 * tree as key, value pair respectively.
91 public JalviewBinding(final MainFrame archaeopteryx,
92 final AlignmentViewport jalviewAlignmentViewport,
93 final Map<SequenceI, PhylogenyNode> alignMappedToNodes,
94 final Map<PhylogenyNode, SequenceI> nodesMappedToAlign)
97 if (archaeopteryx.getMainPanel().getTabbedPane().getTabCount() > 1)
99 JvOptionPane.showMessageDialog(Desktop.desktop,
100 MessageManager.getString("label.tabs_detected_archaeopteryx"),
101 MessageManager.getString("label.problem_reading_tree_file"),
102 JvOptionPane.WARNING_MESSAGE);
106 // deal with/prohibit null values here as that will cause problems
107 parentAvport = jalviewAlignmentViewport;
108 sequencesBoundToNodes = alignMappedToNodes;
109 nodesBoundToSequences = nodesMappedToAlign;
111 treeView = archaeopteryx.getMainPanel().getCurrentTreePanel();
112 treeTabs = archaeopteryx.getMainPanel().getTabbedPane();
113 ssm = parentAvport.getStructureSelectionManager();
115 ssm.addSelectionListener(this);
116 treeView.addMouseListener(this);
117 PaintRefresher.Register(treeView, parentAvport.getSequenceSetId());
119 associatedPanels = PaintRefresher
120 .getAssociatedPanels(parentAvport.getSequenceSetId());
123 treeTabs.addChangeListener(new ChangeListener()
127 public void stateChanged(ChangeEvent e)
130 SwingUtilities.invokeLater(new Runnable()
135 * Resend the selection to the tree view when tabs get switched, this
136 * has to be buried in invokeLater as Forester first resets the tree
137 * view on switching tabs, without invokeLater this would get called
138 * before Forester resets which would nullify the selection.
142 treeView = archaeopteryx.getMainPanel().getCurrentTreePanel();
143 parentAvport.sendSelection();
144 // PaintRefresher.Refresh(treeView,
145 // parentAvport.getSequenceSetId());
157 public void actionPerformed(ActionEvent e)
162 public void mouseClicked(MouseEvent e)
164 SwingUtilities.invokeLater(new Runnable() {
168 * invokeLater so that this always runs after Forester's mouseClicked
172 final PhylogenyNode node = treeView.findNode(e.getX(), e.getY());
175 if ((e.getModifiers() & InputEvent.SHIFT_MASK) == 0) // clear previous
176 // selection if shift
179 parentAvport.setSelectionGroup(null);
182 showNodeSelectionOnAlign(node);
187 partitionTree(e.getX());
189 PaintRefresher.Refresh(treeView, parentAvport.getSequenceSetId());
201 public void mousePressed(final MouseEvent e)
206 public void mouseReleased(MouseEvent e)
211 public void mouseEntered(MouseEvent e)
216 public void mouseExited(MouseEvent e)
222 public void selection(final SequenceGroup seqsel,
223 final ColumnSelection colsel, final HiddenColumns hidden,
224 final SelectionSource source)
226 if (source == parentAvport) // check if source is alignment from where the
229 treeView.setFoundNodes0(
230 new HashSet<Long>(seqsel.getSequences().size()));
232 for (SequenceI selectedSequence : seqsel.getSequences())
234 PhylogenyNode matchingNode = sequencesBoundToNodes.get(selectedSequence);
235 if (matchingNode != null)
237 treeView.getFoundNodes0().add(matchingNode.getId());
240 if (!matchingNode.getBranchData().isHasBranchColor())
242 Color foundNodesColour = treeView.getTreeColorSet()
244 matchingNode.getBranchData()
245 .setBranchColor(new BranchColor(foundNodesColour));
260 * Partially refactored from TreeCanvas
262 public void partitionTree(final int x)
264 Phylogeny tree = treeView.getPhylogeny();
268 // should be calculated on each partition as the tree can theoretically
269 // change in the meantime
270 PhylogenyNode furthestNode = PhylogenyMethods
271 .calculateNodeWithMaxDistanceToRoot(tree);
272 furthestNodeX = furthestNode.getXcoord();
273 rootX = tree.getRoot().getXcoord();
275 if (furthestNodeX != rootX && !(x < rootX || x > furthestNodeX)) // don't
279 // clicked x lies outside
283 // int panelHeight = treeView.getHeight();
284 // g.drawLine(x, 0, x, panelHeight);
286 float threshold = (x - rootX) / (furthestNodeX - rootX);
287 List<PhylogenyNode> foundNodes = getNodesAboveThreshold(threshold,
296 public List<PhylogenyNode> getNodesAboveThreshold(double threshold,
300 List<PhylogenyNode> nodesAboveThreshold = new ArrayList<>();
302 parentAvport.setSelectionGroup(null);
303 parentAvport.getAlignment().deleteAllGroups();
304 parentAvport.clearSequenceColours();
305 if (parentAvport.getCodingComplement() != null)
307 parentAvport.getCodingComplement().setSelectionGroup(null);
308 parentAvport.getCodingComplement().getAlignment().deleteAllGroups();
309 parentAvport.getCodingComplement().clearSequenceColours();
313 colourNodesAboveThreshold(nodesAboveThreshold, threshold,
315 return nodesAboveThreshold;
320 * Partially refactored from TreeCanvas colourGroups (can be made nicer).
328 private List<PhylogenyNode> colourNodesAboveThreshold(
329 List<PhylogenyNode> nodeList, double threshold,
333 for (PhylogenyNode childNode : node.getDescendants())
335 childNode.getBranchData()
336 .setBranchColor(new BranchColor(Color.black));
337 float nodeCutoff = (childNode.getXcoord() - rootX)
338 / (furthestNodeX - rootX);
340 if (nodeCutoff > threshold)
342 nodeList.add(childNode);
344 Color randomColour = new Color((int) (Math.random() * 255),
345 (int) (Math.random() * 255), (int) (Math.random() * 255));
346 childNode.getBranchData()
347 .setBranchColor(new BranchColor(randomColour));
349 List<SequenceI> groupSeqs = new ArrayList<>();
350 SequenceI seq = nodesBoundToSequences.get(childNode);
354 parentAvport.setSequenceColour(seq, randomColour);
357 List<PhylogenyNode> descendantNodes = PhylogenyMethods
358 .getAllDescendants(childNode);
360 for (PhylogenyNode descNode : descendantNodes)
362 seq = nodesBoundToSequences.get(descNode);
366 parentAvport.setSequenceColour(seq, randomColour);
369 descNode.getBranchData()
370 .setBranchColor(new BranchColor(randomColour));
373 if (groupSeqs != null)
376 groupThresholdSequences(groupSeqs, randomColour);
381 colourNodesAboveThreshold(nodeList, threshold, childNode);
386 for (AlignmentPanel associatedPanel : associatedPanels) {
388 associatedPanel.updateAnnotation();
390 final AlignViewportI codingComplement = associatedPanel.getAlignViewport()
391 .getCodingComplement();
392 if (codingComplement != null)
395 ((AlignViewport) codingComplement).getAlignPanel()
404 public void groupThresholdSequences(List<SequenceI> groupedSeqs,
407 SequenceGroup treeGroup = new SequenceGroup(groupedSeqs, null, null,
408 true, true, false, 0,
409 parentAvport.getAlignment().getWidth() - 1);
411 ColourSchemeI cs = null;
412 if (parentAvport.getGlobalColourScheme() != null)
414 if (parentAvport.getGlobalColourScheme() instanceof UserColourScheme)
416 cs = new UserColourScheme(
417 ((UserColourScheme) parentAvport.getGlobalColourScheme())
422 cs = ColourSchemeProperty.getColourScheme(treeGroup,
423 ColourSchemeProperty.getColourName(
424 parentAvport.getGlobalColourScheme()));
428 treeGroup.setColourScheme(cs);
429 treeGroup.getGroupColourScheme().setThreshold(
430 parentAvport.getResidueShading().getThreshold(),
431 parentAvport.isIgnoreGapsConsensus());
433 treeGroup.setName("Tree Group " + nrTreeGroups);
434 treeGroup.setIdColour(groupColour);
436 for (AlignmentPanel associatedPanel : associatedPanels)
438 AlignViewportI altViewport = associatedPanel
441 if (altViewport.getGlobalColourScheme() != null
442 && altViewport.getResidueShading()
443 .conservationApplied())
445 Conservation conserv = new Conservation(treeGroup.getName(),
446 treeGroup.getSequences(null), treeGroup.getStartRes(),
447 treeGroup.getEndRes());
449 conserv.verdict(false, altViewport.getConsPercGaps());
450 treeGroup.getGroupColourScheme().setConservation(conserv);
453 altViewport.getAlignment().addGroup(treeGroup);
454 // TODO can we push all of the below into AlignViewportI?
455 final AlignViewportI codingComplement = altViewport
456 .getCodingComplement();
457 if (codingComplement != null)
459 SequenceGroup mappedGroup = MappingUtils.mapSequenceGroup(treeGroup,
460 parentAvport, codingComplement);
461 if (mappedGroup.getSequences().size() > 0)
463 codingComplement.getAlignment().addGroup(mappedGroup);
464 for (SequenceI seq : mappedGroup.getSequences())
466 codingComplement.setSequenceColour(seq, groupColour.brighter());
476 * may or may not need an extra repaint on the alignment view (check what kira
480 public void showNodeSelectionOnAlign(final PhylogenyNode node)
483 if (node.isInternal())
485 showMatchingChildSequences(node);
490 showMatchingSequence(node);
501 public void showMatchingSequence(final PhylogenyNode nodeToMatch)
503 SequenceI matchingSequence = nodesBoundToSequences.get(nodeToMatch);
504 if (matchingSequence != null)
506 long nodeId = nodeToMatch.getId();
507 addOrRemoveInSet(treeView.getFoundNodes0(), nodeId);
508 treeSelectionChanged(matchingSequence);
509 parentAvport.sendSelection();
515 public void showMatchingChildSequences(final PhylogenyNode parentNode)
517 List<PhylogenyNode> childNodes = PhylogenyMethods
518 .getAllDescendants(parentNode);
521 for (PhylogenyNode childNode : childNodes)
523 // childNode.getBranchData().setBranchColor(new BranchColor(Color.BLUE));
525 SequenceI matchingSequence = nodesBoundToSequences.get(childNode);
526 if (matchingSequence != null)
528 long nodeId = childNode.getId();
529 addOrRemoveInSet(treeView.getFoundNodes0(), nodeId);
531 treeSelectionChanged(matchingSequence);
536 parentAvport.sendSelection();
542 * Refactored from TreeCanvas.
545 * of the node selected in the tree viewer.
548 public void treeSelectionChanged(final SequenceI sequence)
550 if (!parentAvport.isClosed()) // alignment view could be closed
552 SequenceGroup selected = parentAvport.getSelectionGroup();
554 if (selected == null)
556 selected = new SequenceGroup();
557 parentAvport.setSelectionGroup(selected);
560 selected.setEndRes(parentAvport.getAlignment().getWidth() - 1);
561 selected.addOrRemove(sequence, true);
565 public void sortByTree_actionPerformed() {
575 * @param objectToCheck
577 public static <E> void addOrRemoveInSet(Set<E> set, E objectToCheck)
579 if (set.contains(objectToCheck))
581 set.remove(objectToCheck);
585 set.add(objectToCheck);
590 public AlignmentViewport getParentAvport()
595 public void setParentAvport(final AlignmentViewport parentAvport)
597 this.parentAvport = parentAvport;
600 public AlignmentPanel[] getAssociatedPanels()
602 return associatedPanels;
605 public void setAssociatedPanels(AlignmentPanel[] associatedPanels)
607 this.associatedPanels = associatedPanels;