gnumeric-canvas.c 29 KB
Newer Older
1 2 3 4 5 6
/*
 * The Gnumeric Sheet widget.
 *
 * Author:
 *     Miguel de Icaza (miguel@kernel.org)
 */
7
#include <config.h>
8 9

#include <gnome.h>
10 11
#include <gdk/gdkkeysyms.h>
#include <gdk/gdkx.h>
12
#include <string.h>
13 14
#include "gnumeric.h"
#include "gnumeric-sheet.h"
Miguel de Icaza's avatar
New:  
Miguel de Icaza committed
15
#include "sheet-object.h"
16
#include "color.h"
17
#include "cursors.h"
18

Arturo Espinosa's avatar
Today:  
Arturo Espinosa committed
19 20 21
#define CURSOR_COL(gsheet) gsheet->sheet_view->sheet->cursor_col
#define CURSOR_ROW(gsheet) gsheet->sheet_view->sheet->cursor_row

22
/* Public colors: shared by all of our items in Gnumeric */
23

24
GdkColor gs_white, gs_black, gs_light_gray, gs_dark_gray, gs_red;
25

26

27
static GnomeCanvasClass *sheet_parent_class;
28

29 30 31 32 33 34
static void
gnumeric_sheet_destroy (GtkObject *object)
{
	GnumericSheet *gsheet;

	/* Add shutdown code here */
Arturo Espinosa's avatar
Arturo Espinosa committed
35 36
	gsheet = GNUMERIC_SHEET (object);
	
37 38 39 40
	if (GTK_OBJECT_CLASS (sheet_parent_class)->destroy)
		(*GTK_OBJECT_CLASS (sheet_parent_class)->destroy)(object);
}

Arturo Espinosa's avatar
Arturo Espinosa committed
41
static GnumericSheet *
Arturo Espinosa's avatar
Today:  
Arturo Espinosa committed
42
gnumeric_sheet_create (SheetView *sheet_view, GtkWidget *entry)
Arturo Espinosa's avatar
Arturo Espinosa committed
43 44 45 46 47 48 49
{
	GnumericSheet *gsheet;
	GnomeCanvas   *canvas;
	
	gsheet = gtk_type_new (gnumeric_sheet_get_type ());
	canvas = GNOME_CANVAS (gsheet);

Arturo Espinosa's avatar
Today:  
Arturo Espinosa committed
50
	gsheet->sheet_view = sheet_view;
Arturo Espinosa's avatar
Arturo Espinosa committed
51 52
	gsheet->top_col = 0;
	gsheet->top_row = 0;
53
	gsheet->entry   = entry;
54
	
Arturo Espinosa's avatar
Arturo Espinosa committed
55 56 57
	return gsheet;
}

58
void
Arturo Espinosa's avatar
Today:  
Arturo Espinosa committed
59
gnumeric_sheet_get_cell_bounds (GnumericSheet *gsheet, int col, int row, int *x, int *y, int *w, int *h)
60
{
Arturo Espinosa's avatar
Today:  
Arturo Espinosa committed
61 62
	Sheet *sheet;
	
Arturo Espinosa's avatar
Arturo Espinosa committed
63
	g_return_if_fail (gsheet != NULL);
Arturo Espinosa's avatar
Today:  
Arturo Espinosa committed
64
	g_return_if_fail (GNUMERIC_IS_SHEET (gsheet)); 
65

Arturo Espinosa's avatar
Today:  
Arturo Espinosa committed
66
	sheet = gsheet->sheet_view->sheet;
Arturo Espinosa's avatar
Arturo Espinosa committed
67
	
Arturo Espinosa's avatar
Today:  
Arturo Espinosa committed
68 69
	*x = sheet_col_get_distance (sheet, gsheet->top_col, col);
	*y = sheet_row_get_distance (sheet, gsheet->top_row, row);
Arturo Espinosa's avatar
Arturo Espinosa committed
70

Arturo Espinosa's avatar
Today:  
Arturo Espinosa committed
71 72
	*w = sheet_col_get_distance (sheet, col, col + 1);
	*h = sheet_row_get_distance (sheet, row, row + 1);
Arturo Espinosa's avatar
Arturo Espinosa committed
73 74
}

Arturo Espinosa's avatar
Today:  
Arturo Espinosa committed
75 76 77 78 79 80 81 82 83
/*
 * gnumeric_sheet_cursor_set
 *  @gsheet: The sheet
 *  @col:    the column
 *  @row:    the row
 *
 * This informs the GnumericSheet of the cursor position.  It is
 * used to sync the contents of the scrollbars with our position
 */
Arturo Espinosa's avatar
Arturo Espinosa committed
84
void
Arturo Espinosa's avatar
Today:  
Arturo Espinosa committed
85
gnumeric_sheet_cursor_set (GnumericSheet *gsheet, int col, int row)
Arturo Espinosa's avatar
Arturo Espinosa committed
86
{
Arturo Espinosa's avatar
Today:  
Arturo Espinosa committed
87 88 89
	GtkAdjustment *ha, *va;
	SheetView *sheet_view;
	
Arturo Espinosa's avatar
Arturo Espinosa committed
90
	g_return_if_fail (GNUMERIC_IS_SHEET (gsheet));
91

Arturo Espinosa's avatar
Today:  
Arturo Espinosa committed
92
	sheet_view = gsheet->sheet_view;
93

Arturo Espinosa's avatar
Today:  
Arturo Espinosa committed
94 95 96 97 98 99 100 101 102
	if (sheet_view->ha){
		ha = GTK_ADJUSTMENT (sheet_view->ha);
		va = GTK_ADJUSTMENT (sheet_view->va);
		ha->value = col;
		va->value = row;
		
		gtk_adjustment_value_changed (ha);
		gtk_adjustment_value_changed (va);
	}
103 104
}

