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