package jalview.ext.archaeopteryx; import jalview.datamodel.ColumnSelection; import jalview.datamodel.HiddenColumns; import jalview.datamodel.SequenceGroup; import jalview.datamodel.SequenceI; import jalview.ext.treeviewer.ExternalTreeViewerBindingI; import jalview.gui.PaintRefresher; import jalview.structure.SelectionSource; import jalview.structure.StructureSelectionManager; import jalview.viewmodel.AlignmentViewport; import java.awt.event.ActionEvent; import java.awt.event.InputEvent; import java.awt.event.MouseEvent; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import javax.swing.JTabbedPane; import javax.swing.SwingUtilities; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import org.forester.archaeopteryx.MainFrame; import org.forester.phylogeny.Phylogeny; import org.forester.phylogeny.PhylogenyMethods; import org.forester.phylogeny.PhylogenyNode; /** * Class for binding the Archaeopteryx tree viewer to the Jalview alignment that * it originates from, meaning that selecting sequences in the tree viewer also * selects them in the alignment view and vice versa. * * @author kjvanderheide * */ public final class JalviewBinding implements ExternalTreeViewerBindingI { private org.forester.archaeopteryx.TreePanel treeView; private AlignmentViewport parentAvport; private JTabbedPane treeTabs; private final StructureSelectionManager ssm; private Map sequencesBoundToNodes; private Map nodesBoundToSequences; /** * * @param archaeopteryx * * @param jalviewAlignmentViewport * alignment viewport from which the tree was calculated. * * @param alignMappedToNodes * map with sequences used to calculate the tree and matching tree * nodes as key, value pair respectively. * * @param nodesMappedToAlign * map with tree nodes and matching sequences used to calculate the * tree as key, value pair respectively. */ public JalviewBinding(final MainFrame archaeopteryx, final AlignmentViewport jalviewAlignmentViewport, final Map alignMappedToNodes, final Map nodesMappedToAlign) { // deal with/prohibit null values here as that will cause problems parentAvport = jalviewAlignmentViewport; sequencesBoundToNodes = alignMappedToNodes; nodesBoundToSequences = nodesMappedToAlign; treeView = archaeopteryx.getMainPanel().getCurrentTreePanel(); treeTabs = archaeopteryx.getMainPanel().getTabbedPane(); ssm = parentAvport.getStructureSelectionManager(); // archaeopteryx.getMainPanel().getControlPanel().setColorBranches(true); ssm.addSelectionListener(this); treeView.addMouseListener(this); PaintRefresher.Register(treeView, parentAvport.getSequenceSetId()); treeTabs.addChangeListener(new ChangeListener() { @Override public void stateChanged(ChangeEvent e) { SwingUtilities.invokeLater(new Runnable() { @Override /** * Resend the selection to the tree view when tabs get switched, this * has to be buried in invokeLater as Forester first resets the tree * view on switching tabs, without invokeLater this would get called * before Forester resets which would nullify the selection. */ public void run() { parentAvport.sendSelection(); // PaintRefresher.Refresh(treeView, // parentAvport.getSequenceSetId()); } }); } }); } @Override public void actionPerformed(ActionEvent e) { } @Override public void mouseClicked(MouseEvent e) { SwingUtilities.invokeLater(new Runnable() { @Override /** * invokeLater so that this always runs after Forester's mouseClicked */ public void run() { final PhylogenyNode node = treeView.findNode(e.getX(), e.getY()); if (node != null) { if ((e.getModifiers() & InputEvent.SHIFT_MASK) == 0) // clear previous // selection if shift // IS NOT pressed { parentAvport.setSelectionGroup(null); } showNodeSelectionOnAlign(node); } else { partitionTree(e); } } }); } @Override public void mousePressed(final MouseEvent e) { } @Override public void mouseReleased(MouseEvent e) { } @Override public void mouseEntered(MouseEvent e) { } @Override public void mouseExited(MouseEvent e) { } @Override public void selection(final SequenceGroup seqsel, final ColumnSelection colsel, final HiddenColumns hidden, final SelectionSource source) { if (source == parentAvport) // check if source is alignment from where the // tree originates { treeView.setFoundNodes0( new HashSet(seqsel.getSequences().size())); for (SequenceI selectedSequence : seqsel.getSequences()) { PhylogenyNode matchingNode = sequencesBoundToNodes.get(selectedSequence); if (matchingNode != null) { treeView.getFoundNodes0().add(matchingNode.getId()); } } treeView.repaint(); } } /** * Partially refactored from TreeCanvas */ public void partitionTree(final MouseEvent e) { int x = e.getX(); Phylogeny tree = treeView.getPhylogeny(); double treeHeight = tree.calculateHeight(true); if (treeHeight != 0) { int viewWidth = treeView.getWidth(); // treeView.validate(); // System.out.println("selection"); // System.out.println(x); // System.out.println("-------------"); // System.out.println("width"); // System.out.println(viewWidth); } } @Override public void showNodeSelectionOnAlign(final PhylogenyNode node) { if (node.isInternal()) { showMatchingChildSequences(node); } else { showMatchingSequence(node); } } @Override public void showMatchingSequence(final PhylogenyNode nodeToMatch) { SequenceI matchingSequence = nodesBoundToSequences.get(nodeToMatch); if (matchingSequence != null) { long nodeId = nodeToMatch.getId(); addOrRemoveInSet(treeView.getFoundNodes0(), nodeId); treeSelectionChanged(matchingSequence); parentAvport.sendSelection(); } } @Override public void showMatchingChildSequences(final PhylogenyNode parentNode) { List childNodes = PhylogenyMethods .getAllDescendants(parentNode); for (PhylogenyNode childNode : childNodes) { // childNode.getBranchData().setBranchColor(new BranchColor(Color.BLUE)); SequenceI matchingSequence = nodesBoundToSequences.get(childNode); if (matchingSequence != null) { long nodeId = childNode.getId(); addOrRemoveInSet(treeView.getFoundNodes0(), nodeId); treeSelectionChanged(matchingSequence); } } parentAvport.sendSelection(); } /** * Refactored from TreeCanvas. * * @param sequence * of the node selected in the tree viewer. */ @Override public void treeSelectionChanged(final SequenceI sequence) { if (!parentAvport.isClosed()) // alignment view could be closed { SequenceGroup selected = parentAvport.getSelectionGroup(); if (selected == null) { selected = new SequenceGroup(); parentAvport.setSelectionGroup(selected); } selected.setEndRes(parentAvport.getAlignment().getWidth() - 1); selected.addOrRemove(sequence, true); } } /** * TO BE MOVED * * @param set * @param objectToCheck */ public void addOrRemoveInSet(Set set, E objectToCheck) { if (set.contains(objectToCheck)) { set.remove(objectToCheck); } else { set.add(objectToCheck); } } public AlignmentViewport getParentAvport() { return parentAvport; } public void setParentAvport(final AlignmentViewport parentAvport) { this.parentAvport = parentAvport; } }