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

#include <gnome.h>
12 13
#include <gdk/gdkkeysyms.h>
#include <gdk/gdkx.h>
14 15
#include "gnumeric.h"
#include "gnumeric-sheet.h"
16 17 18 19 20 21 22 23 24

static GnomeCanvasClass *sheet_parent_class;

static void
gnumeric_sheet_destroy (GtkObject *object)
{
	GnumericSheet *gsheet;

	/* Add shutdown code here */
Arturo Espinosa's avatar
Arturo Espinosa committed
25 26
	gsheet = GNUMERIC_SHEET (object);
	
27 28 29 30
	if (GTK_OBJECT_CLASS (sheet_parent_class)->destroy)
		(*GTK_OBJECT_CLASS (sheet_parent_class)->destroy)(object);
}

Arturo Espinosa's avatar
Arturo Espinosa committed
31
static GnumericSheet *
32
gnumeric_sheet_create (Sheet *sheet, GtkWidget *entry)
Arturo Espinosa's avatar
Arturo Espinosa committed
33 34 35 36 37 38 39
{
	GnumericSheet *gsheet;
	GnomeCanvas   *canvas;
	
	gsheet = gtk_type_new (gnumeric_sheet_get_type ());
	canvas = GNOME_CANVAS (gsheet);

40 41 42
	gnome_canvas_construct (canvas,
				gtk_widget_get_default_visual (),
				gtk_widget_get_default_colormap ());
Arturo Espinosa's avatar
Arturo Espinosa committed
43 44 45 46
	
	gsheet->sheet   = sheet;
	gsheet->top_col = 0;
	gsheet->top_row = 0;
47
	gsheet->entry   = entry;
48
	
Arturo Espinosa's avatar
Arturo Espinosa committed
49 50 51
	return gsheet;
}

52 53 54
void
gnumeric_sheet_cursor_set (GnumericSheet *sheet, int col, int row)
{
Arturo Espinosa's avatar
Arturo Espinosa committed
55 56
	g_return_if_fail (GNUMERIC_IS_SHEET (sheet));

57 58 59 60
	sheet->cursor_col = col;
	sheet->cursor_row = row;
}

Arturo Espinosa's avatar
Arturo Espinosa committed
61
void
62 63
gnumeric_sheet_accept_pending_output (GnumericSheet *sheet)
{
64
	g_return_if_fail (sheet != NULL);
Arturo Espinosa's avatar
Arturo Espinosa committed
65
	g_return_if_fail (GNUMERIC_IS_SHEET (sheet));
66
	Cell *cell;
Arturo Espinosa's avatar
Arturo Espinosa committed
67
	
68 69 70 71 72 73
	cell = sheet_cell_new_with_text (
		sheet->sheet, sheet->cursor_col, sheet->cursor_row,
		gtk_entry_get_text (GTK_ENTRY (sheet->entry)));

	sheet_cell_add (sheet, cell);

74 75 76 77 78 79 80
	/* Destroy the object */
	if (sheet->item_editor){
		gtk_object_destroy (GTK_OBJECT (sheet->item_editor));
		sheet->item_editor = NULL;
	}
}

Arturo Espinosa's avatar
Arturo Espinosa committed
81 82
void
gnumeric_sheet_load_cell_val (GnumericSheet *gsheet)
83
{
Arturo Espinosa's avatar
Arturo Espinosa committed
84 85 86
	Sheet *sheet; 
	Workbook *wb;
	GtkWidget *entry;
87

Arturo Espinosa's avatar
Arturo Espinosa committed
88 89 90 91 92 93
	g_return_if_fail (GNUMERIC_IS_SHEET (gsheet));
	
	sheet = gsheet->sheet;
	wb = sheet->parent_workbook;
	entry = wb->ea_input;
	
94 95 96
	gtk_entry_set_text (GTK_ENTRY(entry), "");
}

97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121
/*
 * gnumeric_sheet_set_selection:
 *  @Sheet:     The sheet name
 *  @start_col: The starting column.
 *  @start_row: The starting row
 *  @end_col:   The end column
 *  @end_row:   The end row
 *
 * Set the current selection to cover the inclusive area delimited by
 * start_col, start_row, end_col and end_row.  The actual cursor is
 * placed at start_col, start_row
 */
