aboutsummaryrefslogtreecommitdiffstats
path: root/widgets/table/e-tree.c
diff options
context:
space:
mode:
Diffstat (limited to 'widgets/table/e-tree.c')
-rw-r--r--widgets/table/e-tree.c2004
1 files changed, 2004 insertions, 0 deletions
diff --git a/widgets/table/e-tree.c b/widgets/table/e-tree.c
new file mode 100644
index 0000000000..a434d54ea8
--- /dev/null
+++ b/widgets/table/e-tree.c
@@ -0,0 +1,2004 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * e-tree.c: A graphical view of a tree.
+ *
+ * Author:
+ * Miguel de Icaza (miguel@ximian.com)
+ * Chris Lahey <clahey@ximian.com>
+ *
+ * Copyright 1999, 2000, 2001, Ximian, Inc
+ */
+#include <config.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdio.h>
+#include "gal/util/e-i18n.h"
+#include <libgnomeui/gnome-canvas.h>
+#include <gtk/gtksignal.h>
+
+#include "gal/util/e-util.h"
+#include "gal/widgets/e-canvas.h"
+#include "e-tree.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-specification.h"
+#include "e-table-state.h"
+#include "e-table-column-specification.h"
+
+#include "e-table-utils.h"
+
+#define COLUMN_HEADER_HEIGHT 16
+
+#define PARENT_TYPE gtk_table_get_type ()
+
+static GtkObjectClass *parent_class;
+
+enum {
+ CURSOR_CHANGE,
+ CURSOR_ACTIVATED,
+ SELECTION_CHANGE,
+ DOUBLE_CLICK,
+ RIGHT_CLICK,
+ CLICK,
+ KEY_PRESS,
+
+ TREE_DRAG_BEGIN,
+ TREE_DRAG_END,
+ TREE_DRAG_DATA_GET,
+ TREE_DRAG_DATA_DELETE,
+
+ TREE_DRAG_LEAVE,
+ TREE_DRAG_MOTION,
+ TREE_DRAG_DROP,
+ TREE_DRAG_DATA_RECEIVED,
+
+ LAST_SIGNAL
+};
+
+enum {
+ ARG_0,
+ ARG_LENGTH_THRESHOLD,
+};
+
+static gint et_signals [LAST_SIGNAL] = { 0, };
+
+static void et_grab_focus (GtkWidget *widget);
+
+static void et_drag_begin (GtkWidget *widget,
+ GdkDragContext *context,
+ ETree *et);
+static void et_drag_end (GtkWidget *widget,
+ GdkDragContext *context,
+ ETree *et);
+static void et_drag_data_get(GtkWidget *widget,
+ GdkDragContext *context,
+ GtkSelectionData *selection_data,
+ guint info,
+ guint time,
+ ETree *et);
+static void et_drag_data_delete(GtkWidget *widget,
+ GdkDragContext *context,
+ ETree *et);
+
+static void et_drag_leave(GtkWidget *widget,
+ GdkDragContext *context,
+ guint time,
+ ETree *et);
+static gboolean et_drag_motion(GtkWidget *widget,
+ GdkDragContext *context,
+ gint x,
+ gint y,
+ guint time,
+ ETree *et);
+static gboolean et_drag_drop(GtkWidget *widget,
+ GdkDragContext *context,
+ gint x,
+ gint y,
+ guint time,
+ ETree *et);
+static void et_drag_data_received(GtkWidget *widget,
+ GdkDragContext *context,
+ gint x,
+ gint y,
+ GtkSelectionData *selection_data,
+ guint info,
+ guint time,
+ ETree *et);
+static gint e_tree_drag_source_event_cb (GtkWidget *widget,
+ GdkEvent *event,
+ ETree *tree);
+
+static gint et_focus (GtkContainer *container, GtkDirectionType direction);
+
+static void
+et_destroy (GtkObject *object)
+{
+ ETree *et = E_TREE (object);
+
+ 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->sorted));
+ 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->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));
+
+ (*parent_class->destroy)(object);
+}
+
+static void
+e_tree_init (GtkObject *object)
+{
+ ETree *e_tree = E_TREE (object);
+ GtkTable *gtk_table = GTK_TABLE (object);
+
+ GTK_WIDGET_SET_FLAGS (e_tree, GTK_CAN_FOCUS);
+
+ gtk_table->homogeneous = FALSE;
+
+ e_tree->sort_info = NULL;
+ e_tree->reflow_idle_id = 0;
+
+ e_tree->draw_grid = 1;
+ e_tree->draw_focus = 1;
+ e_tree->cursor_mode = E_CURSOR_SIMPLE;
+ e_tree->length_threshold = 200;
+
+ e_tree->horizontal_scrolling = FALSE;
+
+ e_tree->drop_row = -1;
+ e_tree->drop_path = NULL;
+ e_tree->drop_col = -1;
+
+ e_tree->drag_row = -1;
+ e_tree->drag_path = NULL;
+ e_tree->drag_col = -1;
+
+ e_tree->site = NULL;
+ e_tree->drag_source_button_press_event_id = 0;
+ e_tree->drag_source_motion_notify_event_id = 0;
+
+ e_tree->selection = e_table_selection_model_new();
+ e_tree->spec = NULL;
+}
+
+/* Grab_focus handler for the ETree */
+static void
+et_grab_focus (GtkWidget *widget)
+{
+ ETree *e_tree;
+
+ e_tree = E_TREE (widget);
+
+ gtk_widget_grab_focus (GTK_WIDGET (e_tree->table_canvas));
+}
+
+/* Focus handler for the ETree */
+static gint
+et_focus (GtkContainer *container, GtkDirectionType direction)
+{
+ ETree *e_tree;
+
+ e_tree = E_TREE (container);
+
+ if (container->focus_child) {
+ gtk_container_set_focus_child (container, NULL);
+ return FALSE;
+ }
+
+ return gtk_container_focus (GTK_CONTAINER (e_tree->table_canvas), direction);
+}
+
+static void
+header_canvas_size_allocate (GtkWidget *widget, GtkAllocation *alloc, ETree *e_tree)
+{
+ gnome_canvas_set_scroll_region (
+ GNOME_CANVAS (e_tree->header_canvas),
+ 0, 0, alloc->width - 1, /* COLUMN_HEADER_HEIGHT - 1 */
+ E_TABLE_HEADER_ITEM (e_tree->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_tree->header_canvas)->allocation.height !=
+ E_TABLE_HEADER_ITEM (e_tree->header_item)->height)
+ gtk_widget_set_usize (GTK_WIDGET (e_tree->header_canvas), -1,
+ E_TABLE_HEADER_ITEM (e_tree->header_item)->height);
+}
+
+static void
+e_tree_setup_header (ETree *e_tree)
+{
+ char *pointer;
+ e_tree->header_canvas = GNOME_CANVAS (e_canvas_new ());
+ GTK_WIDGET_UNSET_FLAGS (e_tree->header_canvas, GTK_CAN_FOCUS);
+
+ gtk_widget_show (GTK_WIDGET (e_tree->header_canvas));
+
+ pointer = g_strdup_printf("%p", e_tree);
+
+ e_tree->header_item = gnome_canvas_item_new (
+ gnome_canvas_root (e_tree->header_canvas),
+ e_table_header_item_get_type (),
+ "ETableHeader", e_tree->header,
+ "full_header", e_tree->full_header,
+ "sort_info", e_tree->sort_info,
+ "dnd_code", pointer,
+ /* "table", e_tree, FIXME*/
+ NULL);
+
+ g_free(pointer);
+
+ gtk_signal_connect (
+ GTK_OBJECT (e_tree->header_canvas), "size_allocate",
+ GTK_SIGNAL_FUNC (header_canvas_size_allocate), e_tree);
+
+ gtk_widget_set_usize (GTK_WIDGET (e_tree->header_canvas), -1,
+ E_TABLE_HEADER_ITEM (e_tree->header_item)->height);
+}
+
+static gboolean
+tree_canvas_reflow_idle (ETree *e_tree)
+{
+ gdouble height, width;
+ gdouble item_height;
+ GtkAllocation *alloc = &(GTK_WIDGET (e_tree->table_canvas)->allocation);
+
+ gtk_object_get (GTK_OBJECT (e_tree->item),
+ "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_tree->table_canvas),
+ 0, 0, width - 1, height - 1);
+ gtk_object_set (GTK_OBJECT (e_tree->white_item),
+ "y1", item_height + 1,
+ "x2", width,
+ "y2", height,
+ NULL);
+ e_tree->reflow_idle_id = 0;
+ return FALSE;
+}
+
+static void
+tree_canvas_size_allocate (GtkWidget *widget, GtkAllocation *alloc,
+ ETree *e_tree)
+{
+ gdouble width;
+ gdouble height;
+ gdouble item_height;
+
+ width = alloc->width;
+ gtk_object_get (GTK_OBJECT (e_tree->item),
+ "height", &height,
+ NULL);
+ item_height = height;
+ height = MAX ((int)height, alloc->height);
+
+ gtk_object_set (GTK_OBJECT (e_tree->item),
+ "width", width,
+ NULL);
+ gtk_object_set (GTK_OBJECT (e_tree->header),
+ "width", width,
+ NULL);
+ if (e_tree->reflow_idle_id)
+ g_source_remove(e_tree->reflow_idle_id);
+ tree_canvas_reflow_idle(e_tree);
+}
+
+static void
+tree_canvas_reflow (GnomeCanvas *canvas, ETree *e_tree)
+{
+ if (!e_tree->reflow_idle_id)
+ e_tree->reflow_idle_id = g_idle_add_full (400, (GSourceFunc) tree_canvas_reflow_idle, e_tree, NULL);
+}
+
+static void
+item_cursor_change (ETableItem *eti, int row, ETree *et)
+{
+ ETreePath path = e_tree_table_adapter_node_at_row(et->etta, row);
+ path = e_tree_sorted_view_to_model_path(et->sorted, path);
+ gtk_signal_emit (GTK_OBJECT (et),
+ et_signals [CURSOR_CHANGE],
+ row, path);
+}
+
+static void
+item_cursor_activated (ETableItem *eti, int row, ETree *et)
+{
+ ETreePath path = e_tree_table_adapter_node_at_row(et->etta, row);
+ path = e_tree_sorted_view_to_model_path(et->sorted, path);
+ gtk_signal_emit (GTK_OBJECT (et),
+ et_signals [CURSOR_ACTIVATED],
+ row, path);
+}
+
+static void
+item_double_click (ETableItem *eti, int row, int col, GdkEvent *event, ETree *et)
+{
+ ETreePath path = e_tree_table_adapter_node_at_row(et->etta, row);
+ path = e_tree_sorted_view_to_model_path(et->sorted, path);
+ gtk_signal_emit (GTK_OBJECT (et),
+ et_signals [DOUBLE_CLICK],
+ row, path, col, event);
+}
+
+static gint
+item_right_click (ETableItem *eti, int row, int col, GdkEvent *event, ETree *et)
+{
+ int return_val = 0;
+ ETreePath path = e_tree_table_adapter_node_at_row(et->etta, row);
+ path = e_tree_sorted_view_to_model_path(et->sorted, path);
+ gtk_signal_emit (GTK_OBJECT (et),
+ et_signals [RIGHT_CLICK],
+ row, path, col, event, &return_val);
+ return return_val;
+}
+
+static gint
+item_click (ETableItem *eti, int row, int col, GdkEvent *event, ETree *et)
+{
+ int return_val = 0;
+ ETreePath path = e_tree_table_adapter_node_at_row(et->etta, row);
+ path = e_tree_sorted_view_to_model_path(et->sorted, path);
+ gtk_signal_emit (GTK_OBJECT (et),
+ et_signals [CLICK],
+ row, path, col, event, &return_val);
+ return return_val;
+}
+
+static gint
+item_key_press (ETableItem *eti, int row, int col, GdkEvent *event, ETree *et)
+{
+ int return_val = 0;
+ ETreePath path = e_tree_table_adapter_node_at_row(et->etta, row);
+ GdkEventKey *key = (GdkEventKey *) event;
+ GdkEventButton click;
+
+ path = e_tree_sorted_view_to_model_path(et->sorted, path);
+
+ 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, path, col, event, &return_val);
+ break;
+ }
+ return return_val;
+}
+
+static void
+et_selection_model_selection_change (ETableSelectionModel *etsm, ETable *et)
+{
+ gtk_signal_emit (GTK_OBJECT (et),
+ et_signals [SELECTION_CHANGE]);
+}
+
+static void
+et_build_item (ETree *et)
+{
+ et->item = gnome_canvas_item_new(GNOME_CANVAS_GROUP (gnome_canvas_root(et->table_canvas)),
+ e_table_item_get_type(),
+ "ETableHeader", et->header,
+ "ETableModel", et->etta,
+ "table_selection_model", et->selection,
+ "drawgrid", et->draw_grid,
+ "drawfocus", et->draw_focus,
+ "cursor_mode", et->cursor_mode,
+ "length_threshold", et->length_threshold,
+ NULL);
+
+ gtk_signal_connect (GTK_OBJECT (et->item), "cursor_change",
+ GTK_SIGNAL_FUNC (item_cursor_change), et);
+ gtk_signal_connect (GTK_OBJECT (et->item), "cursor_activated",
+ GTK_SIGNAL_FUNC (item_cursor_activated), et);
+ gtk_signal_connect (GTK_OBJECT (et->item), "double_click",
+ GTK_SIGNAL_FUNC (item_double_click), et);
+ gtk_signal_connect (GTK_OBJECT (et->item), "right_click",
+ GTK_SIGNAL_FUNC (item_right_click), et);
+ gtk_signal_connect (GTK_OBJECT (et->item), "click",
+ GTK_SIGNAL_FUNC (item_click), et);
+ gtk_signal_connect (GTK_OBJECT (et->item), "key_press",
+ GTK_SIGNAL_FUNC (item_key_press), et);
+}
+
+static void
+et_canvas_realize (GtkWidget *canvas, ETree *e_tree)
+{
+ gnome_canvas_item_set(
+ e_tree->white_item,
+ "fill_color_gdk", &GTK_WIDGET(e_tree->table_canvas)->style->base[GTK_STATE_NORMAL],
+ NULL);
+}
+
+static void
+et_canvas_button_press (GtkWidget *canvas, GdkEvent *event, ETree *e_tree)
+{
+ 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 void
+e_tree_setup_table (ETree *e_tree)
+{
+ e_tree->table_canvas = GNOME_CANVAS (e_canvas_new ());
+ gtk_signal_connect (
+ GTK_OBJECT (e_tree->table_canvas), "size_allocate",
+ GTK_SIGNAL_FUNC (tree_canvas_size_allocate), e_tree);
+ gtk_signal_connect (
+ GTK_OBJECT (e_tree->table_canvas), "focus_in_event",
+ GTK_SIGNAL_FUNC (gtk_widget_queue_draw), e_tree);
+ gtk_signal_connect (
+ GTK_OBJECT (e_tree->table_canvas), "focus_out_event",
+ GTK_SIGNAL_FUNC (gtk_widget_queue_draw), e_tree);
+
+ gtk_signal_connect (
+ GTK_OBJECT (e_tree), "drag_begin",
+ GTK_SIGNAL_FUNC (et_drag_begin), e_tree);
+ gtk_signal_connect (
+ GTK_OBJECT (e_tree), "drag_end",
+ GTK_SIGNAL_FUNC (et_drag_end), e_tree);
+ gtk_signal_connect (
+ GTK_OBJECT (e_tree), "drag_data_get",
+ GTK_SIGNAL_FUNC (et_drag_data_get), e_tree);
+ gtk_signal_connect (
+ GTK_OBJECT (e_tree), "drag_data_delete",
+ GTK_SIGNAL_FUNC (et_drag_data_delete), e_tree);
+ gtk_signal_connect (
+ GTK_OBJECT (e_tree), "drag_motion",
+ GTK_SIGNAL_FUNC (et_drag_motion), e_tree);
+ gtk_signal_connect (
+ GTK_OBJECT (e_tree), "drag_leave",
+ GTK_SIGNAL_FUNC (et_drag_leave), e_tree);
+ gtk_signal_connect (
+ GTK_OBJECT (e_tree), "drag_drop",
+ GTK_SIGNAL_FUNC (et_drag_drop), e_tree);
+ gtk_signal_connect (
+ GTK_OBJECT (e_tree), "drag_data_received",
+ GTK_SIGNAL_FUNC (et_drag_data_received), e_tree);
+
+ gtk_signal_connect (GTK_OBJECT(e_tree->table_canvas), "reflow",
+ GTK_SIGNAL_FUNC (tree_canvas_reflow), e_tree);
+
+ gtk_widget_show (GTK_WIDGET (e_tree->table_canvas));
+
+ e_tree->white_item = gnome_canvas_item_new(
+ gnome_canvas_root(e_tree->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_tree->table_canvas)->style->base[GTK_STATE_NORMAL],
+ NULL);
+
+ gtk_signal_connect (
+ GTK_OBJECT(e_tree->table_canvas), "realize",
+ GTK_SIGNAL_FUNC(et_canvas_realize), e_tree);
+ gtk_signal_connect (
+ GTK_OBJECT(e_tree->table_canvas), "button_press_event",
+ GTK_SIGNAL_FUNC(et_canvas_button_press), e_tree);
+
+ et_build_item(e_tree);
+}
+
+void
+e_tree_set_state_object(ETree *e_tree, ETableState *state)
+{
+ if (e_tree->header)
+ gtk_object_unref(GTK_OBJECT(e_tree->header));
+ e_tree->header = e_table_state_to_header (GTK_WIDGET(e_tree), e_tree->full_header, state);
+ if (e_tree->header)
+ gtk_object_ref(GTK_OBJECT(e_tree->header));
+
+ gtk_object_set (GTK_OBJECT (e_tree->header),
+ "width", (double) (GTK_WIDGET(e_tree->table_canvas)->allocation.width),
+ NULL);
+
+ if (e_tree->sort_info)
+ gtk_object_unref(GTK_OBJECT(e_tree->sort_info));
+
+ if (state->sort_info)
+ e_tree->sort_info = e_table_sort_info_duplicate(state->sort_info);
+ else
+ e_tree->sort_info = NULL;
+
+ if (e_tree->header_item)
+ gtk_object_set(GTK_OBJECT(e_tree->header_item),
+ "ETableHeader", e_tree->header,
+ "sort_info", e_tree->sort_info,
+ NULL);
+
+ if (e_tree->item)
+ gtk_object_set(GTK_OBJECT(e_tree->item),
+ "ETableHeader", e_tree->header,
+ NULL);
+
+ if (e_tree->sorted)
+ gtk_object_set(GTK_OBJECT(e_tree->sorted),
+ "sort_info", e_tree->sort_info,
+ NULL);
+}
+
+/**
+ * e_tree_set_state:
+ * @e_tree: %ETree object that will be modified
+ * @state_str: a string with the XML representation of the ETableState.
+ *
+ * This routine sets the state (as described by %ETableState) of the
+ * %ETree object.
+ */
+void
+e_tree_set_state (ETree *e_tree,
+ const gchar *state_str)
+{
+ ETableState *state;
+
+ g_return_if_fail(e_tree != NULL);
+ g_return_if_fail(E_IS_TREE(e_tree));
+ 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_tree_set_state_object(e_tree, state);
+
+ gtk_object_unref(GTK_OBJECT(state));
+}
+
+/**
+ * e_tree_load_state:
+ * @e_tree: %ETree object that will be modified
+ * @filename: name of the file containing the state to be loaded into the %ETree
+ *
+ * An %ETableState will be loaded form the file pointed by @filename into the
+ * @e_tree object.
+ */
+void
+e_tree_load_state (ETree *e_tree,
+ const gchar *filename)
+{
+ ETableState *state;
+
+ g_return_if_fail(e_tree != NULL);
+ g_return_if_fail(E_IS_TREE(e_tree));
+ 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_tree_set_state_object(e_tree, state);
+
+ gtk_object_unref(GTK_OBJECT(state));
+}
+
+/**
+ * e_tree_get_state_object:
+ * @e_tree: %ETree object that will be modified
+ *
+ * Returns: the %ETreeState object that encapsulates the current
+ * state of the @e_tree object
+ */
+ETableState *
+e_tree_get_state_object (ETree *e_tree)
+{
+ ETableState *state;
+ int full_col_count;
+ int i, j;
+
+ state = e_table_state_new();
+ state->sort_info = e_tree->sort_info;
+ gtk_object_ref(GTK_OBJECT(state->sort_info));
+
+ state->col_count = e_table_header_count (e_tree->header);
+ full_col_count = e_table_header_count (e_tree->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_tree->header, i);
+ state->columns[i] = -1;
+ for (j = 0; j < full_col_count; j++) {
+ if (col->col_idx == e_table_header_index(e_tree->full_header, j)) {
+ state->columns[i] = j;
+ break;
+ }
+ }
+ state->expansions[i] = col->expansion;
+ }
+
+ return state;
+}
+
+gchar *e_tree_get_state (ETree *e_tree)
+{
+ ETableState *state;
+ gchar *string;
+
+ state = e_tree_get_state_object(e_tree);
+ string = e_table_state_save_to_string(state);
+ gtk_object_unref(GTK_OBJECT(state));
+ return string;
+}
+
+/**
+ * e_tree_save_state:
+ * @e_tree: %ETree object that will be modified
+ * @filename: name of the file containing the state to be loaded into the %ETree
+ *
+ * This routine saves the state of the @e_tree object into the file pointed
+ * by @filename
+ */
+void
+e_tree_save_state (ETree *e_tree,
+ const gchar *filename)
+{
+ ETableState *state;
+
+ state = e_tree_get_state_object(e_tree);
+ e_table_state_save_to_file(state, filename);
+ gtk_object_unref(GTK_OBJECT(state));
+}
+
+static ETree *
+et_real_construct (ETree *e_tree, ETreeModel *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_tree->draw_grid = specification->draw_grid;
+ e_tree->draw_focus = specification->draw_focus;
+ e_tree->cursor_mode = specification->cursor_mode;
+ e_tree->full_header = e_table_spec_to_full_header(specification, ete);
+
+ e_tree->header = e_table_state_to_header (GTK_WIDGET(e_tree), e_tree->full_header, state);
+ e_tree->horizontal_scrolling = specification->horizontal_scrolling;
+
+ e_tree->sort_info = state->sort_info;
+
+ gtk_object_set(GTK_OBJECT(e_tree->header),
+ "sort_info", e_tree->sort_info,
+ NULL);
+
+ e_tree->model = etm;
+ gtk_object_ref (GTK_OBJECT (etm));
+
+ e_tree->sorted = e_tree_sorted_new(etm, e_tree->full_header, e_tree->sort_info);
+
+ e_tree->etta = E_TREE_TABLE_ADAPTER(e_tree_table_adapter_new(E_TREE_MODEL(e_tree->sorted)));
+
+ gtk_object_set(GTK_OBJECT(e_tree->selection),
+ "model", E_TABLE_MODEL(e_tree->etta),
+ "selection_mode", specification->selection_mode,
+ "cursor_mode", specification->cursor_mode,
+ NULL);
+
+ gtk_widget_push_visual (gdk_rgb_get_visual ());
+ gtk_widget_push_colormap (gdk_rgb_get_cmap ());
+
+ e_tree->sorter = e_sorter_new();
+
+ gtk_object_set (GTK_OBJECT (e_tree->selection),
+ "model", e_tree->etta,
+ "sorter", e_tree->sorter,
+ NULL);
+
+ gtk_signal_connect(GTK_OBJECT(e_tree->selection), "selection_changed",
+ GTK_SIGNAL_FUNC(et_selection_model_selection_change), e_tree);
+
+ if (!specification->no_headers) {
+ e_tree_setup_header (e_tree);
+ }
+ e_tree_setup_table (e_tree);
+
+ gtk_layout_get_vadjustment (GTK_LAYOUT (e_tree->table_canvas))->step_increment = 20;
+ gtk_adjustment_changed(gtk_layout_get_vadjustment (GTK_LAYOUT (e_tree->table_canvas)));
+
+ if (!specification->no_headers) {
+ /*
+ * The header
+ */
+ gtk_table_attach (GTK_TABLE (e_tree), GTK_WIDGET (e_tree->header_canvas),
+ 0, 1, 0 + row, 1 + row,
+ GTK_FILL | GTK_EXPAND,
+ GTK_FILL, 0, 0);
+ row ++;
+ }
+ gtk_table_attach (GTK_TABLE (e_tree), GTK_WIDGET (e_tree->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_tree;
+}
+
+ETree *
+e_tree_construct (ETree *e_tree, ETreeModel *etm, ETableExtras *ete,
+ const char *spec_str, const char *state_str)
+{
+ ETableSpecification *specification;
+ ETableState *state;
+
+ g_return_val_if_fail(e_tree != NULL, NULL);
+ g_return_val_if_fail(E_IS_TREE(e_tree), NULL);
+ g_return_val_if_fail(etm != NULL, NULL);
+ g_return_val_if_fail(E_IS_TREE_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_tree = et_real_construct (e_tree, etm, ete, specification, state);
+
+ e_tree->spec = specification;
+ gtk_object_unref(GTK_OBJECT(state));
+
+ return e_tree;
+}
+
+ETree *
+e_tree_construct_from_spec_file (ETree *e_tree, ETreeModel *etm, ETableExtras *ete,
+ const char *spec_fn, const char *state_fn)
+{
+ ETableSpecification *specification;
+ ETableState *state;
+
+ g_return_val_if_fail(e_tree != NULL, NULL);
+ g_return_val_if_fail(E_IS_TREE(e_tree), NULL);
+ g_return_val_if_fail(etm != NULL, NULL);
+ g_return_val_if_fail(E_IS_TREE_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_tree = et_real_construct (e_tree, etm, ete, specification, state);
+
+ e_tree->spec = specification;
+ gtk_object_unref(GTK_OBJECT(state));
+
+ return e_tree;
+}
+
+GtkWidget *
+e_tree_new (ETreeModel *etm, ETableExtras *ete, const char *spec, const char *state)
+{
+ ETree *e_tree;
+
+ g_return_val_if_fail(etm != NULL, NULL);
+ g_return_val_if_fail(E_IS_TREE_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_tree = gtk_type_new (e_tree_get_type ());
+
+ e_tree = e_tree_construct (e_tree, etm, ete, spec, state);
+
+ return GTK_WIDGET (e_tree);
+}
+
+GtkWidget *
+e_tree_new_from_spec_file (ETreeModel *etm, ETableExtras *ete, const char *spec_fn, const char *state_fn)
+{
+ ETree *e_tree;
+
+ g_return_val_if_fail(etm != NULL, NULL);
+ g_return_val_if_fail(E_IS_TREE_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_tree = gtk_type_new (e_tree_get_type ());
+
+ e_tree = e_tree_construct_from_spec_file (e_tree, etm, ete, spec_fn, state_fn);
+
+ return GTK_WIDGET (e_tree);
+}
+
+void
+e_tree_set_cursor (ETree *e_tree, ETreePath path)
+{
+ int row;
+ g_return_if_fail(e_tree != NULL);
+ g_return_if_fail(E_IS_TREE(e_tree));
+ g_return_if_fail(path != NULL);
+
+ path = e_tree_sorted_model_to_view_path(e_tree->sorted, path);
+
+ row = e_tree_table_adapter_row_of_node(E_TREE_TABLE_ADAPTER(e_tree->etta), path);
+
+ if (row == -1)
+ return;
+
+ gtk_object_set(GTK_OBJECT(e_tree->selection),
+ "cursor_row", row,
+ NULL);
+}
+
+ETreePath
+e_tree_get_cursor (ETree *e_tree)
+{
+ int row;
+ ETreePath path;
+ g_return_val_if_fail(e_tree != NULL, NULL);
+ g_return_val_if_fail(E_IS_TREE(e_tree), NULL);
+
+ gtk_object_get(GTK_OBJECT(e_tree->selection),
+ "cursor_row", &row,
+ NULL);
+ path = e_tree_table_adapter_node_at_row(E_TREE_TABLE_ADAPTER(e_tree->etta), row);
+ path = e_tree_sorted_view_to_model_path(e_tree->sorted, path);
+ return path;
+}
+
+void
+e_tree_selected_row_foreach (ETree *e_tree,
+ EForeachFunc callback,
+ gpointer closure)
+{
+ g_return_if_fail(e_tree != NULL);
+ g_return_if_fail(E_IS_TREE(e_tree));
+
+ e_selection_model_foreach(E_SELECTION_MODEL (e_tree->selection),
+ callback,
+ closure);
+}
+
+gint
+e_tree_selected_count (ETree *e_tree)
+{
+ g_return_val_if_fail(e_tree != NULL, -1);
+ g_return_val_if_fail(E_IS_TREE(e_tree), -1);
+
+ return e_selection_model_selected_count(E_SELECTION_MODEL (e_tree->selection));
+}
+
+void
+e_tree_select_all (ETree *tree)
+{
+ g_return_if_fail (tree != NULL);
+ g_return_if_fail (E_IS_TREE (tree));
+
+ e_selection_model_select_all (E_SELECTION_MODEL (tree->selection));
+}
+
+void
+e_tree_invert_selection (ETree *tree)
+{
+ g_return_if_fail (tree != NULL);
+ g_return_if_fail (E_IS_TREE (tree));
+
+ e_selection_model_invert_selection (E_SELECTION_MODEL (tree->selection));
+}
+
+
+EPrintable *
+e_tree_get_printable (ETree *e_tree)
+{
+ g_return_val_if_fail(e_tree != NULL, NULL);
+ g_return_val_if_fail(E_IS_TREE(e_tree), NULL);
+
+ return e_table_item_get_printable(E_TABLE_ITEM(e_tree->item));
+}
+
+static void
+et_get_arg (GtkObject *o, GtkArg *arg, guint arg_id)
+{
+ switch (arg_id){
+ default:
+ break;
+ }
+}
+
+typedef struct {
+ char *arg;
+ gboolean setting;
+} bool_closure;
+
+static void
+et_set_arg (GtkObject *o, GtkArg *arg, guint arg_id)
+{
+ ETree *etree = E_TREE (o);
+
+ switch (arg_id){
+ case ARG_LENGTH_THRESHOLD:
+ etree->length_threshold = GTK_VALUE_INT (*arg);
+ if (etree->item) {
+ gnome_canvas_item_set (GNOME_CANVAS_ITEM(etree->item),
+ "length_threshold", GTK_VALUE_INT (*arg),
+ NULL);
+ }
+ break;
+
+ }
+}
+
+static void
+set_scroll_adjustments (ETree *tree,
+ GtkAdjustment *hadjustment,
+ GtkAdjustment *vadjustment)
+{
+ if (vadjustment != NULL) {
+ vadjustment->step_increment = 20;
+ gtk_adjustment_changed(vadjustment);
+ }
+
+ gtk_layout_set_hadjustment (GTK_LAYOUT(tree->table_canvas),
+ hadjustment);
+ gtk_layout_set_vadjustment (GTK_LAYOUT(tree->table_canvas),
+ vadjustment);
+
+ if (tree->header_canvas != NULL)
+ gtk_layout_set_hadjustment (GTK_LAYOUT(tree->header_canvas),
+ hadjustment);
+}
+
+gint
+e_tree_get_next_row (ETree *e_tree,
+ gint model_row)
+{
+ g_return_val_if_fail(e_tree != NULL, -1);
+ g_return_val_if_fail(E_IS_TREE(e_tree), -1);
+
+ if (e_tree->sorter) {
+ int i;
+ i = e_sorter_model_to_sorted(E_SORTER (e_tree->sorter), model_row);
+ i++;
+ if (i < e_table_model_row_count(E_TABLE_MODEL(e_tree->etta))) {
+ return e_sorter_sorted_to_model(E_SORTER (e_tree->sorter), i);
+ } else
+ return -1;
+ } else
+ if (model_row < e_table_model_row_count(E_TABLE_MODEL(e_tree->etta)) - 1)
+ return model_row + 1;
+ else
+ return -1;
+}
+
+gint
+e_tree_get_prev_row (ETree *e_tree,
+ gint model_row)
+{
+ g_return_val_if_fail(e_tree != NULL, -1);
+ g_return_val_if_fail(E_IS_TREE(e_tree), -1);
+
+ if (e_tree->sorter) {
+ int i;
+ i = e_sorter_model_to_sorted(E_SORTER (e_tree->sorter), model_row);
+ i--;
+ if (i >= 0)
+ return e_sorter_sorted_to_model(E_SORTER (e_tree->sorter), i);
+ else
+ return -1;
+ } else
+ return model_row - 1;
+}
+
+gint
+e_tree_model_to_view_row (ETree *e_tree,
+ gint model_row)
+{
+ g_return_val_if_fail(e_tree != NULL, -1);
+ g_return_val_if_fail(E_IS_TREE(e_tree), -1);
+
+ if (e_tree->sorter)
+ return e_sorter_model_to_sorted(E_SORTER (e_tree->sorter), model_row);
+ else
+ return model_row;
+}
+
+gint
+e_tree_view_to_model_row (ETree *e_tree,
+ gint view_row)
+{
+ g_return_val_if_fail(e_tree != NULL, -1);
+ g_return_val_if_fail(E_IS_TREE(e_tree), -1);
+
+ if (e_tree->sorter)
+ return e_sorter_sorted_to_model (E_SORTER (e_tree->sorter), view_row);
+ else
+ return view_row;
+}
+
+
+gboolean
+e_tree_node_is_expanded (ETree *et, ETreePath path)
+{
+ path = e_tree_sorted_model_to_view_path(et->sorted, path);
+
+ return e_tree_table_adapter_node_is_expanded (et->etta, path);
+}
+
+void
+e_tree_node_set_expanded (ETree *et, ETreePath path, gboolean expanded)
+{
+ g_return_if_fail (et != NULL);
+ g_return_if_fail (E_IS_TREE(et));
+
+ path = e_tree_sorted_model_to_view_path(et->sorted, path);
+
+ e_tree_table_adapter_node_set_expanded (et->etta, path, expanded);
+}
+
+void
+e_tree_node_set_expanded_recurse (ETree *et, ETreePath path, gboolean expanded)
+{
+ g_return_if_fail (et != NULL);
+ g_return_if_fail (E_IS_TREE(et));
+
+ path = e_tree_sorted_model_to_view_path(et->sorted, path);
+
+ e_tree_table_adapter_node_set_expanded_recurse (et->etta, path, expanded);
+}
+
+void
+e_tree_root_node_set_visible (ETree *et, gboolean visible)
+{
+ g_return_if_fail (et != NULL);
+ g_return_if_fail (E_IS_TREE(et));
+
+ e_tree_table_adapter_root_node_set_visible (et->etta, visible);
+}
+
+ETreePath
+e_tree_node_at_row (ETree *et, int row)
+{
+ ETreePath path;
+
+ path = e_tree_table_adapter_node_at_row (et->etta, row);
+ path = e_tree_sorted_view_to_model_path(et->sorted, path);
+
+ return path;
+}
+
+int
+e_tree_row_of_node (ETree *et, ETreePath path)
+{
+ path = e_tree_sorted_model_to_view_path(et->sorted, path);
+ return e_tree_table_adapter_row_of_node (et->etta, path);
+}
+
+gboolean
+e_tree_root_node_is_visible(ETree *et)
+{
+ return e_tree_table_adapter_root_node_is_visible (et->etta);
+}
+
+void
+e_tree_show_node (ETree *et, ETreePath path)
+{
+ g_return_if_fail (et != NULL);
+ g_return_if_fail (E_IS_TREE(et));
+
+ path = e_tree_sorted_model_to_view_path(et->sorted, path);
+
+ e_tree_table_adapter_show_node (et->etta, path);
+}
+
+void
+e_tree_save_expanded_state (ETree *et, char *filename)
+{
+ g_return_if_fail (et != NULL);
+ g_return_if_fail (E_IS_TREE(et));
+
+ e_tree_table_adapter_save_expanded_state (et->etta, filename);
+}
+
+void
+e_tree_load_expanded_state (ETree *et, char *filename)
+{
+ e_tree_table_adapter_load_expanded_state (et->etta, filename);
+}
+
+gint
+e_tree_row_count (ETree *et)
+{
+ return e_table_model_row_count (E_TABLE_MODEL(et->etta));
+}
+
+struct _ETreeDragSourceSite
+{
+ 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_tree_drag_get_data (ETree *tree,
+ int row,
+ int col,
+ GdkDragContext *context,
+ GdkAtom target,
+ guint32 time)
+{
+ ETreePath path;
+ g_return_if_fail(tree != NULL);
+ g_return_if_fail(E_IS_TREE(tree));
+
+ path = e_tree_table_adapter_node_at_row(tree->etta, row);
+ path = e_tree_sorted_view_to_model_path(tree->sorted, path);
+
+ gtk_drag_get_data(GTK_WIDGET(tree),
+ context,
+ target,
+ time);
+
+}
+
+/**
+ * e_tree_drag_highlight:
+ * @tree:
+ * @row:
+ * @col:
+ *
+ * Set col to -1 to highlight the entire row.
+ */
+void
+e_tree_drag_highlight (ETree *tree,
+ int row,
+ int col)
+{
+ g_return_if_fail(tree != NULL);
+ g_return_if_fail(E_IS_TREE(tree));
+}
+
+void
+e_tree_drag_unhighlight (ETree *tree)
+{
+ g_return_if_fail(tree != NULL);
+ g_return_if_fail(E_IS_TREE(tree));
+}
+
+void e_tree_drag_dest_set (ETree *tree,
+ GtkDestDefaults flags,
+ const GtkTargetEntry *targets,
+ gint n_targets,
+ GdkDragAction actions)
+{
+ g_return_if_fail(tree != NULL);
+ g_return_if_fail(E_IS_TREE(tree));
+
+ gtk_drag_dest_set(GTK_WIDGET(tree),
+ flags,
+ targets,
+ n_targets,
+ actions);
+}
+
+void e_tree_drag_dest_set_proxy (ETree *tree,
+ GdkWindow *proxy_window,
+ GdkDragProtocol protocol,
+ gboolean use_coordinates)
+{
+ g_return_if_fail(tree != NULL);
+ g_return_if_fail(E_IS_TREE(tree));
+
+ gtk_drag_dest_set_proxy(GTK_WIDGET(tree),
+ proxy_window,
+ protocol,
+ use_coordinates);
+}
+
+/*
+ * There probably should be functions for setting the targets
+ * as a GtkTargetList
+ */
+
+void
+e_tree_drag_dest_unset (GtkWidget *widget)
+{
+ g_return_if_fail(widget != NULL);
+ g_return_if_fail(E_IS_TREE(widget));
+
+ gtk_drag_dest_unset(widget);
+}
+
+/* Source side */
+
+void
+e_tree_drag_source_set (ETree *tree,
+ GdkModifierType start_button_mask,
+ const GtkTargetEntry *targets,
+ gint n_targets,
+ GdkDragAction actions)
+{
+ ETreeDragSourceSite *site;
+ GtkWidget *canvas;
+
+ g_return_if_fail(tree != NULL);
+ g_return_if_fail(E_IS_TREE(tree));
+
+ canvas = GTK_WIDGET(tree->table_canvas);
+ site = tree->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);
+
+ if (site) {
+ if (site->target_list)
+ gtk_target_list_unref (site->target_list);
+ } else {
+ site = g_new0 (ETreeDragSourceSite, 1);
+
+ tree->drag_source_button_press_event_id =
+ gtk_signal_connect (GTK_OBJECT (canvas), "button_press_event",
+ GTK_SIGNAL_FUNC (e_tree_drag_source_event_cb),
+ tree);
+ tree->drag_source_motion_notify_event_id =
+ gtk_signal_connect (GTK_OBJECT (canvas), "motion_notify_event",
+ GTK_SIGNAL_FUNC (e_tree_drag_source_event_cb),
+ tree);
+
+ tree->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_tree_drag_source_unset (ETree *tree)
+{
+ ETreeDragSourceSite *site;
+
+ g_return_if_fail (tree != NULL);
+ g_return_if_fail (E_IS_TREE(tree));
+
+ site = tree->site;
+
+ if (site) {
+ gtk_signal_disconnect (
+ GTK_OBJECT (tree->table_canvas),
+ tree->drag_source_button_press_event_id);
+ gtk_signal_disconnect (
+ GTK_OBJECT (tree->table_canvas),
+ tree->drag_source_motion_notify_event_id);
+ g_free (site);
+ tree->site = NULL;
+ }
+}
+
+/* There probably should be functions for setting the targets
+ * as a GtkTargetList
+ */
+
+GdkDragContext *
+e_tree_drag_begin (ETree *tree,
+ int row,
+ int col,
+ GtkTargetList *targets,
+ GdkDragAction actions,
+ gint button,
+ GdkEvent *event)
+{
+ ETreePath path;
+ g_return_val_if_fail (tree != NULL, NULL);
+ g_return_val_if_fail (E_IS_TREE(tree), NULL);
+
+ path = e_tree_table_adapter_node_at_row(tree->etta, row);
+ path = e_tree_sorted_view_to_model_path(tree->sorted, path);
+
+ tree->drag_row = row;
+ tree->drag_path = path;
+ tree->drag_col = col;
+
+ return gtk_drag_begin(GTK_WIDGET(tree),
+ targets,
+ actions,
+ button,
+ event);
+}
+
+/**
+ * e_tree_get_cell_at:
+ * @tree: An ETree 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_tree_get_cell_at (ETree *tree,
+ int x, int y,
+ int *row_return, int *col_return)
+{
+ g_return_if_fail (tree != NULL);
+ g_return_if_fail (E_IS_TREE (tree));
+ 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(tree->table_canvas)->hadjustment->value;
+ y += GTK_LAYOUT(tree->table_canvas)->vadjustment->value;
+ e_table_item_compute_location(E_TABLE_ITEM(tree->item), &x, &y, row_return, col_return);
+}
+
+static void
+et_drag_begin (GtkWidget *widget,
+ GdkDragContext *context,
+ ETree *et)
+{
+ gtk_signal_emit (GTK_OBJECT (et),
+ et_signals [TREE_DRAG_BEGIN],
+ et->drag_row,
+ et->drag_path,
+ et->drag_col,
+ context);
+}
+
+static void
+et_drag_end (GtkWidget *widget,
+ GdkDragContext *context,
+ ETree *et)
+{
+ gtk_signal_emit (GTK_OBJECT (et),
+ et_signals [TREE_DRAG_END],
+ et->drag_row,
+ et->drag_path,
+ et->drag_col,
+ context);
+}
+
+static void
+et_drag_data_get(GtkWidget *widget,
+ GdkDragContext *context,
+ GtkSelectionData *selection_data,
+ guint info,
+ guint time,
+ ETree *et)
+{
+ gtk_signal_emit (GTK_OBJECT (et),
+ et_signals [TREE_DRAG_DATA_GET],
+ et->drag_row,
+ et->drag_path,
+ et->drag_col,
+ context,
+ selection_data,
+ info,
+ time);
+}
+
+static void
+et_drag_data_delete(GtkWidget *widget,
+ GdkDragContext *context,
+ ETree *et)
+{
+ gtk_signal_emit (GTK_OBJECT (et),
+ et_signals [TREE_DRAG_DATA_DELETE],
+ et->drag_row,
+ et->drag_path,
+ et->drag_col,
+ context);
+}
+
+static void
+et_drag_leave(GtkWidget *widget,
+ GdkDragContext *context,
+ guint time,
+ ETree *et)
+{
+ gtk_signal_emit (GTK_OBJECT (et),
+ et_signals [TREE_DRAG_LEAVE],
+ et->drop_row,
+ et->drop_path,
+ 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,
+ ETree *et)
+{
+ gboolean ret_val;
+ int row, col;
+ ETreePath path;
+ e_tree_get_cell_at (et,
+ 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 [TREE_DRAG_LEAVE],
+ et->drop_row,
+ et->drop_path,
+ et->drop_col,
+ context,
+ time);
+ }
+
+ path = e_tree_table_adapter_node_at_row(et->etta, row);
+ path = e_tree_sorted_view_to_model_path(et->sorted, path);
+
+ et->drop_row = row;
+ et->drop_path = path;
+ et->drop_col = col;
+ if (row >= 0 && col >= 0)
+ gtk_signal_emit (GTK_OBJECT (et),
+ et_signals [TREE_DRAG_MOTION],
+ et->drop_row,
+ et->drop_path,
+ 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,
+ ETree *et)
+{
+ gboolean ret_val;
+ int row, col;
+ ETreePath path;
+ e_tree_get_cell_at(et,
+ x,
+ y,
+ &row,
+ &col);
+ path = e_tree_table_adapter_node_at_row(et->etta, row);
+ path = e_tree_sorted_view_to_model_path(et->sorted, path);
+
+ 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 [TREE_DRAG_LEAVE],
+ et->drop_row,
+ et->drop_path,
+ et->drop_col,
+ context,
+ time);
+ if (row >= 0 && col >= 0)
+ gtk_signal_emit (GTK_OBJECT (et),
+ et_signals [TREE_DRAG_MOTION],
+ row,
+ path,
+ col,
+ context,
+ x,
+ y,
+ time,
+ &ret_val);
+ }
+ et->drop_row = row;
+ et->drop_path = path;
+ et->drop_col = col;
+ if (row >= 0 && col >= 0)
+ gtk_signal_emit (GTK_OBJECT (et),
+ et_signals [TREE_DRAG_DROP],
+ et->drop_row,
+ et->drop_path,
+ et->drop_col,
+ context,
+ x,
+ y,
+ time,
+ &ret_val);
+ et->drop_row = -1;
+ et->drop_path = NULL;
+ 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,
+ ETree *et)
+{
+ int row, col;
+ ETreePath path;
+ e_tree_get_cell_at(et,
+ x,
+ y,
+ &row,
+ &col);
+ path = e_tree_table_adapter_node_at_row(et->etta, row);
+ path = e_tree_sorted_view_to_model_path(et->sorted, path);
+ gtk_signal_emit (GTK_OBJECT (et),
+ et_signals [TREE_DRAG_DATA_RECEIVED],
+ row,
+ path,
+ col,
+ context,
+ x,
+ y,
+ selection_data,
+ info,
+ time);
+}
+
+static gint
+e_tree_drag_source_event_cb (GtkWidget *widget,
+ GdkEvent *event,
+ ETree *tree)
+{
+ ETreeDragSourceSite *site;
+ site = tree->site;
+
+ switch (event->type) {
+ case GDK_BUTTON_PRESS:
+ if ((GDK_BUTTON1_MASK << (event->button.button - 1)) & site->start_button_mask) {
+ int row, col;
+ e_tree_get_cell_at(tree, 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_tree_drag_begin (tree, 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_tree_class_init (ETreeClass *class)
+{
+ GtkObjectClass *object_class;
+ GtkWidgetClass *widget_class;
+ GtkContainerClass *container_class;
+
+ object_class = (GtkObjectClass *) class;
+ widget_class = (GtkWidgetClass *) class;
+ container_class = (GtkContainerClass *) class;
+
+ 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;
+
+ widget_class->grab_focus = et_grab_focus;
+
+ container_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->tree_drag_begin = NULL;
+ class->tree_drag_end = NULL;
+ class->tree_drag_data_get = NULL;
+ class->tree_drag_data_delete = NULL;
+
+ class->tree_drag_leave = NULL;
+ class->tree_drag_motion = NULL;
+ class->tree_drag_drop = NULL;
+ class->tree_drag_data_received = NULL;
+
+ et_signals [CURSOR_CHANGE] =
+ gtk_signal_new ("cursor_change",
+ GTK_RUN_LAST,
+ object_class->type,
+ GTK_SIGNAL_OFFSET (ETreeClass, cursor_change),
+ gtk_marshal_NONE__POINTER_INT,
+ GTK_TYPE_NONE, 2, GTK_TYPE_POINTER, GTK_TYPE_INT);
+
+ et_signals [CURSOR_ACTIVATED] =
+ gtk_signal_new ("cursor_activated",
+ GTK_RUN_LAST,
+ object_class->type,
+ GTK_SIGNAL_OFFSET (ETreeClass, cursor_activated),
+ gtk_marshal_NONE__POINTER_INT,
+ GTK_TYPE_NONE, 2, GTK_TYPE_POINTER, GTK_TYPE_INT);
+
+ et_signals [SELECTION_CHANGE] =
+ gtk_signal_new ("selection_change",
+ GTK_RUN_LAST,
+ object_class->type,
+ GTK_SIGNAL_OFFSET (ETreeClass, 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 (ETreeClass, double_click),
+ e_marshal_NONE__INT_POINTER_INT_POINTER,
+ GTK_TYPE_NONE, 4, GTK_TYPE_INT, GTK_TYPE_POINTER, GTK_TYPE_INT, GTK_TYPE_POINTER);
+
+ et_signals [RIGHT_CLICK] =
+ gtk_signal_new ("right_click",
+ GTK_RUN_LAST,
+ object_class->type,
+ GTK_SIGNAL_OFFSET (ETreeClass, right_click),
+ e_marshal_INT__INT_POINTER_INT_POINTER,
+ GTK_TYPE_INT, 4, GTK_TYPE_INT, GTK_TYPE_POINTER, GTK_TYPE_INT, GTK_TYPE_POINTER);
+
+ et_signals [CLICK] =
+ gtk_signal_new ("click",
+ GTK_RUN_LAST,
+ object_class->type,
+ GTK_SIGNAL_OFFSET (ETreeClass, click),
+ e_marshal_INT__INT_POINTER_INT_POINTER,
+ GTK_TYPE_INT, 4, GTK_TYPE_INT, GTK_TYPE_POINTER, GTK_TYPE_INT, GTK_TYPE_POINTER);
+
+ et_signals [KEY_PRESS] =
+ gtk_signal_new ("key_press",
+ GTK_RUN_LAST,
+ object_class->type,
+ GTK_SIGNAL_OFFSET (ETreeClass, key_press),
+ e_marshal_INT__INT_POINTER_INT_POINTER,
+ GTK_TYPE_INT, 4, GTK_TYPE_INT, GTK_TYPE_POINTER, GTK_TYPE_INT, GTK_TYPE_POINTER);
+
+ et_signals[TREE_DRAG_BEGIN] =
+ gtk_signal_new ("tree_drag_begin",
+ GTK_RUN_LAST,
+ object_class->type,
+ GTK_SIGNAL_OFFSET (ETreeClass, tree_drag_begin),
+ e_marshal_NONE__INT_POINTER_INT_POINTER,
+ GTK_TYPE_NONE, 4,
+ GTK_TYPE_INT,
+ GTK_TYPE_POINTER,
+ GTK_TYPE_INT,
+ GTK_TYPE_GDK_DRAG_CONTEXT);
+ et_signals[TREE_DRAG_END] =
+ gtk_signal_new ("tree_drag_end",
+ GTK_RUN_LAST,
+ object_class->type,
+ GTK_SIGNAL_OFFSET (ETreeClass, tree_drag_end),
+ e_marshal_NONE__INT_POINTER_INT_POINTER,
+ GTK_TYPE_NONE, 4,
+ GTK_TYPE_INT,
+ GTK_TYPE_POINTER,
+ GTK_TYPE_INT,
+ GTK_TYPE_GDK_DRAG_CONTEXT);
+ et_signals[TREE_DRAG_DATA_GET] =
+ gtk_signal_new ("tree_drag_data_get",
+ GTK_RUN_LAST,
+ object_class->type,
+ GTK_SIGNAL_OFFSET (ETreeClass, tree_drag_data_get),
+ e_marshal_NONE__INT_POINTER_INT_POINTER_POINTER_UINT_UINT,
+ GTK_TYPE_NONE, 7,
+ GTK_TYPE_INT,
+ GTK_TYPE_POINTER,
+ GTK_TYPE_INT,
+ GTK_TYPE_GDK_DRAG_CONTEXT,
+ GTK_TYPE_SELECTION_DATA,
+ GTK_TYPE_UINT,
+ GTK_TYPE_UINT);
+ et_signals[TREE_DRAG_DATA_DELETE] =
+ gtk_signal_new ("tree_drag_data_delete",
+ GTK_RUN_LAST,
+ object_class->type,
+ GTK_SIGNAL_OFFSET (ETreeClass, tree_drag_data_delete),
+ e_marshal_NONE__INT_POINTER_INT_POINTER,
+ GTK_TYPE_NONE, 4,
+ GTK_TYPE_INT,
+ GTK_TYPE_POINTER,
+ GTK_TYPE_INT,
+ GTK_TYPE_GDK_DRAG_CONTEXT);
+
+ et_signals[TREE_DRAG_LEAVE] =
+ gtk_signal_new ("tree_drag_leave",
+ GTK_RUN_LAST,
+ object_class->type,
+ GTK_SIGNAL_OFFSET (ETreeClass, tree_drag_leave),
+ e_marshal_NONE__INT_POINTER_INT_POINTER_UINT,
+ GTK_TYPE_NONE, 5,
+ GTK_TYPE_INT,
+ GTK_TYPE_POINTER,
+ GTK_TYPE_INT,
+ GTK_TYPE_GDK_DRAG_CONTEXT,
+ GTK_TYPE_UINT);
+ et_signals[TREE_DRAG_MOTION] =
+ gtk_signal_new ("tree_drag_motion",
+ GTK_RUN_LAST,
+ object_class->type,
+ GTK_SIGNAL_OFFSET (ETreeClass, tree_drag_motion),
+ e_marshal_BOOL__INT_POINTER_INT_POINTER_INT_INT_UINT,
+ GTK_TYPE_BOOL, 7,
+ GTK_TYPE_INT,
+ GTK_TYPE_POINTER,
+ GTK_TYPE_INT,
+ GTK_TYPE_GDK_DRAG_CONTEXT,
+ GTK_TYPE_INT,
+ GTK_TYPE_INT,
+ GTK_TYPE_UINT);
+ et_signals[TREE_DRAG_DROP] =
+ gtk_signal_new ("tree_drag_drop",
+ GTK_RUN_LAST,
+ object_class->type,
+ GTK_SIGNAL_OFFSET (ETreeClass, tree_drag_drop),
+ e_marshal_BOOL__INT_POINTER_INT_POINTER_INT_INT_UINT,
+ GTK_TYPE_BOOL, 7,
+ GTK_TYPE_INT,
+ GTK_TYPE_POINTER,
+ GTK_TYPE_INT,
+ GTK_TYPE_GDK_DRAG_CONTEXT,
+ GTK_TYPE_INT,
+ GTK_TYPE_INT,
+ GTK_TYPE_UINT);
+ et_signals[TREE_DRAG_DATA_RECEIVED] =
+ gtk_signal_new ("tree_drag_data_received",
+ GTK_RUN_LAST,
+ object_class->type,
+ GTK_SIGNAL_OFFSET (ETreeClass, tree_drag_data_received),
+ e_marshal_NONE__INT_POINTER_INT_POINTER_INT_INT_POINTER_UINT_UINT,
+ GTK_TYPE_NONE, 9,
+ GTK_TYPE_INT,
+ GTK_TYPE_POINTER,
+ 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);
+
+ class->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 (ETreeClass, set_scroll_adjustments),
+ gtk_marshal_NONE__POINTER_POINTER,
+ GTK_TYPE_NONE, 2, GTK_TYPE_ADJUSTMENT, GTK_TYPE_ADJUSTMENT);
+
+ gtk_object_add_arg_type ("ETree::length_threshold", GTK_TYPE_INT,
+ GTK_ARG_WRITABLE, ARG_LENGTH_THRESHOLD);
+}
+
+E_MAKE_TYPE(e_tree, "ETree", ETree, e_tree_class_init, e_tree_init, PARENT_TYPE);