cell.c 36 KB
Newer Older
1
2
3
4
/*
 * cell.c: Cell management of the Gnumeric spreadsheet.
 *
 * Author:
5
 *    Miguel de Icaza 1998, 1999 (miguel@kernel.org)
6
 */
7
#include <config.h>
Arturo Espinosa's avatar
Arturo Espinosa committed
8
#include <gnome.h>
Miguel de Icaza's avatar
Miguel de Icaza committed
9
#include <locale.h>
10
#include <ctype.h>
Arturo Espinosa's avatar
Arturo Espinosa committed
11
#include "gnumeric.h"
12
#include "gnumeric-util.h"
13
#include "gnumeric-sheet.h"
Arturo Espinosa's avatar
Arturo Espinosa committed
14
#include "eval.h"
15
#include "value.h"
16
#include "format.h"
17
#include "color.h"
18
#include "border.h"
19
#include "cursors.h"
20
#include "gutils.h"
21
#include "cell.h"
22
#include "cellspan.h"
23
#include "gnumeric-util.h"
Arturo Espinosa's avatar
Arturo Espinosa committed
24

25
/* FIXME : Move this into workbook */
26
27
static int         redraws_frozen           = 0;
static int         redraws_deep_frozen      = 0;
28
29
static GHashTable *cell_hash_queue;

30
static void
31
32
33
cell_formula_changed (Cell *cell)
{
	g_return_if_fail (cell != NULL);
34

35
	sheet_cell_formula_link (cell);
36

37
38
39
	cell_queue_recalc (cell);
}

40
41
42
static inline void
cell_modified (Cell *cell)
{
43
	Sheet *sheet = cell->sheet;
44
45
46
47

	/* Cells from the clipboard do not have a sheet attached */
	if (sheet)
		sheet->modified = TRUE;
48
49
}

Morten Welinder's avatar
Morten Welinder committed
50

51
/* Empty a cell's value, entered_text, and parsed_node.  */
Jody Goldberg's avatar
typo.    
Jody Goldberg committed
52
static void
53
54
55
cell_cleanout (Cell *cell)
{
	if (cell->parsed_node){
56
57
58
		/* Clipboard cells, e.g., are not attached to a sheet.  */
		if (cell->sheet)
			sheet_cell_formula_unlink (cell);
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
		expr_tree_unref (cell->parsed_node);
		cell->parsed_node = NULL;
	}

	if (cell->value) {
		value_release (cell->value);
		cell->value = NULL;
	}

	if (cell->entered_text) {
		string_unref (cell->entered_text);
		cell->entered_text = NULL;
	}
}

Michael Meeks's avatar
Michael Meeks committed
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
/*
 * cell_set_rendered_text
 * @cell:          the cell we will modify
 * @rendered_text: the text we will display
 *
 * This routine sets the rendered text field of the cell
 * it recomputes the bounding box for the cell as well
 */
static void
cell_set_rendered_text (Cell *cell, const char *rendered_text)
{
	String *oldtext;

	g_return_if_fail (cell != NULL);
	g_return_if_fail (rendered_text != NULL);

	cell_modified (cell);

	oldtext = cell->text;
	cell->text = string_get (rendered_text);
	if (oldtext)
		string_unref (oldtext);

	cell_calc_dimensions (cell, TRUE);
}
99

Jody Goldberg's avatar
typo.    
Jody Goldberg committed
100
void
101
cell_set_formula (Cell *cell, const char *text)
Arturo Espinosa's avatar
Arturo Espinosa committed
102
{
103
	ExprTree *new_expr;
104
	char *error_msg = _("ERROR");
Morten Welinder's avatar
Morten Welinder committed
105
	char *desired_format = NULL;
106
	ParsePosition pp;
Morten Welinder's avatar
Morten Welinder committed
107
	gboolean may_set_format;
108

Arturo Espinosa's avatar
Arturo Espinosa committed
109
110
	g_return_if_fail (cell != NULL);
	g_return_if_fail (text != NULL);
111
	g_return_if_fail (gnumeric_char_start_expr_p (*text));
112

Morten Welinder's avatar
Morten Welinder committed
113
114
	may_set_format = !cell_has_assigned_format (cell);

115
	cell_modified (cell);
116
	new_expr = expr_parse_string (text+1, /* Ignore leading char (=,@,+) */
117
				      parse_pos_cell (&pp, cell),
Morten Welinder's avatar
Morten Welinder committed
118
				      may_set_format ? &desired_format : NULL,
119
				      &error_msg);
120
121
	cell_cleanout (cell);

122
	if (new_expr == NULL) {
123
		cell_set_rendered_text (cell, error_msg);
124
125
126
		cell->entered_text = string_get (text);
		/* FIXME: Supply a proper position?  */
		cell->value = value_new_error (NULL, error_msg);
Arturo Espinosa's avatar
Arturo Espinosa committed
127
		return;
128
129
	}

Morten Welinder's avatar
Morten Welinder committed
130
131
132
	/* Until the value is recomputed, we put in this value.  */
	cell->value = value_new_error (NULL, _("Pending recomputation"));

Morten Welinder's avatar
Morten Welinder committed
133
	if (desired_format) {
134
		cell_set_format (cell, desired_format);
Morten Welinder's avatar
Morten Welinder committed
135
136
		g_free (desired_format);
	}
Miguel de Icaza's avatar
New:    
Miguel de Icaza committed
137

138
	if (new_expr->oper == OPER_ARRAY) {
139
		/* The corner sets up the entire array block */
140
		if (new_expr->u.array.x != 0 || new_expr->u.array.y != 0) {
141
142
143
			/* Throw away the expression, including the inner
			   one.  */
			expr_tree_unref (new_expr->u.array.corner.func.expr);
144
145
146
147
			expr_tree_unref (new_expr);
			return;
		}

148
		/*
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
		 * NOTE : The wrapper supplied by the parser will be released
		 *        and recreated.  new_expr will NOT be valid on exit
		 *        from cell_set_array_formula.
		 */
		cell->parsed_node = new_expr;
		cell_set_array_formula (cell->sheet,
					cell->row->pos, cell->col->pos,
					cell->row->pos +
					    new_expr->u.array.rows -1,
					cell->col->pos +
					    new_expr->u.array.cols -1,
					new_expr->u.array.corner.func.expr);
	} else {
		cell->parsed_node = new_expr;
		cell_formula_changed (cell);
	}
Arturo Espinosa's avatar
Arturo Espinosa committed
165
}
Arturo Espinosa's avatar
Arturo Espinosa committed
166

