Files
evolution/widgets/table/e-tree.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

3459 lines
91 KiB
C

/*
* 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>
*
* Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
*
*/
#include <config.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <gdk/gdkkeysyms.h>
#include <gtk/gtk.h>
#include <libgnomecanvas/gnome-canvas-rect-ellipse.h>
#include "gal-a11y-e-tree.h"
#include <glib/gi18n.h>
#include "e-util/e-util.h"
#include "misc/e-canvas.h"
#include "misc/e-canvas-utils.h"
#include "misc/e-canvas-background.h"
#include "text/e-text.h"
#include "e-table-column-specification.h"
#include "e-table-header-item.h"
#include "e-table-header.h"
#include "e-table-item.h"
#include "e-table-sort-info.h"
#include "e-table-utils.h"
#ifdef E_TREE_USE_TREE_SELECTION
#include "e-tree-selection-model.h"
#else
#include "e-table-selection-model.h"
#endif
#include "e-tree.h"
#include "e-tree-table-adapter.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,
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 {
PROP_0,
PROP_LENGTH_THRESHOLD,
PROP_HORIZONTAL_DRAW_GRID,
PROP_VERTICAL_DRAW_GRID,
PROP_DRAW_FOCUS,
PROP_ETTA,
PROP_UNIFORM_ROW_HEIGHT,
PROP_ALWAYS_SEARCH
};
enum {
ET_SCROLL_UP = 1 << 0,
ET_SCROLL_DOWN = 1 << 1,
ET_SCROLL_LEFT = 1 << 2,
ET_SCROLL_RIGHT = 1 << 3
};
struct ETreePriv {
ETreeModel *model;
ETreeTableAdapter *etta;
ETableHeader *full_header, *header;
guint structure_change_id, expansion_change_id;
ETableSortInfo *sort_info;
ESorter *sorter;
guint sort_info_change_id, group_info_change_id;
ESelectionModel *selection;
ETableSpecification *spec;
ETableSearch *search;
ETableCol *current_search_col;
guint search_search_id;
guint search_accept_id;
gint reflow_idle_id;
gint scroll_idle_id;
gint hover_idle_id;
gint table_model_change_id;
gint table_row_change_id;
gint table_cell_change_id;
gint table_rows_delete_id;
GnomeCanvasItem *info_text;
guint info_text_resize_id;
GnomeCanvas *header_canvas, *table_canvas;
GnomeCanvasItem *header_item, *root;
GnomeCanvasItem *white_item;
GnomeCanvasItem *item;
gint length_threshold;
/*
* Configuration settings
*/
guint alternating_row_colors : 1;
guint horizontal_draw_grid : 1;
guint vertical_draw_grid : 1;
guint draw_focus : 1;
guint row_selection_active : 1;
guint horizontal_scrolling : 1;
guint scroll_direction : 4;
guint do_drag : 1;
guint uniform_row_height : 1;
guint search_col_set : 1;
guint always_search : 1;
ECursorMode cursor_mode;
gint drop_row;
ETreePath drop_path;
gint drop_col;
GnomeCanvasItem *drop_highlight;
gint last_drop_x;
gint last_drop_y;
gint last_drop_time;
GdkDragContext *last_drop_context;
gint hover_x;
gint hover_y;
gint drag_row;
ETreePath drag_path;
gint drag_col;
ETreeDragSourceSite *site;
GList *expanded_list;
};
static guint 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 void scroll_off (ETree *et);
static void scroll_on (ETree *et, guint scroll_direction);
static void hover_off (ETree *et);
static void hover_on (ETree *et, gint x, gint y);
static void context_destroyed (gpointer data, GObject *ctx);
G_DEFINE_TYPE (ETree, e_tree, GTK_TYPE_TABLE)
static void
et_disconnect_from_etta (ETree *et)
{
if (et->priv->table_model_change_id != 0)
g_signal_handler_disconnect (G_OBJECT (et->priv->etta),
et->priv->table_model_change_id);
if (et->priv->table_row_change_id != 0)
g_signal_handler_disconnect (G_OBJECT (et->priv->etta),
et->priv->table_row_change_id);
if (et->priv->table_cell_change_id != 0)
g_signal_handler_disconnect (G_OBJECT (et->priv->etta),
et->priv->table_cell_change_id);
if (et->priv->table_rows_delete_id != 0)
g_signal_handler_disconnect (G_OBJECT (et->priv->etta),
et->priv->table_rows_delete_id);
et->priv->table_model_change_id = 0;
et->priv->table_row_change_id = 0;
et->priv->table_cell_change_id = 0;
et->priv->table_rows_delete_id = 0;
}
static void
clear_current_search_col (ETree *et)
{
et->priv->search_col_set = FALSE;
}
static ETableCol *
current_search_col (ETree *et)
{
if (!et->priv->search_col_set) {
et->priv->current_search_col =
e_table_util_calculate_current_search_col (et->priv->header,
et->priv->full_header,
et->priv->sort_info,
et->priv->always_search);
et->priv->search_col_set = TRUE;
}
return et->priv->current_search_col;
}
static void
e_tree_state_change (ETree *et)
{
g_signal_emit (G_OBJECT (et), et_signals [STATE_CHANGE], 0);
}
static void
change_trigger (GtkObject *object, ETree *et)
{
e_tree_state_change (et);
}
static void
search_col_change_trigger (GtkObject *object, ETree *et)
{
clear_current_search_col (et);
e_tree_state_change (et);
}
static void
disconnect_header (ETree *e_tree)
{
if (e_tree->priv->header == NULL)
return;
if (e_tree->priv->structure_change_id)
g_signal_handler_disconnect (G_OBJECT (e_tree->priv->header),
e_tree->priv->structure_change_id);
if (e_tree->priv->expansion_change_id)
g_signal_handler_disconnect (G_OBJECT (e_tree->priv->header),
e_tree->priv->expansion_change_id);
if (e_tree->priv->sort_info) {
if (e_tree->priv->sort_info_change_id)
g_signal_handler_disconnect (G_OBJECT (e_tree->priv->sort_info),
e_tree->priv->sort_info_change_id);
if (e_tree->priv->group_info_change_id)
g_signal_handler_disconnect (G_OBJECT (e_tree->priv->sort_info),
e_tree->priv->group_info_change_id);
g_object_unref(e_tree->priv->sort_info);
}
g_object_unref(e_tree->priv->header);
e_tree->priv->header = NULL;
e_tree->priv->sort_info = NULL;
}
static void
connect_header (ETree *e_tree, ETableState *state)
{
GValue *val = g_new0 (GValue, 1);
if (e_tree->priv->header != NULL)
disconnect_header (e_tree);
e_tree->priv->header = e_table_state_to_header (GTK_WIDGET(e_tree), e_tree->priv->full_header, state);
e_tree->priv->structure_change_id =
g_signal_connect (G_OBJECT (e_tree->priv->header), "structure_change",
G_CALLBACK (search_col_change_trigger), e_tree);
e_tree->priv->expansion_change_id =
g_signal_connect (G_OBJECT (e_tree->priv->header), "expansion_change",
G_CALLBACK (change_trigger), e_tree);
if (state->sort_info) {
e_tree->priv->sort_info = e_table_sort_info_duplicate(state->sort_info);
e_table_sort_info_set_can_group (e_tree->priv->sort_info, FALSE);
e_tree->priv->sort_info_change_id =
g_signal_connect (G_OBJECT (e_tree->priv->sort_info), "sort_info_changed",
G_CALLBACK (search_col_change_trigger), e_tree);
e_tree->priv->group_info_change_id =
g_signal_connect (G_OBJECT (e_tree->priv->sort_info), "group_info_changed",
G_CALLBACK (search_col_change_trigger), e_tree);
} else
e_tree->priv->sort_info = NULL;
g_value_init (val, G_TYPE_OBJECT);
g_value_set_object (val, e_tree->priv->sort_info);
g_object_set_property (G_OBJECT(e_tree->priv->header), "sort_info", val);
g_free (val);
}
static void
et_dispose (GObject *object)
{
ETree *et = E_TREE (object);
if (et->priv) {
if (et->priv->search) {
if (et->priv->search_search_id)
g_signal_handler_disconnect (et->priv->search,
et->priv->search_search_id);
if (et->priv->search_accept_id)
g_signal_handler_disconnect (et->priv->search,
et->priv->search_accept_id);
g_object_unref (et->priv->search);
}
if (et->priv->reflow_idle_id)
g_source_remove(et->priv->reflow_idle_id);
et->priv->reflow_idle_id = 0;
scroll_off (et);
hover_off (et);
g_list_foreach (
et->priv->expanded_list,
(GFunc) g_free, NULL);
g_list_free (et->priv->expanded_list);
et_disconnect_from_etta (et);
g_object_unref (et->priv->etta);
g_object_unref (et->priv->model);
g_object_unref (et->priv->full_header);
disconnect_header (et);
g_object_unref (et->priv->selection);
if (et->priv->spec)
g_object_unref (et->priv->spec);
et->priv->spec = NULL;
if (et->priv->sorter)
g_object_unref (et->priv->sorter);
et->priv->sorter = NULL;
if (et->priv->header_canvas)
gtk_widget_destroy (GTK_WIDGET (et->priv->header_canvas));
et->priv->header_canvas = NULL;
if (et->priv->site)
e_tree_drag_source_unset (et);
if (et->priv->last_drop_context)
g_object_weak_unref (G_OBJECT(et->priv->last_drop_context), context_destroyed, et);
et->priv->last_drop_context = NULL;
if (et->priv->info_text)
gtk_object_destroy (GTK_OBJECT (et->priv->info_text));
et->priv->info_text = NULL;
et->priv->info_text_resize_id = 0;
gtk_widget_destroy (GTK_WIDGET (et->priv->table_canvas));
g_free(et->priv);
et->priv = NULL;
}
if (G_OBJECT_CLASS (e_tree_parent_class)->dispose)
G_OBJECT_CLASS (e_tree_parent_class)->dispose (object);
}
static void
et_unrealize (GtkWidget *widget)
{
scroll_off (E_TREE (widget));
hover_off (E_TREE (widget));
if (GTK_WIDGET_CLASS (e_tree_parent_class)->unrealize)
GTK_WIDGET_CLASS (e_tree_parent_class)->unrealize (widget);
}
typedef struct {
ETree *et;
gchar *string;
} SearchSearchStruct;
static gboolean
search_search_callback (ETreeModel *model, ETreePath path, gpointer data)
{
SearchSearchStruct *cb_data = data;
gconstpointer value;
ETableCol *col = current_search_col (cb_data->et);
value = e_tree_model_value_at (model, path, cb_data->et->priv->current_search_col->col_idx);
return col->search (value, cb_data->string);
}
static gboolean
et_search_search (ETableSearch *search, gchar *string, ETableSearchFlags flags, ETree *et)
{
ETreePath cursor;
ETreePath found;
SearchSearchStruct cb_data;
ETableCol *col = current_search_col (et);
if (col == NULL)
return FALSE;
cb_data.et = et;
cb_data.string = string;
cursor = e_tree_get_cursor (et);
if (cursor && (flags & E_TABLE_SEARCH_FLAGS_CHECK_CURSOR_FIRST)) {
gconstpointer value;
value = e_tree_model_value_at (et->priv->model, cursor, col->col_idx);
if (col->search (value, string)) {
return TRUE;
}
}
found = e_tree_model_node_find (et->priv->model, cursor, NULL, E_TREE_FIND_NEXT_FORWARD, search_search_callback, &cb_data);
if (found == NULL)
found = e_tree_model_node_find (et->priv->model, NULL, cursor, E_TREE_FIND_NEXT_FORWARD, search_search_callback, &cb_data);
if (found && found != cursor) {
gint model_row;
e_tree_table_adapter_show_node (et->priv->etta, found);
model_row = e_tree_table_adapter_row_of_node (et->priv->etta, found);
cursor = found;
e_selection_model_select_as_key_press(E_SELECTION_MODEL (et->priv->selection), model_row, col->col_idx, GDK_CONTROL_MASK);
return TRUE;
} else if (cursor && !(flags & E_TABLE_SEARCH_FLAGS_CHECK_CURSOR_FIRST)) {
gconstpointer value;
value = e_tree_model_value_at (et->priv->model, cursor, col->col_idx);
return col->search (value, string);
} else
return FALSE;
}
static void
et_search_accept (ETableSearch *search, ETree *et)
{
ETableCol *col = current_search_col (et);
gint cursor;
if (col == NULL)
return;
g_object_get(et->priv->selection,
"cursor_row", &cursor,
NULL);
e_selection_model_select_as_key_press(E_SELECTION_MODEL (et->priv->selection), cursor, col->col_idx, 0);
}
static void
e_tree_init (ETree *e_tree)
{
GtkTable *gtk_table = GTK_TABLE (e_tree);
GTK_WIDGET_SET_FLAGS (GTK_WIDGET (e_tree), GTK_CAN_FOCUS);
gtk_table->homogeneous = FALSE;
e_tree->priv = g_new(ETreePriv, 1);
e_tree->priv->model = NULL;
e_tree->priv->etta = NULL;
e_tree->priv->full_header = NULL;
e_tree->priv->header = NULL;
e_tree->priv->structure_change_id = 0;
e_tree->priv->expansion_change_id = 0;
e_tree->priv->sort_info_change_id = 0;
e_tree->priv->group_info_change_id = 0;
e_tree->priv->sort_info = NULL;
e_tree->priv->sorter = NULL;
e_tree->priv->reflow_idle_id = 0;
e_tree->priv->scroll_idle_id = 0;
e_tree->priv->hover_idle_id = 0;
e_tree->priv->table_model_change_id = 0;
e_tree->priv->table_row_change_id = 0;
e_tree->priv->table_cell_change_id = 0;
e_tree->priv->table_rows_delete_id = 0;
e_tree->priv->alternating_row_colors = 1;
e_tree->priv->horizontal_draw_grid = 1;
e_tree->priv->vertical_draw_grid = 1;
e_tree->priv->draw_focus = 1;
e_tree->priv->cursor_mode = E_CURSOR_SIMPLE;
e_tree->priv->length_threshold = 200;
e_tree->priv->uniform_row_height = FALSE;
e_tree->priv->row_selection_active = FALSE;
e_tree->priv->horizontal_scrolling = FALSE;
e_tree->priv->scroll_direction = 0;
e_tree->priv->drop_row = -1;
e_tree->priv->drop_path = NULL;
e_tree->priv->drop_col = -1;
e_tree->priv->drop_highlight = NULL;
e_tree->priv->last_drop_x = 0;
e_tree->priv->last_drop_y = 0;
e_tree->priv->last_drop_time = 0;
e_tree->priv->last_drop_context = NULL;
e_tree->priv->hover_x = 0;
e_tree->priv->hover_y = 0;
e_tree->priv->drag_row = -1;
e_tree->priv->drag_path = NULL;
e_tree->priv->drag_col = -1;
e_tree->priv->expanded_list = NULL;
e_tree->priv->site = NULL;
e_tree->priv->do_drag = FALSE;
#ifdef E_TREE_USE_TREE_SELECTION
e_tree->priv->selection = E_SELECTION_MODEL(e_tree_selection_model_new());
#else
e_tree->priv->selection = E_SELECTION_MODEL(e_table_selection_model_new());
#endif
e_tree->priv->spec = NULL;
e_tree->priv->info_text = NULL;
e_tree->priv->info_text_resize_id = 0;
e_tree->priv->header_canvas = NULL;
e_tree->priv->table_canvas = NULL;
e_tree->priv->header_item = NULL;
e_tree->priv->root = NULL;
e_tree->priv->white_item = NULL;
e_tree->priv->item = NULL;
e_tree->priv->search = e_table_search_new();
e_tree->priv->search_search_id =
g_signal_connect (G_OBJECT (e_tree->priv->search), "search",
G_CALLBACK (et_search_search), e_tree);
e_tree->priv->search_accept_id =
g_signal_connect (G_OBJECT (e_tree->priv->search), "accept",
G_CALLBACK (et_search_accept), e_tree);
e_tree->priv->current_search_col = NULL;
e_tree->priv->search_col_set = FALSE;
e_tree->priv->always_search = g_getenv ("GAL_ALWAYS_SEARCH") ? TRUE : FALSE;
}
/* 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->priv->table_canvas));
}
/* Focus handler for the ETree */
static gint
et_focus (GtkWidget *container, GtkDirectionType direction)
{
ETree *e_tree;
e_tree = E_TREE (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_tree->priv->table_canvas), direction);
}
static void
set_header_canvas_width (ETree *e_tree)
{
double oldwidth, oldheight, width;
if (!(e_tree->priv->header_item && e_tree->priv->header_canvas && e_tree->priv->table_canvas))
return;
gnome_canvas_get_scroll_region (GNOME_CANVAS (e_tree->priv->table_canvas),
NULL, NULL, &width, NULL);
gnome_canvas_get_scroll_region (GNOME_CANVAS (e_tree->priv->header_canvas),
NULL, NULL, &oldwidth, &oldheight);
if (oldwidth != width ||
oldheight != E_TABLE_HEADER_ITEM (e_tree->priv->header_item)->height - 1)
gnome_canvas_set_scroll_region (
GNOME_CANVAS (e_tree->priv->header_canvas),
0, 0, width, /* COLUMN_HEADER_HEIGHT - 1 */
E_TABLE_HEADER_ITEM (e_tree->priv->header_item)->height - 1);
}
static void
header_canvas_size_allocate (GtkWidget *widget, GtkAllocation *alloc, ETree *e_tree)
{
set_header_canvas_width (e_tree);
/* 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->priv->header_canvas)->allocation.height !=
E_TABLE_HEADER_ITEM (e_tree->priv->header_item)->height)
gtk_widget_set_size_request (GTK_WIDGET (e_tree->priv->header_canvas), -1,
E_TABLE_HEADER_ITEM (e_tree->priv->header_item)->height);
}
static void
e_tree_setup_header (ETree *e_tree)
{
gchar *pointer;
e_tree->priv->header_canvas = GNOME_CANVAS (e_canvas_new ());
GTK_WIDGET_UNSET_FLAGS (e_tree->priv->header_canvas, GTK_CAN_FOCUS);
gtk_widget_show (GTK_WIDGET (e_tree->priv->header_canvas));
pointer = g_strdup_printf("%p", (gpointer) e_tree);
e_tree->priv->header_item = gnome_canvas_item_new (
gnome_canvas_root (e_tree->priv->header_canvas),
e_table_header_item_get_type (),
"ETableHeader", e_tree->priv->header,
"full_header", e_tree->priv->full_header,
"sort_info", e_tree->priv->sort_info,
"dnd_code", pointer,
"tree", e_tree,
NULL);
g_free(pointer);
g_signal_connect (
e_tree->priv->header_canvas, "size_allocate",
G_CALLBACK (header_canvas_size_allocate), e_tree);
gtk_widget_set_size_request (
GTK_WIDGET (e_tree->priv->header_canvas), -1,
E_TABLE_HEADER_ITEM (e_tree->priv->header_item)->height);
}
static gboolean
tree_canvas_reflow_idle (ETree *e_tree)
{
gdouble height, width;
gdouble oldheight, oldwidth;
GtkAllocation *alloc = &(GTK_WIDGET (e_tree->priv->table_canvas)->allocation);
g_object_get (e_tree->priv->item,
"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_tree->priv->table_canvas),
NULL, NULL, &oldwidth, &oldheight);
if (oldwidth != width - 1 ||
oldheight != height - 1) {
gnome_canvas_set_scroll_region (GNOME_CANVAS (e_tree->priv->table_canvas),
0, 0, width - 1, height - 1);
set_header_canvas_width (e_tree);
}
e_tree->priv->reflow_idle_id = 0;
return FALSE;
}
static void
tree_canvas_size_allocate (GtkWidget *widget, GtkAllocation *alloc,
ETree *e_tree)
{
gdouble width;
gdouble height;
GtkAdjustment *adj = GTK_LAYOUT(e_tree->priv->table_canvas)->vadjustment;
ETreePath path = e_tree_get_cursor (e_tree);
gint x, y, w, h;
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_tree->priv->item,
"height", &height,
NULL);
height = MAX ((gint)height, alloc->height);
g_object_set (e_tree->priv->item,
"width", width,
NULL);
g_object_set_property (G_OBJECT (e_tree->priv->header), "width", val);
g_free (val);
if (e_tree->priv->reflow_idle_id)
g_source_remove(e_tree->priv->reflow_idle_id);
tree_canvas_reflow_idle(e_tree);
x = y = w = h = 0;
if (path) {
gint row = e_tree_row_of_node(e_tree, path);
gint col = 0;
if (row >= 0)
e_table_item_get_cell_geometry (E_TABLE_ITEM (e_tree->priv->item),
&row, &col, &x, &y, &w, &h);
}
if (y < adj->value || y + h > adj->value + adj->page_size)
gtk_adjustment_set_value(adj, CLAMP(y - adj->page_size / 2, adj->lower, adj->upper - adj->page_size));
}
static void
tree_canvas_reflow (GnomeCanvas *canvas, ETree *e_tree)
{
if (!e_tree->priv->reflow_idle_id)
e_tree->priv->reflow_idle_id = g_idle_add_full (400, (GSourceFunc) tree_canvas_reflow_idle, e_tree, NULL);
}
static void
item_cursor_change (ETableItem *eti, gint row, ETree *et)
{
ETreePath path = e_tree_table_adapter_node_at_row(et->priv->etta, row);
g_signal_emit (et,
et_signals [CURSOR_CHANGE], 0,
row, path);
}
static void
item_cursor_activated (ETableItem *eti, gint row, ETree *et)
{
ETreePath path = e_tree_table_adapter_node_at_row(et->priv->etta, row);
g_signal_emit (et,
et_signals [CURSOR_ACTIVATED], 0,
row, path);
d(g_print("%s: Emitted CURSOR_ACTIVATED signal on row: %d and path: 0x%p\n", __FUNCTION__, row, path));
}
static void
item_double_click (ETableItem *eti, gint row, gint col, GdkEvent *event, ETree *et)
{
ETreePath path = e_tree_table_adapter_node_at_row(et->priv->etta, row);
g_signal_emit (et,
et_signals [DOUBLE_CLICK], 0,
row, path, col, event);
}
static gint
item_right_click (ETableItem *eti, gint row, gint col, GdkEvent *event, ETree *et)
{
gint return_val = 0;
ETreePath path = e_tree_table_adapter_node_at_row(et->priv->etta, row);
g_signal_emit (et,
et_signals [RIGHT_CLICK], 0,
row, path, col, event, &return_val);
return return_val;
}
static gint
item_click (ETableItem *eti, gint row, gint col, GdkEvent *event, ETree *et)
{
gint return_val = 0;
ETreePath path = e_tree_table_adapter_node_at_row(et->priv->etta, row);
g_signal_emit (et,
et_signals [CLICK], 0,
row, path, col, event, &return_val);
return return_val;
}
static gint
item_key_press (ETableItem *eti, gint row, gint col, GdkEvent *event, ETree *et)
{
gint return_val = 0;
GdkEventKey *key = (GdkEventKey *) event;
ETreePath path;
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->priv->table_canvas));
y = CLAMP(vadj->value + (2 * vadj->page_size - 50), 0, vadj->upper);
y -= vadj->value;
e_tree_get_cell_at (et, 30, y, &row_local, &col_local);
if (row_local == -1)
row_local = e_table_model_row_count (E_TABLE_MODEL(et->priv->etta)) - 1;
row_local = e_tree_view_to_model_row (et, row_local);
col_local = e_selection_model_cursor_col (E_SELECTION_MODEL (et->priv->selection));
e_selection_model_select_as_key_press (E_SELECTION_MODEL (et->priv->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->priv->table_canvas));
y = CLAMP(vadj->value - (vadj->page_size - 50), 0, vadj->upper);
y -= vadj->value;
e_tree_get_cell_at (et, 30, y, &row_local, &col_local);
if (row_local == -1)
row_local = e_table_model_row_count (E_TABLE_MODEL(et->priv->etta)) - 1;
row_local = e_tree_view_to_model_row (et, row_local);
col_local = e_selection_model_cursor_col (E_SELECTION_MODEL (et->priv->selection));
e_selection_model_select_as_key_press (E_SELECTION_MODEL (et->priv->selection), row_local, col_local, key->state);
return_val = 1;
break;
case GDK_plus:
case GDK_KP_Add:
case GDK_Right:
case GDK_KP_Right:
/* Only allow if the Shift modifier is used -- eg. Ctrl-Equal shouldn't be handled. */
if ((key->state & (GDK_SHIFT_MASK | GDK_LOCK_MASK | GDK_MOD1_MASK)) != GDK_SHIFT_MASK)
break;
if (row != -1) {
path = e_tree_table_adapter_node_at_row(et->priv->etta, row);
if (path)
e_tree_table_adapter_node_set_expanded (et->priv->etta, path, TRUE);
}
return_val = 1;
break;
case GDK_underscore:
case GDK_KP_Subtract:
case GDK_Left:
case GDK_KP_Left:
/* Only allow if the Shift modifier is used -- eg. Ctrl-Minus shouldn't be handled. */
if ((key->state & (GDK_SHIFT_MASK | GDK_LOCK_MASK | GDK_MOD1_MASK)) != GDK_SHIFT_MASK)
break;
if (row != -1) {
path = e_tree_table_adapter_node_at_row(et->priv->etta, row);
if (path)
e_tree_table_adapter_node_set_expanded (et->priv->etta, path, FALSE);
}
return_val = 1;
break;
case GDK_BackSpace:
if (e_table_search_backspace (et->priv->search))
return TRUE;
/* Fallthrough */
default:
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->priv->search, key->keyval);
}
path = e_tree_table_adapter_node_at_row(et->priv->etta, row);
g_signal_emit (et,
et_signals [KEY_PRESS], 0,
row, path, col, event, &return_val);
break;
}
return return_val;
}
static gint
item_start_drag (ETableItem *eti, gint row, gint col, GdkEvent *event, ETree *et)
{
ETreePath path;
gint return_val = 0;
path = e_tree_table_adapter_node_at_row(et->priv->etta, row);
g_signal_emit (et,
et_signals [START_DRAG], 0,
row, path, col, event, &return_val);
return return_val;
}
static void
et_selection_model_selection_changed (ETableSelectionModel *etsm, ETree *et)
{
g_signal_emit (et,
et_signals [SELECTION_CHANGE], 0);
}
static void
et_selection_model_selection_row_changed (ETableSelectionModel *etsm, gint row, ETree *et)
{
g_signal_emit (et,
et_signals [SELECTION_CHANGE], 0);
}
static void
et_build_item (ETree *et)
{
et->priv->item = gnome_canvas_item_new(GNOME_CANVAS_GROUP (gnome_canvas_root(et->priv->table_canvas)),
e_table_item_get_type(),
"ETableHeader", et->priv->header,
"ETableModel", et->priv->etta,
"selection_model", et->priv->selection,
"alternating_row_colors", et->priv->alternating_row_colors,
"horizontal_draw_grid", et->priv->horizontal_draw_grid,
"vertical_draw_grid", et->priv->vertical_draw_grid,
"drawfocus", et->priv->draw_focus,
"cursor_mode", et->priv->cursor_mode,
"length_threshold", et->priv->length_threshold,
"uniform_row_height", et->priv->uniform_row_height,
NULL);
g_signal_connect (et->priv->item, "cursor_change",
G_CALLBACK (item_cursor_change), et);
g_signal_connect (et->priv->item, "cursor_activated",
G_CALLBACK (item_cursor_activated), et);
g_signal_connect (et->priv->item, "double_click",
G_CALLBACK (item_double_click), et);
g_signal_connect (et->priv->item, "right_click",
G_CALLBACK (item_right_click), et);
g_signal_connect (et->priv->item, "click",
G_CALLBACK (item_click), et);
g_signal_connect (et->priv->item, "key_press",
G_CALLBACK (item_key_press), et);
g_signal_connect (et->priv->item, "start_drag",
G_CALLBACK (item_start_drag), et);
}
static void
et_canvas_style_set (GtkWidget *widget, GtkStyle *prev_style)
{
gnome_canvas_item_set(
E_TREE(widget)->priv->white_item,
"fill_color_gdk", &widget->style->base[GTK_STATE_NORMAL],
NULL);
}
static gint
white_item_event (GnomeCanvasItem *white_item, GdkEvent *event, ETree *e_tree)
{
gint return_val = 0;
g_signal_emit (e_tree,
et_signals [WHITE_SPACE_EVENT], 0,
event, &return_val);
return return_val;
}
static gint
et_canvas_root_event (GnomeCanvasItem *root, GdkEvent *event, ETree *e_tree)
{
switch (event->type) {
case GDK_BUTTON_PRESS:
case GDK_2BUTTON_PRESS:
case GDK_BUTTON_RELEASE:
if (event->button.button != 4 && event->button.button != 5) {
if (GTK_WIDGET_HAS_FOCUS(root->canvas)) {
GnomeCanvasItem *item = GNOME_CANVAS(root->canvas)->focused_item;
if (E_IS_TABLE_ITEM(item)) {
e_table_item_leave_edit_(E_TABLE_ITEM(item));
return TRUE;
}
}
}
break;
default:
break;
}
return FALSE;
}
/* Handler for focus events in the table_canvas; we have to repaint ourselves
* and give the focus to some ETableItem.
*/
static gint
table_canvas_focus_event_cb (GtkWidget *widget, GdkEventFocus *event, gpointer data)
{
GnomeCanvas *canvas;
ETree *tree;
gtk_widget_queue_draw (widget);
if (!event->in)
return TRUE;
canvas = GNOME_CANVAS (widget);
tree = E_TREE (data);
if (!canvas->focused_item || (e_selection_model_cursor_row (tree->priv->selection) == -1)) {
e_table_item_set_cursor (E_TABLE_ITEM (tree->priv->item), 0, 0);
gnome_canvas_item_grab_focus (tree->priv->item);
}
return TRUE;
}
static void
e_tree_setup_table (ETree *e_tree)
{
e_tree->priv->table_canvas = GNOME_CANVAS (e_canvas_new ());
g_signal_connect (
e_tree->priv->table_canvas, "size_allocate",
G_CALLBACK (tree_canvas_size_allocate), e_tree);
g_signal_connect (
e_tree->priv->table_canvas, "focus_in_event",
G_CALLBACK (table_canvas_focus_event_cb), e_tree);
g_signal_connect (
e_tree->priv->table_canvas, "focus_out_event",
G_CALLBACK (table_canvas_focus_event_cb), e_tree);
g_signal_connect (
e_tree->priv->table_canvas, "drag_begin",
G_CALLBACK (et_drag_begin), e_tree);
g_signal_connect (
e_tree->priv->table_canvas, "drag_end",
G_CALLBACK (et_drag_end), e_tree);
g_signal_connect (
e_tree->priv->table_canvas, "drag_data_get",
G_CALLBACK (et_drag_data_get), e_tree);
g_signal_connect (
e_tree->priv->table_canvas, "drag_data_delete",
G_CALLBACK (et_drag_data_delete), e_tree);
g_signal_connect (
e_tree, "drag_motion",
G_CALLBACK (et_drag_motion), e_tree);
g_signal_connect (
e_tree, "drag_leave",
G_CALLBACK (et_drag_leave), e_tree);
g_signal_connect (
e_tree, "drag_drop",
G_CALLBACK (et_drag_drop), e_tree);
g_signal_connect (
e_tree, "drag_data_received",
G_CALLBACK (et_drag_data_received), e_tree);
g_signal_connect (e_tree->priv->table_canvas, "reflow",
G_CALLBACK (tree_canvas_reflow), e_tree);
gtk_widget_show (GTK_WIDGET (e_tree->priv->table_canvas));
e_tree->priv->white_item = gnome_canvas_item_new(
gnome_canvas_root(e_tree->priv->table_canvas),
e_canvas_background_get_type(),
"fill_color_gdk", &GTK_WIDGET(e_tree->priv->table_canvas)->style->base[GTK_STATE_NORMAL],
NULL);
g_signal_connect (e_tree->priv->white_item, "event",
G_CALLBACK (white_item_event), e_tree);
g_signal_connect (
gnome_canvas_root (e_tree->priv->table_canvas), "event",
G_CALLBACK(et_canvas_root_event), e_tree);
et_build_item(e_tree);
}
/**
* e_tree_set_search_column:
* @e_tree: #ETree object that will be modified
* @col: Column index to use for searches
*
* This routine sets the current search column to be used for keypress
* searches of the #ETree. If -1 is passed in for column, the current
* search column is cleared.
*/
void
e_tree_set_search_column (ETree *e_tree, gint col)
{
if (col == -1) {
clear_current_search_col (e_tree);
return;
}
e_tree->priv->search_col_set = TRUE;
e_tree->priv->current_search_col = e_table_header_get_column (e_tree->priv->full_header, col);
}
void
e_tree_set_state_object(ETree *e_tree, ETableState *state)
{
GValue *val = g_new0 (GValue, 1);
g_value_init (val, G_TYPE_DOUBLE);
connect_header (e_tree, state);
g_value_set_double (val, (double) (GTK_WIDGET(e_tree->priv->table_canvas)->allocation.width));
g_object_set_property (G_OBJECT (e_tree->priv->header), "width", val);
g_free (val);
if (e_tree->priv->header_item)
g_object_set(e_tree->priv->header_item,
"ETableHeader", e_tree->priv->header,
"sort_info", e_tree->priv->sort_info,
NULL);
if (e_tree->priv->item)
g_object_set(e_tree->priv->item,
"ETableHeader", e_tree->priv->header,
NULL);
if (e_tree->priv->etta)
e_tree_table_adapter_set_sort_info (e_tree->priv->etta, e_tree->priv->sort_info);
e_tree_state_change (e_tree);
}
/**
* 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);
g_object_unref(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);
g_object_unref(state);
}
/**
* e_tree_get_state_object:
* @e_tree: #ETree object to act on
*
* Builds an #ETableState corresponding to the current state of the
* #ETree.
*
* Return value:
* The %ETableState object generated.
**/
ETableState *
e_tree_get_state_object (ETree *e_tree)
{
ETableState *state;
gint full_col_count;
gint i, j;
state = e_table_state_new();
state->sort_info = e_tree->priv->sort_info;
if (state->sort_info)
g_object_ref(state->sort_info);
state->col_count = e_table_header_count (e_tree->priv->header);
full_col_count = e_table_header_count (e_tree->priv->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->priv->header, i);
state->columns[i] = -1;
for (j = 0; j < full_col_count; j++) {
if (col->col_idx == e_table_header_index(e_tree->priv->full_header, j)) {
state->columns[i] = j;
break;
}
}
state->expansions[i] = col->expansion;
}
return state;
}
/**
* e_tree_get_state:
* @e_tree: The #ETree 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 #ETree.
**/
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);
g_object_unref(state);
return string;
}
/**
* e_tree_save_state:
* @e_tree: The #ETree to act on
* @filename: name of the file to save to
*
* 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);
g_object_unref(state);
}
/**
* e_tree_get_spec:
* @e_tree: The #ETree to query
*
* Returns the specification object.
*
* Return value:
**/
ETableSpecification *
e_tree_get_spec (ETree *e_tree)
{
return e_tree->priv->spec;
}
static void
et_table_model_changed (ETableModel *model, ETree *et)
{
if (et->priv->horizontal_scrolling)
e_table_header_update_horizontal(et->priv->header);
}
static void
et_table_row_changed (ETableModel *table_model, gint row, ETree *et)
{
et_table_model_changed (table_model, et);
}
static void
et_table_cell_changed (ETableModel *table_model, gint view_col, gint row, ETree *et)
{
et_table_model_changed (table_model, et);
}
static void
et_table_rows_deleted (ETableModel *table_model, gint row, gint count, ETree *et)
{
ETreePath * node, * prev_node;
/* If the cursor is still valid after this deletion, we're done */
if (e_selection_model_cursor_row (et->priv->selection) >= 0
|| row == 0)
return;
prev_node = e_tree_node_at_row (et, row-1);
node = e_tree_get_cursor (et);
/* Check if the cursor is a child of the node directly before the
* deleted region (implying that an expander was collapsed with
* the cursor inside it) */
while (node) {
node = e_tree_model_node_get_parent (et->priv->model, node);
if (node == prev_node) {
/* Set the cursor to the still-visible parent */
e_tree_set_cursor (et, prev_node);
return;
}
}
}
static void
et_connect_to_etta (ETree *et)
{
et->priv->table_model_change_id = g_signal_connect (et->priv->etta, "model_changed",
G_CALLBACK (et_table_model_changed), et);
et->priv->table_row_change_id = g_signal_connect (et->priv->etta, "model_row_changed",
G_CALLBACK (et_table_row_changed), et);
et->priv->table_cell_change_id = g_signal_connect (et->priv->etta, "model_cell_changed",
G_CALLBACK (et_table_cell_changed), et);
et->priv->table_rows_delete_id = g_signal_connect (et->priv->etta, "model_rows_deleted",
G_CALLBACK (et_table_rows_deleted), et);
}
static gboolean
et_real_construct (ETree *e_tree, ETreeModel *etm, ETableExtras *ete,
ETableSpecification *specification, ETableState *state)
{
gint row = 0;
if (ete)
g_object_ref(ete);
else
ete = e_table_extras_new();
e_tree->priv->alternating_row_colors = specification->alternating_row_colors;
e_tree->priv->horizontal_draw_grid = specification->horizontal_draw_grid;
e_tree->priv->vertical_draw_grid = specification->vertical_draw_grid;
e_tree->priv->draw_focus = specification->draw_focus;
e_tree->priv->cursor_mode = specification->cursor_mode;
e_tree->priv->full_header = e_table_spec_to_full_header(specification, ete);
connect_header (e_tree, state);
e_tree->priv->horizontal_scrolling = specification->horizontal_scrolling;
e_tree->priv->model = etm;
g_object_ref (etm);
e_tree->priv->etta = E_TREE_TABLE_ADAPTER(e_tree_table_adapter_new(e_tree->priv->model, e_tree->priv->sort_info, e_tree->priv->full_header));
et_connect_to_etta (e_tree);
gtk_widget_push_colormap (gdk_rgb_get_colormap ());
e_tree->priv->sorter = e_sorter_new();
g_object_set (e_tree->priv->selection,
"sorter", e_tree->priv->sorter,
#ifdef E_TREE_USE_TREE_SELECTION
"model", e_tree->priv->model,
"etta", e_tree->priv->etta,
#else
"model", e_tree->priv->etta,
#endif
"selection_mode", specification->selection_mode,
"cursor_mode", specification->cursor_mode,
NULL);
g_signal_connect(e_tree->priv->selection, "selection_changed",
G_CALLBACK (et_selection_model_selection_changed), e_tree);
g_signal_connect(e_tree->priv->selection, "selection_row_changed",
G_CALLBACK (et_selection_model_selection_row_changed), 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->priv->table_canvas))->step_increment = 20;
gtk_adjustment_changed(gtk_layout_get_vadjustment (GTK_LAYOUT (e_tree->priv->table_canvas)));
gtk_layout_get_hadjustment (GTK_LAYOUT (e_tree->priv->table_canvas))->step_increment = 20;
gtk_adjustment_changed(gtk_layout_get_hadjustment (GTK_LAYOUT (e_tree->priv->table_canvas)));
if (!specification->no_headers) {
/*
* The header
*/
gtk_table_attach (GTK_TABLE (e_tree), GTK_WIDGET (e_tree->priv->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->priv->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_tree != NULL;
}
/**
* e_tree_construct:
* @e_tree: The newly created #ETree 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_tree_new() for use by
* subclasses or language bindings. See e_tree_new() for details.
*
* Return value: %TRUE on success, %FALSE if an error occurred
**/
gboolean
e_tree_construct (ETree *e_tree, ETreeModel *etm, ETableExtras *ete,
const gchar *spec_str, const gchar *state_str)
{
ETableSpecification *specification;
ETableState *state;
g_return_val_if_fail(e_tree != NULL, FALSE);
g_return_val_if_fail(E_IS_TREE(e_tree), FALSE);
g_return_val_if_fail(etm != NULL, FALSE);
g_return_val_if_fail(E_IS_TREE_MODEL(etm), FALSE);
g_return_val_if_fail(ete == NULL || E_IS_TABLE_EXTRAS(ete), FALSE);
g_return_val_if_fail(spec_str != NULL, FALSE);
specification = e_table_specification_new();
if (!e_table_specification_load_from_string(specification, spec_str)) {
g_object_unref (specification);
return FALSE;
}
if (state_str) {
state = e_table_state_new();
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);
}
if (!et_real_construct (e_tree, etm, ete, specification, state)) {
g_object_unref (specification);
g_object_unref (state);
return FALSE;
}
e_tree->priv->spec = specification;
g_object_unref (state);
return TRUE;
}
/**
* e_tree_construct_from_spec_file:
* @e_tree: The newly created #ETree object.
* @etm: The model for this tree
* @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_tree_new_from_spec_file()
* for use by subclasses or language bindings. See
* e_tree_new_from_spec_file() for details.
*
* Return value: %TRUE on success, %FALSE if an error occurred
**/
gboolean
e_tree_construct_from_spec_file (ETree *e_tree, ETreeModel *etm, ETableExtras *ete,
const gchar *spec_fn, const gchar *state_fn)
{
ETableSpecification *specification;
ETableState *state;
g_return_val_if_fail(e_tree != NULL, FALSE);
g_return_val_if_fail(E_IS_TREE(e_tree), FALSE);
g_return_val_if_fail(etm != NULL, FALSE);
g_return_val_if_fail(E_IS_TREE_MODEL(etm), FALSE);
g_return_val_if_fail(ete == NULL || E_IS_TABLE_EXTRAS(ete), FALSE);
g_return_val_if_fail(spec_fn != NULL, FALSE);
specification = e_table_specification_new();
if (!e_table_specification_load_from_file(specification, spec_fn)) {
g_object_unref(specification);
return FALSE;
}
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);
}
if (!et_real_construct (e_tree, etm, ete, specification, state)) {
g_object_unref (specification);
g_object_unref (state);
return FALSE;
}
e_tree->priv->spec = specification;
g_object_unref(state);
return TRUE;
}
/**
* e_tree_new:
* @etm: The model for this tree
* @ete: An optional #ETableExtras (%NULL is valid.)
* @spec: The spec
* @state: An optional state (%NULL is valid.)
*
* This function creates an #ETree from the given parameters. The
* #ETreeModel is a tree 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 #ETree or %NULL if there's an error.
**/
GtkWidget *
e_tree_new (ETreeModel *etm, ETableExtras *ete, const gchar *spec, const gchar *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 = g_object_new (E_TREE_TYPE, NULL);
if (!e_tree_construct (e_tree, etm, ete, spec, state)) {
g_object_unref (e_tree);
return NULL;
}
return (GtkWidget *) e_tree;
}
/**
* e_tree_new_from_spec_file:
* @etm: The model for this tree.
* @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_tree_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_tree_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 #ETree or %NULL if there's an error.
**/
GtkWidget *
e_tree_new_from_spec_file (ETreeModel *etm, ETableExtras *ete, const gchar *spec_fn, const gchar *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 = g_object_new (E_TREE_TYPE, NULL);
if (!e_tree_construct_from_spec_file (e_tree, etm, ete, spec_fn, state_fn)) {
g_object_unref (e_tree);
return NULL;
}
return (GtkWidget *) e_tree;
}
void
e_tree_set_cursor (ETree *e_tree, ETreePath path)
{
#ifndef E_TREE_USE_TREE_SELECTION
gint row;
#endif
g_return_if_fail(e_tree != NULL);
g_return_if_fail(E_IS_TREE(e_tree));
g_return_if_fail(path != NULL);
#ifdef E_TREE_USE_TREE_SELECTION
e_tree_selection_model_select_single_path (E_TREE_SELECTION_MODEL(e_tree->priv->selection), path);
e_tree_selection_model_change_cursor (E_TREE_SELECTION_MODEL(e_tree->priv->selection), path);
#else
row = e_tree_table_adapter_row_of_node(E_TREE_TABLE_ADAPTER(e_tree->priv->etta), path);
if (row == -1)
return;
g_object_set(e_tree->priv->selection,
"cursor_row", row,
NULL);
#endif
}
ETreePath
e_tree_get_cursor (ETree *e_tree)
{
#ifdef E_TREE_USE_TREE_SELECTION
return e_tree_selection_model_get_cursor (E_TREE_SELECTION_MODEL(e_tree->priv->selection));
#else
gint row;
ETreePath path;
g_return_val_if_fail(e_tree != NULL, NULL);
g_return_val_if_fail(E_IS_TREE(e_tree), NULL);
g_object_get(e_tree->priv->selection,
"cursor_row", &row,
NULL);
if (row == -1)
return NULL;
path = e_tree_table_adapter_node_at_row(E_TREE_TABLE_ADAPTER(e_tree->priv->etta), row);
return path;
#endif
}
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_tree->priv->selection,
callback,
closure);
}
#ifdef E_TREE_USE_TREE_SELECTION
void
e_tree_selected_path_foreach (ETree *e_tree,
ETreeForeachFunc callback,
gpointer closure)
{
g_return_if_fail(e_tree != NULL);
g_return_if_fail(E_IS_TREE(e_tree));
e_tree_selection_model_foreach(E_TREE_SELECTION_MODEL (e_tree->priv->selection),
callback,
closure);
}
/* Standard functions */
static void
et_foreach_recurse (ETreeModel *model,
ETreePath path,
ETreeForeachFunc callback,
gpointer closure)
{
ETreePath child;
callback(path, closure);
child = e_tree_model_node_get_first_child(E_TREE_MODEL(model), path);
for (; child; child = e_tree_model_node_get_next(E_TREE_MODEL(model), child))
if (child)
et_foreach_recurse (model, child, callback, closure);
}
void
e_tree_path_foreach (ETree *e_tree,
ETreeForeachFunc callback,
gpointer closure)
{
ETreePath root;
g_return_if_fail(e_tree != NULL);
g_return_if_fail(E_IS_TREE(e_tree));
root = e_tree_model_get_root (e_tree->priv->model);
if (root)
et_foreach_recurse (e_tree->priv->model,
root,
callback,
closure);
}
#endif
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->priv->item));
}
static void
et_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
ETree *etree = E_TREE (object);
switch (prop_id) {
case PROP_ETTA:
g_value_set_object (value, etree->priv->etta);
break;
case PROP_UNIFORM_ROW_HEIGHT:
g_value_set_boolean (value, etree->priv->uniform_row_height);
break;
case PROP_ALWAYS_SEARCH:
g_value_set_boolean (value, etree->priv->always_search);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
typedef struct {
gchar *arg;
gboolean setting;
} bool_closure;
static void
et_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
ETree *etree = E_TREE (object);
switch (prop_id) {
case PROP_LENGTH_THRESHOLD:
etree->priv->length_threshold = g_value_get_int (value);
if (etree->priv->item) {
gnome_canvas_item_set (GNOME_CANVAS_ITEM(etree->priv->item),
"length_threshold", etree->priv->length_threshold,
NULL);
}
break;
case PROP_HORIZONTAL_DRAW_GRID:
etree->priv->horizontal_draw_grid = g_value_get_boolean (value);
if (etree->priv->item) {
gnome_canvas_item_set (GNOME_CANVAS_ITEM(etree->priv->item),
"horizontal_draw_grid", etree->priv->horizontal_draw_grid,
NULL);
}
break;
case PROP_VERTICAL_DRAW_GRID:
etree->priv->vertical_draw_grid = g_value_get_boolean (value);
if (etree->priv->item) {
gnome_canvas_item_set (GNOME_CANVAS_ITEM(etree->priv->item),
"vertical_draw_grid", etree->priv->vertical_draw_grid,
NULL);
}
break;
case PROP_DRAW_FOCUS:
etree->priv->draw_focus = g_value_get_boolean (value);
if (etree->priv->item) {
gnome_canvas_item_set (GNOME_CANVAS_ITEM(etree->priv->item),
"drawfocus", etree->priv->draw_focus,
NULL);
}
break;
case PROP_UNIFORM_ROW_HEIGHT:
etree->priv->uniform_row_height = g_value_get_boolean (value);
if (etree->priv->item) {
gnome_canvas_item_set (GNOME_CANVAS_ITEM(etree->priv->item),
"uniform_row_height", etree->priv->uniform_row_height,
NULL);
}
break;
case PROP_ALWAYS_SEARCH:
if (etree->priv->always_search == g_value_get_boolean (value))
return;
etree->priv->always_search = g_value_get_boolean (value);
clear_current_search_col (etree);
break;
}
}
static void
set_scroll_adjustments (ETree *tree,
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 (tree->priv) {
gtk_layout_set_hadjustment (GTK_LAYOUT(tree->priv->table_canvas),
hadjustment);
gtk_layout_set_vadjustment (GTK_LAYOUT(tree->priv->table_canvas),
vadjustment);
if (tree->priv->header_canvas != NULL)
gtk_layout_set_hadjustment (GTK_LAYOUT(tree->priv->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->priv->sorter) {
gint i;
i = e_sorter_model_to_sorted(E_SORTER (e_tree->priv->sorter), model_row);
i++;
if (i < e_table_model_row_count(E_TABLE_MODEL(e_tree->priv->etta))) {
return e_sorter_sorted_to_model(E_SORTER (e_tree->priv->sorter), i);
} else
return -1;
} else
if (model_row < e_table_model_row_count(E_TABLE_MODEL(e_tree->priv->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->priv->sorter) {
gint i;
i = e_sorter_model_to_sorted(E_SORTER (e_tree->priv->sorter), model_row);
i--;
if (i >= 0)
return e_sorter_sorted_to_model(E_SORTER (e_tree->priv->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->priv->sorter)
return e_sorter_model_to_sorted(E_SORTER (e_tree->priv->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->priv->sorter)
return e_sorter_sorted_to_model (E_SORTER (e_tree->priv->sorter), view_row);
else
return view_row;
}
gboolean
e_tree_node_is_expanded (ETree *et, ETreePath path)
{
g_return_val_if_fail(path, FALSE);
return e_tree_table_adapter_node_is_expanded (et->priv->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));
e_tree_table_adapter_node_set_expanded (et->priv->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));
e_tree_table_adapter_node_set_expanded_recurse (et->priv->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->priv->etta, visible);
}
ETreePath
e_tree_node_at_row (ETree *et, gint row)
{
ETreePath path;
path = e_tree_table_adapter_node_at_row (et->priv->etta, row);
return path;
}
gint
e_tree_row_of_node (ETree *et, ETreePath path)
{
return e_tree_table_adapter_row_of_node (et->priv->etta, path);
}
gboolean
e_tree_root_node_is_visible(ETree *et)
{
return e_tree_table_adapter_root_node_is_visible (et->priv->etta);
}
void
e_tree_show_node (ETree *et, ETreePath path)
{
g_return_if_fail (et != NULL);
g_return_if_fail (E_IS_TREE(et));
e_tree_table_adapter_show_node (et->priv->etta, path);
}
void
e_tree_save_expanded_state (ETree *et, gchar *filename)
{
g_return_if_fail (et != NULL);
g_return_if_fail (E_IS_TREE(et));
e_tree_table_adapter_save_expanded_state (et->priv->etta, filename);
}
void
e_tree_load_expanded_state (ETree *et, gchar *filename)
{
e_tree_table_adapter_load_expanded_state (et->priv->etta, filename);
}
xmlDoc *
e_tree_save_expanded_state_xml (ETree *et)
{
g_return_val_if_fail (et != NULL, NULL);
g_return_val_if_fail (E_IS_TREE (et), NULL);
return e_tree_table_adapter_save_expanded_state_xml (et->priv->etta);
}
void
e_tree_load_expanded_state_xml (ETree *et, xmlDoc *doc)
{
g_return_if_fail (et != NULL);
g_return_if_fail (E_IS_TREE (et));
g_return_if_fail (doc != NULL);
e_tree_table_adapter_load_expanded_state_xml (et->priv->etta, doc);
}
/* state: <0 ... collapse; 0 ... no force - use default; >0 ... expand;
when using this, be sure to reset to 0 once no forcing is required
anymore, aka the build of the tree is done */
void
e_tree_force_expanded_state (ETree *et, gint state)
{
e_tree_table_adapter_force_expanded_state (et->priv->etta, state);
}
gint
e_tree_row_count (ETree *et)
{
return e_table_model_row_count (E_TABLE_MODEL(et->priv->etta));
}
GtkWidget *
e_tree_get_tooltip (ETree *et)
{
return E_CANVAS(et->priv->table_canvas)->tooltip_window;
}
static ETreePath
find_next_in_range (ETree *et, gint start, gint end, ETreePathFunc func, gpointer data)
{
ETreePath path;
gint row;
for (row = start; row <= end; row++) {
path = e_tree_table_adapter_node_at_row (et->priv->etta, row);
if (path && func (et->priv->model, path, data))
return path;
}
return NULL;
}
static ETreePath
find_prev_in_range (ETree *et, gint start, gint end, ETreePathFunc func, gpointer data)
{
ETreePath path;
gint row;
for (row = start; row >= end; row--) {
path = e_tree_table_adapter_node_at_row (et->priv->etta, row);
if (path && func (et->priv->model, path, data))
return path;
}
return NULL;
}
gboolean
e_tree_find_next (ETree *et, ETreeFindNextParams params, ETreePathFunc func, gpointer data)
{
ETreePath cursor, found;
gint row, row_count;
cursor = e_tree_get_cursor (et);
row = e_tree_table_adapter_row_of_node (et->priv->etta, cursor);
row_count = e_table_model_row_count (E_TABLE_MODEL (et->priv->etta));
if (params & E_TREE_FIND_NEXT_FORWARD)
found = find_next_in_range (et, row + 1, row_count - 1, func, data);
else
found = find_prev_in_range (et, row == -1 ? -1 : row - 1, 0, func, data);
if (found) {
e_tree_table_adapter_show_node (et->priv->etta, found);
e_tree_set_cursor (et, found);
return TRUE;
}
if (params & E_TREE_FIND_NEXT_WRAP) {
if (params & E_TREE_FIND_NEXT_FORWARD)
found = find_next_in_range (et, 0, row, func, data);
else
found = find_prev_in_range (et, row_count - 1, row, func, data);
if (found && found != cursor) {
e_tree_table_adapter_show_node (et->priv->etta, found);
e_tree_set_cursor (et, found);
return TRUE;
}
}
return FALSE;
}
void
e_tree_right_click_up (ETree *et)
{
e_selection_model_right_click_up(et->priv->selection);
}
/**
* e_tree_get_model:
* @et: the ETree
*
* Returns the model upon which this ETree is based.
*
* Returns: the model
**/
ETreeModel *
e_tree_get_model (ETree *et)
{
g_return_val_if_fail (et != NULL, NULL);
g_return_val_if_fail (E_IS_TREE (et), NULL);
return et->priv->model;
}
/**
* e_tree_get_selection_model:
* @et: the ETree
*
* Returns the selection model of this ETree.
*
* Returns: the selection model
**/
ESelectionModel *
e_tree_get_selection_model (ETree *et)
{
g_return_val_if_fail (et != NULL, NULL);
g_return_val_if_fail (E_IS_TREE (et), NULL);
return et->priv->selection;
}
/**
* e_tree_get_table_adapter:
* @et: the ETree
*
* Returns the table adapter this ETree uses.
*
* Returns: the model
**/
ETreeTableAdapter *
e_tree_get_table_adapter (ETree *et)
{
g_return_val_if_fail (et != NULL, NULL);
g_return_val_if_fail (E_IS_TREE (et), NULL);
return et->priv->etta;
}
ETableItem *
e_tree_get_item(ETree * et)
{
g_return_val_if_fail (et != NULL, NULL);
g_return_val_if_fail (E_IS_TREE (et), NULL);
return E_TABLE_ITEM (et->priv->item);
}
GnomeCanvasItem *
e_tree_get_header_item(ETree * et)
{
g_return_val_if_fail (et != NULL, NULL);
g_return_val_if_fail (E_IS_TREE (et), NULL);
return et->priv->header_item;
}
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 */
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 */
void
e_tree_drag_get_data (ETree *tree,
gint row,
gint col,
GdkDragContext *context,
GdkAtom target,
guint32 time)
{
g_return_if_fail(tree != NULL);
g_return_if_fail(E_IS_TREE(tree));
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.
* Set row to -1 to turn off the highlight.
*/
void
e_tree_drag_highlight (ETree *tree,
gint row,
gint col)
{
g_return_if_fail(tree != NULL);
g_return_if_fail(E_IS_TREE(tree));
if (row != -1) {
gint x, y, width, height;
if (col == -1) {
e_tree_get_cell_geometry (tree, row, 0, &x, &y, &width, &height);
x = 0;
width = GTK_WIDGET (tree->priv->table_canvas)->allocation.width;
} else {
e_tree_get_cell_geometry (tree, row, col, &x, &y, &width, &height);
x += GTK_LAYOUT(tree->priv->table_canvas)->hadjustment->value;
}
y += GTK_LAYOUT(tree->priv->table_canvas)->vadjustment->value;
if (tree->priv->drop_highlight == NULL) {
tree->priv->drop_highlight =
gnome_canvas_item_new (gnome_canvas_root (tree->priv->table_canvas),
gnome_canvas_rect_get_type (),
"fill_color", NULL,
/* "outline_color", "black",
"width_pixels", 1,*/
"outline_color_gdk", &(GTK_WIDGET (tree)->style->fg[GTK_STATE_NORMAL]),
NULL);
}
gnome_canvas_item_set (tree->priv->drop_highlight,
"x1", (double) x,
"x2", (double) x + width - 1,
"y1", (double) y,
"y2", (double) y + height - 1,
NULL);
} else {
gtk_object_destroy (GTK_OBJECT (tree->priv->drop_highlight));
tree->priv->drop_highlight = NULL;
}
}
void
e_tree_drag_unhighlight (ETree *tree)
{
g_return_if_fail(tree != NULL);
g_return_if_fail(E_IS_TREE(tree));
if (tree->priv->drop_highlight) {
gtk_object_destroy (GTK_OBJECT (tree->priv->drop_highlight));
tree->priv->drop_highlight = NULL;
}
}
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 */
static gint
et_real_start_drag (ETree *tree, gint row, ETreePath path, gint col, GdkEvent *event)
{
GtkDragSourceInfo *info;
GdkDragContext *context;
ETreeDragSourceSite *site;
if (tree->priv->do_drag) {
site = tree->priv->site;
site->state = 0;
context = e_tree_drag_begin (tree, 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;
}
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->priv->table_canvas);
site = tree->priv->site;
tree->priv->do_drag = TRUE;
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->priv->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->priv->site;
if (site) {
if (site->target_list)
gtk_target_list_unref (site->target_list);
g_free (site);
tree->priv->site = NULL;
}
}
/* There probably should be functions for setting the targets
* as a GtkTargetList
*/
GdkDragContext *
e_tree_drag_begin (ETree *tree,
gint row,
gint 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->priv->etta, row);
tree->priv->drag_row = row;
tree->priv->drag_path = path;
tree->priv->drag_col = col;
return gtk_drag_begin(GTK_WIDGET (tree->priv->table_canvas),
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,
gint x, gint y,
gint *row_return, gint *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. */
if (row_return)
*row_return = -1;
if (col_return)
*col_return = -1;
x += GTK_LAYOUT(tree->priv->table_canvas)->hadjustment->value;
y += GTK_LAYOUT(tree->priv->table_canvas)->vadjustment->value;
e_table_item_compute_location(E_TABLE_ITEM(tree->priv->item), &x, &y, row_return, col_return);
}
/**
* e_tree_get_cell_geometry:
* @tree: The tree.
* @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 right hand corner of the cell with respect to the widget.
* @y_return: Returns the y coordinate of the upper right 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.
*
* Computes the data about this cell.
**/
void
e_tree_get_cell_geometry (ETree *tree,
gint row, gint col,
gint *x_return, gint *y_return,
gint *width_return, gint *height_return)
{
g_return_if_fail (tree != NULL);
g_return_if_fail (E_IS_TREE (tree));
g_return_if_fail (row >= 0);
g_return_if_fail (col >= 0);
/* FIXME it would be nice if it could handle a NULL row_return or
* col_return gracefully. */
e_table_item_get_cell_geometry(E_TABLE_ITEM(tree->priv->item), &row, &col, x_return, y_return, width_return, height_return);
if (x_return)
(*x_return) -= GTK_LAYOUT(tree->priv->table_canvas)->hadjustment->value;
if (y_return)
(*y_return) -= GTK_LAYOUT(tree->priv->table_canvas)->vadjustment->value;
}
static void
et_drag_begin (GtkWidget *widget,
GdkDragContext *context,
ETree *et)
{
g_signal_emit (et,
et_signals [TREE_DRAG_BEGIN], 0,
et->priv->drag_row,
et->priv->drag_path,
et->priv->drag_col,
context);
}
static void
et_drag_end (GtkWidget *widget,
GdkDragContext *context,
ETree *et)
{
g_signal_emit (et,
et_signals [TREE_DRAG_END], 0,
et->priv->drag_row,
et->priv->drag_path,
et->priv->drag_col,
context);
}
static void
et_drag_data_get(GtkWidget *widget,
GdkDragContext *context,
GtkSelectionData *selection_data,
guint info,
guint time,
ETree *et)
{
g_signal_emit (et,
et_signals [TREE_DRAG_DATA_GET], 0,
et->priv->drag_row,
et->priv->drag_path,
et->priv->drag_col,
context,
selection_data,
info,
time);
}
static void
et_drag_data_delete(GtkWidget *widget,
GdkDragContext *context,
ETree *et)
{
g_signal_emit (et,
et_signals [TREE_DRAG_DATA_DELETE], 0,
et->priv->drag_row,
et->priv->drag_path,
et->priv->drag_col,
context);
}
static gboolean
do_drag_motion(ETree *et,
GdkDragContext *context,
gint x,
gint y,
guint time)
{
gboolean ret_val = FALSE;
gint row, col;
ETreePath path;
e_tree_get_cell_at (et,
x,
y,
&row,
&col);
if (row != et->priv->drop_row && col != et->priv->drop_col) {
g_signal_emit (et,
et_signals [TREE_DRAG_LEAVE], 0,
et->priv->drop_row,
et->priv->drop_path,
et->priv->drop_col,
context,
time);
}
path = e_tree_table_adapter_node_at_row(et->priv->etta, row);
et->priv->drop_row = row;
et->priv->drop_path = path;
et->priv->drop_col = col;
g_signal_emit (et,
et_signals [TREE_DRAG_MOTION], 0,
et->priv->drop_row,
et->priv->drop_path,
et->priv->drop_col,
context,
x,
y,
time,
&ret_val);
return ret_val;
}
static gboolean
scroll_timeout (gpointer data)
{
ETree *et = data;
gint dx = 0, dy = 0;
GtkAdjustment *v, *h;
double vvalue, hvalue;
if (et->priv->scroll_direction & ET_SCROLL_DOWN)
dy += 20;
if (et->priv->scroll_direction & ET_SCROLL_UP)
dy -= 20;
if (et->priv->scroll_direction & ET_SCROLL_RIGHT)
dx += 20;
if (et->priv->scroll_direction & ET_SCROLL_LEFT)
dx -= 20;
h = GTK_LAYOUT(et->priv->table_canvas)->hadjustment;
v = GTK_LAYOUT(et->priv->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->priv->last_drop_context,
et->priv->last_drop_x,
et->priv->last_drop_y,
et->priv->last_drop_time);
return TRUE;
}
static void
scroll_on (ETree *et, guint scroll_direction)
{
if (et->priv->scroll_idle_id == 0 || scroll_direction != et->priv->scroll_direction) {
if (et->priv->scroll_idle_id != 0)
g_source_remove (et->priv->scroll_idle_id);
et->priv->scroll_direction = scroll_direction;
et->priv->scroll_idle_id = g_timeout_add (100, scroll_timeout, et);
}
}
static void
scroll_off (ETree *et)
{
if (et->priv->scroll_idle_id) {
g_source_remove (et->priv->scroll_idle_id);
et->priv->scroll_idle_id = 0;
}
}
static gboolean
hover_timeout (gpointer data)
{
ETree *et = data;
gint x = et->priv->hover_x;
gint y = et->priv->hover_y;
gint row, col;
ETreePath path;
e_tree_get_cell_at (et,
x,
y,
&row,
&col);
path = e_tree_table_adapter_node_at_row(et->priv->etta, row);
if (path && e_tree_model_node_is_expandable (et->priv->model, path)) {
if (!e_tree_table_adapter_node_is_expanded (et->priv->etta, path)) {
if (e_tree_model_has_save_id (et->priv->model) && e_tree_model_has_get_node_by_id (et->priv->model))
et->priv->expanded_list = g_list_prepend (et->priv->expanded_list, e_tree_model_get_save_id (et->priv->model, path));
e_tree_table_adapter_node_set_expanded (et->priv->etta, path, TRUE);
}
}
return TRUE;
}
static void
hover_on (ETree *et, gint x, gint y)
{
et->priv->hover_x = x;
et->priv->hover_y = y;
if (et->priv->hover_idle_id != 0)
g_source_remove (et->priv->hover_idle_id);
et->priv->hover_idle_id = g_timeout_add (500, hover_timeout, et);
}
static void
hover_off (ETree *et)
{
if (et->priv->hover_idle_id) {
g_source_remove (et->priv->hover_idle_id);
et->priv->hover_idle_id = 0;
}
}
static void
collapse_drag (ETree *et, ETreePath drop)
{
GList *list;
/* We only want to leave open parents of the node dropped in. Not the node itself. */
if (drop) {
drop = e_tree_model_node_get_parent (et->priv->model, drop);
}
for (list = et->priv->expanded_list; list; list = list->next) {
gchar *save_id = list->data;
ETreePath path;
path = e_tree_model_get_node_by_id (et->priv->model, save_id);
if (path) {
ETreePath search;
gboolean found = FALSE;
for (search = drop; search; search = e_tree_model_node_get_parent (et->priv->model, search)) {
if (path == search) {
found = TRUE;
break;
}
}
if (!found)
e_tree_table_adapter_node_set_expanded (et->priv->etta, path, FALSE);
}
g_free (save_id);
}
g_list_free (et->priv->expanded_list);
et->priv->expanded_list = NULL;
}
static void
context_destroyed (gpointer data, GObject *ctx)
{
ETree *et = data;
if (et->priv) {
et->priv->last_drop_x = 0;
et->priv->last_drop_y = 0;
et->priv->last_drop_time = 0;
et->priv->last_drop_context = NULL;
collapse_drag (et, NULL);
scroll_off (et);
hover_off (et);
}
g_object_unref (et);
}
static void
context_connect (ETree *et, GdkDragContext *context)
{
if (context == et->priv->last_drop_context)
return;
if (et->priv->last_drop_context)
g_object_weak_unref (G_OBJECT(et->priv->last_drop_context), context_destroyed, et);
else
g_object_ref (et);
g_object_weak_ref (G_OBJECT(context), context_destroyed, et);
}
static void
et_drag_leave(GtkWidget *widget,
GdkDragContext *context,
guint time,
ETree *et)
{
g_signal_emit (et,
et_signals [TREE_DRAG_LEAVE], 0,
et->priv->drop_row,
et->priv->drop_path,
et->priv->drop_col,
context,
time);
et->priv->drop_row = -1;
et->priv->drop_col = -1;
scroll_off (et);
hover_off (et);
}
static gboolean
et_drag_motion(GtkWidget *widget,
GdkDragContext *context,
gint x,
gint y,
guint time,
ETree *et)
{
gint ret_val;
guint direction = 0;
et->priv->last_drop_x = x;
et->priv->last_drop_y = y;
et->priv->last_drop_time = time;
context_connect (et, context);
et->priv->last_drop_context = context;
if (et->priv->hover_idle_id != 0) {
if (abs (et->priv->hover_x - x) > 3 ||
abs (et->priv->hover_y - y) > 3) {
hover_on (et, x, y);
}
} else {
hover_on (et, x, y);
}
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,
ETree *et)
{
gboolean ret_val = FALSE;
gint row, col;
ETreePath path;
e_tree_get_cell_at(et,
x,
y,
&row,
&col);
path = e_tree_table_adapter_node_at_row(et->priv->etta, row);
if (row != et->priv->drop_row && col != et->priv->drop_row) {
g_signal_emit (et,
et_signals [TREE_DRAG_LEAVE], 0,
et->priv->drop_row,
et->priv->drop_path,
et->priv->drop_col,
context,
time);
g_signal_emit (et,
et_signals [TREE_DRAG_MOTION], 0,
row,
path,
col,
context,
x,
y,
time,
&ret_val);
}
et->priv->drop_row = row;
et->priv->drop_path = path;
et->priv->drop_col = col;
g_signal_emit (et,
et_signals [TREE_DRAG_DROP], 0,
et->priv->drop_row,
et->priv->drop_path,
et->priv->drop_col,
context,
x,
y,
time,
&ret_val);
et->priv->drop_row = -1;
et->priv->drop_path = NULL;
et->priv->drop_col = -1;
collapse_drag (et, path);
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,
ETree *et)
{
gint row, col;
ETreePath path;
e_tree_get_cell_at(et,
x,
y,
&row,
&col);
path = e_tree_table_adapter_node_at_row(et->priv->etta, row);
g_signal_emit (et,
et_signals [TREE_DRAG_DATA_RECEIVED], 0,
row,
path,
col,
context,
x,
y,
selection_data,
info,
time);
}
static void
e_tree_class_init (ETreeClass *class)
{
GObjectClass *object_class;
GtkWidgetClass *widget_class;
object_class = (GObjectClass *) class;
widget_class = (GtkWidgetClass *) class;
object_class->dispose = et_dispose;
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->style_set = et_canvas_style_set;
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->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] =
g_signal_new ("cursor_change",
G_OBJECT_CLASS_TYPE (object_class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (ETreeClass, cursor_change),
NULL, NULL,
e_marshal_NONE__INT_POINTER,
G_TYPE_NONE, 2, G_TYPE_INT, G_TYPE_POINTER);
et_signals [CURSOR_ACTIVATED] =
g_signal_new ("cursor_activated",
G_OBJECT_CLASS_TYPE (object_class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (ETreeClass, cursor_activated),
NULL, NULL,
e_marshal_NONE__INT_POINTER,
G_TYPE_NONE, 2, G_TYPE_INT, G_TYPE_POINTER);
et_signals [SELECTION_CHANGE] =
g_signal_new ("selection_change",
G_OBJECT_CLASS_TYPE (object_class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (ETreeClass, 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 (ETreeClass, double_click),
NULL, NULL,
e_marshal_NONE__INT_POINTER_INT_BOXED,
G_TYPE_NONE, 4, G_TYPE_INT,
G_TYPE_POINTER, 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 (ETreeClass, right_click),
NULL, NULL,
e_marshal_INT__INT_POINTER_INT_BOXED,
G_TYPE_INT, 4, G_TYPE_INT, G_TYPE_POINTER,
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 (ETreeClass, click),
NULL, NULL,
e_marshal_INT__INT_POINTER_INT_BOXED,
G_TYPE_INT, 4, G_TYPE_INT, G_TYPE_POINTER,
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 (ETreeClass, key_press),
NULL, NULL,
e_marshal_INT__INT_POINTER_INT_BOXED,
G_TYPE_INT, 4, G_TYPE_INT, G_TYPE_POINTER,
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 (ETreeClass, start_drag),
NULL, NULL,
e_marshal_NONE__INT_POINTER_INT_BOXED,
G_TYPE_NONE, 4, G_TYPE_INT, G_TYPE_POINTER,
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 (ETreeClass, 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 (ETreeClass, white_space_event),
NULL, NULL,
e_marshal_INT__POINTER,
G_TYPE_INT, 1, GDK_TYPE_EVENT);
et_signals[TREE_DRAG_BEGIN] =
g_signal_new ("tree_drag_begin",
G_OBJECT_CLASS_TYPE (object_class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (ETreeClass, tree_drag_begin),
NULL, NULL,
e_marshal_NONE__INT_POINTER_INT_BOXED,
G_TYPE_NONE, 4,
G_TYPE_INT,
G_TYPE_POINTER,
G_TYPE_INT,
GDK_TYPE_DRAG_CONTEXT);
et_signals[TREE_DRAG_END] =
g_signal_new ("tree_drag_end",
G_OBJECT_CLASS_TYPE (object_class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (ETreeClass, tree_drag_end),
NULL, NULL,
e_marshal_NONE__INT_POINTER_INT_BOXED,
G_TYPE_NONE, 4,
G_TYPE_INT,
G_TYPE_POINTER,
G_TYPE_INT,
GDK_TYPE_DRAG_CONTEXT);
et_signals[TREE_DRAG_DATA_GET] =
g_signal_new ("tree_drag_data_get",
G_OBJECT_CLASS_TYPE (object_class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (ETreeClass, tree_drag_data_get),
NULL, NULL,
e_marshal_NONE__INT_POINTER_INT_OBJECT_BOXED_UINT_UINT,
G_TYPE_NONE, 7,
G_TYPE_INT,
G_TYPE_POINTER,
G_TYPE_INT,
GDK_TYPE_DRAG_CONTEXT,
GTK_TYPE_SELECTION_DATA | G_SIGNAL_TYPE_STATIC_SCOPE,
G_TYPE_UINT,
G_TYPE_UINT);
et_signals[TREE_DRAG_DATA_DELETE] =
g_signal_new ("tree_drag_data_delete",
G_OBJECT_CLASS_TYPE (object_class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (ETreeClass, tree_drag_data_delete),
NULL, NULL,
e_marshal_NONE__INT_POINTER_INT_OBJECT,
G_TYPE_NONE, 4,
G_TYPE_INT,
G_TYPE_POINTER,
G_TYPE_INT,
GDK_TYPE_DRAG_CONTEXT);
et_signals[TREE_DRAG_LEAVE] =
g_signal_new ("tree_drag_leave",
G_OBJECT_CLASS_TYPE (object_class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (ETreeClass, tree_drag_leave),
NULL, NULL,
e_marshal_NONE__INT_POINTER_INT_OBJECT_UINT,
G_TYPE_NONE, 5,
G_TYPE_INT,
G_TYPE_POINTER,
G_TYPE_INT,
GDK_TYPE_DRAG_CONTEXT,
G_TYPE_UINT);
et_signals[TREE_DRAG_MOTION] =
g_signal_new ("tree_drag_motion",
G_OBJECT_CLASS_TYPE (object_class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (ETreeClass, tree_drag_motion),
NULL, NULL,
e_marshal_BOOLEAN__INT_POINTER_INT_OBJECT_INT_INT_UINT,
G_TYPE_BOOLEAN, 7,
G_TYPE_INT,
G_TYPE_POINTER,
G_TYPE_INT,
GDK_TYPE_DRAG_CONTEXT,
G_TYPE_INT,
G_TYPE_INT,
G_TYPE_UINT);
et_signals[TREE_DRAG_DROP] =
g_signal_new ("tree_drag_drop",
G_OBJECT_CLASS_TYPE (object_class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (ETreeClass, tree_drag_drop),
NULL, NULL,
e_marshal_BOOLEAN__INT_POINTER_INT_OBJECT_INT_INT_UINT,
G_TYPE_BOOLEAN, 7,
G_TYPE_INT,
G_TYPE_POINTER,
G_TYPE_INT,
GDK_TYPE_DRAG_CONTEXT,
G_TYPE_INT,
G_TYPE_INT,
G_TYPE_UINT);
et_signals[TREE_DRAG_DATA_RECEIVED] =
g_signal_new ("tree_drag_data_received",
G_OBJECT_CLASS_TYPE (object_class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (ETreeClass, tree_drag_data_received),
NULL, NULL,
e_marshal_NONE__INT_POINTER_INT_OBJECT_INT_INT_BOXED_UINT_UINT,
G_TYPE_NONE, 9,
G_TYPE_INT,
G_TYPE_POINTER,
G_TYPE_INT,
GDK_TYPE_DRAG_CONTEXT,
G_TYPE_INT,
G_TYPE_INT,
GTK_TYPE_SELECTION_DATA,
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 (ETreeClass, 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" ),
_( "Length Threshold" ),
0, G_MAXINT, 0,
G_PARAM_WRITABLE));
g_object_class_install_property (object_class, PROP_HORIZONTAL_DRAW_GRID,
g_param_spec_boolean ("horizontal_draw_grid",
_( "Horizontal Draw Grid" ),
_( "Horizontal Draw Grid" ),
FALSE,
G_PARAM_WRITABLE));
g_object_class_install_property (object_class, PROP_VERTICAL_DRAW_GRID,
g_param_spec_boolean ("vertical_draw_grid",
_( "Vertical Draw Grid" ),
_( "Vertical Draw Grid" ),
FALSE,
G_PARAM_WRITABLE));
g_object_class_install_property (object_class, PROP_DRAW_FOCUS,
g_param_spec_boolean ("drawfocus",
_( "Draw focus" ),
_( "Draw focus" ),
FALSE,
G_PARAM_WRITABLE));
g_object_class_install_property (object_class, PROP_ETTA,
g_param_spec_object ("ETreeTableAdapter",
_( "ETree table adapter" ),
_( "ETree table adapter" ),
E_TREE_TABLE_ADAPTER_TYPE,
G_PARAM_READABLE));
g_object_class_install_property (object_class, PROP_UNIFORM_ROW_HEIGHT,
g_param_spec_boolean ("uniform_row_height",
_( "Uniform row height" ),
_( "Uniform row height" ),
FALSE,
G_PARAM_READWRITE));
g_object_class_install_property (object_class, PROP_ALWAYS_SEARCH,
g_param_spec_boolean ("always_search",
_( "Always search" ),
_( "Always search" ),
FALSE,
G_PARAM_READWRITE));
gtk_widget_class_install_style_property (widget_class,
g_param_spec_boolean ("retro_look",
_("Retro Look"),
_("Draw lines and +/- expanders."),
FALSE,
G_PARAM_READABLE));
gtk_widget_class_install_style_property (widget_class,
g_param_spec_int ("expander_size",
_("Expander Size"),
_("Size of the expander arrow"),
0,
G_MAXINT,
10,
G_PARAM_READABLE));
gal_a11y_e_tree_init ();
}
static void
tree_size_allocate (GtkWidget *widget, GtkAllocation *alloc, ETree *tree)
{
double width;
g_return_if_fail (tree != NULL);
g_return_if_fail (tree->priv != NULL);
g_return_if_fail (tree->priv->info_text != NULL);
gnome_canvas_get_scroll_region (GNOME_CANVAS (tree->priv->table_canvas), NULL, NULL, &width, NULL);
width -= 60.0;
g_object_set (tree->priv->info_text, "width", width, "clip_width", width, NULL);
}
/**
* e_tree_set_info_message:
* @tree: #ETree instance
* @info_message: Message to set. Can be NULL.
*
* Creates an info message in table area, or removes old.
**/
void
e_tree_set_info_message (ETree *tree, const gchar *info_message)
{
g_return_if_fail (tree != NULL);
g_return_if_fail (tree->priv != NULL);
if (!tree->priv->info_text && (!info_message || !*info_message))
return;
if (!info_message || !*info_message) {
g_signal_handler_disconnect (tree, tree->priv->info_text_resize_id);
gtk_object_destroy (GTK_OBJECT (tree->priv->info_text));
tree->priv->info_text = NULL;
return;
}
if (!tree->priv->info_text) {
tree->priv->info_text = gnome_canvas_item_new (GNOME_CANVAS_GROUP (gnome_canvas_root (tree->priv->table_canvas)),
e_text_get_type (),
"anchor", GTK_ANCHOR_NW,
"line_wrap", TRUE,
"clip", TRUE,
"justification", GTK_JUSTIFY_LEFT,
"text", info_message,
"draw_background", FALSE,
"width", (double) GTK_WIDGET (tree->priv->table_canvas)->allocation.width - 60.0,
"clip_width", (double) GTK_WIDGET (tree->priv->table_canvas)->allocation.width - 60.0,
NULL);
e_canvas_item_move_absolute (tree->priv->info_text, 30, 30);
tree->priv->info_text_resize_id = g_signal_connect (tree, "size_allocate", G_CALLBACK (tree_size_allocate), tree);
} else
gnome_canvas_item_set (tree->priv->info_text, "text", info_message, NULL);
}