sheet.c 27.6 KB
Newer Older
Arturo Espinosa's avatar
Arturo Espinosa committed
1 2 3 4 5 6 7 8
/*
 * Sheet.c:  Implements the sheet management and per-sheet storage
 * (C) 1998 The Free Software Foundation
 *
 * Author:
 *  Miguel de Icaza (miguel@gnu.org)
 *
 */
9
#include <config.h>
Arturo Espinosa's avatar
Arturo Espinosa committed
10 11 12
#include <gnome.h>
#include "gnumeric.h"
#include "gnumeric-sheet.h"
Arturo Espinosa's avatar
Arturo Espinosa committed
13
#include "utils.h"
Arturo Espinosa's avatar
Arturo Espinosa committed
14

Arturo Espinosa's avatar
Arturo Espinosa committed
15
void
Arturo Espinosa's avatar
Arturo Espinosa committed
16 17 18 19 20 21 22
sheet_redraw_all (Sheet *sheet)
{
	gnome_canvas_request_redraw (
		GNOME_CANVAS (sheet->sheet_view),
		0, 0, INT_MAX, INT_MAX);
}

23 24 25 26 27 28 29 30 31 32 33 34 35 36
static void
recompute_one_cell (Sheet *sheet, int col, int row, Cell *cell, void *closure)
{
	
}

void
sheet_brute_force_recompute (Sheet *sheet)
{
	sheet_cell_foreach_range (sheet, 1, 0, 0, SHEET_MAX_COL, SHEET_MAX_ROW,
				  recompute_one_cell, NULL);
				  
}

Arturo Espinosa's avatar
Arturo Espinosa committed
37 38 39 40 41 42 43 44
static void
sheet_init_default_styles (Sheet *sheet)
{
	/* The default column style */
	sheet->default_col_style.pos        = -1;
	sheet->default_col_style.style      = style_new ();
	sheet->default_col_style.units      = 40;
	sheet->default_col_style.pixels     = 0;
45 46
	sheet->default_col_style.margin_a   = 1;
	sheet->default_col_style.margin_b   = 1;
Arturo Espinosa's avatar
Arturo Espinosa committed
47
	sheet->default_col_style.selected   = 0;
Arturo Espinosa's avatar
Arturo Espinosa committed
48
	sheet->default_col_style.data       = NULL;
Arturo Espinosa's avatar
Arturo Espinosa committed
49 50 51 52 53 54

	/* The default row style */
	sheet->default_row_style.pos      = -1;
	sheet->default_row_style.style    = style_new ();
	sheet->default_row_style.units    = 20;
	sheet->default_row_style.pixels   = 0;
55 56
	sheet->default_row_style.margin_a = 1;
	sheet->default_row_style.margin_b = 1;
Arturo Espinosa's avatar
Arturo Espinosa committed
57
	sheet->default_row_style.selected = 0;
Arturo Espinosa's avatar
Arturo Espinosa committed
58
	sheet->default_row_style.data     = NULL;
Arturo Espinosa's avatar
Arturo Espinosa committed
59 60 61
}

/* Initialize some of the columns and rows, to test the display engine */
62 63 64
static void
sheet_init_dummy_stuff (Sheet *sheet)
{
Arturo Espinosa's avatar
Arturo Espinosa committed
65
	ColRowInfo *cp, *rp;
66 67 68
	int x, y;

	for (x = 0; x < 40; x += 2){
Arturo Espinosa's avatar
Arturo Espinosa committed
69
		cp = sheet_row_new (sheet);
70 71
		cp->pos = x;
		cp->units = (x+1) * 30;
Arturo Espinosa's avatar
Arturo Espinosa committed
72
		sheet_col_add (sheet, cp);
73 74 75
	}

	for (y = 0; y < 6; y += 2){
Arturo Espinosa's avatar
Arturo Espinosa committed
76
		rp = sheet_row_new (sheet);
77 78
		rp->pos = y;
		rp->units = (20 * (y + 1));
79
		rp->selected = 0;
Arturo Espinosa's avatar
Arturo Espinosa committed
80
		sheet_row_add (sheet, rp);
81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104
	}
}

static GtkWidget *
new_canvas_bar (Sheet *sheet, GtkOrientation o, GnomeCanvasItem **itemp)
{
	GnomeCanvasGroup *group;
	GnomeCanvasItem *item;
	GtkWidget *canvas;
	int w, h;
	
	canvas = gnome_canvas_new (
		gtk_widget_get_default_visual (),
		gtk_widget_get_default_colormap ());

	if (o == GTK_ORIENTATION_VERTICAL){
		w = 60;
		h = 1;
	} else {
		w = 1;
		h = 20;
	}
	gnome_canvas_set_size (GNOME_CANVAS (canvas), w, h);
	group = GNOME_CANVAS_GROUP (GNOME_CANVAS (canvas)->root);
105
	item = gnome_canvas_item_new (group,
106 107 108 109 110
				      item_bar_get_type (),
				      "ItemBar::Sheet", sheet,
				      "ItemBar::Orientation", o,
				      "ItemBar::First", 0,
				      NULL);
Arturo Espinosa's avatar
Arturo Espinosa committed
111

112 113 114 115 116
	*itemp = GNOME_CANVAS_ITEM (item);
	gtk_widget_show (canvas);
	return canvas;
}

