JAL-2794 reworked, everything routes via avport.sendselection 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.PaintRefresher;
9 import jalview.structure.SelectionSource;
10 import jalview.structure.StructureSelectionManager;
11 import jalview.viewmodel.AlignmentViewport;
12
13 import java.awt.event.ActionEvent;
14 import java.awt.event.InputEvent;
15 import java.awt.event.MouseEvent;
16 import java.util.HashSet;
17 import java.util.List;
18 import java.util.Map;
19 import java.util.Set;
20
21 import javax.swing.JTabbedPane;
22 import javax.swing.SwingUtilities;
23 import javax.swing.event.ChangeEvent;
24 import javax.swing.event.ChangeListener;
25
26 import org.forester.archaeopteryx.MainFrame;
27 import org.forester.phylogeny.Phylogeny;
28 import org.forester.phylogeny.PhylogenyMethods;
29 import org.forester.phylogeny.PhylogenyNode;
30
31 /**
32  * Class for binding the Archaeopteryx tree viewer to the Jalview alignment that
33  * it originates from, meaning that selecting sequences in the tree viewer also
34  * selects them in the alignment view and vice versa.
35  * 
36  * @author kjvanderheide
37  *
38  */
39 public final class JalviewBinding
40         implements ExternalTreeViewerBindingI<PhylogenyNode>
41 {
42   private org.forester.archaeopteryx.TreePanel treeView;
43
44   private AlignmentViewport parentAvport;
45
46   private JTabbedPane treeTabs;
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     // deal with/prohibit null values here as that will cause problems
75     parentAvport = jalviewAlignmentViewport;
76     sequencesBoundToNodes = alignMappedToNodes;
77     nodesBoundToSequences = nodesMappedToAlign;
78
79     treeView = archaeopteryx.getMainPanel().getCurrentTreePanel();
80     treeTabs = archaeopteryx.getMainPanel().getTabbedPane();
81     ssm = parentAvport.getStructureSelectionManager();
82
83     // archaeopteryx.getMainPanel().getControlPanel().setColorBranches(true);
84     
85     ssm.addSelectionListener(this);
86     treeView.addMouseListener(this);
87     PaintRefresher.Register(treeView, parentAvport.getSequenceSetId());
88
89
90     treeTabs.addChangeListener(new ChangeListener()
91     {
92
93       @Override
94       public void stateChanged(ChangeEvent e)
95       {
96
97         SwingUtilities.invokeLater(new Runnable()
98         {
99
100           @Override
101           /**
102            * Resend the selection to the tree view when tabs get switched, this
103            * has to be buried in invokeLater as Forester first resets the tree
104            * view on switching tabs, without invokeLater this would get called
105            * before Forester resets which would nullify the selection.
106            */
107           public void run()
108           {
109             parentAvport.sendSelection();
110             // PaintRefresher.Refresh(treeView,
111             // parentAvport.getSequenceSetId());
112
113           }
114         });
115
116       }
117       
118     });
119
120   }
121
122   @Override
123   public void actionPerformed(ActionEvent e)
124   {
125   }
126
127   @Override
128   public void mouseClicked(MouseEvent e)
129   {
130     SwingUtilities.invokeLater(new Runnable() {
131
132       @Override
133       /**
134        * invokeLater so that this always runs after Forester's mouseClicked
135        */
136       public void run()
137       {
138         final PhylogenyNode node = treeView.findNode(e.getX(), e.getY());
139         if (node != null)
140         {
141           if ((e.getModifiers() & InputEvent.SHIFT_MASK) == 0) // clear previous
142           // selection if shift
143           // IS NOT pressed
144           {
145             parentAvport.setSelectionGroup(null);
146
147           }
148           showNodeSelectionOnAlign(node);
149         }
150         else
151         {
152           partitionTree(e);
153         
154         
155       }
156       
157       }
158     });
159
160
161   }
162
163   @Override
164   public void mousePressed(final MouseEvent e)
165   {
166
167   }
168   @Override
169   public void mouseReleased(MouseEvent e)
170   {
171   }
172
173   @Override
174   public void mouseEntered(MouseEvent e)
175   {
176   }
177
178   @Override
179   public void mouseExited(MouseEvent e)
180   {
181   }
182
183
184   @Override
185   public void selection(final SequenceGroup seqsel,
186           final ColumnSelection colsel, final HiddenColumns hidden,
187           final SelectionSource source)
188   {
189     if (source == parentAvport) // check if source is alignment from where the
190     // tree originates
191     {
192       treeView.setFoundNodes0(
193               new HashSet<Long>(seqsel.getSequences().size()));
194
195       for (SequenceI selectedSequence : seqsel.getSequences())
196       {
197         PhylogenyNode matchingNode = sequencesBoundToNodes.get(selectedSequence);
198         if (matchingNode != null)
199         {
200           treeView.getFoundNodes0().add(matchingNode.getId());
201         }
202
203       }
204       treeView.repaint();
205
206     }
207
208
209   }
210
211   /**
212    * Partially refactored from TreeCanvas
213    */
214   public void partitionTree(final MouseEvent e)
215   {
216     int x = e.getX();
217
218     Phylogeny tree = treeView.getPhylogeny();
219     double treeHeight = tree.calculateHeight(true);
220
221
222     if (treeHeight != 0)
223     {
224       int viewWidth = treeView.getWidth();
225
226       // treeView.validate();
227
228       // System.out.println("selection");
229       // System.out.println(x);
230       // System.out.println("-------------");
231       // System.out.println("width");
232       // System.out.println(viewWidth);
233
234     }
235
236
237   }
238
239
240   @Override
241   public void showNodeSelectionOnAlign(final PhylogenyNode node)
242   {
243
244       if (node.isInternal())
245       {
246         showMatchingChildSequences(node);
247       }
248
249       else
250       {
251         showMatchingSequence(node);
252       }
253
254     }
255
256
257
258
259
260   @Override
261   public void showMatchingSequence(final PhylogenyNode nodeToMatch)
262   {
263     SequenceI matchingSequence = nodesBoundToSequences.get(nodeToMatch);
264     if (matchingSequence != null)
265     {
266       long nodeId = nodeToMatch.getId();
267       addOrRemoveInSet(treeView.getFoundNodes0(), nodeId);
268       treeSelectionChanged(matchingSequence);
269       parentAvport.sendSelection();
270
271     }
272   }
273
274   @Override
275   public void showMatchingChildSequences(final PhylogenyNode parentNode)
276   {
277     List<PhylogenyNode> childNodes = PhylogenyMethods
278             .getAllDescendants(parentNode);
279
280
281     for (PhylogenyNode childNode : childNodes)
282     {
283       // childNode.getBranchData().setBranchColor(new BranchColor(Color.BLUE));
284
285       SequenceI matchingSequence = nodesBoundToSequences.get(childNode);
286       if (matchingSequence != null)
287       {
288         long nodeId = childNode.getId();
289         addOrRemoveInSet(treeView.getFoundNodes0(), nodeId);
290
291         treeSelectionChanged(matchingSequence);
292
293       }
294
295     }
296     parentAvport.sendSelection();
297
298
299   }
300
301   /**
302    * Refactored from TreeCanvas.
303    * 
304    * @param sequence
305    *          of the node selected in the tree viewer.
306    */
307   @Override
308   public void treeSelectionChanged(final SequenceI sequence)
309   {
310     if (!parentAvport.isClosed()) // alignment view could be closed
311     {
312       SequenceGroup selected = parentAvport.getSelectionGroup();
313
314       if (selected == null)
315       {
316         selected = new SequenceGroup();
317         parentAvport.setSelectionGroup(selected);
318       }
319
320       selected.setEndRes(parentAvport.getAlignment().getWidth() - 1);
321         selected.addOrRemove(sequence, true);
322     }
323
324   }
325
326   /**
327    * TO BE MOVED
328    * 
329    * @param set
330    * @param objectToCheck
331    */
332   public <E> void addOrRemoveInSet(Set<E> set, E objectToCheck)
333   {
334     if (set.contains(objectToCheck))
335     {
336       set.remove(objectToCheck);
337     }
338     else
339     {
340       set.add(objectToCheck);
341     }
342
343   }
344
345   public AlignmentViewport getParentAvport()
346   {
347     return parentAvport;
348   }
349
350   public void setParentAvport(final AlignmentViewport parentAvport)
351   {
352     this.parentAvport = parentAvport;
353   }
354 }
355
356
357