static GSList * parse_criteria_range (Sheet *sheet, int b_col, int b_row, int e_col, int e_row, int *field_ind, gboolean anchor_end) { GSList *criterias = NULL; GODateConventions const *date_conv = workbook_date_conv (sheet->workbook); int i, j; for (i = b_row; i <= e_row; i++) { GnmDBCriteria *new_criteria = g_new (GnmDBCriteria, 1); GSList *conditions = NULL; for (j = b_col; j <= e_col; j++) { GnmCriteria *cond; GnmCell *cell = sheet_cell_get (sheet, j, i); if (cell != NULL) gnm_cell_eval (cell); if (gnm_cell_is_empty (cell)) continue; cond = parse_criteria (cell->value, date_conv, anchor_end); cond->column = (field_ind != NULL) ? field_ind[j - b_col] : j - b_col; conditions = g_slist_prepend (conditions, cond); } new_criteria->conditions = g_slist_reverse (conditions); criterias = g_slist_prepend (criterias, new_criteria); } return g_slist_reverse (criterias); }
static gboolean cell_has_expr_or_number_or_blank (GnmCell const * cell) { return (gnm_cell_is_empty (cell) || (cell != NULL && gnm_cell_is_number (cell)) || (cell != NULL && gnm_cell_has_expr (cell))); }
/* * escape special characters .. needs work */ static int roff_fprintf (GsfOutput *output, GnmCell *cell) { int len, i; char const *p; char * s; GnmStyle const *style; if (gnm_cell_is_empty (cell)) return 0; style = gnm_cell_get_style (cell); if (style != NULL && gnm_style_get_contents_hidden (style)) return 0; s = gnm_cell_get_rendered_text (cell); len = strlen (s); p = s; for (i = 0; i < len; i++) { switch (*p) { case '.': gsf_output_printf (output, "\\."); break; case '\\': gsf_output_printf (output, "\\\\"); break; default: gsf_output_printf (output, "%c", *p); break; } p++; } g_free (s); return len; }
/** * parse_database_criteria: * @ep: #GnmEvalPos * @database: #GnmValue * @criteria: #GnmValue * * Parses the criteria cell range. * Returns: (element-type GnmDBCriteria) (transfer full): */ GSList * parse_database_criteria (GnmEvalPos const *ep, GnmValue const *database, GnmValue const *criteria) { Sheet *sheet; GnmCell *cell; int i; int b_col, b_row, e_col, e_row; int *field_ind; g_return_val_if_fail (VALUE_IS_CELLRANGE (criteria), NULL); sheet = eval_sheet (criteria->v_range.cell.a.sheet, ep->sheet); b_col = criteria->v_range.cell.a.col; b_row = criteria->v_range.cell.a.row; e_col = criteria->v_range.cell.b.col; e_row = criteria->v_range.cell.b.row; if (e_col < b_col) { int tmp = b_col; b_col = e_col; e_col = tmp; } /* Find the index numbers for the columns of criterias */ field_ind = g_alloca (sizeof (int) * (e_col - b_col + 1)); for (i = b_col; i <= e_col; i++) { cell = sheet_cell_get (sheet, i, b_row); if (cell == NULL) continue; gnm_cell_eval (cell); if (gnm_cell_is_empty (cell)) continue; field_ind[i - b_col] = find_column_of_field (ep, database, cell->value); if (field_ind[i - b_col] == -1) return NULL; } return parse_criteria_range (sheet, b_col, b_row + 1, e_col, e_row, field_ind, FALSE); }
G_MODULE_EXPORT void paradox_file_save (GOFileSaver const *fs, GOIOContext *io_context, WorkbookView const *wb_view, GsfOutput *output) { Sheet *sheet; GnmRange r; gint row, col, i; pxdoc_t *pxdoc = NULL; pxfield_t *pxf; char *data; char *tmpfilename; sheet = wb_view_cur_sheet (wb_view); if (sheet == NULL) { go_io_error_string (io_context, _("Cannot get default sheet.")); return; } r = sheet_get_extent (sheet, FALSE, TRUE); #ifdef PX_MEMORY_DEBUGGING pxdoc = PX_new2 (gn_errorhandler, PX_mp_malloc, PX_mp_realloc, PX_mp_free); #else pxdoc = PX_new2 (gn_errorhandler, gn_malloc, gn_realloc, gn_free); #endif /* Read the field specification and build the field array for * PX_create_fp(). The memory is freed by PX_delete() including * the memory for the field name. */ if ((pxf = (pxfield_t *) pxdoc->malloc (pxdoc, (r.end.col+1)*sizeof (pxfield_t), _("Allocate memory for field definitions."))) == NULL){ go_io_error_string (io_context, _("Cannot allocate memory for field definitions.")); PX_delete (pxdoc); return; } for (col = r.start.col; col <= r.end.col; col++) { GnmCell *cell = sheet_cell_get (sheet, col, 0); if (gnm_cell_is_empty (cell)) { go_io_error_string (io_context, _("First line of sheet must contain database specification.")); PX_delete (pxdoc); return; } else { gchar *fieldstr, *tmp; int len, needsize, needprecision; i = col; fieldstr = gnm_cell_get_rendered_text (cell); needsize = 0; needprecision = 0; /* Search for the first comma which is the end of the field name. */ tmp = strchr (fieldstr, ','); if (NULL == tmp) { g_warning (_("Field specification must be a comma separated value (Name,Type,Size,Prec).")); PX_delete (pxdoc); return; } len = tmp-fieldstr; if (NULL == (pxf[i].px_fname = pxdoc->malloc (pxdoc, len+1, _("Allocate memory for column name.")))) { g_warning (_("Could not allocate memory for %d. field name."), i); PX_delete (pxdoc); return; } strncpy (pxf[i].px_fname, fieldstr, len); pxf[i].px_fname[len] = '\0'; /* Get the field Type */ fieldstr = tmp+1; if (*fieldstr == '\0') { g_warning (_("%d. field specification ended unexpectedly."), i); PX_delete (pxdoc); return; } if (*fieldstr == ',') { g_warning (_("%d. field specification misses type."), i); PX_delete (pxdoc); return; } switch ((int) *fieldstr) { case 'S': pxf[i].px_ftype = pxfShort; pxf[i].px_flen = 2; break; case 'I': pxf[i].px_ftype = pxfLong; pxf[i].px_flen = 4; break; case 'A': case 'C': pxf[i].px_ftype = pxfAlpha; needsize = 1; break; case 'N': pxf[i].px_ftype = pxfNumber; pxf[i].px_flen = 8; break; case '$': pxf[i].px_ftype = pxfCurrency; pxf[i].px_flen = 8; break; case 'L': pxf[i].px_ftype = pxfLogical; pxf[i].px_flen = 1; break; case 'D': pxf[i].px_ftype = pxfDate; pxf[i].px_flen = 4; break; case '+': pxf[i].px_ftype = pxfAutoInc; pxf[i].px_flen = 4; break; case '@': pxf[i].px_ftype = pxfTimestamp; pxf[i].px_flen = 8; break; case 'T': pxf[i].px_ftype = pxfTime; pxf[i].px_flen = 4; break; case '#': pxf[i].px_ftype = pxfBCD; pxf[i].px_flen = 17; needprecision = 1; break; case 'M': pxf[i].px_ftype = pxfMemoBLOb; needsize = 1; break; case 'B': pxf[i].px_ftype = pxfBLOb; needsize = 1; break; case 'F': pxf[i].px_ftype = pxfFmtMemoBLOb; needsize = 1; break; case 'Y': pxf[i].px_ftype = pxfBytes; needsize = 1; break; default: g_warning (_("%d. field type '%c' is unknown."), i, *fieldstr); pxdoc->free (pxdoc, pxf); PX_delete (pxdoc); return; } if (needsize || needprecision) { char *endptr; /* find end of type definition */ tmp = strchr (fieldstr, ','); if (NULL == tmp || *(tmp+1) == '\0') { g_warning (_("Field specification misses the column size.")); PX_delete (pxdoc); return; } fieldstr = tmp+1; if (needsize) pxf[i].px_flen = strtol (fieldstr, &endptr, 10); else pxf[i].px_fdc = strtol (fieldstr, &endptr, 10); if ((endptr == NULL) || (fieldstr == endptr)) { g_warning (_("Field specification misses the column size.")); PX_delete (pxdoc); return; } if (*endptr != '\0') { /* There is also precision which we do not care about. */ fieldstr = endptr+1; g_warning (_("The remainder '%s' of the specification for field %d is being disregarded."), fieldstr, i+1); } } } } /* Create the paradox file */ tmpfilename = tempnam ("/tmp", NULL); if (0 > PX_create_file (pxdoc, pxf, r.end.col+1, tmpfilename, pxfFileTypNonIndexDB)) { g_warning (_("Could not create output file.")); PX_delete (pxdoc); return; } PX_set_inputencoding (pxdoc, "UTF-8"); PX_set_parameter (pxdoc, "targetencoding", "CP1252"); PX_set_tablename (pxdoc, sheet->name_unquoted); if ((data = (char *) pxdoc->malloc (pxdoc, pxdoc->px_head->px_recordsize, _("Allocate memory for record data."))) == NULL) { g_warning (_("Could not allocate memory for record data.")); PX_close (pxdoc); PX_delete (pxdoc); return; } /* Process all cells */ for (row = r.start.row+1; row <= r.end.row; row++) { int i; int offset; offset = 0; memset (data, 0, pxdoc->px_head->px_recordsize); for (col = r.start.col, i = 0; col <= r.end.col; col++) { GnmCell *cell = sheet_cell_get (sheet, col, row); if (!gnm_cell_is_empty (cell)) { char *fieldstr = gnm_cell_get_rendered_text (cell); switch (pxf[i].px_ftype) { case pxfShort: { int value = value_get_as_int (cell->value); PX_put_data_short (pxdoc, &data[offset], 2, (short int) value); break; } case pxfLong: case pxfAutoInc: { int value = value_get_as_int (cell->value); PX_put_data_long (pxdoc, &data[offset], 4, value); break; } case pxfTimestamp: { double value = value_get_as_float (cell->value); /* 60 would be 29.2.1900 which didn't exist. */ if (value < 60) value += 1.0; value += 693594; value *= 86400000.0; PX_put_data_double (pxdoc, &data[offset], 8, value); break; } case pxfCurrency: case pxfNumber: { double value = value_get_as_float (cell->value); PX_put_data_double(pxdoc, &data[offset], 8, value); break; } case pxfAlpha: { char *value = fieldstr; int nlen = strlen (value); if (nlen > pxf[i].px_flen) /* xgettext : last %d gives the number of characters.*/ /* This is input to ngettext. */ g_warning (ngettext ("Field %d in line %d has possibly " "been cut off. Data has %d character.", "Field %d in line %d has possibly " "been cut off. Data has %d characters.", nlen), i+1, row+1, nlen); PX_put_data_alpha (pxdoc, &data[offset], pxf[i].px_flen, value); break; } case pxfMemoBLOb: case pxfFmtMemoBLOb: { char *value = fieldstr; if (0 > PX_put_data_blob (pxdoc, &data[offset], pxf[i].px_flen, value, strlen (value))) { g_warning (_("Field %d in row %d could not be written."), i+1, row+1); } break; } case pxfDate: { long value = value_get_as_int (cell->value); /* 60 would be 29.2.1900 which didn't exist. */ if (value < 60) value++; value += 693594; PX_put_data_long (pxdoc, &data[offset], 4, value); break; } case pxfTime: { double dtmp; int value; dtmp = value_get_as_float (cell->value); dtmp -= ((int) dtmp); value = (int) (dtmp * 86400000.0); PX_put_data_long (pxdoc, &data[offset], 4, value); break; } case pxfLogical: { gboolean err; /* Ignored */ gboolean value = value_get_as_bool (cell->value, &err); PX_put_data_byte (pxdoc, &data[offset], 1, value ? 1 : 0); break; } case pxfBCD: PX_put_data_bcd (pxdoc, &data[offset], pxf[i].px_fdc, fieldstr); break; } } offset += pxf[i].px_flen; i++; } if ((i > 0) && (0 > PX_put_record (pxdoc, data))) { g_warning (_("Could not write record number %d."), i+1); pxdoc->free (pxdoc, data); PX_close (pxdoc); PX_delete (pxdoc); return; } } pxdoc->free (pxdoc, data); PX_close (pxdoc); PX_delete (pxdoc); #ifdef PX_MEMORY_DEBUGGING PX_mp_list_unfreed (); #endif { FILE *fp; size_t size; fp = fopen (tmpfilename, "r"); if (fp) { data = g_malloc (8192); while (0 != (size = fread (data, 1, 8192, fp))) gsf_output_write (output, size, data); fclose (fp); g_free (data); } else g_warning ("Cannot open %s\n", tmpfilename); unlink (tmpfilename); } }
/* * cell_calc_span: * @cell: The cell we will examine * @col1: return value: the first column used by this cell * @col2: return value: the last column used by this cell * * This routine returns the column interval used by a GnmCell. */ void cell_calc_span (GnmCell const *cell, int *col1, int *col2) { Sheet *sheet; int h_align, v_align, left, max_col, min_col; int row, pos; int cell_width_pixel, indented_w; GnmStyle const *style; ColRowInfo const *ci; GnmRange const *merge_left; GnmRange const *merge_right; g_return_if_fail (cell != NULL); sheet = cell->base.sheet; style = gnm_cell_get_style (cell); h_align = gnm_style_default_halign (style, cell); /* * Report only one column is used if * - Cell is in a hidden col * - Cell is a number * - Cell is the top left of a merged cell * - The text fits inside column (for non center across selection) * - The alignment mode are set to "justify" */ if (sheet != NULL && h_align != HALIGN_CENTER_ACROSS_SELECTION && (gnm_cell_is_merged (cell) || (!sheet->display_formulas && gnm_cell_is_number (cell)))) { *col1 = *col2 = cell->pos.col; return; } v_align = gnm_style_get_align_v (style); row = cell->pos.row; indented_w = cell_width_pixel = gnm_cell_rendered_width (cell); if (h_align == HALIGN_LEFT || h_align == HALIGN_RIGHT) { indented_w += gnm_cell_rendered_offset (cell); if (sheet->text_is_rtl) h_align = (h_align == HALIGN_LEFT) ? HALIGN_RIGHT : HALIGN_LEFT; } ci = sheet_col_get_info (sheet, cell->pos.col); if (gnm_cell_is_empty (cell) || !ci->visible || (h_align != HALIGN_CENTER_ACROSS_SELECTION && (gnm_style_get_wrap_text (style) || indented_w <= COL_INTERNAL_WIDTH (ci))) || h_align == HALIGN_JUSTIFY || h_align == HALIGN_FILL || h_align == HALIGN_DISTRIBUTED || v_align == VALIGN_JUSTIFY || v_align == VALIGN_DISTRIBUTED) { *col1 = *col2 = cell->pos.col; return; } gnm_sheet_merge_get_adjacent (sheet, &cell->pos, &merge_left, &merge_right); min_col = (merge_left != NULL) ? merge_left->end.col : -1; max_col = (merge_right != NULL) ? merge_right->start.col : gnm_sheet_get_max_cols (sheet); *col1 = *col2 = cell->pos.col; switch (h_align) { case HALIGN_LEFT: pos = cell->pos.col + 1; left = indented_w - COL_INTERNAL_WIDTH (ci); for (; left > 0 && pos < max_col; pos++){ ColRowInfo const *ci = sheet_col_get_info (sheet, pos); if (ci->visible) { if (!cellspan_is_empty (pos, cell)) return; /* The space consumed is: * - The margin_b from the last column * - The width of the cell */ left -= ci->size_pixels - 1; *col2 = pos; } } return; case HALIGN_RIGHT: pos = cell->pos.col - 1; left = indented_w - COL_INTERNAL_WIDTH (ci); for (; left > 0 && pos > min_col; pos--){ ColRowInfo const *ci = sheet_col_get_info (sheet, pos); if (ci->visible) { if (!cellspan_is_empty (pos, cell)) return; /* The space consumed is: * - The margin_a from the last column * - The width of this cell */ left -= ci->size_pixels - 1; *col1 = pos; } } return; case HALIGN_CENTER: { int remain_left, remain_right; int pos_l, pos_r; pos_l = pos_r = cell->pos.col; left = cell_width_pixel - COL_INTERNAL_WIDTH (ci); remain_left = left / 2 + (left % 2); remain_right = left / 2; for (; remain_left > 0 || remain_right > 0;){ ColRowInfo const *ci; if (--pos_l > min_col){ ci = sheet_col_get_info (sheet, pos_l); if (ci->visible) { if (cellspan_is_empty (pos_l, cell)) { remain_left -= ci->size_pixels - 1; *col1 = pos_l; } else remain_left = 0; } } else remain_left = 0; if (++pos_r < max_col){ ci = sheet_col_get_info (sheet, pos_r); if (ci->visible) { if (cellspan_is_empty (pos_r, cell)) { remain_right -= ci->size_pixels - 1; *col2 = pos_r; } else max_col = remain_right = 0; } } else remain_right = 0; } /* for */ break; } /* case HALIGN_CENTER */ case HALIGN_CENTER_ACROSS_SELECTION: { int const row = cell->pos.row; int pos_l, pos_r; pos_l = pos_r = cell->pos.col; while (--pos_l > min_col) { ColRowInfo const *ci = sheet_col_get_info (sheet, pos_l); if (ci->visible) { if (cellspan_is_empty (pos_l, cell)) { GnmStyle const * const style = sheet_style_get (cell->base.sheet, pos_l, row); if (gnm_style_get_align_h (style) != HALIGN_CENTER_ACROSS_SELECTION) break; *col1 = pos_l; } else break; } } while (++pos_r < max_col) { ColRowInfo const *ci = sheet_col_get_info (sheet, pos_r); if (ci->visible) { if (cellspan_is_empty (pos_r, cell)) { GnmStyle const * const style = sheet_style_get (cell->base.sheet, pos_r, row); if (gnm_style_get_align_h (style) != HALIGN_CENTER_ACROSS_SELECTION) break; *col2 = pos_r; } else break; } } break; } default: g_warning ("Unknown horizontal alignment type %x.", h_align); } /* switch */ }
/* * Write _current_ sheet of the workbook to a DIF format file */ void dif_file_save (GOFileSaver const *fs, GOIOContext *io_context, WorkbookView const *wbv, GsfOutput *out) { GnmLocale *locale; Sheet *sheet; GnmRange r; gint row, col; gboolean ok = TRUE; sheet = wb_view_cur_sheet (wbv); if (sheet == NULL) { go_io_error_string (io_context, _("Cannot get default sheet.")); return; } r = sheet_get_extent (sheet, FALSE, TRUE); /* Write out the standard headers */ gsf_output_puts (out, "TABLE\n" "0,1\n" "\"GNUMERIC\"\n"); gsf_output_printf (out, "VECTORS\n" "0,%d\n" "\"\"\n", r.end.col+1); gsf_output_printf (out, "TUPLES\n" "0,%d\n" "\"\"\n", r.end.row+1); gsf_output_puts (out, "DATA\n" "0,0\n" "\"\"\n"); locale = gnm_push_C_locale (); /* Process all cells */ for (row = r.start.row; ok && row <= r.end.row; row++) { gsf_output_puts (out, "-1,0\n" "BOT\n"); for (col = r.start.col; col <= r.end.col; col++) { GnmCell *cell = sheet_cell_get (sheet, col, row); if (gnm_cell_is_empty (cell)) { gsf_output_puts(out, "1,0\n" "\"\"\n"); } else if (VALUE_IS_BOOLEAN (cell->value)) { if (value_get_as_checked_bool (cell->value)) gsf_output_puts(out, "0,1\n" "TRUE\n"); else gsf_output_puts(out, "0,0\n" "FALSE\n"); } else if (VALUE_IS_ERROR (cell->value)) { if (value_error_classify (cell->value) == GNM_ERROR_NA) gsf_output_puts(out, "0,0\n" "NA\n"); else gsf_output_puts(out, "0,0\n" "ERROR\n"); } else if (VALUE_IS_FLOAT (cell->value)) gsf_output_printf (out, "0,%" GNM_FORMAT_g "\n" "V\n", value_get_as_float (cell->value)); else { gchar *str = gnm_cell_get_rendered_text (cell); ok = gsf_output_printf (out, "1,0\n" "\"%s\"\n", str); g_free (str); } } } gsf_output_puts (out, "-1,0\n" "EOD\n"); gnm_pop_C_locale (locale); if (!ok) go_io_error_string (io_context, _("Error while saving DIF file.")); }
static void search_get_value (gint row, gint column, gpointer _dd, GValue *value) { DialogState *dd = (DialogState *)_dd; GnumericLazyList *ll = GNUMERIC_LAZY_LIST (gtk_tree_view_get_model (dd->matches_table)); GnmSearchFilterResult *item = g_ptr_array_index (dd->matches, row); GnmCell *cell; GnmComment *comment; if (item->locus == GNM_SRL_COMMENT) { cell = NULL; comment = sheet_get_comment (item->ep.sheet, &item->ep.eval); } else { cell = sheet_cell_get (item->ep.sheet, item->ep.eval.col, item->ep.eval.row); comment = NULL; } g_value_init (value, ll->column_headers[column]); #if 0 g_print ("col=%d,row=%d\n", column, row); #endif switch (column) { case COL_SHEET: g_value_set_string (value, item->ep.sheet->name_unquoted); return; case COL_CELL: g_value_set_string (value, cellpos_as_string (&item->ep.eval)); return; case COL_TYPE: switch (item->locus) { case GNM_SRL_COMMENT: g_value_set_static_string (value, _("Comment")); return; case GNM_SRL_VALUE: g_value_set_static_string (value, _("Result")); return; case GNM_SRL_CONTENTS: { GnmValue *v = cell ? cell->value : NULL; char const *type; gboolean is_expr = cell && gnm_cell_has_expr (cell); gboolean is_value = !is_expr && !gnm_cell_is_empty (cell) && v; if (!cell) type = _("Deleted"); else if (is_expr) type = _("Expression"); else if (is_value && VALUE_IS_STRING (v)) type = _("String"); else if (is_value && VALUE_IS_FLOAT (v)) type = _("Number"); else type = _("Other value"); g_value_set_static_string (value, type); return; } #ifndef DEBUG_SWITCH_ENUM default: g_assert_not_reached (); #endif } case COL_CONTENTS: switch (item->locus) { case GNM_SRL_COMMENT: if (comment) g_value_set_string (value, cell_comment_text_get (comment)); else g_value_set_static_string (value, _("Deleted")); return; case GNM_SRL_VALUE: if (cell && cell->value) g_value_take_string (value, value_get_as_string (cell->value)); else g_value_set_static_string (value, _("Deleted")); return; case GNM_SRL_CONTENTS: if (cell) g_value_take_string (value, gnm_cell_get_entered_text (cell)); else g_value_set_static_string (value, _("Deleted")); return; #ifndef DEBUG_SWITCH_ENUM default: g_assert_not_reached (); #endif } #ifndef DEBUG_SWITCH_ENUM default: g_assert_not_reached (); #endif } }
/** * 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; }
static gboolean item_grid_draw_region (GocItem const *item, cairo_t *cr, double x_0, double y_0, double x_1, double y_1) { GocCanvas *canvas = item->canvas; double scale = canvas->pixels_per_unit; gint64 x0 = x_0 * scale, y0 = y_0 * scale, x1 = x_1 * scale, y1 = y_1 * scale; gint width = x1 - x0; gint height = y1 - y0; GnmPane *pane = GNM_PANE (canvas); Sheet const *sheet = scg_sheet (pane->simple.scg); WBCGtk *wbcg = scg_wbcg (pane->simple.scg); GnmCell const * const edit_cell = wbcg->editing_cell; GnmItemGrid *ig = GNM_ITEM_GRID (item); ColRowInfo const *ri = NULL, *next_ri = NULL; int const dir = sheet->text_is_rtl ? -1 : 1; SheetView const *sv = scg_view (ig->scg); WorkbookView *wbv = sv_wbv (sv); gboolean show_function_cell_markers = wbv->show_function_cell_markers; gboolean show_extension_markers = wbv->show_extension_markers; /* we use the selected background color from an entry for selected cells */ GtkWidget *entry = gtk_entry_new (); GtkStyleContext *ctxt = gtk_widget_get_style_context (entry); /* To ensure that far and near borders get drawn we pretend to draw +-2 * pixels around the target area which would include the surrounding * borders if necessary */ /* TODO : there is an opportunity to speed up the redraw loop by only * painting the borders of the edges and not the content. * However, that feels like more hassle that it is worth. Look into this someday. */ int x; gint64 y, start_x, offset; int col, row, n, start_col, end_col; int start_row = gnm_pane_find_row (pane, y0-2, &y); int end_row = gnm_pane_find_row (pane, y1+2, NULL); gint64 const start_y = y - canvas->scroll_y1 * scale; GnmStyleRow sr, next_sr; GnmStyle const **styles; GnmBorder const **borders, **prev_vert; GnmBorder const *none = sheet->hide_grid ? NULL : gnm_style_border_none (); GnmRange view; GSList *merged_active, *merged_active_seen, *merged_used, *merged_unused, *ptr, **lag; int *colwidths = NULL; gboolean const draw_selection = ig->scg->selected_objects == NULL && wbcg->new_object == NULL; start_col = gnm_pane_find_col (pane, x0-2, &start_x); end_col = gnm_pane_find_col (pane, x1+2, NULL); g_return_val_if_fail (start_col <= end_col, TRUE); #if 0 g_printerr ("%s:", cell_coord_name (start_col, start_row)); g_printerr ("%s <= %ld vs ???", cell_coord_name(end_col, end_row), (long)y); g_printerr (" [%s]\n", cell_coord_name (ig->bound.end.col, ig->bound.end.row)); #endif /* clip to bounds */ if (end_col > ig->bound.end.col) end_col = ig->bound.end.col; if (end_row > ig->bound.end.row) end_row = ig->bound.end.row; /* Skip any hidden cols/rows at the start */ for (; start_col <= end_col ; ++start_col) { ri = sheet_col_get_info (sheet, start_col); if (ri->visible) break; } for (; start_row <= end_row ; ++start_row) { ri = sheet_row_get_info (sheet, start_row); if (ri->visible) break; } /* if everything is hidden no need to draw */ if (end_col < ig->bound.start.col || start_col > ig->bound.end.col || end_row < ig->bound.start.row || start_row > ig->bound.end.row) return TRUE; /* Respan all rows that need it. */ for (row = start_row; row <= end_row; row++) { ColRowInfo const *ri = sheet_row_get_info (sheet, row); if (ri->visible && ri->needs_respan) row_calc_spans ((ColRowInfo *)ri, row, sheet); } sheet_style_update_grid_color (sheet); /* Fill entire region with default background (even past far edge) */ cairo_save (cr); if (canvas->direction == GOC_DIRECTION_LTR) gtk_render_background (goc_item_get_style_context (item), cr, x0 - canvas->scroll_x1 * scale, y0 - canvas->scroll_y1 * scale, width, height); else gtk_render_background (goc_item_get_style_context (item), cr, canvas->width - x0 + canvas->scroll_x1 * scale - width, y0 - canvas->scroll_y1 * scale, width, height); cairo_restore (cr); /* Get ordered list of merged regions */ merged_active = merged_active_seen = merged_used = NULL; merged_unused = gnm_sheet_merge_get_overlap (sheet, range_init (&view, start_col, start_row, end_col, end_row)); /* * allocate a single blob of memory for all 8 arrays of pointers. * - 6 arrays of n GnmBorder const * * - 2 arrays of n GnmStyle const * * * then alias the arrays for easy access so that array [col] is valid * for all elements start_col-1 .. end_col+1 inclusive. * Note that this means that in some cases array [-1] is legal. */ n = end_col - start_col + 3; /* 1 before, 1 after, 1 fencepost */ style_row_init (&prev_vert, &sr, &next_sr, start_col, end_col, g_alloca (n * 8 * sizeof (gpointer)), sheet->hide_grid); /* load up the styles for the first row */ next_sr.row = sr.row = row = start_row; sheet_style_get_row (sheet, &sr); /* Collect the column widths */ colwidths = g_alloca (n * sizeof (int)); colwidths -= start_col; for (col = start_col; col <= end_col; col++) { ColRowInfo const *ci = sheet_col_get_info (sheet, col); colwidths[col] = ci->visible ? ci->size_pixels : -1; } goc_canvas_c2w (canvas, start_x / scale, 0, &x, NULL); start_x = x; for (y = start_y; row <= end_row; row = sr.row = next_sr.row, ri = next_ri) { /* Restore the set of ranges seen, but still active. * Reinverting list to maintain the original order */ g_return_val_if_fail (merged_active == NULL, TRUE); #if DEBUG_SELECTION_PAINT g_printerr ("row = %d (startcol = %d)\n", row, start_col); #endif while (merged_active_seen != NULL) { GSList *tmp = merged_active_seen->next; merged_active_seen->next = merged_active; merged_active = merged_active_seen; merged_active_seen = tmp; MERGE_DEBUG (merged_active->data, " : seen -> active\n"); } /* find the next visible row */ while (1) { ++next_sr.row; if (next_sr.row <= end_row) { next_ri = sheet_row_get_info (sheet, next_sr.row); if (next_ri->visible) { sheet_style_get_row (sheet, &next_sr); break; } } else { for (col = start_col ; col <= end_col; ++col) next_sr.vertical [col] = next_sr.bottom [col] = none; break; } } /* look for merges that start on this row, on the first painted row * also check for merges that start above. */ view.start.row = row; lag = &merged_unused; for (ptr = merged_unused; ptr != NULL; ) { GnmRange * const r = ptr->data; if (r->start.row <= row) { GSList *tmp = ptr; ptr = *lag = tmp->next; if (r->end.row < row) { tmp->next = merged_used; merged_used = tmp; MERGE_DEBUG (r, " : unused -> used\n"); } else { ColRowInfo const *ci = sheet_col_get_info (sheet, r->start.col); g_slist_free_1 (tmp); merged_active = g_slist_insert_sorted (merged_active, r, (GCompareFunc)merged_col_cmp); MERGE_DEBUG (r, " : unused -> active\n"); if (ci->visible) item_grid_draw_merged_range (cr, ig, start_x, y, &view, r, draw_selection, ctxt); } } else { lag = &(ptr->next); ptr = ptr->next; } } for (col = start_col, x = start_x; col <= end_col ; col++) { GnmStyle const *style; CellSpanInfo const *span; ColRowInfo const *ci = sheet_col_get_info (sheet, col); #if DEBUG_SELECTION_PAINT g_printerr ("col [%d] = %d\n", col, x); #endif if (!ci->visible) { if (merged_active != NULL) { GnmRange const *r = merged_active->data; if (r->end.col == col) { ptr = merged_active; merged_active = merged_active->next; if (r->end.row <= row) { ptr->next = merged_used; merged_used = ptr; MERGE_DEBUG (r, " : active2 -> used\n"); } else { ptr->next = merged_active_seen; merged_active_seen = ptr; MERGE_DEBUG (r, " : active2 -> seen\n"); } } } continue; } /* Skip any merged regions */ if (merged_active != NULL) { GnmRange const *r = merged_active->data; if (r->start.col <= col) { gboolean clear_top, clear_bottom = FALSE; int i, first = r->start.col; int last = r->end.col; ptr = merged_active; merged_active = merged_active->next; if (r->end.row <= row) { ptr->next = merged_used; merged_used = ptr; MERGE_DEBUG (r, " : active -> used\n"); /* in case something managed the bottom of a merge */ if (r->end.row < row) goto plain_draw; } else { ptr->next = merged_active_seen; merged_active_seen = ptr; MERGE_DEBUG (r, " : active -> seen\n"); if (next_sr.row <= r->end.row) clear_bottom = TRUE; } x += dir * scg_colrow_distance_get ( pane->simple.scg, TRUE, col, last+1); col = last; if (first < start_col) { first = start_col; sr.vertical [first] = NULL; } if (last > end_col) { last = end_col; sr.vertical [last+1] = NULL; } clear_top = (r->start.row != row); /* Clear the borders */ for (i = first ; i <= last ; i++) { if (clear_top) sr.top [i] = NULL; if (clear_bottom) sr.bottom [i] = NULL; if (i > first) sr.vertical [i] = NULL; } continue; } } plain_draw : /* a quick hack to deal with 142267 */ if (dir < 0) x -= ci->size_pixels; style = sr.styles [col]; item_grid_draw_background (cr, ig, style, col, row, x, y, ci->size_pixels, ri->size_pixels, draw_selection, ctxt); /* Is this part of a span? * 1) There are cells allocated in the row * (indicated by ri->spans != NULL) * 2) Look in the rows hash table to see if * there is a span descriptor. */ if (NULL == ri->spans || NULL == (span = row_span_get (ri, col))) { /* If it is being edited pretend it is empty to * avoid problems with long cells' * contents extending past the edge of the edit * box. Ignore blanks too. */ GnmCell const *cell = sheet_cell_get (sheet, col, row); if (!gnm_cell_is_empty (cell) && cell != edit_cell) { if (show_function_cell_markers) draw_function_marker (ig, cell, cr, x, y, ci->size_pixels, ri->size_pixels, dir); cell_draw (cell, cr, x, y, ci->size_pixels, ri->size_pixels, -1, show_extension_markers); } /* Only draw spaning cells after all the backgrounds * that we are going to draw have been drawn. No need * to draw the edit cell, or blanks. */ } else if (edit_cell != span->cell && (col == span->right || col == end_col)) { GnmCell const *cell = span->cell; int const start_span_col = span->left; int const end_span_col = span->right; int real_x = x; ColRowInfo const *cell_col = sheet_col_get_info (sheet, cell->pos.col); int center_offset = cell_col->size_pixels/2; int tmp_width = ci->size_pixels; if (col != cell->pos.col) style = sheet_style_get (sheet, cell->pos.col, row); /* x, y are relative to this cell origin, but the cell * might be using columns to the left (if it is set to right * justify or center justify) compute the pixel difference */ if (dir > 0 && start_span_col != cell->pos.col) center_offset += scg_colrow_distance_get ( pane->simple.scg, TRUE, start_span_col, cell->pos.col); else if (dir < 0 && end_span_col != cell->pos.col) center_offset += scg_colrow_distance_get ( pane->simple.scg, TRUE, cell->pos.col, end_span_col); if (start_span_col != col) { offset = scg_colrow_distance_get ( pane->simple.scg, TRUE, start_span_col, col); tmp_width += offset; if (dir > 0) real_x -= offset; sr.vertical [col] = NULL; } if (end_span_col != col) { offset = scg_colrow_distance_get ( pane->simple.scg, TRUE, col+1, end_span_col + 1); tmp_width += offset; if (dir < 0) real_x -= offset; } if (show_function_cell_markers) draw_function_marker (ig, cell, cr, real_x, y, tmp_width, ri->size_pixels, dir); cell_draw (cell, cr, real_x, y, tmp_width, ri->size_pixels, center_offset, show_extension_markers); } else if (col != span->left) sr.vertical [col] = NULL; if (dir > 0) x += ci->size_pixels; } gnm_style_borders_row_draw (prev_vert, &sr, cr, start_x, y, y+ri->size_pixels, colwidths, TRUE, dir); /* In case there were hidden merges that trailed off the end */ while (merged_active != NULL) { GnmRange const *r = merged_active->data; ptr = merged_active; merged_active = merged_active->next; if (r->end.row <= row) { ptr->next = merged_used; merged_used = ptr; MERGE_DEBUG (r, " : active3 -> used\n"); } else { ptr->next = merged_active_seen; merged_active_seen = ptr; MERGE_DEBUG (r, " : active3 -> seen\n"); } } /* roll the pointers */ borders = prev_vert; prev_vert = sr.vertical; sr.vertical = next_sr.vertical; next_sr.vertical = borders; borders = sr.top; sr.top = sr.bottom; sr.bottom = next_sr.top = next_sr.bottom; next_sr.bottom = borders; styles = sr.styles; sr.styles = next_sr.styles; next_sr.styles = styles; y += ri->size_pixels; } if (ig->bound.start.row > 0 && start_y < 1) ig_cairo_draw_bound (ig, cr, start_x, 1, x, 1); if (ig->bound.start.col > 0) { if (canvas->direction == GOC_DIRECTION_RTL && start_x >= goc_canvas_get_width (canvas)) { x = goc_canvas_get_width (canvas); ig_cairo_draw_bound (ig, cr, x, start_y, x, y); } else if (canvas->direction == GOC_DIRECTION_LTR && start_x < 1) ig_cairo_draw_bound (ig, cr, 1, start_y, 1, y); } g_object_ref_sink (entry); g_object_unref (entry); g_slist_free (merged_used); /* merges with bottom in view */ g_slist_free (merged_active_seen); /* merges with bottom the view */ g_slist_free (merged_unused); /* merges in hidden rows */ g_return_val_if_fail (merged_active == NULL, TRUE); return TRUE; }
/* no spans or merges */ static gboolean preview_grid_draw_region (GocItem const *item, cairo_t *cr, double x0, double y0, double x1, double y1) { GnmPreviewGrid *pg = GNM_PREVIEW_GRID (item); /* To ensure that far and near borders get drawn we pretend to draw +-2 * pixels around the target area which would include the surrounding * borders if necessary */ /* TODO : there is an opportunity to speed up the redraw loop by only * painting the borders of the edges and not the content. * However, that feels like more hassle that it is worth. Look into this someday. */ int x, y, col, row, n; int const start_col = pg_get_col_offset (pg, x0 - 2, &x); int end_col = pg_get_col_offset (pg, x1 + 2, NULL); int diff_x = x; int start_row = pg_get_row_offset (pg, y0 - 2, &y); int end_row = pg_get_row_offset (pg, y1 + 2, NULL); int diff_y = y; int row_height = pg->defaults.row_height; GnmStyleRow sr, next_sr; GnmStyle const **styles; GnmBorder const **borders, **prev_vert; GnmBorder const *none = pg->gridlines ? gnm_style_border_none () : NULL; int *colwidths = NULL; gnm_style_border_none_set_color (style_color_grid ()); /* * allocate a single blob of memory for all 8 arrays of pointers. * - 6 arrays of n GnmBorder const * * - 2 arrays of n GnmStyle const * */ n = end_col - start_col + 3; /* 1 before, 1 after, 1 fencepost */ style_row_init (&prev_vert, &sr, &next_sr, start_col, end_col, g_alloca (n * 8 * sizeof (gpointer)), !pg->gridlines); /* load up the styles for the first row */ next_sr.row = sr.row = row = start_row; pg_style_get_row (pg, &sr); /* Collect the column widths */ colwidths = g_alloca (n * sizeof (int)); colwidths -= start_col; for (col = start_col; col <= end_col; col++) colwidths[col] = pg->defaults.col_width; /* Fill entire region with default background (even past far edge) */ gtk_render_background (goc_item_get_style_context (item), cr, diff_x, diff_y, x1 - x0, y1 - y0); for (y = diff_y; row <= end_row; row = sr.row = next_sr.row) { if (++next_sr.row > end_row) { for (col = start_col ; col <= end_col; ++col) next_sr.vertical[col] = next_sr.bottom[col] = none; } else pg_style_get_row (pg, &next_sr); for (col = start_col, x = diff_x; col <= end_col; col++) { GnmStyle const *style = sr.styles[col]; GnmCell const *cell = pg_fetch_cell (pg, col, row); preview_grid_draw_background (cr, pg, style, col, row, x, y, colwidths[col], row_height); if (!gnm_cell_is_empty (cell)) cell_draw (cell, cr, x, y, colwidths[col], row_height, -1, FALSE); x += colwidths[col]; } gnm_style_borders_row_draw (prev_vert, &sr, cr, diff_x, y, y + row_height, colwidths, TRUE, 1 /* cheat dir == 1 for now */); /* roll the pointers */ borders = prev_vert; prev_vert = sr.vertical; sr.vertical = next_sr.vertical; next_sr.vertical = borders; borders = sr.top; sr.top = sr.bottom; sr.bottom = next_sr.top = next_sr.bottom; next_sr.bottom = borders; styles = sr.styles; sr.styles = next_sr.styles; next_sr.styles = styles; y += row_height; } return TRUE; }