167
168
169
170
171
void
cell_comment_destroy (Cell *cell)
{
	CellComment *comment;
	GList *l;
172

173
174
175
176
177
178
179
180
181
	g_return_if_fail (cell != NULL);

	comment = cell->comment;
	if (!comment)
		return;
	cell->comment = NULL;

	/* Free resources */
	string_unref (comment->comment);
182
183
184
185
186

	if (comment->timer_tag != -1)
		gtk_timeout_remove (comment->timer_tag);

	if (comment->window)
187
		gtk_object_destroy (GTK_OBJECT (comment->window));
188

189
	for (l = comment->realized_list; l; l = l->next)
190
		gtk_object_destroy (l->data);
Morten Welinder's avatar
Morten Welinder committed
191
	g_list_free (comment->realized_list);
192

193
194
195
	g_free (comment);
}

196
197
198
199
200
201
202
static void
cell_comment_cancel_timer (Cell *cell)
{
	if (cell->comment->timer_tag != -1){
		gtk_timeout_remove (cell->comment->timer_tag);
		cell->comment->timer_tag = -1;
	}
203

204
}
205

206
207
208
209
210
static void
cell_display_comment (Cell *cell)
{
	GtkWidget *window, *label;
	int x, y;
211

212
213
	g_return_if_fail (cell != NULL);

214
	cell_comment_cancel_timer (cell);
215

216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
	window = gtk_window_new (GTK_WINDOW_POPUP);
	label = gtk_label_new (cell->comment->comment->str);
	gtk_container_add (GTK_CONTAINER (window), label);

	gdk_window_get_pointer (NULL, &x, &y, NULL);
	gtk_widget_set_uposition (window, x+10, y+10);

	gtk_widget_show_all (window);

	cell->comment->window = window;
}

static gint
cell_popup_comment (gpointer data)
{
	Cell *cell = data;
232

233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
	cell->comment->timer_tag = -1;

	cell_display_comment (cell);
	return FALSE;
}

static int
cell_comment_clicked (GnomeCanvasItem *item, GdkEvent *event, Cell *cell)
{
	GnomeCanvas *canvas = item->canvas;

	switch (event->type){
	case GDK_BUTTON_RELEASE:
		if (event->button.button != 1)
			return FALSE;
		if (cell->comment->window)
			return FALSE;
		cell_display_comment (cell);
		break;

	case GDK_BUTTON_PRESS:
		if (event->button.button != 1)
			return FALSE;
		break;
257

258
259
260
261
262
263
	case GDK_ENTER_NOTIFY:
		cell->comment->timer_tag = gtk_timeout_add (1000, cell_popup_comment, cell);
		cursor_set_widget (canvas, GNUMERIC_CURSOR_ARROW);
		break;

	case GDK_LEAVE_NOTIFY:
264
		cell_comment_cancel_timer (cell);
265
		if (cell->comment->window){
266
			gtk_object_destroy (GTK_OBJECT (cell->comment->window));
267
268
269
			cell->comment->window = NULL;
		}
		break;
270

271
272
273
274
275
276
	default:
		return FALSE;
	}
	return TRUE;
}

277
278
279
280
281
282
283
284
static void
cell_comment_realize (Cell *cell)
{
	GList *l;

	g_return_if_fail (cell->comment != NULL);

	sheet_cell_comment_link (cell);
Morten Welinder's avatar
Morten Welinder committed
285
	for (l = cell->sheet->sheet_views; l; l = l->next){
286
287
		SheetView *sheet_view = SHEET_VIEW (l->data);
		GnomeCanvasItem *o;
288

289
		o = sheet_view_comment_create_marker (
290
			sheet_view,
291
			cell->col->pos, cell->row->pos);
292
		gtk_object_ref (GTK_OBJECT (o));
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312

		cell->comment->realized_list = g_list_prepend (
			cell->comment->realized_list, o);

		gtk_signal_connect (GTK_OBJECT (o), "event",
				    GTK_SIGNAL_FUNC (cell_comment_clicked), cell);
	}
}

