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