105 106
/*
 * gnumeric_sheet_set_selection:
Arturo Espinosa's avatar
Today:  
Arturo Espinosa committed
107
 *  @gsheet:    The sheet name
108 109 110 111 112 113 114
 *  @start_col: The starting column.
 *  @start_row: The starting row
 *  @end_col:   The end column
 *  @end_row:   The end row
 *
 * Set the current selection to cover the inclusive area delimited by
 * start_col, start_row, end_col and end_row.  The actual cursor is
115
 * placed at base_col, base_row
116 117
 */
void
Arturo Espinosa's avatar
Arturo Espinosa committed
118
gnumeric_sheet_set_selection (GnumericSheet *gsheet, SheetSelection *ss)
119
{
Arturo Espinosa's avatar
Arturo Espinosa committed
120
	g_return_if_fail (gsheet != NULL);
121
	g_return_if_fail (ss != NULL);
Arturo Espinosa's avatar
Arturo Espinosa committed
122
	g_return_if_fail (GNUMERIC_IS_SHEET (gsheet));
123

Arturo Espinosa's avatar
Today:  
Arturo Espinosa committed
124 125
	sheet_cursor_set (
		gsheet->sheet_view->sheet,
126
		ss->base_col, ss->base_row,
Arturo Espinosa's avatar
Today:  
Arturo Espinosa committed
127 128
		ss->start_col, ss->start_row,
		ss->end_col, ss->end_row);
129 130 131
}

/*
132
 * move_cursor:
133 134 135
 *   @Sheet:    The sheet where the cursor is located
 *   @col:      The new column for the cursor.
 *   @row:      The new row for the cursor.
136
 *   @clear_selection: If set, clear the selection before moving
137 138 139 140 141
 *   Moves the sheet cursor to a new location, it clears the selection,
 *   accepts any pending output on the editing line and moves the cell
 *   cursor.
 */
static void
Arturo Espinosa's avatar
Today:  
Arturo Espinosa committed
142
move_cursor (GnumericSheet *gsheet, int col, int row, gboolean clear_selection)
143
{
Arturo Espinosa's avatar
Today:  
Arturo Espinosa committed
144
	Sheet *sheet = gsheet->sheet_view->sheet;
145 146

	if (clear_selection)
Arturo Espinosa's avatar
Today:  
Arturo Espinosa committed
147
		sheet_selection_reset_only (sheet);
148

149
	sheet_make_cell_visible (sheet, col, row);
150
	sheet_cursor_set (sheet, col, row, col, row, col, row);
151

Arturo Espinosa's avatar
Today:  
Arturo Espinosa committed
152 153
	if (clear_selection)
		sheet_selection_append (sheet, col, row);
154 155
}

156 157 158 159 160 161 162 163 164
void
gnumeric_sheet_move_cursor (GnumericSheet *gsheet, int col, int row)
{
	g_return_if_fail (gsheet != NULL);
	g_return_if_fail (GNUMERIC_IS_SHEET (gsheet));

	move_cursor (gsheet, col, row, 1);
}

Arturo Espinosa's avatar
Arturo Espinosa committed
165 166 167 168 169 170 171 172
void
gnumeric_sheet_set_cursor_bounds (GnumericSheet *gsheet,
				  int start_col, int start_row,
				  int end_col,   int end_row)
{
	g_return_if_fail (gsheet != NULL);
	g_return_if_fail (GNUMERIC_IS_SHEET (gsheet));

Arturo Espinosa's avatar
Today:  
Arturo Espinosa committed
173 174 175 176
	item_cursor_set_bounds (
		gsheet->item_cursor,
		start_col, start_row,
		end_col, end_row);
Arturo Espinosa's avatar
Arturo Espinosa committed
177 178
}

179
/*
180
 * move_cursor_horizontal:
181 182 183 184 185 186
 *  @Sheet:  The sheet name
 *  @count:  number of units to move the cursor horizontally
 *
 * Moves the cursor count columns
 */
static void
Arturo Espinosa's avatar
Today:  
Arturo Espinosa committed
187
move_cursor_horizontal (GnumericSheet *gsheet, int count)
188
{
Arturo Espinosa's avatar
Today:  
Arturo Espinosa committed
189
	Sheet *sheet = gsheet->sheet_view->sheet;
190 191
	int new_left;
	
192
	new_left = sheet->cursor_col + count;
193 194

	if (new_left < 0)
195
		new_left = 0;
196 197
	if (new_left > SHEET_MAX_COLS-1)
		new_left = SHEET_MAX_COLS-1;
198
	
Arturo Espinosa's avatar
Today:  
Arturo Espinosa committed
199
	move_cursor (gsheet, new_left, sheet->cursor_row, TRUE);
200 201
}

202
/*
203
 * move_cursor_vertical:
204 205 206 207 208
 *  @Sheet:  The sheet name
 *  @count:  number of units to move the cursor vertically
 *
 * Moves the cursor count rows
 */
209
static void
Arturo Espinosa's avatar
Today:  
Arturo Espinosa committed
210
move_cursor_vertical (GnumericSheet *gsheet, int count)
211
{
Arturo Espinosa's avatar
Today:  
Arturo Espinosa committed
212
	Sheet *sheet = gsheet->sheet_view->sheet;
213
	int new_top;
214

Arturo Espinosa's avatar
Today:  
Arturo Espinosa committed
215
	new_top = CURSOR_ROW (gsheet) + count;
216 217

	if (new_top < 0)
218
		new_top = 0;
219 220 221
	if (new_top > SHEET_MAX_ROWS-1)
		new_top = SHEET_MAX_ROWS-1;
	
Arturo Espinosa's avatar
Today:  
Arturo Espinosa committed
222
	move_cursor (gsheet, sheet->cursor_col, new_top, TRUE);
223 224
}

225
static void
226
move_horizontal_selection (GnumericSheet *gsheet, int count)
227
{
Arturo Espinosa's avatar
Today:  
Arturo Espinosa committed
228
	sheet_selection_extend_horizontal (gsheet->sheet_view->sheet, count);
229 230
}

