cell.c 34.5 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 "format.h"
16
#include "color.h"
17
#include "border.h"
18
#include "cursors.h"
19
#include "utils.h"
20
#include "gnumeric-util.h"
21
#include "application.h"
Arturo Espinosa's avatar
Arturo Espinosa committed
22

23
24
static int         redraws_frozen           = 0;
static int         redraws_deep_frozen      = 0;
25
26
static GHashTable *cell_hash_queue;

27
static void
28
29
30
cell_formula_changed (Cell *cell)
{
	g_return_if_fail (cell != NULL);
31

32
	sheet_cell_formula_link (cell);
33

34
35
36
	cell_queue_recalc (cell);
}

37
38
39
static inline void
cell_modified (Cell *cell)
{
40
	Sheet *sheet = cell->sheet;
41
42
43
44

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

Morten Welinder's avatar
Morten Welinder committed
47

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


Jody Goldberg's avatar
typo.    
Jody Goldberg committed
72
void
73
cell_set_formula (Cell *cell, const char *text)
Arturo Espinosa's avatar
Arturo Espinosa committed
74
{
75
	ExprTree *new_expr;
76
	char *error_msg = _("ERROR");
Morten Welinder's avatar
Morten Welinder committed
77
	char *desired_format = NULL;
78
	ParsePosition pp;
Morten Welinder's avatar
Morten Welinder committed
79
	gboolean may_set_format;
80

Arturo Espinosa's avatar
Arturo Espinosa committed
81
82
	g_return_if_fail (cell != NULL);
	g_return_if_fail (text != NULL);
83
	g_return_if_fail (gnumeric_char_start_expr_p (*text));
84

Morten Welinder's avatar
Morten Welinder committed
85
86
	may_set_format = !cell_has_assigned_format (cell);

87
	cell_modified (cell);
88
	new_expr = expr_parse_string (text+1, /* Ignore leading char (=,@,+) */
89
				      parse_pos_cell (&pp, cell),
Morten Welinder's avatar
Morten Welinder committed
90
				      may_set_format ? &desired_format : NULL,
91
				      &error_msg);
92
93
	cell_cleanout (cell);

94
	if (new_expr == NULL) {
95
		cell_set_rendered_text (cell, error_msg);
96
97
98
		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
99
		return;
100
101
	}

Morten Welinder's avatar
Morten Welinder committed
102
103
104
	/* 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
105
	if (desired_format) {
106
		cell_set_format (cell, desired_format);
Morten Welinder's avatar
Morten Welinder committed
107
108
		g_free (desired_format);
	}
Miguel de Icaza's avatar
New:    
Miguel de Icaza committed
109

110
	if (new_expr->oper == OPER_ARRAY) {
111
		/* The corner sets up the entire array block */
112
		if (new_expr->u.array.x != 0 || new_expr->u.array.y != 0) {
113
114
115
			/* Throw away the expression, including the inner
			   one.  */
			expr_tree_unref (new_expr->u.array.corner.func.expr);
116
117
118
119
			expr_tree_unref (new_expr);
			return;
		}

120
		/*
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
		 * 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
137
}
Arturo Espinosa's avatar
Arturo Espinosa committed
138

139
140
141
142
143
void
cell_comment_destroy (Cell *cell)
{
	CellComment *comment;
	GList *l;
144

145
146
147
148
149
150
151
152
153
	g_return_if_fail (cell != NULL);

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

	/* Free resources */
	string_unref (comment->comment);
154
155
156
157
158

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

	if (comment->window)
159
		gtk_object_destroy (GTK_OBJECT (comment->window));
160

161
	for (l = comment->realized_list; l; l = l->next)
162
		gtk_object_destroy (l->data);
Morten Welinder's avatar
Morten Welinder committed
163
	g_list_free (comment->realized_list);
164

165
166
167
	g_free (comment);
}

168
169
170
171
172
173
174
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;
	}
175

176
}
177

178
179
180
181
182
static void
cell_display_comment (Cell *cell)
{
	GtkWidget *window, *label;
	int x, y;
183

184
185
	g_return_if_fail (cell != NULL);

186
	cell_comment_cancel_timer (cell);
187

188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
	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;
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
	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;
229

230
231
232
233
234
235
	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:
236
		cell_comment_cancel_timer (cell);
237
		if (cell->comment->window){
238
			gtk_object_destroy (GTK_OBJECT (cell->comment->window));
239
240
241
			cell->comment->window = NULL;
		}
		break;
242

243
244
245
246
247
248
	default:
		return FALSE;
	}
	return TRUE;
}