Arturo Espinosa's avatar
Arturo Espinosa committed
117 118 119
static void
sheet_col_selection_changed (ItemBar *item_bar, int column, Sheet *sheet)
{
120 121 122 123 124 125 126 127 128 129 130 131 132
	ColRowInfo *ci;

	ci = sheet_col_get (sheet, column);
	if (ci->selected)
		return;
	
	gnumeric_sheet_cursor_set (GNUMERIC_SHEET (sheet->sheet_view), column, 0);
	sheet_selection_clear_only (sheet);
	sheet_col_set_selection (sheet, ci, 1);
	sheet_selection_append_range (sheet,
				      column, 0,
				      column, 0,
				      column, SHEET_MAX_ROWS-1);
Arturo Espinosa's avatar
Arturo Espinosa committed
133 134 135 136 137 138
}

static void
sheet_col_size_changed (ItemBar *item_bar, int col, int width, Sheet *sheet)
{
	sheet_col_set_width (sheet, col, width);
139
	gnumeric_sheet_compute_visible_ranges (GNUMERIC_SHEET (sheet->sheet_view));
Arturo Espinosa's avatar
Arturo Espinosa committed
140 141 142 143 144
}

static void
sheet_row_selection_changed (ItemBar *item_bar, int row, Sheet *sheet)
{
145 146 147 148 149 150 151 152 153 154 155 156 157 158 159
	ColRowInfo *ri;

	ri = sheet_row_get (sheet, row);
	if (ri->selected)
		return;

	gnumeric_sheet_cursor_set (GNUMERIC_SHEET (sheet->sheet_view), 0, row);
	sheet_selection_clear_only (sheet);
	sheet_row_set_selection (sheet, ri, 1);
	sheet_selection_append_range (sheet,
				      0, row,
				      0, row,
				      SHEET_MAX_COLS-1, row);
	
	
Arturo Espinosa's avatar
Arturo Espinosa committed
160 161 162 163 164 165
}

static void
sheet_row_size_changed (ItemBar *item_bar, int row, int height, Sheet *sheet)
{
	sheet_row_set_height (sheet, row, height);
166
	gnumeric_sheet_compute_visible_ranges (GNUMERIC_SHEET (sheet->sheet_view));
Arturo Espinosa's avatar
Arturo Espinosa committed
167 168
}

169 170 171
static guint
cell_hash (gconstpointer key)
{
172
	CellPos *ca = (CellPos *) key;
173 174 175 176 177 178 179

	return (ca->row << 8) | ca->col;
}

static gint
cell_compare (gconstpointer a, gconstpointer b)
{
180
	CellPos *ca, *cb;
181

182 183
	ca = (CellPos *) a;
	cb = (CellPos *) b;
184 185 186 187 188 189 190 191 192

	if (ca->row != cb->row)
		return 0;
	if (ca->col != cb->col)
		return 0;
	
	return 1;
}

Arturo Espinosa's avatar
Arturo Espinosa committed
193 194 195 196
Sheet *
sheet_new (Workbook *wb, char *name)
{
	Sheet *sheet;
197
	int rows_shown, cols_shown;
Arturo Espinosa's avatar
Arturo Espinosa committed
198

199 200
	rows_shown = cols_shown = 40;
	
Arturo Espinosa's avatar
Arturo Espinosa committed
201
	sheet = g_new0 (Sheet, 1);
202
	sheet->signature = SHEET_SIGNATURE;
Arturo Espinosa's avatar
Arturo Espinosa committed
203 204
	sheet->parent_workbook = wb;
	sheet->name = g_strdup (name);
205 206
	sheet->last_zoom_factor_used = -1.0;
	sheet->toplevel = gtk_table_new (0, 0, 0);
207 208
	sheet->max_col_used = cols_shown;
	sheet->max_row_used = rows_shown;
209

210
	sheet->cell_hash = g_hash_table_new (cell_hash, cell_compare);
Arturo Espinosa's avatar
Arturo Espinosa committed
211 212
	sheet_init_default_styles (sheet);
	
213 214
	/* Dummy initialization */
	sheet_init_dummy_stuff (sheet);
215 216

	/* Create the gnumeric sheet and set the initial selection */
Arturo Espinosa's avatar
Arturo Espinosa committed
217
	sheet->sheet_view = gnumeric_sheet_new (sheet);
218 219
	sheet_selection_append (sheet, 0, 0);
	
Arturo Espinosa's avatar
Arturo Espinosa committed
220
	gtk_widget_show (sheet->sheet_view);
221 222 223 224 225 226 227 228 229 230 231
	gtk_widget_show (sheet->toplevel);
	
	gtk_table_attach (GTK_TABLE (sheet->toplevel), sheet->sheet_view,
			  1, 2, 1, 2,
			  GTK_FILL | GTK_EXPAND, GTK_FILL | GTK_EXPAND, 0, 0);

	/* Column canvas */
	sheet->col_canvas = new_canvas_bar (sheet, GTK_ORIENTATION_HORIZONTAL, &sheet->col_item);
	gtk_table_attach (GTK_TABLE (sheet->toplevel), sheet->col_canvas,
			  1, 2, 0, 1,
			  GTK_FILL | GTK_EXPAND, 0, 0, 0);
Arturo Espinosa's avatar
Arturo Espinosa committed
232 233 234 235 236 237
	gtk_signal_connect (GTK_OBJECT (sheet->col_item), "selection_changed",
			    GTK_SIGNAL_FUNC (sheet_col_selection_changed),
			    sheet);
	gtk_signal_connect (GTK_OBJECT (sheet->col_item), "size_changed",
			    GTK_SIGNAL_FUNC (sheet_col_size_changed),
			    sheet);
238 239 240 241 242
	/* Row canvas */
	sheet->row_canvas = new_canvas_bar (sheet, GTK_ORIENTATION_VERTICAL, &sheet->row_item);
	gtk_table_attach (GTK_TABLE (sheet->toplevel), sheet->row_canvas,
			  0, 1, 1, 2,
			  0, GTK_FILL | GTK_EXPAND, 0, 0);
Arturo Espinosa's avatar
Arturo Espinosa committed
243 244 245 246 247 248
	gtk_signal_connect (GTK_OBJECT (sheet->row_item), "selection_changed",
			    GTK_SIGNAL_FUNC (sheet_row_selection_changed),
			    sheet);
	gtk_signal_connect (GTK_OBJECT (sheet->row_item), "size_changed",
			    GTK_SIGNAL_FUNC (sheet_row_size_changed),
			    sheet);
249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265

	/* Scroll bars and their adjustments */
	sheet->va = gtk_adjustment_new (0.0, 0.0, sheet->max_row_used, 1.0, rows_shown, 1.0);
	sheet->ha = gtk_adjustment_new (0.0, 0.0, sheet->max_col_used, 1.0, cols_shown, 1.0);
	
	sheet->hs = gtk_hscrollbar_new (GTK_ADJUSTMENT (sheet->ha));
	sheet->vs = gtk_vscrollbar_new (GTK_ADJUSTMENT (sheet->va));

	/* Attach the horizontal scroll */
	gtk_table_attach (GTK_TABLE (sheet->toplevel), sheet->hs,
			  1, 2, 2, 3,
			  GTK_FILL | GTK_EXPAND, 0, 0, 0);

	/* Attach the vertical scroll */
	gtk_table_attach (GTK_TABLE (sheet->toplevel), sheet->vs,
			  2, 3, 1, 2,
			  0, GTK_FILL | GTK_EXPAND, 0, 0);
Arturo Espinosa's avatar
Arturo Espinosa committed
266
	
267
	sheet_set_zoom_factor (sheet, 1.0);
Arturo Espinosa's avatar
Arturo Espinosa committed
268 269 270
	return sheet;
}

