1 package jalview.ext.archaeopteryx;
3 import jalview.datamodel.ColumnSelection;
4 import jalview.datamodel.HiddenColumns;
5 import jalview.datamodel.SequenceGroup;
6 import jalview.datamodel.SequenceI;
7 import jalview.ext.treeviewer.ExternalTreeViewerBindingI;
8 import jalview.gui.Desktop;
9 import jalview.gui.JvOptionPane;
10 import jalview.gui.PaintRefresher;
11 import jalview.structure.SelectionSource;
12 import jalview.structure.StructureSelectionManager;
13 import jalview.util.MessageManager;
14 import jalview.viewmodel.AlignmentViewport;
16 import java.awt.Color;
17 import java.awt.Graphics;
18 import java.awt.event.ActionEvent;
19 import java.awt.event.InputEvent;
20 import java.awt.event.MouseEvent;
21 import java.util.ArrayList;
22 import java.util.HashSet;
23 import java.util.List;
27 import javax.swing.JTabbedPane;
28 import javax.swing.SwingUtilities;
29 import javax.swing.event.ChangeEvent;
30 import javax.swing.event.ChangeListener;
32 import org.forester.archaeopteryx.MainFrame;
33 import org.forester.archaeopteryx.TreePanelUtil;
34 import org.forester.phylogeny.Phylogeny;
35 import org.forester.phylogeny.PhylogenyMethods;
36 import org.forester.phylogeny.PhylogenyNode;
37 import org.forester.phylogeny.data.BranchColor;
40 * Class for binding the Archaeopteryx tree viewer to the Jalview alignment that
41 * it originates from, meaning that selecting sequences in the tree viewer also
42 * selects them in the alignment view and vice versa.
44 * @author kjvanderheide
47 public final class JalviewBinding
48 implements ExternalTreeViewerBindingI<PhylogenyNode>
50 private org.forester.archaeopteryx.TreePanel treeView;
52 private AlignmentViewport parentAvport;
54 private final JTabbedPane treeTabs;
56 private final StructureSelectionManager ssm;
58 private Map<SequenceI, PhylogenyNode> sequencesBoundToNodes;
60 private Map<PhylogenyNode, SequenceI> nodesBoundToSequences;
64 * @param archaeopteryx
66 * @param jalviewAlignmentViewport
67 * alignment viewport from which the tree was calculated.
69 * @param alignMappedToNodes
70 * map with sequences used to calculate the tree and matching tree
71 * nodes as key, value pair respectively.
73 * @param nodesMappedToAlign
74 * map with tree nodes and matching sequences used to calculate the
75 * tree as key, value pair respectively.
77 public JalviewBinding(final MainFrame archaeopteryx,
78 final AlignmentViewport jalviewAlignmentViewport,
79 final Map<SequenceI, PhylogenyNode> alignMappedToNodes,
80 final Map<PhylogenyNode, SequenceI> nodesMappedToAlign)
83 if (archaeopteryx.getMainPanel().getTabbedPane().getTabCount() > 1)
85 JvOptionPane.showMessageDialog(Desktop.desktop,
86 MessageManager.getString("label.tabs_detected_archaeopteryx"),
87 MessageManager.getString("label.problem_reading_tree_file"),
88 JvOptionPane.WARNING_MESSAGE);
92 // deal with/prohibit null values here as that will cause problems
93 parentAvport = jalviewAlignmentViewport;
94 sequencesBoundToNodes = alignMappedToNodes;
95 nodesBoundToSequences = nodesMappedToAlign;
97 treeView = archaeopteryx.getMainPanel().getCurrentTreePanel();
98 treeTabs = archaeopteryx.getMainPanel().getTabbedPane();
99 ssm = parentAvport.getStructureSelectionManager();
101 ssm.addSelectionListener(this);
102 treeView.addMouseListener(this);
103 PaintRefresher.Register(treeView, parentAvport.getSequenceSetId());
106 treeTabs.addChangeListener(new ChangeListener()
110 public void stateChanged(ChangeEvent e)
113 SwingUtilities.invokeLater(new Runnable()
118 * Resend the selection to the tree view when tabs get switched, this
119 * has to be buried in invokeLater as Forester first resets the tree
120 * view on switching tabs, without invokeLater this would get called
121 * before Forester resets which would nullify the selection.
125 treeView = archaeopteryx.getMainPanel().getCurrentTreePanel();
126 parentAvport.sendSelection();
127 // PaintRefresher.Refresh(treeView,
128 // parentAvport.getSequenceSetId());
140 public void actionPerformed(ActionEvent e)
145 public void mouseClicked(MouseEvent e)
147 SwingUtilities.invokeLater(new Runnable() {
151 * invokeLater so that this always runs after Forester's mouseClicked
155 final PhylogenyNode node = treeView.findNode(e.getX(), e.getY());
158 if ((e.getModifiers() & InputEvent.SHIFT_MASK) == 0) // clear previous
159 // selection if shift
162 parentAvport.setSelectionGroup(null);
165 showNodeSelectionOnAlign(node);
169 partitionTree(e.getX());
171 PaintRefresher.Refresh(treeView, parentAvport.getSequenceSetId());
179 public void mousePressed(final MouseEvent e)
184 public void mouseReleased(MouseEvent e)
189 public void mouseEntered(MouseEvent e)
194 public void mouseExited(MouseEvent e)
200 public void selection(final SequenceGroup seqsel,
201 final ColumnSelection colsel, final HiddenColumns hidden,
202 final SelectionSource source)
204 if (source == parentAvport) // check if source is alignment from where the
207 treeView.setFoundNodes0(
208 new HashSet<Long>(seqsel.getSequences().size()));
210 for (SequenceI selectedSequence : seqsel.getSequences())
212 PhylogenyNode matchingNode = sequencesBoundToNodes.get(selectedSequence);
213 if (matchingNode != null)
215 treeView.getFoundNodes0().add(matchingNode.getId());
227 * Partially refactored from TreeCanvas
229 public void partitionTree(final int x)
231 Phylogeny tree = treeView.getPhylogeny();
235 double longestBranch = tree.calculateHeight(true);
236 if (longestBranch != 0)
238 Graphics g = treeView.getGraphics();
239 int panelHeight = treeView.getHeight();
240 int viewWidth = treeView.getWidth();
241 g.drawLine(x, 0, x, panelHeight);
243 // double relativeTreeWidth = treeDepth / viewWidth;
245 // System.out.println(relativeTreeWidth);
247 float rootX = tree.getRoot().getXcoord();
248 double threshold = ((double) x - rootX) / longestBranch;
249 List<PhylogenyNode> foundNodes = getNodesAboveThreshold(threshold,
250 longestBranch, tree.getRoot());
251 for (PhylogenyNode foundNode : foundNodes)
253 Color randomColor = new Color((int) (Math.random() * 255),
254 (int) (Math.random() * 255), (int) (Math.random() * 255));
255 TreePanelUtil.colorizeSubtree(foundNode,
256 new BranchColor(randomColor));
260 // groupNodes(threshold, tree.getRoot(), longestBranch);
271 public List<PhylogenyNode> getNodesAboveThreshold(double threshold,
272 double treeLength, PhylogenyNode node)
275 List<PhylogenyNode> nodesAboveThreshold = new ArrayList<>();
277 iterateNodesAboveThreshold(nodesAboveThreshold, threshold, treeLength,
279 return nodesAboveThreshold;
283 private List<PhylogenyNode> iterateNodesAboveThreshold(
284 List<PhylogenyNode> nodeList, double threshold,
285 double treeLength, PhylogenyNode node)
287 for (PhylogenyNode childNode : node.getDescendants())
289 double nodeCutoff = childNode.calculateDistanceToRoot() / treeLength;
291 if (nodeCutoff > threshold)
293 nodeList.add(childNode);
298 iterateNodesAboveThreshold(nodeList, threshold, treeLength,
308 // public List<PhylogenyNode> groupNodes(float threshold, PhylogenyNode root,
309 // double treeHeight)
311 // List<PhylogenyNode> groups = new ArrayList<>();
312 // _groupNodes(groups, root, threshold, treeHeight);
313 // System.out.println(groups);
317 // protected void _groupNodes(List<PhylogenyNode> groups, PhylogenyNode nd,
318 // float threshold, double treeHeight)
325 // if ((nd.calculateDistanceToRoot() / treeHeight) > threshold)
331 // for (PhylogenyNode childNode : nd.getDescendants())
333 // _groupNodes(groups, childNode, threshold, treeHeight);
341 * may or may not need an extra repaint on the alignment view (check what kira
345 public void showNodeSelectionOnAlign(final PhylogenyNode node)
348 if (node.isInternal())
350 showMatchingChildSequences(node);
355 showMatchingSequence(node);
366 public void showMatchingSequence(final PhylogenyNode nodeToMatch)
368 SequenceI matchingSequence = nodesBoundToSequences.get(nodeToMatch);
369 if (matchingSequence != null)
371 long nodeId = nodeToMatch.getId();
372 addOrRemoveInSet(treeView.getFoundNodes0(), nodeId);
373 treeSelectionChanged(matchingSequence);
374 parentAvport.sendSelection();
380 public void showMatchingChildSequences(final PhylogenyNode parentNode)
382 List<PhylogenyNode> childNodes = PhylogenyMethods
383 .getAllDescendants(parentNode);
386 for (PhylogenyNode childNode : childNodes)
388 // childNode.getBranchData().setBranchColor(new BranchColor(Color.BLUE));
390 SequenceI matchingSequence = nodesBoundToSequences.get(childNode);
391 if (matchingSequence != null)
393 long nodeId = childNode.getId();
394 addOrRemoveInSet(treeView.getFoundNodes0(), nodeId);
396 treeSelectionChanged(matchingSequence);
401 parentAvport.sendSelection();
407 * Refactored from TreeCanvas.
410 * of the node selected in the tree viewer.
413 public void treeSelectionChanged(final SequenceI sequence)
415 if (!parentAvport.isClosed()) // alignment view could be closed
417 SequenceGroup selected = parentAvport.getSelectionGroup();
419 if (selected == null)
421 selected = new SequenceGroup();
422 parentAvport.setSelectionGroup(selected);
425 selected.setEndRes(parentAvport.getAlignment().getWidth() - 1);
426 selected.addOrRemove(sequence, true);
430 public void sortByTree_actionPerformed() {
431 // parentAvport.mirrorCommand(command, undo, ssm, source);
434 // .addHistoryItem(sortAlignmentIn(treeCanvas.ap));
440 * sort the associated alignment view by the current tree.
445 // public void sortByTree_actionPerformed()// modify for Aptx
448 // // if (treeCanvas.applyToAllViews)
450 // final ArrayList<CommandI> commands = new ArrayList<>();
451 // for (AlignmentPanel ap : PaintRefresher
452 // .getAssociatedPanels(parentAvport.getSequenceSetId()))
454 // commands.add(sortAlignmentIn(ap.av.getAlignPanel()));
456 // av.getAlignPanel().alignFrame.addHistoryItem(new CommandI()
460 // public void undoCommand(AlignmentI[] views)
462 // for (CommandI tsort : commands)
464 // tsort.undoCommand(views);
469 // public int getSize()
471 // return commands.size();
475 // public String getDescription()
477 // return "Tree Sort (many views)";
481 // public void doCommand(AlignmentI[] views)
484 // for (CommandI tsort : commands)
486 // tsort.doCommand(views);
490 // for (AlignmentPanel ap : PaintRefresher
491 // .getAssociatedPanels(av.getSequenceSetId()))
493 // // ensure all the alignFrames refresh their GI after adding an undo item
494 // ap.alignFrame.updateEditMenuBar();
499 // treeCanvas.ap.alignFrame
500 // .addHistoryItem(sortAlignmentIn(treeCanvas.ap));
509 * @param objectToCheck
511 public static <E> void addOrRemoveInSet(Set<E> set, E objectToCheck)
513 if (set.contains(objectToCheck))
515 set.remove(objectToCheck);
519 set.add(objectToCheck);
524 public AlignmentViewport getParentAvport()
529 public void setParentAvport(final AlignmentViewport parentAvport)
531 this.parentAvport = parentAvport;