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