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