249
250
251
252
253
254
255
256
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
257
	for (l = cell->sheet->sheet_views; l; l = l->next){
258
259
		SheetView *sheet_view = SHEET_VIEW (l->data);
		GnomeCanvasItem *o;
260

261
		o = sheet_view_comment_create_marker (
262
			sheet_view,
263
			cell->col->pos, cell->row->pos);
264
		gtk_object_ref (GTK_OBJECT (o));
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284

		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;

285
		gtk_object_unref (GTK_OBJECT (o));
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
	}
	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);
}

309
void
310
cell_set_comment (Cell *cell, const char *str)
311
312
{
	int had_comments = FALSE;
313

314
315
316
317
318
319
320
321
322
323
324
	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;
325

326
327
328
329
330
	cell->comment->comment = string_get (str);

	if (had_comments)
		cell_queue_redraw (cell);

331
332
	if (cell->sheet)
		cell_comment_realize (cell);
333
334
}

Miguel de Icaza's avatar
Miguel de Icaza committed
335
336
337
338
339
340
341
342
/*
 * 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
 */
Miguel de Icaza's avatar
Today:    
Miguel de Icaza committed
343
void
344
cell_set_rendered_text (Cell *cell, const char *rendered_text)
Miguel de Icaza's avatar
Today:    
Miguel de Icaza committed
345
{
346
	String *oldtext;
347

Miguel de Icaza's avatar
Today:    
Miguel de Icaza committed
348
349
	g_return_if_fail (cell != NULL);
	g_return_if_fail (rendered_text != NULL);
350

351
	cell_modified (cell);
Miguel de Icaza's avatar
Today:    
Miguel de Icaza committed
352

353
	oldtext = cell->text;
Miguel de Icaza's avatar
Today:    
Miguel de Icaza committed
354
	cell->text = string_get (rendered_text);
355
356
	if (oldtext)
		string_unref (oldtext);
357

Miguel de Icaza's avatar
Today:    
Miguel de Icaza committed
358
359
360
	cell_calc_dimensions (cell);
}

361
char *
362
363
364
365
cell_get_formatted_val (Cell *cell, StyleColor **col)
{
	MStyle *mstyle;
	char *txt;
366

367
368
369
370
371
372
373
374
375
376
377
378
379
	mstyle = sheet_style_compute (cell->sheet, cell->col->pos,
				      cell->row->pos);
	if (mstyle_is_element_set (mstyle, MSTYLE_FORMAT))
		txt = format_value (mstyle_get_format (mstyle),
				    cell->value, col);
	else {
		g_warning ("No format: serious error");
		txt = g_strdup ("Error");
	}
	mstyle_unref (mstyle);
	return txt;
}

Miguel de Icaza's avatar
Miguel de Icaza committed
380
381
382
383
/*
 * cell_render_value
 * @cell: The cell whose value needs to be rendered
 *
384
385
386
 * 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
387
 */
Miguel de Icaza's avatar
Today:    
Miguel de Icaza committed
388
389
390
void
cell_render_value (Cell *cell)
{
391
	StyleColor *color;
Miguel de Icaza's avatar
Today:    
Miguel de Icaza committed
392
	char *str;
393

Miguel de Icaza's avatar
Today:    
Miguel de Icaza committed
394
395
396
	g_return_if_fail (cell != NULL);
	g_return_if_fail (cell->value != NULL);

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

405
	if (cell->sheet->display_formulas && cell->parsed_node){
406
407
408
409
410
411
412
413
414
		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;
	}
415

Miguel de Icaza's avatar
Today:    
Miguel de Icaza committed
416
417
418
419
	cell_set_rendered_text (cell, str);
	g_free (str);
}

Michael Meeks's avatar
Michael Meeks committed
420
421
422
423
424
425
426
427
428
429
430
431
432
433
/*
 * 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
434
cell_set_value_simple (Cell *cell, Value *v)
Michael Meeks's avatar
Michael Meeks committed
435
436
437
438
439
{
	g_return_if_fail (cell);
	g_return_if_fail (v);

	cell_modified (cell);
440
	cell_cleanout (cell);
Michael Meeks's avatar
Michael Meeks committed
441
442
443
444
445
446
447
448
449
450
451

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

/*
 * cell_set_value
 *
 * Changes the value of a cell
 */
