2 * dialog-scenarios.c: Create, edit, and view scenarios.
5 * Jukka-Pekka Iivonen <jiivonen@hutcs.cs.hut.fi>
7 * (C) Copyright 2003 by Jukka-Pekka Iivonen <jiivonen@hutcs.cs.hut.fi>
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, see <https://www.gnu.org/licenses/>.
22 #include <gnumeric-config.h>
23 #include <glib/gi18n-lib.h>
30 #include <workbook-control.h>
34 #include <dao-gui-utils.h>
40 #include "scenarios.h"
43 #include <goffice/goffice.h>
46 #define DARK_GRAY GO_COLOR_GREY(51) /* "gray20" */
47 #define LIGHT_GRAY GO_COLOR_GREY(199) /* "gray78" */
50 GnmGenericToolState base
;
52 GtkWidget
*show_button
;
53 GtkWidget
*delete_button
;
54 GtkWidget
*summary_button
;
55 GtkWidget
*name_entry
;
57 GtkWidget
*scenarios_treeview
;
58 GSList
*new_report_sheets
;
64 typedef GnmValue
* (*ScenarioValueCB
) (int col
, int row
, GnmValue
*v
, gpointer data
);
67 scenario_for_each_value (GnmScenario
*s
, ScenarioValueCB fn
, gpointer data
)
71 /* Ok button pressed. */
73 scenario_manager_ok (Sheet
*sheet
)
75 GList
*l
, *scenarios
= g_list_copy (sheet
->scenarios
);
77 /* Update scenarios (free the deleted ones). */
78 for (l
= scenarios
; l
; l
= l
->next
) {
79 GnmScenario
*sc
= l
->data
;
80 if (g_object_get_data (G_OBJECT (sc
), "marked_deleted")) {
81 gnm_sheet_scenario_remove (sc
->sheet
, sc
);
84 g_list_free (scenarios
);
86 sheet_redraw_all (sheet
, TRUE
);
89 /* Cancel button pressed. */
91 scenario_recover_all (Sheet
*sheet
)
95 for (l
= sheet
->scenarios
; l
; l
= l
->next
) {
96 GnmScenario
*sc
= l
->data
;
97 g_object_set_data (G_OBJECT (sc
),
98 "marked_deleted", GUINT_TO_POINTER (FALSE
));
102 /* Scenario: Create summary report ***************************************/
105 rm_fun_cb (gpointer key
, gpointer value
, gpointer user_data
)
111 data_analysis_output_t dao
;
114 GHashTable
*names
; /* A hash table for cell names->row. */
121 summary_cb (int col
, int row
, GnmValue
*v
, summary_cb_t
*p
)
123 char *tmp
= dao_find_name (p
->sheet
, col
, row
);
126 /* Check if some of the previous scenarios already included that
127 * cell. If so, it's row will be put into *index. */
128 index
= g_hash_table_lookup (p
->names
, tmp
);
130 dao_set_cell_value (&p
->dao
, 2 + p
->col
, 3 + *index
,
133 /* Set the colors. */
134 dao_set_colors (&p
->dao
, 2 + p
->col
, 3 + *index
,
135 2 + p
->col
, 3 + *index
,
136 gnm_color_new_go (GO_COLOR_BLACK
),
137 gnm_color_new_go (LIGHT_GRAY
));
144 /* Changing cell name. */
145 dao_set_cell (&p
->dao
, 0, 3 + p
->row
, tmp
);
147 /* GnmValue of the cell in this scenario. */
148 dao_set_cell_value (&p
->dao
, 2 + p
->col
, 3 + p
->row
,
151 /* Current value of the cell. */
152 cell
= sheet_cell_fetch (p
->sheet
, col
, row
);
153 dao_set_cell_value (&p
->dao
, 1, 3 + p
->row
,
154 value_dup (cell
->value
));
156 /* Set the colors. */
157 dao_set_colors (&p
->dao
, 2 + p
->col
, 3 + p
->row
,
158 2 + p
->col
, 3 + p
->row
,
159 gnm_color_new_go (GO_COLOR_BLACK
),
160 gnm_color_new_go (LIGHT_GRAY
));
162 /* Insert row number into the hash table. */
165 g_hash_table_insert (p
->names
, tmp
, r
);
167 /* Increment the nbr of rows. */
175 scenario_summary_res_cells (WorkbookControl
*wbc
, GSList
*results
,
181 scenario_summary (WorkbookControl
*wbc
,
188 GList
*scenarios
= sheet
->scenarios
;
190 /* Initialize: Currently only new sheet output supported. */
191 dao_init_new_sheet (&cb
.dao
);
192 dao_prepare_output (wbc
, &cb
.dao
, _("Scenario Summary"));
195 dao_set_cell (&cb
.dao
, 1, 1, _("Current Values"));
196 dao_set_cell (&cb
.dao
, 0, 2, _("Changing Cells:"));
198 /* Go through all scenarios. */
200 cb
.names
= g_hash_table_new (g_str_hash
, g_str_equal
);
202 cb
.results
= results
;
203 for (cb
.col
= 0, cur
= scenarios
; cur
!= NULL
; cb
.col
++,
205 GnmScenario
*s
= cur
->data
;
208 dao_set_cell (&cb
.dao
, 2 + cb
.col
, 1, s
->name
);
210 scenario_for_each_value (s
, (ScenarioValueCB
) summary_cb
, &cb
);
213 /* Set the alignment of names of the changing cells to be right. */
214 dao_set_align (&cb
.dao
, 0, 3, 0, 2 + cb
.row
, GNM_HALIGN_RIGHT
,
219 scenario_summary_res_cells (wbc
, results
, &cb
);
221 /* Destroy the hash table. */
222 g_hash_table_foreach (cb
.names
, (GHFunc
) rm_fun_cb
, NULL
);
223 g_hash_table_destroy (cb
.names
);
225 /* Clean up the report output. */
226 dao_set_bold (&cb
.dao
, 0, 0, 0, 2 + cb
.row
);
227 dao_autofit_columns (&cb
.dao
);
228 dao_set_cell (&cb
.dao
, 0, 0, _("Scenario Summary"));
230 dao_set_colors (&cb
.dao
, 0, 0, cb
.col
+ 1, 1,
231 gnm_color_new_go (GO_COLOR_WHITE
),
232 gnm_color_new_go (DARK_GRAY
));
233 dao_set_colors (&cb
.dao
, 0, 2, 0, 2 + cb
.row
,
234 gnm_color_new_go (GO_COLOR_BLACK
),
235 gnm_color_new_go (LIGHT_GRAY
));
237 dao_set_align (&cb
.dao
, 1, 1, cb
.col
+ 1, 1, GNM_HALIGN_RIGHT
,
240 *new_sheet
= cb
.dao
.sheet
;
243 /********* Scenario Add UI **********************************************/
246 scenario_name_used (const GList
*scenarios
, const gchar
*name
)
248 while (scenarios
!= NULL
) {
249 const GnmScenario
*s
= scenarios
->data
;
251 if (strcmp (s
->name
, name
) == 0)
253 scenarios
= scenarios
->next
;
259 * A scenario name should have at least one printable character.
262 check_name (const gchar
*n
)
264 while (*n
&& g_unichar_isspace (g_utf8_get_char (n
)))
265 n
= g_utf8_next_char (n
);
274 * scenario_add_ok_clicked_cb:
278 * Run the scenario manager tool.
281 scenario_add_ok_clicked_cb (G_GNUC_UNUSED GtkWidget
*button
,
282 ScenariosState
*state
)
284 data_analysis_output_t dao
;
285 WorkbookControl
*wbc
;
288 GnmValue
*cell_range
;
289 GtkWidget
*entry
, *comment_view
;
291 GtkTextIter start
, end
;
295 cell_range
= gnm_expr_entry_parse_as_value
296 (GNM_EXPR_ENTRY (state
->base
.input_entry
), state
->base
.sheet
);
298 if (!cell_range
|| !gnm_sheet_range_from_value (&sr
, cell_range
)) {
299 go_gtk_notice_dialog (GTK_WINDOW (state
->base
.dialog
),
301 _("Invalid changing cells"));
302 gnm_expr_entry_grab_focus (state
->base
.input_entry
, TRUE
);
306 if (sr
.sheet
&& sr
.sheet
!= state
->base
.sheet
) {
307 go_gtk_notice_dialog (GTK_WINDOW (state
->base
.dialog
),
309 _("Changing cells should be on the current "
311 gnm_expr_entry_grab_focus (state
->base
.input_entry
, TRUE
);
314 entry
= go_gtk_builder_get_widget (state
->base
.gui
, "name_entry");
316 name
= g_strdup (gtk_entry_get_text (GTK_ENTRY (entry
)));
317 if (scenario_name_used (state
->base
.sheet
->scenarios
, name
)) {
319 go_gtk_notice_dialog (GTK_WINDOW (state
->base
.dialog
),
321 _("Scenario name already used"));
323 } else if (check_name (name
)) {
325 go_gtk_notice_dialog (GTK_WINDOW (state
->base
.dialog
),
327 _("Invalid scenario name"));
331 comment_view
= go_gtk_builder_get_widget (state
->base
.gui
, "comment_view");
332 buf
= gtk_text_view_get_buffer (GTK_TEXT_VIEW (comment_view
));
333 gtk_text_buffer_get_start_iter (buf
, &start
);
334 gtk_text_buffer_get_end_iter (buf
, &end
);
335 comment
= g_strdup (gtk_text_buffer_get_text (buf
, &start
, &end
,
338 dao_init_new_sheet (&dao
);
339 dao
.sheet
= state
->base
.sheet
;
341 wbc
= GNM_WBC (state
->base
.wbcg
);
343 sc
= gnm_sheet_scenario_new (state
->base
.sheet
, name
);
344 if (comment
&& comment
[0])
345 gnm_scenario_set_comment (sc
, comment
);
346 gnm_scenario_add_area (sc
, &sr
);
348 cmd_scenario_add (wbc
, sc
, state
->base
.sheet
);
352 gtk_widget_destroy (state
->base
.dialog
);
354 value_release (cell_range
);
359 * scenario_add_update_sensitivity_cb:
363 * Update the dialog widgets sensitivity
366 scenario_add_update_sensitivity_cb (G_GNUC_UNUSED GtkWidget
*dummy
,
367 ScenariosState
*state
)
369 gtk_widget_set_sensitive (state
->base
.ok_button
, TRUE
);
373 dialog_scenario_add (WBCGtk
*wbcg
)
375 ScenariosState
*state
;
376 WorkbookControl
*wbc
;
377 GtkWidget
*comment_view
;
378 char const *error_str
= _("Could not create the Scenario Add dialog.");
384 wbc
= GNM_WBC (wbcg
);
386 /* Only pop up one copy per workbook */
387 if (gnm_dialog_raise_if_exists (wbcg
, "ScenarioAdd"))
390 state
= g_new (ScenariosState
, 1);
392 if (dialog_tool_init (&state
->base
, wbcg
, wb_control_cur_sheet (wbc
),
393 GNUMERIC_HELP_LINK_SCENARIOS_ADD
,
394 "res:ui/scenario-add.ui", "ScenarioAdd",
397 G_CALLBACK (scenario_add_ok_clicked_cb
), NULL
,
398 G_CALLBACK (scenario_add_update_sensitivity_cb
),
399 GNM_EE_SHEET_OPTIONAL
))
405 state
->name_entry
= go_gtk_builder_get_widget (state
->base
.gui
, "name_entry");
406 if (state
->name_entry
== NULL
)
409 comment_view
= go_gtk_builder_get_widget (state
->base
.gui
, "comment_view");
410 if (comment_view
== NULL
)
412 buf
= g_string_new (NULL
);
413 g_string_append_printf (buf
, _("Created on "));
414 dao_append_date (buf
);
415 gtk_text_buffer_set_text (gtk_text_view_get_buffer
416 (GTK_TEXT_VIEW (comment_view
)), buf
->str
,
418 g_string_free (buf
, FALSE
);
420 state
->base
.gdao
= NULL
;
422 gnm_dialog_setup_destroy_handlers (GTK_DIALOG (state
->base
.dialog
),
424 GNM_DIALOG_DESTROY_SHEET_REMOVED
);
426 gnm_editable_enters (GTK_WINDOW (state
->base
.dialog
),
427 GTK_WIDGET (state
->name_entry
));
428 scenario_add_update_sensitivity_cb (NULL
, state
);
429 tool_load_selection ((GnmGenericToolState
*)state
, TRUE
);
433 /********* Scenario Manager UI ******************************************/
436 update_comment (ScenariosState
*state
, const gchar
*cells
,
437 const gchar
*comment
)
442 /* Update changing cells box */
443 w
= go_gtk_builder_get_widget (state
->base
.gui
, "changing_cells_entry");
444 gtk_entry_set_text (GTK_ENTRY (w
), cells
);
446 /* Update comment text view */
447 w
= go_gtk_builder_get_widget (state
->base
.gui
, "comment_view");
448 buf
= gtk_text_view_get_buffer (GTK_TEXT_VIEW (w
));
450 gtk_text_buffer_set_text (buf
, comment
, strlen (comment
));
454 set_selection_state (ScenariosState
*state
, gboolean f
)
456 /* Set the sensitivies to FALSE since no selections have been made */
457 gtk_widget_set_sensitive (state
->show_button
, f
);
458 gtk_widget_set_sensitive (state
->delete_button
, f
);
461 GtkTreeSelection
*selection
;
468 selection
= gtk_tree_view_get_selection
470 (state
->scenarios_treeview
));
471 if (!gtk_tree_selection_get_selected (selection
, NULL
, &iter
))
473 model
= gtk_tree_view_get_model
475 (state
->scenarios_treeview
));
477 gtk_tree_model_get (GTK_TREE_MODEL (model
), &iter
,
480 sc
= gnm_sheet_scenario_find (state
->base
.sheet
, name
);
483 cells_txt
= gnm_scenario_get_range_str (sc
);
484 update_comment (state
, cells_txt
, sc
->comment
);
487 update_comment (state
, "", "");
491 update_scenarios_treeview (GtkWidget
*view
, GList
*scenarios
)
497 store
= gtk_list_store_new (1, G_TYPE_STRING
);
499 while (scenarios
!= NULL
) {
500 GnmScenario
*s
= scenarios
->data
;
502 gtk_list_store_append (store
, &iter
);
503 gtk_list_store_set (store
, &iter
, 0, s
->name
, -1);
504 scenarios
= scenarios
->next
;
506 path
= gtk_tree_path_new_from_string ("0");
507 if (gtk_tree_model_get_iter (GTK_TREE_MODEL (store
),
513 gtk_tree_path_free (path
);
515 gtk_tree_view_set_model (GTK_TREE_VIEW (view
),
516 GTK_TREE_MODEL (store
));
517 g_object_unref (store
);
518 gtk_tree_view_append_column
519 (GTK_TREE_VIEW (view
),
520 gtk_tree_view_column_new_with_attributes
522 gtk_cell_renderer_text_new (), "text", 0, NULL
));
526 * scenarios_update_sensitivity_cb:
530 * Update the dialog widgets sensitivity
533 scenarios_update_sensitivity_cb (G_GNUC_UNUSED GtkWidget
*dummy
,
534 ScenariosState
*state
)
536 gtk_widget_set_sensitive (state
->base
.ok_button
, TRUE
);
541 scenario_manager_free (ScenariosState
*state
)
543 g_slist_free (state
->new_report_sheets
);
544 state
->new_report_sheets
= NULL
;
547 g_object_unref (state
->undo
);
553 * scenarios_ok_clicked_cb:
557 * Run the scenario manager tool.
560 scenarios_ok_clicked_cb (G_GNUC_UNUSED GtkWidget
*button
,
561 ScenariosState
*state
)
563 if (state
->current
) {
564 WorkbookControl
*wbc
= GNM_WBC (state
->base
.wbcg
);
565 cmd_scenario_mngr (wbc
, state
->current
, state
->undo
);
568 scenario_manager_ok (state
->base
.sheet
);
570 scenario_manager_free (state
);
571 gtk_widget_destroy (state
->base
.dialog
);
577 restore_old_values (ScenariosState
*state
)
581 if (state
->undo
== NULL
)
584 cc
= GO_CMD_CONTEXT (state
->base
.wbcg
);
585 go_undo_undo_with_data (state
->undo
, cc
);
586 g_object_unref (state
->undo
);
588 state
->current
= NULL
;
592 * scenarios_cancel_clicked_cb:
596 * Cancel clicked on the scenario manager tool.
599 scenarios_cancel_clicked_cb (G_GNUC_UNUSED GtkWidget
*button
,
600 ScenariosState
*state
)
603 WorkbookControl
*wbc
;
605 restore_old_values (state
);
607 wbc
= GNM_WBC (state
->base
.wbcg
);
609 /* Remove report sheets created on this dialog session. */
610 for (cur
= state
->new_report_sheets
; cur
!= NULL
;
612 Sheet
*sheet
= cur
->data
;
614 /* Check that if the focus is on a deleted sheet. */
615 if (wb_control_cur_sheet (wbc
) == sheet
)
616 wb_control_sheet_focus (wbc
, state
->base
.sheet
);
618 /* Delete a report sheet. */
619 workbook_sheet_delete (sheet
);
622 /* Recover the deleted scenarios. */
623 scenario_recover_all (state
->base
.sheet
);
625 scenario_manager_free (state
);
626 gtk_widget_destroy (state
->base
.dialog
);
630 scenarios_show_clicked_cb (G_GNUC_UNUSED GtkWidget
*button
,
631 ScenariosState
*state
)
633 GtkTreeSelection
*selection
;
638 selection
= gtk_tree_view_get_selection
639 (GTK_TREE_VIEW (state
->scenarios_treeview
));
640 if (!gtk_tree_selection_get_selected (selection
, NULL
, &iter
))
642 model
= gtk_tree_view_get_model
643 (GTK_TREE_VIEW (state
->scenarios_treeview
));
645 gtk_tree_model_get (GTK_TREE_MODEL (model
), &iter
, 0, &value
, -1);
647 restore_old_values (state
);
649 state
->current
= gnm_sheet_scenario_find (state
->base
.sheet
, value
);
650 state
->undo
= gnm_scenario_apply (state
->current
);
654 scenarios_delete_clicked_cb (G_GNUC_UNUSED GtkWidget
*button
,
655 ScenariosState
*state
)
657 data_analysis_output_t dao
;
658 GtkTreeSelection
*selection
;
662 gboolean all_deleted
;
666 restore_old_values (state
);
668 selection
= gtk_tree_view_get_selection
669 (GTK_TREE_VIEW (state
->scenarios_treeview
));
670 dao_init_new_sheet (&dao
);
671 dao
.sheet
= state
->base
.sheet
;
672 if (!gtk_tree_selection_get_selected (selection
, NULL
, &iter
))
674 model
= gtk_tree_view_get_model
675 (GTK_TREE_VIEW (state
->scenarios_treeview
));
677 gtk_tree_model_get (GTK_TREE_MODEL (model
), &iter
, 0, &value
, -1);
679 gtk_list_store_remove (GTK_LIST_STORE (model
), &iter
);
681 sc
= gnm_sheet_scenario_find (state
->base
.sheet
, value
);
683 g_object_set_data (G_OBJECT (sc
),
684 "marked_deleted", GUINT_TO_POINTER (TRUE
));
686 set_selection_state (state
, FALSE
);
689 for (l
= state
->base
.sheet
->scenarios
; l
&& all_deleted
; l
= l
->next
) {
690 GnmScenario
*sc
= l
->data
;
691 if (!g_object_get_data (G_OBJECT (sc
), "marked_deleted"))
695 gtk_widget_set_sensitive
696 (state
->summary_button
, !all_deleted
);
700 scenarios_summary_clicked_cb (G_GNUC_UNUSED GtkWidget
*button
,
701 ScenariosState
*state
)
706 restore_old_values (state
);
708 results
= gnm_expr_entry_parse_as_list (
709 GNM_EXPR_ENTRY (state
->base
.input_entry
), state
->base
.sheet
);
711 if (results
== NULL
) {
712 go_gtk_notice_dialog (GTK_WINDOW (state
->base
.dialog
),
714 _("Results entry did not contain valid "
719 scenario_summary (GNM_WBC (state
->base
.wbcg
), state
->base
.sheet
,
720 results
, &new_sheet
);
722 state
->new_report_sheets
=
723 g_slist_prepend (state
->new_report_sheets
,
726 g_slist_free_full (results
, (GDestroyNotify
)value_release
);
730 cb_selection_changed (G_GNUC_UNUSED GtkTreeSelection
*selection
,
731 ScenariosState
*state
)
733 set_selection_state (state
, TRUE
);
737 init_scenario_buttons (ScenariosState
*state
)
741 go_gtk_builder_get_widget (state
->base
.gui
, "show_button");
742 if (state
->show_button
== NULL
)
744 g_signal_connect (G_OBJECT (state
->show_button
),
746 G_CALLBACK (scenarios_show_clicked_cb
), state
);
749 state
->delete_button
=
750 go_gtk_builder_get_widget (state
->base
.gui
, "delete_button");
751 if (state
->delete_button
== NULL
)
753 g_signal_connect (G_OBJECT (state
->delete_button
),
755 G_CALLBACK (scenarios_delete_clicked_cb
), state
);
758 state
->summary_button
=
759 go_gtk_builder_get_widget (state
->base
.gui
, "summary_button");
760 if (state
->summary_button
== NULL
)
762 g_signal_connect (G_OBJECT (state
->summary_button
),
764 G_CALLBACK (scenarios_summary_clicked_cb
),
767 set_selection_state (state
, FALSE
);
773 dialog_scenarios (WBCGtk
*wbcg
)
775 ScenariosState
*state
;
776 WorkbookControl
*wbc
;
779 GtkTreeSelection
*select
;
780 char const *error_str
= _("Could not create the Scenarios dialog.");
782 g_return_if_fail (wbcg
!= NULL
);
784 wbc
= GNM_WBC (wbcg
);
785 sheet
= wb_control_cur_sheet (wbc
);
787 state
= g_new (ScenariosState
, 1);
788 state
->new_report_sheets
= NULL
;
789 state
->current
= NULL
;
791 state
->base
.wb
= wb_control_get_workbook (wbc
);
793 if (dialog_tool_init (&state
->base
, wbcg
, sheet
,
794 GNUMERIC_HELP_LINK_SCENARIOS_VIEW
,
795 "res:ui/scenario-manager.ui", "Scenarios",
796 error_str
, "Scenarios",
797 G_CALLBACK (scenarios_ok_clicked_cb
),
798 G_CALLBACK (scenarios_cancel_clicked_cb
),
799 G_CALLBACK (scenarios_update_sensitivity_cb
),
803 if (init_scenario_buttons (state
))
806 state
->scenarios_treeview
= go_gtk_builder_get_widget
807 (state
->base
.gui
, "scenarios_treeview");
808 if (state
->scenarios_treeview
== NULL
)
811 w
= go_gtk_builder_get_widget (state
->base
.gui
, "changing_cells_entry");
814 gtk_widget_set_sensitive (w
, FALSE
);
815 w
= go_gtk_builder_get_widget (state
->base
.gui
, "comment_view");
819 gtk_widget_set_sensitive (w
, FALSE
);
821 if (state
->base
.sheet
->scenarios
== NULL
)
822 gtk_widget_set_sensitive
823 (state
->summary_button
, FALSE
);
825 update_scenarios_treeview (state
->scenarios_treeview
,
827 select
= gtk_tree_view_get_selection
828 (GTK_TREE_VIEW (state
->scenarios_treeview
));
829 g_signal_connect (select
, "changed",
830 G_CALLBACK (cb_selection_changed
), state
);
832 scenarios_update_sensitivity_cb (NULL
, state
);
833 gtk_widget_show (state
->base
.dialog
);
838 go_gtk_notice_dialog (wbcg_toplevel (wbcg
), GTK_MESSAGE_ERROR
,