271 272 273 274 275 276
static void
cell_hash_free_key (gpointer key, gpointer value, gpointer user_data)
{
	g_free (key);
}
	
277 278 279 280
void
sheet_destroy (Sheet *sheet)
{
	g_assert (sheet != NULL);
281 282
	g_return_if_fail (IS_SHEET (sheet)); 

283 284 285 286 287
	sheet_selection_clear (sheet);
	g_free (sheet->name);
	
	style_destroy (sheet->default_row_style.style);
	style_destroy (sheet->default_col_style.style);
288 289

	g_hash_table_foreach (sheet->cell_hash, cell_hash_free_key, NULL);
290
	gtk_widget_destroy (sheet->toplevel);
291 292
	
	sheet->signature = 0;
293 294 295
	g_free (sheet);
}

296
void
297
sheet_foreach_col (Sheet *sheet, sheet_col_row_callback callback, void *user_data)
298 299 300 301 302 303 304 305 306 307 308 309 310 311
{
	GList *l = sheet->cols_info;

	/* Invoke the callback for the default style */
	(*callback)(sheet, &sheet->default_col_style, user_data);

	/* And then for the rest */
	while (l){
		(*callback)(sheet, l->data, user_data);
		l = l->next;
	}
}

void
312
sheet_foreach_row (Sheet *sheet, sheet_col_row_callback callback, void *user_data)
313 314 315 316 317 318 319 320 321 322 323 324 325 326
{
	GList *l = sheet->rows_info;

	/* Invoke the callback for the default style */
	(callback)(sheet, &sheet->default_row_style, user_data);

	/* And then for the rest */
	while (l){
		(*callback)(sheet, l->data, user_data);
		l = l->next;
	}
}

static void
327
sheet_compute_col_row_new_size (Sheet *sheet, ColRowInfo *ci, void *data)
328 329 330
{
	double pix_per_unit = sheet->last_zoom_factor_used;

331 332
	ci->pixels = (ci->units * pix_per_unit) +
		ci->margin_a + ci->margin_b + 1;
333 334 335 336 337 338 339 340 341 342 343 344 345 346 347
}

/*
 * Updates the values used for display after a zoom change
 */
static void
sheet_reconfigure_zoom (Sheet *sheet)
{
	GnomeCanvas *gnumeric_canvas = GNOME_CANVAS (sheet->sheet_view);
	double pixels_per_unit = gnumeric_canvas->pixels_per_unit;
	
	if (pixels_per_unit == sheet->last_zoom_factor_used)
		return;

	sheet->last_zoom_factor_used = pixels_per_unit;
348 349
	sheet_foreach_col (sheet, sheet_compute_col_row_new_size, NULL);
	sheet_foreach_row (sheet, sheet_compute_col_row_new_size, NULL);
350
	g_warning ("Need to recompute string lenghts of cells\n");
351 352 353 354 355 356 357 358 359 360 361 362 363 364
}

void
sheet_set_zoom_factor (Sheet *sheet, double factor)
{
	gnome_canvas_set_pixels_per_unit (GNOME_CANVAS (sheet->sheet_view), factor);
	gnome_canvas_set_pixels_per_unit (GNOME_CANVAS (sheet->col_canvas), factor);
	gnome_canvas_set_pixels_per_unit (GNOME_CANVAS (sheet->row_canvas), factor);
	sheet_reconfigure_zoom (sheet);
	gnome_canvas_scroll_to (GNOME_CANVAS (sheet->sheet_view), 0, 0);
	gnome_canvas_scroll_to (GNOME_CANVAS (sheet->col_canvas), 0, 0);
	gnome_canvas_scroll_to (GNOME_CANVAS (sheet->row_canvas), 0, 0);
}

