Files
evolution/widgets/table/e-table.c
Christopher James Lahey 51284ee84a Destroy text and pixbuf if they exist whether or not is_pixbuf is set.
2000-12-23  Christopher James Lahey  <clahey@helixcode.com>

	* e-table-col.c, e-table-col.h (etc_destroy): Destroy text and
	pixbuf if they exist whether or not is_pixbuf is set.
	(e_table_col_new_with_pixbuf): Make new_with_pixbuf take a title
	argument which is for when you can't display pixmaps.

	* e-table-column-specification.c
	(e_table_column_specification_load_from_node): Don't translate the
	pixbuf string attribute.

	* e-table-sort-info.c, e-table-sort-info.h
	(e_table_sort_info_load_from_node): Added a state_version
	parameter to the load_from_node function.  This lets the loader
	specify which version of ETableState is being processed.  If it's
	less than .05, use the old nested version.  If it's greater, use
	the new flat version.
	(e_table_sort_info_save_to_node): Changed this to store a list of
	group and leaf nodes instead of nesting the group nodes and leaf
	nodes one inside the other.  This is much easier to understand and
	requires less typing when creating a new ETableSpecification's
	initial ETableState.

	* e-table-state.c: Changed the state-version parameter to 0.1.
	(e_table_state_load_from_node): Use
	e_xml_get_double_prop_by_name_with_default so that we can specify
	a state-version default of 0.1 for people writing ETableStates by
	hand.  Pass the state-version to e_table_sort_info_load_from_node.

	* e-table.c (et_col_spec_to_col): Pass the title from the
	ETableColumnSpecification to the ETableCol even if it's a pixbuf
	column.

svn path=/trunk/; revision=7138
2000-12-23 05:55:20 +00:00

