You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
gnucash/lib/goffice/split/validation.c

321 lines
8.4 KiB

/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
/*
* validation.c: Implementation of validation.
*
* Copyright (C) Jody Goldberg <jody@gnome.org>
*
* based on work by
* Almer S. Tigelaar <almer@gnome.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <gnumeric-config.h>
#include <glib/gi18n.h>
#include "gnumeric.h"
#include "numbers.h"
#include "mathfunc.h"
#include "validation.h"
#include "expr.h"
#include "mstyle.h"
#include "workbook.h"
#include "sheet.h"
#include "cell.h"
#include "value.h"
#include "number-match.h"
#include "workbook-control.h"
#include "parse-util.h"
#include <math.h>
#include <string.h>
/**
* validation_new :
*
* @title : will be copied.
* @msg : will be copied.
* @expr0 : absorb the reference to the expression.
* @expr1 : absorb the reference to the expression.
*/
GnmValidation *
validation_new (ValidationStyle style,
ValidationType type,
ValidationOp op,
char const *title, char const *msg,
GnmExpr const *expr0, GnmExpr const *expr1,
gboolean allow_blank, gboolean use_dropdown)
{
GnmValidation *v;
if (type == VALIDATION_TYPE_CUSTOM && op != VALIDATION_OP_NONE) {
/* This can happen if an .xls file was saved as a .gnumeric. */
g_warning ("VALIDATION_TYPE_CUSTOM needs to go with VALIDATION_OP_NONE. Fixing.");
op = VALIDATION_OP_NONE;
}
v = g_new0 (GnmValidation, 1);
v->ref_count = 1;
v->title = title ? gnm_string_get (title) : NULL;
v->msg = msg ? gnm_string_get (msg) : NULL;
v->expr[0] = expr0;
v->expr[1] = expr1;
v->style = style;
v->type = type;
v->op = op;
v->allow_blank = (allow_blank != FALSE);
v->use_dropdown = (use_dropdown != FALSE);
return v;
}
void
validation_ref (GnmValidation *v)
{
g_return_if_fail (v != NULL);
v->ref_count++;
}
void
validation_unref (GnmValidation *v)
{
g_return_if_fail (v != NULL);
v->ref_count--;
if (v->ref_count < 1) {
int i;
if (v->title != NULL) {
gnm_string_unref (v->title);
v->title = NULL;
}
if (v->msg != NULL) {
gnm_string_unref (v->msg);
v->msg = NULL;
}
for (i = 0 ; i < 2 ; i++)
if (v->expr[i] != NULL) {
gnm_expr_unref (v->expr[i]);
v->expr[i] = NULL;
}
g_free (v);
}
}
/**
* validation_eval:
*
* Either pass @expr or @val.
* The parameters will be validated against the
* validation set in the GnmStyle if applicable.
**/
ValidationStatus
validation_eval (WorkbookControl *wbc, GnmStyle const *mstyle,
Sheet *sheet, GnmCellPos const *pos, gboolean *showed_dialog)
{
GnmValidation *v;
GnmCell *cell;
char *msg = NULL;
gboolean allocated_msg = FALSE;
ValidationStatus result;
v = mstyle_get_validation (mstyle);
if (v == NULL)
return VALIDATION_STATUS_VALID;
if (v->style == VALIDATION_TYPE_ANY)
return VALIDATION_STATUS_VALID;
cell = sheet_cell_get (sheet, pos->col, pos->row);
if (cell != NULL)
dependent_eval (CELL_TO_DEP (cell));
if (cell_is_empty (cell)) {
if (v->allow_blank)
return VALIDATION_STATUS_VALID;
msg = g_strdup_printf (_("Cell %s is not permitted to be blank"),
cell_name (cell));
} else {
GnmExpr const *val_expr = NULL, *expr = NULL;
GnmValue *val = cell->value;
switch (v->type) {
case VALIDATION_TYPE_ANY :
return VALIDATION_STATUS_VALID;
case VALIDATION_TYPE_AS_INT :
case VALIDATION_TYPE_AS_NUMBER :
case VALIDATION_TYPE_AS_DATE : /* What the hell does this do */
case VALIDATION_TYPE_AS_TIME : { /* What the hell does this do */
GnmValue *res = NULL;
/* we know it is not empty */
if (val->type == VALUE_ERROR) {
msg = g_strdup_printf (_("'%s' is an error"),
value_peek_string (val));
break;
} else if (val->type == VALUE_STRING) {
char const *s = value_peek_string (val);
res = format_match_number (s, NULL,
workbook_date_conv (sheet->workbook));
if (res == NULL) {
char const *fmt;
/* FIXME what else is needed */
if (v->type == VALIDATION_TYPE_AS_DATE) {
fmt = N_("'%s' is not a valid date");
} else if (v->type == VALIDATION_TYPE_AS_TIME) {
fmt = N_("'%s' is not a valid time");
} else
fmt = N_("'%s' is not a number");
msg = g_strdup_printf (_(fmt), s);
break;
}
} else
res = value_dup (val);
if (v->type == VALIDATION_TYPE_AS_INT &&
res != NULL && res->type == VALUE_FLOAT) {
gnm_float f = value_get_as_float (res);
gboolean isint = gnumabs (f - gnumeric_fake_round (f)) < 1e-10;
if (!isint) {
char const *valstr = value_peek_string (val);
msg = g_strdup_printf (_("'%s' is not an integer"), valstr);
break;
}
}
val_expr = gnm_expr_new_constant (res);
break;
}
case VALIDATION_TYPE_IN_LIST :
#ifdef GOG_WARN_TODO
#warning TODO
#endif
return VALIDATION_STATUS_VALID;
case VALIDATION_TYPE_TEXT_LENGTH :
/* XL appears to use a very basic value -> string mapping that
* ignores formatting.
* eg len (12/13/01) == len (37238) = 5
* This seems wrong for
*/
val_expr = gnm_expr_new_constant (
value_new_int (strlen (value_peek_string (val))));
break;
case VALIDATION_TYPE_CUSTOM :
expr = v->expr[0];
if (expr == NULL)
return VALIDATION_STATUS_VALID;
gnm_expr_ref (expr);
break;
}
if (msg == NULL && expr == NULL) {
GnmExprOp op;
g_return_val_if_fail (val_expr != NULL, VALIDATION_STATUS_VALID);
switch (v->op) {
case VALIDATION_OP_EQUAL : op = GNM_EXPR_OP_EQUAL; break;
case VALIDATION_OP_NOT_EQUAL : op = GNM_EXPR_OP_NOT_EQUAL; break;
case VALIDATION_OP_GT : op = GNM_EXPR_OP_GT; break;
case VALIDATION_OP_NOT_BETWEEN :
case VALIDATION_OP_LT : op = GNM_EXPR_OP_LT; break;
case VALIDATION_OP_BETWEEN :
case VALIDATION_OP_GTE : op = GNM_EXPR_OP_GTE; break;
case VALIDATION_OP_LTE : op = GNM_EXPR_OP_LTE; break;
default :
g_warning ("Invalid validation operator %d", v->op);
return VALIDATION_STATUS_VALID;
}
if (v->expr [0] == NULL)
return VALIDATION_STATUS_VALID;
gnm_expr_ref (v->expr[0]);
expr = gnm_expr_new_binary (val_expr, op, v->expr[0]);
}
if (expr != NULL) {
GnmParsePos pp;
GnmEvalPos ep;
char *expr_str;
GnmValue *val;
gboolean dummy, valid;
eval_pos_init_cell (&ep, cell);
val = gnm_expr_eval (expr, &ep, GNM_EXPR_EVAL_SCALAR_NON_EMPTY);
valid = value_get_as_bool (val, &dummy);
value_release (val);
if (valid && v->op != VALIDATION_OP_BETWEEN) {
gnm_expr_unref (expr);
return VALIDATION_STATUS_VALID;
}
if ((v->op == VALIDATION_OP_BETWEEN && valid) ||
v->op == VALIDATION_OP_NOT_BETWEEN) {
g_return_val_if_fail (v->expr[1] != NULL, VALIDATION_STATUS_VALID);
gnm_expr_ref (val_expr);
gnm_expr_ref (v->expr[1]);
gnm_expr_unref (expr);
expr = gnm_expr_new_binary (val_expr,
(v->op == VALIDATION_OP_BETWEEN) ? GNM_EXPR_OP_LTE : GNM_EXPR_OP_GT,
v->expr[1]);
val = gnm_expr_eval (expr, &ep, GNM_EXPR_EVAL_SCALAR_NON_EMPTY);
valid = value_get_as_bool (val, &dummy);
value_release (val);
if (valid) {
gnm_expr_unref (expr);
return VALIDATION_STATUS_VALID;
}
}
expr_str = gnm_expr_as_string (expr,
parse_pos_init_evalpos (&pp, &ep),
gnm_expr_conventions_default);
msg = g_strdup_printf (_("%s is not true."), expr_str);
g_free (expr_str);
gnm_expr_unref (expr);
}
}
if (v->msg != NULL && v->msg->str[0] != '\0') {
if (msg != NULL)
g_free (msg);
msg = v->msg->str;
} else {
if (msg != NULL)
allocated_msg = TRUE;
else
msg = _("That value is invalid.\n"
"Restrictions have been placed on this cell's contents.");
}
if (showed_dialog != NULL)
*showed_dialog = TRUE;
result = wb_control_validation_msg (wbc, v->style,
(v->title != NULL && v->title->str[0] != '\0')
? v->title->str
: _("Gnumeric: Validation"),
msg);
if (allocated_msg)
g_free (msg);
return result;
}