231
static void
232
move_vertical_selection (GnumericSheet *gsheet, int count)
233
{
Arturo Espinosa's avatar
Today:  
Arturo Espinosa committed
234
	sheet_selection_extend_vertical (gsheet->sheet_view->sheet, count);
235 236
}

237 238 239 240 241 242 243 244
/*
 * gnumeric_sheet_can_move_cursor
 *  @gsheet:   the object
 *
 * Returns true if the cursor keys should be used to select
 * a cell range (if the cursor is in a spot in the expression
 * where it makes sense to have a cell reference), false if not.
 */
Miguel de Icaza's avatar
Miguel de Icaza committed
245
int
246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261
gnumeric_sheet_can_move_cursor (GnumericSheet *gsheet)
{
	GtkEntry *entry = GTK_ENTRY (gsheet->entry);
	int cursor_pos = GTK_EDITABLE (entry)->current_pos;

	if (gsheet->selecting_cell)
		return TRUE;
	
	if (entry->text [0] != '=')
		return FALSE;
	if (cursor_pos == 0)
		return FALSE;
		
	switch (entry->text [cursor_pos-1]){
	case '=': case '-': case '*': case '/': case '^': 
	case '+': case '&': case '(': case '%': case '!':
262
	case ':': case ',':
263 264 265 266 267 268 269
		return TRUE;
	}
	
	return FALSE;
}

static void
Miguel de Icaza's avatar
Miguel de Icaza committed
270
start_cell_selection_at (GnumericSheet *gsheet, int col, int row)
271 272 273
{
	GnomeCanvas *canvas = GNOME_CANVAS (gsheet);
	GnomeCanvasGroup *group = GNOME_CANVAS_GROUP (canvas->root);
Arturo Espinosa's avatar
Today:  
Arturo Espinosa committed
274
	
275 276 277 278 279 280
	g_return_if_fail (gsheet->selecting_cell == FALSE);
	
	gsheet->selecting_cell = TRUE;
	gsheet->selection = ITEM_CURSOR (gnome_canvas_item_new (
		group,
		item_cursor_get_type (),
Arturo Espinosa's avatar
Today:  
Arturo Espinosa committed
281
		"Sheet", gsheet->sheet_view->sheet,
282 283
		"Grid",  gsheet->item_grid,
		"Style", ITEM_CURSOR_ANTED, NULL));
Miguel de Icaza's avatar
Miguel de Icaza committed
284 285 286
	gsheet->selection->base_col = col;
	gsheet->selection->base_row = row;
	item_cursor_set_bounds (ITEM_CURSOR (gsheet->selection), col, row, col, row);
287
				
288 289 290 291 292
	gsheet->sel_cursor_pos = GTK_EDITABLE (gsheet->entry)->current_pos;
	gsheet->sel_text_len = 0;
}

static void
Miguel de Icaza's avatar
Miguel de Icaza committed
293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313
start_cell_selection (GnumericSheet *gsheet)
{
	Sheet *sheet = gsheet->sheet_view->sheet;

	start_cell_selection_at (gsheet, sheet->cursor_col, sheet->cursor_row);
}

void
gnumeric_sheet_start_cell_selection (GnumericSheet *gsheet, int col, int row)
{
	g_return_if_fail (gsheet != NULL);
	g_return_if_fail (GNUMERIC_IS_SHEET (gsheet));

	if (gsheet->selecting_cell)
		return;

	start_cell_selection_at (gsheet, col, row);
}

void
gnumeric_sheet_stop_cell_selection (GnumericSheet *gsheet)
314
{
Miguel de Icaza's avatar
Miguel de Icaza committed
315 316 317
	g_return_if_fail (gsheet != NULL);
	g_return_if_fail (GNUMERIC_IS_SHEET (gsheet));

318 319 320 321 322 323 324 325
	if (!gsheet->selecting_cell)
		return;
	
	gsheet->selecting_cell = FALSE;
	gtk_object_destroy (GTK_OBJECT (gsheet->selection));
	gsheet->selection = NULL;
}

Arturo Espinosa's avatar
Today:  
Arturo Espinosa committed
326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349
void
gnumeric_sheet_create_editing_cursor (GnumericSheet *gsheet)
{
	GnomeCanvas *canvas = GNOME_CANVAS (gsheet);
	GnomeCanvasItem *item;
	Sheet *sheet;
	int col, row;

	sheet = gsheet->sheet_view->sheet;
	col = sheet->cursor_col;
	row = sheet->cursor_row;
	
	item = gnome_canvas_item_new (GNOME_CANVAS_GROUP(canvas->root),
				      item_edit_get_type (),
				      "ItemEdit::Sheet",    sheet,
				      "ItemEdit::Grid",     gsheet->item_grid,
				      "ItemEdit::Col",      col, 
				      "ItemEdit::Row",      row,
				      "ItemEdit::GtkEntry", sheet->workbook->ea_input,
				      NULL);

	gsheet->item_editor = ITEM_EDIT (item);
}

Arturo Espinosa's avatar
Arturo Espinosa committed
350
static void
Arturo Espinosa's avatar
Today:  
Arturo Espinosa committed
351 352 353 354 355 356 357 358 359 360
destroy_item_editor (GnumericSheet *gsheet)
{
	g_return_if_fail (gsheet->item_editor);
	
	gtk_object_destroy (GTK_OBJECT (gsheet->item_editor));
	gsheet->item_editor = NULL;
}

void
gnumeric_sheet_destroy_editing_cursor (GnumericSheet *gsheet)
Arturo Espinosa's avatar
Arturo Espinosa committed
361
{
Arturo Espinosa's avatar
Today:  
Arturo Espinosa committed
362 363 364
	g_return_if_fail (gsheet != NULL);
	g_return_if_fail (GNUMERIC_IS_SHEET (gsheet));
	
Miguel de Icaza's avatar
Miguel de Icaza committed
365
	gnumeric_sheet_stop_cell_selection (gsheet);
Arturo Espinosa's avatar
Arturo Espinosa committed
366
	
367 368 369
	if (!gsheet->item_editor)
		return;

Arturo Espinosa's avatar
Today:  
Arturo Espinosa committed
370 371 372 373 374 375 376 377 378 379 380 381
	destroy_item_editor (gsheet);
}

