gnumeric-canvas.c 37.3 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 "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 256
{
	GnomeCanvas *canvas = GNOME_CANVAS (gsheet);
	GnomeCanvasGroup *group = GNOME_CANVAS_GROUP (canvas->root);
257 258
	Sheet *sheet = gsheet->scg->sheet;
	WorkbookControlGUI *wbcg = gsheet->scg->wbcg;
259

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

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

268
	gsheet->selecting_cell = TRUE;
Jody Goldberg's avatar
Jody Goldberg committed
269
	gsheet->sel_cursor = ITEM_CURSOR (gnome_canvas_item_new (
270 271
		group,
		item_cursor_get_type (),
272
		"SheetControlGUI", gsheet->scg,
273 274
		"Grid",  gsheet->item_grid,
		"Style", ITEM_CURSOR_ANTED, NULL));
Jody Goldberg's avatar
Jody Goldberg committed
275 276
	item_cursor_set_spin_base (gsheet->sel_cursor, col, row);
	item_cursor_set_bounds (ITEM_CURSOR (gsheet->sel_cursor), col, row, col, row);
277

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

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

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

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
307
gnumeric_sheet_stop_cell_selection (GnumericSheet *gsheet, gboolean const clear_string)
308
{
Miguel de Icaza's avatar
Miguel de Icaza committed
309 310
	g_return_if_fail (GNUMERIC_IS_SHEET (gsheet));

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

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

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

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

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

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

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

	gsheet->item_editor = ITEM_EDIT (item);
}

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

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

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

Miguel de Icaza's avatar
Miguel de Icaza committed
357 358 359 360 361 362 363
/*
 * Invoked by Item-Grid to extend the cursor selection
 */
void
gnumeric_sheet_selection_extend (GnumericSheet *gsheet, int col, int row)
{
	ItemCursor *ic;
364

Miguel de Icaza's avatar
Miguel de Icaza committed
365 366 367 368 369
	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);

Jody Goldberg's avatar
Jody Goldberg committed
370
	ic = gsheet->sel_cursor;
Miguel de Icaza's avatar
Miguel de Icaza committed
371 372 373

	selection_remove_selection_string (gsheet);
	item_cursor_set_bounds (ic,
374 375
				ic->base_corner.col, ic->base_corner.row,
				col, row);
Miguel de Icaza's avatar
Miguel de Icaza committed
376 377 378 379 380 381 382 383 384 385 386
	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;
387

Miguel de Icaza's avatar
Miguel de Icaza committed
388 389 390 391
	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);
392

Jody Goldberg's avatar
Jody Goldberg committed
393
	ic = gsheet->sel_cursor;
394

Miguel de Icaza's avatar
Miguel de Icaza committed
395 396 397 398 399
	selection_remove_selection_string (gsheet);
	item_cursor_set_bounds (ic, col, row, col, row);
	selection_insert_selection_string (gsheet);
}

400 401 402 403 404 405 406 407 408 409
void
gnumeric_sheet_selection_cursor_base (GnumericSheet *gsheet, int col, int row)
{
	ItemCursor *ic;

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

Jody Goldberg's avatar
Jody Goldberg committed
410
	ic = gsheet->sel_cursor;
411 412 413
	item_cursor_set_spin_base (ic, col, row);
}