365
ColRowInfo *
Arturo Espinosa's avatar
Arturo Espinosa committed
366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389
sheet_duplicate_colrow (ColRowInfo *original)
{
	ColRowInfo *info = g_new (ColRowInfo, 1);

	*info = *original;
	
	info->style = style_duplicate (original->style);
	
	return info;
}

ColRowInfo *
sheet_row_new (Sheet *sheet)
{
	return sheet_duplicate_colrow (&sheet->default_row_style);
}

ColRowInfo *
sheet_col_new (Sheet *sheet)
{
	return sheet_duplicate_colrow (&sheet->default_col_style);
}


Arturo Espinosa's avatar
Arturo Espinosa committed
390 391 392 393 394 395 396 397 398
static gint
CRsort (gconstpointer a, gconstpointer b)
{
	ColRowInfo *ia = (ColRowInfo *) a;
	ColRowInfo *ib = (ColRowInfo *) b;

	return (ia->pos - ib->pos);
}

Arturo Espinosa's avatar
Arturo Espinosa committed
399 400 401
void
sheet_col_add (Sheet *sheet, ColRowInfo *cp)
{
Arturo Espinosa's avatar
Arturo Espinosa committed
402
	sheet->cols_info = g_list_insert_sorted (sheet->cols_info, cp, CRsort);
Arturo Espinosa's avatar
Arturo Espinosa committed
403 404 405 406 407
}

void
sheet_row_add (Sheet *sheet, ColRowInfo *rp)
{
Arturo Espinosa's avatar
Arturo Espinosa committed
408
	sheet->rows_info = g_list_insert_sorted (sheet->rows_info, rp, CRsort);
Arturo Espinosa's avatar
Arturo Espinosa committed
409 410 411 412
}

ColRowInfo *
sheet_col_get_info (Sheet *sheet, int col)
413
{
414
	GList *l = sheet->cols_info;
415

416
	for (; l; l = l->next){
417
		ColRowInfo *ci = l->data;
418

419
		if (ci->pos == col)
420 421 422 423
			return ci;
	}

	return &sheet->default_col_style;
424 425
}

426
ColRowInfo *
Arturo Espinosa's avatar
Arturo Espinosa committed
427
sheet_row_get_info (Sheet *sheet, int row)
428
{
429
	GList *l = sheet->rows_info;
430

431
	for (; l; l = l->next){
432
		ColRowInfo *ri = l->data;
433

434
		if (ri->pos == row)
435 436
			return ri;
	}
437

438
	return &sheet->default_row_style;
439 440
}

Arturo Espinosa's avatar
Arturo Espinosa committed
441 442 443 444 445 446
static void
colrow_set_units (Sheet *sheet,ColRowInfo *info)
{
	double pix = sheet->last_zoom_factor_used;
	
	info->units  = (info->pixels -
447
			(info->margin_a + info->margin_b + 1)) / pix;
Arturo Espinosa's avatar
Arturo Espinosa committed
448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465
}

void
sheet_row_set_height (Sheet *sheet, int row, int height)
{
	ColRowInfo *ri;
	int add = 0;
	
	ri = sheet_row_get_info (sheet, row);
	if (ri == &sheet->default_row_style){
		ri = sheet_duplicate_colrow (ri);
		add = 1;
	}

	ri->pos = row;
	ri->pixels = height;
	colrow_set_units (sheet, ri);
	if (add)
466
		sheet_row_add (sheet, ri);
Arturo Espinosa's avatar
Arturo Espinosa committed
467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490
	sheet_redraw_all (sheet);
}

void
sheet_col_set_width (Sheet *sheet, int col, int width)
{
	ColRowInfo *ci;
	int add = 0;
	
	ci = sheet_col_get_info (sheet, col);
	if (ci == &sheet->default_col_style){
		ci = sheet_duplicate_colrow (ci);
		add = 1;
	}

	ci->pixels = width;
	ci->pos = col;
	colrow_set_units (sheet, ci);
	if (add)
		sheet_col_add (sheet, ci);

	sheet_redraw_all (sheet);
}

491 492 493 494 495 496
/*
 * Return the number of pixels between from_col to to_col
 */
int
sheet_col_get_distance (Sheet *sheet, int from_col, int to_col)
{
497
	ColRowInfo *ci;
498 499
	int pixels = 0, i;

500
	g_assert (sheet);
501
	g_assert (from_col <= to_col);
502 503

	/* This can be optimized, yes, but the implementation
504
	 * of the ColRowInfo sets is going to change anyways
505
	 */
506
	for (i = from_col; i < to_col; i++){
Arturo Espinosa's avatar
Arturo Espinosa committed
507
		ci = sheet_col_get_info (sheet, i);
508
		pixels += ci->pixels;
509 510 511 512 513 514 515 516 517 518
	}
	return pixels;
}

/*
 * Return the number of pixels between from_row to to_row
 */
int
sheet_row_get_distance (Sheet *sheet, int from_row, int to_row)
{
519
	ColRowInfo *ri;
520 521 522 523
	int pixels = 0, i;

	g_assert (from_row <= to_row);
	
524 525 526
	/* This can be optimized, yes, but the implementation
	 * of the RowInfo, ColInfo sets is going to change anyways
	 */
527
	for (i = from_row; i < to_row; i++){
Arturo Espinosa's avatar
Arturo Espinosa committed
528
		ri = sheet_row_get_info (sheet, i);
529
		pixels += ri->pixels;
530 531 532
	}
	return pixels;
}
533 534 535 536

