gnumeric-canvas.c 33.1 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 "gnumeric-sheet.h"
10 11 12 13
#include "item-bar.h"
#include "item-cursor.h"
#include "item-edit.h"
#include "item-grid.h"
14
#include "sheet-control-gui.h"
15
#include "gnumeric-util.h"
16
#include "style-color.h"
Jody Goldberg's avatar
Jody Goldberg committed
17
#include "selection.h"
18
#include "parse-util.h"
19
#include "ranges.h"
Jody Goldberg's avatar
Jody Goldberg committed
20
#include "sheet.h"
21
#include "application.h"
22
#include "workbook-view.h"
Miguel de Icaza's avatar
Miguel de Icaza committed
23
#include "workbook-edit.h"
24
#include "workbook-control-gui-priv.h"
25
#include "workbook.h"
26
#include "commands.h"
27 28 29 30

#ifdef ENABLE_BONOBO
#  include "sheet-object-container.h"
#endif
Jody Goldberg's avatar
Jody Goldberg committed
31
#include <gal/widgets/e-cursors.h>
32

33
static GnomeCanvasClass *gsheet_parent_class;
34

35 36 37 38 39 40
static void
gnumeric_sheet_destroy (GtkObject *object)
{
	GnumericSheet *gsheet;

	/* Add shutdown code here */
Arturo Espinosa's avatar
Arturo Espinosa committed
41
	gsheet = GNUMERIC_SHEET (object);
42

43 44
	if (GTK_OBJECT_CLASS (gsheet_parent_class)->destroy)
		(*GTK_OBJECT_CLASS (gsheet_parent_class)->destroy)(object);
45 46
}

Arturo Espinosa's avatar
Arturo Espinosa committed
47 48 49 50 51 52 53
void
gnumeric_sheet_set_cursor_bounds (GnumericSheet *gsheet,
				  int start_col, int start_row,
				  int end_col,   int end_row)
{
	g_return_if_fail (GNUMERIC_IS_SHEET (gsheet));

Arturo Espinosa's avatar
Arturo Espinosa committed
54 55 56 57
	item_cursor_set_bounds (
		gsheet->item_cursor,
		start_col, start_row,
		end_col, end_row);
Arturo Espinosa's avatar
Arturo Espinosa committed
58 59
}

60
/*
61
 * gnumeric_sheet_can_select_expr_range
62 63 64 65 66 67
 *  @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.
 */
Jody Goldberg's avatar
Jody Goldberg committed
68
gboolean
69
gnumeric_sheet_can_select_expr_range (GnumericSheet *gsheet)
70
{
71
	WorkbookControlGUI const *wbcg;
72

Arturo Espinosa's avatar
Arturo Espinosa committed
73
	g_return_val_if_fail (GNUMERIC_IS_SHEET (gsheet), FALSE);
74

75
	wbcg = gsheet->scg->wbcg;
76
	if (workbook_edit_entry_redirect_p (wbcg))
77 78
		return TRUE;

79
	if (!wbcg->editing)
80
		return FALSE;
81

Jody Goldberg's avatar
Jody Goldberg committed
82
	if (gsheet->selecting_cell)
83
		return TRUE;
84

85
	return workbook_editing_expr (wbcg);
Jody Goldberg's avatar
Jody Goldberg committed
86
}
87

Jody Goldberg's avatar
Jody Goldberg committed
88
static void
89
start_range_selection_at (GnumericSheet *gsheet, int col, int row)
90 91
{
	GnomeCanvas *canvas = GNOME_CANVAS (gsheet);
92
	GnomeCanvasItem *tmp;
93
	GnomeCanvasGroup *group = GNOME_CANVAS_GROUP (canvas->root);
94 95
	Sheet *sheet = gsheet->scg->sheet;
	WorkbookControlGUI *wbcg = gsheet->scg->wbcg;
96

97
	g_return_if_fail (gsheet->selecting_cell == FALSE);
98

99
	/* Hide the primary cursor while the range selection cursor is visible
Jody Goldberg's avatar
Jody Goldberg committed
100 101
	 * and we are selecting on a different sheet than the expr being edited
	 */
102
	if (sheet != wb_control_cur_sheet (WORKBOOK_CONTROL (wbcg)))
Jody Goldberg's avatar
Jody Goldberg committed
103
		item_cursor_set_visibility (gsheet->item_cursor, FALSE);
Jody Goldberg's avatar
Jody Goldberg committed
104

105
	gsheet->selecting_cell = TRUE;
106
	tmp = gnome_canvas_item_new (group,
107
		item_cursor_get_type (),
108
		"SheetControlGUI", gsheet->scg,
109 110 111
		"Style", ITEM_CURSOR_ANTED, NULL);
	gsheet->sel_cursor = ITEM_CURSOR (tmp);
	item_cursor_set_bounds (gsheet->sel_cursor, col, row, col, row);
112

Jody Goldberg's avatar
Jody Goldberg committed
113 114 115
	/* If we are selecting a range on a different sheet this may be NULL */
	if (gsheet->item_editor)
		item_edit_disable_highlight (ITEM_EDIT (gsheet->item_editor));
116

117
	gsheet->sel_cursor_pos = GTK_EDITABLE (workbook_get_entry_logical (wbcg))->current_pos;
118 119 120 121
	gsheet->sel_text_len = 0;
}

