1 /*
2 * validation.c: Implementation of validation.
3 *
4 * Copyright (C) Jody Goldberg <jody@gnome.org>
5 *
6 * based on work by
7 * Almer S. Tigelaar <almer@gnome.org>
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
23 #include <gnumeric-config.h>
24 #include <gnumeric.h>
25 #include <validation.h>
26 #include <validation-combo.h>
27
28 #include <numbers.h>
29 #include <expr.h>
30 #include <mstyle.h>
31 #include <sheet.h>
32 #include <cell.h>
33 #include <value.h>
34 #include <workbook.h>
35 #include <workbook-control.h>
36 #include <parse-util.h>
37
38 #include <sheet-view.h>
39 #include <sheet-object.h>
40 #include <sheet-style.h>
41 #include <widgets/gnm-validation-combo-view.h>
42 #include <widgets/gnm-cell-combo-view.h>
43 #include <gsf/gsf-impl-utils.h>
44
45 #include <glib/gi18n-lib.h>
46
47 static const struct {
48 gboolean errors_not_allowed;
49 gboolean strings_not_allowed;
50 gboolean bool_always_ok;
51 } typeinfo[] = {
52 { FALSE, FALSE, TRUE }, /* ANY */
53 { TRUE, TRUE, TRUE }, /* AS_INT */
54 { TRUE, TRUE, TRUE }, /* AS_NUMBER */
55 { TRUE, FALSE, FALSE }, /* IN_LIST */
56 { TRUE, TRUE, TRUE }, /* AS_DATE */
57 { TRUE, TRUE, TRUE }, /* AS_TIME */
58 { TRUE, FALSE, FALSE }, /* TEXT_LENGTH */
59 { FALSE, FALSE, FALSE } /* CUSTOM */
60 };
61
62 #define NONE (GnmExprOp)-1
63
64 static struct {
65 int nops;
66 GnmExprOp ops[2];
67 int ntrue;
68 char const *name;
69 } const opinfo[] = {
70 /* Note: no entry for GNM_VALIDATION_OP_NONE */
71 { 2, { GNM_EXPR_OP_GTE, GNM_EXPR_OP_LTE }, 2, N_("Between") },
72 { 2, { GNM_EXPR_OP_LT, GNM_EXPR_OP_GT }, 1, N_("Not_Between") },
73 { 1, { GNM_EXPR_OP_EQUAL, NONE }, 1, N_("Equal") },
74 { 1, { GNM_EXPR_OP_NOT_EQUAL, NONE }, 1, N_("Not Equal") },
75 { 1, { GNM_EXPR_OP_GT, NONE }, 1, N_("Greater Than") },
76 { 1, { GNM_EXPR_OP_LT, NONE }, 1, N_("Less Than") },
77 { 1, { GNM_EXPR_OP_GTE, NONE }, 1, N_("Greater than or Equal") },
78 { 1, { GNM_EXPR_OP_LTE, NONE }, 1, N_("Less than or Equal") },
79 };
80
81 #undef NONE
82
83 /***************************************************************************/
84
85 static GObjectClass *gvc_parent_klass;
86
87 static void
gnm_validation_combo_finalize(GObject * object)88 gnm_validation_combo_finalize (GObject *object)
89 {
90 GnmValidationCombo *vcombo = GNM_VALIDATION_COMBO (object);
91
92 if (NULL != vcombo->validation) {
93 gnm_validation_unref (vcombo->validation);
94 vcombo->validation = NULL;
95 }
96
97 gvc_parent_klass->finalize (object);
98 }
99
100 static void
gnm_validation_combo_init(G_GNUC_UNUSED SheetObject * so)101 gnm_validation_combo_init (G_GNUC_UNUSED SheetObject *so)
102 {
103 }
104
105 static SheetObjectView *
gnm_validation_combo_view_new(SheetObject * so,SheetObjectViewContainer * container)106 gnm_validation_combo_view_new (SheetObject *so, SheetObjectViewContainer *container)
107 {
108 return gnm_cell_combo_view_new (so,
109 gnm_validation_combo_view_get_type (), container);
110 }
111
112 static void
gnm_validation_combo_class_init(GObjectClass * gobject_class)113 gnm_validation_combo_class_init (GObjectClass *gobject_class)
114 {
115 SheetObjectClass *so_class = GNM_SO_CLASS (gobject_class);
116 gobject_class->finalize = gnm_validation_combo_finalize;
117 so_class->new_view = gnm_validation_combo_view_new;
118
119 gvc_parent_klass = g_type_class_peek_parent (gobject_class);
120 }
121
122 typedef SheetObjectClass GnmValidationComboClass;
GSF_CLASS(GnmValidationCombo,gnm_validation_combo,gnm_validation_combo_class_init,gnm_validation_combo_init,gnm_cell_combo_get_type ())123 GSF_CLASS (GnmValidationCombo, gnm_validation_combo,
124 gnm_validation_combo_class_init, gnm_validation_combo_init,
125 gnm_cell_combo_get_type ())
126
127 SheetObject *
128 gnm_validation_combo_new (GnmValidation const *val, SheetView *sv)
129 {
130 GnmValidationCombo *vcombo;
131
132 g_return_val_if_fail (val != NULL, NULL);
133 g_return_val_if_fail (sv != NULL, NULL);
134
135 vcombo = g_object_new (GNM_VALIDATION_COMBO_TYPE, "sheet-view", sv, NULL);
136 gnm_validation_ref (vcombo->validation = val);
137 return GNM_SO (vcombo);
138 }
139
140 /***************************************************************************/
141
142 GType
gnm_validation_style_get_type(void)143 gnm_validation_style_get_type (void)
144 {
145 static GType etype = 0;
146 if (etype == 0) {
147 static GEnumValue const values[] = {
148 { GNM_VALIDATION_STYLE_NONE,
149 "GNM_VALIDATION_STYLE_NONE", "none"},
150 { GNM_VALIDATION_STYLE_STOP,
151 "GNM_VALIDATION_STYLE_STOP", "stop"},
152 { GNM_VALIDATION_STYLE_WARNING,
153 "GNM_VALIDATION_STYLE_WARNING", "warning"},
154 { GNM_VALIDATION_STYLE_INFO,
155 "GNM_VALIDATION_STYLE_INFO", "info"},
156 { GNM_VALIDATION_STYLE_PARSE_ERROR,
157 "GNM_VALIDATION_STYLE_PARSE_ERROR", "parse-error"},
158 { 0, NULL, NULL }
159 };
160 etype = g_enum_register_static ("GnmValidationStyle",
161 values);
162 }
163 return etype;
164 }
165
166 GType
gnm_validation_type_get_type(void)167 gnm_validation_type_get_type (void)
168 {
169 static GType etype = 0;
170 if (etype == 0) {
171 static GEnumValue const values[] = {
172 { GNM_VALIDATION_TYPE_ANY,
173 "GNM_VALIDATION_TYPE_ANY", "any"},
174 { GNM_VALIDATION_TYPE_AS_INT,
175 "GNM_VALIDATION_TYPE_AS_INT", "int"},
176 { GNM_VALIDATION_TYPE_AS_NUMBER,
177 "GNM_VALIDATION_TYPE_AS_NUMBER", "number"},
178 { GNM_VALIDATION_TYPE_IN_LIST,
179 "GNM_VALIDATION_TYPE_IN_LIST", "list"},
180 { GNM_VALIDATION_TYPE_AS_DATE,
181 "GNM_VALIDATION_TYPE_AS_DATE", "date"},
182 { GNM_VALIDATION_TYPE_AS_TIME,
183 "GNM_VALIDATION_TYPE_AS_TIME", "time"},
184 { GNM_VALIDATION_TYPE_TEXT_LENGTH,
185 "GNM_VALIDATION_TYPE_TEXT_LENGTH", "length"},
186 { GNM_VALIDATION_TYPE_CUSTOM,
187 "GNM_VALIDATION_TYPE_CUSTOM", "custom"},
188 { 0, NULL, NULL }
189 };
190 etype = g_enum_register_static ("GnmValidationType",
191 values);
192 }
193 return etype;
194 }
195
196 GType
gnm_validation_op_get_type(void)197 gnm_validation_op_get_type (void)
198 {
199 static GType etype = 0;
200 if (etype == 0) {
201 static GEnumValue const values[] = {
202 { GNM_VALIDATION_OP_NONE,
203 "GNM_VALIDATION_OP_NONE", "none"},
204 { GNM_VALIDATION_OP_BETWEEN,
205 "GNM_VALIDATION_OP_BETWEEN", "between"},
206 { GNM_VALIDATION_OP_NOT_BETWEEN,
207 "GNM_VALIDATION_OP_NOT_BETWEEN", "not-between"},
208 { GNM_VALIDATION_OP_EQUAL,
209 "GNM_VALIDATION_OP_EQUAL", "equal"},
210 { GNM_VALIDATION_OP_NOT_EQUAL,
211 "GNM_VALIDATION_OP_NOT_EQUAL", "not-equal"},
212 { GNM_VALIDATION_OP_GT,
213 "GNM_VALIDATION_OP_GT", "gt"},
214 { GNM_VALIDATION_OP_LT,
215 "GNM_VALIDATION_OP_LT", "lt"},
216 { GNM_VALIDATION_OP_GTE,
217 "GNM_VALIDATION_OP_GTE", "gte"},
218 { GNM_VALIDATION_OP_LTE,
219 "GNM_VALIDATION_OP_LTE", "lte"},
220 { 0, NULL, NULL }
221 };
222 etype = g_enum_register_static ("GnmValidationOp",
223 values);
224 }
225 return etype;
226 }
227
228
229 /***************************************************************************/
230
231 /**
232 * gnm_validation_new:
233 * @style: #ValidationStyle
234 * @op: #ValidationOp
235 * @sheet: #Sheet
236 * @title: will be copied.
237 * @msg: will be copied.
238 * @texpr0: (transfer full) (nullable): first expression
239 * @texpr1: (transfer full) (nullable): second expression
240 * @allow_blank:
241 * @use_dropdown:
242 *
243 * Does _NOT_ require all necessary information to be set here.
244 * gnm_validation_set_expr can be used to change the expressions after creation,
245 * and gnm_validation_is_ok can be used to ensure that things are properly
246 * setup.
247 *
248 * Returns: (transfer full): a new @GnmValidation object
249 **/
250 GnmValidation *
gnm_validation_new(ValidationStyle style,ValidationType type,ValidationOp op,Sheet * sheet,char const * title,char const * msg,GnmExprTop const * texpr0,GnmExprTop const * texpr1,gboolean allow_blank,gboolean use_dropdown)251 gnm_validation_new (ValidationStyle style,
252 ValidationType type,
253 ValidationOp op,
254 Sheet *sheet,
255 char const *title, char const *msg,
256 GnmExprTop const *texpr0, GnmExprTop const *texpr1,
257 gboolean allow_blank, gboolean use_dropdown)
258 {
259 GnmValidation *v;
260 int nops;
261
262 g_return_val_if_fail ((size_t)type < G_N_ELEMENTS (typeinfo), NULL);
263 g_return_val_if_fail (op >= GNM_VALIDATION_OP_NONE, NULL);
264 g_return_val_if_fail (op < (int)G_N_ELEMENTS (opinfo), NULL);
265 g_return_val_if_fail (IS_SHEET (sheet), NULL);
266
267 switch (type) {
268 case GNM_VALIDATION_TYPE_CUSTOM:
269 case GNM_VALIDATION_TYPE_IN_LIST:
270 nops = 1;
271 if (op != GNM_VALIDATION_OP_NONE) {
272 /*
273 * This can happen if an .xls file was saved
274 * as a .gnumeric.
275 */
276 op = GNM_VALIDATION_OP_NONE;
277 }
278 break;
279 case GNM_VALIDATION_TYPE_ANY:
280 nops = 0;
281 break;
282 default:
283 nops = (op == GNM_VALIDATION_OP_NONE) ? 0 : opinfo[op].nops;
284 }
285
286 v = g_new0 (GnmValidation, 1);
287 v->ref_count = 1;
288 v->title = title && title[0] ? go_string_new (title) : NULL;
289 v->msg = msg && msg[0] ? go_string_new (msg) : NULL;
290
291 dependent_managed_init (&v->deps[0], sheet);
292 if (texpr0) {
293 if (nops > 0)
294 dependent_managed_set_expr (&v->deps[0], texpr0);
295 gnm_expr_top_unref (texpr0);
296 }
297
298 dependent_managed_init (&v->deps[1], sheet);
299 if (texpr1) {
300 if (nops > 1)
301 dependent_managed_set_expr (&v->deps[1], texpr1);
302 gnm_expr_top_unref (texpr1);
303 }
304
305 v->style = style;
306 v->type = type;
307 v->op = op;
308 v->allow_blank = (allow_blank != FALSE);
309 v->use_dropdown = (use_dropdown != FALSE);
310
311 return v;
312 }
313
314 GnmValidation *
gnm_validation_dup_to(GnmValidation * v,Sheet * sheet)315 gnm_validation_dup_to (GnmValidation *v, Sheet *sheet)
316 {
317 GnmValidation *dst;
318 int i;
319
320 g_return_val_if_fail (v != NULL, NULL);
321
322 dst = gnm_validation_new (v->style, v->type, v->op,
323 sheet,
324 v->title ? v->title->str : NULL,
325 v->msg ? v->msg->str : NULL,
326 NULL, NULL,
327 v->allow_blank, v->use_dropdown);
328 for (i = 0; i < 2; i++)
329 gnm_validation_set_expr (dst, v->deps[i].base.texpr, i);
330 return dst;
331 }
332
333 gboolean
gnm_validation_equal(GnmValidation const * a,GnmValidation const * b,gboolean relax_sheet)334 gnm_validation_equal (GnmValidation const *a, GnmValidation const *b,
335 gboolean relax_sheet)
336 {
337 int i;
338
339 g_return_val_if_fail (a != NULL, FALSE);
340 g_return_val_if_fail (b != NULL, FALSE);
341
342 if (a == b)
343 return TRUE;
344
345 if (!relax_sheet &&
346 gnm_validation_get_sheet (a) != gnm_validation_get_sheet (b))
347 return FALSE;
348
349 if (!(g_strcmp0 (a->title ? a->title->str : NULL,
350 b->title ? b->title->str : NULL) == 0 &&
351 g_strcmp0 (a->msg ? a->msg->str : NULL,
352 b->msg ? b->msg->str : NULL) == 0 &&
353 a->style == b->style &&
354 a->type == b->type &&
355 a->op == b->op &&
356 a->allow_blank == b->allow_blank &&
357 a->use_dropdown == b->use_dropdown))
358 return FALSE;
359
360 for (i = 0; i < 2; i++)
361 if (!gnm_expr_top_equal (a->deps[i].base.texpr, b->deps[i].base.texpr))
362 return FALSE;
363
364 return TRUE;
365 }
366
367
368 GnmValidation *
gnm_validation_ref(GnmValidation const * v)369 gnm_validation_ref (GnmValidation const *v)
370 {
371 g_return_val_if_fail (v != NULL, NULL);
372 ((GnmValidation *)v)->ref_count++;
373 return ((GnmValidation *)v);
374 }
375
376 void
gnm_validation_unref(GnmValidation const * val)377 gnm_validation_unref (GnmValidation const *val)
378 {
379 GnmValidation *v = (GnmValidation *)val;
380
381 g_return_if_fail (v != NULL);
382
383 v->ref_count--;
384
385 if (v->ref_count < 1) {
386 int i;
387
388 go_string_unref (v->title);
389 v->title = NULL;
390
391 go_string_unref (v->msg);
392 v->msg = NULL;
393
394 for (i = 0 ; i < 2 ; i++)
395 dependent_managed_set_expr (&v->deps[i], NULL);
396 g_free (v);
397 }
398 }
399
400 GType
gnm_validation_get_type(void)401 gnm_validation_get_type (void)
402 {
403 static GType t = 0;
404
405 if (t == 0) {
406 t = g_boxed_type_register_static ("GnmValidation",
407 (GBoxedCopyFunc)gnm_validation_ref,
408 (GBoxedFreeFunc)gnm_validation_unref);
409 }
410 return t;
411 }
412
413 /**
414 * gnm_validation_get_sheet:
415 * @v: #GnmValidation
416 *
417 * Returns: (transfer none): the sheet.
418 **/
419 Sheet *
gnm_validation_get_sheet(GnmValidation const * v)420 gnm_validation_get_sheet (GnmValidation const *v)
421 {
422 g_return_val_if_fail (v != NULL, NULL);
423 return v->deps[0].base.sheet;
424 }
425
426 /**
427 * gnm_validation_set_expr:
428 * @v: #GnmValidation
429 * @texpr: #GnmExprTop
430 * @indx: 0 or 1
431 *
432 * Assign an expression to a validation. gnm_validation_is_ok can be used to
433 * verify that @v has all of the required information.
434 **/
435 void
gnm_validation_set_expr(GnmValidation * v,GnmExprTop const * texpr,unsigned indx)436 gnm_validation_set_expr (GnmValidation *v,
437 GnmExprTop const *texpr, unsigned indx)
438 {
439 g_return_if_fail (indx <= 1);
440
441 dependent_managed_set_expr (&v->deps[indx], texpr);
442 }
443
444 GError *
gnm_validation_is_ok(GnmValidation const * v)445 gnm_validation_is_ok (GnmValidation const *v)
446 {
447 unsigned nops, i;
448
449 switch (v->type) {
450 case GNM_VALIDATION_TYPE_CUSTOM:
451 case GNM_VALIDATION_TYPE_IN_LIST:
452 nops = 1;
453 break;
454 case GNM_VALIDATION_TYPE_ANY:
455 nops = 0;
456 break;
457 default: nops = (v->op == GNM_VALIDATION_OP_NONE) ? 0 : opinfo[v->op].nops;
458 }
459
460 for (i = 0 ; i < 2 ; i++)
461 if (v->deps[i].base.texpr == NULL) {
462 if (i < nops)
463 return g_error_new (1, 0, N_("Missing formula for validation"));
464 } else {
465 if (i >= nops)
466 return g_error_new (1, 0, N_("Extra formula for validation"));
467 }
468
469 return NULL;
470 }
471
472 static ValidationStatus
validation_barf(WorkbookControl * wbc,GnmValidation const * gv,char * def_msg,gboolean * showed_dialog)473 validation_barf (WorkbookControl *wbc, GnmValidation const *gv,
474 char *def_msg, gboolean *showed_dialog)
475 {
476 char const *msg = gv->msg ? gv->msg->str : def_msg;
477 char const *title = gv->title ? gv->title->str : _("Gnumeric: Validation");
478 ValidationStatus result;
479
480 if (gv->style == GNM_VALIDATION_STYLE_NONE) {
481 /* Invalid, but we're asked to ignore. */
482 result = GNM_VALIDATION_STATUS_VALID;
483 } else {
484 if (showed_dialog) *showed_dialog = TRUE;
485 result = wb_control_validation_msg (wbc, gv->style, title, msg);
486 }
487 g_free (def_msg);
488 return result;
489 }
490
491 static GnmValue *
cb_validate_custom(GnmValueIter const * v_iter,GnmValue const * target)492 cb_validate_custom (GnmValueIter const *v_iter, GnmValue const *target)
493 {
494 if (value_compare (v_iter->v, target, FALSE) == IS_EQUAL)
495 return VALUE_TERMINATE;
496 else
497 return NULL;
498 }
499
500 #define BARF(msg) \
501 do { \
502 return validation_barf (wbc, v, msg, showed_dialog); \
503 } while (0)
504
505 /**
506 * gnm_validation_eval:
507 * @wbc:
508 * @mstyle:
509 * @sheet:
510 * @pos:
511 * @showed_dialog: (out) (optional):
512 *
513 * Checks the validation in @mstyle, if any. Set @showed_dialog to %TRUE
514 * if a dialog was showed as a result.
515 **/
516 ValidationStatus
gnm_validation_eval(WorkbookControl * wbc,GnmStyle const * mstyle,Sheet * sheet,GnmCellPos const * pos,gboolean * showed_dialog)517 gnm_validation_eval (WorkbookControl *wbc, GnmStyle const *mstyle,
518 Sheet *sheet, GnmCellPos const *pos,
519 gboolean *showed_dialog)
520 {
521 GnmValidation const *v;
522 GnmCell *cell;
523 GnmValue *val;
524 gnm_float x;
525 int nok, i;
526 GnmEvalPos ep;
527
528 if (showed_dialog) *showed_dialog = FALSE;
529
530 v = gnm_style_get_validation (mstyle);
531 if (v == NULL)
532 return GNM_VALIDATION_STATUS_VALID;
533
534 if (v->type == GNM_VALIDATION_TYPE_ANY)
535 return GNM_VALIDATION_STATUS_VALID;
536
537 cell = sheet_cell_get (sheet, pos->col, pos->row);
538 if (cell != NULL)
539 gnm_cell_eval (cell);
540
541 if (gnm_cell_is_empty (cell)) {
542 if (v->allow_blank)
543 return GNM_VALIDATION_STATUS_VALID;
544 BARF (g_strdup_printf (_("Cell %s is not permitted to be blank"),
545 cell_name (cell)));
546 }
547
548 val = cell->value;
549 switch (val->v_any.type) {
550 case VALUE_ERROR:
551 if (typeinfo[v->type].errors_not_allowed)
552 BARF (g_strdup_printf (_("Cell %s is not permitted to contain error values"),
553 cell_name (cell)));
554 break;
555
556 case VALUE_BOOLEAN:
557 if (typeinfo[v->type].bool_always_ok)
558 return GNM_VALIDATION_STATUS_VALID;
559 break;
560
561 case VALUE_STRING:
562 if (typeinfo[v->type].strings_not_allowed)
563 BARF (g_strdup_printf (_("Cell %s is not permitted to contain strings"),
564 cell_name (cell)));
565 break;
566
567 default:
568 break;
569 }
570
571 eval_pos_init_cell (&ep, cell);
572
573 switch (v->type) {
574 case GNM_VALIDATION_TYPE_AS_INT:
575 x = value_get_as_float (val);
576 if (gnm_fake_floor (x) == gnm_fake_ceil (x))
577 break;
578 else
579 BARF (g_strdup_printf (_("'%s' is not an integer"),
580 value_peek_string (val)));
581
582 case GNM_VALIDATION_TYPE_AS_NUMBER:
583 x = value_get_as_float (val);
584 break;
585
586 case GNM_VALIDATION_TYPE_AS_DATE: /* What the hell does this do? */
587 x = value_get_as_float (val);
588 if (x < 0)
589 BARF (g_strdup_printf (_("'%s' is not a valid date"),
590 value_peek_string (val)));
591 break;
592
593
594 case GNM_VALIDATION_TYPE_AS_TIME: /* What the hell does this do? */
595 x = value_get_as_float (val);
596 break;
597
598 case GNM_VALIDATION_TYPE_IN_LIST: {
599 GnmExprTop const *texpr = v->deps[0].base.texpr;
600 if (texpr) {
601 GnmValue *list = gnm_expr_top_eval
602 (texpr, &ep,
603 GNM_EXPR_EVAL_PERMIT_NON_SCALAR | GNM_EXPR_EVAL_PERMIT_EMPTY);
604 GnmValue *res = value_area_foreach (list, &ep, CELL_ITER_IGNORE_BLANK,
605 (GnmValueIterFunc) cb_validate_custom, val);
606 value_release (list);
607 if (res == NULL) {
608 GnmParsePos pp;
609 char *expr_str = gnm_expr_top_as_string
610 (texpr,
611 parse_pos_init_evalpos (&pp, &ep),
612 ep.sheet->convs);
613 char *msg = g_strdup_printf (_("%s does not contain the new value."), expr_str);
614 g_free (expr_str);
615 BARF (msg);
616 }
617 }
618 return GNM_VALIDATION_STATUS_VALID;
619 }
620
621 case GNM_VALIDATION_TYPE_TEXT_LENGTH:
622 /* XL appears to use a very basic value->string mapping that
623 * ignores formatting.
624 * eg len (12/13/01) == len (37238) = 5
625 * This seems wrong for
626 */
627 x = g_utf8_strlen (value_peek_string (val), -1);
628 break;
629
630 case GNM_VALIDATION_TYPE_CUSTOM: {
631 gboolean valid;
632 GnmExprTop const *texpr = v->deps[0].base.texpr;
633
634 if (!texpr)
635 return GNM_VALIDATION_STATUS_VALID;
636
637 val = gnm_expr_top_eval (texpr, &ep, GNM_EXPR_EVAL_SCALAR_NON_EMPTY);
638 valid = value_get_as_bool (val, NULL);
639 value_release (val);
640
641 if (valid)
642 return GNM_VALIDATION_STATUS_VALID;
643 else {
644 GnmParsePos pp;
645 char *expr_str = gnm_expr_top_as_string
646 (texpr,
647 parse_pos_init_evalpos (&pp, &ep),
648 ep.sheet->convs);
649 char *msg = g_strdup_printf (_("%s is not true."), expr_str);
650 g_free (expr_str);
651 BARF (msg);
652 }
653 }
654
655 default:
656 g_assert_not_reached ();
657 return GNM_VALIDATION_STATUS_VALID;
658 }
659
660 if (v->op == GNM_VALIDATION_OP_NONE)
661 return GNM_VALIDATION_STATUS_VALID;
662
663 nok = 0;
664 for (i = 0; i < opinfo[v->op].nops; i++) {
665 GnmExprTop const *texpr_i = v->deps[i].base.texpr;
666 GnmExprTop const *texpr;
667 GnmValue *cres;
668
669 if (!texpr_i) {
670 nok++;
671 continue;
672 }
673
674 texpr = gnm_expr_top_new
675 (gnm_expr_new_binary
676 (gnm_expr_new_constant (value_new_float (x)),
677 opinfo[v->op].ops[i],
678 gnm_expr_copy (texpr_i->expr)));
679 cres = gnm_expr_top_eval
680 (texpr, &ep, GNM_EXPR_EVAL_SCALAR_NON_EMPTY);
681 if (value_get_as_bool (cres, NULL))
682 nok++;
683 value_release (cres);
684 gnm_expr_top_unref (texpr);
685 }
686
687 if (nok < opinfo[v->op].ntrue)
688 BARF (g_strdup_printf (_("%s is out of permitted range"),
689 value_peek_string (val)));
690
691 return GNM_VALIDATION_STATUS_VALID;
692 }
693
694 #undef BARF
695
696 typedef struct {
697 WorkbookControl *wbc;
698 Sheet *sheet;
699 GnmCellPos const *pos;
700 gboolean *showed_dialog;
701 ValidationStatus status;
702 } validation_eval_t;
703
704 static GnmValue *
validation_eval_range_cb(GnmCellIter const * iter,validation_eval_t * closure)705 validation_eval_range_cb (GnmCellIter const *iter, validation_eval_t *closure)
706 {
707 ValidationStatus status;
708 gboolean showed_dialog;
709 GnmStyle const *mstyle = sheet_style_get
710 (closure->sheet, iter->pp.eval.col, iter->pp.eval.row);
711
712 if (mstyle != NULL) {
713 status = gnm_validation_eval (closure->wbc, mstyle,
714 closure->sheet, &iter->pp.eval,
715 &showed_dialog);
716 if (closure->showed_dialog)
717 *closure->showed_dialog = *closure->showed_dialog || showed_dialog;
718
719 if (status != GNM_VALIDATION_STATUS_VALID) {
720 closure->status = status;
721 return VALUE_TERMINATE;
722 }
723 }
724
725 return NULL;
726 }
727
728 ValidationStatus
gnm_validation_eval_range(WorkbookControl * wbc,Sheet * sheet,GnmCellPos const * pos,GnmRange const * r,gboolean * showed_dialog)729 gnm_validation_eval_range (WorkbookControl *wbc,
730 Sheet *sheet, GnmCellPos const *pos,
731 GnmRange const *r,
732 gboolean *showed_dialog)
733 {
734 GnmValue *result;
735 validation_eval_t closure;
736 GnmEvalPos ep;
737 GnmValue *cell_range = value_new_cellrange_r (sheet, r);
738
739 closure.wbc = wbc;
740 closure.sheet = sheet;
741 closure.pos = pos;
742 closure.showed_dialog = showed_dialog;
743 closure.status = GNM_VALIDATION_STATUS_VALID;
744
745 eval_pos_init_pos (&ep, sheet, pos);
746
747 result = workbook_foreach_cell_in_range (&ep, cell_range, CELL_ITER_ALL,
748 (CellIterFunc) validation_eval_range_cb,
749 &closure);
750
751 value_release (cell_range);
752
753 if (result == NULL)
754 return GNM_VALIDATION_STATUS_VALID;
755 return closure.status;
756 }
757