void
sheet_get_cell_bounds (Sheet *sheet, ColType col, RowType row, int *x, int *y, int *w, int *h)
{
537 538 539
	GnumericSheet *gsheet;

	g_return_if_fail (sheet != NULL);
540
	g_return_if_fail (IS_SHEET (sheet)); 
541
	g_return_if_fail (GNUMERIC_IS_SHEET (sheet->sheet_view));
542
	
543
	gsheet = GNUMERIC_SHEET (sheet->sheet_view);
544 545 546 547 548 549
	*x = sheet_col_get_distance (sheet, gsheet->top_col, col);
	*y = sheet_row_get_distance (sheet, gsheet->top_row, row);

	*w = sheet_col_get_distance (sheet, col, col + 1);
	*h = sheet_row_get_distance (sheet, row, row + 1);
}
550

551 552 553 554 555 556 557 558 559 560 561 562 563 564 565
int
sheet_selection_equal (SheetSelection *a, SheetSelection *b)
{
	if (a->start_col != b->start_col)
		return 0;
	if (a->start_row != b->start_row)
		return 0;
	
	if (a->end_col != b->end_col)
		return 0;
	if (a->end_row != b->end_row)
		return 0;
	return 1;
}

566
void
567 568 569 570
sheet_selection_append_range (Sheet *sheet,
			      int base_col,  int base_row,
			      int start_col, int start_row,
			      int end_col,   int end_row)
571 572 573
{
	SheetSelection *ss;

574
	g_return_if_fail (sheet != NULL);
575
	g_return_if_fail (IS_SHEET (sheet)); 
576
	
577 578
	ss = g_new0 (SheetSelection, 1);

579 580
	ss->base_col  = base_col;
	ss->base_row  = base_row;
581

582 583 584 585
	ss->start_col = start_col;
	ss->end_col   = end_col;
	ss->start_row = start_row;
	ss->end_row   = end_row;
586 587 588
	
	sheet->selections = g_list_prepend (sheet->selections, ss);

Arturo Espinosa's avatar
Arturo Espinosa committed
589 590 591
	gnumeric_sheet_accept_pending_output (GNUMERIC_SHEET (sheet->sheet_view));
	gnumeric_sheet_load_cell_val (GNUMERIC_SHEET (sheet->sheet_view));
	
592 593
	gnumeric_sheet_set_selection (GNUMERIC_SHEET (sheet->sheet_view), ss);
	sheet_redraw_selection (sheet, ss);
594 595
}

596 597 598 599 600 601
void
sheet_selection_append (Sheet *sheet, int col, int row)
{
	sheet_selection_append_range (sheet, col, row, col, row, col, row);
}

602 603 604 605 606 607 608 609
/*
 * sheet_selection_extend_to
 * @sheet: the sheet
 * @col:   column that gets covered
 * @row:   row that gets covered
 *
 * This extends the selection to cover col, row
 */
610 611 612
void
sheet_selection_extend_to (Sheet *sheet, int col, int row)
{
613 614 615
	SheetSelection *ss, old_selection;

	g_return_if_fail (sheet != NULL);
616 617
	g_return_if_fail (IS_SHEET (sheet));
	
618
	g_assert (sheet->selections);
619

620 621
	ss = (SheetSelection *) sheet->selections->data;

622 623
	old_selection = *ss;
	
624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639
	if (col < ss->base_col){
		ss->start_col = col;
		ss->end_col   = ss->base_col;
	} else {
		ss->start_col = ss->base_col;
		ss->end_col   = col;
	}

	if (row < ss->base_row){
		ss->end_row   = ss->base_row;
		ss->start_row = row;
	} else {
		ss->end_row   = row;
		ss->start_row = ss->base_row;
	}

640 641 642 643 644 645
	gnumeric_sheet_set_selection (GNUMERIC_SHEET (sheet->sheet_view), ss);

	sheet_redraw_selection (sheet, &old_selection);
	sheet_redraw_selection (sheet, ss);
}

646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667
void
sheet_row_set_selection (Sheet *sheet, ColRowInfo *ri, int value)
{
	if (ri->selected == value)
		return;
	
	ri->selected = value;
	gnome_canvas_request_redraw (GNOME_CANVAS (sheet->row_canvas),
		0, 0, INT_MAX, INT_MAX);
}

void
sheet_col_set_selection (Sheet *sheet, ColRowInfo *ci, int value)
{
	if (ci->selected == value)
		return;
	
	ci->selected = value;
	gnome_canvas_request_redraw (GNOME_CANVAS (sheet->col_canvas),
		0, 0, INT_MAX, INT_MAX);
}

668 669 670 671 672 673 674 675
void
sheet_redraw_cell_region (Sheet *sheet, int start_col, int start_row,
			  int end_col, int end_row)
{
	GnumericSheet *gsheet;
	int x, y, w, h;
	
	g_return_if_fail (sheet != NULL);
676
	g_return_if_fail (IS_SHEET (sheet)); 
677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692

	gsheet = GNUMERIC_SHEET (sheet->sheet_view);
	g_return_if_fail (GNUMERIC_IS_SHEET (gsheet));
	
	x = sheet_col_get_distance (sheet, gsheet->top_col, start_col);
	y = sheet_row_get_distance (sheet, gsheet->top_row, start_row);
	w = sheet_col_get_distance (sheet, start_col, end_col+1);
	h = sheet_col_get_distance (sheet, start_col, end_col+1);

	gnome_canvas_request_redraw (GNOME_CANVAS (gsheet), x, y, x+w, y+h);
}

