JAL-3690 refactoring web-services discovery
[jalview.git] / src / jalview / gui / WsJobParameters.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
3  * Copyright (C) $$Year-Rel$$ The Jalview Authors
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
10  * of the License, or (at your option) any later version.
11  *  
12  * Jalview is distributed in the hope that it will be useful, but 
13  * WITHOUT ANY WARRANTY; without even the implied warranty 
14  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
15  * PURPOSE.  See the GNU General Public License for more details.
16  * 
17  * You should have received a copy of the GNU General Public License
18  * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
19  * The Jalview Authors are detailed in the 'AUTHORS' file.
20  */
21 package jalview.gui;
22
23 import jalview.gui.OptsAndParamsPage.OptionBox;
24 import jalview.gui.OptsAndParamsPage.ParamBox;
25 import jalview.util.MessageManager;
26 import jalview.ws.api.UIinfo;
27 import jalview.ws.params.ArgumentI;
28 import jalview.ws.params.OptionI;
29 import jalview.ws.params.ParamDatastoreI;
30 import jalview.ws.params.ParameterI;
31 import jalview.ws.params.WsParamSetI;
32
33 import java.awt.BorderLayout;
34 import java.awt.Component;
35 import java.awt.Dimension;
36 import java.awt.FlowLayout;
37 import java.awt.Font;
38 import java.awt.GridBagConstraints;
39 import java.awt.GridBagLayout;
40 import java.awt.GridLayout;
41 import java.awt.Rectangle;
42 import java.awt.event.ActionEvent;
43 import java.awt.event.ActionListener;
44 import java.awt.event.HierarchyBoundsListener;
45 import java.awt.event.HierarchyEvent;
46 import java.awt.event.ItemEvent;
47 import java.awt.event.ItemListener;
48 import java.util.Hashtable;
49 import java.util.List;
50 import java.util.Vector;
51
52 import javax.swing.JButton;
53 import javax.swing.JComboBox;
54 import javax.swing.JDialog;
55 import javax.swing.JLabel;
56 import javax.swing.JPanel;
57 import javax.swing.JScrollPane;
58 import javax.swing.JTextArea;
59 import javax.swing.border.TitledBorder;
60 import javax.swing.event.DocumentEvent;
61 import javax.swing.event.DocumentListener;
62
63 import net.miginfocom.swing.MigLayout;
64
65 /**
66  * job parameter editing/browsing dialog box. User can browse existing settings
67  * (user + presets + Defaults), and any changes to parameters creates a modified
68  * user parameter set. LOGIC: If the parameter set is modified, and its name is
69  * a valid, non-existant user parameter set, then a save button is shown. If the
70  * parameter set is modified and its name is a valid, extant user parameter set,
71  * then an update button is shown. If user parameter set's name is edited, and
72  * old name exists as a writable user parameter set, then rename button is
73  * shown. If current parameter set is associated with a user defined parameter
74  * set, then : if set is modifed, a 'revert' button is shown. if set is not
75  * modified, a 'delete' button is shown.
76  * 
77  * @author JimP
78  * 
79  */
80 public class WsJobParameters extends JPanel implements ItemListener,
81         ActionListener, DocumentListener, OptsParametersContainerI
82 {
83   private static final int PREFERRED_WIDTH = 540;
84
85   private static final int DEFAULT_HEIGHT = 640;
86
87   // the default parameter set shown to the user
88   private static final String SVC_DEF = "Defaults";
89
90   private int maxOptWidth = 200;
91
92   // URL linkImageURL = getClass().getResource("/images/link.gif");
93
94   // TODO ABSRACT FROM JABAWS CLASSES
95
96   /**
97    * manager for options and parameters.
98    */
99   OptsAndParamsPage opanp;
100
101   /*
102    * panel containing job options
103    */
104   JPanel optionsPanel = new JPanel();
105
106   /*
107    * panel containing job parameters
108    */
109   JPanel paramsPanel = new JPanel();
110
111   JPanel setNamePanel = new JPanel();
112
113   JButton createpref = new JButton();
114
115   JButton deletepref = new JButton();
116
117   JButton revertpref = new JButton();
118
119   JButton updatepref = new JButton();
120
121   JComboBox<String> setName = new JComboBox<>();
122
123   JTextArea setDescr = new JTextArea();
124
125   JScrollPane paramPane = new JScrollPane();
126
127   ParamDatastoreI paramStore;
128
129   // set true when 'Start Job' is clicked
130   boolean startJob = false;
131
132   JDialog frame = null;
133
134   UIinfo service;
135
136   /*
137    * list of service presets in the gui
138    */
139   Hashtable<String, String> servicePresets = null;
140
141   /*
142    * set if dialog is being set - so handlers will avoid spurious events
143    */
144   boolean settingDialog = false;
145
146   private Hashtable<Object, Object> modifiedElements = new Hashtable<>();
147
148   String lastParmSet = null;
149
150   public WsJobParameters(ParamDatastoreI store, WsParamSetI preset,
151           List<ArgumentI> args)
152   {
153     super();
154
155     // parameters dialog in 'compact' format (help as tooltips)
156     opanp = new OptsAndParamsPage(this, true);
157     jbInit();
158     this.paramStore = store;
159     this.service = null;
160     init(preset, args);
161     validate();
162   }
163
164   /**
165    * Constructor given a set of parameters and presets, a service to be invoked,
166    * and a list of (Jabaws client) arguments
167    * 
168    * @param paramStorei
169    * @param service
170    * @param preset
171    * @param jobArgset
172    */
173   public WsJobParameters(ParamDatastoreI paramStorei, UIinfo service,
174           WsParamSetI preset, List<ArgumentI> jobArgset)
175   {
176     super();
177
178     // parameters dialog in 'expanded' format (help text boxes)
179     opanp = new OptsAndParamsPage(this, false);
180
181     jbInit();
182     this.paramStore = paramStorei;
183     if (paramStore == null && service != null)
184     {
185       paramStore = service.getParamStore();
186     }
187     this.service = service;
188     initForService(preset, jobArgset);
189     validate();
190   }
191
192   /**
193    * Shows a modal dialog containing the parameters and Start or Cancel options.
194    * Answers true if the job is started, false if cancelled.
195    * 
196    * @return
197    */
198   public boolean showRunDialog()
199   {
200
201     frame = new JDialog(Desktop.instance, true);
202     if (service != null)
203     {
204       frame.setTitle(MessageManager.formatMessage("label.edit_params_for",
205               new String[]
206       { service.getActionText() }));
207     }
208
209     Rectangle deskr = Desktop.instance.getBounds();
210     Dimension pref = this.getPreferredSize();
211     frame.setBounds(
212             new Rectangle((int) (deskr.getCenterX() - pref.width / 2),
213                     (int) (deskr.getCenterY() - pref.height / 2),
214                     pref.width, pref.height));
215     frame.setContentPane(this);
216
217     // should perhaps recover defaults from user prefs.
218
219     frame.validate();
220     javax.swing.SwingUtilities.invokeLater(new Runnable()
221     {
222       @Override
223       public void run()
224       {
225         // jobPanel.setDividerLocation(0.25);
226
227       }
228     });
229     frame.setVisible(true);
230
231     return startJob;
232   }
233
234   private void jbInit()
235   {
236     this.addHierarchyBoundsListener(new HierarchyBoundsListener()
237     {
238
239       @Override
240       public void ancestorResized(HierarchyEvent arg0)
241       {
242         refreshParamLayout();
243       }
244
245       @Override
246       public void ancestorMoved(HierarchyEvent arg0)
247       {
248         // TODO Auto-generated method stub
249
250       }
251     });
252     updatepref = JvSwingUtils.makeButton(
253             MessageManager.getString("action.update"),
254             MessageManager.getString("label.update_user_parameter_set"),
255             new ActionListener()
256             {
257
258               @Override
259               public void actionPerformed(ActionEvent e)
260               {
261                 update_actionPerformed();
262               }
263             });
264     deletepref = JvSwingUtils.makeButton(
265             MessageManager.getString("action.delete"),
266             MessageManager.getString("label.delete_user_parameter_set"),
267             new ActionListener()
268             {
269
270               @Override
271               public void actionPerformed(ActionEvent e)
272               {
273                 delete_actionPerformed();
274               }
275             });
276     createpref = JvSwingUtils.makeButton(
277             MessageManager.getString("action.create"),
278             MessageManager.getString("label.create_user_parameter_set"),
279             new ActionListener()
280             {
281
282               @Override
283               public void actionPerformed(ActionEvent e)
284               {
285                 create_actionPerformed();
286               }
287             });
288     revertpref = JvSwingUtils.makeButton(
289             MessageManager.getString("action.revert"),
290             MessageManager
291                     .getString("label.revert_changes_user_parameter_set"),
292             new ActionListener()
293             {
294
295               @Override
296               public void actionPerformed(ActionEvent e)
297               {
298                 revert_actionPerformed();
299               }
300             });
301
302     JButton startjob = JvSwingUtils.makeButton(
303             MessageManager.getString("action.start_job"),
304             MessageManager.getString("label.start_job_current_settings"),
305             new ActionListener()
306             {
307               @Override
308               public void actionPerformed(ActionEvent e)
309               {
310                 startjob_actionPerformed();
311               }
312             });
313     JButton canceljob = JvSwingUtils.makeButton(
314             MessageManager.getString("action.cancel_job"),
315             MessageManager.getString("label.cancel_job_close_dialog"),
316             new ActionListener()
317             {
318               @Override
319               public void actionPerformed(ActionEvent e)
320               {
321                 canceljob_actionPerformed();
322               }
323             });
324
325     JPanel setDetails = new JPanel();
326     setDetails.setBorder(
327             new TitledBorder(MessageManager.getString("label.details")));
328     setDetails.setLayout(new BorderLayout());
329     setDescr.setColumns(40);
330     setDescr.setWrapStyleWord(true);
331     setDescr.setLineWrap(true);
332     setDescr.setBackground(getBackground());
333     setDescr.setEditable(true);
334     setDescr.getDocument().addDocumentListener(this);
335     setDescr.setToolTipText(
336             MessageManager.getString("label.edit_notes_parameter_set"));
337     JScrollPane setDescrView = new JScrollPane();
338     setDescrView.getViewport().setView(setDescr);
339     setName.setEditable(true);
340     setName.addItemListener(this);
341     setName.getEditor().addActionListener(this);
342     JPanel setNameInfo = new JPanel(new FlowLayout(FlowLayout.LEFT));
343     GridBagLayout gbl = new GridBagLayout();
344     setNamePanel.setLayout(gbl);
345
346     JLabel setNameLabel = new JLabel(
347             MessageManager.getString("label.current_parameter_set_name"));
348     setNameLabel.setFont(new java.awt.Font("Verdana", Font.PLAIN, 10));
349
350     setNameInfo.add(setNameLabel);
351     setNameInfo.add(setName);
352
353     // initial button visibility
354     updatepref.setVisible(false);
355     deletepref.setVisible(false);
356     revertpref.setVisible(false);
357     createpref.setVisible(false);
358     JPanel setsavebuts = new JPanel();
359     setsavebuts.setLayout(new FlowLayout(FlowLayout.LEFT, 10, 0)); // GridLayout(1,2));
360     JPanel spacer = new JPanel();
361     spacer.setPreferredSize(new Dimension(2, 30));
362     setsavebuts.add(spacer);
363     setsavebuts.add(deletepref);
364     setsavebuts.add(revertpref);
365     setsavebuts.add(createpref);
366     setsavebuts.add(updatepref);
367     // setsavebuts.setSize(new Dimension(150, 30));
368     JPanel buttonArea = new JPanel(new GridLayout(1, 1));
369     buttonArea.add(setsavebuts);
370     setNamePanel.add(setNameInfo);
371     GridBagConstraints gbc = new GridBagConstraints();
372     gbc.gridheight = 2;
373     gbl.setConstraints(setNameInfo, gbc);
374     setNamePanel.add(buttonArea);
375     gbc = new GridBagConstraints();
376     gbc.gridx = 0;
377     gbc.gridy = 2;
378     gbc.gridheight = 1;
379     gbl.setConstraints(buttonArea, gbc);
380     setDetails.add(setDescrView, BorderLayout.CENTER);
381
382     // paramPane.setPreferredSize(new Dimension(360, 400));
383     // paramPane.setPreferredSize(null);
384     optionsPanel.setBorder(
385             new TitledBorder(MessageManager.getString("label.options")));
386     optionsPanel.setOpaque(true);
387     paramsPanel.setBorder(
388             new TitledBorder(MessageManager.getString("label.parameters")));
389     paramsPanel.setOpaque(true);
390     // optsAndparams.setScrollableWidth(ScrollableSizeHint.FIT);
391     // optsAndparams.setScrollableHeight(ScrollableSizeHint.NONE);
392     // optsAndparams.setLayout(new BorderLayout());
393     JPanel optsAndparams = new JPanel();
394     optsAndparams.setLayout(new BorderLayout());
395     optsAndparams.add(optionsPanel, BorderLayout.NORTH);
396     optsAndparams.add(paramsPanel, BorderLayout.CENTER);
397     JPanel jp = new JPanel(new BorderLayout());
398     jp.add(optsAndparams, BorderLayout.CENTER);
399     paramPane.getViewport().setView(jp);
400     paramPane.setBorder(null);
401     setLayout(new BorderLayout());
402
403     JPanel jobPanel = new JPanel();
404     jobPanel.setPreferredSize(null);
405     jobPanel.setLayout(new BorderLayout());
406     jobPanel.add(setDetails, BorderLayout.NORTH);
407     jobPanel.add(paramPane, BorderLayout.CENTER);
408     // jobPanel.setOrientation(JSplitPane.VERTICAL_SPLIT);
409
410     add(setNamePanel, BorderLayout.NORTH);
411     add(jobPanel, BorderLayout.CENTER);
412
413     JPanel dialogpanel = new JPanel();
414     dialogpanel.add(startjob);
415     dialogpanel.add(canceljob);
416     // JAL-1580: setMaximumSize() doesn't work, so just size for the worst case:
417     // check for null is for JUnit usage
418     final int windowHeight = Desktop.instance == null ? DEFAULT_HEIGHT
419             : Desktop.instance.getHeight();
420     // setPreferredSize(new Dimension(PREFERRED_WIDTH, windowHeight));
421     add(dialogpanel, BorderLayout.SOUTH);
422     validate();
423   }
424
425   protected void revert_actionPerformed()
426   {
427     reInitDialog(lastParmSet);
428     updateWebServiceMenus();
429   }
430
431   protected void update_actionPerformed()
432   {
433     if (isUserPreset)
434     {
435       String curname = ((String) setName.getSelectedItem()).trim();
436       _updatePreset(lastParmSet, curname);
437       lastParmSet = curname;
438       isUserPreset = true;
439       initArgSetModified();
440       syncSetNamesWithStore();
441     }
442   }
443
444   private void _deleteUserPreset(String lastParmSet2)
445   {
446     paramStore.deletePreset(lastParmSet2);
447   }
448
449   protected void delete_actionPerformed()
450   {
451     if (isUserPreset)
452     {
453       // delete current preset's saved entry
454       _deleteUserPreset(lastParmSet);
455     }
456     reInitDialog(null); // service default
457     updateWebServiceMenus();
458   }
459
460   protected void create_actionPerformed()
461   {
462     String curname = ((String) setName.getSelectedItem()).trim();
463     if (curname.length() > 0)
464     {
465       _storeCurrentPreset(curname);
466       lastParmSet = curname;
467       isUserPreset = true;
468       reInitDialog(curname);
469       initArgSetModified();
470       updateWebServiceMenus();
471     }
472     else
473     {
474       // TODO: show warning
475       System.err.println("Invalid name. Not saved.");
476     }
477   }
478
479   protected void canceljob_actionPerformed()
480   {
481     startJob = false;
482     if (frame != null)
483     {
484       frame.setVisible(false);
485     }
486   }
487
488   protected void startjob_actionPerformed()
489   {
490     startJob = true;
491     if (frame != null)
492     {
493       frame.setVisible(false);
494     }
495   }
496
497   void initForService(WsParamSetI paramSet, List<ArgumentI> jobArgset)
498   {
499     settingDialog = true;
500
501     init(paramSet, jobArgset);
502
503   }
504
505   void init(WsParamSetI p, List<ArgumentI> jobArgset)
506   {
507     Hashtable<String, String> exnames = new Hashtable<>();
508     for (int i = 0, iSize = setName.getItemCount(); i < iSize; i++)
509     {
510       exnames.put(setName.getItemAt(i), setName.getItemAt(i));
511     }
512     servicePresets = new Hashtable<>();
513     // Add the default entry - if not present already.
514     if (!exnames.contains(SVC_DEF))
515     {
516       setName.addItem(SVC_DEF);
517       exnames.put(SVC_DEF, SVC_DEF);
518       servicePresets.put(SVC_DEF, SVC_DEF);
519     }
520
521     // String curname = (p == null ? "" : p.getName());
522     for (WsParamSetI pr : paramStore.getPresets())
523     {
524       if (!pr.isModifiable())
525       {
526         servicePresets.put(pr.getName(), "preset");
527       }
528       else
529       {
530       }
531       if (!exnames.contains(pr.getName()))
532       {
533         setName.addItem(pr.getName());
534       }
535     }
536     // TODO: if initial jobArgset matches a given user setting or preset then
537     // should recover setting accordingly
538     // updateTable(p, jobArgset);
539     if (p != null)
540     {
541       reInitDialog(p.getName());
542       initArgSetModified();
543     }
544     else
545     {
546       if (jobArgset != null && jobArgset.size() > 0)
547       {
548         curSetName = "Supplied Settings";
549         isUserPreset = false;
550         updateTable(p, jobArgset);
551         setName.setSelectedItem(curSetName);
552         updateButtonDisplay();
553       }
554       else
555       {
556         curSetName = null;
557         reInitDialog(null);
558       }
559     }
560     settingDialog = false;
561   }
562
563   private void updateTable(WsParamSetI p, List<ArgumentI> jobArgset)
564   {
565     boolean setDefaultParams = false;
566     if (lastParmSet == null)
567     {
568       isUserPreset = false;
569       // First call - so provide Service default settings
570       setName.setSelectedItem(lastSetName = SVC_DEF);
571     }
572     if (p == null && SVC_DEF.equals("" + setName.getSelectedItem()))
573     {
574       // indicate that service defaults should be set if available
575       setDefaultParams = true;
576     }
577     // populate table from default parameter set.
578     List<ArgumentI> args = paramStore.getServiceParameters();
579
580     // split to params and required arguments
581     {
582       int cw = 0;
583       boolean optset = false;
584       for (ArgumentI myarg : args)
585       {
586         // Ideally, Argument would implement isRequired !
587         if (myarg instanceof ParameterI)
588         {
589           ParameterI parm = (ParameterI) myarg;
590           opanp.addParameter(parm).validate();
591         }
592         else
593         {
594           if (myarg instanceof OptionI)
595           {
596             OptionI opt = (OptionI) myarg;
597             OptionBox ob = opanp.addOption(opt);
598             ob.resetToDefault(setDefaultParams);
599             if (maxOptWidth < ob.getPreferredSize().width)
600             {
601               maxOptWidth = ob.getPreferredSize().width;
602             }
603             ob.validate();
604             cw += ob.getPreferredSize().width + 5;
605           }
606           else
607           {
608             System.err.println("Ignoring unknown service argument type "
609                     + myarg.getClass().getName());
610           }
611         }
612       }
613       args = null; // no more args to process.
614     }
615     if (p != null)
616     {
617       isUserPreset = false;
618       // initialise setname
619       setName.setSelectedItem(lastSetName = p.getName());
620       setDescr.setText(lastDescrText = p.getDescription());
621       // TODO - URL link
622       try
623       {
624         args = p.getArguments();
625       } catch (Exception e)
626       {
627         e.printStackTrace();
628       }
629       // TODO: check if args should be unselected prior to resetting using the
630       // preset
631     }
632
633     if (jobArgset != null)
634     {
635       argSetModified(jobArgset, true);
636       args = jobArgset;
637     }
638     // get setargs from current object
639     if (args != null)
640     {
641       for (ArgumentI arg : args)
642       {
643         if (arg instanceof ParameterI)
644         {
645           opanp.setParameter((ParameterI) arg);
646         }
647         else
648         {
649           if (arg instanceof OptionI)
650           {
651             // System.out.println("Setting option "
652             // + System.identityHashCode(arg) + ":" + arg.getName()
653             // + " with " + arg.getDefaultValue());
654             opanp.selectOption((OptionI) arg, arg.getValue());
655           }
656         }
657
658       }
659     }
660
661     refreshParamLayout();
662     revalidate();
663   }
664
665   private boolean isModified()
666   {
667     return modifiedElements.size() > 0;
668   }
669
670   /**
671    * reset gui and modification state settings
672    */
673   private void initArgSetModified()
674   {
675     curSetName = null;
676     modifiedElements.clear();
677     updateButtonDisplay();
678   }
679
680   private void updateButtonDisplay()
681   {
682     boolean _update = false, _create = false, _delete = false,
683             _revert = false;
684     if (modifiedElements.size() > 0)
685     {
686       // set modified
687       _revert = true;
688       _update = isUserPreset; // can only update user presets
689       if (!isUserPreset || modifiedElements.containsKey(setName))
690       {
691         // name modified - can create new preset
692         _create = true;
693       }
694     }
695     else
696     {
697       // set unmodified
698     }
699     // can still delete a user preset
700     _delete = isUserPreset;
701
702     createpref.setVisible(_create);
703     updatepref.setVisible(_update);
704     deletepref.setVisible(_delete);
705     revertpref.setVisible(_revert);
706     validate();
707   }
708
709   @Override
710   public void argSetModified(Object modifiedElement, boolean b)
711   {
712     if (settingDialog)
713     {
714       return;
715     }
716     if (!b)
717     {
718       modifiedElements.remove(modifiedElement);
719     }
720     else
721     {
722       if (b && modifiedElement == setName
723               && modifiedElements.contains(modifiedElement))
724       {
725         // HACK! prevents iteration on makeSetNameValid
726         b = false;
727       }
728       modifiedElements.put(modifiedElement, modifiedElement);
729     }
730     // set mod status based on presence of elements in table
731     if (b && modifiedElements.size() > 0)
732     {
733       makeSetNameValid(!isUserPreset);
734       setNamePanel.revalidate();
735     }
736     updateButtonDisplay();
737   }
738
739   private boolean isServicePreset(String selectedItem)
740   {
741     return selectedItem.equals(SVC_DEF)
742             || servicePresets.containsKey(selectedItem);
743   }
744
745   /**
746    * check if the current set name is a valid set name for saving, if not, then
747    * fix it.
748    */
749   private void makeSetNameValid(boolean newuserset)
750   {
751     boolean stn = settingDialog;
752     boolean renamed = false;
753     settingDialog = true;
754     String nm = (curSetName != null ? curSetName
755             : (String) setName.getSelectedItem());
756     // check if the name is reserved - if it is, rename it.
757     if (isServicePreset(nm))
758     {
759       nm = "User " + nm;
760       renamed = true;
761     }
762     String tnm = nm;
763     if (newuserset)
764     {
765       int i = 0;
766       while (paramStore.getPreset(tnm) != null)
767       {
768         tnm = nm + " (" + (++i) + ")";
769         renamed = true;
770       }
771       if (i > 0)
772       {
773         nm = tnm;
774       }
775     }
776
777     boolean makeupdate = false;
778     // sync the gui with the preset database
779     for (int i = 0, iS = setName.getItemCount(); i < iS; i++)
780     {
781       String snm = setName.getItemAt(i);
782       if (snm.equals(nm))
783       {
784         makeupdate = true;
785         // setName.setSelectedIndex(i);
786       }
787     }
788     if (!makeupdate)
789     {
790       setName.addItem(curSetName = nm);
791       setName.setSelectedItem(curSetName);
792     }
793     if (renamed)
794     {
795       settingDialog = false; // we need this name change to be registered.
796       argSetModified(setName, renamed);
797     }
798     settingDialog = stn;
799   }
800
801   /**
802    * Rebuilds the Options and Parameters panels
803    */
804   @Override
805   public void refreshParamLayout()
806   {
807     final int rightMargin = 40;
808     final int availableWidth = paramPane.getViewport().getSize().width
809             - rightMargin
810             - optionsPanel.getBorder().getBorderInsets(optionsPanel).left
811             + optionsPanel.getBorder().getBorderInsets(optionsPanel).right;
812
813     if (opanp.getOptSet().size() > 0)
814     {
815       int hgap = 5;
816       int currentWidth = hgap;
817
818       /*
819        * layout constraint 'nogrid' prevents vertical column alignment,
820        * allowing controls to flow without extra space inserted to align
821        */
822       optionsPanel.setLayout(new MigLayout("nogrid", "", ""));
823       optionsPanel.removeAll();
824       JPanel lastAdded = null;
825
826       /*
827        * add each control in turn; if adding would overflow the right margin,
828        * remove and re-add the previous parameter with "wrap" (after) 
829        * in order to start a new row
830        */
831       for (OptionBox pbox : opanp.getOptSet().values())
832       {
833         pbox.validate();
834         int boxWidth = pbox.getSize().width;
835         currentWidth += boxWidth + hgap;
836         boolean wrapAfterLast = currentWidth > availableWidth
837                 && lastAdded != null;
838         // System.out.println(String.format(
839         // "%s width=%d, paneWidth=%d, currentWidth=%d, wrapAfterLast=%s",
840         // pbox.toString(), boxWidth, panewidth, currentWidth,
841         // wrapAfterLast));
842         if (wrapAfterLast)
843         {
844           optionsPanel.remove(lastAdded);
845           optionsPanel.add(lastAdded, "wrap");
846           currentWidth = hgap + boxWidth;
847         }
848         optionsPanel.add(pbox);
849         lastAdded = pbox;
850       }
851       optionsPanel.revalidate();
852     }
853     else
854     {
855       optionsPanel.setVisible(false);
856     }
857
858     if (opanp.getParamSet().size() > 0)
859     {
860       paramsPanel.removeAll();
861       paramsPanel.setLayout(new MigLayout("", "", ""));
862       int hgap = 5;
863       int currentWidth = hgap;
864
865       JPanel lastAdded = null;
866       for (ParamBox pbox : opanp.getParamSet().values())
867       {
868         pbox.validate();
869         int boxWidth = pbox.getSize().width;
870         currentWidth += boxWidth + hgap;
871         boolean wrapAfterLast = currentWidth > availableWidth
872                 && lastAdded != null;
873         if (wrapAfterLast)
874         {
875           paramsPanel.remove(lastAdded);
876           paramsPanel.add(lastAdded, "wrap");
877           currentWidth = pbox.getSize().width + hgap;
878         }
879         paramsPanel.add(pbox);
880         lastAdded = pbox;
881       }
882
883       /*
884        * s = 2 * sep; for (ParamBox pbox : opanp.getParamSet().values()) {
885        * pbox.validate(); s += sep +
886        * pbox.getPreferredSize().height+pbox.getBorder
887        * ().getBorderInsets(pbox).bottom; }
888        * 
889        * // paramList.setPreferredSize(new Dimension(w, s));
890        * os+=s+2*sep+paramList
891        * .getBorder().getBorderInsets(paramList).bottom+paramList
892        * .getBorder().getBorderInsets(paramList).top;
893        */
894       paramsPanel.revalidate();
895     }
896     else
897     {
898       paramsPanel.setVisible(false);
899     }
900     // TODO: waste some time trying to eliminate any unnecessary .validate calls
901     // here
902     // System.out.println("Size will be : "+w+","+os);
903     // optsAndparams.setPreferredSize(null);
904     // paramPane.getViewport().setView(optsAndparams);
905     paramPane.getViewport().setAutoscrolls(true);
906     paramPane.revalidate();
907     revalidate();
908   }
909   public boolean isServiceDefaults()
910   {
911     return (!isModified()
912             && (lastParmSet != null && lastParmSet.equals(SVC_DEF)));
913   }
914
915   public List<ArgumentI> getJobParams()
916   {
917     return opanp.getCurrentSettings();
918   }
919
920   /*
921    * Hashtable<String, Object[]> editedParams = new Hashtable<String,
922    * Object[]>();
923    * 
924    * store the given parameters in the user parameter set database.
925    * 
926    * @param storeSetName - lastParmSet
927    * 
928    * @param descr - setDescr.getText()
929    * 
930    * @param jobParams - getJobParams()
931    * 
932    * private void _storeUserPreset(String storeSetName, String descr,
933    * List<ArgumentI> jobParams) { // this is a simple hash store. Object[] pset;
934    * editedParams.put(storeSetName, pset = new Object[3]); pset[0] =
935    * storeSetName; pset[1] = descr; pset[2] = jobParams; // writeParam("Saving "
936    * + storeSetName + ": ", jobParams); }
937    * 
938    * private void writeParam(String nm, List<ArgumentI> params) { for (ArgumentI
939    * p : params) { System.out.println(nm + ":" + System.identityHashCode(p) +
940    * " Name: " + p.getName() + " Value: " + p.getDefaultValue()); } }
941    * 
942    * private Object[] _getUserPreset(String setName) { Object[] pset =
943    * editedParams.get(setName); // if (pset != null) // writeParam("Retrieving "
944    * + setName + ": ", (List<Argument>) pset[2]); return pset; }
945    * 
946    * * remove the given user preset from the preset stash
947    * 
948    * @param setName
949    * 
950    * private void _deleteUserPreset(String setName) {
951    * editedParams.remove(setName); }
952    */
953
954   private void syncSetNamesWithStore()
955   {
956     int n = 0;
957     // remove any set names in the drop down menu that aren't either a reserved
958     // setting, or a user defined or service preset.
959     Vector<String> items = new Vector<>();
960     while (n < setName.getItemCount())
961     {
962       String item = setName.getItemAt(n);
963       if (!item.equals(SVC_DEF) && !paramStore.presetExists(item))
964       {
965         setName.removeItemAt(n);
966       }
967       else
968       {
969         items.addElement(item);
970         n++;
971       }
972     }
973     if (!items.contains(SVC_DEF))
974     {
975       setName.addItem(SVC_DEF);
976     }
977     for (WsParamSetI upn : paramStore.getPresets())
978     {
979       if (!items.contains(upn.getName()))
980       {
981         setName.addItem(upn.getName());
982       }
983     }
984   }
985
986   /**
987    * true if lastParmSet is a user preset
988    */
989   boolean isUserPreset = false;
990
991   private void reInitDialog(String nextPreset)
992   {
993     settingDialog = true;
994     // updateTable(null,null); // first reset to defaults
995     WsParamSetI pset = null;
996     if (nextPreset != null && nextPreset.length() > 0)
997     {
998       pset = paramStore.getPreset(nextPreset);
999     }
1000     if (pset != null)
1001     {
1002       if (pset.isModifiable())
1003       {
1004         isUserPreset = true;
1005         setDescr.setText(pset.getDescription());
1006         updateTable(null, pset.getArguments());
1007         lastParmSet = nextPreset;
1008       }
1009       else
1010       {
1011         isUserPreset = false;
1012         setDescr.setText("");
1013         // must be a default preset from service
1014         updateTable(pset, null);
1015         lastParmSet = nextPreset;
1016       }
1017     }
1018     else
1019     {
1020       isUserPreset = false;
1021       // Service defaults
1022       setDescr.setText("");
1023       updateTable(null, null);
1024       lastParmSet = SVC_DEF;
1025     }
1026
1027     initArgSetModified();
1028     syncSetNamesWithStore();
1029     setName.setSelectedItem(lastParmSet);
1030     setNamePanel.validate();
1031     validate();
1032     settingDialog = false;
1033   }
1034
1035   /**
1036    * Rebuild the AlignFrame web service menus (after add/delete of a preset
1037    * option).
1038    */
1039   protected void updateWebServiceMenus()
1040   {
1041     if (Desktop.instance == null)
1042     {
1043       return;
1044     }
1045     for (AlignFrame alignFrame : Desktop.getAlignFrames())
1046     {
1047       alignFrame.buildWebServicesMenu();
1048     }
1049   }
1050
1051   String curSetName = null;
1052
1053   @Override
1054   public void itemStateChanged(ItemEvent e)
1055   {
1056     if (e.getSource() == setName
1057             && e.getStateChange() == ItemEvent.SELECTED)
1058     {
1059       final String setname = (String) setName.getSelectedItem();
1060       // System.out.println("Item state changed for " + setname
1061       // + " (handling ? " + !settingDialog + ")");
1062       if (settingDialog)
1063       {
1064         // ignore event
1065         return;
1066       }
1067       if (setname == null)
1068       {
1069         return;
1070       }
1071       javax.swing.SwingUtilities.invokeLater(new Runnable()
1072       {
1073         @Override
1074         public void run()
1075         {
1076           doPreferenceComboStateChange(setname);
1077         }
1078       });
1079     }
1080   }
1081
1082   private void doPreferenceComboStateChange(String setname)
1083   {
1084     // user has selected a different item from combo-box
1085     if (isModified())
1086     {
1087       String lsetname = (curSetName != null) ? curSetName : lastParmSet;
1088       if (lsetname.equals(setname))
1089       {
1090         // setname was just edited - so ignore this event.
1091         return;
1092       }
1093       settingDialog = true;
1094       System.out.println("Prompting to save " + lsetname);
1095       if (JvOptionPane.showConfirmDialog(this, "Parameter set '" + lsetname
1096               + "' is modifed, and your changes will be lost.\nReally change preset ?",
1097               "Warning: Unsaved Changes",
1098               JvOptionPane.OK_CANCEL_OPTION) != JvOptionPane.OK_OPTION)
1099       {
1100         // revert the combobox to the current item
1101         settingDialog = true;
1102         setName.setSelectedItem(lsetname);
1103         settingDialog = false;
1104         // and leave.
1105         return;
1106         // System.out.println("Saving for " + lsetname);
1107         // _storeCurrentPreset(lsetname);
1108
1109       }
1110     }
1111     settingDialog = true;
1112     reInitDialog(setname);
1113     settingDialog = false;
1114
1115   }
1116
1117   /**
1118    * store current settings as given name. You should then reset gui.
1119    * 
1120    * @param curSetName2
1121    */
1122   private void _storeCurrentPreset(String curSetName2)
1123   {
1124     paramStore.storePreset(curSetName2, setDescr.getText(), getJobParams());
1125   }
1126
1127   private void _updatePreset(String lastParmSet2, String curname)
1128   {
1129     paramStore.updatePreset(lastParmSet2, curname, setDescr.getText(),
1130             getJobParams());
1131
1132   }
1133
1134   /**
1135    * last saved name for this user preset
1136    */
1137   String lastSetName = null;
1138
1139   /**
1140    * last saved value of the description text for this user preset
1141    */
1142   String lastDescrText = null;
1143
1144   @Override
1145   public void actionPerformed(ActionEvent e)
1146   {
1147     if (e.getSource() instanceof Component)
1148     {
1149       Component src = (Component) e.getSource();
1150       if (src.getParent() == setName)
1151       {
1152         // rename any existing records we know about for this set.
1153         String newname = e.getActionCommand().trim();
1154         String msg = null;
1155         if (isServicePreset(newname))
1156         {
1157           final String oldname = curSetName != null ? curSetName
1158                   : lastParmSet;
1159           final Component ourframe = this;
1160           settingDialog = true;
1161           setName.getEditor().setItem(oldname);
1162           settingDialog = false;
1163           javax.swing.SwingUtilities.invokeLater(new Runnable()
1164           {
1165             @Override
1166             public void run()
1167             {
1168               JvOptionPane.showMessageDialog(ourframe,
1169                       MessageManager.getString(
1170                               "label.invalid_name_preset_exists"),
1171                       MessageManager.getString("label.invalid_name"),
1172                       JvOptionPane.WARNING_MESSAGE);
1173             }
1174           });
1175
1176           return;
1177         }
1178         curSetName = newname;
1179         System.err.println("New name for user setting " + curSetName
1180                 + " (was " + setName.getSelectedItem() + ")");
1181         if (curSetName.equals(setName.getSelectedItem()))
1182         {
1183           curSetName = null;
1184         }
1185         if (curSetName != null)
1186         {
1187           argSetModified(setName, true);
1188           return;
1189         }
1190
1191       }
1192     }
1193   }
1194
1195   private void checkDescrModified()
1196   {
1197     if (!settingDialog)
1198     {
1199
1200       argSetModified(setDescr,
1201               (lastDescrText == null
1202                       ? setDescr.getText().trim().length() > 0
1203                       : !setDescr.getText().equals(lastDescrText)));
1204
1205     }
1206   }
1207
1208   @Override
1209   public void insertUpdate(DocumentEvent e)
1210   {
1211     checkDescrModified();
1212   }
1213
1214   @Override
1215   public void removeUpdate(DocumentEvent e)
1216   {
1217     checkDescrModified();
1218   }
1219
1220   @Override
1221   public void changedUpdate(DocumentEvent e)
1222   {
1223     checkDescrModified();
1224   }
1225
1226   /**
1227    * 
1228    * @return null or the service preset selected by the user
1229    */
1230   public WsParamSetI getPreset()
1231   {
1232     if (isUserPreset || isModified()
1233             || (lastParmSet != null && lastParmSet.equals(SVC_DEF)))
1234     {
1235       return null;
1236     }
1237     else
1238     {
1239       return paramStore.getPreset(lastParmSet);
1240     }
1241   }
1242 }