static void
cell_comment_unrealize (Cell *cell)
{
	GList *l;

	g_return_if_fail (cell->comment != NULL);

	sheet_cell_comment_unlink (cell);
	for (l = cell->comment->realized_list; l; l = l->next){
		GnomeCanvasItem *o = l->data;

313
		gtk_object_unref (GTK_OBJECT (o));
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
	}
	g_list_free (cell->comment->realized_list);
	cell->comment->realized_list = NULL;
}

void
cell_realize (Cell *cell)
{
	g_return_if_fail (cell != NULL);

	if (cell->comment)
		cell_comment_realize (cell);
}

void
cell_unrealize (Cell *cell)
{
	g_return_if_fail (cell != NULL);

	if (cell->comment)
		cell_comment_unrealize (cell);
}

337
void
338
cell_set_comment (Cell *cell, const char *str)
339
340
{
	int had_comments = FALSE;
341

342
343
344
345
346
347
348
349
350
351
352
	g_return_if_fail (cell != NULL);
	g_return_if_fail (str != NULL);

	cell_modified (cell);

	cell_comment_destroy (cell);

	cell->comment = g_new (CellComment, 1);
	cell->comment->realized_list = NULL;
	cell->comment->timer_tag = -1;
	cell->comment->window = NULL;
353

354
355
356
357
358
	cell->comment->comment = string_get (str);

	if (had_comments)
		cell_queue_redraw (cell);

359
360
	if (cell->sheet)
		cell_comment_realize (cell);
361
362
}

363
char *
364
cell_get_formatted_val (Cell *cell, StyleColor **colour)
365
366
367
{
	MStyle *mstyle;
	char *txt;
368

369
370
	mstyle = sheet_style_compute (cell->sheet, cell->col->pos,
				      cell->row->pos);
Jody Goldberg's avatar
Jody Goldberg committed
371
372
	if (mstyle_is_element_set (mstyle, MSTYLE_FORMAT)) {
		String *tmp = cell->entered_text;
373
		txt = format_value (mstyle_get_format (mstyle),
Jody Goldberg's avatar
Jody Goldberg committed
374
375
376
				    cell->value, colour,
				    (tmp!=NULL) ? tmp->str : NULL);
	} else {
377
378
379
380
381
382
383
		g_warning ("No format: serious error");
		txt = g_strdup ("Error");
	}
	mstyle_unref (mstyle);
	return txt;
}

Miguel de Icaza's avatar
Miguel de Icaza committed
384
385
386
387
/*
 * cell_render_value
 * @cell: The cell whose value needs to be rendered
 *
388
389
390
 * The value of the cell is formated according to the format style, but if
 * formulas are being displayed then use the text of the formula instead of 
 * its value.
Miguel de Icaza's avatar
Miguel de Icaza committed
391
 */
Miguel de Icaza's avatar
Today:    
Miguel de Icaza committed
392
393
394
void
cell_render_value (Cell *cell)
{
395
	StyleColor *color;
Miguel de Icaza's avatar
Today:    
Miguel de Icaza committed
396
	char *str;
397

Miguel de Icaza's avatar
Today:    
Miguel de Icaza committed
398
399
400
	g_return_if_fail (cell != NULL);
	g_return_if_fail (cell->value != NULL);

Jody Goldberg's avatar
Jody Goldberg committed
401
	if (cell->render_color == (void *)0xdeadbeef) {
Miguel de Icaza's avatar
Miguel de Icaza committed
402
403
		g_error ("This cell is dead!");
	}
404
	if (cell->render_color) {
405
406
407
		style_color_unref (cell->render_color);
		cell->render_color = NULL;
	}
Arturo Espinosa's avatar
Arturo Espinosa committed
408

409
	if (cell->sheet->display_formulas && cell->parsed_node){
410
411
412
413
414
415
416
417
418
		ParsePosition pp;
		char *tmpstr = expr_decode_tree (cell->parsed_node,
						 parse_pos_cell (&pp, cell));
		str = g_strconcat ("=", tmpstr, NULL);
		g_free (tmpstr);
	} else {
		str = cell_get_formatted_val (cell, &color);		
		cell->render_color = color;
	}
419

Miguel de Icaza's avatar
Today:    
Miguel de Icaza committed
420
421
422
423
	cell_set_rendered_text (cell, str);
	g_free (str);
}

Michael Meeks's avatar
Michael Meeks committed
424
425
426
427
428
429
430
431
432
433
434
435
436
437
/*
 * Sets the value for a cell:
 *
 * This is kind of an internal function and should be only called by
 * routines that know what they are doing.  These are the important
 * differences from cell_set_value:
 *
 *    - It does not queue redraws (so you have to queue the redraw yourself
 *      or queue a full redraw).
 *
 *    - It does not queue any recomputations.  You have to queue the recompute
 *      yourself.
 */
void
Morten Welinder's avatar
Morten Welinder committed
438
cell_set_value_simple (Cell *cell, Value *v)
Michael Meeks's avatar
Michael Meeks committed
439
440
441
442
443
{
	g_return_if_fail (cell);
	g_return_if_fail (v);

	cell_modified (cell);
444
	cell_cleanout (cell);
Michael Meeks's avatar
Michael Meeks committed
445
446
447
448
449
450
451
452
453
454
455

	cell->value = v;
	cell_render_value (cell);
}

