1 /*
2  * scenarios.c:
3  *
4  * (C) Copyright 2003 by Jukka-Pekka Iivonen <jiivonen@hutcs.cs.hut.fi>
5  * (C) Copyright 2009 by Morten Welinder <terra@gnome.org>
6  *
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.
11  *
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.
16  *
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/>.
19  */
20 
21 #include <gnumeric-config.h>
22 #include <glib/gi18n-lib.h>
23 #include <gnumeric.h>
24 #include <gsf/gsf-impl-utils.h>
25 
26 #include <sheet.h>
27 #include <cell.h>
28 #include <ranges.h>
29 #include <value.h>
30 #include <workbook.h>
31 #include <expr.h>
32 
33 #include <tools/scenarios.h>
34 #include <tools/dao.h>
35 #include <clipboard.h>
36 #include <parse-util.h>
37 #include <goffice/goffice.h>
38 
39 #include <string.h>
40 
41 /* ------------------------------------------------------------------------- */
42 
43 GnmScenarioItem *
gnm_scenario_item_new(Sheet * sheet)44 gnm_scenario_item_new (Sheet *sheet)
45 {
46 	GnmScenarioItem *sci = g_new0 (GnmScenarioItem, 1);
47 	dependent_managed_init (&sci->dep, sheet);
48 	return sci;
49 }
50 
51 void
gnm_scenario_item_free(GnmScenarioItem * sci)52 gnm_scenario_item_free (GnmScenarioItem *sci)
53 {
54 	gnm_scenario_item_set_range (sci, NULL);
55 	gnm_scenario_item_set_value (sci, NULL);
56 	g_free (sci);
57 }
58 
59 static GnmScenarioItem *
gnm_scenario_item_dup(GnmScenarioItem * src)60 gnm_scenario_item_dup (GnmScenarioItem *src)
61 {
62 	GnmScenarioItem *dst = gnm_scenario_item_new (src->dep.base.sheet);
63 	dependent_managed_set_expr (&dst->dep, src->dep.base.texpr);
64 	dst->value = value_dup (src->value);
65 	return dst;
66 }
67 
68 GType
gnm_scenario_item_get_type(void)69 gnm_scenario_item_get_type (void)
70 {
71 	static GType t = 0;
72 
73 	if (t == 0) {
74 		t = g_boxed_type_register_static ("GnmScenarioItem",
75 			 (GBoxedCopyFunc)gnm_scenario_item_dup,
76 			 (GBoxedFreeFunc)gnm_scenario_item_free);
77 	}
78 	return t;
79 }
80 
81 void
gnm_scenario_item_set_range(GnmScenarioItem * sci,const GnmSheetRange * sr)82 gnm_scenario_item_set_range (GnmScenarioItem *sci, const GnmSheetRange *sr)
83 {
84 	if (sr) {
85 		GnmValue *v = value_new_cellrange_r
86 			(sr->sheet != sci->dep.base.sheet ? sr->sheet : NULL,
87 			 &sr->range);
88 		GnmExprTop const *texpr = gnm_expr_top_new_constant (v);
89 		dependent_managed_set_expr (&sci->dep, texpr);
90 		gnm_expr_top_unref (texpr);
91 	} else
92 		dependent_managed_set_expr (&sci->dep, NULL);
93 }
94 
95 void
gnm_scenario_item_set_value(GnmScenarioItem * sci,const GnmValue * v)96 gnm_scenario_item_set_value (GnmScenarioItem *sci, const GnmValue *v)
97 {
98 	value_release (sci->value);
99 	sci->value = value_dup (v);
100 }
101 
102 gboolean
gnm_scenario_item_valid(const GnmScenarioItem * sci,GnmSheetRange * sr)103 gnm_scenario_item_valid (const GnmScenarioItem *sci, GnmSheetRange *sr)
104 {
105 	GnmExprTop const *texpr;
106 	GnmValue const *vr;
107 
108 	if (!sci || !((texpr = sci->dep.base.texpr)))
109 		return FALSE;
110 
111 	vr = gnm_expr_top_get_constant (texpr);
112 	if (!vr || !VALUE_IS_CELLRANGE (vr))
113 		return FALSE;
114 
115 	if (sr)
116 		gnm_sheet_range_from_value
117 			(sr, gnm_expr_top_get_constant (texpr));
118 	return TRUE;
119 }
120 
121 /* ------------------------------------------------------------------------- */
122 
123 static GObjectClass *gnm_scenario_parent_class;
124 
125 static void
gnm_scenario_finalize(GObject * obj)126 gnm_scenario_finalize (GObject *obj)
127 {
128 	GnmScenario *sc = GNM_SCENARIO (obj);
129 
130 	g_free (sc->name);
131 	g_free (sc->comment);
132 
133 	g_slist_free_full (sc->items, (GDestroyNotify)gnm_scenario_item_free);
134 
135 	gnm_scenario_parent_class->finalize (obj);
136 }
137 
138 static void
gnm_scenario_class_init(GObjectClass * object_class)139 gnm_scenario_class_init (GObjectClass *object_class)
140 {
141 	gnm_scenario_parent_class = g_type_class_peek_parent (object_class);
142 
143 	object_class->finalize = gnm_scenario_finalize;
144 }
145 
146 GSF_CLASS (GnmScenario, gnm_scenario,
147 	   &gnm_scenario_class_init, NULL, G_TYPE_OBJECT)
148 
149 GnmScenario *
gnm_scenario_new(char const * name,Sheet * sheet)150 gnm_scenario_new (char const *name, Sheet *sheet)
151 {
152 	GnmScenario *sc = g_object_new (GNM_SCENARIO_TYPE, NULL);
153 
154 	sc->sheet = sheet;
155 	sc->name = g_strdup (name);
156 
157 	return sc;
158 }
159 
160 /**
161  * gnm_scenario_dup:
162  * @sc: #GnmScenario
163  * @new_sheet: #Sheet
164  *
165  * Returns: (transfer full): the duplicated scenario.
166  **/
167 GnmScenario *
gnm_scenario_dup(GnmScenario * src,Sheet * new_sheet)168 gnm_scenario_dup (GnmScenario *src, Sheet *new_sheet)
169 {
170 	GnmScenario *dst;
171 
172 	dst = gnm_scenario_new (src->name, new_sheet);
173 	gnm_scenario_set_comment (dst, src->comment);
174 	dst->items = g_slist_copy_deep
175 		(src->items, (GCopyFunc)gnm_scenario_item_dup, NULL);
176 	return dst;
177 }
178 
179 void
gnm_scenario_set_comment(GnmScenario * sc,const char * comment)180 gnm_scenario_set_comment (GnmScenario *sc, const char *comment)
181 {
182 	char *s = g_strdup (comment);
183 	g_free (sc->comment);
184 	sc->comment = s;
185 }
186 
187 struct cb_save_cells {
188 	GSList *items;
189 	GnmScenario *sc;
190 };
191 
192 static GnmValue *
cb_save_cells(GnmCellIter const * iter,gpointer user)193 cb_save_cells (GnmCellIter const *iter, gpointer user)
194 {
195 	struct cb_save_cells *pdata = user;
196 	GnmCell *cell = iter->cell;
197 	GnmScenarioItem *sci = gnm_scenario_item_new (pdata->sc->sheet);
198 	GnmSheetRange sr;
199 
200 	/* FIXME: Think about arrays.  */
201 
202 	sr.sheet = cell->base.sheet;
203 	sr.range.start = sr.range.end = iter->pp.eval;
204 	gnm_scenario_item_set_range (sci, &sr);
205 	gnm_scenario_item_set_value (sci, cell->value);
206 
207 	pdata->items = g_slist_prepend (pdata->items, sci);
208 
209 	return NULL;
210 }
211 
212 
213 void
gnm_scenario_add_area(GnmScenario * sc,const GnmSheetRange * sr)214 gnm_scenario_add_area (GnmScenario *sc, const GnmSheetRange *sr)
215 {
216 	GnmScenarioItem *sci;
217 	struct cb_save_cells data;
218 
219 	g_return_if_fail (GNM_IS_SCENARIO (sc));
220 	g_return_if_fail (sr != NULL);
221 
222 	sci = gnm_scenario_item_new (sc->sheet);
223 	gnm_scenario_item_set_range (sci, sr);
224 	sc->items = g_slist_prepend (sc->items, sci);
225 
226 	data.items = NULL;
227 	data.sc = sc;
228 	sheet_foreach_cell_in_range
229 		(eval_sheet (sr->sheet, sc->sheet),
230 		 CELL_ITER_IGNORE_NONEXISTENT, &sr->range,
231 		 cb_save_cells, &data);
232 	sc->items = g_slist_concat (sc->items,
233 				    g_slist_reverse (data.items));
234 }
235 
236 /**
237  * gnm_scenario_apply:
238  * @sc: #GnmScenario
239  *
240  * Returns: (transfer full): the newly allocated #GOUndo.
241  **/
242 GOUndo *
gnm_scenario_apply(GnmScenario * sc)243 gnm_scenario_apply (GnmScenario *sc)
244 {
245 	GOUndo *undo = NULL;
246 	GSList *l;
247 
248 	g_return_val_if_fail (GNM_IS_SCENARIO (sc), NULL);
249 
250 	for (l = sc->items; l; l = l->next) {
251 		GnmScenarioItem *sci = l->data;
252 		GnmValue const *val = sci->value;
253 		GnmSheetRange sr;
254 		Sheet *sheet;
255 
256 		if (!gnm_scenario_item_valid (sci, &sr))
257 			continue;
258 		sheet = eval_sheet (sr.sheet, sc->sheet);
259 
260 		if (val) {
261 			/* FIXME: think about arrays.  */
262 			GnmCell *cell = sheet_cell_fetch
263 				(sheet,
264 				 sr.range.start.col,
265 				 sr.range.start.row);
266 			sheet_cell_set_value (cell, value_dup (val));
267 		} else {
268 			GOUndo *u = clipboard_copy_range_undo (sheet,
269 							       &sr.range);
270 			/* FIXME: Clear the range.  */
271 			undo = go_undo_combine (undo, u);
272 		}
273 	}
274 
275 	return undo;
276 }
277 
278 char *
gnm_scenario_get_range_str(const GnmScenario * sc)279 gnm_scenario_get_range_str (const GnmScenario *sc)
280 {
281 	GString *str;
282 	GSList *l;
283 
284 	g_return_val_if_fail (GNM_IS_SCENARIO (sc), NULL);
285 
286 	str = g_string_new (NULL);
287 	for (l = sc->items; l; l = l->next) {
288 		GnmScenarioItem const *sci = l->data;
289 		GnmValue const *vrange;
290 		if (sci->value || !gnm_scenario_item_valid (sci, NULL))
291 			continue;
292 		if (str->len)
293 			g_string_append_c (str, ',');
294 		vrange = gnm_expr_top_get_constant (sci->dep.base.texpr);
295 		g_string_append (str, value_peek_string (vrange));
296 	}
297 
298 	return g_string_free (str, FALSE);
299 }
300 
301 /* ------------------------------------------------------------------------- */
302 
303