void
gnumeric_sheet_stop_editing (GnumericSheet *gsheet)
{
	g_return_if_fail (gsheet != NULL);
	g_return_if_fail (GNUMERIC_IS_SHEET (gsheet));

	if (!gsheet->item_editor)
		return;

382
	destroy_item_editor (gsheet);
Arturo Espinosa's avatar
Arturo Espinosa committed
383 384
}

385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418
static void
selection_remove_selection_string (GnumericSheet *gsheet)
{
	gtk_editable_delete_text (GTK_EDITABLE (gsheet->entry),
				  gsheet->sel_cursor_pos,
				  gsheet->sel_cursor_pos+gsheet->sel_text_len);
}

static void
selection_insert_selection_string (GnumericSheet *gsheet)
{
	ItemCursor *sel = gsheet->selection;
	char buffer [20];
	int pos;

	/* Get the new selection string */
	strcpy (buffer, cell_name (sel->start_col, sel->start_row));
	if (!(sel->start_col == sel->end_col && sel->start_row == sel->end_row)){
		strcat (buffer, ":");
		strcat (buffer, cell_name (sel->end_col, sel->end_row));
	}
	gsheet->sel_text_len = strlen (buffer);

	pos = gsheet->sel_cursor_pos;
	gtk_editable_insert_text (GTK_EDITABLE (gsheet->entry),
				  buffer, strlen (buffer),
				  &pos);

	/* Set the cursor at the end.  It looks nicer */
	gtk_editable_set_position (GTK_EDITABLE (gsheet->entry),
				   gsheet->sel_cursor_pos +
				   gsheet->sel_text_len);
}

Miguel de Icaza's avatar
Miguel de Icaza committed
419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465
/*
 * Invoked by Item-Grid to extend the cursor selection
 */
void
gnumeric_sheet_selection_extend (GnumericSheet *gsheet, int col, int row)
{
	ItemCursor *ic;
	
	g_return_if_fail (gsheet != NULL);
	g_return_if_fail (GNUMERIC_IS_SHEET (gsheet));
	g_return_if_fail (gsheet->selecting_cell);
	g_return_if_fail (col < SHEET_MAX_COLS);
	g_return_if_fail (row < SHEET_MAX_ROWS);

	ic = gsheet->selection;

	selection_remove_selection_string (gsheet);
	item_cursor_set_bounds (ic,
				MIN (ic->base_col, col),
				MIN (ic->base_row, row),
				MAX (ic->base_col, col),
				MAX (ic->base_row, row));
	selection_insert_selection_string (gsheet);
}

/*
 * Invoked by Item-Grid to place the selection cursor on a specific
 * spot.
 */
void
gnumeric_sheet_selection_cursor_place (GnumericSheet *gsheet, int col, int row)
{
	ItemCursor *ic;
	
	g_return_if_fail (gsheet != NULL);
	g_return_if_fail (GNUMERIC_IS_SHEET (gsheet));
	g_return_if_fail (gsheet->selecting_cell);
	g_return_if_fail (col < SHEET_MAX_COLS);
	g_return_if_fail (row < SHEET_MAX_ROWS);
	
	ic = gsheet->selection;
	
	selection_remove_selection_string (gsheet);
	item_cursor_set_bounds (ic, col, row, col, row);
	selection_insert_selection_string (gsheet);
}

466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485
static void
selection_cursor_move_horizontal (GnumericSheet *gsheet, int dir)
{
	ItemCursor *ic;

	g_return_if_fail (dir == -1 || dir == 1);
	
	if (!gsheet->selecting_cell)
		start_cell_selection (gsheet);

	ic = gsheet->selection;
	if (dir == -1){
		if (ic->start_col == 0)
			return;
	} else {
		if (ic->end_col + 1 > (SHEET_MAX_COLS-1))
			return;
	}
	
	selection_remove_selection_string (gsheet);
Arturo Espinosa's avatar
Today:  
Arturo Espinosa committed
486
	item_cursor_set_bounds (ic, 
487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515
				ic->start_col + dir,
				ic->start_row, 
				ic->end_col + dir,
				ic->end_row);
	selection_insert_selection_string (gsheet);
}

static void
selection_cursor_move_vertical (GnumericSheet *gsheet, int dir)
{
	ItemCursor *ic;

	g_return_if_fail (dir == -1 || dir == 1);

	if (!gsheet->selecting_cell)
		start_cell_selection (gsheet);

	ic = gsheet->selection;
	if (dir == -1){
		if (ic->start_row == 0)
			return;
	} else {
		if (ic->end_row + 1 > (SHEET_MAX_ROWS-1))
			return;
	}

	if (!gsheet->selecting_cell)
		start_cell_selection (gsheet);
	item_cursor_set_bounds (ic,
Arturo Espinosa's avatar
Today:  
Arturo Espinosa committed
516 517 518 519
				ic->start_col,
				ic->start_row + dir,
				ic->end_col,
				ic->end_row + dir);
520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536
	selection_remove_selection_string (gsheet);
	selection_insert_selection_string (gsheet);
}

