rendered-value.c 10.5 KB
Newer Older
Jody Goldberg's avatar
Jody Goldberg committed
1
/* vim: set sw=8: */
2 3 4 5 6

/*
 * rendered-value.c: Management & utility routines for formated
 *     colored text.
 *
Jody Goldberg's avatar
Jody Goldberg committed
7
 * Copyright (C) 2000, 2001 Jody Goldberg (jody@gnome.org)
8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
 * USA
 */
24 25
#include <gnumeric-config.h>
#include "gnumeric.h"
26
#include "rendered-value.h"
27

28 29
#include "expr.h"
#include "cell.h"
30
#include "style.h"
31
#include "style-color.h"
32
#include "sheet.h"
33
#include "sheet-merge.h"
34
#include "sheet-style.h"
35
#include "format.h"
36 37
#include "value.h"
#include "parse-util.h"
38
#include "sheet-control-gui.h"
39
#include "application.h"
Jody Goldberg's avatar
Jody Goldberg committed
40
#include "str.h"
41

42
#include <math.h>
43
#include <ctype.h>
44

45
/**
46
 * rendered_value_new:
47 48
 * @cell:   The cell
 * @mstyle: The mstyle associated with the cell
49
 * @dynamic_width : Allow format to depend on column width.
50
 *
51
 * Formats the value of the cell according to the format style given in @mstyle
52
 *
53 54 55
 * Return value: a new RenderedValue
 **/
RenderedValue *
56
rendered_value_new (Cell *cell, MStyle const *mstyle, gboolean dynamic_width)
57 58
{
	RenderedValue	*res;
59
	Sheet		*sheet;
60
	StyleColor	*color;
61
	float		 col_width = -1.;
62 63 64 65 66
	char *str;

	g_return_val_if_fail (cell != NULL, NULL);
	g_return_val_if_fail (cell->value != NULL, NULL);

67 68 69
	sheet = cell->base.sheet;

	if (cell_has_expr (cell) && sheet != NULL && sheet->display_formulas) {
70
		ParsePos pp;
71
		char *tmpstr = expr_tree_as_string (cell->base.expression,
72
						    parse_pos_init_cell (&pp, cell));
73 74 75
		str = g_strconcat ("=", tmpstr, NULL);
		g_free (tmpstr);
		color = NULL;
76
		dynamic_width = FALSE;
77 78 79
	} else if (mstyle_is_element_set (mstyle, MSTYLE_FORMAT)) {
		/* entered text CAN be null if called by set_value */
		StyleFormat *format = mstyle_get_format (mstyle);
80

81
		/* For format general approximate the cell width in characters */
82 83
		if (style_format_is_general (format)) {
			if (dynamic_width &&
84 85
			    (VALUE_FMT (cell->value) == NULL ||
			     style_format_is_general (VALUE_FMT (cell->value)))) {
86
				StyleFont *style_font =
87
					scg_get_style_font (sheet, mstyle);
88
				float const font_width = style_font_get_width_pts (style_font);
Jody Goldberg's avatar
Jody Goldberg committed
89
				style_font_unref (style_font);
Jody Goldberg's avatar
Jody Goldberg committed
90 91 92 93 94

				if (font_width > 0.) {
					float cell_width;
					if (cell_is_merged (cell)) {
						Range const *merged =
95 96
							sheet_merge_is_corner (cell->base.sheet, &cell->pos);

Jody Goldberg's avatar
Jody Goldberg committed
97 98 99 100 101 102 103
						cell_width = sheet_col_get_distance_pts (cell->base.sheet,
							merged->start.col, merged->end.col + 1);
					} else
						cell_width = cell->col_info->size_pts;
					cell_width -= cell->col_info->margin_a + cell->col_info->margin_b;
					col_width = cell_width / font_width;
				}
104
			} else {
105
				format = VALUE_FMT (cell->value);
106 107 108 109
				dynamic_width = FALSE;
			}
		} else
			dynamic_width = FALSE;
110
		str = format_value (format, cell->value, &color, col_width);
111 112 113
	} else {
		g_warning ("No format: serious error");
		str = g_strdup ("Error");
114 115 116 117 118 119 120
	}

	g_return_val_if_fail (str != NULL, NULL);

	res = g_new (RenderedValue, 1);
	res->rendered_text = string_get (str);
	res->render_color = color;
121
	res->width_pixel = res->height_pixel = res->offset_pixel = 0;
122
	res->dynamic_width = dynamic_width;
123 124 125 126 127 128
	g_free (str);

	return res;
}

