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