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