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