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());
102 treeTabs.addChangeListener(new ChangeListener()
106 public void stateChanged(ChangeEvent e)
109 SwingUtilities.invokeLater(new Runnable()
114 * Resend the selection to the tree view when tabs get switched, this
115 * has to be buried in invokeLater as Forester first resets the tree
116 * view on switching tabs, without invokeLater this would get called
117 * before Forester resets which would nullify the selection.
121 treeView = archaeopteryx.getMainPanel().getCurrentTreePanel();
122 parentAvport.sendSelection();
123 // PaintRefresher.Refresh(treeView,
124 // parentAvport.getSequenceSetId());
136 public void actionPerformed(ActionEvent e)
141 public void mouseClicked(MouseEvent e)
143 SwingUtilities.invokeLater(new Runnable() {
147 * invokeLater so that this always runs after Forester's mouseClicked
151 final PhylogenyNode node = treeView.findNode(e.getX(), e.getY());
154 if ((e.getModifiers() & InputEvent.SHIFT_MASK) == 0) // clear previous
155 // selection if shift
158 parentAvport.setSelectionGroup(null);
161 showNodeSelectionOnAlign(node);
165 partitionTree(e.getX());
169 PaintRefresher.Refresh(treeView, parentAvport.getSequenceSetId());
177 public void mousePressed(final MouseEvent e)
182 public void mouseReleased(MouseEvent e)
187 public void mouseEntered(MouseEvent e)
192 public void mouseExited(MouseEvent e)
198 public void selection(final SequenceGroup seqsel,
199 final ColumnSelection colsel, final HiddenColumns hidden,
200 final SelectionSource source)
202 if (source == parentAvport) // check if source is alignment from where the
205 treeView.setFoundNodes0(
206 new HashSet<Long>(seqsel.getSequences().size()));
208 for (SequenceI selectedSequence : seqsel.getSequences())
210 PhylogenyNode matchingNode = sequencesBoundToNodes.get(selectedSequence);
211 if (matchingNode != null)
213 treeView.getFoundNodes0().add(matchingNode.getId());
225 * Partially refactored from TreeCanvas
227 public void partitionTree(final int x)
229 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);
240 // int viewWidth = treeView.getWidth();
242 // System.out.println("selection");
243 // System.out.println(x);
244 // System.out.println("-------------");
245 // System.out.println("width");
246 // System.out.println(viewWidth);
255 * may or may not need an extra repaint on the alignment view (check what kira
259 public void showNodeSelectionOnAlign(final PhylogenyNode node)
262 if (node.isInternal())
264 showMatchingChildSequences(node);
269 showMatchingSequence(node);
280 public void showMatchingSequence(final PhylogenyNode nodeToMatch)
282 SequenceI matchingSequence = nodesBoundToSequences.get(nodeToMatch);
283 if (matchingSequence != null)
285 long nodeId = nodeToMatch.getId();
286 addOrRemoveInSet(treeView.getFoundNodes0(), nodeId);
287 treeSelectionChanged(matchingSequence);
288 parentAvport.sendSelection();
294 public void showMatchingChildSequences(final PhylogenyNode parentNode)
296 List<PhylogenyNode> childNodes = PhylogenyMethods
297 .getAllDescendants(parentNode);
300 for (PhylogenyNode childNode : childNodes)
302 // childNode.getBranchData().setBranchColor(new BranchColor(Color.BLUE));
304 SequenceI matchingSequence = nodesBoundToSequences.get(childNode);
305 if (matchingSequence != null)
307 long nodeId = childNode.getId();
308 addOrRemoveInSet(treeView.getFoundNodes0(), nodeId);
310 treeSelectionChanged(matchingSequence);
315 parentAvport.sendSelection();
321 * Refactored from TreeCanvas.
324 * of the node selected in the tree viewer.
327 public void treeSelectionChanged(final SequenceI sequence)
329 if (!parentAvport.isClosed()) // alignment view could be closed
331 SequenceGroup selected = parentAvport.getSelectionGroup();
333 if (selected == null)
335 selected = new SequenceGroup();
336 parentAvport.setSelectionGroup(selected);
339 selected.setEndRes(parentAvport.getAlignment().getWidth() - 1);
340 selected.addOrRemove(sequence, true);
344 public void sortByTree_actionPerformed() {
345 // parentAvport.mirrorCommand(command, undo, ssm, source);
348 // .addHistoryItem(sortAlignmentIn(treeCanvas.ap));
354 * sort the associated alignment view by the current tree.
359 // public void sortByTree_actionPerformed()// modify for Aptx
362 // // if (treeCanvas.applyToAllViews)
364 // final ArrayList<CommandI> commands = new ArrayList<>();
365 // for (AlignmentPanel ap : PaintRefresher
366 // .getAssociatedPanels(parentAvport.getSequenceSetId()))
368 // commands.add(sortAlignmentIn(ap.av.getAlignPanel()));
370 // av.getAlignPanel().alignFrame.addHistoryItem(new CommandI()
374 // public void undoCommand(AlignmentI[] views)
376 // for (CommandI tsort : commands)
378 // tsort.undoCommand(views);
383 // public int getSize()
385 // return commands.size();
389 // public String getDescription()
391 // return "Tree Sort (many views)";
395 // public void doCommand(AlignmentI[] views)
398 // for (CommandI tsort : commands)
400 // tsort.doCommand(views);
404 // for (AlignmentPanel ap : PaintRefresher
405 // .getAssociatedPanels(av.getSequenceSetId()))
407 // // ensure all the alignFrames refresh their GI after adding an undo item
408 // ap.alignFrame.updateEditMenuBar();
413 // treeCanvas.ap.alignFrame
414 // .addHistoryItem(sortAlignmentIn(treeCanvas.ap));
423 * @param objectToCheck
425 public static <E> void addOrRemoveInSet(Set<E> set, E objectToCheck)
427 if (set.contains(objectToCheck))
429 set.remove(objectToCheck);
433 set.add(objectToCheck);
438 public AlignmentViewport getParentAvport()
443 public void setParentAvport(final AlignmentViewport parentAvport)
445 this.parentAvport = parentAvport;