Xcos solvers: enable/disable solvers properties on solver kind
[scilab.git] / scilab / modules / xcos / src / java / org / scilab / modules / xcos / actions / dialog / SetupDialog.java
1 /*
2  * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab
3  * Copyright (C) 2010 - DIGITEO - Clement DAVID
4  * Copyright (C) 2013 - Scilab Enterprises - Clement DAVID
5  *
6  * This file must be used under the terms of the CeCILL.
7  * This source file is licensed as described in the file COPYING, which
8  * you should have received as part of this distribution.  The terms
9  * are also available at
10  * http://www.cecill.info/licences/Licence_CeCILL_V2-en.txt
11  *
12  */
13
14 package org.scilab.modules.xcos.actions.dialog;
15
16 import java.awt.Component;
17 import java.awt.GridBagConstraints;
18 import java.awt.GridBagLayout;
19 import java.awt.Image;
20 import java.awt.Insets;
21 import java.awt.event.ActionEvent;
22 import java.awt.event.ActionListener;
23 import java.awt.event.ItemEvent;
24 import java.awt.event.ItemListener;
25 import java.beans.PropertyVetoException;
26 import java.math.BigDecimal;
27 import java.text.DecimalFormat;
28 import java.text.DecimalFormatSymbols;
29 import java.util.Arrays;
30 import java.util.EnumSet;
31 import java.util.logging.Logger;
32
33 import javax.swing.DefaultListCellRenderer;
34 import javax.swing.ImageIcon;
35 import javax.swing.InputVerifier;
36 import javax.swing.JButton;
37 import javax.swing.JComboBox;
38 import javax.swing.JComponent;
39 import javax.swing.JDialog;
40 import javax.swing.JFormattedTextField;
41 import javax.swing.JLabel;
42 import javax.swing.JList;
43
44 import org.scilab.modules.gui.utils.ScilabSwingUtilities;
45 import org.scilab.modules.xcos.actions.SetupAction;
46 import org.scilab.modules.xcos.graph.ScicosParameters;
47 import org.scilab.modules.xcos.graph.XcosDiagram;
48 import org.scilab.modules.xcos.utils.XcosMessages;
49
50 /**
51  * Dialog associated with the {@link SetupAction}.
52  *
53  * Note that this dialog break the Data Abstraction Coupling metric because of
54  * the numbers of graphical components involved in the GUI creation. For the
55  * same reason (GUI class), constants are not used on this code.
56  */
57 // CSOFF: ClassDataAbstractionCoupling
58 // CSOFF: ClassFanOutComplexity
59 // CSOFF: MagicNumber
60 @SuppressWarnings(value = { "serial" })
61 public class SetupDialog extends JDialog {
62     private static final DecimalFormatSymbols FORMAT_SYMBOL = DecimalFormatSymbols.getInstance();
63     private static final DecimalFormat CURRENT_FORMAT = new DecimalFormat("0.0####E00", FORMAT_SYMBOL);
64     private static final BigDecimal MAX_DOUBLE = BigDecimal.valueOf(Double.MAX_VALUE);
65
66     /**
67      * Modifiers used to disable some parameters for a specific solver
68      */
69     private static enum SolverModifiers {
70         ABSOLUTE_TOLERANCE,
71         RELATIVE_TOLERANCE,
72         TOLERANCE_ON_TIME,
73         MAX_INTEGRATION_TIME,
74         MAX_STEP_SIZE
75     }
76
77     /**
78      * Describe a solver, its compilation value, name and tooltip.
79      *
80      * Also contains a field with enable modifier set.
81      */
82     private static class SolverDescriptor implements Comparable<SolverDescriptor> {
83         private final int number;
84         private final String name;
85         private final String tooltip;
86         private final EnumSet<SolverModifiers> modifiers;
87
88         public SolverDescriptor(int number, String name, String tooltip, EnumSet<SolverModifiers> modifiers) {
89             super();
90             this.number = number;
91             this.name = name;
92             this.tooltip = tooltip;
93             this.modifiers = modifiers;
94         }
95
96         public SolverDescriptor(double number) {
97             super();
98             this.number = (int) number;
99             this.name = null;
100             this.tooltip = null;
101             this.modifiers = null;
102         }
103
104         public int getNumber() {
105             return number;
106         }
107
108         public String getTooltip() {
109             return tooltip;
110         }
111
112         @Override
113         public String toString() {
114             return name;
115         }
116
117         public void applyModifiers(
118             JFormattedTextField integrator,
119             JFormattedTextField integratorRel,
120             JFormattedTextField toleranceOnTime,
121             JFormattedTextField maxIntegrationTime,
122             JFormattedTextField maxStepSize) {
123             integrator.setEnabled(modifiers.contains(SolverModifiers.ABSOLUTE_TOLERANCE));
124             integratorRel.setEnabled(modifiers.contains(SolverModifiers.RELATIVE_TOLERANCE));
125             toleranceOnTime.setEnabled(modifiers.contains(SolverModifiers.TOLERANCE_ON_TIME));
126             maxIntegrationTime.setEnabled(modifiers.contains(SolverModifiers.MAX_INTEGRATION_TIME));
127             maxStepSize.setEnabled(modifiers.contains(SolverModifiers.MAX_STEP_SIZE));
128         }
129
130         @Override
131         public int compareTo(SolverDescriptor o) {
132             // inline Integer.compare(number, o.number) to be java 1.6 compatible
133             return (number < o.number) ? -1 : ((number == o.number) ? 0 : 1);
134         }
135     }
136
137     private static final SolverDescriptor[] AVAILABLE_SOLVERS = new SolverDescriptor[] {
138         new SolverDescriptor(0, "LSodar",
139                              "Method: dynamic, Nonlinear solver= dynamic",
140                              EnumSet.allOf(SetupDialog.SolverModifiers.class)),
141
142         new SolverDescriptor(1, "Sundials/CVODE - BDF - NEWTON",
143                              "Method: BDF, Nonlinear solver= NEWTON",
144                              EnumSet.allOf(SetupDialog.SolverModifiers.class)),
145
146         new SolverDescriptor(2, "Sundials/CVODE - BDF - FUNCTIONAL",
147                              "Method: BDF, Nonlinear solver= FUNCTIONAL",
148                              EnumSet.allOf(SetupDialog.SolverModifiers.class)),
149
150         new SolverDescriptor(3, "Sundials/CVODE - ADAMS - NEWTON",
151                              "Method: ADAMS, Nonlinear solver= NEWTON",
152                              EnumSet.allOf(SetupDialog.SolverModifiers.class)),
153
154         new SolverDescriptor(4, "Sundials/CVODE - ADAMS - FUNCTIONAL",
155                              "Method: ADAMS, Nonlinear solver= FUNCTIONAL",
156                              EnumSet.allOf(SetupDialog.SolverModifiers.class)),
157
158         new SolverDescriptor(5, "DOPRI5 - Dormand-Prince 4(5)",
159                              "Method: Fixed step",
160                              EnumSet.of(SetupDialog.SolverModifiers.MAX_STEP_SIZE)),
161
162         new SolverDescriptor(6, "RK45 - Runge-Kutta 4(5)",
163                              "Method: Fixed step",
164                              EnumSet.of(SetupDialog.SolverModifiers.MAX_STEP_SIZE)),
165
166         new SolverDescriptor(7, "Implicit RK45 - Runge-Kutta 4(5)",
167                              "Method: Fixed step, Nonlinear solver= FIXED-POINT",
168                              EnumSet.of(SetupDialog.SolverModifiers.MAX_STEP_SIZE,
169                                         SetupDialog.SolverModifiers.RELATIVE_TOLERANCE)),
170
171         new SolverDescriptor(100, "Sundials/IDA",
172                              "Method: BDF, Nonlinear solver= NEWTON",
173                              EnumSet.allOf(SetupDialog.SolverModifiers.class)),
174
175         new SolverDescriptor(101, "DDaskr",
176                              "Method: BDF, Nonlinear solver= NEWTON",
177                              EnumSet.allOf(SetupDialog.SolverModifiers.class))
178     };
179
180     /**
181      * Validate the user entry and format it.
182      *
183      * Without formatting the entry, bug #7143 appears on jdk6.
184      */
185     private static final InputVerifier VALIDATE_POSITIVE_DOUBLE = new InputVerifier() {
186
187         @Override
188         public boolean verify(javax.swing.JComponent arg0) {
189             boolean ret = false;
190             JFormattedTextField textField = (JFormattedTextField) arg0;
191             try {
192                 BigDecimal value = new BigDecimal(textField.getText());
193                 if (value.compareTo(BigDecimal.ZERO) >= 0 && value.compareTo(MAX_DOUBLE) <= 0) {
194                     ret = true;
195                 }
196
197                 // bug #7143 workaround
198                 textField.setValue(value);
199             } catch (NumberFormatException e) {
200                 return ret;
201             }
202             return ret;
203
204         };
205     };
206
207     /**
208      * Initialize static final fields
209      */
210     static {
211         FORMAT_SYMBOL.setDecimalSeparator('.');
212         CURRENT_FORMAT.setDecimalFormatSymbols(FORMAT_SYMBOL);
213         CURRENT_FORMAT.setParseIntegerOnly(false);
214         CURRENT_FORMAT.setParseBigDecimal(true);
215
216         // assert that the array is sorted
217         Arrays.sort(AVAILABLE_SOLVERS);
218     }
219
220     private final ScicosParameters parameters;
221     private final XcosDiagram rootGraph;
222
223     private JFormattedTextField integration;
224     private JFormattedTextField rts;
225     private JFormattedTextField integrator;
226     private JFormattedTextField integratorRel;
227     private JFormattedTextField toleranceOnTime;
228     private JFormattedTextField maxIntegrationTime;
229     private JComboBox solver;
230     private JFormattedTextField maxStepSize;
231
232     /**
233      * Instanciate a new dialog.
234      *
235      * @param parent
236      *            the current selected graph component
237      * @param parameters
238      *            the current parameters
239      */
240     public SetupDialog(Component parent, XcosDiagram graph, ScicosParameters parameters) {
241         super();
242
243         this.parameters = parameters;
244
245         ImageIcon scilabIcon = new ImageIcon(ScilabSwingUtilities.findIcon("scilab"));
246         Image imageForIcon = scilabIcon.getImage();
247         setLayout(new GridBagLayout());
248         setIconImage(imageForIcon);
249         setTitle(XcosMessages.SETUP_TITLE);
250         setModal(false);
251         setLocationRelativeTo(parent);
252         rootGraph = graph;
253         setDefaultCloseOperation(DISPOSE_ON_CLOSE);
254         ScilabSwingUtilities.closeOnEscape(this);
255
256         initComponents();
257     }
258
259     /**
260      * Initialize the dialog components.
261      *
262      * As this method perform a complex initialization, It doesn't pass NCSS.
263      */
264     // CSOFF: JavaNCSS
265     private void initComponents() {
266         JLabel integrationLabel = new JLabel(XcosMessages.FINAL_INTEGRATION_TIME);
267         integration = new JFormattedTextField(CURRENT_FORMAT);
268         integration.setInputVerifier(VALIDATE_POSITIVE_DOUBLE);
269         integration.setValue(new BigDecimal(parameters.getFinalIntegrationTime()));
270
271         JLabel rtsLabel = new JLabel(XcosMessages.REAL_TIME_SCALING);
272         rts = new JFormattedTextField(CURRENT_FORMAT);
273         rts.setInputVerifier(VALIDATE_POSITIVE_DOUBLE);
274         rts.setValue(new BigDecimal(parameters.getRealTimeScaling()));
275
276         JLabel integratorAbsLabel = new JLabel(XcosMessages.INTEGRATOR_ABSOLUTE_TOLERANCE);
277         integrator = new JFormattedTextField(CURRENT_FORMAT);
278         integrator.setInputVerifier(VALIDATE_POSITIVE_DOUBLE);
279         integrator.setValue(new BigDecimal(parameters.getIntegratorAbsoluteTolerance()));
280
281         JLabel integratorRelLabel = new JLabel(XcosMessages.INTEGRATOR_RELATIVE_TOLERANCE);
282         integratorRel = new JFormattedTextField(CURRENT_FORMAT);
283         integratorRel.setInputVerifier(VALIDATE_POSITIVE_DOUBLE);
284         integratorRel.setValue(new BigDecimal(parameters.getIntegratorRelativeTolerance()));
285
286         JLabel toleranceOnTimeLabel = new JLabel(XcosMessages.TOLERANCE_ON_TIME);
287         toleranceOnTime = new JFormattedTextField(CURRENT_FORMAT);
288         toleranceOnTime.setInputVerifier(VALIDATE_POSITIVE_DOUBLE);
289         toleranceOnTime.setValue(new BigDecimal(parameters.getToleranceOnTime()));
290
291         JLabel maxIntegrationTimeLabel = new JLabel(XcosMessages.MAX_INTEGRATION_TIME_INTERVAL);
292         maxIntegrationTime = new JFormattedTextField(CURRENT_FORMAT);
293         maxIntegrationTime.setInputVerifier(VALIDATE_POSITIVE_DOUBLE);
294         maxIntegrationTime.setValue(new BigDecimal(parameters.getMaxIntegrationTimeInterval()));
295
296         JLabel solverLabel = new JLabel(XcosMessages.SOLVER_CHOICE);
297
298
299         solver = new JComboBox(AVAILABLE_SOLVERS);
300         double solverValue = parameters.getSolver();
301         final int currentIndex = Arrays.binarySearch(AVAILABLE_SOLVERS, new SolverDescriptor(solverValue));
302         final SolverDescriptor current = AVAILABLE_SOLVERS[currentIndex];
303         solver.setSelectedIndex(currentIndex);
304
305         final class ComboboxToolTipRenderer extends DefaultListCellRenderer {
306             @Override
307             public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
308                 JComponent comp = (JComponent) super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
309
310                 if (-1 < index && null != value) {
311                     list.setToolTipText(AVAILABLE_SOLVERS[index].getTooltip());
312                 }
313                 return comp;
314             }
315         }
316         solver.setRenderer(new ComboboxToolTipRenderer());
317
318         solver.addItemListener(new ItemListener() {
319             @Override
320             public void itemStateChanged(ItemEvent e) {
321                 SolverDescriptor current = (SolverDescriptor) e.getItem();
322                 current.applyModifiers(integrator, integratorRel, toleranceOnTime, maxIntegrationTime, maxStepSize);
323             }
324         });
325
326         JLabel maxStepSizeLabel = new JLabel(XcosMessages.MAXIMUN_STEP_SIZE);
327         maxStepSize = new JFormattedTextField(CURRENT_FORMAT);
328         maxStepSize.setInputVerifier(VALIDATE_POSITIVE_DOUBLE);
329         maxStepSize.setValue(new BigDecimal(parameters.getMaximumStepSize()));
330
331         JButton cancelButton = new JButton(XcosMessages.CANCEL);
332         JButton okButton = new JButton(XcosMessages.OK);
333         JButton defaultButton = new JButton(XcosMessages.DEFAULT);
334         JButton setContextButton = new JButton(XcosMessages.SET_CONTEXT);
335         okButton.setPreferredSize(cancelButton.getPreferredSize());
336
337         GridBagConstraints gbc = new GridBagConstraints();
338
339         gbc.gridx = 0;
340         gbc.gridy = 0;
341         gbc.gridheight = 1;
342         gbc.gridwidth = 1;
343         gbc.insets = new Insets(0, 10, 0, 0);
344
345         gbc.gridx = 0;
346         gbc.gridy = 4;
347         gbc.gridheight = 1;
348         gbc.gridwidth = 1;
349         gbc.fill = GridBagConstraints.NONE;
350         gbc.insets = new Insets(0, 10, 0, 0);
351
352         add(integrationLabel, gbc);
353
354         gbc.gridy = 5;
355         add(rtsLabel, gbc);
356
357         gbc.gridy = 6;
358         add(integratorAbsLabel, gbc);
359
360         gbc.gridy = 7;
361         add(integratorRelLabel, gbc);
362
363         gbc.gridy = 8;
364         add(toleranceOnTimeLabel, gbc);
365
366         gbc.gridy = 9;
367         add(maxIntegrationTimeLabel, gbc);
368
369         gbc.gridy = 10;
370         add(solverLabel, gbc);
371
372         gbc.gridy = 11;
373         add(maxStepSizeLabel, gbc);
374
375         gbc.gridy = 12;
376         add(setContextButton, gbc);
377
378         gbc.gridx = 1;
379         gbc.gridy = 4;
380         gbc.gridwidth = GridBagConstraints.REMAINDER;
381         gbc.fill = GridBagConstraints.HORIZONTAL;
382         gbc.insets = new Insets(5, 10, 0, 10);
383         add(integration, gbc);
384
385         gbc.gridy = 5;
386         add(rts, gbc);
387
388         gbc.gridy = 6;
389         add(integrator, gbc);
390
391         gbc.gridy = 7;
392         add(integratorRel, gbc);
393
394         gbc.gridy = 8;
395         add(toleranceOnTime, gbc);
396
397         gbc.gridy = 9;
398         add(maxIntegrationTime, gbc);
399
400         gbc.gridy = 10;
401         add(solver, gbc);
402
403         gbc.gridy = 11;
404         add(maxStepSize, gbc);
405
406         gbc.gridx = 1;
407         gbc.gridy = 14;
408         gbc.gridheight = 1;
409         gbc.gridwidth = 1;
410         gbc.weightx = 1.;
411         gbc.fill = GridBagConstraints.NONE;
412         gbc.insets = new Insets(5, 0, 10, 5);
413         add(okButton, gbc);
414
415         gbc.gridx = 2;
416         gbc.weightx = 0.;
417         gbc.insets = new Insets(5, 0, 10, 10);
418         add(cancelButton, gbc);
419
420         gbc.gridx = 3;
421         gbc.weightx = 0.;
422         gbc.insets = new Insets(5, 0, 10, 10);
423         add(defaultButton, gbc);
424
425         installActionListeners(cancelButton, okButton, defaultButton, setContextButton);
426
427         // at the end, update the enable status of some components
428         current.applyModifiers(integrator, integratorRel, toleranceOnTime, maxIntegrationTime, maxStepSize);
429     }
430
431     // CSON: JavaNCSS
432
433     /**
434      * Install the action listeners on the buttons
435      *
436      * @param cancelButton
437      *            the cancel button (Cancel)
438      * @param okButton
439      *            the OK button (Validate)
440      * @param defaultButton
441      *            the default button (Reset)
442      * @param setContextButton
443      *            the context button (Shortcut to set context)
444      */
445     private void installActionListeners(JButton cancelButton, JButton okButton, JButton defaultButton, JButton setContextButton) {
446         cancelButton.addActionListener(new ActionListener() {
447             @Override
448             public void actionPerformed(ActionEvent e) {
449                 dispose();
450             }
451         });
452
453         defaultButton.addActionListener(new ActionListener() {
454             @Override
455             public void actionPerformed(ActionEvent e) {
456                 integration.setValue(new BigDecimal(ScicosParameters.FINAL_INTEGRATION_TIME));
457                 rts.setValue(new BigDecimal(ScicosParameters.REAL_TIME_SCALING));
458                 integrator.setValue(new BigDecimal(ScicosParameters.INTEGRATOR_ABSOLUTE_TOLERANCE));
459                 integratorRel.setValue(new BigDecimal(ScicosParameters.INTEGRATOR_RELATIVE_TOLERANCE));
460                 toleranceOnTime.setValue(new BigDecimal(ScicosParameters.TOLERANCE_ON_TIME));
461                 maxIntegrationTime.setValue(new BigDecimal(ScicosParameters.MAX_INTEGRATION_TIME_INTERVAL));
462                 solver.setSelectedIndex((int) ScicosParameters.SOLVER);
463                 maxStepSize.setValue(new BigDecimal(ScicosParameters.MAXIMUM_STEP_SIZE));
464             }
465         });
466
467         okButton.addActionListener(new ActionListener() {
468             @Override
469             public void actionPerformed(ActionEvent e) {
470                 try {
471                     /*
472                      * FIXME This logic must be deported to a vetoable
473                      * handler
474                      */
475                     int solverSelectedIndex = solver.getSelectedIndex();
476                     parameters.setSolver(AVAILABLE_SOLVERS[solverSelectedIndex].getNumber());
477
478                     parameters.setFinalIntegrationTime(((BigDecimal) integration.getValue()).doubleValue());
479                     parameters.setRealTimeScaling(((BigDecimal) rts.getValue()).doubleValue());
480                     parameters.setIntegratorAbsoluteTolerance(((BigDecimal) integrator.getValue()).doubleValue());
481                     parameters.setIntegratorRelativeTolerance(((BigDecimal) integratorRel.getValue()).doubleValue());
482                     parameters.setToleranceOnTime(((BigDecimal) toleranceOnTime.getValue()).doubleValue());
483                     parameters.setMaxIntegrationTimeInterval(((BigDecimal) maxIntegrationTime.getValue()).doubleValue());
484                     parameters.setMaximumStepSize(((BigDecimal) maxStepSize.getValue()).doubleValue());
485
486                     dispose();
487
488                 } catch (PropertyVetoException ex) {
489                     Logger.getLogger(SetupAction.class.getName()).severe(ex.toString());
490                 }
491             }
492         });
493
494         setContextButton.addActionListener(new ActionListener() {
495             @Override
496             public void actionPerformed(ActionEvent e) {
497                 final SetContextDialog dialog = new SetContextDialog(SetupDialog.this, rootGraph, parameters);
498
499                 dialog.pack();
500                 dialog.setVisible(true);
501             }
502         });
503     }
504 }
505 // CSON: ClassDataAbstractionCoupling
506 // CSON: ClassFanOutComplexity
507 // CSON: MagicNumber