1 package jalview.ext.archaeopteryx;
3 import jalview.analysis.AlignmentSorter;
4 import jalview.analysis.Conservation;
5 import jalview.api.AlignViewportI;
6 import jalview.commands.CommandI;
7 import jalview.commands.OrderCommand;
8 import jalview.datamodel.ColumnSelection;
9 import jalview.datamodel.HiddenColumns;
10 import jalview.datamodel.SequenceGroup;
11 import jalview.datamodel.SequenceI;
12 import jalview.ext.treeviewer.ExternalTreeViewerBindingI;
13 import jalview.gui.AlignViewport;
14 import jalview.gui.AlignmentPanel;
15 import jalview.gui.Desktop;
16 import jalview.gui.JvOptionPane;
17 import jalview.gui.PaintRefresher;
18 import jalview.schemes.ColourSchemeI;
19 import jalview.schemes.ColourSchemeProperty;
20 import jalview.schemes.UserColourScheme;
21 import jalview.structure.SelectionSource;
22 import jalview.structure.StructureSelectionManager;
23 import jalview.util.MappingUtils;
24 import jalview.util.MessageManager;
25 import jalview.viewmodel.AlignmentViewport;
27 import java.awt.Color;
28 import java.awt.event.ActionEvent;
29 import java.awt.event.InputEvent;
30 import java.awt.event.MouseEvent;
31 import java.util.ArrayList;
32 import java.util.HashSet;
33 import java.util.List;
37 import javax.swing.JTabbedPane;
38 import javax.swing.SwingUtilities;
39 import javax.swing.event.ChangeEvent;
40 import javax.swing.event.ChangeListener;
41 import javax.swing.event.InternalFrameAdapter;
42 import javax.swing.event.InternalFrameEvent;
44 import org.forester.archaeopteryx.MainFrame;
45 import org.forester.phylogeny.Phylogeny;
46 import org.forester.phylogeny.PhylogenyMethods;
47 import org.forester.phylogeny.PhylogenyNode;
48 import org.forester.phylogeny.data.BranchColor;
51 * Class for binding the Archaeopteryx tree viewer to the Jalview alignment that
52 * it originates from, meaning that selecting sequences in the tree viewer also
53 * selects them in the alignment view and vice versa.
55 * @author kjvanderheide
58 public final class JalviewBinding
59 implements ExternalTreeViewerBindingI<PhylogenyNode>
61 private org.forester.archaeopteryx.TreePanel treeView;
63 private AlignmentViewport parentAvport;
65 private final JTabbedPane treeTabs;
67 private final StructureSelectionManager ssm;
69 private AlignmentPanel[] associatedPanels;
71 private Map<SequenceI, PhylogenyNode> sequencesBoundToNodes;
73 private Map<PhylogenyNode, SequenceI> nodesBoundToSequences;
77 private float furthestNodeX;
79 private int nrTreeGroups = 0;
81 private boolean applyToAllViews = false;
85 * @param archaeopteryx
87 * @param jalviewAlignmentViewport
88 * alignment viewport from which the tree was calculated.
90 * @param alignMappedToNodes
91 * map with sequences used to calculate the tree and matching tree
92 * nodes as key, value pair respectively.
94 * @param nodesMappedToAlign
95 * map with tree nodes and matching sequences used to calculate the
96 * tree as key, value pair respectively.
98 public JalviewBinding(final MainFrame archaeopteryx,
99 final AlignmentViewport jalviewAlignmentViewport,
100 final Map<SequenceI, PhylogenyNode> alignMappedToNodes,
101 final Map<PhylogenyNode, SequenceI> nodesMappedToAlign)
104 if (archaeopteryx.getMainPanel().getTabbedPane().getTabCount() > 1)
106 JvOptionPane.showMessageDialog(Desktop.desktop,
107 MessageManager.getString("label.tabs_detected_archaeopteryx"),
108 MessageManager.getString("label.problem_reading_tree_file"),
109 JvOptionPane.WARNING_MESSAGE);
113 // deal with/prohibit null values here as that will cause problems
114 parentAvport = jalviewAlignmentViewport;
115 sequencesBoundToNodes = alignMappedToNodes;
116 nodesBoundToSequences = nodesMappedToAlign;
118 treeView = archaeopteryx.getMainPanel().getCurrentTreePanel();
119 treeTabs = archaeopteryx.getMainPanel().getTabbedPane();
120 ssm = parentAvport.getStructureSelectionManager();
122 ssm.addSelectionListener(this);
123 treeView.addMouseListener(this);
124 PaintRefresher.Register(treeView, parentAvport.getSequenceSetId());
125 associatedPanels = PaintRefresher
126 .getAssociatedPanels(parentAvport.getSequenceSetId());
128 archaeopteryx.addInternalFrameListener(new InternalFrameAdapter()
132 public void internalFrameClosed(InternalFrameEvent e)
134 AptxInit.getAllAptxFrames().remove(archaeopteryx);
139 treeTabs.addChangeListener(new ChangeListener()
143 public void stateChanged(ChangeEvent e)
146 SwingUtilities.invokeLater(new Runnable()
151 * Resend the selection to the tree view when tabs get switched, this
152 * has to be buried in invokeLater as Forester first resets the tree
153 * view on switching tabs, without invokeLater this would get called
154 * before Forester resets which would nullify the selection.
158 treeView = archaeopteryx.getMainPanel().getCurrentTreePanel();
159 parentAvport.sendSelection();
160 // PaintRefresher.Refresh(treeView,
161 // parentAvport.getSequenceSetId());
173 public void actionPerformed(ActionEvent e)
178 public void mouseClicked(MouseEvent e)
180 SwingUtilities.invokeLater(new Runnable() {
184 * invokeLater so that this always runs after Forester's mouseClicked
188 final PhylogenyNode node = treeView.findNode(e.getX(), e.getY());
191 if ((e.getModifiers() & InputEvent.SHIFT_MASK) == 0) // clear previous
192 // selection if shift
195 parentAvport.setSelectionGroup(null);
198 showNodeSelectionOnAlign(node);
203 partitionTree(e.getX());
205 PaintRefresher.Refresh(treeView, parentAvport.getSequenceSetId());
217 public void mousePressed(final MouseEvent e)
222 public void mouseReleased(MouseEvent e)
227 public void mouseEntered(MouseEvent e)
232 public void mouseExited(MouseEvent e)
238 public void selection(final SequenceGroup seqsel,
239 final ColumnSelection colsel, final HiddenColumns hidden,
240 final SelectionSource source)
242 if (source == parentAvport) // check if source is alignment from where the
245 treeView.setFoundNodes0(
246 new HashSet<Long>(seqsel.getSequences().size()));
248 for (SequenceI selectedSequence : seqsel.getSequences())
250 PhylogenyNode matchingNode = sequencesBoundToNodes.get(selectedSequence);
251 if (matchingNode != null)
253 treeView.getFoundNodes0().add(matchingNode.getId());
256 if (!matchingNode.getBranchData().isHasBranchColor())
258 Color foundNodesColour = treeView.getTreeColorSet()
260 matchingNode.getBranchData()
261 .setBranchColor(new BranchColor(foundNodesColour));
276 * Partially refactored from TreeCanvas
278 public void partitionTree(final int x)
280 Phylogeny tree = treeView.getPhylogeny();
284 // should be calculated on each partition as the tree can theoretically
285 // change in the meantime
286 PhylogenyNode furthestNode = PhylogenyMethods
287 .calculateNodeWithMaxDistanceToRoot(tree);
288 furthestNodeX = furthestNode.getXcoord();
289 rootX = tree.getRoot().getXcoord();
291 if (furthestNodeX != rootX && !(x < rootX || x > furthestNodeX)) // don't
295 // clicked x lies outside
299 float threshold = (x - rootX) / (furthestNodeX - rootX);
300 List<PhylogenyNode> foundNodes = getNodesAboveThreshold(threshold,
309 public List<PhylogenyNode> getNodesAboveThreshold(double threshold,
313 List<PhylogenyNode> nodesAboveThreshold = new ArrayList<>();
315 parentAvport.setSelectionGroup(null);
316 parentAvport.getAlignment().deleteAllGroups();
317 parentAvport.clearSequenceColours();
318 if (parentAvport.getCodingComplement() != null)
320 parentAvport.getCodingComplement().setSelectionGroup(null);
321 parentAvport.getCodingComplement().getAlignment().deleteAllGroups();
322 parentAvport.getCodingComplement().clearSequenceColours();
326 colourNodesAboveThreshold(nodesAboveThreshold, threshold,
328 return nodesAboveThreshold;
333 * Partially refactored from TreeCanvas colourGroups (can be made nicer).
341 private List<PhylogenyNode> colourNodesAboveThreshold(
342 List<PhylogenyNode> nodeList, double threshold,
346 for (PhylogenyNode childNode : node.getDescendants())
348 childNode.getBranchData()
349 .setBranchColor(new BranchColor(Color.black));
350 float nodeCutoff = (childNode.getXcoord() - rootX)
351 / (furthestNodeX - rootX);
353 if (nodeCutoff > threshold)
355 nodeList.add(childNode);
357 Color randomColour = new Color((int) (Math.random() * 255),
358 (int) (Math.random() * 255), (int) (Math.random() * 255));
359 childNode.getBranchData()
360 .setBranchColor(new BranchColor(randomColour));
362 List<SequenceI> groupSeqs = new ArrayList<>();
363 SequenceI seq = nodesBoundToSequences.get(childNode);
367 parentAvport.setSequenceColour(seq, randomColour);
370 List<PhylogenyNode> descendantNodes = PhylogenyMethods
371 .getAllDescendants(childNode);
373 for (PhylogenyNode descNode : descendantNodes)
375 seq = nodesBoundToSequences.get(descNode);
379 parentAvport.setSequenceColour(seq, randomColour);
382 descNode.getBranchData()
383 .setBranchColor(new BranchColor(randomColour));
386 if (groupSeqs != null)
389 groupThresholdSequences(groupSeqs, randomColour);
394 colourNodesAboveThreshold(nodeList, threshold, childNode);
399 for (AlignmentPanel associatedPanel : associatedPanels) {
401 associatedPanel.updateAnnotation();
403 final AlignViewportI codingComplement = associatedPanel.getAlignViewport()
404 .getCodingComplement();
405 if (codingComplement != null)
408 ((AlignViewport) codingComplement).getAlignPanel()
417 public void groupThresholdSequences(List<SequenceI> groupedSeqs,
420 SequenceGroup treeGroup = new SequenceGroup(groupedSeqs, null, null,
421 true, true, false, 0,
422 parentAvport.getAlignment().getWidth() - 1);
424 ColourSchemeI cs = null;
425 if (parentAvport.getGlobalColourScheme() != null)
427 if (parentAvport.getGlobalColourScheme() instanceof UserColourScheme)
429 cs = new UserColourScheme(
430 ((UserColourScheme) parentAvport.getGlobalColourScheme())
435 cs = ColourSchemeProperty.getColourScheme(treeGroup,
436 ColourSchemeProperty.getColourName(
437 parentAvport.getGlobalColourScheme()));
441 treeGroup.setColourScheme(cs);
442 treeGroup.getGroupColourScheme().setThreshold(
443 parentAvport.getResidueShading().getThreshold(),
444 parentAvport.isIgnoreGapsConsensus());
446 treeGroup.setName("Tree Group " + nrTreeGroups);
447 treeGroup.setIdColour(groupColour);
449 for (AlignmentPanel associatedPanel : associatedPanels)
451 AlignViewportI altViewport = associatedPanel
454 if (altViewport.getGlobalColourScheme() != null
455 && altViewport.getResidueShading()
456 .conservationApplied())
458 Conservation conserv = new Conservation(treeGroup.getName(),
459 treeGroup.getSequences(null), treeGroup.getStartRes(),
460 treeGroup.getEndRes());
462 conserv.verdict(false, altViewport.getConsPercGaps());
463 treeGroup.getGroupColourScheme().setConservation(conserv);
466 altViewport.getAlignment().addGroup(treeGroup);
467 // TODO can we push all of the below into AlignViewportI?
468 final AlignViewportI codingComplement = altViewport
469 .getCodingComplement();
470 if (codingComplement != null)
472 SequenceGroup mappedGroup = MappingUtils.mapSequenceGroup(treeGroup,
473 parentAvport, codingComplement);
474 if (mappedGroup.getSequences().size() > 0)
476 codingComplement.getAlignment().addGroup(mappedGroup);
477 for (SequenceI seq : mappedGroup.getSequences())
479 codingComplement.setSequenceColour(seq, groupColour.brighter());
489 * may or may not need an extra repaint on the alignment view (check what kira
493 public void showNodeSelectionOnAlign(final PhylogenyNode node)
496 if (node.isInternal())
498 showMatchingChildSequences(node);
503 showMatchingSequence(node);
514 public void showMatchingSequence(final PhylogenyNode nodeToMatch)
516 SequenceI matchingSequence = nodesBoundToSequences.get(nodeToMatch);
517 if (matchingSequence != null)
519 long nodeId = nodeToMatch.getId();
520 addOrRemoveInSet(treeView.getFoundNodes0(), nodeId);
521 treeSelectionChanged(matchingSequence);
522 parentAvport.sendSelection();
528 public void showMatchingChildSequences(final PhylogenyNode parentNode)
530 List<PhylogenyNode> childNodes = PhylogenyMethods
531 .getAllDescendants(parentNode);
534 for (PhylogenyNode childNode : childNodes)
536 // childNode.getBranchData().setBranchColor(new BranchColor(Color.BLUE));
538 SequenceI matchingSequence = nodesBoundToSequences.get(childNode);
539 if (matchingSequence != null)
541 long nodeId = childNode.getId();
542 addOrRemoveInSet(treeView.getFoundNodes0(), nodeId);
544 treeSelectionChanged(matchingSequence);
549 parentAvport.sendSelection();
555 * Refactored from TreeCanvas.
558 * of the node selected in the tree viewer.
561 public void treeSelectionChanged(final SequenceI sequence)
563 if (!parentAvport.isClosed()) // alignment view could be closed
565 SequenceGroup selected = parentAvport.getSelectionGroup();
567 if (selected == null)
569 selected = new SequenceGroup();
570 parentAvport.setSelectionGroup(selected);
573 selected.setEndRes(parentAvport.getAlignment().getWidth() - 1);
574 selected.addOrRemove(sequence, true);
580 public void sortByTree_actionPerformed()// modify for Aptx
583 // if (treeCanvas.applyToAllViews)
585 // final ArrayList<CommandI> commands = new ArrayList<>();
586 // for (AlignmentPanel ap : PaintRefresher
587 // .getAssociatedPanels(parentAvport.getSequenceSetId()))
589 // commands.add(sortAlignmentIn(ap.av.getAlignPanel()));
591 // parentAvport.getAlignPanel().alignFrame.addHistoryItem(new CommandI()
595 // public void undoCommand(AlignmentI[] views)
597 // for (CommandI tsort : commands)
599 // tsort.undoCommand(views);
604 // public int getSize()
606 // return commands.size();
610 // public String getDescription()
612 // return "Tree Sort (many views)";
616 // public void doCommand(AlignmentI[] views)
619 // for (CommandI tsort : commands)
621 // tsort.doCommand(views);
625 // for (AlignmentPanel ap : PaintRefresher
626 // .getAssociatedPanels(av.getSequenceSetId()))
628 // // ensure all the alignFrames refresh their GI after adding an undo item
629 // ap.alignFrame.updateEditMenuBar();
634 // treeCanvas.ap.alignFrame
635 // .addHistoryItem(sortAlignmentIn(treeCanvas.ap));
640 public CommandI sortAlignmentIn(AlignmentPanel ap)
642 // TODO: move to alignment view controller
643 AlignmentViewport viewport = ap.av;
644 SequenceI[] oldOrder = viewport.getAlignment().getSequencesArray();
645 AlignmentSorter.sortByTree(viewport.getAlignment(),
646 treeView.getPhylogeny());
648 undo = new OrderCommand("Tree Sort", oldOrder, viewport.getAlignment());
650 ap.paintAlignment(true, false);
660 * @param objectToCheck
662 public static <E> void addOrRemoveInSet(Set<E> set, E objectToCheck)
664 if (set.contains(objectToCheck))
666 set.remove(objectToCheck);
670 set.add(objectToCheck);
675 public AlignmentViewport getParentAvport()
680 public void setParentAvport(final AlignmentViewport parentAvport)
682 this.parentAvport = parentAvport;
685 public AlignmentPanel[] getAssociatedPanels()
687 return associatedPanels;
690 public void setAssociatedPanels(AlignmentPanel[] associatedPanels)
692 this.associatedPanels = associatedPanels;