void
Morten Welinder's avatar
Morten Welinder committed
452
cell_set_value (Cell *cell, Value *v)
Michael Meeks's avatar
Michael Meeks committed
453
454
455
{
	g_return_if_fail (cell);
	g_return_if_fail (v);
456

Michael Meeks's avatar
Michael Meeks committed
457
458
459
460
461
462
463
464
	cell_queue_redraw (cell);

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

	cell_queue_redraw (cell);
}

465
466
467
468
469
470
471
472
473
474
475
476
/*
 * 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
477
478
 *
 *    - It stores the rendered value of the text as if that is what was entered.
479
 */
480
void
481
cell_set_text_simple (Cell *cell, const char *text)
482
{
Arturo Espinosa's avatar
Arturo Espinosa committed
483
484
	g_return_if_fail (cell != NULL);
	g_return_if_fail (text != NULL);
Arturo Espinosa's avatar
Arturo Espinosa committed
485

486
	cell_modified (cell);
487
	cell_cleanout (cell);
488

489
	if (gnumeric_char_start_expr_p (*text) && text[1] != '\0')
490
		cell_set_formula (cell, text);
491
	else {
492
493
		char *end;
		long l;
494
		int  set=0;
495

Morten Welinder's avatar
Morten Welinder committed
496
		l = strtol (text, &end, 10);
497
498
		if (l != LONG_MAX && l != LONG_MIN &&
		    text != end && (l == (int)l)) {
499
500
501
			/* Allow and ignore spaces at the end of integers.  */
			while (*end == ' ')
				end++;
502
503
504
505
506
			if (*end == 0) {
				cell->value = value_new_int (l);
				set = 1;
			}
		}
507

508
		if (!set) {
509
			double d;
Morten Welinder's avatar
Morten Welinder committed
510
			d = strtod (text, &end);
511
			if (text != end && *end == 0) {
512
513
				/* It is a floating point number.  */
				cell->value = value_new_float ((float_t)d);
Arturo Espinosa's avatar
Arturo Espinosa committed
514
			} else {
515
516
				/* It is text.  */
				cell->value = value_new_string (text);
Arturo Espinosa's avatar
Arturo Espinosa committed
517
518
			}
		}
519

Miguel de Icaza's avatar
Today:    
Miguel de Icaza committed
520
		cell_render_value (cell);
Jody Goldberg's avatar
Jody Goldberg committed
521
522
523
		cell->entered_text = (cell->text != NULL)
		    ? string_ref (cell->text)
		    : string_get (text);
524
	}
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
}

/*
 * 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
540
	/* Queue all of the dependencies for this cell */
541
	deps = cell_get_dependencies (cell);
Arturo Espinosa's avatar
Arturo Espinosa committed
542
	if (deps)
Morten Welinder's avatar
Morten Welinder committed
543
		cell_queue_recalc_list (deps, TRUE);
544
545
}

546

547
548
549
/*
 * cell_set_text
 *
Jody Goldberg's avatar
Jody Goldberg committed
550
551
 * Changes the content of a cell and stores
 * the actual entered text.
552
553
 */
void
554
cell_set_text (Cell *cell, const char *text)
555
556
557
{
	g_return_if_fail (cell != NULL);
	g_return_if_fail (text != NULL);
558

559
	if (cell->parsed_node != NULL && cell->parsed_node->oper == OPER_ARRAY) {
560
561
562
563
		gnumeric_no_modify_array_notice (cell->sheet->workbook);
		return;
	}

564
565
	if (*text == 0)
		g_warning ("Cell value being set to empty string");
566

567
568
569
	cell_queue_redraw (cell);

	cell_set_text_simple (cell, text);
Jody Goldberg's avatar
Jody Goldberg committed
570
571

	/* Store the real entered text */
572
573
574
575
	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
576
	}
577
	cell_content_changed (cell);
578

579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
	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);

600
	/* Ref before unref.  Repeat after me.  */
Morten Welinder's avatar
Morten Welinder committed
601
602
	expr_tree_ref (formula);

603
604
	cell_modified (cell);
	cell_cleanout (cell);
605
606

	cell->parsed_node = formula;
607
608
	/* Until the value is recomputed, we put in this value.  */
	cell->value = value_new_error (NULL, _("Circular reference"));
609
610
611
	cell_formula_changed (cell);
}

