JAL-1517 fix copyright for 2.8.2
[jalview.git] / src / jalview / gui / AppVarna.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer (Version 2.8.2)
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, MessageManager.formatMessage("label.varna_params", new String[]{name}),
153             getBounds().width, getBounds().height);
154     this.pack();
155     showPanel(true);
156   
157   }
158
159   public String replaceOddGaps(String oldStr)
160   {
161     String patternStr = "[^([{<>}])]";
162     String replacementStr = ".";
163     Pattern pattern = Pattern.compile(patternStr);
164     Matcher matcher = pattern.matcher(oldStr);
165     String newStr = matcher.replaceAll(replacementStr);
166     return newStr;
167   }
168
169   public RNA trimRNA(RNA rna, String name)
170   {
171     ShiftList offset = new ShiftList();
172     
173     RNA rnaTrim = new RNA(name);
174     try
175     {
176       rnaTrim.setRNA(rna.getSeq(), replaceOddGaps(rna.getStructDBN()));
177     } catch (ExceptionUnmatchedClosingParentheses e2)
178     {
179       e2.printStackTrace();
180     } catch (ExceptionFileFormatOrSyntax e3)
181     {
182       e3.printStackTrace();
183     }
184
185     StringBuffer seq = new StringBuffer(rnaTrim.getSeq());
186     StringBuffer struc = new StringBuffer(rnaTrim.getStructDBN());
187     int ofstart = -1, sleng = rnaTrim.getSeq().length();
188     for (int i = 0; i < sleng; i++)
189     {
190       // TODO: Jalview utility for gap detection java.utils.isGap()
191       // TODO: Switch to jalview rna datamodel
192       if (jalview.util.Comparison.isGap(seq.charAt(i)))
193       {
194         if (ofstart == -1)
195         {
196           ofstart = i;
197         }
198         if (!rnaTrim.findPair(i).isEmpty())
199         {
200           int m = rnaTrim.findPair(i).get(1);
201           int l = rnaTrim.findPair(i).get(0);
202
203           struc.replace(m, m + 1, "*");
204           struc.replace(l, l + 1, "*");
205         }
206         else
207         {
208           struc.replace(i, i + 1, "*");
209         }
210       }
211       else
212       {
213         if (ofstart > -1)
214         {
215           offset.addShift(offset.shift(ofstart), ofstart - i);
216           ofstart = -1;
217         }
218       }
219     }
220     // final gap
221     if (ofstart > -1)
222     {
223       offset.addShift(offset.shift(ofstart), ofstart - sleng);
224       ofstart = -1;
225     }
226     String newSeq = rnaTrim.getSeq().replace("-", "");
227     rnaTrim.getSeq().replace(".", "");
228     String newStruc = struc.toString().replace("*", "");
229
230     try
231     {
232       rnaTrim.setRNA(newSeq, newStruc);
233       registerOffset(rnaTrim, offset);
234     } catch (ExceptionUnmatchedClosingParentheses e)
235     {
236       // TODO Auto-generated catch block
237       e.printStackTrace();
238     } catch (ExceptionFileFormatOrSyntax e)
239     {
240       // TODO Auto-generated catch block
241       e.printStackTrace();
242     }
243     return rnaTrim;
244   }
245
246   // needs to be many-many
247   Map<RNA, SequenceI> seqs = new Hashtable<RNA, SequenceI>();
248
249   Map<SequenceI, RNA> rnas = new Hashtable<SequenceI, RNA>();
250
251   Map<RNA, ShiftList> offsets = new Hashtable<RNA, ShiftList>();
252
253   Map<RNA, ShiftList> offsetsInv = new Hashtable<RNA, ShiftList>();
254
255   private void registerOffset(RNA rnaTrim, ShiftList offset)
256   {
257     offsets.put(rnaTrim, offset);
258     offsetsInv.put(rnaTrim, offset.getInverse());
259   }
260
261   public void showPanel(boolean show)
262   {
263     this.setVisible(show);
264   }
265
266   private boolean _started = false;
267
268   public void run()
269   {
270     _started = true;
271
272     try
273     {
274       initVarna();
275     } catch (OutOfMemoryError oomerror)
276     {
277       new OOMWarning("When trying to open the Varna viewer!", oomerror);
278     } catch (Exception ex)
279     {
280       Cache.log.error("Couldn't open Varna viewer!", ex);
281     }
282   }
283
284   @Override
285   public void onUINewStructure(VARNAConfig v, RNA r)
286   {
287
288   }
289
290   @Override
291   public void onWarningEmitted(String s)
292   {
293     // TODO Auto-generated method stub
294
295   }
296
297   private class VarnaHighlighter
298   {
299     private HighlightRegionAnnotation _lastHighlight;
300
301     private RNA _lastRNAhighlighted = null;
302
303     public void highlightRegion(RNA rna, int start, int end)
304     {
305       clearSelection(null);
306       HighlightRegionAnnotation highlight = new HighlightRegionAnnotation(
307               rna.getBasesBetween(start, end));
308       rna.addHighlightRegion(highlight);
309       _lastHighlight = highlight;
310       _lastRNAhighlighted = rna;
311
312     }
313
314     public HighlightRegionAnnotation getLastHighlight()
315     {
316       return _lastHighlight;
317     }
318
319     public RNA getLastRNA()
320     {
321       return _lastRNAhighlighted;
322     }
323
324     public void clearSelection(AppVarnaBinding vab)
325     {
326       if (_lastRNAhighlighted != null)
327       {
328         _lastRNAhighlighted.removeHighlightRegion(_lastHighlight);
329         if (vab != null)
330         {
331           vab.updateSelectedRNA(_lastRNAhighlighted);
332         }
333         _lastRNAhighlighted = null;
334         _lastHighlight = null;
335
336       }
337     }
338   }
339
340   VarnaHighlighter mouseOverHighlighter = new VarnaHighlighter(),
341           selectionHighlighter = new VarnaHighlighter();
342
343   /**
344    * If a mouseOver event from the AlignmentPanel is noticed the currently
345    * selected RNA in the VARNA window is highlighted at the specific position.
346    * To be able to remove it before the next highlight it is saved in
347    * _lastHighlight
348    */
349   @Override
350   public void mouseOverSequence(SequenceI sequence, int index)
351   {
352     RNA rna = vab.getSelectedRNA();
353     if (seqs.get(rna) == sequence)
354     {
355       ShiftList shift = offsets.get(rna);
356       if (shift != null)
357       {
358         // System.err.print("Orig pos:"+index);
359         index = shift.shift(index);
360         // System.err.println("\nFinal pos:"+index);
361       }
362       mouseOverHighlighter.highlightRegion(rna, index, index);
363       vab.updateSelectedRNA(rna);
364     }
365   }
366
367   @Override
368   public void onStructureRedrawn()
369   {
370     // TODO Auto-generated method stub
371
372   }
373
374   @Override
375   public void selection(SequenceGroup seqsel, ColumnSelection colsel,
376           SelectionSource source)
377   {
378     if (source != ap.av)
379     {
380       // ignore events from anything but our parent alignpanel
381       // TODO - reuse many-one panel-view system in jmol viewer
382       return;
383     }
384     if (seqsel != null && seqsel.getSize() > 0)
385     {
386       int start = seqsel.getStartRes(), end = seqsel.getEndRes();
387       RNA rna = vab.getSelectedRNA();
388       ShiftList shift = offsets.get(rna);
389       if (shift != null)
390       {
391         start = shift.shift(start);
392         end = shift.shift(end);
393       }
394       selectionHighlighter.highlightRegion(rna, start, end);
395       selectionHighlighter.getLastHighlight().setOutlineColor(
396               seqsel.getOutlineColour());
397       // TODO - translate column markings to positions on structure if present.
398       vab.updateSelectedRNA(rna);
399     }
400     else
401     {
402       selectionHighlighter.clearSelection(vab);
403     }
404   }
405
406   @Override
407   public void onHoverChanged(ModeleBase arg0, ModeleBase arg1)
408   {
409     RNA rna = vab.getSelectedRNA();
410     ShiftList shift = offsetsInv.get(rna);
411     SequenceI seq = seqs.get(rna);
412     if (arg1 != null && seq != null)
413     {
414       if (shift != null)
415       {
416         int i = shift.shift(arg1.getIndex());
417         // System.err.println("shifted "+(arg1.getIndex())+" to "+i);
418         ssm.mouseOverVamsasSequence(seq, i, this);
419       }
420       else
421       {
422         ssm.mouseOverVamsasSequence(seq, arg1.getIndex(), this);
423       }
424     }
425   }
426
427   @Override
428   public void onSelectionChanged(BaseList arg0, BaseList arg1, BaseList arg2)
429   {
430     // TODO translate selected regions in VARNA to a selection on the
431     // alignpanel.
432
433   }
434
435   @Override
436   public void onTranslationChanged()
437   {
438     // TODO Auto-generated method stub
439     
440   }
441
442   @Override
443   public void onZoomLevelChanged()
444   {
445     // TODO Auto-generated method stub
446     
447   }
448
449 }