/*
 * cell_set_value
 *
 * Changes the value of a cell
 */
void
Morten Welinder's avatar
Morten Welinder committed
456
cell_set_value (Cell *cell, Value *v)
Michael Meeks's avatar
Michael Meeks committed
457
458
459
{
	g_return_if_fail (cell);
	g_return_if_fail (v);
460

Michael Meeks's avatar
Michael Meeks committed
461
462
463
464
465
466
467
468
	cell_queue_redraw (cell);

	cell_set_value_simple (cell, v);
	cell_content_changed (cell);

	cell_queue_redraw (cell);
}

469
470
471
472
473
474
475
476
477
478
479
480
/*
 * Sets the text for a cell:
 *
 * This is kind of an internal function and should be only called by
 * routines that know what they are doing.  These are the important
 * differences from cell_set_text:
 *
 *    - It does not queue redraws (so you have to queue the redraw yourself
 *      or queue a full redraw).
 *
 *    - It does not queue any recomputations.  You have to queue the recompute
 *      yourself.
Jody Goldberg's avatar
Jody Goldberg committed
481
482
 *
 *    - It stores the rendered value of the text as if that is what was entered.
483
 */
484
void
485
cell_set_text_simple (Cell *cell, const char *text)
486
{
Arturo Espinosa's avatar
Arturo Espinosa committed
487
488
	g_return_if_fail (cell != NULL);
	g_return_if_fail (text != NULL);
Arturo Espinosa's avatar
Arturo Espinosa committed
489

490
	cell_modified (cell);
491
	cell_cleanout (cell);
492

493
	if (gnumeric_char_start_expr_p (*text) && text[1] != '\0')
494
		cell_set_formula (cell, text);
495
	else {
496
		char *end;
497
		long l = strtol (text, &end, 10);
498
499
		if (l != LONG_MAX && l != LONG_MIN &&
		    text != end && (l == (int)l)) {
500
501
			/* Allow and ignore spaces at the end of integers. */
			/* JEG : 2000/3/23 Why for ints and not doubles ? */
502
503
			while (*end == ' ')
				end++;
504
			if (*end == FALSE)
505
506
				cell->value = value_new_int (l);
		}
507

508
		if (cell->value == NULL) {
509
			double d;
Morten Welinder's avatar
Morten Welinder committed
510
			d = strtod (text, &end);
511
512
513

			/* It is a floating point number.  */
			if (text != end && *end == 0)
514
				cell->value = value_new_float ((float_t)d);
515
516
		}

517
#if 0
518
519
520
521
		if (cell->value == NULL) {
			/* Check to see if it matches via current format */
			if (format_match (text, float_t *v, char **format))
			{
Arturo Espinosa's avatar
Arturo Espinosa committed
522
			}
523
		}
524
#endif
525
526
527
528

		if (cell->value == NULL) {
			/* It is text. Ignore leading single quotes */
			cell->value = value_new_string (text[0] == '\'' ? text+1 : text);
529
530
531
532
533
534
			cell->entered_text = string_get (text);
		} else {
			/* NOTE : do not set the entered text, we can not always parse
			 * the result when we reimport later
			 */
			cell->entered_text = NULL;
Arturo Espinosa's avatar
Arturo Espinosa committed
535
		}
536

537
538
539
540
541
542
		/* FIXME : This calls calc_dimension much too early in the
		 * import process.  There is no point calculating spans or
		 * dimensions before the cells neighbours or format has been
		 * applied.
		 */
		cell_render_value (cell);
543
	}
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
}

/*
 * cell_content_changed:
 *
 * Call this routine if you modify the contents of a cell
 * to trigger a recompute on any dependencies
 */
void
cell_content_changed (Cell *cell)
{
	GList   *deps;

	g_return_if_fail (cell != NULL);

Arturo Espinosa's avatar
Arturo Espinosa committed
559
	/* Queue all of the dependencies for this cell */
560
	deps = cell_get_dependencies (cell);
Arturo Espinosa's avatar
Arturo Espinosa committed
561
	if (deps)
Morten Welinder's avatar
Morten Welinder committed
562
		cell_queue_recalc_list (deps, TRUE);
563
564

	sheet_cell_changed (cell);
565
566
}

567

568
569
570
/*
 * cell_set_text
 *
Jody Goldberg's avatar
Jody Goldberg committed
571
572
 * Changes the content of a cell and stores
 * the actual entered text.
573
574
 */
void
575
cell_set_text (Cell *cell, const char *text)
576
577
578
{
	g_return_if_fail (cell != NULL);
	g_return_if_fail (text != NULL);
579

580
	if (cell->parsed_node != NULL && cell->parsed_node->oper == OPER_ARRAY) {
581
582
583
584
		gnumeric_no_modify_array_notice (cell->sheet->workbook);
		return;
	}

585
586
	if (*text == 0)
		g_warning ("Cell value being set to empty string");
587

588
589
590
	cell_queue_redraw (cell);

	cell_set_text_simple (cell, text);
Jody Goldberg's avatar
Jody Goldberg committed
591
592

	/* Store the real entered text */
593
594
595
596
	if (cell->entered_text != NULL) {
		String *tmp = string_get (text);
		string_unref (cell->entered_text);
		cell->entered_text = tmp;
Jody Goldberg's avatar
Jody Goldberg committed
597
	}
598
	cell_content_changed (cell);
599

600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
	cell_queue_redraw (cell);
}

