From 8d75b6689bdf5dc7c1efad65867cba2a656f2dcc Mon Sep 17 00:00:00 2001 From: Miguel de Icaza Date: Sun, 28 Nov 1999 03:12:22 +0000 Subject: beginning of the keyboard navigation. 1999-11-27 Miguel de Icaza * e-table-item.c (eti_event): beginning of the keyboard navigation. * e-table-model.c (e_table_model_row_changed): new function. (e_table_model_cell_changed): new function. (e_table_model_class_init): New signals. * e-table-item.c (eti_request_region_redraw): x2, y2 offsets were wrong. (eti_select): Repaint selected region. (eti_request_region_redraw): Fix range. (eti_draw): Correct offset computation here. (e_table_item_class_init): New method: row_selection, handles the selection. Now it implement GTK_SELECTION_SINGLE and GTK_SELECTION_MULTIPLE. Focusing and selection should be correct now. svn path=/trunk/; revision=1441 --- widgets/e-table/e-table-item.c | 386 ++++++++++++++++++++++++++++++++++++----- 1 file changed, 340 insertions(+), 46 deletions(-) (limited to 'widgets/e-table/e-table-item.c') diff --git a/widgets/e-table/e-table-item.c b/widgets/e-table/e-table-item.c index 3b35f6c156..a561fbfd15 100644 --- a/widgets/e-table/e-table-item.c +++ b/widgets/e-table/e-table-item.c @@ -1,19 +1,35 @@ /* - * E-table-item.c: A view of a Table. + * E-table-item.c: A GnomeCanvasItem that is a view of an ETableModel. * * Author: * Miguel de Icaza (miguel@gnu.org) * * Copyright 1999, Helix Code, Inc. + * + * TODO: + * Add a border to the thing, so that focusing works properly. + * */ #include +#include +#include +#include #include "e-table-item.h" #include "e-cell.h" #define PARENT_OBJECT_TYPE gnome_canvas_item_get_type () +#define FOCUSED_BORDER 2 + static GnomeCanvasItemClass *eti_parent_class; +enum { + ROW_SELECTION, + LAST_SIGNAL +}; + +static gint eti_signals [LAST_SIGNAL] = { 0, }; + enum { ARG_0, ARG_TABLE_HEADER, @@ -24,6 +40,14 @@ enum { ARG_LENGHT_THRESHOLD }; +/* + * 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) { @@ -43,14 +67,16 @@ eti_realize_cell_views (ETableItem *eti) } } +/* + * During unrealization: we invoke every e-cell (one per column in the current + * setup) to dispose all resources allocated + */ static void eti_unrealize_cell_views (ETableItem *eti) { int i; for (i = 0; i < eti->n_cells; i++){ - ETableCol *col = e_table_header_get_column (eti->header, i); - e_cell_unrealize (eti->cell_views [i]); eti->cell_views [i] = NULL; } @@ -59,6 +85,9 @@ eti_unrealize_cell_views (ETableItem *eti) } +/* + * GnomeCanvasItem::update method + */ static void eti_update (GnomeCanvasItem *item, double *affine, ArtSVP *clip_path, int flags) { @@ -75,14 +104,21 @@ eti_update (GnomeCanvasItem *item, double *affine, ArtSVP *clip_path, int flags) gnome_canvas_group_child_bounds (GNOME_CANVAS_GROUP (item->parent), item); } +/* + * 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 (eti->table_model_change_id); - gtk_signal_disconnect (eti->table_model_selection_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_selection_id); gtk_object_unref (GTK_OBJECT (eti->table_model)); eti->table_model_change_id = 0; @@ -90,14 +126,21 @@ eti_remove_table_model (ETableItem *eti) eti->table_model = 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 (eti->header_structure_change_id); - gtk_signal_disconnect (eti->header_dim_change_id); + 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_object_unref (GTK_OBJECT (eti->header)); eti->header_structure_change_id = 0; @@ -105,6 +148,12 @@ eti_remove_header_model (ETableItem *eti) eti->header = NULL; } +/* + * 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) { @@ -123,6 +172,18 @@ eti_row_height (ETableItem *eti, int row) return max_h; } +/* + * 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) { @@ -148,6 +209,9 @@ eti_get_height (ETableItem *eti) return height; } +/* + * Callback routine: invoked when the ETableModel has suffered a change + */ static void eti_table_model_changed (ETableModel *table_model, ETableItem *eti) { @@ -160,6 +224,11 @@ eti_table_model_changed (ETableModel *table_model, ETableItem *eti) eti_update (GNOME_CANVAS_ITEM (eti), NULL, NULL, 0); } +/* + * eti_request_redraw: + * + * Queues a canvas redraw for the entire ETableItem. + */ static void eti_request_redraw (ETableItem *eti) { @@ -170,6 +239,9 @@ eti_request_redraw (ETableItem *eti) eti->y1 + eti->height + 1); } +/* + * Computes the distance from @start_col to @end_col in pixels. + */ static int eti_col_diff (ETableItem *eti, int start_col, int end_col) { @@ -185,6 +257,9 @@ eti_col_diff (ETableItem *eti, int start_col, int end_col) return total; } +/* + * Computes the distance between @start_row and @end_row in pixels + */ static int eti_row_diff (ETableItem *eti, int start_row, int end_row) { @@ -198,28 +273,38 @@ eti_row_diff (ETableItem *eti, int start_row, int end_row) 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) +eti_request_region_redraw (ETableItem *eti, int start_col, int start_row, int end_col, int end_row, int border) { GnomeCanvas *canvas = GNOME_CANVAS_ITEM (eti)->canvas; int x1, y1, width, height; - int col, row; x1 = eti_col_diff (eti, 0, start_col); y1 = eti_row_diff (eti, 0, start_row); - width = eti_col_diff (eti, start_col, end_col); - height = eti_row_diff (eti, start_col, end_row); - + width = eti_col_diff (eti, start_col, end_col + 1); + height = eti_row_diff (eti, start_row, end_row + 1); + gnome_canvas_request_redraw (canvas, - eti->x1 + x1, eti->y1 + y1, - eti->y1 + width + 1, - eti->x1 + height + 1); + eti->x1 + x1 - border, + eti->y1 + y1 - border, + eti->x1 + x1 + width + 1 + border, + eti->y1 + y1 + height + 1 + border); } static void eti_table_model_row_selection (ETableModel *table_model, int row, gboolean selected, ETableItem *eti) { - eti_request_region_redraw (eti, 0, row, eti->cols, row); + eti_request_region_redraw (eti, 0, row, eti->cols - 1, row, 0); } static void @@ -282,6 +367,9 @@ eti_add_header_model (ETableItem *eti, ETableHeader *header) GTK_SIGNAL_FUNC (eti_header_structure_changed), eti); } +/* + * GtkObject::destroy method + */ static void eti_destroy (GtkObject *object) { @@ -289,6 +377,8 @@ eti_destroy (GtkObject *object) eti_remove_header_model (eti); eti_remove_table_model (eti); + + g_slist_free (eti->selection); if (GTK_OBJECT_CLASS (eti_parent_class)->destroy) (*GTK_OBJECT_CLASS (eti_parent_class)->destroy) (object); @@ -299,7 +389,6 @@ eti_set_arg (GtkObject *o, GtkArg *arg, guint arg_id) { GnomeCanvasItem *item; ETableItem *eti; - int v; item = GNOME_CANVAS_ITEM (o); eti = E_TABLE_ITEM (o); @@ -343,6 +432,8 @@ eti_init (GnomeCanvasItem *item) eti->height = 0; eti->length_threshold = -1; + + eti->selection_mode = GTK_SELECTION_SINGLE; } static void @@ -355,13 +446,24 @@ eti_realize (GnomeCanvasItem *item) if (GNOME_CANVAS_ITEM_CLASS (eti_parent_class)->realize) (*GNOME_CANVAS_ITEM_CLASS (eti_parent_class)->realize)(item); + /* + * Gdk Resource allocation + */ window = canvas_widget->window; - + eti->fill_gc = canvas_widget->style->white_gc; gdk_gc_ref (canvas_widget->style->white_gc); + eti->grid_gc = gdk_gc_new (window); gdk_gc_set_foreground (eti->grid_gc, &canvas_widget->style->bg [GTK_STATE_NORMAL]); + eti->focus_gc = gdk_gc_new (window); + gdk_gc_set_foreground (eti->focus_gc, &canvas_widget->style->black); + gdk_gc_set_line_attributes (eti->focus_gc, 2, GDK_LINE_SOLID, GDK_CAP_BUTT, GDK_JOIN_MITER); + + /* + * + */ eti_realize_cell_views (eti); eti->height = eti_get_height (eti); @@ -377,7 +479,9 @@ eti_unrealize (GnomeCanvasItem *item) eti->fill_gc = NULL; gdk_gc_unref (eti->grid_gc); eti->grid_gc = NULL; - + gdk_gc_unref (eti->focus_gc); + eti->focus_gc = NULL; + eti_unrealize_cell_views (eti); eti->height = 0; @@ -387,26 +491,28 @@ eti_unrealize (GnomeCanvasItem *item) } static void -draw_cell (ETableItem *eti, GdkDrawable *drawable, int col, int row, +draw_cell (ETableItem *eti, GdkDrawable *drawable, int col, int row, gboolean selected, int x1, int y1, int x2, int y2) { - GnomeCanvas *canvas = GNOME_CANVAS_ITEM (eti)->canvas; ECellView *ecell_view; - GdkFont *font; - char text [40]; - - font = GTK_WIDGET (canvas)->style->font; ecell_view = eti->cell_views [col]; - e_cell_draw (ecell_view, drawable, col, row, x1, y1, x2, y2); + e_cell_draw (ecell_view, drawable, col, row, selected, x1, y1, x2, y2); #if 0 + { + GdkFont *font; + GnomeCanvas *canvas = GNOME_CANVAS_ITEM (eti)->canvas; + + font = GTK_WIDGET (canvas)->style->font; + sprintf (text, "%d:%d\n", col, row); gdk_draw_line (drawable, eti->grid_gc, x1, y1, x2, y2); gdk_draw_line (drawable, eti->grid_gc, x1, y2, x2, y1); sprintf (text, "%d:%d\n", col, row); gdk_draw_text (drawable, font, eti->grid_gc, x1, y2, text, strlen (text)); + } #endif } @@ -420,7 +526,8 @@ eti_draw (GnomeCanvasItem *item, GdkDrawable *drawable, int x, int y, int width, int first_col, last_col, x_offset; int first_row, last_row, y_offset, yd; int x1, x2; - int heights; + int f_x1, f_x2, f_y1, f_y2; + gboolean f_found; printf ("Rect: %d %d %d %d\n", x, y, width, height); /* @@ -435,7 +542,7 @@ eti_draw (GnomeCanvasItem *item, GdkDrawable *drawable, int x, int y, int width, */ first_col = -1; last_col = x_offset = 0; - x1 = eti->x1; + x1 = x2 = eti->x1; for (col = 0; col < cols; col++, x1 = x2){ ETableCol *ecol = e_table_header_get_column (eti->header, col); @@ -465,9 +572,8 @@ eti_draw (GnomeCanvasItem *item, GdkDrawable *drawable, int x, int y, int width, y_offset = 0; y1 = y2 = eti->y1; for (row = eti->top_item; row < rows; row++, y1 = y2){ - int xd; - y2 += eti_row_height (eti, row); + y2 += eti_row_height (eti, row) + 1; if (y1 > y + height) break; @@ -489,24 +595,49 @@ eti_draw (GnomeCanvasItem *item, GdkDrawable *drawable, int x, int y, int width, * Draw cells */ yd = y_offset; + f_x1 = f_x2 = f_y1 = f_y2 = -1; + f_found = FALSE; for (row = first_row; row < last_row; row++){ int xd, height; + gboolean selected; height = eti_row_height (eti, row); xd = x_offset; printf ("paint: %d %d\n", yd, yd + height); + + selected = g_slist_find (eti->selection, GINT_TO_POINTER (row)) != NULL; + for (col = first_col; col < last_col; col++){ ETableCol *ecol = e_table_header_get_column (eti->header, col); - draw_cell (eti, drawable, col, row, xd, yd, xd + ecol->width, yd + height); + draw_cell (eti, drawable, col, row, selected, xd, yd, xd + ecol->width, yd + height); + if (col == eti->focused_col && row == eti->focused_row){ + f_x1 = xd; + f_x2 = xd + ecol->width; + f_y1 = yd; + f_y2 = yd + height; + f_found = TRUE; + } + xd += ecol->width; } yd += height + 1; gdk_draw_line (drawable, eti->grid_gc, eti->x1 - x, yd -1, eti->x1 + eti->width - x, yd -1); } + + /* + * Draw focus + */ + if (f_found){ + printf ("FOUD: %d %d %d %d!\n", + f_x1 - 1, f_y1 - 1, f_x2 - f_x1 + 2 , f_y2 - f_y1 + 2); + gdk_draw_rectangle ( + drawable, eti->focus_gc, FALSE, + f_x1 - 1, f_y1 - 1, f_x2 - f_x1 + 2 , f_y2 - f_y1 + 2); + } } static double @@ -547,7 +678,7 @@ find_cell (ETableItem *eti, double x, double y, int *col_res, int *row_res) break; } - y1 = 0; + y1 = y2 = 0; for (row = 0; row < rows; row++, y1 = y2){ if (y < y1) return FALSE; @@ -564,13 +695,6 @@ find_cell (ETableItem *eti, double x, double y, int *col_res, int *row_res) return TRUE; } -static void -eti_select (ETableItem *eti, int col, int row) -{ - eti->selected_col = col; - eti->selected_row = row; -} - static int eti_event (GnomeCanvasItem *item, GdkEvent *e) { @@ -586,22 +710,56 @@ eti_event (GnomeCanvasItem *item, GdkEvent *e) if (!find_cell (eti, e->button.x, e->button.y, &col, &row)) return TRUE; - if (eti->selected_row == row && eti->selected_col == col){ + if (eti->focused_row == row && eti->focused_col == col){ ecell_view = eti->cell_views [col]; e_cell_event (ecell_view, e, col, row); - } else - eti_select (eti, col, row); + } else { + /* + * Focus the cell, and select the row + */ + e_table_item_focus (eti, col, row); + e_table_item_select_row (eti, row); + } break; } case GDK_KEY_PRESS: - case GDK_KEY_RELEASE: if (eti->focused_col == -1) return FALSE; + switch (e->key.keyval){ + case GDK_Left: + if (eti->focused_col > 0) + e_table_item_focus (eti, eti->focused_col - 1, eti->focused_row); + break; + + case GDK_Right: + if ((eti->focused_col + 1) < eti->cols) + e_table_item_focus (eti, eti->focused_col + 1, eti->focused_row); + break; + + case GDK_Up: + if (eti->focused_row > 0) + e_table_item_focus (eti, eti->focused_col, eti->focused_row - 1); + break; + + case GDK_Down: + if ((eti->focused_row + 1) < eti->rows) + e_table_item_focus (eti, eti->focused_col, eti->focused_row + 1); + break; + + default: + } ecell_view = eti->cell_views [eti->focused_col]; + e_cell_event (ecell_view, e, eti->focused_col, eti->focused_row); + break; + + case GDK_KEY_RELEASE: + if (eti->focused_col == -1) + return FALSE; + ecell_view = eti->cell_views [eti->focused_col]; e_cell_event (ecell_view, e, eti->focused_col, eti->focused_row); break; @@ -610,12 +768,27 @@ eti_event (GnomeCanvasItem *item, GdkEvent *e) } return TRUE; } + +/* + * ETableItem::row_selection method + */ +static void +eti_row_selection (ETableItem *eti, int row, gboolean selected) +{ + eti_request_region_redraw (eti, 0, row, eti->cols - 1, row, 0); + if (selected) + eti->selection = g_slist_prepend (eti->selection, GINT_TO_POINTER (row)); + else + eti->selection = g_slist_remove (eti->selection, GINT_TO_POINTER (row)); +} + 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; @@ -627,7 +800,9 @@ eti_class_init (GtkObjectClass *object_class) item_class->draw = eti_draw; item_class->point = eti_point; item_class->event = eti_event; - + + eti_class->row_selection = eti_row_selection; + gtk_object_add_arg_type ("ETableItem::ETableHeader", GTK_TYPE_POINTER, GTK_ARG_WRITABLE, ARG_TABLE_HEADER); gtk_object_add_arg_type ("ETableItem::ETableModel", GTK_TYPE_POINTER, @@ -638,6 +813,17 @@ eti_class_init (GtkObjectClass *object_class) GTK_ARG_WRITABLE, ARG_TABLE_Y); gtk_object_add_arg_type ("ETableItem::drawgrid", GTK_TYPE_BOOL, GTK_ARG_WRITABLE, ARG_TABLE_DRAW_GRID); + + eti_signals [ROW_SELECTION] = + gtk_signal_new ("row_selection", + GTK_RUN_LAST, + object_class->type, + GTK_SIGNAL_OFFSET (ETableItemClass, row_selection), + gtk_marshal_NONE__INT_INT, + GTK_TYPE_NONE, 2, GTK_TYPE_INT, GTK_TYPE_INT); + + gtk_object_class_add_signals (object_class, eti_signals, LAST_SIGNAL); + } GtkType @@ -666,16 +852,124 @@ e_table_item_get_type (void) void e_table_item_focus (ETableItem *eti, int col, int row) { + g_return_if_fail (eti != NULL); + g_return_if_fail (E_IS_TABLE_ITEM (eti)); + if (eti->focused_col != -1) e_table_item_unfocus (eti); eti->focused_col = col; - eti->focused_row = row; + eti->focused_row = row; + + eti_request_region_redraw (eti, col, row, col, row, FOCUSED_BORDER); } void e_table_item_unfocus (ETableItem *eti) { + g_return_if_fail (eti != NULL); + g_return_if_fail (E_IS_TABLE_ITEM (eti)); + + if (eti->focused_row == -1) + return; + + { + const int col = eti->focused_col; + const int row = eti->focused_row; + + eti_request_region_redraw (eti, col, row, col, row, FOCUSED_BORDER); + } eti->focused_col = -1; eti->focused_row = -1; } + +const GSList * +e_table_item_get_selection (ETableItem *eti) +{ + g_return_val_if_fail (eti != NULL, NULL); + g_return_val_if_fail (E_IS_TABLE_ITEM (eti), NULL); + + return eti->selection; +} + +GtkSelectionMode +e_table_item_get_selection_mode (ETableItem *eti) +{ + g_return_val_if_fail (eti != NULL, GTK_SELECTION_SINGLE); + g_return_val_if_fail (E_IS_TABLE_ITEM (eti), GTK_SELECTION_SINGLE); + + return eti->selection_mode; +} + +void +e_table_item_set_selection_mode (ETableItem *eti, GtkSelectionMode selection_mode) +{ + g_return_if_fail (eti != NULL); + g_return_if_fail (E_IS_TABLE_ITEM (eti)); + + if (selection_mode == GTK_SELECTION_BROWSE || + selection_mode == GTK_SELECTION_EXTENDED){ + g_error ("GTK_SELECTION_BROWSE and GTK_SELECTION_EXTENDED are not implemented"); + } + + eti->selection_mode = selection_mode; +} + +gboolean +e_table_item_is_row_selected (ETableItem *eti, int row) +{ + g_return_val_if_fail (eti != NULL, FALSE); + g_return_val_if_fail (E_IS_TABLE_ITEM (eti), FALSE); + + if (g_slist_find (eti->selection, GINT_TO_POINTER (row))) + return TRUE; + else + return FALSE; +} + +void +e_table_item_unselect_row (ETableItem *eti, int row) +{ + g_return_if_fail (eti != NULL); + g_return_if_fail (E_IS_TABLE_ITEM (eti)); + + if (e_table_item_is_row_selected (eti, row)){ + gtk_signal_emit ( + GTK_OBJECT (eti), eti_signals [ROW_SELECTION], + row, 0); + } +} + +void +e_table_item_select_row (ETableItem *eti, int row) +{ + g_return_if_fail (eti != NULL); + g_return_if_fail (E_IS_TABLE_ITEM (eti)); + + switch (eti->selection_mode){ + case GTK_SELECTION_SINGLE: + if (eti->selection){ + gtk_signal_emit ( + GTK_OBJECT (eti), eti_signals [ROW_SELECTION], + GPOINTER_TO_INT (eti->selection->data), 0); + } + g_slist_free (eti->selection); + eti->selection = NULL; + + gtk_signal_emit ( + GTK_OBJECT (eti), eti_signals [ROW_SELECTION], + GINT_TO_POINTER (row), 1); + break; + + case GTK_SELECTION_MULTIPLE: + if (g_slist_find (eti->selection, GINT_TO_POINTER (row))) + return; + gtk_signal_emit ( + GTK_OBJECT (eti), eti_signals [ROW_SELECTION], + GINT_TO_POINTER (row), 1); + break; + + default: + + } +} -- cgit v1.2.3