void
129
rendered_value_destroy (RenderedValue *rv)
130 131 132 133 134 135 136 137 138 139 140 141 142 143
{
	if (rv->rendered_text) {
		string_unref (rv->rendered_text);
		rv->rendered_text = NULL;
	}

	if (rv->render_color) {
		style_color_unref (rv->render_color);
		rv->render_color = NULL;
	}

	g_free (rv);
}

144 145
/**
 * rendered_value_calc_size:
146 147
 * @cell:
 *
148 149 150 151 152 153 154 155 156
 * Calls upon rendered_value_calc_size_ext
 **/
void
rendered_value_calc_size (Cell const *cell)
{
	MStyle *mstyle = cell_get_mstyle (cell);
	rendered_value_calc_size_ext (cell, mstyle);
}

157
/*
158
 * rendered_value_calc_size_ext
159 160 161 162 163 164 165 166 167 168 169 170
 * @cell:      The cell we are working on.
 * @style:     the style formatting constraints (font, alignments)
 * @text:      the string contents.
 *
 * Computes the width and height used by the cell based on alignments
 * constraints in the style using the font specified on the style.
 *
 * NOTE :
 * The line splitting code is VERY similar to cell-draw.c:cell_split_text
 * please keep it that way.
 */
void
171
rendered_value_calc_size_ext (Cell const *cell, MStyle *mstyle)
172
{
173
	Sheet *sheet = cell->base.sheet;
174
	RenderedValue *rv = cell->rendered_value;
175
	StyleFont *style_font = scg_get_style_font (sheet, mstyle);
176 177
	GdkFont *gdk_font = style_font_gdk_font (style_font);
	int font_height = style_font_get_height (style_font);
178
	int const cell_w = COL_INTERNAL_WIDTH (cell->col_info);
179
	StyleHAlignFlags const halign = mstyle_get_align_h (mstyle);
180 181 182
	int text_width;
	char *text;

183
	g_return_if_fail (mstyle != NULL);
184
	g_return_if_fail (rv != NULL);
185

186 187
	text       = rv->rendered_text->str;
	text_width = gdk_string_measure (gdk_font, text);
188

189 190 191
	if (text_width < cell_w ||
	    (cell_is_number (cell) &&
	     sheet != NULL && !sheet->display_formulas)) {
192 193
		rv->width_pixel  = text_width;
		rv->height_pixel = font_height;
194
	} else if (halign == HALIGN_JUSTIFY ||
195
		   mstyle_get_align_v (mstyle) == VALIGN_JUSTIFY ||
196
		   mstyle_get_wrap_text (mstyle)) {
197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237
		char const *p, *line_begin;
		char const *first_whitespace = NULL;
		char const *last_whitespace = NULL;
		gboolean prev_was_space = FALSE;
		int used = 0, used_last_space = 0;
		int h = 0;

		rv->width_pixel  = cell_w;

		for (line_begin = p = text; *p; p++){
			int const len_current = gdk_text_width (gdk_font, p, 1);

			/* Wrap if there is an embeded newline, or we have overflowed */
			if (*p == '\n' || used + len_current > cell_w){
				char const *begin = line_begin;
				int len;

				if (*p == '\n'){
					/* start after newline, preserve whitespace */
					line_begin = p+1;
					len = p - begin;
					used = 0;
				} else if (last_whitespace != NULL){
					/* Split at the run of whitespace */
					line_begin = last_whitespace + 1;
					len = first_whitespace - begin;
					used = len_current + used - used_last_space;
				} else {
					/* Split before the current character */
					line_begin = p; /* next line starts here */
					len = p - begin;
					used = len_current;
				}

				h += font_height;
				first_whitespace = last_whitespace = NULL;
				prev_was_space = FALSE;
				continue;
			}

			used += len_current;
238 239 240 241 242 243
			if (*p == '-') {
				used_last_space = used;
				last_whitespace = p;
				first_whitespace = p+1;
				prev_was_space = TRUE;
			} else if (isspace (*(unsigned char *)p)) {
244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261
				used_last_space = used;
				last_whitespace = p;
				if (!prev_was_space)
					first_whitespace = p;
				prev_was_space = TRUE;
			} else
				prev_was_space = FALSE;
		}

		/* Catch the final bit that did not wrap */
		if (*line_begin)
			h += font_height;

		rv->height_pixel = h;
	} else {
		rv->width_pixel  = text_width;
		rv->height_pixel = font_height;
	}
262

Jody Goldberg's avatar
Jody Goldberg committed
263
	/* 2*width seems to be pretty close to XL's notion */
264
	if (halign == HALIGN_LEFT || halign == HALIGN_RIGHT)
265 266
		rv->offset_pixel = rint (mstyle_get_indent (mstyle) *
			2 * style_font->approx_width.pixels);
267 268 269 270 271 272 273
	style_font_unref (style_font);
}

