aboutsummaryrefslogtreecommitdiffstats
path: root/widgets/e-table-item.c
diff options
context:
space:
mode:
Diffstat (limited to 'widgets/e-table-item.c')
-rw-r--r--widgets/e-table-item.c386
1 files changed, 340 insertions, 46 deletions
diff --git a/widgets/e-table-item.c b/widgets/e-table-item.c
index 3b35f6c156..a561fbfd15 100644
--- a/widgets/e-table-item.c
+++ b/widgets/e-table-item.c
@@ -1,20 +1,36 @@
/*
- * 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 <config.h>
+#include <stdio.h>
+#include <gtk/gtksignal.h>
+#include <gdk/gdkkeysyms.h>
#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,
ARG_TABLE_MODEL,
@@ -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:
+
+ }
+}