JAL-2799 re-enabled change listener for Aptx's tabs (for future work)
[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
102     treeTabs.addChangeListener(new ChangeListener()
103     {
104
105       @Override
106       public void stateChanged(ChangeEvent e)
107       {
108
109         SwingUtilities.invokeLater(new Runnable()
110         {
111
112           @Override
113           /**
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.
118            */
119           public void run()
120           {
121             treeView = archaeopteryx.getMainPanel().getCurrentTreePanel();
122             parentAvport.sendSelection();
123             // PaintRefresher.Refresh(treeView,
124             // parentAvport.getSequenceSetId());
125
126           }
127         });
128
129       }
130
131     });
132
133   }
134
135   @Override
136   public void actionPerformed(ActionEvent e)
137   {
138   }
139
140   @Override
141   public void mouseClicked(MouseEvent e)
142   {
143     SwingUtilities.invokeLater(new Runnable() {
144
145       @Override
146       /**
147        * invokeLater so that this always runs after Forester's mouseClicked
148        */
149       public void run()
150       {
151         final PhylogenyNode node = treeView.findNode(e.getX(), e.getY());
152         if (node != null)
153         {
154           if ((e.getModifiers() & InputEvent.SHIFT_MASK) == 0) // clear previous
155           // selection if shift
156           // IS NOT pressed
157           {
158             parentAvport.setSelectionGroup(null);
159
160           }
161           showNodeSelectionOnAlign(node);
162         }
163         else
164         {
165           partitionTree(e.getX());
166
167       }
168       
169         PaintRefresher.Refresh(treeView, parentAvport.getSequenceSetId());
170       }
171     });
172
173
174   }
175
176   @Override
177   public void mousePressed(final MouseEvent e)
178   {
179
180   }
181   @Override
182   public void mouseReleased(MouseEvent e)
183   {
184   }
185
186   @Override
187   public void mouseEntered(MouseEvent e)
188   {
189   }
190
191   @Override
192   public void mouseExited(MouseEvent e)
193   {
194   }
195
196
197   @Override
198   public void selection(final SequenceGroup seqsel,
199           final ColumnSelection colsel, final HiddenColumns hidden,
200           final SelectionSource source)
201   {
202     if (source == parentAvport) // check if source is alignment from where the
203     // tree originates
204     {
205       treeView.setFoundNodes0(
206               new HashSet<Long>(seqsel.getSequences().size()));
207
208       for (SequenceI selectedSequence : seqsel.getSequences())
209       {
210         PhylogenyNode matchingNode = sequencesBoundToNodes.get(selectedSequence);
211         if (matchingNode != null)
212         {
213           treeView.getFoundNodes0().add(matchingNode.getId());
214         }
215
216       }
217       treeView.repaint();
218
219     }
220
221
222   }
223
224   /**
225    * Partially refactored from TreeCanvas
226    */
227   public void partitionTree(final int x)
228   {
229     Phylogeny tree = treeView.getPhylogeny();
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
240         // int viewWidth = treeView.getWidth();
241
242         // System.out.println("selection");
243         // System.out.println(x);
244         // System.out.println("-------------");
245         // System.out.println("width");
246         // System.out.println(viewWidth);
247       }
248     }
249
250
251   }
252   
253
254   /**
255    * may or may not need an extra repaint on the alignment view (check what kira
256    * does)
257    */
258   @Override
259   public void showNodeSelectionOnAlign(final PhylogenyNode node)
260   {
261
262       if (node.isInternal())
263       {
264         showMatchingChildSequences(node);
265       }
266
267       else
268       {
269         showMatchingSequence(node);
270       }
271
272
273     }
274
275
276
277
278
279   @Override
280   public void showMatchingSequence(final PhylogenyNode nodeToMatch)
281   {
282     SequenceI matchingSequence = nodesBoundToSequences.get(nodeToMatch);
283     if (matchingSequence != null)
284     {
285       long nodeId = nodeToMatch.getId();
286       addOrRemoveInSet(treeView.getFoundNodes0(), nodeId);
287       treeSelectionChanged(matchingSequence);
288       parentAvport.sendSelection();
289
290     }
291   }
292
293   @Override
294   public void showMatchingChildSequences(final PhylogenyNode parentNode)
295   {
296     List<PhylogenyNode> childNodes = PhylogenyMethods
297             .getAllDescendants(parentNode);
298
299
300     for (PhylogenyNode childNode : childNodes)
301     {
302       // childNode.getBranchData().setBranchColor(new BranchColor(Color.BLUE));
303
304       SequenceI matchingSequence = nodesBoundToSequences.get(childNode);
305       if (matchingSequence != null)
306       {
307         long nodeId = childNode.getId();
308         addOrRemoveInSet(treeView.getFoundNodes0(), nodeId);
309
310         treeSelectionChanged(matchingSequence);
311
312       }
313
314     }
315     parentAvport.sendSelection();
316
317
318   }
319
320   /**
321    * Refactored from TreeCanvas.
322    * 
323    * @param sequence
324    *          of the node selected in the tree viewer.
325    */
326   @Override
327   public void treeSelectionChanged(final SequenceI sequence)
328   {
329     if (!parentAvport.isClosed()) // alignment view could be closed
330     {
331       SequenceGroup selected = parentAvport.getSelectionGroup();
332
333       if (selected == null)
334       {
335         selected = new SequenceGroup();
336         parentAvport.setSelectionGroup(selected);
337       }
338
339       selected.setEndRes(parentAvport.getAlignment().getWidth() - 1);
340         selected.addOrRemove(sequence, true);
341     }
342
343   }
344   public void sortByTree_actionPerformed() {
345     // parentAvport.mirrorCommand(command, undo, ssm, source);
346
347     // alignFrame
348     // .addHistoryItem(sortAlignmentIn(treeCanvas.ap));
349     
350   }
351   
352
353   /**
354    * sort the associated alignment view by the current tree.
355    * 
356    * @param e
357    */
358   // @Override
359   // public void sortByTree_actionPerformed()// modify for Aptx
360   // {
361   //
362   // // if (treeCanvas.applyToAllViews)
363   //
364   // final ArrayList<CommandI> commands = new ArrayList<>();
365   // for (AlignmentPanel ap : PaintRefresher
366   // .getAssociatedPanels(parentAvport.getSequenceSetId()))
367   // {
368   // commands.add(sortAlignmentIn(ap.av.getAlignPanel()));
369   // }
370   // av.getAlignPanel().alignFrame.addHistoryItem(new CommandI()
371   // {
372   //
373   // @Override
374   // public void undoCommand(AlignmentI[] views)
375   // {
376   // for (CommandI tsort : commands)
377   // {
378   // tsort.undoCommand(views);
379   // }
380   // }
381   //
382   // @Override
383   // public int getSize()
384   // {
385   // return commands.size();
386   // }
387   //
388   // @Override
389   // public String getDescription()
390   // {
391   // return "Tree Sort (many views)";
392   // }
393   //
394   // @Override
395   // public void doCommand(AlignmentI[] views)
396   // {
397   //
398   // for (CommandI tsort : commands)
399   // {
400   // tsort.doCommand(views);
401   // }
402   // }
403   // });
404   // for (AlignmentPanel ap : PaintRefresher
405   // .getAssociatedPanels(av.getSequenceSetId()))
406   // {
407   // // ensure all the alignFrames refresh their GI after adding an undo item
408   // ap.alignFrame.updateEditMenuBar();
409   // }
410   // }
411   // else
412   // {
413   // treeCanvas.ap.alignFrame
414   // .addHistoryItem(sortAlignmentIn(treeCanvas.ap));
415   // }
416
417
418
419   /**
420    * TO BE MOVED
421    * 
422    * @param set
423    * @param objectToCheck
424    */
425   public static <E> void addOrRemoveInSet(Set<E> set, E objectToCheck)
426   {
427     if (set.contains(objectToCheck))
428     {
429       set.remove(objectToCheck);
430     }
431     else
432     {
433       set.add(objectToCheck);
434     }
435
436   }
437
438   public AlignmentViewport getParentAvport()
439   {
440     return parentAvport;
441   }
442
443   public void setParentAvport(final AlignmentViewport parentAvport)
444   {
445     this.parentAvport = parentAvport;
446   }
447 }
448
449
450