Files
evolution/widgets/table/e-table.c
Matthew Barnes abc0e4c694 Introduce ESelectable and EFocusTracker.
EFocusTracker tracks the input focus within a window and helps keep
the sensitivity of "selectable" actions in the main menu up-to-date.
Selectable actions include Cut, Copy, Paste, Select All and Delete.

EFocusTracker has built-in support for widgets that implement the
GtkEditable interface such as GtkEntry and GtkTextView.  It also
supports custom widgets that implement the ESelectable interface,
which is a subset of GtkEditable and can apply to anything that
displays selectable content (esp. tree views and ETables).

This commit integrates EFocusTracker with EShellWindow, CompEditor,
EMsgComposer, and ESignatureManager.

It also bumps the GtkHTML requirement to 2.29.5 to utilize the new
GtkhtmlEditor:html constructor property.
2009-12-25 15:42:17 -05:00

3365 lines
89 KiB
C

/*
* e-table.c - A graphical view of a Table.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with the program; if not, see <http://www.gnu.org/licenses/>
*
*
* Authors:
* Chris Lahey <clahey@ximian.com>
* Miguel de Icaza <miguel@ximian.com>
*
* Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
*
*/
#include <config.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <glib.h>
#include <glib/gstdio.h>
#include <gdk/gdkkeysyms.h>
#include <gtk/gtk.h>
#include <libgnomecanvas/gnome-canvas.h>
#include <libgnomecanvas/gnome-canvas-rect-ellipse.h>
#include "gal-a11y-e-table.h"
#include <glib/gi18n.h>
#include "e-util/e-util.h"
#include "misc/e-canvas.h"
#include "misc/e-canvas-background.h"
#include "misc/e-canvas-vbox.h"
#include "e-util/e-unicode.h"
#include "e-table.h"
#include "e-table-click-to-add.h"
#include "e-table-column-specification.h"
#include "e-table-group-leaf.h"
#include "e-table-header-item.h"
#include "e-table-header-utils.h"
#include "e-table-subset.h"
#include "e-table-utils.h"
#define COLUMN_HEADER_HEIGHT 16
#define d(x)
#if d(!)0
#define e_table_item_leave_edit_(x) (e_table_item_leave_edit((x)), g_print ("%s: e_table_item_leave_edit\n", __FUNCTION__))
#else
#define e_table_item_leave_edit_(x) (e_table_item_leave_edit((x)))
#endif
enum {
CURSOR_CHANGE,
CURSOR_ACTIVATED,
SELECTION_CHANGE,
DOUBLE_CLICK,
RIGHT_CLICK,
CLICK,
KEY_PRESS,
START_DRAG,
STATE_CHANGE,
WHITE_SPACE_EVENT,
CUT_CLIPBOARD,
COPY_CLIPBOARD,
PASTE_CLIPBOARD,
SELECT_ALL,
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 {
PROP_0,
PROP_LENGTH_THRESHOLD,
PROP_MODEL,
PROP_UNIFORM_ROW_HEIGHT,
PROP_ALWAYS_SEARCH,
PROP_USE_CLICK_TO_ADD
};
enum {
ET_SCROLL_UP = 1 << 0,
ET_SCROLL_DOWN = 1 << 1,
ET_SCROLL_LEFT = 1 << 2,
ET_SCROLL_RIGHT = 1 << 3
};
static guint 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_grab_focus (GtkWidget *widget);
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 et_focus (GtkWidget *container, GtkDirectionType direction);
static void scroll_off (ETable *et);
static void scroll_on (ETable *et, guint scroll_direction);
G_DEFINE_TYPE (ETable, e_table, GTK_TYPE_TABLE)
static void
et_disconnect_model (ETable *et)
{
if (et->model == NULL)
return;
if (et->table_model_change_id != 0)
g_signal_handler_disconnect (G_OBJECT (et->model),
et->table_model_change_id);
if (et->table_row_change_id != 0)
g_signal_handler_disconnect (G_OBJECT (et->model),
et->table_row_change_id);
if (et->table_cell_change_id != 0)
g_signal_handler_disconnect (G_OBJECT (et->model),
et->table_cell_change_id);
if (et->table_rows_inserted_id != 0)
g_signal_handler_disconnect (G_OBJECT (et->model),
et->table_rows_inserted_id);
if (et->table_rows_deleted_id != 0)
g_signal_handler_disconnect (G_OBJECT (et->model),
et->table_rows_deleted_id);
et->table_model_change_id = 0;
et->table_row_change_id = 0;
et->table_cell_change_id = 0;
et->table_rows_inserted_id = 0;
et->table_rows_deleted_id = 0;
}
static void
e_table_state_change (ETable *et)
{
g_signal_emit (G_OBJECT (et), et_signals [STATE_CHANGE], 0);
}
#define CHECK_HORIZONTAL(et) if ((et)->horizontal_scrolling || (et)->horizontal_resize) e_table_header_update_horizontal (et->header);
static void
clear_current_search_col (ETable *et)
{
et->search_col_set = FALSE;
}
static ETableCol *
current_search_col (ETable *et)
{
if (!et->search_col_set) {
et->current_search_col =
e_table_util_calculate_current_search_col (et->header,
et->full_header,
et->sort_info,
et->always_search);
et->search_col_set = TRUE;
}
return et->current_search_col;
}
static void
et_size_request (GtkWidget *widget, GtkRequisition *request)
{
ETable *et = E_TABLE (widget);
if (GTK_WIDGET_CLASS (e_table_parent_class)->size_request)
GTK_WIDGET_CLASS (e_table_parent_class)->size_request (widget, request);
if (et->horizontal_resize)
request->width = MAX (request->width, et->header_width);
}
static void
set_header_width (ETable *et)
{
if (et->horizontal_resize) {
et->header_width = e_table_header_min_width (et->header);
gtk_widget_queue_resize (GTK_WIDGET (et));
}
}
static void
structure_changed (ETableHeader *header, ETable *et)
{
e_table_state_change (et);
set_header_width (et);
clear_current_search_col (et);
}
static void
expansion_changed (ETableHeader *header, ETable *et)
{
e_table_state_change (et);
set_header_width (et);
}
static void
dimension_changed (ETableHeader *header, gint total_width, ETable *et)
{
set_header_width (et);
}
static void
disconnect_header (ETable *e_table)
{
if (e_table->header == NULL)
return;
if (e_table->structure_change_id)
g_signal_handler_disconnect (G_OBJECT (e_table->header),
e_table->structure_change_id);
if (e_table->expansion_change_id)
g_signal_handler_disconnect (G_OBJECT (e_table->header),
e_table->expansion_change_id);
if (e_table->dimension_change_id)
g_signal_handler_disconnect (G_OBJECT (e_table->header),
e_table->dimension_change_id);
g_object_unref(e_table->header);
e_table->header = NULL;
}
static void
connect_header (ETable *e_table, ETableState *state)
{
if (e_table->header != NULL)
disconnect_header (e_table);
e_table->header = e_table_state_to_header (GTK_WIDGET(e_table), e_table->full_header, state);
e_table->structure_change_id =
g_signal_connect (G_OBJECT (e_table->header), "structure_change",
G_CALLBACK (structure_changed), e_table);
e_table->expansion_change_id =
g_signal_connect (G_OBJECT (e_table->header), "expansion_change",
G_CALLBACK (expansion_changed), e_table);
e_table->dimension_change_id =
g_signal_connect (G_OBJECT (e_table->header), "dimension_change",
G_CALLBACK (dimension_changed), e_table);
}
static void
et_dispose (GObject *object)
{
ETable *et = E_TABLE (object);
et_disconnect_model (et);
if (et->search) {
if (et->search_search_id)
g_signal_handler_disconnect (G_OBJECT (et->search),
et->search_search_id);
if (et->search_accept_id)
g_signal_handler_disconnect (G_OBJECT (et->search),
et->search_accept_id);
g_object_unref (et->search);
et->search = NULL;
}
if (et->group_info_change_id) {
g_signal_handler_disconnect (G_OBJECT (et->sort_info),
et->group_info_change_id);
et->group_info_change_id = 0;
}
if (et->sort_info_change_id) {
g_signal_handler_disconnect (G_OBJECT (et->sort_info),
et->sort_info_change_id);
et->sort_info_change_id = 0;
}
if (et->reflow_idle_id) {
g_source_remove(et->reflow_idle_id);
et->reflow_idle_id = 0;
}
scroll_off (et);
disconnect_header (et);
if (et->model) {
g_object_unref (et->model);
et->model = NULL;
}
if (et->full_header) {
g_object_unref (et->full_header);
et->full_header = NULL;
}
if (et->sort_info) {
g_object_unref (et->sort_info);
et->sort_info = NULL;
}
if (et->sorter) {
g_object_unref (et->sorter);
et->sorter = NULL;
}
if (et->selection) {
g_object_unref (et->selection);
et->selection = NULL;
}
if (et->spec) {
g_object_unref (et->spec);
et->spec = NULL;
}
if (et->header_canvas != NULL) {
gtk_widget_destroy (GTK_WIDGET (et->header_canvas));
et->header_canvas = NULL;
}
if (et->site != NULL) {
e_table_drag_source_unset (et);
et->site = NULL;
}
if (et->table_canvas != NULL) {
gtk_widget_destroy (GTK_WIDGET (et->table_canvas));
et->table_canvas = NULL;
}
if (et->rebuild_idle_id != 0) {
g_source_remove (et->rebuild_idle_id);
et->rebuild_idle_id = 0;
}
g_free(et->click_to_add_message);
et->click_to_add_message = NULL;
g_free(et->domain);
et->domain = NULL;
(*G_OBJECT_CLASS (e_table_parent_class)->dispose)(object);
}
static void
et_unrealize (GtkWidget *widget)
{
scroll_off (E_TABLE (widget));
if (GTK_WIDGET_CLASS (e_table_parent_class)->unrealize)
GTK_WIDGET_CLASS (e_table_parent_class)->unrealize (widget);
}
static gboolean
check_row (ETable *et, gint model_row, gint col, ETableSearchFunc search, gchar *string)
{
gconstpointer value;
value = e_table_model_value_at (et->model, col, model_row);
return search (value, string);
}
static gboolean
et_search_search (ETableSearch *search, gchar *string, ETableSearchFlags flags, ETable *et)
{
gint cursor;
gint rows;
gint i;
ETableCol *col = current_search_col (et);
if (col == NULL)
return FALSE;
rows = e_table_model_row_count (et->model);
g_object_get(et->selection,
"cursor_row", &cursor,
NULL);
if ((flags & E_TABLE_SEARCH_FLAGS_CHECK_CURSOR_FIRST) && cursor < rows && cursor >= 0 && check_row (et, cursor, col->col_idx, col->search, string))
return TRUE;
cursor = e_sorter_model_to_sorted (E_SORTER (et->sorter), cursor);
for (i = cursor + 1; i < rows; i++) {
gint model_row = e_sorter_sorted_to_model (E_SORTER (et->sorter), i);
if (check_row (et, model_row, col->col_idx, col->search, string)) {
e_selection_model_select_as_key_press(E_SELECTION_MODEL (et->selection), model_row, col->col_idx, GDK_CONTROL_MASK);
return TRUE;
}
}
for (i = 0; i < cursor; i++) {
gint model_row = e_sorter_sorted_to_model (E_SORTER (et->sorter), i);
if (check_row (et, model_row, col->col_idx, col->search, string)) {
e_selection_model_select_as_key_press(E_SELECTION_MODEL (et->selection), model_row, col->col_idx, GDK_CONTROL_MASK);
return TRUE;
}
}
cursor = e_sorter_sorted_to_model (E_SORTER (et->sorter), cursor);
/* Check if the cursor row is the only matching row. */
return (!(flags & E_TABLE_SEARCH_FLAGS_CHECK_CURSOR_FIRST) && cursor < rows && cursor >= 0 && check_row (et, cursor, col->col_idx, col->search, string));
}
static void
et_search_accept (ETableSearch *search, ETable *et)
{
gint cursor;
ETableCol *col = current_search_col (et);
if (col == NULL)
return;
g_object_get(et->selection,
"cursor_row", &cursor,
NULL);
e_selection_model_select_as_key_press(E_SELECTION_MODEL (et->selection), cursor, col->col_idx, 0);
}
static void
init_search (ETable *e_table)
{
if (e_table->search != NULL)
return;
e_table->search = e_table_search_new();
e_table->search_search_id =
g_signal_connect (G_OBJECT (e_table->search), "search",
G_CALLBACK (et_search_search), e_table);
e_table->search_accept_id =
g_signal_connect (G_OBJECT (e_table->search), "accept",
G_CALLBACK (et_search_accept), e_table);
}
static void
et_finalize (GObject *object)
{
ETable *et = E_TABLE (object);
g_free (et->click_to_add_message);
et->click_to_add_message = NULL;
g_free(et->domain);
et->domain = NULL;
G_OBJECT_CLASS (e_table_parent_class)->finalize (object);
}
static void
e_table_init (ETable *e_table)
{
GtkTable *gtk_table = GTK_TABLE (e_table);
GTK_WIDGET_SET_FLAGS (GTK_WIDGET (e_table), GTK_CAN_FOCUS);
gtk_table->homogeneous = FALSE;
e_table->sort_info = NULL;
e_table->group_info_change_id = 0;
e_table->sort_info_change_id = 0;
e_table->structure_change_id = 0;
e_table->expansion_change_id = 0;
e_table->dimension_change_id = 0;
e_table->reflow_idle_id = 0;
e_table->scroll_idle_id = 0;
e_table->alternating_row_colors = 1;
e_table->horizontal_draw_grid = 1;
e_table->vertical_draw_grid = 1;
e_table->draw_focus = 1;
e_table->cursor_mode = E_CURSOR_SIMPLE;
e_table->length_threshold = 200;
e_table->uniform_row_height = FALSE;
e_table->need_rebuild = 0;
e_table->rebuild_idle_id = 0;
e_table->horizontal_scrolling = FALSE;
e_table->horizontal_resize = FALSE;
e_table->click_to_add_message = NULL;
e_table->domain = NULL;
e_table->drop_row = -1;
e_table->drop_col = -1;
e_table->site = NULL;
e_table->do_drag = 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;
e_table->always_search = g_getenv ("GAL_ALWAYS_SEARCH") ? TRUE : FALSE;
e_table->search = NULL;
e_table->search_search_id = 0;
e_table->search_accept_id = 0;
e_table->current_search_col = NULL;
e_table->header_width = 0;
}
/* Grab_focus handler for the ETable */
static void
et_grab_focus (GtkWidget *widget)
{
ETable *e_table;
e_table = E_TABLE (widget);
gtk_widget_grab_focus (GTK_WIDGET (e_table->table_canvas));
}
/* Focus handler for the ETable */
static gint
et_focus (GtkWidget *container, GtkDirectionType direction)
{
ETable *e_table;
e_table = E_TABLE (container);
if (GTK_CONTAINER (container)->focus_child) {
gtk_container_set_focus_child (GTK_CONTAINER (container), NULL);
return FALSE;
}
return gtk_widget_child_focus (GTK_WIDGET (e_table->table_canvas), direction);
}
static void
set_header_canvas_width (ETable *e_table)
{
double oldwidth, oldheight, width;
if (!(e_table->header_item && e_table->header_canvas && e_table->table_canvas))
return;
gnome_canvas_get_scroll_region (GNOME_CANVAS (e_table->table_canvas),
NULL, NULL, &width, NULL);
gnome_canvas_get_scroll_region (GNOME_CANVAS (e_table->header_canvas),
NULL, NULL, &oldwidth, &oldheight);
if (oldwidth != width ||
oldheight != E_TABLE_HEADER_ITEM (e_table->header_item)->height - 1)
gnome_canvas_set_scroll_region (
GNOME_CANVAS (e_table->header_canvas),
0, 0, width, /* COLUMN_HEADER_HEIGHT - 1 */
E_TABLE_HEADER_ITEM (e_table->header_item)->height - 1);
}
static void
header_canvas_size_allocate (GtkWidget *widget, GtkAllocation *alloc, ETable *e_table)
{
set_header_canvas_width (e_table);
/* 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)
g_object_set (
e_table->header_canvas, "height-request",
E_TABLE_HEADER_ITEM (e_table->header_item)->height,
NULL);
}
static void
group_info_changed (ETableSortInfo *info, ETable *et)
{
gboolean will_be_grouped = e_table_sort_info_grouping_get_count(info) > 0;
clear_current_search_col (et);
if (et->is_grouped || will_be_grouped) {
et->need_rebuild = TRUE;
if (!et->rebuild_idle_id) {
gtk_object_destroy (GTK_OBJECT (et->group));
et->group = NULL;
et->rebuild_idle_id = g_idle_add_full (20, changed_idle, et, NULL);
}
}
e_table_state_change (et);
}
static void
sort_info_changed (ETableSortInfo *info, ETable *et)
{
clear_current_search_col (et);
e_table_state_change (et);
}
static void
e_table_setup_header (ETable *e_table)
{
gchar *pointer;
e_table->header_canvas = GNOME_CANVAS (e_canvas_new ());
gtk_widget_show (GTK_WIDGET (e_table->header_canvas));
pointer = g_strdup_printf("%p", (gpointer) e_table);
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", pointer,
"table", e_table,
NULL);
g_free(pointer);
g_signal_connect (
G_OBJECT (e_table->header_canvas), "size_allocate",
G_CALLBACK (header_canvas_size_allocate), e_table);
g_object_set (
e_table->header_canvas, "height-request",
E_TABLE_HEADER_ITEM (e_table->header_item)->height, NULL);
}
static gboolean
table_canvas_reflow_idle (ETable *e_table)
{
gdouble height, width;
gdouble oldheight, oldwidth;
GtkAllocation *alloc = &(GTK_WIDGET (e_table->table_canvas)->allocation);
g_object_get (e_table->canvas_vbox,
"height", &height,
"width", &width,
NULL);
height = MAX ((gint)height, alloc->height);
width = MAX((gint)width, alloc->width);
/* I have no idea why this needs to be -1, but it works. */
gnome_canvas_get_scroll_region (GNOME_CANVAS (e_table->table_canvas),
NULL, NULL, &oldwidth, &oldheight);
if (oldwidth != width - 1 ||
oldheight != height - 1) {
gnome_canvas_set_scroll_region (GNOME_CANVAS (e_table->table_canvas),
0, 0, width - 1, height - 1);
set_header_canvas_width (e_table);
}
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;
GValue *val = g_new0 (GValue, 1);
g_value_init (val, G_TYPE_DOUBLE);
width = alloc->width;
g_value_set_double (val, width);
g_object_get (e_table->canvas_vbox,
"height", &height,
NULL);
height = MAX ((gint)height, alloc->height);
g_object_set (e_table->canvas_vbox,
"width", width,
NULL);
g_object_set_property (G_OBJECT (e_table->header), "width", val);
g_free (val);
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, gint row, gint col, ETable *et)
{
if (et->cursor_loc == E_TABLE_CURSOR_LOC_TABLE) {
e_selection_model_clear(E_SELECTION_MODEL (et->selection));
}
et->cursor_loc = E_TABLE_CURSOR_LOC_ETCTA;
}
static void
group_cursor_change (ETableGroup *etg, gint row, ETable *et)
{
ETableCursorLoc old_cursor_loc;
old_cursor_loc = et->cursor_loc;
et->cursor_loc = E_TABLE_CURSOR_LOC_TABLE;
g_signal_emit (G_OBJECT (et), et_signals [CURSOR_CHANGE], 0, row);
if (old_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));
}
static void
group_cursor_activated (ETableGroup *etg, gint row, ETable *et)
{
g_signal_emit (G_OBJECT (et), et_signals [CURSOR_ACTIVATED], 0, row);
}
static void
group_double_click (ETableGroup *etg, gint row, gint col, GdkEvent *event, ETable *et)
{
g_signal_emit (G_OBJECT (et), et_signals [DOUBLE_CLICK], 0, row, col, event);
}
static gint
group_right_click (ETableGroup *etg, gint row, gint col, GdkEvent *event, ETable *et)
{
gint return_val = 0;
g_signal_emit (G_OBJECT (et), et_signals [RIGHT_CLICK], 0, row, col, event, &return_val);
return return_val;
}
static gint
group_click (ETableGroup *etg, gint row, gint col, GdkEvent *event, ETable *et)
{
gint return_val = 0;
g_signal_emit (G_OBJECT (et), et_signals [CLICK], 0, row, col, event, &return_val);
return return_val;
}
static gint
group_key_press (ETableGroup *etg, gint row, gint col, GdkEvent *event, ETable *et)
{
gint return_val = 0;
GdkEventKey *key = (GdkEventKey *) event;
gint y, row_local, col_local;
GtkAdjustment *vadj;
switch (key->keyval) {
case GDK_Page_Down:
case GDK_KP_Page_Down:
vadj = gtk_layout_get_vadjustment (GTK_LAYOUT (et->table_canvas));
y = CLAMP(vadj->value + (2 * vadj->page_size - 50), 0, vadj->upper);
y -= vadj->value;
e_table_get_cell_at (et, 30, y, &row_local, &col_local);
if (row_local == -1)
row_local = e_table_model_row_count (et->model) - 1;
row_local = e_table_view_to_model_row (et, row_local);
col_local = e_selection_model_cursor_col (E_SELECTION_MODEL (et->selection));
e_selection_model_select_as_key_press (E_SELECTION_MODEL (et->selection), row_local, col_local, key->state);
return_val = 1;
break;
case GDK_Page_Up:
case GDK_KP_Page_Up:
vadj = gtk_layout_get_vadjustment (GTK_LAYOUT (et->table_canvas));
y = CLAMP(vadj->value - (vadj->page_size - 50), 0, vadj->upper);
y -= vadj->value;
e_table_get_cell_at (et, 30, y, &row_local, &col_local);
if (row_local == -1)
row_local = 0;
row_local = e_table_view_to_model_row (et, row_local);
col_local = e_selection_model_cursor_col (E_SELECTION_MODEL (et->selection));
e_selection_model_select_as_key_press (E_SELECTION_MODEL (et->selection), row_local, col_local, key->state);
return_val = 1;
break;
case GDK_BackSpace:
init_search (et);
if (e_table_search_backspace (et->search))
return TRUE;
/* Fall through */
default:
init_search (et);
if ((key->state & ~(GDK_SHIFT_MASK | GDK_LOCK_MASK | GDK_MOD1_MASK | GDK_MOD2_MASK | GDK_MOD3_MASK | GDK_MOD4_MASK | GDK_MOD5_MASK)) == 0
&& ((key->keyval >= GDK_a && key->keyval <= GDK_z) ||
(key->keyval >= GDK_A && key->keyval <= GDK_Z) ||
(key->keyval >= GDK_0 && key->keyval <= GDK_9)))
e_table_search_input_character (et->search, key->keyval);
g_signal_emit (G_OBJECT (et), et_signals [KEY_PRESS], 0,
row, col, event, &return_val);
break;
}
return return_val;
}
static gint
group_start_drag (ETableGroup *etg, gint row, gint col, GdkEvent *event, ETable *et)
{
gint return_val = 0;
g_signal_emit (G_OBJECT (et), et_signals [START_DRAG], 0,
row, col, event, &return_val);
return return_val;
}
static void
et_table_model_changed (ETableModel *model, ETable *et)
{
et->need_rebuild = TRUE;
if (!et->rebuild_idle_id) {
gtk_object_destroy (GTK_OBJECT (et->group));
et->group = NULL;
et->rebuild_idle_id = g_idle_add_full (20, changed_idle, et, NULL);
}
}
static void
et_table_row_changed (ETableModel *table_model, gint row, ETable *et)
{
if (!et->need_rebuild) {
if (e_table_group_remove (et->group, row))
e_table_group_add (et->group, row);
CHECK_HORIZONTAL(et);
}
}
static void
et_table_cell_changed (ETableModel *table_model, gint view_col, gint row, ETable *et)
{
et_table_row_changed (table_model, row, et);
}
static void
et_table_rows_inserted (ETableModel *table_model, gint row, gint count, ETable *et)
{
/* This number has already been decremented. */
gint row_count = e_table_model_row_count(table_model);
if (!et->need_rebuild) {
gint i;
if (row != row_count - count)
e_table_group_increment(et->group, row, count);
for (i = 0; i < count; i++)
e_table_group_add (et->group, row + i);
CHECK_HORIZONTAL(et);
}
}
static void
et_table_rows_deleted (ETableModel *table_model, gint row, gint count, ETable *et)
{
gint row_count = e_table_model_row_count(table_model);
if (!et->need_rebuild) {
gint i;
for (i = 0; i < count; i++)
e_table_group_remove (et->group, row + i);
if (row != row_count)
e_table_group_decrement(et->group, row, count);
CHECK_HORIZONTAL(et);
}
}
static void
et_build_groups (ETable *et)
{
gboolean was_grouped = et->is_grouped;
et->is_grouped = e_table_sort_info_grouping_get_count(et->sort_info) > 0;
et->group = e_table_group_new (GNOME_CANVAS_GROUP (et->canvas_vbox),
et->full_header,
et->header,
et->model,
et->sort_info,
0);
if (et->use_click_to_add_end)
e_canvas_vbox_add_item_start(E_CANVAS_VBOX(et->canvas_vbox), GNOME_CANVAS_ITEM(et->group));
else
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),
"alternating_row_colors", et->alternating_row_colors,
"horizontal_draw_grid", et->horizontal_draw_grid,
"vertical_draw_grid", et->vertical_draw_grid,
"drawfocus", et->draw_focus,
"cursor_mode", et->cursor_mode,
"length_threshold", et->length_threshold,
"uniform_row_height", et->uniform_row_height,
"selection_model", et->selection,
NULL);
g_signal_connect (G_OBJECT (et->group), "cursor_change",
G_CALLBACK (group_cursor_change), et);
g_signal_connect (G_OBJECT (et->group), "cursor_activated",
G_CALLBACK (group_cursor_activated), et);
g_signal_connect (G_OBJECT (et->group), "double_click",
G_CALLBACK (group_double_click), et);
g_signal_connect (G_OBJECT (et->group), "right_click",
G_CALLBACK (group_right_click), et);
g_signal_connect (G_OBJECT (et->group), "click",
G_CALLBACK (group_click), et);
g_signal_connect (G_OBJECT (et->group), "key_press",
G_CALLBACK (group_key_press), et);
g_signal_connect (G_OBJECT (et->group), "start_drag",
G_CALLBACK (group_start_drag), et);
if (!(et->is_grouped) && was_grouped)
et_disconnect_model (et);
if (et->is_grouped && (!was_grouped)) {
et->table_model_change_id = g_signal_connect (G_OBJECT (et->model), "model_changed",
G_CALLBACK (et_table_model_changed), et);
et->table_row_change_id = g_signal_connect (G_OBJECT (et->model), "model_row_changed",
G_CALLBACK (et_table_row_changed), et);
et->table_cell_change_id = g_signal_connect (G_OBJECT (et->model), "model_cell_changed",
G_CALLBACK (et_table_cell_changed), et);
et->table_rows_inserted_id = g_signal_connect (G_OBJECT (et->model), "model_rows_inserted",
G_CALLBACK (et_table_rows_inserted), et);
et->table_rows_deleted_id = g_signal_connect (G_OBJECT (et->model), "model_rows_deleted",
G_CALLBACK (et_table_rows_deleted), et);
}
if (et->is_grouped)
e_table_fill_table (et, et->model);
}
static gboolean
changed_idle (gpointer data)
{
ETable *et = E_TABLE (data);
if (et->need_rebuild) {
if (et->group)
gtk_object_destroy (GTK_OBJECT (et->group));
et_build_groups(et);
g_object_set (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;
CHECK_HORIZONTAL(et);
return FALSE;
}
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);
CHECK_HORIZONTAL(e_table);
set_header_width (e_table);
}
static gint
white_item_event (GnomeCanvasItem *white_item, GdkEvent *event, ETable *e_table)
{
gint return_val = 0;
g_signal_emit (GTK_OBJECT (e_table), et_signals [WHITE_SPACE_EVENT], 0,
event, &return_val);
return return_val;
}
static void
et_eti_leave_edit (ETable *et)
{
GnomeCanvas *canvas = et->table_canvas;
if (GTK_WIDGET_HAS_FOCUS(canvas)) {
GnomeCanvasItem *item = GNOME_CANVAS(canvas)->focused_item;
if (E_IS_TABLE_ITEM(item)) {
e_table_item_leave_edit_(E_TABLE_ITEM(item));
}
}
}
static gint
et_canvas_root_event (GnomeCanvasItem *root, GdkEvent *event, ETable *e_table)
{
switch (event->type) {
case GDK_BUTTON_PRESS:
case GDK_2BUTTON_PRESS:
case GDK_BUTTON_RELEASE:
if (event->button.button != 4 && event->button.button != 5) {
et_eti_leave_edit (e_table);
return TRUE;
}
break;
default:
break;
}
return FALSE;
}
/* Finds the first descendant of the group that is an ETableItem and focuses it */
static void
focus_first_etable_item (ETableGroup *group)
{
GnomeCanvasGroup *cgroup;
GList *l;
cgroup = GNOME_CANVAS_GROUP (group);
for (l = cgroup->item_list; l; l = l->next) {
GnomeCanvasItem *i;
i = GNOME_CANVAS_ITEM (l->data);
if (E_IS_TABLE_GROUP (i))
focus_first_etable_item (E_TABLE_GROUP (i));
else if (E_IS_TABLE_ITEM (i)) {
e_table_item_set_cursor (E_TABLE_ITEM (i), 0, 0);
gnome_canvas_item_grab_focus (i);
}
}
}
/* Handler for focus events in the table_canvas; we have to repaint ourselves
* always, and also give the focus to some ETableItem if we get focused.
*/
static gint
table_canvas_focus_event_cb (GtkWidget *widget, GdkEventFocus *event, gpointer data)
{
GnomeCanvas *canvas;
ECanvas *ecanvas;
ETable *etable;
gtk_widget_queue_draw (widget);
canvas = GNOME_CANVAS (widget);
ecanvas = E_CANVAS (widget);
if (!event->in) {
gtk_im_context_focus_out(ecanvas->im_context);
return FALSE;
} else {
gtk_im_context_focus_in(ecanvas->im_context);
}
etable = E_TABLE (data);
if (e_table_model_row_count(etable->model) < 1
&& (etable->click_to_add)
&& !(E_TABLE_CLICK_TO_ADD(etable->click_to_add)->row)) {
gnome_canvas_item_grab_focus (etable->canvas_vbox);
gnome_canvas_item_grab_focus (etable->click_to_add);
} else if (!canvas->focused_item && etable->group) {
focus_first_etable_item (etable->group);
} else if (canvas->focused_item) {
ESelectionModel *selection = (ESelectionModel *)etable->selection;
/* check whether click_to_add already got the focus */
if (etable->click_to_add) {
GnomeCanvasItem *row = E_TABLE_CLICK_TO_ADD(etable->click_to_add)->row;
if (canvas->focused_item == row)
return TRUE;
}
if (e_selection_model_cursor_row (selection) == -1)
focus_first_etable_item (etable->group);
}
return FALSE;
}
static gboolean
canvas_vbox_event (ECanvasVbox *vbox, GdkEventKey *key, ETable *etable)
{
if (key->type != GDK_KEY_PRESS &&
key->type != GDK_KEY_RELEASE) {
return FALSE;
}
switch (key->keyval) {
case GDK_Tab:
case GDK_KP_Tab:
case GDK_ISO_Left_Tab:
if ((key->state & GDK_CONTROL_MASK) && etable->click_to_add) {
gnome_canvas_item_grab_focus (etable->click_to_add);
break;
}
default:
return FALSE;
}
return TRUE;
}
static gboolean
click_to_add_event (ETableClickToAdd *etcta, GdkEventKey *key, ETable *etable)
{
if (key->type != GDK_KEY_PRESS &&
key->type != GDK_KEY_RELEASE) {
return FALSE;
}
switch (key->keyval) {
case GDK_Tab:
case GDK_KP_Tab:
case GDK_ISO_Left_Tab:
if (key->state & GDK_CONTROL_MASK) {
if (etable->group) {
if (e_table_model_row_count(etable->model) > 0)
focus_first_etable_item (etable->group);
else
gtk_widget_child_focus (
gtk_widget_get_toplevel(GTK_WIDGET(etable->table_canvas)), GTK_DIR_TAB_FORWARD);
break;
}
}
default:
return FALSE;
}
return FALSE;
}
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 ());
g_signal_connect (
G_OBJECT (e_table->table_canvas), "size_allocate",
G_CALLBACK (table_canvas_size_allocate), e_table);
g_signal_connect (
G_OBJECT (e_table->table_canvas), "focus_in_event",
G_CALLBACK (table_canvas_focus_event_cb), e_table);
g_signal_connect (
G_OBJECT (e_table->table_canvas), "focus_out_event",
G_CALLBACK (table_canvas_focus_event_cb), e_table);
g_signal_connect (
G_OBJECT (e_table), "drag_begin",
G_CALLBACK (et_drag_begin), e_table);
g_signal_connect (
G_OBJECT (e_table), "drag_end",
G_CALLBACK (et_drag_end), e_table);
g_signal_connect (
G_OBJECT (e_table), "drag_data_get",
G_CALLBACK (et_drag_data_get), e_table);
g_signal_connect (
G_OBJECT (e_table), "drag_data_delete",
G_CALLBACK (et_drag_data_delete), e_table);
g_signal_connect (
G_OBJECT (e_table), "drag_motion",
G_CALLBACK (et_drag_motion), e_table);
g_signal_connect (
G_OBJECT (e_table), "drag_leave",
G_CALLBACK (et_drag_leave), e_table);
g_signal_connect (
G_OBJECT (e_table), "drag_drop",
G_CALLBACK (et_drag_drop), e_table);
g_signal_connect (
G_OBJECT (e_table), "drag_data_received",
G_CALLBACK (et_drag_data_received), e_table);
g_signal_connect (G_OBJECT(e_table->table_canvas), "reflow",
G_CALLBACK (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),
e_canvas_background_get_type(),
"fill_color_gdk", &GTK_WIDGET(e_table->table_canvas)->style->base[GTK_STATE_NORMAL],
NULL);
g_signal_connect (G_OBJECT (e_table->white_item), "event",
G_CALLBACK (white_item_event), e_table);
g_signal_connect (G_OBJECT(e_table->table_canvas), "realize",
G_CALLBACK(et_canvas_realize), e_table);
g_signal_connect (G_OBJECT(gnome_canvas_root (e_table->table_canvas)), "event",
G_CALLBACK(et_canvas_root_event), 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);
g_signal_connect (
G_OBJECT (e_table->canvas_vbox), "event",
G_CALLBACK (canvas_vbox_event), e_table);
et_build_groups(e_table);
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);
if (e_table->use_click_to_add_end)
e_canvas_vbox_add_item (
E_CANVAS_VBOX(e_table->canvas_vbox),
e_table->click_to_add);
else
e_canvas_vbox_add_item_start (
E_CANVAS_VBOX(e_table->canvas_vbox),
e_table->click_to_add);
g_signal_connect (
G_OBJECT (e_table->click_to_add), "event",
G_CALLBACK (click_to_add_event), e_table);
g_signal_connect (
G_OBJECT (e_table->click_to_add), "cursor_change",
G_CALLBACK (click_to_add_cursor_change), e_table);
}
}
static void
e_table_fill_table (ETable *e_table, ETableModel *model)
{
e_table_group_add_all (e_table->group);
}
/**
* e_table_set_state_object:
* @e_table: The #ETable object to modify
* @state: The #ETableState to use
*
* This routine sets the state of the #ETable from the given
* #ETableState.
*
**/
void
e_table_set_state_object(ETable *e_table, ETableState *state)
{
GValue *val = g_new0 (GValue, 1);
g_value_init (val, G_TYPE_DOUBLE);
connect_header (e_table, state);
g_value_set_double (val, (double) (GTK_WIDGET(e_table->table_canvas)->allocation.width));
g_object_set_property (G_OBJECT (e_table->header), "width", val);
g_free (val);
if (e_table->sort_info) {
if (e_table->group_info_change_id)
g_signal_handler_disconnect (G_OBJECT (e_table->sort_info),
e_table->group_info_change_id);
if (e_table->sort_info_change_id)
g_signal_handler_disconnect (G_OBJECT (e_table->sort_info),
e_table->sort_info_change_id);
g_object_unref(e_table->sort_info);
}
if (state->sort_info) {
e_table->sort_info = e_table_sort_info_duplicate(state->sort_info);
e_table_sort_info_set_can_group (e_table->sort_info, e_table->allow_grouping);
e_table->group_info_change_id =
g_signal_connect (G_OBJECT (e_table->sort_info), "group_info_changed",
G_CALLBACK (group_info_changed), e_table);
e_table->sort_info_change_id =
g_signal_connect (G_OBJECT (e_table->sort_info), "sort_info_changed",
G_CALLBACK (sort_info_changed), e_table);
}
else
e_table->sort_info = NULL;
if (e_table->sorter)
g_object_set(e_table->sorter,
"sort_info", e_table->sort_info,
NULL);
if (e_table->header_item)
g_object_set(e_table->header_item,
"ETableHeader", e_table->header,
"sort_info", e_table->sort_info,
NULL);
if (e_table->click_to_add)
g_object_set(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);
e_table_state_change (e_table);
}
/**
* e_table_set_state:
* @e_table: The #ETable object to modify
* @state_str: a string representing an #ETableState
*
* This routine sets the state of the #ETable from a string.
*
**/
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);
g_object_unref(state);
}
/**
* e_table_load_state:
* @e_table: The #ETable object to modify
* @filename: name of the file to use
*
* This routine sets the state of the #ETable from a file.
*
**/
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);
g_object_unref(state);
}
/**
* e_table_get_state_object:
* @e_table: #ETable object to act on
*
* Builds an #ETableState corresponding to the current state of the
* #ETable.
*
* Return value:
* The %ETableState object generated.
**/
ETableState *
e_table_get_state_object (ETable *e_table)
{
ETableState *state;
gint full_col_count;
gint i, j;
state = e_table_state_new();
if (state->sort_info)
g_object_unref (state->sort_info);
state->sort_info = e_table->sort_info;
g_object_ref(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);
state->expansions = g_new(double, state->col_count);
for (i = 0; i < state->col_count; i++) {
ETableCol *col = e_table_header_get_column(e_table->header, i);
state->columns[i] = -1;
for (j = 0; j < full_col_count; j++) {
if (col->col_idx == e_table_header_index(e_table->full_header, j)) {
state->columns[i] = j;
break;
}
}
state->expansions[i] = col->expansion;
}
return state;
}
/**
* e_table_get_state:
* @e_table: The #ETable to act on.
*
* Builds a state object based on the current state and returns the
* string corresponding to that state.
*
* Return value:
* A string describing the current state of the #ETable.
**/
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);
g_object_unref(state);
return string;
}
/**
* e_table_save_state:
* @e_table: The #ETable to act on
* @filename: name of the file to save to
*
* Saves the state of the @e_table object into the file pointed by
* @filename.
*
**/
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);
g_object_unref(state);
}
static void
et_selection_model_selection_changed (ETableGroup *etg, ETable *et)
{
g_signal_emit (G_OBJECT (et), et_signals [SELECTION_CHANGE], 0);
}
static void
et_selection_model_selection_row_changed (ETableGroup *etg, gint row, ETable *et)
{
g_signal_emit (G_OBJECT (et), et_signals [SELECTION_CHANGE], 0);
}
static ETable *
et_real_construct (ETable *e_table, ETableModel *etm, ETableExtras *ete,
ETableSpecification *specification, ETableState *state)
{
gint row = 0;
gint col_count, i;
GValue *val = g_new0 (GValue, 1);
g_value_init (val, G_TYPE_OBJECT);
if (ete)
g_object_ref(ete);
else {
ete = e_table_extras_new();
}
e_table->domain = g_strdup (specification->domain);
e_table->use_click_to_add = specification->click_to_add;
e_table->use_click_to_add_end = specification->click_to_add_end;
e_table->click_to_add_message = specification->click_to_add_message ? g_strdup (dgettext (e_table->domain, specification->click_to_add_message)) : NULL;
e_table->alternating_row_colors = specification->alternating_row_colors;
e_table->horizontal_draw_grid = specification->horizontal_draw_grid;
e_table->vertical_draw_grid = specification->vertical_draw_grid;
e_table->draw_focus = specification->draw_focus;
e_table->cursor_mode = specification->cursor_mode;
e_table->full_header = e_table_spec_to_full_header(specification, ete);
g_object_ref (e_table->full_header);
col_count = e_table_header_count (e_table->full_header);
for (i = 0; i < col_count; i++) {
ETableCol *col = e_table_header_get_column(e_table->full_header, i);
if (col && col->search) {
e_table->current_search_col = col;
e_table->search_col_set = TRUE;
break;
}
}
e_table->model = etm;
g_object_ref (etm);
gtk_widget_push_colormap (gdk_rgb_get_colormap ());
connect_header (e_table, state);
e_table->horizontal_scrolling = specification->horizontal_scrolling;
e_table->horizontal_resize = specification->horizontal_resize;
e_table->allow_grouping = specification->allow_grouping;
e_table->sort_info = state->sort_info;
g_object_ref (state->sort_info);
e_table_sort_info_set_can_group (e_table->sort_info, e_table->allow_grouping);
e_table->group_info_change_id =
g_signal_connect (G_OBJECT (e_table->sort_info), "group_info_changed",
G_CALLBACK (group_info_changed), e_table);
e_table->sort_info_change_id =
g_signal_connect (G_OBJECT (e_table->sort_info), "sort_info_changed",
G_CALLBACK (sort_info_changed), e_table);
g_value_set_object (val, e_table->sort_info);
g_object_set_property (G_OBJECT(e_table->header), "sort_info", val);
g_free (val);
e_table->sorter = e_table_sorter_new(etm, e_table->full_header, e_table->sort_info);
g_object_set (e_table->selection,
"model", etm,
"selection_mode", specification->selection_mode,
"cursor_mode", specification->cursor_mode,
"sorter", e_table->sorter,
"header", e_table->header,
NULL);
g_signal_connect(e_table->selection, "selection_changed",
G_CALLBACK (et_selection_model_selection_changed), e_table);
g_signal_connect(e_table->selection, "selection_row_changed",
G_CALLBACK (et_selection_model_selection_row_changed), 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)));
gtk_layout_get_hadjustment (GTK_LAYOUT (e_table->table_canvas))->step_increment = 20;
gtk_adjustment_changed(gtk_layout_get_hadjustment (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 ();
g_object_unref(ete);
return e_table;
}
/**
* e_table_construct:
* @e_table: The newly created #ETable object.
* @etm: The model for this table.
* @ete: An optional #ETableExtras. (%NULL is valid.)
* @spec_str: The spec.
* @state_str: An optional state. (%NULL is valid.)
*
* This is the internal implementation of e_table_new() for use by
* subclasses or language bindings. See e_table_new() for details.
*
* Return value:
* The passed in value @e_table or %NULL if there's an error.
**/
ETable *
e_table_construct (ETable *e_table, ETableModel *etm, ETableExtras *ete,
const gchar *spec_str, const gchar *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);
g_object_ref (etm);
specification = e_table_specification_new();
g_object_ref (specification);
if (!e_table_specification_load_from_string(specification, spec_str)) {
g_object_unref(specification);
return NULL;
}
if (state_str) {
state = e_table_state_new();
g_object_ref (state);
e_table_state_load_from_string(state, state_str);
if (state->col_count <= 0) {
g_object_unref(state);
state = specification->state;
g_object_ref(state);
}
} else {
state = specification->state;
g_object_ref(state);
}
e_table = et_real_construct (e_table, etm, ete, specification, state);
e_table->spec = specification;
g_object_unref(state);
return e_table;
}
/**
* e_table_construct_from_spec_file:
* @e_table: The newly created #ETable object.
* @etm: The model for this table.
* @ete: An optional #ETableExtras. (%NULL is valid.)
* @spec_fn: The filename of the spec.
* @state_fn: An optional state file. (%NULL is valid.)
*
* This is the internal implementation of e_table_new_from_spec_file()
* for use by subclasses or language bindings. See
* e_table_new_from_spec_file() for details.
*
* Return value:
* The passed in value @e_table or %NULL if there's an error.
**/
ETable *
e_table_construct_from_spec_file (ETable *e_table, ETableModel *etm, ETableExtras *ete,
const gchar *spec_fn, const gchar *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)) {
g_object_unref(specification);
return NULL;
}
if (state_fn) {
state = e_table_state_new();
if (!e_table_state_load_from_file(state, state_fn)) {
g_object_unref(state);
state = specification->state;
g_object_ref(state);
}
if (state->col_count <= 0) {
g_object_unref(state);
state = specification->state;
g_object_ref(state);
}
} else {
state = specification->state;
g_object_ref(state);
}
e_table = et_real_construct (e_table, etm, ete, specification, state);
e_table->spec = specification;
g_object_unref(state);
return e_table;
}
/**
* e_table_new:
* @etm: The model for this table.
* @ete: An optional #ETableExtras. (%NULL is valid.)
* @spec: The spec.
* @state: An optional state. (%NULL is valid.)
*
* This function creates an #ETable from the given parameters. The
* #ETableModel is a table model to be represented. The #ETableExtras
* is an optional set of pixbufs, cells, and sorting functions to be
* used when interpreting the spec. If you pass in %NULL it uses the
* default #ETableExtras. (See e_table_extras_new()).
*
* @spec is the specification of the set of viewable columns and the
* default sorting state and such. @state is an optional string
* specifying the current sorting state and such. If @state is NULL,
* then the default state from the spec will be used.
*
* Return value:
* The newly created #ETable or %NULL if there's an error.
**/
GtkWidget *
e_table_new (ETableModel *etm, ETableExtras *ete, const gchar *spec, const gchar *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 = g_object_new (E_TABLE_TYPE, NULL);
e_table = e_table_construct (e_table, etm, ete, spec, state);
return GTK_WIDGET (e_table);
}
/**
* e_table_new_from_spec_file:
* @etm: The model for this table.
* @ete: An optional #ETableExtras. (%NULL is valid.)
* @spec_fn: The filename of the spec.
* @state_fn: An optional state file. (%NULL is valid.)
*
* This is very similar to e_table_new(), except instead of passing in
* strings you pass in the file names of the spec and state to load.
*
* @spec_fn is the filename of the spec to load. If this file doesn't
* exist, e_table_new_from_spec_file will return %NULL.
*
* @state_fn is the filename of the initial state to load. If this is
* %NULL or if the specified file doesn't exist, the default state
* from the spec file is used.
*
* Return value:
* The newly created #ETable or %NULL if there's an error.
**/
GtkWidget *
e_table_new_from_spec_file (ETableModel *etm, ETableExtras *ete, const gchar *spec_fn, const gchar *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 = g_object_new (E_TABLE_TYPE, NULL);
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;
gint i;
const gint sort_count = e_table_sort_info_sorting_get_count (e_table->sort_info);
const gint 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;
}
gint
e_table_set_specification (ETable *e_table, const gchar *spec)
{
xmlDoc *xmlSpec;
gint 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 ((gchar *)spec, strlen(spec));
ret = et_real_set_specification(e_table, xmlSpec);
xmlFreeDoc (xmlSpec);
return ret;
}
void
e_table_save_specification (ETable *e_table, const 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);
e_xml_save_file (filename, doc);
xmlFreeDoc (doc);
}
gint
e_table_load_specification (ETable *e_table, gchar *filename)
{
xmlDoc *xmlSpec;
gint 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 */
#ifdef G_OS_WIN32
{
gchar *locale_filename = g_win32_locale_filename_from_utf8 (filename);
xmlSpec = xmlParseFile (locale_filename);
g_free (locale_filename);
}
#else
xmlSpec = xmlParseFile (filename);
#endif
ret = et_real_set_specification(e_table, xmlSpec);
xmlFreeDoc (xmlSpec);
return ret;
}
#endif
/**
* e_table_set_cursor_row:
* @e_table: The #ETable to set the cursor row of
* @row: The row number
*
* Sets the cursor row and the selection to the given row number.
**/
void
e_table_set_cursor_row (ETable *e_table, gint row)
{
g_return_if_fail(e_table != NULL);
g_return_if_fail(E_IS_TABLE(e_table));
g_return_if_fail(row >= 0);
g_object_set(e_table->selection,
"cursor_row", row,
NULL);
}
/**
* e_table_get_cursor_row:
* @e_table: The #ETable to query
*
* Calculates the cursor row. -1 means that we don't have a cursor.
*
* Return value:
* Cursor row
**/
gint
e_table_get_cursor_row (ETable *e_table)
{
gint row;
g_return_val_if_fail(e_table != NULL, -1);
g_return_val_if_fail(E_IS_TABLE(e_table), -1);
g_object_get(e_table->selection,
"cursor_row", &row,
NULL);
return row;
}
/**
* e_table_selected_row_foreach:
* @e_table: The #ETable to act on
* @callback: The callback function to call
* @closure: The value passed to the callback's closure argument
*
* Calls the given @callback function once for every selected row.
*
* If you change the selection or delete or add rows to the table
* during these callbacks, problems can occur. A standard thing to do
* is to create a list of rows or objects the function is called upon
* and then act upon that list. (In inverse order if it's rows.)
**/
void
e_table_selected_row_foreach (ETable *e_table,
EForeachFunc callback,
gpointer closure)
{
g_return_if_fail(e_table != NULL);
g_return_if_fail(E_IS_TABLE(e_table));
e_selection_model_foreach(E_SELECTION_MODEL (e_table->selection),
callback,
closure);
}
/**
* e_table_selected_count:
* @e_table: The #ETable to query
*
* Counts the number of selected rows.
*
* Return value:
* The number of rows selected.
**/
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_selection_model_selected_count(E_SELECTION_MODEL (e_table->selection));
}
/**
* e_table_select_all:
* @table: The #ETable to modify
*
* Selects all the rows in @table.
**/
void
e_table_select_all (ETable *table)
{
g_return_if_fail (table != NULL);
g_return_if_fail (E_IS_TABLE (table));
e_selection_model_select_all (E_SELECTION_MODEL (table->selection));
}
/**
* e_table_invert_selection:
* @table: The #ETable to modify
*
* Inverts the selection in @table.
**/
void
e_table_invert_selection (ETable *table)
{
g_return_if_fail (table != NULL);
g_return_if_fail (E_IS_TABLE (table));
e_selection_model_invert_selection (E_SELECTION_MODEL (table->selection));
}
/**
* e_table_get_printable:
* @e_table: #ETable to query
*
* Used for printing your #ETable.
*
* Return value:
* The #EPrintable to print.
**/
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);
}
/**
* e_table_right_click_up:
* @table: The #ETable to modify.
*
* Call this function when you're done handling the right click if you
* return TRUE from the "right_click" signal.
**/
void
e_table_right_click_up (ETable *table)
{
e_selection_model_right_click_up(E_SELECTION_MODEL(table->selection));
}
/**
* e_table_commit_click_to_add:
* @table: The #ETable to modify
*
* Commits the current values in the click to add to the table.
**/
void
e_table_commit_click_to_add (ETable *table)
{
et_eti_leave_edit (table);
if (table->click_to_add)
e_table_click_to_add_commit(E_TABLE_CLICK_TO_ADD(table->click_to_add));
}
static void
et_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
ETable *etable = E_TABLE (object);
switch (prop_id) {
case PROP_MODEL:
g_value_set_object (value, etable->model);
break;
case PROP_UNIFORM_ROW_HEIGHT:
g_value_set_boolean (value, etable->uniform_row_height);
break;
case PROP_ALWAYS_SEARCH:
g_value_set_boolean (value, etable->always_search);
break;
case PROP_USE_CLICK_TO_ADD:
g_value_set_boolean (value, etable->use_click_to_add);
break;
default:
break;
}
}
typedef struct {
gchar *arg;
gboolean setting;
} bool_closure;
static void
et_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
ETable *etable = E_TABLE (object);
switch (prop_id) {
case PROP_LENGTH_THRESHOLD:
etable->length_threshold = g_value_get_int (value);
if (etable->group) {
gnome_canvas_item_set (GNOME_CANVAS_ITEM(etable->group),
"length_threshold", etable->length_threshold,
NULL);
}
break;
case PROP_UNIFORM_ROW_HEIGHT:
etable->uniform_row_height = g_value_get_boolean (value);
if (etable->group) {
gnome_canvas_item_set (GNOME_CANVAS_ITEM(etable->group),
"uniform_row_height", etable->uniform_row_height,
NULL);
}
break;
case PROP_ALWAYS_SEARCH:
if (etable->always_search == g_value_get_boolean (value))
return;
etable->always_search = g_value_get_boolean (value);
clear_current_search_col (etable);
break;
case PROP_USE_CLICK_TO_ADD:
if (etable->use_click_to_add == g_value_get_boolean (value))
return;
etable->use_click_to_add = g_value_get_boolean (value);
clear_current_search_col (etable);
if (etable->use_click_to_add) {
etable->click_to_add = gnome_canvas_item_new
(GNOME_CANVAS_GROUP(etable->canvas_vbox),
e_table_click_to_add_get_type (),
"header", etable->header,
"model", etable->model,
"message", etable->click_to_add_message,
NULL);
if (etable->use_click_to_add_end)
e_canvas_vbox_add_item (E_CANVAS_VBOX(etable->canvas_vbox),
etable->click_to_add);
else
e_canvas_vbox_add_item_start (E_CANVAS_VBOX(etable->canvas_vbox),
etable->click_to_add);
g_signal_connect (G_OBJECT (etable->click_to_add), "cursor_change",
G_CALLBACK (click_to_add_cursor_change), etable);
} else {
gtk_object_destroy (GTK_OBJECT (etable->click_to_add));
etable->click_to_add = NULL;
}
break;
}
}
static void
set_scroll_adjustments (ETable *table,
GtkAdjustment *hadjustment,
GtkAdjustment *vadjustment)
{
if (vadjustment != NULL) {
vadjustment->step_increment = 20;
gtk_adjustment_changed(vadjustment);
}
if (hadjustment != NULL) {
hadjustment->step_increment = 20;
gtk_adjustment_changed(hadjustment);
}
if (table->table_canvas != NULL) {
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);
}
/**
* e_table_get_next_row:
* @e_table: The #ETable to query
* @model_row: The model row to go from
*
* This function is used when your table is sorted, but you're using
* model row numbers. It returns the next row in sorted order as a model row.
*
* Return value:
* The model row number.
**/
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) {
gint i;
i = e_sorter_model_to_sorted(E_SORTER (e_table->sorter), model_row);
i++;
if (i < e_table_model_row_count(e_table->model)) {
return e_sorter_sorted_to_model(E_SORTER (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;
}
/**
* e_table_get_prev_row:
* @e_table: The #ETable to query
* @model_row: The model row to go from
*
* This function is used when your table is sorted, but you're using
* model row numbers. It returns the previous row in sorted order as
* a model row.
*
* Return value:
* The model row number.
**/
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) {
gint i;
i = e_sorter_model_to_sorted(E_SORTER (e_table->sorter), model_row);
i--;
if (i >= 0)
return e_sorter_sorted_to_model(E_SORTER (e_table->sorter), i);
else
return -1;
} else
return model_row - 1;
}
/**
* e_table_model_to_view_row:
* @e_table: The #ETable to query
* @model_row: The model row number
*
* Turns a model row into a view row.
*
* Return value:
* The view row number.
**/
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_sorter_model_to_sorted(E_SORTER (e_table->sorter), model_row);
else
return model_row;
}
/**
* e_table_view_to_model_row:
* @e_table: The #ETable to query
* @view_row: The view row number
*
* Turns a view row into a model row.
*
* Return value:
* The model row number.
**/
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_sorter_sorted_to_model (E_SORTER (e_table->sorter), view_row);
else
return view_row;
}
/**
* e_table_get_cell_at:
* @table: An #ETable widget
* @x: X coordinate for the pixel
* @y: Y coordinate for the pixel
* @row_return: Pointer to return the row value
* @col_return: Pointer to return the column value
*
* Return the row and column for the cell in which the pixel at (@x, @y) is
* contained.
**/
void
e_table_get_cell_at (ETable *table,
gint x, gint y,
gint *row_return, gint *col_return)
{
g_return_if_fail (table != NULL);
g_return_if_fail (E_IS_TABLE (table));
g_return_if_fail (row_return != NULL);
g_return_if_fail (col_return != NULL);
/* FIXME it would be nice if it could handle a NULL row_return or
* col_return gracefully. */
x += GTK_LAYOUT(table->table_canvas)->hadjustment->value;
y += GTK_LAYOUT(table->table_canvas)->vadjustment->value;
e_table_group_compute_location(table->group, &x, &y, row_return, col_return);
}
/**
* e_table_get_cell_geometry:
* @table: The #ETable.
* @row: The row to get the geometry of.
* @col: The col to get the geometry of.
* @x_return: Returns the x coordinate of the upper left hand corner of the cell with respect to the widget.
* @y_return: Returns the y coordinate of the upper left hand corner of the cell with respect to the widget.
* @width_return: Returns the width of the cell.
* @height_return: Returns the height of the cell.
*
* Returns the x, y, width, and height of the given cell. These can
* all be #NULL and they just won't be set.
**/
void
e_table_get_cell_geometry (ETable *table,
gint row, gint col,
gint *x_return, gint *y_return,
gint *width_return, gint *height_return)
{
g_return_if_fail (table != NULL);
g_return_if_fail (E_IS_TABLE (table));
e_table_group_get_cell_geometry(table->group, &row, &col, x_return, y_return, width_return, height_return);
if (x_return && table->table_canvas)
(*x_return) -= GTK_LAYOUT(table->table_canvas)->hadjustment->value;
if (y_return) {
if (table->table_canvas)
(*y_return) -= GTK_LAYOUT(table->table_canvas)->vadjustment->value;
if (table->header_canvas)
(*y_return) += GTK_WIDGET(table->header_canvas)->allocation.height;
}
}
/**
* e_table_get_mouse_over_cell:
* Similar to e_table_get_cell_at, only here we check based on the mouse motion information in the group.
**/
void
e_table_get_mouse_over_cell (ETable *table, gint *row, gint *col)
{
g_return_if_fail (table != NULL);
g_return_if_fail (E_IS_TABLE (table));
if (!table->group)
return;
e_table_group_get_mouse_over (table->group, row, col);
}
/**
* e_table_get_selection_model:
* @table: The #ETable to query
*
* Returns the table's #ESelectionModel in case you want to access it
* directly.
*
* Return value:
* The #ESelectionModel.
**/
ESelectionModel *
e_table_get_selection_model (ETable *table)
{
g_return_val_if_fail (table != NULL, NULL);
g_return_val_if_fail (E_IS_TABLE (table), NULL);
return E_SELECTION_MODEL (table->selection);
}
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 */
guint dropped : 1; /* Set after we receive a drop */
guint32 proxy_drop_time; /* Timestamp for proxied drop */
guint 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 */
/**
* e_table_drag_get_data:
* @table:
* @row:
* @col:
* @context:
* @target:
* @time:
*
*
**/
void
e_table_drag_get_data (ETable *table,
gint row,
gint col,
GdkDragContext *context,
GdkAtom target,
guint32 time)
{
g_return_if_fail(table != NULL);
g_return_if_fail(E_IS_TABLE(table));
gtk_drag_get_data(GTK_WIDGET(table),
context,
target,
time);
}
/**
* e_table_drag_highlight:
* @table: The #ETable to highlight
* @row: The row number of the cell to highlight
* @col: The column number of the cell to highlight
*
* Set col to -1 to highlight the entire row. If row is -1, this is
* identical to e_table_drag_unhighlight().
**/
void
e_table_drag_highlight (ETable *table,
gint row,
gint col)
{
g_return_if_fail(table != NULL);
g_return_if_fail(E_IS_TABLE(table));
if (row != -1) {
gint x, y, width, height;
if (col == -1) {
e_table_get_cell_geometry (table, row, 0, &x, &y, &width, &height);
x = 0;
width = GTK_WIDGET (table->table_canvas)->allocation.width;
} else {
e_table_get_cell_geometry (table, row, col, &x, &y, &width, &height);
x += GTK_LAYOUT(table->table_canvas)->hadjustment->value;
}
y += GTK_LAYOUT(table->table_canvas)->vadjustment->value;
if (table->drop_highlight == NULL) {
table->drop_highlight =
gnome_canvas_item_new (gnome_canvas_root (table->table_canvas),
gnome_canvas_rect_get_type (),
"fill_color", NULL,
/* "outline_color", "black",
"width_pixels", 1,*/
"outline_color_gdk", &(GTK_WIDGET (table)->style->fg[GTK_STATE_NORMAL]),
NULL);
}
gnome_canvas_item_set (table->drop_highlight,
"x1", (double) x,
"x2", (double) x + width - 1,
"y1", (double) y,
"y2", (double) y + height - 1,
NULL);
} else {
if (table->drop_highlight) {
gtk_object_destroy (GTK_OBJECT (table->drop_highlight));
table->drop_highlight = NULL;
}
}
}
/**
* e_table_drag_unhighlight:
* @table: The #ETable to unhighlight
*
* Removes the highlight from an #ETable.
**/
void
e_table_drag_unhighlight (ETable *table)
{
g_return_if_fail(table != NULL);
g_return_if_fail(E_IS_TABLE(table));
if (table->drop_highlight) {
gtk_object_destroy (GTK_OBJECT (table->drop_highlight));
table->drop_highlight = NULL;
}
}
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 */
static gint
et_real_start_drag (ETable *table, gint row, gint col, GdkEvent *event)
{
GtkDragSourceInfo *info;
GdkDragContext *context;
ETableDragSourceSite *site;
if (table->do_drag) {
site = table->site;
site->state = 0;
context = e_table_drag_begin (table, row, col,
site->target_list,
site->actions,
1, event);
if (context) {
info = g_dataset_get_data (context, "gtk-info");
if (info && !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;
}
return FALSE;
}
/**
* e_table_drag_source_set:
* @table: The #ETable to set up as a drag site
* @start_button_mask: Mask of allowed buttons to start drag
* @targets: Table of targets for this source
* @n_targets: Number of targets in @targets
* @actions: Actions allowed for this source
*
* Registers this table as a drag site, and possibly adds default behaviors.
**/
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 | GDK_STRUCTURE_MASK);
table->do_drag = TRUE;
if (site) {
if (site->target_list)
gtk_target_list_unref (site->target_list);
} else {
site = g_new0 (ETableDragSourceSite, 1);
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;
}
/**
* e_table_drag_source_unset:
* @table: The #ETable to un set up as a drag site
*
* Unregisters this #ETable as a drag site.
**/
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) {
if (site->target_list)
gtk_target_list_unref (site->target_list);
g_free (site);
table->site = NULL;
}
table->do_drag = FALSE;
}
/* There probably should be functions for setting the targets
* as a GtkTargetList
*/
/**
* e_table_drag_begin:
* @table: The #ETable to drag from
* @row: The row number of the cell
* @col: The col number of the cell
* @targets: The list of targets supported by the drag
* @actions: The available actions supported by the drag
* @button: The button held down for the drag
* @event: The event that initiated the drag
*
* Start a drag from this cell.
*
* Return value:
* The drag context.
**/
GdkDragContext *
e_table_drag_begin (ETable *table,
gint row,
gint 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
et_drag_begin (GtkWidget *widget,
GdkDragContext *context,
ETable *et)
{
g_signal_emit (G_OBJECT (et), et_signals [TABLE_DRAG_BEGIN], 0,
et->drag_row, et->drag_col, context);
}
static void
et_drag_end (GtkWidget *widget,
GdkDragContext *context,
ETable *et)
{
g_signal_emit (G_OBJECT (et), et_signals [TABLE_DRAG_END], 0,
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)
{
g_signal_emit (G_OBJECT (et), et_signals [TABLE_DRAG_DATA_GET], 0,
et->drag_row, et->drag_col, context, selection_data,
info, time);
}
static void
et_drag_data_delete(GtkWidget *widget,
GdkDragContext *context,
ETable *et)
{
g_signal_emit (G_OBJECT (et), et_signals [TABLE_DRAG_DATA_DELETE], 0,
et->drag_row, et->drag_col, context);
}
static gboolean
do_drag_motion(ETable *et,
GdkDragContext *context,
gint x,
gint y,
guint time)
{
gboolean ret_val;
gint row = -1, col = -1;
e_table_get_cell_at (et, x, y, &row, &col);
if (row != et->drop_row && col != et->drop_row) {
g_signal_emit (G_OBJECT (et), et_signals [TABLE_DRAG_LEAVE], 0,
et->drop_row, et->drop_col, context, time);
}
et->drop_row = row;
et->drop_col = col;
g_signal_emit (G_OBJECT (et), et_signals [TABLE_DRAG_MOTION], 0,
et->drop_row, et->drop_col, context, x, y, time, &ret_val);
return ret_val;
}
static gboolean
scroll_timeout (gpointer data)
{
ETable *et = data;
gint dx = 0, dy = 0;
GtkAdjustment *h, *v;
double hvalue, vvalue;
if (et->scroll_direction & ET_SCROLL_DOWN)
dy += 20;
if (et->scroll_direction & ET_SCROLL_UP)
dy -= 20;
if (et->scroll_direction & ET_SCROLL_RIGHT)
dx += 20;
if (et->scroll_direction & ET_SCROLL_LEFT)
dx -= 20;
h = GTK_LAYOUT(et->table_canvas)->hadjustment;
v = GTK_LAYOUT(et->table_canvas)->vadjustment;
hvalue = h->value;
vvalue = v->value;
gtk_adjustment_set_value(h, CLAMP(h->value + dx, h->lower, h->upper - h->page_size));
gtk_adjustment_set_value(v, CLAMP(v->value + dy, v->lower, v->upper - v->page_size));
if (h->value != hvalue ||
v->value != vvalue)
do_drag_motion(et,
et->last_drop_context,
et->last_drop_x,
et->last_drop_y,
et->last_drop_time);
return TRUE;
}
static void
scroll_on (ETable *et, guint scroll_direction)
{
if (et->scroll_idle_id == 0 || scroll_direction != et->scroll_direction) {
if (et->scroll_idle_id != 0)
g_source_remove (et->scroll_idle_id);
et->scroll_direction = scroll_direction;
et->scroll_idle_id = g_timeout_add (100, scroll_timeout, et);
}
}
static void
scroll_off (ETable *et)
{
if (et->scroll_idle_id) {
g_source_remove (et->scroll_idle_id);
et->scroll_idle_id = 0;
}
}
static void
context_destroyed (gpointer data)
{
ETable *et = data;
/* if (!GTK_OBJECT_DESTROYED (et)) */
/* FIXME: */
{
et->last_drop_x = 0;
et->last_drop_y = 0;
et->last_drop_time = 0;
et->last_drop_context = NULL;
scroll_off (et);
}
g_object_unref (et);
}
static void
context_connect (ETable *et, GdkDragContext *context)
{
if (g_dataset_get_data (context, "e-table") == NULL) {
g_object_ref (et);
g_dataset_set_data_full (context, "e-table", et, context_destroyed);
}
}
static void
et_drag_leave(GtkWidget *widget,
GdkDragContext *context,
guint time,
ETable *et)
{
g_signal_emit (G_OBJECT (et), et_signals [TABLE_DRAG_LEAVE], 0,
et->drop_row, et->drop_col, context, time);
et->drop_row = -1;
et->drop_col = -1;
scroll_off (et);
}
static gboolean
et_drag_motion(GtkWidget *widget,
GdkDragContext *context,
gint x,
gint y,
guint time,
ETable *et)
{
gboolean ret_val;
guint direction = 0;
et->last_drop_x = x;
et->last_drop_y = y;
et->last_drop_time = time;
et->last_drop_context = context;
context_connect (et, context);
ret_val = do_drag_motion (et,
context,
x,
y,
time);
if (y < 20)
direction |= ET_SCROLL_UP;
if (y > widget->allocation.height - 20)
direction |= ET_SCROLL_DOWN;
if (x < 20)
direction |= ET_SCROLL_LEFT;
if (x > widget->allocation.width - 20)
direction |= ET_SCROLL_RIGHT;
if (direction != 0)
scroll_on (et, direction);
else
scroll_off (et);
return ret_val;
}
static gboolean
et_drag_drop(GtkWidget *widget,
GdkDragContext *context,
gint x,
gint y,
guint time,
ETable *et)
{
gboolean ret_val;
gint row, col;
e_table_get_cell_at (et, x, y, &row, &col);
if (row != et->drop_row && col != et->drop_row) {
g_signal_emit (G_OBJECT (et), et_signals [TABLE_DRAG_LEAVE], 0,
et->drop_row, et->drop_col, context, time);
g_signal_emit (G_OBJECT (et), et_signals [TABLE_DRAG_MOTION], 0,
row, col, context, x, y, time, &ret_val);
}
et->drop_row = row;
et->drop_col = col;
g_signal_emit (G_OBJECT (et), et_signals [TABLE_DRAG_DROP], 0,
et->drop_row, et->drop_col, context, x, y, time, &ret_val);
et->drop_row = -1;
et->drop_col = -1;
scroll_off (et);
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)
{
gint row, col;
e_table_get_cell_at (et, x, y, &row, &col);
g_signal_emit (G_OBJECT (et), et_signals [TABLE_DRAG_DATA_RECEIVED], 0,
row, col, context, x, y, selection_data, info, time);
}
static void
e_table_class_init (ETableClass *class)
{
GObjectClass *object_class;
GtkWidgetClass *widget_class;
object_class = (GObjectClass *) class;
widget_class = (GtkWidgetClass *) class;
object_class->dispose = et_dispose;
object_class->finalize = et_finalize;
object_class->set_property = et_set_property;
object_class->get_property = et_get_property;
widget_class->grab_focus = et_grab_focus;
widget_class->unrealize = et_unrealize;
widget_class->size_request = et_size_request;
widget_class->focus = et_focus;
class->cursor_change = NULL;
class->cursor_activated = NULL;
class->selection_change = NULL;
class->double_click = NULL;
class->right_click = NULL;
class->click = NULL;
class->key_press = NULL;
class->start_drag = et_real_start_drag;
class->state_change = NULL;
class->white_space_event = NULL;
class->table_drag_begin = NULL;
class->table_drag_end = NULL;
class->table_drag_data_get = NULL;
class->table_drag_data_delete = NULL;
class->table_drag_leave = NULL;
class->table_drag_motion = NULL;
class->table_drag_drop = NULL;
class->table_drag_data_received = NULL;
et_signals [CURSOR_CHANGE] =
g_signal_new ("cursor_change",
G_OBJECT_CLASS_TYPE (object_class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (ETableClass, cursor_change),
NULL, NULL,
g_cclosure_marshal_VOID__INT,
G_TYPE_NONE, 1, G_TYPE_INT);
et_signals [CURSOR_ACTIVATED] =
g_signal_new ("cursor_activated",
G_OBJECT_CLASS_TYPE (object_class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (ETableClass, cursor_activated),
NULL, NULL,
g_cclosure_marshal_VOID__INT,
G_TYPE_NONE, 1, G_TYPE_INT);
et_signals [SELECTION_CHANGE] =
g_signal_new ("selection_change",
G_OBJECT_CLASS_TYPE (object_class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (ETableClass, selection_change),
NULL, NULL,
g_cclosure_marshal_VOID__VOID,
G_TYPE_NONE, 0);
et_signals [DOUBLE_CLICK] =
g_signal_new ("double_click",
G_OBJECT_CLASS_TYPE (object_class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (ETableClass, double_click),
NULL, NULL,
e_marshal_NONE__INT_INT_BOXED,
G_TYPE_NONE, 3, G_TYPE_INT,
G_TYPE_INT, GDK_TYPE_EVENT);
et_signals [RIGHT_CLICK] =
g_signal_new ("right_click",
G_OBJECT_CLASS_TYPE (object_class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (ETableClass, right_click),
NULL, NULL,
e_marshal_INT__INT_INT_BOXED,
G_TYPE_INT, 3, G_TYPE_INT,
G_TYPE_INT, GDK_TYPE_EVENT);
et_signals [CLICK] =
g_signal_new ("click",
G_OBJECT_CLASS_TYPE (object_class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (ETableClass, click),
NULL, NULL,
e_marshal_INT__INT_INT_BOXED,
G_TYPE_INT, 3, G_TYPE_INT,
G_TYPE_INT, GDK_TYPE_EVENT);
et_signals [KEY_PRESS] =
g_signal_new ("key_press",
G_OBJECT_CLASS_TYPE (object_class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (ETableClass, key_press),
NULL, NULL,
e_marshal_INT__INT_INT_BOXED,
G_TYPE_INT, 3, G_TYPE_INT,
G_TYPE_INT, GDK_TYPE_EVENT);
et_signals [START_DRAG] =
g_signal_new ("start_drag",
G_OBJECT_CLASS_TYPE (object_class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (ETableClass, start_drag),
NULL, NULL,
e_marshal_INT__INT_INT_BOXED,
G_TYPE_INT, 3, G_TYPE_INT,
G_TYPE_INT, GDK_TYPE_EVENT);
et_signals [STATE_CHANGE] =
g_signal_new ("state_change",
G_OBJECT_CLASS_TYPE (object_class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (ETableClass, state_change),
NULL, NULL,
g_cclosure_marshal_VOID__VOID,
G_TYPE_NONE, 0);
et_signals [WHITE_SPACE_EVENT] =
g_signal_new ("white_space_event",
G_OBJECT_CLASS_TYPE (object_class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (ETableClass, white_space_event),
NULL, NULL,
e_marshal_INT__BOXED,
G_TYPE_INT, 1, GDK_TYPE_EVENT);
et_signals[TABLE_DRAG_BEGIN] =
g_signal_new ("table_drag_begin",
G_OBJECT_CLASS_TYPE (object_class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (ETableClass, table_drag_begin),
NULL, NULL,
e_marshal_NONE__INT_INT_OBJECT,
G_TYPE_NONE, 3,
G_TYPE_INT,
G_TYPE_INT,
GDK_TYPE_DRAG_CONTEXT);
et_signals[TABLE_DRAG_END] =
g_signal_new ("table_drag_end",
G_OBJECT_CLASS_TYPE (object_class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (ETableClass, table_drag_end),
NULL, NULL,
e_marshal_NONE__INT_INT_OBJECT,
G_TYPE_NONE, 3,
G_TYPE_INT,
G_TYPE_INT,
GDK_TYPE_DRAG_CONTEXT);
et_signals[TABLE_DRAG_DATA_GET] =
g_signal_new ("table_drag_data_get",
G_OBJECT_CLASS_TYPE (object_class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (ETableClass, table_drag_data_get),
NULL, NULL,
e_marshal_NONE__INT_INT_OBJECT_BOXED_UINT_UINT,
G_TYPE_NONE, 6,
G_TYPE_INT,
G_TYPE_INT,
GDK_TYPE_DRAG_CONTEXT,
GTK_TYPE_SELECTION_DATA | G_SIGNAL_TYPE_STATIC_SCOPE,
G_TYPE_UINT,
G_TYPE_UINT);
et_signals[TABLE_DRAG_DATA_DELETE] =
g_signal_new ("table_drag_data_delete",
G_OBJECT_CLASS_TYPE (object_class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (ETableClass, table_drag_data_delete),
NULL, NULL,
e_marshal_NONE__INT_INT_OBJECT,
G_TYPE_NONE, 3,
G_TYPE_INT,
G_TYPE_INT,
GDK_TYPE_DRAG_CONTEXT);
et_signals[TABLE_DRAG_LEAVE] =
g_signal_new ("table_drag_leave",
G_OBJECT_CLASS_TYPE (object_class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (ETableClass, table_drag_leave),
NULL, NULL,
e_marshal_NONE__INT_INT_OBJECT_UINT,
G_TYPE_NONE, 4,
G_TYPE_INT,
G_TYPE_INT,
GDK_TYPE_DRAG_CONTEXT,
G_TYPE_UINT);
et_signals[TABLE_DRAG_MOTION] =
g_signal_new ("table_drag_motion",
G_OBJECT_CLASS_TYPE (object_class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (ETableClass, table_drag_motion),
NULL, NULL,
e_marshal_BOOLEAN__INT_INT_OBJECT_INT_INT_UINT,
G_TYPE_BOOLEAN, 6,
G_TYPE_INT,
G_TYPE_INT,
GDK_TYPE_DRAG_CONTEXT,
G_TYPE_INT,
G_TYPE_INT,
G_TYPE_UINT);
et_signals[TABLE_DRAG_DROP] =
g_signal_new ("table_drag_drop",
G_OBJECT_CLASS_TYPE (object_class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (ETableClass, table_drag_drop),
NULL, NULL,
e_marshal_BOOLEAN__INT_INT_OBJECT_INT_INT_UINT,
G_TYPE_BOOLEAN, 6,
G_TYPE_INT,
G_TYPE_INT,
GDK_TYPE_DRAG_CONTEXT,
G_TYPE_INT,
G_TYPE_INT,
G_TYPE_UINT);
et_signals[TABLE_DRAG_DATA_RECEIVED] =
g_signal_new ("table_drag_data_received",
G_OBJECT_CLASS_TYPE (object_class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (ETableClass, table_drag_data_received),
NULL, NULL,
e_marshal_NONE__INT_INT_OBJECT_INT_INT_BOXED_UINT_UINT,
G_TYPE_NONE, 8,
G_TYPE_INT,
G_TYPE_INT,
GDK_TYPE_DRAG_CONTEXT,
G_TYPE_INT,
G_TYPE_INT,
GTK_TYPE_SELECTION_DATA | G_SIGNAL_TYPE_STATIC_SCOPE,
G_TYPE_UINT,
G_TYPE_UINT);
class->set_scroll_adjustments = set_scroll_adjustments;
widget_class->set_scroll_adjustments_signal =
g_signal_new ("set_scroll_adjustments",
G_OBJECT_CLASS_TYPE (object_class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (ETableClass, set_scroll_adjustments),
NULL, NULL,
e_marshal_NONE__OBJECT_OBJECT,
G_TYPE_NONE, 2, GTK_TYPE_ADJUSTMENT, GTK_TYPE_ADJUSTMENT);
g_object_class_install_property (object_class, PROP_LENGTH_THRESHOLD,
g_param_spec_int ("length_threshold",
_("Length Threshold"),
/*_( */"XXX blurb" /*)*/,
0, G_MAXINT, 0,
G_PARAM_WRITABLE));
g_object_class_install_property (object_class, PROP_UNIFORM_ROW_HEIGHT,
g_param_spec_boolean ("uniform_row_height",
_("Uniform row height"),
/*_( */"XXX blurb" /*)*/,
FALSE,
G_PARAM_READWRITE));
g_object_class_install_property (object_class, PROP_ALWAYS_SEARCH,
g_param_spec_boolean ("always_search",
_("Always search"),
/*_( */"XXX blurb" /*)*/,
FALSE,
G_PARAM_READWRITE));
g_object_class_install_property (object_class, PROP_USE_CLICK_TO_ADD,
g_param_spec_boolean ("use_click_to_add",
_("Use click to add"),
/*_( */"XXX blurb" /*)*/,
FALSE,
G_PARAM_READWRITE));
g_object_class_install_property (object_class, PROP_MODEL,
g_param_spec_object ("model",
_("Model"),
/*_( */"XXX blurb" /*)*/,
E_TABLE_MODEL_TYPE,
G_PARAM_READABLE));
gtk_widget_class_install_style_property (widget_class,
g_param_spec_int ("vertical-spacing",
_("Vertical Row Spacing"),
_("Vertical space between rows. It is added to top and to bottom of a row"),
0,
G_MAXINT,
3,
G_PARAM_READABLE | G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB));
gal_a11y_e_table_init ();
}