gnumeric-canvas.c 37.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 *sheet_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 45 46
	if (GTK_OBJECT_CLASS (sheet_parent_class)->destroy)
		(*GTK_OBJECT_CLASS (sheet_parent_class)->destroy)(object);
}

47
/*
48
 * move_cursor:
Arturo Espinosa's avatar
Arturo Espinosa committed
49 50 51 52 53
 * @gsheet:   The sheet where the cursor is located
 * @col:      The new column for the cursor.
 * @row:      The new row for the cursor.
 * @clear_selection: If set, clear the selection before moving
 *
54 55 56 57 58
 *   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
Arturo Espinosa committed
59
move_cursor (GnumericSheet *gsheet, int col, int row, gboolean clear_selection)
60
{
61
	Sheet *sheet = gsheet->scg->sheet;
62 63 64 65 66 67 68 69 70 71 72 73

	/*
	 * Please note that the order here is important, as
	 * the sheet_make_cell_visible call might scroll the
	 * canvas, you should do all of your screen changes
	 * in an atomic fashion.
	 *
	 * The code at some point did do the selection change
	 * after the sheet moved, causing flicker -mig
	 *
	 * If you dont know what this means, just mail me.
	 */
74

75 76 77
	if (clear_selection)
		sheet_selection_reset_only (sheet);

Jody Goldberg's avatar
Jody Goldberg committed
78
	/* Set the cursor BEFORE making it visible to decrease flicker */
79
	workbook_finish_editing (gsheet->scg->wbcg, TRUE);
80
	sheet_cursor_set (sheet, col, row, col, row, col, row);
81
	sheet_make_cell_visible (sheet, col, row);
82

83
	if (clear_selection)
84
		sheet_selection_add (sheet, col, row);
85 86
}

Arturo Espinosa's avatar
Arturo Espinosa committed
87 88 89 90 91 92 93
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
94 95 96 97
	item_cursor_set_bounds (
		gsheet->item_cursor,
		start_col, start_row,
		end_col, end_row);
Arturo Espinosa's avatar
Arturo Espinosa committed
98 99
}

100
/**
101
 * cursor_horizontal_move:
102 103 104 105
 *
 * @gsheet : The sheet
 * @count  : Number of units to move the cursor horizontally
 * @jump_to_boundaries: skip from the start to the end of ranges
106
 *                       of filled or unfilled cells.
107 108 109 110
 *
 * Moves the cursor count columns
 */
static void
111
cursor_horizontal_move (GnumericSheet *gsheet, int count,
112
			gboolean jump_to_boundaries)
113
{
114
	Sheet *sheet = gsheet->scg->sheet;
115
	int const new_col = sheet_find_boundary_horizontal (sheet,
116 117
		sheet->edit_pos_real.col, sheet->edit_pos_real.row,
		sheet->edit_pos_real.row, count, jump_to_boundaries);
118
	move_cursor (gsheet, new_col, sheet->edit_pos_real.row, TRUE);
119
}
120

121
static void
122 123
cursor_horizontal_extend (GnumericSheet *gsheet,
			  int count, gboolean jump_to_boundaries)
124
{
125
	sheet_selection_extend (gsheet->scg->sheet,
126
				count, jump_to_boundaries, TRUE);
127 128
}

129
/**
130
 * cursor_vertical_move:
131 132 133 134
 *
 * @gsheet : The sheet
 * @count  : Number of units to move the cursor vertically
 * @jump_to_boundaries: skip from the start to the end of ranges
135
 *                       of filled or unfilled cells.
136 137 138
 *
 * Moves the cursor count rows
 */
139
static void
140
cursor_vertical_move (GnumericSheet *gsheet, int count,
141
		      gboolean jump_to_boundaries)
142
{
143
	Sheet *sheet = gsheet->scg->sheet;
144
	int const new_row = sheet_find_boundary_vertical (sheet,
145
		sheet->edit_pos_real.col, sheet->edit_pos_real.row,
146
		sheet->edit_pos_real.col, count, jump_to_boundaries);
147
	move_cursor (gsheet, sheet->edit_pos_real.col, new_row, TRUE);
148 149
}

150
static void
151 152
cursor_vertical_extend (GnumericSheet *gsheet,
			int count, gboolean jump_to_boundaries)
153
{
154
	sheet_selection_extend (gsheet->scg->sheet,
155
				count, jump_to_boundaries, FALSE);
156 157
}

