diff options
author | Chris Toshok <toshok@helixcode.com> | 2000-06-11 12:20:56 +0800 |
---|---|---|
committer | Chris Toshok <toshok@src.gnome.org> | 2000-06-11 12:20:56 +0800 |
commit | aac3f2c8b69f579e04314b21a13d752afe4bd317 (patch) | |
tree | dec49e04bac8721e060f07003aa90bcb76d30479 /widgets | |
parent | 835ec00d01e23256143229d8e2c99f68afae1e6c (diff) | |
download | gsoc2013-evolution-aac3f2c8b69f579e04314b21a13d752afe4bd317.tar gsoc2013-evolution-aac3f2c8b69f579e04314b21a13d752afe4bd317.tar.gz gsoc2013-evolution-aac3f2c8b69f579e04314b21a13d752afe4bd317.tar.bz2 gsoc2013-evolution-aac3f2c8b69f579e04314b21a13d752afe4bd317.tar.lz gsoc2013-evolution-aac3f2c8b69f579e04314b21a13d752afe4bd317.tar.xz gsoc2013-evolution-aac3f2c8b69f579e04314b21a13d752afe4bd317.tar.zst gsoc2013-evolution-aac3f2c8b69f579e04314b21a13d752afe4bd317.zip |
change things so we focus the cell and select the row, and also dispatch
2000-06-10 Chris Toshok <toshok@helixcode.com>
* e-table-item.c (eti_event): change things so we focus the cell
and select the row, and also dispatch the event to that row/cell.
This fixes the problem with the tree where you had to click twice
to activate the tree controls.
* Makefile.am (libetable_a_SOURCES): remove e-tree-gnode.* and add
e-tree-simple.*
(icons): add tree-expanded.xpm and tree-unexpanded.xpm
* e-cell-tree.c (ect_enter_edit): defer to subcell's view.
(ect_leave_edit): defer to subcell's view.
(visible_depth_of_node): visual depth, taking into account that
the root node of the model might not be displayed.
(offset_of_node): return the offset used to shift the subcell
over.
(ect_draw): don't draw vertical lines if we're the leftmode node
(a visual root node). also, don't shift x2 by the subcell offset,
so we get ellipses like we're supposed to.
(ect_event): remove GDK_BUTTON_RELEASE from the list of events
that we care about.
* e-tree-example-1.c: lots of changes, a more dynamic UI so we can
test tree model api stuff.
* e-tree-gnode.c, e-tree-gnode.c: removed files, since their guts
have been rolled into e-tree-model.c
* e-tree-model.c, e-tree-model.h: substantially changed. too much
to really describe here. this should really be considered the
first revision of these files :)
* e-tree-simple.c, e-tree-simple.h: analogous to e-table-simple, a
subclass that allows the use of function pointers.
svn path=/trunk/; revision=3519
Diffstat (limited to 'widgets')
-rw-r--r-- | widgets/e-table/ChangeLog | 36 | ||||
-rw-r--r-- | widgets/e-table/Makefile.am | 10 | ||||
-rw-r--r-- | widgets/e-table/e-cell-tree.c | 57 | ||||
-rw-r--r-- | widgets/e-table/e-table-item.c | 14 | ||||
-rw-r--r-- | widgets/e-table/e-tree-example-1.c | 185 | ||||
-rw-r--r-- | widgets/e-table/e-tree-gnode.c | 210 | ||||
-rw-r--r-- | widgets/e-table/e-tree-gnode.h | 37 | ||||
-rw-r--r-- | widgets/e-table/e-tree-model.c | 549 | ||||
-rw-r--r-- | widgets/e-table/e-tree-model.h | 83 | ||||
-rw-r--r-- | widgets/e-table/e-tree-simple.c | 73 | ||||
-rw-r--r-- | widgets/e-table/e-tree-simple.h | 40 | ||||
-rw-r--r-- | widgets/table/e-cell-tree.c | 57 | ||||
-rw-r--r-- | widgets/table/e-table-item.c | 14 | ||||
-rw-r--r-- | widgets/table/e-tree-example-1.c | 185 | ||||
-rw-r--r-- | widgets/table/e-tree-gnode.c | 210 | ||||
-rw-r--r-- | widgets/table/e-tree-gnode.h | 37 | ||||
-rw-r--r-- | widgets/table/e-tree-model.c | 549 | ||||
-rw-r--r-- | widgets/table/e-tree-model.h | 83 | ||||
-rw-r--r-- | widgets/table/e-tree-simple.c | 73 | ||||
-rw-r--r-- | widgets/table/e-tree-simple.h | 40 |
20 files changed, 1706 insertions, 836 deletions
diff --git a/widgets/e-table/ChangeLog b/widgets/e-table/ChangeLog index cbe7259942..b2dc58456d 100644 --- a/widgets/e-table/ChangeLog +++ b/widgets/e-table/ChangeLog @@ -1,3 +1,39 @@ +2000-06-10 Chris Toshok <toshok@helixcode.com> + + * e-table-item.c (eti_event): change things so we focus the cell + and select the row, and also dispatch the event to that row/cell. + This fixes the problem with the tree where you had to click twice + to activate the tree controls. + + * Makefile.am (libetable_a_SOURCES): remove e-tree-gnode.* and add + e-tree-simple.* + (icons): add tree-expanded.xpm and tree-unexpanded.xpm + + * e-cell-tree.c (ect_enter_edit): defer to subcell's view. + (ect_leave_edit): defer to subcell's view. + (visible_depth_of_node): visual depth, taking into account that + the root node of the model might not be displayed. + (offset_of_node): return the offset used to shift the subcell + over. + (ect_draw): don't draw vertical lines if we're the leftmode node + (a visual root node). also, don't shift x2 by the subcell offset, + so we get ellipses like we're supposed to. + (ect_event): remove GDK_BUTTON_RELEASE from the list of events + that we care about. + + * e-tree-example-1.c: lots of changes, a more dynamic UI so we can + test tree model api stuff. + + * e-tree-gnode.c, e-tree-gnode.c: removed files, since their guts + have been rolled into e-tree-model.c + + * e-tree-model.c, e-tree-model.h: substantially changed. too much + to really describe here. this should really be considered the + first revision of these files :) + + * e-tree-simple.c, e-tree-simple.h: analogous to e-table-simple, a + subclass that allows the use of function pointers. + 2000-06-11 Christopher James Lahey <clahey@helixcode.com> * e-table-model.c: Small syntactic changes. diff --git a/widgets/e-table/Makefile.am b/widgets/e-table/Makefile.am index 9f21f45cd2..65504749ba 100644 --- a/widgets/e-table/Makefile.am +++ b/widgets/e-table/Makefile.am @@ -76,10 +76,10 @@ libetable_a_SOURCES = \ e-table-subset-variable.h \ e-table-text-model.c \ e-table-text-model.h \ - e-tree-gnode.c \ - e-tree-gnode.h \ e-tree-model.c \ - e-tree-model.h + e-tree-model.h \ + e-tree-simple.c \ + e-tree-simple.h noinst_PROGRAMS = \ table-test table-example-1 table-example-2 table-size-test tree-example-1 @@ -159,7 +159,9 @@ icons = \ add-col.xpm \ check-empty.xpm \ check-filled.xpm \ - remove-col.xpm + remove-col.xpm \ + tree-expanded.xpm \ + tree-unexpanded.xpm EXTRA_DIST = \ sample.table \ diff --git a/widgets/e-table/e-cell-tree.c b/widgets/e-table/e-cell-tree.c index 9f40bd0eae..c3459ce4f8 100644 --- a/widgets/e-table/e-cell-tree.c +++ b/widgets/e-table/e-cell-tree.c @@ -49,6 +49,21 @@ static ECellClass *parent_class; static GdkPixbuf *tree_expanded_pixbuf, *tree_unexpanded_pixbuf; +#define INDENT_AMOUNT 16 + +static int +visible_depth_of_node (ETreeModel *tree_model, ETreePath *path) +{ + return (e_tree_model_node_depth (tree_model, path) + - (e_tree_model_root_node_is_visible (tree_model) ? 0 : 1)); +} + +static gint +offset_of_node (ETreeModel *tree_model, ETreePath *path) +{ + return (visible_depth_of_node(tree_model, path) + 1) * INDENT_AMOUNT; +} + static ETreePath* e_cell_tree_get_node (ETreeModel *tree_model, int row) { @@ -136,7 +151,6 @@ ect_unrealize (ECellView *ecv) (* parent_class->unrealize) (ecv); } -#define INDENT_AMOUNT 16 /* * ECell::draw method */ @@ -167,7 +181,7 @@ ect_draw (ECellView *ecell_view, GdkDrawable *drawable, */ node = e_cell_tree_get_node (tree_model, row); - offset = (e_tree_model_node_depth (tree_model, node) + 1) * INDENT_AMOUNT; + offset = offset_of_node (tree_model, node); expandable = e_tree_model_node_is_expandable (tree_model, node); expanded = e_tree_model_node_is_expanded (tree_model, node); subcell_offset = offset; @@ -196,15 +210,8 @@ ect_draw (ECellView *ecell_view, GdkDrawable *drawable, rect.x, rect.y, rect.width, rect.height); gdk_gc_set_foreground (tree_view->gc, foreground); + /* draw our lines */ if (E_CELL_TREE(tree_view->cell_view.ecell)->draw_lines) { - /* draw our lines */ - gdk_draw_line (drawable, tree_view->gc, - rect.x + offset - INDENT_AMOUNT / 2, - rect.y, - rect.x + offset - INDENT_AMOUNT / 2, - (e_tree_model_node_get_next (tree_model, node) - ? rect.y + rect.height - : rect.y + rect.height / 2)); gdk_draw_line (drawable, tree_view->gc, rect.x + offset - INDENT_AMOUNT / 2 + 1, @@ -212,12 +219,22 @@ ect_draw (ECellView *ecell_view, GdkDrawable *drawable, rect.x + offset, rect.y + rect.height / 2); + if (visible_depth_of_node (tree_model, node) != 0) { + gdk_draw_line (drawable, tree_view->gc, + rect.x + offset - INDENT_AMOUNT / 2, + rect.y, + rect.x + offset - INDENT_AMOUNT / 2, + (e_tree_model_node_get_next (tree_model, node) + ? rect.y + rect.height + : rect.y + rect.height / 2)); + } + /* now traverse back up to the root of the tree, checking at 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); offset -= INDENT_AMOUNT; - while (node && ! e_tree_model_node_is_root (tree_model, node)) { + while (node && visible_depth_of_node (tree_model, node) != 0) { if (e_tree_model_node_get_next(tree_model, node)) { gdk_draw_line (drawable, tree_view->gc, rect.x + offset - INDENT_AMOUNT / 2, @@ -255,7 +272,7 @@ ect_draw (ECellView *ecell_view, GdkDrawable *drawable, subcell_offset pixels */ e_cell_draw (tree_view->subcell_view, drawable, model_col, view_col, row, selected, - x1 + subcell_offset, y1, x2 + subcell_offset, y2); + x1 + subcell_offset, y1, x2, y2); } /* @@ -267,13 +284,12 @@ ect_event (ECellView *ecell_view, GdkEvent *event, int model_col, int view_col, ECellTreeView *tree_view = (ECellTreeView *) ecell_view; switch (event->type) { - case GDK_BUTTON_PRESS: - case GDK_BUTTON_RELEASE: { + case GDK_BUTTON_PRESS: { /* if the event happened in our area of control (and we care about it), handle it. */ ETreeModel *tree_model = e_cell_tree_get_tree_model (ecell_view->e_table_model, row); ETreePath *node = e_cell_tree_get_node (tree_model, row); - int offset = (e_tree_model_node_depth (tree_model, node) + 1) * INDENT_AMOUNT; + int offset = offset_of_node (tree_model, node); /* 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) { @@ -284,6 +300,8 @@ ect_event (ECellView *ecell_view, GdkEvent *event, int model_col, int view_col, } return TRUE; } + else if (event->button.x < (offset - INDENT_AMOUNT)) + return TRUE; } default: /* otherwise, pass it off to our subcell_view */ @@ -309,7 +327,10 @@ ect_height (ECellView *ecell_view, int model_col, int view_col, int row) static void * ect_enter_edit (ECellView *ecell_view, int model_col, int view_col, int row) { - return NULL; + /* just defer to our subcell's view */ + ECellTreeView *tree_view = (ECellTreeView *) ecell_view; + + return e_cell_enter_edit (tree_view->subcell_view, model_col, view_col, row); } /* @@ -318,6 +339,10 @@ ect_enter_edit (ECellView *ecell_view, int model_col, int view_col, int row) static void ect_leave_edit (ECellView *ecell_view, int model_col, int view_col, int row, void *edit_context) { + /* just defer to our subcell's view */ + ECellTreeView *tree_view = (ECellTreeView *) ecell_view; + + e_cell_leave_edit (tree_view->subcell_view, model_col, view_col, row, edit_context); } /* diff --git a/widgets/e-table/e-table-item.c b/widgets/e-table/e-table-item.c index 3a4d98bf91..0de4181cdf 100644 --- a/widgets/e-table/e-table-item.c +++ b/widgets/e-table/e-table-item.c @@ -1197,6 +1197,14 @@ eti_event (GnomeCanvasItem *item, GdkEvent *e) if (!find_cell (eti, e->button.x, e->button.y, &col, &row, &x1, &y1)) return TRUE; + if (eti->cursor_row != row || eti->cursor_col != col){ + /* + * Focus the cell, and select the row + */ + e_table_item_leave_edit (eti); + e_table_item_focus (eti, col, row); + } + if (eti->cursor_row == row && eti->cursor_col == col){ ecol = e_table_header_get_column (eti->header, col); @@ -1209,12 +1217,6 @@ eti_event (GnomeCanvasItem *item, GdkEvent *e) e->button.y = y1; e_cell_event (ecell_view, e, ecol->col_idx, col, row); - } else { - /* - * Focus the cell, and select the row - */ - e_table_item_leave_edit (eti); - e_table_item_focus (eti, col, row); } break; case 3: diff --git a/widgets/e-table/e-tree-example-1.c b/widgets/e-table/e-tree-example-1.c index e5563a8071..c598e33ea4 100644 --- a/widgets/e-table/e-tree-example-1.c +++ b/widgets/e-table/e-tree-example-1.c @@ -4,7 +4,6 @@ #include <string.h> #include <gnome.h> #include "e-util/e-cursors.h" -#include "e-tree-gnode.h" #include "e-table-header.h" #include "e-table-header-item.h" #include "e-table-item.h" @@ -12,6 +11,7 @@ #include "e-cell-tree.h" #include "e-cell-checkbox.h" #include "e-table.h" +#include "e-tree-simple.h" #include <gdk-pixbuf/gdk-pixbuf.h> @@ -56,6 +56,8 @@ char *headers [COLS] = { "Date" }; +GtkWidget *e_table; + /* * ETableSimple callbacks * These are the callbacks that define the behavior of our custom model. @@ -73,12 +75,12 @@ my_col_count (ETableModel *etc, void *data) return COLS; } -/* This function returns the value at a particular point in our ETableModel. */ +/* This function returns the value at a particular point in our ETreeModel. */ static void * -my_value_at (ETreeModel *etc, GNode *node, int col, void *data) +my_value_at (ETreeModel *etm, ETreePath *path, int col, void *model_data) { switch (col) { - case 0: return "Re: Two things"; + case 0: return e_tree_model_node_get_data (etm, path); case 1: return "Chris Toshok"; case 2: return "toshok@helixcode.com"; case 3: return "Jun 07 2000"; @@ -86,67 +88,163 @@ my_value_at (ETreeModel *etc, GNode *node, int col, void *data) } } -/* This function sets the value at a particular point in our ETableModel. */ +/* This function sets the value at a particular point in our ETreeModel. */ static void -my_set_value_at (ETableModel *etc, GNode *node, int col, const void *val, void *data) +my_set_value_at (ETreeModel *etm, ETreePath *path, int col, const void *val, void *model_data) { + if (col == 0) { + char *str = e_tree_model_node_get_data (etm, path); + g_free (str); + e_tree_model_node_set_data (etm, path, g_strdup(val)); + } } /* This function returns whether a particular cell is editable. */ static gboolean -my_is_editable (ETableModel *etc, GNode *node, int col, void *data) +my_is_editable (ETreeModel *etm, ETreePath *path, int col, void *model_data) { - return FALSE; + if (col == 0) + return TRUE; + else + return FALSE; } -/* This function duplicates the value passed to it. */ -static void * -my_duplicate_value (ETableModel *etc, int col, const void *value, void *data) +static void +toggle_root (GtkButton *button, gpointer data) +{ + ETreeModel *e_tree_model = (ETreeModel*)data; + e_tree_model_root_node_set_visible (e_tree_model, !e_tree_model_root_node_is_visible (e_tree_model)); +} + +static void +add_sibling (GtkButton *button, gpointer data) +{ + ETreeModel *e_tree_model = E_TREE_MODEL (data); + int selected_row; + ETreePath *selected_node; + ETreePath *parent_node; + + selected_row = e_table_get_selected_view_row (E_TABLE (e_table)); + if (selected_row == -1) + return; + + selected_node = e_tree_model_node_at_row (e_tree_model, selected_row); + g_assert (selected_node); + + parent_node = e_tree_model_node_get_parent (e_tree_model, selected_node); + + e_tree_model_node_insert_before (e_tree_model, parent_node, + selected_node, "User added sibling"); + +} + +static void +add_child (GtkButton *button, gpointer data) +{ + ETreeModel *e_tree_model = E_TREE_MODEL (data); + int selected_row; + ETreePath *selected_node; + + selected_row = e_table_get_selected_view_row (E_TABLE (e_table)); + if (selected_row == -1) + return; + + selected_node = e_tree_model_node_at_row (e_tree_model, selected_row); + g_assert (selected_node); + + e_tree_model_node_insert (e_tree_model, selected_node, + 0, "User added child"); +} + +static void +remove_node (GtkButton *button, gpointer data) { - return g_strdup (value); + ETreeModel *e_tree_model = E_TREE_MODEL (data); + int selected_row; + char *str; + ETreePath *selected_node; + + selected_row = e_table_get_selected_view_row (E_TABLE (e_table)); + if (selected_row == -1) + return; + + selected_node = e_tree_model_node_at_row (e_tree_model, selected_row); + g_assert (selected_node); + + if (e_tree_model_node_get_children (e_tree_model, selected_node, NULL) > 0) + return; + + str = (char*)e_tree_model_node_remove (e_tree_model, selected_node); + printf ("removed node %s\n", str); + g_free (str); } -/* This function frees the value passed to it. */ static void -my_free_value (ETableModel *etc, int col, void *value, void *data) +expand_all (GtkButton *button, gpointer data) { - g_free (value); + ETreeModel *e_tree_model = E_TREE_MODEL (data); + int selected_row; + ETreePath *selected_node; + + selected_row = e_table_get_selected_view_row (E_TABLE (e_table)); + if (selected_row == -1) + return; + + selected_node = e_tree_model_node_at_row (e_tree_model, selected_row); + g_assert (selected_node); + + e_tree_model_node_set_expanded_recurse (e_tree_model, selected_node, TRUE); } -/* This function is for when the model is unfrozen. This can mostly - be ignored for simple models. */ static void -my_thaw (ETableModel *etc, void *data) +collapse_all (GtkButton *button, gpointer data) { + ETreeModel *e_tree_model = E_TREE_MODEL (data); + int selected_row; + ETreePath *selected_node; + + selected_row = e_table_get_selected_view_row (E_TABLE (e_table)); + if (selected_row == -1) + return; + + selected_node = e_tree_model_node_at_row (e_tree_model, selected_row); + g_assert (selected_node); + + e_tree_model_node_set_expanded_recurse (e_tree_model, selected_node, FALSE); } /* We create a window containing our new tree. */ static void create_tree (void) { - GtkWidget *e_table, *window, *frame; + GtkWidget *window, *frame, *button, *vbox; ECell *cell_left_just; ECell *cell_tree; ETableHeader *e_table_header; int i, j; ETreeModel *e_tree_model = NULL; - GNode *root_node; + ETreePath *root_node; + + /* here we create our model. This uses the functions we defined + earlier. */ + e_tree_model = e_tree_simple_new (my_value_at, + my_set_value_at, + my_is_editable, + NULL); /* create a root node with 5 children */ - root_node = g_node_new (NULL); + root_node = e_tree_model_node_insert (e_tree_model, NULL, + 0, g_strdup("Root Node")); + for (i = 0; i < 5; i++){ - GNode *n = g_node_insert (root_node, 0, g_node_new(NULL)); + ETreePath *n = e_tree_model_node_insert (e_tree_model, + root_node, 0, g_strdup("First level of children")); for (j = 0; j < 5; j ++) { - g_node_insert (n, 0, g_node_new(NULL)); + e_tree_model_node_insert (e_tree_model, + n, 0, g_strdup("Second level of children")); } } - /* Next we create our model. This uses the functions we defined - earlier. */ - e_tree_model = e_tree_gnode_new (root_node, - my_value_at, - NULL); - /* * Next we create a header. The ETableHeader is used in two * different way. The first is the full_header. This is the @@ -200,6 +298,7 @@ create_tree (void) window = gtk_window_new (GTK_WINDOW_TOPLEVEL); /* This frame is simply to get a bevel around our table. */ + vbox = gtk_vbox_new (FALSE, 0); frame = gtk_frame_new (NULL); /* @@ -213,8 +312,34 @@ create_tree (void) /* Build the gtk widget hierarchy. */ gtk_container_add (GTK_CONTAINER (frame), e_table); - gtk_container_add (GTK_CONTAINER (window), frame); + gtk_box_pack_start (GTK_BOX (vbox), frame, TRUE, TRUE, 0); + + button = gtk_button_new_with_label ("Toggle Root Node"); + gtk_signal_connect (GTK_OBJECT (button), "clicked", toggle_root, e_tree_model); + gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0); + + button = gtk_button_new_with_label ("Add Sibling"); + gtk_signal_connect (GTK_OBJECT (button), "clicked", add_sibling, e_tree_model); + gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0); + button = gtk_button_new_with_label ("Add Child"); + gtk_signal_connect (GTK_OBJECT (button), "clicked", add_child, e_tree_model); + gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0); + + button = gtk_button_new_with_label ("Remove Node"); + gtk_signal_connect (GTK_OBJECT (button), "clicked", remove_node, e_tree_model); + gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0); + + button = gtk_button_new_with_label ("Expand All Below"); + gtk_signal_connect (GTK_OBJECT (button), "clicked", expand_all, e_tree_model); + gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0); + + button = gtk_button_new_with_label ("Collapse All Below"); + gtk_signal_connect (GTK_OBJECT (button), "clicked", collapse_all, e_tree_model); + gtk_box_pack_end (GTK_BOX (vbox), button, FALSE, FALSE, 0); + + gtk_container_add (GTK_CONTAINER (window), vbox); + /* Size the initial window. */ gtk_widget_set_usize (window, 200, 200); diff --git a/widgets/e-table/e-tree-gnode.c b/widgets/e-table/e-tree-gnode.c deleted file mode 100644 index ce53751b56..0000000000 --- a/widgets/e-table/e-tree-gnode.c +++ /dev/null @@ -1,210 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ -/* - * e-tree-gnode.c: a Tree Model that reflects a GNode structure visually. - * - * Author: - * Chris Toshok (toshok@helixcode.com) - * - * (C) 2000 Helix Code, Inc. - */ -#include <config.h> -#include <gtk/gtksignal.h> -#include "e-util/e-util.h" -#include "e-tree-gnode.h" - -#define PARENT_TYPE E_TREE_MODEL_TYPE - -static ETreePath * -gnode_get_root (ETreeModel *etm) -{ - ETreeGNode *etg = E_TREE_GNODE (etm); - ETreePath *path = NULL; - - path = g_list_append(path, etg->root); - - return path; -} - -static ETreePath * -gnode_get_prev (ETreeModel *etm, ETreePath *node) -{ - ETreePath *prev_path; - - GNode *gnode; - GNode *prev_sibling; - - g_return_val_if_fail (node && node->data, NULL); - - gnode = (GNode*)node->data; - prev_sibling = g_node_prev_sibling(gnode); - - if (!prev_sibling) - return NULL; - - prev_path = g_list_copy (node->next); - prev_path = g_list_prepend (prev_path, prev_sibling); - return prev_path; -} - -static ETreePath * -gnode_get_next (ETreeModel *etm, ETreePath *node) -{ - ETreePath *next_path; - GNode *gnode; - GNode *next_sibling; - - g_return_val_if_fail (node && node->data, NULL); - - gnode = (GNode*)node->data; - next_sibling = g_node_next_sibling(gnode); - - if (!next_sibling) - return NULL; - - next_path = g_list_copy (node->next); - next_path = g_list_prepend (next_path, next_sibling); - return next_path; -} - -static void * -gnode_value_at (ETreeModel *etm, ETreePath *node, int col) -{ - ETreeGNode *etg = E_TREE_GNODE (etm); - GNode *gnode; - - g_return_val_if_fail (node && node->data, NULL); - - gnode = (GNode*)node->data; - - return etg->value_at (etm, gnode, col, etg->data); -} - -static void -gnode_set_value_at (ETreeModel *etm, ETreePath *node, int col, const void *val) -{ - ETreeGNode *etg = E_TREE_GNODE (etm); - GNode *gnode; - - g_return_if_fail (node && node->data); - - gnode = (GNode*)node->data; - - /* XXX */ -} - -static gboolean -gnode_is_editable (ETreeModel *etm, ETreePath *node, int col) -{ - ETreeGNode *etg = E_TREE_GNODE (etm); - GNode *gnode; - - g_return_val_if_fail (node && node->data, FALSE); - - gnode = (GNode*)node->data; - - /* XXX */ - return FALSE; -} - -static guint -gnode_get_children (ETreeModel *etm, ETreePath *node, ETreePath ***paths) -{ - ETreeGNode *etg = E_TREE_GNODE (etm); - GNode *gnode; - guint n_children; - - g_return_val_if_fail (node && node->data, 0); - - gnode = (GNode*)node->data; - - n_children = g_node_n_children (gnode); - - if (paths) - { - int i; - (*paths) = g_malloc (sizeof (ETreePath*) * n_children); - for (i = 0; i < n_children; i ++) { - (*paths)[i] = g_list_copy (node); - (*paths)[i] = g_list_prepend ((*paths)[i], g_node_nth_child (gnode, i)); - } - } - - return n_children; -} - -static void -gnode_release_paths (ETreeModel *etm, ETreePath **paths, guint num_paths) -{ - guint i; - g_return_if_fail (paths); - - for (i = 0; i < num_paths; i ++) - g_list_free (paths[i]); - g_free (paths); -} - -static gboolean -gnode_is_expanded (ETreeModel *etm, ETreePath *node) -{ - ETreeGNode *etg = E_TREE_GNODE (etm); - GNode *gnode; - - g_return_val_if_fail (node && node->data, FALSE); - - gnode = (GNode*)node->data; - - return (gboolean)gnode->data; -} - -static void -gnode_set_expanded (ETreeModel *etm, ETreePath *node, gboolean expanded) -{ - ETreeGNode *etg = E_TREE_GNODE (etm); - GNode *gnode; - int num_descendents; - - g_return_if_fail (node && node->data); - - gnode = (GNode*)node->data; - - /* XXX */ - gnode->data = (gpointer)expanded; - - e_table_model_changed (E_TABLE_MODEL(etm)); -} - -static void -e_tree_gnode_class_init (GtkObjectClass *object_class) -{ - ETreeModelClass *model_class = (ETreeModelClass *) object_class; - - model_class->get_root = gnode_get_root; - model_class->get_next = gnode_get_next; - model_class->get_prev = gnode_get_prev; - model_class->value_at = gnode_value_at; - model_class->set_value_at = gnode_set_value_at; - model_class->is_editable = gnode_is_editable; - model_class->get_children = gnode_get_children; - model_class->release_paths = gnode_release_paths; - model_class->is_expanded = gnode_is_expanded; - model_class->set_expanded = gnode_set_expanded; -} - -E_MAKE_TYPE(e_tree_gnode, "ETreeGNode", ETreeGNode, e_tree_gnode_class_init, NULL, PARENT_TYPE) - -ETreeModel * -e_tree_gnode_new (GNode *root_node, - ETreeGNodeValueAtFn value_at, - void *data) -{ - ETreeGNode *etg; - - etg = gtk_type_new (e_tree_gnode_get_type ()); - - etg->root = root_node; - - etg->value_at = value_at; - etg->data = data; - - return (ETreeModel*)etg; -} diff --git a/widgets/e-table/e-tree-gnode.h b/widgets/e-table/e-tree-gnode.h deleted file mode 100644 index 56e6b19b80..0000000000 --- a/widgets/e-table/e-tree-gnode.h +++ /dev/null @@ -1,37 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ - -#ifndef _E_TREE_GNODE_H_ -#define _E_TREE_GNODE_H_ - -#include "e-tree-model.h" - -#define E_TREE_GNODE_TYPE (e_tree_gnode_get_type ()) -#define E_TREE_GNODE(o) (GTK_CHECK_CAST ((o), E_TREE_GNODE_TYPE, ETreeGNode)) -#define E_TREE_GNODE_CLASS(k) (GTK_CHECK_CLASS_CAST((k), E_TREE_GNODE_TYPE, ETreeGNodeClass)) -#define E_IS_TREE_GNODE(o) (GTK_CHECK_TYPE ((o), E_TREE_GNODE_TYPE)) -#define E_IS_TREE_GNODE_CLASS(k) (GTK_CHECK_CLASS_TYPE ((k), E_TREE_GNODE_TYPE)) - -typedef void *(*ETreeGNodeValueAtFn)(ETreeModel *model, GNode *node, int col, void *data); - - -typedef struct { - ETreeModel parent; - - GNode *root; - - ETreeGNodeValueAtFn value_at; - - void *data; -} ETreeGNode; - -typedef struct { - ETreeModelClass parent_class; -} ETreeGNodeClass; - -GtkType e_tree_gnode_get_type (void); - -ETreeModel *e_tree_gnode_new (GNode *tree, - ETreeGNodeValueAtFn value_at, - void *data); - -#endif /* _E_TREE_GNODE_H_ */ diff --git a/widgets/e-table/e-tree-model.c b/widgets/e-table/e-tree-model.c index d67e59f3f5..c11cb58831 100644 --- a/widgets/e-table/e-tree-model.c +++ b/widgets/e-table/e-tree-model.c @@ -20,98 +20,222 @@ static ETableModel *e_tree_model_parent_class; +typedef struct { + gboolean expanded; + guint visible_descendents; + gpointer node_data; +} ENode; + +enum { + NODE_CHANGED, + NODE_INSERTED, + NODE_REMOVED, + LAST_SIGNAL +}; + +static guint e_tree_model_signals [LAST_SIGNAL] = {0, }; + +static void add_visible_descendents_to_array (ETreeModel *etm, GNode *gnode, int *row, int *count); + + /* virtual methods */ -static ETreePath* -etree_get_root (ETreeModel *etm) +static void +etree_destroy (GtkObject *object) { - /* shouldn't be called */ - g_assert(0); - return NULL; + /* XXX lots of stuff to free here */ } -static void* -etree_value_at (ETreeModel *etm, ETreePath* node, int col) +static ETreePath* +etree_get_root (ETreeModel *etm) { - /* shouldn't be called */ - g_assert(0); - return NULL; + return etm->root; } -static void -etree_set_value_at (ETreeModel *etm, ETreePath* node, int col, const void *val) +static ETreePath* +etree_get_parent (ETreeModel *etm, ETreePath *path) { - /* shouldn't be called */ - g_assert(0); + g_return_val_if_fail (path, NULL); + + return path->parent; } -static gboolean -etree_is_editable (ETreeModel *etm, ETreePath* node, int col) +static ETreePath* +etree_get_next (ETreeModel *etm, ETreePath *node) { - /* shouldn't be called */ - g_assert(0); - return FALSE; + g_return_val_if_fail (node, NULL); + + return g_node_next_sibling(node); } -static gboolean -etree_is_expanded (ETreeModel *etm, ETreePath* node) +static ETreePath* +etree_get_prev (ETreeModel *etm, ETreePath *node) { - /* shouldn't be called */ - g_assert(0); - return FALSE; + g_return_val_if_fail (node, NULL); + + return g_node_prev_sibling (node); } static guint etree_get_children (ETreeModel *etm, ETreePath* node, ETreePath ***paths) { - /* shouldn't be called */ - g_assert(0); - return 0; + guint n_children; + + g_return_val_if_fail (node, 0); + + n_children = g_node_n_children (node); + + if (paths) { + int i; + (*paths) = g_malloc (sizeof (ETreePath*) * n_children); + for (i = 0; i < n_children; i ++) { + (*paths)[i] = g_node_nth_child (node, i); + } + } + + return n_children; } -static void -etree_release_paths (ETreeModel *etm, ETreePath **paths, guint num_paths) +static gboolean +etree_is_expanded (ETreeModel *etm, ETreePath* node) { - /* shouldn't be called */ - g_assert(0); + g_return_val_if_fail (node && node->data, FALSE); + + return ((ENode*)node->data)->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 (!((ENode*)node->data)->expanded) + return FALSE; + } + + return TRUE; } static void etree_set_expanded (ETreeModel *etm, ETreePath* node, gboolean expanded) { - /* shouldn't be called */ - g_assert(0); + GNode *child; + ENode *enode; + int row; + + g_return_if_fail (node && node->data); + + enode = ((ENode*)node->data); + + if (enode->expanded == expanded) + return; + + enode->expanded = expanded; + + row = e_tree_model_row_of_node (etm, node) + 1; + + if (expanded) { + GNode *parent; + + if (e_tree_model_node_is_visible (etm, node)) { + enode->visible_descendents = 0; + for (child = g_node_first_child (node); child; + child = g_node_next_sibling (child)) { + add_visible_descendents_to_array (etm, child, &row, &enode->visible_descendents); + } + } + /* now iterate back up the tree, adding to our + ancestors' visible descendents */ + + for (parent = node->parent; parent; parent = parent->parent) { + ENode *parent_enode = (ENode*)parent->data; + parent_enode->visible_descendents += enode->visible_descendents; + } + } + else { + int i; + GNode *parent; + + if (e_tree_model_node_is_visible (etm, node)) { + for (i = 0; i < enode->visible_descendents; i ++) { + etm->row_array = g_array_remove_index (etm->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) { + ENode *parent_enode = (ENode*)parent->data; + + parent_enode->visible_descendents -= enode->visible_descendents; + } + + enode->visible_descendents = 0; + } } +/* fairly naive implementation */ static void -etree_destroy (GtkObject *object) +etree_set_expanded_recurse (ETreeModel *etm, ETreePath* node, gboolean expanded) { + ETreePath **paths; + guint num_children; + int i; + + e_tree_model_node_set_expanded (etm, node, expanded); + + num_children = e_tree_model_node_get_children (etm, node, &paths); + if (num_children) { + for (i = 0; i < num_children; i ++) { + e_tree_model_node_set_expanded_recurse (etm, paths[i], expanded); + } + + g_free (paths); + } } -guint -e_tree_model_node_num_visible_descendents (ETreeModel *etm, ETreePath *node) +static ETreePath * +etree_node_at_row (ETreeModel *etree, int row) { - int count = 1; - if (e_tree_model_node_is_expanded (etm, node)) { - ETreePath **paths; - int i; - int num_paths; + GNode *node = g_array_index (etree->row_array, GNode*, row); - num_paths = e_tree_model_node_get_children (etm, node, &paths); + return node; +} - for (i = 0; i < num_paths; i ++) - count += e_tree_model_node_num_visible_descendents(etm, paths[i]); + +/* ETable analogs */ +static void* +etree_value_at (ETreeModel *etm, ETreePath* node, int col) +{ + /* shouldn't be called */ + g_assert (0); + return NULL; +} - e_tree_model_release_paths (etm, paths, num_paths); - } +static void +etree_set_value_at (ETreeModel *etm, ETreePath* node, int col, const void *val) +{ + /* shouldn't be called */ + g_assert (0); +} - return count; +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) { - return e_tree_model_node_num_visible_descendents (E_TREE_MODEL (etm), e_tree_model_get_root (E_TREE_MODEL (etm))); + ETreeModel *tree = E_TREE_MODEL (etm); + return tree->row_array->len; } static void * @@ -155,6 +279,7 @@ etable_is_cell_editable (ETableModel *etm, int col, int row) return et_class->is_editable (etree, node, col); } + static void e_tree_model_class_init (GtkObjectClass *klass) { @@ -165,11 +290,38 @@ e_tree_model_class_init (GtkObjectClass *klass) klass->destroy = etree_destroy; + e_tree_model_signals [NODE_CHANGED] = + gtk_signal_new ("node_changed", + GTK_RUN_LAST, + klass->type, + GTK_SIGNAL_OFFSET (ETreeModelClass, node_changed), + gtk_marshal_NONE__POINTER, + GTK_TYPE_NONE, 1, GTK_TYPE_POINTER); + + e_tree_model_signals [NODE_INSERTED] = + gtk_signal_new ("node_inserted", + GTK_RUN_LAST, + klass->type, + GTK_SIGNAL_OFFSET (ETreeModelClass, node_inserted), + gtk_marshal_NONE__POINTER_POINTER, + GTK_TYPE_NONE, 2, GTK_TYPE_POINTER, GTK_TYPE_POINTER); + + e_tree_model_signals [NODE_REMOVED] = + gtk_signal_new ("node_removed", + GTK_RUN_LAST, + klass->type, + GTK_SIGNAL_OFFSET (ETreeModelClass, node_removed), + gtk_marshal_NONE__POINTER_POINTER, + GTK_TYPE_NONE, 2, GTK_TYPE_POINTER, GTK_TYPE_POINTER); + + gtk_object_class_add_signals (klass, e_tree_model_signals, LAST_SIGNAL); + 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; @@ -178,19 +330,68 @@ e_tree_model_class_init (GtkObjectClass *klass) #endif tree_class->get_root = etree_get_root; + tree_class->get_prev = etree_get_prev; + tree_class->get_next = etree_get_next; + tree_class->get_parent = etree_get_parent; + tree_class->value_at = etree_value_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->release_paths = etree_release_paths; 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; } E_MAKE_TYPE(e_tree_model, "ETreeModel", ETreeModel, e_tree_model_class_init, NULL, PARENT_TYPE) + /* signals */ +void +e_tree_model_node_changed (ETreeModel *tree_model, ETreePath *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_CHANGED]); +} + +void +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_INSERTED], + parent_node, inserted_node); +} + +void +e_tree_model_node_removed (ETreeModel *tree_model, ETreePath *parent_node, ETreePath *removed_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_REMOVED], + parent_node, removed_node); +} + + +void +e_tree_model_construct (ETreeModel *etree) +{ + etree->root = NULL; + etree->root_visible = TRUE; + etree->row_array = g_array_new (FALSE, FALSE, sizeof(GNode*)); +} ETreeModel * e_tree_model_new () @@ -202,52 +403,56 @@ e_tree_model_new () return et; } -static ETreePath * -e_tree_model_node_at_row_1 (ETreeModel *etree, int *row, ETreePath *node) +ETreePath * +e_tree_model_get_root (ETreeModel *etree) { - ETreePath *ret = NULL; - - if (*row == 0) - ret = node; - else if (e_tree_model_node_is_expanded (etree, node)) { - int num_children; - int i; - ETreePath **paths; + return ETM_CLASS(etree)->get_root(etree); +} - num_children = e_tree_model_node_get_children (etree, node, &paths); +ETreePath * +e_tree_model_node_at_row (ETreeModel *etree, int row) +{ + return ETM_CLASS(etree)->node_at_row (etree, row); +} - for (i = 0; i < num_children; i ++) { - ETreePath *p; +int +e_tree_model_row_of_node (ETreeModel *etree, ETreePath *node) +{ + int i; - (*row) --; + for (i = 0; i < etree->row_array->len; i ++) + if (g_array_index (etree->row_array, GNode*, i) == node) + return i; - p = e_tree_model_node_at_row_1 (etree, row, paths[i]); + return -1; +} - if (p) { - ret = p; - break; +void +e_tree_model_root_node_set_visible (ETreeModel *etm, gboolean visible) +{ + if (visible != etm->root_visible) { + etm->root_visible = visible; + if (etm->root) { + if (visible) { + etm->row_array = g_array_insert_val (etm->row_array, 0, etm->root); } + else { + ETreePath *root_path = e_tree_model_get_root (etm); + etm->row_array = g_array_remove_index (etm->row_array, 0); + e_tree_model_node_set_expanded (etm, root_path, TRUE); + } + + e_table_model_changed (E_TABLE_MODEL (etm)); } - - /* XXX need to find why this release is causing problems */ - /* e_tree_model_release_paths (etree, paths, num_children); */ } - - return ret; } -ETreePath * -e_tree_model_get_root (ETreeModel *etree) +gboolean +e_tree_model_root_node_is_visible (ETreeModel *etm) { - return ETM_CLASS(etree)->get_root(etree); + return etm->root_visible; } -ETreePath * -e_tree_model_node_at_row (ETreeModel *etree, int row) -{ - /* XXX icky, perform a depth first search of the tree. we need this optimized sorely */ - return e_tree_model_node_at_row_1 (etree, &row, ETM_CLASS(etree)->get_root(etree)); -} ETreePath * e_tree_model_node_get_next (ETreeModel *etree, ETreePath *node) @@ -264,18 +469,13 @@ e_tree_model_node_get_prev (ETreeModel *etree, ETreePath *node) guint e_tree_model_node_depth (ETreeModel *etree, ETreePath *path) { - return g_list_length (path) - 1; + return g_node_depth (path) - 1; } ETreePath * e_tree_model_node_get_parent (ETreeModel *etree, ETreePath *path) { - g_return_val_if_fail (path, NULL); - - if (path->next == NULL) - return NULL; - else - return g_list_copy (path->next); + return ETM_CLASS(etree)->get_parent(etree, path); } gboolean @@ -296,21 +496,190 @@ e_tree_model_node_is_expanded (ETreeModel *etree, ETreePath *path) return ETM_CLASS(etree)->is_expanded (etree, path); } +gboolean +e_tree_model_node_is_visible (ETreeModel *etree, ETreePath *path) +{ + return ETM_CLASS(etree)->is_visible (etree, path); +} + void e_tree_model_node_set_expanded (ETreeModel *etree, ETreePath *path, gboolean expanded) { ETM_CLASS(etree)->set_expanded (etree, path, expanded); } +void +e_tree_model_node_set_expanded_recurse (ETreeModel *etree, ETreePath *path, gboolean expanded) +{ + ETM_CLASS(etree)->set_expanded_recurse (etree, path, expanded); +} + guint e_tree_model_node_get_children (ETreeModel *etree, ETreePath *path, ETreePath ***paths) { return ETM_CLASS(etree)->get_children (etree, path, paths); } +guint +e_tree_model_node_num_visible_descendents (ETreeModel *etm, ETreePath *node) +{ + ENode *enode = (ENode*)node->data; + + return enode->visible_descendents; +} + +gpointer +e_tree_model_node_get_data (ETreeModel *etm, ETreePath *node) +{ + ENode *enode; + + g_return_val_if_fail (node && node->data, NULL); + + enode = (ENode*)node->data; + + return enode->node_data; +} + void -e_tree_model_release_paths (ETreeModel *etree, ETreePath **paths, guint num_paths) +e_tree_model_node_set_data (ETreeModel *etm, ETreePath *node, gpointer node_data) { - ETM_CLASS(etree)->release_paths (etree, paths, num_paths); + ENode *enode; + + g_return_if_fail (node && node->data); + + enode = (ENode*)node->data; + + enode->node_data = node_data; } +ETreePath* +e_tree_model_node_insert (ETreeModel *tree_model, + ETreePath *parent_path, + int position, + gpointer node_data) +{ + ENode *node; + ETreePath *new_path; + + g_return_val_if_fail (parent_path != NULL || tree_model->root == NULL, NULL); + + node = g_new0 (ENode, 1); + + node->expanded = FALSE; + node->node_data = node_data; + + if (parent_path != NULL) { + + new_path = g_node_new (node); + + g_node_insert (parent_path, position, new_path); + + if (e_tree_model_node_is_visible (tree_model, new_path)) { + int parent_row; + GNode *node; + + /* we need to iterate back up to the root, incrementing the number of visible + descendents */ + for (node = parent_path; node; node = node->parent) { + ENode *parent_enode = (ENode*)node->data; + + parent_enode->visible_descendents ++; + } + + /* finally, insert a row into the table */ + parent_row = e_tree_model_row_of_node (tree_model, parent_path); + + tree_model->row_array = g_array_insert_val (tree_model->row_array, + parent_row + position + 1, new_path); + } + } + else { + tree_model->root = g_node_new (node); + if (tree_model->root_visible) + tree_model->row_array = g_array_insert_val (tree_model->row_array, 0, tree_model->root); + new_path = tree_model->root; + } + + /* FIXME: find out the number of descendents that will be visible, + and call insert_row that many times. */ + e_table_model_changed (E_TABLE_MODEL(tree_model)); + + return new_path; +} + +ETreePath * +e_tree_model_node_insert_before (ETreeModel *etree, ETreePath *parent, + ETreePath *sibling, gpointer node_data) +{ + return e_tree_model_node_insert (etree, parent, + g_node_child_position (parent, sibling), + node_data); +} + +gpointer +e_tree_model_node_remove (ETreeModel *etree, ETreePath *path) +{ + GNode *parent = path->parent; + ENode *enode = (ENode*)path->data; + gpointer ret = enode->node_data; + + g_return_val_if_fail (!g_node_first_child(path), NULL); + + /* clean up the display */ + if (parent) { + if (e_tree_model_node_is_visible (etree, path)) { + int row = e_tree_model_row_of_node (etree, path); + etree->row_array = g_array_remove_index (etree->row_array, row); + e_table_model_row_deleted (E_TABLE_MODEL (etree), row); + + /* we need to iterate back up to the root, incrementing the number of visible + descendents */ + for (; parent; parent = parent->parent) { + ENode *parent_enode = (ENode*)parent->data; + + parent_enode->visible_descendents --; + } + } + } + else if (path == etree->root) { + etree->root = NULL; + if (etree->root_visible) { + etree->row_array = g_array_remove_index (etree->row_array, 0); + e_table_model_row_deleted (E_TABLE_MODEL (etree), 0); + } + } + else { + /* XXX invalid path */ + return NULL; + } + + + /* now free up the storage from that path */ + g_node_destroy (path); + g_free (enode); + + return ret; +} + + +static void +add_visible_descendents_to_array (ETreeModel *etm, GNode *gnode, int *row, int *count) +{ + GNode *child; + ENode *enode; + + /* add a row for this node */ + etm->row_array = g_array_insert_val (etm->row_array, (*row), gnode); + e_table_model_row_inserted (E_TABLE_MODEL (etm), (*row)++); + (*count) ++; + + /* then loop over its children, calling this routine for each + of them */ + enode = (ENode*)gnode->data; + if (enode->expanded) { + for (child = g_node_first_child (gnode); child; + child = g_node_next_sibling (child)) { + add_visible_descendents_to_array (etm, child, row, count); + } + } +} diff --git a/widgets/e-table/e-tree-model.h b/widgets/e-table/e-tree-model.h index 6924f3ea0e..45351b8f6c 100644 --- a/widgets/e-table/e-tree-model.h +++ b/widgets/e-table/e-tree-model.h @@ -10,18 +10,13 @@ #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 gpointer ETreePathItem; -typedef GList ETreePath; +typedef GNode ETreePath; typedef struct { ETableModel base; - - ETableModel *source; - - ETreePath *root_node; - - GArray *array; - + GNode *root; + gboolean root_visible; + GArray *row_array; /* used in the mapping between ETable and our tree */ } ETreeModel; typedef struct { @@ -32,41 +27,75 @@ typedef struct { */ ETreePath *(*get_root) (ETreeModel *etm); + ETreePath *(*get_parent) (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); + + 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); + ETreePath* (*node_at_row) (ETreeModel *etm, int row); + + /* + * 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); - guint (*get_children) (ETreeModel *etm, ETreePath* node, ETreePath ***paths); - void (*release_paths) (ETreeModel *etm, ETreePath **paths, guint num_paths); - gboolean (*is_expanded) (ETreeModel *etm, ETreePath* node); - void (*set_expanded) (ETreeModel *etm, ETreePath* node, gboolean expanded); /* * 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); } ETreeModelClass; GtkType e_tree_model_get_type (void); - +void e_tree_model_construct (ETreeModel *etree); ETreeModel *e_tree_model_new (void); -/* operations on "nodes" in the tree */ -ETreePath * e_tree_model_get_root (ETreeModel *etree); -ETreePath * e_tree_model_node_at_row (ETreeModel *etree, int row); -guint e_tree_model_node_depth (ETreeModel *etree, ETreePath *path); +/* 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_next (ETreeModel *etree, ETreePath *path); -ETreePath *e_tree_model_node_get_prev (ETreeModel *etree, ETreePath *path); -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); -guint e_tree_model_node_get_children (ETreeModel *etree, ETreePath *path, ETreePath ***paths); -void e_tree_model_release_paths (ETreeModel *etree, ETreePath **paths, guint num_paths); -guint e_tree_model_node_num_visible_descendents (ETreeModel *etm, ETreePath *node); +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); + +/* 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_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); +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); + +/* +** 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); #endif /* _E_TREE_MODEL_H */ diff --git a/widgets/e-table/e-tree-simple.c b/widgets/e-table/e-tree-simple.c new file mode 100644 index 0000000000..9bf747a43f --- /dev/null +++ b/widgets/e-table/e-tree-simple.c @@ -0,0 +1,73 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * e-tree-simple.c: a Tree Model that offers a function pointer + * interface to using ETreeModel, similar to ETableSimple. + * + * Author: + * Chris Toshok (toshok@helixcode.com) + * + * (C) 2000 Helix Code, Inc. */ + +#include <config.h> +#include <gtk/gtksignal.h> +#include "e-util/e-util.h" +#include "e-tree-simple.h" + +#define PARENT_TYPE E_TREE_MODEL_TYPE + +static void * +simple_value_at (ETreeModel *etm, ETreePath *node, int col) +{ + ETreeSimple *simple = E_TREE_SIMPLE(etm); + + return simple->value_at (etm, node, col, simple->model_data); +} + +static void +simple_set_value_at (ETreeModel *etm, ETreePath *node, int col, const void *val) +{ + ETreeSimple *simple = E_TREE_SIMPLE(etm); + + simple->set_value_at (etm, node, col, val, simple->model_data); +} + +static gboolean +simple_is_editable (ETreeModel *etm, ETreePath *node, int col) +{ + ETreeSimple *simple = E_TREE_SIMPLE(etm); + + return simple->is_editable (etm, node, col, simple->model_data); +} + +static void +e_tree_simple_class_init (GtkObjectClass *object_class) +{ + ETreeModelClass *model_class = (ETreeModelClass *) object_class; + + model_class->value_at = simple_value_at; + model_class->set_value_at = simple_set_value_at; + model_class->is_editable = simple_is_editable; +} + +E_MAKE_TYPE(e_tree_simple, "ETreeSimple", ETreeSimple, e_tree_simple_class_init, NULL, PARENT_TYPE) + +ETreeModel * +e_tree_simple_new (ETreeSimpleValueAtFn value_at, + ETreeSimpleSetValueAtFn set_value_at, + ETreeSimpleIsEditableFn is_editable, + gpointer model_data) +{ + ETreeSimple *etg; + + etg = gtk_type_new (e_tree_simple_get_type ()); + + e_tree_model_construct (E_TREE_MODEL (etg)); + + etg->value_at = value_at; + etg->set_value_at = set_value_at; + etg->is_editable = is_editable; + etg->model_data = model_data; + + return (ETreeModel*)etg; +} + diff --git a/widgets/e-table/e-tree-simple.h b/widgets/e-table/e-tree-simple.h new file mode 100644 index 0000000000..6820b14673 --- /dev/null +++ b/widgets/e-table/e-tree-simple.h @@ -0,0 +1,40 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ + +#ifndef _E_TREE_SIMPLE_H_ +#define _E_TREE_SIMPLE_H_ + +#include "e-tree-model.h" + +#define E_TREE_SIMPLE_TYPE (e_tree_simple_get_type ()) +#define E_TREE_SIMPLE(o) (GTK_CHECK_CAST ((o), E_TREE_SIMPLE_TYPE, ETreeSimple)) +#define E_TREE_SIMPLE_CLASS(k) (GTK_CHECK_CLASS_CAST((k), E_TREE_SIMPLE_TYPE, ETreeSimpleClass)) +#define E_IS_TREE_SIMPLE(o) (GTK_CHECK_TYPE ((o), E_TREE_SIMPLE_TYPE)) +#define E_IS_TREE_SIMPLE_CLASS(k) (GTK_CHECK_CLASS_TYPE ((k), E_TREE_SIMPLE_TYPE)) + + +typedef void* (*ETreeSimpleValueAtFn) (ETreeModel *etree, ETreePath *path, int col, void *model_data); +typedef void (*ETreeSimpleSetValueAtFn) (ETreeModel *etree, ETreePath *path, int col, const void *val, void *model_data); +typedef gboolean (*ETreeSimpleIsEditableFn) (ETreeModel *etree, ETreePath *path, int col, void *model_data); + +typedef struct { + ETreeModel parent; + + ETreeSimpleValueAtFn value_at; + ETreeSimpleSetValueAtFn set_value_at; + ETreeSimpleIsEditableFn is_editable; + + gpointer model_data; +} ETreeSimple; + +typedef struct { + ETreeModelClass parent_class; +} ETreeSimpleClass; + +GtkType e_tree_simple_get_type (void); + +ETreeModel *e_tree_simple_new (ETreeSimpleValueAtFn value_at, + ETreeSimpleSetValueAtFn set_value_at, + ETreeSimpleIsEditableFn is_editable, + gpointer model_data); + +#endif /* _E_TREE_SIMPLE_H_ */ diff --git a/widgets/table/e-cell-tree.c b/widgets/table/e-cell-tree.c index 9f40bd0eae..c3459ce4f8 100644 --- a/widgets/table/e-cell-tree.c +++ b/widgets/table/e-cell-tree.c @@ -49,6 +49,21 @@ static ECellClass *parent_class; static GdkPixbuf *tree_expanded_pixbuf, *tree_unexpanded_pixbuf; +#define INDENT_AMOUNT 16 + +static int +visible_depth_of_node (ETreeModel *tree_model, ETreePath *path) +{ + return (e_tree_model_node_depth (tree_model, path) + - (e_tree_model_root_node_is_visible (tree_model) ? 0 : 1)); +} + +static gint +offset_of_node (ETreeModel *tree_model, ETreePath *path) +{ + return (visible_depth_of_node(tree_model, path) + 1) * INDENT_AMOUNT; +} + static ETreePath* e_cell_tree_get_node (ETreeModel *tree_model, int row) { @@ -136,7 +151,6 @@ ect_unrealize (ECellView *ecv) (* parent_class->unrealize) (ecv); } -#define INDENT_AMOUNT 16 /* * ECell::draw method */ @@ -167,7 +181,7 @@ ect_draw (ECellView *ecell_view, GdkDrawable *drawable, */ node = e_cell_tree_get_node (tree_model, row); - offset = (e_tree_model_node_depth (tree_model, node) + 1) * INDENT_AMOUNT; + offset = offset_of_node (tree_model, node); expandable = e_tree_model_node_is_expandable (tree_model, node); expanded = e_tree_model_node_is_expanded (tree_model, node); subcell_offset = offset; @@ -196,15 +210,8 @@ ect_draw (ECellView *ecell_view, GdkDrawable *drawable, rect.x, rect.y, rect.width, rect.height); gdk_gc_set_foreground (tree_view->gc, foreground); + /* draw our lines */ if (E_CELL_TREE(tree_view->cell_view.ecell)->draw_lines) { - /* draw our lines */ - gdk_draw_line (drawable, tree_view->gc, - rect.x + offset - INDENT_AMOUNT / 2, - rect.y, - rect.x + offset - INDENT_AMOUNT / 2, - (e_tree_model_node_get_next (tree_model, node) - ? rect.y + rect.height - : rect.y + rect.height / 2)); gdk_draw_line (drawable, tree_view->gc, rect.x + offset - INDENT_AMOUNT / 2 + 1, @@ -212,12 +219,22 @@ ect_draw (ECellView *ecell_view, GdkDrawable *drawable, rect.x + offset, rect.y + rect.height / 2); + if (visible_depth_of_node (tree_model, node) != 0) { + gdk_draw_line (drawable, tree_view->gc, + rect.x + offset - INDENT_AMOUNT / 2, + rect.y, + rect.x + offset - INDENT_AMOUNT / 2, + (e_tree_model_node_get_next (tree_model, node) + ? rect.y + rect.height + : rect.y + rect.height / 2)); + } + /* now traverse back up to the root of the tree, checking at 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); offset -= INDENT_AMOUNT; - while (node && ! e_tree_model_node_is_root (tree_model, node)) { + while (node && visible_depth_of_node (tree_model, node) != 0) { if (e_tree_model_node_get_next(tree_model, node)) { gdk_draw_line (drawable, tree_view->gc, rect.x + offset - INDENT_AMOUNT / 2, @@ -255,7 +272,7 @@ ect_draw (ECellView *ecell_view, GdkDrawable *drawable, subcell_offset pixels */ e_cell_draw (tree_view->subcell_view, drawable, model_col, view_col, row, selected, - x1 + subcell_offset, y1, x2 + subcell_offset, y2); + x1 + subcell_offset, y1, x2, y2); } /* @@ -267,13 +284,12 @@ ect_event (ECellView *ecell_view, GdkEvent *event, int model_col, int view_col, ECellTreeView *tree_view = (ECellTreeView *) ecell_view; switch (event->type) { - case GDK_BUTTON_PRESS: - case GDK_BUTTON_RELEASE: { + case GDK_BUTTON_PRESS: { /* if the event happened in our area of control (and we care about it), handle it. */ ETreeModel *tree_model = e_cell_tree_get_tree_model (ecell_view->e_table_model, row); ETreePath *node = e_cell_tree_get_node (tree_model, row); - int offset = (e_tree_model_node_depth (tree_model, node) + 1) * INDENT_AMOUNT; + int offset = offset_of_node (tree_model, node); /* 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) { @@ -284,6 +300,8 @@ ect_event (ECellView *ecell_view, GdkEvent *event, int model_col, int view_col, } return TRUE; } + else if (event->button.x < (offset - INDENT_AMOUNT)) + return TRUE; } default: /* otherwise, pass it off to our subcell_view */ @@ -309,7 +327,10 @@ ect_height (ECellView *ecell_view, int model_col, int view_col, int row) static void * ect_enter_edit (ECellView *ecell_view, int model_col, int view_col, int row) { - return NULL; + /* just defer to our subcell's view */ + ECellTreeView *tree_view = (ECellTreeView *) ecell_view; + + return e_cell_enter_edit (tree_view->subcell_view, model_col, view_col, row); } /* @@ -318,6 +339,10 @@ ect_enter_edit (ECellView *ecell_view, int model_col, int view_col, int row) static void ect_leave_edit (ECellView *ecell_view, int model_col, int view_col, int row, void *edit_context) { + /* just defer to our subcell's view */ + ECellTreeView *tree_view = (ECellTreeView *) ecell_view; + + e_cell_leave_edit (tree_view->subcell_view, model_col, view_col, row, edit_context); } /* diff --git a/widgets/table/e-table-item.c b/widgets/table/e-table-item.c index 3a4d98bf91..0de4181cdf 100644 --- a/widgets/table/e-table-item.c +++ b/widgets/table/e-table-item.c @@ -1197,6 +1197,14 @@ eti_event (GnomeCanvasItem *item, GdkEvent *e) if (!find_cell (eti, e->button.x, e->button.y, &col, &row, &x1, &y1)) return TRUE; + if (eti->cursor_row != row || eti->cursor_col != col){ + /* + * Focus the cell, and select the row + */ + e_table_item_leave_edit (eti); + e_table_item_focus (eti, col, row); + } + if (eti->cursor_row == row && eti->cursor_col == col){ ecol = e_table_header_get_column (eti->header, col); @@ -1209,12 +1217,6 @@ eti_event (GnomeCanvasItem *item, GdkEvent *e) e->button.y = y1; e_cell_event (ecell_view, e, ecol->col_idx, col, row); - } else { - /* - * Focus the cell, and select the row - */ - e_table_item_leave_edit (eti); - e_table_item_focus (eti, col, row); } break; case 3: diff --git a/widgets/table/e-tree-example-1.c b/widgets/table/e-tree-example-1.c index e5563a8071..c598e33ea4 100644 --- a/widgets/table/e-tree-example-1.c +++ b/widgets/table/e-tree-example-1.c @@ -4,7 +4,6 @@ #include <string.h> #include <gnome.h> #include "e-util/e-cursors.h" -#include "e-tree-gnode.h" #include "e-table-header.h" #include "e-table-header-item.h" #include "e-table-item.h" @@ -12,6 +11,7 @@ #include "e-cell-tree.h" #include "e-cell-checkbox.h" #include "e-table.h" +#include "e-tree-simple.h" #include <gdk-pixbuf/gdk-pixbuf.h> @@ -56,6 +56,8 @@ char *headers [COLS] = { "Date" }; +GtkWidget *e_table; + /* * ETableSimple callbacks * These are the callbacks that define the behavior of our custom model. @@ -73,12 +75,12 @@ my_col_count (ETableModel *etc, void *data) return COLS; } -/* This function returns the value at a particular point in our ETableModel. */ +/* This function returns the value at a particular point in our ETreeModel. */ static void * -my_value_at (ETreeModel *etc, GNode *node, int col, void *data) +my_value_at (ETreeModel *etm, ETreePath *path, int col, void *model_data) { switch (col) { - case 0: return "Re: Two things"; + case 0: return e_tree_model_node_get_data (etm, path); case 1: return "Chris Toshok"; case 2: return "toshok@helixcode.com"; case 3: return "Jun 07 2000"; @@ -86,67 +88,163 @@ my_value_at (ETreeModel *etc, GNode *node, int col, void *data) } } -/* This function sets the value at a particular point in our ETableModel. */ +/* This function sets the value at a particular point in our ETreeModel. */ static void -my_set_value_at (ETableModel *etc, GNode *node, int col, const void *val, void *data) +my_set_value_at (ETreeModel *etm, ETreePath *path, int col, const void *val, void *model_data) { + if (col == 0) { + char *str = e_tree_model_node_get_data (etm, path); + g_free (str); + e_tree_model_node_set_data (etm, path, g_strdup(val)); + } } /* This function returns whether a particular cell is editable. */ static gboolean -my_is_editable (ETableModel *etc, GNode *node, int col, void *data) +my_is_editable (ETreeModel *etm, ETreePath *path, int col, void *model_data) { - return FALSE; + if (col == 0) + return TRUE; + else + return FALSE; } -/* This function duplicates the value passed to it. */ -static void * -my_duplicate_value (ETableModel *etc, int col, const void *value, void *data) +static void +toggle_root (GtkButton *button, gpointer data) +{ + ETreeModel *e_tree_model = (ETreeModel*)data; + e_tree_model_root_node_set_visible (e_tree_model, !e_tree_model_root_node_is_visible (e_tree_model)); +} + +static void +add_sibling (GtkButton *button, gpointer data) +{ + ETreeModel *e_tree_model = E_TREE_MODEL (data); + int selected_row; + ETreePath *selected_node; + ETreePath *parent_node; + + selected_row = e_table_get_selected_view_row (E_TABLE (e_table)); + if (selected_row == -1) + return; + + selected_node = e_tree_model_node_at_row (e_tree_model, selected_row); + g_assert (selected_node); + + parent_node = e_tree_model_node_get_parent (e_tree_model, selected_node); + + e_tree_model_node_insert_before (e_tree_model, parent_node, + selected_node, "User added sibling"); + +} + +static void +add_child (GtkButton *button, gpointer data) +{ + ETreeModel *e_tree_model = E_TREE_MODEL (data); + int selected_row; + ETreePath *selected_node; + + selected_row = e_table_get_selected_view_row (E_TABLE (e_table)); + if (selected_row == -1) + return; + + selected_node = e_tree_model_node_at_row (e_tree_model, selected_row); + g_assert (selected_node); + + e_tree_model_node_insert (e_tree_model, selected_node, + 0, "User added child"); +} + +static void +remove_node (GtkButton *button, gpointer data) { - return g_strdup (value); + ETreeModel *e_tree_model = E_TREE_MODEL (data); + int selected_row; + char *str; + ETreePath *selected_node; + + selected_row = e_table_get_selected_view_row (E_TABLE (e_table)); + if (selected_row == -1) + return; + + selected_node = e_tree_model_node_at_row (e_tree_model, selected_row); + g_assert (selected_node); + + if (e_tree_model_node_get_children (e_tree_model, selected_node, NULL) > 0) + return; + + str = (char*)e_tree_model_node_remove (e_tree_model, selected_node); + printf ("removed node %s\n", str); + g_free (str); } -/* This function frees the value passed to it. */ static void -my_free_value (ETableModel *etc, int col, void *value, void *data) +expand_all (GtkButton *button, gpointer data) { - g_free (value); + ETreeModel *e_tree_model = E_TREE_MODEL (data); + int selected_row; + ETreePath *selected_node; + + selected_row = e_table_get_selected_view_row (E_TABLE (e_table)); + if (selected_row == -1) + return; + + selected_node = e_tree_model_node_at_row (e_tree_model, selected_row); + g_assert (selected_node); + + e_tree_model_node_set_expanded_recurse (e_tree_model, selected_node, TRUE); } -/* This function is for when the model is unfrozen. This can mostly - be ignored for simple models. */ static void -my_thaw (ETableModel *etc, void *data) +collapse_all (GtkButton *button, gpointer data) { + ETreeModel *e_tree_model = E_TREE_MODEL (data); + int selected_row; + ETreePath *selected_node; + + selected_row = e_table_get_selected_view_row (E_TABLE (e_table)); + if (selected_row == -1) + return; + + selected_node = e_tree_model_node_at_row (e_tree_model, selected_row); + g_assert (selected_node); + + e_tree_model_node_set_expanded_recurse (e_tree_model, selected_node, FALSE); } /* We create a window containing our new tree. */ static void create_tree (void) { - GtkWidget *e_table, *window, *frame; + GtkWidget *window, *frame, *button, *vbox; ECell *cell_left_just; ECell *cell_tree; ETableHeader *e_table_header; int i, j; ETreeModel *e_tree_model = NULL; - GNode *root_node; + ETreePath *root_node; + + /* here we create our model. This uses the functions we defined + earlier. */ + e_tree_model = e_tree_simple_new (my_value_at, + my_set_value_at, + my_is_editable, + NULL); /* create a root node with 5 children */ - root_node = g_node_new (NULL); + root_node = e_tree_model_node_insert (e_tree_model, NULL, + 0, g_strdup("Root Node")); + for (i = 0; i < 5; i++){ - GNode *n = g_node_insert (root_node, 0, g_node_new(NULL)); + ETreePath *n = e_tree_model_node_insert (e_tree_model, + root_node, 0, g_strdup("First level of children")); for (j = 0; j < 5; j ++) { - g_node_insert (n, 0, g_node_new(NULL)); + e_tree_model_node_insert (e_tree_model, + n, 0, g_strdup("Second level of children")); } } - /* Next we create our model. This uses the functions we defined - earlier. */ - e_tree_model = e_tree_gnode_new (root_node, - my_value_at, - NULL); - /* * Next we create a header. The ETableHeader is used in two * different way. The first is the full_header. This is the @@ -200,6 +298,7 @@ create_tree (void) window = gtk_window_new (GTK_WINDOW_TOPLEVEL); /* This frame is simply to get a bevel around our table. */ + vbox = gtk_vbox_new (FALSE, 0); frame = gtk_frame_new (NULL); /* @@ -213,8 +312,34 @@ create_tree (void) /* Build the gtk widget hierarchy. */ gtk_container_add (GTK_CONTAINER (frame), e_table); - gtk_container_add (GTK_CONTAINER (window), frame); + gtk_box_pack_start (GTK_BOX (vbox), frame, TRUE, TRUE, 0); + + button = gtk_button_new_with_label ("Toggle Root Node"); + gtk_signal_connect (GTK_OBJECT (button), "clicked", toggle_root, e_tree_model); + gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0); + + button = gtk_button_new_with_label ("Add Sibling"); + gtk_signal_connect (GTK_OBJECT (button), "clicked", add_sibling, e_tree_model); + gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0); + button = gtk_button_new_with_label ("Add Child"); + gtk_signal_connect (GTK_OBJECT (button), "clicked", add_child, e_tree_model); + gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0); + + button = gtk_button_new_with_label ("Remove Node"); + gtk_signal_connect (GTK_OBJECT (button), "clicked", remove_node, e_tree_model); + gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0); + + button = gtk_button_new_with_label ("Expand All Below"); + gtk_signal_connect (GTK_OBJECT (button), "clicked", expand_all, e_tree_model); + gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0); + + button = gtk_button_new_with_label ("Collapse All Below"); + gtk_signal_connect (GTK_OBJECT (button), "clicked", collapse_all, e_tree_model); + gtk_box_pack_end (GTK_BOX (vbox), button, FALSE, FALSE, 0); + + gtk_container_add (GTK_CONTAINER (window), vbox); + /* Size the initial window. */ gtk_widget_set_usize (window, 200, 200); diff --git a/widgets/table/e-tree-gnode.c b/widgets/table/e-tree-gnode.c deleted file mode 100644 index ce53751b56..0000000000 --- a/widgets/table/e-tree-gnode.c +++ /dev/null @@ -1,210 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ -/* - * e-tree-gnode.c: a Tree Model that reflects a GNode structure visually. - * - * Author: - * Chris Toshok (toshok@helixcode.com) - * - * (C) 2000 Helix Code, Inc. - */ -#include <config.h> -#include <gtk/gtksignal.h> -#include "e-util/e-util.h" -#include "e-tree-gnode.h" - -#define PARENT_TYPE E_TREE_MODEL_TYPE - -static ETreePath * -gnode_get_root (ETreeModel *etm) -{ - ETreeGNode *etg = E_TREE_GNODE (etm); - ETreePath *path = NULL; - - path = g_list_append(path, etg->root); - - return path; -} - -static ETreePath * -gnode_get_prev (ETreeModel *etm, ETreePath *node) -{ - ETreePath *prev_path; - - GNode *gnode; - GNode *prev_sibling; - - g_return_val_if_fail (node && node->data, NULL); - - gnode = (GNode*)node->data; - prev_sibling = g_node_prev_sibling(gnode); - - if (!prev_sibling) - return NULL; - - prev_path = g_list_copy (node->next); - prev_path = g_list_prepend (prev_path, prev_sibling); - return prev_path; -} - -static ETreePath * -gnode_get_next (ETreeModel *etm, ETreePath *node) -{ - ETreePath *next_path; - GNode *gnode; - GNode *next_sibling; - - g_return_val_if_fail (node && node->data, NULL); - - gnode = (GNode*)node->data; - next_sibling = g_node_next_sibling(gnode); - - if (!next_sibling) - return NULL; - - next_path = g_list_copy (node->next); - next_path = g_list_prepend (next_path, next_sibling); - return next_path; -} - -static void * -gnode_value_at (ETreeModel *etm, ETreePath *node, int col) -{ - ETreeGNode *etg = E_TREE_GNODE (etm); - GNode *gnode; - - g_return_val_if_fail (node && node->data, NULL); - - gnode = (GNode*)node->data; - - return etg->value_at (etm, gnode, col, etg->data); -} - -static void -gnode_set_value_at (ETreeModel *etm, ETreePath *node, int col, const void *val) -{ - ETreeGNode *etg = E_TREE_GNODE (etm); - GNode *gnode; - - g_return_if_fail (node && node->data); - - gnode = (GNode*)node->data; - - /* XXX */ -} - -static gboolean -gnode_is_editable (ETreeModel *etm, ETreePath *node, int col) -{ - ETreeGNode *etg = E_TREE_GNODE (etm); - GNode *gnode; - - g_return_val_if_fail (node && node->data, FALSE); - - gnode = (GNode*)node->data; - - /* XXX */ - return FALSE; -} - -static guint -gnode_get_children (ETreeModel *etm, ETreePath *node, ETreePath ***paths) -{ - ETreeGNode *etg = E_TREE_GNODE (etm); - GNode *gnode; - guint n_children; - - g_return_val_if_fail (node && node->data, 0); - - gnode = (GNode*)node->data; - - n_children = g_node_n_children (gnode); - - if (paths) - { - int i; - (*paths) = g_malloc (sizeof (ETreePath*) * n_children); - for (i = 0; i < n_children; i ++) { - (*paths)[i] = g_list_copy (node); - (*paths)[i] = g_list_prepend ((*paths)[i], g_node_nth_child (gnode, i)); - } - } - - return n_children; -} - -static void -gnode_release_paths (ETreeModel *etm, ETreePath **paths, guint num_paths) -{ - guint i; - g_return_if_fail (paths); - - for (i = 0; i < num_paths; i ++) - g_list_free (paths[i]); - g_free (paths); -} - -static gboolean -gnode_is_expanded (ETreeModel *etm, ETreePath *node) -{ - ETreeGNode *etg = E_TREE_GNODE (etm); - GNode *gnode; - - g_return_val_if_fail (node && node->data, FALSE); - - gnode = (GNode*)node->data; - - return (gboolean)gnode->data; -} - -static void -gnode_set_expanded (ETreeModel *etm, ETreePath *node, gboolean expanded) -{ - ETreeGNode *etg = E_TREE_GNODE (etm); - GNode *gnode; - int num_descendents; - - g_return_if_fail (node && node->data); - - gnode = (GNode*)node->data; - - /* XXX */ - gnode->data = (gpointer)expanded; - - e_table_model_changed (E_TABLE_MODEL(etm)); -} - -static void -e_tree_gnode_class_init (GtkObjectClass *object_class) -{ - ETreeModelClass *model_class = (ETreeModelClass *) object_class; - - model_class->get_root = gnode_get_root; - model_class->get_next = gnode_get_next; - model_class->get_prev = gnode_get_prev; - model_class->value_at = gnode_value_at; - model_class->set_value_at = gnode_set_value_at; - model_class->is_editable = gnode_is_editable; - model_class->get_children = gnode_get_children; - model_class->release_paths = gnode_release_paths; - model_class->is_expanded = gnode_is_expanded; - model_class->set_expanded = gnode_set_expanded; -} - -E_MAKE_TYPE(e_tree_gnode, "ETreeGNode", ETreeGNode, e_tree_gnode_class_init, NULL, PARENT_TYPE) - -ETreeModel * -e_tree_gnode_new (GNode *root_node, - ETreeGNodeValueAtFn value_at, - void *data) -{ - ETreeGNode *etg; - - etg = gtk_type_new (e_tree_gnode_get_type ()); - - etg->root = root_node; - - etg->value_at = value_at; - etg->data = data; - - return (ETreeModel*)etg; -} diff --git a/widgets/table/e-tree-gnode.h b/widgets/table/e-tree-gnode.h deleted file mode 100644 index 56e6b19b80..0000000000 --- a/widgets/table/e-tree-gnode.h +++ /dev/null @@ -1,37 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ - -#ifndef _E_TREE_GNODE_H_ -#define _E_TREE_GNODE_H_ - -#include "e-tree-model.h" - -#define E_TREE_GNODE_TYPE (e_tree_gnode_get_type ()) -#define E_TREE_GNODE(o) (GTK_CHECK_CAST ((o), E_TREE_GNODE_TYPE, ETreeGNode)) -#define E_TREE_GNODE_CLASS(k) (GTK_CHECK_CLASS_CAST((k), E_TREE_GNODE_TYPE, ETreeGNodeClass)) -#define E_IS_TREE_GNODE(o) (GTK_CHECK_TYPE ((o), E_TREE_GNODE_TYPE)) -#define E_IS_TREE_GNODE_CLASS(k) (GTK_CHECK_CLASS_TYPE ((k), E_TREE_GNODE_TYPE)) - -typedef void *(*ETreeGNodeValueAtFn)(ETreeModel *model, GNode *node, int col, void *data); - - -typedef struct { - ETreeModel parent; - - GNode *root; - - ETreeGNodeValueAtFn value_at; - - void *data; -} ETreeGNode; - -typedef struct { - ETreeModelClass parent_class; -} ETreeGNodeClass; - -GtkType e_tree_gnode_get_type (void); - -ETreeModel *e_tree_gnode_new (GNode *tree, - ETreeGNodeValueAtFn value_at, - void *data); - -#endif /* _E_TREE_GNODE_H_ */ diff --git a/widgets/table/e-tree-model.c b/widgets/table/e-tree-model.c index d67e59f3f5..c11cb58831 100644 --- a/widgets/table/e-tree-model.c +++ b/widgets/table/e-tree-model.c @@ -20,98 +20,222 @@ static ETableModel *e_tree_model_parent_class; +typedef struct { + gboolean expanded; + guint visible_descendents; + gpointer node_data; +} ENode; + +enum { + NODE_CHANGED, + NODE_INSERTED, + NODE_REMOVED, + LAST_SIGNAL +}; + +static guint e_tree_model_signals [LAST_SIGNAL] = {0, }; + +static void add_visible_descendents_to_array (ETreeModel *etm, GNode *gnode, int *row, int *count); + + /* virtual methods */ -static ETreePath* -etree_get_root (ETreeModel *etm) +static void +etree_destroy (GtkObject *object) { - /* shouldn't be called */ - g_assert(0); - return NULL; + /* XXX lots of stuff to free here */ } -static void* -etree_value_at (ETreeModel *etm, ETreePath* node, int col) +static ETreePath* +etree_get_root (ETreeModel *etm) { - /* shouldn't be called */ - g_assert(0); - return NULL; + return etm->root; } -static void -etree_set_value_at (ETreeModel *etm, ETreePath* node, int col, const void *val) +static ETreePath* +etree_get_parent (ETreeModel *etm, ETreePath *path) { - /* shouldn't be called */ - g_assert(0); + g_return_val_if_fail (path, NULL); + + return path->parent; } -static gboolean -etree_is_editable (ETreeModel *etm, ETreePath* node, int col) +static ETreePath* +etree_get_next (ETreeModel *etm, ETreePath *node) { - /* shouldn't be called */ - g_assert(0); - return FALSE; + g_return_val_if_fail (node, NULL); + + return g_node_next_sibling(node); } -static gboolean -etree_is_expanded (ETreeModel *etm, ETreePath* node) +static ETreePath* +etree_get_prev (ETreeModel *etm, ETreePath *node) { - /* shouldn't be called */ - g_assert(0); - return FALSE; + g_return_val_if_fail (node, NULL); + + return g_node_prev_sibling (node); } static guint etree_get_children (ETreeModel *etm, ETreePath* node, ETreePath ***paths) { - /* shouldn't be called */ - g_assert(0); - return 0; + guint n_children; + + g_return_val_if_fail (node, 0); + + n_children = g_node_n_children (node); + + if (paths) { + int i; + (*paths) = g_malloc (sizeof (ETreePath*) * n_children); + for (i = 0; i < n_children; i ++) { + (*paths)[i] = g_node_nth_child (node, i); + } + } + + return n_children; } -static void -etree_release_paths (ETreeModel *etm, ETreePath **paths, guint num_paths) +static gboolean +etree_is_expanded (ETreeModel *etm, ETreePath* node) { - /* shouldn't be called */ - g_assert(0); + g_return_val_if_fail (node && node->data, FALSE); + + return ((ENode*)node->data)->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 (!((ENode*)node->data)->expanded) + return FALSE; + } + + return TRUE; } static void etree_set_expanded (ETreeModel *etm, ETreePath* node, gboolean expanded) { - /* shouldn't be called */ - g_assert(0); + GNode *child; + ENode *enode; + int row; + + g_return_if_fail (node && node->data); + + enode = ((ENode*)node->data); + + if (enode->expanded == expanded) + return; + + enode->expanded = expanded; + + row = e_tree_model_row_of_node (etm, node) + 1; + + if (expanded) { + GNode *parent; + + if (e_tree_model_node_is_visible (etm, node)) { + enode->visible_descendents = 0; + for (child = g_node_first_child (node); child; + child = g_node_next_sibling (child)) { + add_visible_descendents_to_array (etm, child, &row, &enode->visible_descendents); + } + } + /* now iterate back up the tree, adding to our + ancestors' visible descendents */ + + for (parent = node->parent; parent; parent = parent->parent) { + ENode *parent_enode = (ENode*)parent->data; + parent_enode->visible_descendents += enode->visible_descendents; + } + } + else { + int i; + GNode *parent; + + if (e_tree_model_node_is_visible (etm, node)) { + for (i = 0; i < enode->visible_descendents; i ++) { + etm->row_array = g_array_remove_index (etm->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) { + ENode *parent_enode = (ENode*)parent->data; + + parent_enode->visible_descendents -= enode->visible_descendents; + } + + enode->visible_descendents = 0; + } } +/* fairly naive implementation */ static void -etree_destroy (GtkObject *object) +etree_set_expanded_recurse (ETreeModel *etm, ETreePath* node, gboolean expanded) { + ETreePath **paths; + guint num_children; + int i; + + e_tree_model_node_set_expanded (etm, node, expanded); + + num_children = e_tree_model_node_get_children (etm, node, &paths); + if (num_children) { + for (i = 0; i < num_children; i ++) { + e_tree_model_node_set_expanded_recurse (etm, paths[i], expanded); + } + + g_free (paths); + } } -guint -e_tree_model_node_num_visible_descendents (ETreeModel *etm, ETreePath *node) +static ETreePath * +etree_node_at_row (ETreeModel *etree, int row) { - int count = 1; - if (e_tree_model_node_is_expanded (etm, node)) { - ETreePath **paths; - int i; - int num_paths; + GNode *node = g_array_index (etree->row_array, GNode*, row); - num_paths = e_tree_model_node_get_children (etm, node, &paths); + return node; +} - for (i = 0; i < num_paths; i ++) - count += e_tree_model_node_num_visible_descendents(etm, paths[i]); + +/* ETable analogs */ +static void* +etree_value_at (ETreeModel *etm, ETreePath* node, int col) +{ + /* shouldn't be called */ + g_assert (0); + return NULL; +} - e_tree_model_release_paths (etm, paths, num_paths); - } +static void +etree_set_value_at (ETreeModel *etm, ETreePath* node, int col, const void *val) +{ + /* shouldn't be called */ + g_assert (0); +} - return count; +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) { - return e_tree_model_node_num_visible_descendents (E_TREE_MODEL (etm), e_tree_model_get_root (E_TREE_MODEL (etm))); + ETreeModel *tree = E_TREE_MODEL (etm); + return tree->row_array->len; } static void * @@ -155,6 +279,7 @@ etable_is_cell_editable (ETableModel *etm, int col, int row) return et_class->is_editable (etree, node, col); } + static void e_tree_model_class_init (GtkObjectClass *klass) { @@ -165,11 +290,38 @@ e_tree_model_class_init (GtkObjectClass *klass) klass->destroy = etree_destroy; + e_tree_model_signals [NODE_CHANGED] = + gtk_signal_new ("node_changed", + GTK_RUN_LAST, + klass->type, + GTK_SIGNAL_OFFSET (ETreeModelClass, node_changed), + gtk_marshal_NONE__POINTER, + GTK_TYPE_NONE, 1, GTK_TYPE_POINTER); + + e_tree_model_signals [NODE_INSERTED] = + gtk_signal_new ("node_inserted", + GTK_RUN_LAST, + klass->type, + GTK_SIGNAL_OFFSET (ETreeModelClass, node_inserted), + gtk_marshal_NONE__POINTER_POINTER, + GTK_TYPE_NONE, 2, GTK_TYPE_POINTER, GTK_TYPE_POINTER); + + e_tree_model_signals [NODE_REMOVED] = + gtk_signal_new ("node_removed", + GTK_RUN_LAST, + klass->type, + GTK_SIGNAL_OFFSET (ETreeModelClass, node_removed), + gtk_marshal_NONE__POINTER_POINTER, + GTK_TYPE_NONE, 2, GTK_TYPE_POINTER, GTK_TYPE_POINTER); + + gtk_object_class_add_signals (klass, e_tree_model_signals, LAST_SIGNAL); + 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; @@ -178,19 +330,68 @@ e_tree_model_class_init (GtkObjectClass *klass) #endif tree_class->get_root = etree_get_root; + tree_class->get_prev = etree_get_prev; + tree_class->get_next = etree_get_next; + tree_class->get_parent = etree_get_parent; + tree_class->value_at = etree_value_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->release_paths = etree_release_paths; 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; } E_MAKE_TYPE(e_tree_model, "ETreeModel", ETreeModel, e_tree_model_class_init, NULL, PARENT_TYPE) + /* signals */ +void +e_tree_model_node_changed (ETreeModel *tree_model, ETreePath *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_CHANGED]); +} + +void +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_INSERTED], + parent_node, inserted_node); +} + +void +e_tree_model_node_removed (ETreeModel *tree_model, ETreePath *parent_node, ETreePath *removed_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_REMOVED], + parent_node, removed_node); +} + + +void +e_tree_model_construct (ETreeModel *etree) +{ + etree->root = NULL; + etree->root_visible = TRUE; + etree->row_array = g_array_new (FALSE, FALSE, sizeof(GNode*)); +} ETreeModel * e_tree_model_new () @@ -202,52 +403,56 @@ e_tree_model_new () return et; } -static ETreePath * -e_tree_model_node_at_row_1 (ETreeModel *etree, int *row, ETreePath *node) +ETreePath * +e_tree_model_get_root (ETreeModel *etree) { - ETreePath *ret = NULL; - - if (*row == 0) - ret = node; - else if (e_tree_model_node_is_expanded (etree, node)) { - int num_children; - int i; - ETreePath **paths; + return ETM_CLASS(etree)->get_root(etree); +} - num_children = e_tree_model_node_get_children (etree, node, &paths); +ETreePath * +e_tree_model_node_at_row (ETreeModel *etree, int row) +{ + return ETM_CLASS(etree)->node_at_row (etree, row); +} - for (i = 0; i < num_children; i ++) { - ETreePath *p; +int +e_tree_model_row_of_node (ETreeModel *etree, ETreePath *node) +{ + int i; - (*row) --; + for (i = 0; i < etree->row_array->len; i ++) + if (g_array_index (etree->row_array, GNode*, i) == node) + return i; - p = e_tree_model_node_at_row_1 (etree, row, paths[i]); + return -1; +} - if (p) { - ret = p; - break; +void +e_tree_model_root_node_set_visible (ETreeModel *etm, gboolean visible) +{ + if (visible != etm->root_visible) { + etm->root_visible = visible; + if (etm->root) { + if (visible) { + etm->row_array = g_array_insert_val (etm->row_array, 0, etm->root); } + else { + ETreePath *root_path = e_tree_model_get_root (etm); + etm->row_array = g_array_remove_index (etm->row_array, 0); + e_tree_model_node_set_expanded (etm, root_path, TRUE); + } + + e_table_model_changed (E_TABLE_MODEL (etm)); } - - /* XXX need to find why this release is causing problems */ - /* e_tree_model_release_paths (etree, paths, num_children); */ } - - return ret; } -ETreePath * -e_tree_model_get_root (ETreeModel *etree) +gboolean +e_tree_model_root_node_is_visible (ETreeModel *etm) { - return ETM_CLASS(etree)->get_root(etree); + return etm->root_visible; } -ETreePath * -e_tree_model_node_at_row (ETreeModel *etree, int row) -{ - /* XXX icky, perform a depth first search of the tree. we need this optimized sorely */ - return e_tree_model_node_at_row_1 (etree, &row, ETM_CLASS(etree)->get_root(etree)); -} ETreePath * e_tree_model_node_get_next (ETreeModel *etree, ETreePath *node) @@ -264,18 +469,13 @@ e_tree_model_node_get_prev (ETreeModel *etree, ETreePath *node) guint e_tree_model_node_depth (ETreeModel *etree, ETreePath *path) { - return g_list_length (path) - 1; + return g_node_depth (path) - 1; } ETreePath * e_tree_model_node_get_parent (ETreeModel *etree, ETreePath *path) { - g_return_val_if_fail (path, NULL); - - if (path->next == NULL) - return NULL; - else - return g_list_copy (path->next); + return ETM_CLASS(etree)->get_parent(etree, path); } gboolean @@ -296,21 +496,190 @@ e_tree_model_node_is_expanded (ETreeModel *etree, ETreePath *path) return ETM_CLASS(etree)->is_expanded (etree, path); } +gboolean +e_tree_model_node_is_visible (ETreeModel *etree, ETreePath *path) +{ + return ETM_CLASS(etree)->is_visible (etree, path); +} + void e_tree_model_node_set_expanded (ETreeModel *etree, ETreePath *path, gboolean expanded) { ETM_CLASS(etree)->set_expanded (etree, path, expanded); } +void +e_tree_model_node_set_expanded_recurse (ETreeModel *etree, ETreePath *path, gboolean expanded) +{ + ETM_CLASS(etree)->set_expanded_recurse (etree, path, expanded); +} + guint e_tree_model_node_get_children (ETreeModel *etree, ETreePath *path, ETreePath ***paths) { return ETM_CLASS(etree)->get_children (etree, path, paths); } +guint +e_tree_model_node_num_visible_descendents (ETreeModel *etm, ETreePath *node) +{ + ENode *enode = (ENode*)node->data; + + return enode->visible_descendents; +} + +gpointer +e_tree_model_node_get_data (ETreeModel *etm, ETreePath *node) +{ + ENode *enode; + + g_return_val_if_fail (node && node->data, NULL); + + enode = (ENode*)node->data; + + return enode->node_data; +} + void -e_tree_model_release_paths (ETreeModel *etree, ETreePath **paths, guint num_paths) +e_tree_model_node_set_data (ETreeModel *etm, ETreePath *node, gpointer node_data) { - ETM_CLASS(etree)->release_paths (etree, paths, num_paths); + ENode *enode; + + g_return_if_fail (node && node->data); + + enode = (ENode*)node->data; + + enode->node_data = node_data; } +ETreePath* +e_tree_model_node_insert (ETreeModel *tree_model, + ETreePath *parent_path, + int position, + gpointer node_data) +{ + ENode *node; + ETreePath *new_path; + + g_return_val_if_fail (parent_path != NULL || tree_model->root == NULL, NULL); + + node = g_new0 (ENode, 1); + + node->expanded = FALSE; + node->node_data = node_data; + + if (parent_path != NULL) { + + new_path = g_node_new (node); + + g_node_insert (parent_path, position, new_path); + + if (e_tree_model_node_is_visible (tree_model, new_path)) { + int parent_row; + GNode *node; + + /* we need to iterate back up to the root, incrementing the number of visible + descendents */ + for (node = parent_path; node; node = node->parent) { + ENode *parent_enode = (ENode*)node->data; + + parent_enode->visible_descendents ++; + } + + /* finally, insert a row into the table */ + parent_row = e_tree_model_row_of_node (tree_model, parent_path); + + tree_model->row_array = g_array_insert_val (tree_model->row_array, + parent_row + position + 1, new_path); + } + } + else { + tree_model->root = g_node_new (node); + if (tree_model->root_visible) + tree_model->row_array = g_array_insert_val (tree_model->row_array, 0, tree_model->root); + new_path = tree_model->root; + } + + /* FIXME: find out the number of descendents that will be visible, + and call insert_row that many times. */ + e_table_model_changed (E_TABLE_MODEL(tree_model)); + + return new_path; +} + +ETreePath * +e_tree_model_node_insert_before (ETreeModel *etree, ETreePath *parent, + ETreePath *sibling, gpointer node_data) +{ + return e_tree_model_node_insert (etree, parent, + g_node_child_position (parent, sibling), + node_data); +} + +gpointer +e_tree_model_node_remove (ETreeModel *etree, ETreePath *path) +{ + GNode *parent = path->parent; + ENode *enode = (ENode*)path->data; + gpointer ret = enode->node_data; + + g_return_val_if_fail (!g_node_first_child(path), NULL); + + /* clean up the display */ + if (parent) { + if (e_tree_model_node_is_visible (etree, path)) { + int row = e_tree_model_row_of_node (etree, path); + etree->row_array = g_array_remove_index (etree->row_array, row); + e_table_model_row_deleted (E_TABLE_MODEL (etree), row); + + /* we need to iterate back up to the root, incrementing the number of visible + descendents */ + for (; parent; parent = parent->parent) { + ENode *parent_enode = (ENode*)parent->data; + + parent_enode->visible_descendents --; + } + } + } + else if (path == etree->root) { + etree->root = NULL; + if (etree->root_visible) { + etree->row_array = g_array_remove_index (etree->row_array, 0); + e_table_model_row_deleted (E_TABLE_MODEL (etree), 0); + } + } + else { + /* XXX invalid path */ + return NULL; + } + + + /* now free up the storage from that path */ + g_node_destroy (path); + g_free (enode); + + return ret; +} + + +static void +add_visible_descendents_to_array (ETreeModel *etm, GNode *gnode, int *row, int *count) +{ + GNode *child; + ENode *enode; + + /* add a row for this node */ + etm->row_array = g_array_insert_val (etm->row_array, (*row), gnode); + e_table_model_row_inserted (E_TABLE_MODEL (etm), (*row)++); + (*count) ++; + + /* then loop over its children, calling this routine for each + of them */ + enode = (ENode*)gnode->data; + if (enode->expanded) { + for (child = g_node_first_child (gnode); child; + child = g_node_next_sibling (child)) { + add_visible_descendents_to_array (etm, child, row, count); + } + } +} diff --git a/widgets/table/e-tree-model.h b/widgets/table/e-tree-model.h index 6924f3ea0e..45351b8f6c 100644 --- a/widgets/table/e-tree-model.h +++ b/widgets/table/e-tree-model.h @@ -10,18 +10,13 @@ #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 gpointer ETreePathItem; -typedef GList ETreePath; +typedef GNode ETreePath; typedef struct { ETableModel base; - - ETableModel *source; - - ETreePath *root_node; - - GArray *array; - + GNode *root; + gboolean root_visible; + GArray *row_array; /* used in the mapping between ETable and our tree */ } ETreeModel; typedef struct { @@ -32,41 +27,75 @@ typedef struct { */ ETreePath *(*get_root) (ETreeModel *etm); + ETreePath *(*get_parent) (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); + + 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); + ETreePath* (*node_at_row) (ETreeModel *etm, int row); + + /* + * 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); - guint (*get_children) (ETreeModel *etm, ETreePath* node, ETreePath ***paths); - void (*release_paths) (ETreeModel *etm, ETreePath **paths, guint num_paths); - gboolean (*is_expanded) (ETreeModel *etm, ETreePath* node); - void (*set_expanded) (ETreeModel *etm, ETreePath* node, gboolean expanded); /* * 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); } ETreeModelClass; GtkType e_tree_model_get_type (void); - +void e_tree_model_construct (ETreeModel *etree); ETreeModel *e_tree_model_new (void); -/* operations on "nodes" in the tree */ -ETreePath * e_tree_model_get_root (ETreeModel *etree); -ETreePath * e_tree_model_node_at_row (ETreeModel *etree, int row); -guint e_tree_model_node_depth (ETreeModel *etree, ETreePath *path); +/* 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_next (ETreeModel *etree, ETreePath *path); -ETreePath *e_tree_model_node_get_prev (ETreeModel *etree, ETreePath *path); -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); -guint e_tree_model_node_get_children (ETreeModel *etree, ETreePath *path, ETreePath ***paths); -void e_tree_model_release_paths (ETreeModel *etree, ETreePath **paths, guint num_paths); -guint e_tree_model_node_num_visible_descendents (ETreeModel *etm, ETreePath *node); +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); + +/* 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_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); +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); + +/* +** 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); #endif /* _E_TREE_MODEL_H */ diff --git a/widgets/table/e-tree-simple.c b/widgets/table/e-tree-simple.c new file mode 100644 index 0000000000..9bf747a43f --- /dev/null +++ b/widgets/table/e-tree-simple.c @@ -0,0 +1,73 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * e-tree-simple.c: a Tree Model that offers a function pointer + * interface to using ETreeModel, similar to ETableSimple. + * + * Author: + * Chris Toshok (toshok@helixcode.com) + * + * (C) 2000 Helix Code, Inc. */ + +#include <config.h> +#include <gtk/gtksignal.h> +#include "e-util/e-util.h" +#include "e-tree-simple.h" + +#define PARENT_TYPE E_TREE_MODEL_TYPE + +static void * +simple_value_at (ETreeModel *etm, ETreePath *node, int col) +{ + ETreeSimple *simple = E_TREE_SIMPLE(etm); + + return simple->value_at (etm, node, col, simple->model_data); +} + +static void +simple_set_value_at (ETreeModel *etm, ETreePath *node, int col, const void *val) +{ + ETreeSimple *simple = E_TREE_SIMPLE(etm); + + simple->set_value_at (etm, node, col, val, simple->model_data); +} + +static gboolean +simple_is_editable (ETreeModel *etm, ETreePath *node, int col) +{ + ETreeSimple *simple = E_TREE_SIMPLE(etm); + + return simple->is_editable (etm, node, col, simple->model_data); +} + +static void +e_tree_simple_class_init (GtkObjectClass *object_class) +{ + ETreeModelClass *model_class = (ETreeModelClass *) object_class; + + model_class->value_at = simple_value_at; + model_class->set_value_at = simple_set_value_at; + model_class->is_editable = simple_is_editable; +} + +E_MAKE_TYPE(e_tree_simple, "ETreeSimple", ETreeSimple, e_tree_simple_class_init, NULL, PARENT_TYPE) + +ETreeModel * +e_tree_simple_new (ETreeSimpleValueAtFn value_at, + ETreeSimpleSetValueAtFn set_value_at, + ETreeSimpleIsEditableFn is_editable, + gpointer model_data) +{ + ETreeSimple *etg; + + etg = gtk_type_new (e_tree_simple_get_type ()); + + e_tree_model_construct (E_TREE_MODEL (etg)); + + etg->value_at = value_at; + etg->set_value_at = set_value_at; + etg->is_editable = is_editable; + etg->model_data = model_data; + + return (ETreeModel*)etg; +} + diff --git a/widgets/table/e-tree-simple.h b/widgets/table/e-tree-simple.h new file mode 100644 index 0000000000..6820b14673 --- /dev/null +++ b/widgets/table/e-tree-simple.h @@ -0,0 +1,40 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ + +#ifndef _E_TREE_SIMPLE_H_ +#define _E_TREE_SIMPLE_H_ + +#include "e-tree-model.h" + +#define E_TREE_SIMPLE_TYPE (e_tree_simple_get_type ()) +#define E_TREE_SIMPLE(o) (GTK_CHECK_CAST ((o), E_TREE_SIMPLE_TYPE, ETreeSimple)) +#define E_TREE_SIMPLE_CLASS(k) (GTK_CHECK_CLASS_CAST((k), E_TREE_SIMPLE_TYPE, ETreeSimpleClass)) +#define E_IS_TREE_SIMPLE(o) (GTK_CHECK_TYPE ((o), E_TREE_SIMPLE_TYPE)) +#define E_IS_TREE_SIMPLE_CLASS(k) (GTK_CHECK_CLASS_TYPE ((k), E_TREE_SIMPLE_TYPE)) + + +typedef void* (*ETreeSimpleValueAtFn) (ETreeModel *etree, ETreePath *path, int col, void *model_data); +typedef void (*ETreeSimpleSetValueAtFn) (ETreeModel *etree, ETreePath *path, int col, const void *val, void *model_data); +typedef gboolean (*ETreeSimpleIsEditableFn) (ETreeModel *etree, ETreePath *path, int col, void *model_data); + +typedef struct { + ETreeModel parent; + + ETreeSimpleValueAtFn value_at; + ETreeSimpleSetValueAtFn set_value_at; + ETreeSimpleIsEditableFn is_editable; + + gpointer model_data; +} ETreeSimple; + +typedef struct { + ETreeModelClass parent_class; +} ETreeSimpleClass; + +GtkType e_tree_simple_get_type (void); + +ETreeModel *e_tree_simple_new (ETreeSimpleValueAtFn value_at, + ETreeSimpleSetValueAtFn set_value_at, + ETreeSimpleIsEditableFn is_editable, + gpointer model_data); + +#endif /* _E_TREE_SIMPLE_H_ */ |