5ef7169257b72cc42436798b41d134dfdb7390a6
[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       Object newValue = updateSliderFromValueField();
556       boolean modified = true;
557       if (newValue.getClass() == lastVal.getClass())
558       {
559         modified = !newValue.equals(lastVal);
560       }
561       pmdialogbox.argSetModified(this, modified);
562     }
563
564     @Override
565     public int getBaseline(int width, int height)
566     {
567       return 0;
568     }
569
570     // from
571     // http://stackoverflow.com/questions/2743177/top-alignment-for-flowlayout
572     // helpful hint of using the Java 1.6 alignBaseLine property of FlowLayout
573     @Override
574     public Component.BaselineResizeBehavior getBaselineResizeBehavior()
575     {
576       return Component.BaselineResizeBehavior.CONSTANT_ASCENT;
577     }
578
579     public ParameterI getParameter()
580     {
581       ParameterI prm = parameter.copy();
582       if (isChoiceParameter)
583       {
584         prm.setValue((String) choicebox.getSelectedItem());
585       }
586       else
587       {
588         prm.setValue(valueField.getText());
589       }
590       return prm;
591     }
592
593     public void init()
594     {
595       // reset the widget's initial value.
596       lastVal = null;
597     }
598
599     @Override
600     public void mouseClicked(MouseEvent e)
601     {
602       if (e.isPopupTrigger()) // for Windows
603       {
604         showUrlPopUp(this, finfo.toString(), e.getX(), e.getY());
605       }
606     }
607
608     @Override
609     public void mouseEntered(MouseEvent e)
610     {
611     }
612
613     @Override
614     public void mouseExited(MouseEvent e)
615     {
616     }
617
618     @Override
619     public void mousePressed(MouseEvent e)
620     {
621       if (e.isPopupTrigger()) // for Mac
622       {
623         showUrlPopUp(this, finfo.toString(), e.getX(), e.getY());
624       }
625     }
626
627     @Override
628     public void mouseReleased(MouseEvent e)
629     {
630     }
631
632     @Override
633     public void stateChanged(ChangeEvent e)
634     {
635       if (!adjusting)
636       {
637         if (!isLogarithmicParameter)
638         {
639           /*
640            * set (int or float formatted) text field value
641            */
642           valueField.setText(isIntegerParameter
643                   ? String.valueOf(slider.getValue())
644                   : formatDouble(
645                           slider.getValue() / (float) sliderScaleFactor));
646         }
647         else
648         {
649           double value = Math.pow(Math.E,
650                   slider.getValue() / (double) sliderScaleFactor);
651           valueField.setText(formatDouble(value));
652         }
653         checkIfModified();
654       }
655     }
656
657     /**
658      * Answers the value formatted as a string to 3 decimal places - in
659      * scientific notation if the value is less than 0.001
660      * 
661      * @param value
662      * @return
663      */
664     String formatDouble(double value)
665     {
666       String format = value < 0.001 ? "%3.1E" : "%3.3f";
667       return String.format(format, value);
668     }
669
670     /**
671      * Formats a number as integer or float (3dp) or scientific notation (1dp)
672      * 
673      * @param n
674      * @return
675      */
676     String formatNumber(Number n)
677     {
678       return n instanceof Integer ? String.valueOf(n.intValue())
679               : formatDouble(n.doubleValue());
680     }
681
682     void updateControls(ParameterI parm)
683     {
684       adjusting = true;
685       boolean init = (choicebox == null && valueField == null);
686       if (init)
687       {
688         if (isChoiceParameter)
689         {
690           choicebox = new JComboBox<>();
691           choicebox.addActionListener(this);
692           controlsPanel.add(choicebox, BorderLayout.CENTER);
693         }
694         else
695         {
696           slider = new JSlider();
697           slider.addChangeListener(this);
698           int cols = parm instanceof StringParameter ? 20 : 0;
699           valueField = new JTextField(cols);
700           valueField.addActionListener(this);
701           valueField.addKeyListener(new KeyAdapter()
702           {
703             @Override
704             public void keyReleased(KeyEvent e)
705             {
706               int keyCode = e.getKeyCode();
707               if (e.isActionKey() && keyCode != KeyEvent.VK_LEFT
708                       && keyCode != KeyEvent.VK_RIGHT)
709               {
710                 if (valueField.getText().trim().length() > 0)
711                 {
712                   actionPerformed(null);
713                 }
714               }
715             }
716           });
717           valueField.setPreferredSize(new Dimension(65, 25));
718           controlsPanel.add(slider, BorderLayout.WEST);
719           controlsPanel.add(valueField, BorderLayout.EAST);
720         }
721       }
722
723       if (parm != null)
724       {
725         if (isChoiceParameter)
726         {
727           if (init)
728           {
729             for (String val : parm.getPossibleValues())
730             {
731               choicebox.addItem(val);
732             }
733           }
734           if (parm.getValue() != null)
735           {
736             choicebox.setSelectedItem(parm.getValue());
737           }
738         }
739         else
740         {
741           valueField.setText(parm.getValue());
742         }
743       }
744       lastVal = updateSliderFromValueField();
745       adjusting = false;
746     }
747
748     /**
749      * Action depends on the type of the input parameter:
750      * <ul>
751      * <li>if a text input, returns the trimmed value</li>
752      * <li>if a choice list, returns the selected value</li>
753      * <li>if a value slider and input field, sets the value of the slider from
754      * the value in the text field, limiting it to any defined min-max
755      * range.</li>
756      * </ul>
757      * Answers the (possibly modified) input value, as a String, Integer, Float
758      * or Double.
759      * 
760      * @return
761      */
762     Object updateSliderFromValueField()
763     {
764       if (validator == null || isStringParameter)
765       {
766         if (isChoiceParameter)
767         {
768           return choicebox.getSelectedItem();
769         }
770         slider.setVisible(false);
771         return valueField.getText().trim();
772       }
773
774       valueField.setText(valueField.getText().trim());
775
776       /*
777        * ensure not outside min-max range
778        * TODO: provide some visual indicator if limit reached
779        */
780       try
781       {
782         valueField.setBackground(Color.WHITE);
783         double d = Double.parseDouble(valueField.getText());
784         if (validator.getMin() != null
785                 && validator.getMin().doubleValue() > d)
786         {
787           valueField.setText(formatNumber(validator.getMin()));
788         }
789         if (validator.getMax() != null
790                 && validator.getMax().doubleValue() < d)
791         {
792           valueField.setText(formatNumber(validator.getMax()));
793         }
794       } catch (NumberFormatException e)
795       {
796         valueField.setBackground(Color.yellow);
797         return Float.NaN;
798       }
799
800       if (isIntegerParameter)
801       {
802         int iVal = 0;
803         try
804         {
805           iVal = Integer.valueOf(valueField.getText());
806         } catch (Exception e)
807         {
808           valueField.setBackground(Color.yellow);
809           return Integer.valueOf(0);
810         }
811
812         if (validator.getMin() != null && validator.getMax() != null)
813         {
814           slider.getModel().setRangeProperties(iVal, 1,
815                   validator.getMin().intValue(),
816                   validator.getMax().intValue() + 1, true);
817         }
818         else
819         {
820           slider.setVisible(false);
821         }
822         return new Integer(iVal);
823       }
824
825       if (isLogarithmicParameter)
826       {
827         double dVal = 0d;
828         try
829         {
830           double eValue = Double.valueOf(valueField.getText());
831           dVal = Math.log(eValue) * sliderScaleFactor;
832         } catch (Exception e)
833         {
834           // shouldn't be possible here
835           valueField.setBackground(Color.yellow);
836           return Double.NaN;
837         }
838         if (validator.getMin() != null && validator.getMax() != null)
839         {
840           double scaleMin = Math.log(validator.getMin().doubleValue())
841                   * sliderScaleFactor;
842           double scaleMax = Math.log(validator.getMax().doubleValue())
843                   * sliderScaleFactor;
844           slider.getModel().setRangeProperties((int) (dVal), 1,
845                   (int) scaleMin, 1 + (int) scaleMax, true);
846         }
847         else
848         {
849           slider.setVisible(false);
850         }
851         return new Double(dVal);
852       }
853
854       float fVal = 0f;
855       try
856       {
857         fVal = Float.valueOf(valueField.getText());
858       } catch (Exception e)
859       {
860         return Float.valueOf(0f); // shouldn't happen
861       }
862       if (validator.getMin() != null && validator.getMax() != null)
863       {
864         float scaleMin = validator.getMin().floatValue()
865                 * sliderScaleFactor;
866         float scaleMax = validator.getMax().floatValue()
867                 * sliderScaleFactor;
868         slider.getModel().setRangeProperties(
869                 (int) (fVal * sliderScaleFactor), 1, (int) scaleMin,
870                 1 + (int) scaleMax, true);
871       }
872       else
873       {
874         slider.setVisible(false);
875       }
876       return new Float(fVal);
877     }
878   }
879
880   /**
881    * Constructor with the option to show 'compact' format (parameter description
882    * as tooltip) or 'expanded' format (parameter description in a textbox which
883    * may be opened or closed). Use compact for simple description text, expanded
884    * for more wordy or formatted text.
885    * 
886    * @param paramContainer
887    */
888   public OptsAndParamsPage(OptsParametersContainerI paramContainer,
889           boolean compact)
890   {
891     poparent = paramContainer;
892     this.compact = compact;
893   }
894
895   public static void showUrlPopUp(JComponent invoker, final String finfo,
896           int x, int y)
897   {
898
899     JPopupMenu mnu = new JPopupMenu();
900     JMenuItem mitem = new JMenuItem(
901             MessageManager.formatMessage("label.view_params", new String[]
902             { finfo }));
903     mitem.addActionListener(new ActionListener()
904     {
905
906       @Override
907       public void actionPerformed(ActionEvent e)
908       {
909         Desktop.showUrl(finfo);
910
911       }
912     });
913     mnu.add(mitem);
914     mnu.show(invoker, x, y);
915   }
916
917   public Map<String, OptionBox> getOptSet()
918   {
919     return optSet;
920   }
921
922   public void setOptSet(Map<String, OptionBox> optSet)
923   {
924     this.optSet = optSet;
925   }
926
927   public Map<String, ParamBox> getParamSet()
928   {
929     return paramSet;
930   }
931
932   public void setParamSet(Map<String, ParamBox> paramSet)
933   {
934     this.paramSet = paramSet;
935   }
936
937   OptionBox addOption(OptionI opt)
938   {
939     OptionBox cb = optSet.get(opt.getName());
940     if (cb == null)
941     {
942       cb = new OptionBox(opt);
943       optSet.put(opt.getName(), cb);
944       // jobOptions.add(cb, FlowLayout.LEFT);
945     }
946     return cb;
947   }
948
949   ParamBox addParameter(ParameterI arg)
950   {
951     ParamBox pb = paramSet.get(arg.getName());
952     if (pb == null)
953     {
954       pb = new ParamBox(poparent, arg);
955       paramSet.put(arg.getName(), pb);
956       // paramList.add(pb);
957     }
958     pb.init();
959     // take the defaults from the parameter
960     pb.updateControls(arg);
961     return pb;
962   }
963
964   void selectOption(OptionI option, String string)
965   {
966     OptionBox cb = optSet.get(option.getName());
967     if (cb == null)
968     {
969       cb = addOption(option);
970     }
971     cb.enabled.setSelected(string != null); // initial state for an option.
972     if (string != null)
973     {
974       if (option.getPossibleValues().contains(string))
975       {
976         cb.val.setSelectedItem(string);
977       }
978       else
979       {
980         throw new Error(String.format("Invalid value '%s' for option '%s'",
981                 string, option.getName()));
982       }
983     }
984     if (option.isRequired() && !cb.enabled.isSelected())
985     {
986       // TODO: indicate paramset is not valid.. option needs to be selected!
987     }
988     cb.setInitialValue();
989   }
990
991   void setParameter(ParameterI arg)
992   {
993     ParamBox pb = paramSet.get(arg.getName());
994     if (pb == null)
995     {
996       addParameter(arg);
997     }
998     else
999     {
1000       pb.updateControls(arg);
1001     }
1002
1003   }
1004
1005   /**
1006    * recover options and parameters from GUI
1007    * 
1008    * @return
1009    */
1010   public List<ArgumentI> getCurrentSettings()
1011   {
1012     List<ArgumentI> argSet = new ArrayList<>();
1013     for (OptionBox opts : getOptSet().values())
1014     {
1015       OptionI opt = opts.getOptionIfEnabled();
1016       if (opt != null)
1017       {
1018         argSet.add(opt);
1019       }
1020     }
1021     for (ParamBox parambox : getParamSet().values())
1022     {
1023       ParameterI parm = parambox.getParameter();
1024       if (parm != null)
1025       {
1026         argSet.add(parm);
1027       }
1028     }
1029
1030     return argSet;
1031   }
1032 }