/**
 * cell_set_formula_tree_simple:
 * @cell:    the cell to set the formula to
 * @formula: an expression tree with the formula
 *
 * This is an internal function.  It should be only called by routines that
 * know what they are doing.  These are the important differences from
 * cell_set_formula:
 *
 *   - It does not queue redraws (so you have to queue the redraw yourself
 *     or queue a full redraw).
 */
void
cell_set_formula_tree_simple (Cell *cell, ExprTree *formula)
{
	g_return_if_fail (cell != NULL);
	g_return_if_fail (formula != NULL);

621
	/* Ref before unref.  Repeat after me.  */
Morten Welinder's avatar
Morten Welinder committed
622
623
	expr_tree_ref (formula);

624
625
	cell_modified (cell);
	cell_cleanout (cell);
626
627

	cell->parsed_node = formula;
628
629
	/* Until the value is recomputed, we put in this value.  */
	cell->value = value_new_error (NULL, _("Circular reference"));
630
631
632
	cell_formula_changed (cell);
}

633
634
635
636
637
638
639
640
641
/**
 * cell_set_array_formula:
 * @sheet:   The sheet to set the formula to.
 * @row_a:   The top row in the destination region.
 * @col_a:   The left column in the destination region.
 * @row_b:   The bottom row in the destination region.
 * @col_b:   The right column in the destination region.
 * @formula: an expression tree with the formula
 *
642
 * Uses cell_set_formula_tree_simple to store the formula as an
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
 * 'array-formula'.  The supplied expression is wrapped in an array
 * operator for each cell in the range and scheduled for recalc.  The
 * upper left corner is handled as a special case and care is taken to
 * out it at the head of the recalc queue.
 */
void
cell_set_array_formula (Sheet *sheet,
			int row_a, int col_a, int row_b, int col_b,
			ExprTree *formula)
{
	int const num_rows = 1 + row_b - row_a;
	int const num_cols = 1 + col_b - col_a;
	int x, y;
	Cell * const corner = sheet_cell_fetch (sheet, col_a, row_a);
	Cell * cell = NULL;
658
	ExprTree *wrapper;
659
660
661
662
663
664
665
666
667
668
669

	g_return_if_fail (num_cols > 0);
	g_return_if_fail (num_rows > 0);
	g_return_if_fail (formula != NULL);
	g_return_if_fail (corner != NULL);

	wrapper = expr_tree_array_formula (0, 0, num_rows, num_cols);
	wrapper->u.array.corner.func.value = NULL;
	wrapper->u.array.corner.func.expr = formula;
	expr_tree_ref (formula);
	cell_set_formula_tree_simple (corner, wrapper);
670
	expr_tree_unref (wrapper);
671
672
673
674
675

	/* The corner must be 1st on the recalc list, queue it later */
	cell_unqueue_from_recalc (corner);

	for (x = 0; x < num_cols; ++x)
676
		for (y = 0; y < num_rows; ++y) {
677
678
			if (x == 0 && y == 0)
				continue;
Michael Meeks's avatar
Michael Meeks committed
679
			cell = sheet_cell_fetch (sheet, col_a + x, row_a + y);
680
681
682
683
			wrapper = expr_tree_array_formula (x, y,
							   num_rows, num_cols);
			wrapper->u.array.corner.cell = corner;
			cell_set_formula_tree_simple (cell, wrapper);
684
			expr_tree_unref (wrapper);
685
686
687
688
689
690
		}

	/* Put the corner at the head of the recalc list */
	cell_queue_recalc (corner);
}

691
692
693
694
695
696
697
698
void
cell_set_formula_tree (Cell *cell, ExprTree *formula)
{
	g_return_if_fail (cell != NULL);

	cell_queue_redraw (cell);

	cell_set_formula_tree_simple (cell, formula);
699
	cell_content_changed (cell);
Arturo Espinosa's avatar
Today:    
Arturo Espinosa committed
700
701

	cell_queue_redraw (cell);
Arturo Espinosa's avatar
Arturo Espinosa committed
702
}
Arturo Espinosa's avatar
Arturo Espinosa committed
703

704
705
706
707
/**
 * cell_copy:
 * @cell: existing cell to duplicate
 *
708
 * Makes a copy of a Cell.
709
 *
710
 * Returns a copy of the cell.
Arturo Espinosa's avatar
Arturo Espinosa committed
711
712
 */