static void
122
start_range_selection (GnumericSheet *gsheet)
Miguel de Icaza's avatar
Miguel de Icaza committed
123
{
124
	Sheet *sheet = gsheet->scg->sheet;
125 126 127
	start_range_selection_at (gsheet,
				  sheet->edit_pos_real.col,
				  sheet->edit_pos_real.row);
Miguel de Icaza's avatar
Miguel de Icaza committed
128 129 130
}

void
131
gnumeric_sheet_start_range_selection (GnumericSheet *gsheet, int col, int row)
Miguel de Icaza's avatar
Miguel de Icaza committed
132 133 134 135 136 137
{
	g_return_if_fail (GNUMERIC_IS_SHEET (gsheet));

	if (gsheet->selecting_cell)
		return;

138
	start_range_selection_at (gsheet, col, row);
Miguel de Icaza's avatar
Miguel de Icaza committed
139 140 141
}

void
142
gnumeric_sheet_stop_range_selection (GnumericSheet *gsheet)
143
{
Miguel de Icaza's avatar
Miguel de Icaza committed
144 145
	g_return_if_fail (GNUMERIC_IS_SHEET (gsheet));

146 147
	if (!gsheet->selecting_cell)
		return;
148

149
	gsheet->selecting_cell = FALSE;
Jody Goldberg's avatar
Jody Goldberg committed
150 151
	gtk_object_destroy (GTK_OBJECT (gsheet->sel_cursor));
	gsheet->sel_cursor = NULL;
Jody Goldberg's avatar
Jody Goldberg committed
152 153 154 155

	/* If we are selecting a range on a different sheet this may be NULL */
	if (gsheet->item_editor)
		item_edit_enable_highlight (ITEM_EDIT (gsheet->item_editor));
Jody Goldberg's avatar
Jody Goldberg committed
156 157 158

	/* Make the primary cursor visible again */
	item_cursor_set_visibility (gsheet->item_cursor, TRUE);
159 160
}

Arturo Espinosa's avatar
Arturo Espinosa committed
161
void
162
gnumeric_sheet_create_editor (GnumericSheet *gsheet)
Arturo Espinosa's avatar
Arturo Espinosa committed
163 164 165 166
{
	GnomeCanvas *canvas = GNOME_CANVAS (gsheet);
	GnomeCanvasItem *item;

167
	g_return_if_fail (gsheet->item_editor == NULL);
168

169
	item = gnome_canvas_item_new (GNOME_CANVAS_GROUP (canvas->root),
Arturo Espinosa's avatar
Arturo Espinosa committed
170
				      item_edit_get_type (),
171
				      "ItemEdit::SheetControlGUI",     gsheet->scg,
Arturo Espinosa's avatar
Arturo Espinosa committed
172 173 174 175 176 177 178 179 180 181
				      NULL);

	gsheet->item_editor = ITEM_EDIT (item);
}

void
gnumeric_sheet_stop_editing (GnumericSheet *gsheet)
{
	g_return_if_fail (GNUMERIC_IS_SHEET (gsheet));

182 183 184 185
	if (gsheet->item_editor != NULL) {
		gtk_object_destroy (GTK_OBJECT (gsheet->item_editor));
		gsheet->item_editor = NULL;
	}
Arturo Espinosa's avatar
Arturo Espinosa committed
186 187
}

Jody Goldberg's avatar
Jody Goldberg committed
188 189 190 191 192 193 194 195
/**
 * gnumeric_sheet_rangesel_cursor_extend :
 *
 * @gsheet :
 * @col :
 * @row :
 *
 * Extend the range selection cursor.  If col < 0 then extend to full 
Miguel de Icaza's avatar
Miguel de Icaza committed
196 197
 */
void
Jody Goldberg's avatar
Jody Goldberg committed
198
gnumeric_sheet_rangesel_cursor_extend (GnumericSheet *gsheet, int col, int row)
Miguel de Icaza's avatar
Miguel de Icaza committed
199 200
{
	ItemCursor *ic;
Jody Goldberg's avatar
Jody Goldberg committed
201
	int base_col, base_row;
202

Miguel de Icaza's avatar
Miguel de Icaza committed
203 204 205
	g_return_if_fail (GNUMERIC_IS_SHEET (gsheet));
	g_return_if_fail (gsheet->selecting_cell);

Jody Goldberg's avatar
Jody Goldberg committed
206
	ic = gsheet->sel_cursor;
Jody Goldberg's avatar
Jody Goldberg committed
207 208 209 210 211 212 213 214 215 216
	if (col < 0) {
		base_col = 0;
		col = SHEET_MAX_COLS - 1;
	} else
		base_col = ic->base_corner.col;
	if (row < 0) {
		base_row = 0;
		row = SHEET_MAX_ROWS - 1;
	} else
		base_row = ic->base_corner.row;
Miguel de Icaza's avatar
Miguel de Icaza committed
217

Jody Goldberg's avatar
Jody Goldberg committed
218
	item_cursor_set_bounds (ic, base_col, base_row, col, row);
Miguel de Icaza's avatar
Miguel de Icaza committed
219 220 221 222 223 224 225
}

/*
 * Invoked by Item-Grid to place the selection cursor on a specific
 * spot.
 */
void
Jody Goldberg's avatar
Jody Goldberg committed
226 227 228
gnumeric_sheet_rangesel_cursor_bounds (GnumericSheet *gsheet,
				       int base_col, int base_row,
				       int move_col, int move_row)
