GUI: Move .ui files from goffice resources to glib resources
[gnumeric.git] / src / dialogs / dialog-simulation.c
blob285457f7c47f8070704a219c9852e8ef31e462aa
1 /* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3 * dialog-simulation.c:
5 * Authors:
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/>.
20 **/
21 #include <gnumeric-config.h>
22 #include <glib/gi18n-lib.h>
23 #include <gnumeric.h>
24 #include "dialogs.h"
25 #include "help.h"
27 #include <sheet.h>
28 #include <cell.h>
29 #include <ranges.h>
30 #include <gui-util.h>
31 #include <tool-dialogs.h>
32 #include <dao-gui-utils.h>
33 #include <value.h>
34 #include <wbc-gtk.h>
36 #include <widgets/gnumeric-expr-entry.h>
37 #include <widgets/gnm-dao.h>
38 #include "simulation.h"
40 #include <string.h>
41 #include <gtk/gtk.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;
51 /**
52 * simulation_update_sensitivity_cb:
53 * @dummy:
54 * @state:
56 * Update the dialog widgets sensitivity
57 **/
58 static void
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);
71 return;
72 } else
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);
81 return;
82 } else
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);
89 return;
92 gtk_label_set_text (GTK_LABEL (state->warning), "");
93 gtk_widget_set_sensitive (state->ok_button, TRUE);
94 return;
97 static gboolean
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))
104 return TRUE;
106 sim->ref_inputs = gnm_rangeref_dup (value_get_rangeref (sim->inputs));
107 sim->ref_outputs = gnm_rangeref_dup (value_get_rangeref (sim->outputs));
109 sim->n_input_vars =
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);
112 sim->n_output_vars =
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);
121 for (i = base_col;
122 i <= MAX (sim->ref_inputs->a.col, sim->ref_inputs->b.col); i++) {
123 for (n = base_row;
124 n<= MAX (sim->ref_inputs->a.row, sim->ref_inputs->b.row);
125 n++) {
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);
135 for (i = base_col;
136 i <= MAX (sim->ref_outputs->a.col, sim->ref_outputs->b.col); i++) {
137 for (n = base_row;
138 n<= MAX (sim->ref_outputs->a.row, sim->ref_outputs->b.row);
139 n++) {
140 GnmCell *cell = sheet_cell_fetch (sim->ref_outputs->a.sheet, i, n);
141 sim->list_outputs = g_slist_append (sim->list_outputs, cell);
145 return FALSE;
148 static void
149 update_log (SimulationState *state, simulation_t *sim)
151 char const *txt [6] = {
152 _("Simulations"), _("Iterations"), _("# Input variables"),
153 _("# Output variables"), _("Runtime"), _("Run on")
155 GtkTreeIter iter;
156 GtkListStore *store;
157 GtkTreePath *path;
158 GtkWidget *view;
159 GString *buf;
160 int i;
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);
168 switch (i) {
169 case 0:
170 g_string_append_printf (buf, "%d",
171 sim->last_round -
172 sim->first_round + 1);
173 break;
174 case 1:
175 g_string_append_printf (buf, "%d", sim->n_iterations);
176 break;
177 case 2:
178 g_string_append_printf (buf, "%d", sim->n_input_vars);
179 break;
180 case 3:
181 g_string_append_printf (buf, "%d", sim->n_output_vars);
182 break;
183 case 4:
184 g_string_append_printf (buf, "%.2" GNM_FORMAT_g,
185 sim->end.tv_sec -
186 sim->start.tv_sec +
187 (sim->end.tv_usec -
188 sim->start.tv_usec) /
189 (gnm_float) G_USEC_PER_SEC);
190 break;
191 case 5:
192 dao_append_date (buf);
193 break;
194 default:
195 g_string_append_printf (buf, "Error");
196 break;
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)) {
206 ; /* Do something */
207 } else {
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
215 (_("Name"),
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
220 (_("Value"),
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);
226 static void
227 update_results_view (simulation_t *sim)
229 GString *buf;
230 int i;
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",
240 sim->cellnames [i],
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);
249 static void
250 prev_button_cb (G_GNUC_UNUSED GtkWidget *button,
251 SimulationState *state)
253 GtkWidget *w;
255 if (results_sim_index > current_sim->first_round)
256 --results_sim_index;
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);
268 static void
269 next_button_cb (G_GNUC_UNUSED GtkWidget *button,
270 SimulationState *state)
272 GtkWidget *w;
274 if (results_sim_index < current_sim->last_round)
275 ++results_sim_index;
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:
289 * @button:
290 * @state:
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.
296 static void
297 simulation_ok_clicked_cb (G_GNUC_UNUSED GtkWidget *button,
298 SimulationState *state)
300 data_analysis_output_t dao;
301 GtkWidget *w;
302 gchar const *err;
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");
317 goto out;
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."
333 goto out;
336 current_sim = &sim;
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),
343 &dao, &sim);
344 g_get_current_time (&sim.end);
346 if (err == NULL) {
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);
356 out:
357 value_release (sim.inputs);
358 value_release (sim.outputs);
360 if (err != NULL)
361 error_in_entry ((GenericToolState *) state,
362 GTK_WIDGET (state->input_entry_2), _(err));
363 return;
368 * cb_tool_close_clicked:
369 * @button:
370 * @state:
372 * Close (destroy) the dialog
374 static void
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);
382 static void
383 init_results_view (SimulationState *state)
385 GtkTextView *view;
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,
391 "results-view"));
392 gtk_text_view_set_buffer (view, results_buffer);
396 * dialog_simulation:
397 * @wbcg:
398 * @sheet:
400 * Show the dialog (guru).
403 void
404 dialog_simulation (WBCGtk *wbcg, G_GNUC_UNUSED Sheet *sheet)
406 SimulationState *state;
407 WorkbookControl *wbc;
408 GtkWidget *w;
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))
416 return;
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."),
423 SIMULATION_KEY,
424 G_CALLBACK (simulation_ok_clicked_cb),
425 G_CALLBACK (cb_tool_cancel_clicked),
426 G_CALLBACK (simulation_update_sensitivity_cb),
428 return;
430 init_results_view (state);
431 current_sim = NULL;
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);
443 gtk_widget_hide (w);
444 w = go_gtk_builder_get_widget (state->gui, "max-button");
445 gtk_widget_set_sensitive (w, FALSE);
446 gtk_widget_hide (w);
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);
454 return;