void
gnumeric_sheet_set_selection (GnumericSheet *sheet, SheetSelection *ss)
{
	g_return_if_fail (sheet != NULL);
	g_return_if_fail (ss != NULL);
	g_return_if_fail (GNUMERIC_IS_SHEET (sheet));

	gnumeric_sheet_cursor_set (sheet, ss->start_col, ss->start_row);
	item_cursor_set_bounds (sheet->item_cursor,
				ss->start_col, ss->start_row,
				ss->end_col, ss->end_row);
}

122
static void
123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152
start_editing_at_cursor (GnumericSheet *sheet, GtkWidget *entry)
{
	GnomeCanvasItem *item;
	GnomeCanvas *canvas = GNOME_CANVAS (sheet);

	item = gnome_canvas_item_new (canvas,
				      GNOME_CANVAS_GROUP(canvas->root),
				      item_edit_get_type (),
				      "ItemEdit::Sheet",    sheet->sheet,
				      "ItemEdit::Grid",     sheet->item_grid,
				      "ItemEdit::Col",      sheet->cursor_col,
				      "ItemEdit::Row",      sheet->cursor_row,
				      "ItemEdit::GtkEntry", entry,
				      NULL);

	sheet->item_editor = ITEM_EDIT (item);
}

/*
 * gnumeric_sheet_move_cursor:
 *   @Sheet:    The sheet where the cursor is located
 *   @col:      The new column for the cursor.
 *   @row:      The new row for the cursor.
 *
 *   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
gnumeric_sheet_move_cursor (GnumericSheet *sheet, int col, int row)
153 154
{
	ItemCursor *item_cursor = sheet->item_cursor;
155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174

	gnumeric_sheet_cursor_set (sheet, col, row);
	
	gnumeric_sheet_accept_pending_output (sheet);
	sheet_selection_clear (sheet->sheet);
	gnumeric_sheet_cursor_set (sheet, col, row);
	item_cursor_set_bounds (item_cursor, col, row, col, row);
	gnumeric_sheet_load_cell_val (sheet);
}

/*
 * gnumeric_sheet_move_cursor_horizontal:
 *  @Sheet:  The sheet name
 *  @count:  number of units to move the cursor horizontally
 *
 * Moves the cursor count columns
 */
static void
gnumeric_sheet_move_cursor_horizontal (GnumericSheet *sheet, int count)
{
175 176
	int new_left;
	
177
	new_left = sheet->cursor_col + count;
178 179 180

	if (new_left < 0)
		return;
181 182
	
	if (new_left < sheet->top_col){
183 184
		g_warning ("do scroll\n");
		return;
185 186 187 188
	}

	if (new_left < 0)
		new_left = 0;
189

190
	gnumeric_sheet_move_cursor (sheet, new_left, sheet->cursor_row);
191 192
}

193 194 195 196 197 198 199
/*
 * gnumeric_sheet_move_cursor_vertical:
 *  @Sheet:  The sheet name
 *  @count:  number of units to move the cursor vertically
 *
 * Moves the cursor count rows
 */
200 201 202 203
static void
gnumeric_sheet_move_cursor_vertical (GnumericSheet *sheet, int count)
{
	int new_top;
204 205

	new_top = sheet->cursor_row + count;
206 207

	if (new_top < 0)
208 209 210 211 212 213
		return;

	if (new_top < sheet->top_row){
		g_warning ("do scroll\n");
		return;
	}
214

215
	gnumeric_sheet_move_cursor (sheet, sheet->cursor_col, new_top);
216 217
}

218 219
static void
gnumeric_sheet_move_horizontal_selection (GnumericSheet *gsheet, int count)
220
{
221
	sheet_selection_extend_horizontal (gsheet->sheet, count);
222 223
}

224
static void
225
gnumeric_sheet_move_vertical_selection (GnumericSheet *gsheet, int count)
226
{
227
	sheet_selection_extend_vertical (gsheet->sheet, count);
228 229
}

