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