JAL-892 JAL-1779 save/restore Varna viewer to/from project
[jalview.git] / src / jalview / gui / AppVarnaBinding.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.awt.Component;
26 import java.awt.Dimension;
27 import java.awt.Font;
28 import java.awt.datatransfer.DataFlavor;
29 import java.awt.datatransfer.Transferable;
30 import java.awt.dnd.DnDConstants;
31 import java.awt.dnd.DropTarget;
32 import java.awt.dnd.DropTargetAdapter;
33 import java.awt.dnd.DropTargetDropEvent;
34 import java.awt.event.ComponentEvent;
35 import java.awt.event.MouseAdapter;
36 import java.awt.event.MouseEvent;
37 import java.io.File;
38 import java.io.IOException;
39 import java.util.ArrayList;
40 import java.util.Collection;
41 import java.util.List;
42
43 import javax.swing.DefaultListModel;
44 import javax.swing.DefaultListSelectionModel;
45 import javax.swing.JLabel;
46 import javax.swing.JList;
47 import javax.swing.JPanel;
48 import javax.swing.JScrollPane;
49 import javax.swing.ListModel;
50 import javax.swing.ListSelectionModel;
51 import javax.swing.event.ListSelectionEvent;
52 import javax.swing.event.ListSelectionListener;
53
54 import fr.orsay.lri.varna.VARNAPanel;
55 import fr.orsay.lri.varna.components.ReorderableJList;
56 import fr.orsay.lri.varna.exceptions.ExceptionLoadingFailed;
57 import fr.orsay.lri.varna.exceptions.ExceptionNonEqualLength;
58 import fr.orsay.lri.varna.models.FullBackup;
59 import fr.orsay.lri.varna.models.VARNAConfig;
60 import fr.orsay.lri.varna.models.rna.RNA;
61
62 import jalview.datamodel.SequenceI;
63 import jalview.ext.varna.JalviewVarnaBinding;
64 import jalview.structure.AtomSpec;
65 import jalview.util.MessageManager;
66
67 public class AppVarnaBinding extends JalviewVarnaBinding
68 {
69   public VARNAPanel vp;
70
71   // remove unused (commented out) fields?
72   // protected JPanel _tools = new JPanel();
73   //
74   // private JPanel _input = new JPanel();
75   //
76   // private JPanel _strPanel = new JPanel();
77   //
78   // private JTextField _str = new JTextField();
79   //
80   // private JTextField _seq = new JTextField();
81   //
82   // private JLabel _strLabel = new JLabel(
83   // MessageManager.getString("label.str"));
84   //
85   // private JButton _updateButton = new JButton(
86   // MessageManager.getString("action.update"));
87   //
88   // private JButton _deleteButton = new JButton(
89   // MessageManager.getString("action.delete"));
90   //
91   // private JButton _duplicateButton = new JButton(
92   // MessageManager.getString("action.snapshot"));
93   //
94   protected JPanel _listPanel = new JPanel();
95
96   private ReorderableJList _sideList = null;
97
98   private static String errorOpt = "error";
99
100   @SuppressWarnings("unused")
101   private boolean _error;
102
103   private Color _backgroundColor = Color.white;
104
105   private static int _nextID = 1;
106
107   @SuppressWarnings("unused")
108   private int _algoCode;
109
110   private BackupHolder _rnaList;
111
112   /**
113    * Constructor
114    */
115   public AppVarnaBinding()
116   {
117     init();
118   }
119
120   /**
121    * Constructs the VARNAPanel and an (empty) selection list of structures to
122    * show in it
123    */
124   private void init()
125   {
126     DefaultListModel<FullBackup> dlm = new DefaultListModel<FullBackup>();
127
128     int marginTools = 40;
129
130     DefaultListSelectionModel m = new DefaultListSelectionModel();
131     m.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
132     m.setLeadAnchorNotificationEnabled(false);
133
134     _sideList = new ReorderableJList();
135     _sideList.setModel(dlm);
136     _sideList.addMouseListener(new MouseAdapter()
137     {
138       @Override
139       public void mouseClicked(MouseEvent e) {
140         AppVarnaBinding.this.mouseClicked(e);
141       }
142     });
143     _sideList.setSelectionModel(m);
144     _sideList.setPreferredSize(new Dimension(100, 0));
145     _sideList.addListSelectionListener(new ListSelectionListener()
146     {
147       public void valueChanged(ListSelectionEvent evt)
148       {
149         changeSelectedStructure_actionPerformed(evt);
150       }
151     });
152     _rnaList = new BackupHolder(dlm, _sideList);
153
154     try
155     {
156       vp = new VARNAPanel("0", ".");
157     } catch (ExceptionNonEqualLength e)
158     {
159       vp.errorDialog(e);
160     }
161     vp.setPreferredSize(new Dimension(400, 400));
162
163     JScrollPane listScroller = new JScrollPane(_sideList);
164     listScroller.setPreferredSize(new Dimension(150, 0));
165
166     vp.setBackground(_backgroundColor);
167
168     Font textFieldsFont = Font.decode("MonoSpaced-PLAIN-12");
169
170     // MC commented out stuff not added to panel - remove?
171     // _seq.setFont(textFieldsFont);
172     // if (!rnaList.isEmpty())
173     // {
174     // _seq.setText(rnaList.get(0).getSeq());
175     // }
176
177     // _updateButton.addActionListener(new ActionListener()
178     // {
179     // public void actionPerformed(ActionEvent e)
180     // {
181     // FullBackup sel = (FullBackup) _sideList.getSelectedValue();
182     // sel.rna.setSequence("A");
183     // }
184     // });
185     //
186     // _strLabel.setPreferredSize(new Dimension(marginTools, 15));
187     // _strLabel.setHorizontalTextPosition(JLabel.LEFT);
188     // _str.setFont(textFieldsFont);
189     // _strPanel.setLayout(new BorderLayout());
190     // _strPanel.add(_strLabel, BorderLayout.WEST);
191     // _strPanel.add(_str, BorderLayout.CENTER);
192     //
193     // _input.setLayout(new GridLayout(1, 0));
194     // _input.add(_strPanel);
195     //
196     // JPanel goPanel = new JPanel();
197     // goPanel.setLayout(new BorderLayout());
198     //
199     // _tools.setLayout(new BorderLayout());
200     // _tools.add(_input, BorderLayout.CENTER);
201     // _tools.add(goPanel, BorderLayout.EAST);
202     //
203     // goPanel.add(_updateButton, BorderLayout.CENTER);
204     //
205     // JPanel ops = new JPanel();
206     // ops.setLayout(new GridLayout(1, 2));
207     // ops.add(_deleteButton);
208     // ops.add(_duplicateButton);
209
210     JLabel j = new JLabel(
211             MessageManager.getString("label.structures_manager"),
212             JLabel.CENTER);
213     _listPanel.setLayout(new BorderLayout());
214
215     _listPanel.add(j, BorderLayout.NORTH);
216     _listPanel.add(listScroller, BorderLayout.CENTER);
217
218     new DropTarget(vp, new DropTargetAdapter() {
219       @Override
220       public void drop(DropTargetDropEvent dtde)
221       {
222         AppVarnaBinding.this.drop(dtde);
223       }
224     });
225   }
226
227   public JPanel getListPanel()
228   {
229     return _listPanel;
230   }
231
232   /**
233    * Returns the currently selected RNA, or null if none selected
234    * 
235    * @return
236    */
237   public RNA getSelectedRNA()
238   {
239     int selectedIndex = _sideList.getSelectedIndex();
240     if (selectedIndex < 0)
241     {
242       return null;
243     }
244     FullBackup selected = _rnaList.getElementAt(selectedIndex);
245     return selected.rna;
246   }
247
248   /**
249    * Substitute currently selected RNA with the edited one
250    * 
251    * @param rnaEdit
252    */
253   public void updateSelectedRNA(RNA rnaEdit)
254   {
255     vp.repaint();
256     vp.showRNA(rnaEdit);
257   }
258
259   public static String generateDefaultName()
260   {
261     return "User file #" + _nextID++;
262   }
263
264   public String[][] getParameterInfo()
265   {
266     String[][] info =
267     {
268         // Parameter Name Kind of Value Description,
269         { "sequenceDBN", "String", "A raw RNA sequence" },
270         { "structureDBN", "String",
271             "An RNA structure in dot bracket notation (DBN)" },
272         { errorOpt, "boolean", "To show errors" }, };
273     return info;
274   }
275
276   @SuppressWarnings("unused")
277   private Color getSafeColor(String col, Color def)
278   {
279     Color result;
280     try
281     {
282       result = Color.decode(col);
283     } catch (Exception e)
284     {
285       try
286       {
287         result = Color.getColor(col, def);
288       } catch (Exception e2)
289       {
290         return def;
291       }
292     }
293     return result;
294   }
295
296   public VARNAPanel get_varnaPanel()
297   {
298     return vp;
299   }
300
301   public void set_varnaPanel(VARNAPanel surface)
302   {
303     vp = surface;
304   }
305
306   public void drop(DropTargetDropEvent dtde)
307   {
308     try
309     {
310       Transferable tr = dtde.getTransferable();
311       DataFlavor[] flavors = tr.getTransferDataFlavors();
312       for (int i = 0; i < flavors.length; i++)
313       {
314         if (flavors[i].isFlavorJavaFileListType())
315         {
316           dtde.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
317           Object ob = tr.getTransferData(flavors[i]);
318           if (ob instanceof List)
319           {
320             List list = (List) ob;
321             for (int j = 0; j < list.size(); j++)
322             {
323               Object o = list.get(j);
324
325               if (dtde.getSource() instanceof DropTarget)
326               {
327                 DropTarget dt = (DropTarget) dtde.getSource();
328                 Component c = dt.getComponent();
329                 if (c instanceof VARNAPanel)
330                 {
331                   String path = o.toString();
332                   VARNAPanel varnaPanel = (VARNAPanel) c;
333                   try
334                   {
335                     FullBackup bck = VARNAPanel.importSession(path);
336                     _rnaList.add(bck.config, bck.rna, bck.name, true);
337                   } catch (ExceptionLoadingFailed e3)
338                   {
339                     int mn = 1;
340                     Collection<RNA> mdls = fr.orsay.lri.varna.factories.RNAFactory
341                             .loadSecStr(path);
342                     for (RNA r : mdls)
343                     {
344                       r.drawRNA(varnaPanel.getConfig());
345                       String name = r.getName();
346                       if (name.equals(""))
347                       {
348                         name = path.substring(path
349                                 .lastIndexOf(File.separatorChar) + 1);
350                       }
351                       if (mdls.size() > 1)
352                       {
353                         name += " (Model " + mn++ + ")";
354                       }
355                       _rnaList.add(varnaPanel.getConfig().clone(), r, name, true);
356                     }
357                   }
358                 }
359               }
360             }
361           }
362           // If we made it this far, everything worked.
363           dtde.dropComplete(true);
364           return;
365         }
366       }
367       // Hmm, the user must not have dropped a file list
368       dtde.rejectDrop();
369     } catch (Exception e)
370     {
371       e.printStackTrace();
372       dtde.rejectDrop();
373     }
374
375   }
376
377   private class BackupHolder
378   {
379     private DefaultListModel<FullBackup> _rnalist;
380
381     private List<RNA> _rnas = new ArrayList<RNA>();
382
383     JList _l;
384
385     public BackupHolder(DefaultListModel<FullBackup> rnaList, JList l)
386     {
387       _rnalist = rnaList;
388       _l = l;
389     }
390
391     public void add(VARNAConfig c, RNA r, String name)
392     {
393       add(c, r, name, false);
394     }
395
396     /**
397      * Adds an entry to the end of the selection list and (optionally) sets it
398      * as selected
399      * 
400      * @param c
401      * @param r
402      * @param name
403      * @param select
404      */
405     public void add(VARNAConfig c, RNA r, String name, boolean select)
406     {
407       if (select)
408       {
409         _l.removeSelectionInterval(0, _rnalist.size());
410       }
411       if (name.equals(""))
412       {
413         name = generateDefaultName();
414       }
415       FullBackup bck = new FullBackup(c, r, name);
416       _rnas.add(r);
417       _rnalist.addElement(bck);
418       if (select)
419       {
420         _l.setSelectedIndex(0);
421       }
422     }
423
424     public FullBackup getElementAt(int i)
425     {
426       return _rnalist.getElementAt(i);
427     }
428   }
429
430   public void mouseClicked(MouseEvent e)
431   {
432     if (e.getClickCount() == 2)
433     {
434       int index = _sideList.locationToIndex(e.getPoint());
435       ListModel<FullBackup> dlm = _sideList.getModel();
436       // FullBackup item = dlm.getElementAt(index);
437
438       _sideList.ensureIndexIsVisible(index);
439       /*
440        * TODO Object newName = JOptionPane.showInputDialog( this,
441        * "Specify a new name for this RNA", "Rename RNA",
442        * JOptionPane.QUESTION_MESSAGE, (Icon)null, null, item.toString()); if
443        * (newName!=null) { item.name = newName.toString();
444        * this._sideList.repaint(); }
445        */
446     }
447   }
448
449   @Override
450   public String[] getPdbFile()
451   {
452     return null;
453   }
454
455   @Override
456   public void releaseReferences(Object svl)
457   {
458   }
459
460   @Override
461   public void updateColours(Object source)
462   {
463   }
464
465   @Override
466   public void componentHidden(ComponentEvent e)
467   {
468   }
469
470   @Override
471   public void componentMoved(ComponentEvent e)
472   {
473   }
474
475   @Override
476   public void componentResized(ComponentEvent e)
477   {
478   }
479
480   @Override
481   public void componentShown(ComponentEvent e)
482   {
483   }
484
485   @Override
486   public void highlightAtoms(List<AtomSpec> atoms)
487   {
488   }
489
490   @Override
491   public boolean isListeningFor(SequenceI seq)
492   {
493     return true;
494   }
495
496   /**
497    * Returns the path to a temporary file containing a representation of the
498    * state of the Varna display, or null on any error
499    * 
500    * @param rna
501    * @param jds
502    * 
503    * @return
504    */
505   public String getStateInfo(RNA rna)
506   {
507     if (vp == null)
508     {
509       return null;
510     }
511
512     /*
513      * we have to show the RNA we want to save in the viewer; get the currently
514      * displayed model first so we can restore it
515      */
516     FullBackup sel = (FullBackup) _sideList.getSelectedValue();
517
518     FullBackup model = null;
519     ListModel models = _sideList.getModel();
520     for (int i = 0; i < models.getSize(); i++)
521     {
522       model = (FullBackup) models.getElementAt(i);
523       if (model.rna == rna)
524       {
525         break;
526       }
527     }
528     if (model == null)
529     {
530       return null;
531     }
532
533     /*
534      * switch display if not already shown
535      */
536     if (sel.rna != rna)
537     {
538       vp.showRNA(model.rna, model.config);
539     }
540
541     try
542     {
543       File temp;
544       temp = File.createTempFile("varna", null);
545       temp.deleteOnExit();
546       String filePath = temp.getAbsolutePath();
547       vp.toXML(filePath);
548
549       /*
550        * restore the previous display if needed
551        */
552       if (sel.rna != rna)
553       {
554         vp.showRNA(sel.rna, sel.config);
555       }
556       
557       return filePath;
558     } catch (IOException e)
559     {
560       return null;
561     }
562   }
563
564   public int getSelectedIndex()
565   {
566     return _sideList.getSelectedIndex();
567   }
568
569   /**
570    * Switch the Varna display to the structure selected in the left hand panel
571    * 
572    * @param evt
573    */
574   protected void changeSelectedStructure_actionPerformed(ListSelectionEvent evt)
575   {
576     if (!evt.getValueIsAdjusting())
577     {
578       showSelectedStructure();
579     }
580   }
581
582   /**
583    * 
584    */
585   protected void showSelectedStructure()
586   {
587     FullBackup sel = (FullBackup) _sideList.getSelectedValue();
588     if (sel != null)
589     {
590       vp.showRNA(sel.rna, sel.config);
591     }
592   }
593
594   /**
595    * Set and display the selected item in the list of structures
596    * 
597    * @param selectedRna
598    */
599   public void setSelectedIndex(int selectedRna)
600   {
601     /*
602      * note this does nothing if, say, selecting item 3 when only 1 has been
603      * added on load
604      */
605     _sideList.setSelectedIndex(selectedRna);
606     // TODO ? need a worker thread to get this to happen properly
607     showSelectedStructure();
608   }
609
610   /**
611    * Add an RNA structure to the selection list
612    * 
613    * @param rna
614    */
615   public void addStructure(RNA rna)
616   {
617     VARNAConfig config = vp.getConfig().clone();
618     addStructure(rna, config);
619   }
620
621   /**
622    * @param rna
623    * @param config
624    */
625   protected void addStructure(RNA rna, VARNAConfig config)
626   {
627     rna.drawRNARadiate(config);
628     _rnaList.add(config, rna, rna.getName());
629   }
630 }