aboutsummaryrefslogtreecommitdiffstats
path: root/widgets
diff options
context:
space:
mode:
Diffstat (limited to 'widgets')
-rw-r--r--widgets/e-table/ChangeLog36
-rw-r--r--widgets/e-table/Makefile.am10
-rw-r--r--widgets/e-table/e-cell-tree.c57
-rw-r--r--widgets/e-table/e-table-item.c14
-rw-r--r--widgets/e-table/e-tree-example-1.c185
-rw-r--r--widgets/e-table/e-tree-gnode.c210
-rw-r--r--widgets/e-table/e-tree-gnode.h37
-rw-r--r--widgets/e-table/e-tree-model.c549
-rw-r--r--widgets/e-table/e-tree-model.h83
-rw-r--r--widgets/e-table/e-tree-simple.c73
-rw-r--r--widgets/e-table/e-tree-simple.h40
-rw-r--r--widgets/table/e-cell-tree.c57
-rw-r--r--widgets/table/e-table-item.c14
-rw-r--r--widgets/table/e-tree-example-1.c185
-rw-r--r--widgets/table/e-tree-gnode.c210
-rw-r--r--widgets/table/e-tree-gnode.h37
-rw-r--r--widgets/table/e-tree-model.c549
-rw-r--r--widgets/table/e-tree-model.h83
-rw-r--r--widgets/table/e-tree-simple.c73
-rw-r--r--widgets/table/e-tree-simple.h40
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_ */