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