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