void
sheet_redraw_selection (Sheet *sheet, SheetSelection *ss)
{
	g_return_if_fail (sheet != NULL);
693
	g_return_if_fail (IS_SHEET (sheet)); 
694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760
	g_return_if_fail (ss != NULL);
	
	sheet_redraw_cell_region (sheet,
				  ss->start_col, ss->start_row,
				  ss->end_col, ss->end_row);
}

int
sheet_row_check_bound (int row, int diff)
{
	int new_val = row + diff;

	if (new_val < 0)
		return 0;
	if (new_val >= SHEET_MAX_ROWS)
		return SHEET_MAX_ROWS - 1;

	return new_val;
}

int
sheet_col_check_bound (int col, int diff)
{
	int new_val = col + diff;

	if (new_val < 0)
		return 0;
	if (new_val >= SHEET_MAX_COLS)
		return SHEET_MAX_COLS - 1;

	return new_val;
}

static void
sheet_selection_change (Sheet *sheet, SheetSelection *old, SheetSelection *new)
{
	GnumericSheet *gsheet = GNUMERIC_SHEET (sheet->sheet_view);
	
	if (sheet_selection_equal (old, new))
		return;
	
		
	gnumeric_sheet_accept_pending_output (gsheet);
	sheet_redraw_selection (sheet, old);
	sheet_redraw_selection (sheet, new);
	
	gnumeric_sheet_set_selection (gsheet, new);
}

/*
 * sheet_selection_extend_horizontal
 * @sheet:  The Sheet *
 * @count:  units to extend the selection horizontally
 */
void
sheet_selection_extend_horizontal (Sheet *sheet, int n)
{
	SheetSelection *ss;
	SheetSelection old_selection;

	/* FIXME: right now we only support units (1 or -1)
	 * to fix this we need to account for the fact that
	 * the selection boundary might change and adjust
	 * appropiately
	 */
	 
	g_return_if_fail (sheet != NULL);
761
	g_return_if_fail (IS_SHEET (sheet)); 
762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778
	g_return_if_fail ((n == 1 || n == -1));
	
	ss = (SheetSelection *)sheet->selections->data;
	old_selection = *ss;
	
	if (ss->base_col < ss->end_col)
		ss->end_col = sheet_col_check_bound (ss->end_col, n);
	else if (ss->base_col > ss->start_col)
		ss->start_col = sheet_col_check_bound (ss->start_col, n);
	else {
		if (n > 0)
			ss->end_col = sheet_col_check_bound (ss->end_col, 1);
		else
			ss->start_col = sheet_col_check_bound (ss->start_col, -1);
	}

	sheet_selection_change (sheet, &old_selection, ss);
779 780
}

781 782 783 784 785 786 787 788 789 790 791 792
/*
 * sheet_selection_extend_vertical
 * @sheet:  The Sheet *
 * @n:      units to extend the selection vertically
 */
void
sheet_selection_extend_vertical (Sheet *sheet, int n)
{
	SheetSelection *ss;
	SheetSelection old_selection;
	
	g_return_if_fail (sheet != NULL);
793
	g_return_if_fail (IS_SHEET (sheet)); 
794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812
	g_return_if_fail ((n == 1 || n == -1));
	
	ss = (SheetSelection *)sheet->selections->data;
	old_selection = *ss;
	
	if (ss->base_row < ss->end_row)
		ss->end_row = sheet_row_check_bound (ss->end_row, n);
	else if (ss->base_row > ss->start_row)
		ss->start_row = sheet_row_check_bound (ss->start_row, n);
	else {
		if (n > 0)
			ss->end_row = sheet_row_check_bound (ss->end_row, 1);
		else
			ss->start_row = sheet_row_check_bound (ss->start_row, -1);
	}

	sheet_selection_change (sheet, &old_selection, ss);
}

813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831
/*
 * Clear the selection from the columns or rows
 */
static int
clean_bar_selection (GList *list)
{
	int cleared = 0;
	
	for (; list; list = list->next){
		ColRowInfo *info = (ColRowInfo *)list->data;

		if (info->selected){
			info->selected = 0;
			cleared++;
		}
	}
	return cleared;
}

832 833 834 835
/*
 * sheet_selection_clear
 * sheet:  The sheet
 *
836 837 838
 * Clears all of the selection ranges.
 * Warning: This does not set a new selection, this should
 * be taken care on the calling routine. 
839
 */
840
void
841
sheet_selection_clear_only (Sheet *sheet)
842
{
843
	GnumericSheet *gsheet;
844 845
	GList *list = sheet->selections;

846
	g_return_if_fail (sheet != NULL);
847
	g_return_if_fail (IS_SHEET (sheet)); 
848 849 850

	gsheet = GNUMERIC_SHEET (sheet->sheet_view);
	
851 852 853
	for (list = sheet->selections; list; list = list->next){
		SheetSelection *ss = list->data;

854
		sheet_redraw_selection (sheet, ss);
855 856 857 858
		g_free (ss);
	}
	g_list_free (sheet->selections);
	sheet->selections = NULL;
859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884

	/* Unselect the column bar */
	if (clean_bar_selection (sheet->cols_info) > 0)
		gnome_canvas_request_redraw (GNOME_CANVAS (sheet->col_canvas),
					     0, 0, INT_MAX, INT_MAX);

	/* Unselect the row bar */
	if (clean_bar_selection (sheet->rows_info) > 0)
		gnome_canvas_request_redraw (GNOME_CANVAS (sheet->row_canvas),
					     0, 0, INT_MAX, INT_MAX);
	
}

/*
 * sheet_selection_clear
 * sheet:  The sheet
 *
 * Clears all of the selection ranges and resets it to a
 * selection that only covers the cursor
 */
