Merge branch 'Jalview-JS/jim/JAL-3253-JAL-3418' into Jalview-JS/JAL-3253-applet
[jalview.git] / srcjar / fr / orsay / lri / varna / applications / VARNAEditor.java
1 /*
2  VARNA is a tool for the automated drawing, visualization and annotation of the secondary structure of RNA, designed as a companion software for web servers and databases.
3  Copyright (C) 2008  Kevin Darty, Alain Denise and Yann Ponty.
4  electronic mail : Yann.Ponty@lri.fr
5  paper mail : LRI, bat 490 Universit� Paris-Sud 91405 Orsay Cedex France
6
7  This file is part of VARNA version 3.1.
8  VARNA version 3.1 is free software: you can redistribute it and/or 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  VARNA version 3.1 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
12  without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13  See the GNU General Public License for more details.
14
15  You should have received a copy of the GNU General Public License along with VARNA version 3.1.
16  If not, see http://www.gnu.org/licenses.
17  */
18 package fr.orsay.lri.varna.applications;
19
20 import java.awt.BorderLayout;
21 import java.awt.Color;
22 import java.awt.Component;
23 import java.awt.Dimension;
24 import java.awt.Font;
25 import java.awt.GridLayout;
26 import java.awt.datatransfer.DataFlavor;
27 import java.awt.datatransfer.Transferable;
28 import java.awt.dnd.DnDConstants;
29 import java.awt.dnd.DropTarget;
30 import java.awt.dnd.DropTargetDragEvent;
31 import java.awt.dnd.DropTargetDropEvent;
32 import java.awt.dnd.DropTargetEvent;
33 import java.awt.dnd.DropTargetListener;
34 import java.awt.event.ActionEvent;
35 import java.awt.event.ActionListener;
36 import java.awt.event.MouseEvent;
37 import java.awt.event.MouseListener;
38 import java.awt.geom.Point2D.Double;
39 import java.io.File;
40 import java.text.DateFormat;
41 import java.util.ArrayList;
42 import java.util.Collection;
43 import java.util.Date;
44 import java.util.Hashtable;
45 import java.util.List;
46 import java.util.Set;
47
48 import javax.swing.DefaultListModel;
49 import javax.swing.DefaultListSelectionModel;
50 import javax.swing.Icon;
51 import javax.swing.JButton;
52 import javax.swing.JFrame;
53 import javax.swing.JLabel;
54 import javax.swing.JList;
55 import javax.swing.JOptionPane;
56 import javax.swing.JPanel;
57 import javax.swing.JScrollPane;
58 import javax.swing.JSplitPane;
59 import javax.swing.JTextField;
60 import javax.swing.ListModel;
61 import javax.swing.ListSelectionModel;
62 import javax.swing.UIManager;
63 import javax.swing.UnsupportedLookAndFeelException;
64 import javax.swing.event.ListSelectionEvent;
65 import javax.swing.event.ListSelectionListener;
66 import javax.swing.text.BadLocationException;
67 import javax.swing.text.DefaultHighlighter;
68
69 import fr.orsay.lri.varna.VARNAPanel;
70 import fr.orsay.lri.varna.components.ReorderableJList;
71 import fr.orsay.lri.varna.exceptions.ExceptionFileFormatOrSyntax;
72 import fr.orsay.lri.varna.exceptions.ExceptionLoadingFailed;
73 import fr.orsay.lri.varna.exceptions.ExceptionNonEqualLength;
74 import fr.orsay.lri.varna.exceptions.ExceptionUnmatchedClosingParentheses;
75 import fr.orsay.lri.varna.factories.RNAFactory;
76 import fr.orsay.lri.varna.interfaces.InterfaceVARNAListener;
77 import fr.orsay.lri.varna.interfaces.InterfaceVARNARNAListener;
78 import fr.orsay.lri.varna.interfaces.InterfaceVARNASelectionListener;
79 import fr.orsay.lri.varna.models.BaseList;
80 import fr.orsay.lri.varna.models.FullBackup;
81 import fr.orsay.lri.varna.models.VARNAConfig;
82 import fr.orsay.lri.varna.models.rna.Mapping;
83 import fr.orsay.lri.varna.models.rna.ModeleBP;
84 import fr.orsay.lri.varna.models.rna.ModeleBase;
85 import fr.orsay.lri.varna.models.rna.RNA;
86
87 public class VARNAEditor extends JFrame implements DropTargetListener, InterfaceVARNAListener, MouseListener {
88
89   /**
90    * 
91    */
92 //  private static final_long serialVersionUID = -790155708306987257L;
93
94   private static final String DEFAULT_SEQUENCE = "CAGCACGACACUAGCAGUCAGUGUCAGACUGCAIACAGCACGACACUAGCAGUCAGUGUCAGACUGCAIACAGCACGACACUAGCAGUCAGUGUCAGACUGCAIA";
95
96   private static final String DEFAULT_STRUCTURE1 = "..(((((...(((((...(((((...(((((.....)))))...))))).....(((((...(((((.....)))))...))))).....)))))...)))))..";
97   private static final String DEFAULT_STRUCTURE2 = "..(((((...(((((...(((((........(((((...(((((.....)))))...)))))..................))))).....)))))...)))))..";
98   // private static final String DEFAULT_STRUCTURE1 = "((((....))))";
99   // private static final String DEFAULT_STRUCTURE2 =
100   // "((((..(((....)))..))))";
101
102   private VARNAPanel _vp;
103
104   private JPanel _tools = new JPanel();
105   private JPanel _input = new JPanel();
106
107   private JPanel _seqPanel = new JPanel();
108   private JPanel _strPanel = new JPanel();
109   private JLabel _info = new JLabel();
110   
111   private JTextField _str = new JTextField(DEFAULT_STRUCTURE1);
112   Object _hoverHighlightStr = null;
113   ArrayList<Object> _selectionHighlightStr = new ArrayList<Object>();
114   
115   private JTextField _seq = new JTextField(DEFAULT_SEQUENCE);
116   Object _hoverHighlightSeq = null;
117   ArrayList<Object> _selectionHighlightSeq = new ArrayList<Object>();
118   
119   
120   private JLabel _strLabel = new JLabel(" Str:");
121   private JLabel _seqLabel = new JLabel(" Seq:");
122   private JButton _deleteButton = new JButton("Delete");
123   private JButton _duplicateButton = new JButton("Duplicate");
124   
125   private JPanel _listPanel = new JPanel();
126   private ReorderableJList _sideList = null;
127
128
129
130   private static String errorOpt = "error";
131   @SuppressWarnings("unused")
132   private boolean _error;
133
134   private Color _backgroundColor = Color.white;
135
136   private static int _nextID = 1;
137   @SuppressWarnings("unused")
138   private int _algoCode;
139   
140   private BackupHolder _rnaList;
141
142
143   public VARNAEditor() {
144     super("VARNA Editor");
145     RNAPanelDemoInit();
146   }
147
148   private void RNAPanelDemoInit() 
149   {
150       DefaultListModel dlm = new DefaultListModel(); 
151       
152
153     int marginTools = 40;
154
155       DefaultListSelectionModel m = new DefaultListSelectionModel();
156       m.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
157       m.setLeadAnchorNotificationEnabled(false);
158       
159       
160     _sideList = new ReorderableJList();
161     _sideList.setModel(dlm);
162     _sideList.addMouseListener(this);
163       _sideList.setSelectionModel(m);
164       _sideList.setPreferredSize(new Dimension(100, 0));
165       _sideList.addListSelectionListener( new ListSelectionListener(){
166       public void valueChanged(ListSelectionEvent arg0) {
167         //System.out.println(arg0);
168         if (!_sideList.isSelectionEmpty() && !arg0.getValueIsAdjusting())
169         {
170           FullBackup  sel = (FullBackup) _sideList.getSelectedValue();
171           Mapping map = Mapping.DefaultOutermostMapping(_vp.getRNA().getSize(), sel.rna.getSize());
172           _vp.showRNAInterpolated(sel.rna,sel.config,map);
173           _seq.setText(sel.rna.getSeq());
174           _str.setText(sel.rna.getStructDBN(true));
175         }
176       }
177       });
178
179       _rnaList = new BackupHolder(dlm,_sideList);
180     RNA _RNA1 = new RNA("User defined 1");
181     RNA _RNA2 = new RNA("User defined 2");
182     try {
183       _vp = new VARNAPanel("0",".");
184       _RNA1.setRNA(DEFAULT_SEQUENCE, DEFAULT_STRUCTURE1);
185       _RNA1.drawRNARadiate(_vp.getConfig());
186       _RNA2.setRNA(DEFAULT_SEQUENCE, DEFAULT_STRUCTURE2);
187       _RNA2.drawRNARadiate(_vp.getConfig());
188     } catch (ExceptionNonEqualLength e) {
189       _vp.errorDialog(e);
190     } catch (ExceptionUnmatchedClosingParentheses e2) {
191     e2.printStackTrace();
192     } catch (ExceptionFileFormatOrSyntax e3) {
193     e3.printStackTrace();
194     }
195     _vp.setPreferredSize(new Dimension(400, 400));
196     // 
197     
198      // BH 2018 this will NOT be a clone in SwingJS
199       _rnaList.add(_vp.getConfig().clone(),_RNA2,generateDefaultName());
200       _rnaList.add(_vp.getConfig().clone(),_RNA1,generateDefaultName(),true);
201
202       JScrollPane listScroller = new JScrollPane(_sideList);
203       listScroller.setPreferredSize(new Dimension(150, 0));
204
205     setBackground(_backgroundColor);
206     _vp.setBackground(_backgroundColor);
207
208
209     Font textFieldsFont = Font.decode("MonoSpaced-PLAIN-12");
210
211     _seqLabel.setHorizontalTextPosition(JLabel.LEFT);
212     _seqLabel.setPreferredSize(new Dimension(marginTools, 15));
213     _seq.setFont(textFieldsFont);
214     _seq.setText(DEFAULT_SEQUENCE);
215     _seq.setEditable(false);
216     
217
218     _seqPanel.setLayout(new BorderLayout());
219     _seqPanel.add(_seqLabel, BorderLayout.WEST);
220     _seqPanel.add(_seq, BorderLayout.CENTER);
221
222     _strLabel.setPreferredSize(new Dimension(marginTools, 15));
223     _strLabel.setHorizontalTextPosition(JLabel.LEFT);
224     _str.setFont(textFieldsFont);
225     _str.setEditable(false);
226     _strPanel.setLayout(new BorderLayout());
227     _strPanel.add(_strLabel, BorderLayout.WEST);
228     _strPanel.add(_str, BorderLayout.CENTER);
229
230     _input.setLayout(new GridLayout(2, 0));
231     _input.add(_seqPanel);
232     _input.add(_strPanel);
233
234
235     _tools.setLayout(new BorderLayout());
236     _tools.add(_input, BorderLayout.CENTER);
237     _tools.add(_info, BorderLayout.SOUTH);
238
239     _deleteButton.addActionListener(new ActionListener() {
240       public void actionPerformed(ActionEvent e) {
241         _rnaList.removeSelected();
242       }
243     });
244 //    _duplicateButton.addActionListener(new ActionListener() {
245 //      public void actionPerformed(ActionEvent e) {
246 //          _rnaList.add((VARNAConfig)_vp.getConfig().clone(),_vp.getRNA().clone(),_vp.getRNA().getName()+"-"+DateFormat.getTimeInstance(DateFormat.LONG).format(new Date()),true); 
247 //      }});
248     
249     JPanel ops = new JPanel();
250     ops.setLayout(new GridLayout(1,2));
251     ops.add(_deleteButton);
252     ops.add(_duplicateButton);
253
254     JLabel j = new JLabel("Structures",JLabel.CENTER);
255     _listPanel.setLayout(new BorderLayout());
256     
257     _listPanel.add(ops,BorderLayout.SOUTH);
258     _listPanel.add(j,BorderLayout.NORTH);
259     _listPanel.add(listScroller,BorderLayout.CENTER);
260
261
262     JSplitPane split = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT,true,_listPanel,_vp);
263     getContentPane().setLayout(new BorderLayout());
264     getContentPane().add(split, BorderLayout.CENTER);
265     getContentPane().add(_tools, BorderLayout.NORTH);
266
267     setVisible(true);
268     DropTarget dt = new DropTarget(_vp, this);
269     
270     _vp.addRNAListener(new InterfaceVARNARNAListener(){
271       public void onSequenceModified(int index, String oldseq, String newseq) {
272         _seq.setText(_vp.getRNA().getSeq());
273       }
274
275       public void onStructureModified(Set<ModeleBP> current,
276           Set<ModeleBP> addedBasePairs, Set<ModeleBP> removedBasePairs) {
277         _str.setText(_vp.getRNA().getStructDBN(true));
278       }
279
280       public void onRNALayoutChanged(Hashtable<Integer, Double> previousPositions) {
281       }
282       
283     });
284     
285     _vp.addSelectionListener(new InterfaceVARNASelectionListener(){
286
287       public void onHoverChanged(ModeleBase oldbase, ModeleBase newBase) {
288         if (_hoverHighlightSeq!=null)
289         {
290           _seq.getHighlighter().removeHighlight(_hoverHighlightSeq);
291           _hoverHighlightSeq = null;
292         }
293         if (_hoverHighlightStr!=null)
294         {
295           _str.getHighlighter().removeHighlight(_hoverHighlightStr);
296           _hoverHighlightStr = null;
297         }
298         if (newBase!=null)
299         {
300           try {
301             _hoverHighlightSeq = _seq.getHighlighter().addHighlight(newBase.getIndex(), newBase.getIndex()+1, new DefaultHighlighter.DefaultHighlightPainter(Color.green) );
302             _hoverHighlightStr = _str.getHighlighter().addHighlight(newBase.getIndex(), newBase.getIndex()+1, new DefaultHighlighter.DefaultHighlightPainter(Color.green) );
303           } catch (BadLocationException e) {
304             e.printStackTrace();
305           }
306         }
307       }
308
309       public void onSelectionChanged(BaseList selection,
310           BaseList addedBases, BaseList removedBases) {
311         for(Object tag: _selectionHighlightSeq)
312         {
313           _seq.getHighlighter().removeHighlight(tag);
314         }
315         _selectionHighlightSeq.clear();
316         for(Object tag: _selectionHighlightStr)
317         {
318           _str.getHighlighter().removeHighlight(tag);
319         }
320         _selectionHighlightStr.clear();
321         for (ModeleBase m: selection.getBases())
322         {
323           try {
324             _selectionHighlightSeq.add(_seq.getHighlighter().addHighlight(m.getIndex(), m.getIndex()+1, new DefaultHighlighter.DefaultHighlightPainter(Color.orange) ));
325             _selectionHighlightStr.add(_str.getHighlighter().addHighlight(m.getIndex(), m.getIndex()+1, new DefaultHighlighter.DefaultHighlightPainter(Color.orange) ));
326           } catch (BadLocationException e) {
327             e.printStackTrace();
328           }
329         }
330       }
331       
332     });
333     
334     _vp.addVARNAListener(this);
335   }
336   
337   public static String generateDefaultName()
338   {
339     return "User file #"+_nextID++;
340   }
341
342   public RNA getRNA() {
343     return (RNA)_sideList.getSelectedValue();
344   }
345
346
347
348   public String[][] getParameterInfo() {
349     String[][] info = {
350         // Parameter Name Kind of Value Description,
351         { "sequenceDBN", "String", "A raw RNA sequence" },
352         { "structureDBN", "String",
353             "An RNA structure in dot bracket notation (DBN)" },
354         { errorOpt, "boolean", "To show errors" }, };
355     return info;
356   }
357
358   public void init() {
359     _vp.setBackground(_backgroundColor);
360     _error = true;
361   }
362
363   @SuppressWarnings("unused")
364   private Color getSafeColor(String col, Color def) {
365     Color result;
366     try {
367       result = Color.decode(col);
368     } catch (Exception e) {
369       try {
370         result = Color.getColor(col, def);
371       } catch (Exception e2) {
372         return def;
373       }
374     }
375     return result;
376   }
377
378   public VARNAPanel get_varnaPanel() {
379     return _vp;
380   }
381
382   public void set_varnaPanel(VARNAPanel surface) {
383     _vp = surface;
384   }
385
386
387   public JTextField get_seq() {
388     return _seq;
389   }
390
391   public void set_seq(JTextField _seq) {
392     this._seq = _seq;
393   }
394
395   public JLabel get_info() {
396     return _info;
397   }
398
399   public void set_info(JLabel _info) {
400     this._info = _info;
401   }
402
403   public static void main(String[] args) {
404     VARNAEditor d = new VARNAEditor();
405     d.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
406     d.pack();
407     d.setVisible(true);
408   }
409   
410
411   public void dragEnter(DropTargetDragEvent arg0) {
412     // TODO Auto-generated method stub
413     
414   }
415
416   public void dragExit(DropTargetEvent arg0) {
417     // TODO Auto-generated method stub
418     
419   }
420
421   public void dragOver(DropTargetDragEvent arg0) {
422     // TODO Auto-generated method stub
423     
424   }
425
426   public void drop(DropTargetDropEvent dtde) {
427       try {
428           Transferable tr = dtde.getTransferable();
429           DataFlavor[] flavors = tr.getTransferDataFlavors();
430           for (int i = 0; i < flavors.length; i++) {
431       if (flavors[i].isFlavorJavaFileListType()) {
432         dtde.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
433         Object ob = tr.getTransferData(flavors[i]);
434         if (ob instanceof List)
435         {
436           List list = (List) ob;
437           for (int j = 0; j < list.size(); j++) {
438           Object o = list.get(j);
439           
440           if (dtde.getSource() instanceof DropTarget)
441           {
442             DropTarget dt = (DropTarget) dtde.getSource();
443             Component c = dt.getComponent();
444             if (c instanceof VARNAPanel)
445             {
446             String path = o.toString();
447               VARNAPanel vp = (VARNAPanel) c;
448             try{
449               FullBackup bck =  VARNAPanel.importSession((File) o); // BH SwingJS
450               _rnaList.add(bck.config, bck.rna,bck.name,true);
451             }
452             catch (ExceptionLoadingFailed e3)
453             {
454               Collection<RNA> rnas = RNAFactory.loadSecStr((File) o); // BH SwingJS 
455               if (rnas.isEmpty())
456               {
457                 throw new ExceptionFileFormatOrSyntax("No RNA could be parsed from that source.");
458               }
459               
460               int id = 1;
461               for(RNA r: rnas)
462               {
463                 r.drawRNA(vp.getConfig());
464                 String name = r.getName();
465                 if (name.equals(""))
466                 { 
467                   name = path.substring(path.lastIndexOf(File.separatorChar)+1);
468                 }
469                 if (rnas.size()>1)
470                 {
471                   name += " - Molecule# "+id++;
472                 }
473                 _rnaList.add(vp.getConfig().clone(),r,name,true);
474               }
475             }           
476             }
477           }
478           }
479         }
480         // If we made it this far, everything worked.
481         dtde.dropComplete(true);
482         return;
483       }
484           }
485           // Hmm, the user must not have dropped a file list
486           dtde.rejectDrop();
487         } catch (Exception e) {
488           e.printStackTrace();
489           dtde.rejectDrop();
490         }
491     
492   }
493
494   public void dropActionChanged(DropTargetDragEvent arg0) {
495   }
496
497   private class BackupHolder{
498     private DefaultListModel _rnaList;
499     private ArrayList<RNA> _rnas = new ArrayList<RNA>();
500     JList _l;
501     
502     public BackupHolder(DefaultListModel rnaList, JList l)
503     {
504       _rnaList = rnaList;
505       _l = l;
506     }
507     
508     public void add(VARNAConfig c, RNA r)
509     {
510       add(c, r, r.getName(),false);
511     }
512
513     public void add(VARNAConfig c, RNA r,boolean select)
514     {
515       add(c, r, r.getName(),select);
516     }
517
518     public void add(VARNAConfig c, RNA r, String name)
519     {
520       add(c, r, name,false);      
521     }
522     public void add(VARNAConfig c, RNA r, String name, boolean select)
523     {
524       if (select){
525         _l.removeSelectionInterval(0, _rnaList.size());
526       }
527       if (name.equals(""))
528       {
529         name = generateDefaultName();
530       }
531       FullBackup bck = new FullBackup(c,r,name);
532       _rnas.add(0, r);
533       _rnaList.add(0,bck);
534       if (select){
535         _l.setSelectedIndex(0);
536       }
537     }
538
539     public void remove(int i)
540     {
541       _rnas.remove(i);
542       _rnaList.remove(i);
543       
544     }
545     public DefaultListModel getModel()
546     {
547       return _rnaList;
548     }
549     public boolean contains(RNA r)
550     {
551       return _rnas.contains(r);
552     }
553     /*public int getSize()
554     {
555       return _rnaList.getSize();
556     }*/
557     public FullBackup getElementAt(int i)
558     {
559       return (FullBackup) _rnaList.getElementAt(i);
560     }
561     
562     public void removeSelected()
563     {
564       int i = _l.getSelectedIndex();
565       if (i!=-1)
566       {
567         if (_rnaList.getSize()==1)
568         {
569           RNA r = new RNA();
570           try {
571           r.setRNA(" ", ".");
572           } catch (ExceptionUnmatchedClosingParentheses e1) {
573           } catch (ExceptionFileFormatOrSyntax e1) {
574           }
575           _vp.showRNA(r);
576           _vp.repaint();
577         }
578         else
579         {  
580          int newi = i+1;
581          if (newi==_rnaList.getSize())
582          {
583            newi = _rnaList.getSize()-2;
584          }
585          FullBackup bck = (FullBackup) _rnaList.getElementAt(newi);
586          _l.setSelectedValue(bck,true);
587         }
588         _rnaList.remove(i);
589       }
590
591     }
592   }
593
594   public void onStructureRedrawn() {
595     // TODO Auto-generated method stub
596     
597   }
598
599   public void onUINewStructure(VARNAConfig v, RNA r) {
600     _rnaList.add(v, r,"",true);
601   }
602
603   public void onWarningEmitted(String s) {
604     // TODO Auto-generated method stub
605     
606   }
607
608   public void mouseClicked(MouseEvent e) {
609          if(e.getClickCount() == 2){
610            int index = _sideList.locationToIndex(e.getPoint());
611            ListModel dlm = _sideList.getModel();
612            FullBackup item = (FullBackup) dlm.getElementAt(index);;
613            _sideList.ensureIndexIsVisible(index);
614            Object newName = JOptionPane.showInputDialog(
615                   this,
616                   "Specify a new name for this RNA",
617                   "Rename RNA", 
618                   JOptionPane.QUESTION_MESSAGE,
619                   (Icon)null,
620                   null,
621                   item.toString());
622            if (newName!=null)
623            {
624              item.name = newName.toString();
625              this._sideList.repaint();
626            }
627            }
628   }
629
630   public void mouseEntered(MouseEvent arg0) {
631     // TODO Auto-generated method stub
632     
633   }
634
635   public void mouseExited(MouseEvent arg0) {
636     // TODO Auto-generated method stub
637     
638   }
639
640   public void mousePressed(MouseEvent arg0) {
641     // TODO Auto-generated method stub
642     
643   }
644
645   public void mouseReleased(MouseEvent arg0) {
646     // TODO Auto-generated method stub
647     
648   }
649
650   public void onZoomLevelChanged() {
651     // TODO Auto-generated method stub
652     
653   }
654
655   public void onTranslationChanged() {
656     // TODO Auto-generated method stub
657     
658   }
659 }