1 /*
2  * dialog-scenarios.c:  Create, edit, and view scenarios.
3  *
4  * Author:
5  *        Jukka-Pekka Iivonen <jiivonen@hutcs.cs.hut.fi>
6  *
7  * (C) Copyright 2003 by Jukka-Pekka Iivonen <jiivonen@hutcs.cs.hut.fi>
8  *
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.
13  *
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.
18  *
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/>.
21  */
22 #include <gnumeric-config.h>
23 #include <glib/gi18n-lib.h>
24 #include <gnumeric.h>
25 #include <dialogs/dialogs.h>
26 #include <dialogs/help.h>
27 
28 #include <gui-util.h>
29 #include <commands.h>
30 #include <workbook-control.h>
31 #include <workbook.h>
32 #include <wbc-gtk.h>
33 #include <sheet.h>
34 #include <dialogs/dao-gui-utils.h>
35 #include <position.h>
36 #include <ranges.h>
37 #include <value.h>
38 #include <cell.h>
39 #include <tools/dao.h>
40 #include <tools/scenarios.h>
41 #include <dialogs/tool-dialogs.h>
42 
43 #include <goffice/goffice.h>
44 #include <string.h>
45 
46 #define DARK_GRAY  GO_COLOR_GREY(51)    /* "gray20" */
47 #define LIGHT_GRAY GO_COLOR_GREY(199)   /* "gray78" */
48 
49 typedef struct {
50 	GnmGenericToolState base;
51 
52 	GtkWidget  *show_button;
53 	GtkWidget  *delete_button;
54 	GtkWidget  *summary_button;
55         GtkWidget  *name_entry;
56 
57         GtkWidget  *scenarios_treeview;
58 	GSList     *new_report_sheets;
59 
60 	GOUndo *undo;
61 	GnmScenario *current;
62 } ScenariosState;
63 
64 typedef GnmValue * (*ScenarioValueCB) (int col, int row, GnmValue *v, gpointer data);
65 
66 static void
scenario_for_each_value(GnmScenario * s,ScenarioValueCB fn,gpointer data)67 scenario_for_each_value (GnmScenario *s, ScenarioValueCB fn, gpointer data)
68 {
69 }
70 
71 /* Ok button pressed. */
72 static void
scenario_manager_ok(Sheet * sheet)73 scenario_manager_ok (Sheet *sheet)
74 {
75 	GList *l, *scenarios = g_list_copy (sheet->scenarios);
76 
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);
82 		}
83 	}
84 	g_list_free (scenarios);
85 
86 	sheet_redraw_all (sheet, TRUE);
87 }
88 
89 /* Cancel button pressed. */
90 static void
scenario_recover_all(Sheet * sheet)91 scenario_recover_all (Sheet *sheet)
92 {
93 	GList *l;
94 
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));
99 	}
100 }
101 
102 /* Scenario: Create summary report ***************************************/
103 
104 static void
rm_fun_cb(gpointer key,gpointer value,gpointer user_data)105 rm_fun_cb (gpointer key, gpointer value, gpointer user_data)
106 {
107 	g_free (value);
108 }
109 
110 typedef struct {
111 	data_analysis_output_t dao;
112 
113 	Sheet      *sheet;
114 	GHashTable *names;  /* A hash table for cell names->row. */
115 	int        col;
116 	int        row;
117 	GSList     *results;
118 } summary_cb_t;
119 
120 static GnmValue *
summary_cb(int col,int row,GnmValue * v,summary_cb_t * p)121 summary_cb (int col, int row, GnmValue *v, summary_cb_t *p)
122 {
123 	char  *tmp = dao_find_name (p->sheet, col, row);
124 	int   *index;
125 
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);
129 	if (index != NULL) {
130 		dao_set_cell_value (&p->dao, 2 + p->col, 3 + *index,
131 				    value_dup (v));
132 
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));
138 
139 	} else {
140 		/* New cell. */
141 		GnmCell *cell;
142 		int  *r;
143 
144 		/* Changing cell name. */
145 		dao_set_cell (&p->dao, 0, 3 + p->row, tmp);
146 
147 		/* GnmValue of the cell in this scenario. */
148 		dao_set_cell_value (&p->dao, 2 + p->col, 3 + p->row,
149 				    value_dup (v));
150 
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));
155 
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));
161 
162 		/* Insert row number into the hash table. */
163 		r  = g_new (int, 1);
164 		*r = p->row;
165 		g_hash_table_insert (p->names, tmp, r);
166 
167 		/* Increment the nbr of rows. */
168 		p->row++;
169 	}
170 
171 	return v;
172 }
173 
174 static void
scenario_summary_res_cells(WorkbookControl * wbc,GSList * results,summary_cb_t * cb)175 scenario_summary_res_cells (WorkbookControl *wbc, GSList *results,
176 			    summary_cb_t *cb)
177 {
178 }
179 
180 static void
scenario_summary(WorkbookControl * wbc,Sheet * sheet,GSList * results,Sheet ** new_sheet)181 scenario_summary (WorkbookControl *wbc,
182 		  Sheet           *sheet,
183 		  GSList          *results,
184 		  Sheet           **new_sheet)
185 {
186 	summary_cb_t cb;
187 	GList        *cur;
188 	GList        *scenarios = sheet->scenarios;
189 
190 	/* Initialize: Currently only new sheet output supported. */
191 	dao_init_new_sheet (&cb.dao);
192 	dao_prepare_output (wbc, &cb.dao, _("Scenario Summary"));
193 
194 	/* Titles. */
195 	dao_set_cell (&cb.dao, 1, 1, _("Current Values"));
196 	dao_set_cell (&cb.dao, 0, 2, _("Changing Cells:"));
197 
198 	/* Go through all scenarios. */
199 	cb.row     = 0;
200 	cb.names   = g_hash_table_new (g_str_hash, g_str_equal);
201 	cb.sheet   = sheet;
202 	cb.results = results;
203 	for (cb.col = 0, cur = scenarios; cur != NULL; cb.col++,
204 		     cur = cur->next) {
205 		GnmScenario *s = cur->data;
206 
207 		/* Scenario name. */
208 		dao_set_cell (&cb.dao, 2 + cb.col, 1, s->name);
209 
210 		scenario_for_each_value (s, (ScenarioValueCB) summary_cb, &cb);
211 	}
212 
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,
215 		       GNM_VALIGN_BOTTOM);
216 
217 	/* Result cells. */
218 	if (results != NULL)
219 		scenario_summary_res_cells (wbc, results, &cb);
220 
221 	/* Destroy the hash table. */
222 	g_hash_table_foreach (cb.names, (GHFunc) rm_fun_cb, NULL);
223 	g_hash_table_destroy (cb.names);
224 
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"));
229 
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));
236 
237 	dao_set_align (&cb.dao, 1, 1, cb.col + 1, 1, GNM_HALIGN_RIGHT,
238 		       GNM_VALIGN_BOTTOM);
239 
240 	*new_sheet = cb.dao.sheet;
241 }
242 
243 /********* Scenario Add UI **********************************************/
244 
245 static gboolean
scenario_name_used(const GList * scenarios,const gchar * name)246 scenario_name_used (const GList *scenarios, const gchar *name)
247 {
248 	while (scenarios != NULL) {
249 		const GnmScenario *s = scenarios->data;
250 
251 		if (strcmp (s->name, name) == 0)
252 			return TRUE;
253 		scenarios = scenarios->next;
254 	}
255 	return FALSE;
256 }
257 
258 /*
259  * A scenario name should have at least one printable character.
260  */
261 static gboolean
check_name(const gchar * n)262 check_name (const gchar *n)
263 {
264 	while (*n && g_unichar_isspace (g_utf8_get_char (n)))
265 		n = g_utf8_next_char (n);
266 
267 	if (*n)
268 		return FALSE;
269 	else
270 		return TRUE;
271 }
272 
273 /**
274  * scenario_add_ok_clicked_cb:
275  * @button:
276  * @state:
277  *
278  * Run the scenario manager tool.
279  **/
280 static void
scenario_add_ok_clicked_cb(G_GNUC_UNUSED GtkWidget * button,ScenariosState * state)281 scenario_add_ok_clicked_cb (G_GNUC_UNUSED GtkWidget *button,
282 			    ScenariosState *state)
283 {
284 	data_analysis_output_t  dao;
285 	WorkbookControl         *wbc;
286 	gchar                   *name;
287 	gchar                   *comment;
288 	GnmValue                   *cell_range;
289 	GtkWidget               *entry, *comment_view;
290 	GtkTextBuffer           *buf;
291 	GtkTextIter             start, end;
292 	GnmScenario             *sc;
293 	GnmSheetRange           sr;
294 
295 	cell_range = gnm_expr_entry_parse_as_value
296 		(GNM_EXPR_ENTRY (state->base.input_entry), state->base.sheet);
297 
298 	if (!cell_range || !gnm_sheet_range_from_value (&sr, cell_range)) {
299 		go_gtk_notice_dialog (GTK_WINDOW (state->base.dialog),
300 				 GTK_MESSAGE_ERROR,
301 				 _("Invalid changing cells"));
302 		gnm_expr_entry_grab_focus (state->base.input_entry, TRUE);
303 		return;
304 	}
305 
306 	if (sr.sheet && sr.sheet != state->base.sheet) {
307 		go_gtk_notice_dialog (GTK_WINDOW (state->base.dialog),
308 				 GTK_MESSAGE_ERROR,
309 				 _("Changing cells should be on the current "
310 				   "sheet only."));
311 		gnm_expr_entry_grab_focus (state->base.input_entry, TRUE);
312 		goto out;
313 	}
314 	entry = go_gtk_builder_get_widget (state->base.gui, "name_entry");
315 
316 	name = g_strdup (gtk_entry_get_text (GTK_ENTRY (entry)));
317 	if (scenario_name_used (state->base.sheet->scenarios, name)) {
318 	        g_free (name);
319 		go_gtk_notice_dialog (GTK_WINDOW (state->base.dialog),
320 				 GTK_MESSAGE_ERROR,
321 				 _("Scenario name already used"));
322 		goto out;
323 	} else if (check_name (name)) {
324 	        g_free (name);
325 		go_gtk_notice_dialog (GTK_WINDOW (state->base.dialog),
326 				 GTK_MESSAGE_ERROR,
327 				 _("Invalid scenario name"));
328 		goto out;
329 	}
330 
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,
336 						      FALSE));
337 
338 	dao_init_new_sheet (&dao);
339 	dao.sheet = state->base.sheet;
340 
341 	wbc = GNM_WBC (state->base.wbcg);
342 
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);
347 
348 	cmd_scenario_add (wbc, sc, state->base.sheet);
349 
350 	g_free (name);
351 	g_free (comment);
352 	gtk_widget_destroy (state->base.dialog);
353  out:
354 	value_release (cell_range);
355 	return;
356 }
357 
358 /**
359  * scenario_add_update_sensitivity_cb:
360  * @dummy:
361  * @state:
362  *
363  * Update the dialog widgets sensitivity
364  **/
365 static void
scenario_add_update_sensitivity_cb(G_GNUC_UNUSED GtkWidget * dummy,ScenariosState * state)366 scenario_add_update_sensitivity_cb (G_GNUC_UNUSED GtkWidget *dummy,
367 				    ScenariosState *state)
368 {
369 	gtk_widget_set_sensitive (state->base.ok_button, TRUE);
370 }
371 
372 void
dialog_scenario_add(WBCGtk * wbcg)373 dialog_scenario_add (WBCGtk *wbcg)
374 {
375         ScenariosState *state;
376 	WorkbookControl  *wbc;
377 	GtkWidget        *comment_view;
378 	char const *error_str = _("Could not create the Scenario Add dialog.");
379 	GString          *buf;
380 
381 	if (wbcg == NULL)
382 		return;
383 
384 	wbc = GNM_WBC (wbcg);
385 
386 	/* Only pop up one copy per workbook */
387 	if (gnm_dialog_raise_if_exists (wbcg, "ScenarioAdd"))
388 		return;
389 
390 	state = g_new (ScenariosState, 1);
391 
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",
395 			      error_str,
396 			      "ScenarioAdd",
397 			      G_CALLBACK (scenario_add_ok_clicked_cb), NULL,
398 			      G_CALLBACK (scenario_add_update_sensitivity_cb),
399 			      GNM_EE_SHEET_OPTIONAL))
400 	{
401 		g_free (state);
402 		return;
403 	}
404 
405 	state->name_entry = go_gtk_builder_get_widget (state->base.gui, "name_entry");
406 	if (state->name_entry == NULL)
407 	        return;
408 
409 	comment_view = go_gtk_builder_get_widget (state->base.gui, "comment_view");
410 	if (comment_view == NULL)
411 	        return;
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,
417 				  strlen (buf->str));
418 	g_string_free (buf, FALSE);
419 
420 	state->base.gdao = NULL;
421 
422 	gnm_dialog_setup_destroy_handlers (GTK_DIALOG (state->base.dialog),
423 					   wbcg,
424 					   GNM_DIALOG_DESTROY_SHEET_REMOVED);
425 
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);
430 }
431 
432 
433 /********* Scenario Manager UI ******************************************/
434 
435 static void
update_comment(ScenariosState * state,const gchar * cells,const gchar * comment)436 update_comment (ScenariosState *state, const gchar *cells,
437 		const gchar *comment)
438 {
439 	GtkWidget     *w;
440 	GtkTextBuffer *buf;
441 
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);
445 
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));
449 
450 	gtk_text_buffer_set_text (buf, comment, strlen (comment));
451 }
452 
453 static void
set_selection_state(ScenariosState * state,gboolean f)454 set_selection_state (ScenariosState *state, gboolean f)
455 {
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);
459 
460 	if (f) {
461 		GtkTreeSelection        *selection;
462 		GtkTreeIter             iter;
463 		GtkTreeModel            *model;
464 		gchar                   *name;
465 		gchar                   *cells_txt;
466 		GnmScenario             *sc;
467 
468 		selection = gtk_tree_view_get_selection
469 			(GTK_TREE_VIEW
470 			 (state->scenarios_treeview));
471 		if (!gtk_tree_selection_get_selected (selection, NULL, &iter))
472 			return;
473 		model = gtk_tree_view_get_model
474 			(GTK_TREE_VIEW
475 			 (state->scenarios_treeview));
476 
477 		gtk_tree_model_get (GTK_TREE_MODEL (model), &iter,
478 				    0, &name, -1);
479 
480 		sc = gnm_sheet_scenario_find (state->base.sheet, name);
481 		if (!sc)
482 			return;
483 		cells_txt = gnm_scenario_get_range_str (sc);
484 		update_comment (state, cells_txt, sc->comment);
485 		g_free (cells_txt);
486 	} else
487 		update_comment (state, "", "");
488 }
489 
490 static void
update_scenarios_treeview(GtkWidget * view,GList * scenarios)491 update_scenarios_treeview (GtkWidget *view, GList *scenarios)
492 {
493 	  GtkTreeIter  iter;
494 	  GtkListStore *store;
495 	  GtkTreePath  *path;
496 
497 	  store = gtk_list_store_new (1, G_TYPE_STRING);
498 
499 	  while (scenarios != NULL) {
500 	          GnmScenario *s = scenarios->data;
501 
502 	          gtk_list_store_append (store, &iter);
503 	          gtk_list_store_set (store, &iter, 0, s->name, -1);
504 	          scenarios = scenarios->next;
505 	  }
506 	  path = gtk_tree_path_new_from_string ("0");
507 	  if (gtk_tree_model_get_iter (GTK_TREE_MODEL (store),
508 				       &iter, path)) {
509 		  ;		/* Do something */
510 	  }  else {
511 		  /* No scenarios */
512 	  }
513 	  gtk_tree_path_free (path);
514 
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
521 		   (_("Name"),
522 		    gtk_cell_renderer_text_new (), "text", 0, NULL));
523 }
524 
525 /**
526  * scenarios_update_sensitivity_cb:
527  * @dummy:
528  * @state:
529  *
530  * Update the dialog widgets sensitivity
531  **/
532 static void
scenarios_update_sensitivity_cb(G_GNUC_UNUSED GtkWidget * dummy,ScenariosState * state)533 scenarios_update_sensitivity_cb (G_GNUC_UNUSED GtkWidget *dummy,
534 				 ScenariosState *state)
535 {
536 	gtk_widget_set_sensitive (state->base.ok_button, TRUE);
537 }
538 
539 
540 static void
scenario_manager_free(ScenariosState * state)541 scenario_manager_free (ScenariosState *state)
542 {
543 	g_slist_free (state->new_report_sheets);
544 	state->new_report_sheets = NULL;
545 
546 	if (state->undo) {
547 		g_object_unref (state->undo);
548 		state->undo = NULL;
549 	}
550 }
551 
552 /**
553  * scenarios_ok_clicked_cb:
554  * @button:
555  * @state:
556  *
557  * Run the scenario manager tool.
558  **/
559 static void
scenarios_ok_clicked_cb(G_GNUC_UNUSED GtkWidget * button,ScenariosState * state)560 scenarios_ok_clicked_cb (G_GNUC_UNUSED GtkWidget *button,
561 			 ScenariosState *state)
562 {
563 	if (state->current) {
564 		WorkbookControl *wbc = GNM_WBC (state->base.wbcg);
565 		cmd_scenario_mngr (wbc, state->current, state->undo);
566 	}
567 
568 	scenario_manager_ok (state->base.sheet);
569 
570 	scenario_manager_free (state);
571 	gtk_widget_destroy (state->base.dialog);
572 
573 	return;
574 }
575 
576 static void
restore_old_values(ScenariosState * state)577 restore_old_values (ScenariosState *state)
578 {
579 	GOCmdContext *cc;
580 
581 	if (state->undo == NULL)
582 		return;
583 
584 	cc = GO_CMD_CONTEXT (state->base.wbcg);
585 	go_undo_undo_with_data (state->undo, cc);
586 	g_object_unref (state->undo);
587 	state->undo = NULL;
588 	state->current = NULL;
589 }
590 
591 /**
592  * scenarios_cancel_clicked_cb:
593  * @button:
594  * @state:
595  *
596  * Cancel clicked on the scenario manager tool.
597  **/
598 static void
scenarios_cancel_clicked_cb(G_GNUC_UNUSED GtkWidget * button,ScenariosState * state)599 scenarios_cancel_clicked_cb (G_GNUC_UNUSED GtkWidget *button,
600 			     ScenariosState *state)
601 {
602 	GSList *cur;
603 	WorkbookControl *wbc;
604 
605 	restore_old_values (state);
606 
607 	wbc = GNM_WBC (state->base.wbcg);
608 
609 	/* Remove report sheets created on this dialog session. */
610 	for (cur = state->new_report_sheets; cur != NULL;
611 	     cur = cur->next) {
612 		Sheet *sheet = cur->data;
613 
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);
617 
618 		/* Delete a report sheet. */
619 		workbook_sheet_delete (sheet);
620 	}
621 
622 	/* Recover the deleted scenarios. */
623 	scenario_recover_all (state->base.sheet);
624 
625 	scenario_manager_free (state);
626 	gtk_widget_destroy (state->base.dialog);
627 }
628 
629 static void
scenarios_show_clicked_cb(G_GNUC_UNUSED GtkWidget * button,ScenariosState * state)630 scenarios_show_clicked_cb (G_GNUC_UNUSED GtkWidget *button,
631 			   ScenariosState *state)
632 {
633 	GtkTreeSelection        *selection;
634 	GtkTreeIter             iter;
635 	GtkTreeModel            *model;
636 	const gchar             *value;
637 
638 	selection = gtk_tree_view_get_selection
639 	        (GTK_TREE_VIEW (state->scenarios_treeview));
640 	if (!gtk_tree_selection_get_selected (selection, NULL, &iter))
641 		return;
642 	model = gtk_tree_view_get_model
643 	        (GTK_TREE_VIEW (state->scenarios_treeview));
644 
645 	gtk_tree_model_get (GTK_TREE_MODEL (model), &iter,  0, &value, -1);
646 
647 	restore_old_values (state);
648 
649 	state->current = gnm_sheet_scenario_find (state->base.sheet, value);
650 	state->undo = gnm_scenario_apply (state->current);
651 }
652 
653 static void
scenarios_delete_clicked_cb(G_GNUC_UNUSED GtkWidget * button,ScenariosState * state)654 scenarios_delete_clicked_cb (G_GNUC_UNUSED GtkWidget *button,
655 			     ScenariosState *state)
656 {
657 	data_analysis_output_t  dao;
658 	GtkTreeSelection        *selection;
659 	GtkTreeIter             iter;
660 	GtkTreeModel            *model;
661 	gchar                   *value;
662 	gboolean                all_deleted;
663 	GnmScenario             *sc;
664 	GList                   *l;
665 
666 	restore_old_values (state);
667 
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))
673 		return;
674 	model = gtk_tree_view_get_model
675 	        (GTK_TREE_VIEW (state->scenarios_treeview));
676 
677 	gtk_tree_model_get (GTK_TREE_MODEL (model), &iter, 0, &value, -1);
678 
679 	gtk_list_store_remove (GTK_LIST_STORE (model), &iter);
680 
681 	sc = gnm_sheet_scenario_find (state->base.sheet, value);
682 	if (sc)
683 		g_object_set_data (G_OBJECT (sc),
684 				   "marked_deleted", GUINT_TO_POINTER (TRUE));
685 
686 	set_selection_state (state, FALSE);
687 
688 	all_deleted = TRUE;
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"))
692 			all_deleted = FALSE;
693 	}
694 
695 	gtk_widget_set_sensitive
696 		(state->summary_button, !all_deleted);
697 }
698 
699 static void
scenarios_summary_clicked_cb(G_GNUC_UNUSED GtkWidget * button,ScenariosState * state)700 scenarios_summary_clicked_cb (G_GNUC_UNUSED GtkWidget *button,
701 			      ScenariosState *state)
702 {
703 	Sheet  *new_sheet;
704 	GSList *results;
705 
706 	restore_old_values (state);
707 
708 	results = gnm_expr_entry_parse_as_list (
709 		GNM_EXPR_ENTRY (state->base.input_entry), state->base.sheet);
710 
711 	if (results == NULL) {
712 		go_gtk_notice_dialog (GTK_WINDOW (state->base.dialog),
713 				 GTK_MESSAGE_ERROR,
714 				 _("Results entry did not contain valid "
715 				   "cell names."));
716 		return;
717 	}
718 
719 	scenario_summary (GNM_WBC (state->base.wbcg), state->base.sheet,
720 			  results, &new_sheet);
721 
722 	state->new_report_sheets =
723 		g_slist_prepend (state->new_report_sheets,
724 				 new_sheet);
725 	if (results)
726 		g_slist_free_full (results, (GDestroyNotify)value_release);
727 }
728 
729 static void
cb_selection_changed(G_GNUC_UNUSED GtkTreeSelection * selection,ScenariosState * state)730 cb_selection_changed (G_GNUC_UNUSED GtkTreeSelection *selection,
731 		      ScenariosState *state)
732 {
733 	set_selection_state (state, TRUE);
734 }
735 
736 static gboolean
init_scenario_buttons(ScenariosState * state)737 init_scenario_buttons (ScenariosState *state)
738 {
739 	/* Show button */
740 	state->show_button =
741 	        go_gtk_builder_get_widget (state->base.gui, "show_button");
742 	if (state->show_button == NULL)
743 	        return TRUE;
744 	g_signal_connect (G_OBJECT (state->show_button),
745 			  "clicked",
746 			  G_CALLBACK (scenarios_show_clicked_cb), state);
747 
748 	/* Delete button */
749 	state->delete_button =
750 	        go_gtk_builder_get_widget (state->base.gui, "delete_button");
751 	if (state->delete_button == NULL)
752 	        return TRUE;
753 	g_signal_connect (G_OBJECT (state->delete_button),
754 			  "clicked",
755 			  G_CALLBACK (scenarios_delete_clicked_cb), state);
756 
757 	/* Summary button */
758 	state->summary_button =
759 	        go_gtk_builder_get_widget (state->base.gui, "summary_button");
760 	if (state->summary_button == NULL)
761 	        return TRUE;
762 	g_signal_connect (G_OBJECT (state->summary_button),
763 			  "clicked",
764 			  G_CALLBACK (scenarios_summary_clicked_cb),
765 			  state);
766 
767 	set_selection_state (state, FALSE);
768 
769 	return FALSE;
770 }
771 
772 void
dialog_scenarios(WBCGtk * wbcg)773 dialog_scenarios (WBCGtk *wbcg)
774 {
775         ScenariosState   *state;
776 	WorkbookControl  *wbc;
777 	Sheet            *sheet;
778 	GtkWidget        *w;
779 	GtkTreeSelection *select;
780 	char const *error_str = _("Could not create the Scenarios dialog.");
781 
782 	g_return_if_fail (wbcg != NULL);
783 
784 	wbc   = GNM_WBC (wbcg);
785 	sheet = wb_control_cur_sheet (wbc);
786 
787 	state = g_new (ScenariosState, 1);
788 	state->new_report_sheets = NULL;
789 	state->current    = NULL;
790 	state->undo = NULL;
791 	state->base.wb = wb_control_get_workbook (wbc);
792 
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),
800 			      0))
801 		goto error_out;
802 
803 	if (init_scenario_buttons (state))
804 		goto error_out;
805 
806 	state->scenarios_treeview = go_gtk_builder_get_widget
807 	        (state->base.gui, "scenarios_treeview");
808 	if (state->scenarios_treeview == NULL)
809 	        goto error_out;
810 
811 	w = go_gtk_builder_get_widget (state->base.gui, "changing_cells_entry");
812 	if (w == NULL)
813 	        goto error_out;
814 	gtk_widget_set_sensitive (w, FALSE);
815 	w = go_gtk_builder_get_widget (state->base.gui, "comment_view");
816 
817 	if (w == NULL)
818 	        goto error_out;
819 	gtk_widget_set_sensitive (w, FALSE);
820 
821 	if (state->base.sheet->scenarios == NULL)
822 		gtk_widget_set_sensitive
823 			(state->summary_button, FALSE);
824 
825 	update_scenarios_treeview (state->scenarios_treeview,
826 				   sheet->scenarios);
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);
831 
832 	scenarios_update_sensitivity_cb (NULL, state);
833 	gtk_widget_show (state->base.dialog);
834 
835         return;
836 
837  error_out:
838 	go_gtk_notice_dialog (wbcg_toplevel (wbcg), GTK_MESSAGE_ERROR,
839 			      "%s", error_str);
840 	g_free (state);
841 
842 	return;
843 }
844