158
/*
159
 * gnumeric_sheet_can_select_expr_range
160 161 162 163 164 165
 *  @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
166
gboolean
167
gnumeric_sheet_can_select_expr_range (GnumericSheet *gsheet)
168
{
169
	WorkbookControlGUI const *wbcg;
170

Arturo Espinosa's avatar
Arturo Espinosa committed
171
	g_return_val_if_fail (GNUMERIC_IS_SHEET (gsheet), FALSE);
172

173
	wbcg = gsheet->scg->wbcg;
174
	if (workbook_edit_entry_redirect_p (wbcg))
175 176
		return TRUE;

177
	if (!wbcg->editing)
178
		return FALSE;
179

Jody Goldberg's avatar
Jody Goldberg committed
180
	if (gsheet->selecting_cell)
181
		return TRUE;
182

183
	return workbook_editing_expr (wbcg);
Jody Goldberg's avatar
Jody Goldberg committed
184
}
185

Jody Goldberg's avatar
Jody Goldberg committed
186 187 188
static void
selection_remove_selection_string (GnumericSheet *gsheet)
{
189
	WorkbookControlGUI const *wbcg = gsheet->scg->wbcg;
190

191 192 193 194 195 196 197
	if (gsheet->sel_text_len <= 0)
		return;

	gtk_editable_delete_text (
		GTK_EDITABLE (workbook_get_entry_logical (wbcg)),
		gsheet->sel_cursor_pos,
		gsheet->sel_cursor_pos + gsheet->sel_text_len);
Jody Goldberg's avatar
Jody Goldberg committed
198
}
199

Jody Goldberg's avatar
Jody Goldberg committed
200 201 202 203
static void
selection_insert_selection_string (GnumericSheet *gsheet)
{
	ItemCursor *sel = gsheet->sel_cursor;
204 205
	Sheet const *sheet = gsheet->scg->sheet;
	WorkbookControlGUI const *wbcg = gsheet->scg->wbcg;
206 207
	GtkEditable *editable = GTK_EDITABLE (workbook_get_entry_logical (wbcg));
	gboolean const inter_sheet = (sheet != wbcg->editing_sheet);
Miguel de Icaza's avatar
Miguel de Icaza committed
208
	char *buffer;
Jody Goldberg's avatar
Jody Goldberg committed
209 210 211 212
	int pos;

	/* Get the new selection string */
	buffer = g_strdup_printf ("%s%s%s%d",
213
				  wbcg->select_abs_col ? "$" : "",
Jody Goldberg's avatar
Jody Goldberg committed
214
				  col_name (sel->pos.start.col),
215
				  wbcg->select_abs_row ? "$" : "",
Jody Goldberg's avatar
Jody Goldberg committed
216 217 218
				  sel->pos.start.row+1);

	if (!range_is_singleton (&sel->pos)) {
Miguel de Icaza's avatar
Miguel de Icaza committed
219
		char *tmp = g_strdup_printf ("%s:%s%s%s%d",
Jody Goldberg's avatar
Jody Goldberg committed
220
					      buffer,
221
					      wbcg->select_abs_col ? "$": "",
Jody Goldberg's avatar
Jody Goldberg committed
222
					      col_name (sel->pos.end.col),
223
					      wbcg->select_abs_row ? "$": "",
Jody Goldberg's avatar
Jody Goldberg committed
224 225 226 227 228 229
					      sel->pos.end.row+1);
		g_free (buffer);
		buffer = tmp;
	}

	if (inter_sheet) {
Miguel de Icaza's avatar
Miguel de Icaza committed
230 231
		char *tmp = g_strdup_printf ("%s!%s", sheet->name_quoted,
					     buffer);
Jody Goldberg's avatar
Jody Goldberg committed
232 233
		g_free (buffer);
		buffer = tmp;
234
	}
235

Jody Goldberg's avatar
Jody Goldberg committed
236 237
	gsheet->sel_text_len = strlen (buffer);
	pos = gsheet->sel_cursor_pos;
238 239

	gtk_editable_set_position (editable, pos);
240 241
	gtk_editable_insert_text (editable, buffer,
				  gsheet->sel_text_len,
Jody Goldberg's avatar
Jody Goldberg committed
242
				  &pos);
243

Jody Goldberg's avatar
Jody Goldberg committed
244
	g_free (buffer);
245

Jody Goldberg's avatar
Jody Goldberg committed
246
	/* Set the cursor at the end.  It looks nicer */
247
	gtk_editable_set_position (editable,
Jody Goldberg's avatar
Jody Goldberg committed
248 249
				   gsheet->sel_cursor_pos +
				   gsheet->sel_text_len);
250 251 252
}

