JAL-3253 preliminary static fixes for JavaScript part 2
[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 final 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<>();
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       @Override
126       public void valueChanged(ListSelectionEvent evt)
127       {
128         changeSelectedStructure_actionPerformed(evt);
129       }
130     });
131     _rnaList = new BackupHolder(dlm, _sideList);
132
133     try
134     {
135       vp = new VARNAPanel("0", ".");
136     } catch (ExceptionNonEqualLength e)
137     {
138       vp.errorDialog(e);
139     }
140     vp.setPreferredSize(new Dimension(400, 400));
141
142     JScrollPane listScroller = new JScrollPane(_sideList);
143     listScroller.setPreferredSize(new Dimension(150, 0));
144
145     vp.setBackground(_backgroundColor);
146
147     JLabel j = new JLabel(
148             MessageManager.getString("label.structures_manager"),
149             JLabel.CENTER);
150     _listPanel.setLayout(new BorderLayout());
151
152     _listPanel.add(j, BorderLayout.NORTH);
153     _listPanel.add(listScroller, BorderLayout.CENTER);
154
155     new DropTarget(vp, new DropTargetAdapter()
156     {
157       @Override
158       public void drop(DropTargetDropEvent dtde)
159       {
160         AppVarnaBinding.this.drop(dtde);
161       }
162     });
163   }
164
165   public JPanel getListPanel()
166   {
167     return _listPanel;
168   }
169
170   /**
171    * Returns the currently selected RNA, or null if none selected
172    * 
173    * @return
174    */
175   public RNA getSelectedRNA()
176   {
177     int selectedIndex = _sideList.getSelectedIndex();
178     if (selectedIndex < 0)
179     {
180       return null;
181     }
182     FullBackup selected = _rnaList.getElementAt(selectedIndex);
183     return selected.rna;
184   }
185
186   /**
187    * Substitute currently selected RNA with the edited one
188    * 
189    * @param rnaEdit
190    */
191   public void updateSelectedRNA(RNA rnaEdit)
192   {
193     vp.repaint();
194     vp.showRNA(rnaEdit);
195   }
196
197   public static String generateDefaultName()
198   {
199     return "User file #" + _nextID++;
200   }
201
202   public String[][] getParameterInfo()
203   {
204     String[][] info = {
205         // Parameter Name Kind of Value Description,
206         { "sequenceDBN", "String", "A raw RNA sequence" },
207         { "structureDBN", "String",
208             "An RNA structure in dot bracket notation (DBN)" },
209         { errorOpt, "boolean", "To show errors" }, };
210     return info;
211   }
212
213   @SuppressWarnings("unused")
214   private Color getSafeColor(String col, Color def)
215   {
216     Color result;
217     try
218     {
219       result = Color.decode(col);
220     } catch (Exception e)
221     {
222       try
223       {
224         result = Color.getColor(col, def);
225       } catch (Exception e2)
226       {
227         return def;
228       }
229     }
230     return result;
231   }
232
233   public VARNAPanel get_varnaPanel()
234   {
235     return vp;
236   }
237
238   public void set_varnaPanel(VARNAPanel surface)
239   {
240     vp = surface;
241   }
242
243   public void drop(DropTargetDropEvent dtde)
244   {
245     try
246     {
247       Transferable tr = dtde.getTransferable();
248       DataFlavor[] flavors = tr.getTransferDataFlavors();
249       for (int i = 0; i < flavors.length; i++)
250       {
251         if (flavors[i].isFlavorJavaFileListType())
252         {
253           dtde.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
254           Object ob = tr.getTransferData(flavors[i]);
255           if (ob instanceof List)
256           {
257             List list = (List) ob;
258             for (int j = 0; j < list.size(); j++)
259             {
260               Object o = list.get(j);
261
262               if (dtde.getSource() instanceof DropTarget)
263               {
264                 DropTarget dt = (DropTarget) dtde.getSource();
265                 Component c = dt.getComponent();
266                 if (c instanceof VARNAPanel)
267                 {
268                   String path = o.toString();
269                   VARNAPanel varnaPanel = (VARNAPanel) c;
270                   try
271                   {
272                     FullBackup bck = VARNAPanel.importSession(path);
273                     _rnaList.add(bck.config, bck.rna, bck.name, true);
274                   } catch (ExceptionLoadingFailed e3)
275                   {
276                     int mn = 1;
277                     Collection<RNA> mdls = fr.orsay.lri.varna.factories.RNAFactory
278                             .loadSecStr(path);
279                     for (RNA r : mdls)
280                     {
281                       r.drawRNA(varnaPanel.getConfig());
282                       String name = r.getName();
283                       if (name.equals(""))
284                       {
285                         name = path.substring(
286                                 path.lastIndexOf(File.separatorChar) + 1);
287                       }
288                       if (mdls.size() > 1)
289                       {
290                         name += " (Model " + mn++ + ")";
291                       }
292                       _rnaList.add(varnaPanel.getConfig().clone(), r, name,
293                               true); 
294                       // BH 2018 SwingJS clone of varnaPanel or its config will 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<>();
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 clone in SwingJS
551     addStructure(rna, config);
552   }
553
554   /**
555    * @param rna
556    * @param config
557    */
558   protected void addStructure(final RNA rna, final VARNAConfig config)
559   {
560     drawRna(rna, config);
561     _rnaList.add(config, rna, rna.getName());
562   }
563
564   /**
565    * @param rna
566    * @param config
567    */
568   protected void drawRna(final RNA rna, final VARNAConfig config)
569   {
570     try
571     {
572       rna.drawRNA(rna.getDrawMode(), config);
573     } catch (ExceptionNAViewAlgorithm e)
574     {
575       // only throwable for draw mode = 3 NAView
576       System.err.println("Error drawing RNA: " + e.getMessage());
577     }
578   }
579 }