414
static void
415
rangesel_horizontal_move (GnumericSheet *gsheet, int dir, gboolean jump_to_boundaries)
416 417 418 419
{
	ItemCursor *ic;

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

421 422 423
	if (!gsheet->selecting_cell)
		start_cell_selection (gsheet);

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

static void
437
rangesel_vertical_move (GnumericSheet *gsheet, int dir, gboolean jump_to_boundaries)
438 439 440 441 442 443 444 445
{
	ItemCursor *ic;

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

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

Jody Goldberg's avatar
Jody Goldberg committed
446
	ic = gsheet->sel_cursor;
447
	ic->base.row = sheet_find_boundary_vertical (gsheet->scg->sheet,
448
		ic->base.col, ic->base.row,
449
		ic->base.col, dir, jump_to_boundaries);
450
	selection_remove_selection_string (gsheet);
451
	item_cursor_set_bounds (ic,
452
		ic->base.col, ic->base.row, ic->base.col, ic->base.row);
453
	selection_insert_selection_string (gsheet);
454 455
	gnumeric_sheet_make_cell_visible (gsheet,
		ic->move_corner.col, ic->move_corner.row, FALSE);
456 457 458
}

static void
459
rangesel_horizontal_extend (GnumericSheet *gsheet, int n, gboolean jump_to_boundaries)
460 461
{
	ItemCursor *ic;
462
	int new_col;
463

464
	g_return_if_fail (n == -1 || n == 1);
465

466
	if (!gsheet->selecting_cell) {
467
		rangesel_horizontal_move (gsheet, n, jump_to_boundaries);
468 469 470
		return;
	}

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

static void
485
rangesel_vertical_extend (GnumericSheet *gsheet, int n, gboolean jump_to_boundaries)
486 487
{
	ItemCursor *ic;
488
	int new_row;
489

490
	g_return_if_fail (n == -1 || n == 1);
491

492
	if (!gsheet->selecting_cell) {
493
		rangesel_vertical_move (gsheet, n, jump_to_boundaries);
494 495 496
		return;
	}

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

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

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

540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555
		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
556

557
	switch (event->keyval) {
Miguel de Icaza's avatar
Miguel de Icaza committed
558
	case GDK_KP_Left:
559
	case GDK_Left:
560
		(*movefn_horizontal)(gsheet, -1, jump_to_bounds);
561 562
		break;

Miguel de Icaza's avatar
Miguel de Icaza committed
563
	case GDK_KP_Right:
564
	case GDK_Right:
565
		(*movefn_horizontal)(gsheet, 1, jump_to_bounds);
566 567
		break;

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

Miguel de Icaza's avatar
Miguel de Icaza committed
573
	case GDK_KP_Down:
574
	case GDK_Down:
575
		(*movefn_vertical)(gsheet, 1, jump_to_bounds);
576 577
		break;

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

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

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

	case GDK_KP_Delete:
607
	case GDK_Delete:
608
		cmd_clear_selection (WORKBOOK_CONTROL (wbcg), sheet, CLEAR_VALUES);
609 610
		break;

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

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

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

	case GDK_Escape:
645
		workbook_finish_editing (wbcg, FALSE);
646
		application_clipboard_unant ();
Arturo Espinosa's avatar
Arturo Espinosa committed
647
		break;
648

Jody Goldberg's avatar
Jody Goldberg committed
649
	case GDK_F4:
650
		if (wbcg->editing && gsheet->sel_cursor) {
Jody Goldberg's avatar
Jody Goldberg committed
651
			selection_remove_selection_string (gsheet);
652 653
			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
654 655 656 657
			selection_insert_selection_string (gsheet);
		}
		break;

Arturo Espinosa's avatar
Arturo Espinosa committed
658
	case GDK_F2:
659
		workbook_start_editing_at_cursor (wbcg, FALSE, FALSE);
660
		/* fall down */
661

662
	default:
663
		if (!wbcg->editing) {
664 665
			if ((event->state & (GDK_MOD1_MASK|GDK_CONTROL_MASK)) != 0)
				return 0;
666

Jody Goldberg's avatar
Jody Goldberg committed
667 668 669 670
			/* If the character is not printable do not start editing */
			if (event->length == 0)
				return 0;

671
			workbook_start_editing_at_cursor (wbcg, TRUE, TRUE);
672
		}
Jody Goldberg's avatar
Jody Goldberg committed
673
		gnumeric_sheet_stop_cell_selection (gsheet, FALSE);
674

Arturo Espinosa's avatar
Arturo Espinosa committed
675
		/* Forward the keystroke to the input line */
676
		return gtk_widget_event (GTK_WIDGET (workbook_get_entry_logical (wbcg)),
Miguel de Icaza's avatar
Miguel de Icaza committed
677
					 (GdkEvent *) event);
678
	}
679 680 681 682

	if (wbcg->editing)
		sheet_update_only_grid (sheet);
	else
683
		sheet_update (sheet);
684 685

	return TRUE;
686 687
}

Miguel de Icaza's avatar
New:  
Miguel de Icaza committed
688 689 690
static gint
gnumeric_sheet_key_mode_object (GnumericSheet *gsheet, GdkEventKey *event)
{
691
	SheetControlGUI *scg = gsheet->scg;
692

693
	switch (event->keyval) {
Miguel de Icaza's avatar
New:  
Miguel de Icaza committed
694
	case GDK_Escape:
695
		scg_mode_edit (scg);
696
		application_clipboard_unant ();
Miguel de Icaza's avatar
New:  
Miguel de Icaza committed
697 698
		break;

699 700
	case GDK_BackSpace: /* Ick! */
	case GDK_KP_Delete:
Miguel de Icaza's avatar
New:  
Miguel de Icaza committed
701
	case GDK_Delete:
702
		gtk_object_destroy (GTK_OBJECT (scg->current_object));
Miguel de Icaza's avatar
New:  
Miguel de Icaza committed
703 704 705 706 707 708 709 710 711
		break;

	default:
		return FALSE;
	}
	return TRUE;
}

static gint
712
gnumeric_sheet_key_press (GtkWidget *widget, GdkEventKey *event)
Miguel de Icaza's avatar
New:  
Miguel de Icaza committed
713 714
{
	GnumericSheet *gsheet = GNUMERIC_SHEET (widget);
715
	SheetControlGUI *scg = gsheet->scg;
716

717
	if (scg->current_object != NULL || scg->new_object != NULL)
Miguel de Icaza's avatar
New:  
Miguel de Icaza committed
718
		return gnumeric_sheet_key_mode_object (gsheet, event);
719
	return gnumeric_sheet_key_mode_sheet (gsheet, event);
Miguel de Icaza's avatar
New:  
Miguel de Icaza committed
720 721
}

722 723 724 725
static gint
gnumeric_sheet_key_release (GtkWidget *widget, GdkEventKey *event)
{
	GnumericSheet *gsheet = GNUMERIC_SHEET (widget);
726
	SheetControlGUI *scg = gsheet->scg;
727 728 729 730 731 732 733 734

	/*
	 * 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.
	 */
735
	if (scg->current_object == NULL &&
736
	    (event->keyval == GDK_Shift_L || event->keyval == GDK_Shift_R))
737 738
		wb_view_selection_desc (wb_control_view (
			WORKBOOK_CONTROL (gsheet->scg->wbcg)), TRUE, NULL);
739 740 741 742

	return FALSE;
}

743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760
/* 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;
}

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

775
	if (wb->filename == NULL)
776
		workbook_save (wbc, wb);
777 778
	if (wb->filename == NULL)
		return;
779

780 781
	moniker = bonobo_moniker_new ();
	bonobo_moniker_set_server (
782 783 784 785
		moniker,
		"IDL:GNOME:Gnumeric:Workbook:1.0",
		wb->filename);

786
	bonobo_moniker_append_item_name (
787
		moniker, "Sheet1");
788
	s = bonobo_moniker_get_as_string (moniker);
789 790 791
	gtk_object_destroy (GTK_OBJECT (moniker));

	gtk_selection_data_set (selection_data, selection_data->target, 8, s, strlen (s)+1);
792
#endif
793 794
}

795 796 797
/*
 * gnumeric_sheet_filenames_dropped :
 */
798 799 800 801 802 803 804 805 806 807 808
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;
809
	WorkbookControl *wbc = WORKBOOK_CONTROL (gsheet->scg->wbcg);
810 811

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

	for (tmp_list = names; tmp_list != NULL ; tmp_list = tmp_list->next) {
814
		WorkbookView *new_wb = workbook_try_read (wbc, tmp_list->data);
815 816 817 818 819

		if (new_wb == NULL) {
#ifdef ENABLE_BONOBO
			/* If it wasn't a workbook, see if we have a control for it */
			SheetObject *so = sheet_object_container_new_file (
820
				gsheet->scg->sheet, tmp_list->data);
821
			if (so != NULL)
822
				scg_mode_create_object (gsheet->scg, so);
823
#endif
824
		}
825 826 827
	}
}

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

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

841
	g_return_val_if_fail (IS_SHEET_CONTROL_GUI (scg), NULL);
842

843 844 845
	gsheet = gtk_type_new (gnumeric_sheet_get_type ());
	gsheet_group = GNOME_CANVAS_GROUP (GNOME_CANVAS (gsheet)->root);
	widget = GTK_WIDGET (gsheet);
846

847 848 849 850 851
	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;
852

853
	/* FIXME: figure out some real size for the canvas scrolling region */
854
	gnome_canvas_set_scroll_region (GNOME_CANVAS (gsheet), 0, 0,
855
					GNUMERIC_SHEET_FACTOR_X, GNUMERIC_SHEET_FACTOR_Y);
856 857

	/* The grid */
858
	item = gnome_canvas_item_new (gsheet_group,
859 860 861
		item_grid_get_type (),
		"ItemGrid::SheetControlGUI", scg,
		NULL);
862 863 864
	gsheet->item_grid = ITEM_GRID (item);

	/* The cursor */
865
	item = gnome_canvas_item_new (gsheet_group,
866 867 868 869
		item_cursor_get_type (),
		"ItemCursor::SheetControlGUI", scg,
		"ItemCursor::Grid", gsheet->item_grid,
		NULL);
870
	gsheet->item_cursor = ITEM_CURSOR (item);
871
	item_cursor_set_bounds (gsheet->item_cursor, 0, 0, 0, 0); /* A1 */
Jody Goldberg's avatar
Jody Goldberg committed
872

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

886 887 888 889 890 891
	return widget;
}

static void
gnumeric_sheet_realize (GtkWidget *widget)
{
892
	GdkWindow *window;
893
	GnumericSheet *gsheet;
894

895 896
	if (GTK_WIDGET_CLASS (sheet_parent_class)->realize)
		(*GTK_WIDGET_CLASS (sheet_parent_class)->realize)(widget);
897

898
	window = widget->window;
899
	gdk_window_set_back_pixmap (GTK_LAYOUT (widget)->bin_window, NULL, FALSE);
900

901
	e_cursor_set (window, E_CURSOR_FAT_CROSS);
902 903 904 905 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 938 939 940 941 942 943 944 945 946 947

	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;
	}

	(*GTK_WIDGET_CLASS (sheet_parent_class)->unrealize)(widget);
Arturo Espinosa's avatar
Arturo Espinosa committed
948 949
}

Jody Goldberg's avatar
Jody Goldberg committed
950 951 952 953 954 955 956 957
/*
 * gnumeric_sheet_compute_visible_ranges : Keeps the top left col/row the same and
 *     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.
 */
958
void