void
sheet_selection_clear (Sheet *sheet)
{
	GnumericSheet *gsheet;

	g_return_if_fail (sheet != NULL);
885
	g_return_if_fail (IS_SHEET (sheet)); 
886 887 888 889
	
	gsheet = GNUMERIC_SHEET (sheet->sheet_view);
	
	sheet_selection_clear_only (sheet);
890
	sheet_selection_append (sheet, gsheet->cursor_col, gsheet->cursor_row);
891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906
}

int
sheet_selection_is_cell_selected (Sheet *sheet, int col, int row)
{
	GList *list = sheet->selections;

	for (list = sheet->selections; list; list = list->next){
		SheetSelection *ss = list->data;

		if ((ss->start_col <= col) && (col <= ss->end_col) &&
		    (ss->start_row <= row) && (row <= ss->end_row))
			return 1;
	}
	return 0;
}
Arturo Espinosa's avatar
Arturo Espinosa committed
907 908 909 910 911 912 913

/*
 * Returns an allocated column:  either an existing one, or a fresh copy
 */
ColRowInfo *
sheet_col_get (Sheet *sheet, int pos)
{
914
	GList *clist;
Arturo Espinosa's avatar
Arturo Espinosa committed
915
	ColRowInfo *col;
916

917
	g_return_val_if_fail (sheet != NULL, NULL);
918
	g_return_val_if_fail (IS_SHEET (sheet), NULL); 
Arturo Espinosa's avatar
Arturo Espinosa committed
919
	
920
	for (clist = sheet->cols_info; clist; clist = clist->next){
Arturo Espinosa's avatar
Arturo Espinosa committed
921 922 923 924 925 926
		col = (ColRowInfo *) clist->data;

		if (col->pos == pos)
			return col;
	}
	col = sheet_col_new (sheet);
927
	col->pos = pos;
Arturo Espinosa's avatar
Arturo Espinosa committed
928 929 930 931 932 933 934 935 936 937 938
	sheet_col_add (sheet, col);
	
	return col;
}

/*
 * Returns an allocated column:  either an existing one, or a fresh copy
 */
ColRowInfo *
sheet_row_get (Sheet *sheet, int pos)
{
939
	GList *rlist;
Arturo Espinosa's avatar
Arturo Espinosa committed
940
	ColRowInfo *row;
941

942
	g_return_val_if_fail (sheet != NULL, NULL);
943
	g_return_val_if_fail (IS_SHEET (sheet), NULL); 
Arturo Espinosa's avatar
Arturo Espinosa committed
944
	
945
	for (rlist = sheet->rows_info; rlist; rlist = rlist->next){
Arturo Espinosa's avatar
Arturo Espinosa committed
946 947 948 949 950
		row = (ColRowInfo *) rlist->data;

		if (row->pos == pos)
			return row;
	}
951 952 953
	row = sheet_row_new (sheet);
	row->pos = pos;
	sheet_row_add (sheet, row);
Arturo Espinosa's avatar
Arturo Espinosa committed
954 955 956 957
	
	return row;
}

958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981
static void
gen_row_blanks (Sheet *sheet, int col, int start_row, int end_row,
		sheet_cell_foreach_callback callback, void *closure)
{
	int row;

	for (row = 0; row < end_row; row++)
		(*callback)(sheet, col, row, NULL, closure);
}

static void
gen_col_blanks (Sheet *sheet, int start_col, int end_col,
		int start_row, int end_row,
		sheet_cell_foreach_callback callback, void *closure)
{
	int col;
       
	for (col = 0; col < end_col; col++)
		gen_row_blanks (sheet, col, start_row, end_row, callback, closure);
}

Cell *
sheet_cell_get (Sheet *sheet, int col, int row)
{
982
	Cell *cell;
983
	CellPos cellref;
984
	
985
	g_return_val_if_fail (sheet != NULL, NULL);
986
	g_return_val_if_fail (IS_SHEET (sheet), NULL); 
987

988 989 990 991 992
	cellref.col = col;
	cellref.row = row;
	cell = g_hash_table_lookup (sheet->cell_hash, &cellref);

	return cell;
993 994
}

Arturo Espinosa's avatar
Arturo Espinosa committed
995 996
/*
 * For each existing cell in the range specified, invoke the
997 998
 * callback routine.  If the only_existing flag is passed, then
 * callbacks are only invoked for existing cells.
Arturo Espinosa's avatar
Arturo Espinosa committed
999 1000
 */
