JAL-1620 version bump and release notes
[jalview.git] / src / jalview / gui / AppVarna.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer (Version 2.8.2b1)
3  * Copyright (C) 2014 The Jalview Authors
4  * 
5  * This file is part of Jalview.
6  * 
7  * Jalview is free software: you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License 
9  * as published by the Free Software Foundation, either version 3
10  * of the License, or (at your option) any later version.
11  *  
12  * Jalview is distributed in the hope that it will be useful, but 
13  * WITHOUT ANY WARRANTY; without even the implied warranty 
14  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
15  * PURPOSE.  See the GNU General Public License for more details.
16  * 
17  * You should have received a copy of the GNU General Public License
18  * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
19  * The Jalview Authors are detailed in the 'AUTHORS' file.
20  */
21 package jalview.gui;
22
23 import jalview.bin.Cache;
24 import jalview.datamodel.ColumnSelection;
25 import jalview.datamodel.SequenceGroup;
26 import jalview.datamodel.SequenceI;
27 import jalview.structure.SecondaryStructureListener;
28 import jalview.structure.SelectionListener;
29 import jalview.structure.SelectionSource;
30 import jalview.structure.StructureSelectionManager;
31 import jalview.structure.VamsasSource;
32 import jalview.util.ShiftList;
33
34 import java.awt.BorderLayout;
35 import java.awt.Color;
36 import java.util.ArrayList;
37 import java.util.Hashtable;
38 import java.util.Map;
39 import java.util.regex.Matcher;
40 import java.util.regex.Pattern;
41
42 import javax.swing.JInternalFrame;
43 import javax.swing.JSplitPane;
44
45 import jalview.bin.Cache;
46 import jalview.util.MessageManager;
47 import jalview.util.ShiftList;
48
49 import fr.orsay.lri.varna.VARNAPanel;
50 import fr.orsay.lri.varna.exceptions.ExceptionFileFormatOrSyntax;
51 import fr.orsay.lri.varna.exceptions.ExceptionUnmatchedClosingParentheses;
52 import fr.orsay.lri.varna.interfaces.InterfaceVARNAListener;
53 import fr.orsay.lri.varna.interfaces.InterfaceVARNASelectionListener;
54 import fr.orsay.lri.varna.models.BaseList;
55 import fr.orsay.lri.varna.models.VARNAConfig;
56 import fr.orsay.lri.varna.models.annotations.HighlightRegionAnnotation;
57 import fr.orsay.lri.varna.models.rna.ModeleBase;
58 import fr.orsay.lri.varna.models.rna.RNA;
59
60 public class AppVarna extends JInternalFrame implements
61         InterfaceVARNAListener, SelectionListener,
62         SecondaryStructureListener// implements
63                                   // Runnable,SequenceStructureBinding,
64                                   // ViewSetProvider
65         , InterfaceVARNASelectionListener, VamsasSource
66
67 {
68   AppVarnaBinding vab;
69
70   VARNAPanel varnaPanel;
71
72   public String name;
73
74   public StructureSelectionManager ssm;
75
76   /*
77    * public AppVarna(){ vab = new AppVarnaBinding(); initVarna(); }
78    */
79
80   AlignmentPanel ap;
81
82   public AppVarna(String sname, SequenceI seq, String strucseq,
83           String struc, String name, AlignmentPanel ap)
84   {
85
86     // System.out.println("1:"+sname);
87     // System.out.println("2:"+seq);
88     // System.out.println("3:"+strucseq);
89     // System.out.println("4:"+struc);
90     // System.out.println("5:"+name);
91     // System.out.println("6:"+ap);
92     this.ap = ap;
93     ArrayList<RNA> rnaList = new ArrayList<RNA>();
94     RNA rna1 = new RNA(name);
95     try
96     {
97
98       rna1.setRNA(strucseq, replaceOddGaps(struc));
99       // System.out.println("The sequence is :"+rna1.getSeq());
100       // System.out.println("The sequence is:"+struc);
101       // System.out.println("The sequence is:"+replaceOddGaps(struc).toString());
102     } catch (ExceptionUnmatchedClosingParentheses e2)
103     {
104       e2.printStackTrace();
105     } catch (ExceptionFileFormatOrSyntax e3)
106     {
107       e3.printStackTrace();
108     }
109     RNA trim = trimRNA(rna1, "trimmed " + sname);
110     rnaList.add(trim);
111     rnaList.add(rna1);
112
113     rnas.put(seq, rna1);
114     rnas.put(seq, trim);
115     rna1.setName(sname + " (with gaps)");
116
117     {
118       seqs.put(trim, seq);
119       seqs.put(rna1, seq);
120
121       /**
122        * if (false || seq.getStart()!=1) { for (RNA rshift:rnaList) { ShiftList
123        * shift=offsets.get(rshift); if (shift==null) { offsets.put(rshift,
124        * shift=new ShiftList());} shift.addShift(1, seq.getStart()-1);
125        * offsetsInv.put(rshift, shift.getInverse()); } }
126        **/
127     }
128     vab = new AppVarnaBinding(rnaList);
129     // vab = new AppVarnaBinding(seq,struc);
130     this.name = sname + " trimmed to " + name;
131     initVarna();
132
133     ssm = ap.getStructureSelectionManager();
134     // System.out.println(ssm.toString());
135     ssm.addStructureViewerListener(this);
136     ssm.addSelectionListener(this);
137   }
138
139   public void initVarna()
140   {
141
142     // vab.setFinishedInit(false);
143     varnaPanel = vab.get_varnaPanel();
144     setBackground(Color.white);
145     JSplitPane split = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, true,
146             vab.getListPanel(), varnaPanel);
147     getContentPane().setLayout(new BorderLayout());
148     getContentPane().add(split, BorderLayout.CENTER);
149     // getContentPane().add(vab.getTools(), BorderLayout.NORTH);
150     varnaPanel.addVARNAListener(this);
151     varnaPanel.addSelectionListener(this);
152     jalview.gui.Desktop.addInternalFrame(this,
153             MessageManager.formatMessage("label.varna_params", new String[]
154             { name }), getBounds().width, getBounds().height);
155     this.pack();
156     showPanel(true);
157
158   }
159
160   public String replaceOddGaps(String oldStr)
161   {
162     String patternStr = "[^([{<>}])]";
163     String replacementStr = ".";
164     Pattern pattern = Pattern.compile(patternStr);
165     Matcher matcher = pattern.matcher(oldStr);
166     String newStr = matcher.replaceAll(replacementStr);
167     return newStr;
168   }
169
170   public RNA trimRNA(RNA rna, String name)
171   {
172     ShiftList offset = new ShiftList();
173
174     RNA rnaTrim = new RNA(name);
175     try
176     {
177       rnaTrim.setRNA(rna.getSeq(), replaceOddGaps(rna.getStructDBN()));
178     } catch (ExceptionUnmatchedClosingParentheses e2)
179     {
180       e2.printStackTrace();
181     } catch (ExceptionFileFormatOrSyntax e3)
182     {
183       e3.printStackTrace();
184     }
185
186     StringBuffer seq = new StringBuffer(rnaTrim.getSeq());
187     StringBuffer struc = new StringBuffer(rnaTrim.getStructDBN());
188     int ofstart = -1, sleng = rnaTrim.getSeq().length();
189     for (int i = 0; i < sleng; i++)
190     {
191       // TODO: Jalview utility for gap detection java.utils.isGap()
192       // TODO: Switch to jalview rna datamodel
193       if (jalview.util.Comparison.isGap(seq.charAt(i)))
194       {
195         if (ofstart == -1)
196         {
197           ofstart = i;
198         }
199         if (!rnaTrim.findPair(i).isEmpty())
200         {
201           int m = rnaTrim.findPair(i).get(1);
202           int l = rnaTrim.findPair(i).get(0);
203
204           struc.replace(m, m + 1, "*");
205           struc.replace(l, l + 1, "*");
206         }
207         else
208         {
209           struc.replace(i, i + 1, "*");
210         }
211       }
212       else
213       {
214         if (ofstart > -1)
215         {
216           offset.addShift(offset.shift(ofstart), ofstart - i);
217           ofstart = -1;
218         }
219       }
220     }
221     // final gap
222     if (ofstart > -1)
223     {
224       offset.addShift(offset.shift(ofstart), ofstart - sleng);
225       ofstart = -1;
226     }
227     String newSeq = rnaTrim.getSeq().replace("-", "");
228     rnaTrim.getSeq().replace(".", "");
229     String newStruc = struc.toString().replace("*", "");
230
231     try
232     {
233       rnaTrim.setRNA(newSeq, newStruc);
234       registerOffset(rnaTrim, offset);
235     } catch (ExceptionUnmatchedClosingParentheses e)
236     {
237       // TODO Auto-generated catch block
238       e.printStackTrace();
239     } catch (ExceptionFileFormatOrSyntax e)
240     {
241       // TODO Auto-generated catch block
242       e.printStackTrace();
243     }
244     return rnaTrim;
245   }
246
247   // needs to be many-many
248   Map<RNA, SequenceI> seqs = new Hashtable<RNA, SequenceI>();
249
250   Map<SequenceI, RNA> rnas = new Hashtable<SequenceI, RNA>();
251
252   Map<RNA, ShiftList> offsets = new Hashtable<RNA, ShiftList>();
253
254   Map<RNA, ShiftList> offsetsInv = new Hashtable<RNA, ShiftList>();
255
256   private void registerOffset(RNA rnaTrim, ShiftList offset)
257   {
258     offsets.put(rnaTrim, offset);
259     offsetsInv.put(rnaTrim, offset.getInverse());
260   }
261
262   public void showPanel(boolean show)
263   {
264     this.setVisible(show);
265   }
266
267   private boolean _started = false;
268
269   public void run()
270   {
271     _started = true;
272
273     try
274     {
275       initVarna();
276     } catch (OutOfMemoryError oomerror)
277     {
278       new OOMWarning("When trying to open the Varna viewer!", oomerror);
279     } catch (Exception ex)
280     {
281       Cache.log.error("Couldn't open Varna viewer!", ex);
282     }
283   }
284
285   @Override
286   public void onUINewStructure(VARNAConfig v, RNA r)
287   {
288
289   }
290
291   @Override
292   public void onWarningEmitted(String s)
293   {
294     // TODO Auto-generated method stub
295
296   }
297
298   private class VarnaHighlighter
299   {
300     private HighlightRegionAnnotation _lastHighlight;
301
302     private RNA _lastRNAhighlighted = null;
303
304     public void highlightRegion(RNA rna, int start, int end)
305     {
306       clearSelection(null);
307       HighlightRegionAnnotation highlight = new HighlightRegionAnnotation(
308               rna.getBasesBetween(start, end));
309       rna.addHighlightRegion(highlight);
310       _lastHighlight = highlight;
311       _lastRNAhighlighted = rna;
312
313     }
314
315     public HighlightRegionAnnotation getLastHighlight()
316     {
317       return _lastHighlight;
318     }
319
320     public RNA getLastRNA()
321     {
322       return _lastRNAhighlighted;
323     }
324
325     public void clearSelection(AppVarnaBinding vab)
326     {
327       if (_lastRNAhighlighted != null)
328       {
329         _lastRNAhighlighted.removeHighlightRegion(_lastHighlight);
330         if (vab != null)
331         {
332           vab.updateSelectedRNA(_lastRNAhighlighted);
333         }
334         _lastRNAhighlighted = null;
335         _lastHighlight = null;
336
337       }
338     }
339   }
340
341   VarnaHighlighter mouseOverHighlighter = new VarnaHighlighter(),
342           selectionHighlighter = new VarnaHighlighter();
343
344   /**
345    * If a mouseOver event from the AlignmentPanel is noticed the currently
346    * selected RNA in the VARNA window is highlighted at the specific position.
347    * To be able to remove it before the next highlight it is saved in
348    * _lastHighlight
349    */
350   @Override
351   public void mouseOverSequence(SequenceI sequence, int index)
352   {
353     RNA rna = vab.getSelectedRNA();
354     if (seqs.get(rna) == sequence)
355     {
356       ShiftList shift = offsets.get(rna);
357       if (shift != null)
358       {
359         // System.err.print("Orig pos:"+index);
360         index = shift.shift(index);
361         // System.err.println("\nFinal pos:"+index);
362       }
363       mouseOverHighlighter.highlightRegion(rna, index, index);
364       vab.updateSelectedRNA(rna);
365     }
366   }
367
368   @Override
369   public void onStructureRedrawn()
370   {
371     // TODO Auto-generated method stub
372
373   }
374
375   @Override
376   public void selection(SequenceGroup seqsel, ColumnSelection colsel,
377           SelectionSource source)
378   {
379     if (source != ap.av)
380     {
381       // ignore events from anything but our parent alignpanel
382       // TODO - reuse many-one panel-view system in jmol viewer
383       return;
384     }
385     if (seqsel != null && seqsel.getSize() > 0)
386     {
387       int start = seqsel.getStartRes(), end = seqsel.getEndRes();
388       RNA rna = vab.getSelectedRNA();
389       ShiftList shift = offsets.get(rna);
390       if (shift != null)
391       {
392         start = shift.shift(start);
393         end = shift.shift(end);
394       }
395       selectionHighlighter.highlightRegion(rna, start, end);
396       selectionHighlighter.getLastHighlight().setOutlineColor(
397               seqsel.getOutlineColour());
398       // TODO - translate column markings to positions on structure if present.
399       vab.updateSelectedRNA(rna);
400     }
401     else
402     {
403       selectionHighlighter.clearSelection(vab);
404     }
405   }
406
407   @Override
408   public void onHoverChanged(ModeleBase arg0, ModeleBase arg1)
409   {
410     RNA rna = vab.getSelectedRNA();
411     ShiftList shift = offsetsInv.get(rna);
412     SequenceI seq = seqs.get(rna);
413     if (arg1 != null && seq != null)
414     {
415       if (shift != null)
416       {
417         int i = shift.shift(arg1.getIndex());
418         // System.err.println("shifted "+(arg1.getIndex())+" to "+i);
419         ssm.mouseOverVamsasSequence(seq, i, this);
420       }
421       else
422       {
423         ssm.mouseOverVamsasSequence(seq, arg1.getIndex(), this);
424       }
425     }
426   }
427
428   @Override
429   public void onSelectionChanged(BaseList arg0, BaseList arg1, BaseList arg2)
430   {
431     // TODO translate selected regions in VARNA to a selection on the
432     // alignpanel.
433
434   }
435
436   @Override
437   public void onTranslationChanged()
438   {
439     // TODO Auto-generated method stub
440
441   }
442
443   @Override
444   public void onZoomLevelChanged()
445   {
446     // TODO Auto-generated method stub
447
448   }
449
450 }