JAL-2799 fixed selections not carrying over to tabs with invokeLater
[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
20 import javax.swing.JTabbedPane;
21 import javax.swing.SwingUtilities;
22 import javax.swing.event.ChangeEvent;
23 import javax.swing.event.ChangeListener;
24
25 import org.forester.archaeopteryx.MainFrame;
26 import org.forester.phylogeny.PhylogenyMethods;
27 import org.forester.phylogeny.PhylogenyNode;
28
29 /**
30  * Class for binding the Archaeopteryx tree viewer to the Jalview alignment that
31  * it originates from, meaning that selecting sequences in the tree viewer also
32  * selects them in the alignment view and vice versa.
33  * 
34  * @author kjvanderheide
35  *
36  */
37 public final class JalviewBinding
38         implements ExternalTreeViewerBindingI<PhylogenyNode>
39 {
40   private org.forester.archaeopteryx.TreePanel treeView;
41
42   private AlignmentViewport parentAvport;
43
44   private JTabbedPane treeTabs;
45
46   private final StructureSelectionManager ssm;
47
48   private Map<SequenceI, PhylogenyNode> sequencesBoundToNodes;
49
50   private Map<PhylogenyNode, SequenceI> nodesBoundToSequences;
51
52   /**
53    * 
54    * @param archaeopteryx
55    * 
56    * @param jalviewAlignmentViewport
57    *          alignment viewport from which the tree was calculated.
58    * 
59    * @param alignMappedToNodes
60    *          map with sequences used to calculate the tree and matching tree
61    *          nodes as key, value pair respectively.
62    * 
63    * @param nodesMappedToAlign
64    *          map with tree nodes and matching sequences used to calculate the
65    *          tree as key, value pair respectively.
66    */
67   public JalviewBinding(final MainFrame archaeopteryx,
68           final AlignmentViewport jalviewAlignmentViewport,
69           final Map<SequenceI, PhylogenyNode> alignMappedToNodes,
70           final Map<PhylogenyNode, SequenceI> nodesMappedToAlign)
71   {
72     // deal with/prohibit null values here as that will cause problems
73     parentAvport = jalviewAlignmentViewport;
74     sequencesBoundToNodes = alignMappedToNodes;
75     nodesBoundToSequences = nodesMappedToAlign;
76
77     treeView = archaeopteryx.getMainPanel().getCurrentTreePanel();
78     treeTabs = archaeopteryx.getMainPanel().getTabbedPane();
79     ssm = parentAvport.getStructureSelectionManager();
80
81    // archaeopteryx.getMainPanel().getControlPanel().setColorBranches(true);
82     
83     ssm.addSelectionListener(this);
84     treeView.addMouseListener(this);
85     PaintRefresher.Register(treeView, parentAvport.getSequenceSetId());
86
87
88     treeTabs.addChangeListener(new ChangeListener()
89     {
90
91       @Override
92       public void stateChanged(ChangeEvent e)
93       {
94
95         SwingUtilities.invokeLater(new Runnable()
96         {
97
98           @Override
99           /**
100            * Resend the selection to the tree view when tabs get switched, this
101            * has to be buried in invokeLater as Forester first resets the tree
102            * view on switching tabs, without invokeLater this would get called
103            * before Forester resets which would nullify the selection
104            */
105           public void run()
106           {
107             parentAvport.sendSelection();
108
109           }
110         });
111
112       }
113       
114     });
115
116   }
117
118   @Override
119   public void actionPerformed(ActionEvent e)
120   {
121   }
122
123   @Override
124   public void mouseClicked(MouseEvent e)
125   {
126   }
127
128   @Override
129   public void mousePressed(final MouseEvent e)
130   {
131     showNodeSelectionOnAlign(e);
132   }
133
134   @Override
135   public void mouseReleased(MouseEvent e)
136   {
137   }
138
139   @Override
140   public void mouseEntered(MouseEvent e)
141   {
142   }
143
144   @Override
145   public void mouseExited(MouseEvent e)
146   {
147   }
148
149
150   @Override
151   public void selection(final SequenceGroup seqsel,
152           final ColumnSelection colsel, final HiddenColumns hidden,
153           final SelectionSource source)
154   {
155     if (source == parentAvport) // check if source is alignment from where the
156     // tree originates
157     {
158       treeView.setFoundNodes0(
159               new HashSet<Long>(seqsel.getSequences().size()));
160
161       for (SequenceI selectedSequence : seqsel.getSequences())
162       {
163         PhylogenyNode matchingNode = sequencesBoundToNodes.get(selectedSequence);
164         if (matchingNode != null)
165         {
166           treeView.getFoundNodes0().add(matchingNode.getId());
167         }
168
169       }
170       PaintRefresher.Refresh(treeView, parentAvport.getSequenceSetId());
171
172     }
173
174
175   }
176
177
178   @Override
179   public void showNodeSelectionOnAlign(final MouseEvent e)
180   {
181     final PhylogenyNode node = treeView.findNode(e.getX(), e.getY());
182     if (node != null)
183     {
184       if ((e.getModifiers() & InputEvent.SHIFT_MASK) == 0) // clear previous
185                                                            // selection if shift
186                                                            // IS NOT pressed
187       {
188         parentAvport.setSelectionGroup(null);
189       }
190
191       if (node.isInternal())
192       {
193         showMatchingChildSequences(node);
194       }
195
196       else
197       {
198         showMatchingSequence(node);
199       }
200
201     }
202   }
203
204
205
206
207   @Override
208   public void showMatchingSequence(final PhylogenyNode nodeToMatch)
209   {
210     SequenceI matchingSequence = nodesBoundToSequences.get(nodeToMatch);
211     if (matchingSequence != null)
212     {
213       treeSelectionChanged(matchingSequence);
214       parentAvport.sendSelection();
215       PaintRefresher.Refresh(treeView, parentAvport.getSequenceSetId());
216     }
217   }
218
219   @Override
220   public void showMatchingChildSequences(final PhylogenyNode parentNode)
221   {
222     List<PhylogenyNode> childNodes = PhylogenyMethods
223             .getAllDescendants(parentNode);
224
225     for (PhylogenyNode childNode : childNodes)
226     {
227       // childNode.getBranchData().setBranchColor(new BranchColor(Color.BLUE));
228
229       SequenceI matchingSequence = nodesBoundToSequences.get(childNode);
230       if (matchingSequence != null)
231       {
232         treeSelectionChanged(matchingSequence);
233
234       }
235     }
236     parentAvport.sendSelection();
237     PaintRefresher.Refresh(treeView, parentAvport.getSequenceSetId());
238   }
239
240   /**
241    * Refactored from TreeCanvas.
242    * 
243    * @param sequence
244    *          of the node selected in the tree viewer.
245    */
246   @Override
247   public void treeSelectionChanged(final SequenceI sequence)
248   {
249     SequenceGroup selected = parentAvport.getSelectionGroup();
250
251     if (selected == null)
252     {
253       selected = new SequenceGroup();
254       parentAvport.setSelectionGroup(selected);
255     }
256
257     selected.setEndRes(parentAvport.getAlignment().getWidth() - 1);
258     selected.addOrRemove(sequence, true);
259
260   }
261
262   public AlignmentViewport getParentAvport()
263   {
264     return parentAvport;
265   }
266
267   public void setParentAvport(final AlignmentViewport parentAvport)
268   {
269     this.parentAvport = parentAvport;
270   }
271 }
272
273
274