GUI: Move .ui files from goffice resources to glib resources
[gnumeric.git] / src / print-cell.c
blob02775b4aac9bfaebd50fac7fde7a77980b27b39c
1 /* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3 * print-cell.c: Printing of cell regions and cells.
5 * Author:
6 * Jody Goldberg 2000-2006 (jody@gnome.org)
7 * Miguel de Icaza 1999 (miguel@kernel.org)
8 * Andreas J. Guelzow 2007 (aguelzow@pyrshep.ca)
9 * Copyright (C) 2007-2009 Morten Welinder (terra@gnome.org)
11 #include <gnumeric-config.h>
12 #include "gnumeric.h"
13 #include "print-cell.h"
15 #include "application.h"
16 #include "dependent.h"
17 #include "gnm-format.h"
18 #include "style-color.h"
19 #include "style-font.h"
20 #include "parse-util.h"
21 #include "cell.h"
22 #include "value.h"
23 #include "style-border.h"
24 #include "style-conditions.h"
25 #include "pattern.h"
26 #include "cellspan.h"
27 #include "ranges.h"
28 #include "sheet.h"
29 #include "sheet-style.h"
30 #include "sheet-merge.h"
31 #include "rendered-value.h"
32 #include "cell-draw.h"
33 #include "print-info.h"
35 #include <string.h>
36 #include <locale.h>
38 #if 0
39 #define MERGE_DEBUG(range, str) do { range_dump (range, str); } while (0)
40 #else
41 #define MERGE_DEBUG(range, str)
42 #endif
45 * base_[xy] : Coordinates of the upper left corner of the cell.
46 * INCLUSIVE of the near grid line
48 * /--- (x1, y1)
49 * v
50 * g------\
51 * | |
52 * \------/
55 static void
56 print_cell_gtk (GnmCell const *cell,
57 cairo_t *context,
58 double x1, double y1,
59 double width, double height, double h_center,
60 GnmPrintInformation const *pinfo)
62 GnmRenderedValue *rv, *rv100 = NULL;
63 GOColor fore_color;
64 gint x, y;
65 Sheet *sheet = cell->base.sheet;
66 double const scale_h = 72. / gnm_app_display_dpi_get (TRUE);
67 double const scale_v = 72. / gnm_app_display_dpi_get (FALSE);
69 gboolean cell_shows_error;
71 if (cell->base.flags & GNM_CELL_HAS_NEW_EXPR)
72 gnm_cell_eval ((GnmCell *)cell);
74 cell_shows_error = (gnm_cell_is_error (cell) != NULL)
75 && !(gnm_cell_has_expr (cell) && sheet->display_formulas);
77 if (cell_shows_error && pinfo->error_display ==
78 GNM_PRINT_ERRORS_AS_BLANK)
79 return;
81 /* Get the sizes exclusive of margins and grids */
82 /* Note: +1 because size_pixels includes leading gridline. */
83 height -= GNM_ROW_MARGIN + GNM_ROW_MARGIN + 1;
84 width -= GNM_COL_MARGIN + GNM_COL_MARGIN + 1;
86 rv = gnm_cell_fetch_rendered_value (cell, TRUE);
88 /* Create a rendered value for printing */
89 if (cell_shows_error &&
90 (pinfo->error_display == GNM_PRINT_ERRORS_AS_NA
91 || pinfo->error_display == GNM_PRINT_ERRORS_AS_DASHES)) {
92 GnmCell *t_cell = (GnmCell *)cell;
93 GnmValue *old = t_cell->value;
94 if (pinfo->error_display == GNM_PRINT_ERRORS_AS_NA)
95 t_cell->value = value_new_error_NA (NULL);
96 else
97 t_cell->value = value_new_error
98 (NULL,
99 /* U+2014 U+200A U+2014 */
100 "\342\200\224\342\200\212\342\200\224");
101 rv100 = gnm_rendered_value_new (t_cell,
102 pango_layout_get_context (rv->layout),
103 rv->variable_width,
104 1.0);
105 rv = rv100;
106 value_release (t_cell->value);
107 t_cell->value = old;
108 } else if (sheet->last_zoom_factor_used != 1) {
110 * We're zoomed and we don't want printing to reflect that.
113 rv100 = gnm_rendered_value_new ((GnmCell *)cell,
114 pango_layout_get_context (rv->layout),
115 rv->variable_width,
116 1.0);
117 rv = rv100;
120 /* Make sure we don't get overflow in print unless we had it in
121 display. */
122 rv->might_overflow = rv->numeric_overflow;
124 if (cell_calc_layout (cell, rv, -1,
125 (int)(width * PANGO_SCALE / scale_h),
126 (int)(height * PANGO_SCALE / scale_v),
127 (int)h_center == -1 ? -1 : (int)(h_center * PANGO_SCALE),
128 &fore_color, &x, &y)) {
130 /* Clip the printed rectangle */
131 cairo_save (context);
132 #ifndef G_OS_WIN32
133 if (!rv->rotation) {
134 /* We do not clip rotated cells. */
135 cairo_new_path (context);
136 cairo_rectangle (context, x1 + GNM_COL_MARGIN, y1 + GNM_ROW_MARGIN,
137 width + 1, height + 1);
138 cairo_clip (context);
140 #endif
141 /* Set the font colour */
142 cairo_set_source_rgba (context,
143 GO_COLOR_TO_CAIRO (fore_color));
145 cairo_translate (context, x1+0.5, y1);
147 if (rv->rotation) {
148 GnmRenderedRotatedValue *rrv = (GnmRenderedRotatedValue *)rv;
149 struct GnmRenderedRotatedValueInfo const *li = rrv->lines;
150 GSList *lines;
152 cairo_scale (context, scale_h, scale_v);
153 cairo_move_to (context, 0.,0.);
154 for (lines = pango_layout_get_lines (rv->layout);
155 lines;
156 lines = lines->next, li++) {
157 cairo_save (context);
158 cairo_move_to (context,
159 PANGO_PIXELS (x + li->dx),
160 PANGO_PIXELS (- y + li->dy));
161 cairo_rotate (context, rv->rotation * (-M_PI / 180));
162 pango_cairo_show_layout_line (context, lines->data);
163 cairo_restore (context);
167 } else {
168 cairo_scale (context, scale_h, scale_v);
169 cairo_move_to (context, x / (double)PANGO_SCALE , - y / (double)PANGO_SCALE);
170 pango_cairo_show_layout (context, rv->layout);
172 cairo_restore(context);
175 if (rv100)
176 gnm_rendered_value_destroy (rv100);
179 static void
180 print_rectangle_gtk (cairo_t *context,
181 double x, double y, double w, double h)
183 cairo_new_path (context);
184 cairo_rectangle (context, x, y, w, h);
185 cairo_fill (context);
188 static void
189 print_cell_background_gtk (cairo_t *context,
190 GnmStyle const *style,
191 G_GNUC_UNUSED int col, G_GNUC_UNUSED int row,
192 double x, double y, double w, double h)
194 if (gnm_pattern_background_set (style, context, FALSE, NULL))
195 /* Remember api excludes the far pixels */
196 print_rectangle_gtk (context, x, y, w+0.2, h+0.2);
197 gnm_style_border_print_diag_gtk (style, context, x, y, x+w, y+h);
202 * print_merged_range:
204 * Handle the special drawing requirements for a 'merged cell'.
205 * First draw the entire range (clipped to the visible region) then redraw any
206 * segments that are selected.
208 static void
209 print_merged_range_gtk (cairo_t *context,
210 Sheet const *sheet,
211 double start_x, double start_y,
212 GnmRange const *view, GnmRange const *range,
213 GnmPrintInformation const *pinfo)
215 double l, r, t, b;
216 int last;
217 GnmCell const *cell = sheet_cell_get (sheet, range->start.col, range->start.row);
218 int const dir = sheet->text_is_rtl ? -1 : 1;
219 GnmStyleConditions *conds;
221 /* load style from corner which may not be visible */
222 GnmStyle const *style = sheet_style_get (sheet, range->start.col, range->start.row);
224 l = r = start_x;
225 if (view->start.col < range->start.col)
226 l += dir * sheet_col_get_distance_pts (sheet,
227 view->start.col, range->start.col);
228 if (range->end.col <= (last = view->end.col))
229 last = range->end.col;
230 r += dir * sheet_col_get_distance_pts (sheet, view->start.col, last+1);
232 t = b = start_y;
233 if (view->start.row < range->start.row)
234 t -= sheet_row_get_distance_pts (sheet,
235 view->start.row, range->start.row);
236 if (range->end.row <= (last = view->end.row))
237 last = range->end.row;
238 b += sheet_row_get_distance_pts (sheet, view->start.row, last+1);
240 if (l == r || t == b)
241 return;
243 conds = gnm_style_get_conditions (style);
244 if (style) {
245 GnmEvalPos ep;
246 int res;
247 eval_pos_init (&ep, (Sheet *)sheet, range->start.col, range->start.row);
248 if ((res = gnm_style_conditions_eval (conds, &ep)) >= 0)
249 style = gnm_style_get_cond_style (style, res);
252 if (gnm_pattern_background_set (style, context, FALSE, NULL))
253 print_rectangle_gtk (context, l, t, r-l+0.2, b-t+0.2);
255 if (range->start.col < view->start.col)
256 l -= dir * sheet_col_get_distance_pts (sheet,
257 range->start.col, view->start.col);
258 if (view->end.col < range->end.col)
259 r += dir * sheet_col_get_distance_pts (sheet,
260 view->end.col+1, range->end.col+1);
261 if (range->start.row < view->start.row)
262 t -= sheet_row_get_distance_pts (sheet,
263 range->start.row, view->start.row);
264 if (view->end.row < range->end.row)
265 b += sheet_row_get_distance_pts (sheet,
266 view->end.row+1, range->end.row+1);
268 if (cell != NULL) {
269 ColRowInfo *ri = sheet_row_get (sheet, range->start.row);
271 if (ri->needs_respan)
272 row_calc_spans (ri, cell->pos.row, sheet);
274 if (sheet->text_is_rtl)
275 print_cell_gtk (cell, context,
276 r, t, l - r, b - t, -1., pinfo);
277 else
278 print_cell_gtk (cell, context,
279 l, t, r - l, b - t, -1., pinfo);
281 gnm_style_border_print_diag_gtk (style, context, l, t, r, b);
284 static gint
285 merged_col_cmp (GnmRange const *a, GnmRange const *b)
287 return a->start.col - b->start.col;
291 void
292 gnm_gtk_print_cell_range (cairo_t *context,
293 Sheet const *sheet, GnmRange *range,
294 double base_x, double base_y,
295 GnmPrintInformation const *pinfo)
297 ColRowInfo const *ri = NULL, *next_ri = NULL;
298 int const dir = sheet->text_is_rtl ? -1 : 1;
299 double const hscale = sheet->display_formulas ? 2 : 1;
300 int start_row, start_col, end_col, end_row;
302 GnmStyleRow sr, next_sr;
303 GnmStyle const **styles;
304 GnmBorder const **borders, **prev_vert;
305 GnmBorder const *none;
306 gpointer *sr_array_data;
308 int n, col, row;
309 double x, y, offset;
310 GnmRange view;
311 GSList *merged_active, *merged_active_seen,
312 *merged_used, *merged_unused, *ptr, **lag;
313 gboolean hide_grid;
315 g_return_if_fail (IS_SHEET (sheet));
316 g_return_if_fail (range != NULL);
317 g_return_if_fail (range->start.col <= range->end.col);
318 g_return_if_fail (range->start.row <= range->end.row);
319 g_return_if_fail (pinfo != NULL);
321 hide_grid = !pinfo->print_grid_lines;
322 none = hide_grid ? NULL : gnm_style_border_none ();
324 start_col = range->start.col;
325 start_row = range->start.row;
326 end_col = range->end.col;
327 end_row = range->end.row;
329 /* Skip any hidden cols/rows at the start */
330 for (; start_col <= end_col ; ++start_col) {
331 ColRowInfo const *ci = sheet_col_get_info (sheet, start_col);
332 if (ci->visible)
333 break;
335 for (; start_row <= end_row ; ++start_row) {
336 ri = sheet_row_get_info (sheet, start_row);
337 if (ri->visible)
338 break;
341 sheet_style_update_grid_color (sheet);
343 /* Get ordered list of merged regions */
344 merged_active = merged_active_seen = merged_used = NULL;
345 merged_unused = gnm_sheet_merge_get_overlap (sheet,
346 range_init (&view, start_col, start_row, end_col, end_row));
349 * allocate a single blob of memory for all 8 arrays of pointers.
350 * - 6 arrays of n GnmBorder const *
351 * - 2 arrays of n GnmStyle const *
353 * then alias the arrays for easy access so that array [col] is valid
354 * for all elements start_col-1 .. end_col+1 inclusive.
355 * Note that this means that in some cases array [-1] is legal.
357 n = end_col - start_col + 3; /* 1 before, 1 after, 1 fencepost */
358 sr_array_data = g_new (gpointer, n * 8);
359 style_row_init (&prev_vert, &sr, &next_sr, start_col, end_col,
360 sr_array_data, hide_grid);
362 /* load up the styles for the first row */
363 next_sr.row = sr.row = row = start_row;
364 sheet_style_get_row (sheet, &sr);
366 for (y = base_y;
367 row <= end_row;
368 row = sr.row = next_sr.row, ri = next_ri) {
369 /* Restore the set of ranges seen, but still active.
370 * Reinverting list to maintain the original order */
371 g_return_if_fail (merged_active == NULL);
373 while (merged_active_seen != NULL) {
374 GSList *tmp = merged_active_seen->next;
375 merged_active_seen->next = merged_active;
376 merged_active = merged_active_seen;
377 merged_active_seen = tmp;
378 MERGE_DEBUG (merged_active->data, " : seen -> active\n");
381 /* find the next visible row */
382 while (1) {
383 ++next_sr.row;
384 if (next_sr.row <= end_row) {
385 next_ri = sheet_row_get_info (sheet, next_sr.row);
386 if (next_ri->visible) {
387 sheet_style_get_row (sheet, &next_sr);
388 break;
390 } else {
391 for (col = start_col ; col <= end_col; ++col)
392 next_sr.vertical [col] =
393 next_sr.bottom [col] = none;
394 break;
398 /* it is safe to const_cast because only a non-default row
399 * will ever get flagged.
401 if (ri->needs_respan)
402 row_calc_spans ((ColRowInfo *)ri, row, sheet);
404 /* look for merges that start on this row, on the first painted row
405 * also check for merges that start above. */
406 view.start.row = row;
407 lag = &merged_unused;
408 for (ptr = merged_unused; ptr != NULL; ) {
409 GnmRange * const r = ptr->data;
411 if (r->start.row <= row) {
412 GSList *tmp = ptr;
413 ptr = *lag = tmp->next;
414 if (r->end.row < row) {
415 tmp->next = merged_used;
416 merged_used = tmp;
417 MERGE_DEBUG (r, " : unused -> used\n");
418 } else {
419 ColRowInfo const *ci =
420 sheet_col_get_info (sheet, r->start.col);
421 g_slist_free_1 (tmp);
422 merged_active = g_slist_insert_sorted (merged_active, r,
423 (GCompareFunc)merged_col_cmp);
424 MERGE_DEBUG (r, " : unused -> active\n");
426 if (ci->visible)
427 print_merged_range_gtk (context, sheet,
428 base_x, y, &view, r,
429 pinfo);
431 } else {
432 lag = &(ptr->next);
433 ptr = ptr->next;
437 for (col = start_col, x = base_x; col <= end_col ; col++) {
438 GnmStyle const *style;
439 CellSpanInfo const *span;
440 ColRowInfo const *ci = sheet_col_get_info (sheet, col);
441 ColRowInfo const *ri = sheet_row_get_info (sheet, row);
443 if (!ci->visible) {
444 if (merged_active != NULL) {
445 GnmRange const *r = merged_active->data;
446 if (r->end.col == col) {
447 ptr = merged_active;
448 merged_active = merged_active->next;
449 if (r->end.row <= row) {
450 ptr->next = merged_used;
451 merged_used = ptr;
452 MERGE_DEBUG (r, " : active2 -> used\n");
453 } else {
454 ptr->next = merged_active_seen;
455 merged_active_seen = ptr;
456 MERGE_DEBUG (r, " : active2 -> seen\n");
460 continue;
463 /* Skip any merged regions */
464 if (merged_active) {
465 GnmRange const *r = merged_active->data;
466 if (r->start.col <= col) {
467 gboolean clear_top, clear_bottom = TRUE;
468 int i, first = r->start.col;
469 int last = r->end.col;
471 x += sheet_col_get_distance_pts (sheet,
472 col, last+1);
473 col = last;
475 if (first < start_col) {
476 first = start_col;
477 sr.vertical [first] = NULL;
479 if (last > end_col) {
480 last = end_col;
481 sr.vertical [last+1] = NULL;
483 clear_top = (r->start.row != row);
485 ptr = merged_active;
486 merged_active = merged_active->next;
487 if (r->end.row <= row) {
488 clear_bottom = FALSE;
489 ptr->next = merged_used;
490 merged_used = ptr;
491 MERGE_DEBUG (r, " : active -> used\n");
492 } else {
493 ptr->next = merged_active_seen;
494 merged_active_seen = ptr;
495 MERGE_DEBUG (r, " : active -> seen\n");
498 /* Clear the borders */
499 for (i = first ; i <= last ; i++) {
500 if (clear_top)
501 sr.top [i] = NULL;
502 if (clear_bottom)
503 sr.bottom [i] = NULL;
504 if (i > first)
505 sr.vertical [i] = NULL;
507 continue;
511 if (dir < 0)
512 x -= ci->size_pts * hscale;
513 style = sr.styles [col];
514 print_cell_background_gtk (context, style, col, row, x, y,
515 ci->size_pts * hscale, ri->size_pts);
517 /* Is this part of a span?
518 * 1) There are cells allocated in the row
519 * (indicated by ri->spans != NULL)
520 * 2) Look in the rows hash table to see if
521 * there is a span descriptor.
523 if (NULL == ri->spans || NULL == (span = row_span_get (ri, col))) {
524 /* no need to draw blanks */
525 GnmCell const *cell = sheet_cell_get (sheet, col, row);
526 if (!gnm_cell_is_empty (cell))
527 print_cell_gtk (cell, context, x, y,
528 ci->size_pts * hscale,
529 ri->size_pts, -1., pinfo);
531 /* Only draw spaning cells after all the backgrounds
532 * that we are going to draw have been drawn. No need
533 * to draw the edit cell, or blanks.
535 } else if (col == span->right || col == end_col) {
536 GnmCell const *cell = span->cell;
537 int const start_span_col = span->left;
538 int const end_span_col = span->right;
539 double real_x = x;
540 ColRowInfo const *cell_col =
541 sheet_col_get_info (sheet, cell->pos.col);
542 double center_offset = cell_col->size_pts * hscale / 2;
543 double tmp_width = ci->size_pts * hscale;
545 if (col != cell->pos.col)
546 style = sheet_style_get (sheet,
547 cell->pos.col, row);
549 /* x, y are relative to this cell origin, but the cell
550 * might be using columns to the left (if it is set to right
551 * justify or center justify) compute the pixel difference
553 if (start_span_col != cell->pos.col)
554 center_offset += sheet_col_get_distance_pts (
555 sheet, start_span_col, cell->pos.col);
557 if (start_span_col != col) {
558 offset = sheet_col_get_distance_pts (
559 sheet, start_span_col, col);
560 tmp_width += offset;
561 if (dir > 0)
562 real_x -= offset;
563 sr.vertical [col] = NULL;
565 if (end_span_col != col) {
566 offset = sheet_col_get_distance_pts (
567 sheet, col+1, end_span_col + 1);
568 tmp_width += offset;
569 if (dir < 0)
570 real_x -= offset;
573 print_cell_gtk (cell, context,
574 real_x, y, tmp_width, ri->size_pts,
575 center_offset, pinfo);
576 } else if (col != span->left)
577 sr.vertical [col] = NULL;
579 if (dir > 0)
580 x += ci->size_pts * hscale;
582 gnm_style_borders_row_print_gtk (prev_vert, &sr,
583 context, base_x, y, y+ri->size_pts,
584 sheet, TRUE, dir);
586 /* In case there were hidden merges that trailed off the end */
587 while (merged_active != NULL) {
588 GnmRange const *r = merged_active->data;
589 ptr = merged_active;
590 merged_active = merged_active->next;
591 if (r->end.row <= row) {
592 ptr->next = merged_used;
593 merged_used = ptr;
594 MERGE_DEBUG (r, " : active3 -> used\n");
595 } else {
596 ptr->next = merged_active_seen;
597 merged_active_seen = ptr;
598 MERGE_DEBUG (r, " : active3 -> seen\n");
601 /* roll the pointers */
602 borders = prev_vert; prev_vert = sr.vertical;
603 sr.vertical = next_sr.vertical; next_sr.vertical = borders;
604 borders = sr.top; sr.top = sr.bottom;
605 sr.bottom = next_sr.top = next_sr.bottom; next_sr.bottom = borders;
606 styles = sr.styles; sr.styles = next_sr.styles; next_sr.styles = styles;
608 y += ri->size_pts;
610 gnm_style_borders_row_print_gtk (prev_vert, &sr,
611 context, base_x, y, y, sheet, FALSE, dir);
613 g_slist_free (merged_used); /* merges with bottom in view */
614 g_slist_free (merged_active_seen); /* merges with bottom the view */
615 g_slist_free (merged_unused); /* merges in hidden rows */
616 g_free (sr_array_data);
617 g_return_if_fail (merged_active == NULL);