abstracted GUI to use jalview.ws.params objects (JAL-591, JAL-633)
[jalview.git] / src / jalview / gui / WsJobParameters.java
1 package jalview.gui;
2
3 import java.awt.BorderLayout;
4 import java.awt.Color;
5 import java.awt.Component;
6 import java.awt.Dimension;
7 import java.awt.FlowLayout;
8 import java.awt.Font;
9 import java.awt.GridLayout;
10 import java.awt.Rectangle;
11 import java.awt.event.ActionEvent;
12 import java.awt.event.ActionListener;
13 import java.awt.event.ComponentEvent;
14 import java.awt.event.ComponentListener;
15 import java.awt.event.ContainerEvent;
16 import java.awt.event.ContainerListener;
17 import java.awt.event.ItemEvent;
18 import java.awt.event.ItemListener;
19 import java.awt.event.KeyEvent;
20 import java.awt.event.KeyListener;
21 import java.awt.event.MouseEvent;
22 import java.awt.event.MouseListener;
23 import java.awt.event.WindowEvent;
24 import java.awt.event.WindowListener;
25 import java.awt.event.WindowStateListener;
26 import java.net.URL;
27 import java.util.ArrayList;
28 import java.util.EventObject;
29 import java.util.HashSet;
30 import java.util.Hashtable;
31 import java.util.Iterator;
32 import java.util.List;
33 import java.util.Map;
34 import java.util.Vector;
35
36 import javax.swing.JButton;
37 import javax.swing.JCheckBox;
38 import javax.swing.JComboBox;
39 import javax.swing.JComponent;
40 import javax.swing.JDialog;
41 import javax.swing.JFrame;
42 import javax.swing.JLabel;
43 import javax.swing.JList;
44 import javax.swing.JMenuItem;
45 import javax.swing.JOptionPane;
46 import javax.swing.JPanel;
47 import javax.swing.JPopupMenu;
48 import javax.swing.JScrollPane;
49 import javax.swing.JSlider;
50 import javax.swing.JSplitPane;
51 import javax.swing.JTable;
52 import javax.swing.JTextArea;
53 import javax.swing.JTextField;
54 import javax.swing.ListSelectionModel;
55 import javax.swing.SwingConstants;
56 import javax.swing.SwingUtilities;
57 import javax.swing.border.TitledBorder;
58 import javax.swing.event.CellEditorListener;
59 import javax.swing.event.ChangeEvent;
60 import javax.swing.event.ChangeListener;
61 import javax.swing.event.DocumentEvent;
62 import javax.swing.event.DocumentListener;
63 import javax.swing.table.*;
64
65 import compbio.metadata.Argument;
66 import compbio.metadata.Option;
67 import compbio.metadata.Parameter;
68 import compbio.metadata.Preset;
69 import compbio.metadata.PresetManager;
70 import compbio.metadata.RunnerConfig;
71 import compbio.metadata.ValueConstrain;
72 import compbio.metadata.WrongParameterException;
73 import compbio.metadata.ValueConstrain.Type;
74
75 import jalview.util.jarInputStreamProvider;
76 import jalview.ws.jws2.JabaParamStore;
77 import jalview.ws.jws2.JabaPreset;
78 import jalview.ws.jws2.Jws2Discoverer;
79 import jalview.ws.jws2.ParameterUtils;
80 import jalview.ws.jws2.Jws2Discoverer.Jws2Instance;
81 import jalview.ws.jws2.dm.JabaOption;
82 import jalview.ws.jws2.dm.JabaParameter;
83 import jalview.ws.params.ArgumentI;
84 import jalview.ws.params.OptionI;
85 import jalview.ws.params.ParamDatastoreI;
86 import jalview.ws.params.ParameterI;
87 import jalview.ws.params.ValueConstrainI;
88 import jalview.ws.params.WsParamSetI;
89
90 /**
91  * job parameter editing/browsing dialog box. User can browse existing settings
92  * (user + presets + Defaults), and any changes to parameters creates a modified
93  * user parameter set. LOGIC: If the parameter set is modified, and its name is
94  * a valid, non-existant user parameter set, then a save button is shown. If the
95  * parameter set is modified and its name is a valid, extant user parameter set,
96  * then an update button is shown. If user parameter set's name is edited, and
97  * old name exists as a writable user parameter set, then rename button is
98  * shown. If current parameter set is associated with a user defined parameter
99  * set, then : if set is modifed, a 'revert' button is shown. if set is not
100  * modified, a 'delete' button is shown.
101  * 
102  * @author JimP
103  * 
104  */
105 public class WsJobParameters extends JPanel implements ItemListener,
106         ActionListener, DocumentListener
107 {
108   private static final String SVC_DEF = "Defaults"; // this is the null
109                                                     // parameter set as shown to
110                                                     // user
111
112   private static final int PARAM_WIDTH = 340, PARAM_HEIGHT = 150,
113           PARAM_CLOSEDHEIGHT = 80;
114
115   private static final int OPTSET_HEIGHT = 30;
116
117   JPanel SetNamePanel = new JPanel();
118
119   JPanel setDetails = new JPanel();
120
121   JSplitPane settingsPanel = new JSplitPane();
122
123   JSplitPane jobPanel = new JSplitPane();
124
125   JPanel jobOptions = new JPanel();
126
127   JScrollPane jobOptionsPane = new JScrollPane();
128
129   JPanel jobParameters = new JPanel();
130
131   JButton createpref = new JButton();
132
133   JButton deletepref = new JButton();
134
135   JButton revertpref = new JButton();
136
137   JButton updatepref = new JButton();
138
139   JButton startjob = new JButton();
140
141   JButton canceljob = new JButton();
142
143   JComboBox setName = new JComboBox();
144
145   JTextArea setDescr = new JTextArea();
146
147   JScrollPane paramPane = new JScrollPane();
148
149   JPanel paramList = new JPanel();
150
151   RunnerConfig serviceOptions;
152
153   ParamDatastoreI paramStore;
154
155   WsJobParameters(Jws2Instance service)
156   {
157     this(service, null);
158   }
159
160   public WsJobParameters(Jws2Instance service, WsParamSetI preset)
161   {
162     this(null, service, preset, null);
163   }
164
165   /**
166    * 
167    * @param desktop
168    *          - if null, create new JFrame outside of desktop
169    * @param service
170    * @param preset
171    */
172   public WsJobParameters(JFrame parent, Jws2Instance service, WsParamSetI preset,
173           List<Argument> jobArgset)
174   {
175     this(parent, null, service, preset, jobArgset);
176   }
177
178   /**
179    * 
180    * @param parent
181    * @param paramStorei
182    * @param service
183    * @param preset
184    * @param jobArgset
185    */
186   public WsJobParameters(JFrame parent, ParamDatastoreI paramStorei,
187           Jws2Instance service, WsParamSetI preset, List<Argument> jobArgset)
188   {
189     super();
190     jbInit();
191     this.paramStore = paramStorei;
192     if (paramStore == null)
193     {
194       paramStore = service.getParamStore();
195     }
196     this.service = service;
197     // argSetModified(false);
198     // populate parameter table
199     initForService(service, preset, jobArgset);
200     // display in new JFrame attached to parent.
201     validate();
202   }
203
204   int response = -1;
205
206   JDialog frame = null;
207
208   /**
209    * shows a modal dialog containing the parameters.
210    * 
211    * @return
212    */
213   public boolean showRunDialog()
214   {
215
216     frame = new JDialog(Desktop.instance, true);
217
218     frame.setTitle("Edit parameters for " + service.getActionText());
219     Rectangle deskr = Desktop.instance.getBounds();
220     frame.setBounds(new Rectangle((int) (deskr.getCenterX() - 240),
221             (int) (deskr.getCenterY() - 250), 480, 500));
222     frame.setContentPane(this);
223     // should recover defaults from user prefs.
224     frame.validate();
225     settingsPanel.setDividerLocation(0.4);
226     jobPanel.setDividerLocation(0.3);
227     frame.setVisible(true);
228
229     if (response > 0)
230     {
231       return true;
232     }
233     return false;
234   }
235
236   protected JButton makeButton(String label, String tooltip,
237           ActionListener action)
238   {
239     JButton button = new JButton();
240     button.setText(label);
241     button.setFont(new java.awt.Font("Verdana", Font.PLAIN, 10));
242     button.setForeground(Color.black);
243     button.setHorizontalAlignment(SwingConstants.CENTER);
244     button.setToolTipText(tooltip);
245     button.addActionListener(action);
246     return button;
247   }
248
249   private void jbInit()
250   {
251     updatepref = makeButton("Update", "Update the stored user preference",
252             new ActionListener()
253             {
254
255               public void actionPerformed(ActionEvent e)
256               {
257                 update_actionPerformed(e);
258               }
259             });
260     deletepref = makeButton("Delete", "Delete the user preference",
261             new ActionListener()
262             {
263
264               public void actionPerformed(ActionEvent e)
265               {
266                 delete_actionPerformed(e);
267               }
268             });
269     createpref = makeButton("Create", "Create a new preference",
270             new ActionListener()
271             {
272
273               public void actionPerformed(ActionEvent e)
274               {
275                 create_actionPerformed(e);
276               }
277             });
278     revertpref = makeButton("Revert",
279             "Undo all changes to the current set", new ActionListener()
280             {
281
282               public void actionPerformed(ActionEvent e)
283               {
284                 revert_actionPerformed(e);
285               }
286             });
287     startjob.setFont(new java.awt.Font("Verdana", Font.PLAIN, 10));
288     startjob.setText("Start");
289     startjob.setToolTipText("Start Job");
290     startjob.addActionListener(new ActionListener()
291     {
292       public void actionPerformed(ActionEvent e)
293       {
294         startjob_actionPerformed(e);
295       }
296     });
297     canceljob.setFont(new java.awt.Font("Verdana", Font.PLAIN, 10));
298     canceljob.setText("Cancel");
299     canceljob.setToolTipText("Cancel Job");
300     canceljob.addActionListener(new ActionListener()
301     {
302       public void actionPerformed(ActionEvent e)
303       {
304         canceljob_actionPerformed(e);
305       }
306     });
307
308     setDetails.setBorder(new TitledBorder("Details"));
309     setDetails.setLayout(new BorderLayout());
310     setDescr.setColumns(40);
311     setDescr.setWrapStyleWord(true);
312     setDescr.setLineWrap(true);
313     setDescr.setBackground(getBackground());
314     setDescr.setEditable(true);
315     setDescr.getDocument().addDocumentListener(this);
316     JScrollPane setDescrView = new JScrollPane();
317     // setDescrView.setPreferredSize(new Dimension(350, 200));
318     setDescrView.getViewport().setView(setDescr);
319     setName.setEditable(true);
320     setName.addItemListener(this);
321     setName.getEditor().addActionListener(this);
322     JPanel setNameInfo = new JPanel(new FlowLayout(FlowLayout.LEFT));
323     SetNamePanel.setLayout(new GridLayout(2, 1));
324     SetNamePanel.setMinimumSize(new Dimension(300, 40));
325     JLabel setNameLabel = new JLabel("Parameter set: ");
326     setNameLabel.setFont(new java.awt.Font("Verdana", Font.PLAIN, 10));
327     // setNameLabel.setHorizontalAlignment(FlowLayout.LEFT);
328     setNameInfo.add(setNameLabel);
329     setNameInfo.add(setName);
330     // initial button visibility
331     updatepref.setVisible(false);
332     deletepref.setVisible(false);
333     revertpref.setVisible(false);
334     createpref.setVisible(false);
335     JPanel setsavebuts = new JPanel();
336     setsavebuts.setLayout(new FlowLayout(FlowLayout.LEFT)); // GridLayout(1,2));
337     ((FlowLayout) setsavebuts.getLayout()).setHgap(10);
338     ((FlowLayout) setsavebuts.getLayout()).setVgap(0);
339     setsavebuts.add(deletepref);
340     setsavebuts.add(revertpref);
341     setsavebuts.add(createpref);
342     setsavebuts.add(updatepref);
343     setsavebuts.setSize(new Dimension(150, 20));
344     JPanel buttonArea = new JPanel(new GridLayout(1, 1));
345     buttonArea.add(setsavebuts);
346     SetNamePanel.add(setNameInfo);
347     SetNamePanel.add(buttonArea);
348     setDetails.add(setDescrView, BorderLayout.CENTER);
349     // setDetails.setPreferredSize(new Dimension(360, 100));
350     jobParameters.setBorder(new TitledBorder("Parameters"));
351     jobParameters.setLayout(new BorderLayout());
352     paramPane.setPreferredSize(new Dimension(360, 300));
353     paramPane.getVerticalScrollBar().setUnitIncrement(20);
354     // paramPanel.setPreferredSize(new Dimension(360, 300));
355     // TODO: relayout buttons nicely
356     paramPane.getViewport().setView(paramList);
357     jobParameters.add(paramPane, BorderLayout.CENTER);
358     JPanel jobOptionsPanel = new JPanel();
359     jobOptionsPanel.setLayout(new BorderLayout());
360     jobOptionsPanel.setBorder(new TitledBorder("Options"));
361     jobOptionsPane.getViewport().setView(jobOptions);
362     jobOptionsPanel.add(jobOptionsPane, BorderLayout.CENTER);
363     settingsPanel.setLeftComponent(jobOptionsPanel);
364     settingsPanel.setRightComponent(jobParameters);
365     settingsPanel.setOrientation(JSplitPane.VERTICAL_SPLIT);
366
367     setLayout(new BorderLayout());
368     // setPreferredSize(new Dimension(400, 600));
369     // setSize(new Dimension(400, 600));
370     jobPanel.setLeftComponent(setDetails);
371     jobPanel.setRightComponent(settingsPanel);
372     jobPanel.setOrientation(JSplitPane.VERTICAL_SPLIT);
373     add(SetNamePanel, BorderLayout.NORTH);
374     add(jobPanel, BorderLayout.CENTER);
375     JPanel dialogpanel = new JPanel();
376     dialogpanel.add(startjob);
377     dialogpanel.add(canceljob);
378     add(dialogpanel, BorderLayout.SOUTH);
379   }
380
381   protected void revert_actionPerformed(ActionEvent e)
382   {
383     reInitDialog(lastParmSet);
384
385   }
386
387   protected void update_actionPerformed(ActionEvent e)
388   {
389     if (isUserPreset)
390     {
391       String curname = ((String) setName.getSelectedItem()).trim();
392       if (curname.length() > 0 && !curname.equals(lastParmSet))
393       {
394         _deleteUserPreset(lastParmSet);
395       }
396       _storeCurrentPreset(curname);
397       lastParmSet = curname;
398       isUserPreset = true;
399       initArgSetModified();
400       syncSetNamesWithStore();
401     }
402   }
403
404   private void _deleteUserPreset(String lastParmSet2)
405   {
406     paramStore.deletePreset(lastParmSet2);
407   }
408
409   protected void delete_actionPerformed(ActionEvent e)
410   {
411     if (isUserPreset)
412     {
413       // delete current preset's saved entry
414       _deleteUserPreset(lastParmSet);
415     }
416     reInitDialog(null); // service default
417   }
418
419   protected void create_actionPerformed(ActionEvent e)
420   {
421     String curname = ((String) setName.getSelectedItem()).trim();
422     if (curname.length() > 0)
423     {
424       _storeCurrentPreset(curname);
425       lastParmSet = curname;
426       isUserPreset = true;
427       initArgSetModified();
428     }
429     else
430     {
431       // TODO: show warning
432       System.err.println("Invalid name. Not saved.");
433     }
434   }
435
436   protected void canceljob_actionPerformed(ActionEvent e)
437   {
438     response = 0;
439     if (frame != null)
440     {
441       frame.setVisible(false);
442     }
443   }
444
445   protected void startjob_actionPerformed(ActionEvent e)
446   {
447     response = 1;
448     if (frame != null)
449     {
450       frame.setVisible(false);
451     }
452   }
453
454   Jws2Instance service;
455
456   /**
457    * list of service presets in the gui
458    */
459   Hashtable servicePresets = null;
460
461   /**
462    * set if dialog is being set - so handlers will avoid spurious events
463    */
464   boolean settingDialog = false;
465
466   void initForService(Jws2Instance service, WsParamSetI jabap,
467           List<Argument> jabajobArgset)
468   {
469     WsParamSetI p = null;
470     List<ArgumentI> jobArgset = null;
471     settingDialog = true;
472     { // instantiate the abstract proxy for Jaba objects
473       jobArgset = jabajobArgset == null ? null : JabaParamStore
474               .getJwsArgsfromJaba(jabajobArgset);
475       p = jabap; // (jabap != null) ? paramStore.getPreset(jabap.getName()) : null;
476     }
477     // TODO: Recover window geometry prefs for this service
478     // jobPanel.setDividerLocation(proportionalLocation)
479     // settingsPanel.setDividerLocation(proportionalLocation)
480     Hashtable exnames = new Hashtable();
481     for (int i = 0, iSize = setName.getItemCount(); i < iSize; i++)
482     {
483       exnames.put((String) setName.getItemAt(i), setName.getItemAt(i));
484     }
485     servicePresets = new Hashtable();
486     // Add the default entry - if not present already.
487     if (!exnames.contains(SVC_DEF))
488     {
489       setName.addItem(SVC_DEF);
490       exnames.put(SVC_DEF, SVC_DEF);
491       servicePresets.put(SVC_DEF, SVC_DEF);
492     }
493     String curname = (p == null ? "" : p.getName());
494     for (WsParamSetI pr : paramStore.getPresets())
495     {
496       if (!pr.isModifiable())
497       {
498         servicePresets.put(pr.getName(), "preset");
499       }
500       else
501       {
502       }
503       if (!exnames.contains(pr.getName()))
504       {
505         setName.addItem(pr.getName());
506       }
507     }
508     // TODO: if initial jobArgset matches a given user setting or preset then
509     // should recover setting accordingly
510     // updateTable(p, jobArgset);
511     if (p != null)
512     {
513       reInitDialog(p.getName());
514       initArgSetModified();
515     }
516     else
517     {
518       if (jobArgset != null && jobArgset.size() > 0)
519       {
520         curSetName = "Supplied Settings";
521         updateTable(p, jobArgset);
522       }
523       else
524       {
525         curSetName = null;
526         reInitDialog(null);
527       }
528     }
529     settingDialog = false;
530
531   }
532
533   @SuppressWarnings("unchecked")
534   private void updateTable(WsParamSetI p, List<ArgumentI> jobArgset)
535   {
536     // populate table from default parameter set.
537     List<ArgumentI> args = paramStore.getServiceParameters();
538
539     // split to params and required arguments
540     {
541       for (ArgumentI myarg : args)
542       {
543         // Ideally, Argument would implement isRequired !
544         if (myarg instanceof ParameterI)
545         {
546           ParameterI parm = (ParameterI) myarg;
547           addParameter(parm);
548         }
549         else
550         {
551           if (myarg instanceof OptionI)
552           {
553             OptionI opt = (OptionI) myarg;
554             addOption(opt).resetToDefault();
555           }
556           else
557           {
558             System.err.println("Ignoring unknown service argument type "
559                     + myarg.getClass().getName());
560           }
561         }
562       }
563       args = null; // no more args to process.
564     }
565     if (p != null)
566     {
567       isUserPreset = false;
568       // initialise setname
569       setName.setSelectedItem(lastSetName = p.getName());
570       setDescr.setText(lastDescrText = p.getDescription());
571       // TODO - URL link
572       try
573       {
574         args = p.getArguments();
575       } catch (Exception e)
576       {
577         e.printStackTrace();
578       }
579       // TODO: check if args should be unselected prior to resetting using the
580       // preset
581     }
582     else
583     {
584       if (lastParmSet == null)
585       {
586         isUserPreset = false;
587         // first call - so create a dummy name
588
589         setName.setSelectedItem(lastSetName = SVC_DEF);
590       }
591     }
592
593     if (jobArgset != null)
594     {
595       argSetModified(jobArgset, true);
596       args = jobArgset;
597     }
598     // get setargs from current object
599     if (args != null)
600     {
601       for (ArgumentI arg : args)
602       {
603         if (arg instanceof ParameterI)
604         {
605           setParameter((ParameterI) arg);
606         }
607         else
608         {
609           if (arg instanceof OptionI)
610           {
611             // System.out.println("Setting option "
612             // + System.identityHashCode(arg) + ":" + arg.getName()
613             // + " with " + arg.getDefaultValue());
614             selectOption((OptionI) arg, arg.getDefaultValue());
615           }
616         }
617
618       }
619     }
620
621     jobOptions.setPreferredSize(new Dimension(PARAM_WIDTH, optSet.size()
622             * OPTSET_HEIGHT));
623     jobOptions.setLayout(new GridLayout(optSet.size(), 1));
624     refreshParamLayout();
625     paramPane.validate();
626     revalidate();
627   }
628
629   private boolean isModified()
630   {
631     return modifiedElements.size() > 0;
632   }
633
634   private Hashtable modifiedElements = new Hashtable();
635
636   /**
637    * reset gui and modification state settings
638    */
639   private void initArgSetModified()
640   {
641     curSetName = null;
642     modifiedElements.clear();
643     updateButtonDisplay();
644   }
645
646   private void updateButtonDisplay()
647   {
648     boolean _update = false, _create = false, _delete = false, _revert = false;
649     if (modifiedElements.size() > 0)
650     {
651       // set modified
652       _revert = true;
653       _update = isUserPreset; // can only update user presets
654       if (!isUserPreset || modifiedElements.containsKey(setName))
655       {
656         // name modified - can create new preset
657         _create = true;
658       }
659     }
660     else
661     {
662       // set unmodified
663     }
664     // can still delete a user preset
665     _delete = isUserPreset;
666
667     createpref.setVisible(_create);
668     updatepref.setVisible(_update);
669     deletepref.setVisible(_delete);
670     revertpref.setVisible(_revert);
671     validate();
672   }
673
674   private void argSetModified(Object modifiedElement, boolean b)
675   {
676     if (settingDialog)
677     {
678       return;
679     }
680     if (!b)
681     {
682       modifiedElements.remove(modifiedElement);
683     }
684     else
685     {
686       if (b && modifiedElement == setName
687               && modifiedElements.contains(modifiedElement))
688       {
689         // HACK! prevents iteration on makeSetNameValid
690         b = false;
691       }
692       modifiedElements.put(modifiedElement, modifiedElement);
693     }
694     // set mod status based on presence of elements in table
695     if (b && modifiedElements.size() > 0)
696     {
697       makeSetNameValid(!isUserPreset);
698       SetNamePanel.revalidate();
699     }
700     updateButtonDisplay();
701   }
702
703   private boolean isServicePreset(String selectedItem)
704   {
705     return selectedItem.equals(SVC_DEF)
706             || servicePresets.containsKey(selectedItem);
707   }
708
709   /**
710    * check if the current set name is a valid set name for saving, if not, then
711    * fix it.
712    */
713   private void makeSetNameValid(boolean newuserset)
714   {
715     boolean stn = settingDialog;
716     boolean renamed = false;
717     settingDialog = true;
718     String nm = (String) setName.getSelectedItem();
719     // check if the name is reserved - if it is, rename it.
720     if (isServicePreset(nm))
721     {
722       nm = "User " + nm;
723       renamed = true;
724     }
725     String tnm = nm;
726     if (newuserset)
727     {
728       int i = 0;
729       while (paramStore.getPreset(tnm) != null)
730       {
731         tnm = nm + " (" + (++i) + ")";
732         renamed = true;
733       }
734       if (i > 0)
735       {
736         nm = tnm;
737       }
738     }
739
740     boolean makeupdate = false;
741     // sync the gui with the preset database
742     for (int i = 0, iS = setName.getItemCount(); i < iS; i++)
743     {
744       String snm = (String) setName.getItemAt(i);
745       if (snm.equals(nm))
746       {
747         makeupdate = true;
748         // setName.setSelectedIndex(i);
749       }
750     }
751     if (!makeupdate)
752     {
753       setName.addItem(curSetName = nm);
754       setName.setSelectedItem(curSetName);
755     }
756     if (renamed)
757     {
758       settingDialog = false; // we need this name change to be registered.
759       argSetModified(setName, renamed);
760     }
761     settingDialog = stn;
762   }
763
764   private void addParameter(ParameterI arg)
765   {
766     ParamBox pb = paramSet.get(arg.getName());
767     if (pb == null)
768     {
769       pb = new ParamBox(this, arg);
770       paramSet.put(arg.getName(), pb);
771       paramList.add(pb);
772     }
773     pb.init();
774     // take the defaults from the parameter
775     pb.updateControls(arg);
776   }
777
778   private void setParameter(ParameterI arg)
779   {
780     ParamBox pb = paramSet.get(arg.getName());
781     if (pb == null)
782     {
783       addParameter(arg);
784     }
785     else
786     {
787       pb.updateControls(arg);
788     }
789
790   }
791
792   private void selectOption(OptionI option, String string)
793   {
794     OptionBox cb = optSet.get(option.getName());
795     if (cb == null)
796     {
797       cb = addOption(option);
798     }
799     cb.enabled.setSelected(string != null); // initial state for an option.
800     if (string != null)
801     {
802       if (option.getPossibleValues().contains(string))
803       {
804         cb.val.setSelectedItem(string);
805       }
806       else
807       {
808         throw new Error("Invalid value " + string + " for option " + option);
809       }
810
811     }
812     if (option.isRequired() && !cb.enabled.isSelected())
813     {
814       // TODO: indicate paramset is not valid.. option needs to be selected!
815     }
816     cb.setInitialValue();
817   }
818
819   Map<String, ParamBox> paramSet = new Hashtable<String, ParamBox>();
820
821   public class ParamBox extends JPanel implements ChangeListener,
822           ActionListener, MouseListener
823   {
824     JButton showDesc = new JButton();
825
826     JTextArea string = new JTextArea();
827
828     JScrollPane descPanel = new JScrollPane();
829
830     JSlider slider = null;
831
832     JTextField valueField = null;
833
834     ValueConstrainI validator = null;
835
836     JPanel settingPanel = new JPanel();
837
838     JPanel controlPanel = new JPanel();
839
840     boolean integ = false;
841
842     boolean choice = false;
843
844     boolean descisvisible = false;
845
846     final WsJobParameters pmdialogbox;
847
848     final URL finfo;
849
850     public ParamBox(final WsJobParameters pmlayout, ParameterI parm)
851     {
852       pmdialogbox = pmlayout;
853       setPreferredSize(new Dimension(PARAM_WIDTH, PARAM_CLOSEDHEIGHT));
854       setBorder(new TitledBorder(parm.getName()));
855       setLayout(null);
856       showDesc.setFont(new Font("Verdana", Font.PLAIN, 6));
857       showDesc.setText("+");
858       string.setFont(new Font("Verdana", Font.PLAIN, 11));
859       string.setBackground(getBackground());
860       // string.setSize(new Dimension(PARAM_WIDTH, 80));
861       string.setEditable(false);
862       descPanel.getViewport().setView(string);
863       // descPanel.setLocation(2,17);
864       descPanel.setVisible(false);
865       // string.setMinimumSize(new Dimension(140,80));
866       // string.setMaximumSize(new Dimension(280,80));
867       final ParamBox me = this;
868       finfo = parm.getFurtherDetails();
869       if (finfo != null)
870       {
871         showDesc.setToolTipText("<html><p>Click to show brief description, and right click to open link for further information.</p></html>");
872         showDesc.addMouseListener(this);
873       }
874       else
875       {
876         showDesc.setToolTipText("<html><p>Click to show brief description.</p></html>");
877       }
878       showDesc.addActionListener(new ActionListener()
879       {
880
881         public void actionPerformed(ActionEvent e)
882         {
883           descisvisible = !descisvisible;
884           descPanel.setVisible(descisvisible);
885           me.setPreferredSize(new Dimension(PARAM_WIDTH,
886                   (descisvisible) ? PARAM_HEIGHT : PARAM_CLOSEDHEIGHT));
887           me.validate();
888           pmlayout.refreshParamLayout();
889         }
890       });
891       string.setWrapStyleWord(true);
892       string.setLineWrap(true);
893       string.setColumns(32);
894       string.setText(parm.getDescription());
895       JPanel firstrow = new JPanel();
896       firstrow.setLayout(null);
897       controlPanel.setLayout(new BorderLayout());
898       controlPanel.setBounds(new Rectangle(39, 10, PARAM_WIDTH - 70,
899               PARAM_CLOSEDHEIGHT - 50));
900       showDesc.setBounds(new Rectangle(10, 10, 16, 16));
901       firstrow.add(showDesc);
902       firstrow.add(controlPanel);
903       firstrow.setBounds(new Rectangle(10, 20, PARAM_WIDTH - 30,
904               PARAM_CLOSEDHEIGHT - 30));
905       add(firstrow);
906       validator = parm.getValidValue();
907       parameter = parm;
908       if (validator != null)
909       {
910         integ = validator.getType() == Type.Integer;
911       }
912       else
913       {
914         if (parameter.getPossibleValues() != null)
915         {
916           choice = true;
917         }
918       }
919       updateControls(parm);
920       descPanel.setBounds(new Rectangle(10, PARAM_CLOSEDHEIGHT,
921               PARAM_WIDTH - 20, PARAM_HEIGHT - PARAM_CLOSEDHEIGHT - 5));
922       add(descPanel);
923       validate();
924     }
925
926     public void init()
927     {
928       // reset the widget's initial value.
929       lastVal = null;
930     }
931
932     boolean adjusting = false;
933
934     ParameterI parameter;
935
936     JComboBox choicebox;
937
938     public int getBoxHeight()
939     {
940       return (descisvisible ? PARAM_HEIGHT : PARAM_CLOSEDHEIGHT);
941     }
942
943     public void updateControls(ParameterI parm)
944     {
945       adjusting = true;
946       boolean init = (choicebox == null && valueField == null);
947       float fVal = 0f;
948       int iVal = 0;
949       if (init)
950       {
951         if (choice)
952         {
953           choicebox = new JComboBox();
954           choicebox.addActionListener(this);
955           controlPanel.add(choicebox, BorderLayout.CENTER);
956         }
957         else
958         {
959           slider = new JSlider();
960           slider.addChangeListener(this);
961           valueField = new JTextField();
962           valueField.addActionListener(this);
963           valueField.setPreferredSize(new Dimension(60, 25));
964           controlPanel.add(slider, BorderLayout.WEST);
965           controlPanel.add(valueField, BorderLayout.EAST);
966
967         }
968       }
969
970       if (parm != null)
971       {
972         if (choice)
973         {
974           if (init)
975           {
976             List vals = parm.getPossibleValues();
977             for (Object val : vals)
978             {
979               choicebox.addItem(val);
980             }
981           }
982
983           if (parm.getDefaultValue() != null)
984           {
985             choicebox.setSelectedItem(parm.getDefaultValue());
986           }
987         }
988         else
989         {
990           valueField.setText(parm.getDefaultValue());
991         }
992       }
993       lastVal = updateSliderFromValueField();
994       adjusting = false;
995     }
996
997     Object lastVal;
998
999     public ParameterI getParameter()
1000     {
1001       ParameterI prm = parameter.copy();
1002       if (choice)
1003       {
1004         prm.setDefaultValue((String) choicebox.getSelectedItem());
1005       }
1006       else
1007       {
1008         prm.setDefaultValue(valueField.getText());
1009       }
1010       return prm;
1011     }
1012
1013     public Object updateSliderFromValueField()
1014     {
1015       int iVal;
1016       float fVal;
1017       if (validator != null)
1018       {
1019         if (integ)
1020         {
1021           iVal = 0;
1022           try
1023           {
1024             valueField.setText(valueField.getText().trim());
1025             iVal = Integer.valueOf(valueField.getText());
1026             if (validator.getMin()!=null && validator.getMin().intValue()>iVal)
1027             {
1028               iVal = validator.getMin().intValue();
1029               // TODO: provide visual indication that hard limit was reached for this parameter
1030             }
1031             if (validator.getMax()!=null && validator.getMax().intValue()<iVal)
1032             {
1033               iVal = validator.getMax().intValue();
1034               // TODO: provide visual indication that hard limit was reached for this parameter
1035             }
1036           } catch (Exception e)
1037           {
1038           }
1039           ;
1040           if (validator.getMin() != null && validator.getMax() != null)
1041           {
1042             slider.getModel().setRangeProperties(iVal, 1,
1043                     validator.getMin().intValue(),
1044                     validator.getMax().intValue(), true);
1045           }
1046           else
1047           {
1048             slider.setVisible(false);
1049           }
1050           return new int[]
1051           { iVal };
1052         }
1053         else
1054         {
1055           fVal = 0f;
1056           try
1057           {
1058             fVal = Float.valueOf(valueField.getText());
1059             if (validator.getMin()!=null && validator.getMin().floatValue()>fVal)
1060             {
1061               fVal = validator.getMin().floatValue();
1062               // TODO: provide visual indication that hard limit was reached for this parameter
1063             }
1064             if (validator.getMax()!=null && validator.getMax().floatValue()<fVal)
1065             {
1066               fVal = validator.getMax().floatValue();
1067               // TODO: provide visual indication that hard limit was reached for this parameter
1068             }
1069           } catch (Exception e)
1070           {
1071           }
1072           ;
1073           if (validator.getMin() != null && validator.getMax() != null)
1074           {
1075             slider.getModel().setRangeProperties((int) fVal * 1000, 1,
1076                     (int) validator.getMin().floatValue() * 1000,
1077                     (int) validator.getMax().floatValue() * 1000, true);
1078           }
1079           else
1080           {
1081             slider.setVisible(false);
1082           }
1083           return new float[]
1084           { fVal };
1085         }
1086       }
1087       else
1088       {
1089         if (!choice)
1090         {
1091           slider.setVisible(false);
1092           return new String[]
1093           { valueField.getText().trim() };
1094         }
1095         else
1096         {
1097           return new String[]
1098           { (String) choicebox.getSelectedItem() };
1099         }
1100       }
1101
1102     }
1103
1104     public void stateChanged(ChangeEvent e)
1105     {
1106       if (!adjusting)
1107       {
1108         valueField.setText(""
1109                 + ((integ) ? ("" + (int) slider.getValue())
1110                         : ("" + (float) (slider.getValue() / 1000f))));
1111         checkIfModified();
1112       }
1113
1114     }
1115
1116     public void actionPerformed(ActionEvent e)
1117     {
1118       if (adjusting)
1119       {
1120         return;
1121       }
1122       if (!choice)
1123       {
1124         updateSliderFromValueField();
1125       }
1126       checkIfModified();
1127     }
1128
1129     private void checkIfModified()
1130     {
1131       Object cstate = updateSliderFromValueField();
1132       boolean notmod = false;
1133       if (cstate.getClass() == lastVal.getClass())
1134       {
1135         if (cstate instanceof int[])
1136         {
1137           notmod = (((int[]) cstate)[0] == ((int[]) lastVal)[0]);
1138         }
1139         else if (cstate instanceof float[])
1140         {
1141           notmod = (((float[]) cstate)[0] == ((float[]) lastVal)[0]);
1142         }
1143         else if (cstate instanceof String[])
1144         {
1145           notmod = (((String[]) cstate)[0].equals(((String[]) lastVal)[0]));
1146         }
1147       }
1148       pmdialogbox.argSetModified(this, !notmod);
1149     }
1150
1151     public void mouseClicked(MouseEvent e)
1152     {
1153       if (javax.swing.SwingUtilities.isRightMouseButton(e))
1154       {
1155         Desktop.showUrl(finfo.toString());
1156       }
1157     }
1158
1159     public void mousePressed(MouseEvent e)
1160     {
1161       // TODO Auto-generated method stub
1162
1163     }
1164
1165     public void mouseReleased(MouseEvent e)
1166     {
1167       // TODO Auto-generated method stub
1168
1169     }
1170
1171     public void mouseEntered(MouseEvent e)
1172     {
1173       // TODO Auto-generated method stub
1174
1175     }
1176
1177     public void mouseExited(MouseEvent e)
1178     {
1179       // TODO Auto-generated method stub
1180
1181     }
1182
1183   }
1184
1185   Map<String, OptionBox> optSet = new Hashtable<String, OptionBox>();
1186
1187   public class OptionBox extends JPanel implements MouseListener,
1188           ActionListener
1189   {
1190     JComboBox val = new JComboBox();
1191
1192     JCheckBox enabled = new JCheckBox();
1193
1194     JLabel optlabel = new JLabel();
1195
1196     URL linkImageURL = getClass().getResource("/images/link.gif");
1197
1198     final URL finfo;
1199
1200     boolean hasLink = false;
1201
1202     OptionI option;
1203
1204     public OptionBox(OptionI opt)
1205     {
1206       option = opt;
1207       setLayout(new BorderLayout());
1208       enabled.setSelected(opt.isRequired()); // TODO: lock required options
1209       enabled.setFont(new Font("Verdana", Font.PLAIN, 11));
1210       enabled.setText("");
1211       enabled.setText(opt.getName());
1212       enabled.addActionListener(this);
1213       finfo = option.getFurtherDetails();
1214       if (finfo != null)
1215       {
1216         hasLink = true;
1217         // optlabel.setToolTipText("<html><p>"+opt.getDescription()+"</p><img src=\""+linkImageURL+"\"/></html>");
1218         enabled.setToolTipText("<html><p>" + opt.getDescription()
1219                 + "</p><img src=\"" + linkImageURL + "\"/></html>");
1220         // optlabel.addMouseListener(this);
1221         enabled.addMouseListener(this);
1222       }
1223       else
1224       {
1225         // optlabel.setToolTipText(opt.getDescription());
1226         enabled.setToolTipText(opt.getDescription());
1227       }
1228       add(enabled, BorderLayout.NORTH);
1229       if (opt.getPossibleValues().size() > 1)
1230       {
1231         setLayout(new GridLayout(1, 2));
1232         for (Object str : opt.getPossibleValues())
1233         {
1234           val.addItem((String) str);
1235         }
1236         val.setSelectedItem((String) opt.getDefaultValue());
1237         val.addActionListener(this);
1238         add(val, BorderLayout.SOUTH);
1239       }
1240       // TODO: add actionListeners for popup (to open further info),
1241       // and to update list of parameters if an option is enabled
1242       // that takes a value.
1243       setInitialValue();
1244     }
1245
1246     public void resetToDefault()
1247     {
1248       enabled.setSelected(false);
1249       if (option.isRequired())
1250       {
1251         // Apply default value
1252         selectOption(option, option.getDefaultValue());
1253       }
1254     }
1255
1256     boolean initEnabled = false;
1257
1258     String initVal = null;
1259
1260     public void setInitialValue()
1261     {
1262       initEnabled = enabled.isSelected();
1263       if (option.getPossibleValues() != null
1264               && option.getPossibleValues().size() > 1)
1265       {
1266         initVal = (String) val.getSelectedItem();
1267       }
1268       else
1269       {
1270         initVal = (initEnabled) ? option.getDefaultValue() : null;
1271       }
1272     }
1273
1274     public OptionI getOptionIfEnabled()
1275     {
1276       if (!enabled.isSelected())
1277       {
1278         return null;
1279       }
1280       OptionI opt = option.copy();
1281
1282       if (val.getSelectedItem() != null)
1283       {
1284         opt.setDefaultValue((String) val.getSelectedItem());
1285       }
1286       return opt;
1287     }
1288
1289     public void actionPerformed(ActionEvent e)
1290     {
1291       if (e.getSource() != enabled)
1292       {
1293         enabled.setSelected(true);
1294       }
1295       checkIfModified();
1296     }
1297
1298     private void checkIfModified()
1299     {
1300       boolean notmod = (initEnabled == enabled.isSelected());
1301       if (enabled.isSelected())
1302       {
1303         if (initVal != null)
1304         {
1305           notmod &= initVal.equals(val.getSelectedItem());
1306         }
1307         else
1308         {
1309           // compare against default service setting
1310           notmod &= option.getDefaultValue() == null
1311                   || option.getDefaultValue().equals(val.getSelectedItem());
1312         }
1313       }
1314       else
1315       {
1316         notmod &= initVal == null;
1317       }
1318       argSetModified(this, !notmod);
1319     }
1320
1321     public void mouseClicked(MouseEvent e)
1322     {
1323       if (javax.swing.SwingUtilities.isRightMouseButton(e))
1324       {
1325         showUrlPopUp(this, finfo.toString(), e.getX(), e.getY());
1326       }
1327     }
1328
1329     public void mousePressed(MouseEvent e)
1330     {
1331       // TODO Auto-generated method stub
1332
1333     }
1334
1335     public void mouseReleased(MouseEvent e)
1336     {
1337       // TODO Auto-generated method stub
1338
1339     }
1340
1341     public void mouseEntered(MouseEvent e)
1342     {
1343       // TODO Auto-generated method stub
1344
1345     }
1346
1347     public void mouseExited(MouseEvent e)
1348     {
1349       // TODO Auto-generated method stub
1350
1351     }
1352
1353   }
1354
1355   private OptionBox addOption(OptionI opt)
1356   {
1357     OptionBox cb = optSet.get(opt.getName());
1358     if (cb == null)
1359     {
1360       cb = new OptionBox(opt);
1361       optSet.put(opt.getName(), cb);
1362       jobOptions.add(cb);
1363     }
1364     return cb;
1365   }
1366
1367   public static void showUrlPopUp(JComponent invoker, final String finfo,
1368           int x, int y)
1369   {
1370
1371     JPopupMenu mnu = new JPopupMenu();
1372     JMenuItem mitem = new JMenuItem("View " + finfo);
1373     mitem.addActionListener(new ActionListener()
1374     {
1375
1376       @Override
1377       public void actionPerformed(ActionEvent e)
1378       {
1379         Desktop.showUrl(finfo);
1380
1381       }
1382     });
1383     mnu.add(mitem);
1384     mnu.show(invoker, x, y);
1385   }
1386
1387   protected void refreshParamLayout()
1388   {
1389     FlowLayout fl = new FlowLayout();
1390     paramList.setLayout(fl);
1391     int s = 2 * fl.getVgap();
1392     for (ParamBox pbox : paramSet.values())
1393     {
1394       s += fl.getVgap() + pbox.getBoxHeight(); // getBoxHeight();
1395     }
1396     paramList.setPreferredSize(new Dimension(PARAM_WIDTH, s));
1397     validate();
1398   }
1399
1400   /**
1401    * testing method - grab a service and parameter set and show the window
1402    * 
1403    * @param args
1404    */
1405   public static void main(String[] args)
1406   {
1407     jalview.ws.jws2.Jws2Discoverer disc = jalview.ws.jws2.Jws2Discoverer
1408             .getDiscoverer();
1409     int p = 0;
1410     if (args.length > 3)
1411     {
1412       Vector<String> services = new Vector<String>();
1413       services.addElement(args[p++]);
1414       Jws2Discoverer.setServiceUrls(services);
1415     }
1416     try
1417     {
1418       disc.run();
1419     } catch (Exception e)
1420     {
1421       System.err.println("Aborting. Problem discovering services.");
1422       e.printStackTrace();
1423       return;
1424     }
1425     Jws2Discoverer.Jws2Instance lastserv = null;
1426     for (Jws2Discoverer.Jws2Instance service : disc.getServices())
1427     {
1428       lastserv = service;
1429       if (p >= args.length || service.serviceType.equalsIgnoreCase(args[p]))
1430       {
1431         if (lastserv != null)
1432         {
1433           List<Preset> prl = null;
1434           Preset pr = null;
1435           if (++p < args.length)
1436           {
1437             PresetManager prman = lastserv.getPresets();
1438             if (prman != null)
1439             {
1440               pr = prman.getPresetByName(args[p]);
1441               if (pr == null)
1442               {
1443                 // just grab the last preset.
1444                 prl = prman.getPresets();
1445               }
1446             }
1447           }
1448           else
1449           {
1450             PresetManager prman = lastserv.getPresets();
1451             if (prman != null)
1452             {
1453               prl = prman.getPresets();
1454             }
1455           }
1456           Iterator<Preset> en = (prl == null) ? null : prl.iterator();
1457           while (en != null && en.hasNext())
1458           {
1459             if (en != null)
1460             {
1461               if (!en.hasNext())
1462               {
1463                 en = prl.iterator();
1464               }
1465               pr = en.next();
1466             }
1467             {
1468               System.out.println("Testing opts dupes for "
1469                       + lastserv.getHost() + " : "
1470                       + lastserv.getActionText() + ":" + pr.getName());
1471               List<Option> rg = lastserv.getRunnerConfig().getOptions();
1472               for (Option o : rg)
1473               {
1474                 try
1475                 {
1476                   Option cpy = jalview.ws.jws2.ParameterUtils.copyOption(o);
1477                 } catch (Exception e)
1478                 {
1479                   System.err.println("Failed to copy " + o.getName());
1480                   e.printStackTrace();
1481                 } catch (Error e)
1482                 {
1483                   System.err.println("Failed to copy " + o.getName());
1484                   e.printStackTrace();
1485                 }
1486               }
1487             }
1488             {
1489               System.out.println("Testing param dupes:");
1490               List<Parameter> rg = lastserv.getRunnerConfig()
1491                       .getParameters();
1492               for (Parameter o : rg)
1493               {
1494                 try
1495                 {
1496                   Parameter cpy = jalview.ws.jws2.ParameterUtils
1497                           .copyParameter(o);
1498                 } catch (Exception e)
1499                 {
1500                   System.err.println("Failed to copy " + o.getName());
1501                   e.printStackTrace();
1502                 } catch (Error e)
1503                 {
1504                   System.err.println("Failed to copy " + o.getName());
1505                   e.printStackTrace();
1506                 }
1507               }
1508             }
1509             {
1510               System.out.println("Testing param write:");
1511               List<String> writeparam = null, readparam = null;
1512               try
1513               {
1514                 writeparam = jalview.ws.jws2.ParameterUtils
1515                         .writeParameterSet(
1516                                 pr.getArguments(lastserv.getRunnerConfig()),
1517                                 " ");
1518                 System.out.println("Testing param read :");
1519                 List<Option> pset = jalview.ws.jws2.ParameterUtils
1520                         .processParameters(writeparam,
1521                                 lastserv.getRunnerConfig(), " ");
1522                 readparam = jalview.ws.jws2.ParameterUtils
1523                         .writeParameterSet(pset, " ");
1524                 Iterator<String> o = pr.getOptions().iterator(), s = writeparam
1525                         .iterator(), t = readparam.iterator();
1526                 boolean failed = false;
1527                 while (s.hasNext() && t.hasNext())
1528                 {
1529                   String on = o.next(), sn = s.next(), st = t.next();
1530                   if (!sn.equals(st))
1531                   {
1532                     System.out.println("Original was " + on
1533                             + " Phase 1 wrote " + sn + "\tPhase 2 wrote "
1534                             + st);
1535                     failed = true;
1536                   }
1537                 }
1538                 if (failed)
1539                 {
1540                   System.out.println("Original parameters:\n"
1541                           + pr.getOptions());
1542                   System.out.println("Wrote parameters in first set:\n"
1543                           + writeparam);
1544                   System.out.println("Wrote parameters in second set:\n"
1545                           + readparam);
1546
1547                 }
1548               } catch (Exception e)
1549               {
1550                 e.printStackTrace();
1551               }
1552             }
1553             WsJobParameters pgui = new WsJobParameters(lastserv, new JabaPreset(lastserv, pr));
1554             JFrame jf = new JFrame("Parameters for "
1555                     + lastserv.getActionText());
1556             JPanel cont = new JPanel();
1557             // jf.setPreferredSize(new Dimension(600, 800));
1558             cont.add(pgui);
1559             jf.add(cont);
1560             final Thread thr = Thread.currentThread();
1561             jf.addWindowListener(new WindowListener()
1562             {
1563
1564               public void windowActivated(WindowEvent e)
1565               {
1566                 // TODO Auto-generated method stub
1567
1568               }
1569
1570               public void windowClosed(WindowEvent e)
1571               {
1572               }
1573
1574               public void windowClosing(WindowEvent e)
1575               {
1576                 thr.interrupt();
1577
1578               }
1579
1580               public void windowDeactivated(WindowEvent e)
1581               {
1582                 // TODO Auto-generated method stub
1583
1584               }
1585
1586               public void windowDeiconified(WindowEvent e)
1587               {
1588                 // TODO Auto-generated method stub
1589
1590               }
1591
1592               public void windowIconified(WindowEvent e)
1593               {
1594                 // TODO Auto-generated method stub
1595
1596               }
1597
1598               public void windowOpened(WindowEvent e)
1599               {
1600                 // TODO Auto-generated method stub
1601
1602               }
1603
1604             });
1605             jf.setVisible(true);
1606             boolean inter = false;
1607             while (!inter)
1608             {
1609               try
1610               {
1611                 Thread.sleep(10000);
1612               } catch (Exception e)
1613               {
1614                 inter = true;
1615               }
1616               ;
1617             }
1618             jf.dispose();
1619           }
1620         }
1621       }
1622     }
1623   }
1624
1625   public List<ArgumentI> getJobParams()
1626   {
1627     List<ArgumentI> argSet = new ArrayList<ArgumentI>();
1628     // recover options and parameters from GUI
1629     for (OptionBox opts : optSet.values())
1630     {
1631       OptionI opt = opts.getOptionIfEnabled();
1632       if (opt != null)
1633       {
1634         argSet.add(opt);
1635       }
1636     }
1637     for (ParamBox parambox : paramSet.values())
1638     {
1639       ParameterI parm = parambox.getParameter();
1640       if (parm != null)
1641       {
1642         argSet.add(parm);
1643       }
1644     }
1645
1646     return argSet;
1647   }
1648
1649   String lastParmSet = null;
1650
1651   /*
1652    * Hashtable<String, Object[]> editedParams = new Hashtable<String,
1653    * Object[]>();
1654    * 
1655    * store the given parameters in the user parameter set database.
1656    * 
1657    * @param storeSetName - lastParmSet
1658    * 
1659    * @param descr - setDescr.getText()
1660    * 
1661    * @param jobParams - getJobParams()
1662    * 
1663    * private void _storeUserPreset(String storeSetName, String descr,
1664    * List<ArgumentI> jobParams) { // this is a simple hash store. Object[] pset;
1665    * editedParams.put(storeSetName, pset = new Object[3]); pset[0] =
1666    * storeSetName; pset[1] = descr; pset[2] = jobParams; // writeParam("Saving "
1667    * + storeSetName + ": ", jobParams); }
1668    * 
1669    * private void writeParam(String nm, List<ArgumentI> params) { for (ArgumentI
1670    * p : params) { System.out.println(nm + ":" + System.identityHashCode(p) +
1671    * " Name: " + p.getName() + " Value: " + p.getDefaultValue()); } }
1672    * 
1673    * private Object[] _getUserPreset(String setName) { Object[] pset =
1674    * editedParams.get(setName); // if (pset != null) // writeParam("Retrieving "
1675    * + setName + ": ", (List<Argument>) pset[2]); return pset; }
1676    * 
1677    * * remove the given user preset from the preset stash
1678    * 
1679    * @param setName
1680    * 
1681    * private void _deleteUserPreset(String setName) {
1682    * editedParams.remove(setName); }
1683    */
1684
1685   private void syncSetNamesWithStore()
1686   {
1687     int n = 0;
1688     // remove any set names in the drop down menu that aren't either a reserved
1689     // setting, or a user defined or service preset.
1690     Vector items = new Vector();
1691     while (n < setName.getItemCount())
1692     {
1693       String item = (String) setName.getItemAt(n);
1694       if (!item.equals(SVC_DEF) && !paramStore.presetExists(item))
1695       {
1696         setName.removeItemAt(n);
1697       }
1698       else
1699       {
1700         items.addElement(item);
1701         n++;
1702       }
1703     }
1704     if (!items.contains(SVC_DEF))
1705     {
1706       setName.addItem(SVC_DEF);
1707     }
1708     for (WsParamSetI upn : paramStore.getPresets())
1709     {
1710       if (!items.contains(upn.getName()))
1711       {
1712         setName.addItem(upn.getName());
1713       }
1714     }
1715   }
1716
1717   /**
1718    * true if lastParmSet is a user preset
1719    */
1720   boolean isUserPreset = false;
1721
1722   private void reInitDialog(String nextPreset)
1723   {
1724     settingDialog = true;
1725     // updateTable(null,null); // first reset to defaults
1726     WsParamSetI pset = null;
1727     if (nextPreset != null && nextPreset.length() > 0)
1728     {
1729       pset = paramStore.getPreset(nextPreset);
1730     }
1731     if (pset != null)
1732     {
1733       if (pset.isModifiable())
1734       {
1735         isUserPreset = true;
1736         setDescr.setText(pset.getDescription());
1737         updateTable(null, pset.getArguments());
1738         lastParmSet = nextPreset;
1739       }
1740       else
1741       {
1742         isUserPreset = false;
1743         setDescr.setText("");
1744         // must be a default preset from service
1745         updateTable(pset, null);
1746         lastParmSet = nextPreset;
1747       }
1748     }
1749     else
1750     {
1751       isUserPreset = false;
1752       // Service defaults
1753       updateTable(null, null);
1754       lastParmSet = SVC_DEF;
1755     }
1756
1757     initArgSetModified();
1758     syncSetNamesWithStore();
1759     setName.setSelectedItem(lastParmSet);
1760     validate();
1761     settingDialog = false;
1762
1763   }
1764
1765   String curSetName = null;
1766
1767   public void itemStateChanged(ItemEvent e)
1768   {
1769     if (e.getSource() == setName && e.getStateChange() == e.SELECTED)
1770     {
1771       String setname = (String) setName.getSelectedItem();
1772       System.out.println("Item state changed for " + setname
1773               + " (handling ? " + !settingDialog + ")");
1774       if (settingDialog)
1775       {
1776         // ignore event
1777         return;
1778       }
1779       if (setname == null)
1780       {
1781         return;
1782       }
1783       // user has selected a different item from combo-box
1784       if (isModified())
1785       {
1786         String lsetname = (curSetName != null) ? curSetName : lastParmSet;
1787         if (lsetname.equals(setname))
1788         {
1789           // setname was just edited - so ignore this event.
1790           return;
1791         }
1792         settingDialog = true;
1793         System.out.println("Prompting to save " + lsetname);
1794         if (javax.swing.JOptionPane.showConfirmDialog(this,
1795                 "Parameter set '" + lsetname + "' is modifed\nsave ?",
1796                 "Save changes ?", javax.swing.JOptionPane.OK_CANCEL_OPTION) == JOptionPane.OK_OPTION)
1797         {
1798           System.out.println("Saving for " + lsetname);
1799           _storeCurrentPreset(lsetname);
1800
1801         }
1802       }
1803       settingDialog = true;
1804       reInitDialog(setname);
1805       settingDialog = false;
1806
1807     }
1808   }
1809
1810   /**
1811    * store current settings as given name. You should then reset gui.
1812    * 
1813    * @param curSetName2
1814    */
1815   private void _storeCurrentPreset(String curSetName2)
1816   {
1817     paramStore.storePreset(curSetName2, setDescr.getText(), getJobParams());
1818   }
1819
1820   /**
1821    * last saved name for this user preset
1822    */
1823   String lastSetName = null;
1824
1825   /**
1826    * last saved value of the description text for this user preset
1827    */
1828   String lastDescrText = null;
1829
1830   public void actionPerformed(ActionEvent e)
1831   {
1832     if (e.getSource() instanceof Component)
1833     {
1834       Component src = (Component) e.getSource();
1835       if (src.getParent() == setName)
1836       {
1837         // rename any existing records we know about for this set.
1838         String newname = (String) e.getActionCommand().trim();
1839         String msg = null;
1840         if (isServicePreset(newname))
1841         {
1842           JOptionPane.showConfirmDialog(this,
1843                   "Invalid name - preset already exists.", "Invalid name",
1844                   JOptionPane.OK_OPTION);
1845           // reset name
1846           setName.setSelectedItem(curSetName);
1847           return;
1848         }
1849         curSetName = newname;
1850         System.err.println("New name for user setting " + curSetName
1851                 + " (was " + setName.getSelectedItem() + ")");
1852         if (curSetName.equals(setName.getSelectedItem()))
1853         {
1854           curSetName = null;
1855         }
1856         if (curSetName != null)
1857         {
1858           argSetModified(setName, true);
1859           return;
1860         }
1861
1862       }
1863     }
1864   }
1865
1866   private void checkDescrModified()
1867   {
1868     if (!settingDialog)
1869     {
1870
1871       argSetModified(
1872               setDescr,
1873               (lastDescrText == null ? setDescr.getText().trim().length() > 0
1874                       : !setDescr.getText().equals(lastDescrText)));
1875
1876     }
1877   }
1878
1879   public void insertUpdate(DocumentEvent e)
1880   {
1881     checkDescrModified();
1882   }
1883
1884   public void removeUpdate(DocumentEvent e)
1885   {
1886     checkDescrModified();
1887   }
1888
1889   public void changedUpdate(DocumentEvent e)
1890   {
1891     checkDescrModified();
1892   }
1893
1894   /**
1895    * 
1896    * @return null or the service preset selected by the user
1897    */
1898   public WsParamSetI getPreset()
1899   {
1900     if (isUserPreset || isModified()
1901             || (lastParmSet != null && lastParmSet.equals(SVC_DEF)))
1902     {
1903       return null;
1904     }
1905     else
1906     {
1907       return paramStore.getPreset(lastParmSet);
1908     }
1909   }
1910 }