Miguel de Icaza's avatar
Miguel de Icaza committed
229 230
{
	ItemCursor *ic;
231

Miguel de Icaza's avatar
Miguel de Icaza committed
232 233
	g_return_if_fail (GNUMERIC_IS_SHEET (gsheet));
	g_return_if_fail (gsheet->selecting_cell);
234

Jody Goldberg's avatar
Jody Goldberg committed
235
	ic = gsheet->sel_cursor;
Jody Goldberg's avatar
Jody Goldberg committed
236
	item_cursor_set_bounds (ic, base_col, base_row, move_col, move_row);
Miguel de Icaza's avatar
Miguel de Icaza committed
237 238
}

239 240 241
void
gnumeric_sheet_rangesel_horizontal_move (GnumericSheet *gsheet, int dir,
					 gboolean jump_to_boundaries)
242 243
{
	ItemCursor *ic;
244
	int col, row;
245 246

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

248
	if (!gsheet->selecting_cell)
249
		start_range_selection (gsheet);
250

Jody Goldberg's avatar
Jody Goldberg committed
251
	ic = gsheet->sel_cursor;
252 253 254 255 256
	row = ic->base_corner.row;
	col = sheet_find_boundary_horizontal (gsheet->scg->sheet,
		ic->base_corner.col, row, row, dir, jump_to_boundaries);
	item_cursor_set_bounds (ic, col, row, col, row);
	gnumeric_sheet_make_cell_visible (gsheet, col, row, FALSE);
257 258
}

259 260 261
void
gnumeric_sheet_rangesel_vertical_move (GnumericSheet *gsheet, int dir,
				       gboolean jump_to_boundaries)
262 263
{
	ItemCursor *ic;
264
	int col, row;
265 266 267 268

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

	if (!gsheet->selecting_cell)
269
		start_range_selection (gsheet);
270

Jody Goldberg's avatar
Jody Goldberg committed
271
	ic = gsheet->sel_cursor;
272 273 274 275 276
	col = ic->base_corner.col;
	row = sheet_find_boundary_vertical (gsheet->scg->sheet,
		col, ic->base_corner.row, col, dir, jump_to_boundaries);
	item_cursor_set_bounds (ic, col, row, col, row);
	gnumeric_sheet_make_cell_visible (gsheet, col, row, FALSE);
277 278
}

279 280 281
void
gnumeric_sheet_rangesel_horizontal_extend (GnumericSheet *gsheet, int n,
					   gboolean jump_to_boundaries)
282 283
{
	ItemCursor *ic;
284
	int new_col;
285

286
	g_return_if_fail (n == -1 || n == 1);
287

288
	if (!gsheet->selecting_cell) {
289 290
		scg_rangesel_horizontal_move (gsheet->scg, n,
					      jump_to_boundaries);
291 292 293
		return;
	}

Jody Goldberg's avatar
Jody Goldberg committed
294
	ic = gsheet->sel_cursor;
295 296
	new_col = sheet_find_boundary_horizontal (gsheet->scg->sheet,
		ic->move_corner.col, ic->move_corner.row, 
297
		ic->base_corner.row, n, jump_to_boundaries);
298
	item_cursor_set_bounds (ic,
299 300 301 302
				ic->base_corner.col, ic->base_corner.row,
				new_col, ic->move_corner.row);
	gnumeric_sheet_make_cell_visible (gsheet,
		ic->move_corner.col, ic->move_corner.row, FALSE);
303 304
}

305 306 307
void
gnumeric_sheet_rangesel_vertical_extend (GnumericSheet *gsheet, int n,
					 gboolean jump_to_boundaries)
308 309
{
	ItemCursor *ic;
310
	int new_row;
311

312
	g_return_if_fail (n == -1 || n == 1);
313

314
	if (!gsheet->selecting_cell) {
315 316
		scg_rangesel_vertical_move (gsheet->scg, n,
					    jump_to_boundaries);
317 318 319
		return;
	}

Jody Goldberg's avatar
Jody Goldberg committed
320
	ic = gsheet->sel_cursor;
321 322
	new_row = sheet_find_boundary_vertical (gsheet->scg->sheet,
		ic->move_corner.col, ic->move_corner.row, 
323
		ic->base_corner.col, n, jump_to_boundaries);
324
	item_cursor_set_bounds (ic,
325 326 327 328
				ic->base_corner.col, ic->base_corner.row,
				ic->move_corner.col, new_row);
	gnumeric_sheet_make_cell_visible (gsheet,
		ic->move_corner.col, ic->move_corner.row, FALSE);
329 330 331
}

/*
Miguel de Icaza's avatar
New:  
Miguel de Icaza committed
332
 * key press event handler for the gnumeric sheet for the sheet mode
333
 */
