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