Introspection fixes.
[gnumeric.git] / src / validation.c
blob8a2b8c6b9afc7679e6c01b9e698f93b182f30c28
1 /* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3 * validation.c: Implementation of validation.
5 * Copyright (C) Jody Goldberg <jody@gnome.org>
7 * based on work by
8 * Almer S. Tigelaar <almer@gnome.org>
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, see <https://www.gnu.org/licenses/>.
24 #include <gnumeric-config.h>
25 #include "gnumeric.h"
26 #include "validation.h"
27 #include "validation-combo.h"
29 #include "numbers.h"
30 #include "expr.h"
31 #include "mstyle.h"
32 #include "sheet.h"
33 #include "cell.h"
34 #include "value.h"
35 #include "workbook.h"
36 #include "workbook-control.h"
37 #include "parse-util.h"
39 #include "sheet-view.h"
40 #include "sheet-object.h"
41 #include "sheet-style.h"
42 #include "gnm-validation-combo-view.h"
43 #include "gnm-cell-combo-view.h"
44 #include <gsf/gsf-impl-utils.h>
46 #include <glib/gi18n-lib.h>
48 static const struct {
49 gboolean errors_not_allowed;
50 gboolean strings_not_allowed;
51 gboolean bool_always_ok;
52 } typeinfo[] = {
53 { FALSE, FALSE, TRUE }, /* ANY */
54 { TRUE, TRUE, TRUE }, /* AS_INT */
55 { TRUE, TRUE, TRUE }, /* AS_NUMBER */
56 { TRUE, FALSE, FALSE }, /* IN_LIST */
57 { TRUE, TRUE, TRUE }, /* AS_DATE */
58 { TRUE, TRUE, TRUE }, /* AS_TIME */
59 { TRUE, FALSE, FALSE }, /* TEXT_LENGTH */
60 { FALSE, FALSE, FALSE } /* CUSTOM */
63 #define NONE (GnmExprOp)-1
65 static struct {
66 int nops;
67 GnmExprOp ops[2];
68 int ntrue;
69 char const *name;
70 } const opinfo[] = {
71 /* Note: no entry for GNM_VALIDATION_OP_NONE */
72 { 2, { GNM_EXPR_OP_GTE, GNM_EXPR_OP_LTE }, 2, N_("Between") },
73 { 2, { GNM_EXPR_OP_LT, GNM_EXPR_OP_GT }, 1, N_("Not_Between") },
74 { 1, { GNM_EXPR_OP_EQUAL, NONE }, 1, N_("Equal") },
75 { 1, { GNM_EXPR_OP_NOT_EQUAL, NONE }, 1, N_("Not Equal") },
76 { 1, { GNM_EXPR_OP_GT, NONE }, 1, N_("Greater Than") },
77 { 1, { GNM_EXPR_OP_LT, NONE }, 1, N_("Less Than") },
78 { 1, { GNM_EXPR_OP_GTE, NONE }, 1, N_("Greater than or Equal") },
79 { 1, { GNM_EXPR_OP_LTE, NONE }, 1, N_("Less than or Equal") },
82 #undef NONE
84 /***************************************************************************/
86 static GObjectClass *gvc_parent_klass;
88 static void
89 gnm_validation_combo_finalize (GObject *object)
91 GnmValidationCombo *vcombo = GNM_VALIDATION_COMBO (object);
93 if (NULL != vcombo->validation) {
94 gnm_validation_unref (vcombo->validation);
95 vcombo->validation = NULL;
98 gvc_parent_klass->finalize (object);
101 static void
102 gnm_validation_combo_init (G_GNUC_UNUSED SheetObject *so)
106 static SheetObjectView *
107 gnm_validation_combo_view_new (SheetObject *so, SheetObjectViewContainer *container)
109 return gnm_cell_combo_view_new (so,
110 gnm_validation_combo_view_get_type (), container);
113 static void
114 gnm_validation_combo_class_init (GObjectClass *gobject_class)
116 SheetObjectClass *so_class = GNM_SO_CLASS (gobject_class);
117 gobject_class->finalize = gnm_validation_combo_finalize;
118 so_class->new_view = gnm_validation_combo_view_new;
120 gvc_parent_klass = g_type_class_peek_parent (gobject_class);
123 typedef SheetObjectClass GnmValidationComboClass;
124 GSF_CLASS (GnmValidationCombo, gnm_validation_combo,
125 gnm_validation_combo_class_init, gnm_validation_combo_init,
126 gnm_cell_combo_get_type ())
128 SheetObject *
129 gnm_validation_combo_new (GnmValidation const *val, SheetView *sv)
131 GnmValidationCombo *vcombo;
133 g_return_val_if_fail (val != NULL, NULL);
134 g_return_val_if_fail (sv != NULL, NULL);
136 vcombo = g_object_new (GNM_VALIDATION_COMBO_TYPE, "sheet-view", sv, NULL);
137 gnm_validation_ref (vcombo->validation = val);
138 return GNM_SO (vcombo);
141 /***************************************************************************/
143 GType
144 gnm_validation_style_get_type (void)
146 static GType etype = 0;
147 if (etype == 0) {
148 static GEnumValue const values[] = {
149 { GNM_VALIDATION_STYLE_NONE,
150 "GNM_VALIDATION_STYLE_NONE", "none"},
151 { GNM_VALIDATION_STYLE_STOP,
152 "GNM_VALIDATION_STYLE_STOP", "stop"},
153 { GNM_VALIDATION_STYLE_WARNING,
154 "GNM_VALIDATION_STYLE_WARNING", "warning"},
155 { GNM_VALIDATION_STYLE_INFO,
156 "GNM_VALIDATION_STYLE_INFO", "info"},
157 { GNM_VALIDATION_STYLE_PARSE_ERROR,
158 "GNM_VALIDATION_STYLE_PARSE_ERROR", "parse-error"},
159 { 0, NULL, NULL }
161 etype = g_enum_register_static ("GnmValidationStyle",
162 values);
164 return etype;
167 GType
168 gnm_validation_type_get_type (void)
170 static GType etype = 0;
171 if (etype == 0) {
172 static GEnumValue const values[] = {
173 { GNM_VALIDATION_TYPE_ANY,
174 "GNM_VALIDATION_TYPE_ANY", "any"},
175 { GNM_VALIDATION_TYPE_AS_INT,
176 "GNM_VALIDATION_TYPE_AS_INT", "int"},
177 { GNM_VALIDATION_TYPE_AS_NUMBER,
178 "GNM_VALIDATION_TYPE_AS_NUMBER", "number"},
179 { GNM_VALIDATION_TYPE_IN_LIST,
180 "GNM_VALIDATION_TYPE_IN_LIST", "list"},
181 { GNM_VALIDATION_TYPE_AS_DATE,
182 "GNM_VALIDATION_TYPE_AS_DATE", "date"},
183 { GNM_VALIDATION_TYPE_AS_TIME,
184 "GNM_VALIDATION_TYPE_AS_TIME", "time"},
185 { GNM_VALIDATION_TYPE_TEXT_LENGTH,
186 "GNM_VALIDATION_TYPE_TEXT_LENGTH", "length"},
187 { GNM_VALIDATION_TYPE_CUSTOM,
188 "GNM_VALIDATION_TYPE_CUSTOM", "custom"},
189 { 0, NULL, NULL }
191 etype = g_enum_register_static ("GnmValidationType",
192 values);
194 return etype;
197 GType
198 gnm_validation_op_get_type (void)
200 static GType etype = 0;
201 if (etype == 0) {
202 static GEnumValue const values[] = {
203 { GNM_VALIDATION_OP_NONE,
204 "GNM_VALIDATION_OP_NONE", "none"},
205 { GNM_VALIDATION_OP_BETWEEN,
206 "GNM_VALIDATION_OP_BETWEEN", "between"},
207 { GNM_VALIDATION_OP_NOT_BETWEEN,
208 "GNM_VALIDATION_OP_NOT_BETWEEN", "not-between"},
209 { GNM_VALIDATION_OP_EQUAL,
210 "GNM_VALIDATION_OP_EQUAL", "equal"},
211 { GNM_VALIDATION_OP_NOT_EQUAL,
212 "GNM_VALIDATION_OP_NOT_EQUAL", "not-equal"},
213 { GNM_VALIDATION_OP_GT,
214 "GNM_VALIDATION_OP_GT", "gt"},
215 { GNM_VALIDATION_OP_LT,
216 "GNM_VALIDATION_OP_LT", "lt"},
217 { GNM_VALIDATION_OP_GTE,
218 "GNM_VALIDATION_OP_GTE", "gte"},
219 { GNM_VALIDATION_OP_LTE,
220 "GNM_VALIDATION_OP_LTE", "lte"},
221 { 0, NULL, NULL }
223 etype = g_enum_register_static ("GnmValidationOp",
224 values);
226 return etype;
230 /***************************************************************************/
233 * gnm_validation_new :
234 * @title: will be copied.
235 * @msg: will be copied.
236 * @texpr0: absorb the reference to the expression (optionally %NULL).
237 * @texpr1: absorb the reference to the expression (optionally %NULL).
239 * Does _NOT_ require all necessary information to be set here.
240 * gnm_validation_set_expr can be used to change the expressions after creation,
241 * and gnm_validation_is_ok can be used to ensure that things are properly setup.
243 * Returns a new @GnmValidation object that needs to be unrefed.
245 GnmValidation *
246 gnm_validation_new (ValidationStyle style,
247 ValidationType type,
248 ValidationOp op,
249 Sheet *sheet,
250 char const *title, char const *msg,
251 GnmExprTop const *texpr0, GnmExprTop const *texpr1,
252 gboolean allow_blank, gboolean use_dropdown)
254 GnmValidation *v;
255 int nops;
257 g_return_val_if_fail ((size_t)type < G_N_ELEMENTS (typeinfo), NULL);
258 g_return_val_if_fail (op >= GNM_VALIDATION_OP_NONE, NULL);
259 g_return_val_if_fail (op < (int)G_N_ELEMENTS (opinfo), NULL);
260 g_return_val_if_fail (IS_SHEET (sheet), NULL);
262 switch (type) {
263 case GNM_VALIDATION_TYPE_CUSTOM:
264 case GNM_VALIDATION_TYPE_IN_LIST:
265 nops = 1;
266 if (op != GNM_VALIDATION_OP_NONE) {
268 * This can happen if an .xls file was saved
269 * as a .gnumeric.
271 op = GNM_VALIDATION_OP_NONE;
273 break;
274 case GNM_VALIDATION_TYPE_ANY:
275 nops = 0;
276 break;
277 default:
278 nops = (op == GNM_VALIDATION_OP_NONE) ? 0 : opinfo[op].nops;
281 v = g_new0 (GnmValidation, 1);
282 v->ref_count = 1;
283 v->title = title && title[0] ? go_string_new (title) : NULL;
284 v->msg = msg && msg[0] ? go_string_new (msg) : NULL;
286 dependent_managed_init (&v->deps[0], sheet);
287 if (texpr0) {
288 if (nops > 0)
289 dependent_managed_set_expr (&v->deps[0], texpr0);
290 gnm_expr_top_unref (texpr0);
293 dependent_managed_init (&v->deps[1], sheet);
294 if (texpr1) {
295 if (nops > 1)
296 dependent_managed_set_expr (&v->deps[1], texpr1);
297 gnm_expr_top_unref (texpr1);
300 v->style = style;
301 v->type = type;
302 v->op = op;
303 v->allow_blank = (allow_blank != FALSE);
304 v->use_dropdown = (use_dropdown != FALSE);
306 return v;
309 GnmValidation *
310 gnm_validation_dup (GnmValidation *v)
312 GnmValidation *dst;
313 int i;
315 g_return_val_if_fail (v != NULL, NULL);
317 dst = gnm_validation_new (v->style, v->type, v->op,
318 gnm_validation_get_sheet (v),
319 v->title ? v->title->str : NULL,
320 v->msg ? v->msg->str : NULL,
321 NULL, NULL,
322 v->allow_blank, v->use_dropdown);
323 for (i = 0; i < 2; i++)
324 gnm_validation_set_expr (dst, v->deps[i].texpr, i);
325 return dst;
328 gboolean
329 gnm_validation_equal (GnmValidation const *a, GnmValidation const *b,
330 gboolean relax_sheet)
332 int i;
334 g_return_val_if_fail (a != NULL, FALSE);
335 g_return_val_if_fail (b != NULL, FALSE);
337 if (a == b)
338 return TRUE;
340 if (!relax_sheet &&
341 gnm_validation_get_sheet (a) != gnm_validation_get_sheet (b))
342 return FALSE;
344 if (!(g_strcmp0 (a->title ? a->title->str : NULL,
345 b->title ? b->title->str : NULL) == 0 &&
346 g_strcmp0 (a->msg ? a->msg->str : NULL,
347 b->msg ? b->msg->str : NULL) == 0 &&
348 a->style == b->style &&
349 a->type == b->type &&
350 a->op == b->op &&
351 a->allow_blank == b->allow_blank &&
352 a->use_dropdown == b->use_dropdown))
353 return FALSE;
355 for (i = 0; i < 2; i++)
356 if (!gnm_expr_top_equal (a->deps[i].texpr, b->deps[i].texpr))
357 return FALSE;
359 return TRUE;
363 GnmValidation *
364 gnm_validation_ref (GnmValidation const *v)
366 g_return_val_if_fail (v != NULL, NULL);
367 ((GnmValidation *)v)->ref_count++;
368 return ((GnmValidation *)v);
371 void
372 gnm_validation_unref (GnmValidation const *val)
374 GnmValidation *v = (GnmValidation *)val;
376 g_return_if_fail (v != NULL);
378 v->ref_count--;
380 if (v->ref_count < 1) {
381 int i;
383 if (v->title != NULL) {
384 go_string_unref (v->title);
385 v->title = NULL;
387 if (v->msg != NULL) {
388 go_string_unref (v->msg);
389 v->msg = NULL;
391 for (i = 0 ; i < 2 ; i++)
392 dependent_managed_set_expr (&v->deps[i], NULL);
393 g_free (v);
397 GType
398 gnm_validation_get_type (void)
400 static GType t = 0;
402 if (t == 0) {
403 t = g_boxed_type_register_static ("GnmValidation",
404 (GBoxedCopyFunc)gnm_validation_ref,
405 (GBoxedFreeFunc)gnm_validation_unref);
407 return t;
411 * gnm_validation_get_sheet:
412 * @v: #GnmValidation
414 * Returns: (transfer none): the sheet.
416 Sheet *
417 gnm_validation_get_sheet (GnmValidation const *v)
419 g_return_val_if_fail (v != NULL, NULL);
420 return v->deps[0].sheet;
423 void
424 gnm_validation_set_sheet (GnmValidation *v, Sheet *sheet)
426 int i;
428 g_return_if_fail (v != NULL);
429 g_return_if_fail (IS_SHEET (sheet));
431 for (i = 0; i < 2; i++)
432 dependent_managed_set_sheet (&v->deps[i], sheet);
437 * gnm_validation_set_expr :
438 * @v: #GnmValidation
439 * @texpr: #GnmExprTop
440 * @indx: 0 or 1
442 * Assign an expression to a validation. gnm_validation_is_ok can be used to
443 * verify that @v has all of the required information.
445 void
446 gnm_validation_set_expr (GnmValidation *v,
447 GnmExprTop const *texpr, unsigned indx)
449 g_return_if_fail (indx <= 1);
451 dependent_managed_set_expr (&v->deps[indx], texpr);
454 GError *
455 gnm_validation_is_ok (GnmValidation const *v)
457 unsigned nops, i;
459 switch (v->type) {
460 case GNM_VALIDATION_TYPE_CUSTOM:
461 case GNM_VALIDATION_TYPE_IN_LIST:
462 nops = 1;
463 break;
464 case GNM_VALIDATION_TYPE_ANY:
465 nops = 0;
466 break;
467 default: nops = (v->op == GNM_VALIDATION_OP_NONE) ? 0 : opinfo[v->op].nops;
470 for (i = 0 ; i < 2 ; i++)
471 if (v->deps[i].texpr == NULL) {
472 if (i < nops)
473 return g_error_new (1, 0, N_("Missing formula for validation"));
474 } else {
475 if (i >= nops)
476 return g_error_new (1, 0, N_("Extra formula for validation"));
479 return NULL;
482 static ValidationStatus
483 validation_barf (WorkbookControl *wbc, GnmValidation const *gv,
484 char *def_msg, gboolean *showed_dialog)
486 char const *msg = gv->msg ? gv->msg->str : def_msg;
487 char const *title = gv->title ? gv->title->str : _("Gnumeric: Validation");
488 ValidationStatus result;
490 if (gv->style == GNM_VALIDATION_STYLE_NONE) {
491 /* Invalid, but we're asked to ignore. */
492 result = GNM_VALIDATION_STATUS_VALID;
493 } else {
494 if (showed_dialog) *showed_dialog = TRUE;
495 result = wb_control_validation_msg (wbc, gv->style, title, msg);
497 g_free (def_msg);
498 return result;
501 static GnmValue *
502 cb_validate_custom (GnmValueIter const *v_iter, GnmValue const *target)
504 if (value_compare (v_iter->v, target, FALSE) == IS_EQUAL)
505 return VALUE_TERMINATE;
506 else
507 return NULL;
510 #define BARF(msg) \
511 do { \
512 return validation_barf (wbc, v, msg, showed_dialog); \
513 } while (0)
516 * gnm_validation_eval:
517 * @wbc:
518 * @mstyle:
519 * @sheet:
521 * validation set in the GnmStyle if applicable.
523 ValidationStatus
524 gnm_validation_eval (WorkbookControl *wbc, GnmStyle const *mstyle,
525 Sheet *sheet, GnmCellPos const *pos, gboolean *showed_dialog)
527 GnmValidation const *v;
528 GnmCell *cell;
529 GnmValue *val;
530 gnm_float x;
531 int nok, i;
532 GnmEvalPos ep;
534 if (showed_dialog) *showed_dialog = FALSE;
536 v = gnm_style_get_validation (mstyle);
537 if (v == NULL)
538 return GNM_VALIDATION_STATUS_VALID;
540 if (v->type == GNM_VALIDATION_TYPE_ANY)
541 return GNM_VALIDATION_STATUS_VALID;
543 cell = sheet_cell_get (sheet, pos->col, pos->row);
544 if (cell != NULL)
545 gnm_cell_eval (cell);
547 if (gnm_cell_is_empty (cell)) {
548 if (v->allow_blank)
549 return GNM_VALIDATION_STATUS_VALID;
550 BARF (g_strdup_printf (_("Cell %s is not permitted to be blank"),
551 cell_name (cell)));
554 val = cell->value;
555 switch (val->v_any.type) {
556 case VALUE_ERROR:
557 if (typeinfo[v->type].errors_not_allowed)
558 BARF (g_strdup_printf (_("Cell %s is not permitted to contain error values"),
559 cell_name (cell)));
560 break;
562 case VALUE_BOOLEAN:
563 if (typeinfo[v->type].bool_always_ok)
564 return GNM_VALIDATION_STATUS_VALID;
565 break;
567 case VALUE_STRING:
568 if (typeinfo[v->type].strings_not_allowed)
569 BARF (g_strdup_printf (_("Cell %s is not permitted to contain strings"),
570 cell_name (cell)));
571 break;
573 default:
574 break;
577 eval_pos_init_cell (&ep, cell);
579 switch (v->type) {
580 case GNM_VALIDATION_TYPE_AS_INT:
581 x = value_get_as_float (val);
582 if (gnm_fake_floor (x) == gnm_fake_ceil (x))
583 break;
584 else
585 BARF (g_strdup_printf (_("'%s' is not an integer"),
586 value_peek_string (val)));
588 case GNM_VALIDATION_TYPE_AS_NUMBER:
589 x = value_get_as_float (val);
590 break;
592 case GNM_VALIDATION_TYPE_AS_DATE: /* What the hell does this do? */
593 x = value_get_as_float (val);
594 if (x < 0)
595 BARF (g_strdup_printf (_("'%s' is not a valid date"),
596 value_peek_string (val)));
597 break;
600 case GNM_VALIDATION_TYPE_AS_TIME: /* What the hell does this do? */
601 x = value_get_as_float (val);
602 break;
604 case GNM_VALIDATION_TYPE_IN_LIST: {
605 GnmExprTop const *texpr = v->deps[0].texpr;
606 if (texpr) {
607 GnmValue *list = gnm_expr_top_eval
608 (texpr, &ep,
609 GNM_EXPR_EVAL_PERMIT_NON_SCALAR | GNM_EXPR_EVAL_PERMIT_EMPTY);
610 GnmValue *res = value_area_foreach (list, &ep, CELL_ITER_IGNORE_BLANK,
611 (GnmValueIterFunc) cb_validate_custom, val);
612 value_release (list);
613 if (res == NULL) {
614 GnmParsePos pp;
615 char *expr_str = gnm_expr_top_as_string
616 (texpr,
617 parse_pos_init_evalpos (&pp, &ep),
618 ep.sheet->convs);
619 char *msg = g_strdup_printf (_("%s does not contain the new value."), expr_str);
620 g_free (expr_str);
621 BARF (msg);
624 return GNM_VALIDATION_STATUS_VALID;
627 case GNM_VALIDATION_TYPE_TEXT_LENGTH:
628 /* XL appears to use a very basic value->string mapping that
629 * ignores formatting.
630 * eg len (12/13/01) == len (37238) = 5
631 * This seems wrong for
633 x = g_utf8_strlen (value_peek_string (val), -1);
634 break;
636 case GNM_VALIDATION_TYPE_CUSTOM: {
637 gboolean valid;
638 GnmExprTop const *texpr = v->deps[0].texpr;
640 if (!texpr)
641 return GNM_VALIDATION_STATUS_VALID;
643 val = gnm_expr_top_eval (texpr, &ep, GNM_EXPR_EVAL_SCALAR_NON_EMPTY);
644 valid = value_get_as_bool (val, NULL);
645 value_release (val);
647 if (valid)
648 return GNM_VALIDATION_STATUS_VALID;
649 else {
650 GnmParsePos pp;
651 char *expr_str = gnm_expr_top_as_string
652 (texpr,
653 parse_pos_init_evalpos (&pp, &ep),
654 ep.sheet->convs);
655 char *msg = g_strdup_printf (_("%s is not true."), expr_str);
656 g_free (expr_str);
657 BARF (msg);
661 default:
662 g_assert_not_reached ();
663 return GNM_VALIDATION_STATUS_VALID;
666 if (v->op == GNM_VALIDATION_OP_NONE)
667 return GNM_VALIDATION_STATUS_VALID;
669 nok = 0;
670 for (i = 0; i < opinfo[v->op].nops; i++) {
671 GnmExprTop const *texpr_i = v->deps[i].texpr;
672 GnmExprTop const *texpr;
673 GnmValue *cres;
675 if (!texpr_i) {
676 nok++;
677 continue;
680 texpr = gnm_expr_top_new
681 (gnm_expr_new_binary
682 (gnm_expr_new_constant (value_new_float (x)),
683 opinfo[v->op].ops[i],
684 gnm_expr_copy (texpr_i->expr)));
685 cres = gnm_expr_top_eval
686 (texpr, &ep, GNM_EXPR_EVAL_SCALAR_NON_EMPTY);
687 if (value_get_as_bool (cres, NULL))
688 nok++;
689 value_release (cres);
690 gnm_expr_top_unref (texpr);
693 if (nok < opinfo[v->op].ntrue)
694 BARF (g_strdup_printf (_("%s is out of permitted range"),
695 value_peek_string (val)));
697 return GNM_VALIDATION_STATUS_VALID;
700 #undef BARF
702 typedef struct {
703 WorkbookControl *wbc;
704 Sheet *sheet;
705 GnmCellPos const *pos;
706 gboolean *showed_dialog;
707 ValidationStatus status;
708 } validation_eval_t;
710 static GnmValue *
711 validation_eval_range_cb (GnmCellIter const *iter, validation_eval_t *closure)
713 ValidationStatus status;
714 gboolean showed_dialog;
715 GnmStyle const *mstyle = sheet_style_get
716 (closure->sheet, iter->pp.eval.col, iter->pp.eval.row);
718 if (mstyle != NULL) {
719 status = gnm_validation_eval (closure->wbc, mstyle,
720 closure->sheet, &iter->pp.eval,
721 &showed_dialog);
722 if (closure->showed_dialog)
723 *closure->showed_dialog = *closure->showed_dialog || showed_dialog;
725 if (status != GNM_VALIDATION_STATUS_VALID) {
726 closure->status = status;
727 return VALUE_TERMINATE;
731 return NULL;
734 ValidationStatus
735 gnm_validation_eval_range (WorkbookControl *wbc,
736 Sheet *sheet, GnmCellPos const *pos, GnmRange const *r,
737 gboolean *showed_dialog)
739 GnmValue *result;
740 validation_eval_t closure;
741 GnmEvalPos ep;
742 GnmValue *cell_range = value_new_cellrange_r (sheet, r);
744 closure.wbc = wbc;
745 closure.sheet = sheet;
746 closure.pos = pos;
747 closure.showed_dialog = showed_dialog;
748 closure.status = GNM_VALIDATION_STATUS_VALID;
750 eval_pos_init_pos (&ep, sheet, pos);
752 result = workbook_foreach_cell_in_range (&ep, cell_range, CELL_ITER_ALL,
753 (CellIterFunc) validation_eval_range_cb,
754 &closure);
756 value_release (cell_range);
758 if (result == NULL)
759 return GNM_VALIDATION_STATUS_VALID;
760 return closure.status;