334
static gint
Miguel de Icaza's avatar
New:  
Miguel de Icaza committed
335
gnumeric_sheet_key_mode_sheet (GnumericSheet *gsheet, GdkEventKey *event)
336
{
337 338
	Sheet *sheet = gsheet->scg->sheet;
	WorkbookControlGUI *wbcg = gsheet->scg->wbcg;
339 340
	void (*movefn_horizontal) (SheetControlGUI *, int, gboolean);
	void (*movefn_vertical)   (SheetControlGUI *, int, gboolean);
341
	gboolean const jump_to_bounds = event->state & GDK_CONTROL_MASK;
342

343 344
	/* Magic : Some of these are accelerators,
	 * we need to catch them before entering because they appear to be printable
345
	 */
346 347 348 349 350 351 352 353
	if (!wbcg->editing && event->keyval == GDK_space &&
	    (event->state & (GDK_SHIFT_MASK|GDK_CONTROL_MASK)))
		return FALSE;

	if (gnumeric_sheet_can_select_expr_range (gsheet)) {
		/* Ignore a few keys (to avoid the selection cursor to be
		 * killed in some cases
		 */
354
		switch (event->keyval) {
Miguel de Icaza's avatar
Miguel de Icaza committed
355 356 357
		case GDK_Shift_L:   case GDK_Shift_R:
		case GDK_Alt_L:     case GDK_Alt_R:
		case GDK_Control_L: case GDK_Control_R:
358 359
			return 1;
		}
Tristan Tarrant's avatar
Tristan Tarrant committed
360

361
		if (event->state & GDK_SHIFT_MASK) {
362 363
			movefn_horizontal = scg_rangesel_horizontal_extend;
			movefn_vertical = scg_rangesel_vertical_extend;
364
		} else {
365 366
			movefn_horizontal = scg_rangesel_horizontal_move;
			movefn_vertical   = scg_rangesel_vertical_move;
367 368 369
		}
	} else {
		if (event->state & GDK_SHIFT_MASK) {
370 371
			movefn_horizontal = scg_cursor_horizontal_extend;
			movefn_vertical   = scg_cursor_vertical_extend;
372
		} else {
373 374
			movefn_horizontal = scg_cursor_horizontal_move;
			movefn_vertical = scg_cursor_vertical_move;
375 376
		}
	}
Tristan Tarrant's avatar
Tristan Tarrant committed
377

378
	switch (event->keyval) {
Miguel de Icaza's avatar
Miguel de Icaza committed
379
	case GDK_KP_Left:
380
	case GDK_Left:
381
		(*movefn_horizontal)(gsheet->scg, -1, jump_to_bounds);
382 383
		break;

Miguel de Icaza's avatar
Miguel de Icaza committed
384
	case GDK_KP_Right:
385
	case GDK_Right:
386
		(*movefn_horizontal)(gsheet->scg, 1, jump_to_bounds);
387 388
		break;

Miguel de Icaza's avatar
Miguel de Icaza committed
389
	case GDK_KP_Up:
390
	case GDK_Up:
391
		(*movefn_vertical)(gsheet->scg, -1, jump_to_bounds);
392 393
		break;

Miguel de Icaza's avatar
Miguel de Icaza committed
394
	case GDK_KP_Down:
395
	case GDK_Down:
396
		(*movefn_vertical)(gsheet->scg, 1, jump_to_bounds);
397 398
		break;

Miguel de Icaza's avatar
Miguel de Icaza committed
399
	case GDK_KP_Page_Up:
400
	case GDK_Page_Up:
401
		if ((event->state & GDK_CONTROL_MASK) != 0)
Jody Goldberg's avatar
Jody Goldberg committed
402
			gtk_notebook_prev_page (wbcg->notebook);
403
		else if ((event->state & GDK_MOD1_MASK) == 0)
404 405 406 407
			(*movefn_vertical)(
				gsheet->scg,
				-(gsheet->row.last_visible-gsheet->row.first),
				FALSE);
408
		else
409 410 411 412
			(*movefn_horizontal)(
				gsheet->scg,
				-(gsheet->col.last_visible-gsheet->col.first),
				FALSE);
413 414
		break;

Miguel de Icaza's avatar
Miguel de Icaza committed
415
	case GDK_KP_Page_Down:
416
	case GDK_Page_Down:
417
		if ((event->state & GDK_CONTROL_MASK) != 0)
Jody Goldberg's avatar
Jody Goldberg committed
418
			gtk_notebook_next_page (wbcg->notebook);
419
		else if ((event->state & GDK_MOD1_MASK) == 0)
420 421 422 423
			(*movefn_vertical)(
				gsheet->scg,
				gsheet->row.last_visible-gsheet->row.first,
				FALSE);
424
		else
425 426 427 428
			(*movefn_horizontal)(
				gsheet->scg,
				gsheet->col.last_visible-gsheet->col.first,
				FALSE);
429 430
		break;

Miguel de Icaza's avatar
Miguel de Icaza committed
431
	case GDK_KP_Home:
432
	case GDK_Home:
Jody Goldberg's avatar
Jody Goldberg committed
433
		if ((event->state & GDK_CONTROL_MASK) != 0)
434
			scg_move_cursor (gsheet->scg, 0, 0, TRUE);
Jody Goldberg's avatar
Jody Goldberg committed
435
		else
436 437
			(*movefn_horizontal)(
				gsheet->scg, -sheet->edit_pos.col, FALSE);
438 439 440
		break;

	case GDK_KP_Delete:
441
	case GDK_Delete:
442
		cmd_clear_selection (WORKBOOK_CONTROL (wbcg), sheet, CLEAR_VALUES);
443 444
		break;

Miguel de Icaza's avatar
Miguel de Icaza committed
445 446
	/*
	 * NOTE : Keep these in sync with the condition
447 448
	 *        for tabs.
	 */
Miguel de Icaza's avatar
Miguel de Icaza committed
449
	case GDK_KP_Enter:
450
	case GDK_Return:
451
		if (wbcg->editing &&
452 453 454 455
		    (event->state == GDK_CONTROL_MASK ||
		     event->state == (GDK_CONTROL_MASK|GDK_SHIFT_MASK) ||
		     event->state == GDK_MOD1_MASK))
			/* Forward the keystroke to the input line */
Miguel de Icaza's avatar
Miguel de Icaza committed
456
			return gtk_widget_event (
457
				GTK_WIDGET (workbook_get_entry_logical (wbcg)),
Miguel de Icaza's avatar
Miguel de Icaza committed
458
				(GdkEvent *) event);
Miguel de Icaza's avatar
Miguel de Icaza committed
459
		/* fall down */
460

Miguel de Icaza's avatar
Miguel de Icaza committed
461
	case GDK_Tab:
462 463
	case GDK_ISO_Left_Tab:
	case GDK_KP_Tab:
464
	{
Jody Goldberg's avatar
Jody Goldberg committed
465
		/* Be careful to restore the editing sheet if we are editing */
466 467
		if (wbcg->editing)
			sheet = wbcg->editing_sheet;
468 469 470 471 472 473 474 475 476
			
		if (workbook_finish_editing (wbcg, TRUE)) {
			/* Figure out the direction */
			gboolean const direction = (event->state & GDK_SHIFT_MASK) ? FALSE : TRUE;
			gboolean const horizontal = (event->keyval == GDK_KP_Enter ||
						     event->keyval == GDK_Return) ? FALSE : TRUE;

			sheet_selection_walk_step (sheet, direction, horizontal);
		}
477
		break;
478
	}
Arturo Espinosa's avatar
Arturo Espinosa committed
479 480

	case GDK_Escape:
481
		workbook_finish_editing (wbcg, FALSE);
482
		application_clipboard_unant ();
Arturo Espinosa's avatar
Arturo Espinosa committed
483
		break;
484

Jody Goldberg's avatar
Jody Goldberg committed
485
	case GDK_F4:
486 487
		if (wbcg->editing && gsheet->sel_cursor)
			workbook_edit_toggle_absolute (wbcg);
Jody Goldberg's avatar
Jody Goldberg committed
488 489
		break;

Arturo Espinosa's avatar
Arturo Espinosa committed
490
	case GDK_F2:
491
		workbook_start_editing_at_cursor (wbcg, FALSE, FALSE);
492
		/* fall down */
493

494
	default:
495
		if (!wbcg->editing) {
496
			if ((event->state & (GDK_MOD1_MASK|GDK_CONTROL_MASK)) != 0)
497
				return FALSE;
498

Jody Goldberg's avatar
Jody Goldberg committed
499 500
			/* If the character is not printable do not start editing */
			if (event->length == 0)
501
				return FALSE;
Jody Goldberg's avatar
Jody Goldberg committed
502

503
			workbook_start_editing_at_cursor (wbcg, TRUE, TRUE);
504
		}
505
		scg_stop_range_selection (gsheet->scg, FALSE);
506

Arturo Espinosa's avatar
Arturo Espinosa committed
507
		/* Forward the keystroke to the input line */
508
		return gtk_widget_event (GTK_WIDGET (workbook_get_entry_logical (wbcg)),
Miguel de Icaza's avatar
Miguel de Icaza committed
509
					 (GdkEvent *) event);
510
	}
511 512 513 514

	if (wbcg->editing)
		sheet_update_only_grid (sheet);
	else
515
		sheet_update (sheet);
516 517

	return TRUE;
518 519
}

