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 double threshold = x / longestBranch;
245 System.out.println(threshold);
246 List<PhylogenyNode> foundNodes = getNodesAboveThreshold(threshold,
247 longestBranch, tree.getRoot());
248 for (PhylogenyNode foundNode : foundNodes)
250 System.out.println(foundNode);
253 // groupNodes(threshold, tree.getRoot(), longestBranch);
264 public List<PhylogenyNode> getNodesAboveThreshold(double threshold,
265 double treeLength, PhylogenyNode node)
268 List<PhylogenyNode> nodesAboveThreshold = new ArrayList<>();
270 iterateNodesAboveThreshold(nodesAboveThreshold, threshold, treeLength,
272 return nodesAboveThreshold;
276 private List<PhylogenyNode> iterateNodesAboveThreshold(
277 List<PhylogenyNode> nodeList, double threshold,
278 double treeLength, PhylogenyNode node)
280 for (PhylogenyNode childNode : node.getDescendants())
282 double nodeCutoff = childNode.calculateDistanceToRoot() / treeLength;
284 if (nodeCutoff > threshold)
291 iterateNodesAboveThreshold(nodeList, threshold, treeLength,
301 // public List<PhylogenyNode> groupNodes(float threshold, PhylogenyNode root,
302 // double treeHeight)
304 // List<PhylogenyNode> groups = new ArrayList<>();
305 // _groupNodes(groups, root, threshold, treeHeight);
306 // System.out.println(groups);
310 // protected void _groupNodes(List<PhylogenyNode> groups, PhylogenyNode nd,
311 // float threshold, double treeHeight)
318 // if ((nd.calculateDistanceToRoot() / treeHeight) > threshold)
324 // for (PhylogenyNode childNode : nd.getDescendants())
326 // _groupNodes(groups, childNode, threshold, treeHeight);
334 * may or may not need an extra repaint on the alignment view (check what kira
338 public void showNodeSelectionOnAlign(final PhylogenyNode node)
341 if (node.isInternal())
343 showMatchingChildSequences(node);
348 showMatchingSequence(node);
359 public void showMatchingSequence(final PhylogenyNode nodeToMatch)
361 SequenceI matchingSequence = nodesBoundToSequences.get(nodeToMatch);
362 if (matchingSequence != null)
364 long nodeId = nodeToMatch.getId();
365 addOrRemoveInSet(treeView.getFoundNodes0(), nodeId);
366 treeSelectionChanged(matchingSequence);
367 parentAvport.sendSelection();
373 public void showMatchingChildSequences(final PhylogenyNode parentNode)
375 List<PhylogenyNode> childNodes = PhylogenyMethods
376 .getAllDescendants(parentNode);
379 for (PhylogenyNode childNode : childNodes)
381 // childNode.getBranchData().setBranchColor(new BranchColor(Color.BLUE));
383 SequenceI matchingSequence = nodesBoundToSequences.get(childNode);
384 if (matchingSequence != null)
386 long nodeId = childNode.getId();
387 addOrRemoveInSet(treeView.getFoundNodes0(), nodeId);
389 treeSelectionChanged(matchingSequence);
394 parentAvport.sendSelection();
400 * Refactored from TreeCanvas.
403 * of the node selected in the tree viewer.
406 public void treeSelectionChanged(final SequenceI sequence)
408 if (!parentAvport.isClosed()) // alignment view could be closed
410 SequenceGroup selected = parentAvport.getSelectionGroup();
412 if (selected == null)
414 selected = new SequenceGroup();
415 parentAvport.setSelectionGroup(selected);
418 selected.setEndRes(parentAvport.getAlignment().getWidth() - 1);
419 selected.addOrRemove(sequence, true);
423 public void sortByTree_actionPerformed() {
424 // parentAvport.mirrorCommand(command, undo, ssm, source);
427 // .addHistoryItem(sortAlignmentIn(treeCanvas.ap));
433 * sort the associated alignment view by the current tree.
438 // public void sortByTree_actionPerformed()// modify for Aptx
441 // // if (treeCanvas.applyToAllViews)
443 // final ArrayList<CommandI> commands = new ArrayList<>();
444 // for (AlignmentPanel ap : PaintRefresher
445 // .getAssociatedPanels(parentAvport.getSequenceSetId()))
447 // commands.add(sortAlignmentIn(ap.av.getAlignPanel()));
449 // av.getAlignPanel().alignFrame.addHistoryItem(new CommandI()
453 // public void undoCommand(AlignmentI[] views)
455 // for (CommandI tsort : commands)
457 // tsort.undoCommand(views);
462 // public int getSize()
464 // return commands.size();
468 // public String getDescription()
470 // return "Tree Sort (many views)";
474 // public void doCommand(AlignmentI[] views)
477 // for (CommandI tsort : commands)
479 // tsort.doCommand(views);
483 // for (AlignmentPanel ap : PaintRefresher
484 // .getAssociatedPanels(av.getSequenceSetId()))
486 // // ensure all the alignFrames refresh their GI after adding an undo item
487 // ap.alignFrame.updateEditMenuBar();
492 // treeCanvas.ap.alignFrame
493 // .addHistoryItem(sortAlignmentIn(treeCanvas.ap));
502 * @param objectToCheck
504 public static <E> void addOrRemoveInSet(Set<E> set, E objectToCheck)
506 if (set.contains(objectToCheck))
508 set.remove(objectToCheck);
512 set.add(objectToCheck);
517 public AlignmentViewport getParentAvport()
522 public void setParentAvport(final AlignmentViewport parentAvport)
524 this.parentAvport = parentAvport;