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