Miguel de Icaza's avatar
New:  
Miguel de Icaza committed
520 521 522
static gint
gnumeric_sheet_key_mode_object (GnumericSheet *gsheet, GdkEventKey *event)
{
523
	SheetControlGUI *scg = gsheet->scg;
524

525
	switch (event->keyval) {
Miguel de Icaza's avatar
New:  
Miguel de Icaza committed
526
	case GDK_Escape:
527
		scg_mode_edit (scg);
528
		application_clipboard_unant ();
Miguel de Icaza's avatar
New:  
Miguel de Icaza committed
529 530
		break;

531 532
	case GDK_BackSpace: /* Ick! */
	case GDK_KP_Delete:
Miguel de Icaza's avatar
New:  
Miguel de Icaza committed
533
	case GDK_Delete:
534
		gtk_object_destroy (GTK_OBJECT (scg->current_object));
Miguel de Icaza's avatar
New:  
Miguel de Icaza committed
535 536 537 538 539 540 541 542 543
		break;

	default:
		return FALSE;
	}
	return TRUE;
}

static gint
544
gnumeric_sheet_key_press (GtkWidget *widget, GdkEventKey *event)
Miguel de Icaza's avatar
New:  
Miguel de Icaza committed
545 546
{
	GnumericSheet *gsheet = GNUMERIC_SHEET (widget);
547
	SheetControlGUI *scg = gsheet->scg;
548

549
	if (scg->current_object != NULL || scg->new_object != NULL)
Miguel de Icaza's avatar
New:  
Miguel de Icaza committed
550
		return gnumeric_sheet_key_mode_object (gsheet, event);
551
	return gnumeric_sheet_key_mode_sheet (gsheet, event);
Miguel de Icaza's avatar
New:  
Miguel de Icaza committed
552 553
}

554 555 556 557
static gint
gnumeric_sheet_key_release (GtkWidget *widget, GdkEventKey *event)
{
	GnumericSheet *gsheet = GNUMERIC_SHEET (widget);
558
	SheetControlGUI *scg = gsheet->scg;
559 560 561 562 563 564 565 566

	/*
	 * The status_region normally displays the current edit_pos
	 * When we extend the selection it changes to displaying the size of
	 * the selected region while we are selecting.  When the shift key
	 * is released, or the mouse button is release we need to reset
	 * to displaying the edit pos.
	 */
567
	if (scg->current_object == NULL &&
568
	    (event->keyval == GDK_Shift_L || event->keyval == GDK_Shift_R))
569 570
		wb_view_selection_desc (wb_control_view (
			WORKBOOK_CONTROL (gsheet->scg->wbcg)), TRUE, NULL);
571

572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604
	return (*GTK_WIDGET_CLASS (gsheet_parent_class)->key_release_event)(widget, event);
}

