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