static void
Miguel de Icaza's avatar
Miguel de Icaza committed
253
start_cell_selection_at (GnumericSheet *gsheet, int col, int row)
254 255
{
	GnomeCanvas *canvas = GNOME_CANVAS (gsheet);
256
	GnomeCanvasItem *tmp;
257
	GnomeCanvasGroup *group = GNOME_CANVAS_GROUP (canvas->root);
258 259
	Sheet *sheet = gsheet->scg->sheet;
	WorkbookControlGUI *wbcg = gsheet->scg->wbcg;
260

261
	g_return_if_fail (gsheet->selecting_cell == FALSE);
262

263
	/* Hide the primary cursor while the range selection cursor is visible
Jody Goldberg's avatar
Jody Goldberg committed
264 265
	 * and we are selecting on a different sheet than the expr being edited
	 */
266
	if (sheet != wb_control_cur_sheet (WORKBOOK_CONTROL (wbcg)))
Jody Goldberg's avatar
Jody Goldberg committed
267
		item_cursor_set_visibility (gsheet->item_cursor, FALSE);
Jody Goldberg's avatar
Jody Goldberg committed
268

269
	gsheet->selecting_cell = TRUE;
270
	tmp = gnome_canvas_item_new (group,
271
		item_cursor_get_type (),
272
		"SheetControlGUI", gsheet->scg,
273 274 275
		"Style", ITEM_CURSOR_ANTED, NULL);
	gsheet->sel_cursor = ITEM_CURSOR (tmp);
	item_cursor_set_bounds (gsheet->sel_cursor, col, row, col, row);
276

Jody Goldberg's avatar
Jody Goldberg committed
277 278 279
	/* 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));
280

281
	gsheet->sel_cursor_pos = GTK_EDITABLE (workbook_get_entry_logical (wbcg))->current_pos;
282 283 284 285
	gsheet->sel_text_len = 0;
}

static void
Miguel de Icaza's avatar
Miguel de Icaza committed
286 287
start_cell_selection (GnumericSheet *gsheet)
{
288
	Sheet *sheet = gsheet->scg->sheet;
Jody Goldberg's avatar
Jody Goldberg committed
289
	start_cell_selection_at (gsheet,
290 291
				 sheet->edit_pos_real.col,
				 sheet->edit_pos_real.row);
Miguel de Icaza's avatar
Miguel de Icaza committed
292 293 294 295 296 297 298 299 300 301 302 303 304 305
}

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

	if (gsheet->selecting_cell)
		return;

	start_cell_selection_at (gsheet, col, row);
}

void
Jody Goldberg's avatar
Jody Goldberg committed
306
gnumeric_sheet_stop_cell_selection (GnumericSheet *gsheet, gboolean const clear_string)
307
{
Miguel de Icaza's avatar
Miguel de Icaza committed
308 309
	g_return_if_fail (GNUMERIC_IS_SHEET (gsheet));

310 311
	if (!gsheet->selecting_cell)
		return;
312

Jody Goldberg's avatar
Jody Goldberg committed
313 314
	if (clear_string)
		selection_remove_selection_string (gsheet);
315
	gsheet->selecting_cell = FALSE;
Jody Goldberg's avatar
Jody Goldberg committed
316 317
	gtk_object_destroy (GTK_OBJECT (gsheet->sel_cursor));
	gsheet->sel_cursor = NULL;
Jody Goldberg's avatar
Jody Goldberg committed
318 319 320 321

	/* 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
322 323 324

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

Arturo Espinosa's avatar
Arturo Espinosa committed
327
void
328
gnumeric_sheet_create_editor (GnumericSheet *gsheet)
Arturo Espinosa's avatar
Arturo Espinosa committed
329 330 331 332
{
	GnomeCanvas *canvas = GNOME_CANVAS (gsheet);
	GnomeCanvasItem *item;

333
	g_return_if_fail (gsheet->item_editor == NULL);
334

335
	item = gnome_canvas_item_new (GNOME_CANVAS_GROUP (canvas->root),
Arturo Espinosa's avatar
Arturo Espinosa committed
336
				      item_edit_get_type (),
337
				      "ItemEdit::SheetControlGUI",     gsheet->scg,
Arturo Espinosa's avatar
Arturo Espinosa committed
338 339 340 341 342 343 344 345 346 347
				      NULL);

	gsheet->item_editor = ITEM_EDIT (item);
}

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

348
	gnumeric_sheet_stop_cell_selection (gsheet, FALSE);
Arturo Espinosa's avatar
Arturo Espinosa committed
349

350 351 352 353
	if (gsheet->item_editor != NULL) {
		gtk_object_destroy (GTK_OBJECT (gsheet->item_editor));
		gsheet->item_editor = NULL;
	}
Arturo Espinosa's avatar
Arturo Espinosa committed
354 355
}

Jody Goldberg's avatar
Jody Goldberg committed
356 357 358 359 360 361 362 363
/**
 * 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
364 365
 */
void
Jody Goldberg's avatar
Jody Goldberg committed
366
gnumeric_sheet_rangesel_cursor_extend (GnumericSheet *gsheet, int col, int row)
Miguel de Icaza's avatar
Miguel de Icaza committed
367 368
{
	ItemCursor *ic;
Jody Goldberg's avatar
Jody Goldberg committed
369
	int base_col, base_row;
370

Miguel de Icaza's avatar
Miguel de Icaza committed
371 372 373
	g_return_if_fail (GNUMERIC_IS_SHEET (gsheet));
	g_return_if_fail (gsheet->selecting_cell);

Jody Goldberg's avatar
Jody Goldberg committed
374
	ic = gsheet->sel_cursor;
Jody Goldberg's avatar
Jody Goldberg committed
375 376 377 378 379 380 381 382 383 384
	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
385 386

	selection_remove_selection_string (gsheet);
Jody Goldberg's avatar
Jody Goldberg committed
387
	item_cursor_set_bounds (ic, base_col, base_row, col, row);
Miguel de Icaza's avatar
Miguel de Icaza committed
388 389 390 391 392 393 394 395
	selection_insert_selection_string (gsheet);
}

/*
 * Invoked by Item-Grid to place the selection cursor on a specific
 * spot.
 */
void
Jody Goldberg's avatar
Jody Goldberg committed
396 397 398
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
399 400
{
	ItemCursor *ic;
401

Miguel de Icaza's avatar
Miguel de Icaza committed
402 403
	g_return_if_fail (GNUMERIC_IS_SHEET (gsheet));
	g_return_if_fail (gsheet->selecting_cell);
404

Jody Goldberg's avatar
Jody Goldberg committed
405
	ic = gsheet->sel_cursor;
Miguel de Icaza's avatar
Miguel de Icaza committed
406
	selection_remove_selection_string (gsheet);
Jody Goldberg's avatar
Jody Goldberg committed
407
	item_cursor_set_bounds (ic, base_col, base_row, move_col, move_row);
Miguel de Icaza's avatar
Miguel de Icaza committed
408 409 410
	selection_insert_selection_string (gsheet);
}

411
static void
412
rangesel_horizontal_move (GnumericSheet *gsheet, int dir, gboolean jump_to_boundaries)
413 414
{
	ItemCursor *ic;
415
	int col, row;
416 417

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

419 420 421
	if (!gsheet->selecting_cell)
		start_cell_selection (gsheet);

Jody Goldberg's avatar
Jody Goldberg committed
422
	ic = gsheet->sel_cursor;
423 424 425
	row = ic->base_corner.row;
	col = sheet_find_boundary_horizontal (gsheet->scg->sheet,
		ic->base_corner.col, row, row, dir, jump_to_boundaries);
426
	selection_remove_selection_string (gsheet);
427
	item_cursor_set_bounds (ic, col, row, col, row);
428
	selection_insert_selection_string (gsheet);
429
	gnumeric_sheet_make_cell_visible (gsheet, col, row, FALSE);
430 431 432
}

static void
433
rangesel_vertical_move (GnumericSheet *gsheet, int dir, gboolean jump_to_boundaries)
434 435
{
	ItemCursor *ic;
436
	int col, row;
437 438 439 440 441 442

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

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

Jody Goldberg's avatar
Jody Goldberg committed
443
	ic = gsheet->sel_cursor;
444 445 446
	col = ic->base_corner.col;
	row = sheet_find_boundary_vertical (gsheet->scg->sheet,
		col, ic->base_corner.row, col, dir, jump_to_boundaries);
447
	selection_remove_selection_string (gsheet);
448
	item_cursor_set_bounds (ic, col, row, col, row);
449
	selection_insert_selection_string (gsheet);
450
	gnumeric_sheet_make_cell_visible (gsheet, col, row, FALSE);
451 452 453
}

static void
454
rangesel_horizontal_extend (GnumericSheet *gsheet, int n, gboolean jump_to_boundaries)
455 456
{
	ItemCursor *ic;
457
	int new_col;
458

459
	g_return_if_fail (n == -1 || n == 1);
460

461
	if (!gsheet->selecting_cell) {
462
		rangesel_horizontal_move (gsheet, n, jump_to_boundaries);
463 464 465
		return;
	}

Jody Goldberg's avatar
Jody Goldberg committed
466
	ic = gsheet->sel_cursor;
467 468
	new_col = sheet_find_boundary_horizontal (gsheet->scg->sheet,
		ic->move_corner.col, ic->move_corner.row, 
469
		ic->base_corner.row, n, jump_to_boundaries);
470 471
	selection_remove_selection_string (gsheet);
	item_cursor_set_bounds (ic,
472 473
				ic->base_corner.col, ic->base_corner.row,
				new_col, ic->move_corner.row);
474
	selection_insert_selection_string (gsheet);
475 476
	gnumeric_sheet_make_cell_visible (gsheet,
		ic->move_corner.col, ic->move_corner.row, FALSE);
477 478 479
}

static void
480
rangesel_vertical_extend (GnumericSheet *gsheet, int n, gboolean jump_to_boundaries)
481 482
{
	ItemCursor *ic;
483
	int new_row;
484

485
	g_return_if_fail (n == -1 || n == 1);
486

487
	if (!gsheet->selecting_cell) {
488
		rangesel_vertical_move (gsheet, n, jump_to_boundaries);
489 490 491
		return;
	}

Jody Goldberg's avatar
Jody Goldberg committed
492
	ic = gsheet->sel_cursor;
493 494
	new_row = sheet_find_boundary_vertical (gsheet->scg->sheet,
		ic->move_corner.col, ic->move_corner.row, 
495
		ic->base_corner.col, n, jump_to_boundaries);
496 497
	selection_remove_selection_string (gsheet);
	item_cursor_set_bounds (ic,
498 499
				ic->base_corner.col, ic->base_corner.row,
				ic->move_corner.col, new_row);
500
	selection_insert_selection_string (gsheet);
501 502
	gnumeric_sheet_make_cell_visible (gsheet,
		ic->move_corner.col, ic->move_corner.row, FALSE);
503 504 505
}

/*
Miguel de Icaza's avatar
New:  
Miguel de Icaza committed
506
 * key press event handler for the gnumeric sheet for the sheet mode
507
 */
508
static gint
Miguel de Icaza's avatar
New:  
Miguel de Icaza committed
509
gnumeric_sheet_key_mode_sheet (GnumericSheet *gsheet, GdkEventKey *event)
510
{
511 512
	Sheet *sheet = gsheet->scg->sheet;
	WorkbookControlGUI *wbcg = gsheet->scg->wbcg;
513 514 515
	void (*movefn_horizontal) (GnumericSheet *, int, gboolean);
	void (*movefn_vertical)   (GnumericSheet *, int, gboolean);
	gboolean const jump_to_bounds = event->state & GDK_CONTROL_MASK;
516

517 518
	/* Magic : Some of these are accelerators,
	 * we need to catch them before entering because they appear to be printable
519
	 */
520 521 522 523 524 525 526 527
	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
		 */
528
		switch (event->keyval) {
Miguel de Icaza's avatar
Miguel de Icaza committed
529 530 531
		case GDK_Shift_L:   case GDK_Shift_R:
		case GDK_Alt_L:     case GDK_Alt_R:
		case GDK_Control_L: case GDK_Control_R:
532 533
			return 1;
		}
Tristan Tarrant's avatar
Tristan Tarrant committed
534

535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550
		if (event->state & GDK_SHIFT_MASK) {
			movefn_horizontal = rangesel_horizontal_extend;
			movefn_vertical = rangesel_vertical_extend;
		} else {
			movefn_horizontal = rangesel_horizontal_move;
			movefn_vertical   = rangesel_vertical_move;
		}
	} else {
		if (event->state & GDK_SHIFT_MASK) {
			movefn_horizontal = cursor_horizontal_extend;
			movefn_vertical   = cursor_vertical_extend;
		} else {
			movefn_horizontal = cursor_horizontal_move;
			movefn_vertical = cursor_vertical_move;
		}
	}
Tristan Tarrant's avatar
Tristan Tarrant committed
551

552
	switch (event->keyval) {
Miguel de Icaza's avatar
Miguel de Icaza committed
553
	case GDK_KP_Left:
554
	case GDK_Left:
555
		(*movefn_horizontal)(gsheet, -1, jump_to_bounds);
556 557
		break;

Miguel de Icaza's avatar
Miguel de Icaza committed
558
	case GDK_KP_Right:
559
	case GDK_Right:
560
		(*movefn_horizontal)(gsheet, 1, jump_to_bounds);
561 562
		break;

Miguel de Icaza's avatar
Miguel de Icaza committed
563
	case GDK_KP_Up:
564
	case GDK_Up:
565
		(*movefn_vertical)(gsheet, -1, jump_to_bounds);
566 567
		break;

Miguel de Icaza's avatar
Miguel de Icaza committed
568
	case GDK_KP_Down:
569
	case GDK_Down:
570
		(*movefn_vertical)(gsheet, 1, jump_to_bounds);
571 572
		break;

Miguel de Icaza's avatar
Miguel de Icaza committed
573
	case GDK_KP_Page_Up:
574
	case GDK_Page_Up:
575
		if ((event->state & GDK_CONTROL_MASK) != 0)
Jody Goldberg's avatar
Jody Goldberg committed
576
			gtk_notebook_prev_page (wbcg->notebook);
577
		else if ((event->state & GDK_MOD1_MASK) == 0)
578
			(*movefn_vertical)(gsheet, -(gsheet->row.last_visible-gsheet->row.first), FALSE);
579
		else
580
			(*movefn_horizontal)(gsheet, -(gsheet->col.last_visible-gsheet->col.first), FALSE);
581 582
		break;

Miguel de Icaza's avatar
Miguel de Icaza committed
583
	case GDK_KP_Page_Down:
584
	case GDK_Page_Down:
585
		if ((event->state & GDK_CONTROL_MASK) != 0)
Jody Goldberg's avatar
Jody Goldberg committed
586
			gtk_notebook_next_page (wbcg->notebook);
587
		else if ((event->state & GDK_MOD1_MASK) == 0)
588
			(*movefn_vertical)(gsheet, gsheet->row.last_visible-gsheet->row.first, FALSE);
589
		else
590
			(*movefn_horizontal)(gsheet, gsheet->col.last_visible-gsheet->col.first, FALSE);
591 592
		break;

Miguel de Icaza's avatar
Miguel de Icaza committed
593
	case GDK_KP_Home:
594
	case GDK_Home:
Jody Goldberg's avatar
Jody Goldberg committed
595 596 597
		if ((event->state & GDK_CONTROL_MASK) != 0)
			move_cursor (gsheet, 0, 0, TRUE);
		else
598
			(*movefn_horizontal)(gsheet, -sheet->edit_pos.col, FALSE);
599 600 601
		break;

	case GDK_KP_Delete:
602
	case GDK_Delete:
603
		cmd_clear_selection (WORKBOOK_CONTROL (wbcg), sheet, CLEAR_VALUES);
604 605
		break;

Miguel de Icaza's avatar
Miguel de Icaza committed
606 607
	/*
	 * NOTE : Keep these in sync with the condition
608 609
	 *        for tabs.
	 */
Miguel de Icaza's avatar
Miguel de Icaza committed
610
	case GDK_KP_Enter:
611
	case GDK_Return:
612
		if (wbcg->editing &&
613 614 615 616
		    (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
617
			return gtk_widget_event (
618
				GTK_WIDGET (workbook_get_entry_logical (wbcg)),
Miguel de Icaza's avatar
Miguel de Icaza committed
619
				(GdkEvent *) event);
Miguel de Icaza's avatar
Miguel de Icaza committed
620
		/* fall down */
621

Miguel de Icaza's avatar
Miguel de Icaza committed
622
	case GDK_Tab:
623 624
	case GDK_ISO_Left_Tab:
	case GDK_KP_Tab:
625 626
	{
		/* Figure out the direction */
627
		gboolean const direction = (event->state & GDK_SHIFT_MASK) ? FALSE : TRUE;
628 629
		gboolean const horizontal = (event->keyval == GDK_KP_Enter ||
					     event->keyval == GDK_Return) ? FALSE : TRUE;
630

Jody Goldberg's avatar
Jody Goldberg committed
631
		/* Be careful to restore the editing sheet if we are editing */
632 633 634
		if (wbcg->editing)
			sheet = wbcg->editing_sheet;
		workbook_finish_editing (wbcg, TRUE);
635
		sheet_selection_walk_step (sheet, direction, horizontal);
636
		break;
637
	}
Arturo Espinosa's avatar
Arturo Espinosa committed
638 639

	case GDK_Escape:
640
		workbook_finish_editing (wbcg, FALSE);
641
		application_clipboard_unant ();
Arturo Espinosa's avatar
Arturo Espinosa committed
642
		break;
643

Jody Goldberg's avatar
Jody Goldberg committed
644
	case GDK_F4:
645
		if (wbcg->editing && gsheet->sel_cursor) {
Jody Goldberg's avatar
Jody Goldberg committed
646
			selection_remove_selection_string (gsheet);
647 648
			wbcg->select_abs_row = (wbcg->select_abs_row == wbcg->select_abs_col);
			wbcg->select_abs_col = !wbcg->select_abs_col;
Jody Goldberg's avatar
Jody Goldberg committed
649 650 651 652
			selection_insert_selection_string (gsheet);
		}
		break;

Arturo Espinosa's avatar
Arturo Espinosa committed
653
	case GDK_F2:
654
		workbook_start_editing_at_cursor (wbcg, FALSE, FALSE);
655
		/* fall down */
656

657
	default:
658
		if (!wbcg->editing) {
659 660
			if ((event->state & (GDK_MOD1_MASK|GDK_CONTROL_MASK)) != 0)
				return 0;
661

Jody Goldberg's avatar
Jody Goldberg committed
662 663 664 665
			/* If the character is not printable do not start editing */
			if (event->length == 0)
				return 0;

666
			workbook_start_editing_at_cursor (wbcg, TRUE, TRUE);
667
		}
Jody Goldberg's avatar
Jody Goldberg committed
668
		gnumeric_sheet_stop_cell_selection (gsheet, FALSE);
669

Arturo Espinosa's avatar
Arturo Espinosa committed
670
		/* Forward the keystroke to the input line */
671
		return gtk_widget_event (GTK_WIDGET (workbook_get_entry_logical (wbcg)),
Miguel de Icaza's avatar
Miguel de Icaza committed
672
					 (GdkEvent *) event);
673
	}
674 675 676 677

	if (wbcg->editing)
		sheet_update_only_grid (sheet);
	else
678
		sheet_update (sheet);
679 680

	return TRUE;
681 682
}

Miguel de Icaza's avatar
New:  
Miguel de Icaza committed
683 684 685
static gint
gnumeric_sheet_key_mode_object (GnumericSheet *gsheet, GdkEventKey *event)
{
686
	SheetControlGUI *scg = gsheet->scg;
687

688
	switch (event->keyval) {
Miguel de Icaza's avatar
New:  
Miguel de Icaza committed
689
	case GDK_Escape:
690
		scg_mode_edit (scg);
691
		application_clipboard_unant ();
Miguel de Icaza's avatar
New:  
Miguel de Icaza committed
692 693
		break;

694 695
	case GDK_BackSpace: /* Ick! */
	case GDK_KP_Delete:
Miguel de Icaza's avatar
New:  
Miguel de Icaza committed
696
	case GDK_Delete:
697
		gtk_object_destroy (GTK_OBJECT (scg->current_object));
Miguel de Icaza's avatar
New:  
Miguel de Icaza committed
698 699 700 701 702 703 704 705 706
		break;

	default:
		return FALSE;
	}
	return TRUE;
}

static gint
707
gnumeric_sheet_key_press (GtkWidget *widget, GdkEventKey *event)
Miguel de Icaza's avatar
New:  
Miguel de Icaza committed
708 709
{
	GnumericSheet *gsheet = GNUMERIC_SHEET (widget);
710
	SheetControlGUI *scg = gsheet->scg;
711

712
	if (scg->current_object != NULL || scg->new_object != NULL)
Miguel de Icaza's avatar
New:  
Miguel de Icaza committed
713
		return gnumeric_sheet_key_mode_object (gsheet, event);
714
	return gnumeric_sheet_key_mode_sheet (gsheet, event);
Miguel de Icaza's avatar
New:  
Miguel de Icaza committed
715 716
}

717 718 719 720
static gint
gnumeric_sheet_key_release (GtkWidget *widget, GdkEventKey *event)
{
	GnumericSheet *gsheet = GNUMERIC_SHEET (widget);
721
	SheetControlGUI *scg = gsheet->scg;
722 723 724 725 726 727 728 729

	/*
	 * 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.
	 */
730
	if (scg->current_object == NULL &&
731
	    (event->keyval == GDK_Shift_L || event->keyval == GDK_Shift_R))
732 733
		wb_view_selection_desc (wb_control_view (
			WORKBOOK_CONTROL (gsheet->scg->wbcg)), TRUE, NULL);
734 735 736 737

	return FALSE;
}

738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755
/* 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);
	return FALSE;
}

/* Focus out handler for the canvas */
static gint
gnumeric_sheet_focus_out (GtkWidget *widget, GdkEventFocus *event)
{
	gdk_im_end ();
	return FALSE;
}

756 757 758 759 760 761 762
static void
gnumeric_sheet_drag_data_get (GtkWidget *widget,
			      GdkDragContext *context,
			      GtkSelectionData *selection_data,
			      guint info,
			      guint time)
{
763
#if 0
764
	BonoboMoniker *moniker;
765
	Sheet *sheet = GNUMERIC_SHEET (widget)->scg->sheet;
766 767
	Workbook *wb = sheet->workbook;
	char *s;
768
	WorkbookControl *wbc = WORKBOOK_CONTROL (gsheet->scg->wbcg);
769

770
	if (wb->filename == NULL)
771
		workbook_save (wbc, wb);
772 773
	if (wb->filename == NULL)
		return;
774

775 776
	moniker = bonobo_moniker_new ();
	bonobo_moniker_set_server (
777 778 779 780
		moniker,
		"IDL:GNOME:Gnumeric:Workbook:1.0",
		wb->filename);

781
	bonobo_moniker_append_item_name (
782
		moniker, "Sheet1");
783
	s = bonobo_moniker_get_as_string (moniker);
784 785 786
	gtk_object_destroy (GTK_OBJECT (moniker));

	gtk_selection_data_set (selection_data, selection_data->target, 8, s, strlen (s)+1);
787
#endif
788 789
}

790 791 792
/*
 * gnumeric_sheet_filenames_dropped :
 */
793 794 795 796 797 798 799 800 801 802 803
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;
804
	WorkbookControl *wbc = WORKBOOK_CONTROL (gsheet->scg->wbcg);
805 806

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

808 809 810
	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);
811

812 813 814
		if (new_wb != NULL) {
			(void) file_finish_load (wbc, new_wb);
		} else {
815 816 817
#ifdef ENABLE_BONOBO
			/* If it wasn't a workbook, see if we have a control for it */
			SheetObject *so = sheet_object_container_new_file (
818
			                  gsheet->scg->sheet, file_name);
819
			if (so != NULL)
820
				scg_mode_create_object (gsheet->scg, so);
821 822 823 824 825 826 827
#else
			gchar *msg;

			msg = g_strdup_printf (_("File \"%s\" has unknown format."),
			                       file_name);
			gnumeric_error_read (COMMAND_CONTEXT (wbc), msg);
			g_free (msg);
828
#endif
829
		}
830 831 832
	}
}

