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