/** * parse_criteria: * @crit_val: #GnmValue * @date_conv: #GODateConventions * * Returns: (transfer full): GnmCriteria which caller must free. * * ">=value" * "<=value" * "<>value" * "<value" * ">value" * "=value" * "pattern" **/ GnmCriteria * parse_criteria (GnmValue const *crit_val, GODateConventions const *date_conv, gboolean anchor_end) { int len; char const *criteria; GnmCriteria *res = g_new0 (GnmCriteria, 1); GnmValue *empty; res->iter_flags = CELL_ITER_IGNORE_BLANK; res->date_conv = date_conv; if (VALUE_IS_NUMBER (crit_val)) { res->fun = criteria_test_equal; res->x = value_dup (crit_val); return res; } criteria = value_peek_string (crit_val); if (strncmp (criteria, "<=", 2) == 0) { res->fun = criteria_test_less_or_equal; len = 2; } else if (strncmp (criteria, ">=", 2) == 0) { res->fun = criteria_test_greater_or_equal; len = 2; } else if (strncmp (criteria, "<>", 2) == 0) { /* "<>" by itself is special: */ res->fun = (criteria[2] == 0) ? criteria_test_nonempty : criteria_test_unequal; len = 2; } else if (*criteria == '<') { res->fun = criteria_test_less; len = 1; } else if (*criteria == '=') { /* "=" by itself is special: */ res->fun = (criteria[1] == 0) ? criteria_test_empty : criteria_test_equal; len = 1; } else if (*criteria == '>') { res->fun = criteria_test_greater; len = 1; } else { res->fun = criteria_test_match; res->has_rx = (gnm_regcomp_XL (&res->rx, criteria, GO_REG_ICASE, TRUE, anchor_end) == GO_REG_OK); len = 0; } res->x = format_match_number (criteria + len, NULL, date_conv); if (res->x == NULL) res->x = value_new_string (criteria + len); else if (len == 0 && VALUE_IS_NUMBER (res->x)) res->fun = criteria_test_equal; empty = value_new_empty (); if (res->fun (empty, res)) res->iter_flags &= ~CELL_ITER_IGNORE_BLANK; value_release (empty); res->ref_count = 1; return res; }
static CritType criteria_inspect_values (GnmValue const *x, gnm_float *xr, gnm_float *yr, GnmCriteria *crit, gboolean coerce_to_float) { GnmValue const *y = crit->x; if (x == NULL || y == NULL) return CRIT_NULL; switch (y->v_any.type) { case VALUE_BOOLEAN: /* If we're searching for a bool -- even one that is from a string search value -- we match only bools. */ if (!VALUE_IS_BOOLEAN (x)) return CRIT_WRONGTYPE; *xr = value_get_as_float (x); *yr = value_get_as_float (y); return CRIT_FLOAT; case VALUE_EMPTY: return CRIT_WRONGTYPE; case VALUE_STRING: if (!VALUE_IS_STRING (x)) return CRIT_WRONGTYPE; return CRIT_STRING; default: g_warning ("This should not happen. Please report."); return CRIT_WRONGTYPE; case VALUE_FLOAT: { GnmValue *vx; *yr = value_get_as_float (y); if (VALUE_IS_BOOLEAN (x) || VALUE_IS_ERROR (x)) return CRIT_WRONGTYPE; else if (VALUE_IS_FLOAT (x)) { *xr = value_get_as_float (x); return CRIT_FLOAT; } if (!coerce_to_float) return CRIT_WRONGTYPE; vx = format_match (value_peek_string (x), NULL, crit->date_conv); if (VALUE_IS_EMPTY (vx) || VALUE_IS_BOOLEAN (y) != VALUE_IS_BOOLEAN (vx)) { value_release (vx); return CRIT_WRONGTYPE; } *xr = value_get_as_float (vx); value_release (vx); return CRIT_FLOAT; } } }
static gboolean criteria_test_unequal (GnmValue const *x, GnmCriteria *crit) { gnm_float xf, yf; switch (criteria_inspect_values (x, &xf, &yf, crit, FALSE)) { default: g_assert_not_reached (); case CRIT_NULL: case CRIT_WRONGTYPE: return TRUE; case CRIT_FLOAT: return xf != yf; case CRIT_STRING: /* FIXME: _ascii_??? */ return g_ascii_strcasecmp (value_peek_string (x), value_peek_string (crit->x)) != 0; } }
static void cb_index_cell (G_GNUC_UNUSED gpointer ignore, GnmCell const *cell, IndexerState *state) { if (cell->value != NULL && VALUE_IS_STRING (cell->value)) { char const *str = value_peek_string (cell->value); if (str != NULL && *str) gsf_xml_out_simple_element (state->output, "data", str); } }
static gboolean criteria_test_greater_or_equal (GnmValue const *x, GnmCriteria *crit) { gnm_float xf, yf; GnmValue const *y = crit->x; switch (criteria_inspect_values (x, &xf, &yf, crit, FALSE)) { default: g_assert_not_reached (); case CRIT_NULL: case CRIT_WRONGTYPE: return FALSE; case CRIT_STRING: return go_utf8_collate_casefold (value_peek_string (x), value_peek_string (y)) >= 0; case CRIT_FLOAT: return xf >= yf; } }
/* * Finds a column index of a field. */ int find_column_of_field (GnmEvalPos const *ep, GnmValue const *database, GnmValue const *field) { Sheet *sheet; GnmCell *cell; gchar *field_name; int begin_col, end_col, row, n, column; int offset; // I'm not certain we should demand this, but the code clearly wants // it. if (!VALUE_IS_CELLRANGE (database)) return -1; offset = database->v_range.cell.a.col; if (VALUE_IS_FLOAT (field)) return value_get_as_int (field) + offset - 1; if (!VALUE_IS_STRING (field)) return -1; sheet = eval_sheet (database->v_range.cell.a.sheet, ep->sheet); field_name = value_get_as_string (field); column = -1; /* find the column that is labeled after `field_name' */ begin_col = database->v_range.cell.a.col; end_col = database->v_range.cell.b.col; row = database->v_range.cell.a.row; for (n = begin_col; n <= end_col; n++) { char const *txt; gboolean match; cell = sheet_cell_get (sheet, n, row); if (cell == NULL) continue; gnm_cell_eval (cell); txt = cell->value ? value_peek_string (cell->value) : ""; match = (g_ascii_strcasecmp (field_name, txt) == 0); if (match) { column = n; break; } } g_free (field_name); return column; }
/** * stf_export_cell: * @stfe: an export options struct * @cell: the cell to write to the file * * Return value: return TRUE on success, FALSE otherwise. **/ static gboolean stf_export_cell (GnmStfExport *stfe, GnmCell *cell) { char const *text = NULL; char *tmp = NULL; gboolean ok; g_return_val_if_fail (stfe != NULL, FALSE); if (cell) { switch (stfe->format) { case GNM_STF_FORMAT_PRESERVE: text = tmp = gnm_cell_get_rendered_text (cell); break; default: case GNM_STF_FORMAT_AUTO: if (cell->value) { GODateConventions const *date_conv = workbook_date_conv (cell->base.sheet->workbook); GOFormat const *format = gnm_cell_get_format (cell); text = tmp = try_auto_date (cell->value, format, date_conv); if (!text) text = tmp = try_auto_float (cell->value, format, date_conv); if (!text) text = value_peek_string (cell->value); } break; case GNM_STF_FORMAT_RAW: if (cell->value) text = value_peek_string (cell->value); break; } } ok = gsf_output_csv_write_field (GSF_OUTPUT_CSV (stfe), text ? text : "", -1); g_free (tmp); return ok; }
static gboolean criteria_test_match (GnmValue const *x, GnmCriteria *crit) { if (!crit->has_rx) return FALSE; // Only strings are matched if (!VALUE_IS_STRING (x)) return FALSE; return go_regexec (&crit->rx, value_peek_string (x), 0, NULL, 0) == GO_REG_OK; }
/* * write_row: * * @output: the stream * @sheet: the gnumeric sheet * @row: the row number * */ static void write_row (GsfOutput *output, Sheet *sheet, gint row, GnmRange *range) { char const *text = NULL; char *formatted_string = NULL; GnmCell *cell; GnmStyle const *style; GODateConventions const *date_conv = sheet->workbook ? workbook_date_conv (sheet->workbook) : NULL; gint col; for (col = range->start.col; col <= range->end.col; col++) { GnmRange const *merge_range; GnmCellPos pos; pos.col = col; pos.row = row; merge_range = gnm_sheet_merge_contains_pos (sheet, &pos); if (merge_range != NULL) { /* If cell is inside a merged region, we use the corner cell of the merged region: */ cell = sheet_cell_get (sheet, merge_range->start.col, merge_range->start.row); } else { cell = sheet_cell_get (sheet, col, row); } if (cell != NULL && cell->value) { text = value_peek_string (cell->value); pwcsv_print_encoded (output, text); style = sheet_style_get (sheet, col, row); GOFormat const *format = gnm_style_get_format (style); // set col_width to 16-something. This works around gnumeric quirk // where, in wider cells, it formats 9,3 as 9,300000000000001 formatted_string = format_value (format, cell->value, 16, date_conv); pwcsv_print_encoded (output, formatted_string); html_write_cell_content (output, cell, style, formatted_string); g_free (formatted_string); /* Without this, we're accumulating lots of heap memory on big spreadsheets. */ gnm_cell_unrender(cell); } else { gsf_output_puts (output, ",,,"); } } gsf_output_puts (output, "\n"); }
static void render_cell (GString *target, HFRenderInfo *info, char const *args) { gboolean use_repeating = FALSE; if (args && ((use_repeating = g_str_has_prefix (args, "rep|")))) args += 4; if (info->sheet) { GnmRangeRef ref; GnmValue const *val; char const *tmp; GnmParsePos ppos; parse_pos_init (&ppos, info->sheet->workbook, (Sheet *)info->sheet, 0, 0); tmp = rangeref_parse (&ref, args, &ppos, sheet_get_conventions (info->sheet)); if (tmp == NULL || tmp == args) { gnm_cellref_init (&ref.a, (Sheet *)(info->sheet), 0, 0, FALSE); } if (ref.a.row_relative) ref.a.row += (use_repeating ? info->top_repeating.row : info->page_area.start.row); if (ref.a.col_relative) ref.a.col += (use_repeating ? info->top_repeating.col : info->page_area.start.col); val = sheet_cell_get_value (ref.a.sheet ? ref.a.sheet : (Sheet *)(info->sheet), ref.a.col, ref.a.row); if (val != NULL) { char const *value; value = value_peek_string (val); g_string_append (target, value); } } else { if (use_repeating) g_string_append (target, "["); g_string_append (target, args); if (use_repeating) g_string_append (target, "]"); } }
char * gnm_scenario_get_range_str (const GnmScenario *sc) { GString *str; GSList *l; g_return_val_if_fail (GNM_IS_SCENARIO (sc), NULL); str = g_string_new (NULL); for (l = sc->items; l; l = l->next) { GnmScenarioItem const *sci = l->data; GnmValue const *vrange; if (sci->value || !gnm_scenario_item_valid (sci, NULL)) continue; if (str->len) g_string_append_c (str, ','); vrange = gnm_expr_top_get_constant (sci->dep.texpr); g_string_append (str, value_peek_string (vrange)); } return g_string_free (str, FALSE); }
gnm_float datetime_value_to_serial_raw (GnmValue const *v, GODateConventions const *conv) { gnm_float serial; if (VALUE_IS_NUMBER (v)) serial = value_get_as_float (v); else { char const *str = value_peek_string (v); GnmValue *conversion = format_match_number (str, go_format_default_date (), conv); if (conversion) { serial = value_get_as_float (conversion); value_release (conversion); } else serial = G_MAXINT; } if (serial < 0 && !gnm_datetime_allow_negative ()) serial = G_MAXINT; return serial; }
/** * find_rows_that_match: * @sheet: #Sheet * @first_col: first column. * @first_row: first row. * @last_col: last column. * @last_row: laset row. * @criterias: (element-type GnmDBCriteria): the criteria to use. * @unique_only: * * Finds the rows from the given database that match the criteria. * Returns: (element-type int) (transfer full): the list of matching rows. **/ GSList * find_rows_that_match (Sheet *sheet, int first_col, int first_row, int last_col, int last_row, GSList *criterias, gboolean unique_only) { GSList *rows = NULL; GSList const *crit_ptr, *cond_ptr; int row; gboolean add_flag; char const *t1, *t2; GnmCell *test_cell; GnmValue const *empty = value_new_empty (); for (row = first_row; row <= last_row; row++) { add_flag = TRUE; for (crit_ptr = criterias; crit_ptr; crit_ptr = crit_ptr->next) { GnmDBCriteria const *crit = crit_ptr->data; add_flag = TRUE; for (cond_ptr = crit->conditions; cond_ptr != NULL ; cond_ptr = cond_ptr->next) { GnmCriteria *cond = cond_ptr->data; test_cell = sheet_cell_get (sheet, cond->column, row); if (test_cell != NULL) gnm_cell_eval (test_cell); if (!cond->fun (test_cell ? test_cell->value : empty, cond)) { add_flag = FALSE; break; } } if (add_flag) break; } if (add_flag) { gint *p; if (unique_only) { GSList *c; GnmCell *cell; gint i, trow; for (c = rows; c != NULL; c = c->next) { trow = *((gint *) c->data); for (i = first_col; i <= last_col; i++) { test_cell = sheet_cell_get (sheet, i, trow); cell = sheet_cell_get (sheet, i, row); /* FIXME: this is probably not right, but crashing is more wrong. */ if (test_cell == NULL || cell == NULL) continue; t1 = cell->value ? value_peek_string (cell->value) : ""; t2 = test_cell->value ? value_peek_string (test_cell->value) : ""; if (strcmp (t1, t2) != 0) goto row_ok; } goto filter_row; row_ok: ; } } p = g_new (gint, 1); *p = row; rows = g_slist_prepend (rows, (gpointer) p); filter_row: ; } } return g_slist_reverse (rows); }
static GnmValue * gnumeric_convert (GnmFuncEvalInfo *ei, GnmValue const * const *argv) { /* Weight and mass constants */ #define one_g_to_sg 0.00006852205001 #define one_g_to_lbm 0.002204622915 #define one_g_to_u 6.02217e+23 #define one_g_to_ozm 0.035273972 /* Distance constants */ #define one_m_to_mi (one_m_to_yd / 1760) #define one_m_to_Nmi (1 / GNM_const (1852.0)) #define one_m_to_in (10000 / GNM_const (254.0)) #define one_m_to_ft (one_m_to_in / 12) #define one_m_to_yd (one_m_to_ft / 3) #define one_m_to_ang GNM_const (1e10) #define one_m_to_pica 236.2204724409449 #define one_m_to_Pica one_m_to_pica * 12 /* Time constants */ #define one_yr_to_day 365.25 #define one_yr_to_hr (24 * one_yr_to_day) #define one_yr_to_mn (60 * one_yr_to_hr) #define one_yr_to_sec (60 * one_yr_to_mn) /* Pressure constants */ #define one_Pa_to_atm 0.9869233e-5 #define one_Pa_to_mmHg 0.00750061708 /* Force constants */ #define one_N_to_dyn 100000 #define one_N_to_lbf 0.224808924 /* Power constants */ #define one_HP_to_W 745.701 /* Energy constants */ #define one_J_to_e 9999995.193 #define one_J_to_c 0.239006249 #define one_J_to_cal 0.238846191 #define one_J_to_eV 6.2146e+18 #define one_J_to_HPh (GNM_const (1.0) / (3600 * one_HP_to_W)) #define one_J_to_Wh (GNM_const (1.0) / 3600) #define one_J_to_flb 23.73042222 #define one_J_to_BTU 0.000947815 /* Magnetism constants */ #define one_T_to_ga 10000 /* Temperature constants */ const gnm_float C_K_offset = GNM_const (273.15); /* Liquid measure constants */ #define one_tsp_to_tbs (GNM_const (1.0) / 3) #define one_tsp_to_oz (GNM_const (1.0) / 6) #define one_tsp_to_cup (GNM_const (1.0) / 48) #define one_tsp_to_pt (GNM_const (1.0) / 96) #define one_tsp_to_qt (GNM_const (1.0) / 192) #define one_tsp_to_gal (GNM_const (1.0) / 768) #define one_tsp_to_l 0.004929994 /* Prefixes */ #define yotta GNM_const (1e+24) #define zetta GNM_const (1e+21) #define exa GNM_const (1e+18) #define peta GNM_const (1e+15) #define tera GNM_const (1e+12) #define giga GNM_const (1e+09) #define mega GNM_const (1e+06) #define kilo GNM_const (1e+03) #define hecto GNM_const (1e+02) #define deka GNM_const (1e+01) #define deci GNM_const (1e-01) #define centi GNM_const (1e-02) #define milli GNM_const (1e-03) #define micro GNM_const (1e-06) #define nano GNM_const (1e-09) #define pico GNM_const (1e-12) #define femto GNM_const (1e-15) #define atto GNM_const (1e-18) #define zepto GNM_const (1e-21) #define yocto GNM_const (1e-24) static const eng_convert_unit_t weight_units[] = { { "g", 1.0 }, { "sg", one_g_to_sg }, { "lbm", one_g_to_lbm }, { "u", one_g_to_u }, { "ozm", one_g_to_ozm }, { NULL, 0.0 } }; static const eng_convert_unit_t distance_units[] = { { "m", 1.0 }, { "mi", one_m_to_mi }, { "Nmi", one_m_to_Nmi }, { "in", one_m_to_in }, { "ft", one_m_to_ft }, { "yd", one_m_to_yd }, { "ang", one_m_to_ang }, { "Pica", one_m_to_Pica }, { "picapt", one_m_to_Pica }, { "pica", one_m_to_pica }, { NULL, 0.0 } }; static const eng_convert_unit_t time_units[] = { { "yr", 1.0 }, { "day", one_yr_to_day }, { "hr", one_yr_to_hr }, { "mn", one_yr_to_mn }, { "sec", one_yr_to_sec }, { NULL, 0.0 } }; static const eng_convert_unit_t pressure_units[] = { { "Pa", 1.0 }, { "atm", one_Pa_to_atm }, { "mmHg", one_Pa_to_mmHg }, { NULL, 0.0 } }; static const eng_convert_unit_t force_units[] = { { "N", 1.0 }, { "dyn", one_N_to_dyn }, { "lbf", one_N_to_lbf }, { NULL, 0.0 } }; static const eng_convert_unit_t energy_units[] = { { "J", 1.0 }, { "e", one_J_to_e }, { "c", one_J_to_c }, { "cal", one_J_to_cal }, { "eV", one_J_to_eV }, { "HPh", one_J_to_HPh }, { "Wh", one_J_to_Wh }, { "flb", one_J_to_flb }, { "BTU", one_J_to_BTU }, { NULL, 0.0 } }; static const eng_convert_unit_t power_units[] = { { "HP", 1.0 }, { "W", one_HP_to_W }, { NULL, 0.0 } }; static const eng_convert_unit_t magnetism_units[] = { { "T", 1.0 }, { "ga", one_T_to_ga }, { NULL, 0.0 } }; static const eng_convert_unit_t liquid_units[] = { { "tsp", 1.0 }, { "tbs", one_tsp_to_tbs }, { "oz", one_tsp_to_oz }, { "cup", one_tsp_to_cup }, { "pt", one_tsp_to_pt }, { "qt", one_tsp_to_qt }, { "gal", one_tsp_to_gal }, { "l", one_tsp_to_l }, { NULL, 0.0 } }; static const eng_convert_unit_t prefixes[] = { { "Y", yotta }, { "Z", zetta }, { "E", exa }, { "P", peta }, { "T", tera }, { "G", giga }, { "M", mega }, { "k", kilo }, { "h", hecto }, { "e", deka }, { "d", deci }, { "c", centi }, { "m", milli }, { "u", micro }, { "n", nano }, { "p", pico }, { "f", femto }, { "a", atto }, { "z", zepto }, { "y", yocto }, { NULL,0.0 } }; gnm_float n; char const *from_unit, *to_unit; GnmValue *v; n = value_get_as_float (argv[0]); from_unit = value_peek_string (argv[1]); to_unit = value_peek_string (argv[2]); if (strcmp (from_unit, "C") == 0 && strcmp (to_unit, "F") == 0) return value_new_float (n * 9 / 5 + 32); else if (strcmp (from_unit, "F") == 0 && strcmp (to_unit, "C") == 0) return value_new_float ((n - 32) * 5 / 9); else if (strcmp (from_unit, "F") == 0 && strcmp (to_unit, "F") == 0) return value_new_float (n); else if (strcmp (from_unit, "F") == 0 && strcmp (to_unit, "K") == 0) return value_new_float ((n - 32) * 5 / 9 + C_K_offset); else if (strcmp (from_unit, "K") == 0 && strcmp (to_unit, "F") == 0) return value_new_float ((n - C_K_offset) * 9 / 5 + 32); else if (strcmp (from_unit, "C") == 0 && strcmp (to_unit, "K") == 0) return value_new_float (n + C_K_offset); else if (strcmp (from_unit, "K") == 0 && strcmp (to_unit, "C") == 0) return value_new_float (n - C_K_offset); if (convert (weight_units, prefixes, from_unit, to_unit, n, &v, ei->pos)) return v; if (convert (distance_units, prefixes, from_unit, to_unit, n, &v, ei->pos)) return v; if (convert (time_units, NULL, from_unit, to_unit, n, &v, ei->pos)) return v; if (convert (pressure_units, prefixes, from_unit, to_unit, n, &v, ei->pos)) return v; if (convert (force_units, prefixes, from_unit, to_unit, n, &v, ei->pos)) return v; if (convert (energy_units, prefixes, from_unit, to_unit, n, &v, ei->pos)) return v; if (convert (power_units, prefixes, from_unit, to_unit, n, &v, ei->pos)) return v; if (convert (magnetism_units, prefixes, from_unit, to_unit, n, &v, ei->pos)) return v; if (convert (liquid_units, prefixes, from_unit, to_unit, n, &v, ei->pos)) return v; if (convert (magnetism_units, prefixes, from_unit, to_unit, n, &v, ei->pos)) return v; return value_new_error_NA (ei->pos); }
/** * gnm_validation_eval: * @wbc: * @mstyle: * @sheet: * * validation set in the GnmStyle if applicable. **/ ValidationStatus gnm_validation_eval (WorkbookControl *wbc, GnmStyle const *mstyle, Sheet *sheet, GnmCellPos const *pos, gboolean *showed_dialog) { GnmValidation const *v; GnmCell *cell; GnmValue *val; gnm_float x; int nok, i; GnmEvalPos ep; if (showed_dialog) *showed_dialog = FALSE; v = gnm_style_get_validation (mstyle); if (v == NULL) return GNM_VALIDATION_STATUS_VALID; if (v->type == GNM_VALIDATION_TYPE_ANY) return GNM_VALIDATION_STATUS_VALID; cell = sheet_cell_get (sheet, pos->col, pos->row); if (cell != NULL) gnm_cell_eval (cell); if (gnm_cell_is_empty (cell)) { if (v->allow_blank) return GNM_VALIDATION_STATUS_VALID; BARF (g_strdup_printf (_("Cell %s is not permitted to be blank"), cell_name (cell))); } val = cell->value; switch (val->type) { case VALUE_ERROR: if (typeinfo[v->type].errors_not_allowed) BARF (g_strdup_printf (_("Cell %s is not permitted to contain error values"), cell_name (cell))); break; case VALUE_BOOLEAN: if (typeinfo[v->type].bool_always_ok) return GNM_VALIDATION_STATUS_VALID; break; case VALUE_STRING: if (typeinfo[v->type].strings_not_allowed) BARF (g_strdup_printf (_("Cell %s is not permitted to contain strings"), cell_name (cell))); break; default: break; } eval_pos_init_cell (&ep, cell); switch (v->type) { case GNM_VALIDATION_TYPE_AS_INT: x = value_get_as_float (val); if (gnm_fake_floor (x) == gnm_fake_ceil (x)) break; else BARF (g_strdup_printf (_("'%s' is not an integer"), value_peek_string (val))); case GNM_VALIDATION_TYPE_AS_NUMBER: x = value_get_as_float (val); break; case GNM_VALIDATION_TYPE_AS_DATE: /* What the hell does this do? */ x = value_get_as_float (val); if (x < 0) BARF (g_strdup_printf (_("'%s' is not a valid date"), value_peek_string (val))); break; case GNM_VALIDATION_TYPE_AS_TIME: /* What the hell does this do? */ x = value_get_as_float (val); break; case GNM_VALIDATION_TYPE_IN_LIST: { GnmExprTop const *texpr = v->deps[0].texpr; if (texpr) { GnmValue *list = gnm_expr_top_eval (texpr, &ep, GNM_EXPR_EVAL_PERMIT_NON_SCALAR | GNM_EXPR_EVAL_PERMIT_EMPTY); GnmValue *res = value_area_foreach (list, &ep, CELL_ITER_IGNORE_BLANK, (GnmValueIterFunc) cb_validate_custom, val); value_release (list); if (res == NULL) { GnmParsePos pp; char *expr_str = gnm_expr_top_as_string (texpr, parse_pos_init_evalpos (&pp, &ep), ep.sheet->convs); char *msg = g_strdup_printf (_("%s does not contain the new value."), expr_str); g_free (expr_str); BARF (msg); } } return GNM_VALIDATION_STATUS_VALID; } case GNM_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 */ x = g_utf8_strlen (value_peek_string (val), -1); break; case GNM_VALIDATION_TYPE_CUSTOM: { gboolean valid; GnmExprTop const *texpr = v->deps[0].texpr; if (!texpr) return GNM_VALIDATION_STATUS_VALID; val = gnm_expr_top_eval (texpr, &ep, GNM_EXPR_EVAL_SCALAR_NON_EMPTY); valid = value_get_as_bool (val, NULL); value_release (val); if (valid) return GNM_VALIDATION_STATUS_VALID; else { GnmParsePos pp; char *expr_str = gnm_expr_top_as_string (texpr, parse_pos_init_evalpos (&pp, &ep), ep.sheet->convs); char *msg = g_strdup_printf (_("%s is not true."), expr_str); g_free (expr_str); BARF (msg); } } default: g_assert_not_reached (); return GNM_VALIDATION_STATUS_VALID; } if (v->op == GNM_VALIDATION_OP_NONE) return GNM_VALIDATION_STATUS_VALID; nok = 0; for (i = 0; i < opinfo[v->op].nops; i++) { GnmExprTop const *texpr_i = v->deps[i].texpr; GnmExprTop const *texpr; GnmValue *cres; if (!texpr_i) { nok++; continue; } texpr = gnm_expr_top_new (gnm_expr_new_binary (gnm_expr_new_constant (value_new_float (x)), opinfo[v->op].ops[i], gnm_expr_copy (texpr_i->expr))); cres = gnm_expr_top_eval (texpr, &ep, GNM_EXPR_EVAL_SCALAR_NON_EMPTY); if (value_get_as_bool (cres, NULL)) nok++; value_release (cres); gnm_expr_top_unref (texpr); } if (nok < opinfo[v->op].ntrue) BARF (g_strdup_printf (_("%s is out of permitted range"), value_peek_string (val))); return GNM_VALIDATION_STATUS_VALID; }
/** * cb_dialog_apply_clicked: * @button: * @state: * * Close (destroy) the dialog **/ static void cb_dialog_apply_clicked (G_GNUC_UNUSED GtkWidget *button, GoalSeekState *state) { char *status_str; GoalSeekStatus status; GnmValue *target; GnmRangeRef const *r; GOFormat *format; if (state->warning_dialog != NULL) gtk_widget_destroy (state->warning_dialog); /* set up source */ target = gnm_expr_entry_parse_as_value (state->set_cell_entry, state->sheet); if (target == NULL) { go_gtk_notice_nonmodal_dialog (GTK_WINDOW(state->dialog), &(state->warning_dialog), GTK_MESSAGE_ERROR, _("You should introduce a valid cell " "name in 'Set Cell:'!")); gnm_expr_entry_grab_focus (state->set_cell_entry, TRUE); return; } r = &target->v_range.cell; state->set_cell = sheet_cell_get (r->a.sheet, r->a.col, r->a.row); value_release (target); if (state->set_cell == NULL || !gnm_cell_has_expr (state->set_cell)) { go_gtk_notice_nonmodal_dialog (GTK_WINDOW(state->dialog), &(state->warning_dialog), GTK_MESSAGE_ERROR, _("The cell named in 'Set Cell:' " "must contain a formula!")); gnm_expr_entry_grab_focus (state->set_cell_entry, TRUE); return; } /* set up source */ target = gnm_expr_entry_parse_as_value (state->change_cell_entry, state->sheet); if (target == NULL) { go_gtk_notice_nonmodal_dialog (GTK_WINDOW(state->dialog), &(state->warning_dialog), GTK_MESSAGE_ERROR, _("You should introduce a valid cell " "name in 'By Changing Cell:'!")); gnm_expr_entry_grab_focus (state->change_cell_entry, TRUE); return; } r = &target->v_range.cell; state->change_cell = sheet_cell_fetch (r->a.sheet, r->a.col, r->a.row); value_release (target); if (gnm_cell_has_expr (state->change_cell)) { go_gtk_notice_nonmodal_dialog (GTK_WINDOW(state->dialog), &(state->warning_dialog), GTK_MESSAGE_ERROR, _("The cell named in 'By changing cell' " "must not contain a formula.")); gnm_expr_entry_grab_focus (state->change_cell_entry, TRUE); return; } format = gnm_style_get_format (gnm_cell_get_style (state->set_cell)); if (entry_to_float_with_format (GTK_ENTRY(state->to_value_entry), &state->target_value, TRUE, format)){ go_gtk_notice_nonmodal_dialog (GTK_WINDOW(state->dialog), &(state->warning_dialog), GTK_MESSAGE_ERROR, _("The value given in 'To Value:' " "is not valid.")); focus_on_entry (GTK_ENTRY(state->to_value_entry)); return; } format = gnm_style_get_format (gnm_cell_get_style (state->change_cell)); if (entry_to_float_with_format (GTK_ENTRY(state->at_least_entry), &state->xmin, TRUE, format)) { state->xmin = -max_range_val; gtk_entry_set_text (GTK_ENTRY (state->at_least_entry), ""); } if (entry_to_float_with_format (GTK_ENTRY(state->at_most_entry), &state->xmax, TRUE, format)) { state->xmax = +max_range_val; gtk_entry_set_text (GTK_ENTRY (state->at_most_entry), ""); } if ((state->old_cell != NULL) && (state->old_value != NULL)) { sheet_cell_set_value (state->old_cell, state->old_value); workbook_recalc (state->wb); state->old_value = NULL; } state->old_cell = state->change_cell; state->old_value = value_dup (state->change_cell->value); status = gnumeric_goal_seek (state); switch (status) { case GOAL_SEEK_OK: { const char *actual_str; const char *solution_str; GOFormat *format = go_format_general (); GnmValue *error_value = value_new_float (state->target_value - value_get_as_float (state->set_cell->value)); char *target_str = format_value (format, error_value, NULL, -1, workbook_date_conv (state->wb)); gtk_label_set_text (GTK_LABEL (state->target_value_label), target_str); g_free (target_str); value_release (error_value); status_str = g_strdup_printf (_("Goal seeking with cell %s found a solution."), cell_name (state->set_cell)); gtk_label_set_text (GTK_LABEL (state->result_label), status_str); g_free (status_str); /* FIXME? Do a format? */ actual_str = state->set_cell->value ? value_peek_string (state->set_cell->value) : ""; gtk_label_set_text (GTK_LABEL (state->current_value_label), actual_str); solution_str = state->change_cell->value ? value_peek_string (state->change_cell->value) : ""; gtk_label_set_text (GTK_LABEL (state->solution_label), solution_str); break; } default: status_str = g_strdup_printf (_("Goal seeking with cell %s did not find a solution."), cell_name (state->set_cell)); gtk_label_set_text (GTK_LABEL (state->result_label), status_str); g_free (status_str); gtk_label_set_text (GTK_LABEL (state->current_value_label), ""); gtk_label_set_text (GTK_LABEL (state->solution_label), ""); gtk_label_set_text (GTK_LABEL (state->target_value_label), ""); break; } state->cancelled = FALSE; gtk_widget_show (state->result_table); return; }
/** * FIXME: In the long term this needs optimising. **/ static GnmValue * val_to_base (GnmFuncEvalInfo *ei, GnmValue const *value, GnmValue const *aplaces, int src_base, int dest_base, gnm_float min_value, gnm_float max_value, Val2BaseFlags flags) { int digit, min, max, places; gnm_float v; GString *buffer; GnmValue *vstring = NULL; g_return_val_if_fail (src_base > 1 && src_base <= 36, value_new_error_VALUE (ei->pos)); g_return_val_if_fail (dest_base > 1 && dest_base <= 36, value_new_error_VALUE (ei->pos)); /* func.c ought to take care of this. */ if (VALUE_IS_BOOLEAN (value)) return value_new_error_VALUE (ei->pos); if (aplaces && VALUE_IS_BOOLEAN (aplaces)) return value_new_error_VALUE (ei->pos); switch (value->type) { default: return value_new_error_NUM (ei->pos); case VALUE_STRING: if (flags & V2B_STRINGS_GENERAL) { vstring = format_match_number (value_peek_string (value), NULL, workbook_date_conv (ei->pos->sheet->workbook)); if (!vstring || !VALUE_IS_FLOAT (vstring)) { value_release (vstring); return value_new_error_VALUE (ei->pos); } } else { char const *str = value_peek_string (value); size_t len; gboolean hsuffix = FALSE; char *err; if ((flags & V2B_STRINGS_BLANK_ZERO) && *str == 0) str = "0"; /* This prevents leading spaces, signs, etc, and "". */ if (!g_ascii_isalnum (*str)) return value_new_error_NUM (ei->pos); len = strlen (str); /* We check length in bytes. Since we are going to require nothing but digits, that is fine. */ if ((flags & V2B_STRINGS_MAXLEN) && len > 10) return value_new_error_NUM (ei->pos); if (flags & V2B_STRINGS_0XH) { if (str[0] == '0' && (str[1] == 'x' || str[1] == 'X')) str += 2; else if (str[len - 1] == 'h' || str[len - 1] == 'H') hsuffix = TRUE; } v = g_ascii_strtoll (str, &err, src_base); if (err == str || err[hsuffix] != 0) return value_new_error_NUM (ei->pos); if (v < min_value || v > max_value) return value_new_error_NUM (ei->pos); break; } /* Fall through. */ case VALUE_FLOAT: { gnm_float val = gnm_fake_trunc (value_get_as_float (vstring ? vstring : value)); char buf[GNM_MANT_DIG + 10]; char *err; value_release (vstring); if (val < min_value || val > max_value) return value_new_error_NUM (ei->pos); g_ascii_formatd (buf, sizeof (buf) - 1, "%.0" GNM_FORMAT_f, val); v = g_ascii_strtoll (buf, &err, src_base); if (*err != 0) return value_new_error_NUM (ei->pos); break; } } if (src_base != 10) { gnm_float b10 = gnm_pow (src_base, 10); if (v >= b10 / 2) /* N's complement */ v = v - b10; } if (flags & V2B_NUMBER) return value_new_float (v); if (v < 0) { min = 1; max = 10; v += gnm_pow (dest_base, max); } else { if (v == 0) min = max = 1; else min = max = (int)(gnm_log (v + 0.5) / gnm_log (dest_base)) + 1; } if (aplaces) { gnm_float fplaces = value_get_as_float (aplaces); if (fplaces < min || fplaces > 10) return value_new_error_NUM (ei->pos); places = (int)fplaces; if (v >= 0 && places > max) max = places; } else places = 1; buffer = g_string_sized_new (max); g_string_set_size (buffer, max); for (digit = max - 1; digit >= 0; digit--) { int thisdigit = gnm_fmod (v + 0.5, dest_base); v = gnm_floor ((v + 0.5) / dest_base); buffer->str[digit] = thisdigit["0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"]; } return value_new_string_nocopy (g_string_free (buffer, FALSE)); }
/** * dialog_init: * @state: * * Create the dialog (guru). * **/ static gboolean dialog_init (SolverState *state) { GtkGrid *grid; GnmSolverParameters *param; GtkCellRenderer *renderer; GtkListStore *store; GtkTreeViewColumn *column; GSList *cl; GnmCell *target_cell; GnmValue const *input; int i; param = state->sheet->solver_parameters; state->gui = gnm_gtk_builder_load ("solver.ui", NULL, GO_CMD_CONTEXT (state->wbcg)); if (state->gui == NULL) return TRUE; state->dialog = go_gtk_builder_get_widget (state->gui, "Solver"); if (state->dialog == NULL) return TRUE; state->notebook = go_gtk_builder_get_widget (state->gui, "solver_notebook"); /* buttons */ state->solve_button = go_gtk_builder_get_widget (state->gui, "solvebutton"); g_signal_connect (G_OBJECT (state->solve_button), "clicked", G_CALLBACK (cb_dialog_solve_clicked), state); state->close_button = go_gtk_builder_get_widget (state->gui, "closebutton"); g_signal_connect (G_OBJECT (state->close_button), "clicked", G_CALLBACK (cb_dialog_close_clicked), state); state->help_button = go_gtk_builder_get_widget (state->gui, "helpbutton"); gnm_init_help_button (state->help_button, GNUMERIC_HELP_LINK_SOLVER); state->add_button = go_gtk_builder_get_widget (state->gui, "addbutton"); gtk_button_set_alignment (GTK_BUTTON (state->add_button), 0.5, .5); g_signal_connect_swapped (G_OBJECT (state->add_button), "clicked", G_CALLBACK (cb_dialog_add_clicked), state); state->change_button = go_gtk_builder_get_widget (state->gui, "changebutton"); g_signal_connect (G_OBJECT (state->change_button), "clicked", G_CALLBACK (cb_dialog_change_clicked), state); state->delete_button = go_gtk_builder_get_widget (state->gui, "deletebutton"); gtk_button_set_alignment (GTK_BUTTON (state->delete_button), 0.5, .5); g_signal_connect (G_OBJECT (state->delete_button), "clicked", G_CALLBACK (cb_dialog_delete_clicked), state); state->stop_button = go_gtk_builder_get_widget (state->gui, "stopbutton"); g_signal_connect_swapped (G_OBJECT (state->stop_button), "clicked", G_CALLBACK (cb_stop_solver), state); /* target_entry */ grid = GTK_GRID (go_gtk_builder_get_widget (state->gui, "parameter-grid")); state->target_entry = gnm_expr_entry_new (state->wbcg, TRUE); gnm_expr_entry_set_flags (state->target_entry, GNM_EE_SINGLE_RANGE | GNM_EE_FORCE_ABS_REF | GNM_EE_SHEET_OPTIONAL, GNM_EE_MASK); gtk_widget_set_hexpand (GTK_WIDGET (state->target_entry), TRUE); gtk_grid_attach (grid, GTK_WIDGET (state->target_entry), 1, 0, 2, 1); gnm_editable_enters (GTK_WINDOW (state->dialog), GTK_WIDGET (state->target_entry)); gtk_widget_show (GTK_WIDGET (state->target_entry)); g_signal_connect_after (G_OBJECT (state->target_entry), "changed", G_CALLBACK (dialog_set_main_button_sensitivity), state); /* change_cell_entry */ state->change_cell_entry = gnm_expr_entry_new (state->wbcg, TRUE); gnm_expr_entry_set_flags (state->change_cell_entry, GNM_EE_SINGLE_RANGE | GNM_EE_FORCE_ABS_REF | GNM_EE_SHEET_OPTIONAL, GNM_EE_MASK); gtk_widget_set_hexpand (GTK_WIDGET (state->change_cell_entry), TRUE); gtk_grid_attach (grid, GTK_WIDGET (state->change_cell_entry), 1, 2, 2, 1); gnm_editable_enters (GTK_WINDOW (state->dialog), GTK_WIDGET (state->change_cell_entry)); gtk_widget_show (GTK_WIDGET (state->change_cell_entry)); g_signal_connect_after (G_OBJECT (state->change_cell_entry), "changed", G_CALLBACK (dialog_set_main_button_sensitivity), state); /* Algorithm */ state->algorithm_combo = GTK_COMBO_BOX (go_gtk_builder_get_widget (state->gui, "algorithm_combo")); renderer = (GtkCellRenderer*) gtk_cell_renderer_text_new(); gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (state->algorithm_combo), renderer, TRUE); gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (state->algorithm_combo), renderer, "text", 0, NULL); fill_algorithm_combo (state, param->options.model_type); for (i = 0; model_type_group[i]; i++) { const char *bname = model_type_group[i]; GtkWidget *w = go_gtk_builder_get_widget(state->gui, bname); gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (w), param->options.model_type == (GnmSolverModelType)i); g_signal_connect (G_OBJECT (w), "clicked", G_CALLBACK (cb_dialog_model_type_clicked), state); } /* Options */ state->max_iter_entry = go_gtk_builder_get_widget (state->gui, "max_iter_entry"); gtk_spin_button_set_value (GTK_SPIN_BUTTON (state->max_iter_entry), param->options.max_iter); state->max_time_entry = go_gtk_builder_get_widget (state->gui, "max_time_entry"); gtk_spin_button_set_value (GTK_SPIN_BUTTON (state->max_time_entry), param->options.max_time_sec); state->gradient_order_entry = go_gtk_builder_get_widget (state->gui, "gradient_order_entry"); gtk_spin_button_set_value (GTK_SPIN_BUTTON (state->gradient_order_entry), param->options.gradient_order); /* lhs_entry */ grid = GTK_GRID (go_gtk_builder_get_widget (state->gui, "constraints-grid")); state->lhs.entry = gnm_expr_entry_new (state->wbcg, TRUE); gnm_expr_entry_set_flags (state->lhs.entry, GNM_EE_SINGLE_RANGE | GNM_EE_FORCE_ABS_REF | GNM_EE_SHEET_OPTIONAL, GNM_EE_MASK); gtk_widget_set_hexpand (GTK_WIDGET (state->lhs.entry), TRUE); gtk_grid_attach (grid, GTK_WIDGET (state->lhs.entry), 0, 4, 1, 1); state->lhs.label = go_gtk_builder_get_widget (state->gui, "lhs_label"); gtk_label_set_mnemonic_widget (GTK_LABEL (state->lhs.label), GTK_WIDGET (state->lhs.entry)); gtk_widget_show (GTK_WIDGET (state->lhs.entry)); g_signal_connect_after (G_OBJECT (state->lhs.entry), "changed", G_CALLBACK (dialog_set_sec_button_sensitivity), state); g_signal_connect_swapped ( gnm_expr_entry_get_entry (GNM_EXPR_ENTRY (state->lhs.entry)), "activate", G_CALLBACK (cb_dialog_add_clicked), state); /* rhs_entry */ state->rhs.entry = gnm_expr_entry_new (state->wbcg, TRUE); gnm_expr_entry_set_flags (state->rhs.entry, GNM_EE_SINGLE_RANGE | GNM_EE_FORCE_ABS_REF | GNM_EE_SHEET_OPTIONAL | GNM_EE_CONSTANT_ALLOWED, GNM_EE_MASK); gtk_widget_set_hexpand (GTK_WIDGET (state->rhs.entry), TRUE); gtk_grid_attach (grid, GTK_WIDGET (state->rhs.entry), 2, 4, 1, 1); gtk_widget_show (GTK_WIDGET (state->rhs.entry)); state->rhs.label = go_gtk_builder_get_widget (state->gui, "rhs_label"); gtk_label_set_mnemonic_widget ( GTK_LABEL (state->rhs.label), GTK_WIDGET (state->rhs.entry)); g_signal_connect_after (G_OBJECT (state->rhs.entry), "changed", G_CALLBACK (dialog_set_sec_button_sensitivity), state); g_signal_connect_swapped ( gnm_expr_entry_get_entry (GNM_EXPR_ENTRY (state->rhs.entry)), "activate", G_CALLBACK (cb_dialog_add_clicked), state); /* type_menu */ state->type_combo = GTK_COMBO_BOX (go_gtk_builder_get_widget (state->gui, "type_menu")); gtk_combo_box_set_active (state->type_combo, 0); g_signal_connect (state->type_combo, "changed", G_CALLBACK (dialog_set_sec_button_sensitivity), state); /* constraint_list */ state->constraint_list = GTK_TREE_VIEW (go_gtk_builder_get_widget (state->gui, "constraint_list")); state->constr = NULL; g_signal_connect (G_OBJECT (gtk_tree_view_get_selection (state->constraint_list)), "changed", G_CALLBACK (constraint_select_click), state); gtk_tree_view_set_reorderable (state->constraint_list, TRUE); store = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_POINTER); gtk_tree_view_set_model (state->constraint_list, GTK_TREE_MODEL(store)); renderer = gtk_cell_renderer_text_new (); column = gtk_tree_view_column_new_with_attributes ( _("Subject to the Constraints:"), renderer, "text", 0, NULL); gtk_tree_view_column_set_expand (column, TRUE); gtk_tree_view_append_column (state->constraint_list, column); { GtkWidget *w = GTK_WIDGET (state->constraint_list); int width, height, vsep; PangoLayout *layout = gtk_widget_create_pango_layout (w, "Mg19"); gtk_widget_style_get (w, "vertical_separator", &vsep, NULL); pango_layout_get_pixel_size (layout, &width, &height); gtk_widget_set_size_request (w, -1, (2 * height + vsep) * (4 + 1)); g_object_unref (layout); } /* Loading the old solver specs... from param */ for (cl = param->constraints; cl; cl = cl->next) { GnmSolverConstraint const *c = cl->data; GtkTreeIter iter; char *str; gtk_list_store_append (store, &iter); str = gnm_solver_constraint_as_str (c, state->sheet); gtk_list_store_set (store, &iter, 0, str, 1, c, -1); g_free (str); } g_object_unref (store); INIT_BOOL_ENTRY ("autoscale_button", options.automatic_scaling); INIT_BOOL_ENTRY ("non_neg_button", options.assume_non_negative); INIT_BOOL_ENTRY ("all_int_button", options.assume_discrete); INIT_BOOL_ENTRY ("program", options.program_report); INIT_BOOL_ENTRY ("sensitivity", options.sensitivity_report); input = gnm_solver_param_get_input (param); if (input != NULL) gnm_expr_entry_load_from_text (state->change_cell_entry, value_peek_string (input)); target_cell = gnm_solver_param_get_target_cell (param); if (target_cell) gnm_expr_entry_load_from_text (state->target_entry, cell_name (target_cell)); else { SheetView *sv = wb_control_cur_sheet_view (GNM_WBC (state->wbcg)); if (sv) { GnmRange first = {sv->edit_pos, sv->edit_pos}; gnm_expr_entry_load_from_range (state->target_entry, state->sheet, &first); } } gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON ( go_gtk_builder_get_widget(state->gui, "max_button")), param->problem_type == GNM_SOLVER_MAXIMIZE); gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON ( go_gtk_builder_get_widget(state->gui, "min_button")), param->problem_type == GNM_SOLVER_MINIMIZE); gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON ( go_gtk_builder_get_widget(state->gui, "no_scenario")), ! param->options.add_scenario); gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON ( go_gtk_builder_get_widget(state->gui, "optimal_scenario")), param->options.add_scenario); state->scenario_name_entry = go_gtk_builder_get_widget (state->gui, "scenario_name_entry"); gtk_entry_set_text (GTK_ENTRY (state->scenario_name_entry), param->options.scenario_name); state->run.status_widget = go_gtk_builder_get_widget (state->gui, "solver_status_label"); state->run.problem_status_widget = go_gtk_builder_get_widget (state->gui, "problem_status_label"); state->run.objective_value_widget = go_gtk_builder_get_widget (state->gui, "objective_value_label"); state->run.timer_widget = go_gtk_builder_get_widget (state->gui, "elapsed_time_label"); state->run.spinner = go_gtk_builder_get_widget (state->gui, "run_spinner"); /* Done */ gnm_expr_entry_grab_focus (state->target_entry, FALSE); wbcg_set_entry (state->wbcg, state->target_entry); dialog_set_main_button_sensitivity (NULL, state); dialog_set_sec_button_sensitivity (NULL, state); /* dialog */ wbc_gtk_attach_guru (state->wbcg, state->dialog); g_signal_connect_swapped (G_OBJECT (state->dialog), "destroy", G_CALLBACK (cb_dialog_solver_destroy), state); g_object_set_data_full (G_OBJECT (state->dialog), "state", state, (GDestroyNotify)unref_state); return FALSE; }
static GnmValue * callback_function_collect (GnmEvalPos const *ep, GnmValue const *value, void *closure) { gnm_float x = 0; collect_floats_t *cl = closure; gboolean ignore = FALSE; switch (value ? value->type : VALUE_EMPTY) { case VALUE_EMPTY: if (cl->flags & COLLECT_IGNORE_BLANKS) ignore = TRUE; else if (cl->flags & COLLECT_ZERO_BLANKS) x = 0; else return value_new_error_VALUE (ep); break; case VALUE_BOOLEAN: if (cl->flags & COLLECT_IGNORE_BOOLS) ignore = TRUE; else if (cl->flags & COLLECT_ZEROONE_BOOLS) x = value_get_as_float (value); else return value_new_error_VALUE (ep); break; case VALUE_CELLRANGE : case VALUE_ARRAY : /* Ranges and arrays are not singleton values treat as errors */ case VALUE_ERROR: if (cl->flags & COLLECT_IGNORE_ERRORS) ignore = TRUE; else if (cl->flags & COLLECT_ZERO_ERRORS) x = 0; else return value_new_error_VALUE (ep); break; case VALUE_FLOAT: x = value_get_as_float (value); break; case VALUE_STRING: if (cl->flags & COLLECT_COERCE_STRINGS) { GnmValue *vc = format_match_number (value_peek_string (value), NULL, cl->date_conv); gboolean bad = !vc || VALUE_IS_BOOLEAN (vc); if (vc) { x = value_get_as_float (vc); value_release (vc); } else x = 0; if (bad) return value_new_error_VALUE (ep); } else if (cl->flags & COLLECT_IGNORE_STRINGS) ignore = TRUE; else if (cl->flags & COLLECT_ZERO_STRINGS) x = 0; else return value_new_error_VALUE (ep); break; default: g_warning ("Trouble in callback_function_collect. (%d)", value->type); ignore = TRUE; } if (ignore) { if (cl->flags & COLLECT_INFO) cl->info = g_slist_prepend (cl->info, GUINT_TO_POINTER (cl->count)); else { return NULL; } } if (cl->count == cl->alloc_count) { cl->alloc_count = cl->alloc_count * 2 + 20; cl->data = g_renew (gnm_float, cl->data, cl->alloc_count); } cl->data[cl->count++] = x; return NULL; }