Update Spanish translation
[gnumeric.git] / src / cell-draw.c
blob9ca0d560825bb1141af26b1e4b21f1053268ad44
1 /*
2 * cell-draw.c: Cell drawing on screen
4 * Author:
5 * Miguel de Icaza 1998, 1999 (miguel@kernel.org)
6 * Jody Goldberg 2000-2002 (jody@gnome.org)
7 * Morten Welinder 2003 (terra@gnome.org)
8 */
9 #include <gnumeric-config.h>
10 #include <gnumeric.h>
11 #include <cell-draw.h>
13 #include <style.h>
14 #include <cell.h>
15 #include <sheet.h>
16 #include <gnm-format.h>
17 #include <rendered-value.h>
18 #include <parse-util.h>
19 #include <sheet-merge.h>
20 #include <goffice/goffice.h>
22 #include <gdk/gdk.h>
23 #include <string.h>
24 #include <math.h>
26 static char const hashes[] =
27 "################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################";
29 static gboolean
30 cell_draw_simplify_cb (PangoAttribute *attribute,
31 gboolean *recalc_height)
33 if ((attribute->klass->type == PANGO_ATTR_RISE) ||
34 (attribute->klass->type == PANGO_ATTR_SCALE)) {
35 *recalc_height = TRUE;
36 return TRUE;
38 return (attribute->klass->type == PANGO_ATTR_SHAPE);
41 static void
42 cell_draw_simplify_attributes (GnmRenderedValue *rv)
44 PangoAttrList *pal = pango_layout_get_attributes (rv->layout);
45 gboolean recalc_height = FALSE;
46 pango_attr_list_unref
47 (pango_attr_list_filter
48 (pal, (PangoAttrFilterFunc) cell_draw_simplify_cb, &recalc_height));
49 if (recalc_height)
50 pango_layout_get_size (rv->layout, NULL,
51 &rv->layout_natural_height);
55 * G G
56 * r r
57 * i i
58 * d d
60 * Grid line a------+
61 * |mmmmmm|
62 * |m m|
63 * |mmmmmm|
64 * Grid line +------+
66 * 'm' == margin
67 * ' ' == space for contents
69 * @h_center: The number of pango units from x1 marking the logical center
70 * of the cell. NOTE This can be asymetric. Passing
71 * <= 0 will use width / 2
73 gboolean
74 cell_calc_layout (G_GNUC_UNUSED GnmCell const *cell, GnmRenderedValue *rv, int y_direction,
75 int width, int height, int h_center,
76 GOColor *res_color, gint *res_x, gint *res_y)
78 int text_base;
79 PangoLayout *layout;
80 int indent;
81 int hoffset;
82 int rect_x, rect_y;
83 gboolean was_drawn;
85 g_return_val_if_fail (rv != NULL, FALSE);
87 layout = rv->layout;
88 indent = (rv->indent_left + rv->indent_right) * PANGO_SCALE;
90 was_drawn = rv->drawn;
91 rv->drawn = TRUE;
93 if (width <= 0 || height <= 0)
94 return FALSE;
96 hoffset = rv->indent_left * PANGO_SCALE;
98 #if 0
99 g_print ("%s: w=%d h=%d\n", cell_name (cell), width, height);
100 #endif
102 /* This rectangle has the whole area used by this cell
103 * excluding the surrounding grid lines and margins */
104 rect_x = PANGO_SCALE * (1 + GNM_COL_MARGIN);
105 rect_y = PANGO_SCALE * y_direction * (1 + GNM_ROW_MARGIN);
107 /* if a number overflows, do special drawing */
108 if (rv->layout_natural_width > width - indent &&
109 rv->might_overflow &&
110 !rv->numeric_overflow) {
111 char const *text = pango_layout_get_text (layout);
112 size_t textlen = strlen (text);
113 #if 0
114 g_print ("nat=%d w=%d i=%d\n", rv->layout_natural_width, width, indent);
115 #endif
116 /* This assumes that two hash marks are wider than
117 the characters in the number. Probably ok. */
118 pango_layout_set_text (layout, hashes,
119 MIN (sizeof (hashes) - 1, 2 * textlen));
120 cell_draw_simplify_attributes (rv);
121 rv->numeric_overflow = TRUE;
122 rv->variable_width = TRUE;
123 rv->hfilled = TRUE;
126 /* Special handling of error dates. */
127 if (!was_drawn && rv->numeric_overflow) {
128 pango_layout_set_text (layout, hashes, -1);
129 cell_draw_simplify_attributes (rv);
130 rv->variable_width = TRUE;
131 rv->hfilled = TRUE;
134 if (rv->rotation && !rv->noborders) {
135 GnmRenderedRotatedValue const *rrv = (GnmRenderedRotatedValue *)rv;
136 if (rrv->sin_a_neg) {
137 hoffset += (width - indent) - rv->layout_natural_width;
139 } else if (!rv->rotation && rv->wrap_text
140 && (rv->effective_halign != GNM_HALIGN_FILL)) {
141 int wanted_width = MAX (0, width - indent);
142 if (wanted_width != pango_layout_get_width (layout)) {
143 pango_layout_set_wrap (layout, PANGO_WRAP_WORD_CHAR);
144 pango_layout_set_width (layout, wanted_width);
145 gnm_rendered_value_remeasure (rv);
147 } else {
148 switch (rv->effective_halign) {
149 case GNM_HALIGN_RIGHT:
150 hoffset += (width - indent) - rv->layout_natural_width;
151 break;
152 case GNM_HALIGN_DISTRIBUTED:
153 case GNM_HALIGN_CENTER:
154 if (h_center == -1)
155 h_center = width / 2;
156 hoffset += h_center + (-indent - rv->layout_natural_width) / 2;
157 break;
158 case GNM_HALIGN_CENTER_ACROSS_SELECTION:
159 hoffset += ((width - indent) - rv->layout_natural_width) / 2;
160 break;
161 case GNM_HALIGN_FILL: {
162 PangoDirection dir = PANGO_DIRECTION_LTR;
163 if (!rv->hfilled &&
164 rv->layout_natural_width > 0 &&
165 width - indent >= 2 * rv->layout_natural_width) {
167 * We ignore kerning between copies in calculating the number
168 * of copies needed. Instead we toss in a zero-width-space.
170 int copies = (width - indent) / rv->layout_natural_width;
171 char const *copy1 = pango_layout_get_text (layout);
172 size_t len1 = strlen (copy1);
173 GString *multi = g_string_sized_new ((len1 + 6) * copies);
174 int i;
175 PangoAttrList *attr = pango_layout_get_attributes (layout);
177 dir = pango_find_base_dir (copy1, -1);
178 for (i = 0; i < copies; i++) {
179 if (i)
180 g_string_append_unichar (multi, UNICODE_ZERO_WIDTH_SPACE_C);
181 g_string_append_len (multi, copy1, len1);
183 pango_layout_set_text (layout, multi->str, multi->len);
184 g_string_free (multi, TRUE);
186 if (attr != NULL && !go_pango_attr_list_is_empty (attr)) {
187 PangoAttrList *attr_c = pango_attr_list_copy (attr);
188 size_t len = len1 + UNICODE_ZERO_WIDTH_SPACE_C_UTF8_LENGTH;
189 for (i = 1; i < copies;
190 i++, len += len1 + UNICODE_ZERO_WIDTH_SPACE_C_UTF8_LENGTH)
191 pango_attr_list_splice (attr, attr_c, len, len1);
192 pango_attr_list_unref (attr_c);
194 } else
195 dir = pango_find_base_dir (pango_layout_get_text (layout), -1);
196 /* right align if text is RTL */
197 if (dir == PANGO_DIRECTION_RTL) {
198 PangoRectangle r;
199 pango_layout_get_extents (layout, NULL, &r);
200 hoffset += (width - indent) - r.width;
203 rv->hfilled = TRUE;
204 break;
207 #ifndef DEBUG_SWITCH_ENUM
208 default:
209 #endif
210 case GNM_HALIGN_GENERAL:
211 g_warning ("Unhandled horizontal alignment.");
212 case GNM_HALIGN_LEFT:
213 break;
217 /* Note that Excel always truncates the cell content only at the */
218 /* bottom even if the request is to align it at the bottom or to */
219 /* center it. We do the same for compatibilities sake. Also see */
220 /* bug #662368 */
221 switch (rv->effective_valign) {
222 #ifndef DEBUG_SWITCH_ENUM
223 default:
224 g_warning ("Unhandled vertical alignment.");
225 /* Fall through. */
226 #endif
227 case GNM_VALIGN_TOP:
228 text_base = rect_y;
229 break;
231 case GNM_VALIGN_BOTTOM: {
232 int dh = height - rv->layout_natural_height;
233 if (rv->rotation == 0 && dh < 0)
234 dh = 0;
235 text_base = rect_y + y_direction * dh;
236 break;
239 case GNM_VALIGN_DISTRIBUTED: /* dunno what this does yet */
240 case GNM_VALIGN_CENTER: {
241 int dh = (height - rv->layout_natural_height) / 2;
242 if (rv->rotation == 0 && dh < 0)
243 dh = 0;
244 text_base = rect_y + y_direction * dh;
245 break;
248 case GNM_VALIGN_JUSTIFY:
249 text_base = rect_y;
250 if (!rv->vfilled && height > rv->layout_natural_height) {
251 int line_count = pango_layout_get_line_count (layout);
252 if (line_count > 1) {
253 int spacing = (height - rv->layout_natural_height) /
254 (line_count - 1);
255 pango_layout_set_spacing (layout, spacing);
256 gnm_rendered_value_remeasure (rv);
259 rv->vfilled = TRUE;
260 break;
263 #if 0
264 if (rv->rotation)
265 g_print ("hoffset=%d, text_base=%d, n_width=%d, n_height=%d\n",
266 hoffset, text_base,
267 rv->layout_natural_width, rv->layout_natural_height);
268 #endif
270 *res_color = gnm_rendered_value_get_color (rv);
271 *res_x = rect_x + hoffset;
272 *res_y = text_base;
274 return TRUE;
278 * This finishes a layout by pretending to draw it. The effect is to
279 * handler numerical overflow, filling, etc.
280 * (Doesn't currently handle vertical fill.)
282 void
283 cell_finish_layout (GnmCell *cell, GnmRenderedValue *rv,
284 int col_width,
285 gboolean inhibit_overflow)
287 gint dummy_x, dummy_y;
288 GOColor dummy_fore_color;
289 int dummy_h_center = -1; /* Affects position only. */
290 int dummy_height = 1; /* Unhandled. */
291 gboolean might_overflow;
292 GnmRenderedValue *cell_rv;
294 cell_rv = gnm_cell_get_rendered_value (cell);
296 if (!rv)
297 rv = cell_rv;
299 if (rv->drawn)
300 return;
302 if (rv->variable_width && rv == cell_rv &&
303 !go_format_is_general (gnm_cell_get_format (cell))) {
305 * We get here when entering a new value in a cell
306 * with a format that has a filler, for example
307 * one of the standard accounting formats. We need
308 * to rerender such that the filler gets a chance
309 * to expand.
311 rv = gnm_cell_render_value (cell, TRUE);
314 might_overflow = rv->might_overflow;
315 if (inhibit_overflow) rv->might_overflow = FALSE;
316 cell_calc_layout (cell, rv, -1, col_width * PANGO_SCALE,
317 dummy_height, dummy_h_center, &dummy_fore_color,
318 &dummy_x, &dummy_y);
319 rv->might_overflow = might_overflow;
323 static void
324 cell_draw_extension_mark_bottom (cairo_t *cr, int x1, int y1, int height, int h_center)
326 cairo_set_source_rgba (cr, 1, 0, 0, 0.7);
327 cairo_new_path (cr);
328 cairo_move_to (cr, x1 + h_center, y1 + height);
329 cairo_rel_line_to (cr, -3, -3);
330 cairo_rel_line_to (cr, 6, 0);
331 cairo_close_path (cr);
332 cairo_fill (cr);
335 static void
336 cell_draw_extension_mark_left (cairo_t *cr, int x1, int y1, int height)
338 cairo_set_source_rgba (cr, 1, 0, 0, 0.7);
339 cairo_new_path (cr);
340 cairo_move_to (cr, x1, y1 + height/2);
341 cairo_rel_line_to (cr, 3, -3);
342 cairo_rel_line_to (cr, 0, 6);
343 cairo_close_path (cr);
344 cairo_fill (cr);
347 static void
348 cell_draw_extension_mark_right (cairo_t *cr, int x1, int y1, int width, int height)
350 cairo_set_source_rgba (cr, 1, 0, 0, 0.7);
351 cairo_new_path (cr);
352 cairo_move_to (cr, x1 + width, y1 + height/2);
353 cairo_rel_line_to (cr, -3, -3);
354 cairo_rel_line_to (cr, 0, 6);
355 cairo_close_path (cr);
356 cairo_fill (cr);
361 static void
362 cell_draw_h_extension_markers (cairo_t *cr, GnmRenderedValue *rv,
363 int x1, int y1,
364 int width, int height)
366 switch (rv->effective_halign) {
367 case GNM_HALIGN_GENERAL:
368 case GNM_HALIGN_LEFT:
369 cell_draw_extension_mark_right (cr, x1, y1, width, height);
370 break;
371 case GNM_HALIGN_RIGHT:
372 cell_draw_extension_mark_left (cr, x1, y1, height);
373 break;
374 case GNM_HALIGN_DISTRIBUTED:
375 case GNM_HALIGN_CENTER:
376 case GNM_HALIGN_CENTER_ACROSS_SELECTION:
377 cell_draw_extension_mark_right (cr, x1, y1, width, height);
378 cell_draw_extension_mark_left (cr, x1, y1, height);
379 break;
380 case GNM_HALIGN_FILL:
381 default:
382 break;
386 static void
387 cell_draw_v_extension_markers (cairo_t *cr,
388 int x1, int y1,
389 int width, int height,
390 int h_center)
392 if (h_center == -1)
393 h_center = width / 2;
394 cell_draw_extension_mark_bottom (cr, x1, y1, height, h_center);
398 * cell_draw:
399 * @cell: #GnmCell const
400 * @cr: #cairo_t
401 * @x:
402 * @y:
403 * @width: including margins and leading grid line
404 * @height: including margins and leading grid line
405 * @h_center:
406 * @show_extension_markers:
408 void
409 cell_draw (GnmCell const *cell, cairo_t *cr,
410 int x1, int y1, int width, int height, int h_center,
411 gboolean show_extension_markers)
413 GOColor fore_color;
414 gint x;
415 gint y;
416 GnmRenderedValue *rv;
418 /* Get the sizes exclusive of margins and grids */
419 /* Note: +1 because size_pixels includes leading gridline. */
420 height -= GNM_ROW_MARGIN + GNM_ROW_MARGIN + 1;
421 width -= GNM_COL_MARGIN + GNM_COL_MARGIN + 1;
423 if (h_center > GNM_COL_MARGIN)
424 h_center = h_center - GNM_COL_MARGIN - 1 + (h_center % 2);
426 rv = gnm_cell_fetch_rendered_value (cell, TRUE);
428 if (cell_calc_layout (cell, rv, +1,
429 width * PANGO_SCALE,
430 height * PANGO_SCALE,
431 h_center == -1 ? -1 : (h_center * PANGO_SCALE),
432 &fore_color, &x, &y)) {
434 cairo_save (cr);
437 * HACK -- do not clip rotated cells. This gives an
438 * approximation to the right effect. (The right way
439 * would be to create a proper cellspan type.)
441 if (!rv->rotation) {
442 cairo_new_path (cr);
443 /* +1 to get past left grid-line. */
444 cairo_rectangle (cr, x1 + 1 + GNM_COL_MARGIN,
445 y1 + 1 + GNM_ROW_MARGIN,
446 width, height);
448 cairo_clip (cr);
451 /* See http://bugzilla.gnome.org/show_bug.cgi?id=105322 */
452 cairo_set_source_rgba (cr, GO_COLOR_TO_CAIRO (fore_color));
454 if (rv->rotation) {
455 GnmRenderedRotatedValue *rrv = (GnmRenderedRotatedValue *)rv;
456 struct GnmRenderedRotatedValueInfo const *li = rrv->lines;
457 GSList *lines;
459 for (lines = pango_layout_get_lines (rv->layout);
460 lines;
461 lines = lines->next, li++) {
462 cairo_save (cr);
463 cairo_move_to (cr, x1 + PANGO_PIXELS (x + li->dx), y1 + PANGO_PIXELS (y + li->dy));
464 cairo_rotate (cr, rv->rotation * (-M_PI / 180));
465 pango_cairo_show_layout_line (cr, lines->data);
466 cairo_restore (cr);
468 } else {
469 cairo_save (cr);
470 cairo_translate (cr, x1 + PANGO_PIXELS (x), y1 + PANGO_PIXELS (y));
471 pango_cairo_show_layout (cr, rv->layout);
472 cairo_restore (cr);
474 if (show_extension_markers &&
475 width < PANGO_PIXELS (rv->layout_natural_width)) {
476 cairo_save (cr);
477 cell_draw_h_extension_markers
478 (cr, rv,
479 x1 + 1 + GNM_COL_MARGIN,
480 y1 + 1 + GNM_ROW_MARGIN,
481 width, height);
482 cairo_restore (cr);
485 if (show_extension_markers &&
486 height < PANGO_PIXELS (rv->layout_natural_height)) {
487 cairo_save (cr);
488 cell_draw_v_extension_markers
489 (cr, x1 + 1 + GNM_COL_MARGIN,
490 y1 + 1 + GNM_ROW_MARGIN,
491 width, height, h_center);
492 cairo_restore (cr);
495 cairo_restore (cr);