static gint
gnumeric_sheet_button_release (GtkWidget *widget, GdkEventButton *button)
{
	GnumericSheet *gsheet = GNUMERIC_SHEET (widget);

	if (button->button != 4 && button->button != 5)
		return (*GTK_WIDGET_CLASS (gsheet_parent_class)->button_release_event)(widget, button);

	/* Roll Up or Left */
	/* Roll Down or Right */
	if ((button->state & GDK_MOD1_MASK)) {
		int col = gsheet->col.last_full - gsheet->col.first;
		if (button->button == 4)
			col = MAX (gsheet->col.first - col, 0);
		else if (gsheet->col.last_full < SHEET_MAX_COLS-1)
			col = gsheet->col.last_full;
		else
			return FALSE;
		gnumeric_sheet_set_left_col (gsheet, col);
	} else {
		int row = gsheet->row.last_full - gsheet->row.first;
		if (button->button == 4)
			row = MAX (gsheet->row.first - row, 0);
		else if (gsheet->row.last_full < SHEET_MAX_ROWS-1)
			row = gsheet->row.last_full;
		else
			return FALSE;
		gnumeric_sheet_set_top_row (gsheet, row);
	}
	return TRUE;
605 606
}

607 608 609 610 611 612 613
/* Focus in handler for the canvas */
static gint
gnumeric_sheet_focus_in (GtkWidget *widget, GdkEventFocus *event)
{
	GnumericSheet *gsheet = GNUMERIC_SHEET (widget);
	if (gsheet->ic)
		gdk_im_begin (gsheet->ic, gsheet->canvas.layout.bin_window);
614
	return (*GTK_WIDGET_CLASS (gsheet_parent_class)->focus_in_event)(widget, event);
615 616 617 618 619 620 621
}

/* Focus out handler for the canvas */
static gint
gnumeric_sheet_focus_out (GtkWidget *widget, GdkEventFocus *event)
{
	gdk_im_end ();
622
	return (*GTK_WIDGET_CLASS (gsheet_parent_class)->focus_out_event)(widget, event);
623 624
}

625 626 627 628 629 630 631
static void
gnumeric_sheet_drag_data_get (GtkWidget *widget,
			      GdkDragContext *context,
			      GtkSelectionData *selection_data,
			      guint info,
			      guint time)
{
632
#if 0
633
	BonoboMoniker *moniker;
634
	Sheet *sheet = GNUMERIC_SHEET (widget)->scg->sheet;
635 636
	Workbook *wb = sheet->workbook;
	char *s;
637
	WorkbookControl *wbc = WORKBOOK_CONTROL (gsheet->scg->wbcg);
638

639
	if (wb->filename == NULL)
640
		workbook_save (wbc, wb);
641 642
	if (wb->filename == NULL)
		return;
643

644 645
	moniker = bonobo_moniker_new ();
	bonobo_moniker_set_server (
646 647 648 649
		moniker,
		"IDL:GNOME:Gnumeric:Workbook:1.0",
		wb->filename);

650
	bonobo_moniker_append_item_name (
651
		moniker, "Sheet1");
652
	s = bonobo_moniker_get_as_string (moniker);
653 654 655
	gtk_object_destroy (GTK_OBJECT (moniker));

	gtk_selection_data_set (selection_data, selection_data->target, 8, s, strlen (s)+1);
656
#endif
657 658
}

659 660 661
/*
 * gnumeric_sheet_filenames_dropped :
 */
662 663 664 665 666 667 668 669 670 671 672
static void
gnumeric_sheet_filenames_dropped (GtkWidget        *widget,
				  GdkDragContext   *context,
				  gint              x,
				  gint              y,
				  GtkSelectionData *selection_data,
				  guint             info,
				  guint             time,
				  GnumericSheet    *gsheet)
{
	GList *names, *tmp_list;
673
	WorkbookControl *wbc = WORKBOOK_CONTROL (gsheet->scg->wbcg);
674 675

	names = gnome_uri_list_extract_filenames ((char *)selection_data->data);
676

677 678 679
	for (tmp_list = names; tmp_list != NULL; tmp_list = tmp_list->next) {
		gchar *file_name = tmp_list->data;
		WorkbookView *new_wb = workbook_try_read (wbc, file_name);
680

681 682 683
		if (new_wb != NULL) {
			(void) file_finish_load (wbc, new_wb);
		} else {
684 685 686
#ifdef ENABLE_BONOBO
			/* If it wasn't a workbook, see if we have a control for it */
			SheetObject *so = sheet_object_container_new_file (
687
			                  gsheet->scg->sheet, file_name);
688
			if (so != NULL)
689
				scg_mode_create_object (gsheet->scg, so);
690 691 692 693 694 695 696
#else
			gchar *msg;

			msg = g_strdup_printf (_("File \"%s\" has unknown format."),
			                       file_name);
			gnumeric_error_read (COMMAND_CONTEXT (wbc), msg);
			g_free (msg);
697
#endif
698
		}
699 700 701
	}
}