612
613
614
615
616
617
618
619
620
/**
 * 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
 *
621
 * Uses cell_set_formula_tree_simple to store the formula as an
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
 * '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;
637
	ExprTree *wrapper;
638
639
640
641
642
643
644
645
646
647
648

	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);
649
	expr_tree_unref (wrapper);
650
651
652
653
654

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

	for (x = 0; x < num_cols; ++x)
655
		for (y = 0; y < num_rows; ++y) {
656
657
			if (x == 0 && y == 0)
				continue;
Michael Meeks's avatar
Michael Meeks committed
658
			cell = sheet_cell_fetch (sheet, col_a + x, row_a + y);
659
660
661
662
			wrapper = expr_tree_array_formula (x, y,
							   num_rows, num_cols);
			wrapper->u.array.corner.cell = corner;
			cell_set_formula_tree_simple (cell, wrapper);
663
			expr_tree_unref (wrapper);
664
665
666
667
668
669
		}

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

670
671
672
673
674
675
676
677
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);
678
	cell_content_changed (cell);
Arturo Espinosa's avatar
Today:    
Arturo Espinosa committed
679
680

	cell_queue_redraw (cell);
Arturo Espinosa's avatar
Arturo Espinosa committed
681
}
Arturo Espinosa's avatar
Arturo Espinosa committed
682

683
684
685
686
/**
 * cell_copy:
 * @cell: existing cell to duplicate
 *
687
 * Makes a copy of a Cell.
688
 *
689
 * Returns a copy of the cell.
Arturo Espinosa's avatar
Arturo Espinosa committed
690
691
 */
Cell *
692
cell_copy (const Cell *cell)
Arturo Espinosa's avatar
Arturo Espinosa committed
693
694
695
696
697
698
699
700
701
702
{
	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
703
	new_cell->flags &= ~CELL_QUEUED_FOR_RECALC;
704

Morten Welinder's avatar
Morten Welinder committed
705
	/* now copy properly the rest */
706
	if (new_cell->parsed_node)
Morten Welinder's avatar
Morten Welinder committed
707
708
709
710
		expr_tree_ref (new_cell->parsed_node);

	if (new_cell->text)
		string_ref (new_cell->text);
711
712
713
714

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

715
716
/*	if (new_cell->style)
	new_cell->style = style_duplicate (new_cell->style);*/
Morten Welinder's avatar
Morten Welinder committed
717
718
719

	if (new_cell->render_color)
		style_color_ref (new_cell->render_color);
720
721
722

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

Morten Welinder's avatar
Morten Welinder committed
724
725
	if (cell->comment) {
		new_cell->comment = NULL;
726
		cell_set_comment (new_cell, cell->comment->comment->str);
Morten Welinder's avatar
Morten Welinder committed
727
	}
728

Arturo Espinosa's avatar
Arturo Espinosa committed
729
730
731
732
733
734
735
736
	return new_cell;
}

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

Morten Welinder's avatar
Morten Welinder committed
737
738
739
740
741
742
	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);
	}

743
	cell_modified (cell);
744
	cell_cleanout (cell);
Arturo Espinosa's avatar
Arturo Espinosa committed
745

746
747
	if (cell->render_color)
		style_color_unref (cell->render_color);
Morten Welinder's avatar
Morten Welinder committed
748
	cell->render_color = (void *)0xdeadbeef;
749

750
	cell_comment_destroy (cell);
751
752

	if (cell->text)
753
		string_unref (cell->text);
Morten Welinder's avatar
Morten Welinder committed
754
755
	cell->text = (void *)0xdeadbeef;

756
/*	cell->style = (void *)0xdeadbeef;*/
757

758
	g_free (cell);
Arturo Espinosa's avatar
Arturo Espinosa committed
759
}
Miguel de Icaza's avatar
Today:    
Miguel de Icaza committed
760

761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
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--;
779
	if (redraws_frozen < 0) {
780
781
782
		g_warning ("unbalanced freeze/thaw\n");
		return;
	}
