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