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