diff options
author | Christopher James Lahey <clahey@ximian.com> | 2001-03-20 12:43:42 +0800 |
---|---|---|
committer | Chris Lahey <clahey@src.gnome.org> | 2001-03-20 12:43:42 +0800 |
commit | 1510304c2de206313c637d9269b4fb451cb50adb (patch) | |
tree | 391fc87bab4413e5eec82de476d6ca08db2ce27d /widgets | |
parent | 0629bbb778bf9634c380067b65a7f62f2f05a676 (diff) | |
download | gsoc2013-evolution-1510304c2de206313c637d9269b4fb451cb50adb.tar gsoc2013-evolution-1510304c2de206313c637d9269b4fb451cb50adb.tar.gz gsoc2013-evolution-1510304c2de206313c637d9269b4fb451cb50adb.tar.bz2 gsoc2013-evolution-1510304c2de206313c637d9269b4fb451cb50adb.tar.lz gsoc2013-evolution-1510304c2de206313c637d9269b4fb451cb50adb.tar.xz gsoc2013-evolution-1510304c2de206313c637d9269b4fb451cb50adb.tar.zst gsoc2013-evolution-1510304c2de206313c637d9269b4fb451cb50adb.zip |
Deal with proxy_node_changed being called on a different root node than
2001-03-19 Christopher James Lahey <clahey@ximian.com>
* e-tree-sorted.c (ets_proxy_node_changed): Deal with
proxy_node_changed being called on a different root node than the
one we have in our tree.
* e-tree-table-adapter.c: Did some general clean up here.
* Merged branch:
2001-03-19 Christopher James Lahey <clahey@ximian.com>
* e-tree-sorted.c (find_child_path): Added this function to allow
us to find paths that have been removed from the source.
(ets_proxy_node_removed): Fixed the memmove here a bit. Call
find_child_path.
* e-tree-table-adapter.c (find_node): Check that the passed in
path isn't NULL.
2001-03-19 Christopher James Lahey <clahey@ximian.com>
* e-table-item.c (eti_reflow): Get width from header object
instead of calculating it ourselves.
* e-table-selection-model.c: Turn off selection saving since it's
so slow.
* e-table.c (e_table_set_state_object): Set the width of the newly
created header object.
* e-tree.c (e_tree_set_state_object): Set the width of the newly
created header object.
(tree_canvas_size_allocate): Don't bother setting the dimensions
of the white background twice.
2001-03-18 Christopher James Lahey <clahey@ximian.com>
* e-table-selection-model.c, e-table-selection-model.h: Made
ETableSelectionModel save the cursor properly across changed
signals if has_save_id is true.
2001-03-18 Christopher James Lahey <clahey@ximian.com>
* e-table-selection-model.c, e-table-selection-model.h: Made
ETableSelectionModel save selection properly across changed
signals if has_save_id is true.
* e-tree-memory.c: A couple of typos.
2001-03-18 Christopher James Lahey <clahey@ximian.com>
* e-tree-memory.c, e-tree-sorted.c: Send pre_changes properly.
2001-03-18 Christopher James Lahey <clahey@ximian.com>
* e-tree-table-adapter.c: Send pre_changes when performing
set_expanded or root_node_set_visible.
2001-03-18 Christopher James Lahey <clahey@ximian.com>
* e-tree-sorted.c (ets_is_expandable): When the API requests
whether the object is expandable and it isn't, make sure to send a
signal when it becomes expandable.
* e-tree-table-adapter.c: Made it so that in a number of cases
where it doesn't need to create an empty hash table node if the
current tree node has no children, it doesn't.
2001-03-18 Christopher James Lahey <clahey@ximian.com>
* e-tree-memory-callbacks.c, e-tree-memory-callbacks.h
(etmc_has_save_id, etmc_get_save_id): Added has_save_id and
get_save_id to the list of methods supported by
e_tree_memory_callbacks.
* e-tree-table-adapter.c, e-tree-table-adapter.h: Added saving of
expanded nodes.
2001-03-18 Christopher James Lahey <clahey@ximian.com>
* e-table-model.c, e-table-model.h (e_table_model_get_save_id):
Changed row_save_id to get_save_id to be consistent with ETree.
* e-tree-model.c, e-tree-model.h: Added "pre_change" signal.
Added has_save_id and get_save_id methods.
* e-tree-sorted.c: Proxy pre_change signal. Implemented
has_save_id and get_save_id. If the base model doesn't provide
has_save_id then we g_strdup_printf the pointer of the base model
ETreePath as the save_id.
* e-tree-table-adapter.c: Proxy pre_change signal. If base model
has_save_id, then use the results of get_save_id as the key for
the hash table of node attributes. Otherwise use the pointer as
before.
2001-03-17 Christopher James Lahey <clahey@ximian.com>
* e-tree-sorted.c (ets_sort_idle): Fixed it so that all nodes get
sorted properly instead of just the top node.
2001-03-17 Christopher James Lahey <clahey@ximian.com>
* e-table-sorting-utils.c (e_table_sorting_utils_tree_sort): Made
tree sorting faster by using a cache.
* e-tree-sorted.c: Added commented out debugging g_prints.
2001-03-17 Christopher James Lahey <clahey@ximian.com>
* e-tree-sorted.c: Switched to using GMemChunks.
2001-03-17 Christopher James Lahey <clahey@ximian.com>
* e-tree-sorted.c (resort_node): Made it so that children of a
node that's being sorted don't send changed signals.
2001-03-17 Christopher James Lahey <clahey@ximian.com>
* e-table-sorting-utils.c, e-table-sorting-utils.h: Switched to
using e_sort and e_search instead of qsort and a linear search.
Added the tree functions e_table_sorting_utils_tree_sort,
e_table_sorting_utils_tree_check_position, and
e_table_sorting_utils_tree_insert.
* e-tree-sorted.c: Made this actually do sorting.
* e-tree-table-adapter.c (etta_proxy_node_changed): The old_size
needs to be the number of visible children + 1 to include the top
node.
* e-tree.c (e_tree_set_state_object): Set the sort_info on the
ETreeSorted when you get a new sort_info.
2001-03-16 Christopher James Lahey <clahey@ximian.com>
* Makefile.am: Added e-tree-sorted.c and e-tree-sorted.h.
* e-table-item.c (eti_realize_cell_views): Only realize the cells
if they're not realized already and if the canvas is realized.
* e-table-sorted.c (ets_proxy_model_cell_changed): Matched the
change to the signature of e_table_sorting_utils_affects_sort.
* e-table-sorting-utils.c, e-table-sorting-utils.h
(e_table_sorting_utils_affects_sort): Changed the signature of
this function to not take the ETableModel source since it doesn't
use it and we need to use this function for ETreeSorted which
doesn't have an ETableModel.
* e-tree-memory.c (etmm_get_expanded_default): Actually implement
the get_expanded_default for this tree.
* e-tree-model.h: Cleaned up the indentation here.
* e-tree-sorted.c, e-tree-sorted.h: New class meant to be used for
sorting trees. It doesn't actually sort yet. It simply acts as
an ETreeProxy which is the hardest part of making ETreeSorted.
* e-tree.c, e-tree.h: Made this use an ETreeSorted.
2001-03-14 Christopher James Lahey <clahey@ximian.com>
* e-table-item.c, e-table-item.h, e-table-selection-model.c,
e-table-selection-model.h, e-table-sorted.c, e-table-sorted.h,
e-table-subset.c, e-table-subset.h, e-table.c, e-table.h: Switch
to handling e_table_model_rows_inserted instead of
e_table_model_row_inserted and e_table_model_rows_deleted instead
of e_table_model_row_deleted.
* e-table-model.c, e-table-model.h: Replaced the signals
"model_row_inserted" and "model_row_deleted" with
"model_rows_inserted" and "model_rows_deleted" so that when
multiple rows are inserted or deleted at the same time they can be
handled properly.
* e-tree-table-adapter.c: Call "model_rows_inserted" and
"model_rows_deleted" instead of "model_changed" when inserting or
deleting multiple rows.
2001-03-14 Christopher James Lahey <clahey@ximian.com>
* e-table-item.c (e_table_item_row_diff): Made this not count the
pixel between rows if it isn't there.
2001-03-14 Christopher James Lahey <clahey@ximian.com>
* e-table-item.c (eti_header_structure_changed): Properly attach &
realize cell views here.
2001-03-13 Christopher James Lahey <clahey@ximian.com>
* e-tree-table-adapter.c (etta_proxy_node_removed): Check that
parent_node and parent aren't NULL before making function calls on
them.
2001-03-13 Christopher James Lahey <clahey@ximian.com>
* e-table-item.c (confirm_height_cache): Fixed a height cache
miscalculation.
* e-tree-table-adapter.c (find_first_child_node): Made the
semantics of this mean that find_first_child_node(adapter, -1)
means return the first node in the tree.
2001-03-13 Christopher James Lahey <clahey@ximian.com>
* e-table-extras.c: Added a "string-integer" comparison function
to the default %ETableExtras so you can do comparisons based on
integer value even if you using strings for the data (this lets
you do editable numbers, for instance.)
* e-table-item.c: Rearranged it a bit so that if you have
draw_grid off it doesn't add space for the horizontal lines, nor
leave them the background color.
* e-table-model.c, e-table-model.h: Added the row_save_id and
has_save_id methods to %ETableModel.
* e-tree.c, e-tree.h: Replaced e_tree_compute_location with
e_tree_get_cell_at.
2001-03-08 Christopher James Lahey <clahey@ximian.com>
* Makefile.am: Added e-table/e-table-utils.c,
e-table/e-tree-memory-callbacks.c, e-table/e-tree-memory.c,
e-table/e-tree-scrolled.c, e-table/e-tree-table-adapter.c, and
e-table/e-tree.c. Removed e-table/e-tree-simple.c. Added
e-table/e-table-utils.h, e-table/e-tree-memory-callbacks.h,
e-table/e-tree-memory.h, e-table/e-tree-scrolled.h,
e-table/e-tree-table-adapter.h, and e-table/e-tree.h. Removed
e-table/e-tree-simple.h.
* e-cell-tree.c: Updated this for the new tree.
* e-table-item.c: Added some redraw requests where appropriate.
* e-table-item.h: Fixed an incorrect class method declaration.
* e-table-model.c, e-table-model.h: Removed
e_table_model_has_sort_group and e_table_model_row_sort_group.
* e-table-scrolled.h: Removed unused headers.
* e-table-simple.c, e-table-simple.h: Rearranged this a bit.
* e-table-sorter.c, e-table-sorting-utils.c,
e-table-sorting-utils.h: Removed sort group stuff. Added the
function e_table_sorting_utils_check_position.
* e-table-utils.c, e-table-utils.h: Utility functions for ETable
and ETree.
* e-table.c: Moved some of the functionality from here to
e-table-utils.c so that it can be reused by ETree.
* e-tree-memory-callbacks.c, e-tree-memory-callbacks.h: Class to
implement an ETreeMemory as callbacks instead of overriding the
class.
* e-tree-memory.c, e-tree-memory.h: ETreeModel that stores a tree
of physical nodes.
* e-tree-model.c, e-tree-model.h: Removed most of the
functionality from here to the classes ETreeMemory and
ETreeTableAdapter. This is now just a simple virtualized tree
class.
* e-tree-scrolled.c, e-tree-scrolled.h: New class. An ETree in an
EScrollFrame.
* e-tree-simple.c: Small change. This is no longer used.
* e-tree-table-adapter.c, e-tree-table-adapter.h: ETableModel that
represents an ETreeModel as a table.
* e-tree.c, e-tree.h: New super class kind of like ETable but for
trees.
End of branch
svn path=/trunk/; revision=8837
Diffstat (limited to 'widgets')
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_ */ + |