void
1001
sheet_cell_foreach_range (Sheet *sheet, int only_existing,
Arturo Espinosa's avatar
Arturo Espinosa committed
1002 1003 1004 1005 1006
			  int start_col, int start_row,
			  int end_col, int end_row,
			  sheet_cell_foreach_callback callback,
			  void *closure)
{
1007
	GList *col;
Arturo Espinosa's avatar
Arturo Espinosa committed
1008
	GList *row;
1009
	int   last_col_gen = -1, last_row_gen = -1;
Arturo Espinosa's avatar
Arturo Espinosa committed
1010

1011
	g_return_if_fail (sheet != NULL);
1012
	g_return_if_fail (IS_SHEET (sheet)); 
1013 1014 1015
	g_return_if_fail (callback != NULL);
	
	col = sheet->cols_info;
Arturo Espinosa's avatar
Arturo Espinosa committed
1016 1017 1018 1019 1020 1021 1022 1023
	for (; col; col = col->next){
		ColRowInfo *ci = col->data;

		if (ci->pos < start_col)
			continue;
		if (ci->pos > end_col)
			break;

1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037
		if (!only_existing){
			if ((last_col_gen > 0) && (ci->pos != last_col_gen+1))
				gen_col_blanks (sheet, last_col_gen, ci->pos,
						start_row, end_row, callback,
						closure);
					
			if (ci->pos > start_col)
				gen_col_blanks (sheet, start_col, ci->pos,
						start_row, end_row, callback,
						closure);
		}
		last_col_gen = ci->pos;

		last_row_gen = -1;
Arturo Espinosa's avatar
Arturo Espinosa committed
1038 1039 1040 1041 1042 1043 1044 1045 1046
		for (row = (GList *) ci->data; row; row = row->data){
			ColRowInfo *ri = row->data;

			if (ri->pos < start_row)
				continue;

			if (ri->pos > end_row)
				break;

1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061
			if (!only_existing){
				if (last_row_gen > 0){
					if (ri->pos != last_row_gen+1)
						gen_row_blanks (sheet, ci->pos,
								last_row_gen,
								ri->pos,
								callback,
								closure);
				}
				if (ri->pos > start_row)
					gen_row_blanks (sheet, ci->pos,
							ri->pos, start_row,
							callback, closure);
			}
			(*callback)(sheet, ci->pos, ri->pos, (Cell *) ri->data, closure);
Arturo Espinosa's avatar
Arturo Espinosa committed
1062 1063 1064 1065 1066 1067 1068
		}
	}
}

Style *
sheet_style_compute (Sheet *sheet, int col, int row)
{
1069
	g_return_val_if_fail (sheet != NULL, NULL);
1070
	g_return_val_if_fail (IS_SHEET (sheet), NULL); 
1071
	
Arturo Espinosa's avatar
Arturo Espinosa committed
1072 1073 1074 1075 1076 1077 1078 1079 1080
	/* FIXME: This should compute the style based on the
	 * story of the styles applied to the worksheet, the
	 * sheet, the column and the row and return that.
	 *
	 * for now, we just return the col style
	 */
	return style_duplicate (sheet_col_get_info (sheet, col)->style);
}

1081 1082 1083 1084 1085 1086
static gint
CRowSort (gconstpointer a, gconstpointer b)
{
	Cell *ca = (Cell *) a;
	Cell *cb = (Cell *) b;

1087
	return cb->row->pos - ca->row->pos;
1088 1089
}

Arturo Espinosa's avatar
Arturo Espinosa committed
1090 1091 1092
Cell *
sheet_cell_new (Sheet *sheet, int col, int row)
{
1093
	Cell *cell;
1094
	CellPos *cellref;
1095
	
1096
	g_return_val_if_fail (sheet != NULL, NULL);
1097
	g_return_val_if_fail (IS_SHEET (sheet), NULL); 
Arturo Espinosa's avatar
Arturo Espinosa committed
1098

1099 1100
	cell = g_new0 (Cell, 1);
	cell->col   = sheet_col_get (sheet, col);
1101
	cell->row   = sheet_row_get (sheet, row);
Arturo Espinosa's avatar
Arturo Espinosa committed
1102
	cell->style = sheet_style_compute (sheet, col, row);
1103

1104 1105
	cell->width = cell->col->margin_a + cell->col->margin_b;
	
1106
	cellref = g_new0 (CellPos, 1);
1107 1108 1109 1110
	cellref->col = col;
	cellref->row = row;
	
	g_hash_table_insert (sheet->cell_hash, cellref, cell);
1111 1112
	cell->col->data = g_list_insert_sorted (cell->col->data, cell, CRowSort);

Arturo Espinosa's avatar
Arturo Espinosa committed
1113 1114 1115
	return cell;
}

Arturo Espinosa's avatar
Arturo Espinosa committed
1116

1117
void
Arturo Espinosa's avatar
Arturo Espinosa committed
1118
cell_set_formula (Sheet *sheet, Cell *cell, char *text)
Arturo Espinosa's avatar
Arturo Espinosa committed
1119
{
Arturo Espinosa's avatar
Arturo Espinosa committed
1120 1121
	char *error_msg;
	Value *v;
1122

1123 1124
	g_return_if_fail (sheet != NULL);
	g_return_if_fail (IS_SHEET (sheet)); 
1125
	g_return_if_fail (cell != NULL);
1126
	g_return_if_fail (text != NULL);
Arturo Espinosa's avatar
Arturo Espinosa committed
1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159
	
	if (cell->text)
		g_free (cell->text);
	
	cell->parsed_node = eval_parse_string (&text [1],
					       &error_msg);
	if (cell->parsed_node == NULL){
		cell->text = g_strdup (error_msg);
		return;
	}

	v = eval_node_value (sheet,
			     cell->parsed_node,
			     &error_msg);

	if (cell->value)
		eval_release_value (cell->value);
	
	if (v == NULL){
		cell->text = g_strdup (error_msg);
		cell->value = NULL;
	} else {
		/* FIXME: Use the format stuff */
		cell->value = v;
		cell->text  = eval_value_string (v);
	}
}


void
cell_set_text (Sheet *sheet, Cell *cell, char *text)
{
	GdkFont *font;
1160 1161 1162

	g_return_if_fail (sheet != NULL);
	g_return_if_fail (IS_SHEET (sheet)); 
Arturo Espinosa's avatar
Arturo Espinosa committed
1163 1164
	g_return_if_fail (cell != NULL);
	g_return_if_fail (text != NULL);
1165 1166 1167 1168 1169

	/* The value entered */
	if (cell->entered_text)
		g_free (cell->entered_text);
	cell->entered_text = g_strdup (text);