JAL-2629 update spikes/mungo to latest
[jalview.git] / src / jalview / gui / OptsAndParamsPage.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.util.MessageManager;
24 import jalview.ws.params.ArgumentI;
25 import jalview.ws.params.OptionI;
26 import jalview.ws.params.ParameterI;
27 import jalview.ws.params.ValueConstrainI;
28 import jalview.ws.params.ValueConstrainI.ValueType;
29 import jalview.ws.params.simple.LogarithmicParameter;
30 import jalview.ws.params.simple.StringParameter;
31
32 import java.awt.BorderLayout;
33 import java.awt.Color;
34 import java.awt.Component;
35 import java.awt.Dimension;
36 import java.awt.FlowLayout;
37 import java.awt.Font;
38 import java.awt.Rectangle;
39 import java.awt.event.ActionEvent;
40 import java.awt.event.ActionListener;
41 import java.awt.event.KeyAdapter;
42 import java.awt.event.KeyEvent;
43 import java.awt.event.MouseEvent;
44 import java.awt.event.MouseListener;
45 import java.net.URL;
46 import java.util.ArrayList;
47 import java.util.LinkedHashMap;
48 import java.util.List;
49 import java.util.Map;
50
51 import javax.swing.JButton;
52 import javax.swing.JCheckBox;
53 import javax.swing.JComboBox;
54 import javax.swing.JComponent;
55 import javax.swing.JLabel;
56 import javax.swing.JMenuItem;
57 import javax.swing.JPanel;
58 import javax.swing.JPopupMenu;
59 import javax.swing.JScrollPane;
60 import javax.swing.JSlider;
61 import javax.swing.JTextArea;
62 import javax.swing.JTextField;
63 import javax.swing.border.TitledBorder;
64 import javax.swing.event.ChangeEvent;
65 import javax.swing.event.ChangeListener;
66
67 import net.miginfocom.swing.MigLayout;
68
69 /**
70  * GUI generator/manager for options and parameters. Originally abstracted from
71  * the WsJobParameters dialog box.
72  * 
73  * @author jprocter
74  * 
75  */
76 public class OptsAndParamsPage
77 {
78   public static final int PARAM_WIDTH = 340;
79
80   public static final int PARAM_HEIGHT = 150;
81
82   public static final int PARAM_CLOSEDHEIGHT = 80;
83
84   URL linkImageURL = getClass().getResource("/images/link.gif");
85
86   Map<String, OptionBox> optSet = new LinkedHashMap<>();
87
88   Map<String, ParamBox> paramSet = new LinkedHashMap<>();
89
90   /*
91    * compact or verbose style parameters
92    */
93   boolean compact = false;
94
95   OptsParametersContainerI poparent;
96
97   /**
98    * A class that models a panel rendering a single option (checkbox or choice
99    * list)
100    */
101   public class OptionBox extends JPanel
102           implements MouseListener, ActionListener
103   {
104     JCheckBox enabled = new JCheckBox();
105
106     final URL finfo;
107
108     boolean hasLink = false;
109
110     boolean initEnabled = false;
111
112     String initVal = null;
113
114     OptionI option;
115
116     JLabel optlabel = new JLabel();
117
118     JComboBox<String> val = new JComboBox<>();
119
120     public OptionBox(OptionI opt)
121     {
122       option = opt;
123       setLayout(new FlowLayout(FlowLayout.LEFT));
124       enabled.setSelected(opt.isRequired()); // TODO: lock required options
125       enabled.setFont(new Font("Verdana", Font.PLAIN, 11));
126       enabled.setText("");
127       enabled.setText(opt.getName());
128       enabled.addActionListener(this);
129       finfo = option.getFurtherDetails();
130       String desc = opt.getDescription();
131       if (finfo != null)
132       {
133         hasLink = true;
134
135         enabled.setToolTipText(JvSwingUtils.wrapTooltip(true,
136                 ((desc == null || desc.trim().length() == 0)
137                         ? MessageManager.getString(
138                                 "label.opt_and_params_further_details")
139                         : desc) + "<br><img src=\"" + linkImageURL
140                         + "\"/>"));
141         enabled.addMouseListener(this);
142       }
143       else
144       {
145         if (desc != null && desc.trim().length() > 0)
146         {
147           enabled.setToolTipText(
148                   JvSwingUtils.wrapTooltip(true, opt.getDescription()));
149         }
150       }
151       add(enabled);
152       for (String str : opt.getPossibleValues())
153       {
154         val.addItem(str);
155       }
156       val.setSelectedItem(opt.getValue());
157       if (opt.getPossibleValues().size() > 1 || opt.isRequired())
158       {
159         val.addActionListener(this);
160         add(val);
161       }
162       // TODO: add actionListeners for popup (to open further info),
163       // and to update list of parameters if an option is enabled
164       // that takes a value. JBPNote: is this TODO still valid ?
165       setInitialValue();
166     }
167
168     @Override
169     public void actionPerformed(ActionEvent e)
170     {
171       if (e.getSource() != enabled)
172       {
173         enabled.setSelected(true);
174       }
175       checkIfModified();
176     }
177
178     private void checkIfModified()
179     {
180       boolean notmod = (initEnabled == enabled.isSelected());
181       if (enabled.isSelected())
182       {
183         if (initVal != null)
184         {
185           notmod &= initVal.equals(val.getSelectedItem());
186         }
187         else
188         {
189           // compare against default service setting
190           notmod &= option.getValue() == null
191                   || option.getValue().equals(val.getSelectedItem());
192         }
193       }
194       else
195       {
196         notmod &= (initVal != null) ? initVal.equals(val.getSelectedItem())
197                 : val.getSelectedItem() != initVal;
198       }
199       poparent.argSetModified(this, !notmod);
200     }
201
202     public OptionI getOptionIfEnabled()
203     {
204       if (!enabled.isSelected())
205       {
206         return null;
207       }
208       OptionI opt = option.copy();
209       if (opt.getPossibleValues() != null
210               && opt.getPossibleValues().size() == 1)
211       {
212         // Hack to make sure the default value for an enabled option with only
213         // one value is actually returned
214         opt.setValue(opt.getPossibleValues().get(0));
215       }
216       if (val.getSelectedItem() != null)
217       {
218         opt.setValue((String) val.getSelectedItem());
219       }
220       else
221       {
222         if (option.getValue() != null)
223         {
224           opt.setValue(option.getValue());
225         }
226       }
227       return opt;
228     }
229
230     @Override
231     public void mouseClicked(MouseEvent e)
232     {
233       if (e.isPopupTrigger()) // for Windows
234       {
235         showUrlPopUp(this, finfo.toString(), e.getX(), e.getY());
236       }
237     }
238
239     @Override
240     public void mouseEntered(MouseEvent e)
241     {
242     }
243
244     @Override
245     public void mouseExited(MouseEvent e)
246     {
247     }
248
249     @Override
250     public void mousePressed(MouseEvent e)
251     {
252       if (e.isPopupTrigger()) // Mac
253       {
254         showUrlPopUp(this, finfo.toString(), e.getX(), e.getY());
255       }
256     }
257
258     @Override
259     public void mouseReleased(MouseEvent e)
260     {
261     }
262
263     public void resetToDefault(boolean setDefaultParams)
264     {
265       enabled.setSelected(false);
266       if (option.isRequired()
267               || (setDefaultParams && option.getValue() != null))
268       {
269         // Apply default value
270         selectOption(option, option.getValue());
271       }
272     }
273
274     public void setInitialValue()
275     {
276       initEnabled = enabled.isSelected();
277       if (option.getPossibleValues() != null
278               && option.getPossibleValues().size() > 1)
279       {
280         initVal = (String) val.getSelectedItem();
281       }
282       else
283       {
284         initVal = (initEnabled) ? (String) val.getSelectedItem() : null;
285       }
286     }
287
288     /**
289      * toString representation for identification in the debugger only
290      */
291     @Override
292     public String toString()
293     {
294       return option == null ? super.toString() : option.toString();
295     }
296
297   }
298
299   /**
300    * A class that models a panel rendering a single parameter
301    */
302   public class ParamBox extends JPanel
303           implements ChangeListener, ActionListener, MouseListener
304   {
305     /*
306      * parameter values (or their logs) are multiplied by this
307      * scaling factor to ensure an integer range for the slider
308      */
309     private int sliderScaleFactor = 1;
310
311     boolean isLogarithmicParameter;
312
313     boolean isChoiceParameter;
314
315     boolean isIntegerParameter;
316
317     boolean adjusting;
318
319     JComboBox<String> choicebox;
320
321     JPanel controlsPanel = new JPanel();
322
323     boolean descriptionIsVisible = false;
324
325     JScrollPane descPanel = new JScrollPane();
326
327     final URL finfo;
328
329     Object lastVal;
330
331     ParameterI parameter;
332
333     final OptsParametersContainerI pmdialogbox;
334
335     JPanel settingPanel = new JPanel();
336
337     JSlider slider;
338
339     JTextArea descriptionText = new JTextArea();
340
341     ValueConstrainI validator;
342
343     JTextField valueField;
344
345     private String descTooltip;
346
347     public ParamBox(final OptsParametersContainerI paramContainer,
348             ParameterI parm)
349     {
350       pmdialogbox = paramContainer;
351       finfo = parm.getFurtherDetails();
352       validator = parm.getValidValue();
353       parameter = parm;
354
355       isLogarithmicParameter = parm instanceof LogarithmicParameter;
356
357       if (validator != null)
358       {
359         ValueType type = validator.getType();
360         isIntegerParameter = type == ValueType.Integer;
361
362         /*
363          * ensure slider has an integer range corresponding to
364          * the min-max range of the parameter
365          */
366         if (validator.getMin() != null && !isIntegerParameter)
367         {
368           double min = validator.getMin().doubleValue();
369           double max = validator.getMax().doubleValue();
370           if (isLogarithmicParameter)
371           {
372             min = Math.log(min);
373             max = Math.log(max);
374           }
375           sliderScaleFactor = (int) (1000000 / (max - min));
376           // todo scaleMin, scaleMax could also be final fields
377         }
378       }
379       else
380       {
381         isChoiceParameter = parameter.getPossibleValues() != null;
382       }
383
384       if (compact)
385       {
386         addCompactParameter(parm);
387       }
388       else
389       {
390         addExpandableParam(parm);
391       }
392     }
393
394     /**
395      * Adds a 'compact' format parameter, with any help text shown as a tooltip
396      * 
397      * @param parm
398      */
399     private void addCompactParameter(ParameterI parm)
400     {
401       setLayout(new MigLayout("", "[][grow]"));
402       String ttipText = null;
403
404       controlsPanel.setLayout(new BorderLayout());
405
406       if (parm.getDescription() != null
407               && parm.getDescription().trim().length() > 0)
408       {
409         ttipText = (JvSwingUtils.wrapTooltip(true,
410                 parm.getDescription() + (finfo != null ? "<br><img src=\""
411                         + linkImageURL + "\"/>"
412                         + MessageManager.getString(
413                                 "label.opt_and_params_further_details")
414                         : "")));
415       }
416
417       JvSwingUtils.addtoLayout(this, ttipText, new JLabel(parm.getName()),
418               controlsPanel, "");
419       updateControls(parm);
420       validate();
421     }
422
423     /**
424      * Adds an 'expanded' format parameter, with any help shown in a panel that
425      * may be shown or hidden
426      * 
427      * @param parm
428      */
429     private void addExpandableParam(ParameterI parm)
430     {
431       setPreferredSize(new Dimension(PARAM_WIDTH, PARAM_CLOSEDHEIGHT));
432       setBorder(new TitledBorder(parm.getName()));
433       setLayout(null);
434       descriptionText.setFont(new Font("Verdana", Font.PLAIN, 11));
435       descriptionText.setBackground(getBackground());
436
437       descriptionText.setEditable(false);
438       descPanel.getViewport().setView(descriptionText);
439
440       descPanel.setVisible(false);
441
442       JPanel firstrow = new JPanel();
443       firstrow.setLayout(null);
444       controlsPanel.setLayout(new BorderLayout());
445       controlsPanel.setBounds(new Rectangle(39, 10, PARAM_WIDTH - 70,
446               PARAM_CLOSEDHEIGHT - 50));
447       firstrow.add(controlsPanel);
448       firstrow.setBounds(new Rectangle(10, 20, PARAM_WIDTH - 30,
449               PARAM_CLOSEDHEIGHT - 30));
450
451       if (parm.getDescription() != null
452               && parm.getDescription().trim().length() > 0)
453       {
454         addExpandableHelp(firstrow, parm);
455       }
456       add(firstrow);
457       validator = parm.getValidValue();
458       parameter = parm;
459       if (validator != null)
460       {
461         isIntegerParameter = validator.getType() == ValueType.Integer;
462       }
463       else
464       {
465         if (parameter.getPossibleValues() != null)
466         {
467           isChoiceParameter = true;
468         }
469       }
470       updateControls(parm);
471       descPanel.setBounds(new Rectangle(10, PARAM_CLOSEDHEIGHT,
472               PARAM_WIDTH - 20, PARAM_HEIGHT - PARAM_CLOSEDHEIGHT - 5));
473       add(descPanel);
474       validate();
475     }
476
477     /**
478      * Adds a button which can be clicked to show or hide help text
479      * 
480      * @param container
481      * @param param
482      */
483     protected void addExpandableHelp(JPanel container, ParameterI param)
484     {
485       JButton showDescBtn = new JButton("+");
486       showDescBtn.setFont(new Font("Verdana", Font.PLAIN, 8));
487       if (finfo != null)
488       {
489         descTooltip = JvSwingUtils.wrapTooltip(true,
490                 MessageManager.formatMessage(
491                         "label.opt_and_params_show_brief_desc_image_link",
492                         new String[]
493                         { linkImageURL.toExternalForm() }));
494         showDescBtn.addMouseListener(this);
495       }
496       else
497       {
498         descTooltip = JvSwingUtils.wrapTooltip(true, MessageManager
499                 .getString("label.opt_and_params_show_brief_desc"));
500       }
501       showDescBtn.setToolTipText(descTooltip);
502       showDescBtn.addActionListener(new ActionListener()
503       {
504         @Override
505         public void actionPerformed(ActionEvent e)
506         {
507           descriptionIsVisible = !descriptionIsVisible;
508           showDescBtn.setText(descriptionIsVisible ? "-" : "+");
509           showDescBtn.setToolTipText(
510                   descriptionIsVisible ? null : descTooltip);
511           descPanel.setVisible(descriptionIsVisible);
512           descPanel.getVerticalScrollBar().setValue(0);
513           ParamBox.this.setPreferredSize(new Dimension(PARAM_WIDTH,
514                   (descriptionIsVisible) ? PARAM_HEIGHT
515                           : PARAM_CLOSEDHEIGHT));
516           ParamBox.this.validate();
517           pmdialogbox.refreshParamLayout();
518         }
519       });
520       descriptionText.setWrapStyleWord(true);
521       descriptionText.setLineWrap(true);
522       descriptionText.setColumns(32);
523       descriptionText.setText(param.getDescription());
524       showDescBtn.setBounds(new Rectangle(10, 10, 16, 16));
525       container.add(showDescBtn);
526     }
527
528     @Override
529     public void actionPerformed(ActionEvent e)
530     {
531       if (adjusting)
532       {
533         return;
534       }
535       if (!isChoiceParameter)
536       {
537         updateSliderFromValueField();
538       }
539       checkIfModified();
540     }
541
542     /**
543      * Checks whether the value of this parameter has been changed and notifies
544      * the parent page accordingly
545      */
546     private void checkIfModified()
547     {
548       Object newValue = updateSliderFromValueField();
549       boolean modified = true;
550       if (newValue.getClass() == lastVal.getClass())
551       {
552         modified = !newValue.equals(lastVal);
553       }
554       pmdialogbox.argSetModified(this, modified);
555     }
556
557     @Override
558     public int getBaseline(int width, int height)
559     {
560       return 0;
561     }
562
563     // from
564     // http://stackoverflow.com/questions/2743177/top-alignment-for-flowlayout
565     // helpful hint of using the Java 1.6 alignBaseLine property of FlowLayout
566     @Override
567     public Component.BaselineResizeBehavior getBaselineResizeBehavior()
568     {
569       return Component.BaselineResizeBehavior.CONSTANT_ASCENT;
570     }
571
572     public ParameterI getParameter()
573     {
574       ParameterI prm = parameter.copy();
575       if (isChoiceParameter)
576       {
577         prm.setValue((String) choicebox.getSelectedItem());
578       }
579       else
580       {
581         prm.setValue(valueField.getText());
582       }
583       return prm;
584     }
585
586     public void init()
587     {
588       // reset the widget's initial value.
589       lastVal = null;
590     }
591
592     @Override
593     public void mouseClicked(MouseEvent e)
594     {
595       if (e.isPopupTrigger()) // for Windows
596       {
597         showUrlPopUp(this, finfo.toString(), e.getX(), e.getY());
598       }
599     }
600
601     @Override
602     public void mouseEntered(MouseEvent e)
603     {
604     }
605
606     @Override
607     public void mouseExited(MouseEvent e)
608     {
609     }
610
611     @Override
612     public void mousePressed(MouseEvent e)
613     {
614       if (e.isPopupTrigger()) // for Mac
615       {
616         showUrlPopUp(this, finfo.toString(), e.getX(), e.getY());
617       }
618     }
619
620     @Override
621     public void mouseReleased(MouseEvent e)
622     {
623     }
624
625     @Override
626     public void stateChanged(ChangeEvent e)
627     {
628       if (!adjusting)
629       {
630         if (!isLogarithmicParameter)
631         {
632           /*
633            * set (int or float formatted) text field value
634            */
635           valueField.setText(isIntegerParameter
636                   ? String.valueOf(slider.getValue())
637                   : formatDouble(
638                           slider.getValue() / (float) sliderScaleFactor));
639         }
640         else
641         {
642           double value = Math.pow(Math.E,
643                   slider.getValue() / (double) sliderScaleFactor);
644           valueField.setText(formatDouble(value));
645         }
646         checkIfModified();
647       }
648     }
649
650     /**
651      * Answers the value formatted as a string to 3 decimal places - in
652      * scientific notation if the value is less than 0.001
653      * 
654      * @param value
655      * @return
656      */
657     String formatDouble(double value)
658     {
659       String format = value < 0.001 ? "%3.1E" : "%3.3f";
660       return String.format(format, value);
661     }
662
663     /**
664      * Formats a number as integer or float (3dp) or scientific notation (1dp)
665      * 
666      * @param n
667      * @return
668      */
669     String formatNumber(Number n)
670     {
671       return n instanceof Integer ? String.valueOf(n.intValue())
672               : formatDouble(n.doubleValue());
673     }
674
675     void updateControls(ParameterI parm)
676     {
677       adjusting = true;
678       boolean init = (choicebox == null && valueField == null);
679       if (init)
680       {
681         if (isChoiceParameter)
682         {
683           choicebox = new JComboBox<>();
684           choicebox.addActionListener(this);
685           controlsPanel.add(choicebox, BorderLayout.CENTER);
686         }
687         else
688         {
689           slider = new JSlider();
690           slider.addChangeListener(this);
691           int cols = parm instanceof StringParameter ? 20 : 0;
692           valueField = new JTextField(cols);
693           valueField.addActionListener(this);
694           valueField.addKeyListener(new KeyAdapter()
695           {
696             @Override
697             public void keyReleased(KeyEvent e)
698             {
699               if (e.isActionKey())
700               {
701                 if (valueField.getText().trim().length() > 0)
702                 {
703                   actionPerformed(null);
704                 }
705               }
706             }
707           });
708           valueField.setPreferredSize(new Dimension(65, 25));
709           controlsPanel.add(slider, BorderLayout.WEST);
710           controlsPanel.add(valueField, BorderLayout.EAST);
711         }
712       }
713
714       if (parm != null)
715       {
716         if (isChoiceParameter)
717         {
718           if (init)
719           {
720             for (String val : parm.getPossibleValues())
721             {
722               choicebox.addItem(val);
723             }
724           }
725           if (parm.getValue() != null)
726           {
727             choicebox.setSelectedItem(parm.getValue());
728           }
729         }
730         else
731         {
732           valueField.setText(parm.getValue());
733         }
734       }
735       lastVal = updateSliderFromValueField();
736       adjusting = false;
737     }
738
739     /**
740      * Action depends on the type of the input parameter:
741      * <ul>
742      * <li>if a text input, returns the trimmed value</li>
743      * <li>if a choice list, returns the selected value</li>
744      * <li>if a value slider and input field, sets the value of the slider from
745      * the value in the text field, limiting it to any defined min-max
746      * range.</li>
747      * </ul>
748      * Answers the (possibly modified) input value, as a String, Integer, Float
749      * or Double.
750      * 
751      * @return
752      */
753     Object updateSliderFromValueField()
754     {
755       if (validator == null)
756       {
757         if (isChoiceParameter)
758         {
759           return choicebox.getSelectedItem();
760         }
761         slider.setVisible(false);
762         return valueField.getText().trim();
763       }
764
765       valueField.setText(valueField.getText().trim());
766
767       /*
768        * ensure not outside min-max range
769        * TODO: provide some visual indicator if limit reached
770        */
771       try
772       {
773         valueField.setBackground(Color.WHITE);
774         double d = Double.parseDouble(valueField.getText());
775         if (validator.getMin() != null
776                 && validator.getMin().doubleValue() > d)
777         {
778           valueField.setText(formatNumber(validator.getMin()));
779         }
780         if (validator.getMax() != null
781                 && validator.getMax().doubleValue() < d)
782         {
783           valueField.setText(formatNumber(validator.getMax()));
784         }
785       } catch (NumberFormatException e)
786       {
787         valueField.setBackground(Color.yellow);
788         return Float.NaN;
789       }
790
791       if (isIntegerParameter)
792       {
793         int iVal = 0;
794         try
795         {
796           iVal = Integer.valueOf(valueField.getText());
797         } catch (Exception e)
798         {
799           valueField.setBackground(Color.yellow);
800           return Integer.valueOf(0);
801         }
802
803         if (validator.getMin() != null && validator.getMax() != null)
804         {
805           slider.getModel().setRangeProperties(iVal, 1,
806                   validator.getMin().intValue(),
807                   validator.getMax().intValue() + 1, true);
808         }
809         else
810         {
811           slider.setVisible(false);
812         }
813         return new Integer(iVal);
814       }
815
816       if (isLogarithmicParameter)
817       {
818         double dVal = 0d;
819         try
820         {
821           double eValue = Double.valueOf(valueField.getText());
822           dVal = Math.log(eValue) * sliderScaleFactor;
823         } catch (Exception e)
824         {
825           // shouldn't be possible here
826           valueField.setBackground(Color.yellow);
827           return Double.NaN;
828         }
829         if (validator.getMin() != null && validator.getMax() != null)
830         {
831           double scaleMin = Math.log(validator.getMin().doubleValue())
832                   * sliderScaleFactor;
833           double scaleMax = Math.log(validator.getMax().doubleValue())
834                   * sliderScaleFactor;
835           slider.getModel().setRangeProperties((int) (dVal), 1,
836                   (int) scaleMin, 1 + (int) scaleMax, true);
837         }
838         else
839         {
840           slider.setVisible(false);
841         }
842         return new Double(dVal);
843       }
844
845       float fVal = 0f;
846       try
847       {
848         fVal = Float.valueOf(valueField.getText());
849       } catch (Exception e)
850       {
851         return Float.valueOf(0f); // shouldn't happen
852       }
853       if (validator.getMin() != null && validator.getMax() != null)
854       {
855         float scaleMin = validator.getMin().floatValue()
856                 * sliderScaleFactor;
857         float scaleMax = validator.getMax().floatValue()
858                 * sliderScaleFactor;
859         slider.getModel().setRangeProperties(
860                 (int) (fVal * sliderScaleFactor), 1, (int) scaleMin,
861                 1 + (int) scaleMax, true);
862       }
863       else
864       {
865         slider.setVisible(false);
866       }
867       return new Float(fVal);
868     }
869   }
870
871   /**
872    * Constructor with the option to show 'compact' format (parameter description
873    * as tooltip) or 'expanded' format (parameter description in a textbox which
874    * may be opened or closed). Use compact for simple description text, expanded
875    * for more wordy or formatted text.
876    * 
877    * @param paramContainer
878    */
879   public OptsAndParamsPage(OptsParametersContainerI paramContainer,
880           boolean compact)
881   {
882     poparent = paramContainer;
883     this.compact = compact;
884   }
885
886   public static void showUrlPopUp(JComponent invoker, final String finfo,
887           int x, int y)
888   {
889
890     JPopupMenu mnu = new JPopupMenu();
891     JMenuItem mitem = new JMenuItem(
892             MessageManager.formatMessage("label.view_params", new String[]
893             { finfo }));
894     mitem.addActionListener(new ActionListener()
895     {
896
897       @Override
898       public void actionPerformed(ActionEvent e)
899       {
900         Desktop.showUrl(finfo);
901
902       }
903     });
904     mnu.add(mitem);
905     mnu.show(invoker, x, y);
906   }
907
908   public Map<String, OptionBox> getOptSet()
909   {
910     return optSet;
911   }
912
913   public void setOptSet(Map<String, OptionBox> optSet)
914   {
915     this.optSet = optSet;
916   }
917
918   public Map<String, ParamBox> getParamSet()
919   {
920     return paramSet;
921   }
922
923   public void setParamSet(Map<String, ParamBox> paramSet)
924   {
925     this.paramSet = paramSet;
926   }
927
928   OptionBox addOption(OptionI opt)
929   {
930     OptionBox cb = optSet.get(opt.getName());
931     if (cb == null)
932     {
933       cb = new OptionBox(opt);
934       optSet.put(opt.getName(), cb);
935       // jobOptions.add(cb, FlowLayout.LEFT);
936     }
937     return cb;
938   }
939
940   ParamBox addParameter(ParameterI arg)
941   {
942     ParamBox pb = paramSet.get(arg.getName());
943     if (pb == null)
944     {
945       pb = new ParamBox(poparent, arg);
946       paramSet.put(arg.getName(), pb);
947       // paramList.add(pb);
948     }
949     pb.init();
950     // take the defaults from the parameter
951     pb.updateControls(arg);
952     return pb;
953   }
954
955   void selectOption(OptionI option, String string)
956   {
957     OptionBox cb = optSet.get(option.getName());
958     if (cb == null)
959     {
960       cb = addOption(option);
961     }
962     cb.enabled.setSelected(string != null); // initial state for an option.
963     if (string != null)
964     {
965       if (option.getPossibleValues().contains(string))
966       {
967         cb.val.setSelectedItem(string);
968       }
969       else
970       {
971         throw new Error(String.format("Invalid value '%s' for option '%s'",
972                 string, option.getName()));
973       }
974     }
975     if (option.isRequired() && !cb.enabled.isSelected())
976     {
977       // TODO: indicate paramset is not valid.. option needs to be selected!
978     }
979     cb.setInitialValue();
980   }
981
982   void setParameter(ParameterI arg)
983   {
984     ParamBox pb = paramSet.get(arg.getName());
985     if (pb == null)
986     {
987       addParameter(arg);
988     }
989     else
990     {
991       pb.updateControls(arg);
992     }
993
994   }
995
996   /**
997    * recover options and parameters from GUI
998    * 
999    * @return
1000    */
1001   public List<ArgumentI> getCurrentSettings()
1002   {
1003     List<ArgumentI> argSet = new ArrayList<>();
1004     for (OptionBox opts : getOptSet().values())
1005     {
1006       OptionI opt = opts.getOptionIfEnabled();
1007       if (opt != null)
1008       {
1009         argSet.add(opt);
1010       }
1011     }
1012     for (ParamBox parambox : getParamSet().values())
1013     {
1014       ParameterI parm = parambox.getParameter();
1015       if (parm != null)
1016       {
1017         argSet.add(parm);
1018       }
1019     }
1020
1021     return argSet;
1022   }
1023 }