evolution-3.6.4/widgets/table/e-table-item.c

No issues found

   1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
   2 /*
   3  * e-table-item.c
   4  *
   5  * This program is free software; you can redistribute it and/or
   6  * modify it under the terms of the GNU Lesser General Public
   7  * License as published by the Free Software Foundation; either
   8  * version 2 of the License, or (at your option) version 3.
   9  *
  10  * This program is distributed in the hope that it will be useful,
  11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  13  * Lesser General Public License for more details.
  14  *
  15  * You should have received a copy of the GNU Lesser General Public
  16  * License along with the program; if not, see <http://www.gnu.org/licenses/>
  17  *
  18  *
  19  * Authors:
  20  *		Chris Lahey <clahey@ximian.com>
  21  *		Miguel de Icaza <miguel@gnu.org>
  22  *
  23  * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
  24  */
  25 /*
  26  * TODO:
  27  *   Add a border to the thing, so that focusing works properly.
  28  */
  29 #ifdef HAVE_CONFIG_H
  30 #include <config.h>
  31 #endif
  32 
  33 #include <math.h>
  34 #include <stdio.h>
  35 #include <string.h>
  36 #include <stdlib.h>
  37 
  38 #include <gtk/gtk.h>
  39 #include <gdk/gdkkeysyms.h>
  40 
  41 #include "gal-a11y-e-table-item-factory.h"
  42 #include "gal-a11y-e-table-item.h"
  43 #include <glib/gi18n.h>
  44 #include "e-util/e-util.h"
  45 #include "misc/e-canvas.h"
  46 #include "misc/e-canvas-utils.h"
  47 
  48 #include "e-cell.h"
  49 #include "e-table-item.h"
  50 #include "e-table-subset.h"
  51 
  52 /* workaround for avoiding API breakage */
  53 #define eti_get_type e_table_item_get_type
  54 G_DEFINE_TYPE (ETableItem, eti, GNOME_TYPE_CANVAS_ITEM)
  55 
  56 #define FOCUSED_BORDER 2
  57 
  58 #define d(x)
  59 
  60 #if d(!)0
  61 #define e_table_item_leave_edit_(x) (e_table_item_leave_edit((x)), g_print ("%s: e_table_item_leave_edit\n", __FUNCTION__))
  62 #else
  63 #define e_table_item_leave_edit_(x) (e_table_item_leave_edit((x)))
  64 #endif
  65 
  66 static void eti_check_cursor_bounds (ETableItem *eti);
  67 static void eti_cancel_drag_due_to_model_change (ETableItem *eti);
  68 
  69 /* FIXME: Do an analysis of which cell functions are needed before
  70  * realize and make sure that all of them are doable by all the cells
  71  * and that all of the others are only done after realization. */
  72 
  73 enum {
  74 	CURSOR_CHANGE,
  75 	CURSOR_ACTIVATED,
  76 	DOUBLE_CLICK,
  77 	RIGHT_CLICK,
  78 	CLICK,
  79 	KEY_PRESS,
  80 	START_DRAG,
  81 	STYLE_SET,
  82 	SELECTION_MODEL_REMOVED,
  83 	SELECTION_MODEL_ADDED,
  84 	LAST_SIGNAL
  85 };
  86 
  87 static guint eti_signals[LAST_SIGNAL] = { 0, };
  88 
  89 enum {
  90 	PROP_0,
  91 	PROP_TABLE_HEADER,
  92 	PROP_TABLE_MODEL,
  93 	PROP_SELECTION_MODEL,
  94 	PROP_TABLE_ALTERNATING_ROW_COLORS,
  95 	PROP_TABLE_HORIZONTAL_DRAW_GRID,
  96 	PROP_TABLE_VERTICAL_DRAW_GRID,
  97 	PROP_TABLE_DRAW_FOCUS,
  98 	PROP_CURSOR_MODE,
  99 	PROP_LENGTH_THRESHOLD,
 100 	PROP_CURSOR_ROW,
 101 	PROP_UNIFORM_ROW_HEIGHT,
 102 
 103 	PROP_MINIMUM_WIDTH,
 104 	PROP_WIDTH,
 105 	PROP_HEIGHT
 106 };
 107 
 108 #define DOUBLE_CLICK_TIME      250
 109 #define TRIPLE_CLICK_TIME      500
 110 
 111 static gint eti_get_height (ETableItem *eti);
 112 static gint eti_row_height (ETableItem *eti, gint row);
 113 static void e_table_item_focus (ETableItem *eti, gint col, gint row, GdkModifierType state);
 114 static void eti_cursor_change (ESelectionModel *selection, gint row, gint col, ETableItem *eti);
 115 static void eti_cursor_activated (ESelectionModel *selection, gint row, gint col, ETableItem *eti);
 116 static void eti_selection_change (ESelectionModel *selection, ETableItem *eti);
 117 static void eti_selection_row_change (ESelectionModel *selection, gint row, ETableItem *eti);
 118 static void e_table_item_redraw_row (ETableItem *eti, gint row);
 119 
 120 #define ETI_SINGLE_ROW_HEIGHT(eti) ((eti)->uniform_row_height_cache != -1 ? (eti)->uniform_row_height_cache : eti_row_height((eti), -1))
 121 #define ETI_MULTIPLE_ROW_HEIGHT(eti,row) ((eti)->height_cache && (eti)->height_cache[(row)] != -1 ? (eti)->height_cache[(row)] : eti_row_height((eti),(row)))
 122 #define ETI_ROW_HEIGHT(eti,row) ((eti)->uniform_row_height ? ETI_SINGLE_ROW_HEIGHT ((eti)) : ETI_MULTIPLE_ROW_HEIGHT((eti),(row)))
 123 
 124 /* tweak_hsv is a really tweaky function. it modifies its first argument, which
 125  * should be the color you want tweaked. delta_h, delta_s and delta_v specify
 126  * how much you want their respective channels modified (and in what direction).
 127  * if it can't do the specified modification, it does it in the oppositon direction */
 128 static void
 129 e_hsv_tweak (GdkColor *color,
 130              gdouble delta_h,
 131              gdouble delta_s,
 132              gdouble delta_v)
 133 {
 134 	gdouble h, s, v, r, g, b;
 135 
 136 	r = color->red   / 65535.0f;
 137 	g = color->green / 65535.0f;
 138 	b = color->blue  / 65535.0f;
 139 
 140 	gtk_rgb_to_hsv (r, g, b, &h, &s, &v);
 141 
 142 	if (h + delta_h < 0) {
 143 		h -= delta_h;
 144 	} else {
 145 		h += delta_h;
 146 	}
 147 
 148 	if (s + delta_s < 0) {
 149 		s -= delta_s;
 150 	} else {
 151 		s += delta_s;
 152 	}
 153 
 154 	if (v + delta_v < 0) {
 155 		v -= delta_v;
 156 	} else {
 157 		v += delta_v;
 158 	}
 159 
 160 	gtk_hsv_to_rgb (h, s, v, &r, &g, &b);
 161 
 162 	color->red   = r * 65535.0f;
 163 	color->green = g * 65535.0f;
 164 	color->blue  = b * 65535.0f;
 165 }
 166 
 167 inline static gint
 168 model_to_view_row (ETableItem *eti,
 169                    gint row)
 170 {
 171 	gint i;
 172 	if (row == -1)
 173 		return -1;
 174 	if (eti->uses_source_model) {
 175 		ETableSubset *etss = E_TABLE_SUBSET (eti->table_model);
 176 		if (eti->row_guess >= 0 && eti->row_guess < etss->n_map) {
 177 			if (etss->map_table[eti->row_guess] == row) {
 178 				return eti->row_guess;
 179 			}
 180 		}
 181 		for (i = 0; i < etss->n_map; i++) {
 182 			if (etss->map_table[i] == row)
 183 				return i;
 184 		}
 185 		return -1;
 186 	} else
 187 		return row;
 188 }
 189 
 190 inline static gint
 191 view_to_model_row (ETableItem *eti,
 192                    gint row)
 193 {
 194 	if (eti->uses_source_model) {
 195 		ETableSubset *etss = E_TABLE_SUBSET (eti->table_model);
 196 		if (row >= 0 && row < etss->n_map) {
 197 			eti->row_guess = row;
 198 			return etss->map_table[row];
 199 		} else
 200 			return -1;
 201 	} else
 202 		return row;
 203 }
 204 
 205 inline static gint
 206 model_to_view_col (ETableItem *eti,
 207                    gint col)
 208 {
 209 	gint i;
 210 	if (col == -1)
 211 		return -1;
 212 	for (i = 0; i < eti->cols; i++) {
 213 		ETableCol *ecol = e_table_header_get_column (eti->header, i);
 214 		if (ecol->col_idx == col)
 215 			return i;
 216 	}
 217 	return -1;
 218 }
 219 
 220 inline static gint
 221 view_to_model_col (ETableItem *eti,
 222                    gint col)
 223 {
 224 	ETableCol *ecol = e_table_header_get_column (eti->header, col);
 225 	return ecol ? ecol->col_idx : -1;
 226 }
 227 
 228 static void
 229 grab_cancelled (ECanvas *canvas,
 230                 GnomeCanvasItem *item,
 231                 gpointer data)
 232 {
 233 	ETableItem *eti = data;
 234 
 235 	eti->grab_cancelled = TRUE;
 236 }
 237 
 238 inline static void
 239 eti_grab (ETableItem *eti,
 240           guint32 time)
 241 {
 242 	GnomeCanvasItem *item = GNOME_CANVAS_ITEM (eti);
 243 	d (g_print ("%s: time: %d\n", __FUNCTION__, time));
 244 	if (eti->grabbed_count == 0) {
 245 		eti->gtk_grabbed = FALSE;
 246 		eti->grab_cancelled = FALSE;
 247 		if (e_canvas_item_grab (E_CANVAS (item->canvas),
 248 				       item,
 249 				       GDK_BUTTON1_MOTION_MASK | GDK_BUTTON2_MOTION_MASK | GDK_BUTTON3_MOTION_MASK
 250 				       | GDK_POINTER_MOTION_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK,
 251 				       NULL, time,
 252 				       grab_cancelled,
 253 				       eti) != GDK_GRAB_SUCCESS) {
 254 			d (g_print ("%s: gtk_grab_add\n", __FUNCTION__));
 255 			gtk_grab_add (GTK_WIDGET (item->canvas));
 256 			eti->gtk_grabbed = TRUE;
 257 		}
 258 	}
 259 	eti->grabbed_count++;
 260 }
 261 
 262 inline static void
 263 eti_ungrab (ETableItem *eti,
 264             guint32 time)
 265 {
 266 	GnomeCanvasItem *item = GNOME_CANVAS_ITEM (eti);
 267 	d (g_print ("%s: time: %d\n", __FUNCTION__, time));
 268 	eti->grabbed_count--;
 269 	if (eti->grabbed_count == 0) {
 270 		if (eti->grab_cancelled) {
 271 			eti->grab_cancelled = FALSE;
 272 		} else {
 273 			if (eti->gtk_grabbed) {
 274 				d (g_print ("%s: gtk_grab_remove\n", __FUNCTION__));
 275 				gtk_grab_remove (GTK_WIDGET (item->canvas));
 276 				eti->gtk_grabbed = FALSE;
 277 			}
 278 			gnome_canvas_item_ungrab (item, time);
 279 			eti->grabbed_col = -1;
 280 			eti->grabbed_row = -1;
 281 		}
 282 	}
 283 }
 284 
 285 inline static gboolean
 286 eti_editing (ETableItem *eti)
 287 {
 288 	d (g_print ("%s: %s\n", __FUNCTION__, (eti->editing_col == -1) ? "false":"true"));
 289 
 290 	if (eti->editing_col == -1)
 291 		return FALSE;
 292 	else
 293 		return TRUE;
 294 }
 295 
 296 inline static GdkColor *
 297 eti_get_cell_background_color (ETableItem *eti,
 298                                gint row,
 299                                gint col,
 300                                gboolean selected,
 301                                gboolean *allocatedp)
 302 {
 303 	ECellView *ecell_view = eti->cell_views[col];
 304 	GtkWidget *canvas;
 305 	GdkColor *background, bg;
 306 	GtkStyle *style;
 307 	gchar *color_spec = NULL;
 308 	gboolean allocated = FALSE;
 309 
 310 	canvas = GTK_WIDGET (GNOME_CANVAS_ITEM (eti)->canvas);
 311 	style = gtk_widget_get_style (canvas);
 312 
 313 	if (selected) {
 314 		if (gtk_widget_has_focus (canvas))
 315 			background = &style->bg[GTK_STATE_SELECTED];
 316 		else
 317 			background = &style->bg[GTK_STATE_ACTIVE];
 318 	} else {
 319 		background = &style->base[GTK_STATE_NORMAL];
 320 	}
 321 
 322 	color_spec = e_cell_get_bg_color (ecell_view, row);
 323 
 324 	if (color_spec != NULL) {
 325 		if (gdk_color_parse (color_spec, &bg)) {
 326 			background = gdk_color_copy (&bg);
 327 			allocated = TRUE;
 328 		}
 329 	}
 330 
 331 	if (eti->alternating_row_colors) {
 332 		if (row % 2) {
 333 
 334 		} else {
 335 			if (!allocated) {
 336 				background = gdk_color_copy (background);
 337 				allocated = TRUE;
 338 			}
 339 			e_hsv_tweak (background, 0.0f, 0.0f, -0.07f);
 340 		}
 341 	}
 342 	if (allocatedp)
 343 		*allocatedp = allocated;
 344 
 345 	return background;
 346 }
 347 
 348 inline static GdkColor *
 349 eti_get_cell_foreground_color (ETableItem *eti,
 350                                gint row,
 351                                gint col,
 352                                gboolean selected,
 353                                gboolean *allocated)
 354 {
 355 	GtkWidget *canvas;
 356 	GdkColor *foreground;
 357 	GtkStyle *style;
 358 
 359 	canvas = GTK_WIDGET (GNOME_CANVAS_ITEM (eti)->canvas);
 360 	style = gtk_widget_get_style (canvas);
 361 
 362 	if (allocated)
 363 		*allocated = FALSE;
 364 
 365 	if (selected) {
 366 		if (gtk_widget_has_focus (canvas))
 367 			foreground = &style->fg[GTK_STATE_SELECTED];
 368 		else
 369 			foreground = &style->fg[GTK_STATE_ACTIVE];
 370 	} else {
 371 		foreground = &style->text[GTK_STATE_NORMAL];
 372 	}
 373 
 374 	return foreground;
 375 }
 376 
 377 static void
 378 eti_free_save_state (ETableItem *eti)
 379 {
 380 	if (eti->save_row == -1 ||
 381 	    !eti->cell_views_realized)
 382 		return;
 383 
 384 	e_cell_free_state (
 385 		eti->cell_views[eti->save_col], view_to_model_col (eti, eti->save_col),
 386 		eti->save_col, eti->save_row, eti->save_state);
 387 	eti->save_row = -1;
 388 	eti->save_col = -1;
 389 	eti->save_state = NULL;
 390 }
 391 
 392 /*
 393  * During realization, we have to invoke the per-ecell realize routine
 394  * (On our current setup, we have one e-cell per column.
 395  *
 396  * We might want to optimize this to only realize the unique e-cells:
 397  * ie, a strings-only table, uses the same e-cell for every column, and
 398  * we might want to avoid realizing each e-cell.
 399  */
 400 static void
 401 eti_realize_cell_views (ETableItem *eti)
 402 {
 403 	GnomeCanvasItem *item;
 404 	gint i;
 405 
 406 	item = GNOME_CANVAS_ITEM (eti);
 407 
 408 	if (eti->cell_views_realized)
 409 		return;
 410 
 411 	if (!(item->flags & GNOME_CANVAS_ITEM_REALIZED))
 412 		return;
 413 
 414 	for (i = 0; i < eti->n_cells; i++)
 415 		e_cell_realize (eti->cell_views[i]);
 416 	eti->cell_views_realized = 1;
 417 }
 418 
 419 static void
 420 eti_attach_cell_views (ETableItem *eti)
 421 {
 422 	gint i;
 423 
 424 	g_return_if_fail (eti->header);
 425 	g_return_if_fail (eti->table_model);
 426 
 427 	/* this is just c&p from model pre change, but it fixes things */
 428 	eti_cancel_drag_due_to_model_change (eti);
 429 	eti_check_cursor_bounds (eti);
 430 	if (eti_editing (eti))
 431 		e_table_item_leave_edit_(eti);
 432 	eti->motion_row = -1;
 433 	eti->motion_col = -1;
 434 
 435 	/*
 436 	 * Now realize the various ECells
 437 	 */
 438 	eti->n_cells = eti->cols;
 439 	eti->cell_views = g_new (ECellView *, eti->n_cells);
 440 
 441 	for (i = 0; i < eti->n_cells; i++) {
 442 		ETableCol *ecol = e_table_header_get_column (eti->header, i);
 443 
 444 		eti->cell_views[i] = e_cell_new_view (ecol->ecell, eti->table_model, eti);
 445 	}
 446 
 447 	eti->needs_compute_height = 1;
 448 	e_canvas_item_request_reflow (GNOME_CANVAS_ITEM (eti));
 449 	eti->needs_redraw = 1;
 450 	gnome_canvas_item_request_update (GNOME_CANVAS_ITEM (eti));
 451 }
 452 
 453 /*
 454  * During unrealization: we invoke every e-cell (one per column in the current
 455  * setup) to dispose all X resources allocated
 456  */
 457 static void
 458 eti_unrealize_cell_views (ETableItem *eti)
 459 {
 460 	gint i;
 461 
 462 	if (eti->cell_views_realized == 0)
 463 		return;
 464 
 465 	eti_free_save_state (eti);
 466 
 467 	for (i = 0; i < eti->n_cells; i++)
 468 		e_cell_unrealize (eti->cell_views[i]);
 469 	eti->cell_views_realized = 0;
 470 }
 471 
 472 static void
 473 eti_detach_cell_views (ETableItem *eti)
 474 {
 475 	gint i;
 476 
 477 	eti_free_save_state (eti);
 478 
 479 	for (i = 0; i < eti->n_cells; i++) {
 480 		e_cell_kill_view (eti->cell_views[i]);
 481 		eti->cell_views[i] = NULL;
 482 	}
 483 
 484 	g_free (eti->cell_views);
 485 	eti->cell_views = NULL;
 486 	eti->n_cells = 0;
 487 }
 488 
 489 static void
 490 eti_bounds (GnomeCanvasItem *item,
 491             gdouble *x1,
 492             gdouble *y1,
 493             gdouble *x2,
 494             gdouble *y2)
 495 {
 496 	cairo_matrix_t i2c;
 497 	ETableItem *eti = E_TABLE_ITEM (item);
 498 
 499 	/* Wrong BBox's are the source of redraw nightmares */
 500 
 501 	*x1 = 0;
 502 	*y1 = 0;
 503 	*x2 = eti->width;
 504 	*y2 = eti->height;
 505 
 506 	gnome_canvas_item_i2c_matrix (GNOME_CANVAS_ITEM (eti), &i2c);
 507 	gnome_canvas_matrix_transform_rect (&i2c, x1, y1, x2, y2);
 508 }
 509 
 510 static void
 511 eti_reflow (GnomeCanvasItem *item,
 512             gint flags)
 513 {
 514 	ETableItem *eti = E_TABLE_ITEM (item);
 515 
 516 	if (eti->needs_compute_height) {
 517 		gint new_height = eti_get_height (eti);
 518 
 519 		if (new_height != eti->height) {
 520 			eti->height = new_height;
 521 			e_canvas_item_request_parent_reflow (GNOME_CANVAS_ITEM (eti));
 522 			eti->needs_redraw = 1;
 523 			gnome_canvas_item_request_update (GNOME_CANVAS_ITEM (eti));
 524 		}
 525 		eti->needs_compute_height = 0;
 526 	}
 527 	if (eti->needs_compute_width) {
 528 		gint new_width = e_table_header_total_width (eti->header);
 529 		if (new_width != eti->width) {
 530 			eti->width = new_width;
 531 			e_canvas_item_request_parent_reflow (GNOME_CANVAS_ITEM (eti));
 532 			eti->needs_redraw = 1;
 533 			gnome_canvas_item_request_update (GNOME_CANVAS_ITEM (eti));
 534 		}
 535 		eti->needs_compute_width = 0;
 536 	}
 537 }
 538 
 539 /*
 540  * GnomeCanvasItem::update method
 541  */
 542 static void
 543 eti_update (GnomeCanvasItem *item,
 544             const cairo_matrix_t *i2c,
 545             gint flags)
 546 {
 547 	ETableItem *eti = E_TABLE_ITEM (item);
 548 	gdouble x1, x2, y1, y2;
 549 
 550 	if (GNOME_CANVAS_ITEM_CLASS (eti_parent_class)->update)
 551 		(*GNOME_CANVAS_ITEM_CLASS (eti_parent_class)->update)(item, i2c, flags);
 552 
 553 	x1 = item->x1;
 554 	y1 = item->y1;
 555 	x2 = item->x2;
 556 	y2 = item->y2;
 557 
 558 	eti_bounds (item, &item->x1, &item->y1, &item->x2, &item->y2);
 559 	if (item->x1 != x1 ||
 560 	    item->y1 != y1 ||
 561 	    item->x2 != x2 ||
 562 	    item->y2 != y2) {
 563 		gnome_canvas_request_redraw (item->canvas, x1, y1, x2, y2);
 564 		eti->needs_redraw = 1;
 565 	}
 566 
 567 	if (eti->needs_redraw) {
 568 		gnome_canvas_request_redraw (
 569 			item->canvas, item->x1, item->y1,
 570 			item->x2, item->y2);
 571 		eti->needs_redraw = 0;
 572 	}
 573 }
 574 
 575 /*
 576  * eti_remove_table_model:
 577  *
 578  * Invoked to release the table model associated with this ETableItem
 579  */
 580 static void
 581 eti_remove_table_model (ETableItem *eti)
 582 {
 583 	if (!eti->table_model)
 584 		return;
 585 
 586 	g_signal_handler_disconnect (
 587 		eti->table_model,
 588 		eti->table_model_pre_change_id);
 589 	g_signal_handler_disconnect (
 590 		eti->table_model,
 591 		eti->table_model_no_change_id);
 592 	g_signal_handler_disconnect (
 593 		eti->table_model,
 594 		eti->table_model_change_id);
 595 	g_signal_handler_disconnect (
 596 		eti->table_model,
 597 		eti->table_model_row_change_id);
 598 	g_signal_handler_disconnect (
 599 		eti->table_model,
 600 		eti->table_model_cell_change_id);
 601 	g_signal_handler_disconnect (
 602 		eti->table_model,
 603 		eti->table_model_rows_inserted_id);
 604 	g_signal_handler_disconnect (
 605 		eti->table_model,
 606 		eti->table_model_rows_deleted_id);
 607 	g_object_unref (eti->table_model);
 608 	if (eti->source_model)
 609 		g_object_unref (eti->source_model);
 610 
 611 	eti->table_model_pre_change_id = 0;
 612 	eti->table_model_no_change_id = 0;
 613 	eti->table_model_change_id = 0;
 614 	eti->table_model_row_change_id = 0;
 615 	eti->table_model_cell_change_id = 0;
 616 	eti->table_model_rows_inserted_id = 0;
 617 	eti->table_model_rows_deleted_id = 0;
 618 	eti->table_model = NULL;
 619 	eti->source_model = NULL;
 620 	eti->uses_source_model = 0;
 621 }
 622 
 623 /*
 624  * eti_remove_table_model:
 625  *
 626  * Invoked to release the table model associated with this ETableItem
 627  */
 628 static void
 629 eti_remove_selection_model (ETableItem *eti)
 630 {
 631 	if (!eti->selection)
 632 		return;
 633 
 634 	g_signal_handler_disconnect (
 635 		eti->selection,
 636 		eti->selection_change_id);
 637 	g_signal_handler_disconnect (
 638 		eti->selection,
 639 		eti->selection_row_change_id);
 640 	g_signal_handler_disconnect (
 641 		eti->selection,
 642 		eti->cursor_change_id);
 643 	g_signal_handler_disconnect (
 644 		eti->selection,
 645 		eti->cursor_activated_id);
 646 	g_object_unref (eti->selection);
 647 
 648 	eti->selection_change_id = 0;
 649 	eti->selection_row_change_id = 0;
 650 	eti->cursor_activated_id = 0;
 651 	eti->selection = NULL;
 652 }
 653 
 654 /*
 655  * eti_remove_header_model:
 656  *
 657  * Invoked to release the header model associated with this ETableItem
 658  */
 659 static void
 660 eti_remove_header_model (ETableItem *eti)
 661 {
 662 	if (!eti->header)
 663 		return;
 664 
 665 	g_signal_handler_disconnect (
 666 		eti->header,
 667 		eti->header_structure_change_id);
 668 	g_signal_handler_disconnect (
 669 		eti->header,
 670 		eti->header_dim_change_id);
 671 	g_signal_handler_disconnect (
 672 		eti->header,
 673 		eti->header_request_width_id);
 674 
 675 	if (eti->cell_views) {
 676 		eti_unrealize_cell_views (eti);
 677 		eti_detach_cell_views (eti);
 678 	}
 679 	g_object_unref (eti->header);
 680 
 681 	eti->header_structure_change_id = 0;
 682 	eti->header_dim_change_id = 0;
 683 	eti->header_request_width_id = 0;
 684 	eti->header = NULL;
 685 }
 686 
 687 /*
 688  * eti_row_height_real:
 689  *
 690  * Returns the height used by row @row.  This does not include the one-pixel
 691  * used as a separator between rows
 692  */
 693 static gint
 694 eti_row_height_real (ETableItem *eti,
 695                      gint row)
 696 {
 697 	const gint cols = e_table_header_count (eti->header);
 698 	gint col;
 699 	gint h, max_h;
 700 
 701 	g_return_val_if_fail (cols == 0 || eti->cell_views, 0);
 702 
 703 	max_h = 0;
 704 
 705 	for (col = 0; col < cols; col++) {
 706 		h = e_cell_height (eti->cell_views[col], view_to_model_col (eti, col), col, row);
 707 
 708 		if (h > max_h)
 709 			max_h = h;
 710 	}
 711 	return max_h;
 712 }
 713 
 714 static void
 715 confirm_height_cache (ETableItem *eti)
 716 {
 717 	gint i;
 718 
 719 	if (eti->uniform_row_height || eti->height_cache)
 720 		return;
 721 	eti->height_cache = g_new (int, eti->rows);
 722 	for (i = 0; i < eti->rows; i++) {
 723 		eti->height_cache[i] = -1;
 724 	}
 725 }
 726 
 727 static gboolean
 728 height_cache_idle (ETableItem *eti)
 729 {
 730 	gint changed = 0;
 731 	gint i;
 732 	confirm_height_cache (eti);
 733 	for (i = eti->height_cache_idle_count; i < eti->rows; i++) {
 734 		if (eti->height_cache[i] == -1) {
 735 			eti_row_height (eti, i);
 736 			changed++;
 737 			if (changed >= 20)
 738 				break;
 739 		}
 740 	}
 741 	if (changed >= 20) {
 742 		eti->height_cache_idle_count = i;
 743 		return TRUE;
 744 	}
 745 	eti->height_cache_idle_id = 0;
 746 	return FALSE;
 747 }
 748 
 749 static void
 750 free_height_cache (ETableItem *eti)
 751 {
 752 	GnomeCanvasItem *item;
 753 
 754 	item = GNOME_CANVAS_ITEM (eti);
 755 
 756 	if (item->flags & GNOME_CANVAS_ITEM_REALIZED) {
 757 		if (eti->height_cache)
 758 			g_free (eti->height_cache);
 759 		eti->height_cache = NULL;
 760 		eti->height_cache_idle_count = 0;
 761 		eti->uniform_row_height_cache = -1;
 762 
 763 		if (eti->uniform_row_height && eti->height_cache_idle_id != 0) {
 764 			g_source_remove (eti->height_cache_idle_id);
 765 			eti->height_cache_idle_id = 0;
 766 		}
 767 
 768 		if ((!eti->uniform_row_height) && eti->height_cache_idle_id == 0)
 769 			eti->height_cache_idle_id = g_idle_add_full (G_PRIORITY_LOW, (GSourceFunc) height_cache_idle, eti, NULL);
 770 	}
 771 }
 772 
 773 static void
 774 calculate_height_cache (ETableItem *eti)
 775 {
 776 	free_height_cache (eti);
 777 	confirm_height_cache (eti);
 778 }
 779 
 780 /*
 781  * eti_row_height:
 782  *
 783  * Returns the height used by row @row.  This does not include the one-pixel
 784  * used as a separator between rows
 785  */
 786 static gint
 787 eti_row_height (ETableItem *eti,
 788                 gint row)
 789 {
 790 	if (eti->uniform_row_height) {
 791 		eti->uniform_row_height_cache = eti_row_height_real (eti, -1);
 792 		return eti->uniform_row_height_cache;
 793 	} else {
 794 		if (!eti->height_cache) {
 795 			calculate_height_cache (eti);
 796 		}
 797 		if (eti->height_cache[row] == -1) {
 798 			eti->height_cache[row] = eti_row_height_real (eti, row);
 799 			if (row > 0 &&
 800 			    eti->length_threshold != -1 &&
 801 			    eti->rows > eti->length_threshold &&
 802 			    eti->height_cache[row] != eti_row_height (eti, 0)) {
 803 				eti->needs_compute_height = 1;
 804 				e_canvas_item_request_reflow (GNOME_CANVAS_ITEM (eti));
 805 			}
 806 		}
 807 		return eti->height_cache[row];
 808 	}
 809 }
 810 
 811 /*
 812  * eti_get_height:
 813  *
 814  * Returns the height of the ETableItem.
 815  *
 816  * The ETableItem might compute the whole height by asking every row its
 817  * size.  There is a special mode (designed to work when there are too
 818  * many rows in the table that performing the previous step could take
 819  * too long) set by the ETableItem->length_threshold that would determine
 820  * when the height is computed by using the first row as the size for
 821  * every other row in the ETableItem.
 822  */
 823 static gint
 824 eti_get_height (ETableItem *eti)
 825 {
 826 	const gint rows = eti->rows;
 827 	gint height_extra = eti->horizontal_draw_grid ? 1 : 0;
 828 
 829 	if (rows == 0)
 830 		return 0;
 831 
 832 	if (eti->uniform_row_height) {
 833 		gint row_height = ETI_ROW_HEIGHT (eti, -1);
 834 		return ((row_height + height_extra) * rows + height_extra);
 835 	} else {
 836 		gint height;
 837 		gint row;
 838 		if (eti->length_threshold != -1) {
 839 			if (rows > eti->length_threshold) {
 840 				gint row_height = ETI_ROW_HEIGHT (eti, 0);
 841 				if (eti->height_cache) {
 842 					height = 0;
 843 					for (row = 0; row < rows; row++) {
 844 						if (eti->height_cache[row] == -1) {
 845 							height += (row_height + height_extra) * (rows - row);
 846 							break;
 847 						}
 848 						else
 849 							height += eti->height_cache[row] + height_extra;
 850 					}
 851 				} else
 852 					height = (ETI_ROW_HEIGHT (eti, 0) + height_extra) * rows;
 853 
 854 				/*
 855 				 * 1 pixel at the top
 856 				 */
 857 				return height + height_extra;
 858 			}
 859 		}
 860 
 861 		height = height_extra;
 862 		for (row = 0; row < rows; row++)
 863 			height += ETI_ROW_HEIGHT (eti, row) + height_extra;
 864 
 865 		return height;
 866 	}
 867 }
 868 
 869 static void
 870 eti_item_region_redraw (ETableItem *eti,
 871                         gint x0,
 872                         gint y0,
 873                         gint x1,
 874                         gint y1)
 875 {
 876 	GnomeCanvasItem *item = GNOME_CANVAS_ITEM (eti);
 877 	gdouble dx1, dy1, dx2, dy2;
 878 	cairo_matrix_t i2c;
 879 
 880 	dx1 = x0;
 881 	dy1 = y0;
 882 	dx2 = x1;
 883 	dy2 = y1;
 884 
 885 	gnome_canvas_item_i2c_matrix (item, &i2c);
 886 	gnome_canvas_matrix_transform_rect (&i2c, &dx1, &dy1, &dx2, &dy2);
 887 
 888 	gnome_canvas_request_redraw (item->canvas, floor (dx1), floor (dy1), ceil (dx2), ceil (dy2));
 889 }
 890 
 891 /*
 892  * Computes the distance between @start_row and @end_row in pixels
 893  */
 894 gint
 895 e_table_item_row_diff (ETableItem *eti,
 896                        gint start_row,
 897                        gint end_row)
 898 {
 899 	gint height_extra = eti->horizontal_draw_grid ? 1 : 0;
 900 
 901 	if (start_row < 0)
 902 		start_row = 0;
 903 	if (end_row > eti->rows)
 904 		end_row = eti->rows;
 905 
 906 	if (eti->uniform_row_height) {
 907 		return ((end_row - start_row) * (ETI_ROW_HEIGHT (eti, -1) + height_extra));
 908 	} else {
 909 		gint row, total;
 910 		total = 0;
 911 		for (row = start_row; row < end_row; row++)
 912 			total += ETI_ROW_HEIGHT (eti, row) + height_extra;
 913 
 914 		return total;
 915 	}
 916 }
 917 
 918 static void
 919 eti_get_region (ETableItem *eti,
 920                 gint start_col,
 921                 gint start_row,
 922                 gint end_col,
 923                 gint end_row,
 924                 gint *x1p,
 925                 gint *y1p,
 926                 gint *x2p,
 927                 gint *y2p)
 928 {
 929 	gint x1, y1, x2, y2;
 930 
 931 	x1 = e_table_header_col_diff (eti->header, 0, start_col);
 932 	y1 = e_table_item_row_diff (eti, 0, start_row);
 933 	x2 = x1 + e_table_header_col_diff (eti->header, start_col, end_col + 1);
 934 	y2 = y1 + e_table_item_row_diff (eti, start_row, end_row + 1);
 935 	if (x1p)
 936 		*x1p = x1;
 937 	if (y1p)
 938 		*y1p = y1;
 939 	if (x2p)
 940 		*x2p = x2;
 941 	if (y2p)
 942 		*y2p = y2;
 943 }
 944 
 945 /*
 946  * eti_request_region_redraw:
 947  *
 948  * Request a canvas redraw on the range (start_col, start_row) to (end_col, end_row).
 949  * This is inclusive (ie, you can use: 0,0-0,0 to redraw the first cell).
 950  *
 951  * The @border argument is a number of pixels around the region that should also be queued
 952  * for redraw.   This is typically used by the focus routines to queue a redraw for the
 953  * border as well.
 954  */
 955 static void
 956 eti_request_region_redraw (ETableItem *eti,
 957                            gint start_col,
 958                            gint start_row,
 959                            gint end_col,
 960                            gint end_row,
 961                            gint border)
 962 {
 963 	gint x1, y1, x2, y2;
 964 
 965 	if (eti->rows > 0) {
 966 
 967 		eti_get_region (
 968 			eti,
 969 			start_col, start_row,
 970 			end_col, end_row,
 971 			&x1, &y1, &x2, &y2);
 972 
 973 		eti_item_region_redraw (
 974 			eti,
 975 			x1 - border,
 976 			y1 - border,
 977 			x2 + 1 + border,
 978 			y2 + 1 + border);
 979 	}
 980 }
 981 
 982 /*
 983  * eti_request_region_show
 984  *
 985  * Request a canvas show on the range (start_col, start_row) to (end_col, end_row).
 986  * This is inclusive (ie, you can use: 0,0-0,0 to show the first cell).
 987  */
 988 static void
 989 eti_request_region_show (ETableItem *eti,
 990                          gint start_col,
 991                          gint start_row,
 992                          gint end_col,
 993                          gint end_row,
 994                          gint delay)
 995 {
 996 	gint x1, y1, x2, y2;
 997 
 998 	eti_get_region (
 999 		eti,
1000 		start_col, start_row,
1001 		end_col, end_row,
1002 		&x1, &y1, &x2, &y2);
1003 
1004 	if (delay)
1005 		e_canvas_item_show_area_delayed (
1006 			GNOME_CANVAS_ITEM (eti), x1, y1, x2, y2, delay);
1007 	else
1008 		e_canvas_item_show_area (
1009 			GNOME_CANVAS_ITEM (eti), x1, y1, x2, y2);
1010 }
1011 
1012 static void
1013 eti_show_cursor (ETableItem *eti,
1014                  gint delay)
1015 {
1016 	GnomeCanvasItem *item;
1017 	gint cursor_row;
1018 
1019 	item = GNOME_CANVAS_ITEM (eti);
1020 
1021 	if (!((item->flags & GNOME_CANVAS_ITEM_REALIZED) && eti->cell_views_realized))
1022 		return;
1023 
1024 	if (eti->frozen_count > 0) {
1025 		eti->queue_show_cursor = TRUE;
1026 		return;
1027 	}
1028 
1029 #if 0
1030 	g_object_get (
1031 		eti->selection,
1032 		"cursor_row", &cursor_row,
1033 		NULL);
1034 #else
1035 	cursor_row = e_selection_model_cursor_row (eti->selection);
1036 #endif
1037 
1038 	d (g_print ("%s: cursor row: %d\n", __FUNCTION__, cursor_row));
1039 
1040 	if (cursor_row != -1) {
1041 		cursor_row = model_to_view_row (eti, cursor_row);
1042 		eti_request_region_show (
1043 			eti,
1044 			0, cursor_row, eti->cols - 1, cursor_row,
1045 			delay);
1046 	}
1047 }
1048 
1049 static void
1050 eti_check_cursor_bounds (ETableItem *eti)
1051 {
1052 	GnomeCanvasItem *item;
1053 	gint x1, y1, x2, y2;
1054 	gint cursor_row;
1055 
1056 	item = GNOME_CANVAS_ITEM (eti);
1057 
1058 	if (!((item->flags & GNOME_CANVAS_ITEM_REALIZED) && eti->cell_views_realized))
1059 		return;
1060 
1061 	if (eti->frozen_count > 0) {
1062 		return;
1063 	}
1064 
1065 	g_object_get (
1066 		eti->selection,
1067 		"cursor_row", &cursor_row,
1068 		NULL);
1069 
1070 	if (cursor_row == -1) {
1071 		eti->cursor_x1 = -1;
1072 		eti->cursor_y1 = -1;
1073 		eti->cursor_x2 = -1;
1074 		eti->cursor_y2 = -1;
1075 		eti->cursor_on_screen = TRUE;
1076 		return;
1077 	}
1078 
1079 	d (g_print ("%s: model cursor row: %d\n", __FUNCTION__, cursor_row));
1080 
1081 	cursor_row = model_to_view_row (eti, cursor_row);
1082 
1083 	d (g_print ("%s: cursor row: %d\n", __FUNCTION__, cursor_row));
1084 
1085 	eti_get_region (
1086 		eti,
1087 		0, cursor_row, eti->cols - 1, cursor_row,
1088 		&x1, &y1, &x2, &y2);
1089 	eti->cursor_x1 = x1;
1090 	eti->cursor_y1 = y1;
1091 	eti->cursor_x2 = x2;
1092 	eti->cursor_y2 = y2;
1093 	eti->cursor_on_screen = e_canvas_item_area_shown (GNOME_CANVAS_ITEM (eti), x1, y1, x2, y2);
1094 
1095 	d (g_print ("%s: cursor on screen: %s\n", __FUNCTION__, eti->cursor_on_screen ? "TRUE" : "FALSE"));
1096 }
1097 
1098 static void
1099 eti_maybe_show_cursor (ETableItem *eti,
1100                        gint delay)
1101 {
1102 	d (g_print ("%s: cursor on screen: %s\n", __FUNCTION__, eti->cursor_on_screen ? "TRUE" : "FALSE"));
1103 	if (eti->cursor_on_screen)
1104 		eti_show_cursor (eti, delay);
1105 	eti_check_cursor_bounds (eti);
1106 }
1107 
1108 static gboolean
1109 eti_idle_show_cursor_cb (gpointer data)
1110 {
1111 	ETableItem *eti = data;
1112 
1113 	if (eti->selection) {
1114 		eti_show_cursor (eti, 0);
1115 		eti_check_cursor_bounds (eti);
1116 	}
1117 
1118 	eti->cursor_idle_id = 0;
1119 	g_object_unref (eti);
1120 	return FALSE;
1121 }
1122 
1123 static void
1124 eti_idle_maybe_show_cursor (ETableItem *eti)
1125 {
1126 	d (g_print ("%s: cursor on screen: %s\n", __FUNCTION__, eti->cursor_on_screen ? "TRUE" : "FALSE"));
1127 	if (eti->cursor_on_screen) {
1128 		g_object_ref (eti);
1129 		if (!eti->cursor_idle_id)
1130 			eti->cursor_idle_id = g_idle_add (eti_idle_show_cursor_cb, eti);
1131 	}
1132 }
1133 
1134 static void
1135 eti_cancel_drag_due_to_model_change (ETableItem *eti)
1136 {
1137 	if (eti->maybe_in_drag) {
1138 		eti->maybe_in_drag = FALSE;
1139 		if (!eti->maybe_did_something)
1140 			e_selection_model_do_something (E_SELECTION_MODEL (eti->selection), eti->drag_row, eti->drag_col, eti->drag_state);
1141 	}
1142 	if (eti->in_drag) {
1143 		eti->in_drag = FALSE;
1144 	}
1145 }
1146 
1147 static void
1148 eti_freeze (ETableItem *eti)
1149 {
1150 	eti->frozen_count++;
1151 	d (g_print ("%s: %d\n", __FUNCTION__, eti->frozen_count));
1152 }
1153 
1154 static void
1155 eti_unfreeze (ETableItem *eti)
1156 {
1157 	if (eti->frozen_count <= 0)
1158 		return;
1159 
1160 	eti->frozen_count--;
1161 	d (g_print ("%s: %d\n", __FUNCTION__, eti->frozen_count));
1162 	if (eti->frozen_count == 0 && eti->queue_show_cursor) {
1163 		eti_show_cursor (eti, 0);
1164 		eti_check_cursor_bounds (eti);
1165 		eti->queue_show_cursor = FALSE;
1166 	}
1167 }
1168 
1169 /*
1170  * Callback routine: invoked before the ETableModel suffers a change
1171  */
1172 static void
1173 eti_table_model_pre_change (ETableModel *table_model,
1174                             ETableItem *eti)
1175 {
1176 	eti_cancel_drag_due_to_model_change (eti);
1177 	eti_check_cursor_bounds (eti);
1178 	if (eti_editing (eti))
1179 		e_table_item_leave_edit_(eti);
1180 	eti->motion_row = -1;
1181 	eti->motion_col = -1;
1182 	eti_freeze (eti);
1183 }
1184 
1185 /*
1186  * Callback routine: invoked when the ETableModel has not suffered a change
1187  */
1188 static void
1189 eti_table_model_no_change (ETableModel *table_model,
1190                            ETableItem *eti)
1191 {
1192 	eti_unfreeze (eti);
1193 }
1194 
1195 /*
1196  * Callback routine: invoked when the ETableModel has suffered a change
1197  */
1198 
1199 static void
1200 eti_table_model_changed (ETableModel *table_model,
1201                          ETableItem *eti)
1202 {
1203 	GnomeCanvasItem *item = GNOME_CANVAS_ITEM (eti);
1204 
1205 	if (!(item->flags & GNOME_CANVAS_ITEM_REALIZED)) {
1206 		eti_unfreeze (eti);
1207 		return;
1208 	}
1209 
1210 	eti->rows = e_table_model_row_count (eti->table_model);
1211 
1212 	free_height_cache (eti);
1213 
1214 	eti_unfreeze (eti);
1215 
1216 	eti->needs_compute_height = 1;
1217 	e_canvas_item_request_reflow (GNOME_CANVAS_ITEM (eti));
1218 	eti->needs_redraw = 1;
1219 	gnome_canvas_item_request_update (GNOME_CANVAS_ITEM (eti));
1220 
1221 	eti_idle_maybe_show_cursor (eti);
1222 }
1223 
1224 static void
1225 eti_table_model_row_changed (ETableModel *table_model,
1226                              gint row,
1227                              ETableItem *eti)
1228 {
1229 	GnomeCanvasItem *item = GNOME_CANVAS_ITEM (eti);
1230 
1231 	if (!(item->flags & GNOME_CANVAS_ITEM_REALIZED)) {
1232 		eti_unfreeze (eti);
1233 		return;
1234 	}
1235 
1236 	if ((!eti->uniform_row_height) && eti->height_cache && eti->height_cache[row] != -1 && eti_row_height_real (eti, row) != eti->height_cache[row]) {
1237 		eti_table_model_changed (table_model, eti);
1238 		return;
1239 	}
1240 
1241 	eti_unfreeze (eti);
1242 
1243 	e_table_item_redraw_row (eti, row);
1244 }
1245 
1246 static void
1247 eti_table_model_cell_changed (ETableModel *table_model,
1248                               gint col,
1249                               gint row,
1250                               ETableItem *eti)
1251 {
1252 	GnomeCanvasItem *item = GNOME_CANVAS_ITEM (eti);
1253 
1254 	if (!(item->flags & GNOME_CANVAS_ITEM_REALIZED)) {
1255 		eti_unfreeze (eti);
1256 		return;
1257 	}
1258 
1259 	if ((!eti->uniform_row_height) && eti->height_cache && eti->height_cache[row] != -1 && eti_row_height_real (eti, row) != eti->height_cache[row]) {
1260 		eti_table_model_changed (table_model, eti);
1261 		return;
1262 	}
1263 
1264 	eti_unfreeze (eti);
1265 
1266 	e_table_item_redraw_row (eti, row);
1267 }
1268 
1269 static void
1270 eti_table_model_rows_inserted (ETableModel *table_model,
1271                                gint row,
1272                                gint count,
1273                                ETableItem *eti)
1274 {
1275 	GnomeCanvasItem *item = GNOME_CANVAS_ITEM (eti);
1276 
1277 	if (!(item->flags & GNOME_CANVAS_ITEM_REALIZED)) {
1278 		eti_unfreeze (eti);
1279 		return;
1280 	}
1281 	eti->rows = e_table_model_row_count (eti->table_model);
1282 
1283 	if (eti->height_cache) {
1284 		gint i;
1285 		eti->height_cache = g_renew (int, eti->height_cache, eti->rows);
1286 		memmove (eti->height_cache + row + count, eti->height_cache + row, (eti->rows - count - row) * sizeof (gint));
1287 		for (i = row; i < row + count; i++)
1288 			eti->height_cache[i] = -1;
1289 	}
1290 
1291 	eti_unfreeze (eti);
1292 
1293 	eti_idle_maybe_show_cursor (eti);
1294 
1295 	eti->needs_compute_height = 1;
1296 	e_canvas_item_request_reflow (GNOME_CANVAS_ITEM (eti));
1297 	eti->needs_redraw = 1;
1298 	gnome_canvas_item_request_update (GNOME_CANVAS_ITEM (eti));
1299 }
1300 
1301 static void
1302 eti_table_model_rows_deleted (ETableModel *table_model,
1303                               gint row,
1304                               gint count,
1305                               ETableItem *eti)
1306 {
1307 	GnomeCanvasItem *item = GNOME_CANVAS_ITEM (eti);
1308 
1309 	if (!(item->flags & GNOME_CANVAS_ITEM_REALIZED)) {
1310 		eti_unfreeze (eti);
1311 		return;
1312 	}
1313 
1314 	eti->rows = e_table_model_row_count (eti->table_model);
1315 
1316 	if (eti->height_cache && (eti->rows > row)) {
1317 		memmove (eti->height_cache + row, eti->height_cache + row + count, (eti->rows - row) * sizeof (gint));
1318 	}
1319 
1320 	eti_unfreeze (eti);
1321 
1322 	eti_idle_maybe_show_cursor (eti);
1323 
1324 	eti->needs_compute_height = 1;
1325 	e_canvas_item_request_reflow (GNOME_CANVAS_ITEM (eti));
1326 	eti->needs_redraw = 1;
1327 	gnome_canvas_item_request_update (GNOME_CANVAS_ITEM (eti));
1328 }
1329 
1330 /**
1331  * e_table_item_redraw_range
1332  * @eti: %ETableItem which will be redrawn
1333  * @start_col: The first col to redraw.
1334  * @start_row: The first row to redraw.
1335  * @end_col: The last col to redraw.
1336  * @end_row: The last row to redraw.
1337  *
1338  * This routine redraws the given %ETableItem in the range given.  The
1339  * range is inclusive at both ends.
1340  */
1341 void
1342 e_table_item_redraw_range (ETableItem *eti,
1343                            gint start_col,
1344                            gint start_row,
1345                            gint end_col,
1346                            gint end_row)
1347 {
1348 	gint border;
1349 	gint cursor_col, cursor_row;
1350 
1351 	g_return_if_fail (eti != NULL);
1352 	g_return_if_fail (E_IS_TABLE_ITEM (eti));
1353 
1354 	g_object_get (
1355 		eti->selection,
1356 		"cursor_col", &cursor_col,
1357 		"cursor_row", &cursor_row,
1358 		NULL);
1359 
1360 	if ((start_col == cursor_col) ||
1361 	    (end_col   == cursor_col) ||
1362 	    (view_to_model_row (eti, start_row) == cursor_row) ||
1363 	    (view_to_model_row (eti, end_row)   == cursor_row))
1364 		border = 2;
1365 	else
1366 		border = 0;
1367 
1368 	eti_request_region_redraw (eti, start_col, start_row, end_col, end_row, border);
1369 }
1370 
1371 static void
1372 e_table_item_redraw_row (ETableItem *eti,
1373                          gint row)
1374 {
1375 	if (row != -1)
1376 		e_table_item_redraw_range (eti, 0, row, eti->cols - 1, row);
1377 }
1378 
1379 static void
1380 eti_add_table_model (ETableItem *eti,
1381                      ETableModel *table_model)
1382 {
1383 	g_return_if_fail (eti->table_model == NULL);
1384 
1385 	eti->table_model = table_model;
1386 	g_object_ref (eti->table_model);
1387 
1388 	eti->table_model_pre_change_id = g_signal_connect (
1389 		table_model, "model_pre_change",
1390 		G_CALLBACK (eti_table_model_pre_change), eti);
1391 
1392 	eti->table_model_no_change_id = g_signal_connect (
1393 		table_model, "model_no_change",
1394 		G_CALLBACK (eti_table_model_no_change), eti);
1395 
1396 	eti->table_model_change_id = g_signal_connect (
1397 		table_model, "model_changed",
1398 		G_CALLBACK (eti_table_model_changed), eti);
1399 
1400 	eti->table_model_row_change_id = g_signal_connect (
1401 		table_model, "model_row_changed",
1402 		G_CALLBACK (eti_table_model_row_changed), eti);
1403 
1404 	eti->table_model_cell_change_id = g_signal_connect (
1405 		table_model, "model_cell_changed",
1406 		G_CALLBACK (eti_table_model_cell_changed), eti);
1407 
1408 	eti->table_model_rows_inserted_id = g_signal_connect (
1409 		table_model, "model_rows_inserted",
1410 		G_CALLBACK (eti_table_model_rows_inserted), eti);
1411 
1412 	eti->table_model_rows_deleted_id = g_signal_connect (
1413 		table_model, "model_rows_deleted",
1414 		G_CALLBACK (eti_table_model_rows_deleted), eti);
1415 
1416 	if (eti->header) {
1417 		eti_detach_cell_views (eti);
1418 		eti_attach_cell_views (eti);
1419 	}
1420 
1421 	if (E_IS_TABLE_SUBSET (table_model)) {
1422 		eti->uses_source_model = 1;
1423 		eti->source_model = E_TABLE_SUBSET (table_model)->source;
1424 		if (eti->source_model)
1425 			g_object_ref (eti->source_model);
1426 	}
1427 
1428 	eti_freeze (eti);
1429 
1430 	eti_table_model_changed (table_model, eti);
1431 }
1432 
1433 static void
1434 eti_add_selection_model (ETableItem *eti,
1435                          ESelectionModel *selection)
1436 {
1437 	g_return_if_fail (eti->selection == NULL);
1438 
1439 	eti->selection = selection;
1440 	g_object_ref (eti->selection);
1441 
1442 	eti->selection_change_id = g_signal_connect (
1443 		selection, "selection_changed",
1444 		G_CALLBACK (eti_selection_change), eti);
1445 
1446 	eti->selection_row_change_id = g_signal_connect (
1447 		selection, "selection_row_changed",
1448 		G_CALLBACK (eti_selection_row_change), eti);
1449 
1450 	eti->cursor_change_id = g_signal_connect (
1451 		selection, "cursor_changed",
1452 		G_CALLBACK (eti_cursor_change), eti);
1453 
1454 	eti->cursor_activated_id = g_signal_connect (
1455 		selection, "cursor_activated",
1456 		G_CALLBACK (eti_cursor_activated), eti);
1457 
1458 	eti_selection_change (selection, eti);
1459 	g_signal_emit_by_name (eti, "selection_model_added", eti->selection);
1460 }
1461 
1462 static void
1463 eti_header_dim_changed (ETableHeader *eth,
1464                         gint col,
1465                         ETableItem *eti)
1466 {
1467 	eti->needs_compute_width = 1;
1468 	e_canvas_item_request_reflow (GNOME_CANVAS_ITEM (eti));
1469 	eti->needs_redraw = 1;
1470 	gnome_canvas_item_request_update (GNOME_CANVAS_ITEM (eti));
1471 }
1472 
1473 static void
1474 eti_header_structure_changed (ETableHeader *eth,
1475                               ETableItem *eti)
1476 {
1477 	eti->cols = e_table_header_count (eti->header);
1478 
1479 	/*
1480 	 * There should be at least one column
1481 	 *  BUT: then you can't remove all columns from a header and add new ones.
1482 	 */
1483 
1484 	if (eti->cell_views) {
1485 		eti_unrealize_cell_views (eti);
1486 		eti_detach_cell_views (eti);
1487 		eti_attach_cell_views (eti);
1488 		eti_realize_cell_views (eti);
1489 	} else {
1490 		if (eti->table_model) {
1491 			eti_attach_cell_views (eti);
1492 			eti_realize_cell_views (eti);
1493 		}
1494 	}
1495 	eti->needs_compute_width = 1;
1496 	e_canvas_item_request_reflow (GNOME_CANVAS_ITEM (eti));
1497 	eti->needs_redraw = 1;
1498 	gnome_canvas_item_request_update (GNOME_CANVAS_ITEM (eti));
1499 }
1500 
1501 static gint
1502 eti_request_column_width (ETableHeader *eth,
1503                           gint col,
1504                           ETableItem *eti)
1505 {
1506 	gint width = 0;
1507 
1508 	if (eti->cell_views && eti->cell_views_realized) {
1509 		width = e_cell_max_width (eti->cell_views[col], view_to_model_col (eti, col), col);
1510 	}
1511 
1512 	return width;
1513 }
1514 
1515 static void
1516 eti_add_header_model (ETableItem *eti,
1517                       ETableHeader *header)
1518 {
1519 	g_return_if_fail (eti->header == NULL);
1520 
1521 	eti->header = header;
1522 	g_object_ref (header);
1523 
1524 	eti_header_structure_changed (header, eti);
1525 
1526 	eti->header_dim_change_id = g_signal_connect (
1527 		header, "dimension_change",
1528 		G_CALLBACK (eti_header_dim_changed), eti);
1529 
1530 	eti->header_structure_change_id = g_signal_connect (
1531 		header, "structure_change",
1532 		G_CALLBACK (eti_header_structure_changed), eti);
1533 
1534 	eti->header_request_width_id = g_signal_connect (
1535 		header, "request_width",
1536 		G_CALLBACK (eti_request_column_width), eti);
1537 }
1538 
1539 /*
1540  * GObject::dispose method
1541  */
1542 static void
1543 eti_dispose (GObject *object)
1544 {
1545 	ETableItem *eti = E_TABLE_ITEM (object);
1546 
1547 	eti_remove_header_model (eti);
1548 	eti_remove_table_model (eti);
1549 	eti_remove_selection_model (eti);
1550 
1551 	if (eti->height_cache_idle_id) {
1552 		g_source_remove (eti->height_cache_idle_id);
1553 		eti->height_cache_idle_id = 0;
1554 	}
1555 	eti->height_cache_idle_count = 0;
1556 
1557 	if (eti->cursor_idle_id) {
1558 		g_source_remove (eti->cursor_idle_id);
1559 		eti->cursor_idle_id = 0;
1560 	}
1561 
1562 	if (eti->height_cache)
1563 		g_free (eti->height_cache);
1564 	eti->height_cache = NULL;
1565 
1566 	/* Chain up to parent's dispose() method. */
1567 	G_OBJECT_CLASS (eti_parent_class)->dispose (object);
1568 }
1569 
1570 static void
1571 eti_set_property (GObject *object,
1572                   guint property_id,
1573                   const GValue *value,
1574                   GParamSpec *pspec)
1575 {
1576 	GnomeCanvasItem *item = GNOME_CANVAS_ITEM (object);
1577 	ETableItem *eti = E_TABLE_ITEM (object);
1578 	gint cursor_col;
1579 
1580 	switch (property_id) {
1581 	case PROP_TABLE_HEADER:
1582 		eti_remove_header_model (eti);
1583 		eti_add_header_model (eti, E_TABLE_HEADER (g_value_get_object (value)));
1584 		break;
1585 
1586 	case PROP_TABLE_MODEL:
1587 		eti_remove_table_model (eti);
1588 		eti_add_table_model (eti, E_TABLE_MODEL (g_value_get_object (value)));
1589 		break;
1590 
1591 	case PROP_SELECTION_MODEL:
1592 		g_signal_emit_by_name (
1593 			eti, "selection_model_removed", eti->selection);
1594 		eti_remove_selection_model (eti);
1595 		if (g_value_get_object (value))
1596 			eti_add_selection_model (eti, E_SELECTION_MODEL (g_value_get_object (value)));
1597 		break;
1598 
1599 	case PROP_LENGTH_THRESHOLD:
1600 		eti->length_threshold = g_value_get_int (value);
1601 		break;
1602 
1603 	case PROP_TABLE_ALTERNATING_ROW_COLORS:
1604 		eti->alternating_row_colors = g_value_get_boolean (value);
1605 		break;
1606 
1607 	case PROP_TABLE_HORIZONTAL_DRAW_GRID:
1608 		eti->horizontal_draw_grid = g_value_get_boolean (value);
1609 		break;
1610 
1611 	case PROP_TABLE_VERTICAL_DRAW_GRID:
1612 		eti->vertical_draw_grid = g_value_get_boolean (value);
1613 		break;
1614 
1615 	case PROP_TABLE_DRAW_FOCUS:
1616 		eti->draw_focus = g_value_get_boolean (value);
1617 		break;
1618 
1619 	case PROP_CURSOR_MODE:
1620 		eti->cursor_mode = g_value_get_int (value);
1621 		break;
1622 
1623 	case PROP_MINIMUM_WIDTH:
1624 	case PROP_WIDTH:
1625 		if ((eti->minimum_width == eti->width && g_value_get_double (value) > eti->width) ||
1626 		    g_value_get_double (value) < eti->width) {
1627 			eti->needs_compute_width = 1;
1628 			e_canvas_item_request_reflow (item);
1629 		}
1630 		eti->minimum_width = g_value_get_double (value);
1631 		break;
1632 	case PROP_CURSOR_ROW:
1633 		g_object_get (
1634 			eti->selection,
1635 			"cursor_col", &cursor_col,
1636 			NULL);
1637 
1638 		e_table_item_focus (eti, cursor_col != -1 ? cursor_col : 0, view_to_model_row (eti, g_value_get_int (value)), 0);
1639 		break;
1640 	case PROP_UNIFORM_ROW_HEIGHT:
1641 		if (eti->uniform_row_height != g_value_get_boolean (value)) {
1642 			eti->uniform_row_height = g_value_get_boolean (value);
1643 			if (item->flags & GNOME_CANVAS_ITEM_REALIZED) {
1644 				free_height_cache (eti);
1645 				eti->needs_compute_height = 1;
1646 				e_canvas_item_request_reflow (item);
1647 				eti->needs_redraw = 1;
1648 				gnome_canvas_item_request_update (item);
1649 			}
1650 		}
1651 		break;
1652 	}
1653 	eti->needs_redraw = 1;
1654 	gnome_canvas_item_request_update (item);
1655 }
1656 
1657 static void
1658 eti_get_property (GObject *object,
1659                   guint property_id,
1660                   GValue *value,
1661                   GParamSpec *pspec)
1662 {
1663 	ETableItem *eti;
1664 	gint row;
1665 
1666 	eti = E_TABLE_ITEM (object);
1667 
1668 	switch (property_id) {
1669 	case PROP_WIDTH:
1670 		g_value_set_double (value, eti->width);
1671 		break;
1672 	case PROP_HEIGHT:
1673 		g_value_set_double (value, eti->height);
1674 		break;
1675 	case PROP_MINIMUM_WIDTH:
1676 		g_value_set_double (value, eti->minimum_width);
1677 		break;
1678 	case PROP_CURSOR_ROW:
1679 		g_object_get (
1680 			eti->selection,
1681 			"cursor_row", &row,
1682 			NULL);
1683 		g_value_set_int (value, model_to_view_row (eti, row));
1684 		break;
1685 	case PROP_UNIFORM_ROW_HEIGHT:
1686 		g_value_set_boolean (value, eti->uniform_row_height);
1687 		break;
1688 	default:
1689 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
1690 		break;
1691 	}
1692 }
1693 
1694 static void
1695 eti_init (ETableItem *eti)
1696 {
1697 	eti->motion_row	       = -1;
1698 	eti->motion_col	       = -1;
1699 	eti->editing_col               = -1;
1700 	eti->editing_row               = -1;
1701 	eti->height                    = 0;
1702 	eti->width                     = 0;
1703 	eti->minimum_width             = 0;
1704 
1705 	eti->save_col                  = -1;
1706 	eti->save_row                  = -1;
1707 	eti->save_state                = NULL;
1708 
1709 	eti->click_count               = 0;
1710 
1711 	eti->height_cache              = NULL;
1712 	eti->height_cache_idle_id      = 0;
1713 	eti->height_cache_idle_count   = 0;
1714 
1715 	eti->length_threshold          = -1;
1716 	eti->uniform_row_height        = FALSE;
1717 
1718 	eti->uses_source_model         = 0;
1719 	eti->source_model              = NULL;
1720 
1721 	eti->row_guess                 = -1;
1722 	eti->cursor_mode               = E_CURSOR_SIMPLE;
1723 
1724 	eti->selection_change_id       = 0;
1725 	eti->selection_row_change_id   = 0;
1726 	eti->cursor_change_id          = 0;
1727 	eti->cursor_activated_id       = 0;
1728 	eti->selection                 = NULL;
1729 
1730 	eti->old_cursor_row            = -1;
1731 
1732 	eti->needs_redraw              = 0;
1733 	eti->needs_compute_height      = 0;
1734 
1735 	eti->in_key_press              = 0;
1736 
1737 	eti->maybe_did_something       = TRUE;
1738 
1739 	eti->grabbed_count             = 0;
1740 	eti->gtk_grabbed               = 0;
1741 
1742 	eti->in_drag                   = 0;
1743 	eti->maybe_in_drag             = 0;
1744 	eti->grabbed                   = 0;
1745 
1746 	eti->grabbed_col               = -1;
1747 	eti->grabbed_row               = -1;
1748 
1749 	eti->cursor_on_screen          = FALSE;
1750 	eti->cursor_x1                 = -1;
1751 	eti->cursor_y1                 = -1;
1752 	eti->cursor_x2                 = -1;
1753 	eti->cursor_y2                 = -1;
1754 
1755 	eti->rows                      = -1;
1756 	eti->cols                      = -1;
1757 
1758 	eti->frozen_count              = 0;
1759 	eti->queue_show_cursor         = FALSE;
1760 
1761 	e_canvas_item_set_reflow_callback (GNOME_CANVAS_ITEM (eti), eti_reflow);
1762 }
1763 
1764 #define gray50_width 2
1765 #define gray50_height 2
1766 static const gchar gray50_bits[] = {
1767 	0x02, 0x01, };
1768 
1769 static gboolean
1770 eti_tree_unfreeze (GtkWidget *widget,
1771                    GdkEvent *event,
1772                    ETableItem *eti)
1773 {
1774 
1775 	if (widget)
1776 		g_object_set_data (G_OBJECT (widget), "freeze-cursor", NULL);
1777 
1778 	return FALSE;
1779 }
1780 
1781 static void
1782 eti_realize (GnomeCanvasItem *item)
1783 {
1784 	ETableItem *eti = E_TABLE_ITEM (item);
1785 
1786 	if (GNOME_CANVAS_ITEM_CLASS (eti_parent_class)->realize)
1787 		(*GNOME_CANVAS_ITEM_CLASS (eti_parent_class)->realize)(item);
1788 
1789 	eti->rows = e_table_model_row_count (eti->table_model);
1790 
1791 	g_signal_connect (
1792 		item->canvas, "scroll_event",
1793 		G_CALLBACK (eti_tree_unfreeze), eti);
1794 
1795 	if (eti->cell_views == NULL)
1796 		eti_attach_cell_views (eti);
1797 
1798 	eti_realize_cell_views (eti);
1799 
1800 	free_height_cache (eti);
1801 
1802 	if (item->canvas->focused_item == NULL && eti->selection) {
1803 		gint row;
1804 
1805 		row = e_selection_model_cursor_row (E_SELECTION_MODEL (eti->selection));
1806 		row = model_to_view_row (eti, row);
1807 		if (row != -1) {
1808 			e_canvas_item_grab_focus (item, FALSE);
1809 			eti_show_cursor (eti, 0);
1810 			eti_check_cursor_bounds (eti);
1811 		}
1812 	}
1813 
1814 	eti->needs_compute_height = 1;
1815 	eti->needs_compute_width = 1;
1816 	e_canvas_item_request_reflow (GNOME_CANVAS_ITEM (eti));
1817 	eti->needs_redraw = 1;
1818 	gnome_canvas_item_request_update (GNOME_CANVAS_ITEM (eti));
1819 }
1820 
1821 static void
1822 eti_unrealize (GnomeCanvasItem *item)
1823 {
1824 	ETableItem *eti = E_TABLE_ITEM (item);
1825 
1826 	if (eti->grabbed_count > 0) {
1827 		d (g_print ("%s: eti_ungrab\n", __FUNCTION__));
1828 		eti_ungrab (eti, -1);
1829 	}
1830 
1831 	if (eti_editing (eti))
1832 		e_table_item_leave_edit_(eti);
1833 
1834 	if (eti->height_cache_idle_id) {
1835 		g_source_remove (eti->height_cache_idle_id);
1836 		eti->height_cache_idle_id = 0;
1837 	}
1838 
1839 	if (eti->height_cache)
1840 		g_free (eti->height_cache);
1841 	eti->height_cache = NULL;
1842 	eti->height_cache_idle_count = 0;
1843 
1844 	eti_unrealize_cell_views (eti);
1845 
1846 	eti->height = 0;
1847 
1848 	if (GNOME_CANVAS_ITEM_CLASS (eti_parent_class)->unrealize)
1849 		(*GNOME_CANVAS_ITEM_CLASS (eti_parent_class)->unrealize)(item);
1850 }
1851 
1852 static void
1853 eti_draw_grid_line (ETableItem *eti,
1854                     cairo_t *cr,
1855                     GtkStyle *style,
1856                     gint x1,
1857                     gint y1,
1858                     gint x2,
1859                     gint y2)
1860 {
1861 	cairo_save (cr);
1862 
1863 	cairo_set_line_width (cr, 1.0);
1864 	gdk_cairo_set_source_color (cr, &style->dark[GTK_STATE_NORMAL]);
1865 
1866 	cairo_move_to (cr, x1 + 0.5, y1 + 0.5);
1867 	cairo_line_to (cr, x2 + 0.5, y2 + 0.5);
1868 	cairo_stroke (cr);
1869 
1870 	cairo_restore (cr);
1871 }
1872 
1873 static void
1874 eti_draw (GnomeCanvasItem *item,
1875           cairo_t *cr,
1876           gint x,
1877           gint y,
1878           gint width,
1879           gint height)
1880 {
1881 	ETableItem *eti = E_TABLE_ITEM (item);
1882 	const gint rows = eti->rows;
1883 	const gint cols = eti->cols;
1884 	gint row, col;
1885 	gint first_col, last_col, x_offset;
1886 	gint first_row, last_row, y_offset, yd;
1887 	gint x1, x2;
1888 	gint f_x1, f_x2, f_y1, f_y2;
1889 	gboolean f_found;
1890 	cairo_matrix_t i2c;
1891 	gdouble eti_base_x, eti_base_y, lower_right_y, lower_right_x;
1892 	GtkWidget *canvas = GTK_WIDGET (item->canvas);
1893 	GtkStyle *style = gtk_widget_get_style (canvas);
1894 	gint height_extra = eti->horizontal_draw_grid ? 1 : 0;
1895 
1896 	/*
1897 	 * Find out our real position after grouping
1898 	 */
1899 	gnome_canvas_item_i2c_matrix (item, &i2c);
1900 	eti_base_x = 0;
1901 	eti_base_y = 0;
1902 	cairo_matrix_transform_point (&i2c, &eti_base_x, &eti_base_y);
1903 
1904 	lower_right_x = eti->width;
1905 	lower_right_y = eti->height;
1906 	cairo_matrix_transform_point (&i2c, &lower_right_x, &lower_right_y);
1907 
1908 	/*
1909 	 * First column to draw, last column to draw
1910 	 */
1911 	first_col = -1;
1912 	x_offset = 0;
1913 	x1 = floor (eti_base_x);
1914 	for (col = 0; col < cols; col++, x1 = x2) {
1915 		ETableCol *ecol = e_table_header_get_column (eti->header, col);
1916 
1917 		x2 = x1 + ecol->width;
1918 
1919 		if (x1 > (x + width))
1920 			break;
1921 		if (x2 < x)
1922 			continue;
1923 		if (first_col == -1) {
1924 			x_offset = x1 - x;
1925 			first_col = col;
1926 		}
1927 	}
1928 	last_col = col;
1929 
1930 	/*
1931 	 * Nothing to paint
1932 	 */
1933 	if (first_col == -1)
1934 		return;
1935 
1936 	/*
1937 	 * Compute row span.
1938 	 */
1939 	if (eti->uniform_row_height) {
1940 		first_row = (y          - floor (eti_base_y) - height_extra) / (ETI_ROW_HEIGHT (eti, -1) + height_extra);
1941 		last_row  = (y + height - floor (eti_base_y)               ) / (ETI_ROW_HEIGHT (eti, -1) + height_extra) + 1;
1942 		if (first_row > last_row)
1943 			return;
1944 		y_offset = floor (eti_base_y) - y + height_extra + first_row * (ETI_ROW_HEIGHT (eti, -1) + height_extra);
1945 		if (first_row < 0)
1946 			first_row = 0;
1947 		if (last_row > eti->rows)
1948 			last_row = eti->rows;
1949 	} else {
1950 		gint y1, y2;
1951 
1952 		y_offset = 0;
1953 		first_row = -1;
1954 
1955 		y1 = y2 = floor (eti_base_y) + height_extra;
1956 		for (row = 0; row < rows; row++, y1 = y2) {
1957 
1958 			y2 += ETI_ROW_HEIGHT (eti, row) + height_extra;
1959 
1960 			if (y1 > y + height)
1961 				break;
1962 
1963 			if (y2 < y)
1964 				continue;
1965 
1966 			if (first_row == -1) {
1967 				y_offset = y1 - y;
1968 				first_row = row;
1969 			}
1970 		}
1971 		last_row = row;
1972 
1973 		if (first_row == -1)
1974 			return;
1975 	}
1976 
1977 	if (first_row == -1)
1978 		return;
1979 
1980 	/*
1981 	 * Draw cells
1982 	 */
1983 	yd = y_offset;
1984 	f_x1 = f_x2 = f_y1 = f_y2 = -1;
1985 	f_found = FALSE;
1986 
1987 	if (eti->horizontal_draw_grid && first_row == 0)
1988 		eti_draw_grid_line (eti, cr, style, eti_base_x - x, yd, eti_base_x + eti->width - x, yd);
1989 
1990 	yd += height_extra;
1991 
1992 	for (row = first_row; row < last_row; row++) {
1993 		gint xd;
1994 		gboolean selected;
1995 		gint cursor_col, cursor_row;
1996 
1997 		height = ETI_ROW_HEIGHT (eti, row);
1998 
1999 		xd = x_offset;
2000 
2001 		selected = e_selection_model_is_row_selected (E_SELECTION_MODEL (eti->selection), view_to_model_row (eti,row));
2002 
2003 		g_object_get (
2004 			eti->selection,
2005 			"cursor_col", &cursor_col,
2006 			"cursor_row", &cursor_row,
2007 			NULL);
2008 
2009 		for (col = first_col; col < last_col; col++) {
2010 			ETableCol *ecol = e_table_header_get_column (eti->header, col);
2011 			ECellView *ecell_view = eti->cell_views[col];
2012 			gboolean col_selected = selected;
2013 			gboolean cursor = FALSE;
2014 			ECellFlags flags;
2015 			gboolean free_background;
2016 			GdkColor *background;
2017 			gint x1, x2, y1, y2;
2018 			cairo_pattern_t *pat;
2019 
2020 			switch (eti->cursor_mode) {
2021 			case E_CURSOR_SIMPLE:
2022 			case E_CURSOR_SPREADSHEET:
2023 				if (cursor_col == ecol->col_idx && cursor_row == view_to_model_row (eti, row)) {
2024 					col_selected = !col_selected;
2025 					cursor = TRUE;
2026 				}
2027 				break;
2028 			case E_CURSOR_LINE:
2029 				/* Nothing */
2030 				break;
2031 			}
2032 
2033 			x1 = xd;
2034 			y1 = yd + 1;
2035 			x2 = x1 + ecol->width;
2036 			y2 = yd + height;
2037 
2038 			background = eti_get_cell_background_color (eti, row, col, col_selected, &free_background);
2039 
2040 			cairo_save (cr);
2041 			pat = cairo_pattern_create_linear (0, y1, 0, y2);
2042 			cairo_pattern_add_color_stop_rgba (
2043 				pat, 0.0, background->red / 65535.0 ,
2044 				background->green / 65535.0,
2045 				background->blue / 65535.0, selected ? 0.8: 1.0);
2046 			if (selected)
2047 				cairo_pattern_add_color_stop_rgba (
2048 					pat, 0.5, background->red / 65535.0 ,
2049 					background->green / 65535.0,
2050 					background->blue / 65535.0, 0.9);
2051 
2052 			cairo_pattern_add_color_stop_rgba (
2053 				pat, 1, background->red / 65535.0 ,
2054 				background->green / 65535.0,
2055 				background->blue / 65535.0, selected ? 0.8 : 1.0);
2056 			cairo_rectangle (cr, x1, y1, ecol->width, height - 1);
2057 			cairo_set_source (cr, pat);
2058 			cairo_fill_preserve (cr);
2059 			cairo_pattern_destroy (pat);
2060 			cairo_set_line_width (cr, 0);
2061 			cairo_stroke (cr);
2062 			cairo_restore (cr);
2063 
2064 			cairo_save (cr);
2065 			cairo_set_line_width (cr, 1.0);
2066 			cairo_set_source_rgba (
2067 				cr, background->red / 65535.0 ,
2068 				background->green / 65535.0,
2069 				background->blue / 65535.0, 1);
2070 			cairo_move_to (cr, x1, y1);
2071 			cairo_line_to (cr, x2, y1);
2072 			cairo_stroke (cr);
2073 
2074 			cairo_set_line_width (cr, 1.0);
2075 			cairo_set_source_rgba (
2076 				cr, background->red / 65535.0 ,
2077 				background->green / 65535.0,
2078 				background->blue / 65535.0, 1);
2079 			cairo_move_to (cr, x1, y2);
2080 			cairo_line_to (cr, x2, y2);
2081 			cairo_stroke (cr);
2082 			cairo_restore (cr);
2083 
2084 			if (free_background)
2085 				gdk_color_free (background);
2086 
2087 			flags = col_selected ? E_CELL_SELECTED : 0;
2088 			flags |= gtk_widget_has_focus (canvas) ? E_CELL_FOCUSED : 0;
2089 			flags |= cursor ? E_CELL_CURSOR : 0;
2090 
2091 			switch (ecol->justification) {
2092 			case GTK_JUSTIFY_LEFT:
2093 				flags |= E_CELL_JUSTIFY_LEFT;
2094 				break;
2095 			case GTK_JUSTIFY_RIGHT:
2096 				flags |= E_CELL_JUSTIFY_RIGHT;
2097 				break;
2098 			case GTK_JUSTIFY_CENTER:
2099 				flags |= E_CELL_JUSTIFY_CENTER;
2100 				break;
2101 			case GTK_JUSTIFY_FILL:
2102 				flags |= E_CELL_JUSTIFY_FILL;
2103 				break;
2104 			}
2105 
2106 			e_cell_draw (
2107 				ecell_view, cr, ecol->col_idx, col, row, flags,
2108 				xd, yd, xd + ecol->width, yd + height);
2109 
2110 			if (!f_found && !selected) {
2111 				switch (eti->cursor_mode) {
2112 				case E_CURSOR_LINE:
2113 					if (view_to_model_row (eti, row) == cursor_row) {
2114 						f_x1 = floor (eti_base_x) - x;
2115 						f_x2 = floor (lower_right_x) - x;
2116 						f_y1 = yd + 1;
2117 						f_y2 = yd + height;
2118 						f_found = TRUE;
2119 					}
2120 					break;
2121 				case E_CURSOR_SIMPLE:
2122 				case E_CURSOR_SPREADSHEET:
2123 					if (view_to_model_col (eti, col) == cursor_col && view_to_model_row (eti, row) == cursor_row) {
2124 						f_x1 = xd;
2125 						f_x2 = xd + ecol->width;
2126 						f_y1 = yd;
2127 						f_y2 = yd + height;
2128 						f_found = TRUE;
2129 					}
2130 					break;
2131 				}
2132 			}
2133 
2134 			xd += ecol->width;
2135 		}
2136 		yd += height;
2137 
2138 		if (eti->horizontal_draw_grid) {
2139 			eti_draw_grid_line (eti, cr, style, eti_base_x - x, yd, eti_base_x + eti->width - x, yd);
2140 			yd++;
2141 		}
2142 	}
2143 
2144 	if (eti->vertical_draw_grid) {
2145 		gint xd = x_offset;
2146 
2147 		for (col = first_col; col <= last_col; col++) {
2148 			ETableCol *ecol = e_table_header_get_column (eti->header, col);
2149 
2150 			eti_draw_grid_line (eti, cr, style, xd, y_offset, xd, yd - 1);
2151 
2152 			/*
2153 			 * This looks wierd, but it is to draw the last line
2154 			 */
2155 			if (ecol)
2156 				xd += ecol->width;
2157 		}
2158 	}
2159 
2160 	/*
2161 	 * Draw focus
2162 	 */
2163 	if (eti->draw_focus && f_found) {
2164 		static const double dash[] = { 1.0, 1.0 };
2165 		cairo_set_line_width (cr, 1.0);
2166 		cairo_rectangle (
2167 			cr,
2168 			f_x1 + 0.5, f_x2 + 0.5,
2169 			f_x2 - f_x1 - 1, f_y2 - f_y1);
2170 
2171 		gdk_cairo_set_source_color (cr, &style->bg[GTK_STATE_NORMAL]);
2172 		cairo_stroke_preserve (cr);
2173 
2174 		cairo_set_dash (cr, dash, G_N_ELEMENTS (dash), 0.0);
2175 		gdk_cairo_set_source_color (cr, &style->fg[GTK_STATE_NORMAL]);
2176 		cairo_stroke (cr);
2177 	}
2178 }
2179 
2180 static GnomeCanvasItem *
2181 eti_point (GnomeCanvasItem *item,
2182            gdouble x,
2183            gdouble y,
2184            gint cx,
2185            gint cy)
2186 {
2187 	return item;
2188 }
2189 
2190 static gboolean
2191 find_cell (ETableItem *eti,
2192            gdouble x,
2193            gdouble y,
2194            gint *view_col_res,
2195            gint *view_row_res,
2196            gdouble *x1_res,
2197            gdouble *y1_res)
2198 {
2199 	const gint cols = eti->cols;
2200 	const gint rows = eti->rows;
2201 	gdouble x1, y1, x2, y2;
2202 	gint col, row;
2203 
2204 	gint height_extra = eti->horizontal_draw_grid ? 1 : 0;
2205 
2206 	/* FIXME: this routine is inneficient, fix later */
2207 
2208 	if (eti->grabbed_col >= 0 && eti->grabbed_row >= 0) {
2209 		*view_col_res = eti->grabbed_col;
2210 		*view_row_res = eti->grabbed_row;
2211 		*x1_res = x - e_table_header_col_diff (eti->header, 0, eti->grabbed_col);
2212 		*y1_res = y - e_table_item_row_diff (eti, 0, eti->grabbed_row);
2213 		return TRUE;
2214 	}
2215 
2216 	if (cols == 0 || rows == 0)
2217 		return FALSE;
2218 
2219 	x1 = 0;
2220 	for (col = 0; col < cols - 1; col++, x1 = x2) {
2221 		ETableCol *ecol = e_table_header_get_column (eti->header, col);
2222 
2223 		if (x < x1)
2224 			return FALSE;
2225 
2226 		x2 = x1 + ecol->width;
2227 
2228 		if (x <= x2)
2229 			break;
2230 	}
2231 
2232 	if (eti->uniform_row_height) {
2233 		if (y < height_extra)
2234 			return FALSE;
2235 		row = (y - height_extra) / (ETI_ROW_HEIGHT (eti, -1) + height_extra);
2236 		y1 = row * (ETI_ROW_HEIGHT (eti, -1) + height_extra) + height_extra;
2237 		if (row >= eti->rows)
2238 			return FALSE;
2239 	} else {
2240 		y1 = y2 = height_extra;
2241 		if (y < height_extra)
2242 			return FALSE;
2243 		for (row = 0; row < rows; row++, y1 = y2) {
2244 			y2 += ETI_ROW_HEIGHT (eti, row) + height_extra;
2245 
2246 			if (y <= y2)
2247 				break;
2248 		}
2249 
2250 		if (row == rows)
2251 			return FALSE;
2252 	}
2253 	*view_col_res = col;
2254 	if (x1_res)
2255 		*x1_res = x - x1;
2256 	*view_row_res = row;
2257 	if (y1_res)
2258 		*y1_res = y - y1;
2259 	return TRUE;
2260 }
2261 
2262 static void
2263 eti_cursor_move (ETableItem *eti,
2264                  gint row,
2265                  gint column)
2266 {
2267 	e_table_item_leave_edit_(eti);
2268 	e_table_item_focus (eti, view_to_model_col (eti, column), view_to_model_row (eti, row), 0);
2269 }
2270 
2271 static void
2272 eti_cursor_move_left (ETableItem *eti)
2273 {
2274 	gint cursor_col, cursor_row;
2275 	g_object_get (
2276 		eti->selection,
2277 		"cursor_col", &cursor_col,
2278 		"cursor_row", &cursor_row,
2279 		NULL);
2280 
2281 	eti_cursor_move (eti, model_to_view_row (eti, cursor_row), model_to_view_col (eti, cursor_col) - 1);
2282 }
2283 
2284 static void
2285 eti_cursor_move_right (ETableItem *eti)
2286 {
2287 	gint cursor_col, cursor_row;
2288 	g_object_get (
2289 		eti->selection,
2290 		"cursor_col", &cursor_col,
2291 		"cursor_row", &cursor_row,
2292 		NULL);
2293 
2294 	eti_cursor_move (eti, model_to_view_row (eti, cursor_row), model_to_view_col (eti, cursor_col) + 1);
2295 }
2296 
2297 static gint
2298 eti_e_cell_event (ETableItem *item,
2299                   ECellView *ecell_view,
2300                   GdkEvent *event,
2301                   gint time,
2302                   gint model_col,
2303                   gint view_col,
2304                   gint row,
2305                   ECellFlags flags)
2306 {
2307 	ECellActions actions = 0;
2308 	gint ret_val;
2309 
2310 	ret_val = e_cell_event (ecell_view, event, model_col, view_col, row, flags, &actions);
2311 
2312 	if (actions & E_CELL_GRAB) {
2313 		d (g_print ("%s: eti_grab\n", __FUNCTION__));
2314 		eti_grab (item, time);
2315 		item->grabbed_col = view_col;
2316 		item->grabbed_row = row;
2317 	}
2318 
2319 	if (actions & E_CELL_UNGRAB) {
2320 		d (g_print ("%s: eti_ungrab\n", __FUNCTION__));
2321 		eti_ungrab (item, time);
2322 		item->grabbed_col = -1;
2323 		item->grabbed_row = -1;
2324 	}
2325 
2326 	return ret_val;
2327 }
2328 
2329 /* FIXME: cursor */
2330 static gint
2331 eti_event (GnomeCanvasItem *item,
2332            GdkEvent *e)
2333 {
2334 	ETableItem *eti = E_TABLE_ITEM (item);
2335 	ECellView *ecell_view;
2336 	gboolean return_val = TRUE;
2337 #if d(!)0
2338 	gboolean leave = FALSE;
2339 #endif
2340 
2341 	if (!eti->header)
2342 		return FALSE;
2343 
2344 	switch (e->type) {
2345 	case GDK_BUTTON_PRESS: {
2346 		gdouble x1, y1;
2347 		gdouble realx, realy;
2348 		GdkEventButton button;
2349 		gint col, row;
2350 		gint cursor_row, cursor_col;
2351 		gint new_cursor_row, new_cursor_col;
2352 		ECellFlags flags = 0;
2353 
2354 		d (g_print ("%s: GDK_BUTTON_PRESS received, button %d\n", __FUNCTION__, e->button.button));
2355 
2356 		switch (e->button.button) {
2357 		case 1: /* Fall through. */
2358 		case 2:
2359 			e_canvas_item_grab_focus (GNOME_CANVAS_ITEM (eti), TRUE);
2360 			gnome_canvas_item_w2i (item, &e->button.x, &e->button.y);
2361 
2362 			realx = e->button.x;
2363 			realy = e->button.y;
2364 
2365 			if (!find_cell (eti, realx, realy, &col, &row, &x1, &y1)) {
2366 				if (eti_editing (eti))
2367 					e_table_item_leave_edit_(eti);
2368 				return TRUE;
2369 			}
2370 
2371 			ecell_view = eti->cell_views[col];
2372 			button = *(GdkEventButton *) e;
2373 			button.x = x1;
2374 			button.y = y1;
2375 
2376 			g_object_get (
2377 				eti->selection,
2378 				"cursor_row", &cursor_row,
2379 				"cursor_col", &cursor_col,
2380 				NULL);
2381 
2382 			if (cursor_col == view_to_model_col (eti, col) && cursor_row == view_to_model_row (eti, row)) {
2383 				flags = E_CELL_CURSOR;
2384 			} else {
2385 				flags = 0;
2386 			}
2387 
2388 			return_val = eti_e_cell_event (eti, ecell_view, (GdkEvent *) &button, button.time, view_to_model_col (eti, col), col, row, flags);
2389 			if (return_val)
2390 				return TRUE;
2391 
2392 			g_signal_emit (
2393 				eti, eti_signals[CLICK], 0,
2394 				row, view_to_model_col (eti, col), &button, &return_val);
2395 
2396 			if (return_val) {
2397 				eti->click_count = 0;
2398 				return TRUE;
2399 			}
2400 
2401 			g_object_get (
2402 				eti->selection,
2403 				"cursor_row", &cursor_row,
2404 				"cursor_col", &cursor_col,
2405 				NULL);
2406 
2407 			eti->maybe_did_something =
2408 				e_selection_model_maybe_do_something (E_SELECTION_MODEL (eti->selection), view_to_model_row (eti, row), view_to_model_col (eti, col), button.state);
2409 			g_object_get (
2410 				eti->selection,
2411 				"cursor_row", &new_cursor_row,
2412 				"cursor_col", &new_cursor_col,
2413 				NULL);
2414 
2415 			if (cursor_row != new_cursor_row || cursor_col != new_cursor_col) {
2416 				eti->click_count = 1;
2417 			} else {
2418 				eti->click_count++;
2419 				eti->row_guess = row;
2420 
2421 				if ((!eti_editing (eti)) && e_table_model_is_cell_editable (eti->table_model, cursor_col, row)) {
2422 					e_table_item_enter_edit (eti, col, row);
2423 				}
2424 
2425 				/*
2426 				 * Adjust the event positions
2427 				 */
2428 
2429 				if (eti_editing (eti)) {
2430 					return_val = eti_e_cell_event (
2431 						eti, ecell_view, (GdkEvent *) &button, button.time,
2432 						view_to_model_col (eti, col), col, row, E_CELL_EDITING | E_CELL_CURSOR);
2433 					if (return_val)
2434 						return TRUE;
2435 				}
2436 			}
2437 
2438 			if (e->button.button == 1) {
2439 				return_val = TRUE;
2440 
2441 				eti->maybe_in_drag = TRUE;
2442 				eti->drag_row      = new_cursor_row;
2443 				eti->drag_col      = new_cursor_col;
2444 				eti->drag_x        = realx;
2445 				eti->drag_y        = realy;
2446 				eti->drag_state    = e->button.state;
2447 				eti->grabbed       = TRUE;
2448 				d (g_print ("%s: eti_grab\n", __FUNCTION__));
2449 				eti_grab (eti, e->button.time);
2450 			}
2451 
2452 			break;
2453 		case 3:
2454 			e_canvas_item_grab_focus (GNOME_CANVAS_ITEM (eti), TRUE);
2455 			gnome_canvas_item_w2i (item, &e->button.x, &e->button.y);
2456 			if (!find_cell (eti, e->button.x, e->button.y, &col, &row, &x1, &y1))
2457 				return TRUE;
2458 
2459 			e_selection_model_right_click_down (E_SELECTION_MODEL (eti->selection), view_to_model_row (eti, row), view_to_model_col (eti, col), 0);
2460 
2461 			g_signal_emit (
2462 				eti, eti_signals[RIGHT_CLICK], 0,
2463 				row, view_to_model_col (eti, col), e, &return_val);
2464 			if (!return_val)
2465 				e_selection_model_right_click_up (E_SELECTION_MODEL (eti->selection));
2466 			break;
2467 		case 4:
2468 		case 5:
2469 			return FALSE;
2470 
2471 		}
2472 		break;
2473 	}
2474 
2475 	case GDK_BUTTON_RELEASE: {
2476 		gdouble x1, y1;
2477 		gint col, row;
2478 		gint cursor_row, cursor_col;
2479 
2480 		d (g_print ("%s: GDK_BUTTON_RELEASE received, button %d\n", __FUNCTION__, e->button.button));
2481 
2482 		if (eti->grabbed_count > 0) {
2483 			d (g_print ("%s: eti_ungrab\n", __FUNCTION__));
2484 			eti_ungrab (eti, e->button.time);
2485 		}
2486 
2487 		if (e->button.button == 1) {
2488 			if (eti->maybe_in_drag) {
2489 				eti->maybe_in_drag = FALSE;
2490 				if (!eti->maybe_did_something)
2491 					e_selection_model_do_something (E_SELECTION_MODEL (eti->selection), eti->drag_row, eti->drag_col, eti->drag_state);
2492 			}
2493 			if (eti->in_drag) {
2494 				eti->in_drag = FALSE;
2495 			}
2496 		}
2497 
2498 		switch (e->button.button) {
2499 		case 1: /* Fall through. */
2500 		case 2:
2501 
2502 			gnome_canvas_item_w2i (item, &e->button.x, &e->button.y);
2503 #if d(!)0
2504 			{
2505 				gboolean cell_found = find_cell (eti, e->button.x, e->button.y, &col, &row, &x1, &y1);
2506 				g_print (
2507 					"%s: find_cell(%f, %f) = %s(%d, %d, %f, %f)\n", __FUNCTION__, e->button.x, e->button.y,
2508 					cell_found?"true":"false", col, row, x1, y1);
2509 			}
2510 #endif
2511 
2512 			if (!find_cell (eti, e->button.x, e->button.y, &col, &row, &x1, &y1))
2513 				return TRUE;
2514 
2515 			g_object_get (
2516 				eti->selection,
2517 				"cursor_row", &cursor_row,
2518 				"cursor_col", &cursor_col,
2519 				NULL);
2520 
2521 			if (eti_editing (eti) && cursor_row == view_to_model_row (eti, row) && cursor_col == view_to_model_col (eti, col)) {
2522 
2523 				d (g_print ("%s: GDK_BUTTON_RELEASE received, button %d, line: %d\n", __FUNCTION__, e->button.button, __LINE__))
2524 ;
2525 
2526 				ecell_view = eti->cell_views[col];
2527 
2528 				/*
2529 				 * Adjust the event positions
2530 				 */
2531 				e->button.x = x1;
2532 				e->button.y = y1;
2533 
2534 				return_val = eti_e_cell_event (
2535 					eti, ecell_view, e, e->button.time,
2536 					view_to_model_col (eti, col), col, row, E_CELL_EDITING | E_CELL_CURSOR);
2537 			}
2538 			break;
2539 		case 3:
2540 			e_selection_model_right_click_up (E_SELECTION_MODEL (eti->selection));
2541 			return_val = TRUE;
2542 			break;
2543 		case 4:
2544 		case 5:
2545 			return FALSE;
2546 
2547 		}
2548 		break;
2549 	}
2550 
2551 	case GDK_2BUTTON_PRESS: {
2552 		gint model_col, model_row;
2553 #if 0
2554 		gdouble x1, y1;
2555 #endif
2556 
2557 		d (g_print ("%s: GDK_2BUTTON_PRESS received, button %d\n", __FUNCTION__, e->button.button));
2558 
2559 		/*
2560 		 * click_count is so that if you click on two
2561 		 * different rows we don't send a double click signal.
2562 		 */
2563 
2564 		if (eti->click_count >= 2) {
2565 
2566 			gnome_canvas_item_w2i (item, &e->button.x, &e->button.y);
2567 
2568 #if 0
2569 			if (!find_cell (eti, e->button.x, e->button.y, &current_col, &current_row, &x1, &y1))
2570 				return TRUE;
2571 #endif
2572 
2573 			g_object_get (
2574 				eti->selection,
2575 				"cursor_row", &model_row,
2576 				"cursor_col", &model_col,
2577 				NULL);
2578 
2579 			e->button.x -= e_table_header_col_diff (eti->header, 0, model_to_view_col (eti, model_col));
2580 			e->button.y -= e_table_item_row_diff (eti, 0, model_to_view_row (eti, model_row));
2581 
2582 			if (e->button.button == 1) {
2583 				if (eti->maybe_in_drag) {
2584 					eti->maybe_in_drag = FALSE;
2585 					if (!eti->maybe_did_something)
2586 						e_selection_model_do_something (E_SELECTION_MODEL (eti->selection), eti->drag_row, eti->drag_col, eti->drag_state);
2587 				}
2588 				if (eti->in_drag) {
2589 					eti->in_drag = FALSE;
2590 				}
2591 				if (eti_editing (eti))
2592 					e_table_item_leave_edit_ (eti);
2593 
2594 			}
2595 
2596 			if (eti->grabbed_count > 0) {
2597 				d (g_print ("%s: eti_ungrab\n", __FUNCTION__));
2598 				eti_ungrab (eti, e->button.time);
2599 			}
2600 
2601 			if (model_row != -1 && model_col != -1) {
2602 				g_signal_emit (
2603 					eti, eti_signals[DOUBLE_CLICK], 0,
2604 					model_row, model_col, e);
2605 			}
2606 		}
2607 		break;
2608 	}
2609 	case GDK_MOTION_NOTIFY: {
2610 		gint col, row, flags;
2611 		gdouble x1, y1;
2612 		gint cursor_col, cursor_row;
2613 
2614 		gnome_canvas_item_w2i (item, &e->motion.x, &e->motion.y);
2615 
2616 		if (eti->maybe_in_drag) {
2617 			if (abs (e->motion.x - eti->drag_x) >= 3 ||
2618 			    abs (e->motion.y - eti->drag_y) >= 3) {
2619 				gboolean drag_handled;
2620 
2621 				eti->maybe_in_drag = 0;
2622 				g_signal_emit (
2623 					eti, eti_signals[START_DRAG], 0,
2624 					eti->drag_row, eti->drag_col, e, &drag_handled);
2625 				if (drag_handled)
2626 					eti->in_drag = 1;
2627 				else
2628 					eti->in_drag = 0;
2629 			}
2630 		}
2631 
2632 		if (!find_cell (eti, e->motion.x, e->motion.y, &col, &row, &x1, &y1))
2633 			return TRUE;
2634 
2635 		if (eti->motion_row != -1 && eti->motion_col != -1 &&
2636 		    (row != eti->motion_row || col != eti->motion_col)) {
2637 			GdkEvent *cross = gdk_event_new (GDK_LEAVE_NOTIFY);
2638 			cross->crossing.time = e->motion.time;
2639 			return_val = eti_e_cell_event (
2640 				eti, eti->cell_views[eti->motion_col],
2641 				cross, cross->crossing.time,
2642 				view_to_model_col (eti, eti->motion_col),
2643 				eti->motion_col, eti->motion_row, 0);
2644 		}
2645 
2646 		eti->motion_row = row;
2647 		eti->motion_col = col;
2648 
2649 		g_object_get (
2650 			eti->selection,
2651 			"cursor_row", &cursor_row,
2652 			"cursor_col", &cursor_col,
2653 			NULL);
2654 
2655 		flags = 0;
2656 		if (cursor_row == view_to_model_row (eti, row) && cursor_col == view_to_model_col (eti, col)) {
2657 			flags = E_CELL_EDITING | E_CELL_CURSOR;
2658 		}
2659 
2660 		ecell_view = eti->cell_views[col];
2661 
2662 		/*
2663 		 * Adjust the event positions
2664 		 */
2665 		e->motion.x = x1;
2666 		e->motion.y = y1;
2667 
2668 		return_val = eti_e_cell_event (
2669 			eti, ecell_view, e, e->motion.time,
2670 			view_to_model_col (eti, col), col, row, flags);
2671 		break;
2672 	}
2673 
2674 	case GDK_KEY_PRESS: {
2675 		gint cursor_row, cursor_col;
2676 		gint handled = TRUE;
2677 
2678 		d (g_print ("%s: GDK_KEY_PRESS received, keyval: %d\n", __FUNCTION__, (gint) e->key.keyval));
2679 
2680 		g_object_get (
2681 			eti->selection,
2682 			"cursor_row", &cursor_row,
2683 			"cursor_col", &cursor_col,
2684 			NULL);
2685 
2686 		if (cursor_row == -1 && cursor_col == -1)
2687 			return FALSE;
2688 
2689 		eti->in_key_press = TRUE;
2690 
2691 		switch (e->key.keyval) {
2692 		case GDK_KEY_Left:
2693 		case GDK_KEY_KP_Left:
2694 			if (eti_editing (eti)) {
2695 				handled = FALSE;
2696 				break;
2697 			}
2698 
2699 			g_signal_emit (
2700 				eti, eti_signals[KEY_PRESS], 0,
2701 				model_to_view_row (eti, cursor_row), cursor_col, e, &return_val);
2702 			if ((!return_val) &&
2703 			   (atk_get_root () || eti->cursor_mode != E_CURSOR_LINE) &&
2704 			   cursor_col != view_to_model_col (eti, 0))
2705 				eti_cursor_move_left (eti);
2706 			return_val = 1;
2707 			break;
2708 
2709 		case GDK_KEY_Right:
2710 		case GDK_KEY_KP_Right:
2711 			if (eti_editing (eti)) {
2712 				handled = FALSE;
2713 				break;
2714 			}
2715 
2716 			g_signal_emit (
2717 				eti, eti_signals[KEY_PRESS], 0,
2718 				model_to_view_row (eti, cursor_row), cursor_col, e, &return_val);
2719 			if ((!return_val) &&
2720 			   (atk_get_root () || eti->cursor_mode != E_CURSOR_LINE) &&
2721 			   cursor_col != view_to_model_col (eti, eti->cols - 1))
2722 				eti_cursor_move_right (eti);
2723 			return_val = 1;
2724 			break;
2725 
2726 		case GDK_KEY_Up:
2727 		case GDK_KEY_KP_Up:
2728 		case GDK_KEY_Down:
2729 		case GDK_KEY_KP_Down:
2730 			if ((e->key.state & GDK_MOD1_MASK)
2731 			    && ((e->key.keyval == GDK_KEY_Down) || (e->key.keyval == GDK_KEY_KP_Down))) {
2732 				gint view_col = model_to_view_col (eti, cursor_col);
2733 
2734 				if ((view_col >= 0) && (view_col < eti->cols))
2735 					if (eti_e_cell_event (eti, eti->cell_views[view_col], e, ((GdkEventKey *) e)->time, cursor_col, view_col, model_to_view_row (eti, cursor_row),  E_CELL_CURSOR))
2736 						return TRUE;
2737 			} else
2738 			return_val = e_selection_model_key_press (E_SELECTION_MODEL (eti->selection), (GdkEventKey *) e);
2739 			break;
2740 		case GDK_KEY_Home:
2741 		case GDK_KEY_KP_Home:
2742 			if (eti_editing (eti)) {
2743 				handled = FALSE;
2744 				break;
2745 			}
2746 
2747 			if (eti->cursor_mode != E_CURSOR_LINE) {
2748 				eti_cursor_move (eti, model_to_view_row (eti, cursor_row), 0);
2749 				return_val = TRUE;
2750 			} else
2751 				return_val = e_selection_model_key_press (E_SELECTION_MODEL (eti->selection), (GdkEventKey *) e);
2752 			break;
2753 		case GDK_KEY_End:
2754 		case GDK_KEY_KP_End:
2755 			if (eti_editing (eti)) {
2756 				handled = FALSE;
2757 				break;
2758 			}
2759 
2760 			if (eti->cursor_mode != E_CURSOR_LINE) {
2761 				eti_cursor_move (eti, model_to_view_row (eti, cursor_row), eti->cols - 1);
2762 				return_val = TRUE;
2763 			} else
2764 				return_val = e_selection_model_key_press (E_SELECTION_MODEL (eti->selection), (GdkEventKey *) e);
2765 			break;
2766 		case GDK_KEY_Tab:
2767 		case GDK_KEY_KP_Tab:
2768 		case GDK_KEY_ISO_Left_Tab:
2769 			if ((e->key.state & GDK_CONTROL_MASK) != 0) {
2770 				return_val = FALSE;
2771 				break;
2772 			}
2773 			if (eti->cursor_mode == E_CURSOR_SPREADSHEET) {
2774 				if ((e->key.state & GDK_SHIFT_MASK) != 0) {
2775 				/* shift tab */
2776 					if (cursor_col != view_to_model_col (eti, 0))
2777 						eti_cursor_move_left (eti);
2778 					else if (cursor_row != view_to_model_row (eti, 0))
2779 						eti_cursor_move (eti, model_to_view_row (eti, cursor_row) - 1, eti->cols - 1);
2780 					else
2781 						return_val = FALSE;
2782 				} else {
2783 					if (cursor_col != view_to_model_col (eti, eti->cols - 1))
2784 						eti_cursor_move_right (eti);
2785 					else if (cursor_row != view_to_model_row (eti, eti->rows - 1))
2786 						eti_cursor_move (eti, model_to_view_row (eti, cursor_row) + 1, 0);
2787 					else
2788 						return_val = FALSE;
2789 				}
2790 				g_object_get (
2791 					eti->selection,
2792 					"cursor_row", &cursor_row,
2793 					"cursor_col", &cursor_col,
2794 					NULL);
2795 
2796 				if (cursor_col >= 0 && cursor_row >= 0 && return_val &&
2797 				    (!eti_editing (eti)) && e_table_model_is_cell_editable (eti->table_model, cursor_col, model_to_view_row (eti, cursor_row))) {
2798 					e_table_item_enter_edit (eti, model_to_view_col (eti, cursor_col), model_to_view_row (eti, cursor_row));
2799 				}
2800 				break;
2801 			} else {
2802 			/* Let tab send you to the next widget. */
2803 			return_val = FALSE;
2804 			break;
2805 			}
2806 
2807 		case GDK_KEY_Return:
2808 		case GDK_KEY_KP_Enter:
2809 		case GDK_KEY_ISO_Enter:
2810 		case GDK_KEY_3270_Enter:
2811 			if (eti_editing (eti)) {
2812 				ecell_view = eti->cell_views[eti->editing_col];
2813 				return_val = eti_e_cell_event (
2814 					eti, ecell_view, e, e->key.time,
2815 					view_to_model_col (eti, eti->editing_col),
2816 					eti->editing_col, eti->editing_row, E_CELL_EDITING | E_CELL_CURSOR | E_CELL_PREEDIT);
2817 				if (!return_val)
2818 					break;
2819 			}
2820 			g_signal_emit (
2821 				eti, eti_signals[KEY_PRESS], 0,
2822 				model_to_view_row (eti, cursor_row), cursor_col, e, &return_val);
2823 			if (!return_val)
2824 				return_val = e_selection_model_key_press (E_SELECTION_MODEL (eti->selection), (GdkEventKey *) e);
2825 			break;
2826 
2827 		default:
2828 			handled = FALSE;
2829 			break;
2830 		}
2831 
2832 		if (!handled) {
2833 			switch (e->key.keyval) {
2834 			case GDK_KEY_Scroll_Lock:
2835 			case GDK_KEY_Sys_Req:
2836 			case GDK_KEY_Shift_L:
2837 			case GDK_KEY_Shift_R:
2838 			case GDK_KEY_Control_L:
2839 			case GDK_KEY_Control_R:
2840 			case GDK_KEY_Caps_Lock:
2841 			case GDK_KEY_Shift_Lock:
2842 			case GDK_KEY_Meta_L:
2843 			case GDK_KEY_Meta_R:
2844 			case GDK_KEY_Alt_L:
2845 			case GDK_KEY_Alt_R:
2846 			case GDK_KEY_Super_L:
2847 			case GDK_KEY_Super_R:
2848 			case GDK_KEY_Hyper_L:
2849 			case GDK_KEY_Hyper_R:
2850 			case GDK_KEY_ISO_Lock:
2851 				break;
2852 
2853 			default:
2854 				if (!eti_editing (eti)) {
2855 					gint col, row;
2856 					row = model_to_view_row (eti, cursor_row);
2857 					col = model_to_view_col (eti, cursor_col);
2858 					if (col != -1 && row != -1 && e_table_model_is_cell_editable (eti->table_model, cursor_col, row)) {
2859 						e_table_item_enter_edit (eti, col, row);
2860 					}
2861 				}
2862 				if (!eti_editing (eti)) {
2863 					g_signal_emit (
2864 						eti, eti_signals[KEY_PRESS], 0,
2865 						model_to_view_row (eti, cursor_row), cursor_col, e, &return_val);
2866 					if (!return_val)
2867 						e_selection_model_key_press (E_SELECTION_MODEL (eti->selection), (GdkEventKey *) e);
2868 				} else {
2869 					ecell_view = eti->cell_views[eti->editing_col];
2870 					return_val = eti_e_cell_event (
2871 						eti, ecell_view, e, e->key.time,
2872 						view_to_model_col (eti, eti->editing_col),
2873 						eti->editing_col, eti->editing_row, E_CELL_EDITING | E_CELL_CURSOR);
2874 					if (!return_val)
2875 						e_selection_model_key_press (E_SELECTION_MODEL (eti->selection), (GdkEventKey *) e);
2876 				}
2877 				break;
2878 			}
2879 		}
2880 		eti->in_key_press = FALSE;
2881 		break;
2882 	}
2883 
2884 	case GDK_KEY_RELEASE: {
2885 		gint cursor_row, cursor_col;
2886 
2887 		d (g_print ("%s: GDK_KEY_RELEASE received, keyval: %d\n", __FUNCTION__, (gint) e->key.keyval));
2888 
2889 		g_object_get (
2890 			eti->selection,
2891 			"cursor_row", &cursor_row,
2892 			"cursor_col", &cursor_col,
2893 			NULL);
2894 
2895 		if (cursor_col == -1)
2896 			return FALSE;
2897 
2898 		if (eti_editing (eti)) {
2899 			ecell_view = eti->cell_views[eti->editing_col];
2900 			return_val = eti_e_cell_event (
2901 				eti, ecell_view, e, e->key.time,
2902 				view_to_model_col (eti, eti->editing_col),
2903 				eti->editing_col, eti->editing_row, E_CELL_EDITING | E_CELL_CURSOR);
2904 		}
2905 		break;
2906 	}
2907 
2908 	case GDK_LEAVE_NOTIFY:
2909 		d (leave = TRUE);
2910 	case GDK_ENTER_NOTIFY:
2911 		d (g_print ("%s: %s received\n", __FUNCTION__, leave ? "GDK_LEAVE_NOTIFY" : "GDK_ENTER_NOTIFY"));
2912 		if (eti->motion_row != -1 && eti->motion_col != -1)
2913 			return_val = eti_e_cell_event (
2914 				eti, eti->cell_views[eti->motion_col],
2915 				e, e->crossing.time,
2916 				view_to_model_col (eti, eti->motion_col),
2917 				eti->motion_col, eti->motion_row, 0);
2918 		eti->motion_row = -1;
2919 		eti->motion_col = -1;
2920 
2921 		break;
2922 
2923 	case GDK_FOCUS_CHANGE:
2924 		d (g_print ("%s: GDK_FOCUS_CHANGE received, %s\n", __FUNCTION__, e->focus_change.in ? "in": "out"));
2925 		if (e->focus_change.in) {
2926 			if (eti->save_row != -1 &&
2927 			    eti->save_col != -1 &&
2928 			    !eti_editing (eti) &&
2929 			    e_table_model_is_cell_editable (eti->table_model, view_to_model_col (eti, eti->save_col), eti->save_row)) {
2930 				e_table_item_enter_edit (eti, eti->save_col, eti->save_row);
2931 				e_cell_load_state (
2932 					eti->cell_views[eti->editing_col], view_to_model_col (eti, eti->save_col),
2933 					eti->save_col, eti->save_row, eti->edit_ctx, eti->save_state);
2934 				eti_free_save_state (eti);
2935 			}
2936 		} else {
2937 			if (eti_editing (eti)) {
2938 				eti_free_save_state (eti);
2939 
2940 				eti->save_row   = eti->editing_row;
2941 				eti->save_col   = eti->editing_col;
2942 				eti->save_state = e_cell_save_state (
2943 					eti->cell_views[eti->editing_col], view_to_model_col (eti, eti->editing_col),
2944 					eti->editing_col, eti->editing_row, eti->edit_ctx);
2945 				e_table_item_leave_edit_(eti);
2946 			}
2947 		}
2948 
2949 	default:
2950 		return_val = FALSE;
2951 	}
2952 	/* d(g_print("%s: returning: %s\n", __FUNCTION__, return_val?"true":"false"));*/
2953 
2954 	return return_val;
2955 }
2956 
2957 static void
2958 eti_style_set (ETableItem *eti,
2959                GtkStyle *previous_style)
2960 {
2961 	GnomeCanvasItem *item = GNOME_CANVAS_ITEM (eti);
2962 
2963 	if (!(item->flags & GNOME_CANVAS_ITEM_REALIZED))
2964 		return;
2965 
2966 	if (eti->cell_views_realized) {
2967 		gint i;
2968 		gint n_cells = eti->n_cells;
2969 
2970 		for (i = 0; i < n_cells; i++) {
2971 			e_cell_style_set (eti->cell_views[i], previous_style);
2972 		}
2973 	}
2974 
2975 	eti->needs_compute_height = 1;
2976 	e_canvas_item_request_reflow (GNOME_CANVAS_ITEM (eti));
2977 	eti->needs_redraw = 1;
2978 	gnome_canvas_item_request_update (GNOME_CANVAS_ITEM (eti));
2979 
2980 	free_height_cache (eti);
2981 
2982 	eti_idle_maybe_show_cursor (eti);
2983 }
2984 
2985 static void
2986 eti_class_init (ETableItemClass *class)
2987 {
2988 	GnomeCanvasItemClass *item_class = GNOME_CANVAS_ITEM_CLASS (class);
2989 	GObjectClass *object_class = G_OBJECT_CLASS (class);
2990 
2991 	object_class->dispose       = eti_dispose;
2992 	object_class->set_property  = eti_set_property;
2993 	object_class->get_property  = eti_get_property;
2994 
2995 	item_class->update          = eti_update;
2996 	item_class->realize         = eti_realize;
2997 	item_class->unrealize       = eti_unrealize;
2998 	item_class->draw            = eti_draw;
2999 	item_class->point           = eti_point;
3000 	item_class->event           = eti_event;
3001 
3002 	class->cursor_change    = NULL;
3003 	class->cursor_activated = NULL;
3004 	class->double_click     = NULL;
3005 	class->right_click      = NULL;
3006 	class->click            = NULL;
3007 	class->key_press        = NULL;
3008 	class->start_drag       = NULL;
3009 	class->style_set        = eti_style_set;
3010 	class->selection_model_removed = NULL;
3011 	class->selection_model_added = NULL;
3012 
3013 	g_object_class_install_property (
3014 		object_class,
3015 		PROP_TABLE_HEADER,
3016 		g_param_spec_object (
3017 			"ETableHeader",
3018 			"Table header",
3019 			"Table header",
3020 			E_TYPE_TABLE_HEADER,
3021 			G_PARAM_WRITABLE));
3022 
3023 	g_object_class_install_property (
3024 		object_class,
3025 		PROP_TABLE_MODEL,
3026 		g_param_spec_object (
3027 			"ETableModel",
3028 			"Table model",
3029 			"Table model",
3030 			E_TYPE_TABLE_MODEL,
3031 			G_PARAM_WRITABLE));
3032 
3033 	g_object_class_install_property (
3034 		object_class,
3035 		PROP_SELECTION_MODEL,
3036 		g_param_spec_object (
3037 			"selection_model",
3038 			"Selection model",
3039 			"Selection model",
3040 			E_TYPE_SELECTION_MODEL,
3041 			G_PARAM_WRITABLE));
3042 
3043 	g_object_class_install_property (
3044 		object_class,
3045 		PROP_TABLE_ALTERNATING_ROW_COLORS,
3046 		g_param_spec_boolean (
3047 			"alternating_row_colors",
3048 			"Alternating Row Colors",
3049 			"Alternating Row Colors",
3050 			FALSE,
3051 			G_PARAM_WRITABLE));
3052 
3053 	g_object_class_install_property (
3054 		object_class,
3055 		PROP_TABLE_HORIZONTAL_DRAW_GRID,
3056 		g_param_spec_boolean (
3057 			"horizontal_draw_grid",
3058 			"Horizontal Draw Grid",
3059 			"Horizontal Draw Grid",
3060 			FALSE,
3061 			G_PARAM_WRITABLE));
3062 
3063 	g_object_class_install_property (
3064 		object_class,
3065 		PROP_TABLE_VERTICAL_DRAW_GRID,
3066 		g_param_spec_boolean (
3067 			"vertical_draw_grid",
3068 			"Vertical Draw Grid",
3069 			"Vertical Draw Grid",
3070 			FALSE,
3071 			G_PARAM_WRITABLE));
3072 
3073 	g_object_class_install_property (
3074 		object_class,
3075 		PROP_TABLE_DRAW_FOCUS,
3076 		g_param_spec_boolean (
3077 			"drawfocus",
3078 			"Draw focus",
3079 			"Draw focus",
3080 			FALSE,
3081 			G_PARAM_WRITABLE));
3082 
3083 	g_object_class_install_property (
3084 		object_class,
3085 		PROP_CURSOR_MODE,
3086 		g_param_spec_int (
3087 			"cursor_mode",
3088 			"Cursor mode",
3089 			"Cursor mode",
3090 			E_CURSOR_LINE,
3091 			E_CURSOR_SPREADSHEET,
3092 			E_CURSOR_LINE,
3093 			G_PARAM_WRITABLE));
3094 
3095 	g_object_class_install_property (
3096 		object_class,
3097 		PROP_LENGTH_THRESHOLD,
3098 		g_param_spec_int (
3099 			"length_threshold",
3100 			"Length Threshold",
3101 			"Length Threshold",
3102 			-1, G_MAXINT, 0,
3103 			G_PARAM_WRITABLE));
3104 
3105 	g_object_class_install_property (
3106 		object_class,
3107 		PROP_MINIMUM_WIDTH,
3108 		g_param_spec_double (
3109 			"minimum_width",
3110 			"Minimum width",
3111 			"Minimum Width",
3112 			0.0, G_MAXDOUBLE, 0.0,
3113 			G_PARAM_READWRITE));
3114 
3115 	g_object_class_install_property (
3116 		object_class,
3117 		PROP_WIDTH,
3118 		g_param_spec_double (
3119 			"width",
3120 			"Width",
3121 			"Width",
3122 			0.0, G_MAXDOUBLE, 0.0,
3123 			G_PARAM_READWRITE));
3124 
3125 	g_object_class_install_property (
3126 		object_class,
3127 		PROP_HEIGHT,
3128 		g_param_spec_double (
3129 			"height",
3130 			"Height",
3131 			"Height",
3132 			0.0, G_MAXDOUBLE, 0.0,
3133 			G_PARAM_READABLE));
3134 
3135 	g_object_class_install_property (
3136 		object_class,
3137 		PROP_CURSOR_ROW,
3138 		g_param_spec_int (
3139 			"cursor_row",
3140 			"Cursor row",
3141 			"Cursor row",
3142 			0, G_MAXINT, 0,
3143 			G_PARAM_READWRITE));
3144 
3145 	g_object_class_install_property (
3146 		object_class,
3147 		PROP_UNIFORM_ROW_HEIGHT,
3148 		g_param_spec_boolean (
3149 			"uniform_row_height",
3150 			"Uniform row height",
3151 			"Uniform row height",
3152 			FALSE,
3153 			G_PARAM_READWRITE));
3154 
3155 	eti_signals[CURSOR_CHANGE] = g_signal_new (
3156 		"cursor_change",
3157 		G_OBJECT_CLASS_TYPE (object_class),
3158 		G_SIGNAL_RUN_LAST,
3159 		G_STRUCT_OFFSET (ETableItemClass, cursor_change),
3160 		NULL, NULL,
3161 		g_cclosure_marshal_VOID__INT,
3162 		G_TYPE_NONE, 1,
3163 		G_TYPE_INT);
3164 
3165 	eti_signals[CURSOR_ACTIVATED] = g_signal_new (
3166 		"cursor_activated",
3167 		G_OBJECT_CLASS_TYPE (object_class),
3168 		G_SIGNAL_RUN_LAST,
3169 		G_STRUCT_OFFSET (ETableItemClass, cursor_activated),
3170 		NULL, NULL,
3171 		g_cclosure_marshal_VOID__INT,
3172 		G_TYPE_NONE, 1,
3173 		G_TYPE_INT);
3174 
3175 	eti_signals[DOUBLE_CLICK] = g_signal_new (
3176 		"double_click",
3177 		G_OBJECT_CLASS_TYPE (object_class),
3178 		G_SIGNAL_RUN_LAST,
3179 		G_STRUCT_OFFSET (ETableItemClass, double_click),
3180 		NULL, NULL,
3181 		e_marshal_NONE__INT_INT_BOXED,
3182 		G_TYPE_NONE, 3,
3183 		G_TYPE_INT,
3184 		G_TYPE_INT,
3185 		GDK_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
3186 
3187 	eti_signals[START_DRAG] = g_signal_new (
3188 		"start_drag",
3189 		G_OBJECT_CLASS_TYPE (object_class),
3190 		G_SIGNAL_RUN_LAST,
3191 		G_STRUCT_OFFSET (ETableItemClass, start_drag),
3192 		g_signal_accumulator_true_handled, NULL,
3193 		e_marshal_BOOLEAN__INT_INT_BOXED,
3194 		G_TYPE_BOOLEAN, 3,
3195 		G_TYPE_INT,
3196 		G_TYPE_INT,
3197 		GDK_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
3198 
3199 	eti_signals[RIGHT_CLICK] = g_signal_new (
3200 		"right_click",
3201 		G_OBJECT_CLASS_TYPE (object_class),
3202 		G_SIGNAL_RUN_LAST,
3203 		G_STRUCT_OFFSET (ETableItemClass, right_click),
3204 		g_signal_accumulator_true_handled, NULL,
3205 		e_marshal_BOOLEAN__INT_INT_BOXED,
3206 		G_TYPE_BOOLEAN, 3,
3207 		G_TYPE_INT,
3208 		G_TYPE_INT,
3209 		GDK_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
3210 
3211 	eti_signals[CLICK] = g_signal_new (
3212 		"click",
3213 		G_OBJECT_CLASS_TYPE (object_class),
3214 		G_SIGNAL_RUN_LAST,
3215 		G_STRUCT_OFFSET (ETableItemClass, click),
3216 		g_signal_accumulator_true_handled, NULL,
3217 		e_marshal_BOOLEAN__INT_INT_BOXED,
3218 		G_TYPE_BOOLEAN, 3,
3219 		G_TYPE_INT,
3220 		G_TYPE_INT,
3221 		GDK_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
3222 
3223 	eti_signals[KEY_PRESS] = g_signal_new (
3224 		"key_press",
3225 		G_OBJECT_CLASS_TYPE (object_class),
3226 		G_SIGNAL_RUN_LAST,
3227 		G_STRUCT_OFFSET (ETableItemClass, key_press),
3228 		g_signal_accumulator_true_handled, NULL,
3229 		e_marshal_BOOLEAN__INT_INT_BOXED,
3230 		G_TYPE_BOOLEAN, 3,
3231 		G_TYPE_INT,
3232 		G_TYPE_INT,
3233 		GDK_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
3234 
3235 	eti_signals[STYLE_SET] = g_signal_new (
3236 		"style_set",
3237 		G_OBJECT_CLASS_TYPE (object_class),
3238 		G_SIGNAL_RUN_LAST,
3239 		G_STRUCT_OFFSET (ETableItemClass, style_set),
3240 		NULL, NULL,
3241 		g_cclosure_marshal_VOID__OBJECT,
3242 		G_TYPE_NONE, 1,
3243 		GTK_TYPE_STYLE);
3244 
3245 	eti_signals[SELECTION_MODEL_REMOVED] = g_signal_new (
3246 		"selection_model_removed",
3247 		G_TYPE_FROM_CLASS (object_class),
3248 		G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
3249 		G_STRUCT_OFFSET (ETableItemClass, selection_model_removed),
3250 		NULL, NULL,
3251 		g_cclosure_marshal_VOID__POINTER,
3252 		G_TYPE_NONE, 1,
3253 		G_TYPE_POINTER);
3254 
3255 	eti_signals[SELECTION_MODEL_ADDED] = g_signal_new (
3256 		"selection_model_added",
3257 		G_TYPE_FROM_CLASS (object_class),
3258 		G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
3259 		G_STRUCT_OFFSET (ETableItemClass, selection_model_added),
3260 		NULL, NULL,
3261 		g_cclosure_marshal_VOID__POINTER,
3262 		G_TYPE_NONE, 1,
3263 		G_TYPE_POINTER);
3264 
3265 	/* A11y Init */
3266 	gal_a11y_e_table_item_init ();
3267 }
3268 
3269 /**
3270  * e_table_item_set_cursor:
3271  * @eti: %ETableItem which will have the cursor set.
3272  * @col: Column to select.  -1 means the last column.
3273  * @row: Row to select.  -1 means the last row.
3274  *
3275  * This routine sets the cursor of the %ETableItem canvas item.
3276  */
3277 void
3278 e_table_item_set_cursor (ETableItem *eti,
3279                          gint col,
3280                          gint row)
3281 {
3282 	e_table_item_focus (eti, col, view_to_model_row (eti, row), 0);
3283 }
3284 
3285 static void
3286 e_table_item_focus (ETableItem *eti,
3287                     gint col,
3288                     gint row,
3289                     GdkModifierType state)
3290 {
3291 	g_return_if_fail (eti != NULL);
3292 	g_return_if_fail (E_IS_TABLE_ITEM (eti));
3293 
3294 	if (row == -1) {
3295 		row = view_to_model_row (eti, eti->rows - 1);
3296 	}
3297 
3298 	if (col == -1) {
3299 		col = eti->cols - 1;
3300 	}
3301 
3302 	if (row != -1) {
3303 		e_selection_model_do_something (
3304 			E_SELECTION_MODEL (eti->selection),
3305 			row, col, state);
3306 	}
3307 }
3308 
3309 /**
3310  * e_table_item_get_focused_column:
3311  * @eti: %ETableItem which will have the cursor retrieved.
3312  *
3313  * This routine gets the cursor of the %ETableItem canvas item.
3314  *
3315  * Returns: The current cursor column.
3316  */
3317 gint
3318 e_table_item_get_focused_column (ETableItem *eti)
3319 {
3320 	gint cursor_col;
3321 
3322 	g_return_val_if_fail (eti != NULL, -1);
3323 	g_return_val_if_fail (E_IS_TABLE_ITEM (eti), -1);
3324 
3325 	g_object_get (
3326 		eti->selection,
3327 		"cursor_col", &cursor_col,
3328 		NULL);
3329 
3330 	return cursor_col;
3331 }
3332 
3333 static void
3334 eti_cursor_change (ESelectionModel *selection,
3335                    gint row,
3336                    gint col,
3337                    ETableItem *eti)
3338 {
3339 	GnomeCanvasItem *item = GNOME_CANVAS_ITEM (eti);
3340 	gint view_row;
3341 
3342 	if (!(item->flags & GNOME_CANVAS_ITEM_REALIZED))
3343 		return;
3344 
3345 	view_row = model_to_view_row (eti, row);
3346 
3347 	if (eti->old_cursor_row != -1 && view_row != eti->old_cursor_row)
3348 		e_table_item_redraw_row (eti, eti->old_cursor_row);
3349 
3350 	if (view_row == -1) {
3351 		e_table_item_leave_edit_(eti);
3352 		eti->old_cursor_row = -1;
3353 		return;
3354 	}
3355 
3356 	if (!e_table_model_has_change_pending (eti->table_model)) {
3357 		if (!eti->in_key_press) {
3358 			eti_maybe_show_cursor (eti, DOUBLE_CLICK_TIME + 10);
3359 		} else {
3360 			eti_maybe_show_cursor (eti, 0);
3361 		}
3362 	}
3363 
3364 	e_canvas_item_grab_focus (GNOME_CANVAS_ITEM (eti), FALSE);
3365 	if (eti_editing (eti))
3366 		e_table_item_leave_edit_(eti);
3367 
3368 	g_signal_emit (eti, eti_signals[CURSOR_CHANGE], 0, view_row);
3369 
3370 	e_table_item_redraw_row (eti, view_row);
3371 
3372 	eti->old_cursor_row = view_row;
3373 }
3374 
3375 static void
3376 eti_cursor_activated (ESelectionModel *selection,
3377                       gint row,
3378                       gint col,
3379                       ETableItem *eti)
3380 {
3381 	GnomeCanvasItem *item = GNOME_CANVAS_ITEM (eti);
3382 	gint view_row;
3383 	gint view_col;
3384 
3385 	if (!(item->flags & GNOME_CANVAS_ITEM_REALIZED))
3386 		return;
3387 
3388 	view_row = model_to_view_row (eti, row);
3389 	view_col = model_to_view_col (eti, col);
3390 
3391 	if (view_row != -1 && view_col != -1) {
3392 		if (!e_table_model_has_change_pending (eti->table_model)) {
3393 			if (!eti->in_key_press) {
3394 				eti_show_cursor (eti, DOUBLE_CLICK_TIME + 10);
3395 			} else {
3396 				eti_show_cursor (eti, 0);
3397 			}
3398 			eti_check_cursor_bounds (eti);
3399 		}
3400 	}
3401 
3402 	if (eti_editing (eti))
3403 		e_table_item_leave_edit_(eti);
3404 
3405 	if (view_row != -1)
3406 		g_signal_emit (
3407 			eti, eti_signals[CURSOR_ACTIVATED], 0, view_row);
3408 }
3409 
3410 static void
3411 eti_selection_change (ESelectionModel *selection,
3412                       ETableItem *eti)
3413 {
3414 	GnomeCanvasItem *item = GNOME_CANVAS_ITEM (eti);
3415 
3416 	if (!(item->flags & GNOME_CANVAS_ITEM_REALIZED))
3417 		return;
3418 
3419 	eti->needs_redraw = TRUE;
3420 	gnome_canvas_item_request_update (GNOME_CANVAS_ITEM (eti));
3421 }
3422 
3423 static void
3424 eti_selection_row_change (ESelectionModel *selection,
3425                           gint row,
3426                           ETableItem *eti)
3427 {
3428 	GnomeCanvasItem *item = GNOME_CANVAS_ITEM (eti);
3429 
3430 	if (!(item->flags & GNOME_CANVAS_ITEM_REALIZED))
3431 		return;
3432 
3433 	if (!eti->needs_redraw) {
3434 		e_table_item_redraw_row (eti, model_to_view_row (eti, row));
3435 	}
3436 }
3437 
3438 /**
3439  * e_table_item_enter_edit
3440  * @eti: %ETableItem which will start being edited
3441  * @col: The view col to edit.
3442  * @row: The view row to edit.
3443  *
3444  * This routine starts the given %ETableItem editing at the given view
3445  * column and row.
3446  */
3447 void
3448 e_table_item_enter_edit (ETableItem *eti,
3449                          gint col,
3450                          gint row)
3451 {
3452 	g_return_if_fail (eti != NULL);
3453 	g_return_if_fail (E_IS_TABLE_ITEM (eti));
3454 
3455 	d (g_print ("%s: %d, %d, eti_editing() = %s\n", __FUNCTION__, col, row, eti_editing (eti)?"true":"false"));
3456 
3457 	if (eti_editing (eti))
3458 		e_table_item_leave_edit_(eti);
3459 
3460 	eti->editing_col = col;
3461 	eti->editing_row = row;
3462 
3463 	eti->edit_ctx = e_cell_enter_edit (eti->cell_views[col], view_to_model_col (eti, col), col, row);
3464 }
3465 
3466 /**
3467  * e_table_item_leave_edit_
3468  * @eti: %ETableItem which will stop being edited
3469  *
3470  * This routine stops the given %ETableItem from editing.
3471  */
3472 void
3473 e_table_item_leave_edit (ETableItem *eti)
3474 {
3475 	gint col, row;
3476 	gpointer edit_ctx;
3477 
3478 	g_return_if_fail (eti != NULL);
3479 	g_return_if_fail (E_IS_TABLE_ITEM (eti));
3480 
3481 	d (g_print ("%s: eti_editing() = %s\n", __FUNCTION__, eti_editing (eti)?"true":"false"));
3482 
3483 	if (!eti_editing (eti))
3484 		return;
3485 
3486 	col = eti->editing_col;
3487 	row = eti->editing_row;
3488 	edit_ctx = eti->edit_ctx;
3489 
3490 	eti->editing_col = -1;
3491 	eti->editing_row = -1;
3492 	eti->edit_ctx = NULL;
3493 
3494 	e_cell_leave_edit (
3495 		eti->cell_views[col],
3496 		view_to_model_col (eti, col),
3497 		col, row, edit_ctx);
3498 }
3499 
3500 /**
3501  * e_table_item_compute_location
3502  * @eti: %ETableItem to look in.
3503  * @x: A pointer to the x location to find in the %ETableItem.
3504  * @y: A pointer to the y location to find in the %ETableItem.
3505  * @row: A pointer to the location to store the found row in.
3506  * @col: A pointer to the location to store the found col in.
3507  *
3508  * This routine locates the pixel location (*x, *y) in the
3509  * %ETableItem.  If that location is in the %ETableItem, *row and *col
3510  * are set to the view row and column where it was found.  If that
3511  * location is not in the %ETableItem, the height of the %ETableItem
3512  * is removed from the value y points to.
3513  */
3514 void
3515 e_table_item_compute_location (ETableItem *eti,
3516                                gint *x,
3517                                gint *y,
3518                                gint *row,
3519                                gint *col)
3520 {
3521 	/* Save the grabbed row but make sure that we don't get flawed
3522 	 * results because the cursor is grabbed. */
3523 	gint grabbed_row = eti->grabbed_row;
3524 	eti->grabbed_row = -1;
3525 
3526 	if (!find_cell (eti, *x, *y, col, row, NULL, NULL)) {
3527 		*y -= eti->height;
3528 	}
3529 
3530 	eti->grabbed_row = grabbed_row;
3531 }
3532 
3533 /**
3534  * e_table_item_compute_mouse_over:
3535  * Similar to e_table_item_compute_location, only here recalculating
3536  * the position inside the item too.
3537  **/
3538 void
3539 e_table_item_compute_mouse_over (ETableItem *eti,
3540                                  gint x,
3541                                  gint y,
3542                                  gint *row,
3543                                  gint *col)
3544 {
3545 	gdouble realx, realy;
3546 	/* Save the grabbed row but make sure that we don't get flawed
3547 	 * results because the cursor is grabbed. */
3548 	gint grabbed_row = eti->grabbed_row;
3549 	eti->grabbed_row = -1;
3550 
3551 	realx = x;
3552 	realy = y;
3553 
3554 	gnome_canvas_item_w2i (GNOME_CANVAS_ITEM (eti), &realx, &realy);
3555 
3556 	if (!find_cell (eti, (gint) realx, (gint) realy, col, row, NULL, NULL)) {
3557 		*row = -1;
3558 		*col = -1;
3559 	}
3560 
3561 	eti->grabbed_row = grabbed_row;
3562 }
3563 
3564 void
3565 e_table_item_get_cell_geometry (ETableItem *eti,
3566                                 gint *row,
3567                                 gint *col,
3568                                 gint *x,
3569                                 gint *y,
3570                                 gint *width,
3571                                 gint *height)
3572 {
3573 	if (eti->rows > *row) {
3574 		if (x)
3575 			*x = e_table_header_col_diff (eti->header, 0, *col);
3576 		if (y)
3577 			*y = e_table_item_row_diff (eti, 0, *row);
3578 		if (width)
3579 			*width = e_table_header_col_diff (eti->header, *col, *col + 1);
3580 		if (height)
3581 			*height = ETI_ROW_HEIGHT (eti, *row);
3582 		*row = -1;
3583 		*col = -1;
3584 	} else {
3585 		*row -= eti->rows;
3586 	}
3587 }
3588 
3589 typedef struct {
3590 	ETableItem *item;
3591 	gint rows_printed;
3592 } ETableItemPrintContext;
3593 
3594 static gdouble *
3595 e_table_item_calculate_print_widths (ETableHeader *eth,
3596                                      gdouble width)
3597 {
3598 	gint i;
3599 	gdouble extra;
3600 	gdouble expansion;
3601 	gint last_resizable = -1;
3602 	gdouble scale = 1.0L;
3603 	gdouble *widths = g_new (gdouble, e_table_header_count (eth));
3604 	/* - 1 to account for the last pixel border. */
3605 	extra = width - 1;
3606 	expansion = 0;
3607 	for (i = 0; i < eth->col_count; i++) {
3608 		extra -= eth->columns[i]->min_width * scale;
3609 		if (eth->columns[i]->resizable && eth->columns[i]->expansion > 0)
3610 			last_resizable = i;
3611 		expansion += eth->columns[i]->resizable ? eth->columns[i]->expansion : 0;
3612 		widths[i] = eth->columns[i]->min_width * scale;
3613 	}
3614 	for (i = 0; i <= last_resizable; i++) {
3615 		widths[i] += extra * (eth->columns[i]->resizable ? eth->columns[i]->expansion : 0) / expansion;
3616 	}
3617 
3618 	return widths;
3619 }
3620 
3621 static gdouble
3622 eti_printed_row_height (ETableItem *eti,
3623                         gdouble *widths,
3624                         GtkPrintContext *context,
3625                         gint row)
3626 {
3627 	gint col;
3628 	gint cols = eti->cols;
3629 	gdouble height = 0;
3630 	for (col = 0; col < cols; col++) {
3631 		ECellView *ecell_view = eti->cell_views[col];
3632 		gdouble this_height = e_cell_print_height (
3633 			ecell_view, context, view_to_model_col (eti, col), col, row,
3634 			widths[col] - 1);
3635 		if (this_height > height)
3636 			height = this_height;
3637 	}
3638 	return height;
3639 }
3640 
3641 #define CHECK(x) if((x) == -1) return -1;
3642 
3643 static gint
3644 gp_draw_rect (GtkPrintContext *context,
3645               gdouble x,
3646               gdouble y,
3647               gdouble width,
3648               gdouble height)
3649 {
3650 	cairo_t *cr;
3651 	cr = gtk_print_context_get_cairo_context (context);
3652 	cairo_save (cr);
3653 	cairo_rectangle (cr, x, y, width, height);
3654 	cairo_set_line_width (cr, 0.5);
3655 	cairo_stroke (cr);
3656 	cairo_restore (cr);
3657 	return 0;
3658 }
3659 
3660 static void
3661 e_table_item_print_page (EPrintable *ep,
3662                          GtkPrintContext *context,
3663                          gdouble width,
3664                          gdouble height,
3665                          gboolean quantize,
3666                          ETableItemPrintContext *itemcontext)
3667 {
3668 	ETableItem *eti = itemcontext->item;
3669 	const gint rows = eti->rows;
3670 	const gint cols = eti->cols;
3671 	gdouble max_height;
3672 	gint rows_printed = itemcontext->rows_printed;
3673 	gint row, col, next_page = 0;
3674 	gdouble yd = height;
3675 	cairo_t *cr;
3676 	gdouble *widths;
3677 
3678 	cr = gtk_print_context_get_cairo_context (context);
3679 	max_height = gtk_print_context_get_height (context);
3680 	widths = e_table_item_calculate_print_widths (itemcontext->item->header, width);
3681 
3682 	/*
3683 	 * Draw cells
3684 	 */
3685 
3686 	if (eti->horizontal_draw_grid) {
3687 		gp_draw_rect (context, 0, yd, width, 1);
3688 	}
3689 	yd++;
3690 
3691 	for (row = rows_printed; row < rows; row++) {
3692 		gdouble xd = 1, row_height;
3693 		row_height = eti_printed_row_height (eti, widths, context, row);
3694 
3695 		if (quantize) {
3696 			if (yd + row_height + 1 > max_height && row != rows_printed) {
3697 				next_page = 1;
3698 				break;
3699 			}
3700 		} else {
3701 			if (yd > max_height) {
3702 				next_page = 1;
3703 				break;
3704 			}
3705 		}
3706 
3707 		for (col = 0; col < cols; col++) {
3708 			ECellView *ecell_view = eti->cell_views[col];
3709 
3710 			cairo_save (cr);
3711 			cairo_translate (cr, xd, yd);
3712 			cairo_rectangle (cr, 0, 0, widths[col] - 1, row_height);
3713 			cairo_clip (cr);
3714 
3715 			e_cell_print (
3716 				ecell_view, context,
3717 				view_to_model_col (eti, col),
3718 				col,
3719 				row,
3720 				widths[col] - 1,
3721 				row_height + 2);
3722 
3723 			cairo_restore (cr);
3724 
3725 			xd += widths[col];
3726 		}
3727 
3728 	yd += row_height;
3729 		if (eti->horizontal_draw_grid) {
3730 			gp_draw_rect (context, 0, yd, width, 1);
3731 		}
3732 		yd++;
3733 	}
3734 
3735 	itemcontext->rows_printed = row;
3736 	if (eti->vertical_draw_grid) {
3737 		gdouble xd = 0;
3738 		for (col = 0; col < cols; col++) {
3739 			gp_draw_rect (context, xd, height, 1, yd - height);
3740 			xd += widths[col];
3741 		}
3742 		gp_draw_rect (context, xd, height, 1, yd - height);
3743 	}
3744 
3745 	if (next_page)
3746 		cairo_show_page (cr);
3747 
3748 	g_free (widths);
3749 }
3750 
3751 static gboolean
3752 e_table_item_data_left (EPrintable *ep,
3753                         ETableItemPrintContext *itemcontext)
3754 {
3755 	ETableItem *item = itemcontext->item;
3756 	gint rows_printed = itemcontext->rows_printed;
3757 
3758 	g_signal_stop_emission_by_name (ep, "data_left");
3759 	return rows_printed < item->rows;
3760 }
3761 
3762 static void
3763 e_table_item_reset (EPrintable *ep,
3764                     ETableItemPrintContext *itemcontext)
3765 {
3766 	itemcontext->rows_printed = 0;
3767 }
3768 
3769 static gdouble
3770 e_table_item_height (EPrintable *ep,
3771                      GtkPrintContext *context,
3772                      gdouble width,
3773                      gdouble max_height,
3774                      gboolean quantize,
3775                      ETableItemPrintContext *itemcontext)
3776 {
3777 	ETableItem *item = itemcontext->item;
3778 	const gint rows = item->rows;
3779 	gint rows_printed = itemcontext->rows_printed;
3780 	gdouble *widths;
3781 	gint row;
3782 	gdouble yd = 0;
3783 
3784 	widths = e_table_item_calculate_print_widths (itemcontext->item->header, width);
3785 
3786 	/*
3787 	 * Draw cells
3788 	 */
3789 	yd++;
3790 
3791 	for (row = rows_printed; row < rows; row++) {
3792 		gdouble row_height;
3793 
3794 		row_height = eti_printed_row_height (item, widths, context, row);
3795 		if (quantize) {
3796 			if (max_height != -1 && yd + row_height + 1 > max_height && row != rows_printed) {
3797 				break;
3798 			}
3799 		} else {
3800 			if (max_height != -1 && yd > max_height) {
3801 				break;
3802 			}
3803 		}
3804 
3805 		yd += row_height;
3806 
3807 		yd++;
3808 	}
3809 
3810 	g_free (widths);
3811 
3812 	if (max_height != -1 && (!quantize) && yd > max_height)
3813 		yd = max_height;
3814 
3815 	g_signal_stop_emission_by_name (ep, "height");
3816 	return yd;
3817 }
3818 
3819 static gboolean
3820 e_table_item_will_fit (EPrintable *ep,
3821                        GtkPrintContext *context,
3822                        gdouble width,
3823                        gdouble max_height,
3824                        gboolean quantize,
3825                        ETableItemPrintContext *itemcontext)
3826 {
3827 	ETableItem *item = itemcontext->item;
3828 	const gint rows = item->rows;
3829 	gint rows_printed = itemcontext->rows_printed;
3830 	gdouble *widths;
3831 	gint row;
3832 	gdouble yd = 0;
3833 	gboolean ret_val = TRUE;
3834 
3835 	widths = e_table_item_calculate_print_widths (itemcontext->item->header, width);
3836 
3837 	/*
3838 	 * Draw cells
3839 	 */
3840 	yd++;
3841 
3842 	for (row = rows_printed; row < rows; row++) {
3843 		gdouble row_height;
3844 
3845 		row_height = eti_printed_row_height (item, widths, context, row);
3846 		if (quantize) {
3847 			if (max_height != -1 && yd + row_height + 1 > max_height && row != rows_printed) {
3848 				ret_val = FALSE;
3849 				break;
3850 			}
3851 		} else {
3852 			if (max_height != -1 && yd > max_height) {
3853 				ret_val = FALSE;
3854 				break;
3855 			}
3856 		}
3857 
3858 		yd += row_height;
3859 
3860 		yd++;
3861 	}
3862 
3863 	g_free (widths);
3864 
3865 	g_signal_stop_emission_by_name (ep, "will_fit");
3866 	return ret_val;
3867 }
3868 
3869 static void
3870 e_table_item_printable_destroy (gpointer data,
3871                                 GObject *where_object_was)
3872 {
3873 	ETableItemPrintContext *itemcontext = data;
3874 
3875 	g_object_unref (itemcontext->item);
3876 	g_free (itemcontext);
3877 }
3878 
3879 /**
3880  * e_table_item_get_printable
3881  * @eti: %ETableItem which will be printed
3882  *
3883  * This routine creates and returns an %EPrintable that can be used to
3884  * print the given %ETableItem.
3885  *
3886  * Returns: The %EPrintable.
3887  */
3888 EPrintable *
3889 e_table_item_get_printable (ETableItem *item)
3890 {
3891 	EPrintable *printable = e_printable_new ();
3892 	ETableItemPrintContext *itemcontext;
3893 
3894 	itemcontext = g_new (ETableItemPrintContext, 1);
3895 	itemcontext->item = item;
3896 	g_object_ref (item);
3897 	itemcontext->rows_printed = 0;
3898 
3899 	g_signal_connect (
3900 		printable, "print_page",
3901 		G_CALLBACK (e_table_item_print_page), itemcontext);
3902 	g_signal_connect (
3903 		printable, "data_left",
3904 		G_CALLBACK (e_table_item_data_left), itemcontext);
3905 	g_signal_connect (
3906 		printable, "reset",
3907 		G_CALLBACK (e_table_item_reset), itemcontext);
3908 	g_signal_connect (
3909 		printable, "height",
3910 		G_CALLBACK (e_table_item_height), itemcontext);
3911 	g_signal_connect (
3912 		printable, "will_fit",
3913 		G_CALLBACK (e_table_item_will_fit), itemcontext);
3914 
3915 	g_object_weak_ref (
3916 		G_OBJECT (printable),
3917 		e_table_item_printable_destroy, itemcontext);
3918 
3919 	return printable;
3920 }