static void
selection_expand_horizontal (GnumericSheet *gsheet, int dir)
{
	ItemCursor *ic;

	g_return_if_fail (dir == -1 || dir == 1);

	if (!gsheet->selecting_cell){
		selection_cursor_move_vertical (gsheet, dir);
		return;
	}

	ic = gsheet->selection;
Miguel de Icaza's avatar
Miguel de Icaza committed
537
	if (ic->end_col == SHEET_MAX_COLS-1)
538 539
			return;

Miguel de Icaza's avatar
Miguel de Icaza committed
540 541 542
	if (dir == -1 && ic->start_col == ic->end_col)
		return;
	
543 544
	selection_remove_selection_string (gsheet);
	item_cursor_set_bounds (ic,
Miguel de Icaza's avatar
Miguel de Icaza committed
545
				ic->start_col,
546
				ic->start_row,
Miguel de Icaza's avatar
Miguel de Icaza committed
547
				ic->end_col  + dir,
548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565
				ic->end_row);
	selection_insert_selection_string (gsheet);
}

static void
selection_expand_vertical (GnumericSheet *gsheet, int dir)
{
	ItemCursor *ic;

	g_return_if_fail (dir == -1 || dir == 1);

	if (!gsheet->selecting_cell){
		selection_cursor_move_vertical (gsheet, dir);
		return;
	}

	ic = gsheet->selection;
	
Miguel de Icaza's avatar
Miguel de Icaza committed
566 567
	if (ic->end_row == SHEET_MAX_ROWS-1)
		return;
568

Miguel de Icaza's avatar
Miguel de Icaza committed
569 570 571
	if (dir == -1 && ic->start_row == ic->end_row)
		return;
	
572 573 574
	selection_remove_selection_string (gsheet);
	item_cursor_set_bounds (ic,
				ic->start_col,
Miguel de Icaza's avatar
Miguel de Icaza committed
575
				ic->start_row,
576
				ic->end_col,
Miguel de Icaza's avatar
Miguel de Icaza committed
577
				ic->end_row + dir);
578 579 580 581
	selection_insert_selection_string (gsheet);
}

/*
Miguel de Icaza's avatar
New:  
Miguel de Icaza committed
582
 * key press event handler for the gnumeric sheet for the sheet mode
583
 */