/* Return the value as a single string without format infomation.
 * Caller is responsible for freeing the result
 */
char *
274
rendered_value_get_text (RenderedValue const *rv)
275
{
276
	g_return_val_if_fail (rv != NULL && rv->rendered_text != NULL, g_strdup ("ERROR"));
277 278 279 280 281 282 283 284 285 286 287 288 289 290
	return g_strdup (rv->rendered_text->str);
}

/**
 * cell_get_rendered_text:
 * @cell: the cell from which we want to pull the content from
 *
 * This returns a g_malloc()ed region of memory with a text representation
 * of the cell contents.
 *
 * This will return a text expression if the cell contains a formula, or
 * a string representation of the value.
 */
char *
291
cell_get_rendered_text (Cell const *cell)
292
{
293
	g_return_val_if_fail (cell != NULL, g_strdup("ERROR"));
294

295
	/* A precursor to just in time rendering Ick! */
296
	if (cell->rendered_value == NULL)
297
		cell_render_value ((Cell *)cell, TRUE);
298

299 300 301
	return rendered_value_get_text (cell->rendered_value);
}

302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320
/**
 * cell_get_render_color:
 * @cell: the cell from which we want to pull the color from
 *
 * the returned value is a pointer to the StyleColor.
 * no reference has been added!
 */
StyleColor  *
cell_get_render_color (Cell const *cell)
{
	g_return_val_if_fail (cell != NULL, NULL);

	/* A precursor to just in time rendering Ick! */
	if (cell->rendered_value == NULL)
		cell_render_value ((Cell *)cell, TRUE);

	return cell->rendered_value->render_color;
}

321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337
/**
 * cell_get_entered_text:
 * @cell: the cell from which we want to pull the content from
 *
 * This returns a g_malloc()ed region of memory with a text representation
 * of the cell contents.
 *
 * This will return a text expression if the cell contains a formula, or
 * a string representation of the value.
 */
char *
cell_get_entered_text (Cell const *cell)
{
	g_return_val_if_fail (cell != NULL, NULL);

	if (cell_has_expr (cell)) {
		char *func, *ret;
338
		ParsePos pp;
339

340
		func = expr_tree_as_string (cell->base.expression,
341
			parse_pos_init_cell (&pp, cell));
342 343 344 345 346 347
		ret = g_strconcat ("=", func, NULL);
		g_free (func);

		return ret;
	}

348 349 350
	if (cell->value != NULL) {
		if (cell->value->type == VALUE_STRING)
			return g_strconcat ("\'", cell->value->v_str.val->str, NULL);
351
		return format_value (NULL, cell->value, NULL, -1);
352
	}
353 354 355

	g_warning ("A cell with no expression, and no value ??");
	return g_strdup ("<ERROR>");
356 357 358
}

int
jody goldberg's avatar
jody goldberg committed
359
cell_rendered_height (Cell const *cell)
360
{
361
	if (cell == NULL)
362
		return 0;
363 364 365 366 367 368 369 370 371 372 373 374 375 376

	/* If we are not rendered and are an expressions just use the font height */
	if (cell->rendered_value == NULL) {
		if (cell_has_expr (cell)) {
			MStyle const *style = sheet_style_get (cell->base.sheet,
				cell->pos.col, cell->pos.row);
			StyleFont *style_font = scg_get_style_font (cell->base.sheet, style);
			int res = style_font_get_height (style_font);
			style_font_unref (style_font);
			return res;
		}
		return 0;
	}
	return cell->rendered_value->height_pixel;
377 378
}
int
jody goldberg's avatar
jody goldberg committed
379
cell_rendered_width (Cell const *cell)
380
{
381 382 383
	if (!cell || !cell->rendered_value)
		return 0;
	else
jody goldberg's avatar
jody goldberg committed
384
		return cell->rendered_value->width_pixel;
385
}
386 387 388 389 390 391
int
cell_rendered_offset (Cell const * cell)
{
	if (!cell || !cell->rendered_value)
		return 0;
	else
392
		return cell->rendered_value->offset_pixel;
393
}