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