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.Graphics;
17 import java.awt.event.ActionEvent;
18 import java.awt.event.InputEvent;
19 import java.awt.event.MouseEvent;
20 import java.util.ArrayList;
21 import java.util.HashSet;
22 import java.util.List;
26 import javax.swing.JTabbedPane;
27 import javax.swing.SwingUtilities;
28 import javax.swing.event.ChangeEvent;
29 import javax.swing.event.ChangeListener;
31 import org.forester.archaeopteryx.MainFrame;
32 import org.forester.phylogeny.Phylogeny;
33 import org.forester.phylogeny.PhylogenyMethods;
34 import org.forester.phylogeny.PhylogenyNode;
37 * Class for binding the Archaeopteryx tree viewer to the Jalview alignment that
38 * it originates from, meaning that selecting sequences in the tree viewer also
39 * selects them in the alignment view and vice versa.
41 * @author kjvanderheide
44 public final class JalviewBinding
45 implements ExternalTreeViewerBindingI<PhylogenyNode>
47 private org.forester.archaeopteryx.TreePanel treeView;
49 private AlignmentViewport parentAvport;
51 private final JTabbedPane treeTabs;
53 private final StructureSelectionManager ssm;
55 private Map<SequenceI, PhylogenyNode> sequencesBoundToNodes;
57 private Map<PhylogenyNode, SequenceI> nodesBoundToSequences;
61 * @param archaeopteryx
63 * @param jalviewAlignmentViewport
64 * alignment viewport from which the tree was calculated.
66 * @param alignMappedToNodes
67 * map with sequences used to calculate the tree and matching tree
68 * nodes as key, value pair respectively.
70 * @param nodesMappedToAlign
71 * map with tree nodes and matching sequences used to calculate the
72 * tree as key, value pair respectively.
74 public JalviewBinding(final MainFrame archaeopteryx,
75 final AlignmentViewport jalviewAlignmentViewport,
76 final Map<SequenceI, PhylogenyNode> alignMappedToNodes,
77 final Map<PhylogenyNode, SequenceI> nodesMappedToAlign)
80 if (archaeopteryx.getMainPanel().getTabbedPane().getTabCount() > 1)
82 JvOptionPane.showMessageDialog(Desktop.desktop,
83 MessageManager.getString("label.tabs_detected_archaeopteryx"),
84 MessageManager.getString("label.problem_reading_tree_file"),
85 JvOptionPane.WARNING_MESSAGE);
89 // deal with/prohibit null values here as that will cause problems
90 parentAvport = jalviewAlignmentViewport;
91 sequencesBoundToNodes = alignMappedToNodes;
92 nodesBoundToSequences = nodesMappedToAlign;
94 treeView = archaeopteryx.getMainPanel().getCurrentTreePanel();
95 treeTabs = archaeopteryx.getMainPanel().getTabbedPane();
96 ssm = parentAvport.getStructureSelectionManager();
98 ssm.addSelectionListener(this);
99 treeView.addMouseListener(this);
100 PaintRefresher.Register(treeView, parentAvport.getSequenceSetId());
103 treeTabs.addChangeListener(new ChangeListener()
107 public void stateChanged(ChangeEvent e)
110 SwingUtilities.invokeLater(new Runnable()
115 * Resend the selection to the tree view when tabs get switched, this
116 * has to be buried in invokeLater as Forester first resets the tree
117 * view on switching tabs, without invokeLater this would get called
118 * before Forester resets which would nullify the selection.
122 treeView = archaeopteryx.getMainPanel().getCurrentTreePanel();
123 parentAvport.sendSelection();
124 // PaintRefresher.Refresh(treeView,
125 // parentAvport.getSequenceSetId());
137 public void actionPerformed(ActionEvent e)
142 public void mouseClicked(MouseEvent e)
144 SwingUtilities.invokeLater(new Runnable() {
148 * invokeLater so that this always runs after Forester's mouseClicked
152 final PhylogenyNode node = treeView.findNode(e.getX(), e.getY());
155 if ((e.getModifiers() & InputEvent.SHIFT_MASK) == 0) // clear previous
156 // selection if shift
159 parentAvport.setSelectionGroup(null);
162 showNodeSelectionOnAlign(node);
166 partitionTree(e.getX());
168 PaintRefresher.Refresh(treeView, parentAvport.getSequenceSetId());
176 public void mousePressed(final MouseEvent e)
181 public void mouseReleased(MouseEvent e)
186 public void mouseEntered(MouseEvent e)
191 public void mouseExited(MouseEvent e)
197 public void selection(final SequenceGroup seqsel,
198 final ColumnSelection colsel, final HiddenColumns hidden,
199 final SelectionSource source)
201 if (source == parentAvport) // check if source is alignment from where the
204 treeView.setFoundNodes0(
205 new HashSet<Long>(seqsel.getSequences().size()));
207 for (SequenceI selectedSequence : seqsel.getSequences())
209 PhylogenyNode matchingNode = sequencesBoundToNodes.get(selectedSequence);
210 if (matchingNode != null)
212 treeView.getFoundNodes0().add(matchingNode.getId());
224 * Partially refactored from TreeCanvas
226 public void partitionTree(final int x)
228 Phylogeny tree = treeView.getPhylogeny();
232 double longestBranch = tree.calculateHeight(true);
233 if (longestBranch != 0)
235 Graphics g = treeView.getGraphics();
236 int panelHeight = treeView.getHeight();
237 int viewWidth = treeView.getWidth();
238 g.drawLine(x, 0, x, panelHeight);
240 // double relativeTreeWidth = treeDepth / viewWidth;
242 // System.out.println(relativeTreeWidth);
244 float xRoot = tree.getRoot().getXcoord();
245 double threshold = ((double) x - xRoot) / longestBranch;
246 System.out.println(threshold);
247 List<PhylogenyNode> foundNodes = getNodesAboveThreshold(threshold,
248 longestBranch, tree.getRoot());
249 for (PhylogenyNode foundNode : foundNodes)
251 System.out.println(foundNode);
254 // groupNodes(threshold, tree.getRoot(), longestBranch);
265 public List<PhylogenyNode> getNodesAboveThreshold(double threshold,
266 double treeLength, PhylogenyNode node)
269 List<PhylogenyNode> nodesAboveThreshold = new ArrayList<>();
271 iterateNodesAboveThreshold(nodesAboveThreshold, threshold, treeLength,
273 return nodesAboveThreshold;
277 private List<PhylogenyNode> iterateNodesAboveThreshold(
278 List<PhylogenyNode> nodeList, double threshold,
279 double treeLength, PhylogenyNode node)
281 for (PhylogenyNode childNode : node.getDescendants())
283 double nodeCutoff = childNode.calculateDistanceToRoot() / treeLength;
285 if (nodeCutoff > threshold)
292 iterateNodesAboveThreshold(nodeList, threshold, treeLength,
302 // public List<PhylogenyNode> groupNodes(float threshold, PhylogenyNode root,
303 // double treeHeight)
305 // List<PhylogenyNode> groups = new ArrayList<>();
306 // _groupNodes(groups, root, threshold, treeHeight);
307 // System.out.println(groups);
311 // protected void _groupNodes(List<PhylogenyNode> groups, PhylogenyNode nd,
312 // float threshold, double treeHeight)
319 // if ((nd.calculateDistanceToRoot() / treeHeight) > threshold)
325 // for (PhylogenyNode childNode : nd.getDescendants())
327 // _groupNodes(groups, childNode, threshold, treeHeight);
335 * may or may not need an extra repaint on the alignment view (check what kira
339 public void showNodeSelectionOnAlign(final PhylogenyNode node)
342 if (node.isInternal())
344 showMatchingChildSequences(node);
349 showMatchingSequence(node);
360 public void showMatchingSequence(final PhylogenyNode nodeToMatch)
362 SequenceI matchingSequence = nodesBoundToSequences.get(nodeToMatch);
363 if (matchingSequence != null)
365 long nodeId = nodeToMatch.getId();
366 addOrRemoveInSet(treeView.getFoundNodes0(), nodeId);
367 treeSelectionChanged(matchingSequence);
368 parentAvport.sendSelection();
374 public void showMatchingChildSequences(final PhylogenyNode parentNode)
376 List<PhylogenyNode> childNodes = PhylogenyMethods
377 .getAllDescendants(parentNode);
380 for (PhylogenyNode childNode : childNodes)
382 // childNode.getBranchData().setBranchColor(new BranchColor(Color.BLUE));
384 SequenceI matchingSequence = nodesBoundToSequences.get(childNode);
385 if (matchingSequence != null)
387 long nodeId = childNode.getId();
388 addOrRemoveInSet(treeView.getFoundNodes0(), nodeId);
390 treeSelectionChanged(matchingSequence);
395 parentAvport.sendSelection();
401 * Refactored from TreeCanvas.
404 * of the node selected in the tree viewer.
407 public void treeSelectionChanged(final SequenceI sequence)
409 if (!parentAvport.isClosed()) // alignment view could be closed
411 SequenceGroup selected = parentAvport.getSelectionGroup();
413 if (selected == null)
415 selected = new SequenceGroup();
416 parentAvport.setSelectionGroup(selected);
419 selected.setEndRes(parentAvport.getAlignment().getWidth() - 1);
420 selected.addOrRemove(sequence, true);
424 public void sortByTree_actionPerformed() {
425 // parentAvport.mirrorCommand(command, undo, ssm, source);
428 // .addHistoryItem(sortAlignmentIn(treeCanvas.ap));
434 * sort the associated alignment view by the current tree.
439 // public void sortByTree_actionPerformed()// modify for Aptx
442 // // if (treeCanvas.applyToAllViews)
444 // final ArrayList<CommandI> commands = new ArrayList<>();
445 // for (AlignmentPanel ap : PaintRefresher
446 // .getAssociatedPanels(parentAvport.getSequenceSetId()))
448 // commands.add(sortAlignmentIn(ap.av.getAlignPanel()));
450 // av.getAlignPanel().alignFrame.addHistoryItem(new CommandI()
454 // public void undoCommand(AlignmentI[] views)
456 // for (CommandI tsort : commands)
458 // tsort.undoCommand(views);
463 // public int getSize()
465 // return commands.size();
469 // public String getDescription()
471 // return "Tree Sort (many views)";
475 // public void doCommand(AlignmentI[] views)
478 // for (CommandI tsort : commands)
480 // tsort.doCommand(views);
484 // for (AlignmentPanel ap : PaintRefresher
485 // .getAssociatedPanels(av.getSequenceSetId()))
487 // // ensure all the alignFrames refresh their GI after adding an undo item
488 // ap.alignFrame.updateEditMenuBar();
493 // treeCanvas.ap.alignFrame
494 // .addHistoryItem(sortAlignmentIn(treeCanvas.ap));
503 * @param objectToCheck
505 public static <E> void addOrRemoveInSet(Set<E> set, E objectToCheck)
507 if (set.contains(objectToCheck))
509 set.remove(objectToCheck);
513 set.add(objectToCheck);
518 public AlignmentViewport getParentAvport()
523 public void setParentAvport(final AlignmentViewport parentAvport)
525 this.parentAvport = parentAvport;