Arturo Espinosa's avatar
Arturo Espinosa committed
702
GtkWidget *
703
gnumeric_sheet_new (SheetControlGUI *scg)
Arturo Espinosa's avatar
Arturo Espinosa committed
704
{
705
	static GtkTargetEntry const drag_types[] = {
706 707
		{ "text/uri-list", 0, 0 },
	};
708 709 710 711 712 713
	static gint const n_drag_types = sizeof (drag_types) / sizeof (drag_types [0]);

	GnomeCanvasItem	 *item;
	GnumericSheet	 *gsheet;
	GnomeCanvasGroup *gsheet_group;
	GtkWidget	 *widget;
714

715
	g_return_val_if_fail (IS_SHEET_CONTROL_GUI (scg), NULL);
716

717 718 719
	gsheet = gtk_type_new (gnumeric_sheet_get_type ());
	gsheet_group = GNOME_CANVAS_GROUP (GNOME_CANVAS (gsheet)->root);
	widget = GTK_WIDGET (gsheet);
720

721 722 723 724 725
	gsheet->scg = scg;
	gsheet->row.first = gsheet->row.last_full = gsheet->row.last_visible = 0;
	gsheet->col.first = gsheet->col.last_full = gsheet->col.last_visible = 0;
	gsheet->row_offset.first = gsheet->row_offset.last_full = gsheet->row_offset.last_visible = 0;
	gsheet->col_offset.first = gsheet->col_offset.last_full = gsheet->col_offset.last_visible = 0;
726

727
	/* FIXME: figure out some real size for the canvas scrolling region */
728
	gnome_canvas_set_scroll_region (GNOME_CANVAS (gsheet), 0, 0,
729
					GNUMERIC_SHEET_FACTOR_X, GNUMERIC_SHEET_FACTOR_Y);
730 731

	/* The grid */
732
	item = gnome_canvas_item_new (gsheet_group,
733 734 735
		item_grid_get_type (),
		"ItemGrid::SheetControlGUI", scg,
		NULL);
736 737 738
	gsheet->item_grid = ITEM_GRID (item);

	/* The cursor */
739
	item = gnome_canvas_item_new (gsheet_group,
740 741 742
		item_cursor_get_type (),
		"ItemCursor::SheetControlGUI", scg,
		NULL);
743
	gsheet->item_cursor = ITEM_CURSOR (item);
744
	item_cursor_set_bounds (gsheet->item_cursor, 0, 0, 0, 0); /* A1 */
Jody Goldberg's avatar
Jody Goldberg committed
745

746 747 748
	/* Setup a test of Drag and Drop */
	gtk_signal_connect (GTK_OBJECT (widget),
		"drag_data_get",
749
		GTK_SIGNAL_FUNC (gnumeric_sheet_drag_data_get), NULL);
750
	gtk_drag_dest_set (widget,
751 752 753
		GTK_DEST_DEFAULT_ALL,
		drag_types, n_drag_types,
		GDK_ACTION_COPY);
754
	gtk_signal_connect (GTK_OBJECT (widget),
755 756 757
		"drag_data_received",
		GTK_SIGNAL_FUNC (gnumeric_sheet_filenames_dropped),
		widget);
758

759 760 761 762 763 764
	return widget;
}

static void
gnumeric_sheet_realize (GtkWidget *widget)
{
765
	GdkWindow *window;
766
	GnumericSheet *gsheet;
767

768 769
	if (GTK_WIDGET_CLASS (gsheet_parent_class)->realize)
		(*GTK_WIDGET_CLASS (gsheet_parent_class)->realize)(widget);
770

771
	window = widget->window;
772
	gdk_window_set_back_pixmap (GTK_LAYOUT (widget)->bin_window, NULL, FALSE);
773

774
	e_cursor_set (window, E_CURSOR_FAT_CROSS);
775 776 777 778 779 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

	gsheet = GNUMERIC_SHEET (widget);
	if (gdk_im_ready () && (gsheet->ic_attr = gdk_ic_attr_new ()) != NULL) {
		GdkEventMask mask;
		GdkICAttr *attr = gsheet->ic_attr;
		GdkICAttributesType attrmask = GDK_IC_ALL_REQ;
		GdkIMStyle style;
		GdkIMStyle supported_style = GDK_IM_PREEDIT_NONE |
			GDK_IM_PREEDIT_NOTHING |
			GDK_IM_STATUS_NONE |
			GDK_IM_STATUS_NOTHING;

		attr->style = style = gdk_im_decide_style (supported_style);
		attr->client_window = gsheet->canvas.layout.bin_window;

		gsheet->ic = gdk_ic_new (attr, attrmask);
		if (gsheet->ic != NULL) {
			mask = gdk_window_get_events (attr->client_window);
			mask |= gdk_ic_get_events (gsheet->ic);
			gdk_window_set_events (attr->client_window, mask);

			if (GTK_WIDGET_HAS_FOCUS (widget))
				gdk_im_begin (gsheet->ic, attr->client_window);
		} else
			g_warning ("Can't create input context.");
	}
}

static void
gnumeric_sheet_unrealize (GtkWidget *widget)
{
	GnumericSheet *gsheet;

	gsheet = GNUMERIC_SHEET (widget);
	g_return_if_fail (gsheet != NULL);

	if (gsheet->ic) {
		gdk_ic_destroy (gsheet->ic);
		gsheet->ic = NULL;
	}
	if (gsheet->ic_attr) {
		gdk_ic_attr_destroy (gsheet->ic_attr);
		gsheet->ic_attr = NULL;
	}

820
	(*GTK_WIDGET_CLASS (gsheet_parent_class)->unrealize)(widget);
Arturo Espinosa's avatar
Arturo Espinosa committed
821 822
}

