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