230 231 232 233
static gint
gnumeric_sheet_key (GtkWidget *widget, GdkEventKey *event)
{
	GnumericSheet *sheet = GNUMERIC_SHEET (widget);
Arturo Espinosa's avatar
Arturo Espinosa committed
234
	Workbook *wb = sheet->sheet->parent_workbook;
235 236 237 238 239 240 241 242 243 244 245
	void (*movefn_horizontal)(GnumericSheet *, int);
	void (*movefn_vertical)(GnumericSheet *, int);
	
	if ((event->state & GDK_SHIFT_MASK) != 0){
		movefn_horizontal = gnumeric_sheet_move_horizontal_selection;
		movefn_vertical   = gnumeric_sheet_move_vertical_selection;
	} else {
		movefn_horizontal = gnumeric_sheet_move_cursor_horizontal;
		movefn_vertical = gnumeric_sheet_move_cursor_vertical;
		
	}
246 247 248

	switch (event->keyval){
	case GDK_Left:
249
		(*movefn_horizontal)(sheet, -1);
250 251 252
		break;

	case GDK_Right:
253
		(*movefn_horizontal)(sheet, 1);
254 255 256
		break;

	case GDK_Up:
257
		(*movefn_vertical)(sheet, -1);
258 259 260
		break;

	case GDK_Down:
261
		(*movefn_vertical)(sheet, 1);
262 263
		break;

Arturo Espinosa's avatar
Arturo Espinosa committed
264 265 266 267
	case GDK_F2:
		gtk_window_set_focus (GTK_WINDOW (wb->toplevel), wb->ea_input);
		/* fallback */
		
268
	default:
269 270 271 272
		if (!sheet->item_editor){
			if (event->keyval >= 0x20 && event->keyval <= 0xff)
			    start_editing_at_cursor (sheet, wb->ea_input);
		}
Arturo Espinosa's avatar
Arturo Espinosa committed
273 274 275 276

		/* Forward the keystroke to the input line */
		gtk_widget_event (sheet->entry, (GdkEvent *) event);
		
277 278 279 280
	}
	return 1;
}

Arturo Espinosa's avatar
Arturo Espinosa committed
281 282 283
GtkWidget *
gnumeric_sheet_new (Sheet *sheet)
{
284
	GnomeCanvasItem *item;
Arturo Espinosa's avatar
Arturo Espinosa committed
285
	GnumericSheet *gsheet;
286 287 288
	GnomeCanvas   *gsheet_canvas;
	GnomeCanvasGroup *gsheet_group;
	GtkWidget *widget;
289
	GtkWidget *entry = sheet->parent_workbook->ea_input;
Arturo Espinosa's avatar
Arturo Espinosa committed
290
	
291
	gsheet = gnumeric_sheet_create (sheet, entry);
Arturo Espinosa's avatar
Arturo Espinosa committed
292
	gnome_canvas_set_size (GNOME_CANVAS (gsheet), 300, 100);
293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311

	/* handy shortcuts */
	gsheet_canvas = GNOME_CANVAS (gsheet);
	gsheet_group = GNOME_CANVAS_GROUP (gsheet_canvas->root);
	
	/* The grid */
	item = gnome_canvas_item_new (gsheet_canvas, gsheet_group,
				      item_grid_get_type (),
				      "ItemGrid::Sheet", sheet,
				      NULL);
	gsheet->item_grid = ITEM_GRID (item);

	/* The cursor */
	item = gnome_canvas_item_new (gsheet_canvas, gsheet_group,
				      item_cursor_get_type (),
				      "ItemCursor::Sheet", sheet,
				      "ItemCursor::Grid", gsheet->item_grid,
				      NULL);
	gsheet->item_cursor = ITEM_CURSOR (item);
312

313 314 315 316 317 318 319 320 321 322
	widget = GTK_WIDGET (gsheet);

	return widget;
}

static void
gnumeric_sheet_realize (GtkWidget *widget)
{
	if (GTK_WIDGET_CLASS (sheet_parent_class)->realize)
		(*GTK_WIDGET_CLASS (sheet_parent_class)->realize)(widget);
Arturo Espinosa's avatar
Arturo Espinosa committed
323 324
}