584
static gint
Miguel de Icaza's avatar
New:  
Miguel de Icaza committed
585
gnumeric_sheet_key_mode_sheet (GnumericSheet *gsheet, GdkEventKey *event)
586
{
Arturo Espinosa's avatar
Today:  
Arturo Espinosa committed
587 588
	Sheet *sheet = gsheet->sheet_view->sheet;
	Workbook *wb = sheet->workbook;
Arturo Espinosa's avatar
Arturo Espinosa committed
589 590
	void (*movefn_horizontal) (GnumericSheet *, int);
	void (*movefn_vertical)   (GnumericSheet *, int);
Arturo Espinosa's avatar
Arturo Espinosa committed
591
	int  cursor_move = gnumeric_sheet_can_move_cursor (gsheet);
Miguel de Icaza's avatar
Miguel de Icaza committed
592
	
593
	if ((event->state & GDK_SHIFT_MASK) != 0){
594 595
		if (cursor_move){
			movefn_horizontal = selection_expand_horizontal;
Miguel de Icaza's avatar
Miguel de Icaza committed
596
			movefn_vertical = selection_expand_vertical;
597 598 599 600
		} else {
			movefn_horizontal = move_horizontal_selection;
			movefn_vertical   = move_vertical_selection;
		}
601
	} else {
602 603 604 605 606 607 608
		if (cursor_move){
			movefn_horizontal = selection_cursor_move_horizontal;
			movefn_vertical   = selection_cursor_move_vertical;
		} else {
			movefn_horizontal = move_cursor_horizontal;
			movefn_vertical = move_cursor_vertical;
		}
609
	}
610

611 612 613 614 615
	/* Ignore a few keys (to avoid the selection cursor to be killed
	 * in some cases
	 */
	if (cursor_move){
		switch (event->keyval){
Miguel de Icaza's avatar
Miguel de Icaza committed
616 617 618
		case GDK_Shift_L:   case GDK_Shift_R:
		case GDK_Alt_L:     case GDK_Alt_R:
		case GDK_Control_L: case GDK_Control_R:
619 620 621
			return 1;
		}
	}
Tristan Tarrant's avatar
Tristan Tarrant committed
622

623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642
	/*
	 * The following sequences do not trigger an editor-start
	 * but if the editor is running we forward the events to it.
	 */
	if (!gsheet->item_editor){
		
		if ((event->state & GDK_CONTROL_MASK) != 0) {

			switch (event->keyval) {

			case GDK_space:
				sheet_selection_reset_only (sheet);
				sheet_selection_append_range (
					sheet,
					sheet->cursor_col, sheet->cursor_row,
					sheet->cursor_col, 0,
					sheet->cursor_col, SHEET_MAX_ROWS-1);
				sheet_redraw_all (sheet);
				return 1;
			}
Tristan Tarrant's avatar
Tristan Tarrant committed
643
		}
644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659

		if ((event->state & GDK_SHIFT_MASK) != 0){

			switch (event->keyval) {
				/* select row */
			case GDK_space:
				sheet_selection_reset_only (sheet);
				sheet_selection_append_range (
					sheet,
					sheet->cursor_col, sheet->cursor_row,
					0, sheet->cursor_row,
					SHEET_MAX_COLS-1, sheet->cursor_row);
				sheet_redraw_all (sheet);
				return 1;
			}
		}
Arturo Espinosa's avatar
Arturo Espinosa committed
660

Tristan Tarrant's avatar
Tristan Tarrant committed
661 662
	}

663
	switch (event->keyval){
Miguel de Icaza's avatar
Miguel de Icaza committed
664
	case GDK_KP_Left:
665
	case GDK_Left:
Arturo Espinosa's avatar
Arturo Espinosa committed
666
		(*movefn_horizontal)(gsheet, -1);
667 668
		break;

Miguel de Icaza's avatar
Miguel de Icaza committed
669
	case GDK_KP_Right:
670
	case GDK_Right:
Arturo Espinosa's avatar
Arturo Espinosa committed
671
		(*movefn_horizontal)(gsheet, 1);
672 673
		break;

Miguel de Icaza's avatar
Miguel de Icaza committed
674
	case GDK_KP_Up:
675
	case GDK_Up:
Arturo Espinosa's avatar
Arturo Espinosa committed
676
		(*movefn_vertical)(gsheet, -1);
677 678
		break;

Miguel de Icaza's avatar
Miguel de Icaza committed
679
	case GDK_KP_Down:
680
	case GDK_Down:
Arturo Espinosa's avatar
Arturo Espinosa committed
681
		(*movefn_vertical)(gsheet, 1);
682 683
		break;

Miguel de Icaza's avatar
Miguel de Icaza committed
684
	case GDK_KP_Page_Up:
685 686 687 688
	case GDK_Page_Up:
	        (*movefn_vertical)(gsheet, -(gsheet->last_visible_row-gsheet->top_row));
		break;

Miguel de Icaza's avatar
Miguel de Icaza committed
689
	case GDK_KP_Page_Down:
690 691 692 693
	case GDK_Page_Down:
	        (*movefn_vertical)(gsheet, gsheet->last_visible_row-gsheet->top_row);
		break;

Miguel de Icaza's avatar
Miguel de Icaza committed
694
	case GDK_KP_Home:
695
	case GDK_Home:
696
		if ((event->state & GDK_CONTROL_MASK) != 0){
697
			sheet_make_cell_visible (sheet, 0, 0);
698 699 700 701
			sheet_cursor_move (sheet, 0, 0);
			break;
		} else
			(*movefn_horizontal)(gsheet, -CURSOR_COL(gsheet));
702 703 704 705
		break;

	case GDK_KP_Delete:
	case GDK_Delete: 
Miguel de Icaza's avatar
Miguel de Icaza committed
706
		sheet_selection_clear (sheet);
707 708
		break;

Miguel de Icaza's avatar
Miguel de Icaza committed
709
	case GDK_KP_Enter:
710
	case GDK_Return:
Miguel de Icaza's avatar
Miguel de Icaza committed
711 712 713
		if ((event->state & GDK_CONTROL_MASK) != 0){
			if (gsheet->item_editor){
				Cell *cell;
714 715
				char *text;
				
716
				sheet_accept_pending_input (sheet);
Miguel de Icaza's avatar
Miguel de Icaza committed
717 718 719 720
				cell = sheet_cell_get (sheet,
						       sheet->cursor_col,
						       sheet->cursor_row);

721
				/* I am assuming sheet_accept_pending_input
Miguel de Icaza's avatar
Miguel de Icaza committed
722 723 724 725 726 727
				 * will always create the cell with the given
				 * input (based on the fact that we had an
				 * gsheet->item_editor when we entered this
				 * part of the code
				 */
				g_return_val_if_fail (cell != NULL, 1);
728 729 730
				text = cell_get_text (cell);
				sheet_fill_selection_with (sheet, text);
				g_free (text);
Miguel de Icaza's avatar
Miguel de Icaza committed
731 732 733 734 735 736
			}
			return 1;
		}
		/* fall down */
		
	case GDK_Tab:
737 738 739 740 741 742 743 744 745 746
	{
		int col, row;
		int walking_selection;
		int direction, horizontal;

		/* Figure out the direction */
		direction = (event->state & GDK_SHIFT_MASK) ? 0 : 1;
		horizontal = (event->keyval == GDK_Tab) ? 1 : 0;
		
		walking_selection = sheet_selection_walk_step (
Arturo Espinosa's avatar
Today:  
Arturo Espinosa committed
747 748
			sheet, direction, horizontal,
			sheet->cursor_col, sheet->cursor_row,
749 750
			&col, &row);
		move_cursor (gsheet, col, row, walking_selection == 0);
751
		break;
752
	}
Arturo Espinosa's avatar
Arturo Espinosa committed
753 754

	case GDK_Escape:
Arturo Espinosa's avatar
Today:  
Arturo Espinosa committed
755
		sheet_cancel_pending_input (sheet);
Arturo Espinosa's avatar
Arturo Espinosa committed
756
		break;
757
		
Arturo Espinosa's avatar
Arturo Espinosa committed
758 759
	case GDK_F2:
		gtk_window_set_focus (GTK_WINDOW (wb->toplevel), wb->ea_input);
760
		/* fall down */
761

762
	default:
Arturo Espinosa's avatar
Arturo Espinosa committed
763
		if (!gsheet->item_editor){
Arturo Espinosa's avatar
Arturo Espinosa committed
764 765 766
			if ((event->state & (GDK_MOD1_MASK|GDK_CONTROL_MASK)) != 0)
				return 0;
			
Miguel de Icaza's avatar
Miguel de Icaza committed
767 768 769
			if ((event->keyval >= 0x20 && event->keyval <= 0xff) ||
			    (event->keyval >= GDK_KP_Add && event->keyval <= GDK_KP_9))
				sheet_start_editing_at_cursor (sheet);
770
		}
Miguel de Icaza's avatar
Miguel de Icaza committed
771
		gnumeric_sheet_stop_cell_selection (gsheet);
772
		
Arturo Espinosa's avatar
Arturo Espinosa committed
773
		/* Forward the keystroke to the input line */
Arturo Espinosa's avatar
Arturo Espinosa committed
774
		gtk_widget_event (gsheet->entry, (GdkEvent *) event);
Arturo Espinosa's avatar
Arturo Espinosa committed
775
		
776 777 778 779
	}
	return 1;
}

Miguel de Icaza's avatar
New:  
Miguel de Icaza committed
780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819
static gint
gnumeric_sheet_key_mode_object (GnumericSheet *gsheet, GdkEventKey *event)
{
	Sheet *sheet = gsheet->sheet_view->sheet;
	
	switch (event->keyval){
	case GDK_Escape:
		sheet_set_mode_type (sheet, SHEET_MODE_SHEET);
		break;

	case GDK_BackSpace:
	case GDK_Delete:
		sheet_object_destroy (sheet->current_object);
		sheet_set_mode_type (sheet, SHEET_MODE_SHEET);
		break;

	default:
		return FALSE;
	}
	return TRUE;
}

