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