1 package jalview.ext.archaeopteryx;
3 import jalview.analysis.Conservation;
4 import jalview.api.AlignViewportI;
5 import jalview.commands.CommandI;
6 import jalview.datamodel.ColumnSelection;
7 import jalview.datamodel.HiddenColumns;
8 import jalview.datamodel.SequenceGroup;
9 import jalview.datamodel.SequenceI;
10 import jalview.ext.treeviewer.ExternalTreeViewerBindingI;
11 import jalview.gui.AlignViewport;
12 import jalview.gui.AlignmentPanel;
13 import jalview.gui.Desktop;
14 import jalview.gui.JvOptionPane;
15 import jalview.gui.PaintRefresher;
16 import jalview.schemes.ColourSchemeI;
17 import jalview.schemes.ColourSchemeProperty;
18 import jalview.schemes.UserColourScheme;
19 import jalview.structure.SelectionSource;
20 import jalview.structure.StructureSelectionManager;
21 import jalview.util.MappingUtils;
22 import jalview.util.MessageManager;
23 import jalview.viewmodel.AlignmentViewport;
25 import java.awt.Color;
26 import java.awt.event.ActionEvent;
27 import java.awt.event.InputEvent;
28 import java.awt.event.MouseEvent;
29 import java.util.ArrayList;
30 import java.util.HashSet;
31 import java.util.List;
35 import javax.swing.JTabbedPane;
36 import javax.swing.SwingUtilities;
37 import javax.swing.event.ChangeEvent;
38 import javax.swing.event.ChangeListener;
39 import javax.swing.event.InternalFrameAdapter;
40 import javax.swing.event.InternalFrameEvent;
42 import org.forester.archaeopteryx.MainFrame;
43 import org.forester.phylogeny.Phylogeny;
44 import org.forester.phylogeny.PhylogenyMethods;
45 import org.forester.phylogeny.PhylogenyNode;
46 import org.forester.phylogeny.data.BranchColor;
49 * Class for binding the Archaeopteryx tree viewer to the Jalview alignment that
50 * it originates from, meaning that selecting sequences in the tree viewer also
51 * selects them in the alignment view and vice versa.
53 * @author kjvanderheide
56 public final class JalviewBinding
57 implements ExternalTreeViewerBindingI<PhylogenyNode>
59 private org.forester.archaeopteryx.TreePanel treeView;
61 private AlignmentViewport parentAvport;
63 private final JTabbedPane treeTabs;
65 private final StructureSelectionManager ssm;
67 private AlignmentPanel[] associatedPanels;
69 private Map<SequenceI, PhylogenyNode> sequencesBoundToNodes;
71 private Map<PhylogenyNode, SequenceI> nodesBoundToSequences;
75 private float furthestNodeX;
77 private int nrTreeGroups = 0;
79 private boolean applyToAllViews = false;
83 * @param archaeopteryx
85 * @param jalviewAlignmentViewport
86 * alignment viewport from which the tree was calculated.
88 * @param alignMappedToNodes
89 * map with sequences used to calculate the tree and matching tree
90 * nodes as key, value pair respectively.
92 * @param nodesMappedToAlign
93 * map with tree nodes and matching sequences used to calculate the
94 * tree as key, value pair respectively.
96 public JalviewBinding(final MainFrame archaeopteryx,
97 final AlignmentViewport jalviewAlignmentViewport,
98 final Map<SequenceI, PhylogenyNode> alignMappedToNodes,
99 final Map<PhylogenyNode, SequenceI> nodesMappedToAlign)
102 if (archaeopteryx.getMainPanel().getTabbedPane().getTabCount() > 1)
104 JvOptionPane.showMessageDialog(Desktop.desktop,
105 MessageManager.getString("label.tabs_detected_archaeopteryx"),
106 MessageManager.getString("label.problem_reading_tree_file"),
107 JvOptionPane.WARNING_MESSAGE);
111 // deal with/prohibit null values here as that will cause problems
112 parentAvport = jalviewAlignmentViewport;
113 sequencesBoundToNodes = alignMappedToNodes;
114 nodesBoundToSequences = nodesMappedToAlign;
116 treeView = archaeopteryx.getMainPanel().getCurrentTreePanel();
117 treeTabs = archaeopteryx.getMainPanel().getTabbedPane();
118 ssm = parentAvport.getStructureSelectionManager();
120 ssm.addSelectionListener(this);
121 treeView.addMouseListener(this);
122 PaintRefresher.Register(treeView, parentAvport.getSequenceSetId());
123 associatedPanels = PaintRefresher
124 .getAssociatedPanels(parentAvport.getSequenceSetId());
126 archaeopteryx.addInternalFrameListener(new InternalFrameAdapter()
130 public void internalFrameClosed(InternalFrameEvent e)
132 AptxInit.getAllAptxFrames().remove(archaeopteryx);
137 treeTabs.addChangeListener(new ChangeListener()
141 public void stateChanged(ChangeEvent e)
144 SwingUtilities.invokeLater(new Runnable()
149 * Resend the selection to the tree view when tabs get switched, this
150 * has to be buried in invokeLater as Forester first resets the tree
151 * view on switching tabs, without invokeLater this would get called
152 * before Forester resets which would nullify the selection.
156 treeView = archaeopteryx.getMainPanel().getCurrentTreePanel();
157 parentAvport.sendSelection();
158 // PaintRefresher.Refresh(treeView,
159 // parentAvport.getSequenceSetId());
171 public void actionPerformed(ActionEvent e)
176 public void mouseClicked(MouseEvent e)
178 SwingUtilities.invokeLater(new Runnable() {
182 * invokeLater so that this always runs after Forester's mouseClicked
186 final PhylogenyNode node = treeView.findNode(e.getX(), e.getY());
189 if ((e.getModifiers() & InputEvent.SHIFT_MASK) == 0) // clear previous
190 // selection if shift
193 parentAvport.setSelectionGroup(null);
196 showNodeSelectionOnAlign(node);
201 partitionTree(e.getX());
203 PaintRefresher.Refresh(treeView, parentAvport.getSequenceSetId());
215 public void mousePressed(final MouseEvent e)
220 public void mouseReleased(MouseEvent e)
225 public void mouseEntered(MouseEvent e)
230 public void mouseExited(MouseEvent e)
236 public void selection(final SequenceGroup seqsel,
237 final ColumnSelection colsel, final HiddenColumns hidden,
238 final SelectionSource source)
240 if (source == parentAvport) // check if source is alignment from where the
243 treeView.setFoundNodes0(
244 new HashSet<Long>(seqsel.getSequences().size()));
246 for (SequenceI selectedSequence : seqsel.getSequences())
248 PhylogenyNode matchingNode = sequencesBoundToNodes.get(selectedSequence);
249 if (matchingNode != null)
251 treeView.getFoundNodes0().add(matchingNode.getId());
254 if (!matchingNode.getBranchData().isHasBranchColor())
256 Color foundNodesColour = treeView.getTreeColorSet()
258 matchingNode.getBranchData()
259 .setBranchColor(new BranchColor(foundNodesColour));
274 * Partially refactored from TreeCanvas
276 public void partitionTree(final int x)
278 Phylogeny tree = treeView.getPhylogeny();
282 // should be calculated on each partition as the tree can theoretically
283 // change in the meantime
284 PhylogenyNode furthestNode = PhylogenyMethods
285 .calculateNodeWithMaxDistanceToRoot(tree);
286 furthestNodeX = furthestNode.getXcoord();
287 rootX = tree.getRoot().getXcoord();
289 if (furthestNodeX != rootX && !(x < rootX || x > furthestNodeX)) // don't
293 // clicked x lies outside
297 float threshold = (x - rootX) / (furthestNodeX - rootX);
298 List<PhylogenyNode> foundNodes = getNodesAboveThreshold(threshold,
307 public List<PhylogenyNode> getNodesAboveThreshold(double threshold,
311 List<PhylogenyNode> nodesAboveThreshold = new ArrayList<>();
313 parentAvport.setSelectionGroup(null);
314 parentAvport.getAlignment().deleteAllGroups();
315 parentAvport.clearSequenceColours();
316 if (parentAvport.getCodingComplement() != null)
318 parentAvport.getCodingComplement().setSelectionGroup(null);
319 parentAvport.getCodingComplement().getAlignment().deleteAllGroups();
320 parentAvport.getCodingComplement().clearSequenceColours();
324 colourNodesAboveThreshold(nodesAboveThreshold, threshold,
326 return nodesAboveThreshold;
331 * Partially refactored from TreeCanvas colourGroups (can be made nicer).
339 private List<PhylogenyNode> colourNodesAboveThreshold(
340 List<PhylogenyNode> nodeList, double threshold,
344 for (PhylogenyNode childNode : node.getDescendants())
346 childNode.getBranchData()
347 .setBranchColor(new BranchColor(Color.black));
348 float nodeCutoff = (childNode.getXcoord() - rootX)
349 / (furthestNodeX - rootX);
351 if (nodeCutoff > threshold)
353 nodeList.add(childNode);
355 Color randomColour = new Color((int) (Math.random() * 255),
356 (int) (Math.random() * 255), (int) (Math.random() * 255));
357 childNode.getBranchData()
358 .setBranchColor(new BranchColor(randomColour));
360 List<SequenceI> groupSeqs = new ArrayList<>();
361 SequenceI seq = nodesBoundToSequences.get(childNode);
365 parentAvport.setSequenceColour(seq, randomColour);
368 List<PhylogenyNode> descendantNodes = PhylogenyMethods
369 .getAllDescendants(childNode);
371 for (PhylogenyNode descNode : descendantNodes)
373 seq = nodesBoundToSequences.get(descNode);
377 parentAvport.setSequenceColour(seq, randomColour);
380 descNode.getBranchData()
381 .setBranchColor(new BranchColor(randomColour));
384 if (groupSeqs != null)
387 groupThresholdSequences(groupSeqs, randomColour);
392 colourNodesAboveThreshold(nodeList, threshold, childNode);
397 for (AlignmentPanel associatedPanel : associatedPanels) {
399 associatedPanel.updateAnnotation();
401 final AlignViewportI codingComplement = associatedPanel.getAlignViewport()
402 .getCodingComplement();
403 if (codingComplement != null)
406 ((AlignViewport) codingComplement).getAlignPanel()
415 public void groupThresholdSequences(List<SequenceI> groupedSeqs,
418 SequenceGroup treeGroup = new SequenceGroup(groupedSeqs, null, null,
419 true, true, false, 0,
420 parentAvport.getAlignment().getWidth() - 1);
422 ColourSchemeI cs = null;
423 if (parentAvport.getGlobalColourScheme() != null)
425 if (parentAvport.getGlobalColourScheme() instanceof UserColourScheme)
427 cs = new UserColourScheme(
428 ((UserColourScheme) parentAvport.getGlobalColourScheme())
433 cs = ColourSchemeProperty.getColourScheme(treeGroup,
434 ColourSchemeProperty.getColourName(
435 parentAvport.getGlobalColourScheme()));
439 treeGroup.setColourScheme(cs);
440 treeGroup.getGroupColourScheme().setThreshold(
441 parentAvport.getResidueShading().getThreshold(),
442 parentAvport.isIgnoreGapsConsensus());
444 treeGroup.setName("Tree Group " + nrTreeGroups);
445 treeGroup.setIdColour(groupColour);
447 for (AlignmentPanel associatedPanel : associatedPanels)
449 AlignViewportI altViewport = associatedPanel
452 if (altViewport.getGlobalColourScheme() != null
453 && altViewport.getResidueShading()
454 .conservationApplied())
456 Conservation conserv = new Conservation(treeGroup.getName(),
457 treeGroup.getSequences(null), treeGroup.getStartRes(),
458 treeGroup.getEndRes());
460 conserv.verdict(false, altViewport.getConsPercGaps());
461 treeGroup.getGroupColourScheme().setConservation(conserv);
464 altViewport.getAlignment().addGroup(treeGroup);
465 // TODO can we push all of the below into AlignViewportI?
466 final AlignViewportI codingComplement = altViewport
467 .getCodingComplement();
468 if (codingComplement != null)
470 SequenceGroup mappedGroup = MappingUtils.mapSequenceGroup(treeGroup,
471 parentAvport, codingComplement);
472 if (mappedGroup.getSequences().size() > 0)
474 codingComplement.getAlignment().addGroup(mappedGroup);
475 for (SequenceI seq : mappedGroup.getSequences())
477 codingComplement.setSequenceColour(seq, groupColour.brighter());
487 * may or may not need an extra repaint on the alignment view (check what kira
491 public void showNodeSelectionOnAlign(final PhylogenyNode node)
494 if (node.isInternal())
496 showMatchingChildSequences(node);
501 showMatchingSequence(node);
512 public void showMatchingSequence(final PhylogenyNode nodeToMatch)
514 SequenceI matchingSequence = nodesBoundToSequences.get(nodeToMatch);
515 if (matchingSequence != null)
517 long nodeId = nodeToMatch.getId();
518 addOrRemoveInSet(treeView.getFoundNodes0(), nodeId);
519 treeSelectionChanged(matchingSequence);
520 parentAvport.sendSelection();
526 public void showMatchingChildSequences(final PhylogenyNode parentNode)
528 List<PhylogenyNode> childNodes = PhylogenyMethods
529 .getAllDescendants(parentNode);
532 for (PhylogenyNode childNode : childNodes)
534 // childNode.getBranchData().setBranchColor(new BranchColor(Color.BLUE));
536 SequenceI matchingSequence = nodesBoundToSequences.get(childNode);
537 if (matchingSequence != null)
539 long nodeId = childNode.getId();
540 addOrRemoveInSet(treeView.getFoundNodes0(), nodeId);
542 treeSelectionChanged(matchingSequence);
547 parentAvport.sendSelection();
553 * Refactored from TreeCanvas.
556 * of the node selected in the tree viewer.
559 public void treeSelectionChanged(final SequenceI sequence)
561 if (!parentAvport.isClosed()) // alignment view could be closed
563 SequenceGroup selected = parentAvport.getSelectionGroup();
565 if (selected == null)
567 selected = new SequenceGroup();
568 parentAvport.setSelectionGroup(selected);
571 selected.setEndRes(parentAvport.getAlignment().getWidth() - 1);
572 selected.addOrRemove(sequence, true);
578 public void sortByTree_actionPerformed()// modify for Aptx
581 // if (treeCanvas.applyToAllViews)
583 // final ArrayList<CommandI> commands = new ArrayList<>();
584 // for (AlignmentPanel ap : PaintRefresher
585 // .getAssociatedPanels(parentAvport.getSequenceSetId()))
587 // commands.add(sortAlignmentIn(ap.av.getAlignPanel()));
589 // parentAvport.getAlignPanel().alignFrame.addHistoryItem(new CommandI()
593 // public void undoCommand(AlignmentI[] views)
595 // for (CommandI tsort : commands)
597 // tsort.undoCommand(views);
602 // public int getSize()
604 // return commands.size();
608 // public String getDescription()
610 // return "Tree Sort (many views)";
614 // public void doCommand(AlignmentI[] views)
617 // for (CommandI tsort : commands)
619 // tsort.doCommand(views);
623 // for (AlignmentPanel ap : PaintRefresher
624 // .getAssociatedPanels(av.getSequenceSetId()))
626 // // ensure all the alignFrames refresh their GI after adding an undo item
627 // ap.alignFrame.updateEditMenuBar();
632 // treeCanvas.ap.alignFrame
633 // .addHistoryItem(sortAlignmentIn(treeCanvas.ap));
638 public CommandI sortAlignmentIn(AlignmentPanel ap)
640 // // TODO: move to alignment view controller
641 // AlignmentViewport viewport = ap.av;
642 // SequenceI[] oldOrder = viewport.getAlignment().getSequencesArray();
643 // AlignmentSorter.sortByTree(viewport.getAlignment(), tree);
645 // undo = new OrderCommand("Tree Sort", oldOrder, viewport.getAlignment());
647 // ap.paintAlignment(true, false);
658 * @param objectToCheck
660 public static <E> void addOrRemoveInSet(Set<E> set, E objectToCheck)
662 if (set.contains(objectToCheck))
664 set.remove(objectToCheck);
668 set.add(objectToCheck);
673 public AlignmentViewport getParentAvport()
678 public void setParentAvport(final AlignmentViewport parentAvport)
680 this.parentAvport = parentAvport;
683 public AlignmentPanel[] getAssociatedPanels()
685 return associatedPanels;
688 public void setAssociatedPanels(AlignmentPanel[] associatedPanels)
690 this.associatedPanels = associatedPanels;