JAL-2844 added selection threshold (non-adjusted calculation for now)
[jalview.git] / src / jalview / ext / archaeopteryx / JalviewBinding.java
1 package jalview.ext.archaeopteryx;
2
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;
15
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;
22 import java.util.Map;
23 import java.util.Set;
24
25 import javax.swing.JTabbedPane;
26 import javax.swing.SwingUtilities;
27 import javax.swing.event.ChangeEvent;
28 import javax.swing.event.ChangeListener;
29
30 import org.forester.archaeopteryx.MainFrame;
31 import org.forester.phylogeny.Phylogeny;
32 import org.forester.phylogeny.PhylogenyMethods;
33 import org.forester.phylogeny.PhylogenyNode;
34
35 /**
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.
39  * 
40  * @author kjvanderheide
41  *
42  */
43 public final class JalviewBinding
44         implements ExternalTreeViewerBindingI<PhylogenyNode>
45 {
46   private org.forester.archaeopteryx.TreePanel treeView;
47
48   private AlignmentViewport parentAvport;
49
50   private final JTabbedPane treeTabs;
51
52   private final StructureSelectionManager ssm;
53
54   private Map<SequenceI, PhylogenyNode> sequencesBoundToNodes;
55
56   private Map<PhylogenyNode, SequenceI> nodesBoundToSequences;
57
58   /**
59    * 
60    * @param archaeopteryx
61    * 
62    * @param jalviewAlignmentViewport
63    *          alignment viewport from which the tree was calculated.
64    * 
65    * @param alignMappedToNodes
66    *          map with sequences used to calculate the tree and matching tree
67    *          nodes as key, value pair respectively.
68    * 
69    * @param nodesMappedToAlign
70    *          map with tree nodes and matching sequences used to calculate the
71    *          tree as key, value pair respectively.
72    */
73   public JalviewBinding(final MainFrame archaeopteryx,
74           final AlignmentViewport jalviewAlignmentViewport,
75           final Map<SequenceI, PhylogenyNode> alignMappedToNodes,
76           final Map<PhylogenyNode, SequenceI> nodesMappedToAlign)
77   {
78
79     if (archaeopteryx.getMainPanel().getTabbedPane().getTabCount() > 1)
80     {
81       JvOptionPane.showMessageDialog(Desktop.desktop,
82               MessageManager.getString("label.tabs_detected_archaeopteryx"),
83               MessageManager.getString("label.problem_reading_tree_file"),
84               JvOptionPane.WARNING_MESSAGE);
85       ;
86     }
87
88     // deal with/prohibit null values here as that will cause problems
89     parentAvport = jalviewAlignmentViewport;
90     sequencesBoundToNodes = alignMappedToNodes;
91     nodesBoundToSequences = nodesMappedToAlign;
92
93     treeView = archaeopteryx.getMainPanel().getCurrentTreePanel();
94     treeTabs = archaeopteryx.getMainPanel().getTabbedPane();
95     ssm = parentAvport.getStructureSelectionManager();
96     
97     ssm.addSelectionListener(this);
98     treeView.addMouseListener(this);
99     PaintRefresher.Register(treeView, parentAvport.getSequenceSetId());
100
101     treeTabs.addChangeListener(new ChangeListener()
102     {
103
104       @Override
105       public void stateChanged(ChangeEvent e)
106       {
107
108         SwingUtilities.invokeLater(new Runnable()
109         {
110
111           @Override
112           /**
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.
117            */
118           public void run()
119           {
120             treeView = archaeopteryx.getMainPanel().getCurrentTreePanel();
121             parentAvport.sendSelection();
122             // PaintRefresher.Refresh(treeView,
123             // parentAvport.getSequenceSetId());
124
125           }
126         });
127
128       }
129
130     });
131
132   }
133
134   @Override
135   public void actionPerformed(ActionEvent e)
136   {
137   }
138
139   @Override
140   public void mouseClicked(MouseEvent e)
141   {
142     SwingUtilities.invokeLater(new Runnable() {
143
144       @Override
145       /**
146        * invokeLater so that this always runs after Forester's mouseClicked
147        */
148       public void run()
149       {
150         final PhylogenyNode node = treeView.findNode(e.getX(), e.getY());
151         if (node != null)
152         {
153           if ((e.getModifiers() & InputEvent.SHIFT_MASK) == 0) // clear previous
154           // selection if shift
155           // IS NOT pressed
156           {
157             parentAvport.setSelectionGroup(null);
158
159           }
160           showNodeSelectionOnAlign(node);
161         }
162         else
163         {
164           partitionTree(e.getX());
165
166       }
167       
168         PaintRefresher.Refresh(treeView, parentAvport.getSequenceSetId());
169       }
170     });
171
172
173   }
174
175   @Override
176   public void mousePressed(final MouseEvent e)
177   {
178
179   }
180   @Override
181   public void mouseReleased(MouseEvent e)
182   {
183   }
184
185   @Override
186   public void mouseEntered(MouseEvent e)
187   {
188   }
189
190   @Override
191   public void mouseExited(MouseEvent e)
192   {
193   }
194
195
196   @Override
197   public void selection(final SequenceGroup seqsel,
198           final ColumnSelection colsel, final HiddenColumns hidden,
199           final SelectionSource source)
200   {
201     if (source == parentAvport) // check if source is alignment from where the
202     // tree originates
203     {
204       treeView.setFoundNodes0(
205               new HashSet<Long>(seqsel.getSequences().size()));
206
207       for (SequenceI selectedSequence : seqsel.getSequences())
208       {
209         PhylogenyNode matchingNode = sequencesBoundToNodes.get(selectedSequence);
210         if (matchingNode != null)
211         {
212           treeView.getFoundNodes0().add(matchingNode.getId());
213         }
214
215       }
216       treeView.repaint();
217
218     }
219
220
221   }
222
223   /**
224    * Partially refactored from TreeCanvas
225    */
226   public void partitionTree(final int x)
227   {
228     Phylogeny tree = treeView.getPhylogeny();
229
230     if (!tree.isEmpty())
231     {
232       double treeDepth = tree.calculateHeight(true);
233       if (treeDepth != 0)
234       {
235         Graphics g = treeView.getGraphics();
236         int panelHeight = treeView.getHeight();
237         g.drawLine(x, 0, x, panelHeight);
238
239         int viewWidth = treeView.getWidth();
240         float threshold = (float) x / (float) viewWidth;
241
242       }
243     }
244
245
246   }
247   
248
249   /**
250    * may or may not need an extra repaint on the alignment view (check what kira
251    * does)
252    */
253   @Override
254   public void showNodeSelectionOnAlign(final PhylogenyNode node)
255   {
256
257       if (node.isInternal())
258       {
259         showMatchingChildSequences(node);
260       }
261
262       else
263       {
264         showMatchingSequence(node);
265       }
266
267
268     }
269
270
271
272
273
274   @Override
275   public void showMatchingSequence(final PhylogenyNode nodeToMatch)
276   {
277     SequenceI matchingSequence = nodesBoundToSequences.get(nodeToMatch);
278     if (matchingSequence != null)
279     {
280       long nodeId = nodeToMatch.getId();
281       addOrRemoveInSet(treeView.getFoundNodes0(), nodeId);
282       treeSelectionChanged(matchingSequence);
283       parentAvport.sendSelection();
284
285     }
286   }
287
288   @Override
289   public void showMatchingChildSequences(final PhylogenyNode parentNode)
290   {
291     List<PhylogenyNode> childNodes = PhylogenyMethods
292             .getAllDescendants(parentNode);
293
294
295     for (PhylogenyNode childNode : childNodes)
296     {
297       // childNode.getBranchData().setBranchColor(new BranchColor(Color.BLUE));
298
299       SequenceI matchingSequence = nodesBoundToSequences.get(childNode);
300       if (matchingSequence != null)
301       {
302         long nodeId = childNode.getId();
303         addOrRemoveInSet(treeView.getFoundNodes0(), nodeId);
304
305         treeSelectionChanged(matchingSequence);
306
307       }
308
309     }
310     parentAvport.sendSelection();
311
312
313   }
314
315   /**
316    * Refactored from TreeCanvas.
317    * 
318    * @param sequence
319    *          of the node selected in the tree viewer.
320    */
321   @Override
322   public void treeSelectionChanged(final SequenceI sequence)
323   {
324     if (!parentAvport.isClosed()) // alignment view could be closed
325     {
326       SequenceGroup selected = parentAvport.getSelectionGroup();
327
328       if (selected == null)
329       {
330         selected = new SequenceGroup();
331         parentAvport.setSelectionGroup(selected);
332       }
333
334       selected.setEndRes(parentAvport.getAlignment().getWidth() - 1);
335         selected.addOrRemove(sequence, true);
336     }
337
338   }
339   public void sortByTree_actionPerformed() {
340     // parentAvport.mirrorCommand(command, undo, ssm, source);
341
342     // alignFrame
343     // .addHistoryItem(sortAlignmentIn(treeCanvas.ap));
344     
345   }
346   
347
348   /**
349    * sort the associated alignment view by the current tree.
350    * 
351    * @param e
352    */
353   // @Override
354   // public void sortByTree_actionPerformed()// modify for Aptx
355   // {
356   //
357   // // if (treeCanvas.applyToAllViews)
358   //
359   // final ArrayList<CommandI> commands = new ArrayList<>();
360   // for (AlignmentPanel ap : PaintRefresher
361   // .getAssociatedPanels(parentAvport.getSequenceSetId()))
362   // {
363   // commands.add(sortAlignmentIn(ap.av.getAlignPanel()));
364   // }
365   // av.getAlignPanel().alignFrame.addHistoryItem(new CommandI()
366   // {
367   //
368   // @Override
369   // public void undoCommand(AlignmentI[] views)
370   // {
371   // for (CommandI tsort : commands)
372   // {
373   // tsort.undoCommand(views);
374   // }
375   // }
376   //
377   // @Override
378   // public int getSize()
379   // {
380   // return commands.size();
381   // }
382   //
383   // @Override
384   // public String getDescription()
385   // {
386   // return "Tree Sort (many views)";
387   // }
388   //
389   // @Override
390   // public void doCommand(AlignmentI[] views)
391   // {
392   //
393   // for (CommandI tsort : commands)
394   // {
395   // tsort.doCommand(views);
396   // }
397   // }
398   // });
399   // for (AlignmentPanel ap : PaintRefresher
400   // .getAssociatedPanels(av.getSequenceSetId()))
401   // {
402   // // ensure all the alignFrames refresh their GI after adding an undo item
403   // ap.alignFrame.updateEditMenuBar();
404   // }
405   // }
406   // else
407   // {
408   // treeCanvas.ap.alignFrame
409   // .addHistoryItem(sortAlignmentIn(treeCanvas.ap));
410   // }
411
412
413
414   /**
415    * TO BE MOVED
416    * 
417    * @param set
418    * @param objectToCheck
419    */
420   public static <E> void addOrRemoveInSet(Set<E> set, E objectToCheck)
421   {
422     if (set.contains(objectToCheck))
423     {
424       set.remove(objectToCheck);
425     }
426     else
427     {
428       set.add(objectToCheck);
429     }
430
431   }
432
433   public AlignmentViewport getParentAvport()
434   {
435     return parentAvport;
436   }
437
438   public void setParentAvport(final AlignmentViewport parentAvport)
439   {
440     this.parentAvport = parentAvport;
441   }
442 }
443
444
445