Arturo Espinosa's avatar
Arturo Espinosa committed
833
GtkWidget *
834
gnumeric_sheet_new (SheetControlGUI *scg)
Arturo Espinosa's avatar
Arturo Espinosa committed
835
{
836
	static GtkTargetEntry const drag_types[] = {
837 838
		{ "text/uri-list", 0, 0 },
	};
839 840 841 842 843 844
	static gint const n_drag_types = sizeof (drag_types) / sizeof (drag_types [0]);

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

846
	g_return_val_if_fail (IS_SHEET_CONTROL_GUI (scg), NULL);
847

848 849 850
	gsheet = gtk_type_new (gnumeric_sheet_get_type ());
	gsheet_group = GNOME_CANVAS_GROUP (GNOME_CANVAS (gsheet)->root);
	widget = GTK_WIDGET (gsheet);
851

852 853 854 855 856
	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;
857

858
	/* FIXME: figure out some real size for the canvas scrolling region */
859
	gnome_canvas_set_scroll_region (GNOME_CANVAS (gsheet), 0, 0,
860
					GNUMERIC_SHEET_FACTOR_X, GNUMERIC_SHEET_FACTOR_Y);
861 862

	/* The grid */
863
	item = gnome_canvas_item_new (gsheet_group,
864 865 866
		item_grid_get_type (),
		"ItemGrid::SheetControlGUI", scg,
		NULL);
867 868 869
	gsheet->item_grid = ITEM_GRID (item);

	/* The cursor */
870
	item = gnome_canvas_item_new (gsheet_group,
871 872 873
		item_cursor_get_type (),
		"ItemCursor::SheetControlGUI", scg,
		NULL);
874
	gsheet->item_cursor = ITEM_CURSOR (item);
875
	item_cursor_set_bounds (gsheet->item_cursor, 0, 0, 0, 0); /* A1 */
Jody Goldberg's avatar
Jody Goldberg committed
876

877 878 879
	/* Setup a test of Drag and Drop */
	gtk_signal_connect (GTK_OBJECT (widget),
		"drag_data_get",
880
		GTK_SIGNAL_FUNC (gnumeric_sheet_drag_data_get), NULL);
881
	gtk_drag_dest_set (widget,
882 883 884
		GTK_DEST_DEFAULT_ALL,
		drag_types, n_drag_types,
		GDK_ACTION_COPY);
885
	gtk_signal_connect (GTK_OBJECT (widget),
886 887 888
		"drag_data_received",
		GTK_SIGNAL_FUNC (gnumeric_sheet_filenames_dropped),
		widget);
889

890 891 892 893 894 895
	return widget;
}

static void
gnumeric_sheet_realize (GtkWidget *widget)
{
896
	GdkWindow *window;
897
	GnumericSheet *gsheet;
898

899 900
	if (GTK_WIDGET_CLASS (sheet_parent_class)->realize)
		(*GTK_WIDGET_CLASS (sheet_parent_class)->realize)(widget);
901

902
	window = widget->window;
903
	gdk_window_set_back_pixmap (GTK_LAYOUT (widget)->bin_window, NULL, FALSE);
904

905
	e_cursor_set (window, E_CURSOR_FAT_CROSS);
906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937