74308564ca8fd5cfaa97403e67f73133e3945d1d
[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(
285                                 path.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                       // BH 2018 SwingJS clone of varnaPanel or its config will be the object itself, not a clone
294                     }
295                   }
296                 }
297               }
298             }
299           }
300           // If we made it this far, everything worked.
301           dtde.dropComplete(true);
302           return;
303         }
304       }
305       // Hmm, the user must not have dropped a file list
306       dtde.rejectDrop();
307     } catch (Exception e)
308     {
309       e.printStackTrace();
310       dtde.rejectDrop();
311     }
312
313   }
314
315   private class BackupHolder
316   {
317     private DefaultListModel<FullBackup> _rnalist;
318
319     private List<RNA> _rnas = new ArrayList<RNA>();
320
321     JList _l;
322
323     public BackupHolder(DefaultListModel<FullBackup> rnaList, JList l)
324     {
325       _rnalist = rnaList;
326       _l = l;
327     }
328
329     public void add(VARNAConfig c, RNA r, String name)
330     {
331       add(c, r, name, false);
332     }
333
334     /**
335      * Adds an entry to the end of the selection list and (optionally) sets it
336      * as selected
337      * 
338      * @param c
339      * @param r
340      * @param name
341      * @param select
342      */
343     public void add(VARNAConfig c, RNA r, String name, boolean select)
344     {
345       if (select)
346       {
347         _l.removeSelectionInterval(0, _rnalist.size());
348       }
349       if (name.equals(""))
350       {
351         name = generateDefaultName();
352       }
353       FullBackup bck = new FullBackup(c, r, name);
354       _rnas.add(r);
355       _rnalist.addElement(bck);
356       if (select)
357       {
358         _l.setSelectedIndex(0);
359       }
360     }
361
362     public FullBackup getElementAt(int i)
363     {
364       return _rnalist.getElementAt(i);
365     }
366   }
367
368   public void mouseClicked(MouseEvent e)
369   {
370     if (e.getClickCount() == 2)
371     {
372       int index = _sideList.locationToIndex(e.getPoint());
373       ListModel<FullBackup> dlm = _sideList.getModel();
374       // FullBackup item = dlm.getElementAt(index);
375
376       _sideList.ensureIndexIsVisible(index);
377       /*
378        * TODO Object newName = JvOptionPane.showInputDialog( this,
379        * "Specify a new name for this RNA", "Rename RNA",
380        * JvOptionPane.QUESTION_MESSAGE, (Icon)null, null, item.toString()); if
381        * (newName!=null) { item.name = newName.toString();
382        * this._sideList.repaint(); }
383        */
384     }
385   }
386
387   @Override
388   public String[] getStructureFiles()
389   {
390     return null;
391   }
392
393   @Override
394   public void releaseReferences(Object svl)
395   {
396   }
397
398   @Override
399   public void updateColours(Object source)
400   {
401   }
402
403   @Override
404   public void componentHidden(ComponentEvent e)
405   {
406   }
407
408   @Override
409   public void componentMoved(ComponentEvent e)
410   {
411   }
412
413   @Override
414   public void componentResized(ComponentEvent e)
415   {
416   }
417
418   @Override
419   public void componentShown(ComponentEvent e)
420   {
421   }
422
423   @Override
424   public void highlightAtoms(List<AtomSpec> atoms)
425   {
426   }
427
428   @Override
429   public boolean isListeningFor(SequenceI seq)
430   {
431     return true;
432   }
433
434   /**
435    * Returns the path to a temporary file containing a representation of the
436    * state of the Varna display, or null on any error
437    * 
438    * @param rna
439    * @param jds
440    * 
441    * @return
442    */
443   public String getStateInfo(RNA rna)
444   {
445     if (vp == null)
446     {
447       return null;
448     }
449
450     /*
451      * we have to show the RNA we want to save in the viewer; get the currently
452      * displayed model first so we can restore it
453      */
454     FullBackup sel = (FullBackup) _sideList.getSelectedValue();
455
456     FullBackup model = null;
457     ListModel models = _sideList.getModel();
458     for (int i = 0; i < models.getSize(); i++)
459     {
460       model = (FullBackup) models.getElementAt(i);
461       if (model.rna == rna)
462       {
463         break;
464       }
465     }
466     if (model == null)
467     {
468       return null;
469     }
470
471     /*
472      * switch display
473      */
474     vp.showRNA(model.rna, model.config);
475
476     try
477     {
478       File temp;
479       temp = File.createTempFile("varna", null);
480       temp.deleteOnExit();
481       String filePath = temp.getAbsolutePath();
482       vp.toXML(filePath);
483
484       /*
485        * restore the previous display
486        */
487       vp.showRNA(sel.rna, sel.config);
488
489       return filePath;
490     } catch (IOException e)
491     {
492       return null;
493     }
494   }
495
496   public int getSelectedIndex()
497   {
498     return _sideList.getSelectedIndex();
499   }
500
501   /**
502    * Switch the Varna display to the structure selected in the left hand panel
503    * 
504    * @param evt
505    */
506   protected void changeSelectedStructure_actionPerformed(
507           ListSelectionEvent evt)
508   {
509     if (!evt.getValueIsAdjusting())
510     {
511       showSelectedStructure();
512     }
513   }
514
515   /**
516    * 
517    */
518   protected void showSelectedStructure()
519   {
520     FullBackup sel = (FullBackup) _sideList.getSelectedValue();
521     if (sel != null)
522     {
523       vp.showRNA(sel.rna, sel.config);
524     }
525   }
526
527   /**
528    * Set and display the selected item in the list of structures
529    * 
530    * @param selectedRna
531    */
532   public void setSelectedIndex(final int selectedRna)
533   {
534     /*
535      * note this does nothing if, say, selecting item 3 when only 1 has been
536      * added on load
537      */
538     _sideList.setSelectedIndex(selectedRna);
539     // TODO ? need a worker thread to get this to happen properly
540   }
541
542   /**
543    * Add an RNA structure to the selection list
544    * 
545    * @param rna
546    */
547   public void addStructure(RNA rna)
548   {
549     VARNAConfig config = vp.getConfig().clone(); // BH 2018 this will NOT be a clone in SwingJS
550     addStructure(rna, config);
551   }
552
553   /**
554    * @param rna
555    * @param config
556    */
557   protected void addStructure(final RNA rna, final VARNAConfig config)
558   {
559     drawRna(rna, config);
560     _rnaList.add(config, rna, rna.getName());
561   }
562
563   /**
564    * @param rna
565    * @param config
566    */
567   protected void drawRna(final RNA rna, final VARNAConfig config)
568   {
569     try
570     {
571       rna.drawRNA(rna.getDrawMode(), config);
572     } catch (ExceptionNAViewAlgorithm e)
573     {
574       // only throwable for draw mode = 3 NAView
575       System.err.println("Error drawing RNA: " + e.getMessage());
576     }
577   }
578 }