static gint
gnumeric_sheet_key (GtkWidget *widget, GdkEventKey *event)
{
	GnumericSheet *gsheet = GNUMERIC_SHEET (widget);
	Sheet *sheet = gsheet->sheet_view->sheet;
	
	switch (sheet->mode){
	case SHEET_MODE_SHEET:
		return gnumeric_sheet_key_mode_sheet (gsheet, event);

	case SHEET_MODE_OBJECT_SELECTED:
		return gnumeric_sheet_key_mode_object (gsheet, event);

	default:
		return TRUE;
	}
}

Arturo Espinosa's avatar
Arturo Espinosa committed
820
GtkWidget *
Arturo Espinosa's avatar
Today:  
Arturo Espinosa committed
821
gnumeric_sheet_new (SheetView *sheet_view, ItemBar *colbar, ItemBar *rowbar)
Arturo Espinosa's avatar
Arturo Espinosa committed
822
{
823
	GnomeCanvasItem *item;
Arturo Espinosa's avatar
Arturo Espinosa committed
824
	GnumericSheet *gsheet;
825 826 827
	GnomeCanvas   *gsheet_canvas;
	GnomeCanvasGroup *gsheet_group;
	GtkWidget *widget;
828
	GtkWidget *entry;
Arturo Espinosa's avatar
Today:  
Arturo Espinosa committed
829 830 831 832 833
	Sheet     *sheet;
	Workbook  *workbook;
	
	g_return_val_if_fail (sheet_view  != NULL, NULL);
	g_return_val_if_fail (IS_SHEET_VIEW (sheet_view), NULL);
834 835 836 837 838
	g_return_val_if_fail (colbar != NULL, NULL);
	g_return_val_if_fail (rowbar != NULL, NULL);
	g_return_val_if_fail (IS_ITEM_BAR (colbar), NULL);
	g_return_val_if_fail (IS_ITEM_BAR (rowbar), NULL);

Arturo Espinosa's avatar
Today:  
Arturo Espinosa committed
839 840 841 842 843
	sheet = sheet_view->sheet;
	workbook = sheet->workbook;
	
	entry = workbook->ea_input;
	gsheet = gnumeric_sheet_create (sheet_view, entry);
Arturo Espinosa's avatar
Arturo Espinosa committed
844
	gnome_canvas_set_size (GNOME_CANVAS (gsheet), 300, 100);
Arturo Espinosa's avatar
Today:  
Arturo Espinosa committed
845
	
846 847
	/* FIXME: figure out some real size for the canvas scrolling region */
	gnome_canvas_set_scroll_region (GNOME_CANVAS (gsheet), 0, 0, 1000000, 1000000);
848 849 850 851

	/* handy shortcuts */
	gsheet_canvas = GNOME_CANVAS (gsheet);
	gsheet_group = GNOME_CANVAS_GROUP (gsheet_canvas->root);
852 853 854

	gsheet->colbar = colbar;
	gsheet->rowbar = rowbar;
855 856
	
	/* The grid */
857
	item = gnome_canvas_item_new (gsheet_group,
858
				      item_grid_get_type (),
Arturo Espinosa's avatar
Today:  
Arturo Espinosa committed
859
				      "ItemGrid::SheetView", sheet_view,
860 861 862 863
				      NULL);
	gsheet->item_grid = ITEM_GRID (item);

	/* The cursor */
864
	item = gnome_canvas_item_new (gsheet_group,
865 866 867 868 869
				      item_cursor_get_type (),
				      "ItemCursor::Sheet", sheet,
				      "ItemCursor::Grid", gsheet->item_grid,
				      NULL);
	gsheet->item_cursor = ITEM_CURSOR (item);
870 871
	item_cursor_set_bounds (gsheet->item_cursor, 0, 0, 1, 1);
		
872 873 874 875 876
	widget = GTK_WIDGET (gsheet);

	return widget;
}

877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917
gnumeric_sheet_pattern_t gnumeric_sheet_patterns [GNUMERIC_SHEET_PATTERNS] = {
	{ N_("75%"),
	  { 0xee, 0xbb, 0xee, 0xbb, 0xee, 0xbb, 0xee, 0xbb } },

	{ N_("50%"),
	  { 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55 } },

	{ N_("25%"),
	  { 0x11, 0x44, 0x11, 0x44, 0x11, 0x44, 0x11, 0x44 } },

	{ N_("12%"),
	  { 0x01, 0x08, 0x40, 0x02, 0x10, 0x80, 0x04, 0x20 } },
	
	{ N_("6%"),
	  { 0x80, 0x00, 0x04, 0x00, 0x80, 0x00, 0x04, 0x00 } },

	{ N_("Horizontal lines"),
	  { 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00 } },

	{ N_("Vertical lines"),
	  { 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa } },

	{ N_("Diagonal lines"),
	  { 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80 } },

	{ N_("Diagonal lines"),
	  { 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01 } },

	{ N_("Diagonal lines"),
	  { 0x11, 0x22, 0x44, 0x88, 0x11, 0x22, 0x44, 0x88 } },

	{ N_("Diagonal lines"),
	  { 0x88, 0x44, 0x22, 0x11, 0x88, 0x44, 0x22, 0x11 } },

	{ N_("Crossed diagonals"),
	  { 0x81, 0x42, 0x24, 0x18, 0x18, 0x24, 0x42, 0x81 } },

	{ N_("Bricks"),
	  { 0x80, 0x80, 0x80, 0xff, 0x04, 0x04, 0x04, 0xff } },
};

918