325 326
void
gnumeric_sheet_compute_visible_ranges (GnumericSheet *gsheet)
327
{
328
	GnomeCanvas   *canvas = GNOME_CANVAS (gsheet);
Arturo Espinosa's avatar
Arturo Espinosa committed
329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367
	int pixels, col, row;

	/* Find out the last visible col and the last full visible column */
	pixels = 0;
	col = gsheet->top_col;
	do {
		ColRowInfo *ci;
		int cb;
		
		ci = sheet_col_get_info (gsheet->sheet, col);
		cb = pixels + ci->pixels;
		
		if (cb == canvas->width){
			gsheet->last_visible_col = col;
			gsheet->last_full_col = col;
		} if (cb > canvas->width){
			gsheet->last_visible_col = col;
			if (col == gsheet->top_col)
				gsheet->last_full_col = gsheet->top_col;
			else
				gsheet->last_full_col = col - 1;
		}
		pixels = cb;
		col++;
	} while (pixels < canvas->width);

	/* Find out the last visible row and the last fully visible row */
	pixels = 0;
	row = gsheet->top_row;
	do {
		ColRowInfo *ri;
		int cb;
		
		ri = sheet_row_get_info (gsheet->sheet, row);
		cb = pixels + ri->pixels;
		
		if (cb == canvas->height){
			gsheet->last_visible_row = row;
			gsheet->last_full_row = row;
368
		} if (cb > canvas->height){
Arturo Espinosa's avatar
Arturo Espinosa committed
369 370 371 372 373 374 375 376
			gsheet->last_visible_row = row;
			if (col == gsheet->top_row)
				gsheet->last_full_row = gsheet->top_row;
			else
				gsheet->last_full_row = row - 1;
		}
		pixels = cb;
		row++;
377 378
	} while (pixels < canvas->height);
}
Arturo Espinosa's avatar
Arturo Espinosa committed
379

380 381 382 383 384 385
static void
gnumeric_size_allocate (GtkWidget *widget, GtkAllocation *allocation)
{
	(*GTK_WIDGET_CLASS (sheet_parent_class)->size_allocate)(widget, allocation);

	gnumeric_sheet_compute_visible_ranges (GNUMERIC_SHEET (widget));
386 387
}

Arturo Espinosa's avatar
Arturo Espinosa committed
388 389 390 391 392 393 394 395 396 397 398 399 400 401 402
static void
gnumeric_sheet_class_init (GnumericSheetClass *class)
{
	GtkObjectClass *object_class;
	GtkWidgetClass *widget_class;
	GnomeCanvasClass *canvas_class;

	object_class = (GtkObjectClass *) class;
	widget_class = (GtkWidgetClass *) class;
	canvas_class = (GnomeCanvasClass *) class;
	
	sheet_parent_class = gtk_type_class (gnome_canvas_get_type());

	/* Method override */
	object_class->destroy = gnumeric_sheet_destroy;
403
	widget_class->realize = gnumeric_sheet_realize;
404
 	widget_class->size_allocate = gnumeric_size_allocate;
405
	widget_class->key_press_event = gnumeric_sheet_key;
Arturo Espinosa's avatar
Arturo Espinosa committed
406 407
}

Arturo Espinosa's avatar
Arturo Espinosa committed
408 409 410 411 412
static void
gnumeric_sheet_init (GnumericSheet *gsheet)
{
	GnomeCanvas *canvas = GNOME_CANVAS (gsheet);

413 414
	GTK_WIDGET_SET_FLAGS (canvas, GTK_CAN_FOCUS);
	GTK_WIDGET_SET_FLAGS (canvas, GTK_CAN_DEFAULT);
Arturo Espinosa's avatar
Arturo Espinosa committed
415 416
}

417 418 419 420 421 422 423 424 425 426 427
GtkType
gnumeric_sheet_get_type (void)
{
	static GtkType gnumeric_sheet_type = 0;

	if (!gnumeric_sheet_type){
		GtkTypeInfo gnumeric_sheet_info = {
			"GnumericSheet",
			sizeof (GnumericSheet),
			sizeof (GnumericSheetClass),
			(GtkClassInitFunc) gnumeric_sheet_class_init,
Arturo Espinosa's avatar
Arturo Espinosa committed
428
			(GtkObjectInitFunc) gnumeric_sheet_init,
429 430 431 432 433
			NULL, /* reserved 1 */
			NULL, /* reserved 2 */
			(GtkClassInitFunc) NULL
		};

Arturo Espinosa's avatar
Arturo Espinosa committed
434
		gnumeric_sheet_type = gtk_type_unique (gnome_canvas_get_type (), &gnumeric_sheet_info);
435 436 437 438
	}

	return gnumeric_sheet_type;
}