1 /* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
3 * cell-draw.c: Cell drawing on screen
6 * Miguel de Icaza 1998, 1999 (miguel@kernel.org)
7 * Jody Goldberg 2000-2002 (jody@gnome.org)
8 * Morten Welinder 2003 (terra@gnome.org)
10 #include <gnumeric-config.h>
12 #include "cell-draw.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>
27 static char const hashes
[] =
28 "################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################";
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
;
39 return (attribute
->klass
->type
== PANGO_ATTR_SHAPE
);
43 cell_draw_simplify_attributes (GnmRenderedValue
*rv
)
45 PangoAttrList
*pal
= pango_layout_get_attributes (rv
->layout
);
46 gboolean recalc_height
= FALSE
;
48 (pango_attr_list_filter
49 (pal
, (PangoAttrFilterFunc
) cell_draw_simplify_cb
, &recalc_height
));
51 pango_layout_get_size (rv
->layout
, NULL
,
52 &rv
->layout_natural_height
);
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
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
)
86 g_return_val_if_fail (rv
!= NULL
, FALSE
);
89 indent
= (rv
->indent_left
+ rv
->indent_right
) * PANGO_SCALE
;
91 was_drawn
= rv
->drawn
;
94 if (width
<= 0 || height
<= 0)
97 hoffset
= rv
->indent_left
* PANGO_SCALE
;
100 g_print ("%s: w=%d h=%d\n", cell_name (cell
), width
, height
);
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
);
115 g_print ("nat=%d w=%d i=%d\n", rv
->layout_natural_width
, width
, indent
);
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
;
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
;
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
);
149 switch (rv
->effective_halign
) {
150 case GNM_HALIGN_RIGHT
:
151 hoffset
+= (width
- indent
) - rv
->layout_natural_width
;
153 case GNM_HALIGN_DISTRIBUTED
:
154 case GNM_HALIGN_CENTER
:
156 h_center
= width
/ 2;
157 hoffset
+= h_center
+ (-indent
- rv
->layout_natural_width
) / 2;
159 case GNM_HALIGN_CENTER_ACROSS_SELECTION
:
160 hoffset
+= ((width
- indent
) - rv
->layout_natural_width
) / 2;
162 case GNM_HALIGN_FILL
: {
163 PangoDirection dir
= PANGO_DIRECTION_LTR
;
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
);
176 PangoAttrList
*attr
= pango_layout_get_attributes (layout
);
178 dir
= pango_find_base_dir (copy1
, -1);
179 for (i
= 0; i
< copies
; 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
);
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
) {
200 pango_layout_get_extents (layout
, NULL
, &r
);
201 hoffset
+= (width
- indent
) - r
.width
;
208 #ifndef DEBUG_SWITCH_ENUM
211 case GNM_HALIGN_GENERAL
:
212 g_warning ("Unhandled horizontal alignment.");
213 case GNM_HALIGN_LEFT
:
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 */
222 switch (rv
->effective_valign
) {
223 #ifndef DEBUG_SWITCH_ENUM
225 g_warning ("Unhandled vertical alignment.");
232 case GNM_VALIGN_BOTTOM
: {
233 int dh
= height
- rv
->layout_natural_height
;
234 if (rv
->rotation
== 0 && dh
< 0)
236 text_base
= rect_y
+ y_direction
* dh
;
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)
245 text_base
= rect_y
+ y_direction
* dh
;
249 case GNM_VALIGN_JUSTIFY
:
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
) /
256 pango_layout_set_spacing (layout
, spacing
);
257 gnm_rendered_value_remeasure (rv
);
266 g_print ("hoffset=%d, text_base=%d, n_width=%d, n_height=%d\n",
268 rv
->layout_natural_width
, rv
->layout_natural_height
);
271 *res_color
= gnm_rendered_value_get_color (rv
);
272 *res_x
= rect_x
+ hoffset
;
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.)
284 cell_finish_layout (GnmCell
*cell
, GnmRenderedValue
*rv
,
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
);
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
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
,
320 rv
->might_overflow
= might_overflow
;
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);
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
);
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);
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
);
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);
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
);
363 cell_draw_h_extension_markers (cairo_t
*cr
, GnmRenderedValue
*rv
,
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
);
372 case GNM_HALIGN_RIGHT
:
373 cell_draw_extension_mark_left (cr
, x1
, y1
, height
);
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
);
381 case GNM_HALIGN_FILL
:
388 cell_draw_v_extension_markers (cairo_t
*cr
,
390 int width
, int height
,
394 h_center
= width
/ 2;
395 cell_draw_extension_mark_bottom (cr
, x1
, y1
, height
, h_center
);
400 * @cell: #GnmCell const
404 * @width: including margins and leading grid line
405 * @height: including margins and leading grid line
407 * @show_extension_markers:
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
)
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,
431 height
* PANGO_SCALE
,
432 h_center
== -1 ? -1 : (h_center
* PANGO_SCALE
),
433 &fore_color
, &x
, &y
)) {
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.)
444 /* +1 to get past left grid-line. */
445 cairo_rectangle (cr
, x1
+ 1 + GNM_COL_MARGIN
,
446 y1
+ 1 + GNM_ROW_MARGIN
,
452 /* See http://bugzilla.gnome.org/show_bug.cgi?id=105322 */
453 cairo_set_source_rgba (cr
, GO_COLOR_TO_CAIRO (fore_color
));
456 GnmRenderedRotatedValue
*rrv
= (GnmRenderedRotatedValue
*)rv
;
457 struct GnmRenderedRotatedValueInfo
const *li
= rrv
->lines
;
460 for (lines
= pango_layout_get_lines (rv
->layout
);
462 lines
= lines
->next
, li
++) {
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
);
471 cairo_translate (cr
, x1
+ PANGO_PIXELS (x
), y1
+ PANGO_PIXELS (y
));
472 pango_cairo_show_layout (cr
, rv
->layout
);
475 if (show_extension_markers
&&
476 width
< PANGO_PIXELS (rv
->layout_natural_width
)) {
478 cell_draw_h_extension_markers
480 x1
+ 1 + GNM_COL_MARGIN
,
481 y1
+ 1 + GNM_ROW_MARGIN
,
486 if (show_extension_markers
&&
487 height
< PANGO_PIXELS (rv
->layout_natural_height
)) {
489 cell_draw_v_extension_markers
490 (cr
, x1
+ 1 + GNM_COL_MARGIN
,
491 y1
+ 1 + GNM_ROW_MARGIN
,
492 width
, height
, h_center
);