783
	if (redraws_frozen == 0) {
784
785
786
787
788
		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
789
790
791
792
793
794
795
796
797
798
799
800
801
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");
}
802
803
804
805
806
807
808
809
810

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
811
812
813
void
cell_queue_redraw (Cell *cell)
{
Michael Meeks's avatar
Michael Meeks committed
814
	/* You wake up dead after a deep freeze */
815
	if (redraws_deep_frozen > 0)
Michael Meeks's avatar
Michael Meeks committed
816
817
		return;

818
	g_return_if_fail (cell != NULL);
819

820
	if (redraws_frozen) {
821
822
823
		queue_cell (cell);
		return;
	}
Morten Welinder's avatar
Morten Welinder committed
824

Miguel de Icaza's avatar
Today:    
Miguel de Icaza committed
825
826
827
828
829
	sheet_redraw_cell_region (cell->sheet,
				  cell->col->pos, cell->row->pos,
				  cell->col->pos, cell->row->pos);
}

830
831
832
833
834
835
836
837
838
/*
 * 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
839
cell_set_format_simple (Cell *cell, const char *format)
840
{
841
	MStyle *mstyle = mstyle_new ();
842

843
	mstyle_set_format (mstyle, format);
844

845
	cell_set_mstyle (cell, mstyle);
846
847
}

848
849
850
851
852
853
/*
 * 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
854
void
855
cell_set_format (Cell *cell, const char *format)
Miguel de Icaza's avatar
Today:    
Miguel de Icaza committed
856
{
857
	g_return_if_fail (cell != NULL);
858

859
	cell_set_format_simple (cell, format);
860

861
862
863
	/* re-render the cell text */
	cell_render_value (cell);
	cell_queue_redraw (cell);
864
865
}

866
867
868
869
void
cell_comment_reposition (Cell *cell)
{
	GList *l;
870

871
872
	g_return_if_fail (cell != NULL);
	g_return_if_fail (cell->comment != NULL);
873

874
	/* FIXME : This should use the sheet_view list */
875
876
877
878
879
880
881
882
	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);
	}
}

883
/*
884
 * cell_relocate:
885
886
 * @cell:           The cell that is changing position
 * @check_bonunds : Should expressions be bounds checked.
887
888
 *
 * This routine is used to move a cell to a different location:
889
890
 *
 * Auxiliary items canvas items attached to the cell are moved.
891
 */
Arturo Espinosa's avatar
Arturo Espinosa committed
892
void
893
cell_relocate (Cell *cell, gboolean const check_bounds)
Arturo Espinosa's avatar
Arturo Espinosa committed
894
895
{
	g_return_if_fail (cell != NULL);
896
897

	/* 1. Tag the cell as modified */
898
899
	cell_modified (cell);

900
	/* 2. If the cell contains a formula, relocate the formula */
Michael Meeks's avatar
Michael Meeks committed
901
	if (cell->parsed_node) {
902
		sheet_cell_formula_unlink (cell);
903
904
905
906

		/*
		 * WARNING WARNING WARNING
		 *
907
		 * This will only work if the new array cell has already
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
		 * 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);
		}

923
924
925
926
927
		/* 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) {
928
929
			ExprRelocateInfo	rinfo;
			EvalPosition 		pos;
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
			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;
			}
		}

947
		/* The following call also relinks the cell.  */
948
		cell_formula_changed (cell);
949
	}
950

951
	/* 3. Move any auxiliary canvas items */
Miguel de Icaza's avatar
Miguel de Icaza committed
952
953
	if (cell->comment)
		cell_comment_reposition (cell);
Michael Meeks's avatar
Michael Meeks committed
954
955
956

	/* 4. Tag the contents as having changed */
	cell_content_changed (cell);
Arturo Espinosa's avatar
Arturo Espinosa committed
957
958
959
960
961
962
963
964
965
966
967
}

/*
 * 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);

968
	/* FIXME: does this work at all?  -- MW */
969
	cell_modified (cell);
Arturo Espinosa's avatar
Arturo Espinosa committed
970
}
971
972

int
973
cell_get_horizontal_align (const Cell *cell, int align)
974
{
975
	g_return_val_if_fail (cell != NULL, HALIGN_RIGHT);
976

977
978
979
	if (align == HALIGN_GENERAL) {
		if (cell->value) {
			if (cell->value->type == VALUE_FLOAT ||
980
			    cell->value->type == VALUE_INTEGER)
981
				align = HALIGN_RIGHT;
982
			else
983
				align = HALIGN_LEFT;
984
		} else
985
986
987
988
			align = HALIGN_RIGHT;
	}

	return align;
989
990
}

991
gboolean inline
992
cell_is_number (const Cell *cell)
993
{
994
	return cell->value && VALUE_IS_NUMBER (cell->value);
995
}
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
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;
	}
}
1015
1016

