5 * Jukka-Pekka Iivonen <jiivonen@hutcs.cs.hut.fi>
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, see <https://www.gnu.org/licenses/>.
20 #include <gnumeric-config.h>
21 #include <glib/gi18n-lib.h>
23 #include <dialogs/dialogs.h>
24 #include <dialogs/help.h>
30 #include <dialogs/tool-dialogs.h>
31 #include <dialogs/dao-gui-utils.h>
35 #include <widgets/gnm-expr-entry.h>
36 #include <widgets/gnm-dao.h>
37 #include <tools/simulation.h>
41 #define SIMULATION_KEY "simulation-dialog"
43 typedef GnmGenericToolState SimulationState
;
45 static GtkTextBuffer
*results_buffer
;
46 static int results_sim_index
;
47 static simulation_t
*current_sim
;
50 * simulation_update_sensitivity_cb:
54 * Update the dialog widgets sensitivity
57 simulation_update_sensitivity_cb (G_GNUC_UNUSED GtkWidget
*dummy
,
58 SimulationState
*state
)
60 GnmValue
*input_range
= NULL
;
61 GnmValue
*output_vars
= NULL
;
63 input_range
= gnm_expr_entry_parse_as_value (
64 GNM_EXPR_ENTRY (state
->input_entry
), state
->sheet
);
65 if (input_range
== NULL
) {
66 gtk_label_set_text (GTK_LABEL (state
->warning
),
67 _("The input variable range is invalid."));
68 gtk_widget_set_sensitive (state
->ok_button
, FALSE
);
71 value_release (input_range
);
73 output_vars
= gnm_expr_entry_parse_as_value
74 (state
->input_entry_2
, state
->sheet
);
75 if (output_vars
== NULL
) {
76 gtk_label_set_text (GTK_LABEL (state
->warning
),
77 _("The output variable range is invalid."));
78 gtk_widget_set_sensitive (state
->ok_button
, FALSE
);
81 value_release (output_vars
);
83 if (!gnm_dao_is_ready (GNM_DAO (state
->gdao
))) {
84 gtk_label_set_text (GTK_LABEL (state
->warning
),
85 _("The output range is invalid."));
86 gtk_widget_set_sensitive (state
->ok_button
, FALSE
);
90 gtk_label_set_text (GTK_LABEL (state
->warning
), "");
91 gtk_widget_set_sensitive (state
->ok_button
, TRUE
);
96 prepare_ranges (simulation_t
*sim
)
98 int i
, n
, base_col
, base_row
;
100 if (!VALUE_IS_CELLRANGE (sim
->inputs
) ||
101 !VALUE_IS_CELLRANGE (sim
->outputs
))
104 sim
->ref_inputs
= gnm_rangeref_dup (value_get_rangeref (sim
->inputs
));
105 sim
->ref_outputs
= gnm_rangeref_dup (value_get_rangeref (sim
->outputs
));
108 (abs (sim
->ref_inputs
->a
.col
- sim
->ref_inputs
->b
.col
) + 1) *
109 (abs (sim
->ref_inputs
->a
.row
- sim
->ref_inputs
->b
.row
) + 1);
111 (abs (sim
->ref_outputs
->a
.col
- sim
->ref_outputs
->b
.col
) + 1) *
112 (abs (sim
->ref_outputs
->a
.row
- sim
->ref_outputs
->b
.row
) + 1);
113 sim
->n_vars
= sim
->n_input_vars
+ sim
->n_output_vars
;
115 /* Get the intput cells into a list. */
116 sim
->list_inputs
= NULL
;
117 base_col
= MIN (sim
->ref_inputs
->a
.col
, sim
->ref_inputs
->b
.col
);
118 base_row
= MIN (sim
->ref_inputs
->a
.row
, sim
->ref_inputs
->b
.row
);
120 i
<= MAX (sim
->ref_inputs
->a
.col
, sim
->ref_inputs
->b
.col
); i
++) {
122 n
<= MAX (sim
->ref_inputs
->a
.row
, sim
->ref_inputs
->b
.row
);
124 GnmCell
*cell
= sheet_cell_fetch (sim
->ref_inputs
->a
.sheet
, i
, n
);
125 sim
->list_inputs
= g_slist_append (sim
->list_inputs
, cell
);
129 /* Get the output cells into a list. */
130 sim
->list_outputs
= NULL
;
131 base_col
= MIN (sim
->ref_outputs
->a
.col
, sim
->ref_outputs
->b
.col
);
132 base_row
= MIN (sim
->ref_outputs
->a
.row
, sim
->ref_outputs
->b
.row
);
134 i
<= MAX (sim
->ref_outputs
->a
.col
, sim
->ref_outputs
->b
.col
); i
++) {
136 n
<= MAX (sim
->ref_outputs
->a
.row
, sim
->ref_outputs
->b
.row
);
138 GnmCell
*cell
= sheet_cell_fetch (sim
->ref_outputs
->a
.sheet
, i
, n
);
139 sim
->list_outputs
= g_slist_append (sim
->list_outputs
, cell
);
147 update_log (SimulationState
*state
, simulation_t
*sim
)
149 char const *txt
[6] = {
150 _("Simulations"), _("Iterations"), _("# Input variables"),
151 _("# Output variables"), _("Runtime"), _("Run on")
160 view
= go_gtk_builder_get_widget (state
->gui
, "last-run-view");
162 store
= gtk_list_store_new (2, G_TYPE_STRING
, G_TYPE_STRING
);
164 for (i
= 0; i
< 6; i
++) {
165 buf
= g_string_new (NULL
);
168 g_string_append_printf (buf
, "%d",
170 sim
->first_round
+ 1);
173 g_string_append_printf (buf
, "%d", sim
->n_iterations
);
176 g_string_append_printf (buf
, "%d", sim
->n_input_vars
);
179 g_string_append_printf (buf
, "%d", sim
->n_output_vars
);
182 g_string_append_printf (buf
, "%.2" GNM_FORMAT_g
,
186 sim
->start
.tv_usec
) /
187 (gnm_float
) G_USEC_PER_SEC
);
190 dao_append_date (buf
);
193 g_string_append_printf (buf
, "Error");
197 gtk_list_store_append (store
, &iter
);
198 gtk_list_store_set (store
, &iter
, 0, txt
[i
], 1, buf
->str
, -1);
199 g_string_free (buf
, FALSE
);
202 path
= gtk_tree_path_new_from_string ("0");
203 if (gtk_tree_model_get_iter (GTK_TREE_MODEL (store
), &iter
, path
)) {
206 g_warning ("Did not get a valid iterator");
208 gtk_tree_path_free (path
);
210 gtk_tree_view_append_column
211 (GTK_TREE_VIEW (view
),
212 gtk_tree_view_column_new_with_attributes
214 gtk_cell_renderer_text_new (), "text", 0, NULL
));
215 gtk_tree_view_append_column
216 (GTK_TREE_VIEW (view
),
217 gtk_tree_view_column_new_with_attributes
219 gtk_cell_renderer_text_new (), "text", 1, NULL
));
220 gtk_tree_view_set_model (GTK_TREE_VIEW (view
), GTK_TREE_MODEL (store
));
221 g_object_unref (store
);
225 update_results_view (simulation_t
*sim
)
230 buf
= g_string_new (NULL
);
232 g_string_append_printf (buf
, "Simulation #%d\n\n", results_sim_index
+ 1);
233 g_string_append_printf (buf
, "%-20s %10s %10s %10s\n", _("Variable"),
234 _("Min"), _("Average"), _("Max"));
235 for (i
= 0; i
< sim
->n_vars
; i
++)
236 g_string_append_printf (buf
, "%-20s %10" GNM_FORMAT_g
" %10"
237 GNM_FORMAT_G
" %10" GNM_FORMAT_g
"\n",
239 sim
->stats
[results_sim_index
]->min
[i
],
240 sim
->stats
[results_sim_index
]->mean
[i
],
241 sim
->stats
[results_sim_index
]->max
[i
]);
243 gtk_text_buffer_set_text (results_buffer
, buf
->str
, strlen (buf
->str
));
244 g_string_free (buf
, FALSE
);
248 prev_button_cb (G_GNUC_UNUSED GtkWidget
*button
,
249 SimulationState
*state
)
253 if (results_sim_index
> current_sim
->first_round
)
256 if (results_sim_index
== current_sim
->first_round
) {
257 w
= go_gtk_builder_get_widget (state
->gui
, "prev-button");
258 gtk_widget_set_sensitive (w
, FALSE
);
261 w
= go_gtk_builder_get_widget (state
->gui
, "next-button");
262 gtk_widget_set_sensitive (w
, TRUE
);
263 update_results_view (current_sim
);
267 next_button_cb (G_GNUC_UNUSED GtkWidget
*button
,
268 SimulationState
*state
)
272 if (results_sim_index
< current_sim
->last_round
)
275 if (results_sim_index
== current_sim
->last_round
) {
276 w
= go_gtk_builder_get_widget (state
->gui
, "next-button");
277 gtk_widget_set_sensitive (w
, FALSE
);
280 w
= go_gtk_builder_get_widget (state
->gui
, "prev-button");
281 gtk_widget_set_sensitive (w
, TRUE
);
282 update_results_view (current_sim
);
286 * simulation_ok_clicked_cb:
290 * Retrieve the information from the dialog and call the advanced_filter.
291 * Note that we assume that the ok_button is only active if the entry fields
292 * contain sensible data.
295 simulation_ok_clicked_cb (G_GNUC_UNUSED GtkWidget
*button
,
296 SimulationState
*state
)
298 data_analysis_output_t dao
;
301 static simulation_t sim
;
303 simulation_tool_destroy (current_sim
);
305 sim
.inputs
= gnm_expr_entry_parse_as_value
306 (GNM_EXPR_ENTRY (state
->input_entry
), state
->sheet
);
308 sim
.outputs
= gnm_expr_entry_parse_as_value
309 (state
->input_entry_2
, state
->sheet
);
311 parse_output ((GnmGenericToolState
*) state
, &dao
);
313 if (prepare_ranges (&sim
)) {
314 err
= (gchar
*) N_("Invalid variable range was given");
318 w
= go_gtk_builder_get_widget (state
->gui
, "iterations");
319 sim
.n_iterations
= gtk_spin_button_get_value (GTK_SPIN_BUTTON (w
));
321 w
= go_gtk_builder_get_widget (state
->gui
, "first_round");
322 sim
.first_round
= gtk_spin_button_get_value (GTK_SPIN_BUTTON (w
)) - 1;
324 w
= go_gtk_builder_get_widget (state
->gui
, "last_round");
325 sim
.last_round
= gtk_spin_button_get_value (GTK_SPIN_BUTTON (w
)) - 1;
327 if (sim
.first_round
> sim
.last_round
) {
328 err
= (gchar
*) N_("First round number should be less than or "
329 "equal to the number of the last round."
336 w
= go_gtk_builder_get_widget (state
->gui
, "max-time");
337 sim
.max_time
= gtk_spin_button_get_value (GTK_SPIN_BUTTON (w
)) - 1;
339 g_get_current_time (&sim
.start
);
340 err
= simulation_tool (GNM_WBC (state
->wbcg
),
342 g_get_current_time (&sim
.end
);
345 results_sim_index
= sim
.first_round
;
346 update_log (state
, &sim
);
347 update_results_view (&sim
);
349 if (sim
.last_round
> results_sim_index
) {
350 w
= go_gtk_builder_get_widget (state
->gui
, "next-button");
351 gtk_widget_set_sensitive (w
, TRUE
);
355 value_release (sim
.inputs
);
356 value_release (sim
.outputs
);
359 error_in_entry ((GnmGenericToolState
*) state
,
360 GTK_WIDGET (state
->input_entry_2
), _(err
));
366 * cb_tool_close_clicked:
370 * Close (destroy) the dialog
373 cb_tool_cancel_clicked (G_GNUC_UNUSED GtkWidget
*button
,
374 GnmGenericToolState
*state
)
376 simulation_tool_destroy (current_sim
);
377 gtk_widget_destroy (state
->dialog
);
381 init_results_view (SimulationState
*state
)
384 GtkTextTagTable
*tag_table
;
386 tag_table
= gtk_text_tag_table_new ();
387 results_buffer
= gtk_text_buffer_new (tag_table
);
388 view
= GTK_TEXT_VIEW (go_gtk_builder_get_widget (state
->gui
,
390 gtk_text_view_set_buffer (view
, results_buffer
);
398 * Show the dialog (guru).
402 dialog_simulation (WBCGtk
*wbcg
, G_GNUC_UNUSED Sheet
*sheet
)
404 SimulationState
*state
;
405 WorkbookControl
*wbc
;
408 g_return_if_fail (wbcg
!= NULL
);
410 wbc
= GNM_WBC (wbcg
);
412 /* Only pop up one copy per workbook */
413 if (gnm_dialog_raise_if_exists (wbcg
, SIMULATION_KEY
))
416 state
= g_new (SimulationState
, 1);
417 if (dialog_tool_init (state
, wbcg
, wb_control_cur_sheet (wbc
),
418 GNUMERIC_HELP_LINK_SIMULATION
,
419 "res:ui/simulation.ui", "Simulation",
420 _("Could not create the Simulation dialog."),
422 G_CALLBACK (simulation_ok_clicked_cb
),
423 G_CALLBACK (cb_tool_cancel_clicked
),
424 G_CALLBACK (simulation_update_sensitivity_cb
),
428 init_results_view (state
);
431 w
= go_gtk_builder_get_widget (state
->gui
, "prev-button");
432 gtk_widget_set_sensitive (w
, FALSE
);
433 g_signal_connect_after (G_OBJECT (w
), "clicked",
434 G_CALLBACK (prev_button_cb
), state
);
435 w
= go_gtk_builder_get_widget (state
->gui
, "next-button");
436 g_signal_connect_after (G_OBJECT (w
), "clicked",
437 G_CALLBACK (next_button_cb
), state
);
438 gtk_widget_set_sensitive (w
, FALSE
);
439 w
= go_gtk_builder_get_widget (state
->gui
, "min-button");
440 gtk_widget_set_sensitive (w
, FALSE
);
442 w
= go_gtk_builder_get_widget (state
->gui
, "max-button");
443 gtk_widget_set_sensitive (w
, FALSE
);
446 gnm_dialog_setup_destroy_handlers (GTK_DIALOG (state
->dialog
), wbcg
,
447 GNM_DIALOG_DESTROY_SHEET_REMOVED
);
449 gnm_dao_set_put (GNM_DAO (state
->gdao
), FALSE
, FALSE
);
450 simulation_update_sensitivity_cb (NULL
, state
);
451 tool_load_selection ((GnmGenericToolState
*)state
, TRUE
);