2 * cell-draw.c: Cell drawing on screen
5 * Miguel de Icaza 1998, 1999 (miguel@kernel.org)
6 * Jody Goldberg 2000-2002 (jody@gnome.org)
7 * Morten Welinder 2003 (terra@gnome.org)
9 #include <gnumeric-config.h>
11 #include <cell-draw.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>
26 static char const hashes
[] =
27 "################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################";
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
;
38 return (attribute
->klass
->type
== PANGO_ATTR_SHAPE
);
42 cell_draw_simplify_attributes (GnmRenderedValue
*rv
)
44 PangoAttrList
*pal
= pango_layout_get_attributes (rv
->layout
);
45 gboolean recalc_height
= FALSE
;
47 (pango_attr_list_filter
48 (pal
, (PangoAttrFilterFunc
) cell_draw_simplify_cb
, &recalc_height
));
50 pango_layout_get_size (rv
->layout
, NULL
,
51 &rv
->layout_natural_height
);
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
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
)
85 g_return_val_if_fail (rv
!= NULL
, FALSE
);
88 indent
= (rv
->indent_left
+ rv
->indent_right
) * PANGO_SCALE
;
90 was_drawn
= rv
->drawn
;
93 if (width
<= 0 || height
<= 0)
96 hoffset
= rv
->indent_left
* PANGO_SCALE
;
99 g_print ("%s: w=%d h=%d\n", cell_name (cell
), width
, height
);
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
);
114 g_print ("nat=%d w=%d i=%d\n", rv
->layout_natural_width
, width
, indent
);
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
;
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
;
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
);
148 switch (rv
->effective_halign
) {
149 case GNM_HALIGN_RIGHT
:
150 hoffset
+= (width
- indent
) - rv
->layout_natural_width
;
152 case GNM_HALIGN_DISTRIBUTED
:
153 case GNM_HALIGN_CENTER
:
155 h_center
= width
/ 2;
156 hoffset
+= h_center
+ (-indent
- rv
->layout_natural_width
) / 2;
158 case GNM_HALIGN_CENTER_ACROSS_SELECTION
:
159 hoffset
+= ((width
- indent
) - rv
->layout_natural_width
) / 2;
161 case GNM_HALIGN_FILL
: {
162 PangoDirection dir
= PANGO_DIRECTION_LTR
;
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
);
175 PangoAttrList
*attr
= pango_layout_get_attributes (layout
);
177 dir
= pango_find_base_dir (copy1
, -1);
178 for (i
= 0; i
< copies
; 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
);
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
) {
199 pango_layout_get_extents (layout
, NULL
, &r
);
200 hoffset
+= (width
- indent
) - r
.width
;
207 #ifndef DEBUG_SWITCH_ENUM
210 case GNM_HALIGN_GENERAL
:
211 g_warning ("Unhandled horizontal alignment.");
212 case GNM_HALIGN_LEFT
:
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 */
221 switch (rv
->effective_valign
) {
222 #ifndef DEBUG_SWITCH_ENUM
224 g_warning ("Unhandled vertical alignment.");
231 case GNM_VALIGN_BOTTOM
: {
232 int dh
= height
- rv
->layout_natural_height
;
233 if (rv
->rotation
== 0 && dh
< 0)
235 text_base
= rect_y
+ y_direction
* dh
;
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)
244 text_base
= rect_y
+ y_direction
* dh
;
248 case GNM_VALIGN_JUSTIFY
:
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
) /
255 pango_layout_set_spacing (layout
, spacing
);
256 gnm_rendered_value_remeasure (rv
);
265 g_print ("hoffset=%d, text_base=%d, n_width=%d, n_height=%d\n",
267 rv
->layout_natural_width
, rv
->layout_natural_height
);
270 *res_color
= gnm_rendered_value_get_color (rv
);
271 *res_x
= rect_x
+ hoffset
;
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.)
283 cell_finish_layout (GnmCell
*cell
, GnmRenderedValue
*rv
,
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
);
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
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
,
319 rv
->might_overflow
= might_overflow
;
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);
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
);
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);
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
);
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);
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
);
362 cell_draw_h_extension_markers (cairo_t
*cr
, GnmRenderedValue
*rv
,
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
);
371 case GNM_HALIGN_RIGHT
:
372 cell_draw_extension_mark_left (cr
, x1
, y1
, height
);
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
);
380 case GNM_HALIGN_FILL
:
387 cell_draw_v_extension_markers (cairo_t
*cr
,
389 int width
, int height
,
393 h_center
= width
/ 2;
394 cell_draw_extension_mark_bottom (cr
, x1
, y1
, height
, h_center
);
399 * @cell: #GnmCell const
403 * @width: including margins and leading grid line
404 * @height: including margins and leading grid line
406 * @show_extension_markers:
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
)
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,
430 height
* PANGO_SCALE
,
431 h_center
== -1 ? -1 : (h_center
* PANGO_SCALE
),
432 &fore_color
, &x
, &y
)) {
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.)
443 /* +1 to get past left grid-line. */
444 cairo_rectangle (cr
, x1
+ 1 + GNM_COL_MARGIN
,
445 y1
+ 1 + GNM_ROW_MARGIN
,
451 /* See http://bugzilla.gnome.org/show_bug.cgi?id=105322 */
452 cairo_set_source_rgba (cr
, GO_COLOR_TO_CAIRO (fore_color
));
455 GnmRenderedRotatedValue
*rrv
= (GnmRenderedRotatedValue
*)rv
;
456 struct GnmRenderedRotatedValueInfo
const *li
= rrv
->lines
;
459 for (lines
= pango_layout_get_lines (rv
->layout
);
461 lines
= lines
->next
, li
++) {
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
);
470 cairo_translate (cr
, x1
+ PANGO_PIXELS (x
), y1
+ PANGO_PIXELS (y
));
471 pango_cairo_show_layout (cr
, rv
->layout
);
474 if (show_extension_markers
&&
475 width
< PANGO_PIXELS (rv
->layout_natural_width
)) {
477 cell_draw_h_extension_markers
479 x1
+ 1 + GNM_COL_MARGIN
,
480 y1
+ 1 + GNM_ROW_MARGIN
,
485 if (show_extension_markers
&&
486 height
< PANGO_PIXELS (rv
->layout_natural_height
)) {
488 cell_draw_v_extension_markers
489 (cr
, x1
+ 1 + GNM_COL_MARGIN
,
490 y1
+ 1 + GNM_ROW_MARGIN
,
491 width
, height
, h_center
);