Cell *
713
cell_copy (const Cell *cell)
Arturo Espinosa's avatar
Arturo Espinosa committed
714
715
716
717
718
719
720
721
722
723
{
	Cell *new_cell;

	g_return_val_if_fail (cell != NULL, NULL);

	new_cell = g_new (Cell, 1);

	/* bitmap copy first */
	*new_cell = *cell;

Morten Welinder's avatar
Morten Welinder committed
724
	new_cell->flags &= ~CELL_QUEUED_FOR_RECALC;
725

Morten Welinder's avatar
Morten Welinder committed
726
	/* now copy properly the rest */
727
	if (new_cell->parsed_node)
Morten Welinder's avatar
Morten Welinder committed
728
729
730
731
		expr_tree_ref (new_cell->parsed_node);

	if (new_cell->text)
		string_ref (new_cell->text);
732
733
734
735

	if (new_cell->entered_text)
		string_ref (new_cell->entered_text);

736
737
/*	if (new_cell->style)
	new_cell->style = style_duplicate (new_cell->style);*/
Morten Welinder's avatar
Morten Welinder committed
738
739
740

	if (new_cell->render_color)
		style_color_ref (new_cell->render_color);
741
742
743

	if (new_cell->value)
		new_cell->value = value_duplicate (new_cell->value);
Arturo Espinosa's avatar
Arturo Espinosa committed
744

Morten Welinder's avatar
Morten Welinder committed
745
746
	if (cell->comment) {
		new_cell->comment = NULL;
747
		cell_set_comment (new_cell, cell->comment->comment->str);
Morten Welinder's avatar
Morten Welinder committed
748
	}
749

Arturo Espinosa's avatar
Arturo Espinosa committed
750
751
752
753
754
755
756
757
	return new_cell;
}

void
cell_destroy (Cell *cell)
{
	g_return_if_fail (cell != NULL);

Morten Welinder's avatar
Morten Welinder committed
758
759
760
761
762
763
	if (cell_hash_queue && g_hash_table_lookup (cell_hash_queue, cell)) {
		g_warning ("FIXME: Deleting cell %s which was queued for redraw",
			   cell_name (cell->col->pos, cell->row->pos));
		g_hash_table_remove (cell_hash_queue, cell);
	}

764
	cell_modified (cell);
765
	cell_cleanout (cell);
Arturo Espinosa's avatar
Arturo Espinosa committed
766

767
768
	if (cell->render_color)
		style_color_unref (cell->render_color);
Morten Welinder's avatar
Morten Welinder committed
769
	cell->render_color = (void *)0xdeadbeef;
770

771
	cell_comment_destroy (cell);
772
773

	if (cell->text)
774
		string_unref (cell->text);
Morten Welinder's avatar
Morten Welinder committed
775
776
	cell->text = (void *)0xdeadbeef;

777
/*	cell->style = (void *)0xdeadbeef;*/
778

779
	g_free (cell);
Arturo Espinosa's avatar
Arturo Espinosa committed
780
}
Miguel de Icaza's avatar
Today:    
Miguel de Icaza committed
781

782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
void
cell_freeze_redraws (void)
{
	redraws_frozen++;
	if (redraws_frozen == 1)
		cell_hash_queue = g_hash_table_new (g_direct_hash, g_direct_equal);
}

static void
call_cell_queue_redraw (gpointer key, gpointer value, gpointer user_data)
{
	cell_queue_redraw (value);
}

void
cell_thaw_redraws (void)
{
	redraws_frozen--;
800
	if (redraws_frozen < 0) {
801
802
803
		g_warning ("unbalanced freeze/thaw\n");
		return;
	}
804
	if (redraws_frozen == 0) {
805
806
807
808
809
		g_hash_table_foreach (cell_hash_queue, call_cell_queue_redraw, NULL);
		g_hash_table_destroy (cell_hash_queue);
		cell_hash_queue = NULL;
	}
}
Michael Meeks's avatar
Michael Meeks committed
810
811
812
813
814
815
816
817
818
819
820
821
822
void
cell_deep_freeze_redraws (void)
{
	redraws_deep_frozen++;
}

void
cell_deep_thaw_redraws (void)
{
	redraws_deep_frozen--;
	if (redraws_frozen < 0)
		g_warning ("unbalanced deep freeze/thaw\n");
}
823
824
825
826
827
828
829
830
831

static void
queue_cell (Cell *cell)
{
	if (g_hash_table_lookup (cell_hash_queue, cell))
		return;
	g_hash_table_insert (cell_hash_queue, cell, cell);
}

Miguel de Icaza's avatar
Today:    
Miguel de Icaza committed
832
833
834
void
cell_queue_redraw (Cell *cell)
{
Michael Meeks's avatar
Michael Meeks committed
835
	/* You wake up dead after a deep freeze */
836
	if (redraws_deep_frozen > 0)
Michael Meeks's avatar
Michael Meeks committed
837
838
		return;

839
	g_return_if_fail (cell != NULL);
840

841
	if (redraws_frozen) {
842
843
844
		queue_cell (cell);
		return;
	}
Morten Welinder's avatar
Morten Welinder committed
845

Miguel de Icaza's avatar
Today:    
Miguel de Icaza committed
846
847
848
849
850
	sheet_redraw_cell_region (cell->sheet,
				  cell->col->pos, cell->row->pos,
				  cell->col->pos, cell->row->pos);
}

851
852
853
854
855
856
857
858
859
/*
 * cell_set_format_simple:
 *
 * This routine is similar to cell_set_format, but it does not queue
 * any redraws, nor expects the cell to have a value.
 *
 * Make sure you queue a draw in the future for this cell.
 */
void
860
cell_set_format_simple (Cell *cell, const char *format)
861
{
862
	MStyle *mstyle = mstyle_new ();
863

864
	mstyle_set_format (mstyle, format);
865

866
	cell_set_mstyle (cell, mstyle);
867
868
}

869
870
871
872
873
874
/*
 * cell_set_format:
 *
 * Changes the format for CELL to be FORMAT.  FORMAT should be
 * a number display format as specified on the manual
 */