919 920 921
static void
gnumeric_sheet_realize (GtkWidget *widget)
{
922
	GnumericSheet *gsheet = GNUMERIC_SHEET (widget);
923
	GdkWindow *window;
924 925
	int i;
	
926 927
	if (GTK_WIDGET_CLASS (sheet_parent_class)->realize)
		(*GTK_WIDGET_CLASS (sheet_parent_class)->realize)(widget);
928

929
	window = widget->window;
930
	gdk_window_set_back_pixmap (GTK_LAYOUT (widget)->bin_window, NULL, FALSE);
931 932 933 934

	for (i = 0; i < GNUMERIC_SHEET_PATTERNS; i++)
		gsheet->patterns [i] = gdk_bitmap_create_from_data (
			window, gnumeric_sheet_patterns [i].pattern, 8, 8);
935

Arturo Espinosa's avatar
Arturo Espinosa committed
936
	cursor_set (window, GNUMERIC_CURSOR_FAT_CROSS);
Arturo Espinosa's avatar
Arturo Espinosa committed
937 938
}

939 940
void
gnumeric_sheet_compute_visible_ranges (GnumericSheet *gsheet)
941
{
942
	GnomeCanvas   *canvas = GNOME_CANVAS (gsheet);
Arturo Espinosa's avatar
Arturo Espinosa committed
943 944 945 946 947 948 949 950 951
	int pixels, col, row;

	/* Find out the last visible col and the last full visible column */
	pixels = 0;
	col = gsheet->top_col;
	do {
		ColRowInfo *ci;
		int cb;
		
Arturo Espinosa's avatar
Today:  
Arturo Espinosa committed
952
		ci = sheet_col_get_info (gsheet->sheet_view->sheet, col);
Arturo Espinosa's avatar
Arturo Espinosa committed
953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975
		cb = pixels + ci->pixels;
		
		if (cb == canvas->width){
			gsheet->last_visible_col = col;
			gsheet->last_full_col = col;
		} if (cb > canvas->width){
			gsheet->last_visible_col = col;
			if (col == gsheet->top_col)
				gsheet->last_full_col = gsheet->top_col;
			else
				gsheet->last_full_col = col - 1;
		}
		pixels = cb;
		col++;
	} while (pixels < canvas->width);

	/* Find out the last visible row and the last fully visible row */
	pixels = 0;
	row = gsheet->top_row;
	do {
		ColRowInfo *ri;
		int cb;
		
Arturo Espinosa's avatar
Today:  
Arturo Espinosa committed
976
		ri = sheet_row_get_info (gsheet->sheet_view->sheet, row);
Arturo Espinosa's avatar
Arturo Espinosa committed
977 978 979 980 981
		cb = pixels + ri->pixels;
		
		if (cb == canvas->height){
			gsheet->last_visible_row = row;
			gsheet->last_full_row = row;
982
		} if (cb > canvas->height){
Arturo Espinosa's avatar
Arturo Espinosa committed
983 984 985 986 987 988 989 990
			gsheet->last_visible_row = row;
			if (col == gsheet->top_row)
				gsheet->last_full_row = gsheet->top_row;
			else
				gsheet->last_full_row = row - 1;
		}
		pixels = cb;
		row++;
991 992
	} while (pixels < canvas->height);
}
Arturo Espinosa's avatar
Arturo Espinosa committed
993

994 995
static int
gnumeric_sheet_bar_set_top_row (GnumericSheet *gsheet, int new_top_row)
996
{
997 998
	GnomeCanvas *rowc;
	Sheet *sheet;
999
	int row_distance;
1000
	int x;
1001 1002 1003 1004 1005 1006

	g_return_val_if_fail (gsheet != NULL, 0);
	g_return_val_if_fail (new_top_row >= 0 && new_top_row <= SHEET_MAX_ROWS-1, 0);

	rowc = GNOME_CANVAS_ITEM (gsheet->rowbar)->canvas;
	sheet = gsheet->sheet_view->sheet;
1007 1008
	gsheet->top_row = new_top_row;
	row_distance = sheet_row_get_distance (sheet, 0, gsheet->top_row);
1009 1010 1011

	gnome_canvas_get_scroll_offsets (rowc, &x, NULL);
	gnome_canvas_scroll_to (rowc, x, row_distance);
1012 1013 1014 1015
	
	return row_distance;
}

1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030
void
gnumeric_sheet_set_top_row (GnumericSheet *gsheet, int new_top_row)
{
	int distance, x;
	
	g_return_if_fail (gsheet != NULL);
	g_return_if_fail (new_top_row >= 0 && new_top_row <= SHEET_MAX_ROWS-1);

	distance = gnumeric_sheet_bar_set_top_row (gsheet, new_top_row);
	gnome_canvas_get_scroll_offsets (GNOME_CANVAS (gsheet), &x, NULL);
	gnome_canvas_scroll_to (GNOME_CANVAS (gsheet), x, distance);
}

static int
gnumeric_sheet_bar_set_top_col (GnumericSheet *gsheet, int new_top_col)
1031
{
1032 1033
	GnomeCanvas *colc;
	Sheet *sheet;
1034
	int col_distance;
1035
	int y;
1036

1037 1038 1039 1040 1041 1042
	g_return_val_if_fail (gsheet != NULL, 0);
	g_return_val_if_fail (new_top_col >= 0 && new_top_col <= SHEET_MAX_COLS-1, 0);

	colc = GNOME_CANVAS_ITEM (gsheet->colbar)->canvas;
	sheet = gsheet->sheet_view->sheet;

1043 1044
	gsheet->top_col = new_top_col;
	col_distance = sheet_col_get_distance (sheet, 0, gsheet->top_col);
1045 1046 1047

	gnome_canvas_get_scroll_offsets (colc, NULL, &y);
	gnome_canvas_scroll_to (colc, col_distance, y);
1048 1049 1050 1051
	
	return col_distance;
}

1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064
void
gnumeric_sheet_set_top_col (GnumericSheet *gsheet, int new_top_col)
{
	int distance, y;
	
	g_return_if_fail (gsheet != NULL);
	g_return_if_fail (new_top_col >= 0 && new_top_col <= SHEET_MAX_COLS-1);