diff options
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", >K_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", >K_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_ */ + |