1 /* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
6 * Jukka-Pekka Iivonen <jiivonen@hutcs.cs.hut.fi>
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, see <https://www.gnu.org/licenses/>.
21 #include <gnumeric-config.h>
22 #include <glib/gi18n-lib.h>
31 #include <tool-dialogs.h>
32 #include <dao-gui-utils.h>
36 #include <widgets/gnumeric-expr-entry.h>
37 #include <widgets/gnm-dao.h>
38 #include "simulation.h"
43 #define SIMULATION_KEY "simulation-dialog"
45 typedef GenericToolState SimulationState
;
47 static GtkTextBuffer
*results_buffer
;
48 static int results_sim_index
;
49 static simulation_t
*current_sim
;
52 * simulation_update_sensitivity_cb:
56 * Update the dialog widgets sensitivity
59 simulation_update_sensitivity_cb (G_GNUC_UNUSED GtkWidget
*dummy
,
60 SimulationState
*state
)
62 GnmValue
*input_range
= NULL
;
63 GnmValue
*output_vars
= NULL
;
65 input_range
= gnm_expr_entry_parse_as_value (
66 GNM_EXPR_ENTRY (state
->input_entry
), state
->sheet
);
67 if (input_range
== NULL
) {
68 gtk_label_set_text (GTK_LABEL (state
->warning
),
69 _("The input variable range is invalid."));
70 gtk_widget_set_sensitive (state
->ok_button
, FALSE
);
73 value_release (input_range
);
75 output_vars
= gnm_expr_entry_parse_as_value
76 (state
->input_entry_2
, state
->sheet
);
77 if (output_vars
== NULL
) {
78 gtk_label_set_text (GTK_LABEL (state
->warning
),
79 _("The output variable range is invalid."));
80 gtk_widget_set_sensitive (state
->ok_button
, FALSE
);
83 value_release (output_vars
);
85 if (!gnm_dao_is_ready (GNM_DAO (state
->gdao
))) {
86 gtk_label_set_text (GTK_LABEL (state
->warning
),
87 _("The output range is invalid."));
88 gtk_widget_set_sensitive (state
->ok_button
, FALSE
);
92 gtk_label_set_text (GTK_LABEL (state
->warning
), "");
93 gtk_widget_set_sensitive (state
->ok_button
, TRUE
);
98 prepare_ranges (simulation_t
*sim
)
100 int i
, n
, base_col
, base_row
;
102 if (!VALUE_IS_CELLRANGE (sim
->inputs
) ||
103 !VALUE_IS_CELLRANGE (sim
->outputs
))
106 sim
->ref_inputs
= gnm_rangeref_dup (value_get_rangeref (sim
->inputs
));
107 sim
->ref_outputs
= gnm_rangeref_dup (value_get_rangeref (sim
->outputs
));
110 (abs (sim
->ref_inputs
->a
.col
- sim
->ref_inputs
->b
.col
) + 1) *
111 (abs (sim
->ref_inputs
->a
.row
- sim
->ref_inputs
->b
.row
) + 1);
113 (abs (sim
->ref_outputs
->a
.col
- sim
->ref_outputs
->b
.col
) + 1) *
114 (abs (sim
->ref_outputs
->a
.row
- sim
->ref_outputs
->b
.row
) + 1);
115 sim
->n_vars
= sim
->n_input_vars
+ sim
->n_output_vars
;
117 /* Get the intput cells into a list. */
118 sim
->list_inputs
= NULL
;
119 base_col
= MIN (sim
->ref_inputs
->a
.col
, sim
->ref_inputs
->b
.col
);
120 base_row
= MIN (sim
->ref_inputs
->a
.row
, sim
->ref_inputs
->b
.row
);
122 i
<= MAX (sim
->ref_inputs
->a
.col
, sim
->ref_inputs
->b
.col
); i
++) {
124 n
<= MAX (sim
->ref_inputs
->a
.row
, sim
->ref_inputs
->b
.row
);
126 GnmCell
*cell
= sheet_cell_fetch (sim
->ref_inputs
->a
.sheet
, i
, n
);
127 sim
->list_inputs
= g_slist_append (sim
->list_inputs
, cell
);
131 /* Get the output cells into a list. */
132 sim
->list_outputs
= NULL
;
133 base_col
= MIN (sim
->ref_outputs
->a
.col
, sim
->ref_outputs
->b
.col
);
134 base_row
= MIN (sim
->ref_outputs
->a
.row
, sim
->ref_outputs
->b
.row
);
136 i
<= MAX (sim
->ref_outputs
->a
.col
, sim
->ref_outputs
->b
.col
); i
++) {
138 n
<= MAX (sim
->ref_outputs
->a
.row
, sim
->ref_outputs
->b
.row
);
140 GnmCell
*cell
= sheet_cell_fetch (sim
->ref_outputs
->a
.sheet
, i
, n
);
141 sim
->list_outputs
= g_slist_append (sim
->list_outputs
, cell
);
149 update_log (SimulationState
*state
, simulation_t
*sim
)
151 char const *txt
[6] = {
152 _("Simulations"), _("Iterations"), _("# Input variables"),
153 _("# Output variables"), _("Runtime"), _("Run on")
162 view
= go_gtk_builder_get_widget (state
->gui
, "last-run-view");
164 store
= gtk_list_store_new (2, G_TYPE_STRING
, G_TYPE_STRING
);
166 for (i
= 0; i
< 6; i
++) {
167 buf
= g_string_new (NULL
);
170 g_string_append_printf (buf
, "%d",
172 sim
->first_round
+ 1);
175 g_string_append_printf (buf
, "%d", sim
->n_iterations
);
178 g_string_append_printf (buf
, "%d", sim
->n_input_vars
);
181 g_string_append_printf (buf
, "%d", sim
->n_output_vars
);
184 g_string_append_printf (buf
, "%.2" GNM_FORMAT_g
,
188 sim
->start
.tv_usec
) /
189 (gnm_float
) G_USEC_PER_SEC
);
192 dao_append_date (buf
);
195 g_string_append_printf (buf
, "Error");
199 gtk_list_store_append (store
, &iter
);
200 gtk_list_store_set (store
, &iter
, 0, txt
[i
], 1, buf
->str
, -1);
201 g_string_free (buf
, FALSE
);
204 path
= gtk_tree_path_new_from_string ("0");
205 if (gtk_tree_model_get_iter (GTK_TREE_MODEL (store
), &iter
, path
)) {
208 g_warning ("Did not get a valid iterator");
210 gtk_tree_path_free (path
);
212 gtk_tree_view_append_column
213 (GTK_TREE_VIEW (view
),
214 gtk_tree_view_column_new_with_attributes
216 gtk_cell_renderer_text_new (), "text", 0, NULL
));
217 gtk_tree_view_append_column
218 (GTK_TREE_VIEW (view
),
219 gtk_tree_view_column_new_with_attributes
221 gtk_cell_renderer_text_new (), "text", 1, NULL
));
222 gtk_tree_view_set_model (GTK_TREE_VIEW (view
), GTK_TREE_MODEL (store
));
223 g_object_unref (store
);
227 update_results_view (simulation_t
*sim
)
232 buf
= g_string_new (NULL
);
234 g_string_append_printf (buf
, "Simulation #%d\n\n", results_sim_index
+ 1);
235 g_string_append_printf (buf
, "%-20s %10s %10s %10s\n", _("Variable"),
236 _("Min"), _("Average"), _("Max"));
237 for (i
= 0; i
< sim
->n_vars
; i
++)
238 g_string_append_printf (buf
, "%-20s %10" GNM_FORMAT_g
" %10"
239 GNM_FORMAT_G
" %10" GNM_FORMAT_g
"\n",
241 sim
->stats
[results_sim_index
]->min
[i
],
242 sim
->stats
[results_sim_index
]->mean
[i
],
243 sim
->stats
[results_sim_index
]->max
[i
]);
245 gtk_text_buffer_set_text (results_buffer
, buf
->str
, strlen (buf
->str
));
246 g_string_free (buf
, FALSE
);
250 prev_button_cb (G_GNUC_UNUSED GtkWidget
*button
,
251 SimulationState
*state
)
255 if (results_sim_index
> current_sim
->first_round
)
258 if (results_sim_index
== current_sim
->first_round
) {
259 w
= go_gtk_builder_get_widget (state
->gui
, "prev-button");
260 gtk_widget_set_sensitive (w
, FALSE
);
263 w
= go_gtk_builder_get_widget (state
->gui
, "next-button");
264 gtk_widget_set_sensitive (w
, TRUE
);
265 update_results_view (current_sim
);
269 next_button_cb (G_GNUC_UNUSED GtkWidget
*button
,
270 SimulationState
*state
)
274 if (results_sim_index
< current_sim
->last_round
)
277 if (results_sim_index
== current_sim
->last_round
) {
278 w
= go_gtk_builder_get_widget (state
->gui
, "next-button");
279 gtk_widget_set_sensitive (w
, FALSE
);
282 w
= go_gtk_builder_get_widget (state
->gui
, "prev-button");
283 gtk_widget_set_sensitive (w
, TRUE
);
284 update_results_view (current_sim
);
288 * simulation_ok_clicked_cb:
292 * Retrieve the information from the dialog and call the advanced_filter.
293 * Note that we assume that the ok_button is only active if the entry fields
294 * contain sensible data.
297 simulation_ok_clicked_cb (G_GNUC_UNUSED GtkWidget
*button
,
298 SimulationState
*state
)
300 data_analysis_output_t dao
;
303 static simulation_t sim
;
305 simulation_tool_destroy (current_sim
);
307 sim
.inputs
= gnm_expr_entry_parse_as_value
308 (GNM_EXPR_ENTRY (state
->input_entry
), state
->sheet
);
310 sim
.outputs
= gnm_expr_entry_parse_as_value
311 (state
->input_entry_2
, state
->sheet
);
313 parse_output ((GenericToolState
*) state
, &dao
);
315 if (prepare_ranges (&sim
)) {
316 err
= (gchar
*) N_("Invalid variable range was given");
320 w
= go_gtk_builder_get_widget (state
->gui
, "iterations");
321 sim
.n_iterations
= gtk_spin_button_get_value (GTK_SPIN_BUTTON (w
));
323 w
= go_gtk_builder_get_widget (state
->gui
, "first_round");
324 sim
.first_round
= gtk_spin_button_get_value (GTK_SPIN_BUTTON (w
)) - 1;
326 w
= go_gtk_builder_get_widget (state
->gui
, "last_round");
327 sim
.last_round
= gtk_spin_button_get_value (GTK_SPIN_BUTTON (w
)) - 1;
329 if (sim
.first_round
> sim
.last_round
) {
330 err
= (gchar
*) N_("First round number should be less than or "
331 "equal to the number of the last round."
338 w
= go_gtk_builder_get_widget (state
->gui
, "max-time");
339 sim
.max_time
= gtk_spin_button_get_value (GTK_SPIN_BUTTON (w
)) - 1;
341 g_get_current_time (&sim
.start
);
342 err
= simulation_tool (GNM_WBC (state
->wbcg
),
344 g_get_current_time (&sim
.end
);
347 results_sim_index
= sim
.first_round
;
348 update_log (state
, &sim
);
349 update_results_view (&sim
);
351 if (sim
.last_round
> results_sim_index
) {
352 w
= go_gtk_builder_get_widget (state
->gui
, "next-button");
353 gtk_widget_set_sensitive (w
, TRUE
);
357 value_release (sim
.inputs
);
358 value_release (sim
.outputs
);
361 error_in_entry ((GenericToolState
*) state
,
362 GTK_WIDGET (state
->input_entry_2
), _(err
));
368 * cb_tool_close_clicked:
372 * Close (destroy) the dialog
375 cb_tool_cancel_clicked (G_GNUC_UNUSED GtkWidget
*button
,
376 GenericToolState
*state
)
378 simulation_tool_destroy (current_sim
);
379 gtk_widget_destroy (state
->dialog
);
383 init_results_view (SimulationState
*state
)
386 GtkTextTagTable
*tag_table
;
388 tag_table
= gtk_text_tag_table_new ();
389 results_buffer
= gtk_text_buffer_new (tag_table
);
390 view
= GTK_TEXT_VIEW (go_gtk_builder_get_widget (state
->gui
,
392 gtk_text_view_set_buffer (view
, results_buffer
);
400 * Show the dialog (guru).
404 dialog_simulation (WBCGtk
*wbcg
, G_GNUC_UNUSED Sheet
*sheet
)
406 SimulationState
*state
;
407 WorkbookControl
*wbc
;
410 g_return_if_fail (wbcg
!= NULL
);
412 wbc
= GNM_WBC (wbcg
);
414 /* Only pop up one copy per workbook */
415 if (gnm_dialog_raise_if_exists (wbcg
, SIMULATION_KEY
))
418 state
= g_new (SimulationState
, 1);
419 if (dialog_tool_init (state
, wbcg
, wb_control_cur_sheet (wbc
),
420 GNUMERIC_HELP_LINK_SIMULATION
,
421 "res:ui/simulation.ui", "Simulation",
422 _("Could not create the Simulation dialog."),
424 G_CALLBACK (simulation_ok_clicked_cb
),
425 G_CALLBACK (cb_tool_cancel_clicked
),
426 G_CALLBACK (simulation_update_sensitivity_cb
),
430 init_results_view (state
);
433 w
= go_gtk_builder_get_widget (state
->gui
, "prev-button");
434 gtk_widget_set_sensitive (w
, FALSE
);
435 g_signal_connect_after (G_OBJECT (w
), "clicked",
436 G_CALLBACK (prev_button_cb
), state
);
437 w
= go_gtk_builder_get_widget (state
->gui
, "next-button");
438 g_signal_connect_after (G_OBJECT (w
), "clicked",
439 G_CALLBACK (next_button_cb
), state
);
440 gtk_widget_set_sensitive (w
, FALSE
);
441 w
= go_gtk_builder_get_widget (state
->gui
, "min-button");
442 gtk_widget_set_sensitive (w
, FALSE
);
444 w
= go_gtk_builder_get_widget (state
->gui
, "max-button");
445 gtk_widget_set_sensitive (w
, FALSE
);
448 gnm_dialog_setup_destroy_handlers (GTK_DIALOG (state
->dialog
), wbcg
,
449 GNM_DIALOG_DESTROY_SHEET_REMOVED
);
451 gnm_dao_set_put (GNM_DAO (state
->gdao
), FALSE
, FALSE
);
452 simulation_update_sensitivity_cb (NULL
, state
);
453 tool_load_selection ((GenericToolState
*)state
, TRUE
);