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