Miguel de Icaza's avatar
Today:    
Miguel de Icaza committed
875
void
876
cell_set_format (Cell *cell, const char *format)
Miguel de Icaza's avatar
Today:    
Miguel de Icaza committed
877
{
878
	g_return_if_fail (cell != NULL);
879

880
	cell_set_format_simple (cell, format);
881

882
883
884
	/* re-render the cell text */
	cell_render_value (cell);
	cell_queue_redraw (cell);
885
886
}

887
888
889
890
void
cell_comment_reposition (Cell *cell)
{
	GList *l;
891

892
893
	g_return_if_fail (cell != NULL);
	g_return_if_fail (cell->comment != NULL);
894

895
	/* FIXME : This should use the sheet_view list */
896
897
898
899
900
901
902
903
	for (l = cell->comment->realized_list; l; l = l->next){
		GnomeCanvasItem *o = l->data;
		SheetView *sheet_view = GNUMERIC_SHEET (o->canvas)->sheet_view;

		sheet_view_comment_relocate (sheet_view, cell->col->pos, cell->row->pos, o);
	}
}

904
/*
905
 * cell_relocate:
906
907
 * @cell:           The cell that is changing position
 * @check_bonunds : Should expressions be bounds checked.
908
909
 *
 * This routine is used to move a cell to a different location:
910
911
 *
 * Auxiliary items canvas items attached to the cell are moved.
912
 */
Arturo Espinosa's avatar
Arturo Espinosa committed
913
void
914
cell_relocate (Cell *cell, gboolean const check_bounds)
Arturo Espinosa's avatar
Arturo Espinosa committed
915
916
{
	g_return_if_fail (cell != NULL);
917
918

	/* 1. Tag the cell as modified */
919
920
	cell_modified (cell);

921
	/* 2. If the cell contains a formula, relocate the formula */
Michael Meeks's avatar
Michael Meeks committed
922
	if (cell->parsed_node) {
923
		sheet_cell_formula_unlink (cell);
924
925
926
927

		/*
		 * WARNING WARNING WARNING
		 *
928
		 * This will only work if the new array cell has already
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
		 * been inserted.
		 *
		 * WARNING WARNING WARNING
		 */
		/* If cell was part of an array, reset the corner pointer */
		if (cell->parsed_node->oper == OPER_ARRAY) {
			int const x = cell->parsed_node->u.array.x;
			int const y = cell->parsed_node->u.array.y;
			if (x != 0 || y != 0)
				cell->parsed_node->u.array.corner.cell =
					sheet_cell_get (cell->sheet,
							cell->col->pos - x,
							cell->row->pos - y);
		}

944
945
946
947
948
		/* We do not actually need to change any references
		 * the move is from its current location to its current
		 * location.  All the move is doing is a bounds check.
		 */
		if (check_bounds) {
949
950
			ExprRelocateInfo	rinfo;
			EvalPosition 		pos;
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
			ExprTree    	*expr = cell->parsed_node;

			rinfo.origin.start.col =
				rinfo.origin.end.col = cell->col->pos;
			rinfo.origin.start.row =
				rinfo.origin.end.row = cell->row->pos;
			rinfo.origin_sheet = rinfo.target_sheet = cell->sheet;
			rinfo.col_offset = 0;
			rinfo.row_offset = 0;
			expr = expr_relocate (expr, eval_pos_cell (&pos, cell), &rinfo);

			if (expr != NULL) {
				expr_tree_unref (cell->parsed_node);
				cell->parsed_node = expr;
			}
		}

968
		/* The following call also relinks the cell.  */
969
		cell_formula_changed (cell);
970
	}
971

972
	/* 3. Move any auxiliary canvas items */
Miguel de Icaza's avatar
Miguel de Icaza committed
973
974
	if (cell->comment)
		cell_comment_reposition (cell);
Michael Meeks's avatar
Michael Meeks committed
975
976
977

	/* 4. Tag the contents as having changed */
	cell_content_changed (cell);
Arturo Espinosa's avatar
Arturo Espinosa committed
978
979
980
981
982
983
984
985
986
987
988
}

/*
 * This routine drops the formula and just keeps the value
 */
void
cell_make_value (Cell *cell)
{
	g_return_if_fail (cell != NULL);
	g_return_if_fail (cell->parsed_node != NULL);

989
	/* FIXME: does this work at all?  -- MW */
990
	cell_modified (cell);
Arturo Espinosa's avatar
Arturo Espinosa committed
991
}
992
993

int
994
cell_get_horizontal_align (const Cell *cell, int align)
995
{
996
	g_return_val_if_fail (cell != NULL, HALIGN_RIGHT);
997

998
999
1000
	if (align == HALIGN_GENERAL) {
		if (cell->value) {
			if (cell->value->type == VALUE_FLOAT ||
1001
			    cell->value->type == VALUE_INTEGER)
1002
				align = HALIGN_RIGHT;
1003
			else
1004
				align = HALIGN_LEFT;
1005
		} else
1006
1007
1008
1009
			align = HALIGN_RIGHT;
	}

	return align;
1010
1011
}

