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