JAL-2937 use preferences for presets, simplify some constructors
[jalview.git] / src / jalview / gui / WsJobParameters.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.gui.OptsAndParamsPage.OptionBox;
24 import jalview.gui.OptsAndParamsPage.ParamBox;
25 import jalview.util.MessageManager;
26 import jalview.ws.jws2.JabaParamStore;
27 import jalview.ws.jws2.JabaPreset;
28 import jalview.ws.jws2.Jws2Discoverer;
29 import jalview.ws.jws2.jabaws2.Jws2Instance;
30 import jalview.ws.params.ArgumentI;
31 import jalview.ws.params.OptionI;
32 import jalview.ws.params.ParamDatastoreI;
33 import jalview.ws.params.ParameterI;
34 import jalview.ws.params.WsParamSetI;
35
36 import java.awt.BorderLayout;
37 import java.awt.Component;
38 import java.awt.Dimension;
39 import java.awt.FlowLayout;
40 import java.awt.Font;
41 import java.awt.GridBagConstraints;
42 import java.awt.GridBagLayout;
43 import java.awt.GridLayout;
44 import java.awt.Rectangle;
45 import java.awt.event.ActionEvent;
46 import java.awt.event.ActionListener;
47 import java.awt.event.HierarchyBoundsListener;
48 import java.awt.event.HierarchyEvent;
49 import java.awt.event.ItemEvent;
50 import java.awt.event.ItemListener;
51 import java.awt.event.WindowEvent;
52 import java.awt.event.WindowListener;
53 import java.net.URL;
54 import java.util.Hashtable;
55 import java.util.Iterator;
56 import java.util.List;
57 import java.util.Vector;
58
59 import javax.swing.JButton;
60 import javax.swing.JComboBox;
61 import javax.swing.JDialog;
62 import javax.swing.JFrame;
63 import javax.swing.JLabel;
64 import javax.swing.JPanel;
65 import javax.swing.JScrollPane;
66 import javax.swing.JSplitPane;
67 import javax.swing.JTextArea;
68 import javax.swing.border.TitledBorder;
69 import javax.swing.event.DocumentEvent;
70 import javax.swing.event.DocumentListener;
71
72 import compbio.metadata.Argument;
73 import compbio.metadata.Option;
74 import compbio.metadata.Parameter;
75 import compbio.metadata.Preset;
76 import compbio.metadata.PresetManager;
77 import compbio.metadata.RunnerConfig;
78 import net.miginfocom.swing.MigLayout;
79
80 /**
81  * job parameter editing/browsing dialog box. User can browse existing settings
82  * (user + presets + Defaults), and any changes to parameters creates a modified
83  * user parameter set. LOGIC: If the parameter set is modified, and its name is
84  * a valid, non-existant user parameter set, then a save button is shown. If the
85  * parameter set is modified and its name is a valid, extant user parameter set,
86  * then an update button is shown. If user parameter set's name is edited, and
87  * old name exists as a writable user parameter set, then rename button is
88  * shown. If current parameter set is associated with a user defined parameter
89  * set, then : if set is modifed, a 'revert' button is shown. if set is not
90  * modified, a 'delete' button is shown.
91  * 
92  * @author JimP
93  * 
94  */
95 public class WsJobParameters extends JPanel implements ItemListener,
96         ActionListener, DocumentListener, OptsParametersContainerI
97 {
98   private static final int PREFERRED_WIDTH = 540;
99
100   private static final int DEFAULT_HEIGHT = 640;
101
102   URL linkImageURL = getClass().getResource("/images/link.gif");
103
104   private static final String SVC_DEF = "Defaults"; // this is the null
105                                                     // parameter set as shown to
106                                                     // user
107
108   /**
109    * manager for options and parameters.
110    */
111   OptsAndParamsPage opanp = new OptsAndParamsPage(this);
112
113   /**
114    * panel containing job options
115    */
116   JPanel jobOptions = new JPanel();
117
118   /**
119    * panel containing job parameters
120    */
121   JPanel paramList = new JPanel();
122
123   JPanel SetNamePanel = new JPanel();
124
125   JPanel setDetails = new JPanel();
126
127   JSplitPane settingsPanel = new JSplitPane();
128
129   JPanel jobPanel = new JPanel();
130
131   JScrollPane jobOptionsPane = new JScrollPane();
132
133   JButton createpref = new JButton();
134
135   JButton deletepref = new JButton();
136
137   JButton revertpref = new JButton();
138
139   JButton updatepref = new JButton();
140
141   JButton startjob = new JButton();
142
143   JButton canceljob = new JButton();
144
145   JComboBox setName = new JComboBox();
146
147   JTextArea setDescr = new JTextArea();
148
149   JScrollPane paramPane = new JScrollPane();
150
151   // ScrollablePanel optsAndparams = new ScrollablePanel();
152   JPanel optsAndparams = new JPanel();
153
154   RunnerConfig serviceOptions;
155
156   ParamDatastoreI paramStore;
157
158   private int MAX_OPTWIDTH = 200;
159
160   WsJobParameters(Jws2Instance service)
161   {
162     this(service, null);
163   }
164
165   public WsJobParameters(ParamDatastoreI store, WsParamSetI preset,
166           List<ArgumentI> args)
167   {
168     super();
169     jbInit();
170     this.paramStore = store;
171     this.service = null;
172     // argSetModified(false);
173     // populate parameter table
174     init(preset, args);
175     // display in new JFrame attached to parent.
176     validate();
177   }
178
179   public WsJobParameters(Jws2Instance service, WsParamSetI preset)
180   {
181     this(service, preset, null);
182   }
183
184   /**
185    * 
186    * @param service
187    * @param preset
188    */
189   public WsJobParameters(Jws2Instance service,
190           WsParamSetI preset, List<Argument> jobArgset)
191   {
192     this(null, service, preset, jobArgset);
193   }
194
195   /**
196    * 
197    * @param paramStorei
198    * @param service
199    * @param preset
200    * @param jobArgset
201    */
202   public WsJobParameters(ParamDatastoreI paramStorei, Jws2Instance service,
203           WsParamSetI preset, List<Argument> jobArgset)
204   {
205     super();
206     jbInit();
207     this.paramStore = paramStorei;
208     if (paramStore == null && service != null)
209     {
210       paramStore = service.getParamStore();
211     }
212     this.service = service;
213     // argSetModified(false);
214     // populate parameter table
215     initForService(preset, jobArgset);
216     // display in new JFrame attached to parent.
217     validate();
218   }
219
220   int response = -1;
221
222   JDialog frame = null;
223
224   /**
225    * shows a modal dialog containing the parameters.
226    * 
227    * @return
228    */
229   public boolean showRunDialog()
230   {
231
232     frame = new JDialog(Desktop.instance, true);
233     if (service != null)
234     {
235       frame.setTitle(MessageManager.formatMessage("label.edit_params_for",
236               new String[]
237       { service.getActionText() }));
238     }
239
240     Rectangle deskr = Desktop.instance.getBounds();
241     Dimension pref = this.getPreferredSize();
242     frame.setBounds(
243             new Rectangle((int) (deskr.getCenterX() - pref.width / 2),
244                     (int) (deskr.getCenterY() - pref.height / 2),
245                     pref.width, pref.height));
246     frame.setContentPane(this);
247
248     // should perhaps recover defaults from user prefs.
249
250     frame.validate();
251     javax.swing.SwingUtilities.invokeLater(new Runnable()
252     {
253       @Override
254       public void run()
255       {
256         // jobPanel.setDividerLocation(0.25);
257
258       }
259     });
260     frame.setVisible(true);
261
262     if (response > 0)
263     {
264       return true;
265     }
266     return false;
267   }
268
269   private void jbInit()
270   {
271     this.addHierarchyBoundsListener(new HierarchyBoundsListener()
272     {
273
274       @Override
275       public void ancestorResized(HierarchyEvent arg0)
276       {
277         refreshParamLayout();
278       }
279
280       @Override
281       public void ancestorMoved(HierarchyEvent arg0)
282       {
283         // TODO Auto-generated method stub
284
285       }
286     });
287     updatepref = JvSwingUtils.makeButton(
288             MessageManager.getString("action.update"),
289             MessageManager.getString("label.update_user_parameter_set"),
290             new ActionListener()
291             {
292
293               @Override
294               public void actionPerformed(ActionEvent e)
295               {
296                 update_actionPerformed(e);
297               }
298             });
299     deletepref = JvSwingUtils.makeButton(
300             MessageManager.getString("action.delete"),
301             MessageManager.getString("label.delete_user_parameter_set"),
302             new ActionListener()
303             {
304
305               @Override
306               public void actionPerformed(ActionEvent e)
307               {
308                 delete_actionPerformed(e);
309               }
310             });
311     createpref = JvSwingUtils.makeButton(
312             MessageManager.getString("action.create"),
313             MessageManager.getString("label.create_user_parameter_set"),
314             new ActionListener()
315             {
316
317               @Override
318               public void actionPerformed(ActionEvent e)
319               {
320                 create_actionPerformed(e);
321               }
322             });
323     revertpref = JvSwingUtils.makeButton(
324             MessageManager.getString("action.revert"),
325             MessageManager
326                     .getString("label.revert_changes_user_parameter_set"),
327             new ActionListener()
328             {
329
330               @Override
331               public void actionPerformed(ActionEvent e)
332               {
333                 revert_actionPerformed(e);
334               }
335             });
336     startjob = JvSwingUtils.makeButton(
337             MessageManager.getString("action.start_job"),
338             MessageManager.getString("label.start_job_current_settings"),
339             new ActionListener()
340             {
341               @Override
342               public void actionPerformed(ActionEvent e)
343               {
344                 startjob_actionPerformed(e);
345               }
346             });
347     canceljob = JvSwingUtils.makeButton(
348             MessageManager.getString("action.cancel_job"),
349             MessageManager.getString("label.cancel_job_close_dialog"),
350             new ActionListener()
351             {
352               @Override
353               public void actionPerformed(ActionEvent e)
354               {
355                 canceljob_actionPerformed(e);
356               }
357             });
358
359     setDetails.setBorder(
360             new TitledBorder(MessageManager.getString("label.details")));
361     setDetails.setLayout(new BorderLayout());
362     setDescr.setColumns(40);
363     setDescr.setWrapStyleWord(true);
364     setDescr.setLineWrap(true);
365     setDescr.setBackground(getBackground());
366     setDescr.setEditable(true);
367     setDescr.getDocument().addDocumentListener(this);
368     setDescr.setToolTipText(
369             MessageManager.getString("label.edit_notes_parameter_set"));
370     JScrollPane setDescrView = new JScrollPane();
371     setDescrView.getViewport().setView(setDescr);
372     setName.setEditable(true);
373     setName.addItemListener(this);
374     setName.getEditor().addActionListener(this);
375     JPanel setNameInfo = new JPanel(new FlowLayout(FlowLayout.LEFT));
376     GridBagLayout gbl = new GridBagLayout();
377     SetNamePanel.setLayout(gbl);
378
379     JLabel setNameLabel = new JLabel(
380             MessageManager.getString("label.current_parameter_set_name"));
381     setNameLabel.setFont(new java.awt.Font("Verdana", Font.PLAIN, 10));
382
383     setNameInfo.add(setNameLabel);
384     setNameInfo.add(setName);
385
386     // initial button visibility
387     updatepref.setVisible(false);
388     deletepref.setVisible(false);
389     revertpref.setVisible(false);
390     createpref.setVisible(false);
391     JPanel setsavebuts = new JPanel();
392     setsavebuts.setLayout(new FlowLayout(FlowLayout.LEFT)); // GridLayout(1,2));
393     ((FlowLayout) setsavebuts.getLayout()).setHgap(10);
394     ((FlowLayout) setsavebuts.getLayout()).setVgap(0);
395     JPanel spacer = new JPanel();
396     spacer.setPreferredSize(new Dimension(2, 30));
397     setsavebuts.add(spacer);
398     setsavebuts.add(deletepref);
399     setsavebuts.add(revertpref);
400     setsavebuts.add(createpref);
401     setsavebuts.add(updatepref);
402     // setsavebuts.setSize(new Dimension(150, 30));
403     JPanel buttonArea = new JPanel(new GridLayout(1, 1));
404     buttonArea.add(setsavebuts);
405     SetNamePanel.add(setNameInfo);
406     GridBagConstraints gbc = new GridBagConstraints();
407     gbc.gridheight = 2;
408     gbl.setConstraints(setNameInfo, gbc);
409     SetNamePanel.add(buttonArea);
410     gbc = new GridBagConstraints();
411     gbc.gridx = 0;
412     gbc.gridy = 2;
413     gbc.gridheight = 1;
414     gbl.setConstraints(buttonArea, gbc);
415     setDetails.add(setDescrView, BorderLayout.CENTER);
416
417     // paramPane.setPreferredSize(new Dimension(360, 400));
418     // paramPane.setPreferredSize(null);
419     jobOptions.setBorder(
420             new TitledBorder(MessageManager.getString("label.options")));
421     jobOptions.setOpaque(true);
422     paramList.setBorder(
423             new TitledBorder(MessageManager.getString("label.parameters")));
424     paramList.setOpaque(true);
425     JPanel bjo = new JPanel(new BorderLayout());
426     JPanel bjp = new JPanel(new BorderLayout());
427     bjo.add(jobOptions, BorderLayout.CENTER);
428     bjp.add(paramList, BorderLayout.CENTER);
429     bjp.setOpaque(true);
430     bjo.setOpaque(true);
431     // optsAndparams.setScrollableWidth(ScrollableSizeHint.FIT);
432     // optsAndparams.setScrollableHeight(ScrollableSizeHint.NONE);
433     // optsAndparams.setLayout(new BorderLayout());
434     optsAndparams.setLayout(new BorderLayout());
435     optsAndparams.add(jobOptions, BorderLayout.NORTH);
436     optsAndparams.add(paramList, BorderLayout.CENTER);
437     JPanel jp = new JPanel(new BorderLayout());
438     jp.add(optsAndparams, BorderLayout.CENTER);
439     paramPane.getViewport().setView(jp);
440     paramPane.setBorder(null);
441     setLayout(new BorderLayout());
442     jobPanel.setPreferredSize(null);
443     jobPanel.setLayout(new BorderLayout());
444     jobPanel.add(setDetails, BorderLayout.NORTH);
445     jobPanel.add(paramPane, BorderLayout.CENTER);
446     // jobPanel.setOrientation(JSplitPane.VERTICAL_SPLIT);
447
448     add(SetNamePanel, BorderLayout.NORTH);
449     add(jobPanel, BorderLayout.CENTER);
450
451     JPanel dialogpanel = new JPanel();
452     dialogpanel.add(startjob);
453     dialogpanel.add(canceljob);
454     // JAL-1580: setMaximumSize() doesn't work, so just size for the worst case:
455     // check for null is for JUnit usage
456     final int windowHeight = Desktop.instance == null ? DEFAULT_HEIGHT
457             : Desktop.instance.getHeight();
458     // setPreferredSize(new Dimension(PREFERRED_WIDTH, windowHeight));
459     add(dialogpanel, BorderLayout.SOUTH);
460     validate();
461   }
462
463   protected void revert_actionPerformed(ActionEvent e)
464   {
465     reInitDialog(lastParmSet);
466     updateWebServiceMenus();
467   }
468
469   protected void update_actionPerformed(ActionEvent e)
470   {
471     if (isUserPreset)
472     {
473       String curname = ((String) setName.getSelectedItem()).trim();
474       _updatePreset(lastParmSet, curname);
475       lastParmSet = curname;
476       isUserPreset = true;
477       initArgSetModified();
478       syncSetNamesWithStore();
479     }
480   }
481
482   private void _deleteUserPreset(String lastParmSet2)
483   {
484     paramStore.deletePreset(lastParmSet2);
485   }
486
487   protected void delete_actionPerformed(ActionEvent e)
488   {
489     if (isUserPreset)
490     {
491       // delete current preset's saved entry
492       _deleteUserPreset(lastParmSet);
493     }
494     reInitDialog(null); // service default
495     updateWebServiceMenus();
496   }
497
498   protected void create_actionPerformed(ActionEvent e)
499   {
500     String curname = ((String) setName.getSelectedItem()).trim();
501     if (curname.length() > 0)
502     {
503       _storeCurrentPreset(curname);
504       lastParmSet = curname;
505       isUserPreset = true;
506       reInitDialog(curname);
507       initArgSetModified();
508       updateWebServiceMenus();
509     }
510     else
511     {
512       // TODO: show warning
513       System.err.println("Invalid name. Not saved.");
514     }
515   }
516
517   protected void canceljob_actionPerformed(ActionEvent e)
518   {
519     response = 0;
520     if (frame != null)
521     {
522       frame.setVisible(false);
523     }
524   }
525
526   protected void startjob_actionPerformed(ActionEvent e)
527   {
528     response = 1;
529     if (frame != null)
530     {
531       frame.setVisible(false);
532     }
533   }
534
535   Jws2Instance service;
536
537   /**
538    * list of service presets in the gui
539    */
540   Hashtable servicePresets = null;
541
542   /**
543    * set if dialog is being set - so handlers will avoid spurious events
544    */
545   boolean settingDialog = false;
546
547   void initForService(WsParamSetI jabap, List<Argument> jabajobArgset)
548   {
549     WsParamSetI p = null;
550     List<ArgumentI> jobArgset = null;
551     settingDialog = true;
552     { // instantiate the abstract proxy for Jaba objects
553       jobArgset = jabajobArgset == null ? null
554               : JabaParamStore.getJwsArgsfromJaba(jabajobArgset);
555       p = jabap; // (jabap != null) ? paramStore.getPreset(jabap.getName()) :
556                  // null;
557     }
558
559     init(p, jobArgset);
560
561   }
562
563   void init(WsParamSetI p, List<ArgumentI> jobArgset)
564   {
565     Hashtable exnames = new Hashtable();
566     for (int i = 0, iSize = setName.getItemCount(); i < iSize; i++)
567     {
568       exnames.put(setName.getItemAt(i), setName.getItemAt(i));
569     }
570     servicePresets = new Hashtable();
571     // Add the default entry - if not present already.
572     if (!exnames.contains(SVC_DEF))
573     {
574       setName.addItem(SVC_DEF);
575       exnames.put(SVC_DEF, SVC_DEF);
576       servicePresets.put(SVC_DEF, SVC_DEF);
577     }
578     String curname = (p == null ? "" : p.getName());
579     for (WsParamSetI pr : paramStore.getPresets())
580     {
581       if (!pr.isModifiable())
582       {
583         servicePresets.put(pr.getName(), "preset");
584       }
585       else
586       {
587       }
588       if (!exnames.contains(pr.getName()))
589       {
590         setName.addItem(pr.getName());
591       }
592     }
593     // TODO: if initial jobArgset matches a given user setting or preset then
594     // should recover setting accordingly
595     // updateTable(p, jobArgset);
596     if (p != null)
597     {
598       reInitDialog(p.getName());
599       initArgSetModified();
600     }
601     else
602     {
603       if (jobArgset != null && jobArgset.size() > 0)
604       {
605         curSetName = "Supplied Settings";
606         isUserPreset = false;
607         updateTable(p, jobArgset);
608         setName.setSelectedItem(curSetName);
609         updateButtonDisplay();
610       }
611       else
612       {
613         curSetName = null;
614         reInitDialog(null);
615       }
616     }
617     settingDialog = false;
618   }
619
620   private void updateTable(WsParamSetI p, List<ArgumentI> jobArgset)
621   {
622     boolean setDefaultParams = false;
623     if (lastParmSet == null)
624     {
625       isUserPreset = false;
626       // First call - so provide Service default settings
627       setName.setSelectedItem(lastSetName = SVC_DEF);
628     }
629     if (p == null && SVC_DEF.equals("" + setName.getSelectedItem()))
630     {
631       // indicate that service defaults should be set if available
632       setDefaultParams = true;
633     }
634     // populate table from default parameter set.
635     List<ArgumentI> args = paramStore.getServiceParameters();
636
637     // split to params and required arguments
638     {
639       int cw = 0;
640       boolean optset = false;
641       for (ArgumentI myarg : args)
642       {
643         // Ideally, Argument would implement isRequired !
644         if (myarg instanceof ParameterI)
645         {
646           ParameterI parm = (ParameterI) myarg;
647           opanp.addParameter(parm).validate();
648         }
649         else
650         {
651           if (myarg instanceof OptionI)
652           {
653             OptionI opt = (OptionI) myarg;
654             OptionBox ob = opanp.addOption(opt);
655             ob.resetToDefault(setDefaultParams);
656             if (MAX_OPTWIDTH < ob.getPreferredSize().width)
657             {
658               MAX_OPTWIDTH = ob.getPreferredSize().width;
659             }
660             ob.validate();
661             cw += ob.getPreferredSize().width + 5;
662           }
663           else
664           {
665             System.err.println("Ignoring unknown service argument type "
666                     + myarg.getClass().getName());
667           }
668         }
669       }
670       args = null; // no more args to process.
671     }
672     if (p != null)
673     {
674       isUserPreset = false;
675       // initialise setname
676       setName.setSelectedItem(lastSetName = p.getName());
677       setDescr.setText(lastDescrText = p.getDescription());
678       // TODO - URL link
679       try
680       {
681         args = p.getArguments();
682       } catch (Exception e)
683       {
684         e.printStackTrace();
685       }
686       // TODO: check if args should be unselected prior to resetting using the
687       // preset
688     }
689
690     if (jobArgset != null)
691     {
692       argSetModified(jobArgset, true);
693       args = jobArgset;
694     }
695     // get setargs from current object
696     if (args != null)
697     {
698       for (ArgumentI arg : args)
699       {
700         if (arg instanceof ParameterI)
701         {
702           opanp.setParameter((ParameterI) arg);
703         }
704         else
705         {
706           if (arg instanceof OptionI)
707           {
708             // System.out.println("Setting option "
709             // + System.identityHashCode(arg) + ":" + arg.getName()
710             // + " with " + arg.getDefaultValue());
711             opanp.selectOption((OptionI) arg, arg.getValue());
712           }
713         }
714
715       }
716     }
717
718     refreshParamLayout();
719     revalidate();
720   }
721
722   private boolean isModified()
723   {
724     return modifiedElements.size() > 0;
725   }
726
727   private Hashtable modifiedElements = new Hashtable();
728
729   /**
730    * reset gui and modification state settings
731    */
732   private void initArgSetModified()
733   {
734     curSetName = null;
735     modifiedElements.clear();
736     updateButtonDisplay();
737   }
738
739   private void updateButtonDisplay()
740   {
741     boolean _update = false, _create = false, _delete = false,
742             _revert = false;
743     if (modifiedElements.size() > 0)
744     {
745       // set modified
746       _revert = true;
747       _update = isUserPreset; // can only update user presets
748       if (!isUserPreset || modifiedElements.containsKey(setName))
749       {
750         // name modified - can create new preset
751         _create = true;
752       }
753     }
754     else
755     {
756       // set unmodified
757     }
758     // can still delete a user preset
759     _delete = isUserPreset;
760
761     createpref.setVisible(_create);
762     updatepref.setVisible(_update);
763     deletepref.setVisible(_delete);
764     revertpref.setVisible(_revert);
765     validate();
766   }
767
768   @Override
769   public void argSetModified(Object modifiedElement, boolean b)
770   {
771     if (settingDialog)
772     {
773       return;
774     }
775     if (!b)
776     {
777       modifiedElements.remove(modifiedElement);
778     }
779     else
780     {
781       if (b && modifiedElement == setName
782               && modifiedElements.contains(modifiedElement))
783       {
784         // HACK! prevents iteration on makeSetNameValid
785         b = false;
786       }
787       modifiedElements.put(modifiedElement, modifiedElement);
788     }
789     // set mod status based on presence of elements in table
790     if (b && modifiedElements.size() > 0)
791     {
792       makeSetNameValid(!isUserPreset);
793       SetNamePanel.revalidate();
794     }
795     updateButtonDisplay();
796   }
797
798   private boolean isServicePreset(String selectedItem)
799   {
800     return selectedItem.equals(SVC_DEF)
801             || servicePresets.containsKey(selectedItem);
802   }
803
804   /**
805    * check if the current set name is a valid set name for saving, if not, then
806    * fix it.
807    */
808   private void makeSetNameValid(boolean newuserset)
809   {
810     boolean stn = settingDialog;
811     boolean renamed = false;
812     settingDialog = true;
813     String nm = (curSetName != null ? curSetName
814             : (String) setName.getSelectedItem());
815     // check if the name is reserved - if it is, rename it.
816     if (isServicePreset(nm))
817     {
818       nm = "User " + nm;
819       renamed = true;
820     }
821     String tnm = nm;
822     if (newuserset)
823     {
824       int i = 0;
825       while (paramStore.getPreset(tnm) != null)
826       {
827         tnm = nm + " (" + (++i) + ")";
828         renamed = true;
829       }
830       if (i > 0)
831       {
832         nm = tnm;
833       }
834     }
835
836     boolean makeupdate = false;
837     // sync the gui with the preset database
838     for (int i = 0, iS = setName.getItemCount(); i < iS; i++)
839     {
840       String snm = (String) setName.getItemAt(i);
841       if (snm.equals(nm))
842       {
843         makeupdate = true;
844         // setName.setSelectedIndex(i);
845       }
846     }
847     if (!makeupdate)
848     {
849       setName.addItem(curSetName = nm);
850       setName.setSelectedItem(curSetName);
851     }
852     if (renamed)
853     {
854       settingDialog = false; // we need this name change to be registered.
855       argSetModified(setName, renamed);
856     }
857     settingDialog = stn;
858   }
859
860   @Override
861   public void refreshParamLayout()
862   {
863     // optsAndparams.setPreferredSize(null);
864     FlowLayout fl = new FlowLayout(FlowLayout.LEFT);
865     int sep = fl.getVgap();
866     boolean fh = true;
867     int s = jobOptions.getBorder().getBorderInsets(jobOptions).bottom
868             + jobOptions.getBorder().getBorderInsets(jobOptions).top
869             + 2 * sep;
870
871     int panewidth = paramPane.getViewport().getSize().width - 120
872             - jobOptions.getBorder().getBorderInsets(jobOptions).left
873             + jobOptions.getBorder().getBorderInsets(jobOptions).right;
874
875     // int w = 2 * fl.getHgap()
876     // + (MAX_OPTWIDTH > OptsAndParamsPage.PARAM_WIDTH ? MAX_OPTWIDTH
877     // : OptsAndParamsPage.PARAM_WIDTH);
878     int hgap = fl.getHgap();
879     int cw = hgap;
880
881     if (opanp.getOptSet().size() > 0)
882     {
883
884       jobOptions.setLayout(new MigLayout("", "", ""));
885       jobOptions.removeAll();
886
887       for (OptionBox pbox : opanp.getOptSet().values())
888       {
889         pbox.validate();
890         cw += pbox.getSize().width + hgap;
891         if (cw + 120 > panewidth)
892         {
893           jobOptions.add(pbox, "wrap");
894           // System.out.println("Wrap on "+pbox.option.getName());
895           cw = hgap + pbox.getSize().width;
896           fh = true;
897         }
898         else
899         {
900           jobOptions.add(pbox);
901         }
902         if (fh)
903         {
904           // finalh += pbox.getSize().height + fl.getVgap();
905           fh = false;
906         }
907       }
908       jobOptions.revalidate();
909     }
910     else
911     {
912       jobOptions.setVisible(false);
913     }
914
915     // Now layout the parameters assuming they occupy one column - to calculate
916     // total height of options+parameters
917     fl = new FlowLayout(FlowLayout.LEFT);
918     // helpful hint from
919     // http://stackoverflow.com/questions/2743177/top-alignment-for-flowlayout
920     fl.setAlignOnBaseline(true);
921     if (opanp.getParamSet().size() > 0)
922     {
923       paramList.removeAll();
924       paramList.setLayout(new MigLayout("", "", ""));
925       fh = true;
926       for (ParamBox pbox : opanp.getParamSet().values())
927       {
928         pbox.validate();
929         cw += pbox.getSize().width + hgap;
930         if (cw + 160 > panewidth)
931         {
932           paramList.add(pbox, "wrap");
933           cw = pbox.getSize().width + hgap;
934           fh = true;
935         }
936         else
937         {
938           paramList.add(pbox);
939         }
940         if (fh)
941         {
942           // finalh += pbox.getSize().height + fl.getVgap();
943           fh = false;
944         }
945
946       }
947       /*
948        * s = 2 * sep; for (ParamBox pbox : opanp.getParamSet().values()) {
949        * pbox.validate(); s += sep +
950        * pbox.getPreferredSize().height+pbox.getBorder
951        * ().getBorderInsets(pbox).bottom; }
952        * 
953        * // paramList.setPreferredSize(new Dimension(w, s));
954        * os+=s+2*sep+paramList
955        * .getBorder().getBorderInsets(paramList).bottom+paramList
956        * .getBorder().getBorderInsets(paramList).top;
957        */
958       paramList.revalidate();
959     }
960     else
961     {
962       paramList.setVisible(false);
963     }
964     // TODO: waste some time trying to eliminate any unnecessary .validate calls
965     // here
966     // System.out.println("Size will be : "+w+","+os);
967     // optsAndparams.setPreferredSize(null);
968     // paramPane.getViewport().setView(optsAndparams);
969     paramPane.getViewport().setAutoscrolls(true);
970     paramPane.revalidate();
971     revalidate();
972   }
973
974   /**
975    * testing method - grab a service and parameter set and show the window
976    * 
977    * @param args
978    */
979   public static void main(String[] args)
980   {
981     jalview.ws.jws2.Jws2Discoverer disc = jalview.ws.jws2.Jws2Discoverer
982             .getDiscoverer();
983     int p = 0;
984     if (args.length > 0)
985     {
986       Vector<String> services = new Vector<>();
987       services.addElement(args[p++]);
988       Jws2Discoverer.getDiscoverer().setServiceUrls(services);
989     }
990     try
991     {
992       disc.run();
993     } catch (Exception e)
994     {
995       System.err.println("Aborting. Problem discovering services.");
996       e.printStackTrace();
997       return;
998     }
999     Jws2Instance lastserv = null;
1000     for (Jws2Instance service : disc.getServices())
1001     {
1002       lastserv = service;
1003       if (p >= args.length || service.serviceType.equalsIgnoreCase(args[p]))
1004       {
1005         if (lastserv != null)
1006         {
1007           List<Preset> prl = null;
1008           Preset pr = null;
1009           if (++p < args.length)
1010           {
1011             PresetManager prman = lastserv.getPresets();
1012             if (prman != null)
1013             {
1014               pr = prman.getPresetByName(args[p]);
1015               if (pr == null)
1016               {
1017                 // just grab the last preset.
1018                 prl = prman.getPresets();
1019               }
1020             }
1021           }
1022           else
1023           {
1024             PresetManager prman = lastserv.getPresets();
1025             if (prman != null)
1026             {
1027               prl = prman.getPresets();
1028             }
1029           }
1030           Iterator<Preset> en = (prl == null) ? null : prl.iterator();
1031           while (en != null && en.hasNext())
1032           {
1033             if (en != null)
1034             {
1035               if (!en.hasNext())
1036               {
1037                 en = prl.iterator();
1038               }
1039               pr = en.next();
1040             }
1041             {
1042               System.out.println("Testing opts dupes for "
1043                       + lastserv.getUri() + " : " + lastserv.getActionText()
1044                       + ":" + pr.getName());
1045               List<Option> rg = lastserv.getRunnerConfig().getOptions();
1046               for (Option o : rg)
1047               {
1048                 try
1049                 {
1050                   Option cpy = jalview.ws.jws2.ParameterUtils.copyOption(o);
1051                 } catch (Exception e)
1052                 {
1053                   System.err.println("Failed to copy " + o.getName());
1054                   e.printStackTrace();
1055                 } catch (Error e)
1056                 {
1057                   System.err.println("Failed to copy " + o.getName());
1058                   e.printStackTrace();
1059                 }
1060               }
1061             }
1062             {
1063               System.out.println("Testing param dupes:");
1064               List<Parameter> rg = lastserv.getRunnerConfig()
1065                       .getParameters();
1066               for (Parameter o : rg)
1067               {
1068                 try
1069                 {
1070                   Parameter cpy = jalview.ws.jws2.ParameterUtils
1071                           .copyParameter(o);
1072                 } catch (Exception e)
1073                 {
1074                   System.err.println("Failed to copy " + o.getName());
1075                   e.printStackTrace();
1076                 } catch (Error e)
1077                 {
1078                   System.err.println("Failed to copy " + o.getName());
1079                   e.printStackTrace();
1080                 }
1081               }
1082             }
1083             {
1084               System.out.println("Testing param write:");
1085               List<String> writeparam = null, readparam = null;
1086               try
1087               {
1088                 writeparam = jalview.ws.jws2.ParameterUtils
1089                         .writeParameterSet(
1090                                 pr.getArguments(lastserv.getRunnerConfig()),
1091                                 " ");
1092                 System.out.println("Testing param read :");
1093                 List<Option> pset = jalview.ws.jws2.ParameterUtils
1094                         .processParameters(writeparam,
1095                                 lastserv.getRunnerConfig(), " ");
1096                 readparam = jalview.ws.jws2.ParameterUtils
1097                         .writeParameterSet(pset, " ");
1098                 Iterator<String> o = pr.getOptions().iterator(),
1099                         s = writeparam.iterator(), t = readparam.iterator();
1100                 boolean failed = false;
1101                 while (s.hasNext() && t.hasNext())
1102                 {
1103                   String on = o.next(), sn = s.next(), st = t.next();
1104                   if (!sn.equals(st))
1105                   {
1106                     System.out.println(
1107                             "Original was " + on + " Phase 1 wrote " + sn
1108                                     + "\tPhase 2 wrote " + st);
1109                     failed = true;
1110                   }
1111                 }
1112                 if (failed)
1113                 {
1114                   System.out.println(
1115                           "Original parameters:\n" + pr.getOptions());
1116                   System.out.println(
1117                           "Wrote parameters in first set:\n" + writeparam);
1118                   System.out.println(
1119                           "Wrote parameters in second set:\n" + readparam);
1120
1121                 }
1122               } catch (Exception e)
1123               {
1124                 e.printStackTrace();
1125               }
1126             }
1127             WsJobParameters pgui = new WsJobParameters(lastserv,
1128                     new JabaPreset(lastserv, pr));
1129             JFrame jf = new JFrame(MessageManager
1130                     .formatMessage("label.ws_parameters_for", new String[]
1131                     { lastserv.getActionText() }));
1132             JPanel cont = new JPanel(new BorderLayout());
1133             pgui.validate();
1134             cont.setPreferredSize(pgui.getPreferredSize());
1135             cont.add(pgui, BorderLayout.CENTER);
1136             jf.setLayout(new BorderLayout());
1137             jf.add(cont, BorderLayout.CENTER);
1138             jf.validate();
1139             final Thread thr = Thread.currentThread();
1140             jf.addWindowListener(new WindowListener()
1141             {
1142
1143               @Override
1144               public void windowActivated(WindowEvent e)
1145               {
1146                 // TODO Auto-generated method stub
1147
1148               }
1149
1150               @Override
1151               public void windowClosed(WindowEvent e)
1152               {
1153               }
1154
1155               @Override
1156               public void windowClosing(WindowEvent e)
1157               {
1158                 thr.interrupt();
1159
1160               }
1161
1162               @Override
1163               public void windowDeactivated(WindowEvent e)
1164               {
1165                 // TODO Auto-generated method stub
1166
1167               }
1168
1169               @Override
1170               public void windowDeiconified(WindowEvent e)
1171               {
1172                 // TODO Auto-generated method stub
1173
1174               }
1175
1176               @Override
1177               public void windowIconified(WindowEvent e)
1178               {
1179                 // TODO Auto-generated method stub
1180
1181               }
1182
1183               @Override
1184               public void windowOpened(WindowEvent e)
1185               {
1186                 // TODO Auto-generated method stub
1187
1188               }
1189
1190             });
1191             jf.setVisible(true);
1192             boolean inter = false;
1193             while (!inter)
1194             {
1195               try
1196               {
1197                 Thread.sleep(10000);
1198               } catch (Exception e)
1199               {
1200                 inter = true;
1201               }
1202               ;
1203             }
1204             jf.dispose();
1205           }
1206         }
1207       }
1208     }
1209   }
1210
1211   public boolean isServiceDefaults()
1212   {
1213     return (!isModified()
1214             && (lastParmSet != null && lastParmSet.equals(SVC_DEF)));
1215   }
1216
1217   public List<ArgumentI> getJobParams()
1218   {
1219     return opanp.getCurrentSettings();
1220   }
1221
1222   String lastParmSet = null;
1223
1224   /*
1225    * Hashtable<String, Object[]> editedParams = new Hashtable<String,
1226    * Object[]>();
1227    * 
1228    * store the given parameters in the user parameter set database.
1229    * 
1230    * @param storeSetName - lastParmSet
1231    * 
1232    * @param descr - setDescr.getText()
1233    * 
1234    * @param jobParams - getJobParams()
1235    * 
1236    * private void _storeUserPreset(String storeSetName, String descr,
1237    * List<ArgumentI> jobParams) { // this is a simple hash store. Object[] pset;
1238    * editedParams.put(storeSetName, pset = new Object[3]); pset[0] =
1239    * storeSetName; pset[1] = descr; pset[2] = jobParams; // writeParam("Saving "
1240    * + storeSetName + ": ", jobParams); }
1241    * 
1242    * private void writeParam(String nm, List<ArgumentI> params) { for (ArgumentI
1243    * p : params) { System.out.println(nm + ":" + System.identityHashCode(p) +
1244    * " Name: " + p.getName() + " Value: " + p.getDefaultValue()); } }
1245    * 
1246    * private Object[] _getUserPreset(String setName) { Object[] pset =
1247    * editedParams.get(setName); // if (pset != null) // writeParam("Retrieving "
1248    * + setName + ": ", (List<Argument>) pset[2]); return pset; }
1249    * 
1250    * * remove the given user preset from the preset stash
1251    * 
1252    * @param setName
1253    * 
1254    * private void _deleteUserPreset(String setName) {
1255    * editedParams.remove(setName); }
1256    */
1257
1258   private void syncSetNamesWithStore()
1259   {
1260     int n = 0;
1261     // remove any set names in the drop down menu that aren't either a reserved
1262     // setting, or a user defined or service preset.
1263     Vector items = new Vector();
1264     while (n < setName.getItemCount())
1265     {
1266       String item = (String) setName.getItemAt(n);
1267       if (!item.equals(SVC_DEF) && !paramStore.presetExists(item))
1268       {
1269         setName.removeItemAt(n);
1270       }
1271       else
1272       {
1273         items.addElement(item);
1274         n++;
1275       }
1276     }
1277     if (!items.contains(SVC_DEF))
1278     {
1279       setName.addItem(SVC_DEF);
1280     }
1281     for (WsParamSetI upn : paramStore.getPresets())
1282     {
1283       if (!items.contains(upn.getName()))
1284       {
1285         setName.addItem(upn.getName());
1286       }
1287     }
1288   }
1289
1290   /**
1291    * true if lastParmSet is a user preset
1292    */
1293   boolean isUserPreset = false;
1294
1295   private void reInitDialog(String nextPreset)
1296   {
1297     settingDialog = true;
1298     // updateTable(null,null); // first reset to defaults
1299     WsParamSetI pset = null;
1300     if (nextPreset != null && nextPreset.length() > 0)
1301     {
1302       pset = paramStore.getPreset(nextPreset);
1303     }
1304     if (pset != null)
1305     {
1306       if (pset.isModifiable())
1307       {
1308         isUserPreset = true;
1309         setDescr.setText(pset.getDescription());
1310         updateTable(null, pset.getArguments());
1311         lastParmSet = nextPreset;
1312       }
1313       else
1314       {
1315         isUserPreset = false;
1316         setDescr.setText("");
1317         // must be a default preset from service
1318         updateTable(pset, null);
1319         lastParmSet = nextPreset;
1320       }
1321     }
1322     else
1323     {
1324       isUserPreset = false;
1325       // Service defaults
1326       setDescr.setText("");
1327       updateTable(null, null);
1328       lastParmSet = SVC_DEF;
1329     }
1330
1331     initArgSetModified();
1332     syncSetNamesWithStore();
1333     setName.setSelectedItem(lastParmSet);
1334     SetNamePanel.validate();
1335     validate();
1336     settingDialog = false;
1337   }
1338
1339   /**
1340    * Rebuild the AlignFrame web service menus (after add/delete of a preset
1341    * option).
1342    */
1343   protected void updateWebServiceMenus()
1344   {
1345     for (AlignFrame alignFrame : Desktop.getAlignFrames())
1346     {
1347       alignFrame.BuildWebServiceMenu();
1348     }
1349   }
1350
1351   String curSetName = null;
1352
1353   @Override
1354   public void itemStateChanged(ItemEvent e)
1355   {
1356     if (e.getSource() == setName && e.getStateChange() == e.SELECTED)
1357     {
1358       final String setname = (String) setName.getSelectedItem();
1359       // System.out.println("Item state changed for " + setname
1360       // + " (handling ? " + !settingDialog + ")");
1361       if (settingDialog)
1362       {
1363         // ignore event
1364         return;
1365       }
1366       if (setname == null)
1367       {
1368         return;
1369       }
1370       javax.swing.SwingUtilities.invokeLater(new Runnable()
1371       {
1372         @Override
1373         public void run()
1374         {
1375           doPreferenceComboStateChange(setname);
1376         }
1377       });
1378     }
1379   }
1380
1381   private void doPreferenceComboStateChange(String setname)
1382   {
1383     // user has selected a different item from combo-box
1384     if (isModified())
1385     {
1386       String lsetname = (curSetName != null) ? curSetName : lastParmSet;
1387       if (lsetname.equals(setname))
1388       {
1389         // setname was just edited - so ignore this event.
1390         return;
1391       }
1392       settingDialog = true;
1393       System.out.println("Prompting to save " + lsetname);
1394       if (JvOptionPane.showConfirmDialog(this, "Parameter set '" + lsetname
1395               + "' is modifed, and your changes will be lost.\nReally change preset ?",
1396               "Warning: Unsaved Changes",
1397               JvOptionPane.OK_CANCEL_OPTION) != JvOptionPane.OK_OPTION)
1398       {
1399         // revert the combobox to the current item
1400         settingDialog = true;
1401         setName.setSelectedItem(lsetname);
1402         settingDialog = false;
1403         // and leave.
1404         return;
1405         // System.out.println("Saving for " + lsetname);
1406         // _storeCurrentPreset(lsetname);
1407
1408       }
1409     }
1410     settingDialog = true;
1411     reInitDialog(setname);
1412     settingDialog = false;
1413
1414   }
1415
1416   private void _renameExistingPreset(String oldName, String curSetName2)
1417   {
1418     paramStore.updatePreset(oldName, curSetName2, setDescr.getText(),
1419             getJobParams());
1420   }
1421
1422   /**
1423    * store current settings as given name. You should then reset gui.
1424    * 
1425    * @param curSetName2
1426    */
1427   private void _storeCurrentPreset(String curSetName2)
1428   {
1429     paramStore.storePreset(curSetName2, setDescr.getText(), getJobParams());
1430   }
1431
1432   private void _updatePreset(String lastParmSet2, String curname)
1433   {
1434     paramStore.updatePreset(lastParmSet2, curname, setDescr.getText(),
1435             getJobParams());
1436
1437   }
1438
1439   /**
1440    * last saved name for this user preset
1441    */
1442   String lastSetName = null;
1443
1444   /**
1445    * last saved value of the description text for this user preset
1446    */
1447   String lastDescrText = null;
1448
1449   @Override
1450   public void actionPerformed(ActionEvent e)
1451   {
1452     if (e.getSource() instanceof Component)
1453     {
1454       Component src = (Component) e.getSource();
1455       if (src.getParent() == setName)
1456       {
1457         // rename any existing records we know about for this set.
1458         String newname = e.getActionCommand().trim();
1459         String msg = null;
1460         if (isServicePreset(newname))
1461         {
1462           final String oldname = curSetName != null ? curSetName
1463                   : lastParmSet;
1464           final Component ourframe = this;
1465           settingDialog = true;
1466           setName.getEditor().setItem(oldname);
1467           settingDialog = false;
1468           javax.swing.SwingUtilities.invokeLater(new Runnable()
1469           {
1470             @Override
1471             public void run()
1472             {
1473               JvOptionPane.showMessageDialog(ourframe,
1474                       MessageManager.getString(
1475                               "label.invalid_name_preset_exists"),
1476                       MessageManager.getString("label.invalid_name"),
1477                       JvOptionPane.WARNING_MESSAGE);
1478             }
1479           });
1480
1481           return;
1482         }
1483         curSetName = newname;
1484         System.err.println("New name for user setting " + curSetName
1485                 + " (was " + setName.getSelectedItem() + ")");
1486         if (curSetName.equals(setName.getSelectedItem()))
1487         {
1488           curSetName = null;
1489         }
1490         if (curSetName != null)
1491         {
1492           argSetModified(setName, true);
1493           return;
1494         }
1495
1496       }
1497     }
1498   }
1499
1500   private void checkDescrModified()
1501   {
1502     if (!settingDialog)
1503     {
1504
1505       argSetModified(setDescr,
1506               (lastDescrText == null
1507                       ? setDescr.getText().trim().length() > 0
1508                       : !setDescr.getText().equals(lastDescrText)));
1509
1510     }
1511   }
1512
1513   @Override
1514   public void insertUpdate(DocumentEvent e)
1515   {
1516     checkDescrModified();
1517   }
1518
1519   @Override
1520   public void removeUpdate(DocumentEvent e)
1521   {
1522     checkDescrModified();
1523   }
1524
1525   @Override
1526   public void changedUpdate(DocumentEvent e)
1527   {
1528     checkDescrModified();
1529   }
1530
1531   /**
1532    * 
1533    * @return null or the service preset selected by the user
1534    */
1535   public WsParamSetI getPreset()
1536   {
1537     if (isUserPreset || isModified()
1538             || (lastParmSet != null && lastParmSet.equals(SVC_DEF)))
1539     {
1540       return null;
1541     }
1542     else
1543     {
1544       return paramStore.getPreset(lastParmSet);
1545     }
1546   }
1547 }