/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ /* * E-table-item.c: A GnomeCanvasItem that is a view of an ETableModel. * * Author: * Christopher James Lahey * Miguel de Icaza (miguel@gnu.org) * * Copyright 1999, 2000, 2001, Ximian, Inc. * * TODO: * Add a border to the thing, so that focusing works properly. * */ #include #include "e-table-item.h" #include #include #include #include #include "e-table-subset.h" #include "e-cell.h" #include "gal/widgets/e-hsv-utils.h" #include "gal/widgets/e-canvas.h" #include "gal/widgets/e-canvas-utils.h" #include "gal/util/e-util.h" #include #define PARENT_OBJECT_TYPE gnome_canvas_item_get_type () #define FOCUSED_BORDER 2 /* #define DO_TOOLTIPS 1 */ /* #define ALTERNATE_COLORS 1 */ #define d(x) /* FIXME: Do an analysis of which cell functions are needed before realize and make sure that all of them are doable by all the cells and that all of the others are only done after realization. */ static GnomeCanvasItemClass *eti_parent_class; enum { CURSOR_CHANGE, CURSOR_ACTIVATED, DOUBLE_CLICK, RIGHT_CLICK, CLICK, KEY_PRESS, LAST_SIGNAL }; static gint eti_signals [LAST_SIGNAL] = { 0, }; enum { ARG_0, ARG_TABLE_HEADER, ARG_TABLE_MODEL, ARG_SELECTION_MODEL, ARG_TABLE_HORIZONTAL_DRAW_GRID, ARG_TABLE_VERTICAL_DRAW_GRID, ARG_TABLE_DRAW_FOCUS, ARG_CURSOR_MODE, ARG_LENGTH_THRESHOLD, ARG_CURSOR_ROW, ARG_MINIMUM_WIDTH, ARG_WIDTH, ARG_HEIGHT, }; #define DOUBLE_CLICK_TIME 250 #define TRIPLE_CLICK_TIME 500 static int eti_get_height (ETableItem *eti); static int eti_row_height (ETableItem *eti, int row); static void e_table_item_focus (ETableItem *eti, int col, int row, GdkModifierType state); static void eti_cursor_change (ESelectionModel *selection, int row, int col, ETableItem *eti); static void eti_cursor_activated (ESelectionModel *selection, int row, int col, ETableItem *eti); static void eti_selection_change (ESelectionModel *selection, ETableItem *eti); #ifdef SHOW_ON_CHANGED static void eti_request_region_show (ETableItem *eti, int start_col, int start_row, int end_col, int end_row); #endif #define ETI_ROW_HEIGHT(eti,row) ((eti)->height_cache && (eti)->height_cache[(row)] != -1 ? (eti)->height_cache[(row)] : eti_row_height((eti),(row))) inline static gint model_to_view_row(ETableItem *eti, int row) { int i; if (eti->uses_source_model) { ETableSubset *etss = E_TABLE_SUBSET(eti->table_model); if (eti->row_guess >= 0 && eti->row_guess < etss->n_map) { if (etss->map_table[eti->row_guess] == row) { return eti->row_guess; } } for (i = 0; i < etss->n_map; i++) { if (etss->map_table[i] == row) return i; } return -1; } else return row; } inline static gint view_to_model_row(ETableItem *eti, int row) { if (eti->uses_source_model) { ETableSubset *etss = E_TABLE_SUBSET(eti->table_model); if (row >= 0 && row < etss->n_map) return etss->map_table[row]; else return -1; } else return row; } inline static gint model_to_view_col(ETableItem *eti, int col) { int i; for (i = 0; i < eti->cols; i++) { ETableCol *ecol = e_table_header_get_column (eti->header, i); if (ecol->col_idx == col) return i; } return -1; } inline static gint view_to_model_col(ETableItem *eti, int col) { ETableCol *ecol = e_table_header_get_column (eti->header, col); return ecol ? ecol->col_idx : -1; } static gboolean eti_editing (ETableItem *eti) { if (eti->editing_col == -1) return FALSE; else return TRUE; } /* * During realization, we have to invoke the per-ecell realize routine * (On our current setup, we have one e-cell per column. * * We might want to optimize this to only realize the unique e-cells: * ie, a strings-only table, uses the same e-cell for every column, and * we might want to avoid realizing each e-cell. */ static void eti_realize_cell_views (ETableItem *eti) { int i; if (eti->cell_views_realized) return; if (!(GTK_OBJECT_FLAGS(eti) & GNOME_CANVAS_ITEM_REALIZED)) return; for (i = 0; i < eti->n_cells; i++) e_cell_realize (eti->cell_views [i]); eti->cell_views_realized = 1; } static void eti_attach_cell_views (ETableItem *eti) { int i; g_assert (eti->header); g_assert (eti->table_model); /* * Now realize the various ECells */ eti->n_cells = eti->cols; eti->cell_views = g_new (ECellView *, eti->n_cells); for (i = 0; i < eti->n_cells; i++){ ETableCol *ecol = e_table_header_get_column (eti->header, i); eti->cell_views [i] = e_cell_new_view (ecol->ecell, eti->table_model, eti); } eti->needs_compute_height = 1; e_canvas_item_request_reflow (GNOME_CANVAS_ITEM (eti)); eti->needs_redraw = 1; gnome_canvas_item_request_update (GNOME_CANVAS_ITEM (eti)); } /* * During unrealization: we invoke every e-cell (one per column in the current * setup) to dispose all X resources allocated */ static void eti_unrealize_cell_views (ETableItem *eti) { int i; if (eti->cell_views_realized == 0) return; for (i = 0; i < eti->n_cells; i++) e_cell_unrealize (eti->cell_views [i]); eti->cell_views_realized = 0; } static void eti_detach_cell_views (ETableItem *eti) { int i; for (i = 0; i < eti->n_cells; i++){ e_cell_kill_view (eti->cell_views [i]); eti->cell_views [i] = NULL; } g_free (eti->cell_views); eti->cell_views = NULL; eti->n_cells = 0; } static void eti_bounds (GnomeCanvasItem *item, double *x1, double *y1, double *x2, double *y2) { double i2c [6]; ArtPoint c1, c2, i1, i2; ETableItem *eti = E_TABLE_ITEM (item); /* Wrong BBox's are the source of redraw nightmares */ gnome_canvas_item_i2c_affine (GNOME_CANVAS_ITEM (eti), i2c); i1.x = eti->x1; i1.y = eti->y1; i2.x = eti->x1 + eti->width; i2.y = eti->y1 + eti->height; art_affine_point (&c1, &i1, i2c); art_affine_point (&c2, &i2, i2c); *x1 = c1.x; *y1 = c1.y; *x2 = c2.x + 1; *y2 = c2.y + 1; } static void eti_reflow (GnomeCanvasItem *item, gint flags) { ETableItem *eti = E_TABLE_ITEM (item); if (eti->needs_compute_height) { int new_height = eti_get_height (eti); if (new_height != eti->height) { eti->height = new_height; e_canvas_item_request_parent_reflow (GNOME_CANVAS_ITEM (eti)); eti->needs_redraw = 1; gnome_canvas_item_request_update (GNOME_CANVAS_ITEM (eti)); } eti->needs_compute_height = 0; } if (eti->needs_compute_width) { int new_width = e_table_header_total_width(eti->header); if (new_width != eti->width) { eti->width = new_width; e_canvas_item_request_parent_reflow (GNOME_CANVAS_ITEM (eti)); eti->needs_redraw = 1; gnome_canvas_item_request_update (GNOME_CANVAS_ITEM (eti)); } eti->needs_compute_width = 0; } } /* * GnomeCanvasItem::update method */ static void eti_update (GnomeCanvasItem *item, double *affine, ArtSVP *clip_path, int flags) { ArtPoint o1, o2; ETableItem *eti = E_TABLE_ITEM (item); if (GNOME_CANVAS_ITEM_CLASS (eti_parent_class)->update) (*GNOME_CANVAS_ITEM_CLASS (eti_parent_class)->update)(item, affine, clip_path, flags); o1.x = item->x1; o1.y = item->y1; o2.x = item->x2; o2.y = item->y2; eti_bounds (item, &item->x1, &item->y1, &item->x2, &item->y2); if (item->x1 != o1.x || item->y1 != o1.y || item->x2 != o2.x || item->y2 != o2.y) { gnome_canvas_request_redraw (item->canvas, o1.x, o1.y, o2.x, o2.y); eti->needs_redraw = 1; } if (eti->needs_redraw) { gnome_canvas_request_redraw (item->canvas, item->x1, item->y1, item->x2, item->y2); eti->needs_redraw = 0; } } /* * eti_remove_table_model: * * Invoked to release the table model associated with this ETableItem */ static void eti_remove_table_model (ETableItem *eti) { if (!eti->table_model) return; gtk_signal_disconnect (GTK_OBJECT (eti->table_model), eti->table_model_pre_change_id); gtk_signal_disconnect (GTK_OBJECT (eti->table_model), eti->table_model_change_id); gtk_signal_disconnect (GTK_OBJECT (eti->table_model), eti->table_model_row_change_id); gtk_signal_disconnect (GTK_OBJECT (eti->table_model), eti->table_model_cell_change_id); gtk_signal_disconnect (GTK_OBJECT (eti->table_model), eti->table_model_rows_inserted_id); gtk_signal_disconnect (GTK_OBJECT (eti->table_model), eti->table_model_rows_deleted_id); gtk_object_unref (GTK_OBJECT (eti->table_model)); if (eti->source_model) gtk_object_unref (GTK_OBJECT (eti->source_model)); eti->table_model_pre_change_id = 0; eti->table_model_change_id = 0; eti->table_model_row_change_id = 0; eti->table_model_cell_change_id = 0; eti->table_model_rows_inserted_id = 0; eti->table_model_rows_deleted_id = 0; eti->table_model = NULL; eti->source_model = NULL; eti->uses_source_model = 0; } /* * eti_remove_table_model: * * Invoked to release the table model associated with this ETableItem */ static void eti_remove_selection_model (ETableItem *eti) { if (!eti->selection) return; gtk_signal_disconnect (GTK_OBJECT (eti->selection), eti->selection_change_id); gtk_signal_disconnect (GTK_OBJECT (eti->selection), eti->cursor_change_id); gtk_signal_disconnect (GTK_OBJECT (eti->selection), eti->cursor_activated_id); gtk_object_unref (GTK_OBJECT (eti->selection)); eti->selection_change_id = 0; eti->cursor_activated_id = 0; eti->selection = NULL; } /* * eti_remove_header_model: * * Invoked to release the header model associated with this ETableItem */ static void eti_remove_header_model (ETableItem *eti) { if (!eti->header) return; gtk_signal_disconnect (GTK_OBJECT (eti->header), eti->header_structure_change_id); gtk_signal_disconnect (GTK_OBJECT (eti->header), eti->header_dim_change_id); gtk_signal_disconnect (GTK_OBJECT (eti->header), eti->header_request_width_id); if (eti->cell_views){ eti_unrealize_cell_views (eti); eti_detach_cell_views (eti); } gtk_object_unref (GTK_OBJECT (eti->header)); eti->header_structure_change_id = 0; eti->header_dim_change_id = 0; eti->header_request_width_id = 0; eti->header = NULL; } /* * eti_row_height_real: * * Returns the height used by row @row. This does not include the one-pixel * used as a separator between rows */ static int eti_row_height_real (ETableItem *eti, int row) { const int cols = e_table_header_count (eti->header); int col; int h, max_h; g_assert (cols == 0 || eti->cell_views); max_h = 0; for (col = 0; col < cols; col++){ h = e_cell_height (eti->cell_views [col], view_to_model_col(eti, col), col, row); if (h > max_h) max_h = h; } return max_h; } static void confirm_height_cache (ETableItem *eti) { int i; if (eti->height_cache) return; eti->height_cache = g_new(int, eti->rows); for (i = 0; i < eti->rows; i++) { eti->height_cache[i] = -1; } } static gboolean height_cache_idle(ETableItem *eti) { int changed = 0; int i; confirm_height_cache(eti); for (i = eti->height_cache_idle_count; i < eti->rows; i++) { if (eti->height_cache[i] == -1) { eti_row_height(eti, i); changed ++; if (changed >= 20) break; } } if (changed >= 20) { eti->height_cache_idle_count = i; return TRUE; } eti->height_cache_idle_id = 0; return FALSE; } static void free_height_cache (ETableItem *eti) { if (eti->height_cache) g_free (eti->height_cache); eti->height_cache = NULL; eti->height_cache_idle_count = 0; if (eti->height_cache_idle_id == 0) eti->height_cache_idle_id = g_idle_add_full(G_PRIORITY_LOW, (GSourceFunc) height_cache_idle, eti, NULL); } static void calculate_height_cache (ETableItem *eti) { free_height_cache(eti); confirm_height_cache(eti); } /* * eti_row_height: * * Returns the height used by row @row. This does not include the one-pixel * used as a separator between rows */ static int eti_row_height (ETableItem *eti, int row) { if (!eti->height_cache) { calculate_height_cache (eti); } if (eti->height_cache[row] == -1) { eti->height_cache[row] = eti_row_height_real(eti, row); if (row > 0 && eti->length_threshold != -1 && eti->rows > eti->length_threshold && eti->height_cache[row] != eti_row_height(eti, 0)) { eti->needs_compute_height = 1; e_canvas_item_request_reflow(GNOME_CANVAS_ITEM(eti)); } } return eti->height_cache[row]; } /* * eti_get_height: * * Returns the height of the ETableItem. * * The ETableItem might compute the whole height by asking every row its * size. There is a special mode (designed to work when there are too * many rows in the table that performing the previous step could take * too long) set by the ETableItem->length_threshold that would determine * when the height is computed by using the first row as the size for * every other row in the ETableItem. */ static int eti_get_height (ETableItem *eti) { const int rows = eti->rows; int row; int height; int height_extra = eti->horizontal_draw_grid ? 1 : 0; if (rows == 0) return 0; if (eti->length_threshold != -1){ if (rows > eti->length_threshold){ int row_height = eti_row_height(eti, 0); if (eti->height_cache) { height = 0; for (row = 0; row < rows; row++) { if (eti->height_cache[row] == -1) { height += (row_height + height_extra) * (rows - row); break; } else height += eti->height_cache[row] + height_extra; } } else height = (eti_row_height (eti, 0) + height_extra) * rows; /* * 1 pixel at the top */ return height + height_extra; } } height = height_extra; for (row = 0; row < rows; row++) height += eti_row_height (eti, row) + height_extra; return height; } static void eti_item_region_redraw (ETableItem *eti, int x0, int y0, int x1, int y1) { GnomeCanvasItem *item = GNOME_CANVAS_ITEM (eti); ArtDRect rect; double i2c [6]; rect.x0 = x0; rect.y0 = y0; rect.x1 = x1; rect.y1 = y1; gnome_canvas_item_i2c_affine (item, i2c); art_drect_affine_transform (&rect, &rect, i2c); gnome_canvas_request_redraw (item->canvas, rect.x0, rect.y0, rect.x1, rect.y1); } /* * Callback routine: invoked before the ETableModel has suffers a change */ static void eti_table_model_pre_change (ETableModel *table_model, ETableItem *eti) { if (eti_editing (eti)) e_table_item_leave_edit (eti); } /* * Callback routine: invoked when the ETableModel has suffered a change */ static void eti_table_model_changed (ETableModel *table_model, ETableItem *eti) { if (!(GTK_OBJECT_FLAGS(eti) & GNOME_CANVAS_ITEM_REALIZED)) return; #ifdef SHOW_ON_CHANGED int view_row; #endif eti->rows = e_table_model_row_count (eti->table_model); free_height_cache(eti); eti->needs_compute_height = 1; e_canvas_item_request_reflow (GNOME_CANVAS_ITEM (eti)); eti->needs_redraw = 1; gnome_canvas_item_request_update (GNOME_CANVAS_ITEM (eti)); #ifdef SHOW_ON_CHANGED view_row = model_to_view_row(eti, eti->cursor_row); if (view_row >= 0 && eti->cursor_col >= 0) eti_request_region_show (eti, eti->cursor_col, view_row, eti->cursor_col, view_row); #endif } /* * Computes the distance between @start_row and @end_row in pixels */ int e_table_item_row_diff (ETableItem *eti, int start_row, int end_row) { int row, total; int height_extra = eti->horizontal_draw_grid ? 1 : 0; total = 0; for (row = start_row; row < end_row; row++) total += eti_row_height (eti, row) + height_extra; return total; } /* * eti_request_region_redraw: * * Request a canvas redraw on the range (start_col, start_row) to (end_col, end_row). * This is inclusive (ie, you can use: 0,0-0,0 to redraw the first cell). * * The @border argument is a number of pixels around the region that should also be queued * for redraw. This is typically used by the focus routines to queue a redraw for the * border as well. */ static void eti_request_region_redraw (ETableItem *eti, int start_col, int start_row, int end_col, int end_row, int border) { int x1, y1, width, height; if (eti->rows > 0) { x1 = e_table_header_col_diff (eti->header, 0, start_col); y1 = e_table_item_row_diff (eti, 0, start_row); width = e_table_header_col_diff (eti->header, start_col, end_col + 1); height = e_table_item_row_diff (eti, start_row, end_row + 1); eti_item_region_redraw (eti, eti->x1 + x1 - border, eti->y1 + y1 - border, eti->x1 + x1 + width + 1 + border, eti->y1 + y1 + height + 1 + border); } } /* * eti_request_region_show * * Request a canvas show on the range (start_col, start_row) to (end_col, end_row). * This is inclusive (ie, you can use: 0,0-0,0 to show the first cell). */ static void eti_request_region_show (ETableItem *eti, int start_col, int start_row, int end_col, int end_row, int delay) { int x1, y1, x2, y2; x1 = e_table_header_col_diff (eti->header, 0, start_col); y1 = e_table_item_row_diff (eti, 0, start_row); x2 = x1 + e_table_header_col_diff (eti->header, start_col, end_col + 1); y2 = y1 + e_table_item_row_diff (eti, start_row, end_row + 1); if (delay) e_canvas_item_show_area_delayed(GNOME_CANVAS_ITEM(eti), x1, y1, x2, y2, delay); else e_canvas_item_show_area(GNOME_CANVAS_ITEM(eti), x1, y1, x2, y2); } static void eti_table_model_row_changed (ETableModel *table_model, int row, ETableItem *eti) { if (!(GTK_OBJECT_FLAGS(eti) & GNOME_CANVAS_ITEM_REALIZED)) return; if (eti->renderers_can_change_size && eti->height_cache && eti->height_cache[row] != -1 && eti_row_height_real(eti, row) != eti->height_cache[row]) { eti_table_model_changed (table_model, eti); return; } eti_request_region_redraw (eti, 0, row, eti->cols - 1, row, 0); } static void eti_table_model_cell_changed (ETableModel *table_model, int col, int row, ETableItem *eti) { if (!(GTK_OBJECT_FLAGS(eti) & GNOME_CANVAS_ITEM_REALIZED)) return; if (eti->renderers_can_change_size && eti->height_cache && eti->height_cache[row] != -1 && eti_row_height_real(eti, row) != eti->height_cache[row]) { eti_table_model_changed (table_model, eti); return; } eti_request_region_redraw (eti, 0, row, eti->cols - 1, row, 0); } static void eti_table_model_rows_inserted (ETableModel *table_model, int row, int count, ETableItem *eti) { if (!(GTK_OBJECT_FLAGS(eti) & GNOME_CANVAS_ITEM_REALIZED)) return; eti->rows = e_table_model_row_count (eti->table_model); if (eti->height_cache) { int i; eti->height_cache = g_renew(int, eti->height_cache, eti->rows); memmove(eti->height_cache + row + count, eti->height_cache + row, (eti->rows - count - row) * sizeof(int)); for (i = row; i < row + count; i++) eti->height_cache[i] = -1; } eti->needs_compute_height = 1; e_canvas_item_request_reflow (GNOME_CANVAS_ITEM (eti)); eti->needs_redraw = 1; gnome_canvas_item_request_update (GNOME_CANVAS_ITEM (eti)); } static void eti_table_model_rows_deleted (ETableModel *table_model, int row, int count, ETableItem *eti) { if (!(GTK_OBJECT_FLAGS(eti) & GNOME_CANVAS_ITEM_REALIZED)) return; eti->rows = e_table_model_row_count (eti->table_model); if (eti->height_cache) memmove(eti->height_cache + row, eti->height_cache + row + count, (eti->rows - row) * sizeof(int)); eti->needs_compute_height = 1; e_canvas_item_request_reflow (GNOME_CANVAS_ITEM (eti)); eti->needs_redraw = 1; gnome_canvas_item_request_update (GNOME_CANVAS_ITEM (eti)); } /** * e_table_item_redraw_range * @eti: %ETableItem which will be redrawn * @start_col: The first col to redraw. * @start_row: The first row to redraw. * @end_col: The last col to redraw. * @end_row: The last row to redraw. * * This routine redraws the given %ETableItem in the range given. The * range is inclusive at both ends. */ void e_table_item_redraw_range (ETableItem *eti, int start_col, int start_row, int end_col, int end_row) { int border; int cursor_col, cursor_row; g_return_if_fail (eti != NULL); g_return_if_fail (E_IS_TABLE_ITEM (eti)); gtk_object_get(GTK_OBJECT(eti->selection), "cursor_col", &cursor_col, "cursor_row", &cursor_row, NULL); if ((start_col == cursor_col) || (end_col == cursor_col) || (view_to_model_row(eti, start_row) == cursor_row) || (view_to_model_row(eti, end_row) == cursor_row)) border = 2; else border = 0; eti_request_region_redraw(eti, start_col, start_row, end_col, end_row, border); } static void eti_add_table_model (ETableItem *eti, ETableModel *table_model) { g_assert (eti->table_model == NULL); eti->table_model = table_model; gtk_object_ref (GTK_OBJECT (eti->table_model)); eti->table_model_pre_change_id = gtk_signal_connect ( GTK_OBJECT (table_model), "model_pre_change", GTK_SIGNAL_FUNC (eti_table_model_pre_change), eti); eti->table_model_change_id = gtk_signal_connect ( GTK_OBJECT (table_model), "model_changed", GTK_SIGNAL_FUNC (eti_table_model_changed), eti); eti->table_model_row_change_id = gtk_signal_connect ( GTK_OBJECT (table_model), "model_row_changed", GTK_SIGNAL_FUNC (eti_table_model_row_changed), eti); eti->table_model_cell_change_id = gtk_signal_connect ( GTK_OBJECT (table_model), "model_cell_changed", GTK_SIGNAL_FUNC (eti_table_model_cell_changed), eti); eti->table_model_rows_inserted_id = gtk_signal_connect ( GTK_OBJECT (table_model), "model_rows_inserted", GTK_SIGNAL_FUNC (eti_table_model_rows_inserted), eti); eti->table_model_rows_deleted_id = gtk_signal_connect ( GTK_OBJECT (table_model), "model_rows_deleted", GTK_SIGNAL_FUNC (eti_table_model_rows_deleted), eti); if (eti->header) { eti_detach_cell_views (eti); eti_attach_cell_views (eti); } if (E_IS_TABLE_SUBSET(table_model)) { eti->uses_source_model = 1; eti->source_model = E_TABLE_SUBSET(table_model)->source; if (eti->source_model) gtk_object_ref(GTK_OBJECT(eti->source_model)); } eti_table_model_changed (table_model, eti); } static void eti_add_selection_model (ETableItem *eti, ESelectionModel *selection) { g_assert (eti->selection == NULL); eti->selection = selection; gtk_object_ref (GTK_OBJECT (eti->selection)); eti->selection_change_id = gtk_signal_connect ( GTK_OBJECT (selection), "selection_changed", GTK_SIGNAL_FUNC (eti_selection_change), eti); eti->cursor_change_id = gtk_signal_connect ( GTK_OBJECT (selection), "cursor_changed", GTK_SIGNAL_FUNC (eti_cursor_change), eti); eti->cursor_activated_id = gtk_signal_connect ( GTK_OBJECT (selection), "cursor_activated", GTK_SIGNAL_FUNC (eti_cursor_activated), eti); eti_selection_change(selection, eti); } static void eti_header_dim_changed (ETableHeader *eth, int col, ETableItem *eti) { eti->needs_compute_width = 1; e_canvas_item_request_reflow (GNOME_CANVAS_ITEM (eti)); eti->needs_redraw = 1; gnome_canvas_item_request_update (GNOME_CANVAS_ITEM (eti)); } static void eti_header_structure_changed (ETableHeader *eth, ETableItem *eti) { eti->cols = e_table_header_count (eti->header); /* * There should be at least one column * BUT: then you can't remove all columns from a header and add new ones. */ /*g_assert (eti->cols != 0);*/ if (eti->cell_views){ eti_unrealize_cell_views (eti); eti_detach_cell_views (eti); eti_attach_cell_views (eti); eti_realize_cell_views (eti); } else { if (eti->table_model) { eti_attach_cell_views (eti); eti_realize_cell_views (eti); } } eti->needs_compute_width = 1; e_canvas_item_request_reflow (GNOME_CANVAS_ITEM (eti)); eti->needs_redraw = 1; gnome_canvas_item_request_update (GNOME_CANVAS_ITEM (eti)); } static int eti_request_column_width (ETableHeader *eth, int col, ETableItem *eti) { int width = 0; if (eti->cell_views) { width = e_cell_max_width (eti->cell_views[col], view_to_model_col(eti, col), col); } return width; } static void eti_add_header_model (ETableItem *eti, ETableHeader *header) { g_assert (eti->header == NULL); eti->header = header; gtk_object_ref (GTK_OBJECT (header)); eti_header_structure_changed (header, eti); eti->header_dim_change_id = gtk_signal_connect ( GTK_OBJECT (header), "dimension_change", GTK_SIGNAL_FUNC (eti_header_dim_changed), eti); eti->header_structure_change_id = gtk_signal_connect ( GTK_OBJECT (header), "structure_change", GTK_SIGNAL_FUNC (eti_header_structure_changed), eti); eti->header_request_width_id = gtk_signal_connect (GTK_OBJECT (header), "request_width", GTK_SIGNAL_FUNC (eti_request_column_width), eti); } /* * GtkObject::destroy method */ static void eti_destroy (GtkObject *object) { ETableItem *eti = E_TABLE_ITEM (object); eti_remove_header_model (eti); eti_remove_table_model (eti); eti_remove_selection_model (eti); if (eti->height_cache_idle_id) g_source_remove(eti->height_cache_idle_id); if (eti->height_cache) g_free (eti->height_cache); eti->height_cache = NULL; eti->height_cache_idle_count = 0; e_canvas_hide_tooltip (E_CANVAS(GNOME_CANVAS_ITEM(eti)->canvas)); if (eti->tooltip->timer) { gtk_timeout_remove (eti->tooltip->timer); eti->tooltip->timer = 0; } g_free (eti->tooltip); if (GTK_OBJECT_CLASS (eti_parent_class)->destroy) (*GTK_OBJECT_CLASS (eti_parent_class)->destroy) (object); } static void eti_set_arg (GtkObject *o, GtkArg *arg, guint arg_id) { GnomeCanvasItem *item; ETableItem *eti; int cursor_col; item = GNOME_CANVAS_ITEM (o); eti = E_TABLE_ITEM (o); switch (arg_id){ case ARG_TABLE_HEADER: eti_remove_header_model (eti); eti_add_header_model (eti, E_TABLE_HEADER(GTK_VALUE_OBJECT (*arg))); break; case ARG_TABLE_MODEL: eti_remove_table_model (eti); eti_add_table_model (eti, E_TABLE_MODEL(GTK_VALUE_OBJECT (*arg))); break; case ARG_SELECTION_MODEL: eti_remove_selection_model (eti); if (GTK_VALUE_OBJECT (*arg)) eti_add_selection_model (eti, E_SELECTION_MODEL(GTK_VALUE_OBJECT (*arg))); break; case ARG_LENGTH_THRESHOLD: eti->length_threshold = GTK_VALUE_INT (*arg); break; case ARG_TABLE_HORIZONTAL_DRAW_GRID: eti->horizontal_draw_grid = GTK_VALUE_BOOL (*arg); break; case ARG_TABLE_VERTICAL_DRAW_GRID: eti->vertical_draw_grid = GTK_VALUE_BOOL (*arg); break; case ARG_TABLE_DRAW_FOCUS: eti->draw_focus = GTK_VALUE_BOOL (*arg); break; case ARG_CURSOR_MODE: eti->cursor_mode = GTK_VALUE_INT (*arg); break; case ARG_MINIMUM_WIDTH: case ARG_WIDTH: if ((eti->minimum_width == eti->width && GTK_VALUE_DOUBLE (*arg) > eti->width) || GTK_VALUE_DOUBLE (*arg) < eti->width) { eti->needs_compute_width = 1; e_canvas_item_request_reflow (GNOME_CANVAS_ITEM(eti)); } eti->minimum_width = GTK_VALUE_DOUBLE (*arg); break; case ARG_CURSOR_ROW: gtk_object_get(GTK_OBJECT(eti->selection), "cursor_col", &cursor_col, NULL); e_table_item_focus (eti, cursor_col != -1 ? cursor_col : 0, view_to_model_row(eti, GTK_VALUE_INT (*arg)), 0); break; } eti->needs_redraw = 1; gnome_canvas_item_request_update (GNOME_CANVAS_ITEM(eti)); } static void eti_get_arg (GtkObject *o, GtkArg *arg, guint arg_id) { GnomeCanvasItem *item; ETableItem *eti; int row; item = GNOME_CANVAS_ITEM (o); eti = E_TABLE_ITEM (o); switch (arg_id){ case ARG_WIDTH: GTK_VALUE_DOUBLE (*arg) = eti->width; break; case ARG_HEIGHT: GTK_VALUE_DOUBLE (*arg) = eti->height; break; case ARG_MINIMUM_WIDTH: GTK_VALUE_DOUBLE (*arg) = eti->minimum_width; break; case ARG_CURSOR_ROW: gtk_object_get(GTK_OBJECT(eti->selection), "cursor_row", &row, NULL); GTK_VALUE_INT (*arg) = model_to_view_row(eti, row); break; default: arg->type = GTK_TYPE_INVALID; } } static void eti_init (GnomeCanvasItem *item) { ETableItem *eti = E_TABLE_ITEM (item); eti->editing_col = -1; eti->editing_row = -1; eti->height = 0; eti->width = 0; eti->minimum_width = 0; eti->height_cache = NULL; eti->height_cache_idle_id = 0; eti->height_cache_idle_count = 0; eti->length_threshold = -1; eti->renderers_can_change_size = 1; eti->uses_source_model = 0; eti->source_model = NULL; eti->row_guess = -1; eti->cursor_mode = E_CURSOR_SIMPLE; eti->selection_change_id = 0; eti->cursor_change_id = 0; eti->cursor_activated_id = 0; eti->selection = NULL; eti->needs_redraw = 0; eti->needs_compute_height = 0; eti->in_key_press = 0; eti->tooltip = g_new0 (ETableTooltip, 1); eti->tooltip->timer = 0; eti->tooltip->eti = GNOME_CANVAS_ITEM (eti); eti->grabbed_col = -1; eti->grabbed_row = -1; e_canvas_item_set_reflow_callback (GNOME_CANVAS_ITEM (eti), eti_reflow); } #define gray50_width 2 #define gray50_height 2 static const char gray50_bits[] = { 0x02, 0x01, }; static void eti_realize (GnomeCanvasItem *item) { ETableItem *eti = E_TABLE_ITEM (item); GtkWidget *canvas_widget = GTK_WIDGET (item->canvas); GdkWindow *window; if (GNOME_CANVAS_ITEM_CLASS (eti_parent_class)->realize) (*GNOME_CANVAS_ITEM_CLASS (eti_parent_class)->realize)(item); eti->rows = e_table_model_row_count (eti->table_model); /* * Gdk Resource allocation */ window = canvas_widget->window; eti->fill_gc = gdk_gc_new (window); eti->grid_gc = gdk_gc_new (window); gdk_gc_set_foreground (eti->grid_gc, &canvas_widget->style->dark [GTK_STATE_NORMAL]); eti->focus_gc = gdk_gc_new (window); gdk_gc_set_foreground (eti->focus_gc, &canvas_widget->style->bg [GTK_STATE_NORMAL]); gdk_gc_set_background (eti->focus_gc, &canvas_widget->style->fg [GTK_STATE_NORMAL]); eti->stipple = gdk_bitmap_create_from_data (NULL, gray50_bits, gray50_width, gray50_height); gdk_gc_set_ts_origin (eti->focus_gc, 0, 0); gdk_gc_set_stipple (eti->focus_gc, eti->stipple); gdk_gc_set_fill (eti->focus_gc, GDK_OPAQUE_STIPPLED); if (eti->cell_views == NULL) eti_attach_cell_views (eti); eti_realize_cell_views (eti); free_height_cache(eti); eti->needs_compute_height = 1; eti->needs_compute_width = 1; e_canvas_item_request_reflow (GNOME_CANVAS_ITEM (eti)); eti->needs_redraw = 1; gnome_canvas_item_request_update (GNOME_CANVAS_ITEM (eti)); } static void eti_unrealize (GnomeCanvasItem *item) { ETableItem *eti = E_TABLE_ITEM (item); if (eti_editing (eti)) e_table_item_leave_edit (eti); gdk_gc_unref (eti->fill_gc); eti->fill_gc = NULL; gdk_gc_unref (eti->grid_gc); eti->grid_gc = NULL; gdk_gc_unref (eti->focus_gc); eti->focus_gc = NULL; gdk_bitmap_unref (eti->stipple); eti->stipple = NULL; eti_unrealize_cell_views (eti); eti->height = 0; if (GNOME_CANVAS_ITEM_CLASS (eti_parent_class)->unrealize) (*GNOME_CANVAS_ITEM_CLASS (eti_parent_class)->unrealize)(item); } static void eti_draw (GnomeCanvasItem *item, GdkDrawable *drawable, int x, int y, int width, int height) { ETableItem *eti = E_TABLE_ITEM (item); const int rows = eti->rows; const int cols = eti->cols; int row, col, y1, y2; int first_col, last_col, x_offset; int first_row, last_row, y_offset, yd; int x1, x2; int f_x1, f_x2, f_y1, f_y2; gboolean f_found; double i2c [6]; ArtPoint eti_base, eti_base_item, lower_right; GtkWidget *canvas = GTK_WIDGET(item->canvas); int height_extra = eti->horizontal_draw_grid ? 1 : 0; /* * Find out our real position after grouping */ gnome_canvas_item_i2c_affine (item, i2c); eti_base_item.x = eti->x1; eti_base_item.y = eti->y1; art_affine_point (&eti_base, &eti_base_item, i2c); eti_base_item.x = eti->x1 + eti->width; eti_base_item.y = eti->y1 + eti->height; art_affine_point (&lower_right, &eti_base_item, i2c); /* * First column to draw, last column to draw */ first_col = -1; last_col = x_offset = 0; x1 = x2 = floor (eti_base.x); for (col = 0; col < cols; col++, x1 = x2){ ETableCol *ecol = e_table_header_get_column (eti->header, col); x2 = x1 + ecol->width; if (x1 > (x + width)) break; if (x2 < x) continue; if (first_col == -1){ x_offset = x1 - x; first_col = col; } } last_col = col; /* * Nothing to paint */ if (first_col == -1) return; /* * Compute row span. */ first_row = -1; y_offset = 0; y1 = y2 = floor (eti_base.y) + height_extra; for (row = 0; row < rows; row++, y1 = y2){ y2 += ETI_ROW_HEIGHT (eti, row) + height_extra; if (y1 > y + height) break; if (y2 < y) continue; if (first_row == -1){ y_offset = y1 - y; first_row = row; } } last_row = row; if (first_row == -1) return; /* * Draw cells */ yd = y_offset; f_x1 = f_x2 = f_y1 = f_y2 = -1; f_found = FALSE; if (eti->horizontal_draw_grid && first_row == 0){ gdk_draw_line ( drawable, eti->grid_gc, eti_base.x - x, yd, eti_base.x + eti->width - x, yd); } yd += height_extra; for (row = first_row; row < last_row; row++){ int xd, height; gboolean selected; gint cursor_col, cursor_row; height = ETI_ROW_HEIGHT (eti, row); xd = x_offset; /* printf ("paint: %d %d\n", yd, yd + height); */ selected = e_selection_model_is_row_selected(E_SELECTION_MODEL (eti->selection), view_to_model_row(eti,row)); gtk_object_get(GTK_OBJECT(eti->selection), "cursor_col", &cursor_col, "cursor_row", &cursor_row, NULL); for (col = first_col; col < last_col; col++){ ETableCol *ecol = e_table_header_get_column (eti->header, col); ECellView *ecell_view = eti->cell_views [col]; gboolean col_selected = selected; ECellFlags flags; gboolean free_background = FALSE; GdkColor *background; switch (eti->cursor_mode) { case E_CURSOR_SIMPLE: case E_CURSOR_SPREADSHEET: if (cursor_col == ecol->col_idx && cursor_row == view_to_model_row(eti, row)) col_selected = !col_selected; break; case E_CURSOR_LINE: /* Nothing */ break; } if (col_selected){ if (GTK_WIDGET_HAS_FOCUS(canvas)) background = &canvas->style->bg [GTK_STATE_SELECTED]; else background = &canvas->style->bg [GTK_STATE_ACTIVE]; } else { background = &canvas->style->base [GTK_STATE_NORMAL]; } #ifdef ALTERNATE_COLORS if (row % 2) { } else { free_background = TRUE; background = gdk_color_copy (background); e_hsv_tweak (background, 0.0f, 0.0f, -0.05f); gdk_color_alloc (gtk_widget_get_colormap (GTK_WIDGET (canvas)), background); } #endif gdk_gc_set_foreground (eti->fill_gc, background); gdk_draw_rectangle (drawable, eti->fill_gc, TRUE, xd, yd, ecol->width, height); if (free_background) gdk_color_free (background); flags = col_selected ? E_CELL_SELECTED : 0; flags |= GTK_WIDGET_HAS_FOCUS(canvas) ? E_CELL_FOCUSED : 0; switch (ecol->justification) { case GTK_JUSTIFY_LEFT: flags |= E_CELL_JUSTIFY_LEFT; break; case GTK_JUSTIFY_RIGHT: flags |= E_CELL_JUSTIFY_RIGHT; break; case GTK_JUSTIFY_CENTER: flags |= E_CELL_JUSTIFY_CENTER; break; case GTK_JUSTIFY_FILL: flags |= E_CELL_JUSTIFY_FILL; break; } e_cell_draw (ecell_view, drawable, ecol->col_idx, col, row, flags, xd, yd, xd + ecol->width, yd + height); if (!f_found) { switch (eti->cursor_mode) { case E_CURSOR_LINE: if (view_to_model_row(eti, row) == cursor_row) { f_x1 = floor (eti_base.x) - x; f_x2 = floor (lower_right.x) - x; f_y1 = yd; f_y2 = yd + height; f_found = TRUE; } break; case E_CURSOR_SIMPLE: case E_CURSOR_SPREADSHEET: if (view_to_model_col(eti, col) == cursor_col && view_to_model_row(eti, row) == cursor_row) { f_x1 = xd; f_x2 = xd + ecol->width; f_y1 = yd; f_y2 = yd + height; f_found = TRUE; } break; } } xd += ecol->width; } yd += height; if (eti->horizontal_draw_grid) { gdk_draw_line ( drawable, eti->grid_gc, eti_base.x - x, yd, eti_base.x + eti->width - x, yd); yd++; } } if (eti->vertical_draw_grid){ int xd = x_offset; for (col = first_col; col <= last_col; col++){ ETableCol *ecol = e_table_header_get_column (eti->header, col); gdk_draw_line ( drawable, eti->grid_gc, xd, y_offset, xd, yd - 1); /* * This looks wierd, but it is to draw the last line */ if (ecol) xd += ecol->width; } } /* * Draw focus */ if (eti->draw_focus && f_found) { gdk_gc_set_ts_origin (eti->focus_gc, f_x1, f_y1); gdk_draw_rectangle (drawable, eti->focus_gc, FALSE, f_x1, f_y1, f_x2 - f_x1 - 1, f_y2 - f_y1 - 1); } } static double eti_point (GnomeCanvasItem *item, double x, double y, int cx, int cy, GnomeCanvasItem **actual_item) { *actual_item = item; return 0.0; } static gboolean find_cell (ETableItem *eti, double x, double y, int *col_res, int *row_res, double *x1_res, double *y1_res) { const int cols = eti->cols; const int rows = eti->rows; gdouble x1, y1, x2, y2; int col, row; int height_extra = eti->horizontal_draw_grid ? 1 : 0; /* FIXME: this routine is inneficient, fix later */ if (eti->grabbed_col >= 0 && eti->grabbed_row >= 0) { *col_res = eti->grabbed_col; *row_res = eti->grabbed_row; *x1_res = x - eti->x1 - e_table_header_col_diff (eti->header, 0, eti->grabbed_col); *y1_res = y - eti->y1 - e_table_item_row_diff (eti, 0, eti->grabbed_row); return TRUE; } if (cols == 0 || rows == 0) return FALSE; x -= eti->x1; y -= eti->y1; x1 = 0; for (col = 0; col < cols - 1; col++, x1 = x2){ ETableCol *ecol = e_table_header_get_column (eti->header, col); if (x < x1) return FALSE; x2 = x1 + ecol->width; if (x <= x2) break; } y1 = y2 = 0; for (row = 0; row < rows - 1; row++, y1 = y2){ if (y < y1) return FALSE; y2 += ETI_ROW_HEIGHT (eti, row) + height_extra; if (y <= y2) break; } *col_res = col; if (x1_res) *x1_res = x - x1; *row_res = row; if (y1_res) *y1_res = y - y1; return TRUE; } static void eti_cursor_move (ETableItem *eti, gint row, gint column) { e_table_item_leave_edit (eti); e_table_item_focus (eti, view_to_model_col(eti, column), view_to_model_row(eti, row), 0); } static void eti_cursor_move_left (ETableItem *eti) { int cursor_col, cursor_row; gtk_object_get(GTK_OBJECT(eti->selection), "cursor_col", &cursor_col, "cursor_row", &cursor_row, NULL); eti_cursor_move (eti, model_to_view_row(eti, cursor_row), model_to_view_col(eti, cursor_col) - 1); } static void eti_cursor_move_right (ETableItem *eti) { int cursor_col, cursor_row; gtk_object_get(GTK_OBJECT(eti->selection), "cursor_col", &cursor_col, "cursor_row", &cursor_row, NULL); eti_cursor_move (eti, model_to_view_row(eti, cursor_row), model_to_view_col(eti, cursor_col) + 1); } #ifdef DO_TOOLTIPS static int _do_tooltip (ETableItem *eti) { ECellView *ecell_view; int x = 0, y = 0; int i; int height_extra = eti->horizontal_draw_grid ? 1 : 0; e_canvas_hide_tooltip (E_CANVAS(GNOME_CANVAS_ITEM(eti)->canvas)); if (eti_editing (eti)) return FALSE; ecell_view = eti->cell_views[eti->tooltip->col]; for (i = 0; i < eti->tooltip->col; i++) x += eti->header->columns[i]->width; eti->tooltip->x = x; for (i = 0; i < eti->tooltip->row; i++) y += (ETI_ROW_HEIGHT (eti, i) + height_extra); eti->tooltip->y = y; eti->tooltip->row_height = ETI_ROW_HEIGHT (eti, i); e_cell_show_tooltip (ecell_view, view_to_model_col (eti, eti->tooltip->col), eti->tooltip->col, eti->tooltip->row, eti->header->columns[eti->tooltip->col]->width, eti->tooltip); return FALSE; } #endif static gint eti_e_cell_event (ETableItem *item, ECellView *ecell_view, GdkEvent *event, int time, int model_col, int view_col, int row, ECellFlags flags) { ECellActions actions = 0; gint ret_val; ret_val = e_cell_event (ecell_view, event, model_col, view_col, row, flags, &actions); if (actions & E_CELL_GRAB) { gnome_canvas_item_grab(GNOME_CANVAS_ITEM(item), GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK, NULL, time); item->grabbed_col = view_col; item->grabbed_row = row; } if (actions & E_CELL_UNGRAB) { gnome_canvas_item_ungrab(GNOME_CANVAS_ITEM(item), time); item->grabbed_col = -1; item->grabbed_row = -1; } return ret_val; } /* FIXME: cursor */ static int eti_event (GnomeCanvasItem *item, GdkEvent *e) { ETableItem *eti = E_TABLE_ITEM (item); ECellView *ecell_view; gint return_val = TRUE; switch (e->type){ case GDK_BUTTON_PRESS: { double x1, y1; GdkEventButton button; int col, row; gint cursor_row, cursor_col; if (eti->tooltip->timer) { gtk_timeout_remove (eti->tooltip->timer); eti->tooltip->timer = 0; } switch (e->button.button) { case 1: /* Fall through. */ case 2: e_canvas_item_grab_focus(GNOME_CANVAS_ITEM(eti), TRUE); gnome_canvas_item_w2i (item, &e->button.x, &e->button.y); if (!find_cell (eti, e->button.x, e->button.y, &col, &row, &x1, &y1)) { if (eti_editing (eti)) e_table_item_leave_edit (eti); return TRUE; } ecell_view = eti->cell_views [col]; button = *(GdkEventButton *)e; button.x = x1; button.y = y1; return_val = eti_e_cell_event (eti, ecell_view, (GdkEvent *) &button, button.time, view_to_model_col(eti, col), col, row, 0); if (return_val) return TRUE; return_val = FALSE; gtk_signal_emit (GTK_OBJECT (eti), eti_signals [CLICK], row, view_to_model_col(eti, col), &button, &return_val); if (return_val) return TRUE; e_selection_model_do_something(E_SELECTION_MODEL (eti->selection), view_to_model_row(eti, row), view_to_model_col(eti, col), button.state); gtk_object_get(GTK_OBJECT(eti->selection), "cursor_row", &cursor_row, "cursor_col", &cursor_col, NULL); if (cursor_row == view_to_model_row(eti, row) && cursor_col == view_to_model_col(eti, col)){ if ((!eti_editing(eti)) && e_table_model_is_cell_editable(eti->table_model, col, row)) { e_table_item_enter_edit (eti, col, row); } /* * Adjust the event positions */ return_val = eti_e_cell_event (eti, ecell_view, (GdkEvent *) &button, button.time, view_to_model_col(eti, col), col, row, E_CELL_EDITING); } d(g_print("Single click\n")); break; case 3: e_canvas_item_grab_focus(GNOME_CANVAS_ITEM(eti), TRUE); gnome_canvas_item_w2i (item, &e->button.x, &e->button.y); if (!find_cell (eti, e->button.x, e->button.y, &col, &row, &x1, &y1)) return TRUE; e_selection_model_maybe_do_something(E_SELECTION_MODEL (eti->selection), view_to_model_row(eti, row), view_to_model_col(eti, col), 0); gtk_signal_emit (GTK_OBJECT (eti), eti_signals [RIGHT_CLICK], row, view_to_model_col(eti, col), e, &return_val); break; case 4: case 5: return FALSE; break; } break; } case GDK_BUTTON_RELEASE: { double x1, y1; int col, row; gint cursor_row, cursor_col; if (eti->tooltip->timer) { gtk_timeout_remove (eti->tooltip->timer); eti->tooltip->timer = 0; } e_canvas_hide_tooltip (E_CANVAS(GNOME_CANVAS_ITEM(eti)->canvas)); switch (e->button.button) { case 1: /* Fall through. */ case 2: gnome_canvas_item_w2i (item, &e->button.x, &e->button.y); if (!find_cell (eti, e->button.x, e->button.y, &col, &row, &x1, &y1)) return TRUE; gtk_object_get(GTK_OBJECT(eti->selection), "cursor_row", &cursor_row, "cursor_col", &cursor_col, NULL); if (cursor_row == view_to_model_row(eti, row) && cursor_col == view_to_model_col(eti, col)){ ecell_view = eti->cell_views [col]; /* * Adjust the event positions */ e->button.x = x1; e->button.y = y1; return_val = eti_e_cell_event (eti, ecell_view, e, e->button.time, view_to_model_col(eti, col), col, row, E_CELL_EDITING); } d(g_print ("Release\n")); break; case 3: case 4: case 5: return FALSE; break; } break; } case GDK_2BUTTON_PRESS: { int col, row; GdkEventButton button; if (e->button.button == 5 || e->button.button == 4) return FALSE; gnome_canvas_item_w2i (item, &e->button.x, &e->button.y); #if 0 if (!find_cell (eti, e->button.x, e->button.y, &col, &row, &x1, &y1)) return TRUE; #endif gtk_object_get(GTK_OBJECT(eti->selection), "cursor_row", &row, "cursor_col", &col, NULL); #if 0 button = *(GdkEventButton *)e; button.x = x1; button.y = y1; #endif gtk_signal_emit (GTK_OBJECT (eti), eti_signals [DOUBLE_CLICK], row, col, &button); d(g_print("Double click\n")); break; } case GDK_MOTION_NOTIFY: { int col, row; double x1, y1; gint cursor_col, cursor_row; gnome_canvas_item_w2i (item, &e->motion.x, &e->motion.y); if (!find_cell (eti, e->motion.x, e->motion.y, &col, &row, &x1, &y1)) return TRUE; gtk_object_get(GTK_OBJECT(eti->selection), "cursor_row", &cursor_row, "cursor_col", &cursor_col, NULL); e_canvas_hide_tooltip (E_CANVAS(GNOME_CANVAS_ITEM(eti)->canvas)); #ifdef DO_TOOLTIPS if (eti->tooltip->timer > 0) gtk_timeout_remove (eti->tooltip->timer); eti->tooltip->col = col; eti->tooltip->row = row; eti->tooltip->cx = e->motion.x; eti->tooltip->cy = e->motion.y; eti->tooltip->timer = gtk_timeout_add (100, (GSourceFunc)_do_tooltip, eti); #endif if (cursor_row == view_to_model_row(eti, row) && cursor_col == view_to_model_col(eti, col)){ ecell_view = eti->cell_views [col]; /* * Adjust the event positions */ e->motion.x = x1; e->motion.y = y1; return_val = eti_e_cell_event (eti, ecell_view, e, e->motion.time, view_to_model_col(eti, col), col, row, E_CELL_EDITING); } break; } case GDK_KEY_PRESS: { gint cursor_row, cursor_col; gint handled = TRUE; gtk_object_get(GTK_OBJECT(eti->selection), "cursor_row", &cursor_row, "cursor_col", &cursor_col, NULL); e_canvas_hide_tooltip (E_CANVAS(GNOME_CANVAS_ITEM(eti)->canvas)); if (cursor_col == -1) return FALSE; eti->in_key_press = TRUE; switch (e->key.keyval){ case GDK_Left: if (eti_editing (eti)) { handled = FALSE; break; } gtk_signal_emit (GTK_OBJECT (eti), eti_signals [KEY_PRESS], model_to_view_row(eti, cursor_row), cursor_col, e, &return_val); if ((!return_val) && eti->cursor_mode != E_CURSOR_LINE && cursor_col != view_to_model_col(eti, 0)) eti_cursor_move_left (eti); break; case GDK_Right: if (eti_editing (eti)) { handled = FALSE; break; } gtk_signal_emit (GTK_OBJECT (eti), eti_signals [KEY_PRESS], model_to_view_row(eti, cursor_row), cursor_col, e, &return_val); if ((!return_val) && eti->cursor_mode != E_CURSOR_LINE && cursor_col != view_to_model_col(eti, eti->cols - 1)) eti_cursor_move_right (eti); break; case GDK_Up: case GDK_Down: return_val = e_selection_model_key_press(E_SELECTION_MODEL (eti->selection), (GdkEventKey *) e); break; case GDK_Home: case GDK_KP_Home: if (eti->cursor_mode != E_CURSOR_LINE) { eti_cursor_move (eti, model_to_view_row(eti, cursor_row), 0); return_val = TRUE; } else return_val = e_selection_model_key_press(E_SELECTION_MODEL (eti->selection), (GdkEventKey *) e); break; case GDK_End: case GDK_KP_End: if (eti->cursor_mode != E_CURSOR_LINE) { eti_cursor_move (eti, model_to_view_row(eti, cursor_row), eti->cols - 1); return_val = TRUE; } else return_val = e_selection_model_key_press(E_SELECTION_MODEL (eti->selection), (GdkEventKey *) e); break; case GDK_Tab: case GDK_KP_Tab: case GDK_ISO_Left_Tab: if (eti->cursor_mode == E_CURSOR_SPREADSHEET) { if ((e->key.state & GDK_SHIFT_MASK) != 0){ /* shift tab */ if (cursor_col != view_to_model_col(eti, 0)) eti_cursor_move_left (eti); else if (cursor_row != view_to_model_row(eti, 0)) eti_cursor_move (eti, model_to_view_row(eti, cursor_row) - 1, eti->cols - 1); else return_val = FALSE; } else { if (cursor_col != view_to_model_col (eti, eti->cols - 1)) eti_cursor_move_right (eti); else if (cursor_row != view_to_model_row(eti, eti->rows - 1)) eti_cursor_move (eti, model_to_view_row(eti, cursor_row) + 1, 0); else return_val = FALSE; } gtk_object_get(GTK_OBJECT(eti->selection), "cursor_row", &cursor_row, "cursor_col", &cursor_col, NULL); if (cursor_col >= 0 && cursor_row >= 0 && return_val && (!eti_editing(eti)) && e_table_model_is_cell_editable(eti->table_model, model_to_view_col(eti, cursor_col), model_to_view_row(eti, cursor_row))) { e_table_item_enter_edit (eti, model_to_view_col(eti, cursor_col), model_to_view_row(eti, cursor_row)); } break; } else { /* Let tab send you to the next widget. */ return_val = FALSE; break; } case GDK_Return: case GDK_KP_Enter: case GDK_ISO_Enter: case GDK_3270_Enter: if (eti_editing (eti)){ e_table_item_leave_edit (eti); #if 0 ecell_view = eti->cell_views [eti->editing_col]; return_val = eti_e_cell_event (eti, ecell_view, e, e->key.time, view_to_model_col(eti, eti->editing_col), eti->editing_col, eti->editing_row, E_CELL_EDITING); #endif } gtk_signal_emit (GTK_OBJECT (eti), eti_signals [KEY_PRESS], model_to_view_row(eti, cursor_row), cursor_col, e, &return_val); if (!return_val) return_val = e_selection_model_key_press(E_SELECTION_MODEL (eti->selection), (GdkEventKey *) e); break; default: handled = FALSE; break; } if (!handled) { if (!eti_editing (eti)){ gint col, row; row = model_to_view_row(eti, cursor_row); col = model_to_view_col(eti, cursor_col); if (col != -1 && row != -1 && e_table_model_is_cell_editable(eti->table_model, col, row)) { e_table_item_enter_edit (eti, col, row); } } if (!eti_editing (eti)){ gtk_signal_emit (GTK_OBJECT (eti), eti_signals [KEY_PRESS], model_to_view_row(eti, cursor_row), cursor_col, e, &return_val); if (!return_val) e_selection_model_key_press(E_SELECTION_MODEL (eti->selection), (GdkEventKey *) e); } else { ecell_view = eti->cell_views [eti->editing_col]; return_val = eti_e_cell_event (eti, ecell_view, e, e->key.time, view_to_model_col(eti, eti->editing_col), eti->editing_col, eti->editing_row, E_CELL_EDITING); if (!return_val) e_selection_model_key_press(E_SELECTION_MODEL (eti->selection), (GdkEventKey *) e); } } eti->in_key_press = FALSE; break; } case GDK_KEY_RELEASE: { gint cursor_row, cursor_col; gtk_object_get(GTK_OBJECT(eti->selection), "cursor_row", &cursor_row, "cursor_col", &cursor_col, NULL); if (cursor_col == -1) return FALSE; if (eti_editing (eti)){ ecell_view = eti->cell_views [eti->editing_col]; return_val = eti_e_cell_event (eti, ecell_view, e, e->key.time, view_to_model_col(eti, eti->editing_col), eti->editing_col, eti->editing_row, E_CELL_EDITING); } break; } case GDK_LEAVE_NOTIFY: case GDK_ENTER_NOTIFY: if (eti->tooltip->timer > 0) gtk_timeout_remove (eti->tooltip->timer); eti->tooltip->timer = 0; break; case GDK_FOCUS_CHANGE: if (! e->focus_change.in) { if (eti_editing (eti)) e_table_item_leave_edit (eti); } default: return_val = FALSE; } return return_val; } static void eti_class_init (GtkObjectClass *object_class) { GnomeCanvasItemClass *item_class = (GnomeCanvasItemClass *) object_class; ETableItemClass *eti_class = (ETableItemClass *) object_class; eti_parent_class = gtk_type_class (PARENT_OBJECT_TYPE); object_class->destroy = eti_destroy; object_class->set_arg = eti_set_arg; object_class->get_arg = eti_get_arg; item_class->update = eti_update; item_class->realize = eti_realize; item_class->unrealize = eti_unrealize; item_class->draw = eti_draw; item_class->point = eti_point; item_class->event = eti_event; eti_class->cursor_change = NULL; eti_class->cursor_activated = NULL; eti_class->double_click = NULL; eti_class->right_click = NULL; eti_class->click = NULL; eti_class->key_press = NULL; gtk_object_add_arg_type ("ETableItem::ETableHeader", E_TABLE_HEADER_TYPE, GTK_ARG_WRITABLE, ARG_TABLE_HEADER); gtk_object_add_arg_type ("ETableItem::ETableModel", E_TABLE_MODEL_TYPE, GTK_ARG_WRITABLE, ARG_TABLE_MODEL); gtk_object_add_arg_type ("ETableItem::selection_model", E_SELECTION_MODEL_TYPE, GTK_ARG_WRITABLE, ARG_SELECTION_MODEL); gtk_object_add_arg_type ("ETableItem::horizontal_draw_grid", GTK_TYPE_BOOL, GTK_ARG_WRITABLE, ARG_TABLE_HORIZONTAL_DRAW_GRID); gtk_object_add_arg_type ("ETableItem::vertical_draw_grid", GTK_TYPE_BOOL, GTK_ARG_WRITABLE, ARG_TABLE_VERTICAL_DRAW_GRID); gtk_object_add_arg_type ("ETableItem::drawfocus", GTK_TYPE_BOOL, GTK_ARG_WRITABLE, ARG_TABLE_DRAW_FOCUS); gtk_object_add_arg_type ("ETableItem::cursor_mode", GTK_TYPE_INT, GTK_ARG_WRITABLE, ARG_CURSOR_MODE); gtk_object_add_arg_type ("ETableItem::length_threshold", GTK_TYPE_INT, GTK_ARG_WRITABLE, ARG_LENGTH_THRESHOLD); gtk_object_add_arg_type ("ETableItem::minimum_width", GTK_TYPE_DOUBLE, GTK_ARG_READWRITE, ARG_MINIMUM_WIDTH); gtk_object_add_arg_type ("ETableItem::width", GTK_TYPE_DOUBLE, GTK_ARG_READWRITE, ARG_WIDTH); gtk_object_add_arg_type ("ETableItem::height", GTK_TYPE_DOUBLE, GTK_ARG_READABLE, ARG_HEIGHT); gtk_object_add_arg_type ("ETableItem::cursor_row", GTK_TYPE_INT, GTK_ARG_READWRITE, ARG_CURSOR_ROW); eti_signals [CURSOR_CHANGE] = gtk_signal_new ("cursor_change", GTK_RUN_LAST, object_class->type, GTK_SIGNAL_OFFSET (ETableItemClass, cursor_change), gtk_marshal_NONE__INT, GTK_TYPE_NONE, 1, GTK_TYPE_INT); eti_signals [CURSOR_ACTIVATED] = gtk_signal_new ("cursor_activated", GTK_RUN_LAST, object_class->type, GTK_SIGNAL_OFFSET (ETableItemClass, cursor_activated), gtk_marshal_NONE__INT, GTK_TYPE_NONE, 1, GTK_TYPE_INT); eti_signals [DOUBLE_CLICK] = gtk_signal_new ("double_click", GTK_RUN_LAST, object_class->type, GTK_SIGNAL_OFFSET (ETableItemClass, double_click), gtk_marshal_NONE__INT_INT_POINTER, GTK_TYPE_NONE, 3, GTK_TYPE_INT, GTK_TYPE_INT, GTK_TYPE_GDK_EVENT); eti_signals [RIGHT_CLICK] = gtk_signal_new ("right_click", GTK_RUN_LAST, object_class->type, GTK_SIGNAL_OFFSET (ETableItemClass, right_click), e_marshal_INT__INT_INT_POINTER, GTK_TYPE_INT, 3, GTK_TYPE_INT, GTK_TYPE_INT, GTK_TYPE_GDK_EVENT); eti_signals [CLICK] = gtk_signal_new ("click", GTK_RUN_LAST, object_class->type, GTK_SIGNAL_OFFSET (ETableItemClass, click), e_marshal_INT__INT_INT_POINTER, GTK_TYPE_INT, 3, GTK_TYPE_INT, GTK_TYPE_INT, GTK_TYPE_GDK_EVENT); eti_signals [KEY_PRESS] = gtk_signal_new ("key_press", GTK_RUN_LAST, object_class->type, GTK_SIGNAL_OFFSET (ETableItemClass, key_press), e_marshal_INT__INT_INT_POINTER, GTK_TYPE_INT, 3, GTK_TYPE_INT, GTK_TYPE_INT, GTK_TYPE_GDK_EVENT); gtk_object_class_add_signals (object_class, eti_signals, LAST_SIGNAL); } GtkType e_table_item_get_type (void) { static GtkType type = 0; if (!type){ GtkTypeInfo info = { "ETableItem", sizeof (ETableItem), sizeof (ETableItemClass), (GtkClassInitFunc) eti_class_init, (GtkObjectInitFunc) eti_init, NULL, /* reserved 1 */ NULL, /* reserved 2 */ (GtkClassInitFunc) NULL }; type = gtk_type_unique (PARENT_OBJECT_TYPE, &info); } return type; } /** * e_table_item_set_cursor: * @eti: %ETableItem which will have the cursor set. * @col: Column to select. -1 means the last column. * @row: Row to select. -1 means the last row. * * This routine sets the cursor of the %ETableItem canvas item. */ void e_table_item_set_cursor (ETableItem *eti, int col, int row) { e_table_item_focus(eti, col, view_to_model_row(eti, row), 0); } static void e_table_item_focus (ETableItem *eti, int col, int row, GdkModifierType state) { g_return_if_fail (eti != NULL); g_return_if_fail (E_IS_TABLE_ITEM (eti)); if (row == -1) { row = view_to_model_row(eti, eti->rows - 1); } if (col == -1) { col = eti->cols - 1; } if (row != -1) { e_selection_model_do_something(E_SELECTION_MODEL (eti->selection), row, col, state); } } /** * e_table_item_get_focused_column: * @eti: %ETableItem which will have the cursor retrieved. * * This routine gets the cursor of the %ETableItem canvas item. * * Returns: The current cursor column. */ gint e_table_item_get_focused_column (ETableItem *eti) { int cursor_col; g_return_val_if_fail (eti != NULL, -1); g_return_val_if_fail (E_IS_TABLE_ITEM (eti), -1); gtk_object_get(GTK_OBJECT(eti->selection), "cursor_col", &cursor_col, NULL); return cursor_col; } static void eti_cursor_change (ESelectionModel *selection, int row, int col, ETableItem *eti) { int view_row; int view_col; if (!(GTK_OBJECT_FLAGS(eti) & GNOME_CANVAS_ITEM_REALIZED)) return; view_row = model_to_view_row(eti, row); view_col = model_to_view_col(eti, col); if (view_row == -1 || view_col == -1) { e_table_item_leave_edit (eti); return; } if (! e_table_model_has_change_pending (eti->table_model)) { if (!eti->in_key_press) { eti_request_region_show (eti, view_col, view_row, view_col, view_row, DOUBLE_CLICK_TIME + 10); } else { eti_request_region_show (eti, view_col, view_row, view_col, view_row, 0); } } e_canvas_item_grab_focus(GNOME_CANVAS_ITEM(eti), FALSE); if (eti_editing(eti)) e_table_item_leave_edit (eti); gtk_signal_emit (GTK_OBJECT (eti), eti_signals [CURSOR_CHANGE], view_row); eti->needs_redraw = TRUE; gnome_canvas_item_request_update(GNOME_CANVAS_ITEM(eti)); } static void eti_cursor_activated (ESelectionModel *selection, int row, int col, ETableItem *eti) { int view_row; int view_col; if (!(GTK_OBJECT_FLAGS(eti) & GNOME_CANVAS_ITEM_REALIZED)) return; view_row = model_to_view_row(eti, row); view_col = model_to_view_col(eti, col); if (view_row == -1 || view_col == -1) { e_table_item_leave_edit (eti); return; } if (eti_editing(eti)) e_table_item_leave_edit (eti); gtk_signal_emit (GTK_OBJECT (eti), eti_signals [CURSOR_ACTIVATED], view_row); } static void eti_selection_change (ESelectionModel *selection, ETableItem *eti) { if (!(GTK_OBJECT_FLAGS(eti) & GNOME_CANVAS_ITEM_REALIZED)) return; eti->needs_redraw = TRUE; gnome_canvas_item_request_update(GNOME_CANVAS_ITEM(eti)); } /** * e_table_item_enter_edit * @eti: %ETableItem which will start being edited * @col: The view col to edit. * @row: The view row to edit. * * This routine starts the given %ETableItem editing at the given view * column and row. */ void e_table_item_enter_edit (ETableItem *eti, int col, int row) { g_return_if_fail (eti != NULL); g_return_if_fail (E_IS_TABLE_ITEM (eti)); if (eti_editing (eti)) e_table_item_leave_edit(eti); eti->editing_col = col; eti->editing_row = row; eti->edit_ctx = e_cell_enter_edit (eti->cell_views [col], view_to_model_col(eti, col), col, row); } /** * e_table_item_leave_edit * @eti: %ETableItem which will stop being edited * * This routine stops the given %ETableItem from editing. */ void e_table_item_leave_edit (ETableItem *eti) { int col, row; void *edit_ctx; g_return_if_fail (eti != NULL); g_return_if_fail (E_IS_TABLE_ITEM (eti)); if (!eti_editing (eti)) return; col = eti->editing_col; row = eti->editing_row; edit_ctx = eti->edit_ctx; eti->editing_col = -1; eti->editing_row = -1; eti->edit_ctx = NULL; e_cell_leave_edit (eti->cell_views [col], view_to_model_col(eti, col), col, row, edit_ctx); } /** * e_table_item_compute_location * @eti: %ETableItem to look in. * @x: A pointer to the x location to find in the %ETableItem. * @y: A pointer to the y location to find in the %ETableItem. * @row: A pointer to the location to store the found row in. * @col: A pointer to the location to store the found col in. * * This routine locates the pixel location (*x, *y) in the * %ETableItem. If that location is in the %ETableItem, *row and *col * are set to the view row and column where it was found. If that * location is not in the %ETableItem, the height of the %ETableItem * is removed from the value y points to. */ void e_table_item_compute_location (ETableItem *eti, int *x, int *y, int *row, int *col) { /* Save the grabbed row but make sure that we don't get flawed results because the cursor is grabbed. */ int grabbed_row = eti->grabbed_row; eti->grabbed_row = -1; if (!find_cell (eti, *x, *y, col, row, NULL, NULL)) { *y -= eti_get_height(eti); } eti->grabbed_row = grabbed_row; } typedef struct { ETableItem *item; int rows_printed; } ETableItemPrintContext; static gdouble * e_table_item_calculate_print_widths (ETableHeader *eth, gdouble width) { int i; double extra; double expansion; int last_resizable = -1; gdouble scale = 300.0L / 70.0L; gdouble *widths = g_new(gdouble, e_table_header_count(eth)); /* - 1 to account for the last pixel border. */ extra = width - 1; expansion = 0; for (i = 0; i < eth->col_count; i++) { extra -= eth->columns[i]->min_width * scale; if (eth->columns[i]->resizable && eth->columns[i]->expansion > 0) last_resizable = i; expansion += eth->columns[i]->resizable ? eth->columns[i]->expansion : 0; widths[i] = eth->columns[i]->min_width * scale; } for (i = 0; i <= last_resizable; i++) { widths[i] += extra * (eth->columns[i]->resizable ? eth->columns[i]->expansion : 0)/expansion; } return widths; } static gdouble eti_printed_row_height (ETableItem *eti, gdouble *widths, GnomePrintContext *context, gint row) { int col; int cols = eti->cols; gdouble height = 0; for (col = 0; col < cols; col++) { ECellView *ecell_view = eti->cell_views [col]; gdouble this_height = e_cell_print_height (ecell_view, context, view_to_model_col(eti, col), col, row, widths[col] - 1); if (this_height > height) height = this_height; } return height; } #define CHECK(x) if((x) == -1) return -1; static gint gp_draw_rect (GnomePrintContext *context, gdouble x, gdouble y, gdouble width, gdouble height) { CHECK(gnome_print_moveto(context, x, y)); CHECK(gnome_print_lineto(context, x + width, y)); CHECK(gnome_print_lineto(context, x + width, y - height)); CHECK(gnome_print_lineto(context, x, y - height)); CHECK(gnome_print_lineto(context, x, y)); return gnome_print_fill(context); } static void e_table_item_print_page (EPrintable *ep, GnomePrintContext *context, gdouble width, gdouble height, gboolean quantize, ETableItemPrintContext *itemcontext) { ETableItem *eti = itemcontext->item; const int rows = eti->rows; const int cols = eti->cols; int rows_printed = itemcontext->rows_printed; gdouble *widths; int row, col; gdouble yd = height; widths = e_table_item_calculate_print_widths (itemcontext->item->header, width); /* * Draw cells */ if (eti->horizontal_draw_grid){ gp_draw_rect(context, 0, yd, width, 1); } yd--; for (row = rows_printed; row < rows; row++){ gdouble xd = 1, row_height; row_height = eti_printed_row_height(eti, widths, context, row); if (quantize) { if (yd - row_height - 1 < 0 && row != rows_printed) { break; } } else { if (yd < 0) { break; } } for (col = 0; col < cols; col++){ ECellView *ecell_view = eti->cell_views [col]; if (gnome_print_gsave(context) == -1) /* FIXME */; if (gnome_print_translate(context, xd, yd - row_height) == -1) /* FIXME */; if (gnome_print_moveto(context, 0, 0) == -1) /* FIXME */; if (gnome_print_lineto(context, widths[col] - 1, 0) == -1) /* FIXME */; if (gnome_print_lineto(context, widths[col] - 1, row_height) == -1) /* FIXME */; if (gnome_print_lineto(context, 0, row_height) == -1) /* FIXME */; if (gnome_print_lineto(context, 0, 0) == -1) /* FIXME */; if (gnome_print_clip(context) == -1) /* FIXME */; e_cell_print (ecell_view, context, view_to_model_col(eti, col), col, row, widths[col] - 1, row_height); if (gnome_print_grestore(context) == -1) /* FIXME */; xd += widths[col]; } yd -= row_height; if (eti->horizontal_draw_grid){ gp_draw_rect(context, 0, yd, width, 1); } yd--; } itemcontext->rows_printed = row; if (eti->vertical_draw_grid){ gdouble xd = 0; for (col = 0; col < cols; col++){ gp_draw_rect(context, xd, height, 1, height - yd); xd += widths[col]; } gp_draw_rect(context, xd, height, 1, height - yd); } g_free (widths); } static gboolean e_table_item_data_left (EPrintable *ep, ETableItemPrintContext *itemcontext) { ETableItem *item = itemcontext->item; int rows_printed = itemcontext->rows_printed; gtk_signal_emit_stop_by_name(GTK_OBJECT(ep), "data_left"); return rows_printed < item->rows; } static void e_table_item_reset (EPrintable *ep, ETableItemPrintContext *itemcontext) { itemcontext->rows_printed = 0; } static gdouble e_table_item_height (EPrintable *ep, GnomePrintContext *context, gdouble width, gdouble max_height, gboolean quantize, ETableItemPrintContext *itemcontext) { ETableItem *item = itemcontext->item; const int rows = item->rows; int rows_printed = itemcontext->rows_printed; gdouble *widths; int row; gdouble yd = 0; widths = e_table_item_calculate_print_widths (itemcontext->item->header, width); /* * Draw cells */ yd++; for (row = rows_printed; row < rows; row++){ gdouble row_height; row_height = eti_printed_row_height(item, widths, context, row); if (quantize) { if (max_height != -1 && yd + row_height + 1 > max_height && row != rows_printed) { break; } } else { if (max_height != -1 && yd > max_height) { break; } } yd += row_height; yd++; } g_free (widths); if (max_height != -1 && (!quantize) && yd > max_height) yd = max_height; gtk_signal_emit_stop_by_name(GTK_OBJECT(ep), "height"); return yd; } static gboolean e_table_item_will_fit (EPrintable *ep, GnomePrintContext *context, gdouble width, gdouble max_height, gboolean quantize, ETableItemPrintContext *itemcontext) { ETableItem *item = itemcontext->item; const int rows = item->rows; int rows_printed = itemcontext->rows_printed; gdouble *widths; int row; gdouble yd = 0; gboolean ret_val = TRUE; widths = e_table_item_calculate_print_widths (itemcontext->item->header, width); /* * Draw cells */ yd++; for (row = rows_printed; row < rows; row++){ gdouble row_height; row_height = eti_printed_row_height(item, widths, context, row); if (quantize) { if (max_height != -1 && yd + row_height + 1 > max_height && row != rows_printed) { ret_val = FALSE; break; } } else { if (max_height != -1 && yd > max_height) { ret_val = FALSE; break; } } yd += row_height; yd++; } g_free (widths); gtk_signal_emit_stop_by_name(GTK_OBJECT(ep), "will_fit"); return ret_val; } static void e_table_item_printable_destroy (GtkObject *object, ETableItemPrintContext *itemcontext) { gtk_object_unref(GTK_OBJECT(itemcontext->item)); g_free(itemcontext); } /** * e_table_item_get_printable * @eti: %ETableItem which will be printed * * This routine creates and returns an %EPrintable that can be used to * print the given %ETableItem. * * Returns: The %EPrintable. */ EPrintable * e_table_item_get_printable (ETableItem *item) { EPrintable *printable = e_printable_new(); ETableItemPrintContext *itemcontext; itemcontext = g_new(ETableItemPrintContext, 1); itemcontext->item = item; gtk_object_ref(GTK_OBJECT(item)); itemcontext->rows_printed = 0; gtk_signal_connect (GTK_OBJECT(printable), "print_page", GTK_SIGNAL_FUNC(e_table_item_print_page), itemcontext); gtk_signal_connect (GTK_OBJECT(printable), "data_left", GTK_SIGNAL_FUNC(e_table_item_data_left), itemcontext); gtk_signal_connect (GTK_OBJECT(printable), "reset", GTK_SIGNAL_FUNC(e_table_item_reset), itemcontext); gtk_signal_connect (GTK_OBJECT(printable), "height", GTK_SIGNAL_FUNC(e_table_item_height), itemcontext); gtk_signal_connect (GTK_OBJECT(printable), "will_fit", GTK_SIGNAL_FUNC(e_table_item_will_fit), itemcontext); gtk_signal_connect (GTK_OBJECT(printable), "destroy", GTK_SIGNAL_FUNC(e_table_item_printable_destroy), itemcontext); return printable; }