1012
gboolean inline
1013
cell_is_number (const Cell *cell)
1014
{
1015
	return cell->value && VALUE_IS_NUMBER (cell->value);
1016
}
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
gboolean
cell_is_zero (const Cell *cell)
{
	Value const * const v = cell->value;
	if (v == NULL)
		return FALSE;
	switch (v->type) {
	case VALUE_BOOLEAN : return !v->v.v_bool;
	case VALUE_INTEGER : return v->v.v_int == 0;
	case VALUE_FLOAT :
	{
		double const res = v->v.v_float;
		return (-1e-10 < res && res < 1e-10);
	}

	default :
		return FALSE;
	}
}
1036
1037

static inline int
1038
cell_contents_fit_inside_column (const Cell *cell)
1039
{
1040
	if (cell->width_pixel <= COL_INTERNAL_WIDTH (cell->col))
1041
1042
1043
1044
1045
1046
		return TRUE;
	else
		return FALSE;
}

/*
1047
 * cell_calculate_span:
1048
1049
1050
1051
1052
1053
1054
 * @cell:   The cell we will examine
 * @col1:   return value: the first column used by this cell
 * @col2:   return value: the last column used by this cell
 *
 * This routine returns the column interval used by a Cell.
 */
void
1055
1056
cell_calculate_span (Cell const * const cell,
		     int * const col1, int * const col2)
1057
1058
1059
1060
{
	Sheet *sheet;
	int align, left;
	int row, pos, margin;
1061
	MStyle *mstyle;
1062

1063
1064
	g_return_if_fail (cell != NULL);

1065
1066
1067
1068
        /*
	 * If the cell is a number, or the text fits inside the column, or the
	 * alignment modes are set to "justify", then we report only one
	 * column is used.
1069
	 */
1070

1071
	if (cell_is_number (cell)) {
1072
1073
1074
1075
1076
		*col1 = *col2 = cell->col->pos;
		return;
	}

	sheet = cell->sheet;
1077
1078
1079
1080
	mstyle = sheet_style_compute (cell->sheet, cell->col->pos,
				      cell->row->pos);
	align = cell_get_horizontal_align (cell,
					   mstyle_get_align_h (mstyle));
1081
1082
	row   = cell->row->pos;

1083
1084
1085
	if ((cell_contents_fit_inside_column (cell) &&
	     align != HALIGN_CENTER_ACROSS_SELECTION) ||
	    align == HALIGN_JUSTIFY ||
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
	    align == HALIGN_FILL ||
	    mstyle_get_fit_in_cell (mstyle) ||
	    mstyle_get_align_v (mstyle) == VALIGN_JUSTIFY) {
		*col1 = *col2 = cell->col->pos;
		mstyle_unref (mstyle);
		return;
	}
	mstyle_unref (mstyle);

	switch (align) {
1096
1097
1098
	case HALIGN_LEFT:
		*col1 = *col2 = cell->col->pos;
		pos = cell->col->pos + 1;
1099
		left = cell->width_pixel - COL_INTERNAL_WIDTH (cell->col);
1100
		margin = cell->col->margin_b;
1101

1102
1103
1104
1105
1106
1107
		for (; left > 0 && pos < SHEET_MAX_COLS-1; pos++){
			ColRowInfo *ci;
			Cell *sibling;

			sibling = sheet_cell_get (sheet, pos, row);

Arturo Espinosa's avatar
Arturo Espinosa committed
1108
			if (!cell_is_blank (sibling))
1109
1110
1111
				return;

			ci = sheet_col_get_info (sheet, pos);
1112

1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
			/* The space consumed is:
			 *    - The margin_b from the last column
			 *    - The width of the cell
			 */
			left -= COL_INTERNAL_WIDTH (ci) +
				margin + ci->margin_a;
			margin = ci->margin_b;
			(*col2)++;
		}
		return;
1123

1124
1125
1126
	case HALIGN_RIGHT:
		*col1 = *col2 = cell->col->pos;
		pos = cell->col->pos - 1;
1127
		left = cell->width_pixel - COL_INTERNAL_WIDTH (cell->col);
1128
		margin = cell->col->margin_a;
1129

1130
1131
1132
1133
1134
1135
		for (; left > 0 && pos >= 0; pos--){
			ColRowInfo *ci;
			Cell *sibling;

			sibling = sheet_cell_get (sheet, pos, row);

Arturo Espinosa's avatar
Arturo Espinosa committed
1136
			if (!cell_is_blank (sibling))
1137
1138
1139
1140
1141
1142
				return;

			ci = sheet_col_get_info (sheet, pos);

			/* The space consumed is:
			 *   - The margin_a from the last column
Jody Goldberg's avatar
Jody Goldberg committed
1143
			 *   - The width of this cell
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
			 */
			left -= COL_INTERNAL_WIDTH (ci) +
				margin + ci->margin_b;
			margin = ci->margin_a;
			(*col1)--;
		}
		return;

	case HALIGN_CENTER: {
		int left_left, left_right;
		int margin_a, margin_b;
1155

1156
		*col1 = *col2 = cell->col->pos;
1157
		left = cell->width_pixel -  COL_INTERNAL_WIDTH (cell->col);
1158

1159
1160
1161
1162
		left_left  = left / 2 + (left % 2);
		left_right = left / 2;
		margin_a = cell->col->margin_a;
		margin_b = cell->col->margin_b;
1163

1164
1165
1166
1167
		for (; left_left > 0 || left_right > 0;){
			ColRowInfo *ci;
			Cell *left_sibling, *right_sibling;

Arturo Espinosa's avatar