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