static inline int
1017
cell_contents_fit_inside_column (const Cell *cell)
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
{
	if (cell->width < COL_INTERNAL_WIDTH (cell->col))
		return TRUE;
	else
		return FALSE;
}

/*
 * cell_get_span:
 * @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
cell_get_span (Cell *cell, int *col1, int *col2)
{
	Sheet *sheet;
	int align, left;
	int row, pos, margin;
1039
	MStyle *mstyle;
1040

1041
1042
	g_return_if_fail (cell != NULL);

1043
1044
1045
1046
        /*
	 * 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.
1047
	 */
1048

1049
	if (cell_is_number (cell) ||
1050
	    cell_contents_fit_inside_column (cell)) {
1051
1052
1053
1054
1055
		*col1 = *col2 = cell->col->pos;
		return;
	}

	sheet = cell->sheet;
1056
1057
1058
1059
	mstyle = sheet_style_compute (cell->sheet, cell->col->pos,
				      cell->row->pos);
	align = cell_get_horizontal_align (cell,
					   mstyle_get_align_h (mstyle));
1060
1061
	row   = cell->row->pos;

1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
	if (align == HALIGN_JUSTIFY ||
	    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) {
1073
1074
1075
1076
1077
	case HALIGN_LEFT:
		*col1 = *col2 = cell->col->pos;
		pos = cell->col->pos + 1;
		left = cell->width - COL_INTERNAL_WIDTH (cell->col);
		margin = cell->col->margin_b;
1078

1079
1080
1081
1082
1083
1084
		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
1085
			if (!cell_is_blank (sibling))
1086
1087
1088
				return;

			ci = sheet_col_get_info (sheet, pos);
1089

1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
			/* 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;
1100

1101
1102
1103
1104
1105
	case HALIGN_RIGHT:
		*col1 = *col2 = cell->col->pos;
		pos = cell->col->pos - 1;
		left = cell->width - COL_INTERNAL_WIDTH (cell->col);
		margin = cell->col->margin_a;
1106

1107
1108
1109
1110
1111
1112
		for (; left > 0 && pos >= 0; pos--){
			ColRowInfo *ci;
			Cell *sibling;

			sibling = sheet_cell_get (sheet, pos, row);

Arturo Espinosa's avatar
Arturo Espinosa committed
1113
			if (!cell_is_blank (sibling))
1114
1115
1116
1117
1118
1119
				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
1120
			 *   - The width of this cell
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
			 */
			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;
1132

1133
1134
		*col1 = *col2 = cell->col->pos;
		left = cell->width -  COL_INTERNAL_WIDTH (cell->col);
1135

1136
1137
1138
1139
		left_left  = left / 2 + (left % 2);
		left_right = left / 2;
		margin_a = cell->col->margin_a;
		margin_b = cell->col->margin_b;
1140

1141
1142
1143
1144
		for (; left_left > 0 || left_right > 0;){
			ColRowInfo *ci;
			Cell *left_sibling, *right_sibling;

Arturo Espinosa's avatar
Arturo Espinosa committed
1145
			if (*col1 - 1 >= 0){
1146
1147
				left_sibling = sheet_cell_get (sheet, *col1 - 1, row);

Arturo Espinosa's avatar
Arturo Espinosa committed
1148
				if (!cell_is_blank (left_sibling))
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
					left_left = 0;
				else {
					ci = sheet_col_get_info (sheet, *col1 - 1);

					left_left -= COL_INTERNAL_WIDTH (ci) +
						margin_a + ci->margin_b;
					margin_a = ci->margin_a;
					(*col1)--;
				}
			} else
				left_left = 0;

			if (*col2 + 1 < SHEET_MAX_COLS-1){
				right_sibling = sheet_cell_get (sheet, *col2 + 1, row);

Arturo Espinosa's avatar
Arturo Espinosa committed
1164
				if (!cell_is_blank (right_sibling))
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
					left_right = 0;
				else {
					ci = sheet_col_get_info (sheet, *col2 + 1);

					left_right -= COL_INTERNAL_WIDTH (ci) +
						margin_b + ci->margin_a;
					margin_b = ci->margin_b;
					(*col2)++;
				}
			} else
				left_right = 0;
1176

1177
		} /* for */
Arturo Espinosa's avatar
Arturo Espinosa committed
1178
		break;
1179