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