eb40cf0441949643afd17c7298e83ed195952390
[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 - Newton",
176                              "Method: BDF, Nonlinear solver= NEWTON",
177                              EnumSet.allOf(SetupDialog.SolverModifiers.class)),
178
179         new SolverDescriptor(102, "DDaskr - GMRes",
180                              "Method: BDF, Nonlinear solver= GMRES",
181                              EnumSet.allOf(SetupDialog.SolverModifiers.class))
182     };
183
184     /**
185      * Validate the user entry and format it.
186      *
187      * Without formatting the entry, bug #7143 appears on jdk6.
188      */
189     private static final InputVerifier VALIDATE_POSITIVE_DOUBLE = new InputVerifier() {
190
191         @Override
192         public boolean verify(javax.swing.JComponent arg0) {
193             boolean ret = false;
194             JFormattedTextField textField = (JFormattedTextField) arg0;
195             try {
196                 BigDecimal value = new BigDecimal(textField.getText());
197                 if (value.compareTo(BigDecimal.ZERO) >= 0 && value.compareTo(MAX_DOUBLE) <= 0) {
198                     ret = true;
199                 }
200
201                 // bug #7143 workaround
202                 textField.setValue(value);
203             } catch (NumberFormatException e) {
204                 return ret;
205             }
206             return ret;
207
208         };
209     };
210
211     /**
212      * Initialize static final fields
213      */
214     static {
215         FORMAT_SYMBOL.setDecimalSeparator('.');
216         CURRENT_FORMAT.setDecimalFormatSymbols(FORMAT_SYMBOL);
217         CURRENT_FORMAT.setParseIntegerOnly(false);
218         CURRENT_FORMAT.setParseBigDecimal(true);
219
220         // assert that the array is sorted
221         Arrays.sort(AVAILABLE_SOLVERS);
222     }
223
224     private final ScicosParameters parameters;
225     private final XcosDiagram rootGraph;
226
227     private JFormattedTextField integration;
228     private JFormattedTextField rts;
229     private JFormattedTextField integrator;
230     private JFormattedTextField integratorRel;
231     private JFormattedTextField toleranceOnTime;
232     private JFormattedTextField maxIntegrationTime;
233     private JComboBox solver;
234     private JFormattedTextField maxStepSize;
235
236     /**
237      * Instanciate a new dialog.
238      *
239      * @param parent
240      *            the current selected graph component
241      * @param parameters
242      *            the current parameters
243      */
244     public SetupDialog(Component parent, XcosDiagram graph, ScicosParameters parameters) {
245         super();
246
247         this.parameters = parameters;
248
249         ImageIcon scilabIcon = new ImageIcon(ScilabSwingUtilities.findIcon("scilab"));
250         Image imageForIcon = scilabIcon.getImage();
251         setLayout(new GridBagLayout());
252         setIconImage(imageForIcon);
253         setTitle(XcosMessages.SETUP_TITLE);
254         setModal(false);
255         setLocationRelativeTo(parent);
256         rootGraph = graph;
257         setDefaultCloseOperation(DISPOSE_ON_CLOSE);
258         ScilabSwingUtilities.closeOnEscape(this);
259
260         initComponents();
261     }
262
263     /**
264      * Initialize the dialog components.
265      *
266      * As this method perform a complex initialization, It doesn't pass NCSS.
267      */
268     // CSOFF: JavaNCSS
269     private void initComponents() {
270         JLabel integrationLabel = new JLabel(XcosMessages.FINAL_INTEGRATION_TIME);
271         integration = new JFormattedTextField(CURRENT_FORMAT);
272         integration.setInputVerifier(VALIDATE_POSITIVE_DOUBLE);
273         integration.setValue(new BigDecimal(parameters.getFinalIntegrationTime()));
274
275         JLabel rtsLabel = new JLabel(XcosMessages.REAL_TIME_SCALING);
276         rts = new JFormattedTextField(CURRENT_FORMAT);
277         rts.setInputVerifier(VALIDATE_POSITIVE_DOUBLE);
278         rts.setValue(new BigDecimal(parameters.getRealTimeScaling()));
279
280         JLabel integratorAbsLabel = new JLabel(XcosMessages.INTEGRATOR_ABSOLUTE_TOLERANCE);
281         integrator = new JFormattedTextField(CURRENT_FORMAT);
282         integrator.setInputVerifier(VALIDATE_POSITIVE_DOUBLE);
283         integrator.setValue(new BigDecimal(parameters.getIntegratorAbsoluteTolerance()));
284
285         JLabel integratorRelLabel = new JLabel(XcosMessages.INTEGRATOR_RELATIVE_TOLERANCE);
286         integratorRel = new JFormattedTextField(CURRENT_FORMAT);
287         integratorRel.setInputVerifier(VALIDATE_POSITIVE_DOUBLE);
288         integratorRel.setValue(new BigDecimal(parameters.getIntegratorRelativeTolerance()));
289
290         JLabel toleranceOnTimeLabel = new JLabel(XcosMessages.TOLERANCE_ON_TIME);
291         toleranceOnTime = new JFormattedTextField(CURRENT_FORMAT);
292         toleranceOnTime.setInputVerifier(VALIDATE_POSITIVE_DOUBLE);
293         toleranceOnTime.setValue(new BigDecimal(parameters.getToleranceOnTime()));
294
295         JLabel maxIntegrationTimeLabel = new JLabel(XcosMessages.MAX_INTEGRATION_TIME_INTERVAL);
296         maxIntegrationTime = new JFormattedTextField(CURRENT_FORMAT);
297         maxIntegrationTime.setInputVerifier(VALIDATE_POSITIVE_DOUBLE);
298         maxIntegrationTime.setValue(new BigDecimal(parameters.getMaxIntegrationTimeInterval()));
299
300         JLabel solverLabel = new JLabel(XcosMessages.SOLVER_CHOICE);
301
302
303         solver = new JComboBox(AVAILABLE_SOLVERS);
304         double solverValue = parameters.getSolver();
305         final int currentIndex = Arrays.binarySearch(AVAILABLE_SOLVERS, new SolverDescriptor(solverValue));
306         final SolverDescriptor current = AVAILABLE_SOLVERS[currentIndex];
307         solver.setSelectedIndex(currentIndex);
308
309         final class ComboboxToolTipRenderer extends DefaultListCellRenderer {
310             @Override
311             public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
312                 JComponent comp = (JComponent) super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
313
314                 if (-1 < index && null != value) {
315                     list.setToolTipText(AVAILABLE_SOLVERS[index].getTooltip());
316                 }
317                 return comp;
318             }
319         }
320         solver.setRenderer(new ComboboxToolTipRenderer());
321
322         solver.addItemListener(new ItemListener() {
323             @Override
324             public void itemStateChanged(ItemEvent e) {
325                 SolverDescriptor current = (SolverDescriptor) e.getItem();
326                 current.applyModifiers(integrator, integratorRel, toleranceOnTime, maxIntegrationTime, maxStepSize);
327             }
328         });
329
330         JLabel maxStepSizeLabel = new JLabel(XcosMessages.MAXIMUN_STEP_SIZE);
331         maxStepSize = new JFormattedTextField(CURRENT_FORMAT);
332         maxStepSize.setInputVerifier(VALIDATE_POSITIVE_DOUBLE);
333         maxStepSize.setValue(new BigDecimal(parameters.getMaximumStepSize()));
334
335         JButton cancelButton = new JButton(XcosMessages.CANCEL);
336         JButton okButton = new JButton(XcosMessages.OK);
337         JButton defaultButton = new JButton(XcosMessages.DEFAULT);
338         JButton setContextButton = new JButton(XcosMessages.SET_CONTEXT);
339         okButton.setPreferredSize(cancelButton.getPreferredSize());
340
341         GridBagConstraints gbc = new GridBagConstraints();
342
343         gbc.gridx = 0;
344         gbc.gridy = 0;
345         gbc.gridheight = 1;
346         gbc.gridwidth = 1;
347         gbc.insets = new Insets(0, 10, 0, 0);
348
349         gbc.gridx = 0;
350         gbc.gridy = 4;
351         gbc.gridheight = 1;
352         gbc.gridwidth = 1;
353         gbc.fill = GridBagConstraints.NONE;
354         gbc.insets = new Insets(0, 10, 0, 0);
355
356         add(integrationLabel, gbc);
357
358         gbc.gridy = 5;
359         add(rtsLabel, gbc);
360
361         gbc.gridy = 6;
362         add(integratorAbsLabel, gbc);
363
364         gbc.gridy = 7;
365         add(integratorRelLabel, gbc);
366
367         gbc.gridy = 8;
368         add(toleranceOnTimeLabel, gbc);
369
370         gbc.gridy = 9;
371         add(maxIntegrationTimeLabel, gbc);
372
373         gbc.gridy = 10;
374         add(solverLabel, gbc);
375
376         gbc.gridy = 11;
377         add(maxStepSizeLabel, gbc);
378
379         gbc.gridy = 12;
380         add(setContextButton, gbc);
381
382         gbc.gridx = 1;
383         gbc.gridy = 4;
384         gbc.gridwidth = GridBagConstraints.REMAINDER;
385         gbc.fill = GridBagConstraints.HORIZONTAL;
386         gbc.insets = new Insets(5, 10, 0, 10);
387         add(integration, gbc);
388
389         gbc.gridy = 5;
390         add(rts, gbc);
391
392         gbc.gridy = 6;
393         add(integrator, gbc);
394
395         gbc.gridy = 7;
396         add(integratorRel, gbc);
397
398         gbc.gridy = 8;
399         add(toleranceOnTime, gbc);
400
401         gbc.gridy = 9;
402         add(maxIntegrationTime, gbc);
403
404         gbc.gridy = 10;
405         add(solver, gbc);
406
407         gbc.gridy = 11;
408         add(maxStepSize, gbc);
409
410         gbc.gridx = 1;
411         gbc.gridy = 14;
412         gbc.gridheight = 1;
413         gbc.gridwidth = 1;
414         gbc.weightx = 1.;
415         gbc.fill = GridBagConstraints.NONE;
416         gbc.insets = new Insets(5, 0, 10, 5);
417         add(okButton, gbc);
418
419         gbc.gridx = 2;
420         gbc.weightx = 0.;
421         gbc.insets = new Insets(5, 0, 10, 10);
422         add(cancelButton, gbc);
423
424         gbc.gridx = 3;
425         gbc.weightx = 0.;
426         gbc.insets = new Insets(5, 0, 10, 10);
427         add(defaultButton, gbc);
428
429         installActionListeners(cancelButton, okButton, defaultButton, setContextButton);
430
431         // at the end, update the enable status of some components
432         current.applyModifiers(integrator, integratorRel, toleranceOnTime, maxIntegrationTime, maxStepSize);
433     }
434
435     // CSON: JavaNCSS
436
437     /**
438      * Install the action listeners on the buttons
439      *
440      * @param cancelButton
441      *            the cancel button (Cancel)
442      * @param okButton
443      *            the OK button (Validate)
444      * @param defaultButton
445      *            the default button (Reset)
446      * @param setContextButton
447      *            the context button (Shortcut to set context)
448      */
449     private void installActionListeners(JButton cancelButton, JButton okButton, JButton defaultButton, JButton setContextButton) {
450         cancelButton.addActionListener(new ActionListener() {
451             @Override
452             public void actionPerformed(ActionEvent e) {
453                 dispose();
454             }
455         });
456
457         defaultButton.addActionListener(new ActionListener() {
458             @Override
459             public void actionPerformed(ActionEvent e) {
460                 integration.setValue(new BigDecimal(ScicosParameters.FINAL_INTEGRATION_TIME));
461                 rts.setValue(new BigDecimal(ScicosParameters.REAL_TIME_SCALING));
462                 integrator.setValue(new BigDecimal(ScicosParameters.INTEGRATOR_ABSOLUTE_TOLERANCE));
463                 integratorRel.setValue(new BigDecimal(ScicosParameters.INTEGRATOR_RELATIVE_TOLERANCE));
464                 toleranceOnTime.setValue(new BigDecimal(ScicosParameters.TOLERANCE_ON_TIME));
465                 maxIntegrationTime.setValue(new BigDecimal(ScicosParameters.MAX_INTEGRATION_TIME_INTERVAL));
466                 solver.setSelectedIndex((int) ScicosParameters.SOLVER);
467                 maxStepSize.setValue(new BigDecimal(ScicosParameters.MAXIMUM_STEP_SIZE));
468             }
469         });
470
471         okButton.addActionListener(new ActionListener() {
472             @Override
473             public void actionPerformed(ActionEvent e) {
474                 try {
475                     /*
476                      * FIXME This logic must be deported to a vetoable
477                      * handler
478                      */
479                     int solverSelectedIndex = solver.getSelectedIndex();
480                     parameters.setSolver(AVAILABLE_SOLVERS[solverSelectedIndex].getNumber());
481
482                     parameters.setFinalIntegrationTime(((BigDecimal) integration.getValue()).doubleValue());
483                     parameters.setRealTimeScaling(((BigDecimal) rts.getValue()).doubleValue());
484                     parameters.setIntegratorAbsoluteTolerance(((BigDecimal) integrator.getValue()).doubleValue());
485                     parameters.setIntegratorRelativeTolerance(((BigDecimal) integratorRel.getValue()).doubleValue());
486                     parameters.setToleranceOnTime(((BigDecimal) toleranceOnTime.getValue()).doubleValue());
487                     parameters.setMaxIntegrationTimeInterval(((BigDecimal) maxIntegrationTime.getValue()).doubleValue());
488                     parameters.setMaximumStepSize(((BigDecimal) maxStepSize.getValue()).doubleValue());
489
490                     dispose();
491
492                 } catch (PropertyVetoException ex) {
493                     Logger.getLogger(SetupAction.class.getName()).severe(ex.toString());
494                 }
495             }
496         });
497
498         setContextButton.addActionListener(new ActionListener() {
499             @Override
500             public void actionPerformed(ActionEvent e) {
501                 final SetContextDialog dialog = new SetContextDialog(SetupDialog.this, rootGraph, parameters);
502
503                 dialog.pack();
504                 dialog.setVisible(true);
505             }
506         });
507     }
508 }
509 // CSON: ClassDataAbstractionCoupling
510 // CSON: ClassFanOutComplexity
511 // CSON: MagicNumber