Update Spanish translation
[gnumeric.git] / src / print-cell.c
blobf9072ce89866581e1a41bfe7e8609da978693575
1 /*
2 * print-cell.c: Printing of cell regions and cells.
4 * Author:
5 * Jody Goldberg 2000-2006 (jody@gnome.org)
6 * Miguel de Icaza 1999 (miguel@kernel.org)
7 * Andreas J. Guelzow 2007 (aguelzow@pyrshep.ca)
8 * Copyright (C) 2007-2009 Morten Welinder (terra@gnome.org)
9 */
10 #include <gnumeric-config.h>
11 #include <gnumeric.h>
12 #include <print-cell.h>
14 #include <application.h>
15 #include <dependent.h>
16 #include <gnm-format.h>
17 #include <style-color.h>
18 #include <style-font.h>
19 #include <parse-util.h>
20 #include <cell.h>
21 #include <value.h>
22 #include <style-border.h>
23 #include <style-conditions.h>
24 #include <pattern.h>
25 #include <cellspan.h>
26 #include <ranges.h>
27 #include <sheet.h>
28 #include <sheet-style.h>
29 #include <sheet-merge.h>
30 #include <rendered-value.h>
31 #include <cell-draw.h>
32 #include <print-info.h>
34 #include <string.h>
35 #include <locale.h>
37 #if 0
38 #define MERGE_DEBUG(range, str) do { range_dump (range, str); } while (0)
39 #else
40 #define MERGE_DEBUG(range, str)
41 #endif
44 * base_[xy] : Coordinates of the upper left corner of the cell.
45 * INCLUSIVE of the near grid line
47 * /--- (x1, y1)
48 * v
49 * g------\
50 * | |
51 * \------/
54 static void
55 print_cell_gtk (GnmCell const *cell,
56 cairo_t *context,
57 double x1, double y1,
58 double width, double height, double h_center,
59 GnmPrintInformation const *pinfo)
61 GnmRenderedValue *rv, *rv100 = NULL;
62 GOColor fore_color;
63 gint x, y;
64 Sheet *sheet = cell->base.sheet;
65 double const scale_h = 72. / gnm_app_display_dpi_get (TRUE);
66 double const scale_v = 72. / gnm_app_display_dpi_get (FALSE);
68 gboolean cell_shows_error;
70 if (cell->base.flags & GNM_CELL_HAS_NEW_EXPR)
71 gnm_cell_eval ((GnmCell *)cell);
73 cell_shows_error = (gnm_cell_is_error (cell) != NULL)
74 && !(gnm_cell_has_expr (cell) && sheet->display_formulas);
76 if (cell_shows_error && pinfo->error_display ==
77 GNM_PRINT_ERRORS_AS_BLANK)
78 return;
80 /* Get the sizes exclusive of margins and grids */
81 /* Note: +1 because size_pixels includes leading gridline. */
82 height -= GNM_ROW_MARGIN + GNM_ROW_MARGIN + 1;
83 width -= GNM_COL_MARGIN + GNM_COL_MARGIN + 1;
85 rv = gnm_cell_fetch_rendered_value (cell, TRUE);
87 /* Create a rendered value for printing */
88 if (cell_shows_error &&
89 (pinfo->error_display == GNM_PRINT_ERRORS_AS_NA
90 || pinfo->error_display == GNM_PRINT_ERRORS_AS_DASHES)) {
91 GnmCell *t_cell = (GnmCell *)cell;
92 GnmValue *old = t_cell->value;
93 if (pinfo->error_display == GNM_PRINT_ERRORS_AS_NA)
94 t_cell->value = value_new_error_NA (NULL);
95 else
96 t_cell->value = value_new_error
97 (NULL,
98 /* U+2014 U+200A U+2014 */
99 "\342\200\224\342\200\212\342\200\224");
100 rv100 = gnm_rendered_value_new (t_cell,
101 pango_layout_get_context (rv->layout),
102 rv->variable_width,
103 1.0);
104 rv = rv100;
105 value_release (t_cell->value);
106 t_cell->value = old;
107 } else if (sheet->last_zoom_factor_used != 1) {
109 * We're zoomed and we don't want printing to reflect that.
112 rv100 = gnm_rendered_value_new ((GnmCell *)cell,
113 pango_layout_get_context (rv->layout),
114 rv->variable_width,
115 1.0);
116 rv = rv100;
119 /* Make sure we don't get overflow in print unless we had it in
120 display. */
121 rv->might_overflow = rv->numeric_overflow;
123 if (cell_calc_layout (cell, rv, -1,
124 (int)(width * PANGO_SCALE / scale_h),
125 (int)(height * PANGO_SCALE / scale_v),
126 (int)h_center == -1 ? -1 : (int)(h_center * PANGO_SCALE),
127 &fore_color, &x, &y)) {
129 /* Clip the printed rectangle */
130 cairo_save (context);
131 #ifndef G_OS_WIN32
132 if (!rv->rotation) {
133 /* We do not clip rotated cells. */
134 cairo_new_path (context);
135 cairo_rectangle (context, x1 + GNM_COL_MARGIN, y1 + GNM_ROW_MARGIN,
136 width + 1, height + 1);
137 cairo_clip (context);
139 #endif
140 /* Set the font colour */
141 cairo_set_source_rgba (context,
142 GO_COLOR_TO_CAIRO (fore_color));
144 cairo_translate (context, x1+0.5, y1);
146 if (rv->rotation) {
147 GnmRenderedRotatedValue *rrv = (GnmRenderedRotatedValue *)rv;
148 struct GnmRenderedRotatedValueInfo const *li = rrv->lines;
149 GSList *lines;
151 cairo_scale (context, scale_h, scale_v);
152 cairo_move_to (context, 0.,0.);
153 for (lines = pango_layout_get_lines (rv->layout);
154 lines;
155 lines = lines->next, li++) {
156 cairo_save (context);
157 cairo_move_to (context,
158 PANGO_PIXELS (x + li->dx),
159 PANGO_PIXELS (- y + li->dy));
160 cairo_rotate (context, rv->rotation * (-M_PI / 180));
161 pango_cairo_show_layout_line (context, lines->data);
162 cairo_restore (context);
166 } else {
167 cairo_scale (context, scale_h, scale_v);
168 cairo_move_to (context, x / (double)PANGO_SCALE , - y / (double)PANGO_SCALE);
169 pango_cairo_show_layout (context, rv->layout);
171 cairo_restore(context);
174 if (rv100)
175 gnm_rendered_value_destroy (rv100);
178 static void
179 print_rectangle_gtk (cairo_t *context,
180 double x, double y, double w, double h)
182 cairo_new_path (context);
183 cairo_rectangle (context, x, y, w, h);
184 cairo_fill (context);
187 static void
188 print_cell_background_gtk (cairo_t *context,
189 GnmStyle const *style,
190 G_GNUC_UNUSED int col, G_GNUC_UNUSED int row,
191 double x, double y, double w, double h)
193 if (gnm_pattern_background_set (style, context, FALSE, NULL))
194 /* Remember api excludes the far pixels */
195 print_rectangle_gtk (context, x, y, w+0.2, h+0.2);
196 gnm_style_border_print_diag_gtk (style, context, x, y, x+w, y+h);
201 * print_merged_range:
203 * Handle the special drawing requirements for a 'merged cell'.
204 * First draw the entire range (clipped to the visible region) then redraw any
205 * segments that are selected.
207 static void
208 print_merged_range_gtk (cairo_t *context,
209 Sheet const *sheet,
210 double start_x, double start_y,
211 GnmRange const *view, GnmRange const *range,
212 GnmPrintInformation const *pinfo)
214 double l, r, t, b;
215 int last;
216 GnmCell const *cell = sheet_cell_get (sheet, range->start.col, range->start.row);
217 int const dir = sheet->text_is_rtl ? -1 : 1;
218 GnmStyleConditions *conds;
220 /* load style from corner which may not be visible */
221 GnmStyle const *style = sheet_style_get (sheet, range->start.col, range->start.row);
223 l = r = start_x;
224 if (view->start.col < range->start.col)
225 l += dir * sheet_col_get_distance_pts (sheet,
226 view->start.col, range->start.col);
227 if (range->end.col <= (last = view->end.col))
228 last = range->end.col;
229 r += dir * sheet_col_get_distance_pts (sheet, view->start.col, last+1);
231 t = b = start_y;
232 if (view->start.row < range->start.row)
233 t -= sheet_row_get_distance_pts (sheet,
234 view->start.row, range->start.row);
235 if (range->end.row <= (last = view->end.row))
236 last = range->end.row;
237 b += sheet_row_get_distance_pts (sheet, view->start.row, last+1);
239 if (l == r || t == b)
240 return;
242 conds = gnm_style_get_conditions (style);
243 if (style) {
244 GnmEvalPos ep;
245 int res;
246 eval_pos_init (&ep, (Sheet *)sheet, range->start.col, range->start.row);
247 if ((res = gnm_style_conditions_eval (conds, &ep)) >= 0)
248 style = gnm_style_get_cond_style (style, res);
251 if (gnm_pattern_background_set (style, context, FALSE, NULL))
252 print_rectangle_gtk (context, l, t, r-l+0.2, b-t+0.2);
254 if (range->start.col < view->start.col)
255 l -= dir * sheet_col_get_distance_pts (sheet,
256 range->start.col, view->start.col);
257 if (view->end.col < range->end.col)
258 r += dir * sheet_col_get_distance_pts (sheet,
259 view->end.col+1, range->end.col+1);
260 if (range->start.row < view->start.row)
261 t -= sheet_row_get_distance_pts (sheet,
262 range->start.row, view->start.row);
263 if (view->end.row < range->end.row)
264 b += sheet_row_get_distance_pts (sheet,
265 view->end.row+1, range->end.row+1);
267 if (cell != NULL) {
268 ColRowInfo *ri = sheet_row_get (sheet, range->start.row);
270 if (ri->needs_respan)
271 row_calc_spans (ri, cell->pos.row, sheet);
273 if (sheet->text_is_rtl)
274 print_cell_gtk (cell, context,
275 r, t, l - r, b - t, -1., pinfo);
276 else
277 print_cell_gtk (cell, context,
278 l, t, r - l, b - t, -1., pinfo);
280 gnm_style_border_print_diag_gtk (style, context, l, t, r, b);
283 static gint
284 merged_col_cmp (GnmRange const *a, GnmRange const *b)
286 return a->start.col - b->start.col;
290 void
291 gnm_gtk_print_cell_range (cairo_t *context,
292 Sheet const *sheet, GnmRange *range,
293 double base_x, double base_y,
294 GnmPrintInformation const *pinfo)
296 ColRowInfo const *ri = NULL, *next_ri = NULL;
297 int const dir = sheet->text_is_rtl ? -1 : 1;
298 double const hscale = sheet->display_formulas ? 2 : 1;
299 int start_row, start_col, end_col, end_row;
301 GnmStyleRow sr, next_sr;
302 GnmStyle const **styles;
303 GnmBorder const **borders, **prev_vert;
304 GnmBorder const *none;
305 gpointer *sr_array_data;
307 int n, col, row;
308 double x, y, offset;
309 GnmRange view;
310 GSList *merged_active, *merged_active_seen,
311 *merged_used, *merged_unused, *ptr, **lag;
312 gboolean hide_grid;
314 g_return_if_fail (IS_SHEET (sheet));
315 g_return_if_fail (range != NULL);
316 g_return_if_fail (range->start.col <= range->end.col);
317 g_return_if_fail (range->start.row <= range->end.row);
318 g_return_if_fail (pinfo != NULL);
320 hide_grid = !pinfo->print_grid_lines;
321 none = hide_grid ? NULL : gnm_style_border_none ();
323 start_col = range->start.col;
324 start_row = range->start.row;
325 end_col = range->end.col;
326 end_row = range->end.row;
328 /* Skip any hidden cols/rows at the start */
329 for (; start_col <= end_col ; ++start_col) {
330 ColRowInfo const *ci = sheet_col_get_info (sheet, start_col);
331 if (ci->visible)
332 break;
334 for (; start_row <= end_row ; ++start_row) {
335 ri = sheet_row_get_info (sheet, start_row);
336 if (ri->visible)
337 break;
340 sheet_style_update_grid_color (sheet);
342 /* Get ordered list of merged regions */
343 merged_active = merged_active_seen = merged_used = NULL;
344 merged_unused = gnm_sheet_merge_get_overlap (sheet,
345 range_init (&view, start_col, start_row, end_col, end_row));
348 * allocate a single blob of memory for all 8 arrays of pointers.
349 * - 6 arrays of n GnmBorder const *
350 * - 2 arrays of n GnmStyle const *
352 * then alias the arrays for easy access so that array [col] is valid
353 * for all elements start_col-1 .. end_col+1 inclusive.
354 * Note that this means that in some cases array [-1] is legal.
356 n = end_col - start_col + 3; /* 1 before, 1 after, 1 fencepost */
357 sr_array_data = g_new (gpointer, n * 8);
358 style_row_init (&prev_vert, &sr, &next_sr, start_col, end_col,
359 sr_array_data, hide_grid);
361 /* load up the styles for the first row */
362 next_sr.row = sr.row = row = start_row;
363 sheet_style_get_row (sheet, &sr);
365 for (y = base_y;
366 row <= end_row;
367 row = sr.row = next_sr.row, ri = next_ri) {
368 /* Restore the set of ranges seen, but still active.
369 * Reinverting list to maintain the original order */
370 g_return_if_fail (merged_active == NULL);
372 while (merged_active_seen != NULL) {
373 GSList *tmp = merged_active_seen->next;
374 merged_active_seen->next = merged_active;
375 merged_active = merged_active_seen;
376 merged_active_seen = tmp;
377 MERGE_DEBUG (merged_active->data, " : seen -> active\n");
380 /* find the next visible row */
381 while (1) {
382 ++next_sr.row;
383 if (next_sr.row <= end_row) {
384 next_ri = sheet_row_get_info (sheet, next_sr.row);
385 if (next_ri->visible) {
386 sheet_style_get_row (sheet, &next_sr);
387 break;
389 } else {
390 for (col = start_col ; col <= end_col; ++col)
391 next_sr.vertical [col] =
392 next_sr.bottom [col] = none;
393 break;
397 /* it is safe to const_cast because only a non-default row
398 * will ever get flagged.
400 if (ri->needs_respan)
401 row_calc_spans ((ColRowInfo *)ri, row, sheet);
403 /* look for merges that start on this row, on the first painted row
404 * also check for merges that start above. */
405 view.start.row = row;
406 lag = &merged_unused;
407 for (ptr = merged_unused; ptr != NULL; ) {
408 GnmRange * const r = ptr->data;
410 if (r->start.row <= row) {
411 GSList *tmp = ptr;
412 ptr = *lag = tmp->next;
413 if (r->end.row < row) {
414 tmp->next = merged_used;
415 merged_used = tmp;
416 MERGE_DEBUG (r, " : unused -> used\n");
417 } else {
418 ColRowInfo const *ci =
419 sheet_col_get_info (sheet, r->start.col);
420 g_slist_free_1 (tmp);
421 merged_active = g_slist_insert_sorted (merged_active, r,
422 (GCompareFunc)merged_col_cmp);
423 MERGE_DEBUG (r, " : unused -> active\n");
425 if (ci->visible)
426 print_merged_range_gtk (context, sheet,
427 base_x, y, &view, r,
428 pinfo);
430 } else {
431 lag = &(ptr->next);
432 ptr = ptr->next;
436 for (col = start_col, x = base_x; col <= end_col ; col++) {
437 GnmStyle const *style;
438 CellSpanInfo const *span;
439 ColRowInfo const *ci = sheet_col_get_info (sheet, col);
440 ColRowInfo const *ri = sheet_row_get_info (sheet, row);
442 if (!ci->visible) {
443 if (merged_active != NULL) {
444 GnmRange const *r = merged_active->data;
445 if (r->end.col == col) {
446 ptr = merged_active;
447 merged_active = merged_active->next;
448 if (r->end.row <= row) {
449 ptr->next = merged_used;
450 merged_used = ptr;
451 MERGE_DEBUG (r, " : active2 -> used\n");
452 } else {
453 ptr->next = merged_active_seen;
454 merged_active_seen = ptr;
455 MERGE_DEBUG (r, " : active2 -> seen\n");
459 continue;
462 /* Skip any merged regions */
463 if (merged_active) {
464 GnmRange const *r = merged_active->data;
465 if (r->start.col <= col) {
466 gboolean clear_top, clear_bottom = TRUE;
467 int i, first = r->start.col;
468 int last = r->end.col;
470 x += sheet_col_get_distance_pts (sheet,
471 col, last+1);
472 col = last;
474 if (first < start_col) {
475 first = start_col;
476 sr.vertical [first] = NULL;
478 if (last > end_col) {
479 last = end_col;
480 sr.vertical [last+1] = NULL;
482 clear_top = (r->start.row != row);
484 ptr = merged_active;
485 merged_active = merged_active->next;
486 if (r->end.row <= row) {
487 clear_bottom = FALSE;
488 ptr->next = merged_used;
489 merged_used = ptr;
490 MERGE_DEBUG (r, " : active -> used\n");
491 } else {
492 ptr->next = merged_active_seen;
493 merged_active_seen = ptr;
494 MERGE_DEBUG (r, " : active -> seen\n");
497 /* Clear the borders */
498 for (i = first ; i <= last ; i++) {
499 if (clear_top)
500 sr.top [i] = NULL;
501 if (clear_bottom)
502 sr.bottom [i] = NULL;
503 if (i > first)
504 sr.vertical [i] = NULL;
506 continue;
510 if (dir < 0)
511 x -= ci->size_pts * hscale;
512 style = sr.styles [col];
513 print_cell_background_gtk (context, style, col, row, x, y,
514 ci->size_pts * hscale, ri->size_pts);
516 /* Is this part of a span?
517 * 1) There are cells allocated in the row
518 * (indicated by ri->spans != NULL)
519 * 2) Look in the rows hash table to see if
520 * there is a span descriptor.
522 if (NULL == ri->spans || NULL == (span = row_span_get (ri, col))) {
523 /* no need to draw blanks */
524 GnmCell const *cell = sheet_cell_get (sheet, col, row);
525 if (!gnm_cell_is_empty (cell))
526 print_cell_gtk (cell, context, x, y,
527 ci->size_pts * hscale,
528 ri->size_pts, -1., pinfo);
530 /* Only draw spaning cells after all the backgrounds
531 * that we are going to draw have been drawn. No need
532 * to draw the edit cell, or blanks.
534 } else if (col == span->right || col == end_col) {
535 GnmCell const *cell = span->cell;
536 int const start_span_col = span->left;
537 int const end_span_col = span->right;
538 double real_x = x;
539 ColRowInfo const *cell_col =
540 sheet_col_get_info (sheet, cell->pos.col);
541 double center_offset = cell_col->size_pts * hscale / 2;
542 double tmp_width = ci->size_pts * hscale;
544 if (col != cell->pos.col)
545 style = sheet_style_get (sheet,
546 cell->pos.col, row);
548 /* x, y are relative to this cell origin, but the cell
549 * might be using columns to the left (if it is set to right
550 * justify or center justify) compute the pixel difference
552 if (start_span_col != cell->pos.col)
553 center_offset += sheet_col_get_distance_pts (
554 sheet, start_span_col, cell->pos.col);
556 if (start_span_col != col) {
557 offset = sheet_col_get_distance_pts (
558 sheet, start_span_col, col);
559 tmp_width += offset;
560 if (dir > 0)
561 real_x -= offset;
562 sr.vertical [col] = NULL;
564 if (end_span_col != col) {
565 offset = sheet_col_get_distance_pts (
566 sheet, col+1, end_span_col + 1);
567 tmp_width += offset;
568 if (dir < 0)
569 real_x -= offset;
572 print_cell_gtk (cell, context,
573 real_x, y, tmp_width, ri->size_pts,
574 center_offset, pinfo);
575 } else if (col != span->left)
576 sr.vertical [col] = NULL;
578 if (dir > 0)
579 x += ci->size_pts * hscale;
581 gnm_style_borders_row_print_gtk (prev_vert, &sr,
582 context, base_x, y, y+ri->size_pts,
583 sheet, TRUE, dir);
585 /* In case there were hidden merges that trailed off the end */
586 while (merged_active != NULL) {
587 GnmRange const *r = merged_active->data;
588 ptr = merged_active;
589 merged_active = merged_active->next;
590 if (r->end.row <= row) {
591 ptr->next = merged_used;
592 merged_used = ptr;
593 MERGE_DEBUG (r, " : active3 -> used\n");
594 } else {
595 ptr->next = merged_active_seen;
596 merged_active_seen = ptr;
597 MERGE_DEBUG (r, " : active3 -> seen\n");
600 /* roll the pointers */
601 borders = prev_vert; prev_vert = sr.vertical;
602 sr.vertical = next_sr.vertical; next_sr.vertical = borders;
603 borders = sr.top; sr.top = sr.bottom;
604 sr.bottom = next_sr.top = next_sr.bottom; next_sr.bottom = borders;
605 styles = sr.styles; sr.styles = next_sr.styles; next_sr.styles = styles;
607 y += ri->size_pts;
609 gnm_style_borders_row_print_gtk (prev_vert, &sr,
610 context, base_x, y, y, sheet, FALSE, dir);
612 g_slist_free (merged_used); /* merges with bottom in view */
613 g_slist_free (merged_active_seen); /* merges with bottom the view */
614 g_slist_free (merged_unused); /* merges in hidden rows */
615 g_free (sr_array_data);
616 g_return_if_fail (merged_active == NULL);