Update Spanish translation
[gnumeric.git] / src / validation.c
blob5e45443441f15d52aca6b985c87d7da7bba82ee0
1 /*
2 * validation.c: Implementation of validation.
4 * Copyright (C) Jody Goldberg <jody@gnome.org>
6 * based on work by
7 * Almer S. Tigelaar <almer@gnome.org>
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/>.
23 #include <gnumeric-config.h>
24 #include <gnumeric.h>
25 #include <validation.h>
26 #include <validation-combo.h>
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>
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>
45 #include <glib/gi18n-lib.h>
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 */
62 #define NONE (GnmExprOp)-1
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") },
81 #undef NONE
83 /***************************************************************************/
85 static GObjectClass *gvc_parent_klass;
87 static void
88 gnm_validation_combo_finalize (GObject *object)
90 GnmValidationCombo *vcombo = GNM_VALIDATION_COMBO (object);
92 if (NULL != vcombo->validation) {
93 gnm_validation_unref (vcombo->validation);
94 vcombo->validation = NULL;
97 gvc_parent_klass->finalize (object);
100 static void
101 gnm_validation_combo_init (G_GNUC_UNUSED SheetObject *so)
105 static SheetObjectView *
106 gnm_validation_combo_view_new (SheetObject *so, SheetObjectViewContainer *container)
108 return gnm_cell_combo_view_new (so,
109 gnm_validation_combo_view_get_type (), container);
112 static void
113 gnm_validation_combo_class_init (GObjectClass *gobject_class)
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;
119 gvc_parent_klass = g_type_class_peek_parent (gobject_class);
122 typedef SheetObjectClass GnmValidationComboClass;
123 GSF_CLASS (GnmValidationCombo, gnm_validation_combo,
124 gnm_validation_combo_class_init, gnm_validation_combo_init,
125 gnm_cell_combo_get_type ())
127 SheetObject *
128 gnm_validation_combo_new (GnmValidation const *val, SheetView *sv)
130 GnmValidationCombo *vcombo;
132 g_return_val_if_fail (val != NULL, NULL);
133 g_return_val_if_fail (sv != NULL, NULL);
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);
140 /***************************************************************************/
142 GType
143 gnm_validation_style_get_type (void)
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 }
160 etype = g_enum_register_static ("GnmValidationStyle",
161 values);
163 return etype;
166 GType
167 gnm_validation_type_get_type (void)
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 }
190 etype = g_enum_register_static ("GnmValidationType",
191 values);
193 return etype;
196 GType
197 gnm_validation_op_get_type (void)
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 }
222 etype = g_enum_register_static ("GnmValidationOp",
223 values);
225 return etype;
229 /***************************************************************************/
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:
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.
248 * Returns: (transfer full): a new @GnmValidation object
250 GnmValidation *
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)
259 GnmValidation *v;
260 int nops;
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);
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) {
273 * This can happen if an .xls file was saved
274 * as a .gnumeric.
276 op = GNM_VALIDATION_OP_NONE;
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;
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;
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);
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);
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);
311 return v;
314 GnmValidation *
315 gnm_validation_dup (GnmValidation *v)
317 GnmValidation *dst;
318 int i;
320 g_return_val_if_fail (v != NULL, NULL);
322 dst = gnm_validation_new (v->style, v->type, v->op,
323 gnm_validation_get_sheet (v),
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].texpr, i);
330 return dst;
333 gboolean
334 gnm_validation_equal (GnmValidation const *a, GnmValidation const *b,
335 gboolean relax_sheet)
337 int i;
339 g_return_val_if_fail (a != NULL, FALSE);
340 g_return_val_if_fail (b != NULL, FALSE);
342 if (a == b)
343 return TRUE;
345 if (!relax_sheet &&
346 gnm_validation_get_sheet (a) != gnm_validation_get_sheet (b))
347 return FALSE;
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;
360 for (i = 0; i < 2; i++)
361 if (!gnm_expr_top_equal (a->deps[i].texpr, b->deps[i].texpr))
362 return FALSE;
364 return TRUE;
368 GnmValidation *
369 gnm_validation_ref (GnmValidation const *v)
371 g_return_val_if_fail (v != NULL, NULL);
372 ((GnmValidation *)v)->ref_count++;
373 return ((GnmValidation *)v);
376 void
377 gnm_validation_unref (GnmValidation const *val)
379 GnmValidation *v = (GnmValidation *)val;
381 g_return_if_fail (v != NULL);
383 v->ref_count--;
385 if (v->ref_count < 1) {
386 int i;
388 go_string_unref (v->title);
389 v->title = NULL;
391 go_string_unref (v->msg);
392 v->msg = NULL;
394 for (i = 0 ; i < 2 ; i++)
395 dependent_managed_set_expr (&v->deps[i], NULL);
396 g_free (v);
400 GType
401 gnm_validation_get_type (void)
403 static GType t = 0;
405 if (t == 0) {
406 t = g_boxed_type_register_static ("GnmValidation",
407 (GBoxedCopyFunc)gnm_validation_ref,
408 (GBoxedFreeFunc)gnm_validation_unref);
410 return t;
414 * gnm_validation_get_sheet:
415 * @v: #GnmValidation
417 * Returns: (transfer none): the sheet.
419 Sheet *
420 gnm_validation_get_sheet (GnmValidation const *v)
422 g_return_val_if_fail (v != NULL, NULL);
423 return v->deps[0].sheet;
426 void
427 gnm_validation_set_sheet (GnmValidation *v, Sheet *sheet)
429 int i;
431 g_return_if_fail (v != NULL);
432 g_return_if_fail (IS_SHEET (sheet));
434 for (i = 0; i < 2; i++)
435 dependent_managed_set_sheet (&v->deps[i], sheet);
440 * gnm_validation_set_expr:
441 * @v: #GnmValidation
442 * @texpr: #GnmExprTop
443 * @indx: 0 or 1
445 * Assign an expression to a validation. gnm_validation_is_ok can be used to
446 * verify that @v has all of the required information.
448 void
449 gnm_validation_set_expr (GnmValidation *v,
450 GnmExprTop const *texpr, unsigned indx)
452 g_return_if_fail (indx <= 1);
454 dependent_managed_set_expr (&v->deps[indx], texpr);
457 GError *
458 gnm_validation_is_ok (GnmValidation const *v)
460 unsigned nops, i;
462 switch (v->type) {
463 case GNM_VALIDATION_TYPE_CUSTOM:
464 case GNM_VALIDATION_TYPE_IN_LIST:
465 nops = 1;
466 break;
467 case GNM_VALIDATION_TYPE_ANY:
468 nops = 0;
469 break;
470 default: nops = (v->op == GNM_VALIDATION_OP_NONE) ? 0 : opinfo[v->op].nops;
473 for (i = 0 ; i < 2 ; i++)
474 if (v->deps[i].texpr == NULL) {
475 if (i < nops)
476 return g_error_new (1, 0, N_("Missing formula for validation"));
477 } else {
478 if (i >= nops)
479 return g_error_new (1, 0, N_("Extra formula for validation"));
482 return NULL;
485 static ValidationStatus
486 validation_barf (WorkbookControl *wbc, GnmValidation const *gv,
487 char *def_msg, gboolean *showed_dialog)
489 char const *msg = gv->msg ? gv->msg->str : def_msg;
490 char const *title = gv->title ? gv->title->str : _("Gnumeric: Validation");
491 ValidationStatus result;
493 if (gv->style == GNM_VALIDATION_STYLE_NONE) {
494 /* Invalid, but we're asked to ignore. */
495 result = GNM_VALIDATION_STATUS_VALID;
496 } else {
497 if (showed_dialog) *showed_dialog = TRUE;
498 result = wb_control_validation_msg (wbc, gv->style, title, msg);
500 g_free (def_msg);
501 return result;
504 static GnmValue *
505 cb_validate_custom (GnmValueIter const *v_iter, GnmValue const *target)
507 if (value_compare (v_iter->v, target, FALSE) == IS_EQUAL)
508 return VALUE_TERMINATE;
509 else
510 return NULL;
513 #define BARF(msg) \
514 do { \
515 return validation_barf (wbc, v, msg, showed_dialog); \
516 } while (0)
519 * gnm_validation_eval:
520 * @wbc:
521 * @mstyle:
522 * @sheet:
523 * @pos:
524 * @showed_dialog: (out) (optional):
526 * Checks the validation in @mstyle, if any. Set @showed_dialog to %TRUE
527 * if a dialog was showed as a result.
529 ValidationStatus
530 gnm_validation_eval (WorkbookControl *wbc, GnmStyle const *mstyle,
531 Sheet *sheet, GnmCellPos const *pos,
532 gboolean *showed_dialog)
534 GnmValidation const *v;
535 GnmCell *cell;
536 GnmValue *val;
537 gnm_float x;
538 int nok, i;
539 GnmEvalPos ep;
541 if (showed_dialog) *showed_dialog = FALSE;
543 v = gnm_style_get_validation (mstyle);
544 if (v == NULL)
545 return GNM_VALIDATION_STATUS_VALID;
547 if (v->type == GNM_VALIDATION_TYPE_ANY)
548 return GNM_VALIDATION_STATUS_VALID;
550 cell = sheet_cell_get (sheet, pos->col, pos->row);
551 if (cell != NULL)
552 gnm_cell_eval (cell);
554 if (gnm_cell_is_empty (cell)) {
555 if (v->allow_blank)
556 return GNM_VALIDATION_STATUS_VALID;
557 BARF (g_strdup_printf (_("Cell %s is not permitted to be blank"),
558 cell_name (cell)));
561 val = cell->value;
562 switch (val->v_any.type) {
563 case VALUE_ERROR:
564 if (typeinfo[v->type].errors_not_allowed)
565 BARF (g_strdup_printf (_("Cell %s is not permitted to contain error values"),
566 cell_name (cell)));
567 break;
569 case VALUE_BOOLEAN:
570 if (typeinfo[v->type].bool_always_ok)
571 return GNM_VALIDATION_STATUS_VALID;
572 break;
574 case VALUE_STRING:
575 if (typeinfo[v->type].strings_not_allowed)
576 BARF (g_strdup_printf (_("Cell %s is not permitted to contain strings"),
577 cell_name (cell)));
578 break;
580 default:
581 break;
584 eval_pos_init_cell (&ep, cell);
586 switch (v->type) {
587 case GNM_VALIDATION_TYPE_AS_INT:
588 x = value_get_as_float (val);
589 if (gnm_fake_floor (x) == gnm_fake_ceil (x))
590 break;
591 else
592 BARF (g_strdup_printf (_("'%s' is not an integer"),
593 value_peek_string (val)));
595 case GNM_VALIDATION_TYPE_AS_NUMBER:
596 x = value_get_as_float (val);
597 break;
599 case GNM_VALIDATION_TYPE_AS_DATE: /* What the hell does this do? */
600 x = value_get_as_float (val);
601 if (x < 0)
602 BARF (g_strdup_printf (_("'%s' is not a valid date"),
603 value_peek_string (val)));
604 break;
607 case GNM_VALIDATION_TYPE_AS_TIME: /* What the hell does this do? */
608 x = value_get_as_float (val);
609 break;
611 case GNM_VALIDATION_TYPE_IN_LIST: {
612 GnmExprTop const *texpr = v->deps[0].texpr;
613 if (texpr) {
614 GnmValue *list = gnm_expr_top_eval
615 (texpr, &ep,
616 GNM_EXPR_EVAL_PERMIT_NON_SCALAR | GNM_EXPR_EVAL_PERMIT_EMPTY);
617 GnmValue *res = value_area_foreach (list, &ep, CELL_ITER_IGNORE_BLANK,
618 (GnmValueIterFunc) cb_validate_custom, val);
619 value_release (list);
620 if (res == NULL) {
621 GnmParsePos pp;
622 char *expr_str = gnm_expr_top_as_string
623 (texpr,
624 parse_pos_init_evalpos (&pp, &ep),
625 ep.sheet->convs);
626 char *msg = g_strdup_printf (_("%s does not contain the new value."), expr_str);
627 g_free (expr_str);
628 BARF (msg);
631 return GNM_VALIDATION_STATUS_VALID;
634 case GNM_VALIDATION_TYPE_TEXT_LENGTH:
635 /* XL appears to use a very basic value->string mapping that
636 * ignores formatting.
637 * eg len (12/13/01) == len (37238) = 5
638 * This seems wrong for
640 x = g_utf8_strlen (value_peek_string (val), -1);
641 break;
643 case GNM_VALIDATION_TYPE_CUSTOM: {
644 gboolean valid;
645 GnmExprTop const *texpr = v->deps[0].texpr;
647 if (!texpr)
648 return GNM_VALIDATION_STATUS_VALID;
650 val = gnm_expr_top_eval (texpr, &ep, GNM_EXPR_EVAL_SCALAR_NON_EMPTY);
651 valid = value_get_as_bool (val, NULL);
652 value_release (val);
654 if (valid)
655 return GNM_VALIDATION_STATUS_VALID;
656 else {
657 GnmParsePos pp;
658 char *expr_str = gnm_expr_top_as_string
659 (texpr,
660 parse_pos_init_evalpos (&pp, &ep),
661 ep.sheet->convs);
662 char *msg = g_strdup_printf (_("%s is not true."), expr_str);
663 g_free (expr_str);
664 BARF (msg);
668 default:
669 g_assert_not_reached ();
670 return GNM_VALIDATION_STATUS_VALID;
673 if (v->op == GNM_VALIDATION_OP_NONE)
674 return GNM_VALIDATION_STATUS_VALID;
676 nok = 0;
677 for (i = 0; i < opinfo[v->op].nops; i++) {
678 GnmExprTop const *texpr_i = v->deps[i].texpr;
679 GnmExprTop const *texpr;
680 GnmValue *cres;
682 if (!texpr_i) {
683 nok++;
684 continue;
687 texpr = gnm_expr_top_new
688 (gnm_expr_new_binary
689 (gnm_expr_new_constant (value_new_float (x)),
690 opinfo[v->op].ops[i],
691 gnm_expr_copy (texpr_i->expr)));
692 cres = gnm_expr_top_eval
693 (texpr, &ep, GNM_EXPR_EVAL_SCALAR_NON_EMPTY);
694 if (value_get_as_bool (cres, NULL))
695 nok++;
696 value_release (cres);
697 gnm_expr_top_unref (texpr);
700 if (nok < opinfo[v->op].ntrue)
701 BARF (g_strdup_printf (_("%s is out of permitted range"),
702 value_peek_string (val)));
704 return GNM_VALIDATION_STATUS_VALID;
707 #undef BARF
709 typedef struct {
710 WorkbookControl *wbc;
711 Sheet *sheet;
712 GnmCellPos const *pos;
713 gboolean *showed_dialog;
714 ValidationStatus status;
715 } validation_eval_t;
717 static GnmValue *
718 validation_eval_range_cb (GnmCellIter const *iter, validation_eval_t *closure)
720 ValidationStatus status;
721 gboolean showed_dialog;
722 GnmStyle const *mstyle = sheet_style_get
723 (closure->sheet, iter->pp.eval.col, iter->pp.eval.row);
725 if (mstyle != NULL) {
726 status = gnm_validation_eval (closure->wbc, mstyle,
727 closure->sheet, &iter->pp.eval,
728 &showed_dialog);
729 if (closure->showed_dialog)
730 *closure->showed_dialog = *closure->showed_dialog || showed_dialog;
732 if (status != GNM_VALIDATION_STATUS_VALID) {
733 closure->status = status;
734 return VALUE_TERMINATE;
738 return NULL;
741 ValidationStatus
742 gnm_validation_eval_range (WorkbookControl *wbc,
743 Sheet *sheet, GnmCellPos const *pos,
744 GnmRange const *r,
745 gboolean *showed_dialog)
747 GnmValue *result;
748 validation_eval_t closure;
749 GnmEvalPos ep;
750 GnmValue *cell_range = value_new_cellrange_r (sheet, r);
752 closure.wbc = wbc;
753 closure.sheet = sheet;
754 closure.pos = pos;
755 closure.showed_dialog = showed_dialog;
756 closure.status = GNM_VALIDATION_STATUS_VALID;
758 eval_pos_init_pos (&ep, sheet, pos);
760 result = workbook_foreach_cell_in_range (&ep, cell_range, CELL_ITER_ALL,
761 (CellIterFunc) validation_eval_range_cb,
762 &closure);
764 value_release (cell_range);
766 if (result == NULL)
767 return GNM_VALIDATION_STATUS_VALID;
768 return closure.status;