Jody Goldberg's avatar
Jody Goldberg committed
823
/*
824
 * gsheet_compute_visible_region : Keeps the top left col/row the same and
Jody Goldberg's avatar
Jody Goldberg committed
825 826 827 828 829 830
 *     recalculates the visible boundaries.
 *
 * @full_recompute :
 *       if TRUE recompute the pixel offsets of the top left row/col
 *       else assumes that the pixel offsets of the top left have not changed.
 */
831
void
832 833
gsheet_compute_visible_region (GnumericSheet *gsheet,
			       gboolean const full_recompute)
834
{
835 836
	SheetControlGUI const * const scg = gsheet->scg;
	Sheet const * const sheet = scg->sheet;
837
	GnomeCanvas   *canvas = GNOME_CANVAS (gsheet);
838
	int pixels, col, row, width, height;
Arturo Espinosa's avatar
Arturo Espinosa committed
839

Jody Goldberg's avatar
Jody Goldberg committed
840 841 842
	/* When col/row sizes change we need to do a full recompute */
	if (full_recompute) {
		gsheet->col_offset.first =
843
			scg_colrow_distance_get (scg, TRUE, 0, gsheet->col.first);
844 845
		gnome_canvas_scroll_to (scg->col_item->canvas,
			gsheet->col_offset.first, 0);
Jody Goldberg's avatar
Jody Goldberg committed
846
		gsheet->row_offset.first =
847
			scg_colrow_distance_get (scg, FALSE, 0, gsheet->row.first);
848 849
		gnome_canvas_scroll_to (scg->row_item->canvas,
			0, gsheet->row_offset.first);
Jody Goldberg's avatar
Jody Goldberg committed
850

851
		gnome_canvas_scroll_to (GNOME_CANVAS (gsheet),
Jody Goldberg's avatar
Jody Goldberg committed
852 853 854 855
					gsheet->col_offset.first,
					gsheet->row_offset.first);
	}

Arturo Espinosa's avatar
Arturo Espinosa committed
856 857
	/* Find out the last visible col and the last full visible column */
	pixels = 0;
858
	col = gsheet->col.first;
859
	width = GTK_WIDGET (canvas)->allocation.width;
860

Arturo Espinosa's avatar
Arturo Espinosa committed
861
	do {
Jody Goldberg's avatar
Jody Goldberg committed
862
		ColRowInfo const * const ci = sheet_col_get_info (sheet, col);
863 864
		if (ci->visible) {
			int const bound = pixels + ci->size_pixels;
865

866
			if (bound == width) {
867 868
				gsheet->col.last_visible = col;
				gsheet->col.last_full = col;
869
				break;
870
			}
871
			if (bound > width) {
872 873 874
				gsheet->col.last_visible = col;
				if (col == gsheet->col.first)
					gsheet->col.last_full = gsheet->col.first;
875
				else
876
					gsheet->col.last_full = col - 1;
877 878
				break;
			}
879
			pixels = bound;
Arturo Espinosa's avatar
Arturo Espinosa committed
880
		}
881 882 883 884
		++col;
	} while (pixels < width && col < SHEET_MAX_COLS);

	if (col >= SHEET_MAX_COLS) {
885 886
		gsheet->col.last_visible = SHEET_MAX_COLS-1;
		gsheet->col.last_full = SHEET_MAX_COLS-1;
887
	}
Arturo Espinosa's avatar
Arturo Espinosa committed
888 889 890

	/* Find out the last visible row and the last fully visible row */
	pixels = 0;
891
	row = gsheet->row.first;
892
	height = GTK_WIDGET (canvas)->allocation.height;
Arturo Espinosa's avatar
Arturo Espinosa committed
893
	do {
Jody Goldberg's avatar
Jody Goldberg committed
894
		ColRowInfo const * const ri = sheet_row_get_info (sheet, row);
895 896
		if (ri->visible) {
			int const bound = pixels + ri->size_pixels;
897

898
			if (bound == height) {
899 900
				gsheet->row.last_visible = row;
				gsheet->row.last_full = row;
901
				break;
902
			}
903
			if (bound > height) {
904 905 906
				gsheet->row.last_visible = row;
				if (row == gsheet->row.first)
					gsheet->row.last_full = gsheet->row.first;
907
				else
908
					gsheet->row.last_full = row - 1;
909 910
				break;
			}
911
			pixels = bound;
Arturo Espinosa's avatar
Arturo Espinosa committed
912
		}
913
		++row;
914 915
	} while (pixels < height && row < SHEET_MAX_ROWS);

Miguel de Icaza's avatar
Miguel de Icaza committed
916
	if (row >= SHEET_MAX_ROWS) {
917 918
		gsheet->row.last_visible = SHEET_MAX_ROWS-1;
		gsheet->row.last_full = SHEET_MAX_ROWS-1;
919
	}
Jody Goldberg's avatar
Jody Goldberg committed
920 921

	/* Update the scrollbar sizes */
922
	scg_scrollbar_config (scg);
923 924

	/* Force the cursor to update its bounds relative to the new visible region */
925
	item_cursor_reposition (gsheet->item_cursor);
926
}
Arturo Espinosa's avatar
Arturo Espinosa committed
927

928
static int
929
gnumeric_sheet_bar_set_top_row (GnumericSheet *gsheet, int new_first_row)
930
{
931
	GnomeCanvas *rowc;
932
	int row_offset;
933
	int x;
934 935

	g_return_val_if_fail (gsheet != NULL, 0);
Jody Goldberg's avatar
Jody Goldberg committed
936