/*
* E-table-item.c: A view of a Table.
*
* Author:
* Miguel de Icaza (miguel@gnu.org)
*
* Copyright 1999, Helix Code, Inc.
*/
#include <config.h>
#include "e-table-item.h"
#include "e-cell.h"
#define PARENT_OBJECT_TYPE gnome_canvas_item_get_type ()
static GnomeCanvasItemClass *eti_parent_class;
enum {
ARG_0,
ARG_TABLE_HEADER,
ARG_TABLE_MODEL,
ARG_TABLE_X,
ARG_TABLE_Y,
ARG_TABLE_DRAW_GRID,
ARG_LENGHT_THRESHOLD
};
static void
eti_realize_cell_views (ETableItem *eti)
{
GnomeCanvasItem *item = GNOME_CANVAS_ITEM (eti);
int i;
/*
* 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 *col = e_table_header_get_column (eti->header, i);
eti->cell_views [i] = e_cell_realize (col->ecell, item->canvas);
}
}
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;
}
g_free (eti->cell_views);
eti->n_cells = 0;
}
static void
eti_update (GnomeCanvasItem *item, double *affine, ArtSVP *clip_path, int flags)
{
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);
item->x1 = eti->x1;
item->y1 = eti->y1;
item->x2 = eti->x1 + eti->width;
item->y2 = eti->y1 + eti->height;
gnome_canvas_group_child_bounds (GNOME_CANVAS_GROUP (item->parent), item);
}
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_object_unref (GTK_OBJECT (eti->table_model));
eti->table_model_change_id = 0;
eti->table_model_selection_id = 0;
eti->table_model = NULL;
}
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_object_unref (GTK_OBJECT (eti->header));
eti->header_structure_change_id = 0;
eti->header_dim_change_id = 0;
eti->header = NULL;
}
static int
eti_row_height (ETableItem *eti, int row)
{
const int cols = e_table_header_count (eti->header);
int col;
int h, max_h;
max_h = 0;
for (col = 0; col < cols; col++){
h = e_cell_height (eti->cell_views [col], col, row);
if (h > max_h)
max_h = h;
}
return max_h;
}
static int
eti_get_height (ETableItem *eti)
{
const int rows = eti->rows;
int row;
int height = 0;
if (rows == 0)
return 0;
if (rows > eti->length_threshold){
height = eti_row_height (eti, 0) * rows;
return height;
}
for (row = 0; row < rows; row++)
height += eti_row_height (eti, row);
/* Add division lines pixels */
height += rows;
return height;
}
static void
eti_table_model_changed (ETableModel *table_model, ETableItem *eti)
{
eti->cols = e_table_model_column_count (eti->table_model);
eti->rows = e_table_model_row_count (eti->table_model);
if (eti->cell_views)
eti->height = eti_get_height (eti);
eti_update (GNOME_CANVAS_ITEM (eti), NULL, NULL, 0);
}
static void
eti_request_redraw (ETableItem *eti)
{
GnomeCanvas *canvas = GNOME_CANVAS_ITEM (eti)->canvas;
gnome_canvas_request_redraw (canvas, eti->x1, eti->y1,
eti->x1 + eti->width + 1,
eti->y1 + eti->height + 1);
}
static int
eti_col_diff (ETableItem *eti, int start_col, int end_col)
{
int col, total;
total = 0;
for (col = start_col; col < end_col; col++){
ETableCol *ecol = e_table_header_get_column (eti->header, col);
total += ecol->width;
}
return total;
}
static int
eti_row_diff (ETableItem *eti, int start_row, int end_row)
{
int row, total;
total = 0;
for (row = start_row; row < end_row; row++)
total += eti_row_height (eti, row) + 1;
return total;
}
static void
eti_request_region_redraw (ETableItem *eti, int start_col, int start_row, int end_col, int end_row)
{
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);
gnome_canvas_request_redraw (canvas,
eti->x1 + x1, eti->y1 + y1,
eti->y1 + width + 1,
eti->x1 + height + 1);
}
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);
}
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 (table_model));
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_selection_id = gtk_signal_connect (
GTK_OBJECT (table_model), "row_selection",
GTK_SIGNAL_FUNC (eti_table_model_row_selection), eti);
eti_table_model_changed (table_model, eti);
}
static void
eti_header_dim_changed (ETableHeader *eth, int col, ETableItem *eti)
{
eti_request_redraw (eti);
eti->width = e_table_header_total_width (eti->header);
eti_update (GNOME_CANVAS_ITEM (eti), NULL, NULL, 0);
eti_request_redraw (eti);
}
static void
eti_header_structure_changed (ETableHeader *eth, ETableItem *eti)
{
eti_request_redraw (eti);
eti->width = e_table_header_total_width (eti->header);
eti_unrealize_cell_views (eti);
eti_realize_cell_views (eti);
eti_update (GNOME_CANVAS_ITEM (eti), NULL, NULL, 0);
eti_request_redraw (eti);
}
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->width = e_table_header_total_width (header);
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);
}
static void
eti_destroy (GtkObject *object)
{
ETableItem *eti = E_TABLE_ITEM (object);
eti_remove_header_model (eti);
eti_remove_table_model (eti);
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 v;
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, GTK_VALUE_POINTER (*arg));
break;
case ARG_TABLE_MODEL:
eti_remove_table_model (eti);
eti_add_table_model (eti, GTK_VALUE_POINTER (*arg));
break;
case ARG_TABLE_X:
eti->x1 = GTK_VALUE_INT (*arg);
break;
case ARG_TABLE_Y:
eti->y1 = GTK_VALUE_INT (*arg);
break;
case ARG_LENGHT_THRESHOLD:
eti->length_threshold = GTK_VALUE_INT (*arg);
break;
case ARG_TABLE_DRAW_GRID:
eti->draw_grid = GTK_VALUE_BOOL (*arg);
}
eti_update (item, NULL, NULL, 0);
}
static void
eti_init (GnomeCanvasItem *item)
{
ETableItem *eti = E_TABLE_ITEM (item);
eti->focused_col = -1;
eti->focused_row = -1;
eti->height = 0;
eti->length_threshold = -1;
}
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);
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_realize_cell_views (eti);
eti->height = eti_get_height (eti);
eti_update (item, NULL, NULL, 0);
}
static void
eti_unrealize (GnomeCanvasItem *item)
{
ETableItem *eti = E_TABLE_ITEM (item);
gdk_gc_unref (eti->fill_gc);
eti->fill_gc = NULL;
gdk_gc_unref (eti->grid_gc);
eti->grid_gc = 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
draw_cell (ETableItem *eti, GdkDrawable *drawable, int col, int row,
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);
#if 0
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
}
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 heights;
printf ("Rect: %d %d %d %d\n", x, y, width, height);
/*
* Clear the background
*/
gdk_draw_rectangle (
drawable, eti->fill_gc, TRUE,
eti->x1 - x, eti->y1 - y, eti->width, eti->height);
/*
* First column to draw, last column to draw
*/
first_col = -1;
last_col = x_offset = 0;
x1 = eti->x1;
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 = eti->y1;
for (row = eti->top_item; row < rows; row++, y1 = y2){
int xd;
y2 += eti_row_height (eti, row);
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;
for (row = first_row; row < last_row; row++){
int xd, height;
height = eti_row_height (eti, row);
xd = x_offset;
printf ("paint: %d %d\n", yd, yd + height);
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);
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);
}
}
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)
{
const int cols = eti->cols;
const int rows = eti->rows;
gdouble x1, y1, x2, y2;
int col, row;
/* FIXME: inneficient, fix later */
x -= eti->x1;
y -= eti->y1;
x1 = 0;
for (col = 0; col < cols; 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)
continue;
*col_res = col;
break;
}
y1 = 0;
for (row = 0; row < rows; row++, y1 = y2){
if (y < y1)
return FALSE;
y2 += eti_row_height (eti, row) + 1;
if (y > y2)
continue;
*row_res = row;
break;
}
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)
{
ETableItem *eti = E_TABLE_ITEM (item);
ECellView *ecell_view;
switch (e->type){
case GDK_BUTTON_PRESS:
case GDK_BUTTON_RELEASE:
case GDK_2BUTTON_PRESS: {
int col, row;
if (!find_cell (eti, e->button.x, e->button.y, &col, &row))
return TRUE;
if (eti->selected_row == row && eti->selected_col == col){
ecell_view = eti->cell_views [col];
e_cell_event (ecell_view, e, col, row);
} else
eti_select (eti, col, row);
break;
}
case GDK_KEY_PRESS:
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;
default:
return FALSE;
}
return TRUE;
}
static void
eti_class_init (GtkObjectClass *object_class)
{
GnomeCanvasItemClass *item_class = (GnomeCanvasItemClass *) object_class;
eti_parent_class = gtk_type_class (PARENT_OBJECT_TYPE);
object_class->destroy = eti_destroy;
object_class->set_arg = eti_set_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;
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,
GTK_ARG_WRITABLE, ARG_TABLE_MODEL);
gtk_object_add_arg_type ("ETableItem::x", GTK_TYPE_INT,
GTK_ARG_WRITABLE, ARG_TABLE_X);
gtk_object_add_arg_type ("ETableItem::y", GTK_TYPE_INT,
GTK_ARG_WRITABLE, ARG_TABLE_Y);
gtk_object_add_arg_type ("ETableItem::drawgrid", GTK_TYPE_BOOL,
GTK_ARG_WRITABLE, ARG_TABLE_DRAW_GRID);
}
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;
}
void
e_table_item_focus (ETableItem *eti, int col, int row)
{
if (eti->focused_col != -1)
e_table_item_unfocus (eti);
eti->focused_col = col;
eti->focused_row = row;
}
void
e_table_item_unfocus (ETableItem *eti)
{
eti->focused_col = -1;
eti->focused_row = -1;
}