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