aboutsummaryrefslogtreecommitdiffstats
path: root/widgets/table
diff options
context:
space:
mode:
Diffstat (limited to 'widgets/table')
-rw-r--r--widgets/table/e-cell-tree.c105
-rw-r--r--widgets/table/e-table-config.c6
-rw-r--r--widgets/table/e-table-extras.c10
-rw-r--r--widgets/table/e-table-item.c131
-rw-r--r--widgets/table/e-table-item.h6
-rw-r--r--widgets/table/e-table-model.c171
-rw-r--r--widgets/table/e-table-model.h32
-rw-r--r--widgets/table/e-table-scrolled.h2
-rw-r--r--widgets/table/e-table-selection-model.c129
-rw-r--r--widgets/table/e-table-selection-model.h9
-rw-r--r--widgets/table/e-table-simple.h10
-rw-r--r--widgets/table/e-table-sorted.c81
-rw-r--r--widgets/table/e-table-sorter.c176
-rw-r--r--widgets/table/e-table-sorting-utils.c562
-rw-r--r--widgets/table/e-table-sorting-utils.h60
-rw-r--r--widgets/table/e-table-subset.c32
-rw-r--r--widgets/table/e-table-subset.h8
-rw-r--r--widgets/table/e-table-utils.c111
-rw-r--r--widgets/table/e-table-utils.h22
-rw-r--r--widgets/table/e-table.c150
-rw-r--r--widgets/table/e-table.h4
-rw-r--r--widgets/table/e-tree-memory-callbacks.c231
-rw-r--r--widgets/table/e-tree-memory-callbacks.h88
-rw-r--r--widgets/table/e-tree-memory.c548
-rw-r--r--widgets/table/e-tree-memory.h54
-rw-r--r--widgets/table/e-tree-model.c1755
-rw-r--r--widgets/table/e-tree-model.h202
-rw-r--r--widgets/table/e-tree-scrolled.c207
-rw-r--r--widgets/table/e-tree-scrolled.h54
-rw-r--r--widgets/table/e-tree-simple.c2
-rw-r--r--widgets/table/e-tree-sorted.c1126
-rw-r--r--widgets/table/e-tree-sorted.h53
-rw-r--r--widgets/table/e-tree-table-adapter.c974
-rw-r--r--widgets/table/e-tree-table-adapter.h64
-rw-r--r--widgets/table/e-tree.c2004
-rw-r--r--widgets/table/e-tree.h289
36 files changed, 7007 insertions, 2461 deletions
diff --git a/widgets/table/e-cell-tree.c b/widgets/table/e-cell-tree.c
index 62c78da748..c38d2efce7 100644
--- a/widgets/table/e-cell-tree.c
+++ b/widgets/table/e-cell-tree.c
@@ -13,6 +13,9 @@
*/
#include <config.h>
+
+#include "e-cell-tree.h"
+
#include <gtk/gtkenums.h>
#include <gtk/gtkentry.h>
#include <gtk/gtkwindow.h>
@@ -21,9 +24,9 @@
#include <gdk/gdkkeysyms.h>
#include <libgnomeui/gnome-canvas.h>
#include <stdio.h>
-#include "e-table-sorted-variable.h"
+
+#include "e-tree-table-adapter.h"
#include "e-tree-model.h"
-#include "e-cell-tree.h"
#include "gal/util/e-util.h"
#include "e-table-item.h"
@@ -49,29 +52,38 @@ static ECellClass *parent_class;
#define INDENT_AMOUNT 16
-static int
-visible_depth_of_node (ETreeModel *tree_model, ETreePath *path)
+static ETreePath
+e_cell_tree_get_node (ETableModel *table_model, int row)
{
- return (e_tree_model_node_depth (tree_model, path)
- - (e_tree_model_root_node_is_visible (tree_model) ? 0 : 1));
+ return e_table_model_value_at (table_model, -1, row);
}
-static gint
-offset_of_node (ETreeModel *tree_model, ETreePath *path)
+static ETreeModel*
+e_cell_tree_get_tree_model (ETableModel *table_model, int row)
{
- return (visible_depth_of_node(tree_model, path) + 1) * INDENT_AMOUNT;
+ return e_table_model_value_at (table_model, -2, row);
}
-static ETreePath*
-e_cell_tree_get_node (ETableModel *table_model, int row)
+static ETreeTableAdapter *
+e_cell_tree_get_tree_table_adapter (ETableModel *table_model, int row)
{
- return (ETreePath*)e_table_model_value_at (table_model, -1, row);
+ return e_table_model_value_at (table_model, -3, row);
}
-static ETreeModel*
-e_cell_tree_get_tree_model (ETableModel *table_model, int row)
+static int
+visible_depth_of_node (ETableModel *model, int row)
{
- return (ETreeModel*)e_table_model_value_at (table_model, -2, row);
+ ETreeModel *tree_model = e_cell_tree_get_tree_model(model, row);
+ ETreeTableAdapter *adapter = e_cell_tree_get_tree_table_adapter(model, row);
+ ETreePath path = e_cell_tree_get_node(model, row);
+ return (e_tree_model_node_depth (tree_model, path)
+ - (e_tree_table_adapter_root_node_is_visible (adapter) ? 0 : 1));
+}
+
+static gint
+offset_of_node (ETableModel *table_model, int row)
+{
+ return (visible_depth_of_node(table_model, row) + 1) * INDENT_AMOUNT;
}
/*
@@ -159,7 +171,8 @@ ect_draw (ECellView *ecell_view, GdkDrawable *drawable,
{
ECellTreeView *tree_view = (ECellTreeView *)ecell_view;
ETreeModel *tree_model = e_cell_tree_get_tree_model(ecell_view->e_table_model, row);
- ETreePath *node;
+ ETreeTableAdapter *tree_table_adapter = e_cell_tree_get_tree_table_adapter(ecell_view->e_table_model, row);
+ ETreePath node;
GdkRectangle rect, *clip_rect;
GtkWidget *canvas = GTK_WIDGET (tree_view->canvas);
GdkGC *fg_gc = canvas->style->fg_gc[GTK_STATE_ACTIVE];
@@ -175,21 +188,21 @@ ect_draw (ECellView *ecell_view, GdkDrawable *drawable,
if (/* XXX */ TRUE) {
GdkPixbuf *node_image;
int node_image_width = 0, node_image_height = 0;
- ETreePath *parent_node;
+ ETreePath parent_node;
node = e_cell_tree_get_node (ecell_view->e_table_model, row);
expandable = e_tree_model_node_is_expandable (tree_model, node);
- expanded = e_tree_model_node_is_expanded (tree_model, node);
+ expanded = e_tree_table_adapter_node_is_expanded (tree_table_adapter, node);
- if (visible_depth_of_node (tree_model, node) > 0 || expandable) {
- offset = offset_of_node (tree_model, node);
+ if (visible_depth_of_node (ecell_view->e_table_model, row) > 0 || expandable) {
+ offset = offset_of_node (ecell_view->e_table_model, row);
} else {
offset = 0;
}
subcell_offset = offset;
- node_image = e_tree_model_icon_of_node (tree_model, node);
+ node_image = e_tree_model_icon_at (tree_model, node);
if (node_image) {
node_image_width = gdk_pixbuf_get_width (node_image);
@@ -219,7 +232,9 @@ ect_draw (ECellView *ecell_view, GdkDrawable *drawable,
/* draw our lines */
if (E_CELL_TREE(tree_view->cell_view.ecell)->draw_lines) {
- if (visible_depth_of_node (tree_model, node) > 0
+ int depth;
+
+ if (visible_depth_of_node (ecell_view->e_table_model, row) > 0
|| e_tree_model_node_get_children (tree_model, node, NULL) > 0)
gdk_draw_line (drawable, tree_view->gc,
rect.x + offset - INDENT_AMOUNT / 2 + 1,
@@ -227,7 +242,7 @@ ect_draw (ECellView *ecell_view, GdkDrawable *drawable,
rect.x + offset,
rect.y + rect.height / 2);
- if (visible_depth_of_node (tree_model, node) != 0) {
+ if (visible_depth_of_node (ecell_view->e_table_model, row) != 0) {
gdk_draw_line (drawable, tree_view->gc,
rect.x + offset - INDENT_AMOUNT / 2,
rect.y,
@@ -242,7 +257,8 @@ ect_draw (ECellView *ecell_view, GdkDrawable *drawable,
correct vertical pipe for it's configuration. */
parent_node = e_tree_model_node_get_parent (tree_model, node);
offset -= INDENT_AMOUNT;
- while (parent_node && visible_depth_of_node (tree_model, parent_node) != 0) {
+ depth = visible_depth_of_node (ecell_view->e_table_model, row) - 1;
+ while (parent_node && depth != 0) {
if (e_tree_model_node_get_next(tree_model, parent_node)) {
gdk_draw_line (drawable, tree_view->gc,
rect.x + offset - INDENT_AMOUNT / 2,
@@ -251,6 +267,7 @@ ect_draw (ECellView *ecell_view, GdkDrawable *drawable,
rect.y + rect.height);
}
parent_node = e_tree_model_node_get_parent (tree_model, parent_node);
+ depth --;
offset -= INDENT_AMOUNT;
}
}
@@ -309,8 +326,9 @@ ect_event (ECellView *ecell_view, GdkEvent *event, int model_col, int view_col,
{
ECellTreeView *tree_view = (ECellTreeView *) ecell_view;
ETreeModel *tree_model = e_cell_tree_get_tree_model (ecell_view->e_table_model, row);
- ETreePath *node = e_cell_tree_get_node (ecell_view->e_table_model, row);
- int offset = offset_of_node (tree_model, node);
+ ETreeTableAdapter *tree_table_adapter = e_cell_tree_get_tree_table_adapter(ecell_view->e_table_model, row);
+ ETreePath node = e_cell_tree_get_node (ecell_view->e_table_model, row);
+ int offset = offset_of_node (ecell_view->e_table_model, row);
switch (event->type) {
case GDK_BUTTON_PRESS: {
@@ -320,9 +338,9 @@ ect_event (ECellView *ecell_view, GdkEvent *event, int model_col, int view_col,
/* only activate the tree control if the click/release happens in the icon's area. */
if (event->button.x > (offset - INDENT_AMOUNT) && event->button.x < offset) {
if (e_tree_model_node_is_expandable (tree_model, node)) {
- e_tree_model_node_set_expanded (tree_model,
- node,
- !e_tree_model_node_is_expanded(tree_model, node));
+ e_tree_table_adapter_node_set_expanded (tree_table_adapter,
+ node,
+ !e_tree_table_adapter_node_is_expanded(tree_table_adapter, node));
return TRUE;
}
}
@@ -399,7 +417,8 @@ ect_max_width (ECellView *ecell_view, int model_col, int view_col)
for (row = 0; row < number_of_rows; row++) {
ETreeModel *tree_model = e_cell_tree_get_tree_model(ecell_view->e_table_model, row);
- ETreePath *node;
+ ETreeTableAdapter *tree_table_adapter = e_cell_tree_get_tree_table_adapter(ecell_view->e_table_model, row);
+ ETreePath node;
GdkPixbuf *node_image;
int node_image_width = 0, node_image_height = 0;
@@ -408,12 +427,12 @@ ect_max_width (ECellView *ecell_view, int model_col, int view_col)
node = e_cell_tree_get_node (ecell_view->e_table_model, row);
- offset = offset_of_node (tree_model, node);
+ offset = offset_of_node (ecell_view->e_table_model, row);
expandable = e_tree_model_node_is_expandable (tree_model, node);
- expanded = e_tree_model_node_is_expanded (tree_model, node);
+ expanded = e_tree_table_adapter_node_is_expanded (tree_table_adapter, node);
subcell_offset = offset;
- node_image = e_tree_model_icon_of_node (tree_model, node);
+ node_image = e_tree_model_icon_at (tree_model, node);
if (node_image) {
node_image_width = gdk_pixbuf_get_width (node_image);
@@ -447,11 +466,11 @@ ect_show_tooltip (ECellView *ecell_view, int model_col, int view_col, int row,
{
ECellTreeView *tree_view = (ECellTreeView *) ecell_view;
ETreeModel *tree_model = e_cell_tree_get_tree_model (ecell_view->e_table_model, row);
- ETreePath *node = e_cell_tree_get_node (ecell_view->e_table_model, row);
- int offset = offset_of_node (tree_model, node);
+ ETreePath node = e_cell_tree_get_node (ecell_view->e_table_model, row);
+ int offset = offset_of_node (ecell_view->e_table_model, row);
GdkPixbuf *node_image;
- node_image = e_tree_model_icon_of_node (tree_model, node);
+ node_image = e_tree_model_icon_at (tree_model, node);
if (node_image)
offset += gdk_pixbuf_get_width (node_image);
@@ -492,14 +511,16 @@ ect_print (ECellView *ecell_view, GnomePrintContext *context,
if (/* XXX only if we're the active sort */ TRUE) {
ETreeModel *tree_model = e_cell_tree_get_tree_model (ecell_view->e_table_model, row);
- ETreePath *node = e_cell_tree_get_node (ecell_view->e_table_model, row);
- int offset = offset_of_node (tree_model, node);
+ ETreeTableAdapter *tree_table_adapter = e_cell_tree_get_tree_table_adapter(ecell_view->e_table_model, row);
+ ETreePath node = e_cell_tree_get_node (ecell_view->e_table_model, row);
+ int offset = offset_of_node (ecell_view->e_table_model, row);
int subcell_offset = offset;
gboolean expandable = e_tree_model_node_is_expandable (tree_model, node);
- gboolean expanded = e_tree_model_node_is_expanded (tree_model, node);
+ gboolean expanded = e_tree_table_adapter_node_is_expanded (tree_table_adapter, node);
/* draw our lines */
if (E_CELL_TREE(tree_view->cell_view.ecell)->draw_lines) {
+ int depth;
if (!e_tree_model_node_is_root (tree_model, node)
|| e_tree_model_node_get_children (tree_model, node, NULL) > 0) {
@@ -512,7 +533,7 @@ ect_print (ECellView *ecell_view, GnomePrintContext *context,
height / 2);
}
- if (visible_depth_of_node (tree_model, node) != 0) {
+ if (visible_depth_of_node (ecell_view->e_table_model, row) != 0) {
gnome_print_moveto (context,
offset - INDENT_AMOUNT / 2,
height);
@@ -527,8 +548,9 @@ ect_print (ECellView *ecell_view, GnomePrintContext *context,
each level if the node has siblings, and drawing the
correct vertical pipe for it's configuration. */
node = e_tree_model_node_get_parent (tree_model, node);
+ depth = visible_depth_of_node (ecell_view->e_table_model, row) - 1;
offset -= INDENT_AMOUNT;
- while (node && visible_depth_of_node (tree_model, node) != 0) {
+ while (node && depth != 0) {
if (e_tree_model_node_get_next(tree_model, node)) {
gnome_print_moveto (context,
offset - INDENT_AMOUNT / 2,
@@ -538,6 +560,7 @@ ect_print (ECellView *ecell_view, GnomePrintContext *context,
0);
}
node = e_tree_model_node_get_parent (tree_model, node);
+ depth --;
offset -= INDENT_AMOUNT;
}
}
diff --git a/widgets/table/e-table-config.c b/widgets/table/e-table-config.c
index f5381a0e4d..96a7ee2f51 100644
--- a/widgets/table/e-table-config.c
+++ b/widgets/table/e-table-config.c
@@ -363,16 +363,18 @@ do_sort_and_group_config_dialog (ETableConfig *config, gboolean is_sort)
config_group_info_update (config);
}
-GtkWidget *
+#if 0
+static GtkWidget *
e_table_proxy_etable_new (void)
{
return gtk_label_new ("Waiting for the ETable/ETree\nmerger to be commited");
}
+#endif
static void
config_button_fields (GtkWidget *widget, ETableConfig *config)
{
- gnome_dialog_run (config->dialog_show_fields);
+ gnome_dialog_run (GNOME_DIALOG(config->dialog_show_fields));
gnome_dialog_close (GNOME_DIALOG (config->dialog_show_fields));
}
diff --git a/widgets/table/e-table-extras.c b/widgets/table/e-table-extras.c
index 05642f1022..cd942193d2 100644
--- a/widgets/table/e-table-extras.c
+++ b/widgets/table/e-table-extras.c
@@ -72,6 +72,15 @@ ete_class_init (GtkObjectClass *klass)
klass->destroy = ete_destroy;
}
+static gint
+e_strint_compare(gconstpointer data1, gconstpointer data2)
+{
+ int int1 = atoi(data1);
+ int int2 = atoi(data2);
+
+ return g_int_compare(GINT_TO_POINTER(int1), GINT_TO_POINTER(int2));
+}
+
static void
ete_init (ETableExtras *extras)
{
@@ -81,6 +90,7 @@ ete_init (ETableExtras *extras)
e_table_extras_add_compare(extras, "string", g_str_compare);
e_table_extras_add_compare(extras, "integer", g_int_compare);
+ e_table_extras_add_compare(extras, "string-integer", e_strint_compare);
e_table_extras_add_cell(extras, "checkbox", e_cell_checkbox_new());
e_table_extras_add_cell(extras, "date", e_cell_date_new (NULL, GTK_JUSTIFY_LEFT));
diff --git a/widgets/table/e-table-item.c b/widgets/table/e-table-item.c
index e4a4126f43..1395e9cb24 100644
--- a/widgets/table/e-table-item.c
+++ b/widgets/table/e-table-item.c
@@ -63,7 +63,6 @@ enum {
static int eti_get_height (ETableItem *eti);
-static int eti_get_minimum_width (ETableItem *eti);
static int eti_row_height (ETableItem *eti, int row);
static void e_table_item_focus (ETableItem *eti, int col, int row, GdkModifierType state);
static void eti_cursor_change (ETableSelectionModel *selection, int row, int col, ETableItem *eti);
@@ -149,6 +148,12 @@ static void
eti_realize_cell_views (ETableItem *eti)
{
int i;
+
+ if (eti->cell_views_realized)
+ return;
+
+ if (!(GTK_OBJECT_FLAGS(eti) & GNOME_CANVAS_ITEM_REALIZED))
+ return;
for (i = 0; i < eti->n_cells; i++)
e_cell_realize (eti->cell_views [i]);
@@ -254,8 +259,7 @@ eti_reflow (GnomeCanvasItem *item, gint flags)
eti->needs_compute_height = 0;
}
if (eti->needs_compute_width) {
- int new_width = eti_get_minimum_width (eti);
- new_width = MAX(new_width, eti->minimum_width);
+ int new_width = e_table_header_total_width(eti->header);
if (new_width != eti->width) {
eti->width = new_width;
e_canvas_item_request_parent_reflow (GNOME_CANVAS_ITEM (eti));
@@ -319,9 +323,9 @@ eti_remove_table_model (ETableItem *eti)
gtk_signal_disconnect (GTK_OBJECT (eti->table_model),
eti->table_model_cell_change_id);
gtk_signal_disconnect (GTK_OBJECT (eti->table_model),
- eti->table_model_row_inserted_id);
+ eti->table_model_rows_inserted_id);
gtk_signal_disconnect (GTK_OBJECT (eti->table_model),
- eti->table_model_row_deleted_id);
+ eti->table_model_rows_deleted_id);
gtk_object_unref (GTK_OBJECT (eti->table_model));
if (eti->source_model)
gtk_object_unref (GTK_OBJECT (eti->source_model));
@@ -330,8 +334,8 @@ eti_remove_table_model (ETableItem *eti)
eti->table_model_change_id = 0;
eti->table_model_row_change_id = 0;
eti->table_model_cell_change_id = 0;
- eti->table_model_row_inserted_id = 0;
- eti->table_model_row_deleted_id = 0;
+ eti->table_model_rows_inserted_id = 0;
+ eti->table_model_rows_deleted_id = 0;
eti->table_model = NULL;
eti->source_model = NULL;
eti->uses_source_model = 0;
@@ -418,14 +422,25 @@ eti_row_height_real (ETableItem *eti, int row)
return max_h;
}
+static void
+confirm_height_cache (ETableItem *eti)
+{
+ int i;
+
+ if (eti->height_cache)
+ return;
+ eti->height_cache = g_new(int, eti->rows);
+ for (i = 0; i < eti->rows; i++) {
+ eti->height_cache[i] = -1;
+ }
+}
+
static gboolean
height_cache_idle(ETableItem *eti)
{
int changed = 0;
int i;
- if (!eti->height_cache) {
- eti->height_cache = g_new(int, eti->rows);
- }
+ confirm_height_cache(eti);
for (i = eti->height_cache_idle_count; i < eti->rows; i++) {
if (eti->height_cache[i] == -1) {
eti_row_height(eti, i);
@@ -457,12 +472,8 @@ free_height_cache (ETableItem *eti)
static void
calculate_height_cache (ETableItem *eti)
{
- int i;
free_height_cache(eti);
- eti->height_cache = g_new(int, eti->rows);
- for (i = 0; i < eti->rows; i++) {
- eti->height_cache[i] = -1;
- }
+ confirm_height_cache(eti);
}
@@ -509,6 +520,7 @@ eti_get_height (ETableItem *eti)
const int rows = eti->rows;
int row;
int height;
+ int height_extra = eti->draw_grid ? 1 : 0;
if (rows == 0)
return 0;
@@ -520,42 +532,29 @@ eti_get_height (ETableItem *eti)
height = 0;
for (row = 0; row < rows; row++) {
if (eti->height_cache[row] == -1) {
- height += (row_height + 1) * (rows - row);
+ height += (row_height + height_extra) * (rows - row);
break;
}
else
- height += eti->height_cache[row] + 1;
+ height += eti->height_cache[row] + height_extra;
}
} else
- height = (eti_row_height (eti, 0) + 1) * rows;
+ height = (eti_row_height (eti, 0) + height_extra) * rows;
/*
* 1 pixel at the top
*/
- return height + 1;
+ return height + height_extra;
}
}
- height = 1;
+ height = height_extra;
for (row = 0; row < rows; row++)
- height += eti_row_height (eti, row) + 1;
+ height += eti_row_height (eti, row) + height_extra;
return height;
}
-static int
-eti_get_minimum_width (ETableItem *eti)
-{
- int width = 0;
- int col;
- for (col = 0; col < eti->cols; col++){
- ETableCol *ecol = e_table_header_get_column (eti->header, col);
-
- width += ecol->min_width;
- }
- return width;
-}
-
static void
eti_item_region_redraw (ETableItem *eti, int x0, int y0, int x1, int y1)
{
@@ -617,11 +616,12 @@ int
e_table_item_row_diff (ETableItem *eti, int start_row, int end_row)
{
int row, total;
+ int height_extra = eti->draw_grid ? 1 : 0;
total = 0;
for (row = start_row; row < end_row; row++)
- total += eti_row_height (eti, row) + 1;
+ total += eti_row_height (eti, row) + height_extra;
return total;
}
@@ -708,14 +708,16 @@ eti_table_model_cell_changed (ETableModel *table_model, int col, int row, ETable
}
static void
-eti_table_model_row_inserted (ETableModel *table_model, int row, ETableItem *eti)
+eti_table_model_rows_inserted (ETableModel *table_model, int row, int count, ETableItem *eti)
{
eti->rows = e_table_model_row_count (eti->table_model);
if (eti->height_cache) {
+ int i;
eti->height_cache = g_renew(int, eti->height_cache, eti->rows);
- memmove(eti->height_cache + row + 1, eti->height_cache + row, (eti->rows - 1 - row) * sizeof(int));
- eti->height_cache[row] = -1;
+ memmove(eti->height_cache + row + count, eti->height_cache + row, (eti->rows - count - row) * sizeof(int));
+ for (i = row; i < row + count; i++)
+ eti->height_cache[i] = -1;
}
eti->needs_compute_height = 1;
@@ -725,12 +727,12 @@ eti_table_model_row_inserted (ETableModel *table_model, int row, ETableItem *eti
}
static void
-eti_table_model_row_deleted (ETableModel *table_model, int row, ETableItem *eti)
+eti_table_model_rows_deleted (ETableModel *table_model, int row, int count, ETableItem *eti)
{
eti->rows = e_table_model_row_count (eti->table_model);
if (eti->height_cache)
- memmove(eti->height_cache + row, eti->height_cache + row + 1, (eti->rows - row) * sizeof(int));
+ memmove(eti->height_cache + row, eti->height_cache + row + count, (eti->rows - row) * sizeof(int));
eti->needs_compute_height = 1;
e_canvas_item_request_reflow (GNOME_CANVAS_ITEM (eti));
@@ -800,13 +802,13 @@ eti_add_table_model (ETableItem *eti, ETableModel *table_model)
GTK_OBJECT (table_model), "model_cell_changed",
GTK_SIGNAL_FUNC (eti_table_model_cell_changed), eti);
- eti->table_model_row_inserted_id = gtk_signal_connect (
- GTK_OBJECT (table_model), "model_row_inserted",
- GTK_SIGNAL_FUNC (eti_table_model_row_inserted), eti);
+ eti->table_model_rows_inserted_id = gtk_signal_connect (
+ GTK_OBJECT (table_model), "model_rows_inserted",
+ GTK_SIGNAL_FUNC (eti_table_model_rows_inserted), eti);
- eti->table_model_row_deleted_id = gtk_signal_connect (
- GTK_OBJECT (table_model), "model_row_deleted",
- GTK_SIGNAL_FUNC (eti_table_model_row_deleted), eti);
+ eti->table_model_rows_deleted_id = gtk_signal_connect (
+ GTK_OBJECT (table_model), "model_rows_deleted",
+ GTK_SIGNAL_FUNC (eti_table_model_rows_deleted), eti);
if (eti->header) {
eti_detach_cell_views (eti);
@@ -851,13 +853,14 @@ eti_header_dim_changed (ETableHeader *eth, int col, ETableItem *eti)
{
eti->needs_compute_width = 1;
e_canvas_item_request_reflow (GNOME_CANVAS_ITEM (eti));
+ eti->needs_redraw = 1;
+ gnome_canvas_item_request_update (GNOME_CANVAS_ITEM (eti));
}
static void
eti_header_structure_changed (ETableHeader *eth, ETableItem *eti)
{
eti->cols = e_table_header_count (eti->header);
- eti->width = e_table_header_total_width (eti->header);
/*
* There should be at least one column
@@ -872,12 +875,14 @@ eti_header_structure_changed (ETableHeader *eth, ETableItem *eti)
eti_realize_cell_views (eti);
} else {
if (eti->table_model) {
- eti_detach_cell_views (eti);
eti_attach_cell_views (eti);
+ eti_realize_cell_views (eti);
}
}
eti->needs_compute_width = 1;
e_canvas_item_request_reflow (GNOME_CANVAS_ITEM (eti));
+ eti->needs_redraw = 1;
+ gnome_canvas_item_request_update (GNOME_CANVAS_ITEM (eti));
}
static int
@@ -992,11 +997,12 @@ eti_set_arg (GtkObject *o, GtkArg *arg, guint arg_id)
case ARG_MINIMUM_WIDTH:
case ARG_WIDTH:
- if (eti->minimum_width == eti->width && GTK_VALUE_DOUBLE (*arg) > eti->width)
- e_canvas_item_request_reflow (GNOME_CANVAS_ITEM (eti));
+ if ((eti->minimum_width == eti->width && GTK_VALUE_DOUBLE (*arg) > eti->width) ||
+ GTK_VALUE_DOUBLE (*arg) < eti->width) {
+ eti->needs_compute_width = 1;
+ e_canvas_item_request_reflow (GNOME_CANVAS_ITEM(eti));
+ }
eti->minimum_width = GTK_VALUE_DOUBLE (*arg);
- if (eti->minimum_width < eti->width)
- e_canvas_item_request_reflow (GNOME_CANVAS_ITEM (eti));
break;
case ARG_CURSOR_ROW:
gtk_object_get(GTK_OBJECT(eti->selection),
@@ -1156,6 +1162,7 @@ eti_realize (GnomeCanvasItem *item)
eti_realize_cell_views (eti);
eti->needs_compute_height = 1;
+ eti->needs_compute_width = 1;
e_canvas_item_request_reflow (GNOME_CANVAS_ITEM (eti));
eti->needs_redraw = 1;
gnome_canvas_item_request_update (GNOME_CANVAS_ITEM (eti));
@@ -1202,6 +1209,7 @@ eti_draw (GnomeCanvasItem *item, GdkDrawable *drawable, int x, int y, int width,
ArtPoint eti_base, eti_base_item, lower_right;
GtkWidget *canvas = GTK_WIDGET(item->canvas);
GdkColor *background;
+ int height_extra = eti->draw_grid ? 1 : 0;
/*
* Clear the background
@@ -1257,10 +1265,10 @@ eti_draw (GnomeCanvasItem *item, GdkDrawable *drawable, int x, int y, int width,
*/
first_row = -1;
y_offset = 0;
- y1 = y2 = floor (eti_base.y) + 1;
+ y1 = y2 = floor (eti_base.y) + height_extra;
for (row = 0; row < rows; row++, y1 = y2){
- y2 += ETI_ROW_HEIGHT (eti, row) + 1;
+ y2 += ETI_ROW_HEIGHT (eti, row) + height_extra;
if (y1 > y + height)
break;
@@ -1290,7 +1298,8 @@ eti_draw (GnomeCanvasItem *item, GdkDrawable *drawable, int x, int y, int width,
drawable, eti->grid_gc,
eti_base.x - x, yd, eti_base.x + eti->width - x, yd);
}
- yd++;
+
+ yd += height_extra;
for (row = first_row; row < last_row; row++){
int xd, height;
@@ -1393,11 +1402,13 @@ eti_draw (GnomeCanvasItem *item, GdkDrawable *drawable, int x, int y, int width,
}
yd += height;
- if (eti->draw_grid)
+ if (eti->draw_grid) {
gdk_draw_line (
drawable, eti->grid_gc,
eti_base.x - x, yd, eti_base.x + eti->width - x, yd);
- yd++;
+
+ yd++;
+ }
}
if (eti->draw_grid){
@@ -1444,6 +1455,8 @@ find_cell (ETableItem *eti, double x, double y, int *col_res, int *row_res, doub
const int rows = eti->rows;
gdouble x1, y1, x2, y2;
int col, row;
+
+ int height_extra = eti->draw_grid ? 1 : 0;
/* FIXME: this routine is inneficient, fix later */
@@ -1479,7 +1492,7 @@ find_cell (ETableItem *eti, double x, double y, int *col_res, int *row_res, doub
if (y < y1)
return FALSE;
- y2 += ETI_ROW_HEIGHT (eti, row) + 1;
+ y2 += ETI_ROW_HEIGHT (eti, row) + height_extra;
if (y <= y2)
break;
@@ -1573,7 +1586,7 @@ _do_tooltip (ETableItem *eti)
eti->tooltip->x = x;
for (i = 0; i < eti->tooltip->row; i++)
- y += (ETI_ROW_HEIGHT (eti, i) + 1);
+ y += (ETI_ROW_HEIGHT (eti, i) + height_extra);
eti->tooltip->y = y;
eti->tooltip->row_height = ETI_ROW_HEIGHT (eti, i);
diff --git a/widgets/table/e-table-item.h b/widgets/table/e-table-item.h
index 3c29691c22..73d7032b1f 100644
--- a/widgets/table/e-table-item.h
+++ b/widgets/table/e-table-item.h
@@ -41,8 +41,8 @@ typedef struct {
int table_model_change_id;
int table_model_row_change_id;
int table_model_cell_change_id;
- int table_model_row_inserted_id;
- int table_model_row_deleted_id;
+ int table_model_rows_inserted_id;
+ int table_model_rows_deleted_id;
int selection_change_id;
int cursor_change_id;
@@ -105,7 +105,7 @@ typedef struct {
void (*cursor_change) (ETableItem *eti, int row);
void (*cursor_activated) (ETableItem *eti, int row);
- void (*double_click) (ETableItem *eti, int row);
+ void (*double_click) (ETableItem *eti, int row, int col, GdkEvent *event);
gint (*right_click) (ETableItem *eti, int row, int col, GdkEvent *event);
gint (*click) (ETableItem *eti, int row, int col, GdkEvent *event);
gint (*key_press) (ETableItem *eti, int row, int col, GdkEvent *event);
diff --git a/widgets/table/e-table-model.c b/widgets/table/e-table-model.c
index 41a55d1a8f..a45150ac73 100644
--- a/widgets/table/e-table-model.c
+++ b/widgets/table/e-table-model.c
@@ -28,8 +28,8 @@ enum {
MODEL_PRE_CHANGE,
MODEL_ROW_CHANGED,
MODEL_CELL_CHANGED,
- MODEL_ROW_INSERTED,
- MODEL_ROW_DELETED,
+ MODEL_ROWS_INSERTED,
+ MODEL_ROWS_DELETED,
ROW_SELECTION,
LAST_SIGNAL
};
@@ -68,6 +68,23 @@ e_table_model_row_count (ETableModel *e_table_model)
}
/**
+ * e_table_model_append_row:
+ * @e_table_model: the table model to append the a row to.
+ * @source:
+ * @row:
+ *
+ */
+void
+e_table_model_append_row (ETableModel *e_table_model, ETableModel *source, int row)
+{
+ g_return_if_fail (e_table_model != NULL);
+ g_return_if_fail (E_IS_TABLE_MODEL (e_table_model));
+
+ if (ETM_CLASS (e_table_model)->append_row)
+ ETM_CLASS (e_table_model)->append_row (e_table_model, source, row);
+}
+
+/**
* e_table_value_at:
* @e_table_model: the e-table-model to operate on
* @col: column in the model to pull data from.
@@ -131,46 +148,6 @@ e_table_model_is_cell_editable (ETableModel *e_table_model, int col, int row)
return ETM_CLASS (e_table_model)->is_cell_editable (e_table_model, col, row);
}
-/**
- * e_table_model_append_row:
- * @e_table_model: the table model to append the a row to.
- * @source:
- * @row:
- *
- */
-void
-e_table_model_append_row (ETableModel *e_table_model, ETableModel *source, int row)
-{
- g_return_if_fail (e_table_model != NULL);
- g_return_if_fail (E_IS_TABLE_MODEL (e_table_model));
-
- if (ETM_CLASS (e_table_model)->append_row)
- ETM_CLASS (e_table_model)->append_row (e_table_model, source, row);
-}
-
-const char *
-e_table_model_row_sort_group(ETableModel *e_table_model, int row)
-{
- g_return_val_if_fail (e_table_model != NULL, "/");
- g_return_val_if_fail (E_IS_TABLE_MODEL (e_table_model), "/");
-
- if (ETM_CLASS (e_table_model)->row_sort_group)
- return ETM_CLASS (e_table_model)->row_sort_group (e_table_model, row);
- else
- return "/";
-}
-
-gboolean
-e_table_model_has_sort_group(ETableModel *e_table_model)
-{
- g_return_val_if_fail (e_table_model != NULL, FALSE);
- g_return_val_if_fail (E_IS_TABLE_MODEL (e_table_model), FALSE);
-
- if (ETM_CLASS (e_table_model)->has_sort_group)
- return ETM_CLASS (e_table_model)->has_sort_group (e_table_model);
- else
- return FALSE;
-}
void *
e_table_model_duplicate_value (ETableModel *e_table_model, int col, const void *value)
@@ -194,6 +171,30 @@ e_table_model_free_value (ETableModel *e_table_model, int col, void *value)
ETM_CLASS (e_table_model)->free_value (e_table_model, col, value);
}
+char *
+e_table_model_get_save_id(ETableModel *e_table_model, int row)
+{
+ g_return_val_if_fail (e_table_model != NULL, "/");
+ g_return_val_if_fail (E_IS_TABLE_MODEL (e_table_model), "/");
+
+ if (ETM_CLASS (e_table_model)->get_save_id)
+ return ETM_CLASS (e_table_model)->get_save_id (e_table_model, row);
+ else
+ return NULL;
+}
+
+gboolean
+e_table_model_has_save_id(ETableModel *e_table_model)
+{
+ g_return_val_if_fail (e_table_model != NULL, FALSE);
+ g_return_val_if_fail (E_IS_TABLE_MODEL (e_table_model), FALSE);
+
+ if (ETM_CLASS (e_table_model)->has_save_id)
+ return ETM_CLASS (e_table_model)->has_save_id (e_table_model);
+ else
+ return FALSE;
+}
+
void *
e_table_model_initialize_value (ETableModel *e_table_model, int col)
{
@@ -277,44 +278,46 @@ e_table_model_class_init (GtkObjectClass *object_class)
gtk_marshal_NONE__INT_INT,
GTK_TYPE_NONE, 2, GTK_TYPE_INT, GTK_TYPE_INT);
- e_table_model_signals [MODEL_ROW_INSERTED] =
- gtk_signal_new ("model_row_inserted",
+ e_table_model_signals [MODEL_ROWS_INSERTED] =
+ gtk_signal_new ("model_rows_inserted",
GTK_RUN_LAST,
object_class->type,
- GTK_SIGNAL_OFFSET (ETableModelClass, model_row_inserted),
- gtk_marshal_NONE__INT,
- GTK_TYPE_NONE, 1, GTK_TYPE_INT);
+ GTK_SIGNAL_OFFSET (ETableModelClass, model_rows_inserted),
+ gtk_marshal_NONE__INT_INT,
+ GTK_TYPE_NONE, 2, GTK_TYPE_INT, GTK_TYPE_INT);
- e_table_model_signals [MODEL_ROW_DELETED] =
- gtk_signal_new ("model_row_deleted",
+ e_table_model_signals [MODEL_ROWS_DELETED] =
+ gtk_signal_new ("model_rows_deleted",
GTK_RUN_LAST,
object_class->type,
- GTK_SIGNAL_OFFSET (ETableModelClass, model_row_deleted),
- gtk_marshal_NONE__INT,
- GTK_TYPE_NONE, 1, GTK_TYPE_INT);
+ GTK_SIGNAL_OFFSET (ETableModelClass, model_rows_deleted),
+ gtk_marshal_NONE__INT_INT,
+ GTK_TYPE_NONE, 2, GTK_TYPE_INT, GTK_TYPE_INT);
gtk_object_class_add_signals (object_class, e_table_model_signals, LAST_SIGNAL);
klass->column_count = NULL;
klass->row_count = NULL;
+ klass->append_row = NULL;
+
klass->value_at = NULL;
klass->set_value_at = NULL;
klass->is_cell_editable = NULL;
- klass->append_row = NULL;
- klass->row_sort_group = NULL;
- klass->has_sort_group = NULL;
+ klass->get_save_id = NULL;
+ klass->has_save_id = NULL;
klass->duplicate_value = NULL;
klass->free_value = NULL;
klass->initialize_value = NULL;
klass->value_is_empty = NULL;
klass->value_to_string = NULL;
+
klass->model_changed = NULL;
klass->model_row_changed = NULL;
klass->model_cell_changed = NULL;
- klass->model_row_inserted = NULL;
- klass->model_row_deleted = NULL;
+ klass->model_rows_inserted = NULL;
+ klass->model_rows_deleted = NULL;
}
@@ -444,17 +447,18 @@ e_table_model_cell_changed (ETableModel *e_table_model, int col, int row)
}
/**
- * e_table_model_row_inserted:
+ * e_table_model_rows_inserted:
* @e_table_model: the table model to notify of the change
* @row: the row that was inserted into the model.
+ * @count: The number of rows that were inserted.
*
* Use this function to notify any views of the table model that
- * the row @row has been inserted into the model. This function
- * will emit the "model_row_inserted" signal on the @e_table_model
- * object
+ * @count rows at row @row have been inserted into the model. This
+ * function will emit the "model_rows_inserted" signal on the
+ * @e_table_model object
*/
void
-e_table_model_row_inserted (ETableModel *e_table_model, int row)
+e_table_model_rows_inserted (ETableModel *e_table_model, int row, int count)
{
g_return_if_fail (e_table_model != NULL);
g_return_if_fail (E_IS_TABLE_MODEL (e_table_model));
@@ -463,22 +467,38 @@ e_table_model_row_inserted (ETableModel *e_table_model, int row)
d(g_print("Emitting row_inserted on model 0x%p, row %d.\n", e_table_model, row));
d(depth++);
gtk_signal_emit (GTK_OBJECT (e_table_model),
- e_table_model_signals [MODEL_ROW_INSERTED], row);
+ e_table_model_signals [MODEL_ROWS_INSERTED], row, count);
d(depth--);
}
/**
+ * e_table_model_row_inserted:
+ * @e_table_model: the table model to notify of the change
+ * @row: the row that was inserted into the model.
+ *
+ * Use this function to notify any views of the table model that the
+ * row @row has been inserted into the model. This function will emit
+ * the "model_rows_inserted" signal on the @e_table_model object
+ */
+void
+e_table_model_row_inserted (ETableModel *e_table_model, int row)
+{
+ e_table_model_rows_inserted(e_table_model, row, 1);
+}
+
+/**
* e_table_model_row_deleted:
* @e_table_model: the table model to notify of the change
* @row: the row that was deleted
+ * @count: The number of rows deleted
*
* Use this function to notify any views of the table model that
- * the row @row has been deleted from the model. This function
- * will emit the "model_row_deleted" signal on the @e_table_model
- * object
+ * @count rows at row @row have been deleted from the model. This
+ * function will emit the "model_rows_deleted" signal on the
+ * @e_table_model object
*/
void
-e_table_model_row_deleted (ETableModel *e_table_model, int row)
+e_table_model_rows_deleted (ETableModel *e_table_model, int row, int count)
{
g_return_if_fail (e_table_model != NULL);
g_return_if_fail (E_IS_TABLE_MODEL (e_table_model));
@@ -487,6 +507,21 @@ e_table_model_row_deleted (ETableModel *e_table_model, int row)
d(g_print("Emitting row_deleted on model 0x%p, row %d.\n", e_table_model, row));
d(depth++);
gtk_signal_emit (GTK_OBJECT (e_table_model),
- e_table_model_signals [MODEL_ROW_DELETED], row);
+ e_table_model_signals [MODEL_ROWS_DELETED], row, count);
d(depth--);
}
+
+/**
+ * e_table_model_row_deleted:
+ * @e_table_model: the table model to notify of the change
+ * @row: the row that was deleted
+ *
+ * Use this function to notify any views of the table model that the
+ * row @row has been deleted from the model. This function will emit
+ * the "model_rows_deleted" signal on the @e_table_model object
+ */
+void
+e_table_model_row_deleted (ETableModel *e_table_model, int row)
+{
+ e_table_model_rows_deleted(e_table_model, row, 1);
+}
diff --git a/widgets/table/e-table-model.h b/widgets/table/e-table-model.h
index 21febaf992..8cce0054d3 100644
--- a/widgets/table/e-table-model.h
+++ b/widgets/table/e-table-model.h
@@ -26,14 +26,14 @@ typedef struct {
*/
int (*column_count) (ETableModel *etm);
int (*row_count) (ETableModel *etm);
+ void (*append_row) (ETableModel *etm, ETableModel *source, int row);
+
void *(*value_at) (ETableModel *etm, int col, int row);
void (*set_value_at) (ETableModel *etm, int col, int row, const void *value);
gboolean (*is_cell_editable) (ETableModel *etm, int col, int row);
- void (*append_row) (ETableModel *etm, ETableModel *source, int row);
- /* the sort group id for this row */
- const char *(*row_sort_group) (ETableModel *etm, int row);
- gboolean (*has_sort_group) (ETableModel *etm);
+ char *(*get_save_id) (ETableModel *etm, int row);
+ gboolean (*has_save_id) (ETableModel *etm);
/* Allocate a copy of the given value. */
void *(*duplicate_value) (ETableModel *etm, int col, const void *value);
@@ -45,6 +45,7 @@ typedef struct {
gboolean (*value_is_empty) (ETableModel *etm, int col, const void *value);
/* Return an allocated string. */
char *(*value_to_string) (ETableModel *etm, int col, const void *value);
+
/*
* Signals
@@ -58,13 +59,13 @@ typedef struct {
* A row inserted: row_inserted
* A row deleted: row_deleted
*/
- void (*model_pre_change) (ETableModel *etm);
+ void (*model_pre_change) (ETableModel *etm);
- void (*model_changed) (ETableModel *etm);
- void (*model_row_changed) (ETableModel *etm, int row);
- void (*model_cell_changed) (ETableModel *etm, int col, int row);
- void (*model_row_inserted) (ETableModel *etm, int row);
- void (*model_row_deleted) (ETableModel *etm, int row);
+ void (*model_changed) (ETableModel *etm);
+ void (*model_row_changed) (ETableModel *etm, int row);
+ void (*model_cell_changed) (ETableModel *etm, int col, int row);
+ void (*model_rows_inserted) (ETableModel *etm, int row, int count);
+ void (*model_rows_deleted) (ETableModel *etm, int row, int count);
} ETableModelClass;
GtkType e_table_model_get_type (void);
@@ -72,13 +73,15 @@ GtkType e_table_model_get_type (void);
int e_table_model_column_count (ETableModel *e_table_model);
const char *e_table_model_column_name (ETableModel *e_table_model, int col);
int e_table_model_row_count (ETableModel *e_table_model);
+void e_table_model_append_row (ETableModel *e_table_model, ETableModel *source, int row);
+
void *e_table_model_value_at (ETableModel *e_table_model, int col, int row);
void e_table_model_set_value_at (ETableModel *e_table_model, int col, int row, const void *value);
gboolean e_table_model_is_cell_editable (ETableModel *e_table_model, int col, int row);
-void e_table_model_append_row (ETableModel *e_table_model, ETableModel *source, int row);
-const char *e_table_model_row_sort_group (ETableModel *e_table_model, int row);
-gboolean e_table_model_has_sort_group (ETableModel *e_table_model);
+char *e_table_model_get_save_id (ETableModel *etm, int row);
+gboolean e_table_model_has_save_id (ETableModel *etm);
+
void *e_table_model_duplicate_value (ETableModel *e_table_model, int col, const void *value);
void e_table_model_free_value (ETableModel *e_table_model, int col, void *value);
@@ -93,6 +96,9 @@ void e_table_model_pre_change (ETableModel *e_table_model);
void e_table_model_changed (ETableModel *e_table_model);
void e_table_model_row_changed (ETableModel *e_table_model, int row);
void e_table_model_cell_changed (ETableModel *e_table_model, int col, int row);
+void e_table_model_rows_inserted (ETableModel *e_table_model, int row, int count);
+void e_table_model_rows_deleted (ETableModel *e_table_model, int row, int count);
+
void e_table_model_row_inserted (ETableModel *e_table_model, int row);
void e_table_model_row_deleted (ETableModel *e_table_model, int row);
diff --git a/widgets/table/e-table-scrolled.h b/widgets/table/e-table-scrolled.h
index 9b2d2a510e..82643b214d 100644
--- a/widgets/table/e-table-scrolled.h
+++ b/widgets/table/e-table-scrolled.h
@@ -4,9 +4,7 @@
#include <gal/widgets/e-scroll-frame.h>
#include <gal/e-table/e-table-model.h>
-#include <gal/e-table/e-table-header.h>
#include <gal/e-table/e-table.h>
-#include <gal/widgets/e-printable.h>
BEGIN_GNOME_DECLS
diff --git a/widgets/table/e-table-selection-model.c b/widgets/table/e-table-selection-model.c
index a2a0f99aa9..faeb1fad31 100644
--- a/widgets/table/e-table-selection-model.c
+++ b/widgets/table/e-table-selection-model.c
@@ -26,35 +26,125 @@ enum {
ARG_MODEL,
};
+#if 0
+static void
+save_to_hash(int model_row, gpointer closure)
+{
+ ETableSelectionModel *etsm = closure;
+ gchar *key = e_table_model_get_save_id(etsm->model, model_row);
+
+ g_hash_table_insert(etsm->hash, key, key);
+}
+#endif
+
+static void
+free_key(gpointer key, gpointer value, gpointer closure)
+{
+ g_free(key);
+}
+
+static void
+free_hash(ETableSelectionModel *etsm)
+{
+ if (etsm->hash) {
+ g_hash_table_foreach(etsm->hash, free_key, NULL);
+ g_hash_table_destroy(etsm->hash);
+ etsm->hash = NULL;
+ }
+ g_free(etsm->cursor_id);
+ etsm->cursor_id = NULL;
+}
+
+static void
+model_pre_change (ETableModel *etm, ETableSelectionModel *etsm)
+{
+ free_hash(etsm);
+
+#if 0
+ if (etsm->model && e_table_model_has_save_id(etsm->model)) {
+ gint cursor_row;
+ etsm->hash = g_hash_table_new(g_str_hash, g_str_equal);
+ e_selection_model_foreach(E_SELECTION_MODEL(etsm), save_to_hash, etsm);
+ gtk_object_get(GTK_OBJECT(etsm),
+ "cursor_row", &cursor_row,
+ NULL);
+ if (cursor_row != -1) {
+ etsm->cursor_id = e_table_model_get_save_id(etm, cursor_row);
+ }
+ }
+#endif
+}
+
static void
model_changed(ETableModel *etm, ETableSelectionModel *etsm)
{
e_selection_model_clear(E_SELECTION_MODEL(etsm));
+
+#if 0
+ if (etm && e_table_model_has_save_id(etm)) {
+ int row_count = e_table_model_row_count(etm);
+ int i;
+ if (e_selection_model_confirm_row_count(E_SELECTION_MODEL(etsm))) {
+ for (i = 0; i < row_count; i++) {
+ char *save_id = e_table_model_get_save_id(etm, i);
+ if (g_hash_table_lookup(etsm->hash, save_id))
+ e_selection_model_change_one_row(E_SELECTION_MODEL(etsm), i, TRUE);
+ if (etsm->cursor_id && !strcmp(etsm->cursor_id, save_id)) {
+ e_selection_model_change_cursor(E_SELECTION_MODEL(etsm), i);
+ g_free(etsm->cursor_id);
+ etsm->cursor_id = NULL;
+ }
+ g_free(save_id);
+ }
+ }
+ }
+#endif
+
+ if (etsm->hash)
+ free_hash(etsm);
+}
+
+static void
+model_row_changed(ETableModel *etm, int row, ETableSelectionModel *etsm)
+{
+ if (etsm->hash)
+ free_hash(etsm);
+}
+
+static void
+model_cell_changed(ETableModel *etm, int col, int row, ETableSelectionModel *etsm)
+{
+ if (etsm->hash)
+ free_hash(etsm);
}
#if 1
static void
-model_row_inserted(ETableModel *etm, int row, ETableSelectionModel *etsm)
+model_rows_inserted(ETableModel *etm, int row, int count, ETableSelectionModel *etsm)
{
- e_selection_model_insert_row(E_SELECTION_MODEL(etsm), row);
+ e_selection_model_insert_rows(E_SELECTION_MODEL(etsm), row, count);
+ if (etsm->hash)
+ free_hash(etsm);
}
static void
-model_row_deleted(ETableModel *etm, int row, ETableSelectionModel *etsm)
+model_rows_deleted(ETableModel *etm, int row, int count, ETableSelectionModel *etsm)
{
- e_selection_model_delete_row(E_SELECTION_MODEL(etsm), row);
+ e_selection_model_delete_rows(E_SELECTION_MODEL(etsm), row, count);
+ if (etsm->hash)
+ free_hash(etsm);
}
#else
static void
-model_row_inserted(ETableModel *etm, int row, ETableSelectionModel *etsm)
+model_rows_inserted(ETableModel *etm, int row, int count, ETableSelectionModel *etsm)
{
model_changed(etm, etsm);
}
static void
-model_row_deleted(ETableModel *etm, int row, ETableSelectionModel *etsm)
+model_rows_deleted(ETableModel *etm, int row, int count, ETableSelectionModel *etsm)
{
model_changed(etm, etsm);
}
@@ -66,12 +156,18 @@ add_model(ETableSelectionModel *etsm, ETableModel *model)
etsm->model = model;
if (model) {
gtk_object_ref(GTK_OBJECT(model));
+ etsm->model_pre_change_id = gtk_signal_connect(GTK_OBJECT(model), "model_pre_change",
+ GTK_SIGNAL_FUNC(model_pre_change), etsm);
etsm->model_changed_id = gtk_signal_connect(GTK_OBJECT(model), "model_changed",
GTK_SIGNAL_FUNC(model_changed), etsm);
- etsm->model_row_inserted_id = gtk_signal_connect(GTK_OBJECT(model), "model_row_inserted",
- GTK_SIGNAL_FUNC(model_row_inserted), etsm);
- etsm->model_row_deleted_id = gtk_signal_connect(GTK_OBJECT(model), "model_row_deleted",
- GTK_SIGNAL_FUNC(model_row_deleted), etsm);
+ etsm->model_row_changed_id = gtk_signal_connect(GTK_OBJECT(model), "model_row_changed",
+ GTK_SIGNAL_FUNC(model_row_changed), etsm);
+ etsm->model_cell_changed_id = gtk_signal_connect(GTK_OBJECT(model), "model_cell_changed",
+ GTK_SIGNAL_FUNC(model_cell_changed), etsm);
+ etsm->model_rows_inserted_id = gtk_signal_connect(GTK_OBJECT(model), "model_rows_inserted",
+ GTK_SIGNAL_FUNC(model_rows_inserted), etsm);
+ etsm->model_rows_deleted_id = gtk_signal_connect(GTK_OBJECT(model), "model_rows_deleted",
+ GTK_SIGNAL_FUNC(model_rows_deleted), etsm);
}
}
@@ -80,11 +176,17 @@ drop_model(ETableSelectionModel *etsm)
{
if (etsm->model) {
gtk_signal_disconnect(GTK_OBJECT(etsm->model),
+ etsm->model_pre_change_id);
+ gtk_signal_disconnect(GTK_OBJECT(etsm->model),
etsm->model_changed_id);
gtk_signal_disconnect(GTK_OBJECT(etsm->model),
- etsm->model_row_inserted_id);
+ etsm->model_row_changed_id);
+ gtk_signal_disconnect(GTK_OBJECT(etsm->model),
+ etsm->model_cell_changed_id);
+ gtk_signal_disconnect(GTK_OBJECT(etsm->model),
+ etsm->model_rows_inserted_id);
gtk_signal_disconnect(GTK_OBJECT(etsm->model),
- etsm->model_row_deleted_id);
+ etsm->model_rows_deleted_id);
gtk_object_unref(GTK_OBJECT(etsm->model));
}
etsm->model = NULL;
@@ -98,6 +200,7 @@ etsm_destroy (GtkObject *object)
etsm = E_TABLE_SELECTION_MODEL (object);
drop_model(etsm);
+ free_hash(etsm);
if (GTK_OBJECT_CLASS(parent_class)->destroy)
GTK_OBJECT_CLASS(parent_class)->destroy (object);
@@ -132,6 +235,8 @@ static void
e_table_selection_model_init (ETableSelectionModel *selection)
{
selection->model = NULL;
+ selection->hash = NULL;
+ selection->cursor_id = NULL;
}
static void
diff --git a/widgets/table/e-table-selection-model.h b/widgets/table/e-table-selection-model.h
index 42333b2493..02b1949257 100644
--- a/widgets/table/e-table-selection-model.h
+++ b/widgets/table/e-table-selection-model.h
@@ -23,12 +23,19 @@ typedef struct {
ETableModel *model;
+ guint model_pre_change_id;
guint model_changed_id;
- guint model_row_inserted_id, model_row_deleted_id;
+ guint model_row_changed_id;
+ guint model_cell_changed_id;
+ guint model_rows_inserted_id;
+ guint model_rows_deleted_id;
guint frozen : 1;
guint selection_model_changed : 1;
guint group_info_changed : 1;
+
+ GHashTable *hash;
+ gchar *cursor_id;
} ETableSelectionModel;
typedef struct {
diff --git a/widgets/table/e-table-simple.h b/widgets/table/e-table-simple.h
index cf5ec6d4f2..f0f949afc0 100644
--- a/widgets/table/e-table-simple.h
+++ b/widgets/table/e-table-simple.h
@@ -16,10 +16,12 @@ extern "C" {
typedef int (*ETableSimpleColumnCountFn) (ETableModel *etm, void *data);
typedef int (*ETableSimpleRowCountFn) (ETableModel *etm, void *data);
+typedef void (*ETableSimpleAppendRowFn) (ETableModel *etm, ETableModel *model, int row, void *data);
+
typedef void *(*ETableSimpleValueAtFn) (ETableModel *etm, int col, int row, void *data);
typedef void (*ETableSimpleSetValueAtFn) (ETableModel *etm, int col, int row, const void *val, void *data);
typedef gboolean (*ETableSimpleIsCellEditableFn) (ETableModel *etm, int col, int row, void *data);
-typedef void (*ETableSimpleAppendRowFn) (ETableModel *etm, ETableModel *model, int row, void *data);
+
typedef void *(*ETableSimpleDuplicateValueFn) (ETableModel *etm, int col, const void *val, void *data);
typedef void (*ETableSimpleFreeValueFn) (ETableModel *etm, int col, void *val, void *data);
typedef void *(*ETableSimpleInitializeValueFn) (ETableModel *etm, int col, void *data);
@@ -31,15 +33,17 @@ typedef struct {
ETableSimpleColumnCountFn col_count;
ETableSimpleRowCountFn row_count;
+ ETableSimpleAppendRowFn append_row;
+
ETableSimpleValueAtFn value_at;
ETableSimpleSetValueAtFn set_value_at;
ETableSimpleIsCellEditableFn is_cell_editable;
+
ETableSimpleDuplicateValueFn duplicate_value;
ETableSimpleFreeValueFn free_value;
ETableSimpleInitializeValueFn initialize_value;
ETableSimpleValueIsEmptyFn value_is_empty;
ETableSimpleValueToStringFn value_to_string;
- ETableSimpleAppendRowFn append_row;
void *data;
} ETableSimple;
@@ -51,9 +55,11 @@ GtkType e_table_simple_get_type (void);
ETableModel *e_table_simple_new (ETableSimpleColumnCountFn col_count,
ETableSimpleRowCountFn row_count,
+
ETableSimpleValueAtFn value_at,
ETableSimpleSetValueAtFn set_value_at,
ETableSimpleIsCellEditableFn is_cell_editable,
+
ETableSimpleDuplicateValueFn duplicate_value,
ETableSimpleFreeValueFn free_value,
ETableSimpleInitializeValueFn initialize_value,
diff --git a/widgets/table/e-table-sorted.c b/widgets/table/e-table-sorted.c
index a876ed385c..63ccc570b6 100644
--- a/widgets/table/e-table-sorted.c
+++ b/widgets/table/e-table-sorted.c
@@ -31,8 +31,8 @@ static void ets_sort (ETableSorted *ets);
static void ets_proxy_model_changed (ETableSubset *etss, ETableModel *source);
static void ets_proxy_model_row_changed (ETableSubset *etss, ETableModel *source, int row);
static void ets_proxy_model_cell_changed (ETableSubset *etss, ETableModel *source, int col, int row);
-static void ets_proxy_model_row_inserted (ETableSubset *etss, ETableModel *source, int row);
-static void ets_proxy_model_row_deleted (ETableSubset *etss, ETableModel *source, int row);
+static void ets_proxy_model_rows_inserted (ETableSubset *etss, ETableModel *source, int row, int count);
+static void ets_proxy_model_rows_deleted (ETableSubset *etss, ETableModel *source, int row, int count);
static void
ets_destroy (GtkObject *object)
@@ -68,8 +68,8 @@ ets_class_init (GtkObjectClass *object_class)
etss_class->proxy_model_changed = ets_proxy_model_changed;
etss_class->proxy_model_row_changed = ets_proxy_model_row_changed;
etss_class->proxy_model_cell_changed = ets_proxy_model_cell_changed;
- etss_class->proxy_model_row_inserted = ets_proxy_model_row_inserted;
- etss_class->proxy_model_row_deleted = ets_proxy_model_row_deleted;
+ etss_class->proxy_model_rows_inserted = ets_proxy_model_rows_inserted;
+ etss_class->proxy_model_rows_deleted = ets_proxy_model_rows_deleted;
object_class->destroy = ets_destroy;
}
@@ -172,14 +172,14 @@ static void
ets_proxy_model_cell_changed (ETableSubset *subset, ETableModel *source, int col, int row)
{
ETableSorted *ets = E_TABLE_SORTED(subset);
- if (e_table_sorting_utils_affects_sort(source, ets->sort_info, ets->full_header, col))
+ if (e_table_sorting_utils_affects_sort(ets->sort_info, ets->full_header, col))
ets_proxy_model_row_changed(subset, source, row);
else if (ets_parent_class->proxy_model_cell_changed)
(ets_parent_class->proxy_model_cell_changed) (subset, source, col, row);
}
static void
-ets_proxy_model_row_inserted (ETableSubset *etss, ETableModel *source, int row)
+ets_proxy_model_rows_inserted (ETableSubset *etss, ETableModel *source, int row, int count)
{
ETableModel *etm = E_TABLE_MODEL(etss);
ETableSorted *ets = E_TABLE_SORTED(etss);
@@ -189,64 +189,69 @@ ets_proxy_model_row_inserted (ETableSubset *etss, ETableModel *source, int row)
for (i = 0; i < etss->n_map; i++) {
if (etss->map_table[i] >= row)
- etss->map_table[i] ++;
+ etss->map_table[i] += count;
}
- etss->map_table = g_realloc (etss->map_table, (etss->n_map + 1) * sizeof(int));
-
- i = etss->n_map;
- if (ets->sort_idle_id == 0) {
- /* this is to see if we're inserting a lot of things between idle loops.
- If we are, we're busy, its faster to just append and perform a full sort later */
- ets->insert_count++;
- if (ets->insert_count > ETS_INSERT_MAX) {
- /* schedule a sort, and append instead */
- ets->sort_idle_id = g_idle_add_full(50, (GSourceFunc) ets_sort_idle, ets, NULL);
- } else {
- /* make sure we have an idle handler to reset the count every now and then */
- if (ets->insert_idle_id == 0) {
- ets->insert_idle_id = g_idle_add_full(40, (GSourceFunc) ets_insert_idle, ets, NULL);
+ etss->map_table = g_realloc (etss->map_table, (etss->n_map + count) * sizeof(int));
+
+ for (; count > 0; count --) {
+ i = etss->n_map;
+ if (ets->sort_idle_id == 0) {
+ /* this is to see if we're inserting a lot of things between idle loops.
+ If we are, we're busy, its faster to just append and perform a full sort later */
+ ets->insert_count++;
+ if (ets->insert_count > ETS_INSERT_MAX) {
+ /* schedule a sort, and append instead */
+ ets->sort_idle_id = g_idle_add_full(50, (GSourceFunc) ets_sort_idle, ets, NULL);
+ } else {
+ /* make sure we have an idle handler to reset the count every now and then */
+ if (ets->insert_idle_id == 0) {
+ ets->insert_idle_id = g_idle_add_full(40, (GSourceFunc) ets_insert_idle, ets, NULL);
+ }
+ i = e_table_sorting_utils_insert(etss->source, ets->sort_info, ets->full_header, etss->map_table, etss->n_map, row);
+ memmove(etss->map_table + i + 1, etss->map_table + i, (etss->n_map - i) * sizeof(int));
}
- i = e_table_sorting_utils_insert(etss->source, ets->sort_info, ets->full_header, etss->map_table, etss->n_map, row);
- memmove(etss->map_table + i + 1, etss->map_table + i, (etss->n_map - i) * sizeof(int));
}
- }
- etss->map_table[i] = row;
- etss->n_map++;
+ etss->map_table[i] = row;
+ etss->n_map++;
- e_table_model_row_inserted (etm, i);
- d(g_print("inserted row %d", row));
+ e_table_model_row_inserted (etm, i);
+ d(g_print("inserted row %d", row));
+ }
d(e_table_subset_print_debugging(etss));
}
static void
-ets_proxy_model_row_deleted (ETableSubset *etss, ETableModel *source, int row)
+ets_proxy_model_rows_deleted (ETableSubset *etss, ETableModel *source, int row, int count)
{
ETableModel *etm = E_TABLE_MODEL(etss);
int i;
gboolean shift;
+ int j;
shift = row == etss->n_map - 1;
- for (i = 0; i < etss->n_map; i++){
- if (etss->map_table[i] == row) {
- e_table_model_pre_change (etm);
- memmove (etss->map_table + i, etss->map_table + i + 1, (etss->n_map - i - 1) * sizeof(int));
- etss->n_map --;
- if (shift)
- e_table_model_row_deleted (etm, i);
+ for (j = 0; j < count; j++) {
+ for (i = 0; i < etss->n_map; i++){
+ if (etss->map_table[i] == row) {
+ e_table_model_pre_change (etm);
+ memmove (etss->map_table + i, etss->map_table + i + 1, (etss->n_map - i - 1) * sizeof(int));
+ etss->n_map --;
+ if (shift)
+ e_table_model_row_deleted (etm, i);
+ }
}
}
if (!shift) {
for (i = 0; i < etss->n_map; i++) {
if (etss->map_table[i] >= row)
- etss->map_table[i] --;
+ etss->map_table[i] -= count;
}
e_table_model_changed (etm);
}
- d(g_print("deleted row %d", row));
+ d(g_print("deleted row %d count %d", row, count));
d(e_table_subset_print_debugging(etss));
}
diff --git a/widgets/table/e-table-sorter.c b/widgets/table/e-table-sorter.c
index 2dbbddf366..3fac99b2cb 100644
--- a/widgets/table/e-table-sorter.c
+++ b/widgets/table/e-table-sorter.c
@@ -241,177 +241,6 @@ ets_clean(ETableSorter *ets)
ets->needs_sorting = -1;
}
-struct _group_info {
- char *group;
- int row;
-};
-
-struct _rowinfo {
- int row;
- struct _subinfo *subinfo;
- struct _group_info *groupinfo;
-};
-
-struct _subinfo {
- int start;
- GArray *rowsort; /* an array of row info's */
-};
-
-/* builds the info needed to sort everything */
-static struct _subinfo *
-ets_sort_build_subset(ETableSorter *ets, struct _group_info *groupinfo, int start, int *end)
-{
- int rows = e_table_model_row_count (ets->source);
- int i, lastinsert;
- GArray *rowsort = g_array_new(0, 0, sizeof(struct _rowinfo));
- struct _subinfo *subinfo, *newsub;
- char *id, *newid;
- int idlen, newidlen;
- int cmp;
- int cmplen;
-
- subinfo = g_malloc0(sizeof(*subinfo));
- subinfo->rowsort = rowsort;
- subinfo->start = start;
- lastinsert = -1;
- id = groupinfo[start].group;
- newid = strrchr(id, '/');
- idlen = strlen(id);
- if (newid)
- cmplen = newid-id;
- else
- cmplen = idlen;
- d(printf("%d scanning level %s\n", start, id));
- for (i=start;i<rows;i++) {
- newid = groupinfo[i].group;
- newidlen = strlen(newid);
- d(printf("%d checking group %s\n", start, newid));
- cmp = strncmp(id, newid, cmplen);
- /* check for common parent */
- if (idlen == newidlen && cmp == 0) {
- struct _rowinfo rowinfo;
-
- d(printf("%d Same parent\n", start));
- rowinfo.row = groupinfo[i].row;
- rowinfo.subinfo = NULL;
- rowinfo.groupinfo = &groupinfo[i];
- lastinsert = rowsort->len;
- g_array_append_val(rowsort, rowinfo);
-#ifdef DEBUG
- total++;
-#endif
- } else if (newidlen > idlen) {
- /* must be a new subtree */
- d(printf("%d checking subtree instead\n", start));
- newsub = ets_sort_build_subset(ets, groupinfo, i, &i);
- d(printf("found %d nodes in subtree\n", newsub->rowsort->len));
- g_array_index(rowsort, struct _rowinfo, lastinsert).subinfo = newsub;
- } else {
- i--;
- break;
- }
- }
- if (end)
- *end = i;
- d(printf("finished level %s start was %d end was %d\n", id, start, i));
- return subinfo;
-}
-
-/* sort each level, and then sort each level below that level (once we know
- where the sublevel will fit in the overall list) */
-static int
-ets_sort_subset(ETableSorter *ets, struct _subinfo *subinfo, int startoffset)
-{
- GArray *rowsort = subinfo->rowsort;
- int offset, i;
-
- d(printf("sorting subset start %d rows %d\n", startoffset, rowsort->len));
-
- /* first, sort the actual data */
- qsort(rowsort->data, rowsort->len, sizeof(struct _rowinfo), qsort_callback);
-
- /* then put it back in the map table, where appropriate */
- offset = startoffset;
- for (i=0;i<rowsort->len;i++) {
- struct _rowinfo *rowinfo;
-
- d(printf("setting offset %d\n", offset));
-
- rowinfo = &g_array_index(rowsort, struct _rowinfo, i);
- ets->sorted[offset] = rowinfo->row;
- if (rowinfo->subinfo) {
- offset = ets_sort_subset(ets, rowinfo->subinfo, offset+1);
- } else
- offset += 1;
- }
- d(printf("end sort subset start %d\n", startoffset));
-
- return offset;
-}
-
-static void
-ets_sort_free_subset(ETableSorter *ets, struct _subinfo *subinfo)
-{
- int i;
-
- for (i=0;i<subinfo->rowsort->len;i++) {
- struct _rowinfo *rowinfo;
-
- rowinfo = &g_array_index(subinfo->rowsort, struct _rowinfo, i);
- if (rowinfo->subinfo)
- ets_sort_free_subset(ets, rowinfo->subinfo);
- }
- g_array_free(subinfo->rowsort, TRUE);
- g_free(subinfo);
-}
-
-static int
-sort_groups_compare(const void *ap, const void *bp)
-{
- struct _group_info *a = (struct _group_info *)ap;
- struct _group_info *b = (struct _group_info *)bp;
-
- return strcmp(a->group, b->group);
-}
-
-/* use the sort group to select subsorts */
-static void
-ets_sort_by_group (ETableSorter *ets)
-{
- int rows = e_table_model_row_count (ets->source);
- struct _group_info *groups;
- struct _subinfo *subinfo;
- int i;
-
- d(printf("sorting %d rows\n", rows));
-
- if (rows == 0)
- return;
-
- /* get all the rows' sort groups */
- groups = g_new(struct _group_info, rows);
- for (i=0;i<rows;i++) {
- groups[i].row = i;
- groups[i].group = g_strdup(e_table_model_row_sort_group(ets->source, groups[i].row));
- }
-
- /* sort the group info */
- qsort(groups, rows, sizeof(struct _group_info), sort_groups_compare);
-
- d(printf("sorted groups:\n");
- for (i=0;i<rows;i++) {
- printf(" %s\n", groups[i].group);
- });
-
- /* now sort based on the group info */
- subinfo = ets_sort_build_subset(ets, groups, 0, NULL);
- for (i=0;i<rows;i++) {
- g_free(groups[i].group);
- }
- g_free(groups);
- ets_sort_subset(ets, subinfo, 0);
- ets_sort_free_subset(ets, subinfo);
-}
static void
ets_sort(ETableSorter *ets)
@@ -461,12 +290,7 @@ ets_sort(ETableSorter *ets)
ascending_closure[j] = column.ascending;
}
- if (e_table_model_has_sort_group (ets->source)) {
- ets_sort_by_group (ets);
- }
- else {
qsort(ets->sorted, rows, sizeof(int), qsort_callback);
- }
g_free(vals_closure);
g_free(ascending_closure);
diff --git a/widgets/table/e-table-sorting-utils.c b/widgets/table/e-table-sorting-utils.c
index 364397d17f..4b2d581206 100644
--- a/widgets/table/e-table-sorting-utils.c
+++ b/widgets/table/e-table-sorting-utils.c
@@ -1,7 +1,9 @@
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+#include <config.h>
#include <string.h>
#include <e-table-sorting-utils.h>
+#include <gal/util/e-util.h>
#define d(x)
@@ -37,64 +39,34 @@ etsu_compare(ETableModel *source, ETableSortInfo *sort_info, ETableHeader *full_
return comp_val;
}
-static ETableSortInfo *sort_info_closure;
+typedef struct {
+ int cols;
+ void **vals;
+ int *ascending;
+ GCompareFunc *compare;
+} ETableSortClosure;
-static void **vals_closure;
-static int cols_closure;
-static int *ascending_closure;
-static GCompareFunc *compare_closure;
+typedef struct {
+ ETreeModel *tree;
+ ETableSortInfo *sort_info;
+ ETableHeader *full_header;
+} ETreeSortClosure;
/* FIXME: Make it not cache the second and later columns (as if anyone cares.) */
static int
-qsort_callback(const void *data1, const void *data2)
+e_sort_callback(const void *data1, const void *data2, gpointer user_data)
{
gint row1 = *(int *)data1;
gint row2 = *(int *)data2;
+ ETableSortClosure *closure = user_data;
int j;
- int sort_count = e_table_sort_info_sorting_get_count(sort_info_closure);
- int comp_val = 0;
- int ascending = 1;
- for (j = 0; j < sort_count; j++) {
- comp_val = (*(compare_closure[j]))(vals_closure[cols_closure * row1 + j], vals_closure[cols_closure * row2 + j]);
- ascending = ascending_closure[j];
- if (comp_val != 0)
- break;
- }
- if (comp_val == 0) {
- if (row1 < row2)
- comp_val = -1;
- if (row1 > row2)
- comp_val = 1;
- }
- if (!ascending)
- comp_val = -comp_val;
- return comp_val;
-}
-
-struct _subinfo {
- int start;
- GArray *rowsort; /* an array of row info's */
-};
-
-struct _rowinfo {
- int row;
- struct _subinfo *subinfo;
- struct _group_info *groupinfo;
-};
-
-static int
-qsort_callback_complex(const void *data1, const void *data2)
-{
- gint row1 = ((struct _rowinfo *)data1)->row;
- gint row2 = ((struct _rowinfo *)data2)->row;
- int j;
- int sort_count = e_table_sort_info_sorting_get_count(sort_info_closure);
+ int sort_count = closure->cols;
int comp_val = 0;
int ascending = 1;
for (j = 0; j < sort_count; j++) {
- comp_val = (*(compare_closure[j]))(vals_closure[cols_closure * row1 + j], vals_closure[cols_closure * row2 + j]);
- ascending = ascending_closure[j];
+ comp_val = (*(closure->compare[j]))(closure->vals[closure->cols * row1 + j], closure->vals[closure->cols * row2 + j]);
+ ascending = closure->ascending[j];
if (comp_val != 0)
break;
}
@@ -109,219 +81,6 @@ qsort_callback_complex(const void *data1, const void *data2)
return comp_val;
}
-/* if sortgroup is like:
-0 1 1 1
-1 1 2 2
-2 2 3 2
-3 2 4 3
-4 3 5 3
-5 2 6 1
-6 1 0 1
-
- Want to sort the 1's first
- Then sort each group of 2's, offsetting into the output by the new root 1 location
- ... Recursively ...
-*/
-
-struct _group_info {
- char *group;
- int row;
-};
-
-#ifdef DEBUG
-#undef DEBUG
-#endif
-/*#define DEBUG*/
-
-#ifdef DEBUG
-static int total=0;
-static int total_sorted=0;
-#endif
-
-/* builds the info needed to sort everything */
-static struct _subinfo *
-etsu_sort_build_subset(int rows, struct _group_info *groupinfo, int start, int *end)
-{
- int i, lastinsert;
- GArray *rowsort = g_array_new(0, 0, sizeof(struct _rowinfo));
- struct _subinfo *subinfo, *newsub;
- char *id, *newid;
- int idlen, newidlen;
- int cmp;
- int cmplen;
-
- subinfo = g_malloc0(sizeof(*subinfo));
- subinfo->rowsort = rowsort;
- subinfo->start = start;
- lastinsert = -1;
- id = groupinfo[start].group;
- newid = strrchr(id, '/');
- idlen = strlen(id);
- if (newid)
- cmplen = newid-id;
- else
- cmplen = idlen;
- d(printf("%d scanning level %s\n", start, id));
- for (i=start;i<rows;i++) {
- newid = groupinfo[i].group;
- newidlen = strlen(newid);
- d(printf("%d checking group %s\n", start, newid));
- cmp = strncmp(id, newid, cmplen);
- /* check for common parent */
- if (idlen == newidlen && cmp == 0) {
- struct _rowinfo rowinfo;
-
- d(printf("%d Same parent\n", start));
- rowinfo.row = groupinfo[i].row;
- rowinfo.subinfo = NULL;
- rowinfo.groupinfo = &groupinfo[i];
- lastinsert = rowsort->len;
- g_array_append_val(rowsort, rowinfo);
-#ifdef DEBUG
- total++;
-#endif
- } else if (newidlen > idlen) {
- /* must be a new subtree */
- d(printf("%d checking subtree instead\n", start));
- newsub = etsu_sort_build_subset(rows, groupinfo, i, &i);
- d(printf("found %d nodes in subtree\n", newsub->rowsort->len));
- g_array_index(rowsort, struct _rowinfo, lastinsert).subinfo = newsub;
- } else {
- i--;
- break;
- }
- }
- if (end)
- *end = i;
- d(printf("finished level %s start was %d end was %d\n", id, start, i));
- return subinfo;
-}
-
-/* sort each level, and then sort each level below that level (once we know
- where the sublevel will fit in the overall list) */
-static int
-etsu_sort_subset(int *map_table, struct _subinfo *subinfo, int startoffset)
-{
- GArray *rowsort = subinfo->rowsort;
- int offset, i;
-
- d(printf("sorting subset start %d rows %d\n", startoffset, rowsort->len));
-
- /* first, sort the actual data */
- qsort(rowsort->data, rowsort->len, sizeof(struct _rowinfo), qsort_callback_complex);
-
- /* then put it back in the map table, where appropriate */
- offset = startoffset;
- for (i=0;i<rowsort->len;i++) {
- struct _rowinfo *rowinfo;
-
- d(printf("setting offset %d\n", offset));
-
- rowinfo = &g_array_index(rowsort, struct _rowinfo, i);
- map_table[offset] = rowinfo->row;
- if (rowinfo->subinfo) {
- offset = etsu_sort_subset(map_table, rowinfo->subinfo, offset+1);
- } else
- offset += 1;
- }
- d(printf("end sort subset start %d\n", startoffset));
-
- return offset;
-}
-
-static void
-etsu_sort_free_subset(struct _subinfo *subinfo)
-{
- int i;
-
- for (i=0;i<subinfo->rowsort->len;i++) {
- struct _rowinfo *rowinfo;
-
- rowinfo = &g_array_index(subinfo->rowsort, struct _rowinfo, i);
- if (rowinfo->subinfo)
- etsu_sort_free_subset(rowinfo->subinfo);
- }
- g_array_free(subinfo->rowsort, TRUE);
- g_free(subinfo);
-}
-
-static int
-sort_groups_compare(const void *ap, const void *bp)
-{
- struct _group_info *a = (struct _group_info *)ap;
- struct _group_info *b = (struct _group_info *)bp;
-
- return strcmp(a->group, b->group);
-}
-
-#ifdef DEBUG
-static void
-print_id(int key, int val, void *data)
-{
- printf("gained id %d\n", key);
-}
-#endif
-
-/* use the sort group to select subsorts */
-static void
-etsu_sort_by_group(ETableModel *source, int *map_table, int rows)
-{
- struct _group_info *groups;
- struct _subinfo *subinfo;
- int i;
-#ifdef DEBUG
- GHashTable *members = g_hash_table_new(0, 0);
-
- total = 0;
- total_sorted = 0;
-#endif
-
- d(printf("sorting %d rows\n", rows));
-
- if (rows == 0)
- return;
-
- /* get the subset rows */
- groups = g_malloc(sizeof(struct _group_info) * rows);
- for (i=0;i<rows;i++) {
- groups[i].row = map_table[i];
- groups[i].group = g_strdup(e_table_model_row_sort_group(source, groups[i].row));
-#ifdef DEBUG
- g_hash_table_insert(members, map_table[i], 1);
- map_table[i] = 0;
-#endif
- }
-
- /* sort the group info */
- qsort(groups, rows, sizeof(struct _group_info), sort_groups_compare);
-
- d(printf("sorted groups:\n");
- for (i=0;i<rows;i++) {
- printf(" %s\n", groups[i].group);
- });
-
- /* now sort based on the group info */
- subinfo = etsu_sort_build_subset(rows, groups, 0, NULL);
- for (i=0;i<rows;i++) {
- g_free(groups[i].group);
- }
- g_free(groups);
- etsu_sort_subset(map_table, subinfo, 0);
- etsu_sort_free_subset(subinfo);
-#ifdef DEBUG
- for (i=0;i<rows;i++) {
- if (g_hash_table_lookup(members, map_table[i]) == 0) {
- printf("lost id %d\n", map_table[i]);
- }
- g_hash_table_remove(members, map_table[i]);
- }
- g_hash_table_foreach(members, print_id, 0);
-
- printf("total rows = %d, total processed = %d, total sorted = %d\n", rows, total, total_sorted);
-#endif
-
-}
-
void
e_table_sorting_utils_sort(ETableModel *source, ETableSortInfo *sort_info, ETableHeader *full_header, int *map_table, int rows)
{
@@ -329,6 +88,7 @@ e_table_sorting_utils_sort(ETableModel *source, ETableSortInfo *sort_info, ETabl
int i;
int j;
int cols;
+ ETableSortClosure closure;
g_return_if_fail(source != NULL);
g_return_if_fail(E_IS_TABLE_MODEL(source));
@@ -339,11 +99,12 @@ e_table_sorting_utils_sort(ETableModel *source, ETableSortInfo *sort_info, ETabl
total_rows = e_table_model_row_count(source);
cols = e_table_sort_info_sorting_get_count(sort_info);
- cols_closure = cols;
- vals_closure = g_new(void *, total_rows * cols);
- sort_info_closure = sort_info;
- ascending_closure = g_new(int, cols);
- compare_closure = g_new(GCompareFunc, cols);
+ closure.cols = cols;
+
+ closure.vals = g_new(void *, total_rows * cols);
+ closure.ascending = g_new(int, cols);
+ closure.compare = g_new(GCompareFunc, cols);
+
for (j = 0; j < cols; j++) {
ETableSortColumn column = e_table_sort_info_sorting_get_nth(sort_info, j);
ETableCol *col;
@@ -351,33 +112,27 @@ e_table_sorting_utils_sort(ETableModel *source, ETableSortInfo *sort_info, ETabl
if (col == NULL)
col = e_table_header_get_column (full_header, e_table_header_count (full_header) - 1);
for (i = 0; i < rows; i++) {
- vals_closure[map_table[i] * cols + j] = e_table_model_value_at (source, col->col_idx, map_table[i]);
+ closure.vals[map_table[i] * cols + j] = e_table_model_value_at (source, col->col_idx, map_table[i]);
}
- compare_closure[j] = col->compare;
- ascending_closure[j] = column.ascending;
+ closure.compare[j] = col->compare;
+ closure.ascending[j] = column.ascending;
}
- if (e_table_model_has_sort_group(source)) {
- etsu_sort_by_group(source, map_table, rows);
- } else {
- qsort(map_table, rows, sizeof(int), qsort_callback);
- }
- g_free(vals_closure);
- g_free(ascending_closure);
- g_free(compare_closure);
+ e_sort(map_table, rows, sizeof(int), e_sort_callback, &closure);
+
+ g_free(closure.vals);
+ g_free(closure.ascending);
+ g_free(closure.compare);
}
gboolean
-e_table_sorting_utils_affects_sort (ETableModel *source,
- ETableSortInfo *sort_info,
+e_table_sorting_utils_affects_sort (ETableSortInfo *sort_info,
ETableHeader *full_header,
int col)
{
int j;
int cols;
- g_return_val_if_fail(source != NULL, TRUE);
- g_return_val_if_fail(E_IS_TABLE_MODEL(source), TRUE);
g_return_val_if_fail(sort_info != NULL, TRUE);
g_return_val_if_fail(E_IS_TABLE_SORT_INFO(sort_info), TRUE);
g_return_val_if_fail(full_header != NULL, TRUE);
@@ -398,6 +153,7 @@ e_table_sorting_utils_affects_sort (ETableModel *source,
}
+/* FIXME: This could be done in time log n instead of time n with a binary search. */
int
e_table_sorting_utils_insert(ETableModel *source, ETableSortInfo *sort_info, ETableHeader *full_header, int *map_table, int rows, int row)
{
@@ -405,62 +161,13 @@ e_table_sorting_utils_insert(ETableModel *source, ETableSortInfo *sort_info, ETa
i = 0;
/* handle insertions when we have a 'sort group' */
- if (e_table_model_has_sort_group(source)) {
- /* find the row this row maps to */
- char *group = g_strdup(e_table_model_row_sort_group(source, row));
- const char *newgroup;
- int cmp, grouplen, newgrouplen;
-
- newgroup = strrchr(group, '/');
- grouplen = strlen(group);
- if (newgroup)
- cmp = newgroup-group;
- else
- cmp = grouplen;
-
- /* find first common parent */
- while (i < rows) {
- newgroup = e_table_model_row_sort_group(source, map_table[i]);
- if (strncmp(newgroup, group, cmp) == 0) {
- break;
- }
- i++;
- }
-
- /* check matching records */
- while (i<row) {
- newgroup = e_table_model_row_sort_group(source, map_table[i]);
- newgrouplen = strlen(newgroup);
- if (strncmp(newgroup, group, cmp) == 0) {
- /* common parent, check for same level */
- if (grouplen == newgrouplen) {
- if (etsu_compare(source, sort_info, full_header, map_table[i], row) >= 0)
- break;
- } else if (strncmp(newgroup + cmp, group + cmp, grouplen - cmp) == 0)
- /* Found a child of the inserted node. Insert here. */
- break;
- } else {
- /* ran out of common parents, insert here */
- break;
- }
- i++;
- }
- g_free(group);
- } else {
- while (i < rows && etsu_compare(source, sort_info, full_header, map_table[i], row) < 0)
- i++;
- }
+ while (i < rows && etsu_compare(source, sort_info, full_header, map_table[i], row) < 0)
+ i++;
return i;
}
-#if 0
-void *bsearch(const void *key, const void *base, size_t nmemb,
- size_t size, int (*compar)(const void *, const void *, void *), gpointer user_data)
-{
-
-}
-
+/* FIXME: This could be done in time log n instead of time n with a binary search. */
int
e_table_sorting_utils_check_position (ETableModel *source, ETableSortInfo *sort_info, ETableHeader *full_header, int *map_table, int rows, int view_row)
{
@@ -469,60 +176,153 @@ e_table_sorting_utils_check_position (ETableModel *source, ETableSortInfo *sort_
i = view_row;
row = map_table[i];
- /* handle insertions when we have a 'sort group' */
- if (e_table_model_has_sort_group(source)) {
- /* find the row this row maps to */
- char *group = g_strdup(e_table_model_row_sort_group(source, row));
- const char *newgroup;
- int cmp, grouplen, newgrouplen;
-
- newgroup = strrchr(group, '/');
- grouplen = strlen(group);
- if (newgroup)
- cmp = newgroup-group;
- else
- cmp = grouplen;
-
- /* find first common parent */
- while (i < rows) {
- newgroup = e_table_model_row_sort_group(source, map_table[i]);
- if (strncmp(newgroup, group, cmp) == 0) {
- break;
- }
- i++;
- }
- /* check matching records */
- while (i < row) {
- newgroup = e_table_model_row_sort_group(source, map_table[i]);
- newgrouplen = strlen(newgroup);
- if (strncmp(newgroup, group, cmp) == 0) {
- /* common parent, check for same level */
- if (grouplen == newgrouplen) {
- if (etsu_compare(source, sort_info, full_header, map_table[i], row) >= 0)
- break;
- } else if (strncmp(newgroup + cmp, group + cmp, grouplen - cmp) == 0)
- /* Found a child of the inserted node. Insert here. */
- break;
- } else {
- /* ran out of common parents, insert here */
- break;
- }
- i++;
- }
- g_free(group);
- } else {
- i = view_row;
- if (i < rows && etsu_compare(source, sort_info, full_header, map_table[i + 1], row) < 0) {
+ i = view_row;
+ if (i < rows - 1 && etsu_compare(source, sort_info, full_header, map_table[i + 1], row) < 0) {
+ i ++;
+ while (i < rows - 1 && etsu_compare(source, sort_info, full_header, map_table[i], row) < 0)
i ++;
- while (i < rows - 1 && etsu_compare(source, sort_info, full_header, map_table[i], row) < 0)
- i ++;
- } else if (i > 0 && etsu_compare(source, sort_info, full_header, map_table[i - 1], row) > 0) {
+ } else if (i > 0 && etsu_compare(source, sort_info, full_header, map_table[i - 1], row) > 0) {
+ i --;
+ while (i > 0 && etsu_compare(source, sort_info, full_header, map_table[i], row) > 0)
i --;
- while (i > 0 && etsu_compare(source, sort_info, full_header, map_table[i], row) > 0)
- i --;
+ }
+ return i;
+}
+
+
+
+
+/* This takes source rows. */
+static int
+etsu_tree_compare(ETreeModel *source, ETableSortInfo *sort_info, ETableHeader *full_header, ETreePath path1, ETreePath path2)
+{
+ int j;
+ int sort_count = e_table_sort_info_sorting_get_count(sort_info);
+ int comp_val = 0;
+ int ascending = 1;
+
+ for (j = 0; j < sort_count; j++) {
+ ETableSortColumn column = e_table_sort_info_sorting_get_nth(sort_info, j);
+ ETableCol *col;
+ col = e_table_header_get_column_by_col_idx(full_header, column.column);
+ if (col == NULL)
+ col = e_table_header_get_column (full_header, e_table_header_count (full_header) - 1);
+ comp_val = (*col->compare)(e_tree_model_value_at (source, path1, col->col_idx),
+ e_tree_model_value_at (source, path2, col->col_idx));
+ ascending = column.ascending;
+ if (comp_val != 0)
+ break;
+ }
+ if (!ascending)
+ comp_val = -comp_val;
+ return comp_val;
+}
+
+static int
+e_sort_tree_callback(const void *data1, const void *data2, gpointer user_data)
+{
+ ETreePath *path1 = *(ETreePath *)data1;
+ ETreePath *path2 = *(ETreePath *)data2;
+ ETreeSortClosure *closure = user_data;
+
+ return etsu_tree_compare(closure->tree, closure->sort_info, closure->full_header, path1, path2);
+}
+
+void
+e_table_sorting_utils_tree_sort(ETreeModel *source, ETableSortInfo *sort_info, ETableHeader *full_header, ETreePath *map_table, int count)
+{
+ ETableSortClosure closure;
+ int cols;
+ int i, j;
+ int *map;
+ ETreePath *map_copy;
+ g_return_if_fail(source != NULL);
+ g_return_if_fail(E_IS_TREE_MODEL(source));
+ g_return_if_fail(sort_info != NULL);
+ g_return_if_fail(E_IS_TABLE_SORT_INFO(sort_info));
+ g_return_if_fail(full_header != NULL);
+ g_return_if_fail(E_IS_TABLE_HEADER(full_header));
+
+ cols = e_table_sort_info_sorting_get_count(sort_info);
+ closure.cols = cols;
+
+ closure.vals = g_new(void *, count * cols);
+ closure.ascending = g_new(int, cols);
+ closure.compare = g_new(GCompareFunc, cols);
+
+ for (j = 0; j < cols; j++) {
+ ETableSortColumn column = e_table_sort_info_sorting_get_nth(sort_info, j);
+ ETableCol *col;
+
+ col = e_table_header_get_column_by_col_idx(full_header, column.column);
+ if (col == NULL)
+ col = e_table_header_get_column (full_header, e_table_header_count (full_header) - 1);
+
+ for (i = 0; i < count; i++) {
+ closure.vals[i * cols + j] = e_tree_model_value_at (source, map_table[i], col->col_idx);
}
+ closure.ascending[j] = column.ascending;
+ closure.compare[j] = col->compare;
+ }
+
+ map = g_new(int, count);
+ for (i = 0; i < count; i++) {
+ map[i] = i;
+ }
+
+ e_sort(map, count, sizeof(int), e_sort_callback, &closure);
+
+ map_copy = g_new(ETreePath, count);
+ for (i = 0; i < count; i++) {
+ map_copy[i] = map_table[i];
+ }
+ for (i = 0; i < count; i++) {
+ map_table[i] = map_copy[map[i]];
+ }
+
+ g_free(map);
+ g_free(map_copy);
+
+ g_free(closure.vals);
+ g_free(closure.ascending);
+ g_free(closure.compare);
+}
+
+/* FIXME: This could be done in time log n instead of time n with a binary search. */
+int
+e_table_sorting_utils_tree_check_position (ETreeModel *source, ETableSortInfo *sort_info, ETableHeader *full_header, ETreePath *map_table, int count, int old_index)
+{
+ int i;
+ ETreePath path;
+
+ i = old_index;
+ path = map_table[i];
+
+ if (i < count - 1 && etsu_tree_compare(source, sort_info, full_header, map_table[i + 1], path) < 0) {
+ i ++;
+ while (i < count - 1 && etsu_tree_compare(source, sort_info, full_header, map_table[i], path) < 0)
+ i ++;
+ } else if (i > 0 && etsu_tree_compare(source, sort_info, full_header, map_table[i - 1], path) > 0) {
+ i --;
+ while (i > 0 && etsu_tree_compare(source, sort_info, full_header, map_table[i], path) > 0)
+ i --;
}
return i;
}
-#endif
+
+/* FIXME: This does not pay attention to making sure that it's a stable insert. This needs to be fixed. */
+int
+e_table_sorting_utils_tree_insert(ETreeModel *source, ETableSortInfo *sort_info, ETableHeader *full_header, ETreePath *map_table, int count, ETreePath path)
+{
+ int start;
+ int end;
+ ETreeSortClosure closure;
+
+ closure.tree = source;
+ closure.sort_info = sort_info;
+ closure.full_header = full_header;
+
+ e_bsearch(&path, map_table, count, sizeof(ETreePath), e_sort_tree_callback, &closure, &start, &end);
+ return end;
+}
diff --git a/widgets/table/e-table-sorting-utils.h b/widgets/table/e-table-sorting-utils.h
index f65f7c49ce..559bd8e82c 100644
--- a/widgets/table/e-table-sorting-utils.h
+++ b/widgets/table/e-table-sorting-utils.h
@@ -7,26 +7,52 @@ extern "C" {
#endif /* __cplusplus */
#include <gal/e-table/e-table-model.h>
+#include <gal/e-table/e-tree-model.h>
#include <gal/e-table/e-table-sort-info.h>
#include <gal/e-table/e-table-header.h>
+gboolean e_table_sorting_utils_affects_sort (ETableSortInfo *sort_info,
+ ETableHeader *full_header,
+ int col);
-void e_table_sorting_utils_sort (ETableModel *source,
- ETableSortInfo *sort_info,
- ETableHeader *full_header,
- int *map_table,
- int rows);
-
-gboolean e_table_sorting_utils_affects_sort (ETableModel *source,
- ETableSortInfo *sort_info,
- ETableHeader *full_header,
- int col);
-
-int e_table_sorting_utils_insert (ETableModel *source,
- ETableSortInfo *sort_info,
- ETableHeader *full_header,
- int *map_table,
- int rows,
- int row);
+
+
+void e_table_sorting_utils_sort (ETableModel *source,
+ ETableSortInfo *sort_info,
+ ETableHeader *full_header,
+ int *map_table,
+ int rows);
+int e_table_sorting_utils_insert (ETableModel *source,
+ ETableSortInfo *sort_info,
+ ETableHeader *full_header,
+ int *map_table,
+ int rows,
+ int row);
+int e_table_sorting_utils_check_position (ETableModel *source,
+ ETableSortInfo *sort_info,
+ ETableHeader *full_header,
+ int *map_table,
+ int rows,
+ int view_row);
+
+
+
+void e_table_sorting_utils_tree_sort (ETreeModel *source,
+ ETableSortInfo *sort_info,
+ ETableHeader *full_header,
+ ETreePath *map_table,
+ int count);
+int e_table_sorting_utils_tree_check_position (ETreeModel *source,
+ ETableSortInfo *sort_info,
+ ETableHeader *full_header,
+ ETreePath *map_table,
+ int count,
+ int old_index);
+int e_table_sorting_utils_tree_insert (ETreeModel *source,
+ ETableSortInfo *sort_info,
+ ETableHeader *full_header,
+ ETreePath *map_table,
+ int count,
+ ETreePath path);
#ifdef __cplusplus
}
diff --git a/widgets/table/e-table-subset.c b/widgets/table/e-table-subset.c
index 4b3d1e5d3b..2907698fc7 100644
--- a/widgets/table/e-table-subset.c
+++ b/widgets/table/e-table-subset.c
@@ -40,9 +40,9 @@ etss_destroy (GtkObject *object)
gtk_signal_disconnect (GTK_OBJECT (etss->source),
etss->table_model_cell_changed_id);
gtk_signal_disconnect (GTK_OBJECT (etss->source),
- etss->table_model_row_inserted_id);
+ etss->table_model_rows_inserted_id);
gtk_signal_disconnect (GTK_OBJECT (etss->source),
- etss->table_model_row_deleted_id);
+ etss->table_model_rows_deleted_id);
gtk_object_unref (GTK_OBJECT (etss->source));
etss->source = NULL;
@@ -50,8 +50,8 @@ etss_destroy (GtkObject *object)
etss->table_model_changed_id = 0;
etss->table_model_row_changed_id = 0;
etss->table_model_cell_changed_id = 0;
- etss->table_model_row_inserted_id = 0;
- etss->table_model_row_deleted_id = 0;
+ etss->table_model_rows_inserted_id = 0;
+ etss->table_model_rows_deleted_id = 0;
}
g_free (etss->map_table);
@@ -176,8 +176,8 @@ etss_class_init (GtkObjectClass *object_class)
klass->proxy_model_changed = etss_proxy_model_changed_real;
klass->proxy_model_row_changed = etss_proxy_model_row_changed_real;
klass->proxy_model_cell_changed = etss_proxy_model_cell_changed_real;
- klass->proxy_model_row_inserted = NULL;
- klass->proxy_model_row_deleted = NULL;
+ klass->proxy_model_rows_inserted = NULL;
+ klass->proxy_model_rows_deleted = NULL;
}
static void
@@ -305,17 +305,17 @@ etss_proxy_model_cell_changed (ETableModel *etm, int row, int col, ETableSubset
}
static void
-etss_proxy_model_row_inserted (ETableModel *etm, int row, ETableSubset *etss)
+etss_proxy_model_rows_inserted (ETableModel *etm, int row, int col, ETableSubset *etss)
{
- if (ETSS_CLASS(etss)->proxy_model_row_inserted)
- (ETSS_CLASS(etss)->proxy_model_row_inserted) (etss, etm, row);
+ if (ETSS_CLASS(etss)->proxy_model_rows_inserted)
+ (ETSS_CLASS(etss)->proxy_model_rows_inserted) (etss, etm, row, col);
}
static void
-etss_proxy_model_row_deleted (ETableModel *etm, int row, ETableSubset *etss)
+etss_proxy_model_rows_deleted (ETableModel *etm, int row, int col, ETableSubset *etss)
{
- if (ETSS_CLASS(etss)->proxy_model_row_deleted)
- (ETSS_CLASS(etss)->proxy_model_row_deleted) (etss, etm, row);
+ if (ETSS_CLASS(etss)->proxy_model_rows_deleted)
+ (ETSS_CLASS(etss)->proxy_model_rows_deleted) (etss, etm, row, col);
}
ETableModel *
@@ -347,10 +347,10 @@ e_table_subset_construct (ETableSubset *etss, ETableModel *source, int nvals)
GTK_SIGNAL_FUNC (etss_proxy_model_row_changed), etss);
etss->table_model_cell_changed_id = gtk_signal_connect (GTK_OBJECT (source), "model_cell_changed",
GTK_SIGNAL_FUNC (etss_proxy_model_cell_changed), etss);
- etss->table_model_row_inserted_id = gtk_signal_connect (GTK_OBJECT (source), "model_row_inserted",
- GTK_SIGNAL_FUNC (etss_proxy_model_row_inserted), etss);
- etss->table_model_row_deleted_id = gtk_signal_connect (GTK_OBJECT (source), "model_row_deleted",
- GTK_SIGNAL_FUNC (etss_proxy_model_row_deleted), etss);
+ etss->table_model_rows_inserted_id = gtk_signal_connect (GTK_OBJECT (source), "model_rows_inserted",
+ GTK_SIGNAL_FUNC (etss_proxy_model_rows_inserted), etss);
+ etss->table_model_rows_deleted_id = gtk_signal_connect (GTK_OBJECT (source), "model_rows_deleted",
+ GTK_SIGNAL_FUNC (etss_proxy_model_rows_deleted), etss);
return E_TABLE_MODEL (etss);
}
diff --git a/widgets/table/e-table-subset.h b/widgets/table/e-table-subset.h
index 0ab2fa5be7..cd26fbb203 100644
--- a/widgets/table/e-table-subset.h
+++ b/widgets/table/e-table-subset.h
@@ -28,8 +28,8 @@ typedef struct {
int table_model_changed_id;
int table_model_row_changed_id;
int table_model_cell_changed_id;
- int table_model_row_inserted_id;
- int table_model_row_deleted_id;
+ int table_model_rows_inserted_id;
+ int table_model_rows_deleted_id;
} ETableSubset;
typedef struct {
@@ -39,8 +39,8 @@ typedef struct {
void (*proxy_model_changed) (ETableSubset *etss, ETableModel *etm);
void (*proxy_model_row_changed) (ETableSubset *etss, ETableModel *etm, int row);
void (*proxy_model_cell_changed) (ETableSubset *etss, ETableModel *etm, int col, int row);
- void (*proxy_model_row_inserted) (ETableSubset *etss, ETableModel *etm, int row);
- void (*proxy_model_row_deleted) (ETableSubset *etss, ETableModel *etm, int row);
+ void (*proxy_model_rows_inserted) (ETableSubset *etss, ETableModel *etm, int row, int count);
+ void (*proxy_model_rows_deleted) (ETableSubset *etss, ETableModel *etm, int row, int count);
} ETableSubsetClass;
GtkType e_table_subset_get_type (void);
diff --git a/widgets/table/e-table-utils.c b/widgets/table/e-table-utils.c
new file mode 100644
index 0000000000..9c9aea6911
--- /dev/null
+++ b/widgets/table/e-table-utils.c
@@ -0,0 +1,111 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * e-table.utils.c: Utilities for ETable.
+ *
+ * Author:
+ * Chris Lahey <clahey@ximian.com>
+ *
+ * Copyright 2001, Ximian, Inc
+ */
+
+#include <config.h>
+#include "gal/util/e-i18n.h"
+#include "e-table-utils.h"
+#include "e-table-header-utils.h"
+
+ETableHeader *
+e_table_state_to_header (GtkWidget *widget, ETableHeader *full_header, ETableState *state)
+{
+ ETableHeader *nh;
+ const int max_cols = e_table_header_count (full_header);
+ int column;
+
+ g_return_val_if_fail (widget, NULL);
+ g_return_val_if_fail (full_header, NULL);
+ g_return_val_if_fail (state, NULL);
+
+ nh = e_table_header_new ();
+
+ gtk_object_set(GTK_OBJECT(nh),
+ "width_extras", e_table_header_width_extras(widget->style),
+ NULL);
+
+ for (column = 0; column < state->col_count; column++) {
+ int col;
+ double expansion;
+ ETableCol *table_col;
+
+ col = state->columns[column];
+ expansion = state->expansions[column];
+
+ if (col >= max_cols)
+ continue;
+
+ table_col = e_table_header_get_column (full_header, col);
+
+ if (expansion >= -1)
+ table_col->expansion = expansion;
+
+ e_table_header_add_column (nh, table_col, -1);
+ }
+
+ return nh;
+}
+
+static ETableCol *
+et_col_spec_to_col (ETableColumnSpecification *col_spec,
+ ETableExtras *ete)
+{
+ ETableCol *col = NULL;
+ ECell *cell;
+ GCompareFunc compare;
+
+ cell = e_table_extras_get_cell(ete, col_spec->cell);
+ compare = e_table_extras_get_compare(ete, col_spec->compare);
+
+ if (cell && compare) {
+ if (col_spec->pixbuf && *col_spec->pixbuf) {
+ GdkPixbuf *pixbuf;
+
+ pixbuf = e_table_extras_get_pixbuf(
+ ete, col_spec->pixbuf);
+ if (pixbuf) {
+ col = e_table_col_new_with_pixbuf (
+ col_spec->model_col, gettext (col_spec->title),
+ pixbuf, col_spec->expansion,
+ col_spec->minimum_width,
+ cell, compare, col_spec->resizable);
+ }
+ }
+ if (col == NULL && col_spec->title && *col_spec->title) {
+ col = e_table_col_new (
+ col_spec->model_col, gettext (col_spec->title),
+ col_spec->expansion, col_spec->minimum_width,
+ cell, compare, col_spec->resizable);
+ }
+ }
+ return col;
+}
+
+ETableHeader *
+e_table_spec_to_full_header (ETableSpecification *spec,
+ ETableExtras *ete)
+{
+ ETableHeader *nh;
+ int column;
+
+ g_return_val_if_fail (spec, NULL);
+ g_return_val_if_fail (ete, NULL);
+
+ nh = e_table_header_new ();
+
+ for (column = 0; spec->columns[column]; column++) {
+ ETableCol *col = et_col_spec_to_col (
+ spec->columns[column], ete);
+
+ if (col)
+ e_table_header_add_column (nh, col, -1);
+ }
+
+ return nh;
+}
diff --git a/widgets/table/e-table-utils.h b/widgets/table/e-table-utils.h
new file mode 100644
index 0000000000..d78f1a5da2
--- /dev/null
+++ b/widgets/table/e-table-utils.h
@@ -0,0 +1,22 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+#ifndef _E_TABLE_UTILS_H_
+#define _E_TABLE_UTILS_H_
+
+#include <gal/e-table/e-table-header.h>
+#include <gal/e-table/e-table-state.h>
+#include <gal/e-table/e-table-specification.h>
+#include <gal/e-table/e-table-extras.h>
+
+BEGIN_GNOME_DECLS
+
+ETableHeader *
+e_table_state_to_header (GtkWidget *widget, ETableHeader *full_header, ETableState *state);
+
+ETableHeader *
+e_table_spec_to_full_header (ETableSpecification *spec,
+ ETableExtras *ete);
+
+END_GNOME_DECLS
+
+#endif /* _E_TABLE_UTILS_H_ */
+
diff --git a/widgets/table/e-table.c b/widgets/table/e-table.c
index a740dd8ecd..8ed176a4da 100644
--- a/widgets/table/e-table.c
+++ b/widgets/table/e-table.c
@@ -32,6 +32,8 @@
#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 ()
@@ -133,18 +135,18 @@ et_disconnect_model (ETable *et)
if (et->table_cell_change_id != 0)
gtk_signal_disconnect (GTK_OBJECT (et->model),
et->table_cell_change_id);
- if (et->table_row_inserted_id != 0)
+ if (et->table_rows_inserted_id != 0)
gtk_signal_disconnect (GTK_OBJECT (et->model),
- et->table_row_inserted_id);
- if (et->table_row_deleted_id != 0)
+ et->table_rows_inserted_id);
+ if (et->table_rows_deleted_id != 0)
gtk_signal_disconnect (GTK_OBJECT (et->model),
- et->table_row_deleted_id);
+ et->table_rows_deleted_id);
et->table_model_change_id = 0;
et->table_row_change_id = 0;
et->table_cell_change_id = 0;
- et->table_row_inserted_id = 0;
- et->table_row_deleted_id = 0;
+ et->table_rows_inserted_id = 0;
+ et->table_rows_deleted_id = 0;
}
static void
@@ -514,27 +516,31 @@ et_table_cell_changed (ETableModel *table_model, int view_col, int row, ETable *
}
static void
-et_table_row_inserted (ETableModel *table_model, int row, ETable *et)
+et_table_rows_inserted (ETableModel *table_model, int row, int count, ETable *et)
{
/* This number has already been decremented. */
int row_count = e_table_model_row_count(table_model);
if (!et->need_rebuild) {
- if (row != row_count - 1)
- e_table_group_increment(et->group, row, 1);
- e_table_group_add (et->group, row);
+ int i;
+ if (row != row_count - count)
+ e_table_group_increment(et->group, row, count);
+ for (i = 0; i < count; i++)
+ e_table_group_add (et->group, row);
if (et->horizontal_scrolling)
e_table_header_update_horizontal(et->header);
}
}
static void
-et_table_row_deleted (ETableModel *table_model, int row, ETable *et)
+et_table_rows_deleted (ETableModel *table_model, int row, int count, ETable *et)
{
int row_count = e_table_model_row_count(table_model);
if (!et->need_rebuild) {
- e_table_group_remove (et->group, row);
+ int i;
+ for (i = 0; i < count; i++)
+ e_table_group_remove (et->group, row);
if (row != row_count)
- e_table_group_decrement(et->group, row, 1);
+ e_table_group_decrement(et->group, row, count);
if (et->horizontal_scrolling)
e_table_header_update_horizontal(et->header);
}
@@ -589,11 +595,11 @@ et_build_groups (ETable *et)
et->table_cell_change_id = gtk_signal_connect (GTK_OBJECT (et->model), "model_cell_changed",
GTK_SIGNAL_FUNC (et_table_cell_changed), et);
- et->table_row_inserted_id = gtk_signal_connect (GTK_OBJECT (et->model), "model_row_inserted",
- GTK_SIGNAL_FUNC (et_table_row_inserted), et);
+ et->table_rows_inserted_id = gtk_signal_connect (GTK_OBJECT (et->model), "model_rows_inserted",
+ GTK_SIGNAL_FUNC (et_table_rows_inserted), et);
- et->table_row_deleted_id = gtk_signal_connect (GTK_OBJECT (et->model), "model_row_deleted",
- GTK_SIGNAL_FUNC (et_table_row_deleted), et);
+ et->table_rows_deleted_id = gtk_signal_connect (GTK_OBJECT (et->model), "model_rows_deleted",
+ GTK_SIGNAL_FUNC (et_table_rows_deleted), et);
}
@@ -741,115 +747,19 @@ e_table_fill_table (ETable *e_table, ETableModel *model)
e_table_group_add_all (e_table->group);
}
-static ETableCol *
-et_col_spec_to_col (ETable *e_table,
- ETableColumnSpecification *col_spec,
- ETableExtras *ete)
-{
- ETableCol *col = NULL;
- ECell *cell;
- GCompareFunc compare;
-
- cell = e_table_extras_get_cell(ete, col_spec->cell);
- compare = e_table_extras_get_compare(ete, col_spec->compare);
-
- if (cell && compare) {
- if (col_spec->pixbuf && *col_spec->pixbuf) {
- GdkPixbuf *pixbuf;
-
- pixbuf = e_table_extras_get_pixbuf(
- ete, col_spec->pixbuf);
- if (pixbuf) {
- col = e_table_col_new_with_pixbuf (
- col_spec->model_col, gettext (col_spec->title),
- pixbuf, col_spec->expansion,
- col_spec->minimum_width,
- cell, compare, col_spec->resizable);
- }
- }
- if (col == NULL && col_spec->title && *col_spec->title) {
- col = e_table_col_new (
- col_spec->model_col, gettext (col_spec->title),
- col_spec->expansion, col_spec->minimum_width,
- cell, compare, col_spec->resizable);
- }
- }
- return col;
-}
-
-static ETableHeader *
-et_spec_to_full_header (ETable *e_table,
- ETableSpecification *spec,
- ETableExtras *ete)
-{
- ETableHeader *nh;
- int column;
-
- g_return_val_if_fail (e_table, NULL);
- g_return_val_if_fail (spec, NULL);
- g_return_val_if_fail (ete, NULL);
-
- nh = e_table_header_new ();
-
- for (column = 0; spec->columns[column]; column++) {
- ETableCol *col = et_col_spec_to_col (
- e_table, spec->columns[column], ete);
-
- if (col)
- e_table_header_add_column (nh, col, -1);
- }
-
- return nh;
-}
-
-static ETableHeader *
-et_state_to_header (ETable *e_table, ETableHeader *full_header, ETableState *state)
-{
- ETableHeader *nh;
- const int max_cols = e_table_header_count (full_header);
- int column;
-
- g_return_val_if_fail (e_table, NULL);
- g_return_val_if_fail (full_header, NULL);
- g_return_val_if_fail (state, NULL);
-
- nh = e_table_header_new ();
-
- gtk_object_set(GTK_OBJECT(nh),
- "width_extras", e_table_header_width_extras(GTK_WIDGET(e_table)->style),
- NULL);
-
- for (column = 0; column < state->col_count; column++) {
- int col;
- double expansion;
- ETableCol *table_col;
-
- col = state->columns[column];
- expansion = state->expansions[column];
-
- if (col >= max_cols)
- continue;
-
- table_col = e_table_header_get_column (full_header, col);
-
- if (expansion >= -1)
- table_col->expansion = expansion;
-
- e_table_header_add_column (nh, table_col, -1);
- }
-
- return nh;
-}
-
void
e_table_set_state_object(ETable *e_table, ETableState *state)
{
if (e_table->header)
gtk_object_unref(GTK_OBJECT(e_table->header));
- e_table->header = et_state_to_header (e_table, e_table->full_header, state);
+ e_table->header = e_table_state_to_header (GTK_WIDGET(e_table), e_table->full_header, state);
if (e_table->header)
gtk_object_ref(GTK_OBJECT(e_table->header));
+ gtk_object_set (GTK_OBJECT (e_table->header),
+ "width", (double) (GTK_WIDGET(e_table->table_canvas)->allocation.width),
+ NULL);
+
if (e_table->sort_info) {
if (e_table->group_info_change_id)
gtk_signal_disconnect (GTK_OBJECT (e_table->sort_info),
@@ -1031,7 +941,7 @@ et_real_construct (ETable *e_table, ETableModel *etm, ETableExtras *ete,
e_table->draw_grid = specification->draw_grid;
e_table->draw_focus = specification->draw_focus;
e_table->cursor_mode = specification->cursor_mode;
- e_table->full_header = et_spec_to_full_header(e_table, specification, ete);
+ e_table->full_header = e_table_spec_to_full_header(specification, ete);
gtk_object_set(GTK_OBJECT(e_table->selection),
"selection_mode", specification->selection_mode,
@@ -1044,7 +954,7 @@ et_real_construct (ETable *e_table, ETableModel *etm, ETableExtras *ete,
gtk_widget_push_visual (gdk_rgb_get_visual ());
gtk_widget_push_colormap (gdk_rgb_get_cmap ());
- e_table->header = et_state_to_header (e_table, e_table->full_header, state);
+ e_table->header = e_table_state_to_header (GTK_WIDGET(e_table), e_table->full_header, state);
e_table->horizontal_scrolling = specification->horizontal_scrolling;
e_table->sort_info = state->sort_info;
diff --git a/widgets/table/e-table.h b/widgets/table/e-table.h
index 17cee2f70b..17e52d5f07 100644
--- a/widgets/table/e-table.h
+++ b/widgets/table/e-table.h
@@ -52,8 +52,8 @@ typedef struct {
int table_model_change_id;
int table_row_change_id;
int table_cell_change_id;
- int table_row_inserted_id;
- int table_row_deleted_id;
+ int table_rows_inserted_id;
+ int table_rows_deleted_id;
int group_info_change_id;
diff --git a/widgets/table/e-tree-memory-callbacks.c b/widgets/table/e-tree-memory-callbacks.c
new file mode 100644
index 0000000000..2168ba81c5
--- /dev/null
+++ b/widgets/table/e-tree-memory-callbacks.c
@@ -0,0 +1,231 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * e-tree-memory-callbacks.c: a Tree Model that offers a function pointer
+ * interface to using ETreeModel, similar to ETableSimple.
+ *
+ * Author:
+ * Chris Toshok (toshok@ximian.com)
+ *
+ * (C) 2000 Ximian, Inc. */
+
+#include <config.h>
+#include <gtk/gtksignal.h>
+#include "gal/util/e-util.h"
+#include "e-tree-memory-callbacks.h"
+
+#define PARENT_TYPE E_TREE_MEMORY_TYPE
+
+static GdkPixbuf *
+etmc_icon_at (ETreeModel *etm, ETreePath node)
+{
+ ETreeMemoryCallbacks *etmc = E_TREE_MEMORY_CALLBACKS(etm);
+
+ return etmc->icon_at (etm, node, etmc->model_data);
+}
+
+static int
+etmc_column_count (ETreeModel *etm)
+{
+ ETreeMemoryCallbacks *etmc = E_TREE_MEMORY_CALLBACKS(etm);
+
+ if (etmc->column_count)
+ return etmc->column_count (etm, etmc->model_data);
+ else
+ return 0;
+}
+
+
+static gboolean
+etmc_has_save_id (ETreeModel *etm)
+{
+ ETreeMemoryCallbacks *etmc = E_TREE_MEMORY_CALLBACKS(etm);
+
+ if (etmc->has_save_id)
+ return etmc->has_save_id (etm, etmc->model_data);
+ else
+ return FALSE;
+}
+
+static char *
+etmc_get_save_id (ETreeModel *etm, ETreePath node)
+{
+ ETreeMemoryCallbacks *etmc = E_TREE_MEMORY_CALLBACKS(etm);
+
+ if (etmc->get_save_id)
+ return etmc->get_save_id (etm, node, etmc->model_data);
+ else
+ return NULL;
+}
+
+
+static void *
+etmc_value_at (ETreeModel *etm, ETreePath node, int col)
+{
+ ETreeMemoryCallbacks *etmc = E_TREE_MEMORY_CALLBACKS(etm);
+
+ return etmc->value_at (etm, node, col, etmc->model_data);
+}
+
+static void
+etmc_set_value_at (ETreeModel *etm, ETreePath node, int col, const void *val)
+{
+ ETreeMemoryCallbacks *etmc = E_TREE_MEMORY_CALLBACKS(etm);
+
+ etmc->set_value_at (etm, node, col, val, etmc->model_data);
+}
+
+static gboolean
+etmc_is_editable (ETreeModel *etm, ETreePath node, int col)
+{
+ ETreeMemoryCallbacks *etmc = E_TREE_MEMORY_CALLBACKS(etm);
+
+ return etmc->is_editable (etm, node, col, etmc->model_data);
+}
+
+
+/* The default for etmc_duplicate_value is to return the raw value. */
+static void *
+etmc_duplicate_value (ETreeModel *etm, int col, const void *value)
+{
+ ETreeMemoryCallbacks *etmc = E_TREE_MEMORY_CALLBACKS(etm);
+
+ if (etmc->duplicate_value)
+ return etmc->duplicate_value (etm, col, value, etmc->model_data);
+ else
+ return (void *)value;
+}
+
+static void
+etmc_free_value (ETreeModel *etm, int col, void *value)
+{
+ ETreeMemoryCallbacks *etmc = E_TREE_MEMORY_CALLBACKS(etm);
+
+ if (etmc->free_value)
+ etmc->free_value (etm, col, value, etmc->model_data);
+}
+
+static void *
+etmc_initialize_value (ETreeModel *etm, int col)
+{
+ ETreeMemoryCallbacks *etmc = E_TREE_MEMORY_CALLBACKS(etm);
+
+ if (etmc->initialize_value)
+ return etmc->initialize_value (etm, col, etmc->model_data);
+ else
+ return NULL;
+}
+
+static gboolean
+etmc_value_is_empty (ETreeModel *etm, int col, const void *value)
+{
+ ETreeMemoryCallbacks *etmc = E_TREE_MEMORY_CALLBACKS(etm);
+
+ if (etmc->value_is_empty)
+ return etmc->value_is_empty (etm, col, value, etmc->model_data);
+ else
+ return FALSE;
+}
+
+static char *
+etmc_value_to_string (ETreeModel *etm, int col, const void *value)
+{
+ ETreeMemoryCallbacks *etmc = E_TREE_MEMORY_CALLBACKS(etm);
+
+ if (etmc->value_to_string)
+ return etmc->value_to_string (etm, col, value, etmc->model_data);
+ else
+ return g_strdup ("");
+}
+
+static void
+e_tree_memory_callbacks_class_init (GtkObjectClass *object_class)
+{
+ ETreeModelClass *model_class = (ETreeModelClass *) object_class;
+
+ model_class->icon_at = etmc_icon_at;
+
+ model_class->column_count = etmc_column_count;
+
+ model_class->has_save_id = etmc_has_save_id;
+ model_class->get_save_id = etmc_get_save_id;
+
+ model_class->value_at = etmc_value_at;
+ model_class->set_value_at = etmc_set_value_at;
+ model_class->is_editable = etmc_is_editable;
+
+ model_class->duplicate_value = etmc_duplicate_value;
+ model_class->free_value = etmc_free_value;
+ model_class->initialize_value = etmc_initialize_value;
+ model_class->value_is_empty = etmc_value_is_empty;
+ model_class->value_to_string = etmc_value_to_string;
+}
+
+E_MAKE_TYPE(e_tree_memory_callbacks, "ETreeMemoryCallbacks", ETreeMemoryCallbacks, e_tree_memory_callbacks_class_init, NULL, PARENT_TYPE)
+
+/**
+ * e_tree_memory_callbacks_new:
+ *
+ * This initializes a new ETreeMemoryCallbacksModel object.
+ * ETreeMemoryCallbacksModel is an implementaiton of the somewhat
+ * abstract class ETreeMemory. The ETreeMemoryCallbacksModel is
+ * designed to allow people to easily create ETreeMemorys without
+ * having to create a new GtkType derived from ETreeMemory every time
+ * they need one.
+ *
+ * Instead, ETreeMemoryCallbacksModel uses a setup based in callback functions, every
+ * callback function signature mimics the signature of each ETreeModel method
+ * and passes the extra @data pointer to each one of the method to provide them
+ * with any context they might want to use.
+ *
+ * ETreeMemoryCallbacks is to ETreeMemory as ETableSimple is to ETableModel.
+ *
+ * Return value: An ETreeMemoryCallbacks object (which is also an
+ * ETreeMemory and thus an ETreeModel object).
+ *
+ */
+ETreeModel *
+e_tree_memory_callbacks_new (ETreeMemoryCallbacksIconAtFn icon_at,
+
+ ETreeMemoryCallbacksColumnCountFn column_count,
+
+ ETreeMemoryCallbacksHasSaveIdFn has_save_id,
+ ETreeMemoryCallbacksGetSaveIdFn get_save_id,
+
+ ETreeMemoryCallbacksValueAtFn value_at,
+ ETreeMemoryCallbacksSetValueAtFn set_value_at,
+ ETreeMemoryCallbacksIsEditableFn is_editable,
+
+ ETreeMemoryCallbacksDuplicateValueFn duplicate_value,
+ ETreeMemoryCallbacksFreeValueFn free_value,
+ ETreeMemoryCallbacksInitializeValueFn initialize_value,
+ ETreeMemoryCallbacksValueIsEmptyFn value_is_empty,
+ ETreeMemoryCallbacksValueToStringFn value_to_string,
+
+ gpointer model_data)
+{
+ ETreeMemoryCallbacks *etmc;
+
+ etmc = gtk_type_new (e_tree_memory_callbacks_get_type ());
+
+ etmc->icon_at = icon_at;
+
+ etmc->column_count = column_count;
+
+ etmc->has_save_id = has_save_id;
+ etmc->get_save_id = get_save_id;
+
+ etmc->value_at = value_at;
+ etmc->set_value_at = set_value_at;
+ etmc->is_editable = is_editable;
+
+ etmc->duplicate_value = duplicate_value;
+ etmc->free_value = free_value;
+ etmc->initialize_value = initialize_value;
+ etmc->value_is_empty = value_is_empty;
+ etmc->value_to_string = value_to_string;
+
+ etmc->model_data = model_data;
+
+ return (ETreeModel*)etmc;
+}
+
diff --git a/widgets/table/e-tree-memory-callbacks.h b/widgets/table/e-tree-memory-callbacks.h
new file mode 100644
index 0000000000..b419ea8d4f
--- /dev/null
+++ b/widgets/table/e-tree-memory-callbacks.h
@@ -0,0 +1,88 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+#ifndef _E_TREE_MEMORY_CALLBACKS_H_
+#define _E_TREE_MEMORY_CALLBACKS_H_
+
+#include <gal/e-table/e-tree-memory.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+#define E_TREE_MEMORY_CALLBACKS_TYPE (e_tree_memory_callbacks_get_type ())
+#define E_TREE_MEMORY_CALLBACKS(o) (GTK_CHECK_CAST ((o), E_TREE_MEMORY_CALLBACKS_TYPE, ETreeMemoryCallbacks))
+#define E_TREE_MEMORY_CALLBACKS_CLASS(k) (GTK_CHECK_CLASS_CAST((k), E_TREE_MEMORY_CALLBACKS_TYPE, ETreeMemoryCallbacksClass))
+#define E_IS_TREE_MEMORY_CALLBACKS(o) (GTK_CHECK_TYPE ((o), E_TREE_MEMORY_CALLBACKS_TYPE))
+#define E_IS_TREE_MEMORY_CALLBACKS_CLASS(k) (GTK_CHECK_CLASS_TYPE ((k), E_TREE_MEMORY_CALLBACKS_TYPE))
+
+
+typedef GdkPixbuf* (*ETreeMemoryCallbacksIconAtFn) (ETreeModel *etree, ETreePath path, void *model_data);
+
+typedef gint (*ETreeMemoryCallbacksColumnCountFn) (ETreeModel *etree, void *model_data);
+
+typedef gboolean (*ETreeMemoryCallbacksHasSaveIdFn) (ETreeModel *etree, void *model_data);
+typedef gchar *(*ETreeMemoryCallbacksGetSaveIdFn) (ETreeModel *etree, ETreePath path, void *model_data);
+
+typedef void* (*ETreeMemoryCallbacksValueAtFn) (ETreeModel *etree, ETreePath path, int col, void *model_data);
+typedef void (*ETreeMemoryCallbacksSetValueAtFn) (ETreeModel *etree, ETreePath path, int col, const void *val, void *model_data);
+typedef gboolean (*ETreeMemoryCallbacksIsEditableFn) (ETreeModel *etree, ETreePath path, int col, void *model_data);
+
+typedef void *(*ETreeMemoryCallbacksDuplicateValueFn) (ETreeModel *etm, int col, const void *val, void *data);
+typedef void (*ETreeMemoryCallbacksFreeValueFn) (ETreeModel *etm, int col, void *val, void *data);
+typedef void *(*ETreeMemoryCallbacksInitializeValueFn) (ETreeModel *etm, int col, void *data);
+typedef gboolean (*ETreeMemoryCallbacksValueIsEmptyFn) (ETreeModel *etm, int col, const void *val, void *data);
+typedef char *(*ETreeMemoryCallbacksValueToStringFn) (ETreeModel *etm, int col, const void *val, void *data);
+
+typedef struct {
+ ETreeMemory parent;
+
+ ETreeMemoryCallbacksIconAtFn icon_at;
+
+ ETreeMemoryCallbacksColumnCountFn column_count;
+
+ ETreeMemoryCallbacksHasSaveIdFn has_save_id;
+ ETreeMemoryCallbacksGetSaveIdFn get_save_id;
+
+ ETreeMemoryCallbacksValueAtFn value_at;
+ ETreeMemoryCallbacksSetValueAtFn set_value_at;
+ ETreeMemoryCallbacksIsEditableFn is_editable;
+
+ ETreeMemoryCallbacksDuplicateValueFn duplicate_value;
+ ETreeMemoryCallbacksFreeValueFn free_value;
+ ETreeMemoryCallbacksInitializeValueFn initialize_value;
+ ETreeMemoryCallbacksValueIsEmptyFn value_is_empty;
+ ETreeMemoryCallbacksValueToStringFn value_to_string;
+
+ gpointer model_data;
+} ETreeMemoryCallbacks;
+
+typedef struct {
+ ETreeMemoryClass parent_class;
+} ETreeMemoryCallbacksClass;
+
+GtkType e_tree_memory_callbacks_get_type (void);
+
+ETreeModel *e_tree_memory_callbacks_new (ETreeMemoryCallbacksIconAtFn icon_at,
+
+ ETreeMemoryCallbacksColumnCountFn column_count,
+
+ ETreeMemoryCallbacksHasSaveIdFn has_save_id,
+ ETreeMemoryCallbacksGetSaveIdFn get_save_id,
+
+ ETreeMemoryCallbacksValueAtFn value_at,
+ ETreeMemoryCallbacksSetValueAtFn set_value_at,
+ ETreeMemoryCallbacksIsEditableFn is_editable,
+
+ ETreeMemoryCallbacksDuplicateValueFn duplicate_value,
+ ETreeMemoryCallbacksFreeValueFn free_value,
+ ETreeMemoryCallbacksInitializeValueFn initialize_value,
+ ETreeMemoryCallbacksValueIsEmptyFn value_is_empty,
+ ETreeMemoryCallbacksValueToStringFn value_to_string,
+
+ gpointer model_data);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* _E_TREE_MEMORY_CALLBACKS_H_ */
diff --git a/widgets/table/e-tree-memory.c b/widgets/table/e-tree-memory.c
new file mode 100644
index 0000000000..88e9cd9a55
--- /dev/null
+++ b/widgets/table/e-tree-memory.c
@@ -0,0 +1,548 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * e-tree-memory.c: a Tree Model implementation that the programmer builds in memory.
+ *
+ * Author:
+ * Chris Toshok (toshok@ximian.com)
+ * Chris Lahey <clahey@ximian.com>
+ *
+ * Adapted from the gtree code and ETableModel.
+ *
+ * (C) 2000, 2001 Ximian, Inc.
+ */
+#include <config.h>
+
+#include <stdio.h>
+#include <errno.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#include <gnome-xml/parser.h>
+#include <gnome-xml/xmlmemory.h>
+
+#include <gtk/gtksignal.h>
+#include <stdlib.h>
+#include "gal/util/e-util.h"
+#include "gal/util/e-xml-utils.h"
+#include "e-tree-memory.h"
+
+#define PARENT_TYPE E_TREE_MODEL_TYPE
+
+#define TREEPATH_CHUNK_AREA_SIZE (30 * sizeof (ETreeMemoryPath))
+
+static ETreeModel *parent_class;
+static GMemChunk *node_chunk;
+
+typedef struct ETreeMemoryPath ETreeMemoryPath;
+
+struct ETreeMemoryPath {
+ gpointer node_data;
+
+ /* parent/child/sibling pointers */
+ ETreeMemoryPath *parent;
+ ETreeMemoryPath *next_sibling;
+ ETreeMemoryPath *prev_sibling;
+ ETreeMemoryPath *first_child;
+ ETreeMemoryPath *last_child;
+
+ gint num_children;
+};
+
+struct ETreeMemoryPriv {
+ ETreeMemoryPath *root;
+ gboolean expanded_default; /* whether nodes are created expanded or collapsed by default */
+ gint frozen;
+};
+
+
+/* ETreeMemoryPath functions */
+
+static int
+e_tree_memory_path_depth (ETreeMemoryPath *path)
+{
+ int depth = 0;
+
+ g_return_val_if_fail(path != NULL, -1);
+
+ for ( path = path->parent; path; path = path->parent)
+ depth ++;
+ return depth;
+}
+
+static void
+e_tree_memory_path_insert (ETreeMemoryPath *parent, int position, ETreeMemoryPath *child)
+{
+ g_return_if_fail (position <= parent->num_children && position >= -1);
+
+ child->parent = parent;
+
+ if (parent->first_child == NULL)
+ parent->first_child = child;
+
+ if (position == -1 || position == parent->num_children) {
+ child->prev_sibling = parent->last_child;
+ if (parent->last_child)
+ parent->last_child->next_sibling = child;
+ parent->last_child = child;
+ } else {
+ ETreeMemoryPath *c;
+ for (c = parent->first_child; c; c = c->next_sibling) {
+ if (position == 0) {
+ child->next_sibling = c;
+ child->prev_sibling = c->prev_sibling;
+
+ if (child->next_sibling)
+ child->next_sibling->prev_sibling = child;
+ if (child->prev_sibling)
+ child->prev_sibling->next_sibling = child;
+
+ if (parent->first_child == c)
+ parent->first_child = child;
+ break;
+ }
+ position --;
+ }
+ }
+
+ parent->num_children++;
+}
+
+static void
+e_tree_path_unlink (ETreeMemoryPath *path)
+{
+ ETreeMemoryPath *parent = path->parent;
+
+ /* unlink first/last child if applicable */
+ if (parent) {
+ if (path == parent->first_child)
+ parent->first_child = path->next_sibling;
+ if (path == parent->last_child)
+ parent->last_child = path->prev_sibling;
+
+ parent->num_children --;
+ }
+
+ /* unlink prev/next sibling links */
+ if (path->next_sibling)
+ path->next_sibling->prev_sibling = path->prev_sibling;
+ if (path->prev_sibling)
+ path->prev_sibling->next_sibling = path->next_sibling;
+
+ path->parent = NULL;
+ path->next_sibling = NULL;
+ path->prev_sibling = NULL;
+}
+
+
+
+/**
+ * e_tree_memory_freeze:
+ * @etmm: the ETreeModel to freeze.
+ *
+ * This function prepares an ETreeModel for a period of much change.
+ * All signals regarding changes to the tree are deferred until we
+ * thaw the tree.
+ *
+ **/
+void
+e_tree_memory_freeze(ETreeMemory *etmm)
+{
+ ETreeMemoryPriv *priv = etmm->priv;
+
+ e_tree_model_pre_change(E_TREE_MODEL(etmm));
+
+ priv->frozen ++;
+}
+
+/**
+ * e_tree_memory_thaw:
+ * @etmm: the ETreeMemory to thaw.
+ *
+ * This function thaws an ETreeMemory. All the defered signals can add
+ * up to a lot, we don't know - so we just emit a model_changed
+ * signal.
+ *
+ **/
+void
+e_tree_memory_thaw(ETreeMemory *etmm)
+{
+ ETreeMemoryPriv *priv = etmm->priv;
+
+ if (priv->frozen > 0)
+ priv->frozen --;
+ if (priv->frozen == 0) {
+ e_tree_model_node_changed(E_TREE_MODEL(etmm), priv->root);
+ }
+}
+
+
+/* virtual methods */
+
+static void
+etmm_destroy (GtkObject *object)
+{
+ ETreeMemory *etmm = E_TREE_MEMORY (object);
+ ETreeMemoryPriv *priv = etmm->priv;
+
+ /* XXX lots of stuff to free here */
+
+ if (priv->root)
+ e_tree_memory_node_remove (etmm, priv->root);
+
+ g_free (priv);
+
+ GTK_OBJECT_CLASS (parent_class)->destroy (object);
+}
+
+static ETreePath
+etmm_get_root (ETreeModel *etm)
+{
+ ETreeMemoryPriv *priv = E_TREE_MEMORY(etm)->priv;
+ return priv->root;
+}
+
+static ETreePath
+etmm_get_parent (ETreeModel *etm, ETreePath node)
+{
+ ETreeMemoryPath *path = node;
+ return path->parent;
+}
+
+static ETreePath
+etmm_get_first_child (ETreeModel *etm, ETreePath node)
+{
+ ETreeMemoryPath *path = node;
+ return path->first_child;
+}
+
+static ETreePath
+etmm_get_last_child (ETreeModel *etm, ETreePath node)
+{
+ ETreeMemoryPath *path = node;
+ return path->last_child;
+}
+
+static ETreePath
+etmm_get_next (ETreeModel *etm, ETreePath node)
+{
+ ETreeMemoryPath *path = node;
+ return path->next_sibling;
+}
+
+static ETreePath
+etmm_get_prev (ETreeModel *etm, ETreePath node)
+{
+ ETreeMemoryPath *path = node;
+ return path->prev_sibling;
+}
+
+static gboolean
+etmm_is_root (ETreeModel *etm, ETreePath node)
+{
+ ETreeMemoryPath *path = node;
+ return e_tree_memory_path_depth (path) == 0;
+}
+
+static gboolean
+etmm_is_expandable (ETreeModel *etm, ETreePath node)
+{
+ ETreeMemoryPath *path = node;
+ return path->first_child != NULL;
+}
+
+static guint
+etmm_get_children (ETreeModel *etm, ETreePath node, ETreePath **nodes)
+{
+ ETreeMemoryPath *path = node;
+ guint n_children;
+
+ n_children = path->num_children;
+
+ if (nodes) {
+ ETreeMemoryPath *p;
+ int i = 0;
+
+ (*nodes) = g_malloc (sizeof (ETreePath) * n_children);
+ for (p = path->first_child; p; p = p->next_sibling) {
+ (*nodes)[i++] = p;
+ }
+ }
+
+ return n_children;
+}
+
+static guint
+etmm_depth (ETreeModel *etm, ETreePath path)
+{
+ return e_tree_memory_path_depth(path);
+}
+
+static gboolean
+etmm_get_expanded_default (ETreeModel *etm)
+{
+ ETreeMemory *etmm = E_TREE_MEMORY (etm);
+ ETreeMemoryPriv *priv = etmm->priv;
+
+ return priv->expanded_default;
+}
+
+
+static void
+e_tree_memory_class_init (GtkObjectClass *klass)
+{
+ ETreeModelClass *tree_class = (ETreeModelClass *) klass;
+
+ parent_class = gtk_type_class (PARENT_TYPE);
+
+ node_chunk = g_mem_chunk_create (ETreeMemoryPath, TREEPATH_CHUNK_AREA_SIZE, G_ALLOC_AND_FREE);
+
+ klass->destroy = etmm_destroy;
+
+ tree_class->get_root = etmm_get_root;
+ tree_class->get_prev = etmm_get_prev;
+ tree_class->get_next = etmm_get_next;
+ tree_class->get_first_child = etmm_get_first_child;
+ tree_class->get_last_child = etmm_get_last_child;
+ tree_class->get_parent = etmm_get_parent;
+
+ tree_class->is_root = etmm_is_root;
+ tree_class->is_expandable = etmm_is_expandable;
+ tree_class->get_children = etmm_get_children;
+ tree_class->depth = etmm_depth;
+ tree_class->get_expanded_default = etmm_get_expanded_default;
+}
+
+static void
+e_tree_memory_init (GtkObject *object)
+{
+ ETreeMemory *etmm = (ETreeMemory *)object;
+
+ ETreeMemoryPriv *priv;
+
+ priv = g_new0 (ETreeMemoryPriv, 1);
+ etmm->priv = priv;
+
+ priv->root = NULL;
+ priv->frozen = 0;
+ priv->expanded_default = 0;
+}
+
+E_MAKE_TYPE(e_tree_memory, "ETreeMemory", ETreeMemory, e_tree_memory_class_init, e_tree_memory_init, PARENT_TYPE)
+
+
+
+/**
+ * e_tree_memory_construct:
+ * @etree:
+ *
+ *
+ **/
+void
+e_tree_memory_construct (ETreeMemory *etmm)
+{
+}
+
+/**
+ * e_tree_memory_new
+ *
+ * XXX docs here.
+ *
+ * return values: a newly constructed ETreeMemory.
+ */
+ETreeMemory *
+e_tree_memory_new (void)
+{
+ ETreeMemory *etmm;
+
+ etmm = gtk_type_new (e_tree_memory_get_type ());
+
+ e_tree_memory_construct(etmm);
+
+ return etmm;
+}
+
+void
+e_tree_memory_set_expanded_default (ETreeMemory *etree, gboolean expanded)
+{
+ etree->priv->expanded_default = expanded;
+}
+
+/**
+ * e_tree_memory_node_get_data:
+ * @etmm:
+ * @node:
+ *
+ *
+ *
+ * Return value:
+ **/
+gpointer
+e_tree_memory_node_get_data (ETreeMemory *etmm, ETreePath node)
+{
+ ETreeMemoryPath *path = node;
+
+ g_return_val_if_fail (path, NULL);
+
+ return path->node_data;
+}
+
+/**
+ * e_tree_memory_node_set_data:
+ * @etmm:
+ * @node:
+ * @node_data:
+ *
+ *
+ **/
+void
+e_tree_memory_node_set_data (ETreeMemory *etmm, ETreePath node, gpointer node_data)
+{
+ ETreeMemoryPath *path = node;
+
+ g_return_if_fail (path);
+
+ path->node_data = node_data;
+}
+
+/**
+ * e_tree_memory_node_insert:
+ * @tree_model:
+ * @parent_path:
+ * @position:
+ * @node_data:
+ *
+ *
+ *
+ * Return value:
+ **/
+ETreePath
+e_tree_memory_node_insert (ETreeMemory *tree_model,
+ ETreePath parent_node,
+ int position,
+ gpointer node_data)
+{
+ ETreeMemoryPriv *priv;
+ ETreeMemoryPath *new_path;
+ ETreeMemoryPath *parent_path = parent_node;
+
+ g_return_val_if_fail(tree_model != NULL, NULL);
+
+ priv = tree_model->priv;
+
+ g_return_val_if_fail (parent_path != NULL || priv->root == NULL, NULL);
+
+ priv = tree_model->priv;
+
+ if (!tree_model->priv->frozen)
+ e_tree_model_pre_change(E_TREE_MODEL(tree_model));
+
+ new_path = g_chunk_new0 (ETreeMemoryPath, node_chunk);
+
+ new_path->node_data = node_data;
+
+ if (parent_path != NULL) {
+ e_tree_memory_path_insert (parent_path, position, new_path);
+ if (!tree_model->priv->frozen)
+ e_tree_model_node_inserted (E_TREE_MODEL(tree_model), parent_path, new_path);
+ }
+ else {
+ priv->root = new_path;
+ if (!tree_model->priv->frozen)
+ e_tree_model_node_changed(E_TREE_MODEL(tree_model), new_path);
+ }
+
+ return new_path;
+}
+
+ETreePath e_tree_memory_node_insert_id (ETreeMemory *etree, ETreePath parent, int position, gpointer node_data, char *id)
+{
+ return e_tree_memory_node_insert(etree, parent, position, node_data);
+}
+
+/**
+ * e_tree_memory_node_insert_before:
+ * @etree:
+ * @parent:
+ * @sibling:
+ * @node_data:
+ *
+ *
+ *
+ * Return value:
+ **/
+ETreePath
+e_tree_memory_node_insert_before (ETreeMemory *etree,
+ ETreePath parent,
+ ETreePath sibling,
+ gpointer node_data)
+{
+ ETreeMemoryPath *child;
+ ETreeMemoryPath *parent_path = parent;
+ ETreeMemoryPath *sibling_path = sibling;
+ int position = 0;
+
+ g_return_val_if_fail(etree != NULL, NULL);
+
+ if (sibling != NULL) {
+ for (child = parent_path->first_child; child; child = child->next_sibling) {
+ if (child == sibling_path)
+ break;
+ position ++;
+ }
+ } else
+ position = parent_path->num_children;
+ return e_tree_memory_node_insert (etree, parent, position, node_data);
+}
+
+/* just blows away child data, doesn't take into account unlinking/etc */
+static void
+child_free(ETreeMemory *etree, ETreeMemoryPath *node)
+{
+ ETreeMemoryPath *child, *next;
+
+ child = node->first_child;
+ while (child) {
+ next = child->next_sibling;
+ child_free(etree, child);
+ child = next;
+ }
+
+ g_chunk_free(node, node_chunk);
+}
+
+/**
+ * e_tree_memory_node_remove:
+ * @etree:
+ * @path:
+ *
+ *
+ *
+ * Return value:
+ **/
+gpointer
+e_tree_memory_node_remove (ETreeMemory *etree, ETreePath node)
+{
+ ETreeMemoryPath *path = node;
+ ETreeMemoryPath *parent = path->parent;
+ gpointer ret = path->node_data;
+
+ g_return_val_if_fail(etree != NULL, NULL);
+
+ if (!etree->priv->frozen)
+ e_tree_model_pre_change(E_TREE_MODEL(etree));
+
+ /* unlink this node - we only have to unlink the root node being removed,
+ since the others are only references from this node */
+ e_tree_path_unlink (path);
+
+ /*printf("removing %d nodes from position %d\n", visible, base);*/
+ if (!etree->priv->frozen)
+ e_tree_model_node_removed(E_TREE_MODEL(etree), parent, path);
+
+ child_free(etree, path);
+
+ if (path == etree->priv->root)
+ etree->priv->root = NULL;
+
+ return ret;
+}
diff --git a/widgets/table/e-tree-memory.h b/widgets/table/e-tree-memory.h
new file mode 100644
index 0000000000..264df86acb
--- /dev/null
+++ b/widgets/table/e-tree-memory.h
@@ -0,0 +1,54 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+#ifndef _E_TREE_MEMORY_H_
+#define _E_TREE_MEMORY_H_
+
+#include <gdk-pixbuf/gdk-pixbuf.h>
+#include <gal/e-table/e-tree-model.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+
+#define E_TREE_MEMORY_TYPE (e_tree_memory_get_type ())
+#define E_TREE_MEMORY(o) (GTK_CHECK_CAST ((o), E_TREE_MEMORY_TYPE, ETreeMemory))
+#define E_TREE_MEMORY_CLASS(k) (GTK_CHECK_CLASS_CAST((k), E_TREE_MEMORY_TYPE, ETreeMemoryClass))
+#define E_IS_TREE_MEMORY(o) (GTK_CHECK_TYPE ((o), E_TREE_MEMORY_TYPE))
+#define E_IS_TREE_MEMORY_CLASS(k) (GTK_CHECK_CLASS_TYPE ((k), E_TREE_MEMORY_TYPE))
+
+typedef struct ETreeMemory ETreeMemory;
+typedef struct ETreeMemoryPriv ETreeMemoryPriv;
+typedef struct ETreeMemoryClass ETreeMemoryClass;
+
+struct ETreeMemory {
+ ETreeModel base;
+ ETreeMemoryPriv *priv;
+};
+
+struct ETreeMemoryClass {
+ ETreeModelClass parent_class;
+};
+
+GtkType e_tree_memory_get_type (void);
+void e_tree_memory_construct (ETreeMemory *etree);
+ETreeMemory *e_tree_memory_new (void);
+
+/* node operations */
+ETreePath e_tree_memory_node_insert (ETreeMemory *etree, ETreePath parent, int position, gpointer node_data);
+ETreePath e_tree_memory_node_insert_id (ETreeMemory *etree, ETreePath parent, int position, gpointer node_data, char *id);
+ETreePath e_tree_memory_node_insert_before (ETreeMemory *etree, ETreePath parent, ETreePath sibling, gpointer node_data);
+gpointer e_tree_memory_node_remove (ETreeMemory *etree, ETreePath path);
+
+/* Freeze and thaw */
+void e_tree_memory_freeze (ETreeMemory *etree);
+void e_tree_memory_thaw (ETreeMemory *etree);
+
+void e_tree_memory_set_expanded_default (ETreeMemory *etree, gboolean expanded);
+gpointer e_tree_memory_node_get_data (ETreeMemory *etm, ETreePath node);
+void e_tree_memory_node_set_data (ETreeMemory *etm, ETreePath node, gpointer node_data);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* _E_TREE_MEMORY_H */
diff --git a/widgets/table/e-tree-model.c b/widgets/table/e-tree-model.c
index 0658cef7db..389e37223a 100644
--- a/widgets/table/e-tree-model.c
+++ b/widgets/table/e-tree-model.c
@@ -4,10 +4,11 @@
*
* Author:
* Chris Toshok (toshok@ximian.com)
+ * Chris Lahey <clahey@ximian.com>
*
* Adapted from the gtree code and ETableModel.
*
- * (C) 2000 Ximian, Inc.
+ * (C) 2000, 2001 Ximian, Inc.
*/
#include <config.h>
@@ -27,566 +28,37 @@
#define ETM_CLASS(e) ((ETreeModelClass *)((GtkObject *)e)->klass)
-#define PARENT_TYPE E_TABLE_MODEL_TYPE
+#define PARENT_TYPE (gtk_object_get_type())
-#define TREEPATH_CHUNK_AREA_SIZE (30 * sizeof (ETreePath))
-
-static ETableModel *e_tree_model_parent_class;
-
-struct ETreeModelPriv {
- GMemChunk *node_chunk;
- ETreePath *root;
- gboolean root_visible;
- GArray *row_array; /* used in the mapping between ETable and our tree */
- GHashTable *expanded_state; /* used for loading/saving expanded state */
- GString *sort_group; /* for caching the last sort group info */
- gboolean expanded_default; /* whether nodes are created expanded or collapsed by default */
- gint frozen;
-};
-
-struct ETreePath {
- gboolean expanded;
- gboolean expanded_set;
- guint visible_descendents;
- char *save_id;
- ETreePathCompareFunc compare;
- gpointer node_data;
-
- /* parent/child/sibling pointers */
- ETreePath *parent;
- ETreePath *next_sibling;
- ETreePath *prev_sibling;
- ETreePath *first_child;
- ETreePath *last_child;
- guint32 num_children;
-};
+static GtkObjectClass *parent_class;
enum {
+ PRE_CHANGE,
NODE_CHANGED,
+ NODE_DATA_CHANGED,
+ NODE_COL_CHANGED,
NODE_INSERTED,
NODE_REMOVED,
- NODE_COLLAPSED,
- NODE_EXPANDED,
LAST_SIGNAL
};
static guint e_tree_model_signals [LAST_SIGNAL] = {0, };
-static void add_visible_descendents_to_array (ETreeModel *etm, ETreePath *node, int *row, int *count);
-
-
-/* ETreePath functions */
-
-static int
-e_tree_path_depth (ETreePath *path)
-{
- int depth = 0;
- while (path) {
- depth ++;
- path = path->parent;
- }
- return depth;
-}
-
-static void
-e_tree_path_insert (ETreePath *parent, int position, ETreePath *child)
-{
- g_return_if_fail (position <= parent->num_children || position == -1);
-
- child->parent = parent;
-
- if (parent->first_child == NULL)
- parent->first_child = child;
-
- if (position == -1 || position == parent->num_children) {
- child->prev_sibling = parent->last_child;
- if (parent->last_child)
- parent->last_child->next_sibling = child;
- parent->last_child = child;
- }
- else {
- ETreePath *c;
- for (c = parent->first_child; c; c = c->next_sibling) {
- if (position == 0) {
- child->next_sibling = c;
- child->prev_sibling = c->prev_sibling;
-
- if (child->next_sibling)
- child->next_sibling->prev_sibling = child;
- if (child->prev_sibling)
- child->prev_sibling->next_sibling = child;
-
- if (parent->first_child == c)
- parent->first_child = child;
- break;
- }
- position --;
- }
- }
-
- parent->num_children++;
-}
-
-static void
-e_tree_path_unlink (ETreePath *path)
-{
- ETreePath *parent = path->parent;
-
- /* unlink first/last child if applicable */
- if (parent) {
- if (path == parent->first_child)
- parent->first_child = path->next_sibling;
- if (path == parent->last_child)
- parent->last_child = path->prev_sibling;
-
- parent->num_children --;
- }
-
- /* unlink prev/next sibling links */
- if (path->next_sibling)
- path->next_sibling->prev_sibling = path->prev_sibling;
- if (path->prev_sibling)
- path->prev_sibling->next_sibling = path->next_sibling;
-
- path->parent = NULL;
- path->next_sibling = NULL;
- path->prev_sibling = NULL;
-}
-
-/**
- * e_tree_model_node_traverse:
- * @model:
- * @path:
- * @func:
- * @data:
- *
- *
- **/
-void
-e_tree_model_node_traverse (ETreeModel *model, ETreePath *path, ETreePathFunc func, gpointer data)
-{
- ETreePath *child;
-
- g_return_if_fail (path);
-
- child = path->first_child;
-
- while (child) {
- ETreePath *next_child = child->next_sibling;
- e_tree_model_node_traverse (model, child, func, data);
- if (func (model, child, data) == TRUE)
- return;
-
- child = next_child;
- }
-}
-
-
-
-/**
- * e_tree_model_freeze:
- * @etm: the ETreeModel to freeze.
- *
- * This function prepares an ETreeModel for a period of much change.
- * All signals regarding changes to the tree are deferred until we
- * thaw the tree.
- *
- **/
-void
-e_tree_model_freeze(ETreeModel *etm)
-{
- ETreeModelPriv *priv = etm->priv;
-
- priv->frozen ++;
-}
-
-/**
- * e_tree_model_thaw:
- * @etm: the ETreeModel to thaw.
- *
- * This function thaws an ETreeModel. All the defered signals can add
- * up to a lot, we don't know - so we just emit a model_changed
- * signal.
- *
- **/
-void
-e_tree_model_thaw(ETreeModel *etm)
-{
- ETreeModelPriv *priv = etm->priv;
-
- if (priv->frozen > 0)
- priv->frozen --;
- if (priv->frozen == 0) {
- e_table_model_changed(E_TABLE_MODEL(etm));
- }
-}
-
-
-/* virtual methods */
-
-static void
-etree_destroy (GtkObject *object)
-{
- ETreeModel *etree = E_TREE_MODEL (object);
- ETreeModelPriv *priv = etree->priv;
-
- /* XXX lots of stuff to free here */
-
- if (priv->root)
- e_tree_model_node_remove (etree, priv->root);
-
- g_array_free (priv->row_array, TRUE);
- g_hash_table_destroy (priv->expanded_state);
-
- g_string_free(priv->sort_group, TRUE);
-
- g_free (priv);
-
- GTK_OBJECT_CLASS (e_tree_model_parent_class)->destroy (object);
-}
-
-static ETreePath*
-etree_get_root (ETreeModel *etm)
-{
- ETreeModelPriv *priv = etm->priv;
- return priv->root;
-}
-
-static ETreePath*
-etree_get_parent (ETreeModel *etm, ETreePath *path)
-{
- g_return_val_if_fail (path, NULL);
-
- return path->parent;
-}
-
-static ETreePath*
-etree_get_next (ETreeModel *etm, ETreePath *node)
-{
- g_return_val_if_fail (node, NULL);
-
- return node->next_sibling;
-}
-
-static ETreePath*
-etree_get_prev (ETreeModel *etm, ETreePath *node)
-{
- g_return_val_if_fail (node, NULL);
-
- return node->prev_sibling;
-}
-
-static ETreePath*
-etree_get_first_child (ETreeModel *etm, ETreePath *node)
-{
- g_return_val_if_fail (node, NULL);
-
- return node->first_child;
-}
-
-static ETreePath*
-etree_get_last_child (ETreeModel *etm, ETreePath *node)
-{
- g_return_val_if_fail (node, NULL);
-
- return node->last_child;
-}
-
-static guint
-etree_get_children (ETreeModel *etm, ETreePath* node, ETreePath ***paths)
-{
- guint n_children;
-
- g_return_val_if_fail (node, 0);
-
- n_children = node->num_children;
-
- if (paths) {
- ETreePath *p;
- int i = 0;
- (*paths) = g_malloc (sizeof (ETreePath*) * n_children);
- for (p = node->first_child; p; p = p->next_sibling) {
- (*paths)[i++] = p;
- }
- }
-
- return n_children;
-}
-
-static gboolean
-etree_is_expanded (ETreeModel *etm, ETreePath* node)
-{
- g_return_val_if_fail (node, FALSE);
-
- return node->expanded;
-}
-
-static gboolean
-etree_is_visible (ETreeModel *etm, ETreePath* node)
-{
- g_return_val_if_fail (node, FALSE);
-
- for (node = node->parent; node; node = node->parent) {
- if (!node->expanded)
- return FALSE;
- }
-
- return TRUE;
-}
-
-static void
-etree_set_expanded (ETreeModel *etm, ETreePath* node, gboolean expanded)
-{
- ETreeModelPriv *priv = etm->priv;
- ETreePath *child;
- int row;
-
- g_return_if_fail (node);
-
- node->expanded_set = TRUE;
-
- if (node->expanded == expanded)
- return;
-
- if (expanded) {
- gboolean allow_expand = TRUE;
- e_tree_model_node_expanded (etm, node, &allow_expand);
- if (!allow_expand)
- return;
- }
-
- node->expanded = expanded;
- if (node->save_id) {
- g_hash_table_insert (priv->expanded_state, node->save_id, (gpointer)expanded);
- }
-
- /* if the node wasn't visible at present */
- if ((row = e_tree_model_row_of_node (etm, node)) == -1) {
- if (!expanded) {
- e_tree_model_node_collapsed (etm, node);
- }
- return;
- }
-
- row++;
-
- if (expanded) {
- ETreePath *parent;
-
- if (e_tree_model_node_is_visible (etm, node)) {
- node->visible_descendents = 0;
- for (child = node->first_child; child;
- child = child->next_sibling) {
- add_visible_descendents_to_array (etm, child, &row, &node->visible_descendents);
- }
- }
- /* now iterate back up the tree, adding to our
- ancestors' visible descendents */
-
- for (parent = node->parent; parent; parent = parent->parent) {
- parent->visible_descendents += node->visible_descendents;
- }
- }
- else {
- int i;
- ETreePath *parent;
-
- if (e_tree_model_node_is_visible (etm, node)) {
- for (i = 0; i < node->visible_descendents; i ++) {
- priv->row_array = g_array_remove_index (priv->row_array, row);
- e_table_model_row_deleted (E_TABLE_MODEL (etm), row);
- }
- }
- /* now iterate back up the tree, subtracting from our
- ancestors' visible descendents */
-
- for (parent = node->parent; parent; parent = parent->parent) {
- parent->visible_descendents -= node->visible_descendents;
- }
-
- node->visible_descendents = 0;
-
- e_tree_model_node_collapsed (etm, node);
- }
-}
-
-/**
- * e_tree_model_set_expanded_default:
- * @etree: The ETreeModel we're setting the default expanded behavior on.
- * @expanded: Whether or not newly inserted parent nodes should be expanded by default.
- *
- *
- **/
-void
-e_tree_model_show_node (ETreeModel *etm, ETreePath* node)
-{
- ETreePath *parent;
-
- parent = e_tree_model_node_get_parent(etm, node);
- if (parent) {
- e_tree_model_show_node(etm, parent);
- e_tree_model_node_set_expanded(etm, parent, TRUE);
- }
-}
-
-void
-e_tree_model_set_expanded_default (ETreeModel *etree,
- gboolean expanded)
-{
- ETreeModelPriv *priv = etree->priv;
-
- priv->expanded_default = expanded;
-}
-
-/* fairly naive implementation */
-static void
-etree_set_expanded_recurse (ETreeModel *etm, ETreePath* node, gboolean expanded)
-{
- ETreePath *child;
-
- e_tree_model_node_set_expanded (etm, node, expanded);
-
- for (child = node->first_child; child; child = child->next_sibling)
- e_tree_model_node_set_expanded_recurse (etm, child, expanded);
-}
-
-static ETreePath *
-etree_node_at_row (ETreeModel *etree, int row)
-{
- ETreeModelPriv *priv = etree->priv;
-
- g_return_val_if_fail (row < priv->row_array->len, NULL);
-
- return g_array_index (priv->row_array, ETreePath*, row);
-}
-
-
-/* ETable analogs */
-static void*
-etree_value_at (ETreeModel *etm, ETreePath* node, int col)
-{
- /* shouldn't be called */
- g_assert (0);
- return NULL;
-}
-
-static GdkPixbuf*
-etree_icon_at (ETreeModel *etm, ETreePath* node)
-{
- /* shouldn't be called */
- g_assert (0);
- return NULL;
-}
-
-static void
-etree_set_value_at (ETreeModel *etm, ETreePath* node, int col, const void *val)
-{
- /* shouldn't be called */
- g_assert (0);
-}
-
-static gboolean
-etree_is_editable (ETreeModel *etm, ETreePath* node, int col)
-{
- /* shouldn't be called */
- g_assert(0);
- return FALSE;
-}
-
-
-/* ETable virtual functions we map */
-static int
-etable_row_count (ETableModel *etm)
-{
- ETreeModel *tree = E_TREE_MODEL (etm);
- ETreeModelPriv *priv = tree->priv;
- return priv->row_array->len;
-}
-
-static void *
-etable_value_at (ETableModel *etm, int col, int row)
-{
- ETreeModel *etree = E_TREE_MODEL(etm);
- ETreeModelClass *et_class = ETM_CLASS(etm);
- ETreePath* node = e_tree_model_node_at_row (etree, row);
-
- if (node == NULL)
- g_warning ("node is NULL for row %d in etable_value_at\n", row);
-
- if (col == -1)
- return node;
- else if (col == -2)
- return etm;
- else
- return et_class->value_at (etree, node, col);
-}
-
-static void
-etable_set_value_at (ETableModel *etm, int col, int row, const void *val)
-{
- ETreeModel *etree = E_TREE_MODEL(etm);
- ETreeModelClass *et_class = ETM_CLASS(etm);
- ETreePath* node = e_tree_model_node_at_row (etree, row);
-
- g_return_if_fail (node);
-
- et_class->set_value_at (etree, node, col, val);
-}
-
-static gboolean
-etable_is_cell_editable (ETableModel *etm, int col, int row)
-{
- ETreeModel *etree = E_TREE_MODEL(etm);
- ETreeModelClass *et_class = ETM_CLASS(etm);
- ETreePath* node = e_tree_model_node_at_row (etree, row);
-
- g_return_val_if_fail (node, FALSE);
-
- return et_class->is_editable (etree, node, col);
-}
-
-static void
-build_sort_group(GString *out, ETreePath *node)
-{
- if (node->parent) {
- build_sort_group(out, node->parent);
- }
- g_string_sprintfa(out, "/%p", node);
-}
-
-static const char *
-etable_row_sort_group(ETableModel *etm, int row)
-{
- ETreeModel *etree = E_TREE_MODEL(etm);
- ETreeModelPriv *priv = etree->priv;
- ETreePath* node = e_tree_model_node_at_row (etree, row);
-
- g_return_val_if_fail (node, "");
-
- g_string_truncate(priv->sort_group, 0);
- if (node)
- build_sort_group(priv->sort_group, node);
-
- return priv->sort_group->str;
-}
-
-static gboolean
-etable_has_sort_group(ETableModel *etm)
-{
- /* could optimise for the flat &/or unexpanded tree case */
- return TRUE;
-}
-
static void
e_tree_model_class_init (GtkObjectClass *klass)
{
- ETableModelClass *table_class = (ETableModelClass *) klass;
ETreeModelClass *tree_class = (ETreeModelClass *) klass;
- e_tree_model_parent_class = gtk_type_class (PARENT_TYPE);
-
- klass->destroy = etree_destroy;
+ parent_class = gtk_type_class (PARENT_TYPE);
+
+ e_tree_model_signals [PRE_CHANGE] =
+ gtk_signal_new ("pre_change",
+ GTK_RUN_LAST,
+ klass->type,
+ GTK_SIGNAL_OFFSET (ETreeModelClass, pre_change),
+ gtk_marshal_NONE__NONE,
+ GTK_TYPE_NONE, 0);
e_tree_model_signals [NODE_CHANGED] =
gtk_signal_new ("node_changed",
@@ -596,6 +68,22 @@ e_tree_model_class_init (GtkObjectClass *klass)
gtk_marshal_NONE__POINTER,
GTK_TYPE_NONE, 1, GTK_TYPE_POINTER);
+ e_tree_model_signals [NODE_DATA_CHANGED] =
+ gtk_signal_new ("node_data_changed",
+ GTK_RUN_LAST,
+ klass->type,
+ GTK_SIGNAL_OFFSET (ETreeModelClass, node_data_changed),
+ gtk_marshal_NONE__POINTER,
+ GTK_TYPE_NONE, 1, GTK_TYPE_POINTER);
+
+ e_tree_model_signals [NODE_COL_CHANGED] =
+ gtk_signal_new ("node_col_changed",
+ GTK_RUN_LAST,
+ klass->type,
+ GTK_SIGNAL_OFFSET (ETreeModelClass, node_col_changed),
+ gtk_marshal_NONE__POINTER_INT,
+ GTK_TYPE_NONE, 2, GTK_TYPE_POINTER, GTK_TYPE_INT);
+
e_tree_model_signals [NODE_INSERTED] =
gtk_signal_new ("node_inserted",
GTK_RUN_LAST,
@@ -612,66 +100,50 @@ e_tree_model_class_init (GtkObjectClass *klass)
gtk_marshal_NONE__POINTER_POINTER,
GTK_TYPE_NONE, 2, GTK_TYPE_POINTER, GTK_TYPE_POINTER);
- e_tree_model_signals [NODE_COLLAPSED] =
- gtk_signal_new ("node_collapsed",
- GTK_RUN_LAST,
- klass->type,
- GTK_SIGNAL_OFFSET (ETreeModelClass, node_collapsed),
- gtk_marshal_NONE__POINTER,
- GTK_TYPE_NONE, 1, GTK_TYPE_POINTER);
+ gtk_object_class_add_signals (klass, e_tree_model_signals, LAST_SIGNAL);
- e_tree_model_signals [NODE_EXPANDED] =
- gtk_signal_new ("node_expanded",
- GTK_RUN_LAST,
- klass->type,
- GTK_SIGNAL_OFFSET (ETreeModelClass, node_expanded),
- gtk_marshal_NONE__POINTER_POINTER,
- GTK_TYPE_NONE, 2, GTK_TYPE_POINTER, GTK_TYPE_POINTER);
+ tree_class->get_root = NULL;
- gtk_object_class_add_signals (klass, e_tree_model_signals, LAST_SIGNAL);
+ tree_class->get_parent = NULL;
+ tree_class->get_first_child = NULL;
+ tree_class->get_last_child = NULL;
+ tree_class->get_next = NULL;
+ tree_class->get_prev = NULL;
+
+ tree_class->is_root = NULL;
+ tree_class->is_expandable = NULL;
+ tree_class->get_children = NULL;
+ tree_class->depth = NULL;
+
+ tree_class->icon_at = NULL;
+
+ tree_class->get_expanded_default = NULL;
+ tree_class->column_count = NULL;
- table_class->row_count = etable_row_count;
- table_class->value_at = etable_value_at;
- table_class->set_value_at = etable_set_value_at;
- table_class->is_cell_editable = etable_is_cell_editable;
-#if 0
- /* XX need to pass these through */
- table_class->duplicate_value = etable_duplicate_value;
- table_class->free_value = etable_free_value;
- table_class->initialize_value = etable_initialize_value;
- table_class->value_is_empty = etable_value_is_empty;
- table_class->value_to_string = etable_value_to_string;
- table_class->thaw = etable_thaw;
-#endif
-
- table_class->row_sort_group = etable_row_sort_group;
- table_class->has_sort_group = etable_has_sort_group;
-
- tree_class->get_root = etree_get_root;
- tree_class->get_prev = etree_get_prev;
- tree_class->get_next = etree_get_next;
- tree_class->get_first_child = etree_get_first_child;
- tree_class->get_last_child = etree_get_last_child;
- tree_class->get_parent = etree_get_parent;
-
- tree_class->value_at = etree_value_at;
- tree_class->icon_at = etree_icon_at;
- tree_class->set_value_at = etree_set_value_at;
- tree_class->is_editable = etree_is_editable;
-
- tree_class->get_children = etree_get_children;
- tree_class->is_expanded = etree_is_expanded;
- tree_class->is_visible = etree_is_visible;
- tree_class->set_expanded = etree_set_expanded;
- tree_class->set_expanded_recurse = etree_set_expanded_recurse;
- tree_class->node_at_row = etree_node_at_row;
+ tree_class->has_save_id = NULL;
+ tree_class->get_save_id = NULL;
+
+ tree_class->value_at = NULL;
+ tree_class->set_value_at = NULL;
+ tree_class->is_editable = NULL;
+
+ tree_class->duplicate_value = NULL;
+ tree_class->free_value = NULL;
+ tree_class->initialize_value = NULL;
+ tree_class->value_is_empty = NULL;
+ tree_class->value_to_string = NULL;
+
+ tree_class->pre_change = NULL;
+ tree_class->node_changed = NULL;
+ tree_class->node_data_changed = NULL;
+ tree_class->node_col_changed = NULL;
+ tree_class->node_inserted = NULL;
+ tree_class->node_removed = NULL;
}
static void
e_tree_init (GtkObject *object)
{
- ETreeModel *etree = (ETreeModel *)object;
- e_tree_model_construct (etree);
}
E_MAKE_TYPE(e_tree_model, "ETreeModel", ETreeModel, e_tree_model_class_init, e_tree_init, PARENT_TYPE)
@@ -689,136 +161,114 @@ E_MAKE_TYPE(e_tree_model, "ETreeModel", ETreeModel, e_tree_model_class_init, e_t
* Return value:
**/
void
-e_tree_model_node_changed (ETreeModel *tree_model, ETreePath *node)
+e_tree_model_pre_change (ETreeModel *tree_model)
{
- int row;
g_return_if_fail (tree_model != NULL);
g_return_if_fail (E_IS_TREE_MODEL (tree_model));
- row = e_tree_model_row_of_node (tree_model, node);
- if (row != -1)
- e_table_model_row_changed (E_TABLE_MODEL (tree_model), row);
-
gtk_signal_emit (GTK_OBJECT (tree_model),
- e_tree_model_signals [NODE_CHANGED], node);
+ e_tree_model_signals [PRE_CHANGE]);
}
/**
- * e_tree_model_node_inserted:
+ * e_tree_model_node_changed:
* @tree_model:
- * @parent_node:
- * @inserted_node:
+ * @node:
*
*
+ *
+ * Return value:
**/
void
-e_tree_model_node_inserted (ETreeModel *tree_model,
- ETreePath *parent_node,
- ETreePath *inserted_node)
+e_tree_model_node_changed (ETreeModel *tree_model, ETreePath node)
{
- int row;
g_return_if_fail (tree_model != NULL);
g_return_if_fail (E_IS_TREE_MODEL (tree_model));
-
- row = e_tree_model_row_of_node (tree_model, inserted_node);
- if (row != -1)
- e_table_model_row_inserted (E_TABLE_MODEL (tree_model), row);
gtk_signal_emit (GTK_OBJECT (tree_model),
- e_tree_model_signals [NODE_INSERTED],
- parent_node, inserted_node);
+ e_tree_model_signals [NODE_CHANGED], node);
}
/**
- * e_tree_model_node_removed:
+ * e_tree_model_node_data_changed:
* @tree_model:
- * @parent_node:
- * @removed_node:
+ * @node:
+ *
*
*
+ * Return value:
**/
void
-e_tree_model_node_removed (ETreeModel *tree_model, ETreePath *parent_node, ETreePath *removed_node)
+e_tree_model_node_data_changed (ETreeModel *tree_model, ETreePath node)
{
- int row;
-
g_return_if_fail (tree_model != NULL);
g_return_if_fail (E_IS_TREE_MODEL (tree_model));
-
- row = e_tree_model_row_of_node (tree_model, removed_node);
- if (row != -1)
- e_table_model_row_deleted (E_TABLE_MODEL (tree_model), row);
gtk_signal_emit (GTK_OBJECT (tree_model),
- e_tree_model_signals [NODE_REMOVED],
- parent_node, removed_node);
+ e_tree_model_signals [NODE_DATA_CHANGED], node);
}
/**
- * e_tree_model_node_collapsed:
+ * e_tree_model_node_col_changed:
* @tree_model:
* @node:
*
*
+ *
+ * Return value:
**/
void
-e_tree_model_node_collapsed (ETreeModel *tree_model, ETreePath *node)
+e_tree_model_node_col_changed (ETreeModel *tree_model, ETreePath node, int col)
{
g_return_if_fail (tree_model != NULL);
g_return_if_fail (E_IS_TREE_MODEL (tree_model));
gtk_signal_emit (GTK_OBJECT (tree_model),
- e_tree_model_signals [NODE_COLLAPSED],
- node);
+ e_tree_model_signals [NODE_COL_CHANGED], node, col);
}
/**
- * e_tree_model_node_expanded:
+ * e_tree_model_node_inserted:
* @tree_model:
- * @node:
- * @allow_expand:
+ * @parent_node:
+ * @inserted_node:
*
*
**/
void
-e_tree_model_node_expanded (ETreeModel *tree_model, ETreePath *node, gboolean *allow_expand)
+e_tree_model_node_inserted (ETreeModel *tree_model,
+ ETreePath parent_node,
+ ETreePath inserted_node)
{
g_return_if_fail (tree_model != NULL);
g_return_if_fail (E_IS_TREE_MODEL (tree_model));
-
+
gtk_signal_emit (GTK_OBJECT (tree_model),
- e_tree_model_signals [NODE_EXPANDED],
- node, allow_expand);
+ e_tree_model_signals [NODE_INSERTED],
+ parent_node, inserted_node);
}
-
-
/**
- * e_tree_model_construct:
- * @etree:
+ * e_tree_model_node_removed:
+ * @tree_model:
+ * @parent_node:
+ * @removed_node:
*
*
**/
void
-e_tree_model_construct (ETreeModel *etree)
+e_tree_model_node_removed (ETreeModel *tree_model, ETreePath parent_node, ETreePath removed_node)
{
- ETreeModelPriv *priv;
-
- g_return_if_fail (etree != NULL);
- g_return_if_fail (E_IS_TREE_MODEL (etree));
-
- priv = g_new0 (ETreeModelPriv, 1);
- etree->priv = priv;
-
- priv->node_chunk = g_mem_chunk_create (ETreePath, TREEPATH_CHUNK_AREA_SIZE, G_ALLOC_AND_FREE);
- priv->root = NULL;
- priv->root_visible = TRUE;
- priv->row_array = g_array_new (FALSE, FALSE, sizeof(ETreePath*));
- priv->expanded_state = g_hash_table_new (g_str_hash, g_str_equal);
- priv->sort_group = g_string_new("");
- priv->frozen = 0;
+ g_return_if_fail (tree_model != NULL);
+ g_return_if_fail (E_IS_TREE_MODEL (tree_model));
+
+ gtk_signal_emit (GTK_OBJECT (tree_model),
+ e_tree_model_signals [NODE_REMOVED],
+ parent_node, removed_node);
}
+
+
/**
* e_tree_model_new
*
@@ -844,124 +294,35 @@ e_tree_model_new ()
*
* return values: the ETreePath corresponding to the root node.
*/
-ETreePath *
+ETreePath
e_tree_model_get_root (ETreeModel *etree)
{
g_return_val_if_fail (etree != NULL, NULL);
g_return_val_if_fail (E_IS_TREE_MODEL (etree), NULL);
- return ETM_CLASS(etree)->get_root(etree);
-}
-
-/**
- * e_tree_model_node_at_row
- * @etree: the ETreeModel.
- * @row:
- *
- * XXX docs here.
- *
- * return values: the ETreePath corresponding to @row.
- */
-ETreePath *
-e_tree_model_node_at_row (ETreeModel *etree, int row)
-{
- g_return_val_if_fail (etree != NULL, NULL);
- g_return_val_if_fail (E_IS_TREE_MODEL (etree), NULL);
-
- return ETM_CLASS(etree)->node_at_row (etree, row);
-}
-
-/**
- * e_tree_model_icon_of_node
- * @etree: The ETreeModel.
- * @path: The ETreePath to the node we're getting the icon of.
- *
- * XXX docs here.
- *
- * return values: the GdkPixbuf associated with this node.
- */
-GdkPixbuf *
-e_tree_model_icon_of_node (ETreeModel *etree, ETreePath *path)
-{
- g_return_val_if_fail (etree != NULL, NULL);
- g_return_val_if_fail (E_IS_TREE_MODEL (etree), NULL);
-
- return ETM_CLASS(etree)->icon_at (etree, path);
-}
-
-/**
- * e_tree_model_row_of_node
- * @etree: The ETreeModel.
- * @node: The node whose row we're looking up.
- *
- * return values: an int.
- */
-int
-e_tree_model_row_of_node (ETreeModel *etree, ETreePath *node)
-{
- ETreeModelPriv *priv;
- int i;
-
- g_return_val_if_fail (etree != NULL, 0);
- g_return_val_if_fail (E_IS_TREE_MODEL (etree), 0);
-
- priv = etree->priv;
- for (i = 0; i < priv->row_array->len; i ++)
- if (g_array_index (priv->row_array, ETreePath*, i) == node)
- return i;
-
- return -1;
-}
-
-/**
- * e_tree_model_root_node_set_visible
- *
- * return values: none
- */
-void
-e_tree_model_root_node_set_visible (ETreeModel *etm, gboolean visible)
-{
- ETreeModelPriv *priv;
-
- g_return_if_fail (etm != NULL);
- g_return_if_fail (E_IS_TREE_MODEL (etm));
-
- priv = etm->priv;
- if (visible != priv->root_visible) {
- priv->root_visible = visible;
- if (priv->root) {
- if (visible) {
- priv->row_array = g_array_insert_val (priv->row_array, 0, priv->root);
- }
- else {
- ETreePath *root_path = e_tree_model_get_root (etm);
- e_tree_model_node_set_expanded (etm, root_path, TRUE);
- priv->row_array = g_array_remove_index (priv->row_array, 0);
- }
-
- e_table_model_changed (E_TABLE_MODEL (etm));
- }
- }
+ if (ETM_CLASS(etree)->get_root)
+ return ETM_CLASS(etree)->get_root(etree);
+ else
+ return NULL;
}
/**
- * e_tree_model_root_node_is_visible:
- * @etm:
+ * e_tree_model_node_get_parent:
+ * @etree:
+ * @path:
*
*
*
* Return value:
**/
-gboolean
-e_tree_model_root_node_is_visible (ETreeModel *etm)
+ETreePath
+e_tree_model_node_get_parent (ETreeModel *etree, ETreePath node)
{
- ETreeModelPriv *priv;
-
- g_return_val_if_fail (etm != NULL, FALSE);
- g_return_val_if_fail (E_IS_TREE_MODEL (etm), FALSE);
-
- priv = etm->priv;
- return priv->root_visible;
+ g_return_val_if_fail(etree != NULL, NULL);
+ if (ETM_CLASS(etree)->get_parent)
+ return ETM_CLASS(etree)->get_parent(etree, node);
+ else
+ return NULL;
}
/**
@@ -973,13 +334,16 @@ e_tree_model_root_node_is_visible (ETreeModel *etm)
*
* Return value:
**/
-ETreePath *
-e_tree_model_node_get_first_child (ETreeModel *etree, ETreePath *node)
+ETreePath
+e_tree_model_node_get_first_child (ETreeModel *etree, ETreePath node)
{
g_return_val_if_fail (etree != NULL, NULL);
g_return_val_if_fail (E_IS_TREE_MODEL (etree), NULL);
- return ETM_CLASS(etree)->get_first_child(etree, node);
+ if (ETM_CLASS(etree)->get_first_child)
+ return ETM_CLASS(etree)->get_first_child(etree, node);
+ else
+ return NULL;
}
/**
@@ -991,13 +355,16 @@ e_tree_model_node_get_first_child (ETreeModel *etree, ETreePath *node)
*
* Return value:
**/
-ETreePath *
-e_tree_model_node_get_last_child (ETreeModel *etree, ETreePath *node)
+ETreePath
+e_tree_model_node_get_last_child (ETreeModel *etree, ETreePath node)
{
g_return_val_if_fail (etree != NULL, NULL);
g_return_val_if_fail (E_IS_TREE_MODEL (etree), NULL);
- return ETM_CLASS(etree)->get_last_child(etree, node);
+ if (ETM_CLASS(etree)->get_last_child)
+ return ETM_CLASS(etree)->get_last_child(etree, node);
+ else
+ return NULL;
}
@@ -1010,13 +377,16 @@ e_tree_model_node_get_last_child (ETreeModel *etree, ETreePath *node)
*
* Return value:
**/
-ETreePath *
-e_tree_model_node_get_next (ETreeModel *etree, ETreePath *node)
+ETreePath
+e_tree_model_node_get_next (ETreeModel *etree, ETreePath node)
{
g_return_val_if_fail (etree != NULL, NULL);
g_return_val_if_fail (E_IS_TREE_MODEL (etree), NULL);
- return ETM_CLASS(etree)->get_next(etree, node);
+ if (ETM_CLASS(etree)->get_next)
+ return ETM_CLASS(etree)->get_next(etree, node);
+ else
+ return NULL;
}
/**
@@ -1028,47 +398,16 @@ e_tree_model_node_get_next (ETreeModel *etree, ETreePath *node)
*
* Return value:
**/
-ETreePath *
-e_tree_model_node_get_prev (ETreeModel *etree, ETreePath *node)
+ETreePath
+e_tree_model_node_get_prev (ETreeModel *etree, ETreePath node)
{
g_return_val_if_fail (etree != NULL, NULL);
g_return_val_if_fail (E_IS_TREE_MODEL (etree), NULL);
- return ETM_CLASS(etree)->get_prev(etree, node);
-}
-
-/**
- * e_tree_model_node_depth:
- * @etree:
- * @path:
- *
- *
- *
- * Return value:
- **/
-guint
-e_tree_model_node_depth (ETreeModel *etree, ETreePath *path)
-{
- g_return_val_if_fail (etree != NULL, 0);
- g_return_val_if_fail (E_IS_TREE_MODEL (etree), 0);
-
- return e_tree_path_depth (path) - 1;
-}
-
-/**
- * e_tree_model_node_get_parent:
- * @etree:
- * @path:
- *
- *
- *
- * Return value:
- **/
-ETreePath *
-e_tree_model_node_get_parent (ETreeModel *etree, ETreePath *path)
-{
- g_return_val_if_fail(etree != NULL, NULL);
- return ETM_CLASS(etree)->get_parent(etree, path);
+ if (ETM_CLASS(etree)->get_prev)
+ return ETM_CLASS(etree)->get_prev(etree, node);
+ else
+ return NULL;
}
/**
@@ -1081,10 +420,14 @@ e_tree_model_node_get_parent (ETreeModel *etree, ETreePath *path)
* Return value:
**/
gboolean
-e_tree_model_node_is_root (ETreeModel *etree, ETreePath *path)
+e_tree_model_node_is_root (ETreeModel *etree, ETreePath node)
{
g_return_val_if_fail(etree != NULL, FALSE);
- return (e_tree_model_node_depth (etree, path) == 0);
+
+ if (ETM_CLASS(etree)->is_root)
+ return ETM_CLASS(etree)->is_root(etree, node);
+ else
+ return FALSE;
}
/**
@@ -1097,30 +440,28 @@ e_tree_model_node_is_root (ETreeModel *etree, ETreePath *path)
* Return value:
**/
gboolean
-e_tree_model_node_is_expandable (ETreeModel *etree, ETreePath *path)
+e_tree_model_node_is_expandable (ETreeModel *etree, ETreePath node)
{
g_return_val_if_fail(etree != NULL, FALSE);
- return (e_tree_model_node_get_children (etree, path, NULL) > 0);
+
+ if (ETM_CLASS(etree)->is_expandable)
+ return ETM_CLASS(etree)->is_expandable(etree, node);
+ else
+ return FALSE;
}
-/**
- * e_tree_model_node_is_expanded:
- * @etree:
- * @path:
- *
- *
- *
- * Return value:
- **/
-gboolean
-e_tree_model_node_is_expanded (ETreeModel *etree, ETreePath *path)
+guint
+e_tree_model_node_get_children (ETreeModel *etree, ETreePath node, ETreePath **nodes)
{
- g_return_val_if_fail(etree != NULL, FALSE);
- return ETM_CLASS(etree)->is_expanded (etree, path);
+ g_return_val_if_fail(etree != NULL, 0);
+ if (ETM_CLASS(etree)->get_children)
+ return ETM_CLASS(etree)->get_children (etree, node, nodes);
+ else
+ return 0;
}
/**
- * e_tree_model_node_is_visible:
+ * e_tree_model_node_depth:
* @etree:
* @path:
*
@@ -1128,290 +469,182 @@ e_tree_model_node_is_expanded (ETreeModel *etree, ETreePath *path)
*
* Return value:
**/
-gboolean
-e_tree_model_node_is_visible (ETreeModel *etree, ETreePath *path)
+guint
+e_tree_model_node_depth (ETreeModel *etree, ETreePath node)
{
- g_return_val_if_fail(etree != NULL, FALSE);
- return ETM_CLASS(etree)->is_visible (etree, path);
+ g_return_val_if_fail (etree != NULL, 0);
+ g_return_val_if_fail (E_IS_TREE_MODEL (etree), 0);
+
+ if (ETM_CLASS(etree)->depth)
+ return ETM_CLASS(etree)->depth(etree, node);
+ else
+ return 0;
}
/**
- * e_tree_model_node_set_expanded:
- * @etree:
- * @path:
- * @expanded:
- *
- *
- **/
-void
-e_tree_model_node_set_expanded (ETreeModel *etree, ETreePath *path, gboolean expanded)
+ * e_tree_model_icon_at
+ * @etree: The ETreeModel.
+ * @path: The ETreePath to the node we're getting the icon of.
+ *
+ * XXX docs here.
+ *
+ * return values: the GdkPixbuf associated with this node.
+ */
+GdkPixbuf *
+e_tree_model_icon_at (ETreeModel *etree, ETreePath node)
{
- g_return_if_fail(etree != NULL);
- ETM_CLASS(etree)->set_expanded (etree, path, expanded);
+ g_return_val_if_fail (etree != NULL, NULL);
+ g_return_val_if_fail (E_IS_TREE_MODEL (etree), NULL);
+
+ if (ETM_CLASS(etree)->icon_at)
+ return ETM_CLASS(etree)->icon_at (etree, node);
+ else
+ return NULL;
}
/**
- * e_tree_model_node_set_expanded_recurse:
- * @etree:
- * @path:
- * @expanded:
- *
- *
- **/
-void
-e_tree_model_node_set_expanded_recurse (ETreeModel *etree, ETreePath *path, gboolean expanded)
+ * e_tree_model_get_expanded_default
+ * @etree: The ETreeModel.
+ *
+ * XXX docs here.
+ *
+ * return values: Whether nodes should be expanded by default.
+ */
+gboolean
+e_tree_model_get_expanded_default (ETreeModel *etree)
{
- g_return_if_fail(etree != NULL);
- ETM_CLASS(etree)->set_expanded_recurse (etree, path, expanded);
-}
+ g_return_val_if_fail (etree != NULL, FALSE);
+ g_return_val_if_fail (E_IS_TREE_MODEL (etree), FALSE);
-guint
-e_tree_model_node_get_children (ETreeModel *etree, ETreePath *path, ETreePath ***paths)
-{
- g_return_val_if_fail(etree != NULL, 0);
- return ETM_CLASS(etree)->get_children (etree, path, paths);
+ if (ETM_CLASS(etree)->get_expanded_default)
+ return ETM_CLASS(etree)->get_expanded_default (etree);
+ else
+ return FALSE;
}
/**
- * e_tree_model_node_num_visible_descendents:
- * @etm:
- * @node:
- *
- *
- *
- * Return value:
- **/
-guint
-e_tree_model_node_num_visible_descendents (ETreeModel *etm, ETreePath *node)
+ * e_tree_model_column_count
+ * @etree: The ETreeModel.
+ *
+ * XXX docs here.
+ *
+ * return values: The number of columns
+ */
+gint
+e_tree_model_column_count (ETreeModel *etree)
{
- g_return_val_if_fail(node != NULL, 0);
- return node->visible_descendents;
+ g_return_val_if_fail (etree != NULL, 0);
+ g_return_val_if_fail (E_IS_TREE_MODEL (etree), 0);
+
+ if (ETM_CLASS(etree)->column_count)
+ return ETM_CLASS(etree)->column_count (etree);
+ else
+ return 0;
}
/**
- * e_tree_model_node_get_data:
- * @etm:
- * @node:
- *
- *
- *
- * Return value:
- **/
-gpointer
-e_tree_model_node_get_data (ETreeModel *etm, ETreePath *node)
+ * e_tree_model_has_save_id
+ * @etree: The ETreeModel.
+ *
+ * XXX docs here.
+ *
+ * return values: Whether this tree has valid save id data.
+ */
+gboolean
+e_tree_model_has_save_id (ETreeModel *etree)
{
- g_return_val_if_fail (node, NULL);
+ g_return_val_if_fail (etree != NULL, FALSE);
+ g_return_val_if_fail (E_IS_TREE_MODEL (etree), FALSE);
- return node->node_data;
+ if (ETM_CLASS(etree)->has_save_id)
+ return ETM_CLASS(etree)->has_save_id (etree);
+ else
+ return FALSE;
}
/**
- * e_tree_model_node_set_data:
- * @etm:
- * @node:
- * @node_data:
- *
- *
- **/
-void
-e_tree_model_node_set_data (ETreeModel *etm, ETreePath *node, gpointer node_data)
+ * e_tree_model_get_save_id
+ * @etree: The ETreeModel.
+ * @node: The ETreePath.
+ *
+ * XXX docs here.
+ *
+ * return values: The save id for this path.
+ */
+gchar *
+e_tree_model_get_save_id (ETreeModel *etree, ETreePath node)
{
- g_return_if_fail (node);
+ g_return_val_if_fail (etree != NULL, NULL);
+ g_return_val_if_fail (E_IS_TREE_MODEL (etree), NULL);
- node->node_data = node_data;
+ if (ETM_CLASS(etree)->get_save_id)
+ return ETM_CLASS(etree)->get_save_id (etree, node);
+ else
+ return NULL;
}
/**
- * e_tree_model_node_insert:
- * @tree_model:
- * @parent_path:
- * @position:
- * @node_data:
- *
- *
- *
- * Return value:
- **/
-ETreePath*
-e_tree_model_node_insert (ETreeModel *tree_model,
- ETreePath *parent_path,
- int position,
- gpointer node_data)
+ * e_tree_model_icon_of_node
+ * @etree: The ETreeModel.
+ * @path: The ETreePath to the node we're getting the icon of.
+ *
+ * XXX docs here.
+ *
+ * return values: the GdkPixbuf associated with this node.
+ */
+void *
+e_tree_model_value_at (ETreeModel *etree, ETreePath node, int col)
{
- ETreeModelPriv *priv;
- ETreePath *new_path;
-
- g_return_val_if_fail(tree_model != NULL, NULL);
-
- priv = tree_model->priv;
-
- g_return_val_if_fail (parent_path != NULL || priv->root == NULL, NULL);
-
- priv = tree_model->priv;
-
- new_path = g_chunk_new0 (ETreePath, priv->node_chunk);
-
- new_path->expanded = FALSE;
- new_path->node_data = node_data;
-
- if (parent_path != NULL) {
-
- if (parent_path->first_child == NULL
- && !parent_path->expanded_set) {
- e_tree_model_node_set_expanded (tree_model,
- parent_path,
- priv->expanded_default);
- }
-
- e_tree_path_insert (parent_path, position, new_path);
-
- if (e_tree_model_node_is_visible (tree_model, new_path)) {
- int parent_row, child_offset = 0;
- ETreePath *n;
-
- /* we need to iterate back up to the root, incrementing the number of visible
- descendents */
- for (n = parent_path; n; n = n->parent) {
- n->visible_descendents ++;
- }
-
- /* determine if we are inserting at the end of this parent */
- if (position == -1 || position == parent_path->num_children) {
- position = e_tree_model_node_num_visible_descendents (tree_model, parent_path) - 1;
- } else {
- /* if we're not inserting at the end of the array, position is the child node we're
- inserting at, not the absolute row position - need to count expanded nodes before it too */
- int i = position;
-
- n = e_tree_model_node_get_first_child(tree_model, parent_path);
- while (n != NULL && i > 0) {
- child_offset += n->visible_descendents;
- n = n->next_sibling;
- i--;
- }
- }
-
-
- /* if the parent is the root, no need to search for its position since it aint there */
- if (parent_path->parent == NULL) {
- parent_row = -1;
- } else {
- parent_row = e_tree_model_row_of_node (tree_model, parent_path);
- }
-
- e_table_model_pre_change(E_TABLE_MODEL(tree_model));
-
- priv->row_array = g_array_insert_val (priv->row_array,
- parent_row + position + 1 + child_offset, new_path);
-
- /* only do this if we know a changed signal isn't coming later on */
- if (priv->frozen == 0)
- e_table_model_row_inserted (E_TABLE_MODEL(tree_model), parent_row + position + 1 + child_offset);
- }
-
- if (parent_path->compare)
- e_tree_model_node_sort (tree_model, parent_path);
- }
- else {
- priv->root = new_path;
- if (priv->root_visible) {
- priv->row_array = g_array_insert_val (priv->row_array, 0, priv->root);
- e_table_model_row_inserted (E_TABLE_MODEL (tree_model), 0);
- }
- else {
- /* need to mark the new node as expanded or
- we'll never see it's children */
- new_path->expanded = TRUE;
- new_path->expanded_set = TRUE;
- }
- }
-
- return new_path;
+ g_return_val_if_fail (etree != NULL, NULL);
+ g_return_val_if_fail (E_IS_TREE_MODEL (etree), NULL);
+
+ if (ETM_CLASS(etree)->value_at)
+ return ETM_CLASS(etree)->value_at (etree, node, col);
+ else
+ return NULL;
}
/**
- * e_tree_model_node_insert_id:
- * @tree_model:
- * @parent_path:
- * @position:
- * @node_data:
- * @save_id:
- *
- *
- *
- * Return value:
- **/
-ETreePath*
-e_tree_model_node_insert_id (ETreeModel *tree_model,
- ETreePath *parent_path,
- int position,
- gpointer node_data,
- const char *save_id)
+ * e_tree_model_icon_of_node
+ * @etree: The ETreeModel.
+ * @path: The ETreePath to the node we're getting the icon of.
+ *
+ * XXX docs here.
+ *
+ * return values: the GdkPixbuf associated with this node.
+ */
+void
+e_tree_model_set_value_at (ETreeModel *etree, ETreePath node, int col, const void *val)
{
- ETreePath *path;
-
- g_return_val_if_fail(tree_model != NULL, NULL);
-
- path = e_tree_model_node_insert (tree_model, parent_path, position, node_data);
-
- e_tree_model_node_set_save_id (tree_model, path, save_id);
+ g_return_if_fail (etree != NULL);
+ g_return_if_fail (E_IS_TREE_MODEL (etree));
- return path;
+ if (ETM_CLASS(etree)->set_value_at)
+ ETM_CLASS(etree)->set_value_at (etree, node, col, val);
}
/**
- * e_tree_model_node_insert_before:
+ * e_tree_model_node_is_editable:
* @etree:
- * @parent:
- * @sibling:
- * @node_data:
+ * @path:
*
*
*
* Return value:
**/
-ETreePath *
-e_tree_model_node_insert_before (ETreeModel *etree,
- ETreePath *parent,
- ETreePath *sibling,
- gpointer node_data)
+gboolean
+e_tree_model_node_is_editable (ETreeModel *etree, ETreePath node, int col)
{
- ETreePath *child;
- int position = 0;
-
- g_return_val_if_fail(etree != NULL, NULL);
-
- for (child = parent->first_child; child; child = child->next_sibling) {
- if (child == sibling)
- break;
- position ++;
- }
- return e_tree_model_node_insert (etree, parent, position, node_data);
-}
+ g_return_val_if_fail(etree != NULL, FALSE);
-/* just blows away child data, doesn't take into account unlinking/etc */
-static void
-child_free(ETreeModel *etree, ETreePath *node)
-{
- ETreePath *child, *next;
- ETreeModelPriv *priv = etree->priv;
-
- child = node->first_child;
- while (child) {
- next = child->next_sibling;
- child_free(etree, child);
- child = next;
- }
-
- if (node->save_id) {
- g_hash_table_remove(priv->expanded_state, node->save_id);
- g_free(node->save_id);
- }
- g_chunk_free(node, priv->node_chunk);
+ if (ETM_CLASS(etree)->is_editable)
+ return ETM_CLASS(etree)->is_editable(etree, node, col);
+ else
+ return FALSE;
}
/**
- * e_tree_model_node_remove:
+ * e_tree_model_duplicate_value:
* @etree:
* @path:
*
@@ -1419,393 +652,91 @@ child_free(ETreeModel *etree, ETreePath *node)
*
* Return value:
**/
-gpointer
-e_tree_model_node_remove (ETreeModel *etree, ETreePath *path)
+void *
+e_tree_model_duplicate_value (ETreeModel *etree, int col, const void *value)
{
- ETreeModelPriv *priv = etree->priv;
- ETreePath *parent = path->parent;
- gpointer ret = path->node_data;
- int row, visible = 0, base = 0;
- gboolean dochanged;
-
g_return_val_if_fail(etree != NULL, NULL);
- /* work out what range of visible rows to remove */
- if (parent) {
- if (e_tree_model_node_is_visible(etree, path)) {
- base = e_tree_model_row_of_node(etree, path);
- visible = path->visible_descendents + 1;
- }
- } else if (path == priv->root) {
- priv->root = NULL;
- if (priv->root_visible) {
- base = 0;
- visible = path->visible_descendents + 1;
- } else {
- base = 0;
- visible = path->visible_descendents;
- }
- } else {
- g_warning("Trying to remove invalid path %p", path);
+ if (ETM_CLASS(etree)->duplicate_value)
+ return ETM_CLASS(etree)->duplicate_value(etree, col, value);
+ else
return NULL;
- }
-
- /* unlink this node - we only have to unlink the root node being removed,
- since the others are only references from this node */
- e_tree_path_unlink (path);
-
- /*printf("removing %d nodes from position %d\n", visible, base);*/
-
- if (visible > 0) {
- /* fix up the parent visible counts */
- for (; parent; parent = parent->parent) {
- parent->visible_descendents -= visible;
- }
-
- /* if we have a lot of nodes to remove, then we dont row_deleted each one */
- /* could probably be tuned, but this'll do, since its normally only when we
- remove the whole lot do we really care */
- dochanged = (visible > 1000) || (visible > (priv->row_array->len / 4));
-
- e_table_model_pre_change(E_TABLE_MODEL (etree));
-
- /* and physically remove them */
- if (visible == priv->row_array->len) {
- g_array_set_size(priv->row_array, 0);
- } else {
- memmove(&g_array_index(priv->row_array, ETreePath *, base),
- &g_array_index(priv->row_array, ETreePath *, base+visible),
- (priv->row_array->len - (base+visible)) * sizeof(ETreePath *));
- g_array_set_size(priv->row_array, priv->row_array->len - visible);
- }
-
- /* tell the system we've removed (these) nodes */
- if (priv->frozen == 0) {
- if (dochanged) {
- e_table_model_changed(E_TABLE_MODEL(etree));
- } else {
- for (row=visible-1;row>=0;row--) {
- e_table_model_row_deleted(E_TABLE_MODEL(etree), row+base);
- }
- }
- }
- }
-
- child_free(etree, path);
-
- return ret;
-}
-
-static void
-add_visible_descendents_to_array (ETreeModel *etm, ETreePath *node, int *row, int *count)
-{
- ETreeModelPriv *priv = etm->priv;
-
- /* add a row for this node */
- e_table_model_pre_change(E_TABLE_MODEL (etm));
- priv->row_array = g_array_insert_val (priv->row_array, (*row), node);
- if (priv->frozen == 0)
- e_table_model_row_inserted (E_TABLE_MODEL (etm), (*row));
- (*row) ++;
- (*count) ++;
-
- /* then loop over its children, calling this routine for each
- of them */
- if (node->expanded) {
- ETreePath *child;
- for (child = node->first_child; child;
- child = child->next_sibling) {
- add_visible_descendents_to_array (etm, child, row, count);
- }
- }
-}
-
-static void
-save_expanded_state_func (char *key, gboolean expanded, gpointer user_data)
-{
- if (expanded) {
- xmlNode *root = (xmlNode*)user_data;
- xmlNode *node_root = xmlNewNode (NULL, (xmlChar*) "node");
-
- xmlAddChild (root, node_root);
- xmlNewChild (node_root, NULL, (xmlChar *) "id", (xmlChar*) key);
- }
}
/**
- * e_tree_model_save_expanded_state:
- * @etm:
- * @filename:
+ * e_tree_model_free_value:
+ * @etree:
+ * @path:
*
*
*
* Return value:
**/
-gboolean
-e_tree_model_save_expanded_state (ETreeModel *etm, const char *filename)
-{
- xmlDoc *doc;
- xmlNode *root;
- int fd, rv;
- xmlChar *buf;
- int buf_size;
- ETreeModelPriv *priv = etm->priv;
-
- g_return_val_if_fail(etm != NULL, FALSE);
-
- doc = xmlNewDoc ((xmlChar*) "1.0");
- root = xmlNewDocNode (doc, NULL,
- (xmlChar *) "expanded_state",
- NULL);
- xmlDocSetRootElement (doc, root);
-
- g_hash_table_foreach (priv->expanded_state,
- (GHFunc)save_expanded_state_func,
- root);
-
- fd = open (filename, O_CREAT | O_TRUNC | O_WRONLY, 0777);
-
- xmlDocDumpMemory (doc, &buf, &buf_size);
-
- if (buf == NULL) {
- g_error ("Failed to write %s: xmlBufferCreate() == NULL", filename);
- return FALSE;
- }
-
- rv = write (fd, buf, buf_size);
- xmlFree (buf);
- close (fd);
-
- if (0 > rv) {
- g_error ("Failed to write new %s: %d\n", filename, errno);
- unlink (filename);
- return FALSE;
- }
-
- return TRUE;
-}
-
-static char *
-get_string_value (xmlNode *node,
- const char *name)
+void
+e_tree_model_free_value (ETreeModel *etree, int col, void *value)
{
- xmlNode *p;
- xmlChar *xml_string;
- char *retval;
-
- p = e_xml_get_child_by_name (node, (xmlChar *) name);
- if (p == NULL)
- return NULL;
-
- p = e_xml_get_child_by_name (p, (xmlChar *) "text");
- if (p == NULL) /* there's no text between the tags, return the empty string */
- return g_strdup("");
-
- xml_string = xmlNodeListGetString (node->doc, p, 1);
- retval = g_strdup ((char *) xml_string);
- xmlFree (xml_string);
+ g_return_if_fail(etree != NULL);
- return retval;
+ if (ETM_CLASS(etree)->free_value)
+ ETM_CLASS(etree)->free_value(etree, col, value);
}
/**
- * e_tree_model_load_expanded_state:
- * @etm:
- * @filename:
+ * e_tree_model_initialize_value:
+ * @etree:
+ * @path:
*
*
*
* Return value:
**/
-gboolean
-e_tree_model_load_expanded_state (ETreeModel *etm, const char *filename)
+void *
+e_tree_model_initialize_value (ETreeModel *etree, int col)
{
- ETreeModelPriv *priv = etm->priv;
- xmlDoc *doc;
- xmlNode *root;
- xmlNode *child;
-
- g_return_val_if_fail(etm != NULL, FALSE);
-
- doc = xmlParseFile (filename);
- if (!doc)
- return FALSE;
-
- root = xmlDocGetRootElement (doc);
- if (root == NULL || strcmp (root->name, "expanded_state")) {
- xmlFreeDoc (doc);
- return FALSE;
- }
-
- for (child = root->childs; child; child = child->next) {
- char *id;
-
- if (strcmp (child->name, "node")) {
- g_warning ("unknown node '%s' in %s", child->name, filename);
- continue;
- }
-
- id = get_string_value (child, "id");
-
- g_hash_table_insert (priv->expanded_state, id, (gpointer)TRUE);
- }
-
- xmlFreeDoc (doc);
+ g_return_val_if_fail(etree != NULL, NULL);
- return TRUE;
+ if (ETM_CLASS(etree)->initialize_value)
+ return ETM_CLASS(etree)->initialize_value(etree, col);
+ else
+ return NULL;
}
-
/**
- * e_tree_model_node_set_save_id:
- * @etm:
- * @node:
- * @id:
+ * e_tree_model_value_is_empty:
+ * @etree:
+ * @path:
*
*
- **/
-void
-e_tree_model_node_set_save_id (ETreeModel *etm, ETreePath *node, const char *id)
-{
- char *key;
- gboolean expanded_state;
- ETreeModelPriv *priv;
-
- g_return_if_fail(etm != NULL);
- g_return_if_fail (E_TREE_MODEL (etm));
- g_return_if_fail (node);
-
- priv = etm->priv;
-
- if (g_hash_table_lookup_extended (priv->expanded_state,
- id, (gpointer*)&key, (gpointer*)&expanded_state)) {
-
- e_tree_model_node_set_expanded (etm, node,
- expanded_state);
-
- /* important that this comes after the e_tree_model_node_set_expanded */
- node->save_id = key;
- }
- else {
- node->save_id = g_strdup (id);
-
- g_hash_table_insert (priv->expanded_state, node->save_id, (gpointer)node->expanded);
- }
-}
-
-
-
-/**
- * e_tree_model_node_set_compare_function:
- * @tree_model:
- * @node:
- * @compare:
- *
*
+ * Return value:
**/
-void
-e_tree_model_node_set_compare_function (ETreeModel *tree_model,
- ETreePath *node,
- ETreePathCompareFunc compare)
+gboolean
+e_tree_model_value_is_empty (ETreeModel *etree, int col, const void *value)
{
- gboolean need_sort;
-
- g_return_if_fail (tree_model != NULL);
- g_return_if_fail (E_TREE_MODEL (tree_model));
- g_return_if_fail (node);
+ g_return_val_if_fail(etree != NULL, TRUE);
- need_sort = (compare != node->compare);
-
- node->compare = compare;
-
- if (need_sort)
- e_tree_model_node_sort (tree_model, node);
-}
-
-typedef struct {
- ETreeModel *model;
- ETreePath *path;
- ETreePathCompareFunc compare;
- gboolean was_expanded;
-} ETreeSortInfo;
-
-static gint
-e_tree_model_node_compare (ETreeSortInfo *info1, ETreeSortInfo *info2)
-{
- return info1->compare (info1->model, info1->path, info2->path);
+ if (ETM_CLASS(etree)->value_is_empty)
+ return ETM_CLASS(etree)->value_is_empty(etree, col, value);
+ else
+ return TRUE;
}
/**
- * e_tree_model_node_sort:
- * @tree_model:
- * @node:
+ * e_tree_model_value_to_string:
+ * @etree:
+ * @path:
+ *
*
*
+ * Return value:
**/
-void
-e_tree_model_node_sort (ETreeModel *tree_model,
- ETreePath *node)
+char *
+e_tree_model_value_to_string (ETreeModel *etree, int col, const void *value)
{
- int num_nodes = node->num_children;
- ETreeSortInfo *sort_info;
- int i;
- int child_index;
- gboolean node_expanded;
- ETreeModelPriv *priv = tree_model->priv;;
-
- node_expanded = e_tree_model_node_is_expanded (tree_model, node);
-
- g_return_if_fail (tree_model != NULL);
- g_return_if_fail (E_TREE_MODEL (tree_model));
- g_return_if_fail (node);
-
- g_return_if_fail (node->compare);
-
- if (num_nodes == 0)
- return;
+ g_return_val_if_fail(etree != NULL, g_strdup(""));
- e_table_model_pre_change(E_TABLE_MODEL (tree_model));
-
- sort_info = g_new (ETreeSortInfo, num_nodes);
-
- child_index = e_tree_model_row_of_node (tree_model, node) + 1;
-
- /* collect our info and remove the children */
- for (i = 0; i < num_nodes; i ++) {
- sort_info[i].path = node->first_child;
- sort_info[i].was_expanded = e_tree_model_node_is_expanded (tree_model, sort_info[i].path);
- sort_info[i].model = tree_model;
- sort_info[i].compare = node->compare;
-
- e_tree_model_node_set_expanded(tree_model, sort_info[i].path, FALSE);
- if (node_expanded)
- priv->row_array = g_array_remove_index (priv->row_array, child_index);
- e_tree_path_unlink (sort_info[i].path);
- }
-
- /* sort things */
- qsort (sort_info, num_nodes, sizeof(ETreeSortInfo), (GCompareFunc)e_tree_model_node_compare);
-
- /* reinsert the children nodes into the tree in the sorted order */
- for (i = 0; i < num_nodes; i ++) {
- e_tree_path_insert (node, i, sort_info[i].path);
- if (node_expanded)
- priv->row_array = g_array_insert_val (priv->row_array, child_index + i,
- sort_info[i].path);
- }
-
- /* make another pass expanding the children as needed.
-
- XXX this used to be in the loop above, but a recent change
- (either here or in the etable code) causes an assertion and
- a crash */
- for (i = 0; i < num_nodes; i ++) {
- e_tree_model_node_set_expanded (tree_model, sort_info[i].path, sort_info[i].was_expanded);
- }
-
- g_free (sort_info);
-
- if (priv->frozen == 0)
- e_table_model_changed (E_TABLE_MODEL (tree_model));
+ if (ETM_CLASS(etree)->value_to_string)
+ return ETM_CLASS(etree)->value_to_string(etree, col, value);
+ else
+ return g_strdup("");
}
-
diff --git a/widgets/table/e-tree-model.h b/widgets/table/e-tree-model.h
index 50bd8064a5..21d3b9b844 100644
--- a/widgets/table/e-tree-model.h
+++ b/widgets/table/e-tree-model.h
@@ -3,7 +3,6 @@
#define _E_TREE_MODEL_H_
#include <gdk-pixbuf/gdk-pixbuf.h>
-#include <gal/e-table/e-table-model.h>
#ifdef __cplusplus
extern "C" {
@@ -16,127 +15,150 @@ extern "C" {
#define E_IS_TREE_MODEL(o) (GTK_CHECK_TYPE ((o), E_TREE_MODEL_TYPE))
#define E_IS_TREE_MODEL_CLASS(k) (GTK_CHECK_CLASS_TYPE ((k), E_TREE_MODEL_TYPE))
-typedef struct ETreePath ETreePath;
+typedef void * ETreePath;
typedef struct ETreeModel ETreeModel;
-typedef struct ETreeModelPriv ETreeModelPriv;
typedef struct ETreeModelClass ETreeModelClass;
-typedef gint (*ETreePathCompareFunc)(ETreeModel *model, ETreePath *path1, ETreePath *path2);
-typedef gboolean (*ETreePathFunc)(ETreeModel *model, ETreePath *path, gpointer data);
+typedef gint (*ETreePathCompareFunc)(ETreeModel *model, ETreePath path1, ETreePath path2);
+typedef gboolean (*ETreePathFunc)(ETreeModel *model, ETreePath path, gpointer data);
struct ETreeModel {
- ETableModel base;
- ETreeModelPriv *priv;
+ GtkObject base;
};
struct ETreeModelClass {
- ETableModelClass parent_class;
+ GtkObjectClass parent_class;
/*
* Virtual methods
*/
- ETreePath *(*get_root) (ETreeModel *etm);
+ ETreePath (*get_root) (ETreeModel *etm);
- ETreePath *(*get_parent) (ETreeModel *etm, ETreePath* node);
- ETreePath *(*get_first_child) (ETreeModel *etm, ETreePath* node);
- ETreePath *(*get_last_child) (ETreeModel *etm, ETreePath* node);
- ETreePath *(*get_next) (ETreeModel *etm, ETreePath* node);
- ETreePath *(*get_prev) (ETreeModel *etm, ETreePath* node);
- guint (*get_children) (ETreeModel *etm, ETreePath* node, ETreePath ***paths);
+ ETreePath (*get_parent) (ETreeModel *etm, ETreePath node);
+ ETreePath (*get_first_child) (ETreeModel *etm, ETreePath node);
+ ETreePath (*get_last_child) (ETreeModel *etm, ETreePath node);
+ ETreePath (*get_next) (ETreeModel *etm, ETreePath node);
+ ETreePath (*get_prev) (ETreeModel *etm, ETreePath node);
- gboolean (*is_expanded) (ETreeModel *etm, ETreePath* node);
- gboolean (*is_visible) (ETreeModel *etm, ETreePath* node);
- void (*set_expanded) (ETreeModel *etm, ETreePath* node, gboolean expanded);
- void (*set_expanded_recurse) (ETreeModel *etm, ETreePath *node, gboolean expanded);
- void (*set_expanded_level) (ETreeModel *etm, ETreePath *node, gboolean expanded, int level);
+ gboolean (*is_root) (ETreeModel *etm, ETreePath node);
+ gboolean (*is_expandable) (ETreeModel *etm, ETreePath node);
+ guint (*get_children) (ETreeModel *etm, ETreePath node, ETreePath **paths);
+ guint (*depth) (ETreeModel *etm, ETreePath node);
- GdkPixbuf *(*icon_at) (ETreeModel *etm, ETreePath* node);
- ETreePath* (*node_at_row) (ETreeModel *etm, int row);
+ GdkPixbuf *(*icon_at) (ETreeModel *etm, ETreePath node);
+
+ gboolean (*get_expanded_default) (ETreeModel *etm);
+ gint (*column_count) (ETreeModel *etm);
+
+ gboolean (*has_save_id) (ETreeModel *etm);
+ gchar *(*get_save_id) (ETreeModel *etm, ETreePath node);
/*
* ETable analogs
*/
- void *(*value_at) (ETreeModel *etm, ETreePath* node, int col);
- void (*set_value_at) (ETreeModel *etm, ETreePath* node, int col, const void *val);
- gboolean (*is_editable) (ETreeModel *etm, ETreePath* node, int col);
+ void *(*value_at) (ETreeModel *etm, ETreePath node, int col);
+ void (*set_value_at) (ETreeModel *etm, ETreePath node, int col, const void *val);
+ gboolean (*is_editable) (ETreeModel *etm, ETreePath node, int col);
+ void *(*duplicate_value) (ETreeModel *etm, int col, const void *value);
+ void (*free_value) (ETreeModel *etm, int col, void *value);
+ void *(*initialize_value) (ETreeModel *etm, int col);
+ gboolean (*value_is_empty) (ETreeModel *etm, int col, const void *value);
+ char *(*value_to_string) (ETreeModel *etm, int col, const void *value);
/*
* Signals
*/
- void (*node_changed) (ETreeModel *etm, ETreePath *node);
- void (*node_inserted) (ETreeModel *etm, ETreePath *parent, ETreePath *inserted_node);
- void (*node_removed) (ETreeModel *etm, ETreePath *parent, ETreePath *removed_node);
- void (*node_collapsed) (ETreeModel *etm, ETreePath *node);
- void (*node_expanded) (ETreeModel *etm, ETreePath *node, gboolean *allow_expand);
-
+ void (*pre_change) (ETreeModel *etm);
+ void (*node_changed) (ETreeModel *etm, ETreePath node);
+ void (*node_data_changed) (ETreeModel *etm, ETreePath node);
+ void (*node_col_changed) (ETreeModel *etm, ETreePath node, int col);
+ void (*node_inserted) (ETreeModel *etm, ETreePath parent, ETreePath inserted_node);
+ void (*node_removed) (ETreeModel *etm, ETreePath parent, ETreePath removed_node);
};
-
-GtkType e_tree_model_get_type (void);
-void e_tree_model_construct (ETreeModel *etree);
-ETreeModel *e_tree_model_new (void);
+GtkType e_tree_model_get_type (void);
+ETreeModel *e_tree_model_new (void);
/* tree traversal operations */
-ETreePath *e_tree_model_get_root (ETreeModel *etree);
-ETreePath *e_tree_model_node_get_parent (ETreeModel *etree, ETreePath *path);
-ETreePath *e_tree_model_node_get_first_child (ETreeModel *etree, ETreePath *path);
-ETreePath *e_tree_model_node_get_last_child (ETreeModel *etree, ETreePath *path);
-ETreePath *e_tree_model_node_get_next (ETreeModel *etree, ETreePath *path);
-ETreePath *e_tree_model_node_get_prev (ETreeModel *etree, ETreePath *path);
-
-/* node operations */
-ETreePath *e_tree_model_node_insert (ETreeModel *etree, ETreePath *parent, int position, gpointer node_data);
-ETreePath *e_tree_model_node_insert_before (ETreeModel *etree, ETreePath *parent, ETreePath *sibling, gpointer node_data);
-gpointer e_tree_model_node_remove (ETreeModel *etree, ETreePath *path);
-
-/* Freeze and thaw */
-void e_tree_model_freeze (ETreeModel *etree);
-void e_tree_model_thaw (ETreeModel *etree);
+ETreePath e_tree_model_get_root (ETreeModel *etree);
+ETreePath e_tree_model_node_get_parent (ETreeModel *etree,
+ ETreePath path);
+ETreePath e_tree_model_node_get_first_child (ETreeModel *etree,
+ ETreePath path);
+ETreePath e_tree_model_node_get_last_child (ETreeModel *etree,
+ ETreePath path);
+ETreePath e_tree_model_node_get_next (ETreeModel *etree,
+ ETreePath path);
+ETreePath e_tree_model_node_get_prev (ETreeModel *etree,
+ ETreePath path);
/* node accessors */
-gboolean e_tree_model_node_is_root (ETreeModel *etree, ETreePath *path);
-gboolean e_tree_model_node_is_expandable (ETreeModel *etree, ETreePath *path);
-gboolean e_tree_model_node_is_expanded (ETreeModel *etree, ETreePath *path);
-gboolean e_tree_model_node_is_visible (ETreeModel *etree, ETreePath *path);
-void e_tree_model_set_expanded_default (ETreeModel *etree, gboolean expanded);
-void e_tree_model_node_set_expanded (ETreeModel *etree, ETreePath *path, gboolean expanded);
-void e_tree_model_node_set_expanded_recurse (ETreeModel *etree, ETreePath *path, gboolean expanded);
-guint e_tree_model_node_get_children (ETreeModel *etree, ETreePath *path, ETreePath ***paths);
-guint e_tree_model_node_depth (ETreeModel *etree, ETreePath *path);
-guint e_tree_model_node_num_visible_descendents (ETreeModel *etm, ETreePath *node);
-gpointer e_tree_model_node_get_data (ETreeModel *etm, ETreePath *node);
-void e_tree_model_node_set_data (ETreeModel *etm, ETreePath *node, gpointer node_data);
-
-/* display oriented routines */
-ETreePath *e_tree_model_node_at_row (ETreeModel *etree, int row);
-GdkPixbuf *e_tree_model_icon_of_node (ETreeModel *etree, ETreePath *path);
-int e_tree_model_row_of_node (ETreeModel *etree, ETreePath *path);
-void e_tree_model_root_node_set_visible (ETreeModel *etree, gboolean visible);
-gboolean e_tree_model_root_node_is_visible (ETreeModel *etree);
-
-/* sort routine, analogous to gtk_ctree_node_sort */
-void e_tree_model_node_set_compare_function (ETreeModel *tree_model, ETreePath *node, ETreePathCompareFunc compare);
-void e_tree_model_node_sort (ETreeModel *tree_model, ETreePath *node);
+gboolean e_tree_model_node_is_root (ETreeModel *etree,
+ ETreePath path);
+gboolean e_tree_model_node_is_expandable (ETreeModel *etree,
+ ETreePath path);
+guint e_tree_model_node_get_children (ETreeModel *etree,
+ ETreePath path,
+ ETreePath **paths);
+guint e_tree_model_node_depth (ETreeModel *etree,
+ ETreePath path);
+GdkPixbuf *e_tree_model_icon_at (ETreeModel *etree,
+ ETreePath path);
+gboolean e_tree_model_get_expanded_default (ETreeModel *model);
+gint e_tree_model_column_count (ETreeModel *model);
+
+
+gboolean e_tree_model_has_save_id (ETreeModel *model);
+gchar *e_tree_model_get_save_id (ETreeModel *model,
+ ETreePath node);
+
+void *e_tree_model_value_at (ETreeModel *etree,
+ ETreePath node,
+ int col);
+void e_tree_model_set_value_at (ETreeModel *etree,
+ ETreePath node,
+ int col,
+ const void *val);
+gboolean e_tree_model_node_is_editable (ETreeModel *etree,
+ ETreePath node,
+ int col);
+void *e_tree_model_duplicate_value (ETreeModel *etree,
+ int col,
+ const void *value);
+void e_tree_model_free_value (ETreeModel *etree,
+ int col,
+ void *value);
+void *e_tree_model_initialize_value (ETreeModel *etree,
+ int col);
+gboolean e_tree_model_value_is_empty (ETreeModel *etree,
+ int col,
+ const void *value);
+char *e_tree_model_value_to_string (ETreeModel *etree,
+ int col,
+ const void *value);
+
+/* depth first traversal of path's descendents, calling func on each one */
+void e_tree_model_node_traverse (ETreeModel *model,
+ ETreePath path,
+ ETreePathFunc func,
+ gpointer data);
/*
** Routines for emitting signals on the ETreeModel
*/
-void e_tree_model_node_changed (ETreeModel *tree_model, ETreePath *node);
-void e_tree_model_node_inserted (ETreeModel *tree_model, ETreePath *parent_node, ETreePath *inserted_node);
-void e_tree_model_node_removed (ETreeModel *tree_model, ETreePath *parent_node, ETreePath *removed_node);
-void e_tree_model_node_collapsed (ETreeModel *tree_model, ETreePath *node);
-void e_tree_model_node_expanded (ETreeModel *tree_model, ETreePath *node, gboolean *allow_expand);
-
-/* expanded state saving stuff */
-gboolean e_tree_model_save_expanded_state (ETreeModel *etm, const char *filename);
-gboolean e_tree_model_load_expanded_state (ETreeModel *etm, const char *filename);
-void e_tree_model_node_set_save_id (ETreeModel *etm, ETreePath *node, const char *id);
-ETreePath* e_tree_model_node_insert_id (ETreeModel *tree_model, ETreePath *parent_path,
- int position, gpointer node_data, const char *save_id);
-
-/* depth first traversal of path's descendents, calling func on each one */
-void e_tree_model_node_traverse (ETreeModel *model, ETreePath *path, ETreePathFunc func, gpointer data);
-
-void e_tree_model_show_node (ETreeModel *etm, ETreePath* node);
+void e_tree_model_pre_change (ETreeModel *tree_model);
+void e_tree_model_node_changed (ETreeModel *tree_model,
+ ETreePath node);
+void e_tree_model_node_data_changed (ETreeModel *tree_model,
+ ETreePath node);
+void e_tree_model_node_col_changed (ETreeModel *tree_model,
+ ETreePath node,
+ int col);
+void e_tree_model_node_inserted (ETreeModel *tree_model,
+ ETreePath parent_node,
+ ETreePath inserted_node);
+void e_tree_model_node_removed (ETreeModel *tree_model,
+ ETreePath parent_node,
+ ETreePath removed_node);
#ifdef __cplusplus
}
diff --git a/widgets/table/e-tree-scrolled.c b/widgets/table/e-tree-scrolled.c
new file mode 100644
index 0000000000..35a5be3729
--- /dev/null
+++ b/widgets/table/e-tree-scrolled.c
@@ -0,0 +1,207 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * E-tree-scrolled.c: A graphical view of a Tree.
+ *
+ * Author:
+ * Chris Lahey <clahey@ximian.com>
+ * Miguel de Icaza (miguel@ximian.com)
+ *
+ * Copyright 2000, 1999, Ximian, Inc
+ */
+#include <config.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdio.h>
+#include <libgnomeui/gnome-canvas.h>
+#include <gtk/gtksignal.h>
+#include <gnome-xml/parser.h>
+#include <gnome-xml/xmlmemory.h>
+
+#include "e-tree-scrolled.h"
+
+#define COLUMN_HEADER_HEIGHT 16
+
+#define PARENT_TYPE e_scroll_frame_get_type ()
+
+static GtkObjectClass *parent_class;
+
+enum {
+ ARG_0,
+ ARG_TREE,
+};
+
+static void
+e_tree_scrolled_init (GtkObject *object)
+{
+ ETreeScrolled *ets;
+ EScrollFrame *scroll_frame;
+
+ ets = E_TREE_SCROLLED (object);
+ scroll_frame = E_SCROLL_FRAME (object);
+
+ GTK_WIDGET_SET_FLAGS (ets, GTK_CAN_FOCUS);
+
+ ets->tree = gtk_type_new(e_tree_get_type());
+
+ e_scroll_frame_set_policy (scroll_frame, GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
+ e_scroll_frame_set_shadow_type (scroll_frame, GTK_SHADOW_IN);
+}
+
+static void
+e_tree_scrolled_real_construct (ETreeScrolled *ets)
+{
+ gtk_container_add(GTK_CONTAINER(ets), GTK_WIDGET(ets->tree));
+
+ gtk_widget_show(GTK_WIDGET(ets->tree));
+}
+
+ETreeScrolled *e_tree_scrolled_construct (ETreeScrolled *ets,
+ ETreeModel *etm,
+ ETableExtras *ete,
+ const char *spec,
+ const char *state)
+{
+ g_return_val_if_fail(ets != NULL, NULL);
+ g_return_val_if_fail(E_IS_TREE_SCROLLED(ets), 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 != NULL, NULL);
+
+ e_tree_construct(ets->tree, etm, ete, spec, state);
+
+ e_tree_scrolled_real_construct(ets);
+
+ return ets;
+}
+
+GtkWidget *e_tree_scrolled_new (ETreeModel *etm,
+ ETableExtras *ete,
+ const char *spec,
+ const char *state)
+{
+ ETreeScrolled *ets;
+
+ 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);
+
+ ets = E_TREE_SCROLLED (gtk_widget_new (e_tree_scrolled_get_type (),
+ "hadjustment", NULL,
+ "vadjustment", NULL,
+ NULL));
+
+ ets = e_tree_scrolled_construct (ets, etm, ete, spec, state);
+
+ return GTK_WIDGET (ets);
+}
+
+ETreeScrolled *e_tree_scrolled_construct_from_spec_file (ETreeScrolled *ets,
+ ETreeModel *etm,
+ ETableExtras *ete,
+ const char *spec_fn,
+ const char *state_fn)
+{
+ g_return_val_if_fail(ets != NULL, NULL);
+ g_return_val_if_fail(E_IS_TREE_SCROLLED(ets), 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);
+
+ e_tree_construct_from_spec_file(ets->tree, etm, ete, spec_fn, state_fn);
+
+ e_tree_scrolled_real_construct(ets);
+
+ return ets;
+}
+
+GtkWidget *e_tree_scrolled_new_from_spec_file (ETreeModel *etm,
+ ETableExtras *ete,
+ const char *spec_fn,
+ const char *state_fn)
+{
+ ETreeScrolled *ets;
+
+ 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);
+
+ ets = gtk_type_new (e_tree_scrolled_get_type ());
+
+ ets = e_tree_scrolled_construct_from_spec_file (ets, etm, ete, spec_fn, state_fn);
+
+ return GTK_WIDGET (ets);
+}
+
+ETree *
+e_tree_scrolled_get_tree (ETreeScrolled *ets)
+{
+ return ets->tree;
+}
+
+static void
+ets_get_arg (GtkObject *o, GtkArg *arg, guint arg_id)
+{
+ ETreeScrolled *ets = E_TREE_SCROLLED (o);
+
+ switch (arg_id){
+ case ARG_TREE:
+ if (ets->tree)
+ GTK_VALUE_OBJECT (*arg) = GTK_OBJECT(ets->tree);
+ else
+ GTK_VALUE_OBJECT (*arg) = NULL;
+ break;
+ }
+}
+
+/* Grab_focus handler for the scrolled ETree */
+static void
+ets_grab_focus (GtkWidget *widget)
+{
+ ETreeScrolled *ets;
+
+ ets = E_TREE_SCROLLED (widget);
+
+ gtk_widget_grab_focus (GTK_WIDGET (ets->tree));
+}
+
+/* Focus handler for the scrolled ETree */
+static gint
+ets_focus (GtkContainer *container, GtkDirectionType direction)
+{
+ ETreeScrolled *ets;
+
+ ets = E_TREE_SCROLLED (container);
+
+ return gtk_container_focus (GTK_CONTAINER (ets->tree), direction);
+}
+
+static void
+e_tree_scrolled_class_init (ETreeScrolledClass *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->get_arg = ets_get_arg;
+
+ widget_class->grab_focus = ets_grab_focus;
+
+ container_class->focus = ets_focus;
+
+ gtk_object_add_arg_type ("ETreeScrolled::tree", GTK_TYPE_OBJECT,
+ GTK_ARG_READABLE, ARG_TREE);
+}
+
+E_MAKE_TYPE(e_tree_scrolled, "ETreeScrolled", ETreeScrolled, e_tree_scrolled_class_init, e_tree_scrolled_init, PARENT_TYPE);
+
diff --git a/widgets/table/e-tree-scrolled.h b/widgets/table/e-tree-scrolled.h
new file mode 100644
index 0000000000..374a036f0b
--- /dev/null
+++ b/widgets/table/e-tree-scrolled.h
@@ -0,0 +1,54 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+#ifndef _E_TREE_SCROLLED_H_
+#define _E_TREE_SCROLLED_H_
+
+#include <gal/widgets/e-scroll-frame.h>
+#include <gal/e-table/e-tree-model.h>
+#include <gal/e-table/e-tree.h>
+
+BEGIN_GNOME_DECLS
+
+#define E_TREE_SCROLLED_TYPE (e_tree_scrolled_get_type ())
+#define E_TREE_SCROLLED(o) (GTK_CHECK_CAST ((o), E_TREE_SCROLLED_TYPE, ETreeScrolled))
+#define E_TREE_SCROLLED_CLASS(k) (GTK_CHECK_CLASS_CAST((k), E_TREE_SCROLLED_TYPE, ETreeScrolledClass))
+#define E_IS_TREE_SCROLLED(o) (GTK_CHECK_TYPE ((o), E_TREE_SCROLLED_TYPE))
+#define E_IS_TREE_SCROLLED_CLASS(k) (GTK_CHECK_CLASS_TYPE ((k), E_TREE_SCROLLED_TYPE))
+
+typedef struct {
+ EScrollFrame parent;
+
+ ETree *tree;
+} ETreeScrolled;
+
+typedef struct {
+ EScrollFrameClass parent_class;
+} ETreeScrolledClass;
+
+GtkType e_tree_scrolled_get_type (void);
+
+ETreeScrolled *e_tree_scrolled_construct (ETreeScrolled *ets,
+ ETreeModel *etm,
+ ETableExtras *ete,
+ const char *spec,
+ const char *state);
+GtkWidget *e_tree_scrolled_new (ETreeModel *etm,
+ ETableExtras *ete,
+ const char *spec,
+ const char *state);
+
+ETreeScrolled *e_tree_scrolled_construct_from_spec_file (ETreeScrolled *ets,
+ ETreeModel *etm,
+ ETableExtras *ete,
+ const char *spec_fn,
+ const char *state_fn);
+GtkWidget *e_tree_scrolled_new_from_spec_file (ETreeModel *etm,
+ ETableExtras *ete,
+ const char *spec_fn,
+ const char *state_fn);
+
+ETree *e_tree_scrolled_get_tree (ETreeScrolled *ets);
+
+END_GNOME_DECLS
+
+#endif /* _E_TREE_SCROLLED_H_ */
+
diff --git a/widgets/table/e-tree-simple.c b/widgets/table/e-tree-simple.c
index 5741d1cf0f..5ff26b088a 100644
--- a/widgets/table/e-tree-simple.c
+++ b/widgets/table/e-tree-simple.c
@@ -181,8 +181,6 @@ e_tree_simple_new (ETableSimpleColumnCountFn col_count,
etg = gtk_type_new (e_tree_simple_get_type ());
- e_tree_model_construct (E_TREE_MODEL (etg));
-
etg->col_count = col_count;
etg->duplicate_value = duplicate_value;
etg->free_value = free_value;
diff --git a/widgets/table/e-tree-sorted.c b/widgets/table/e-tree-sorted.c
new file mode 100644
index 0000000000..168dc6aec7
--- /dev/null
+++ b/widgets/table/e-tree-sorted.c
@@ -0,0 +1,1126 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * e-tree-sorted.c: a Tree Model implementation that the programmer builds in sorted.
+ *
+ * Author:
+ * Chris Toshok (toshok@ximian.com)
+ * Chris Lahey <clahey@ximian.com>
+ *
+ * Adapted from the gtree code and ETableModel.
+ *
+ * (C) 2000, 2001 Ximian, Inc.
+ */
+
+/* FIXME: Overall e-tree-sorted.c needs to be made more efficient. */
+
+
+#include <config.h>
+
+#include <stdio.h>
+#include <errno.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#include <gnome-xml/parser.h>
+#include <gnome-xml/xmlmemory.h>
+
+#include <gtk/gtksignal.h>
+#include <stdlib.h>
+#include "gal/util/e-util.h"
+#include "gal/util/e-xml-utils.h"
+#include "e-tree-sorted.h"
+#include "e-table-sorting-utils.h"
+
+#define PARENT_TYPE E_TREE_MODEL_TYPE
+
+/* maximum insertions between an idle event that we will do without scheduling an idle sort */
+#define ETS_INSERT_MAX (4)
+
+#define TREEPATH_CHUNK_AREA_SIZE (30 * sizeof (ETreeSortedPath))
+
+#define d(x)
+
+static ETreeModel *parent_class;
+static GMemChunk *node_chunk;
+
+typedef struct ETreeSortedPath ETreeSortedPath;
+
+struct ETreeSortedPath {
+ ETreePath corresponding;
+
+ /* parent/child/sibling pointers */
+ ETreeSortedPath *parent;
+ gint num_children;
+ ETreeSortedPath **children;
+ int position;
+
+ guint needs_resort : 1;
+ guint child_needs_resort : 1;
+ guint resort_all_children : 1;
+ guint needs_regen_to_sort : 1;
+};
+
+struct ETreeSortedPriv {
+ ETreeModel *source;
+ ETreeSortedPath *root;
+
+ ETableSortInfo *sort_info;
+ ETableHeader *full_header;
+
+ int tree_model_pre_change_id;
+ int tree_model_node_changed_id;
+ int tree_model_node_data_changed_id;
+ int tree_model_node_col_changed_id;
+ int tree_model_node_inserted_id;
+ int tree_model_node_removed_id;
+
+ int sort_info_changed_id;
+ int sort_idle_id;
+ int insert_idle_id;
+ int insert_count;
+};
+
+enum {
+ ARG_0,
+
+ ARG_SORT_INFO,
+};
+
+static void ets_sort_info_changed (ETableSortInfo *sort_info, ETreeSorted *ets);
+static void resort_node (ETreeSorted *ets, ETreeSortedPath *path, gboolean resort_all_children, gboolean send_signals);
+static void mark_path_needs_resort (ETreeSorted *ets, ETreeSortedPath *path, gboolean needs_rebuild, gboolean resort_all_children);
+static void schedule_resort (ETreeSorted *ets, ETreeSortedPath *path, gboolean needs_regen, gboolean resort_all_children);
+static void free_path (ETreeSortedPath *path);
+static void generate_children(ETreeSorted *ets, ETreeSortedPath *path, gboolean needs_resort);
+
+
+
+/* idle callbacks */
+
+static gboolean
+ets_sort_idle(gpointer user_data)
+{
+ ETreeSorted *ets = user_data;
+ ets->priv->sort_idle_id = 0;
+ if (ets->priv->root) {
+ resort_node (ets, ets->priv->root, FALSE, TRUE);
+ }
+ return FALSE;
+}
+
+static gboolean
+ets_insert_idle(ETreeSorted *ets)
+{
+ ets->priv->insert_count = 0;
+ ets->priv->insert_idle_id = 0;
+ return FALSE;
+}
+
+
+
+/* Helper functions */
+
+static ETreeSortedPath *
+find_path(ETreeSorted *ets, ETreePath corresponding)
+{
+ int depth;
+ ETreePath *sequence;
+ int i;
+ ETreeSortedPath *path;
+
+ if (corresponding == NULL)
+ return NULL;
+
+#if 0
+ if (etta->priv->last_access != -1 && etta->priv->map_table[etta->priv->last_access] == path)
+ return etta->priv->last_access;
+#endif
+
+ depth = e_tree_model_node_depth(ets->priv->source, corresponding);
+
+ sequence = g_new(ETreePath, depth + 1);
+
+ sequence[0] = corresponding;
+
+ for (i = 0; i < depth; i++)
+ sequence[i + 1] = e_tree_model_node_get_parent(ets->priv->source, sequence[i]);
+
+ path = ets->priv->root;
+
+ for (i = depth - 1; i >= 0 && path != NULL; i --) {
+ int j;
+
+ if (path->num_children == -1) {
+ path = NULL;
+ break;
+ }
+
+ for (j = 0; j < path->num_children; j++) {
+ if (path->children[j]->corresponding == sequence[i]) {
+ break;
+ }
+ }
+
+ if (j < path->num_children) {
+ path = path->children[j];
+ } else {
+ path = NULL;
+ }
+ }
+ g_free (sequence);
+
+#if 0
+ ets->priv->last_access = row;
+#endif
+
+ return path;
+}
+
+static ETreeSortedPath *
+find_child_path(ETreeSorted *ets, ETreeSortedPath *parent, ETreePath corresponding)
+{
+ int i;
+
+ if (corresponding == NULL)
+ return NULL;
+
+ if (parent->num_children == -1) {
+ return NULL;
+ }
+
+ for (i = 0; i < parent->num_children; i++)
+ if (parent->children[i]->corresponding == corresponding)
+ return parent->children[i];
+
+ return NULL;
+}
+
+static ETreeSortedPath *
+find_or_create_path(ETreeSorted *ets, ETreePath corresponding)
+{
+ int depth;
+ ETreePath *sequence;
+ int i;
+ ETreeSortedPath *path;
+
+ if (corresponding == NULL)
+ return NULL;
+
+#if 0
+ if (etta->priv->last_access != -1 && etta->priv->map_table[etta->priv->last_access] == path)
+ return etta->priv->last_access;
+#endif
+
+ depth = e_tree_model_node_depth(ets->priv->source, corresponding);
+
+ sequence = g_new(ETreePath, depth + 1);
+
+ sequence[0] = corresponding;
+
+ for (i = 0; i < depth; i++)
+ sequence[i + 1] = e_tree_model_node_get_parent(ets->priv->source, sequence[i]);
+
+ path = ets->priv->root;
+
+ for (i = depth - 1; i >= 0 && path != NULL; i --) {
+ int j;
+
+ if (path->num_children == -1) {
+ generate_children(ets, path, TRUE);
+ }
+
+ for (j = 0; j < path->num_children; j++) {
+ if (path->children[j]->corresponding == sequence[i]) {
+ break;
+ }
+ }
+
+ if (j < path->num_children) {
+ path = path->children[j];
+ } else {
+ path = NULL;
+ }
+ }
+ g_free (sequence);
+
+#if 0
+ ets->priv->last_access = row;
+#endif
+
+ return path;
+}
+
+static void
+free_children (ETreeSortedPath *path)
+{
+ int i;
+
+ if (path == NULL)
+ return;
+
+ for (i = 0; i < path->num_children; i++) {
+ free_path(path->children[i]);
+ }
+
+ g_free(path->children);
+ path->children = NULL;
+ path->num_children = -1;
+}
+
+static void
+free_path (ETreeSortedPath *path)
+{
+ free_children(path);
+ g_chunk_free(path, node_chunk);
+}
+
+static ETreeSortedPath *
+new_path (ETreeSortedPath *parent, ETreePath corresponding)
+{
+ ETreeSortedPath *path;
+
+ path = g_chunk_new0 (ETreeSortedPath, node_chunk);
+
+ path->corresponding = corresponding;
+ path->parent = parent;
+ path->num_children = -1;
+ path->children = NULL;
+ path->position = -1;
+ path->child_needs_resort = 0;
+ path->resort_all_children = 0;
+ path->needs_resort = 0;
+ path->needs_regen_to_sort = 0;
+
+ return path;
+}
+
+static void
+reposition_path (ETreeSorted *ets, ETreeSortedPath *path)
+{
+ int new_index;
+ int old_index = path->position;
+ ETreeSortedPath *parent = path->parent;
+ if (parent) {
+ if (ets->priv->sort_idle_id == 0) {
+ ets->priv->insert_count++;
+ if (ets->priv->insert_count > ETS_INSERT_MAX) {
+ /* schedule a sort, and append instead */
+ schedule_resort(ets, parent, TRUE, FALSE);
+ } else {
+ /* make sure we have an idle handler to reset the count every now and then */
+ if (ets->priv->insert_idle_id == 0) {
+ ets->priv->insert_idle_id = g_idle_add_full(40, (GSourceFunc) ets_insert_idle, ets, NULL);
+ }
+
+ new_index = e_table_sorting_utils_tree_check_position
+ (E_TREE_MODEL(ets),
+ ets->priv->sort_info,
+ ets->priv->full_header,
+ (ETreePath *) parent->children,
+ parent->num_children,
+ old_index);
+
+ if (new_index > old_index) {
+ int i;
+ e_tree_model_pre_change(E_TREE_MODEL(ets));
+ memmove(parent->children + old_index, parent->children + old_index + 1, sizeof (ETreePath) * (new_index - old_index));
+ parent->children[new_index] = path;
+ for (i = old_index; i <= new_index; i++)
+ parent->children[i]->position = i;
+ e_tree_model_node_changed(E_TREE_MODEL(ets), parent);
+ } else if (new_index < old_index) {
+ int i;
+ e_tree_model_pre_change(E_TREE_MODEL(ets));
+ memmove(parent->children + new_index + 1, parent->children + new_index, sizeof (ETreePath) * (old_index - new_index));
+ parent->children[new_index] = path;
+ for (i = new_index; i <= old_index; i++)
+ parent->children[i]->position = i;
+ e_tree_model_node_changed(E_TREE_MODEL(ets), parent);
+ }
+ }
+ } else
+ mark_path_needs_resort(ets, parent, TRUE, FALSE);
+ }
+}
+
+static void
+generate_children(ETreeSorted *ets, ETreeSortedPath *path, gboolean needs_resort)
+{
+ ETreePath child;
+ int i;
+ int count;
+
+ free_children(path);
+
+ count = 0;
+ for (child = e_tree_model_node_get_first_child(ets->priv->source, path->corresponding);
+ child;
+ child = e_tree_model_node_get_next(ets->priv->source, child)) {
+ count ++;
+ }
+
+ path->num_children = count;
+ path->children = g_new(ETreeSortedPath *, count);
+ for (child = e_tree_model_node_get_first_child(ets->priv->source, path->corresponding), i = 0;
+ child;
+ child = e_tree_model_node_get_next(ets->priv->source, child), i++) {
+ path->children[i] = new_path(path, child);
+ path->children[i]->position = i;
+ }
+ if (needs_resort)
+ schedule_resort (ets, path, FALSE, TRUE);
+}
+
+static void
+resort_node (ETreeSorted *ets, ETreeSortedPath *path, gboolean resort_all_children, gboolean send_signals)
+{
+ gboolean needs_resort;
+ if (path) {
+ needs_resort = path->needs_resort || resort_all_children;
+ if (needs_resort && send_signals)
+ e_tree_model_pre_change(E_TREE_MODEL(ets));
+ if (needs_resort) {
+ int i;
+ d(g_print("Start sort of node %p\n", path));
+ if (path->needs_regen_to_sort)
+ generate_children(ets, path, FALSE);
+ d(g_print("Regened sort of node %p\n", path));
+ if (path->num_children > 0) {
+ e_table_sorting_utils_tree_sort (E_TREE_MODEL(ets),
+ ets->priv->sort_info,
+ ets->priv->full_header,
+ (ETreePath *) path->children,
+ path->num_children);
+ d(g_print("Renumbering sort of node %p\n", path));
+ for (i = 0; i < path->num_children; i++) {
+ path->children[i]->position = i;
+ }
+ }
+ d(g_print("End sort of node %p\n", path));
+ }
+ if (path->resort_all_children)
+ resort_all_children = TRUE;
+ if ((resort_all_children || path->child_needs_resort) && path->num_children >= 0) {
+ int i;
+ for (i = 0; i < path->num_children; i++) {
+ resort_node(ets, path->children[i], resort_all_children, send_signals && !needs_resort);
+ }
+ path->child_needs_resort = 0;
+ }
+ path->needs_resort = 0;
+ path->child_needs_resort = 0;
+ path->needs_regen_to_sort = 0;
+ path->resort_all_children = 0;
+ if (needs_resort && send_signals)
+ e_tree_model_node_changed(E_TREE_MODEL(ets), path);
+ }
+}
+
+static void
+mark_path_child_needs_resort (ETreeSorted *ets, ETreeSortedPath *path)
+{
+ if (path == NULL)
+ return;
+ if (!path->child_needs_resort) {
+ path->child_needs_resort = 1;
+ mark_path_child_needs_resort (ets, path->parent);
+ }
+}
+
+static void
+mark_path_needs_resort (ETreeSorted *ets, ETreeSortedPath *path, gboolean needs_regen, gboolean resort_all_children)
+{
+ if (path == NULL)
+ return;
+ path->needs_resort = 1;
+ path->needs_regen_to_sort = needs_regen;
+ path->resort_all_children = resort_all_children;
+ mark_path_child_needs_resort(ets, path->parent);
+}
+
+static void
+schedule_resort (ETreeSorted *ets, ETreeSortedPath *path, gboolean needs_regen, gboolean resort_all_children)
+{
+ mark_path_needs_resort(ets, path, needs_regen, resort_all_children);
+ if (ets->priv->sort_idle_id == 0) {
+ ets->priv->sort_idle_id = g_idle_add_full(50, (GSourceFunc) ets_sort_idle, ets, NULL);
+ }
+ ets->priv->insert_count = 0;
+ if (ets->priv->insert_idle_id != 0) {
+ g_source_remove(ets->priv->insert_idle_id);
+ ets->priv->insert_idle_id = 0;
+ }
+}
+
+
+
+/* virtual methods */
+
+static void
+ets_destroy (GtkObject *object)
+{
+ ETreeSorted *ets = E_TREE_SORTED (object);
+ ETreeSortedPriv *priv = ets->priv;
+
+ /* FIXME lots of stuff to free here */
+
+ free_path(priv->root);
+
+ if (priv->source) {
+ gtk_signal_disconnect (GTK_OBJECT (priv->source),
+ priv->tree_model_pre_change_id);
+ gtk_signal_disconnect (GTK_OBJECT (priv->source),
+ priv->tree_model_node_changed_id);
+ gtk_signal_disconnect (GTK_OBJECT (priv->source),
+ priv->tree_model_node_data_changed_id);
+ gtk_signal_disconnect (GTK_OBJECT (priv->source),
+ priv->tree_model_node_col_changed_id);
+ gtk_signal_disconnect (GTK_OBJECT (priv->source),
+ priv->tree_model_node_inserted_id);
+ gtk_signal_disconnect (GTK_OBJECT (priv->source),
+ priv->tree_model_node_removed_id);
+
+ gtk_object_unref (GTK_OBJECT (priv->source));
+ priv->source = NULL;
+
+ priv->tree_model_pre_change_id = 0;
+ priv->tree_model_node_changed_id = 0;
+ priv->tree_model_node_data_changed_id = 0;
+ priv->tree_model_node_col_changed_id = 0;
+ priv->tree_model_node_inserted_id = 0;
+ priv->tree_model_node_removed_id = 0;
+ }
+
+ if (priv->sort_info) {
+ gtk_signal_disconnect (GTK_OBJECT (priv->sort_info),
+ priv->sort_info_changed_id);
+
+ gtk_object_unref (GTK_OBJECT (priv->sort_info));
+ priv->sort_info = NULL;
+
+ priv->sort_info_changed_id = 0;
+ }
+
+ if (ets->priv->sort_idle_id) {
+ g_source_remove(ets->priv->sort_idle_id);
+ ets->priv->sort_idle_id = 0;
+ }
+ if (ets->priv->insert_idle_id) {
+ g_source_remove(ets->priv->insert_idle_id);
+ ets->priv->insert_idle_id = 0;
+ }
+
+ if (priv->full_header)
+ gtk_object_unref(GTK_OBJECT(priv->full_header));
+
+ g_free (priv);
+
+ GTK_OBJECT_CLASS (parent_class)->destroy (object);
+}
+
+/* Set_arg handler for the text item */
+static void
+ets_set_arg (GtkObject *object, GtkArg *arg, guint arg_id)
+{
+ ETreeSorted *ets;
+
+ ets = E_TREE_SORTED (object);
+
+ switch (arg_id) {
+ case ARG_SORT_INFO:
+ if (ets->priv->sort_info) {
+ gtk_signal_disconnect (GTK_OBJECT (ets->priv->sort_info),
+ ets->priv->sort_info_changed_id);
+
+ gtk_object_unref (GTK_OBJECT (ets->priv->sort_info));
+ ets->priv->sort_info_changed_id = 0;
+ }
+ if (GTK_VALUE_OBJECT (*arg))
+ ets->priv->sort_info = E_TABLE_SORT_INFO(GTK_VALUE_OBJECT (*arg));
+ else
+ ets->priv->sort_info = NULL;
+ if (ets->priv->sort_info) {
+ gtk_object_ref(GTK_OBJECT(ets->priv->sort_info));
+
+ ets->priv->sort_info_changed_id = gtk_signal_connect (GTK_OBJECT (ets->priv->sort_info), "sort_info_changed",
+ GTK_SIGNAL_FUNC (ets_sort_info_changed), ets);
+ }
+ if (ets->priv->root)
+ schedule_resort (ets, ets->priv->root, TRUE, TRUE);
+ break;
+
+ default:
+ return;
+ }
+}
+
+/* Get_arg handler for the text item */
+static void
+ets_get_arg (GtkObject *object, GtkArg *arg, guint arg_id)
+{
+ ETreeSorted *ets;
+
+ ets = E_TREE_SORTED (object);
+
+ switch (arg_id) {
+ case ARG_SORT_INFO:
+ if (ets->priv->sort_info)
+ GTK_VALUE_OBJECT (*arg) = GTK_OBJECT(ets->priv->sort_info);
+ else
+ GTK_VALUE_OBJECT (*arg) = NULL;
+ break;
+
+ default:
+ arg->type = GTK_TYPE_INVALID;
+ break;
+ }
+}
+
+static ETreePath
+ets_get_root (ETreeModel *etm)
+{
+ ETreeSortedPriv *priv = E_TREE_SORTED(etm)->priv;
+ if (priv->root == NULL) {
+ ETreeSorted *ets = E_TREE_SORTED(etm);
+ ETreePath corresponding = e_tree_model_get_root(ets->priv->source);
+
+ if (corresponding) {
+ priv->root = new_path(NULL, corresponding);
+ }
+ }
+ if (priv->root && priv->root->num_children == -1) {
+ generate_children(E_TREE_SORTED(etm), priv->root, TRUE);
+ }
+
+ return priv->root;
+}
+
+static ETreePath
+ets_get_parent (ETreeModel *etm, ETreePath node)
+{
+ ETreeSortedPath *path = node;
+ return path->parent;
+}
+
+static ETreePath
+ets_get_first_child (ETreeModel *etm, ETreePath node)
+{
+ ETreeSortedPath *path = node;
+ ETreeSorted *ets = E_TREE_SORTED(etm);
+
+ if (path->num_children == -1)
+ generate_children(ets, path, TRUE);
+
+ if (path->num_children > 0)
+ return path->children[0];
+ else
+ return NULL;
+}
+
+static ETreePath
+ets_get_last_child (ETreeModel *etm, ETreePath node)
+{
+ ETreeSortedPath *path = node;
+ ETreeSorted *ets = E_TREE_SORTED(etm);
+
+ if (path->num_children == -1)
+ generate_children(ets, path, TRUE);
+
+ if (path->num_children > 0)
+ return path->children[path->num_children - 1];
+ else
+ return NULL;
+}
+
+static ETreePath
+ets_get_next (ETreeModel *etm, ETreePath node)
+{
+ ETreeSortedPath *path = node;
+ ETreeSortedPath *parent = path->parent;
+ if (parent) {
+ if (parent->num_children > path->position + 1)
+ return parent->children[path->position + 1];
+ else
+ return NULL;
+ } else
+ return NULL;
+}
+
+static ETreePath
+ets_get_prev (ETreeModel *etm, ETreePath node)
+{
+ ETreeSortedPath *path = node;
+ ETreeSortedPath *parent = path->parent;
+ if (parent) {
+ if (path->position - 1 >= 0)
+ return parent->children[path->position - 1];
+ else
+ return NULL;
+ } else
+ return NULL;
+}
+
+static gboolean
+ets_is_root (ETreeModel *etm, ETreePath node)
+{
+ ETreeSortedPath *path = node;
+ ETreeSorted *ets = E_TREE_SORTED(etm);
+
+ return e_tree_model_node_is_root (ets->priv->source, path->corresponding);
+}
+
+static gboolean
+ets_is_expandable (ETreeModel *etm, ETreePath node)
+{
+ ETreeSortedPath *path = node;
+ ETreeSorted *ets = E_TREE_SORTED(etm);
+ gboolean expandable = e_tree_model_node_is_expandable (ets->priv->source, path->corresponding);
+
+ if (path->num_children == -1) {
+ generate_children(ets, node, TRUE);
+ }
+
+ return expandable;
+}
+
+static guint
+ets_get_children (ETreeModel *etm, ETreePath node, ETreePath **nodes)
+{
+ ETreeSortedPath *path = node;
+ guint n_children;
+
+ if (path->num_children == -1) {
+ generate_children(E_TREE_SORTED(etm), node, TRUE);
+ }
+
+ n_children = path->num_children;
+
+ if (nodes) {
+ int i;
+
+ (*nodes) = g_malloc (sizeof (ETreePath) * n_children);
+ for (i = 0; i < n_children; i ++) {
+ (*nodes)[i] = path->children[i];
+ }
+ }
+
+ return n_children;
+}
+
+static guint
+ets_depth (ETreeModel *etm, ETreePath node)
+{
+ ETreeSortedPath *path = node;
+ ETreeSorted *ets = E_TREE_SORTED(etm);
+
+ return e_tree_model_node_depth(ets->priv->source, path->corresponding);
+}
+
+static GdkPixbuf *
+ets_icon_at (ETreeModel *etm, ETreePath node)
+{
+ ETreeSortedPath *path = node;
+ ETreeSorted *ets = E_TREE_SORTED(etm);
+
+ return e_tree_model_icon_at(ets->priv->source, path->corresponding);
+}
+
+static gboolean
+ets_get_expanded_default (ETreeModel *etm)
+{
+ ETreeSorted *ets = E_TREE_SORTED(etm);
+
+ return e_tree_model_get_expanded_default(ets->priv->source);
+}
+
+static gint
+ets_column_count (ETreeModel *etm)
+{
+ ETreeSorted *ets = E_TREE_SORTED(etm);
+
+ return e_tree_model_column_count(ets->priv->source);
+}
+
+
+static gboolean
+ets_has_save_id (ETreeModel *etm)
+{
+ return TRUE;
+}
+
+static gchar *
+ets_get_save_id (ETreeModel *etm, ETreePath node)
+{
+ ETreeSorted *ets = E_TREE_SORTED(etm);
+ ETreeSortedPath *path = node;
+
+ if (e_tree_model_has_save_id(ets->priv->source))
+ return e_tree_model_get_save_id(ets->priv->source, path->corresponding);
+ else
+ return g_strdup_printf("%p", path->corresponding);
+}
+
+
+static void *
+ets_value_at (ETreeModel *etm, ETreePath node, int col)
+{
+ ETreeSorted *ets = E_TREE_SORTED(etm);
+ ETreeSortedPath *path = node;
+
+ return e_tree_model_value_at(ets->priv->source, path->corresponding, col);
+}
+
+static void
+ets_set_value_at (ETreeModel *etm, ETreePath node, int col, const void *val)
+{
+ ETreeSorted *ets = E_TREE_SORTED(etm);
+ ETreeSortedPath *path = node;
+
+ e_tree_model_set_value_at (ets->priv->source, path->corresponding, col, val);
+}
+
+static gboolean
+ets_is_editable (ETreeModel *etm, ETreePath node, int col)
+{
+ ETreeSorted *ets = E_TREE_SORTED(etm);
+ ETreeSortedPath *path = node;
+
+ return e_tree_model_node_is_editable (ets->priv->source, path->corresponding, col);
+}
+
+
+/* The default for ets_duplicate_value is to return the raw value. */
+static void *
+ets_duplicate_value (ETreeModel *etm, int col, const void *value)
+{
+ ETreeSorted *ets = E_TREE_SORTED(etm);
+
+ return e_tree_model_duplicate_value (ets->priv->source, col, value);
+}
+
+static void
+ets_free_value (ETreeModel *etm, int col, void *value)
+{
+ ETreeSorted *ets = E_TREE_SORTED(etm);
+
+ e_tree_model_free_value (ets->priv->source, col, value);
+}
+
+static void *
+ets_initialize_value (ETreeModel *etm, int col)
+{
+ ETreeSorted *ets = E_TREE_SORTED(etm);
+
+ return e_tree_model_initialize_value (ets->priv->source, col);
+}
+
+static gboolean
+ets_value_is_empty (ETreeModel *etm, int col, const void *value)
+{
+ ETreeSorted *ets = E_TREE_SORTED(etm);
+
+ return e_tree_model_value_is_empty (ets->priv->source, col, value);
+}
+
+static char *
+ets_value_to_string (ETreeModel *etm, int col, const void *value)
+{
+ ETreeSorted *ets = E_TREE_SORTED(etm);
+
+ return e_tree_model_value_to_string (ets->priv->source, col, value);
+}
+
+
+
+/* Proxy functions */
+
+static void
+ets_proxy_pre_change (ETreeModel *etm, ETreeSorted *ets)
+{
+ e_tree_model_pre_change(E_TREE_MODEL(ets));
+}
+
+static void
+ets_proxy_node_changed (ETreeModel *etm, ETreePath node, ETreeSorted *ets)
+{
+ if (e_tree_model_node_is_root(ets->priv->source, node)) {
+ if (ets->priv->root) {
+ free_path(ets->priv->root);
+ }
+ ets->priv->root = new_path(NULL, node);
+ e_tree_model_node_changed(E_TREE_MODEL(ets), ets->priv->root);
+ } else {
+ ETreeSortedPath *path = find_path(ets, node);
+
+ if (path) {
+ free_children(path);
+ reposition_path(ets, path);
+ e_tree_model_node_changed(E_TREE_MODEL(ets), path);
+ }
+ }
+}
+
+static void
+ets_proxy_node_data_changed (ETreeModel *etm, ETreePath node, ETreeSorted *ets)
+{
+ ETreeSortedPath *path = find_path(ets, node);
+
+ if (path) {
+ reposition_path(ets, path);
+ e_tree_model_node_data_changed(E_TREE_MODEL(ets), path);
+ }
+}
+
+static void
+ets_proxy_node_col_changed (ETreeModel *etm, ETreePath node, int col, ETreeSorted *ets)
+{
+ ETreeSortedPath *path = find_path(ets, node);
+
+ if (path) {
+ if (e_table_sorting_utils_affects_sort(ets->priv->sort_info, ets->priv->full_header, col))
+ reposition_path(ets, path);
+ e_tree_model_node_col_changed(E_TREE_MODEL(ets), path, col);
+ }
+}
+
+static void
+ets_proxy_node_inserted (ETreeModel *etm, ETreePath parent, ETreePath child, ETreeSorted *ets)
+{
+ ETreeSortedPath *parent_path = find_path(ets, parent);
+
+ if (parent_path && parent_path->num_children != -1) {
+ int i;
+ int j;
+ ETreeSortedPath *path;
+ i = parent_path->num_children;
+
+ path = new_path(parent_path, child);
+ if (ets->priv->sort_idle_id == 0) {
+ ets->priv->insert_count++;
+ if (ets->priv->insert_count > ETS_INSERT_MAX) {
+ /* schedule a sort, and append instead */
+ schedule_resort(ets, parent_path, TRUE, FALSE);
+ } else {
+ /* make sure we have an idle handler to reset the count every now and then */
+ if (ets->priv->insert_idle_id == 0) {
+ ets->priv->insert_idle_id = g_idle_add_full(40, (GSourceFunc) ets_insert_idle, ets, NULL);
+ }
+ i = e_table_sorting_utils_tree_insert
+ (ets->priv->source,
+ ets->priv->sort_info,
+ ets->priv->full_header,
+ (ETreePath *) parent_path->children,
+ parent_path->num_children,
+ path);
+ }
+ } else {
+ mark_path_needs_resort(ets, parent_path, TRUE, FALSE);
+ }
+ parent_path->num_children ++;
+ parent_path->children = g_renew(ETreeSortedPath *, parent_path->children, parent_path->num_children);
+ memmove(parent_path->children + i + 1, parent_path->children + i, (parent_path->num_children - 1 - i) * sizeof(int));
+ parent_path->children[i] = path;
+ for (j = i; j < parent_path->num_children; j++) {
+ parent_path->children[j]->position = j;
+ }
+ e_tree_model_node_inserted(E_TREE_MODEL(ets), parent_path, parent_path->children[i]);
+ } else if (ets->priv->root == NULL && parent == NULL) {
+ if (child) {
+ ets->priv->root = new_path(NULL, child);
+ e_tree_model_node_inserted(E_TREE_MODEL(ets), NULL, ets->priv->root);
+ }
+ }
+}
+
+static void
+ets_proxy_node_removed (ETreeModel *etm, ETreePath parent, ETreePath child, ETreeSorted *ets)
+{
+ ETreeSortedPath *parent_path = find_path(ets, parent);
+ ETreeSortedPath *path;
+
+ if (parent_path)
+ path = find_child_path(ets, parent_path, child);
+ else
+ path = find_path(ets, child);
+
+ if (path && parent_path && parent_path->num_children != -1) {
+ int i = path->position;
+ parent_path->num_children --;
+ memmove(parent_path->children + i, parent_path->children + i + 1, sizeof(ETreeSortedPath *) * (parent_path->num_children - i));
+ for (; i < parent_path->num_children; i++) {
+ parent_path->children[i]->position = i;
+ }
+ e_tree_model_node_removed(E_TREE_MODEL(ets), parent_path, path);
+ free_path(path);
+ } else if (path && path == ets->priv->root) {
+ ets->priv->root = NULL;
+ e_tree_model_node_removed(E_TREE_MODEL(ets), NULL, path);
+ free_path(path);
+ }
+}
+
+static void
+ets_sort_info_changed (ETableSortInfo *sort_info, ETreeSorted *ets)
+{
+ schedule_resort(ets, ets->priv->root, TRUE, TRUE);
+}
+
+
+
+/* Initialization and creation */
+
+static void
+e_tree_sorted_class_init (GtkObjectClass *klass)
+{
+ ETreeModelClass *tree_class = (ETreeModelClass *) klass;
+
+ parent_class = gtk_type_class (PARENT_TYPE);
+
+ node_chunk = g_mem_chunk_create (ETreeSortedPath, TREEPATH_CHUNK_AREA_SIZE, G_ALLOC_AND_FREE);
+
+ klass->destroy = ets_destroy;
+ klass->set_arg = ets_set_arg;
+ klass->get_arg = ets_get_arg;
+
+ tree_class->get_root = ets_get_root;
+ tree_class->get_parent = ets_get_parent;
+ tree_class->get_first_child = ets_get_first_child;
+ tree_class->get_last_child = ets_get_last_child;
+ tree_class->get_prev = ets_get_prev;
+ tree_class->get_next = ets_get_next;
+
+ tree_class->is_root = ets_is_root;
+ tree_class->is_expandable = ets_is_expandable;
+ tree_class->get_children = ets_get_children;
+ tree_class->depth = ets_depth;
+
+ tree_class->icon_at = ets_icon_at;
+
+ tree_class->get_expanded_default = ets_get_expanded_default;
+ tree_class->column_count = ets_column_count;
+
+ tree_class->has_save_id = ets_has_save_id;
+ tree_class->get_save_id = ets_get_save_id;
+
+
+
+
+ tree_class->value_at = ets_value_at;
+ tree_class->set_value_at = ets_set_value_at;
+ tree_class->is_editable = ets_is_editable;
+
+ tree_class->duplicate_value = ets_duplicate_value;
+ tree_class->free_value = ets_free_value;
+ tree_class->initialize_value = ets_initialize_value;
+ tree_class->value_is_empty = ets_value_is_empty;
+ tree_class->value_to_string = ets_value_to_string;
+
+ gtk_object_add_arg_type ("ETreeSorted::sort_info", E_TABLE_SORT_INFO_TYPE,
+ GTK_ARG_READWRITE, ARG_SORT_INFO);
+}
+
+static void
+e_tree_sorted_init (GtkObject *object)
+{
+ ETreeSorted *ets = (ETreeSorted *)object;
+
+ ETreeSortedPriv *priv;
+
+ priv = g_new0 (ETreeSortedPriv, 1);
+ ets->priv = priv;
+
+ priv->root = NULL;
+ priv->source = NULL;
+
+ priv->sort_info = NULL;
+ priv->full_header = NULL;
+
+ priv->tree_model_pre_change_id = 0;
+ priv->tree_model_node_changed_id = 0;
+ priv->tree_model_node_data_changed_id = 0;
+ priv->tree_model_node_col_changed_id = 0;
+ priv->tree_model_node_inserted_id = 0;
+ priv->tree_model_node_removed_id = 0;
+
+ priv->sort_info_changed_id = 0;
+ priv->sort_idle_id = 0;
+ priv->insert_idle_id = 0;
+ priv->insert_count = 0;
+}
+
+E_MAKE_TYPE(e_tree_sorted, "ETreeSorted", ETreeSorted, e_tree_sorted_class_init, e_tree_sorted_init, PARENT_TYPE)
+
+/**
+ * e_tree_sorted_construct:
+ * @etree:
+ *
+ *
+ **/
+void
+e_tree_sorted_construct (ETreeSorted *ets, ETreeModel *source, ETableHeader *full_header, ETableSortInfo *sort_info)
+{
+ ets->priv->source = source;
+ if (source) gtk_object_ref(GTK_OBJECT(source));
+
+ ets->priv->full_header = full_header;
+ if (full_header) gtk_object_ref(GTK_OBJECT(full_header));
+
+ ets->priv->sort_info = sort_info;
+ if (sort_info) gtk_object_ref(GTK_OBJECT(sort_info));
+
+ ets->priv->tree_model_pre_change_id = gtk_signal_connect (GTK_OBJECT (source), "pre_change",
+ GTK_SIGNAL_FUNC (ets_proxy_pre_change), ets);
+ ets->priv->tree_model_node_changed_id = gtk_signal_connect (GTK_OBJECT (source), "node_changed",
+ GTK_SIGNAL_FUNC (ets_proxy_node_changed), ets);
+ ets->priv->tree_model_node_data_changed_id = gtk_signal_connect (GTK_OBJECT (source), "node_data_changed",
+ GTK_SIGNAL_FUNC (ets_proxy_node_data_changed), ets);
+ ets->priv->tree_model_node_col_changed_id = gtk_signal_connect (GTK_OBJECT (source), "node_col_changed",
+ GTK_SIGNAL_FUNC (ets_proxy_node_col_changed), ets);
+ ets->priv->tree_model_node_inserted_id = gtk_signal_connect (GTK_OBJECT (source), "node_inserted",
+ GTK_SIGNAL_FUNC (ets_proxy_node_inserted), ets);
+ ets->priv->tree_model_node_removed_id = gtk_signal_connect (GTK_OBJECT (source), "node_removed",
+ GTK_SIGNAL_FUNC (ets_proxy_node_removed), ets);
+
+ ets->priv->sort_info_changed_id = gtk_signal_connect (GTK_OBJECT (sort_info), "sort_info_changed",
+ GTK_SIGNAL_FUNC (ets_sort_info_changed), ets);
+}
+
+/**
+ * e_tree_sorted_new
+ *
+ * FIXME docs here.
+ *
+ * return values: a newly constructed ETreeSorted.
+ */
+ETreeSorted *
+e_tree_sorted_new (ETreeModel *source, ETableHeader *full_header, ETableSortInfo *sort_info)
+{
+ ETreeSorted *ets;
+
+ ets = gtk_type_new (e_tree_sorted_get_type ());
+
+ e_tree_sorted_construct(ets, source, full_header, sort_info);
+
+ return ets;
+}
+
+ETreePath
+e_tree_sorted_view_to_model_path (ETreeSorted *ets,
+ ETreePath view_path)
+{
+ ETreeSortedPath *path = view_path;
+ if (path)
+ return path->corresponding;
+ else
+ return NULL;
+}
+
+ETreePath
+e_tree_sorted_model_to_view_path (ETreeSorted *ets,
+ ETreePath model_path)
+{
+ ETreeSortedPath *path = find_or_create_path(ets, model_path);
+
+ return path;
+}
+
diff --git a/widgets/table/e-tree-sorted.h b/widgets/table/e-tree-sorted.h
new file mode 100644
index 0000000000..87094e89a6
--- /dev/null
+++ b/widgets/table/e-tree-sorted.h
@@ -0,0 +1,53 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+#ifndef _E_TREE_SORTED_H_
+#define _E_TREE_SORTED_H_
+
+#include <gdk-pixbuf/gdk-pixbuf.h>
+#include <gal/e-table/e-tree-model.h>
+#include <gal/e-table/e-table-sort-info.h>
+#include <gal/e-table/e-table-header.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+
+#define E_TREE_SORTED_TYPE (e_tree_sorted_get_type ())
+#define E_TREE_SORTED(o) (GTK_CHECK_CAST ((o), E_TREE_SORTED_TYPE, ETreeSorted))
+#define E_TREE_SORTED_CLASS(k) (GTK_CHECK_CLASS_CAST((k), E_TREE_SORTED_TYPE, ETreeSortedClass))
+#define E_IS_TREE_SORTED(o) (GTK_CHECK_TYPE ((o), E_TREE_SORTED_TYPE))
+#define E_IS_TREE_SORTED_CLASS(k) (GTK_CHECK_CLASS_TYPE ((k), E_TREE_SORTED_TYPE))
+
+typedef struct ETreeSorted ETreeSorted;
+typedef struct ETreeSortedPriv ETreeSortedPriv;
+typedef struct ETreeSortedClass ETreeSortedClass;
+
+struct ETreeSorted {
+ ETreeModel base;
+
+ ETreeSortedPriv *priv;
+};
+
+struct ETreeSortedClass {
+ ETreeModelClass parent_class;
+};
+
+GtkType e_tree_sorted_get_type (void);
+void e_tree_sorted_construct (ETreeSorted *etree,
+ ETreeModel *source,
+ ETableHeader *full_header,
+ ETableSortInfo *sort_info);
+ETreeSorted *e_tree_sorted_new (ETreeModel *source,
+ ETableHeader *full_header,
+ ETableSortInfo *sort_info);
+
+ETreePath e_tree_sorted_view_to_model_path (ETreeSorted *ets,
+ ETreePath view_path);
+ETreePath e_tree_sorted_model_to_view_path (ETreeSorted *ets,
+ ETreePath model_path);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* _E_TREE_SORTED_H */
diff --git a/widgets/table/e-tree-table-adapter.c b/widgets/table/e-tree-table-adapter.c
new file mode 100644
index 0000000000..481deb8eb5
--- /dev/null
+++ b/widgets/table/e-tree-table-adapter.c
@@ -0,0 +1,974 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * e-tree-table-adapter.c: Implements a table that contains a subset of another table.
+ *
+ * Author:
+ * Chris Lahey <clahey@ximian.com>
+ * Chris Toshok <toshok@ximian.com>
+ *
+ * (C) 2000, 2001 Ximian, Inc.
+ */
+#include <config.h>
+#include <stdlib.h>
+#include <string.h>
+#include <gtk/gtksignal.h>
+#include "gal/util/e-util.h"
+#include "gal/util/e-xml-utils.h"
+#include "e-tree-table-adapter.h"
+#include "gnome-xml/tree.h"
+#include "gnome-xml/parser.h"
+
+#define PARENT_TYPE E_TABLE_MODEL_TYPE
+#define d(x)
+
+#define INCREMENT_AMOUNT 100
+
+static ETableModelClass *parent_class;
+
+
+struct ETreeTableAdapterPriv {
+ ETreeModel *source;
+ int n_map;
+ int n_vals_allocated;
+ ETreePath *map_table;
+ GHashTable *attributes;
+
+ guint root_visible : 1;
+
+ int last_access;
+
+ int tree_model_pre_change_id;
+ int tree_model_node_changed_id;
+ int tree_model_node_data_changed_id;
+ int tree_model_node_col_changed_id;
+ int tree_model_node_inserted_id;
+ int tree_model_node_removed_id;
+};
+
+typedef struct ETreeTableAdapterNode {
+ guint expanded : 1;
+ guint expandable : 1;
+ guint expandable_set : 1;
+
+ /* parent/child/sibling pointers */
+ guint32 num_visible_children;
+} ETreeTableAdapterNode;
+
+static ETreeTableAdapterNode *
+find_node(ETreeTableAdapter *adapter, ETreePath path)
+{
+ ETreeTableAdapterNode *node;
+
+ if (path == NULL)
+ return NULL;
+
+ if (e_tree_model_has_save_id(adapter->priv->source)) {
+ char *save_id;
+ save_id = e_tree_model_get_save_id(adapter->priv->source, path);
+ node = g_hash_table_lookup(adapter->priv->attributes, save_id);
+ g_free(save_id);
+ } else {
+ node = g_hash_table_lookup(adapter->priv->attributes, path);
+ }
+ if (node && !node->expandable_set) {
+ node->expandable = e_tree_model_node_is_expandable(adapter->priv->source, path);
+ node->expandable_set = 1;
+ }
+
+ return node;
+}
+
+static ETreeTableAdapterNode *
+find_or_create_node(ETreeTableAdapter *etta, ETreePath path)
+{
+ ETreeTableAdapterNode *node;
+
+ node = find_node(etta, path);
+
+ if (!node) {
+ node = g_new(ETreeTableAdapterNode, 1);
+ if (e_tree_model_node_is_root(etta->priv->source, path))
+ node->expanded = TRUE;
+ else
+ node->expanded = e_tree_model_get_expanded_default(etta->priv->source);
+ node->expandable = e_tree_model_node_is_expandable(etta->priv->source, path);
+ node->expandable_set = 1;
+ node->num_visible_children = 0;
+
+ if (e_tree_model_has_save_id(etta->priv->source)) {
+ char *save_id;
+ save_id = e_tree_model_get_save_id(etta->priv->source, path);
+ g_hash_table_insert(etta->priv->attributes, save_id, node);
+ } else {
+ g_hash_table_insert(etta->priv->attributes, path, node);
+ }
+ }
+
+ return node;
+}
+
+static void
+add_expanded_node(ETreeTableAdapter *etta, char *save_id, gboolean expanded)
+{
+ ETreeTableAdapterNode *node;
+
+ node = g_hash_table_lookup(etta->priv->attributes, save_id);
+
+ if (node) {
+ node->expandable_set = 0;
+ node->expanded = expanded;
+ return;
+ }
+
+ node = g_new(ETreeTableAdapterNode, 1);
+
+ node->expanded = expanded;
+ node->expandable = 0;
+ node->expandable_set = 0;
+ node->num_visible_children = 0;
+
+ g_hash_table_insert(etta->priv->attributes, save_id, node);
+}
+
+static void
+etta_expand_to(ETreeTableAdapter *etta, int size)
+{
+ if (size > etta->priv->n_vals_allocated) {
+ etta->priv->n_vals_allocated = MAX(etta->priv->n_vals_allocated + INCREMENT_AMOUNT, size);
+ etta->priv->map_table = g_renew (ETreePath, etta->priv->map_table, etta->priv->n_vals_allocated);
+ }
+
+}
+
+static void
+etta_update_parent_child_counts(ETreeTableAdapter *etta, ETreePath path, int change)
+{
+ for (path = e_tree_model_node_get_parent(etta->priv->source, path);
+ path;
+ path = e_tree_model_node_get_parent(etta->priv->source, path)) {
+ ETreeTableAdapterNode *node = find_or_create_node(etta, path);
+ node->num_visible_children += change;
+ }
+ etta->priv->n_map += change;
+}
+
+static int
+find_next_node(ETreeTableAdapter *adapter, int row)
+{
+ ETreePath path = adapter->priv->map_table[row];
+ if (path) {
+ ETreePath next_sibling = e_tree_model_node_get_next(adapter->priv->source, path);
+ ETreeTableAdapterNode *current = find_node (adapter, path);
+ if (next_sibling)
+ return row + (current ? current->num_visible_children : 0) + 1;
+ else
+ return -1;
+ } else
+ return -1;
+}
+
+static int
+find_first_child_node(ETreeTableAdapter *adapter, int row)
+{
+ if (row != -1) {
+ ETreePath path = adapter->priv->map_table[row];
+ ETreePath first_child = e_tree_model_node_get_first_child(adapter->priv->source, path);
+ ETreeTableAdapterNode *current = find_node (adapter, path);
+ if (first_child && current && current->expanded)
+ return row + 1;
+ else
+ return -1;
+ } else
+ return 0;
+}
+
+static int
+find_child_row_num(ETreeTableAdapter *etta, int row, ETreePath path)
+{
+ row = find_first_child_node(etta, row);
+
+ while (row != -1 && path != etta->priv->map_table[row]) {
+ row = find_next_node(etta, row);
+ }
+
+ return row;
+}
+
+static int
+find_row_num(ETreeTableAdapter *etta, ETreePath path)
+{
+ int depth;
+ ETreePath *sequence;
+ int i;
+ int row;
+
+ if (etta->priv->map_table == NULL)
+ return -1;
+
+ if (path == NULL)
+ return -1;
+
+ if (etta->priv->last_access != -1 && etta->priv->map_table[etta->priv->last_access] == path)
+ return etta->priv->last_access;
+
+ depth = e_tree_model_node_depth(etta->priv->source, path);
+
+ sequence = g_new(ETreePath, depth + 1);
+
+ sequence[0] = path;
+
+ for (i = 0; i < depth; i++) {
+ ETreeTableAdapterNode *node;
+
+ sequence[i + 1] = e_tree_model_node_get_parent(etta->priv->source, sequence[i]);
+
+ node = find_node(etta, sequence[i + 1]);
+ if (! ((node && node->expanded) || e_tree_model_get_expanded_default(etta->priv->source))) {
+ g_free(sequence);
+ return -1;
+ }
+ }
+
+ row = 0;
+
+ for (i = depth; i >= 0; i --) {
+ while (row != -1 && sequence[i] != etta->priv->map_table[row]) {
+ row = find_next_node(etta, row);
+ }
+ if (row == -1)
+ break;
+ if (i == 0)
+ break;
+ row = find_first_child_node(etta, row);
+ }
+ g_free (sequence);
+
+ etta->priv->last_access = row;
+ return row;
+}
+
+static int
+array_size_from_path(ETreeTableAdapter *etta, ETreePath path)
+{
+ int size = 1;
+
+ ETreeTableAdapterNode *node = NULL;
+
+ if (e_tree_model_node_is_expandable(etta->priv->source, path))
+ node = find_or_create_node(etta, path);
+
+ if (node && node->expanded) {
+ ETreePath children;
+
+ for (children = e_tree_model_node_get_first_child(etta->priv->source, path);
+ children;
+ children = e_tree_model_node_get_next(etta->priv->source, children)) {
+ size += array_size_from_path(etta, children);
+ }
+ }
+
+ return size;
+}
+
+static int
+fill_array_from_path(ETreeTableAdapter *etta, ETreePath *array, ETreePath path)
+{
+ ETreeTableAdapterNode *node = NULL;
+ int index = 0;
+
+ array[index] = path;
+
+ index ++;
+
+ if (e_tree_model_node_is_expandable(etta->priv->source, path))
+ node = find_or_create_node(etta, path);
+ else
+ node = find_node(etta, path);
+
+ if (node && node->expanded) {
+ ETreePath children;
+
+ for (children = e_tree_model_node_get_first_child(etta->priv->source, path);
+ children;
+ children = e_tree_model_node_get_next(etta->priv->source, children)) {
+ index += fill_array_from_path(etta, array + index, children);
+ }
+ }
+
+ if (node)
+ node->num_visible_children = index - 1;
+
+ return index;
+}
+
+static void
+free_string (gpointer key, gpointer value, gpointer data)
+{
+ g_free(key);
+}
+
+static void
+etta_destroy (GtkObject *object)
+{
+ ETreeTableAdapter *etta = E_TREE_TABLE_ADAPTER (object);
+
+ if (etta->priv->source && e_tree_model_has_save_id(etta->priv->source)) {
+ g_hash_table_foreach(etta->priv->attributes, free_string, NULL);
+ }
+ g_hash_table_destroy (etta->priv->attributes);
+
+ if (etta->priv->source) {
+ gtk_signal_disconnect (GTK_OBJECT (etta->priv->source),
+ etta->priv->tree_model_pre_change_id);
+ gtk_signal_disconnect (GTK_OBJECT (etta->priv->source),
+ etta->priv->tree_model_node_changed_id);
+ gtk_signal_disconnect (GTK_OBJECT (etta->priv->source),
+ etta->priv->tree_model_node_data_changed_id);
+ gtk_signal_disconnect (GTK_OBJECT (etta->priv->source),
+ etta->priv->tree_model_node_col_changed_id);
+ gtk_signal_disconnect (GTK_OBJECT (etta->priv->source),
+ etta->priv->tree_model_node_inserted_id);
+ gtk_signal_disconnect (GTK_OBJECT (etta->priv->source),
+ etta->priv->tree_model_node_removed_id);
+
+ gtk_object_unref (GTK_OBJECT (etta->priv->source));
+ etta->priv->source = NULL;
+
+ etta->priv->tree_model_pre_change_id = 0;
+ etta->priv->tree_model_node_changed_id = 0;
+ etta->priv->tree_model_node_data_changed_id = 0;
+ etta->priv->tree_model_node_col_changed_id = 0;
+ etta->priv->tree_model_node_inserted_id = 0;
+ etta->priv->tree_model_node_removed_id = 0;
+ }
+
+ g_free (etta->priv->map_table);
+
+ g_free (etta->priv);
+
+ GTK_OBJECT_CLASS (parent_class)->destroy (object);
+}
+
+static int
+etta_column_count (ETableModel *etm)
+{
+ ETreeTableAdapter *etta = (ETreeTableAdapter *)etm;
+
+ return e_tree_model_column_count (etta->priv->source);
+}
+
+static gboolean
+etta_has_save_id (ETableModel *etm)
+{
+ ETreeTableAdapter *etta = (ETreeTableAdapter *)etm;
+
+ return e_tree_model_has_save_id (etta->priv->source);
+}
+
+static gchar *
+etta_get_save_id (ETableModel *etm, int row)
+{
+ ETreeTableAdapter *etta = (ETreeTableAdapter *)etm;
+
+ if (etta->priv->root_visible)
+ return e_tree_model_get_save_id (etta->priv->source, etta->priv->map_table [row]);
+ else
+ return e_tree_model_get_save_id (etta->priv->source, etta->priv->map_table [row + 1]);
+}
+
+static int
+etta_row_count (ETableModel *etm)
+{
+ ETreeTableAdapter *etta = (ETreeTableAdapter *)etm;
+
+ if (etta->priv->root_visible)
+ return etta->priv->n_map;
+ else {
+ if (etta->priv->n_map > 0)
+ return etta->priv->n_map - 1;
+ else
+ return 0;
+ }
+}
+
+static void *
+etta_value_at (ETableModel *etm, int col, int row)
+{
+ ETreeTableAdapter *etta = (ETreeTableAdapter *)etm;
+
+ etta->priv->last_access = row;
+ d(g_print("g) Setting last_access to %d\n", row));
+
+ switch (col) {
+ case -1:
+ if (etta->priv->root_visible)
+ return etta->priv->map_table [row];
+ else
+ return etta->priv->map_table [row + 1];
+ case -2:
+ return etta->priv->source;
+ case -3:
+ return etta;
+ default:
+ if (etta->priv->root_visible)
+ return e_tree_model_value_at (etta->priv->source, etta->priv->map_table [row], col);
+ else
+ return e_tree_model_value_at (etta->priv->source, etta->priv->map_table [row + 1], col);
+ }
+}
+
+static void
+etta_set_value_at (ETableModel *etm, int col, int row, const void *val)
+{
+ ETreeTableAdapter *etta = (ETreeTableAdapter *)etm;
+
+ etta->priv->last_access = row;
+ d(g_print("h) Setting last_access to %d\n", row));
+ if (etta->priv->root_visible)
+ e_tree_model_set_value_at (etta->priv->source, etta->priv->map_table [row], col, val);
+ else
+ e_tree_model_set_value_at (etta->priv->source, etta->priv->map_table [row + 1], col, val);
+}
+
+static gboolean
+etta_is_cell_editable (ETableModel *etm, int col, int row)
+{
+ ETreeTableAdapter *etta = (ETreeTableAdapter *)etm;
+
+ if (etta->priv->root_visible)
+ return e_tree_model_node_is_editable (etta->priv->source, etta->priv->map_table [row], col);
+ else
+ return e_tree_model_node_is_editable (etta->priv->source, etta->priv->map_table [row + 1], col);
+}
+
+static void
+etta_append_row (ETableModel *etm, ETableModel *source, int row)
+{
+#if 0
+ ETreeTableAdapter *etta = (ETreeTableAdapter *)etm;
+ e_table_model_append_row (etta->priv->source, source, row);
+#endif
+}
+
+static void *
+etta_duplicate_value (ETableModel *etm, int col, const void *value)
+{
+ ETreeTableAdapter *etta = (ETreeTableAdapter *)etm;
+
+ return e_tree_model_duplicate_value (etta->priv->source, col, value);
+}
+
+static void
+etta_free_value (ETableModel *etm, int col, void *value)
+{
+ ETreeTableAdapter *etta = (ETreeTableAdapter *)etm;
+
+ e_tree_model_free_value (etta->priv->source, col, value);
+}
+
+static void *
+etta_initialize_value (ETableModel *etm, int col)
+{
+ ETreeTableAdapter *etta = (ETreeTableAdapter *)etm;
+
+ return e_tree_model_initialize_value (etta->priv->source, col);
+}
+
+static gboolean
+etta_value_is_empty (ETableModel *etm, int col, const void *value)
+{
+ ETreeTableAdapter *etta = (ETreeTableAdapter *)etm;
+
+ return e_tree_model_value_is_empty (etta->priv->source, col, value);
+}
+
+static char *
+etta_value_to_string (ETableModel *etm, int col, const void *value)
+{
+ ETreeTableAdapter *etta = (ETreeTableAdapter *)etm;
+
+ return e_tree_model_value_to_string (etta->priv->source, col, value);
+}
+
+static void
+etta_class_init (ETreeTableAdapterClass *klass)
+{
+ ETableModelClass *table_class = (ETableModelClass *) klass;
+ GtkObjectClass *object_class = (GtkObjectClass *) klass;
+
+ parent_class = gtk_type_class (PARENT_TYPE);
+
+ object_class->destroy = etta_destroy;
+
+ table_class->column_count = etta_column_count;
+ table_class->has_save_id = etta_has_save_id;
+ table_class->get_save_id = etta_get_save_id;
+ table_class->row_count = etta_row_count;
+ table_class->value_at = etta_value_at;
+ table_class->set_value_at = etta_set_value_at;
+ table_class->is_cell_editable = etta_is_cell_editable;
+ table_class->append_row = etta_append_row;
+ table_class->duplicate_value = etta_duplicate_value;
+ table_class->free_value = etta_free_value;
+ table_class->initialize_value = etta_initialize_value;
+ table_class->value_is_empty = etta_value_is_empty;
+ table_class->value_to_string = etta_value_to_string;
+}
+
+static void
+etta_init (ETreeTableAdapter *etta)
+{
+ etta->priv = g_new(ETreeTableAdapterPriv, 1);
+
+ etta->priv->last_access = 0;
+ etta->priv->map_table = NULL;
+ etta->priv->n_map = 0;
+ etta->priv->n_vals_allocated = 0;
+
+ etta->priv->root_visible = TRUE;
+
+ etta->priv->attributes = NULL;
+}
+
+E_MAKE_TYPE(e_tree_table_adapter, "ETreeTableAdapter", ETreeTableAdapter, etta_class_init, etta_init, PARENT_TYPE);
+
+static void
+etta_proxy_pre_change (ETreeModel *etm, ETreeTableAdapter *etta)
+{
+ e_table_model_pre_change(E_TABLE_MODEL(etta));
+}
+
+static void
+etta_proxy_node_changed (ETreeModel *etm, ETreePath path, ETreeTableAdapter *etta)
+{
+ if (e_tree_model_node_is_root(etm, path)) {
+ int size;
+
+ size = array_size_from_path(etta, path);
+ etta_expand_to(etta, size);
+ etta->priv->n_map = size;
+ fill_array_from_path(etta, etta->priv->map_table, path);
+ } else {
+ int row = find_row_num(etta, path);
+ int size;
+ int old_size;
+
+ if (row == -1)
+ return;
+
+ size = array_size_from_path(etta, path);
+ if (e_tree_model_node_is_expandable(etta->priv->source, path)) {
+ ETreeTableAdapterNode *node = find_or_create_node(etta, path);
+ old_size = node->num_visible_children + 1;
+ } else {
+ ETreeTableAdapterNode *node = find_node(etta, path);
+ if (node)
+ old_size = node->num_visible_children + 1;
+ else
+ old_size = 1;
+ }
+
+ etta_expand_to(etta, etta->priv->n_map + size - old_size);
+
+ memmove(etta->priv->map_table + row + size,
+ etta->priv->map_table + row + old_size,
+ (etta->priv->n_map - row - old_size) * sizeof (ETreePath));
+ fill_array_from_path(etta, etta->priv->map_table + row, path);
+ etta_update_parent_child_counts(etta, path, size - old_size);
+ }
+
+ e_table_model_changed(E_TABLE_MODEL(etta));
+}
+
+static void
+etta_proxy_node_data_changed (ETreeModel *etm, ETreePath path, ETreeTableAdapter *etta)
+{
+ int row = find_row_num(etta, path);
+ if (row != -1) {
+ if (etta->priv->root_visible)
+ e_table_model_row_changed(E_TABLE_MODEL(etta), row);
+ else if (row != 0)
+ e_table_model_row_changed(E_TABLE_MODEL(etta), row - 1);
+ }
+}
+
+static void
+etta_proxy_node_col_changed (ETreeModel *etm, ETreePath path, int col, ETreeTableAdapter *etta)
+{
+ int row = find_row_num(etta, path);
+ if (row != -1) {
+ if (etta->priv->root_visible)
+ e_table_model_cell_changed(E_TABLE_MODEL(etta), col, row);
+ else if (row != 0)
+ e_table_model_cell_changed(E_TABLE_MODEL(etta), col, row - 1);
+ }
+}
+
+static void
+etta_proxy_node_inserted (ETreeModel *etm, ETreePath parent, ETreePath child, ETreeTableAdapter *etta)
+{
+ int row;
+
+ if (e_tree_model_node_is_root(etm, child)) {
+ row = 0;
+ } else {
+ ETreePath children;
+ int parent_row;
+ ETreeTableAdapterNode *parent_node;
+
+ parent_row = find_row_num(etta, parent);
+ if (parent_row == -1)
+ return;
+
+ parent_node = find_or_create_node(etta, parent);
+ if (parent_node->expandable != e_tree_model_node_is_expandable(etta->priv->source, parent)) {
+ parent_node->expandable = e_tree_model_node_is_expandable(etta->priv->source, parent);
+ if (etta->priv->root_visible)
+ e_table_model_row_changed(E_TABLE_MODEL(etta), parent_row);
+ else if (parent_row != 0)
+ e_table_model_row_changed(E_TABLE_MODEL(etta), parent_row - 1);
+ }
+ if (!parent_node->expanded)
+ return;
+
+ row = find_first_child_node(etta, parent_row);
+ children = e_tree_model_node_get_first_child(etta->priv->source, parent);
+
+ while (row != -1 && children != NULL && children == etta->priv->map_table[row]) {
+ children = e_tree_model_node_get_next(etta->priv->source, children);
+ row = find_next_node(etta, row);
+ }
+ }
+
+ if (row != -1) {
+ int size;
+
+ size = array_size_from_path(etta, child);
+
+ etta_expand_to(etta, etta->priv->n_map + size);
+
+ memmove(etta->priv->map_table + row + size,
+ etta->priv->map_table + row,
+ (etta->priv->n_map - row) * sizeof (ETreePath));
+
+ fill_array_from_path(etta, etta->priv->map_table + row, child);
+ etta_update_parent_child_counts(etta, child, size);
+
+ if (etta->priv->root_visible)
+ e_table_model_rows_inserted(E_TABLE_MODEL(etta), row, size);
+ else if (row != 0)
+ e_table_model_rows_inserted(E_TABLE_MODEL(etta), row - 1, size);
+ else
+ e_table_model_rows_inserted(E_TABLE_MODEL(etta), 0, size - 1);
+ }
+}
+
+static void
+etta_proxy_node_removed (ETableModel *etm, ETreePath parent, ETreePath child, ETreeTableAdapter *etta)
+{
+ int parent_row = find_row_num(etta, parent);
+ int row = find_child_row_num(etta, parent_row, child);
+ ETreeTableAdapterNode *parent_node = find_node(etta, parent);
+ if (parent_row != -1 && parent_node) {
+ if (parent_node->expandable != e_tree_model_node_is_expandable(etta->priv->source, parent)) {
+ parent_node->expandable = e_tree_model_node_is_expandable(etta->priv->source, parent);
+ if (etta->priv->root_visible)
+ e_table_model_row_changed(E_TABLE_MODEL(etta), parent_row);
+ else if (parent_row != 0)
+ e_table_model_row_changed(E_TABLE_MODEL(etta), parent_row - 1);
+ }
+ }
+ if (row != -1) {
+ ETreeTableAdapterNode *node = find_node(etta, child);
+ int to_remove = (node ? node->num_visible_children : 0) + 1;
+
+ memmove(etta->priv->map_table + row,
+ etta->priv->map_table + row + to_remove,
+ (etta->priv->n_map - row - to_remove) * sizeof (ETreePath));
+
+ if (parent_node)
+ parent_node->num_visible_children -= to_remove;
+ if (parent)
+ etta_update_parent_child_counts(etta, parent, - to_remove);
+
+ if (etta->priv->root_visible)
+ e_table_model_rows_deleted(E_TABLE_MODEL(etta), row, to_remove);
+ else if (row != 0)
+ e_table_model_rows_deleted(E_TABLE_MODEL(etta), row - 1, to_remove);
+ else
+ e_table_model_rows_deleted(E_TABLE_MODEL(etta), 0, to_remove - 1);
+ }
+}
+
+ETableModel *
+e_tree_table_adapter_construct (ETreeTableAdapter *etta, ETreeModel *source)
+{
+ ETreePath root;
+
+ etta->priv->source = source;
+ gtk_object_ref (GTK_OBJECT (source));
+
+ if (e_tree_model_has_save_id(source))
+ etta->priv->attributes = g_hash_table_new(g_str_hash, g_str_equal);
+ else
+ etta->priv->attributes = g_hash_table_new(NULL, NULL);
+
+ root = e_tree_model_get_root (source);
+
+ if (root) {
+ etta->priv->n_map = array_size_from_path(etta, root);
+ etta->priv->n_vals_allocated = etta->priv->n_map;
+ etta->priv->map_table = g_new(ETreePath, etta->priv->n_map);
+ fill_array_from_path(etta, etta->priv->map_table, root);
+ }
+
+ etta->priv->tree_model_pre_change_id = gtk_signal_connect (GTK_OBJECT (source), "pre_change",
+ GTK_SIGNAL_FUNC (etta_proxy_pre_change), etta);
+ etta->priv->tree_model_node_changed_id = gtk_signal_connect (GTK_OBJECT (source), "node_changed",
+ GTK_SIGNAL_FUNC (etta_proxy_node_changed), etta);
+ etta->priv->tree_model_node_data_changed_id = gtk_signal_connect (GTK_OBJECT (source), "node_data_changed",
+ GTK_SIGNAL_FUNC (etta_proxy_node_data_changed), etta);
+ etta->priv->tree_model_node_col_changed_id = gtk_signal_connect (GTK_OBJECT (source), "node_col_changed",
+ GTK_SIGNAL_FUNC (etta_proxy_node_col_changed), etta);
+ etta->priv->tree_model_node_inserted_id = gtk_signal_connect (GTK_OBJECT (source), "node_inserted",
+ GTK_SIGNAL_FUNC (etta_proxy_node_inserted), etta);
+ etta->priv->tree_model_node_removed_id = gtk_signal_connect (GTK_OBJECT (source), "node_removed",
+ GTK_SIGNAL_FUNC (etta_proxy_node_removed), etta);
+
+ return E_TABLE_MODEL (etta);
+}
+
+ETableModel *
+e_tree_table_adapter_new (ETreeModel *source)
+{
+ ETreeTableAdapter *etta = gtk_type_new (E_TREE_TABLE_ADAPTER_TYPE);
+
+ e_tree_table_adapter_construct (etta, source);
+
+ return (ETableModel *) etta;
+}
+
+typedef struct {
+ xmlNode *root;
+ ETreeModel *tree;
+} TreeAndRoot;
+
+static void
+save_expanded_state_func (gpointer keyp, gpointer value, gpointer data)
+{
+ gchar *key = keyp;
+ ETreeTableAdapterNode *node = value;
+ TreeAndRoot *tar = data;
+ xmlNode *root = tar->root;
+ ETreeModel *etm = tar->tree;
+ xmlNode *xmlnode;
+
+ if (node->expanded != e_tree_model_get_expanded_default(etm)) {
+ xmlnode = xmlNewChild (root, NULL, "node", NULL);
+ e_xml_set_string_prop_by_name(xmlnode, "id", key);
+ }
+}
+
+void
+e_tree_table_adapter_save_expanded_state (ETreeTableAdapter *etta, const char *filename)
+{
+ xmlDoc *doc;
+ xmlNode *root;
+ ETreeTableAdapterPriv *priv;
+ TreeAndRoot tar;
+
+ g_return_if_fail(etta != NULL);
+
+ priv = etta->priv;
+
+ doc = xmlNewDoc ((xmlChar*) "1.0");
+ root = xmlNewDocNode (doc, NULL,
+ (xmlChar *) "expanded_state",
+ NULL);
+ xmlDocSetRootElement (doc, root);
+
+ e_xml_set_integer_prop_by_name(root, "vers", 1);
+
+ tar.root = root;
+ tar.tree = etta->priv->source;
+
+ g_hash_table_foreach (priv->attributes,
+ save_expanded_state_func,
+ &tar);
+
+ xmlSaveFile (filename, doc);
+
+ xmlFreeDoc (doc);
+}
+
+void
+e_tree_table_adapter_load_expanded_state (ETreeTableAdapter *etta, const char *filename)
+{
+ ETreeTableAdapterPriv *priv;
+ xmlDoc *doc;
+ xmlNode *root;
+ xmlNode *child;
+ int vers;
+
+ g_return_if_fail(etta != NULL);
+
+ priv = etta->priv;
+
+ doc = xmlParseFile (filename);
+ if (!doc)
+ return;
+
+ root = xmlDocGetRootElement (doc);
+ if (root == NULL || strcmp (root->name, "expanded_state")) {
+ xmlFreeDoc (doc);
+ return;
+ }
+
+ vers = e_xml_get_integer_prop_by_name_with_default(root, "vers", 0);
+ if (vers != 1) {
+ xmlFreeDoc (doc);
+ return;
+ }
+
+ for (child = root->childs; child; child = child->next) {
+ char *id;
+
+ if (strcmp (child->name, "node")) {
+ d(g_warning ("unknown node '%s' in %s", child->name, filename));
+ continue;
+ }
+
+ id = e_xml_get_string_prop_by_name_with_default (child, "id", "");
+
+ if (!strcmp(id, "")) {
+ g_free(id);
+ return;
+ }
+
+ add_expanded_node(etta, id, !e_tree_model_get_expanded_default(etta->priv->source));
+ }
+
+ xmlFreeDoc (doc);
+}
+
+void e_tree_table_adapter_root_node_set_visible (ETreeTableAdapter *etta, gboolean visible)
+{
+ if (etta->priv->root_visible == visible)
+ return;
+
+ e_table_model_pre_change (E_TABLE_MODEL(etta));
+
+ etta->priv->root_visible = visible;
+ if (!visible) {
+ ETreePath root = e_tree_model_get_root(etta->priv->source);
+ if (root)
+ e_tree_table_adapter_node_set_expanded(etta, root, TRUE);
+ }
+ e_table_model_changed(E_TABLE_MODEL(etta));
+}
+
+void e_tree_table_adapter_node_set_expanded (ETreeTableAdapter *etta, ETreePath path, gboolean expanded)
+{
+ ETreeTableAdapterNode *node;
+ int row;
+
+ node = find_or_create_node(etta, path);
+ row = find_row_num(etta, path);
+
+ if (expanded != node->expanded) {
+ e_table_model_pre_change (E_TABLE_MODEL(etta));
+
+ node->expanded = expanded;
+
+ if (row != -1) {
+ if (etta->priv->root_visible)
+ e_table_model_row_changed(E_TABLE_MODEL(etta), row);
+ else if (row != 0)
+ e_table_model_row_changed(E_TABLE_MODEL(etta), row - 1);
+
+ if (expanded) {
+ int num_children = array_size_from_path(etta, path) - 1;
+ etta_expand_to(etta, etta->priv->n_map + num_children);
+ memmove(etta->priv->map_table + row + 1 + num_children,
+ etta->priv->map_table + row + 1,
+ (etta->priv->n_map - row - 1) * sizeof (ETreePath));
+ fill_array_from_path(etta, etta->priv->map_table + row, path);
+ etta_update_parent_child_counts(etta, path, num_children);
+ if (num_children != 0) {
+ if (etta->priv->root_visible)
+ e_table_model_rows_inserted(E_TABLE_MODEL(etta), row + 1, num_children);
+ else
+ e_table_model_rows_inserted(E_TABLE_MODEL(etta), row, num_children);
+ }
+ } else {
+ int num_children = node->num_visible_children;
+ memmove(etta->priv->map_table + row + 1,
+ etta->priv->map_table + row + 1 + num_children,
+ (etta->priv->n_map - row - 1 - num_children) * sizeof (ETreePath));
+ node->num_visible_children = 0;
+ etta_update_parent_child_counts(etta, path, - num_children);
+ if (num_children != 0) {
+ if (etta->priv->root_visible)
+ e_table_model_rows_deleted(E_TABLE_MODEL(etta), row + 1, num_children);
+ else
+ e_table_model_rows_deleted(E_TABLE_MODEL(etta), row, num_children);
+ }
+ }
+ }
+ }
+}
+
+void e_tree_table_adapter_node_set_expanded_recurse (ETreeTableAdapter *etta, ETreePath path, gboolean expanded)
+{
+ ETreePath children;
+
+ e_tree_table_adapter_node_set_expanded(etta, path, expanded);
+
+ for (children = e_tree_model_node_get_first_child(etta->priv->source, path);
+ children;
+ children = e_tree_model_node_get_next(etta->priv->source, children)) {
+ e_tree_table_adapter_node_set_expanded_recurse(etta, children, expanded);
+ }
+}
+
+ETreePath e_tree_table_adapter_node_at_row (ETreeTableAdapter *etta, int row)
+{
+ if (etta->priv->root_visible)
+ return etta->priv->map_table[row];
+ else
+ return etta->priv->map_table[row + 1];
+}
+
+int e_tree_table_adapter_row_of_node (ETreeTableAdapter *etta, ETreePath path)
+{
+ if (etta->priv->root_visible)
+ return find_row_num(etta, path);
+ else
+ return find_row_num(etta, path) - 1;
+}
+
+gboolean e_tree_table_adapter_root_node_is_visible(ETreeTableAdapter *etta)
+{
+ return etta->priv->root_visible;
+}
+
+void e_tree_table_adapter_show_node (ETreeTableAdapter *etta, ETreePath path)
+{
+ ETreePath parent;
+
+ parent = e_tree_model_node_get_parent(etta->priv->source, path);
+
+ if (parent) {
+ e_tree_table_adapter_node_set_expanded(etta, parent, TRUE);
+ e_tree_table_adapter_show_node(etta, parent);
+ }
+}
+
+gboolean e_tree_table_adapter_node_is_expanded (ETreeTableAdapter *etta, ETreePath path)
+{
+ if (e_tree_model_node_is_expandable(etta->priv->source, path)) {
+ ETreeTableAdapterNode *node = find_or_create_node(etta, path);
+ return node->expanded;
+ } else
+ return FALSE;
+}
diff --git a/widgets/table/e-tree-table-adapter.h b/widgets/table/e-tree-table-adapter.h
new file mode 100644
index 0000000000..34e38c1fb7
--- /dev/null
+++ b/widgets/table/e-tree-table-adapter.h
@@ -0,0 +1,64 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+#ifndef _E_TREE_TABLE_ADAPTER_H_
+#define _E_TREE_TABLE_ADAPTER_H_
+
+#include <gtk/gtkobject.h>
+#include <gal/e-table/e-table-model.h>
+#include <gal/e-table/e-tree-model.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+#define E_TREE_TABLE_ADAPTER_TYPE (e_tree_table_adapter_get_type ())
+#define E_TREE_TABLE_ADAPTER(o) (GTK_CHECK_CAST ((o), E_TREE_TABLE_ADAPTER_TYPE, ETreeTableAdapter))
+#define E_TREE_TABLE_ADAPTER_CLASS(k) (GTK_CHECK_CLASS_CAST((k), E_TREE_TABLE_ADAPTER_TYPE, ETreeTableAdapterClass))
+#define E_IS_TREE_TABLE_ADAPTER(o) (GTK_CHECK_TYPE ((o), E_TREE_TABLE_ADAPTER_TYPE))
+#define E_IS_TREE_TABLE_ADAPTER_CLASS(k) (GTK_CHECK_CLASS_TYPE ((k), E_TREE_TABLE_ADAPTER_TYPE))
+
+typedef struct ETreeTableAdapterPriv ETreeTableAdapterPriv;
+
+typedef struct {
+ ETableModel base;
+
+ ETreeTableAdapterPriv *priv;
+} ETreeTableAdapter;
+
+typedef struct {
+ ETableModelClass parent_class;
+} ETreeTableAdapterClass;
+
+GtkType e_tree_table_adapter_get_type (void);
+ETableModel *e_tree_table_adapter_new (ETreeModel *source);
+ETableModel *e_tree_table_adapter_construct (ETreeTableAdapter *ets,
+ ETreeModel *source);
+
+gboolean e_tree_table_adapter_node_is_expanded (ETreeTableAdapter *etta,
+ ETreePath path);
+void e_tree_table_adapter_node_set_expanded (ETreeTableAdapter *etta,
+ ETreePath path,
+ gboolean expanded);
+void e_tree_table_adapter_node_set_expanded_recurse (ETreeTableAdapter *etta,
+ ETreePath path,
+ gboolean expanded);
+void e_tree_table_adapter_root_node_set_visible (ETreeTableAdapter *etta,
+ gboolean visible);
+ETreePath e_tree_table_adapter_node_at_row (ETreeTableAdapter *etta,
+ int row);
+int e_tree_table_adapter_row_of_node (ETreeTableAdapter *etta,
+ ETreePath path);
+gboolean e_tree_table_adapter_root_node_is_visible (ETreeTableAdapter *etta);
+
+void e_tree_table_adapter_show_node (ETreeTableAdapter *etta,
+ ETreePath path);
+
+void e_tree_table_adapter_save_expanded_state (ETreeTableAdapter *etta,
+ const char *filename);
+void e_tree_table_adapter_load_expanded_state (ETreeTableAdapter *etta,
+ const char *filename);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* _E_TREE_TABLE_ADAPTER_H_ */
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);
diff --git a/widgets/table/e-tree.h b/widgets/table/e-tree.h
new file mode 100644
index 0000000000..9726fdd996
--- /dev/null
+++ b/widgets/table/e-tree.h
@@ -0,0 +1,289 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+#ifndef _E_TREE_H_
+#define _E_TREE_H_
+
+#include <libgnomeui/gnome-canvas.h>
+#include <gtk/gtktable.h>
+#include <gnome-xml/tree.h>
+#include <gal/e-table/e-table-header.h>
+#include <gal/e-table/e-table-group.h>
+#include <gal/e-table/e-table-sort-info.h>
+#include <gal/e-table/e-table-item.h>
+#include <gal/e-table/e-table-selection-model.h>
+#include <gal/e-table/e-table-extras.h>
+#include <gal/e-table/e-table-specification.h>
+#include <gal/widgets/e-printable.h>
+#include <gal/e-table/e-table-state.h>
+
+#include <gal/e-table/e-tree-model.h>
+#include <gal/e-table/e-tree-table-adapter.h>
+#include <gal/e-table/e-tree-sorted.h>
+
+BEGIN_GNOME_DECLS
+
+#define E_TREE_TYPE (e_tree_get_type ())
+#define E_TREE(o) (GTK_CHECK_CAST ((o), E_TREE_TYPE, ETree))
+#define E_TREE_CLASS(k) (GTK_CHECK_CLASS_CAST((k), E_TREE_TYPE, ETreeClass))
+#define E_IS_TREE(o) (GTK_CHECK_TYPE ((o), E_TREE_TYPE))
+#define E_IS_TREE_CLASS(k) (GTK_CHECK_CLASS_TYPE ((k), E_TREE_TYPE))
+
+typedef struct _ETreeDragSourceSite ETreeDragSourceSite;
+
+typedef struct {
+ GtkTable parent;
+
+ ETreeModel *model;
+ ETreeSorted *sorted;
+ ETreeTableAdapter *etta;
+
+ ETableHeader *full_header, *header;
+
+ ETableSortInfo *sort_info;
+ ESorter *sorter;
+
+ ETableSelectionModel *selection;
+ ETableSpecification *spec;
+
+ int reflow_idle_id;
+
+ GnomeCanvas *header_canvas, *table_canvas;
+
+ GnomeCanvasItem *header_item, *root;
+
+ GnomeCanvasItem *white_item;
+ GnomeCanvasItem *item;
+
+ gint length_threshold;
+
+ /*
+ * Configuration settings
+ */
+ guint draw_grid : 1;
+ guint draw_focus : 1;
+ guint row_selection_active : 1;
+
+ guint horizontal_scrolling : 1;
+
+ ECursorMode cursor_mode;
+
+ int drop_row;
+ ETreePath drop_path;
+ int drop_col;
+
+ int drag_row;
+ ETreePath drag_path;
+ int drag_col;
+ ETreeDragSourceSite *site;
+
+ int drag_source_button_press_event_id;
+ int drag_source_motion_notify_event_id;
+} ETree;
+
+typedef struct {
+ GtkTableClass parent_class;
+
+ void (*cursor_change) (ETree *et, int row, ETreePath path);
+ void (*cursor_activated) (ETree *et, int row, ETreePath path);
+ void (*selection_change) (ETree *et);
+ void (*double_click) (ETree *et, int row, ETreePath path, int col, GdkEvent *event);
+ gint (*right_click) (ETree *et, int row, ETreePath path, int col, GdkEvent *event);
+ gint (*click) (ETree *et, int row, ETreePath path, int col, GdkEvent *event);
+ gint (*key_press) (ETree *et, int row, ETreePath path, int col, GdkEvent *event);
+
+ void (*set_scroll_adjustments) (ETree *tree,
+ GtkAdjustment *hadjustment,
+ GtkAdjustment *vadjustment);
+
+ /* Source side drag signals */
+ void (* tree_drag_begin) (ETree *tree,
+ int row,
+ ETreePath path,
+ int col,
+ GdkDragContext *context);
+ void (* tree_drag_end) (ETree *tree,
+ int row,
+ ETreePath path,
+ int col,
+ GdkDragContext *context);
+ void (* tree_drag_data_get) (ETree *tree,
+ int row,
+ ETreePath path,
+ int col,
+ GdkDragContext *context,
+ GtkSelectionData *selection_data,
+ guint info,
+ guint time);
+ void (* tree_drag_data_delete) (ETree *tree,
+ int row,
+ ETreePath path,
+ int col,
+ GdkDragContext *context);
+
+ /* Target side drag signals */
+ void (* tree_drag_leave) (ETree *tree,
+ int row,
+ ETreePath path,
+ int col,
+ GdkDragContext *context,
+ guint time);
+ gboolean (* tree_drag_motion) (ETree *tree,
+ int row,
+ ETreePath path,
+ int col,
+ GdkDragContext *context,
+ gint x,
+ gint y,
+ guint time);
+ gboolean (* tree_drag_drop) (ETree *tree,
+ int row,
+ ETreePath path,
+ int col,
+ GdkDragContext *context,
+ gint x,
+ gint y,
+ guint time);
+ void (* tree_drag_data_received) (ETree *tree,
+ int row,
+ ETreePath path,
+ int col,
+ GdkDragContext *context,
+ gint x,
+ gint y,
+ GtkSelectionData *selection_data,
+ guint info,
+ guint time);
+} ETreeClass;
+
+GtkType e_tree_get_type (void);
+
+ETree *e_tree_construct (ETree *e_tree,
+ ETreeModel *etm,
+ ETableExtras *ete,
+ const char *spec,
+ const char *state);
+GtkWidget *e_tree_new (ETreeModel *etm,
+ ETableExtras *ete,
+ const char *spec,
+ const char *state);
+
+/* Create an ETree using files. */
+ETree *e_tree_construct_from_spec_file (ETree *e_tree,
+ ETreeModel *etm,
+ ETableExtras *ete,
+ const char *spec_fn,
+ const char *state_fn);
+GtkWidget *e_tree_new_from_spec_file (ETreeModel *etm,
+ ETableExtras *ete,
+ const char *spec_fn,
+ const char *state_fn);
+
+/* To save the state */
+gchar *e_tree_get_state (ETree *e_tree);
+void e_tree_save_state (ETree *e_tree,
+ const gchar *filename);
+ETableState *e_tree_get_state_object (ETree *e_tree);
+
+/* note that it is more efficient to provide the state at creation time */
+void e_tree_set_state (ETree *e_tree,
+ const gchar *state);
+void e_tree_set_state_object (ETree *e_tree,
+ ETableState *state);
+void e_tree_load_state (ETree *e_tree,
+ const gchar *filename);
+
+void e_tree_set_cursor (ETree *e_tree,
+ ETreePath path);
+
+/* NULL means we don't have the cursor. */
+ETreePath e_tree_get_cursor (ETree *e_tree);
+void e_tree_selected_row_foreach (ETree *e_tree,
+ EForeachFunc callback,
+ gpointer closure);
+gint e_tree_selected_count (ETree *e_tree);
+EPrintable *e_tree_get_printable (ETree *e_tree);
+
+gint e_tree_get_next_row (ETree *e_tree,
+ gint model_row);
+gint e_tree_get_prev_row (ETree *e_tree,
+ gint model_row);
+
+gint e_tree_model_to_view_row (ETree *e_tree,
+ gint model_row);
+gint e_tree_view_to_model_row (ETree *e_tree,
+ gint view_row);
+void e_tree_get_cell_at (ETree *tree,
+ int x, int y,
+ int *row_return, int *col_return);
+
+/* Drag & drop stuff. */
+/* Target */
+void e_tree_drag_get_data (ETree *tree,
+ int row,
+ int col,
+ GdkDragContext *context,
+ GdkAtom target,
+ guint32 time);
+void e_tree_drag_highlight (ETree *tree,
+ int row,
+ int col); /* col == -1 to highlight entire row. */
+void e_tree_drag_unhighlight (ETree *tree);
+void e_tree_drag_dest_set (ETree *tree,
+ GtkDestDefaults flags,
+ const GtkTargetEntry *targets,
+ gint n_targets,
+ GdkDragAction actions);
+void e_tree_drag_dest_set_proxy (ETree *tree,
+ GdkWindow *proxy_window,
+ GdkDragProtocol protocol,
+ gboolean use_coordinates);
+
+/* There probably should be functions for setting the targets
+ * as a GtkTargetList
+ */
+void e_tree_drag_dest_unset (GtkWidget *widget);
+
+/* Source side */
+void e_tree_drag_source_set (ETree *tree,
+ GdkModifierType start_button_mask,
+ const GtkTargetEntry *targets,
+ gint n_targets,
+ GdkDragAction actions);
+void e_tree_drag_source_unset (ETree *tree);
+
+/* 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);
+
+/* selection stuff */
+void e_tree_select_all (ETree *tree);
+void e_tree_invert_selection (ETree *tree);
+
+
+/* Adapter functions */
+
+gboolean e_tree_node_is_expanded (ETree *et, ETreePath path);
+void e_tree_node_set_expanded (ETree *et, ETreePath path, gboolean expanded);
+void e_tree_node_set_expanded_recurse (ETree *et, ETreePath path, gboolean expanded);
+void e_tree_root_node_set_visible (ETree *et, gboolean visible);
+ETreePath e_tree_node_at_row (ETree *et, int row);
+int e_tree_row_of_node (ETree *et, ETreePath path);
+gboolean e_tree_root_node_is_visible(ETree *et);
+
+void e_tree_show_node (ETree *et, ETreePath path);
+
+void e_tree_save_expanded_state (ETree *et, char *filename);
+void e_tree_load_expanded_state (ETree *et, char *filename);
+
+int e_tree_row_count (ETree *et);
+
+END_GNOME_DECLS
+
+#endif /* _E_TREE_H_ */
+