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.HashSet;
21 import java.util.List;
25 import javax.swing.JTabbedPane;
26 import javax.swing.SwingUtilities;
27 import javax.swing.event.ChangeEvent;
28 import javax.swing.event.ChangeListener;
30 import org.forester.archaeopteryx.MainFrame;
31 import org.forester.phylogeny.Phylogeny;
32 import org.forester.phylogeny.PhylogenyMethods;
33 import org.forester.phylogeny.PhylogenyNode;
36 * Class for binding the Archaeopteryx tree viewer to the Jalview alignment that
37 * it originates from, meaning that selecting sequences in the tree viewer also
38 * selects them in the alignment view and vice versa.
40 * @author kjvanderheide
43 public final class JalviewBinding
44 implements ExternalTreeViewerBindingI<PhylogenyNode>
46 private org.forester.archaeopteryx.TreePanel treeView;
48 private AlignmentViewport parentAvport;
50 private final JTabbedPane treeTabs;
52 private final StructureSelectionManager ssm;
54 private Map<SequenceI, PhylogenyNode> sequencesBoundToNodes;
56 private Map<PhylogenyNode, SequenceI> nodesBoundToSequences;
60 * @param archaeopteryx
62 * @param jalviewAlignmentViewport
63 * alignment viewport from which the tree was calculated.
65 * @param alignMappedToNodes
66 * map with sequences used to calculate the tree and matching tree
67 * nodes as key, value pair respectively.
69 * @param nodesMappedToAlign
70 * map with tree nodes and matching sequences used to calculate the
71 * tree as key, value pair respectively.
73 public JalviewBinding(final MainFrame archaeopteryx,
74 final AlignmentViewport jalviewAlignmentViewport,
75 final Map<SequenceI, PhylogenyNode> alignMappedToNodes,
76 final Map<PhylogenyNode, SequenceI> nodesMappedToAlign)
79 if (archaeopteryx.getMainPanel().getTabbedPane().getTabCount() > 1)
81 JvOptionPane.showMessageDialog(Desktop.desktop,
82 MessageManager.getString("label.tabs_detected_archaeopteryx"),
83 MessageManager.getString("label.problem_reading_tree_file"),
84 JvOptionPane.WARNING_MESSAGE);
88 // deal with/prohibit null values here as that will cause problems
89 parentAvport = jalviewAlignmentViewport;
90 sequencesBoundToNodes = alignMappedToNodes;
91 nodesBoundToSequences = nodesMappedToAlign;
93 treeView = archaeopteryx.getMainPanel().getCurrentTreePanel();
94 treeTabs = archaeopteryx.getMainPanel().getTabbedPane();
95 ssm = parentAvport.getStructureSelectionManager();
97 ssm.addSelectionListener(this);
98 treeView.addMouseListener(this);
99 PaintRefresher.Register(treeView, parentAvport.getSequenceSetId());
101 treeTabs.addChangeListener(new ChangeListener()
105 public void stateChanged(ChangeEvent e)
108 SwingUtilities.invokeLater(new Runnable()
113 * Resend the selection to the tree view when tabs get switched, this
114 * has to be buried in invokeLater as Forester first resets the tree
115 * view on switching tabs, without invokeLater this would get called
116 * before Forester resets which would nullify the selection.
120 treeView = archaeopteryx.getMainPanel().getCurrentTreePanel();
121 parentAvport.sendSelection();
122 // PaintRefresher.Refresh(treeView,
123 // parentAvport.getSequenceSetId());
135 public void actionPerformed(ActionEvent e)
140 public void mouseClicked(MouseEvent e)
142 SwingUtilities.invokeLater(new Runnable() {
146 * invokeLater so that this always runs after Forester's mouseClicked
150 final PhylogenyNode node = treeView.findNode(e.getX(), e.getY());
153 if ((e.getModifiers() & InputEvent.SHIFT_MASK) == 0) // clear previous
154 // selection if shift
157 parentAvport.setSelectionGroup(null);
160 showNodeSelectionOnAlign(node);
164 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 treeDepth = tree.calculateHeight(true);
235 Graphics g = treeView.getGraphics();
236 int panelHeight = treeView.getHeight();
237 g.drawLine(x, 0, x, panelHeight);
239 int viewWidth = treeView.getWidth();
240 float threshold = (float) x / (float) viewWidth;
250 * may or may not need an extra repaint on the alignment view (check what kira
254 public void showNodeSelectionOnAlign(final PhylogenyNode node)
257 if (node.isInternal())
259 showMatchingChildSequences(node);
264 showMatchingSequence(node);
275 public void showMatchingSequence(final PhylogenyNode nodeToMatch)
277 SequenceI matchingSequence = nodesBoundToSequences.get(nodeToMatch);
278 if (matchingSequence != null)
280 long nodeId = nodeToMatch.getId();
281 addOrRemoveInSet(treeView.getFoundNodes0(), nodeId);
282 treeSelectionChanged(matchingSequence);
283 parentAvport.sendSelection();
289 public void showMatchingChildSequences(final PhylogenyNode parentNode)
291 List<PhylogenyNode> childNodes = PhylogenyMethods
292 .getAllDescendants(parentNode);
295 for (PhylogenyNode childNode : childNodes)
297 // childNode.getBranchData().setBranchColor(new BranchColor(Color.BLUE));
299 SequenceI matchingSequence = nodesBoundToSequences.get(childNode);
300 if (matchingSequence != null)
302 long nodeId = childNode.getId();
303 addOrRemoveInSet(treeView.getFoundNodes0(), nodeId);
305 treeSelectionChanged(matchingSequence);
310 parentAvport.sendSelection();
316 * Refactored from TreeCanvas.
319 * of the node selected in the tree viewer.
322 public void treeSelectionChanged(final SequenceI sequence)
324 if (!parentAvport.isClosed()) // alignment view could be closed
326 SequenceGroup selected = parentAvport.getSelectionGroup();
328 if (selected == null)
330 selected = new SequenceGroup();
331 parentAvport.setSelectionGroup(selected);
334 selected.setEndRes(parentAvport.getAlignment().getWidth() - 1);
335 selected.addOrRemove(sequence, true);
339 public void sortByTree_actionPerformed() {
340 // parentAvport.mirrorCommand(command, undo, ssm, source);
343 // .addHistoryItem(sortAlignmentIn(treeCanvas.ap));
349 * sort the associated alignment view by the current tree.
354 // public void sortByTree_actionPerformed()// modify for Aptx
357 // // if (treeCanvas.applyToAllViews)
359 // final ArrayList<CommandI> commands = new ArrayList<>();
360 // for (AlignmentPanel ap : PaintRefresher
361 // .getAssociatedPanels(parentAvport.getSequenceSetId()))
363 // commands.add(sortAlignmentIn(ap.av.getAlignPanel()));
365 // av.getAlignPanel().alignFrame.addHistoryItem(new CommandI()
369 // public void undoCommand(AlignmentI[] views)
371 // for (CommandI tsort : commands)
373 // tsort.undoCommand(views);
378 // public int getSize()
380 // return commands.size();
384 // public String getDescription()
386 // return "Tree Sort (many views)";
390 // public void doCommand(AlignmentI[] views)
393 // for (CommandI tsort : commands)
395 // tsort.doCommand(views);
399 // for (AlignmentPanel ap : PaintRefresher
400 // .getAssociatedPanels(av.getSequenceSetId()))
402 // // ensure all the alignFrames refresh their GI after adding an undo item
403 // ap.alignFrame.updateEditMenuBar();
408 // treeCanvas.ap.alignFrame
409 // .addHistoryItem(sortAlignmentIn(treeCanvas.ap));
418 * @param objectToCheck
420 public static <E> void addOrRemoveInSet(Set<E> set, E objectToCheck)
422 if (set.contains(objectToCheck))
424 set.remove(objectToCheck);
428 set.add(objectToCheck);
433 public AlignmentViewport getParentAvport()
438 public void setParentAvport(final AlignmentViewport parentAvport)
440 this.parentAvport = parentAvport;