CRONBACH: Simplify.
[gnumeric.git] / src / validation.c
blobe3373f32eba8a34fc85963a0ca1f9c68495b6790
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 void
364 gnm_validation_ref (GnmValidation const *v)
366 g_return_if_fail (v != NULL);
367 ((GnmValidation *)v)->ref_count++;
370 void
371 gnm_validation_unref (GnmValidation const *val)
373 GnmValidation *v = (GnmValidation *)val;
375 g_return_if_fail (v != NULL);
377 v->ref_count--;
379 if (v->ref_count < 1) {
380 int i;
382 if (v->title != NULL) {
383 go_string_unref (v->title);
384 v->title = NULL;
386 if (v->msg != NULL) {
387 go_string_unref (v->msg);
388 v->msg = NULL;
390 for (i = 0 ; i < 2 ; i++)
391 dependent_managed_set_expr (&v->deps[i], NULL);
392 g_free (v);
396 GType
397 gnm_validation_get_type (void)
399 static GType t = 0;
401 if (t == 0) {
402 t = g_boxed_type_register_static ("GnmValidation",
403 (GBoxedCopyFunc)gnm_validation_ref,
404 (GBoxedFreeFunc)gnm_validation_unref);
406 return t;
410 * gnm_validation_get_sheet:
411 * @v: #GnmValidation
413 * Returns: (transfer none): the sheet.
415 Sheet *
416 gnm_validation_get_sheet (GnmValidation const *v)
418 g_return_val_if_fail (v != NULL, NULL);
419 return v->deps[0].sheet;
422 void
423 gnm_validation_set_sheet (GnmValidation *v, Sheet *sheet)
425 int i;
427 g_return_if_fail (v != NULL);
428 g_return_if_fail (IS_SHEET (sheet));
430 for (i = 0; i < 2; i++)
431 dependent_managed_set_sheet (&v->deps[i], sheet);
436 * gnm_validation_set_expr :
437 * @v: #GnmValidation
438 * @texpr: #GnmExprTop
439 * @indx: 0 or 1
441 * Assign an expression to a validation. gnm_validation_is_ok can be used to
442 * verify that @v has all of the required information.
444 void
445 gnm_validation_set_expr (GnmValidation *v,
446 GnmExprTop const *texpr, unsigned indx)
448 g_return_if_fail (indx <= 1);
450 dependent_managed_set_expr (&v->deps[indx], texpr);
453 GError *
454 gnm_validation_is_ok (GnmValidation const *v)
456 unsigned nops, i;
458 switch (v->type) {
459 case GNM_VALIDATION_TYPE_CUSTOM:
460 case GNM_VALIDATION_TYPE_IN_LIST:
461 nops = 1;
462 break;
463 case GNM_VALIDATION_TYPE_ANY:
464 nops = 0;
465 break;
466 default: nops = (v->op == GNM_VALIDATION_OP_NONE) ? 0 : opinfo[v->op].nops;
469 for (i = 0 ; i < 2 ; i++)
470 if (v->deps[i].texpr == NULL) {
471 if (i < nops)
472 return g_error_new (1, 0, N_("Missing formula for validation"));
473 } else {
474 if (i >= nops)
475 return g_error_new (1, 0, N_("Extra formula for validation"));
478 return NULL;
481 static ValidationStatus
482 validation_barf (WorkbookControl *wbc, GnmValidation const *gv,
483 char *def_msg, gboolean *showed_dialog)
485 char const *msg = gv->msg ? gv->msg->str : def_msg;
486 char const *title = gv->title ? gv->title->str : _("Gnumeric: Validation");
487 ValidationStatus result;
489 if (gv->style == GNM_VALIDATION_STYLE_NONE) {
490 /* Invalid, but we're asked to ignore. */
491 result = GNM_VALIDATION_STATUS_VALID;
492 } else {
493 if (showed_dialog) *showed_dialog = TRUE;
494 result = wb_control_validation_msg (wbc, gv->style, title, msg);
496 g_free (def_msg);
497 return result;
500 static GnmValue *
501 cb_validate_custom (GnmValueIter const *v_iter, GnmValue const *target)
503 if (value_compare (v_iter->v, target, FALSE) == IS_EQUAL)
504 return VALUE_TERMINATE;
505 else
506 return NULL;
509 #define BARF(msg) \
510 do { \
511 return validation_barf (wbc, v, msg, showed_dialog); \
512 } while (0)
515 * gnm_validation_eval:
516 * @wbc:
517 * @mstyle:
518 * @sheet:
520 * validation set in the GnmStyle if applicable.
522 ValidationStatus
523 gnm_validation_eval (WorkbookControl *wbc, GnmStyle const *mstyle,
524 Sheet *sheet, GnmCellPos const *pos, gboolean *showed_dialog)
526 GnmValidation const *v;
527 GnmCell *cell;
528 GnmValue *val;
529 gnm_float x;
530 int nok, i;
531 GnmEvalPos ep;
533 if (showed_dialog) *showed_dialog = FALSE;
535 v = gnm_style_get_validation (mstyle);
536 if (v == NULL)
537 return GNM_VALIDATION_STATUS_VALID;
539 if (v->type == GNM_VALIDATION_TYPE_ANY)
540 return GNM_VALIDATION_STATUS_VALID;
542 cell = sheet_cell_get (sheet, pos->col, pos->row);
543 if (cell != NULL)
544 gnm_cell_eval (cell);
546 if (gnm_cell_is_empty (cell)) {
547 if (v->allow_blank)
548 return GNM_VALIDATION_STATUS_VALID;
549 BARF (g_strdup_printf (_("Cell %s is not permitted to be blank"),
550 cell_name (cell)));
553 val = cell->value;
554 switch (val->v_any.type) {
555 case VALUE_ERROR:
556 if (typeinfo[v->type].errors_not_allowed)
557 BARF (g_strdup_printf (_("Cell %s is not permitted to contain error values"),
558 cell_name (cell)));
559 break;
561 case VALUE_BOOLEAN:
562 if (typeinfo[v->type].bool_always_ok)
563 return GNM_VALIDATION_STATUS_VALID;
564 break;
566 case VALUE_STRING:
567 if (typeinfo[v->type].strings_not_allowed)
568 BARF (g_strdup_printf (_("Cell %s is not permitted to contain strings"),
569 cell_name (cell)));
570 break;
572 default:
573 break;
576 eval_pos_init_cell (&ep, cell);
578 switch (v->type) {
579 case GNM_VALIDATION_TYPE_AS_INT:
580 x = value_get_as_float (val);
581 if (gnm_fake_floor (x) == gnm_fake_ceil (x))
582 break;
583 else
584 BARF (g_strdup_printf (_("'%s' is not an integer"),
585 value_peek_string (val)));
587 case GNM_VALIDATION_TYPE_AS_NUMBER:
588 x = value_get_as_float (val);
589 break;
591 case GNM_VALIDATION_TYPE_AS_DATE: /* What the hell does this do? */
592 x = value_get_as_float (val);
593 if (x < 0)
594 BARF (g_strdup_printf (_("'%s' is not a valid date"),
595 value_peek_string (val)));
596 break;
599 case GNM_VALIDATION_TYPE_AS_TIME: /* What the hell does this do? */
600 x = value_get_as_float (val);
601 break;
603 case GNM_VALIDATION_TYPE_IN_LIST: {
604 GnmExprTop const *texpr = v->deps[0].texpr;
605 if (texpr) {
606 GnmValue *list = gnm_expr_top_eval
607 (texpr, &ep,
608 GNM_EXPR_EVAL_PERMIT_NON_SCALAR | GNM_EXPR_EVAL_PERMIT_EMPTY);
609 GnmValue *res = value_area_foreach (list, &ep, CELL_ITER_IGNORE_BLANK,
610 (GnmValueIterFunc) cb_validate_custom, val);
611 value_release (list);
612 if (res == NULL) {
613 GnmParsePos pp;
614 char *expr_str = gnm_expr_top_as_string
615 (texpr,
616 parse_pos_init_evalpos (&pp, &ep),
617 ep.sheet->convs);
618 char *msg = g_strdup_printf (_("%s does not contain the new value."), expr_str);
619 g_free (expr_str);
620 BARF (msg);
623 return GNM_VALIDATION_STATUS_VALID;
626 case GNM_VALIDATION_TYPE_TEXT_LENGTH:
627 /* XL appears to use a very basic value->string mapping that
628 * ignores formatting.
629 * eg len (12/13/01) == len (37238) = 5
630 * This seems wrong for
632 x = g_utf8_strlen (value_peek_string (val), -1);
633 break;
635 case GNM_VALIDATION_TYPE_CUSTOM: {
636 gboolean valid;
637 GnmExprTop const *texpr = v->deps[0].texpr;
639 if (!texpr)
640 return GNM_VALIDATION_STATUS_VALID;
642 val = gnm_expr_top_eval (texpr, &ep, GNM_EXPR_EVAL_SCALAR_NON_EMPTY);
643 valid = value_get_as_bool (val, NULL);
644 value_release (val);
646 if (valid)
647 return GNM_VALIDATION_STATUS_VALID;
648 else {
649 GnmParsePos pp;
650 char *expr_str = gnm_expr_top_as_string
651 (texpr,
652 parse_pos_init_evalpos (&pp, &ep),
653 ep.sheet->convs);
654 char *msg = g_strdup_printf (_("%s is not true."), expr_str);
655 g_free (expr_str);
656 BARF (msg);
660 default:
661 g_assert_not_reached ();
662 return GNM_VALIDATION_STATUS_VALID;
665 if (v->op == GNM_VALIDATION_OP_NONE)
666 return GNM_VALIDATION_STATUS_VALID;
668 nok = 0;
669 for (i = 0; i < opinfo[v->op].nops; i++) {
670 GnmExprTop const *texpr_i = v->deps[i].texpr;
671 GnmExprTop const *texpr;
672 GnmValue *cres;
674 if (!texpr_i) {
675 nok++;
676 continue;
679 texpr = gnm_expr_top_new
680 (gnm_expr_new_binary
681 (gnm_expr_new_constant (value_new_float (x)),
682 opinfo[v->op].ops[i],
683 gnm_expr_copy (texpr_i->expr)));
684 cres = gnm_expr_top_eval
685 (texpr, &ep, GNM_EXPR_EVAL_SCALAR_NON_EMPTY);
686 if (value_get_as_bool (cres, NULL))
687 nok++;
688 value_release (cres);
689 gnm_expr_top_unref (texpr);
692 if (nok < opinfo[v->op].ntrue)
693 BARF (g_strdup_printf (_("%s is out of permitted range"),
694 value_peek_string (val)));
696 return GNM_VALIDATION_STATUS_VALID;
699 #undef BARF
701 typedef struct {
702 WorkbookControl *wbc;
703 Sheet *sheet;
704 GnmCellPos const *pos;
705 gboolean *showed_dialog;
706 ValidationStatus status;
707 } validation_eval_t;
709 static GnmValue *
710 validation_eval_range_cb (GnmCellIter const *iter, validation_eval_t *closure)
712 ValidationStatus status;
713 gboolean showed_dialog;
714 GnmStyle const *mstyle = sheet_style_get
715 (closure->sheet, iter->pp.eval.col, iter->pp.eval.row);
717 if (mstyle != NULL) {
718 status = gnm_validation_eval (closure->wbc, mstyle,
719 closure->sheet, &iter->pp.eval,
720 &showed_dialog);
721 if (closure->showed_dialog)
722 *closure->showed_dialog = *closure->showed_dialog || showed_dialog;
724 if (status != GNM_VALIDATION_STATUS_VALID) {
725 closure->status = status;
726 return VALUE_TERMINATE;
730 return NULL;
733 ValidationStatus
734 gnm_validation_eval_range (WorkbookControl *wbc,
735 Sheet *sheet, GnmCellPos const *pos, GnmRange const *r,
736 gboolean *showed_dialog)
738 GnmValue *result;
739 validation_eval_t closure;
740 GnmEvalPos ep;
741 GnmValue *cell_range = value_new_cellrange_r (sheet, r);
743 closure.wbc = wbc;
744 closure.sheet = sheet;
745 closure.pos = pos;
746 closure.showed_dialog = showed_dialog;
747 closure.status = GNM_VALIDATION_STATUS_VALID;
749 eval_pos_init_pos (&ep, sheet, pos);
751 result = workbook_foreach_cell_in_range (&ep, cell_range, CELL_ITER_ALL,
752 (CellIterFunc) validation_eval_range_cb,
753 &closure);
755 value_release (cell_range);
757 if (result == NULL)
758 return GNM_VALIDATION_STATUS_VALID;
759 return closure.status;