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