2115 lines
56 KiB
C

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
/*
* E-table.c: A graphical view of a Table.
*
* Author:
* Miguel de Icaza (miguel@helixcode.com)
* Chris Lahey (clahey@helixcode.com)
*
* Copyright 1999, Helix Code, Inc
*/
#include <config.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#ifdef HAVE_ALLOCA_H
#include <alloca.h>
#endif
#include <stdio.h>
#include <libgnomeui/gnome-canvas.h>
#include <gtk/gtksignal.h>
#include "gal/util/e-util.h"
#include "gal/widgets/e-canvas.h"
#include "gal/widgets/e-canvas-vbox.h"
#include "e-table.h"
#include "e-table-header-item.h"
#include "e-table-header-utils.h"
#include "e-table-subset.h"
#include "e-table-item.h"
#include "e-table-group.h"
#include "e-table-group-leaf.h"
#include "e-table-click-to-add.h"
#include "e-table-specification.h"
#include "e-table-state.h"
#include "e-table-column-specification.h"
#define COLUMN_HEADER_HEIGHT 16
#define PARENT_TYPE gtk_table_get_type ()
static GtkObjectClass *e_table_parent_class;
enum {
CURSOR_CHANGE,
SELECTION_CHANGE,
DOUBLE_CLICK,
RIGHT_CLICK,
CLICK,
KEY_PRESS,
TABLE_DRAG_BEGIN,
TABLE_DRAG_END,
TABLE_DRAG_DATA_GET,
TABLE_DRAG_DATA_DELETE,
TABLE_DRAG_LEAVE,
TABLE_DRAG_MOTION,
TABLE_DRAG_DROP,
TABLE_DRAG_DATA_RECEIVED,
LAST_SIGNAL
};
enum {
ARG_0,
ARG_TABLE_DRAW_FOCUS,
ARG_LENGTH_THRESHOLD,
};
static gint et_signals [LAST_SIGNAL] = { 0, };
static void e_table_fill_table (ETable *e_table, ETableModel *model);
static gboolean changed_idle (gpointer data);
static void et_drag_begin (GtkWidget *widget,
GdkDragContext *context,
ETable *et);
static void et_drag_end (GtkWidget *widget,
GdkDragContext *context,
ETable *et);
static void et_drag_data_get(GtkWidget *widget,
GdkDragContext *context,
GtkSelectionData *selection_data,
guint info,
guint time,
ETable *et);
static void et_drag_data_delete(GtkWidget *widget,
GdkDragContext *context,
ETable *et);
static void et_drag_leave(GtkWidget *widget,
GdkDragContext *context,
guint time,
ETable *et);
static gboolean et_drag_motion(GtkWidget *widget,
GdkDragContext *context,
gint x,
gint y,
guint time,
ETable *et);
static gboolean et_drag_drop(GtkWidget *widget,
GdkDragContext *context,
gint x,
gint y,
guint time,
ETable *et);
static void et_drag_data_received(GtkWidget *widget,
GdkDragContext *context,
gint x,
gint y,
GtkSelectionData *selection_data,
guint info,
guint time,
ETable *et);
static gint e_table_drag_source_event_cb (GtkWidget *widget,
GdkEvent *event,
ETable *table);
static void
et_destroy (GtkObject *object)
{
ETable *et = E_TABLE (object);
gtk_signal_disconnect (GTK_OBJECT (et->model),
et->table_model_change_id);
gtk_signal_disconnect (GTK_OBJECT (et->model),
et->table_row_change_id);
gtk_signal_disconnect (GTK_OBJECT (et->model),
et->table_cell_change_id);
gtk_signal_disconnect (GTK_OBJECT (et->model),
et->table_row_inserted_id);
gtk_signal_disconnect (GTK_OBJECT (et->model),
et->table_row_deleted_id);
if (et->group_info_change_id)
gtk_signal_disconnect (GTK_OBJECT (et->sort_info),
et->group_info_change_id);
if (et->reflow_idle_id)
g_source_remove(et->reflow_idle_id);
et->reflow_idle_id = 0;
gtk_object_unref (GTK_OBJECT (et->model));
gtk_object_unref (GTK_OBJECT (et->full_header));
gtk_object_unref (GTK_OBJECT (et->header));
gtk_object_unref (GTK_OBJECT (et->sort_info));
gtk_object_unref (GTK_OBJECT (et->sorter));
gtk_object_unref (GTK_OBJECT (et->selection));
if (et->spec)
gtk_object_unref (GTK_OBJECT (et->spec));
if (et->header_canvas != NULL)
gtk_widget_destroy (GTK_WIDGET (et->header_canvas));
gtk_widget_destroy (GTK_WIDGET (et->table_canvas));
if (et->rebuild_idle_id) {
g_source_remove (et->rebuild_idle_id);
et->rebuild_idle_id = 0;
}
g_free(et->click_to_add_message);
(*e_table_parent_class->destroy)(object);
}
static void
e_table_init (GtkObject *object)
{
ETable *e_table = E_TABLE (object);
GtkTable *gtk_table = GTK_TABLE (object);
gtk_table->homogeneous = FALSE;
e_table->sort_info = NULL;
e_table->group_info_change_id = 0;
e_table->reflow_idle_id = 0;
e_table->draw_grid = 1;
e_table->draw_focus = 1;
e_table->cursor_mode = E_TABLE_CURSOR_SIMPLE;
e_table->length_threshold = 200;
e_table->need_rebuild = 0;
e_table->rebuild_idle_id = 0;
e_table->click_to_add_message = NULL;
e_table->drag_get_data_row = -1;
e_table->drag_get_data_col = -1;
e_table->drop_row = -1;
e_table->drop_col = -1;
e_table->site = NULL;
e_table->drag_source_button_press_event_id = 0;
e_table->drag_source_motion_notify_event_id = 0;
e_table->sorter = NULL;
e_table->selection = e_table_selection_model_new();
e_table->cursor_loc = E_TABLE_CURSOR_LOC_NONE;
e_table->spec = NULL;
}
static void
header_canvas_size_allocate (GtkWidget *widget, GtkAllocation *alloc, ETable *e_table)
{
gnome_canvas_set_scroll_region (
GNOME_CANVAS (e_table->header_canvas),
0, 0, alloc->width - 1, /* COLUMN_HEADER_HEIGHT - 1 */
E_TABLE_HEADER_ITEM (e_table->header_item)->height - 1);
/* When the header item is created ->height == 0,
as the font is only created when everything is realized.
So we set the usize here as well, so that the size of the
header is correct */
if (GTK_WIDGET (e_table->header_canvas)->allocation.height !=
E_TABLE_HEADER_ITEM (e_table->header_item)->height)
gtk_widget_set_usize (GTK_WIDGET (e_table->header_canvas), -1,
E_TABLE_HEADER_ITEM (e_table->header_item)->height);
}
static void
sort_info_changed (ETableSortInfo *info, ETable *et)
{
et->need_rebuild = TRUE;
if (!et->rebuild_idle_id)
et->rebuild_idle_id = g_idle_add_full (20, changed_idle, et, NULL);
}
static void
e_table_setup_header (ETable *e_table)
{
e_table->header_canvas = GNOME_CANVAS (e_canvas_new ());
gtk_widget_show (GTK_WIDGET (e_table->header_canvas));
e_table->header_item = gnome_canvas_item_new (
gnome_canvas_root (e_table->header_canvas),
e_table_header_item_get_type (),
"ETableHeader", e_table->header,
"full_header", e_table->full_header,
"sort_info", e_table->sort_info,
"dnd_code", "(unset)",
"table", e_table,
NULL);
gtk_signal_connect (
GTK_OBJECT (e_table->header_canvas), "size_allocate",
GTK_SIGNAL_FUNC (header_canvas_size_allocate), e_table);
gtk_widget_set_usize (GTK_WIDGET (e_table->header_canvas), -1,
E_TABLE_HEADER_ITEM (e_table->header_item)->height);
}
static gboolean
table_canvas_reflow_idle (ETable *e_table)
{
gdouble height, width;
gdouble item_height;
GtkAllocation *alloc = &(GTK_WIDGET (e_table->table_canvas)->allocation);
gtk_object_get (GTK_OBJECT (e_table->canvas_vbox),
"height", &height,
"width", &width,
NULL);
item_height = height;
height = MAX ((int)height, alloc->height);
width = MAX((int)width, alloc->width);
/* I have no idea why this needs to be -1, but it works. */
gnome_canvas_set_scroll_region (
GNOME_CANVAS (e_table->table_canvas),
0, 0, width - 1, height - 1);
gtk_object_set (GTK_OBJECT (e_table->white_item),
"y1", item_height + 1,
"x2", width,
"y2", height,
NULL);
e_table->reflow_idle_id = 0;
return FALSE;
}
static void
table_canvas_size_allocate (GtkWidget *widget, GtkAllocation *alloc,
ETable *e_table)
{
gdouble width;
gdouble height;
gdouble item_height;
width = alloc->width;
gtk_object_get (GTK_OBJECT (e_table->canvas_vbox),
"height", &height,
NULL);
item_height = height;
height = MAX ((int)height, alloc->height);
gtk_object_set (GTK_OBJECT (e_table->canvas_vbox),
"width", width,
NULL);
gtk_object_set (GTK_OBJECT (e_table->header),
"width", width,
NULL);
gtk_object_set (GTK_OBJECT (e_table->white_item),
"y1", item_height + 1,
"x2", width,
"y2", height,
NULL);
if (e_table->reflow_idle_id)
g_source_remove(e_table->reflow_idle_id);
table_canvas_reflow_idle(e_table);
}
static void
table_canvas_reflow (GnomeCanvas *canvas, ETable *e_table)
{
if (!e_table->reflow_idle_id)
e_table->reflow_idle_id = g_idle_add_full (400, (GSourceFunc) table_canvas_reflow_idle, e_table, NULL);
}
static void
click_to_add_cursor_change (ETableClickToAdd *etcta, int row, int col, ETable *et)
{
if (et->cursor_loc == E_TABLE_CURSOR_LOC_TABLE) {
e_table_selection_model_clear(et->selection);
}
et->cursor_loc = E_TABLE_CURSOR_LOC_ETCTA;
}
static void
group_cursor_change (ETableGroup *etg, int row, ETable *et)
{
if (et->cursor_loc == E_TABLE_CURSOR_LOC_ETCTA && et->click_to_add) {
e_table_click_to_add_commit(E_TABLE_CLICK_TO_ADD(et->click_to_add));
}
et->cursor_loc = E_TABLE_CURSOR_LOC_TABLE;
gtk_signal_emit (GTK_OBJECT (et),
et_signals [CURSOR_CHANGE],
row);
}
static void
group_double_click (ETableGroup *etg, int row, int col, GdkEvent *event, ETable *et)
{
gtk_signal_emit (GTK_OBJECT (et),
et_signals [DOUBLE_CLICK],
row, col, event);
}
static gint
group_right_click (ETableGroup *etg, int row, int col, GdkEvent *event, ETable *et)
{
int return_val = 0;
gtk_signal_emit (GTK_OBJECT (et),
et_signals [RIGHT_CLICK],
row, col, event, &return_val);
return return_val;
}
static gint
group_click (ETableGroup *etg, int row, int col, GdkEvent *event, ETable *et)
{
int return_val = 0;
gtk_signal_emit (GTK_OBJECT (et),
et_signals [CLICK],
row, col, event, &return_val);
return return_val;
}
static gint
group_key_press (ETableGroup *etg, int row, int col, GdkEvent *event, ETable *et)
{
int return_val = 0;
GdkEventKey *key = (GdkEventKey *) event;
GdkEventButton click;
switch (key->keyval) {
case GDK_Page_Down:
gtk_adjustment_set_value(gtk_layout_get_vadjustment (GTK_LAYOUT (et->table_canvas)),
CLAMP(gtk_layout_get_vadjustment (GTK_LAYOUT (et->table_canvas))->value +
(gtk_layout_get_vadjustment (GTK_LAYOUT (et->table_canvas))->page_size - 20),
0,
gtk_layout_get_vadjustment (GTK_LAYOUT (et->table_canvas))->upper -
gtk_layout_get_vadjustment (GTK_LAYOUT (et->table_canvas))->page_size));
click.type = GDK_BUTTON_PRESS;
click.window = GTK_LAYOUT (et->table_canvas)->bin_window;
click.send_event = key->send_event;
click.time = key->time;
click.x = 30;
click.y = gtk_layout_get_vadjustment (GTK_LAYOUT (et->table_canvas))->page_size - 1;
click.state = key->state;
click.button = 1;
gtk_widget_event(GTK_WIDGET(et->table_canvas),
(GdkEvent *) &click);
return_val = 1;
break;
case GDK_Page_Up:
gtk_adjustment_set_value(gtk_layout_get_vadjustment (GTK_LAYOUT (et->table_canvas)),
gtk_layout_get_vadjustment (GTK_LAYOUT (et->table_canvas))->value -
(gtk_layout_get_vadjustment (GTK_LAYOUT (et->table_canvas))->page_size - 20));
click.type = GDK_BUTTON_PRESS;
click.window = GTK_LAYOUT (et->table_canvas)->bin_window;
click.send_event = key->send_event;
click.time = key->time;
click.x = 30;
click.y = 1;
click.state = key->state;
click.button = 1;
gtk_widget_event(GTK_WIDGET(et->table_canvas),
(GdkEvent *) &click);
return_val = 1;
break;
default:
gtk_signal_emit (GTK_OBJECT (et),
et_signals [KEY_PRESS],
row, col, event, &return_val);
break;
}
return return_val;
}
static gboolean
changed_idle (gpointer data)
{
ETable *et = E_TABLE (data);
if (et->need_rebuild) {
gtk_object_destroy (GTK_OBJECT (et->group));
et->group = e_table_group_new (GNOME_CANVAS_GROUP (et->canvas_vbox),
et->full_header,
et->header,
et->model,
et->sort_info,
0);
e_canvas_vbox_add_item(E_CANVAS_VBOX(et->canvas_vbox), GNOME_CANVAS_ITEM(et->group));
gnome_canvas_item_set(GNOME_CANVAS_ITEM(et->group),
"drawgrid", et->draw_grid,
"drawfocus", et->draw_focus,
"cursor_mode", et->cursor_mode,
"length_threshold", et->length_threshold,
"table_selection_model", et->selection,
NULL);
gtk_signal_connect (GTK_OBJECT (et->group), "cursor_change",
GTK_SIGNAL_FUNC (group_cursor_change), et);
gtk_signal_connect (GTK_OBJECT (et->group), "double_click",
GTK_SIGNAL_FUNC (group_double_click), et);
gtk_signal_connect (GTK_OBJECT (et->group), "right_click",
GTK_SIGNAL_FUNC (group_right_click), et);
gtk_signal_connect (GTK_OBJECT (et->group), "click",
GTK_SIGNAL_FUNC (group_click), et);
gtk_signal_connect (GTK_OBJECT (et->group), "key_press",
GTK_SIGNAL_FUNC (group_key_press), et);
e_table_fill_table (et, et->model);
gtk_object_set (GTK_OBJECT (et->canvas_vbox),
"width", (double) GTK_WIDGET (et->table_canvas)->allocation.width,
NULL);
if (GTK_WIDGET_REALIZED(et->table_canvas))
table_canvas_size_allocate (GTK_WIDGET(et->table_canvas), &GTK_WIDGET(et->table_canvas)->allocation, et);
}
et->need_rebuild = 0;
et->rebuild_idle_id = 0;
return FALSE;
}
static void
et_table_model_changed (ETableModel *model, ETable *et)
{
et->need_rebuild = TRUE;
if (!et->rebuild_idle_id)
et->rebuild_idle_id = g_idle_add_full (20, changed_idle, et, NULL);
}
static void
et_table_row_changed (ETableModel *table_model, int row, ETable *et)
{
if (!et->need_rebuild) {
if (e_table_group_remove (et->group, row))
e_table_group_add (et->group, row);
}
}
static void
et_table_cell_changed (ETableModel *table_model, int view_col, int row, ETable *et)
{
et_table_row_changed (table_model, row, et);
}
static void
et_table_row_inserted (ETableModel *table_model, int row, ETable *et)
{
/* This number has already been decremented. */
int row_count = e_table_model_row_count(table_model);
if (!et->need_rebuild) {
if (row != row_count - 1)
e_table_group_increment(et->group, row, 1);
e_table_group_add (et->group, row);
}
}
static void
et_table_row_deleted (ETableModel *table_model, int row, ETable *et)
{
int row_count = e_table_model_row_count(table_model);
if (!et->need_rebuild) {
e_table_group_remove (et->group, row);
if (row != row_count)
e_table_group_decrement(et->group, row, 1);
}
}
static void
et_canvas_realize (GtkWidget *canvas, ETable *e_table)
{
gnome_canvas_item_set(e_table->white_item,
"fill_color_gdk", &GTK_WIDGET(e_table->table_canvas)->style->base[GTK_STATE_NORMAL],
NULL);
}
static void
e_table_setup_table (ETable *e_table, ETableHeader *full_header, ETableHeader *header,
ETableModel *model)
{
e_table->table_canvas = GNOME_CANVAS (e_canvas_new ());
gtk_signal_connect (
GTK_OBJECT (e_table->table_canvas), "size_allocate",
GTK_SIGNAL_FUNC (table_canvas_size_allocate), e_table);
gtk_signal_connect (
GTK_OBJECT (e_table->table_canvas), "focus_in_event",
GTK_SIGNAL_FUNC (gtk_widget_queue_draw), e_table);
gtk_signal_connect (
GTK_OBJECT (e_table->table_canvas), "focus_out_event",
GTK_SIGNAL_FUNC (gtk_widget_queue_draw), e_table);
gtk_signal_connect (
GTK_OBJECT (e_table), "drag_begin",
GTK_SIGNAL_FUNC (et_drag_begin), e_table);
gtk_signal_connect (
GTK_OBJECT (e_table), "drag_end",
GTK_SIGNAL_FUNC (et_drag_end), e_table);
gtk_signal_connect (
GTK_OBJECT (e_table), "drag_data_get",
GTK_SIGNAL_FUNC (et_drag_data_get), e_table);
gtk_signal_connect (
GTK_OBJECT (e_table), "drag_data_delete",
GTK_SIGNAL_FUNC (et_drag_data_delete), e_table);
gtk_signal_connect (
GTK_OBJECT (e_table), "drag_motion",
GTK_SIGNAL_FUNC (et_drag_motion), e_table);
gtk_signal_connect (
GTK_OBJECT (e_table), "drag_leave",
GTK_SIGNAL_FUNC (et_drag_leave), e_table);
gtk_signal_connect (
GTK_OBJECT (e_table), "drag_drop",
GTK_SIGNAL_FUNC (et_drag_drop), e_table);
gtk_signal_connect (
GTK_OBJECT (e_table), "drag_data_received",
GTK_SIGNAL_FUNC (et_drag_data_received), e_table);
gtk_signal_connect (GTK_OBJECT(e_table->table_canvas), "reflow",
GTK_SIGNAL_FUNC (table_canvas_reflow), e_table);
gtk_widget_show (GTK_WIDGET (e_table->table_canvas));
e_table->white_item = gnome_canvas_item_new(gnome_canvas_root(e_table->table_canvas),
gnome_canvas_rect_get_type(),
"x1", (double) 0,
"y1", (double) 0,
"x2", (double) 100,
"y2", (double) 100,
"fill_color_gdk", &GTK_WIDGET(e_table->table_canvas)->style->base[GTK_STATE_NORMAL],
NULL);
gtk_signal_connect(GTK_OBJECT(e_table->table_canvas), "realize",
GTK_SIGNAL_FUNC(et_canvas_realize), e_table);
e_table->canvas_vbox = gnome_canvas_item_new(gnome_canvas_root(e_table->table_canvas),
e_canvas_vbox_get_type(),
"spacing", 10.0,
NULL);
if (e_table->use_click_to_add) {
e_table->click_to_add = gnome_canvas_item_new (GNOME_CANVAS_GROUP(e_table->canvas_vbox),
e_table_click_to_add_get_type (),
"header", e_table->header,
"model", e_table->model,
"message", e_table->click_to_add_message,
NULL);
e_canvas_vbox_add_item(E_CANVAS_VBOX(e_table->canvas_vbox), e_table->click_to_add);
gtk_signal_connect(GTK_OBJECT (e_table->click_to_add), "cursor_change",
GTK_SIGNAL_FUNC(click_to_add_cursor_change), e_table);
}
e_table->group = e_table_group_new (
GNOME_CANVAS_GROUP (e_table->canvas_vbox),
full_header, header,
model, e_table->sort_info, 0);
e_canvas_vbox_add_item(E_CANVAS_VBOX(e_table->canvas_vbox), GNOME_CANVAS_ITEM(e_table->group));
gnome_canvas_item_set(GNOME_CANVAS_ITEM(e_table->group),
"drawgrid", e_table->draw_grid,
"drawfocus", e_table->draw_focus,
"cursor_mode", e_table->cursor_mode,
"length_threshold", e_table->length_threshold,
"table_selection_model", e_table->selection,
NULL);
gtk_signal_connect (GTK_OBJECT (e_table->group), "cursor_change",
GTK_SIGNAL_FUNC(group_cursor_change), e_table);
gtk_signal_connect (GTK_OBJECT (e_table->group), "double_click",
GTK_SIGNAL_FUNC(group_double_click), e_table);
gtk_signal_connect (GTK_OBJECT (e_table->group), "right_click",
GTK_SIGNAL_FUNC(group_right_click), e_table);
gtk_signal_connect (GTK_OBJECT (e_table->group), "click",
GTK_SIGNAL_FUNC(group_click), e_table);
gtk_signal_connect (GTK_OBJECT (e_table->group), "key_press",
GTK_SIGNAL_FUNC(group_key_press), e_table);
e_table->table_model_change_id = gtk_signal_connect (
GTK_OBJECT (model), "model_changed",
GTK_SIGNAL_FUNC (et_table_model_changed), e_table);
e_table->table_row_change_id = gtk_signal_connect (
GTK_OBJECT (model), "model_row_changed",
GTK_SIGNAL_FUNC (et_table_row_changed), e_table);
e_table->table_cell_change_id = gtk_signal_connect (
GTK_OBJECT (model), "model_cell_changed",
GTK_SIGNAL_FUNC (et_table_cell_changed), e_table);
e_table->table_row_inserted_id = gtk_signal_connect (
GTK_OBJECT (model), "model_row_inserted",
GTK_SIGNAL_FUNC (et_table_row_inserted), e_table);
e_table->table_row_deleted_id = gtk_signal_connect (
GTK_OBJECT (model), "model_row_deleted",
GTK_SIGNAL_FUNC (et_table_row_deleted), e_table);
}
static void
e_table_fill_table (ETable *e_table, ETableModel *model)
{
e_table_group_add_all (e_table->group);
}
static ETableCol *
et_col_spec_to_col (ETable *e_table, ETableColumnSpecification *col_spec, ETableExtras *ete)
{
ETableCol *col = NULL;
ECell *cell;
GCompareFunc compare;
cell = e_table_extras_get_cell(ete, col_spec->cell);
compare = e_table_extras_get_compare(ete, col_spec->compare);
if (cell && compare) {
if (col_spec->pixbuf && *col_spec->pixbuf) {
GdkPixbuf *pixbuf;
pixbuf = e_table_extras_get_pixbuf(ete, col_spec->pixbuf);
if (pixbuf) {
col = e_table_col_new_with_pixbuf (col_spec->model_col, col_spec->title_, pixbuf,
col_spec->expansion, col_spec->minimum_width,
cell, compare, col_spec->resizable);
}
}
if (col == NULL && col_spec->title_ && *col_spec->title_) {
col = e_table_col_new (col_spec->model_col, col_spec->title_,
col_spec->expansion, col_spec->minimum_width,
cell, compare, col_spec->resizable);
}
}
return col;
}
static ETableHeader *
et_spec_to_full_header (ETable *e_table, ETableSpecification *spec, ETableExtras *ete)
{
ETableHeader *nh;
int column;
g_return_val_if_fail (e_table, NULL);
g_return_val_if_fail (spec, NULL);
g_return_val_if_fail (ete, NULL);
nh = e_table_header_new ();
for (column = 0; spec->columns[column]; column++) {
ETableCol *col = et_col_spec_to_col(e_table, spec->columns[column], ete);
if (col)
e_table_header_add_column (nh, col, -1);
}
return nh;
}
static ETableHeader *
et_state_to_header (ETable *e_table, ETableHeader *full_header, ETableState *state)
{
ETableHeader *nh;
const int max_cols = e_table_header_count (full_header);
int column;
g_return_val_if_fail (e_table, NULL);
g_return_val_if_fail (full_header, NULL);
g_return_val_if_fail (state, NULL);
nh = e_table_header_new ();
gtk_object_set(GTK_OBJECT(nh),
"width_extras", e_table_header_width_extras(GTK_WIDGET(e_table)->style),
NULL);
for (column = 0; column < state->col_count; column++) {
int col;
col = state->columns[column];
if (col >= max_cols)
continue;
e_table_header_add_column (nh, e_table_header_get_column (full_header, col), -1);
}
return nh;
}
void
e_table_set_state_object(ETable *e_table, ETableState *state)
{
if (e_table->header)
gtk_object_unref(GTK_OBJECT(e_table->header));
e_table->header = et_state_to_header (e_table, e_table->full_header, state);
if (e_table->header)
gtk_object_ref(GTK_OBJECT(e_table->header));
if (e_table->sort_info) {
if (e_table->group_info_change_id)
gtk_signal_disconnect (GTK_OBJECT (e_table->sort_info),
e_table->group_info_change_id);
gtk_object_unref(GTK_OBJECT(e_table->sort_info));
}
e_table->sort_info = state->sort_info;
if (e_table->sort_info) {
gtk_object_ref(GTK_OBJECT(e_table->sort_info));
e_table->group_info_change_id =
gtk_signal_connect (GTK_OBJECT (e_table->sort_info),
"group_info_changed",
GTK_SIGNAL_FUNC (sort_info_changed),
e_table);
}
if (e_table->sorter)
gtk_object_set(GTK_OBJECT(e_table->sorter),
"sort_info", e_table->sort_info,
NULL);
if (e_table->header_item)
gtk_object_set(GTK_OBJECT(e_table->header_item),
"ETableHeader", e_table->header,
"sort_info", e_table->sort_info,
NULL);
if (e_table->click_to_add)
gtk_object_set(GTK_OBJECT(e_table->click_to_add),
"header", e_table->header,
NULL);
e_table->need_rebuild = TRUE;
if (!e_table->rebuild_idle_id)
e_table->rebuild_idle_id = g_idle_add_full (20, changed_idle, e_table, NULL);
}
void e_table_set_state (ETable *e_table,
const gchar *state_str)
{
ETableState *state;
g_return_if_fail(e_table != NULL);
g_return_if_fail(E_IS_TABLE(e_table));
g_return_if_fail(state_str != NULL);
state = e_table_state_new();
e_table_state_load_from_string(state, state_str);
if (state->col_count > 0)
e_table_set_state_object(e_table, state);
gtk_object_unref(GTK_OBJECT(state));
}
void e_table_load_state (ETable *e_table,
const gchar *filename)
{
ETableState *state;
g_return_if_fail(e_table != NULL);
g_return_if_fail(E_IS_TABLE(e_table));
g_return_if_fail(filename != NULL);
state = e_table_state_new();
e_table_state_load_from_file(state, filename);
if (state->col_count > 0)
e_table_set_state_object(e_table, state);
gtk_object_unref(GTK_OBJECT(state));
}
ETableState *
e_table_get_state_object (ETable *e_table)
{
ETableState *state;
int full_col_count;
int i, j;
state = e_table_state_new();
state->sort_info = e_table->sort_info;
gtk_object_ref(GTK_OBJECT(state->sort_info));
state->col_count = e_table_header_count (e_table->header);
full_col_count = e_table_header_count (e_table->full_header);
state->columns = g_new(int, state->col_count);
for (i = 0; i < state->col_count; i++) {
int col_idx = e_table_header_index(e_table->header, i);
state->columns[i] = -1;
for (j = 0; j < full_col_count; j++) {
if (col_idx == e_table_header_index(e_table->full_header, j)) {
state->columns[i] = j;
break;
}
}
}
return state;
}
gchar *e_table_get_state (ETable *e_table)
{
ETableState *state;
gchar *string;
state = e_table_get_state_object(e_table);
string = e_table_state_save_to_string(state);
gtk_object_unref(GTK_OBJECT(state));
return string;
}
void e_table_save_state (ETable *e_table,
const gchar *filename)
{
ETableState *state;
state = e_table_get_state_object(e_table);
e_table_state_save_to_file(state, filename);
gtk_object_unref(GTK_OBJECT(state));
}
static void
et_selection_model_selection_change (ETableGroup *etg, ETable *et)
{
gtk_signal_emit (GTK_OBJECT (et),
et_signals [SELECTION_CHANGE]);
}
static ETable *
et_real_construct (ETable *e_table, ETableModel *etm, ETableExtras *ete,
ETableSpecification *specification, ETableState *state)
{
int row = 0;
if (ete)
gtk_object_ref(GTK_OBJECT(ete));
else
ete = e_table_extras_new();
e_table->use_click_to_add = specification->click_to_add;
e_table->click_to_add_message = g_strdup(specification->click_to_add_message_);
e_table->draw_grid = specification->draw_grid;
e_table->cursor_mode = specification->cursor_mode;
e_table->full_header = et_spec_to_full_header(e_table, specification, ete);
e_table->model = etm;
gtk_object_ref (GTK_OBJECT (etm));
gtk_widget_push_visual (gdk_rgb_get_visual ());
gtk_widget_push_colormap (gdk_rgb_get_cmap ());
e_table->header = et_state_to_header (e_table, e_table->full_header, state);
e_table->sort_info = state->sort_info;
e_table->group_info_change_id =
gtk_signal_connect (GTK_OBJECT (e_table->sort_info), "group_info_changed",
GTK_SIGNAL_FUNC (sort_info_changed), e_table);
gtk_object_set(GTK_OBJECT(e_table->header),
"sort_info", e_table->sort_info,
NULL);
e_table->sorter = e_table_sorter_new(etm, e_table->full_header, e_table->sort_info);
gtk_object_set (GTK_OBJECT (e_table->selection),
"model", etm,
"sorter", e_table->sorter,
NULL);
gtk_signal_connect(GTK_OBJECT(e_table->selection), "selection_changed",
GTK_SIGNAL_FUNC(et_selection_model_selection_change), e_table);
if (!specification->no_headers) {
e_table_setup_header (e_table);
}
e_table_setup_table (e_table, e_table->full_header, e_table->header, etm);
e_table_fill_table (e_table, etm);
gtk_layout_get_vadjustment (GTK_LAYOUT (e_table->table_canvas))->step_increment = 20;
gtk_adjustment_changed(gtk_layout_get_vadjustment (GTK_LAYOUT (e_table->table_canvas)));
if (!specification->no_headers) {
/*
* The header
*/
gtk_table_attach (GTK_TABLE (e_table), GTK_WIDGET (e_table->header_canvas),
0, 1, 0 + row, 1 + row,
GTK_FILL | GTK_EXPAND,
GTK_FILL, 0, 0);
row ++;
}
gtk_table_attach (GTK_TABLE (e_table), GTK_WIDGET (e_table->table_canvas),
0, 1, 0 + row, 1 + row,
GTK_FILL | GTK_EXPAND,
GTK_FILL | GTK_EXPAND,
0, 0);
gtk_widget_pop_colormap ();
gtk_widget_pop_visual ();
gtk_object_unref(GTK_OBJECT(ete));
return e_table;
}
ETable *
e_table_construct (ETable *e_table, ETableModel *etm, ETableExtras *ete,
const char *spec_str, const char *state_str)
{
ETableSpecification *specification;
ETableState *state;
g_return_val_if_fail(e_table != NULL, NULL);
g_return_val_if_fail(E_IS_TABLE(e_table), NULL);
g_return_val_if_fail(etm != NULL, NULL);
g_return_val_if_fail(E_IS_TABLE_MODEL(etm), NULL);
g_return_val_if_fail(ete == NULL || E_IS_TABLE_EXTRAS(ete), NULL);
g_return_val_if_fail(spec_str != NULL, NULL);
specification = e_table_specification_new();
e_table_specification_load_from_string(specification, spec_str);
if (state_str) {
state = e_table_state_new();
e_table_state_load_from_string(state, state_str);
if (state->col_count <= 0) {
gtk_object_unref(GTK_OBJECT(state));
state = specification->state;
gtk_object_ref(GTK_OBJECT(state));
}
} else {
state = specification->state;
gtk_object_ref(GTK_OBJECT(state));
}
e_table = et_real_construct (e_table, etm, ete, specification, state);
e_table->spec = specification;
gtk_object_unref(GTK_OBJECT(state));
return e_table;
}
ETable *
e_table_construct_from_spec_file (ETable *e_table, ETableModel *etm, ETableExtras *ete,
const char *spec_fn, const char *state_fn)
{
ETableSpecification *specification;
ETableState *state;
g_return_val_if_fail(e_table != NULL, NULL);
g_return_val_if_fail(E_IS_TABLE(e_table), NULL);
g_return_val_if_fail(etm != NULL, NULL);
g_return_val_if_fail(E_IS_TABLE_MODEL(etm), NULL);
g_return_val_if_fail(ete == NULL || E_IS_TABLE_EXTRAS(ete), NULL);
g_return_val_if_fail(spec_fn != NULL, NULL);
specification = e_table_specification_new();
if (!e_table_specification_load_from_file(specification, spec_fn)) {
gtk_object_unref(GTK_OBJECT(specification));
return NULL;
}
if (state_fn) {
state = e_table_state_new();
if (!e_table_state_load_from_file(state, state_fn)) {
gtk_object_unref(GTK_OBJECT(state));
state = specification->state;
gtk_object_ref(GTK_OBJECT(state));
}
if (state->col_count <= 0) {
gtk_object_unref(GTK_OBJECT(state));
state = specification->state;
gtk_object_ref(GTK_OBJECT(state));
}
} else {
state = specification->state;
gtk_object_ref(GTK_OBJECT(state));
}
e_table = et_real_construct (e_table, etm, ete, specification, state);
e_table->spec = specification;
gtk_object_unref(GTK_OBJECT(state));
return e_table;
}
GtkWidget *
e_table_new (ETableModel *etm, ETableExtras *ete, const char *spec, const char *state)
{
ETable *e_table;
g_return_val_if_fail(etm != NULL, NULL);
g_return_val_if_fail(E_IS_TABLE_MODEL(etm), NULL);
g_return_val_if_fail(ete == NULL || E_IS_TABLE_EXTRAS(ete), NULL);
g_return_val_if_fail(spec != NULL, NULL);
e_table = gtk_type_new (e_table_get_type ());
e_table = e_table_construct (e_table, etm, ete, spec, state);
return GTK_WIDGET (e_table);
}
GtkWidget *
e_table_new_from_spec_file (ETableModel *etm, ETableExtras *ete, const char *spec_fn, const char *state_fn)
{
ETable *e_table;
g_return_val_if_fail(etm != NULL, NULL);
g_return_val_if_fail(E_IS_TABLE_MODEL(etm), NULL);
g_return_val_if_fail(ete == NULL || E_IS_TABLE_EXTRAS(ete), NULL);
g_return_val_if_fail(spec_fn != NULL, NULL);
e_table = gtk_type_new (e_table_get_type ());
e_table = e_table_construct_from_spec_file (e_table, etm, ete, spec_fn, state_fn);
return GTK_WIDGET (e_table);
}
#if 0
static xmlNode *
et_build_column_spec (ETable *e_table)
{
xmlNode *columns_shown;
gint i;
gint col_count;
columns_shown = xmlNewNode (NULL, "columns-shown");
col_count = e_table_header_count (e_table->header);
for (i = 0; i < col_count; i++){
gchar *text = g_strdup_printf ("%d", e_table_header_index(e_table->header, i));
xmlNewChild (columns_shown, NULL, "column", text);
g_free (text);
}
return columns_shown;
}
static xmlNode *
et_build_grouping_spec (ETable *e_table)
{
xmlNode *node;
xmlNode *grouping;
int i;
const int sort_count = e_table_sort_info_sorting_get_count (e_table->sort_info);
const int group_count = e_table_sort_info_grouping_get_count (e_table->sort_info);
grouping = xmlNewNode (NULL, "grouping");
node = grouping;
for (i = 0; i < group_count; i++) {
ETableSortColumn column = e_table_sort_info_grouping_get_nth(e_table->sort_info, i);
xmlNode *new_node = xmlNewChild(node, NULL, "group", NULL);
e_xml_set_integer_prop_by_name (new_node, "column", column.column);
e_xml_set_integer_prop_by_name (new_node, "ascending", column.ascending);
node = new_node;
}
for (i = 0; i < sort_count; i++) {
ETableSortColumn column = e_table_sort_info_sorting_get_nth(e_table->sort_info, i);
xmlNode *new_node = xmlNewChild(node, NULL, "leaf", NULL);
e_xml_set_integer_prop_by_name (new_node, "column", column.column);
e_xml_set_integer_prop_by_name (new_node, "ascending", column.ascending);
node = new_node;
}
return grouping;
}
static xmlDoc *
et_build_tree (ETable *e_table)
{
xmlDoc *doc;
xmlNode *root;
doc = xmlNewDoc ("1.0");
if (doc == NULL)
return NULL;
root = xmlNewDocNode (doc, NULL, "ETableSpecification", NULL);
xmlDocSetRootElement (doc, root);
xmlAddChild (root, et_build_column_spec (e_table));
xmlAddChild (root, et_build_grouping_spec (e_table));
return doc;
}
gchar *
e_table_get_specification (ETable *e_table)
{
xmlDoc *doc;
xmlChar *buffer;
gint size;
g_return_val_if_fail(e_table != NULL, NULL);
g_return_val_if_fail(E_IS_TABLE(e_table), NULL);
doc = et_build_tree (e_table);
xmlDocDumpMemory (doc, &buffer, &size);
xmlFreeDoc (doc);
return buffer;
}
int
e_table_set_specification (ETable *e_table, const char *spec)
{
xmlDoc *xmlSpec;
int ret;
g_return_val_if_fail(e_table != NULL, -1);
g_return_val_if_fail(E_IS_TABLE(e_table), -1);
g_return_val_if_fail(spec != NULL, -1);
/* doesn't work yet, sigh */
xmlSpec = xmlParseMemory ((char *)spec, strlen(spec));
ret = et_real_set_specification(e_table, xmlSpec);
xmlFreeDoc (xmlSpec);
return ret;
}
void
e_table_save_specification (ETable *e_table, gchar *filename)
{
xmlDoc *doc = et_build_tree (e_table);
g_return_if_fail(e_table != NULL);
g_return_if_fail(E_IS_TABLE(e_table));
g_return_if_fail(filename != NULL);
xmlSaveFile (filename, doc);
xmlFreeDoc (doc);
}
int
e_table_load_specification (ETable *e_table, gchar *filename)
{
xmlDoc *xmlSpec;
int ret;
g_return_val_if_fail(e_table != NULL, -1);
g_return_val_if_fail(E_IS_TABLE(e_table), -1);
g_return_val_if_fail(filename != NULL, -1);
/* doesn't work yet, yay */
xmlSpec = xmlParseFile (filename);
ret = et_real_set_specification(e_table, xmlSpec);
xmlFreeDoc (xmlSpec);
return ret;
}
#endif
void
e_table_set_cursor_row (ETable *e_table, int row)
{
g_return_if_fail(e_table != NULL);
g_return_if_fail(E_IS_TABLE(e_table));
g_return_if_fail(row >= 0);
gtk_object_set(GTK_OBJECT(e_table->selection),
"cursor_row", row,
NULL);
}
int
e_table_get_cursor_row (ETable *e_table)
{
int row;
g_return_val_if_fail(e_table != NULL, -1);
g_return_val_if_fail(E_IS_TABLE(e_table), -1);
gtk_object_get(GTK_OBJECT(e_table->selection),
"cursor_row", &row,
NULL);
return row;
}
void
e_table_selected_row_foreach (ETable *e_table,
ETableForeachFunc callback,
gpointer closure)
{
g_return_if_fail(e_table != NULL);
g_return_if_fail(E_IS_TABLE(e_table));
e_table_selection_model_foreach(e_table->selection,
callback,
closure);
}
gint
e_table_selected_count (ETable *e_table)
{
g_return_val_if_fail(e_table != NULL, -1);
g_return_val_if_fail(E_IS_TABLE(e_table), -1);
return e_table_selection_model_selected_count(e_table->selection);
}
void
e_table_select_all (ETable *table)
{
g_return_if_fail (table != NULL);
g_return_if_fail (E_IS_TABLE (table));
e_table_selection_model_select_all (table->selection);
}
void
e_table_invert_selection (ETable *table)
{
g_return_if_fail (table != NULL);
g_return_if_fail (E_IS_TABLE (table));
e_table_selection_model_invert_selection (table->selection);
}
EPrintable *
e_table_get_printable (ETable *e_table)
{
g_return_val_if_fail(e_table != NULL, NULL);
g_return_val_if_fail(E_IS_TABLE(e_table), NULL);
return e_table_group_get_printable(e_table->group);
}
static void
et_get_arg (GtkObject *o, GtkArg *arg, guint arg_id)
{
ETable *etable = E_TABLE (o);
switch (arg_id){
case ARG_TABLE_DRAW_FOCUS:
GTK_VALUE_BOOL (*arg) = etable->draw_focus;
break;
}
}
typedef struct {
char *arg;
gboolean setting;
} bool_closure;
static void
et_set_arg (GtkObject *o, GtkArg *arg, guint arg_id)
{
ETable *etable = E_TABLE (o);
switch (arg_id){
case ARG_LENGTH_THRESHOLD:
etable->length_threshold = GTK_VALUE_INT (*arg);
if (etable->group) {
gnome_canvas_item_set (GNOME_CANVAS_ITEM(etable->group),
"length_threshold", GTK_VALUE_INT (*arg),
NULL);
}
break;
case ARG_TABLE_DRAW_FOCUS:
etable->draw_focus = GTK_VALUE_BOOL (*arg);
if (etable->group) {
gnome_canvas_item_set (GNOME_CANVAS_ITEM(etable->group),
"drawfocus", GTK_VALUE_BOOL (*arg),
NULL);
}
break;
}
}
static void
set_scroll_adjustments (ETable *table,
GtkAdjustment *hadjustment,
GtkAdjustment *vadjustment)
{
if (vadjustment != NULL) {
vadjustment->step_increment = 20;
gtk_adjustment_changed(vadjustment);
}
gtk_layout_set_hadjustment (GTK_LAYOUT(table->table_canvas),
hadjustment);
gtk_layout_set_vadjustment (GTK_LAYOUT(table->table_canvas),
vadjustment);
if (table->header_canvas != NULL)
gtk_layout_set_hadjustment (GTK_LAYOUT(table->header_canvas),
hadjustment);
}
gint
e_table_get_next_row (ETable *e_table,
gint model_row)
{
g_return_val_if_fail(e_table != NULL, -1);
g_return_val_if_fail(E_IS_TABLE(e_table), -1);
if (e_table->sorter) {
int i;
i = e_table_sorter_model_to_sorted(e_table->sorter, model_row);
i++;
if (i < e_table_model_row_count(e_table->model)) {
return e_table_sorter_sorted_to_model(e_table->sorter, i);
} else
return -1;
} else
if (model_row < e_table_model_row_count(e_table->model) - 1)
return model_row + 1;
else
return -1;
}
gint
e_table_get_prev_row (ETable *e_table,
gint model_row)
{
g_return_val_if_fail(e_table != NULL, -1);
g_return_val_if_fail(E_IS_TABLE(e_table), -1);
if (e_table->sorter) {
int i;
i = e_table_sorter_model_to_sorted(e_table->sorter, model_row);
i--;
if (i >= 0)
return e_table_sorter_sorted_to_model(e_table->sorter, i);
else
return -1;
} else
return model_row - 1;
}
gint
e_table_model_to_view_row (ETable *e_table,
gint model_row)
{
g_return_val_if_fail(e_table != NULL, -1);
g_return_val_if_fail(E_IS_TABLE(e_table), -1);
if (e_table->sorter)
return e_table_sorter_model_to_sorted(e_table->sorter, model_row);
else
return model_row;
}
gint
e_table_view_to_model_row (ETable *e_table,
gint view_row)
{
g_return_val_if_fail(e_table != NULL, -1);
g_return_val_if_fail(E_IS_TABLE(e_table), -1);
if (e_table->sorter)
return e_table_sorter_sorted_to_model(e_table->sorter, view_row);
else
return view_row;
}
struct _ETableDragSourceSite
{
GdkModifierType start_button_mask;
GtkTargetList *target_list; /* Targets for drag data */
GdkDragAction actions; /* Possible actions */
GdkColormap *colormap; /* Colormap for drag icon */
GdkPixmap *pixmap; /* Icon for drag data */
GdkBitmap *mask;
/* Stored button press information to detect drag beginning */
gint state;
gint x, y;
gint row, col;
};
typedef enum
{
GTK_DRAG_STATUS_DRAG,
GTK_DRAG_STATUS_WAIT,
GTK_DRAG_STATUS_DROP
} GtkDragStatus;
typedef struct _GtkDragDestInfo GtkDragDestInfo;
typedef struct _GtkDragSourceInfo GtkDragSourceInfo;
struct _GtkDragDestInfo
{
GtkWidget *widget; /* Widget in which drag is in */
GdkDragContext *context; /* Drag context */
GtkDragSourceInfo *proxy_source; /* Set if this is a proxy drag */
GtkSelectionData *proxy_data; /* Set while retrieving proxied data */
gboolean dropped : 1; /* Set after we receive a drop */
guint32 proxy_drop_time; /* Timestamp for proxied drop */
gboolean proxy_drop_wait : 1; /* Set if we are waiting for a
* status reply before sending
* a proxied drop on.
*/
gint drop_x, drop_y; /* Position of drop */
};
struct _GtkDragSourceInfo
{
GtkWidget *widget;
GtkTargetList *target_list; /* Targets for drag data */
GdkDragAction possible_actions; /* Actions allowed by source */
GdkDragContext *context; /* drag context */
GtkWidget *icon_window; /* Window for drag */
GtkWidget *ipc_widget; /* GtkInvisible for grab, message passing */
GdkCursor *cursor; /* Cursor for drag */
gint hot_x, hot_y; /* Hot spot for drag */
gint button; /* mouse button starting drag */
GtkDragStatus status; /* drag status */
GdkEvent *last_event; /* motion event waiting for response */
gint start_x, start_y; /* Initial position */
gint cur_x, cur_y; /* Current Position */
GList *selections; /* selections we've claimed */
GtkDragDestInfo *proxy_dest; /* Set if this is a proxy drag */
guint drop_timeout; /* Timeout for aborting drop */
guint destroy_icon : 1; /* If true, destroy icon_window
*/
};
/* Drag & drop stuff. */
/* Target */
void e_table_drag_get_data (ETable *table,
int row,
int col,
GdkDragContext *context,
GdkAtom target,
guint32 time)
{
g_return_if_fail(table != NULL);
g_return_if_fail(E_IS_TABLE(table));
table->drag_get_data_row = row;
table->drag_get_data_col = col;
gtk_drag_get_data(GTK_WIDGET(table),
context,
target,
time);
}
void e_table_drag_highlight (ETable *table,
int row,
int col) /* col == -1 to highlight entire row. */
{
g_return_if_fail(table != NULL);
g_return_if_fail(E_IS_TABLE(table));
}
void e_table_drag_unhighlight (ETable *table)
{
g_return_if_fail(table != NULL);
g_return_if_fail(E_IS_TABLE(table));
}
void e_table_drag_dest_set (ETable *table,
GtkDestDefaults flags,
const GtkTargetEntry *targets,
gint n_targets,
GdkDragAction actions)
{
g_return_if_fail(table != NULL);
g_return_if_fail(E_IS_TABLE(table));
gtk_drag_dest_set(GTK_WIDGET(table),
flags,
targets,
n_targets,
actions);
}
void e_table_drag_dest_set_proxy (ETable *table,
GdkWindow *proxy_window,
GdkDragProtocol protocol,
gboolean use_coordinates)
{
g_return_if_fail(table != NULL);
g_return_if_fail(E_IS_TABLE(table));
gtk_drag_dest_set_proxy(GTK_WIDGET(table),
proxy_window,
protocol,
use_coordinates);
}
/* There probably should be functions for setting the targets
* as a GtkTargetList
*/
void e_table_drag_dest_unset (GtkWidget *widget)
{
g_return_if_fail(widget != NULL);
g_return_if_fail(E_IS_TABLE(widget));
gtk_drag_dest_unset(widget);
}
/* Source side */
void e_table_drag_source_set (ETable *table,
GdkModifierType start_button_mask,
const GtkTargetEntry *targets,
gint n_targets,
GdkDragAction actions)
{
ETableDragSourceSite *site;
GtkWidget *canvas;
g_return_if_fail(table != NULL);
g_return_if_fail(E_IS_TABLE(table));
canvas = GTK_WIDGET(table->table_canvas);
site = table->site;
gtk_widget_add_events (canvas,
gtk_widget_get_events (canvas) |
GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
GDK_BUTTON_MOTION_MASK);
if (site) {
if (site->target_list)
gtk_target_list_unref (site->target_list);
} else {
site = g_new0 (ETableDragSourceSite, 1);
table->drag_source_button_press_event_id =
gtk_signal_connect (GTK_OBJECT (canvas), "button_press_event",
GTK_SIGNAL_FUNC (e_table_drag_source_event_cb),
table);
table->drag_source_motion_notify_event_id =
gtk_signal_connect (GTK_OBJECT (canvas), "motion_notify_event",
GTK_SIGNAL_FUNC (e_table_drag_source_event_cb),
table);
table->site = site;
}
site->start_button_mask = start_button_mask;
if (targets)
site->target_list = gtk_target_list_new (targets, n_targets);
else
site->target_list = NULL;
site->actions = actions;
}
void e_table_drag_source_unset (ETable *table)
{
ETableDragSourceSite *site;
g_return_if_fail (table != NULL);
g_return_if_fail (E_IS_TABLE(table));
site = table->site;
if (site) {
gtk_signal_disconnect (GTK_OBJECT (table->table_canvas), table->drag_source_button_press_event_id);
gtk_signal_disconnect (GTK_OBJECT (table->table_canvas), table->drag_source_motion_notify_event_id);
g_free(site);
table->site = NULL;
}
}
/* There probably should be functions for setting the targets
* as a GtkTargetList
*/
GdkDragContext *
e_table_drag_begin (ETable *table,
int row,
int col,
GtkTargetList *targets,
GdkDragAction actions,
gint button,
GdkEvent *event)
{
g_return_val_if_fail (table != NULL, NULL);
g_return_val_if_fail (E_IS_TABLE(table), NULL);
table->drag_row = row;
table->drag_col = col;
return gtk_drag_begin(GTK_WIDGET(table),
targets,
actions,
button,
event);
}
static void
e_table_compute_location(ETable *table,
GtkWidget *widget,
int x,
int y,
int *row,
int *col)
{
if (!(row || col))
return;
e_table_group_compute_location(table->group, &x, &y, row, col);
}
static void
et_drag_begin (GtkWidget *widget,
GdkDragContext *context,
ETable *et)
{
gtk_signal_emit (GTK_OBJECT (et),
et_signals [TABLE_DRAG_BEGIN],
et->drag_row,
et->drag_col,
context);
}
static void
et_drag_end (GtkWidget *widget,
GdkDragContext *context,
ETable *et)
{
gtk_signal_emit (GTK_OBJECT (et),
et_signals [TABLE_DRAG_END],
et->drag_row,
et->drag_col,
context);
}
static void
et_drag_data_get(GtkWidget *widget,
GdkDragContext *context,
GtkSelectionData *selection_data,
guint info,
guint time,
ETable *et)
{
gtk_signal_emit (GTK_OBJECT (et),
et_signals [TABLE_DRAG_DATA_GET],
et->drag_row,
et->drag_col,
context,
selection_data,
info,
time);
}
static void
et_drag_data_delete(GtkWidget *widget,
GdkDragContext *context,
ETable *et)
{
gtk_signal_emit (GTK_OBJECT (et),
et_signals [TABLE_DRAG_DATA_DELETE],
et->drag_row,
et->drag_col,
context);
}
static void
et_drag_leave(GtkWidget *widget,
GdkDragContext *context,
guint time,
ETable *et)
{
gtk_signal_emit (GTK_OBJECT (et),
et_signals [TABLE_DRAG_LEAVE],
et->drop_row,
et->drop_col,
context,
time);
et->drop_row = -1;
et->drop_col = -1;
}
static gboolean
et_drag_motion(GtkWidget *widget,
GdkDragContext *context,
gint x,
gint y,
guint time,
ETable *et)
{
gboolean ret_val;
int row, col;
e_table_compute_location(et,
widget,
x,
y,
&row,
&col);
if (et->drop_row >= 0 && et->drop_col >= 0 &&
row != et->drop_row && col != et->drop_row) {
gtk_signal_emit (GTK_OBJECT (et),
et_signals [TABLE_DRAG_LEAVE],
et->drop_row,
et->drop_col,
context,
time);
}
et->drop_row = row;
et->drop_col = col;
if (row >= 0 && col >= 0)
gtk_signal_emit (GTK_OBJECT (et),
et_signals [TABLE_DRAG_MOTION],
et->drop_row,
et->drop_col,
context,
x,
y,
time,
&ret_val);
return ret_val;
}
static gboolean
et_drag_drop(GtkWidget *widget,
GdkDragContext *context,
gint x,
gint y,
guint time,
ETable *et)
{
gboolean ret_val;
int row, col;
e_table_compute_location(et,
widget,
x,
y,
&row,
&col);
if (et->drop_row >= 0 && et->drop_col >= 0 &&
row != et->drop_row && col != et->drop_row) {
gtk_signal_emit (GTK_OBJECT (et),
et_signals [TABLE_DRAG_LEAVE],
et->drop_row,
et->drop_col,
context,
time);
if (row >= 0 && col >= 0)
gtk_signal_emit (GTK_OBJECT (et),
et_signals [TABLE_DRAG_MOTION],
row,
col,
context,
x,
y,
time,
&ret_val);
}
et->drop_row = row;
et->drop_col = col;
if (row >= 0 && col >= 0)
gtk_signal_emit (GTK_OBJECT (et),
et_signals [TABLE_DRAG_DROP],
et->drop_row,
et->drop_col,
context,
x,
y,
time,
&ret_val);
et->drop_row = -1;
et->drop_col = -1;
return ret_val;
}
static void
et_drag_data_received(GtkWidget *widget,
GdkDragContext *context,
gint x,
gint y,
GtkSelectionData *selection_data,
guint info,
guint time,
ETable *et)
{
int row, col;
e_table_compute_location(et,
widget,
x,
y,
&row,
&col);
gtk_signal_emit (GTK_OBJECT (et),
et_signals [TABLE_DRAG_DATA_RECEIVED],
row,
col,
context,
x,
y,
selection_data,
info,
time);
}
static gint
e_table_drag_source_event_cb (GtkWidget *widget,
GdkEvent *event,
ETable *table)
{
ETableDragSourceSite *site;
site = table->site;
switch (event->type) {
case GDK_BUTTON_PRESS:
if ((GDK_BUTTON1_MASK << (event->button.button - 1)) & site->start_button_mask) {
int row, col;
e_table_compute_location(table, widget, event->button.x, event->button.y, &row, &col);
if (row >= 0 && col >= 0) {
site->state |= (GDK_BUTTON1_MASK << (event->button.button - 1));
site->x = event->button.x;
site->y = event->button.y;
site->row = row;
site->col = col;
}
}
break;
case GDK_BUTTON_RELEASE:
if ((GDK_BUTTON1_MASK << (event->button.button - 1)) & site->start_button_mask) {
site->state &= ~(GDK_BUTTON1_MASK << (event->button.button - 1));
}
break;
case GDK_MOTION_NOTIFY:
if (site->state & event->motion.state & site->start_button_mask) {
/* FIXME: This is really broken and can leave us
* with a stuck grab
*/
int i;
for (i=1; i<6; i++) {
if (site->state & event->motion.state &
GDK_BUTTON1_MASK << (i - 1))
break;
}
if (MAX (abs (site->x - event->motion.x),
abs (site->y - event->motion.y)) > 3) {
GtkDragSourceInfo *info;
GdkDragContext *context;
site->state = 0;
context = e_table_drag_begin (table, site->row, site->col,
site->target_list,
site->actions,
i, event);
info = g_dataset_get_data (context, "gtk-info");
if (!info->icon_window) {
if (site->pixmap)
gtk_drag_set_icon_pixmap (context,
site->colormap,
site->pixmap,
site->mask, -2, -2);
else
gtk_drag_set_icon_default (context);
}
return TRUE;
}
}
break;
default: /* hit for 2/3BUTTON_PRESS */
break;
}
return FALSE;
}
static void
e_table_class_init (GtkObjectClass *object_class)
{
ETableClass *klass = E_TABLE_CLASS(object_class);
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(object_class);
e_table_parent_class = gtk_type_class (PARENT_TYPE);
object_class->destroy = et_destroy;
object_class->set_arg = et_set_arg;
object_class->get_arg = et_get_arg;
klass->cursor_change = NULL;
klass->selection_change = NULL;
klass->double_click = NULL;
klass->right_click = NULL;
klass->click = NULL;
klass->key_press = NULL;
klass->table_drag_begin = NULL;
klass->table_drag_end = NULL;
klass->table_drag_data_get = NULL;
klass->table_drag_data_delete = NULL;
klass->table_drag_leave = NULL;
klass->table_drag_motion = NULL;
klass->table_drag_drop = NULL;
klass->table_drag_data_received = NULL;
et_signals [CURSOR_CHANGE] =
gtk_signal_new ("cursor_change",
GTK_RUN_LAST,
object_class->type,
GTK_SIGNAL_OFFSET (ETableClass, cursor_change),
gtk_marshal_NONE__INT,
GTK_TYPE_NONE, 1, GTK_TYPE_INT);
et_signals [SELECTION_CHANGE] =
gtk_signal_new ("selection_change",
GTK_RUN_LAST,
object_class->type,
GTK_SIGNAL_OFFSET (ETableClass, selection_change),
gtk_marshal_NONE__NONE,
GTK_TYPE_NONE, 0);
et_signals [DOUBLE_CLICK] =
gtk_signal_new ("double_click",
GTK_RUN_LAST,
object_class->type,
GTK_SIGNAL_OFFSET (ETableClass, double_click),
gtk_marshal_NONE__INT_INT_POINTER,
GTK_TYPE_NONE, 3, GTK_TYPE_INT, GTK_TYPE_INT, GTK_TYPE_POINTER);
et_signals [RIGHT_CLICK] =
gtk_signal_new ("right_click",
GTK_RUN_LAST,
object_class->type,
GTK_SIGNAL_OFFSET (ETableClass, right_click),
e_marshal_INT__INT_INT_POINTER,
GTK_TYPE_INT, 3, GTK_TYPE_INT, GTK_TYPE_INT, GTK_TYPE_POINTER);
et_signals [CLICK] =
gtk_signal_new ("click",
GTK_RUN_LAST,
object_class->type,
GTK_SIGNAL_OFFSET (ETableClass, click),
e_marshal_INT__INT_INT_POINTER,
GTK_TYPE_INT, 3, GTK_TYPE_INT, GTK_TYPE_INT, GTK_TYPE_POINTER);
et_signals [KEY_PRESS] =
gtk_signal_new ("key_press",
GTK_RUN_LAST,
object_class->type,
GTK_SIGNAL_OFFSET (ETableClass, key_press),
e_marshal_INT__INT_INT_POINTER,
GTK_TYPE_INT, 3, GTK_TYPE_INT, GTK_TYPE_INT, GTK_TYPE_POINTER);
et_signals[TABLE_DRAG_BEGIN] =
gtk_signal_new ("table_drag_begin",
GTK_RUN_LAST,
object_class->type,
GTK_SIGNAL_OFFSET (ETableClass, table_drag_begin),
gtk_marshal_NONE__INT_INT_POINTER,
GTK_TYPE_NONE, 3,
GTK_TYPE_INT,
GTK_TYPE_INT,
GTK_TYPE_GDK_DRAG_CONTEXT);
et_signals[TABLE_DRAG_END] =
gtk_signal_new ("table_drag_end",
GTK_RUN_LAST,
object_class->type,
GTK_SIGNAL_OFFSET (ETableClass, table_drag_end),
gtk_marshal_NONE__INT_INT_POINTER,
GTK_TYPE_NONE, 3,
GTK_TYPE_INT,
GTK_TYPE_INT,
GTK_TYPE_GDK_DRAG_CONTEXT);
et_signals[TABLE_DRAG_DATA_GET] =
gtk_signal_new ("table_drag_data_get",
GTK_RUN_LAST,
object_class->type,
GTK_SIGNAL_OFFSET (ETableClass, table_drag_data_get),
e_marshal_NONE__INT_INT_POINTER_POINTER_UINT_UINT,
GTK_TYPE_NONE, 6,
GTK_TYPE_INT,
GTK_TYPE_INT,
GTK_TYPE_GDK_DRAG_CONTEXT,
GTK_TYPE_SELECTION_DATA,
GTK_TYPE_UINT,
GTK_TYPE_UINT);
et_signals[TABLE_DRAG_DATA_DELETE] =
gtk_signal_new ("table_drag_data_delete",
GTK_RUN_LAST,
object_class->type,
GTK_SIGNAL_OFFSET (ETableClass, table_drag_data_delete),
gtk_marshal_NONE__INT_INT_POINTER,
GTK_TYPE_NONE, 3,
GTK_TYPE_INT,
GTK_TYPE_INT,
GTK_TYPE_GDK_DRAG_CONTEXT);
et_signals[TABLE_DRAG_LEAVE] =
gtk_signal_new ("table_drag_leave",
GTK_RUN_LAST,
object_class->type,
GTK_SIGNAL_OFFSET (ETableClass, table_drag_leave),
e_marshal_NONE__INT_INT_POINTER_UINT,
GTK_TYPE_NONE, 4,
GTK_TYPE_INT,
GTK_TYPE_INT,
GTK_TYPE_GDK_DRAG_CONTEXT,
GTK_TYPE_UINT);
et_signals[TABLE_DRAG_MOTION] =
gtk_signal_new ("table_drag_motion",
GTK_RUN_LAST,
object_class->type,
GTK_SIGNAL_OFFSET (ETableClass, table_drag_motion),
e_marshal_BOOL__INT_INT_POINTER_INT_INT_UINT,
GTK_TYPE_BOOL, 6,
GTK_TYPE_INT,
GTK_TYPE_INT,
GTK_TYPE_GDK_DRAG_CONTEXT,
GTK_TYPE_INT,
GTK_TYPE_INT,
GTK_TYPE_UINT);
et_signals[TABLE_DRAG_DROP] =
gtk_signal_new ("table_drag_drop",
GTK_RUN_LAST,
object_class->type,
GTK_SIGNAL_OFFSET (ETableClass, table_drag_drop),
e_marshal_BOOL__INT_INT_POINTER_INT_INT_UINT,
GTK_TYPE_BOOL, 6,
GTK_TYPE_INT,
GTK_TYPE_INT,
GTK_TYPE_GDK_DRAG_CONTEXT,
GTK_TYPE_INT,
GTK_TYPE_INT,
GTK_TYPE_UINT);
et_signals[TABLE_DRAG_DATA_RECEIVED] =
gtk_signal_new ("table_drag_data_received",
GTK_RUN_LAST,
object_class->type,
GTK_SIGNAL_OFFSET (ETableClass, table_drag_data_received),
e_marshal_NONE__INT_INT_POINTER_INT_INT_POINTER_UINT_UINT,
GTK_TYPE_NONE, 8,
GTK_TYPE_INT,
GTK_TYPE_INT,
GTK_TYPE_GDK_DRAG_CONTEXT,
GTK_TYPE_INT,
GTK_TYPE_INT,
GTK_TYPE_SELECTION_DATA,
GTK_TYPE_UINT,
GTK_TYPE_UINT);
gtk_object_class_add_signals (object_class, et_signals, LAST_SIGNAL);
klass->set_scroll_adjustments = set_scroll_adjustments;
widget_class->set_scroll_adjustments_signal =
gtk_signal_new ("set_scroll_adjustments",
GTK_RUN_LAST,
object_class->type,
GTK_SIGNAL_OFFSET (ETableClass, set_scroll_adjustments),
gtk_marshal_NONE__POINTER_POINTER,
GTK_TYPE_NONE, 2, GTK_TYPE_ADJUSTMENT, GTK_TYPE_ADJUSTMENT);
gtk_object_add_arg_type ("ETable::drawfocus", GTK_TYPE_BOOL,
GTK_ARG_READWRITE, ARG_TABLE_DRAW_FOCUS);
gtk_object_add_arg_type ("ETable::length_threshold", GTK_TYPE_INT,
GTK_ARG_WRITABLE, ARG_LENGTH_THRESHOLD);
}
E_MAKE_TYPE(e_table, "ETable", ETable, e_table_class_init, e_table_init, PARENT_TYPE);