From e581631a9ce0803ba629ac67cd8934a6e23753ae Mon Sep 17 00:00:00 2001
From: Chris Toshok <toshok@helixcode.com>
Date: Thu, 8 Jun 2000 23:16:22 +0000
Subject: New files. A tree model using a GNode structure to store it's info.

2000-06-08  Chris Toshok  <toshok@helixcode.com>

	* e-tree-gnode.c, e-tree-gnode.h: New files.  A tree model using a
	GNode structure to store it's info.

	* e-tree-model.c, e-tree-model.h: New files.  A proxy model
	sitting between a table model and the real tree model (of which
	ETreeGNode is an example).

	* e-cell-tree.c, e-cell-tree.h: New files.  A cell renderer
	capable of wrapping up a subcell and drawing the tree controls for
	expanding/collapsing trees.

	* tree-expanded.xpm, tree-unexpanded.xpm: New files.  the standard
	+ and - icons.

	* e-tree-example-1.c: New file, giving a (pretty poor :) example
	of using ETreeGNode.

	* Makefile.am: at the tree stuff to the build, and build
	tree-example-1.

	* .cvsignore: ignore tree-example-1.

svn path=/trunk/; revision=3483
---
 widgets/e-table/.cvsignore          |   1 +
 widgets/e-table/ChangeLog           |  24 +++
 widgets/e-table/Makefile.am         |  21 +-
 widgets/e-table/e-cell-tree.c       | 385 ++++++++++++++++++++++++++++++++++++
 widgets/e-table/e-cell-tree.h       |  42 ++++
 widgets/e-table/e-tree-example-1.c  | 243 +++++++++++++++++++++++
 widgets/e-table/e-tree-gnode.c      | 210 ++++++++++++++++++++
 widgets/e-table/e-tree-gnode.h      |  37 ++++
 widgets/e-table/e-tree-model.c      | 316 +++++++++++++++++++++++++++++
 widgets/e-table/e-tree-model.h      |  72 +++++++
 widgets/e-table/tree-expanded.xpm   |  22 +++
 widgets/e-table/tree-unexpanded.xpm |  22 +++
 widgets/table/.cvsignore            |   1 +
 widgets/table/e-cell-tree.c         | 385 ++++++++++++++++++++++++++++++++++++
 widgets/table/e-cell-tree.h         |  42 ++++
 widgets/table/e-tree-example-1.c    | 243 +++++++++++++++++++++++
 widgets/table/e-tree-gnode.c        | 210 ++++++++++++++++++++
 widgets/table/e-tree-gnode.h        |  37 ++++
 widgets/table/e-tree-model.c        | 316 +++++++++++++++++++++++++++++
 widgets/table/e-tree-model.h        |  72 +++++++
 widgets/table/tree-expanded.xpm     |  22 +++
 widgets/table/tree-unexpanded.xpm   |  22 +++
 22 files changed, 2743 insertions(+), 2 deletions(-)
 create mode 100644 widgets/e-table/e-cell-tree.c
 create mode 100644 widgets/e-table/e-cell-tree.h
 create mode 100644 widgets/e-table/e-tree-example-1.c
 create mode 100644 widgets/e-table/e-tree-gnode.c
 create mode 100644 widgets/e-table/e-tree-gnode.h
 create mode 100644 widgets/e-table/e-tree-model.c
 create mode 100644 widgets/e-table/e-tree-model.h
 create mode 100644 widgets/e-table/tree-expanded.xpm
 create mode 100644 widgets/e-table/tree-unexpanded.xpm
 create mode 100644 widgets/table/e-cell-tree.c
 create mode 100644 widgets/table/e-cell-tree.h
 create mode 100644 widgets/table/e-tree-example-1.c
 create mode 100644 widgets/table/e-tree-gnode.c
 create mode 100644 widgets/table/e-tree-gnode.h
 create mode 100644 widgets/table/e-tree-model.c
 create mode 100644 widgets/table/e-tree-model.h
 create mode 100644 widgets/table/tree-expanded.xpm
 create mode 100644 widgets/table/tree-unexpanded.xpm

diff --git a/widgets/e-table/.cvsignore b/widgets/e-table/.cvsignore
index 1c61242460..ecfa617948 100644
--- a/widgets/e-table/.cvsignore
+++ b/widgets/e-table/.cvsignore
@@ -9,3 +9,4 @@ table-test
 table-example-1
 table-example-2
 table-size-test
+tree-example-1
diff --git a/widgets/e-table/ChangeLog b/widgets/e-table/ChangeLog
index ba7b064f65..5f6000a4eb 100644
--- a/widgets/e-table/ChangeLog
+++ b/widgets/e-table/ChangeLog
@@ -1,3 +1,27 @@
+2000-06-08  Chris Toshok  <toshok@helixcode.com>
+
+	* e-tree-gnode.c, e-tree-gnode.h: New files.  A tree model using a
+	GNode structure to store it's info.
+
+	* e-tree-model.c, e-tree-model.h: New files.  A proxy model
+	sitting between a table model and the real tree model (of which
+	ETreeGNode is an example).
+
+	* e-cell-tree.c, e-cell-tree.h: New files.  A cell renderer
+	capable of wrapping up a subcell and drawing the tree controls for
+	expanding/collapsing trees.
+
+	* tree-expanded.xpm, tree-unexpanded.xpm: New files.  the standard
+	+ and - icons.
+
+	* e-tree-example-1.c: New file, giving a (pretty poor :) example
+	of using ETreeGNode.
+	
+	* Makefile.am: at the tree stuff to the build, and build
+	tree-example-1.
+
+	* .cvsignore: ignore tree-example-1.
+	
 2000-06-08  Christopher James Lahey  <clahey@helixcode.com>
 
 	* The field chooser works now.
diff --git a/widgets/e-table/Makefile.am b/widgets/e-table/Makefile.am
index bb0cc97edb..8048435753 100644
--- a/widgets/e-table/Makefile.am
+++ b/widgets/e-table/Makefile.am
@@ -31,6 +31,8 @@ libetable_a_SOURCES = 				\
 	e-cell-text.h				\
 	e-cell-toggle.c				\
 	e-cell-toggle.h				\
+	e-cell-tree.c				\
+	e-cell-tree.h				\
 	e-table.c				\
 	e-table.h				\
 	e-table-col-dnd.h			\
@@ -72,10 +74,14 @@ libetable_a_SOURCES = 				\
 	e-table-subset-variable.c		\
 	e-table-subset-variable.h		\
 	e-table-text-model.c			\
-	e-table-text-model.h
+	e-table-text-model.h			\
+	e-tree-gnode.c				\
+	e-tree-gnode.h				\
+	e-tree-model.c				\
+	e-tree-model.h
 
 noinst_PROGRAMS =	\
-	table-test table-example-1 table-example-2 table-size-test
+	table-test table-example-1 table-example-2 table-size-test tree-example-1
 
 table_test_SOURCES = 	\
 	test-table.c	\
@@ -125,6 +131,17 @@ table_example_2_LDADD =				\
 
 table_example_2_LDFLAGS = `gnome-config --libs gdk_pixbuf`
 
+tree_example_1_SOURCES = 	\
+	e-tree-example-1.c
+
+tree_example_1_LDFLAGS = `gnome-config --libs gdk_pixbuf`
+
+tree_example_1_LDADD =				\
+	libetable.a				\
+	$(EXTRA_GNOME_LIBS)			\
+	$(top_builddir)/widgets/e-text/libetext.a \
+	$(top_builddir)/e-util/libeutil.la	
+
 icons = 			\
 	arrow-down.xpm		\
 	arrow-up.xpm		\
diff --git a/widgets/e-table/e-cell-tree.c b/widgets/e-table/e-cell-tree.c
new file mode 100644
index 0000000000..9f40bd0eae
--- /dev/null
+++ b/widgets/e-table/e-cell-tree.c
@@ -0,0 +1,385 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/* e-cell-tree.c - Tree cell renderer
+ * Copyright (C) 2000 Helix Code, Inc.
+ *
+ * Author: Chris Toshok <toshok@helixcode.com>
+ *
+ * A majority of code taken from:
+ *
+ * the ECellText renderer.
+ *
+ * Copyright (C) 1998 The Free Software Foundation
+ *
+ */
+
+#include <config.h>
+#include <gtk/gtkenums.h>
+#include <gtk/gtkentry.h>
+#include <gtk/gtkwindow.h>
+#include <gtk/gtkinvisible.h>
+#include <gtk/gtksignal.h>
+#include <gdk/gdkkeysyms.h>
+#include <libgnomeui/gnome-canvas.h>
+#include <stdio.h>
+#include "e-table-sorted-variable.h"
+#include "e-tree-model.h"
+#include "e-cell-tree.h"
+#include "e-util/e-util.h"
+#include "e-table-item.h"
+
+#include <gdk/gdkx.h> /* for BlackPixel */
+#include <ctype.h>
+#include <math.h>
+
+#include "tree-expanded.xpm"
+#include "tree-unexpanded.xpm"
+
+#define PARENT_TYPE e_cell_get_type ()
+
+typedef struct {
+	ECellView    cell_view;
+	ECellView   *subcell_view;
+	GdkGC       *gc;
+
+	GnomeCanvas *canvas;
+
+} ECellTreeView;
+
+static ECellClass *parent_class;
+
+static GdkPixbuf   *tree_expanded_pixbuf, *tree_unexpanded_pixbuf;
+
+static ETreePath*
+e_cell_tree_get_node (ETreeModel *tree_model, int row)
+{
+	return (ETreePath*)e_table_model_value_at (E_TABLE_MODEL(tree_model), -1, row);
+}
+
+static ETreeModel*
+e_cell_tree_get_tree_model (ETableModel *table_model, int row)
+{
+	return (ETreeModel*)e_table_model_value_at (table_model, -2, row);
+}
+
+/*
+ * ECell::new_view method
+ */
+static ECellView *
+ect_new_view (ECell *ecell, ETableModel *table_model, void *e_table_item_view)
+{
+	ECellTree *ect = E_CELL_TREE (ecell);
+	ECellTreeView *tree_view = g_new0 (ECellTreeView, 1);
+	GnomeCanvas *canvas = GNOME_CANVAS_ITEM (e_table_item_view)->canvas;
+	
+	tree_view->cell_view.ecell = ecell;
+	tree_view->cell_view.e_table_model = table_model;
+	tree_view->cell_view.e_table_item_view = e_table_item_view;
+	
+	/* create our subcell view */
+	tree_view->subcell_view = e_cell_new_view (ect->subcell, table_model, e_table_item_view /* XXX */);
+
+	tree_view->canvas = canvas;
+
+	return (ECellView *)tree_view;
+}
+
+/*
+ * ECell::kill_view method
+ */
+static void
+ect_kill_view (ECellView *ecv)
+{
+	ECellTreeView *tree_view = (ECellTreeView *) ecv;
+
+	/* kill our subcell view */
+	e_cell_kill_view (tree_view->subcell_view);
+
+	g_free (tree_view);
+}
+
+/*
+ * ECell::realize method
+ */
+static void
+ect_realize (ECellView *ecell_view)
+{
+	ECellTreeView *tree_view = (ECellTreeView *) ecell_view;
+
+	/* realize our subcell view */
+	e_cell_realize (tree_view->subcell_view);
+
+	tree_view->gc = gdk_gc_new (GTK_WIDGET (tree_view->canvas)->window);
+
+	gdk_gc_set_line_attributes (tree_view->gc, 1, 
+				    GDK_LINE_ON_OFF_DASH, None, None);
+	gdk_gc_set_dashes (tree_view->gc, 0, "\1\1", 2);
+
+	if (parent_class->realize)
+		(* parent_class->realize) (ecell_view);
+}
+
+/*
+ * ECell::unrealize method
+ */
+static void
+ect_unrealize (ECellView *ecv)
+{
+	ECellTreeView *tree_view = (ECellTreeView *) ecv;
+
+	/* unrealize our subcell view. */
+	e_cell_unrealize (tree_view->subcell_view);
+
+	gdk_gc_unref (tree_view->gc);
+	tree_view->gc = NULL;
+
+	if (parent_class->unrealize)
+		(* parent_class->unrealize) (ecv);
+}
+
+#define INDENT_AMOUNT 16
+/*
+ * ECell::draw method
+ */
+static void
+ect_draw (ECellView *ecell_view, GdkDrawable *drawable,
+	  int model_col, int view_col, int row, gboolean selected,
+	  int x1, int y1, int x2, int y2)
+{
+	ECellTreeView *tree_view = (ECellTreeView *)ecell_view;
+	ETreeModel *tree_model = e_cell_tree_get_tree_model(ecell_view->e_table_model, row);
+	ETreePath *node;
+	GdkRectangle rect, *clip_rect;
+	GtkWidget *canvas = GTK_WIDGET (tree_view->canvas);
+	GdkGC *fg_gc = canvas->style->fg_gc[GTK_STATE_ACTIVE];
+	GdkColor *background, *foreground;
+
+	int offset, subcell_offset;
+	gboolean expanded, expandable;
+
+	/* only draw the tree effects if we're the active sort */
+	if (/* XXX */ TRUE) {
+		/*
+		 * need to get the following things from the model
+		 * 1. depth of item.
+		 * 2. whether or not it has any children.
+		 * 3. whether the item is a toplevel item.
+		 * 3. ... anything else?
+		 */
+		node = e_cell_tree_get_node (tree_model, row);
+
+		offset = (e_tree_model_node_depth (tree_model, node) + 1) * INDENT_AMOUNT;
+		expandable = e_tree_model_node_is_expandable (tree_model, node);
+		expanded = e_tree_model_node_is_expanded (tree_model, node);
+		subcell_offset = offset;
+
+		/*
+		 * Be a nice citizen: clip to the region we are supposed to draw on
+		 */
+		rect.x = x1;
+		rect.y = y1;
+		rect.width = offset;
+		rect.height = y2 - y1;
+	
+		gdk_gc_set_clip_rectangle (tree_view->gc, &rect);
+		gdk_gc_set_clip_rectangle (fg_gc, &rect);
+		clip_rect = &rect;
+
+		if (selected){
+			background = &canvas->style->bg [GTK_STATE_SELECTED];
+			foreground = &canvas->style->text [GTK_STATE_SELECTED];
+		} else {
+			background = &canvas->style->base [GTK_STATE_NORMAL];
+			foreground = &canvas->style->text [GTK_STATE_NORMAL];
+		}
+		gdk_gc_set_foreground (tree_view->gc, background);
+		gdk_draw_rectangle (drawable, tree_view->gc, TRUE,
+				    rect.x, rect.y, rect.width, rect.height);
+		gdk_gc_set_foreground (tree_view->gc, foreground);
+
+		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,
+				       rect.y + rect.height / 2,
+				       rect.x + offset,
+				       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)) {
+				if (e_tree_model_node_get_next(tree_model, node)) {
+					gdk_draw_line (drawable, tree_view->gc,
+						       rect.x + offset - INDENT_AMOUNT / 2,
+						       rect.y,
+						       rect.x + offset - INDENT_AMOUNT / 2,
+						       rect.y + rect.height);
+				}
+				node = e_tree_model_node_get_parent (tree_model, node);
+				offset -= INDENT_AMOUNT;
+			}
+		}
+
+		/* now draw our icon if we're expandable */
+		if (expandable) {
+			GdkPixbuf *image = expanded ? tree_expanded_pixbuf : tree_unexpanded_pixbuf;
+			int width, height;
+
+			width = gdk_pixbuf_get_width(image);
+			height = gdk_pixbuf_get_height(image);
+
+			gdk_pixbuf_render_to_drawable_alpha (image,
+							     drawable,
+							     0, 0,
+							     x1 + subcell_offset - INDENT_AMOUNT / 2 - width / 2,
+							     y1 + (y2 - y1) / 2 - height / 2,
+							     width, height,
+							     GDK_PIXBUF_ALPHA_BILEVEL,
+							     128,
+							     GDK_RGB_DITHER_NORMAL,
+							     width, 0);
+		}
+	}
+
+	/* Now cause our subcell to draw its contents, shifted by
+	   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);
+}
+
+/*
+ * ECell::event method
+ */
+static gint
+ect_event (ECellView *ecell_view, GdkEvent *event, int model_col, int view_col, int row)
+{
+	ECellTreeView *tree_view = (ECellTreeView *) ecell_view;
+
+	switch (event->type) {
+	case GDK_BUTTON_PRESS:
+	case GDK_BUTTON_RELEASE: {
+		/* 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;
+
+		/* only activate the tree control if the click/release happens in the icon's area. */
+		if (event->button.x > (offset - INDENT_AMOUNT) && event->button.x < offset) {
+			if (e_tree_model_node_is_expandable (tree_model, node)) {
+				e_tree_model_node_set_expanded (tree_model,
+								node,
+								!e_tree_model_node_is_expanded(tree_model, node));
+			}
+			return TRUE;
+		}
+	}
+	default:
+		/* otherwise, pass it off to our subcell_view */
+		e_cell_event(tree_view->subcell_view, event, model_col, view_col, row);
+		return TRUE;
+	}
+}
+
+/*
+ * ECell::height method
+ */
+static int
+ect_height (ECellView *ecell_view, int model_col, int view_col, int row) 
+{
+	ECellTreeView *tree_view = (ECellTreeView *) ecell_view;
+
+	return e_cell_height (tree_view->subcell_view, model_col, view_col, row);
+}
+
+/*
+ * ECellView::enter_edit method
+ */
+static void *
+ect_enter_edit (ECellView *ecell_view, int model_col, int view_col, int row)
+{
+	return NULL;
+}
+
+/*
+ * ECellView::leave_edit method
+ */
+static void
+ect_leave_edit (ECellView *ecell_view, int model_col, int view_col, int row, void *edit_context)
+{
+}
+
+/*
+ * GtkObject::destroy method
+ */
+static void
+ect_destroy (GtkObject *object)
+{
+	ECellTree *ect = E_CELL_TREE (object);
+
+	/* destroy our subcell */
+	gtk_object_destroy (GTK_OBJECT (ect->subcell));
+
+	GTK_OBJECT_CLASS (parent_class)->destroy (object);
+}
+
+static void
+e_cell_tree_class_init (GtkObjectClass *object_class)
+{
+	ECellClass *ecc = (ECellClass *) object_class;
+
+	object_class->destroy = ect_destroy;
+
+	ecc->new_view   = ect_new_view;
+	ecc->kill_view  = ect_kill_view;
+	ecc->realize    = ect_realize;
+	ecc->unrealize  = ect_unrealize;
+	ecc->draw       = ect_draw;
+	ecc->event      = ect_event;
+	ecc->height     = ect_height;
+	ecc->enter_edit = ect_enter_edit;
+	ecc->leave_edit = ect_leave_edit;
+
+	parent_class = gtk_type_class (PARENT_TYPE);
+
+	/*
+	 * Create our pixbuf for expanding/unexpanding
+	 */
+	tree_expanded_pixbuf = gdk_pixbuf_new_from_xpm_data(tree_expanded_xpm);
+	tree_unexpanded_pixbuf = gdk_pixbuf_new_from_xpm_data(tree_unexpanded_xpm);
+}
+
+E_MAKE_TYPE(e_cell_tree, "ECellTree", ECellTree, e_cell_tree_class_init, NULL, PARENT_TYPE);
+
+void
+e_cell_tree_construct (ECellTree *ect,
+		       gboolean draw_lines,
+		       ECell *subcell)
+{		       
+	ect->subcell = subcell;
+	ect->draw_lines = draw_lines;
+}
+
+
+ECell *
+e_cell_tree_new (ETableModel *etm,
+		 gboolean draw_lines,
+		 ECell *subcell)
+{
+	ECellTree *ect = gtk_type_new (e_cell_tree_get_type ());
+
+	e_cell_tree_construct (ect, draw_lines, subcell);
+
+	return (ECell *) ect;
+}
diff --git a/widgets/e-table/e-cell-tree.h b/widgets/e-table/e-cell-tree.h
new file mode 100644
index 0000000000..6bd842b39e
--- /dev/null
+++ b/widgets/e-table/e-cell-tree.h
@@ -0,0 +1,42 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/* ECellTree - Tree item for e-table.
+ * Copyright (C) 2000 Helix Code, Inc.
+ * Author: Chris Toshok <toshok@helixcode.com>
+ *
+ */
+#ifndef _E_CELL_TREE_H_
+#define _E_CELL_TREE_H_
+
+#include <libgnomeui/gnome-canvas.h>
+#include "e-cell.h"
+
+#define E_CELL_TREE_TYPE        (e_cell_tree_get_type ())
+#define E_CELL_TREE(o)          (GTK_CHECK_CAST ((o), E_CELL_TREE_TYPE, ECellTree))
+#define E_CELL_TREE_CLASS(k)    (GTK_CHECK_CLASS_CAST((k), E_CELL_TREE_TYPE, ECellTreeClass))
+#define E_IS_CELL_TREE(o)       (GTK_CHECK_TYPE ((o), E_CELL_TREE_TYPE))
+#define E_IS_CELL_TREE_CLASS(k) (GTK_CHECK_CLASS_TYPE ((k), E_CELL_TREE_TYPE))
+
+typedef struct {
+	ECell parent;
+
+	gboolean draw_lines;
+
+	GdkPixbuf   *expanded_image;
+	GdkPixbuf   *unexpanded_image;
+
+	ECell *subcell;
+} ECellTree;
+
+typedef struct {
+	ECellClass parent_class;
+} ECellTreeClass;
+
+GtkType    e_cell_tree_get_type (void);
+ECell     *e_cell_tree_new      (ETableModel *model, gboolean draw_lines,
+				 ECell *subcell);
+void       e_cell_tree_construct (ECellTree *ect, gboolean draw_lines,
+				  ECell *subcell);
+
+#endif /* _E_CELL_TREE_H_ */
+
+
diff --git a/widgets/e-table/e-tree-example-1.c b/widgets/e-table/e-tree-example-1.c
new file mode 100644
index 0000000000..e5563a8071
--- /dev/null
+++ b/widgets/e-table/e-tree-example-1.c
@@ -0,0 +1,243 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/* This code is GPL. */
+#include <stdio.h>
+#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"
+#include "e-cell-text.h"
+#include "e-cell-tree.h"
+#include "e-cell-checkbox.h"
+#include "e-table.h"
+
+#include <gdk-pixbuf/gdk-pixbuf.h>
+
+#define ROWS 10
+#define COLS 4
+
+#define IMPORTANCE_COLUMN 4
+#define COLOR_COLUMN 5
+
+/*
+ * Here we define the initial layout of the table.  This is an xml
+ * format that allows you to change the initial ordering of the
+ * columns or to do sorting or grouping initially.  This specification
+ * shows all 5 columns, but moves the importance column nearer to the
+ * front.  It also sorts by the "Full Name" column (ascending.)
+ * Sorting and grouping take the model column as their arguments
+ * (sorting is specified by the "column" argument to the leaf elemnt.
+ */
+
+#define INITIAL_SPEC "<ETableSpecification>                    	       \
+	<columns-shown>                  			       \
+		<column> 0 </column>     			       \
+		<column> 4 </column>     			       \
+		<column> 1 </column>     			       \
+		<column> 2 </column>     			       \
+		<column> 3 </column>     			       \
+	</columns-shown>                 			       \
+	<grouping></grouping>                                         \
+</ETableSpecification>"
+
+/*
+ * Virtual Column list:
+ * 0   Subject
+ * 1   Full Name
+ * 2   Email
+ * 3   Date
+ */
+char *headers [COLS] = {
+  "Subject",
+  "Full Name",
+  "Email",
+  "Date"
+};
+
+/*
+ * ETableSimple callbacks
+ * These are the callbacks that define the behavior of our custom model.
+ */
+
+/*
+ * Since our model is a constant size, we can just return its size in
+ * the column and row count fields.
+ */
+
+/* This function returns the number of columns in our ETableModel. */
+static int
+my_col_count (ETableModel *etc, void *data)
+{
+	return COLS;
+}
+
+/* This function returns the value at a particular point in our ETableModel. */
+static void *
+my_value_at (ETreeModel *etc, GNode *node, int col, void *data)
+{
+	switch (col) {
+	case 0: return "Re: Two things";
+	case 1: return "Chris Toshok";
+	case 2: return "toshok@helixcode.com";
+	case 3: return "Jun 07 2000";
+	default: return NULL;
+	}
+}
+
+/* This function sets the value at a particular point in our ETableModel. */
+static void
+my_set_value_at (ETableModel *etc, GNode *node, int col, const void *val, void *data)
+{
+}
+
+/* This function returns whether a particular cell is editable. */
+static gboolean
+my_is_editable (ETableModel *etc, GNode *node, int col, void *data)
+{
+	return FALSE;
+}
+
+/* This function duplicates the value passed to it. */
+static void *
+my_duplicate_value (ETableModel *etc, int col, const void *value, void *data)
+{
+	return g_strdup (value);
+}
+
+/* This function frees the value passed to it. */
+static void
+my_free_value (ETableModel *etc, int col, void *value, void *data)
+{
+	g_free (value);
+}
+
+/* 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)
+{
+}
+
+/* We create a window containing our new tree. */
+static void
+create_tree (void)
+{
+	GtkWidget *e_table, *window, *frame;
+	ECell *cell_left_just;
+	ECell *cell_tree;
+	ETableHeader *e_table_header;
+	int i, j;
+	ETreeModel *e_tree_model = NULL;
+	GNode *root_node;
+
+	/* create a root node with 5 children */
+	root_node = g_node_new (NULL);
+	for (i = 0; i < 5; i++){
+		GNode *n = g_node_insert (root_node, 0, g_node_new(NULL));
+		for (j = 0; j < 5; j ++) {
+			g_node_insert (n, 0, g_node_new(NULL));
+		}
+	}
+
+	/* 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
+	 * list of possible columns in the view.  The second use is
+	 * completely internal.  Many of the ETableHeader functions are
+	 * for that purpose.  The only functions we really need are
+	 * e_table_header_new and e_table_header_add_col.
+	 *
+	 * First we create the header.
+	 */
+	e_table_header = e_table_header_new ();
+	
+	/*
+	 * Next we have to build renderers for all of the columns.
+	 * Since all our columns are text columns, we can simply use
+	 * the same renderer over and over again.  If we had different
+	 * types of columns, we could use a different renderer for
+	 * each column.
+	 */
+	cell_left_just = e_cell_text_new (E_TABLE_MODEL(e_tree_model), NULL, GTK_JUSTIFY_LEFT);
+	
+	/* 
+	 * This renderer is used for the tree column (the leftmost one), and
+	 * has as its subcell renderer the text renderer.  this means that
+	 * text is displayed to the right of the tree pipes.
+	 */
+	cell_tree = e_cell_tree_new (E_TABLE_MODEL(e_tree_model), TRUE, cell_left_just);
+
+	/*
+	 * Next we create a column object for each view column and add
+	 * them to the header.  We don't create a column object for
+	 * the importance column since it will not be shown.
+	 */
+	for (i = 0; i < COLS; i++) {
+		/* Create the column. */
+		ETableCol *ecol = e_table_col_new (
+						   i, headers [i],
+						   80, 20,
+						   i == 0 ? cell_tree
+						   : cell_left_just,
+						   g_str_compare, TRUE);
+		/* Add it to the header. */
+		e_table_header_add_column (e_table_header, ecol, i);
+	}
+
+	/*
+	 * Here we create a window for our new table.  This window
+	 * will get shown and the person will be able to test their
+	 * item.
+	 */
+	window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+
+	/* This frame is simply to get a bevel around our table. */
+	frame = gtk_frame_new (NULL);
+
+	/*
+	 * Here we create the table.  We give it the three pieces of
+	 * the table we've created, the header, the model, and the
+	 * initial layout.  It does the rest.
+	 */
+	e_table = e_table_new (e_table_header, E_TABLE_MODEL(e_tree_model), INITIAL_SPEC);
+
+	if (!e_table) printf ("BAH!");
+
+	/* Build the gtk widget hierarchy. */
+	gtk_container_add (GTK_CONTAINER (frame), e_table);
+	gtk_container_add (GTK_CONTAINER (window), frame);
+
+	/* Size the initial window. */
+	gtk_widget_set_usize (window, 200, 200);
+
+	/* Show it all. */
+	gtk_widget_show_all (window);
+}
+
+/* This is the main function which just initializes gnome and call our create_tree function */
+
+int
+main (int argc, char *argv [])
+{
+	gnome_init ("TableExample", "TableExample", argc, argv);
+	e_cursors_init ();
+
+	gtk_widget_push_visual (gdk_rgb_get_visual ());
+	gtk_widget_push_colormap (gdk_rgb_get_cmap ());
+
+	create_tree ();
+	
+	gtk_main ();
+
+	e_cursors_shutdown ();
+	return 0;
+}
+
diff --git a/widgets/e-table/e-tree-gnode.c b/widgets/e-table/e-tree-gnode.c
new file mode 100644
index 0000000000..ce53751b56
--- /dev/null
+++ b/widgets/e-table/e-tree-gnode.c
@@ -0,0 +1,210 @@
+/* -*- 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
new file mode 100644
index 0000000000..56e6b19b80
--- /dev/null
+++ b/widgets/e-table/e-tree-gnode.h
@@ -0,0 +1,37 @@
+/* -*- 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
new file mode 100644
index 0000000000..d67e59f3f5
--- /dev/null
+++ b/widgets/e-table/e-tree-model.c
@@ -0,0 +1,316 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * e-tree-model.c: a Tree Model
+ *
+ * Author:
+ *   Chris Toshok (toshok@helixcode.com)
+ *
+ * Adapted from the gtree code and ETableModel.
+ *
+ * (C) 2000 Helix Code, Inc.
+ */
+#include <config.h>
+#include <gtk/gtksignal.h>
+#include "e-util/e-util.h"
+#include "e-tree-model.h"
+
+#define ETM_CLASS(e) ((ETreeModelClass *)((GtkObject *)e)->klass)
+
+#define PARENT_TYPE E_TABLE_MODEL_TYPE
+
+static ETableModel *e_tree_model_parent_class;
+
+/* virtual methods */
+
+static ETreePath*
+etree_get_root (ETreeModel *etm)
+{
+	/* shouldn't be called */
+	g_assert(0);
+	return NULL;
+}
+
+static void*
+etree_value_at (ETreeModel *etm, ETreePath* node, int col)
+{
+	/* shouldn't be called */
+	g_assert(0);
+	return NULL;
+}
+
+static void
+etree_set_value_at (ETreeModel *etm, ETreePath* node, int col, const void *val)
+{
+	/* shouldn't be called */
+	g_assert(0);
+}
+
+static gboolean
+etree_is_editable (ETreeModel *etm, ETreePath* node, int col)
+{
+	/* shouldn't be called */
+	g_assert(0);
+	return FALSE;
+}
+
+static gboolean
+etree_is_expanded (ETreeModel *etm, ETreePath* node)
+{
+	/* shouldn't be called */
+	g_assert(0);
+	return FALSE;
+}
+
+static guint
+etree_get_children (ETreeModel *etm, ETreePath* node, ETreePath ***paths)
+{
+	/* shouldn't be called */
+	g_assert(0);
+	return 0;
+}
+
+static void
+etree_release_paths (ETreeModel *etm, ETreePath **paths, guint num_paths)
+{
+	/* shouldn't be called */
+	g_assert(0);
+}
+
+static void
+etree_set_expanded (ETreeModel *etm, ETreePath* node, gboolean expanded)
+{
+	/* shouldn't be called */
+	g_assert(0);
+}
+
+static void
+etree_destroy (GtkObject *object)
+{
+}
+
+guint
+e_tree_model_node_num_visible_descendents (ETreeModel *etm, ETreePath *node)
+{
+	int count = 1;
+	if (e_tree_model_node_is_expanded (etm, node)) {
+		ETreePath **paths;
+		int i;
+		int num_paths;
+
+		num_paths = e_tree_model_node_get_children (etm, node, &paths);
+
+		for (i = 0; i < num_paths; i ++)
+			count += e_tree_model_node_num_visible_descendents(etm, paths[i]);
+
+		e_tree_model_release_paths (etm, paths, num_paths);
+	}
+
+	return count;
+}
+
+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)));
+}
+
+static void *
+etable_value_at (ETableModel *etm, int col, int row)
+{
+	ETreeModel *etree = E_TREE_MODEL(etm);
+	ETreeModelClass *et_class = ETM_CLASS(etm);
+	ETreePath* node = e_tree_model_node_at_row (etree, row);
+
+	g_return_val_if_fail (node, NULL);
+
+	if (col == -1)
+		return node;
+	else if (col == -2)
+		return etm;
+	else
+		return et_class->value_at (etree, node, col);
+}
+
+static void
+etable_set_value_at (ETableModel *etm, int col, int row, const void *val)
+{
+	ETreeModel *etree = E_TREE_MODEL(etm);
+	ETreeModelClass *et_class = ETM_CLASS(etm);
+	ETreePath* node = e_tree_model_node_at_row (etree, row);
+
+	g_return_if_fail (node);
+
+	et_class->set_value_at (etree, node, col, val);
+}
+
+static gboolean
+etable_is_cell_editable (ETableModel *etm, int col, int row)
+{
+	ETreeModel *etree = E_TREE_MODEL(etm);
+	ETreeModelClass *et_class = ETM_CLASS(etm);
+	ETreePath* node = e_tree_model_node_at_row (etree, row);
+
+	g_return_val_if_fail (node, FALSE);
+
+	return et_class->is_editable (etree, node, col);
+}
+
+static void
+e_tree_model_class_init (GtkObjectClass *klass)
+{
+	ETableModelClass *table_class = (ETableModelClass *) klass;
+	ETreeModelClass *tree_class = (ETreeModelClass *) klass;
+
+	e_tree_model_parent_class = gtk_type_class (PARENT_TYPE);
+	
+	klass->destroy = etree_destroy;
+
+	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
+	table_class->duplicate_value  = etable_duplicate_value;
+	table_class->free_value       = etable_free_value;
+	table_class->initialize_value = etable_initialize_value;
+	table_class->value_is_empty   = etable_value_is_empty;
+	table_class->thaw             = etable_thaw;
+#endif
+
+	tree_class->get_root          = etree_get_root;
+	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->set_expanded      = etree_set_expanded;
+}
+
+E_MAKE_TYPE(e_tree_model, "ETreeModel", ETreeModel, e_tree_model_class_init, NULL, PARENT_TYPE)
+
+/* signals */
+
+ETreeModel *
+e_tree_model_new ()
+{
+	ETreeModel *et;
+
+	et = gtk_type_new (e_tree_model_get_type ());
+
+	return et;
+}
+
+static ETreePath *
+e_tree_model_node_at_row_1 (ETreeModel *etree, int *row, ETreePath *node)
+{
+	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;
+
+		num_children = e_tree_model_node_get_children (etree, node, &paths);
+
+		for (i = 0; i < num_children; i ++) {
+			ETreePath *p;
+
+			(*row) --;
+
+			p = e_tree_model_node_at_row_1 (etree, row, paths[i]);
+
+			if (p) {
+				ret = p;
+				break;
+			}
+		}
+
+		/* 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)
+{
+	return ETM_CLASS(etree)->get_root(etree);
+}
+
+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)
+{
+	return ETM_CLASS(etree)->get_next(etree, node);
+}
+
+ETreePath *
+e_tree_model_node_get_prev (ETreeModel *etree, ETreePath *node)
+{
+	return ETM_CLASS(etree)->get_prev(etree, node);
+}
+
+guint
+e_tree_model_node_depth (ETreeModel *etree, ETreePath *path)
+{
+	return g_list_length (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);
+}
+
+gboolean
+e_tree_model_node_is_root (ETreeModel *etree, ETreePath *path)
+{
+	return (e_tree_model_node_depth (etree, path) == 0);
+}
+
+gboolean
+e_tree_model_node_is_expandable (ETreeModel *etree, ETreePath *path)
+{
+	return (e_tree_model_node_get_children (etree, path, NULL) > 0);
+}
+
+gboolean
+e_tree_model_node_is_expanded (ETreeModel *etree, ETreePath *path)
+{
+	return ETM_CLASS(etree)->is_expanded (etree, path);
+}
+
+void
+e_tree_model_node_set_expanded (ETreeModel *etree, ETreePath *path, gboolean expanded)
+{
+	ETM_CLASS(etree)->set_expanded (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);
+}
+
+void
+e_tree_model_release_paths (ETreeModel *etree, ETreePath **paths, guint num_paths)
+{
+	ETM_CLASS(etree)->release_paths (etree, paths, num_paths);
+}
+
diff --git a/widgets/e-table/e-tree-model.h b/widgets/e-table/e-tree-model.h
new file mode 100644
index 0000000000..6924f3ea0e
--- /dev/null
+++ b/widgets/e-table/e-tree-model.h
@@ -0,0 +1,72 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+#ifndef _E_TREE_MODEL_H_
+#define _E_TREE_MODEL_H_
+
+#include "e-table-model.h"
+
+#define E_TREE_MODEL_TYPE        (e_tree_model_get_type ())
+#define E_TREE_MODEL(o)          (GTK_CHECK_CAST ((o), E_TREE_MODEL_TYPE, ETreeModel))
+#define E_TREE_MODEL_CLASS(k)    (GTK_CHECK_CLASS_CAST((k), E_TREE_MODEL_TYPE, ETreeModelClass))
+#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 struct {
+	ETableModel base;
+
+	ETableModel *source;
+
+	ETreePath *root_node;
+
+	GArray  *array;
+
+} ETreeModel;
+
+typedef struct {
+	ETableModelClass parent_class;
+
+	/*
+	 * Virtual methods
+	 */
+	ETreePath *(*get_root)      (ETreeModel *etm);
+
+	ETreePath *(*get_next)      (ETreeModel *etm, ETreePath* node);
+	ETreePath *(*get_prev)      (ETreeModel *etm, ETreePath* node);
+
+	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
+	 */
+
+} ETreeModelClass;
+
+GtkType     e_tree_model_get_type (void);
+
+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);
+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);
+
+#endif /* _E_TREE_MODEL_H */
diff --git a/widgets/e-table/tree-expanded.xpm b/widgets/e-table/tree-expanded.xpm
new file mode 100644
index 0000000000..fc748953eb
--- /dev/null
+++ b/widgets/e-table/tree-expanded.xpm
@@ -0,0 +1,22 @@
+/* XPM */
+static char * tree_expanded_xpm[] = {
+"16 16 3 1",
+" 	c None",
+".	c #000000",
+"+	c #FFFFFF",
+"                ",
+"                ",
+"                ",
+"                ",
+"    .........   ",
+"    .+++++++.   ",
+"    .+++++++.   ",
+"    .+++++++.   ",
+"    .+.....+.   ",
+"    .+++++++.   ",
+"    .+++++++.   ",
+"    .+++++++.   ",
+"    .........   ",
+"                ",
+"                ",
+"                "};
diff --git a/widgets/e-table/tree-unexpanded.xpm b/widgets/e-table/tree-unexpanded.xpm
new file mode 100644
index 0000000000..0dfb12a0a5
--- /dev/null
+++ b/widgets/e-table/tree-unexpanded.xpm
@@ -0,0 +1,22 @@
+/* XPM */
+static char * tree_unexpanded_xpm[] = {
+"16 16 3 1",
+" 	c None",
+".	c #000000",
+"+	c #FFFFFF",
+"                ",
+"                ",
+"                ",
+"                ",
+"    .........   ",
+"    .+++++++.   ",
+"    .+++.+++.   ",
+"    .+++.+++.   ",
+"    .+.....+.   ",
+"    .+++.+++.   ",
+"    .+++.+++.   ",
+"    .+++++++.   ",
+"    .........   ",
+"                ",
+"                ",
+"                "};
diff --git a/widgets/table/.cvsignore b/widgets/table/.cvsignore
index 1c61242460..ecfa617948 100644
--- a/widgets/table/.cvsignore
+++ b/widgets/table/.cvsignore
@@ -9,3 +9,4 @@ table-test
 table-example-1
 table-example-2
 table-size-test
+tree-example-1
diff --git a/widgets/table/e-cell-tree.c b/widgets/table/e-cell-tree.c
new file mode 100644
index 0000000000..9f40bd0eae
--- /dev/null
+++ b/widgets/table/e-cell-tree.c
@@ -0,0 +1,385 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/* e-cell-tree.c - Tree cell renderer
+ * Copyright (C) 2000 Helix Code, Inc.
+ *
+ * Author: Chris Toshok <toshok@helixcode.com>
+ *
+ * A majority of code taken from:
+ *
+ * the ECellText renderer.
+ *
+ * Copyright (C) 1998 The Free Software Foundation
+ *
+ */
+
+#include <config.h>
+#include <gtk/gtkenums.h>
+#include <gtk/gtkentry.h>
+#include <gtk/gtkwindow.h>
+#include <gtk/gtkinvisible.h>
+#include <gtk/gtksignal.h>
+#include <gdk/gdkkeysyms.h>
+#include <libgnomeui/gnome-canvas.h>
+#include <stdio.h>
+#include "e-table-sorted-variable.h"
+#include "e-tree-model.h"
+#include "e-cell-tree.h"
+#include "e-util/e-util.h"
+#include "e-table-item.h"
+
+#include <gdk/gdkx.h> /* for BlackPixel */
+#include <ctype.h>
+#include <math.h>
+
+#include "tree-expanded.xpm"
+#include "tree-unexpanded.xpm"
+
+#define PARENT_TYPE e_cell_get_type ()
+
+typedef struct {
+	ECellView    cell_view;
+	ECellView   *subcell_view;
+	GdkGC       *gc;
+
+	GnomeCanvas *canvas;
+
+} ECellTreeView;
+
+static ECellClass *parent_class;
+
+static GdkPixbuf   *tree_expanded_pixbuf, *tree_unexpanded_pixbuf;
+
+static ETreePath*
+e_cell_tree_get_node (ETreeModel *tree_model, int row)
+{
+	return (ETreePath*)e_table_model_value_at (E_TABLE_MODEL(tree_model), -1, row);
+}
+
+static ETreeModel*
+e_cell_tree_get_tree_model (ETableModel *table_model, int row)
+{
+	return (ETreeModel*)e_table_model_value_at (table_model, -2, row);
+}
+
+/*
+ * ECell::new_view method
+ */
+static ECellView *
+ect_new_view (ECell *ecell, ETableModel *table_model, void *e_table_item_view)
+{
+	ECellTree *ect = E_CELL_TREE (ecell);
+	ECellTreeView *tree_view = g_new0 (ECellTreeView, 1);
+	GnomeCanvas *canvas = GNOME_CANVAS_ITEM (e_table_item_view)->canvas;
+	
+	tree_view->cell_view.ecell = ecell;
+	tree_view->cell_view.e_table_model = table_model;
+	tree_view->cell_view.e_table_item_view = e_table_item_view;
+	
+	/* create our subcell view */
+	tree_view->subcell_view = e_cell_new_view (ect->subcell, table_model, e_table_item_view /* XXX */);
+
+	tree_view->canvas = canvas;
+
+	return (ECellView *)tree_view;
+}
+
+/*
+ * ECell::kill_view method
+ */
+static void
+ect_kill_view (ECellView *ecv)
+{
+	ECellTreeView *tree_view = (ECellTreeView *) ecv;
+
+	/* kill our subcell view */
+	e_cell_kill_view (tree_view->subcell_view);
+
+	g_free (tree_view);
+}
+
+/*
+ * ECell::realize method
+ */
+static void
+ect_realize (ECellView *ecell_view)
+{
+	ECellTreeView *tree_view = (ECellTreeView *) ecell_view;
+
+	/* realize our subcell view */
+	e_cell_realize (tree_view->subcell_view);
+
+	tree_view->gc = gdk_gc_new (GTK_WIDGET (tree_view->canvas)->window);
+
+	gdk_gc_set_line_attributes (tree_view->gc, 1, 
+				    GDK_LINE_ON_OFF_DASH, None, None);
+	gdk_gc_set_dashes (tree_view->gc, 0, "\1\1", 2);
+
+	if (parent_class->realize)
+		(* parent_class->realize) (ecell_view);
+}
+
+/*
+ * ECell::unrealize method
+ */
+static void
+ect_unrealize (ECellView *ecv)
+{
+	ECellTreeView *tree_view = (ECellTreeView *) ecv;
+
+	/* unrealize our subcell view. */
+	e_cell_unrealize (tree_view->subcell_view);
+
+	gdk_gc_unref (tree_view->gc);
+	tree_view->gc = NULL;
+
+	if (parent_class->unrealize)
+		(* parent_class->unrealize) (ecv);
+}
+
+#define INDENT_AMOUNT 16
+/*
+ * ECell::draw method
+ */
+static void
+ect_draw (ECellView *ecell_view, GdkDrawable *drawable,
+	  int model_col, int view_col, int row, gboolean selected,
+	  int x1, int y1, int x2, int y2)
+{
+	ECellTreeView *tree_view = (ECellTreeView *)ecell_view;
+	ETreeModel *tree_model = e_cell_tree_get_tree_model(ecell_view->e_table_model, row);
+	ETreePath *node;
+	GdkRectangle rect, *clip_rect;
+	GtkWidget *canvas = GTK_WIDGET (tree_view->canvas);
+	GdkGC *fg_gc = canvas->style->fg_gc[GTK_STATE_ACTIVE];
+	GdkColor *background, *foreground;
+
+	int offset, subcell_offset;
+	gboolean expanded, expandable;
+
+	/* only draw the tree effects if we're the active sort */
+	if (/* XXX */ TRUE) {
+		/*
+		 * need to get the following things from the model
+		 * 1. depth of item.
+		 * 2. whether or not it has any children.
+		 * 3. whether the item is a toplevel item.
+		 * 3. ... anything else?
+		 */
+		node = e_cell_tree_get_node (tree_model, row);
+
+		offset = (e_tree_model_node_depth (tree_model, node) + 1) * INDENT_AMOUNT;
+		expandable = e_tree_model_node_is_expandable (tree_model, node);
+		expanded = e_tree_model_node_is_expanded (tree_model, node);
+		subcell_offset = offset;
+
+		/*
+		 * Be a nice citizen: clip to the region we are supposed to draw on
+		 */
+		rect.x = x1;
+		rect.y = y1;
+		rect.width = offset;
+		rect.height = y2 - y1;
+	
+		gdk_gc_set_clip_rectangle (tree_view->gc, &rect);
+		gdk_gc_set_clip_rectangle (fg_gc, &rect);
+		clip_rect = &rect;
+
+		if (selected){
+			background = &canvas->style->bg [GTK_STATE_SELECTED];
+			foreground = &canvas->style->text [GTK_STATE_SELECTED];
+		} else {
+			background = &canvas->style->base [GTK_STATE_NORMAL];
+			foreground = &canvas->style->text [GTK_STATE_NORMAL];
+		}
+		gdk_gc_set_foreground (tree_view->gc, background);
+		gdk_draw_rectangle (drawable, tree_view->gc, TRUE,
+				    rect.x, rect.y, rect.width, rect.height);
+		gdk_gc_set_foreground (tree_view->gc, foreground);
+
+		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,
+				       rect.y + rect.height / 2,
+				       rect.x + offset,
+				       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)) {
+				if (e_tree_model_node_get_next(tree_model, node)) {
+					gdk_draw_line (drawable, tree_view->gc,
+						       rect.x + offset - INDENT_AMOUNT / 2,
+						       rect.y,
+						       rect.x + offset - INDENT_AMOUNT / 2,
+						       rect.y + rect.height);
+				}
+				node = e_tree_model_node_get_parent (tree_model, node);
+				offset -= INDENT_AMOUNT;
+			}
+		}
+
+		/* now draw our icon if we're expandable */
+		if (expandable) {
+			GdkPixbuf *image = expanded ? tree_expanded_pixbuf : tree_unexpanded_pixbuf;
+			int width, height;
+
+			width = gdk_pixbuf_get_width(image);
+			height = gdk_pixbuf_get_height(image);
+
+			gdk_pixbuf_render_to_drawable_alpha (image,
+							     drawable,
+							     0, 0,
+							     x1 + subcell_offset - INDENT_AMOUNT / 2 - width / 2,
+							     y1 + (y2 - y1) / 2 - height / 2,
+							     width, height,
+							     GDK_PIXBUF_ALPHA_BILEVEL,
+							     128,
+							     GDK_RGB_DITHER_NORMAL,
+							     width, 0);
+		}
+	}
+
+	/* Now cause our subcell to draw its contents, shifted by
+	   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);
+}
+
+/*
+ * ECell::event method
+ */
+static gint
+ect_event (ECellView *ecell_view, GdkEvent *event, int model_col, int view_col, int row)
+{
+	ECellTreeView *tree_view = (ECellTreeView *) ecell_view;
+
+	switch (event->type) {
+	case GDK_BUTTON_PRESS:
+	case GDK_BUTTON_RELEASE: {
+		/* 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;
+
+		/* only activate the tree control if the click/release happens in the icon's area. */
+		if (event->button.x > (offset - INDENT_AMOUNT) && event->button.x < offset) {
+			if (e_tree_model_node_is_expandable (tree_model, node)) {
+				e_tree_model_node_set_expanded (tree_model,
+								node,
+								!e_tree_model_node_is_expanded(tree_model, node));
+			}
+			return TRUE;
+		}
+	}
+	default:
+		/* otherwise, pass it off to our subcell_view */
+		e_cell_event(tree_view->subcell_view, event, model_col, view_col, row);
+		return TRUE;
+	}
+}
+
+/*
+ * ECell::height method
+ */
+static int
+ect_height (ECellView *ecell_view, int model_col, int view_col, int row) 
+{
+	ECellTreeView *tree_view = (ECellTreeView *) ecell_view;
+
+	return e_cell_height (tree_view->subcell_view, model_col, view_col, row);
+}
+
+/*
+ * ECellView::enter_edit method
+ */
+static void *
+ect_enter_edit (ECellView *ecell_view, int model_col, int view_col, int row)
+{
+	return NULL;
+}
+
+/*
+ * ECellView::leave_edit method
+ */
+static void
+ect_leave_edit (ECellView *ecell_view, int model_col, int view_col, int row, void *edit_context)
+{
+}
+
+/*
+ * GtkObject::destroy method
+ */
+static void
+ect_destroy (GtkObject *object)
+{
+	ECellTree *ect = E_CELL_TREE (object);
+
+	/* destroy our subcell */
+	gtk_object_destroy (GTK_OBJECT (ect->subcell));
+
+	GTK_OBJECT_CLASS (parent_class)->destroy (object);
+}
+
+static void
+e_cell_tree_class_init (GtkObjectClass *object_class)
+{
+	ECellClass *ecc = (ECellClass *) object_class;
+
+	object_class->destroy = ect_destroy;
+
+	ecc->new_view   = ect_new_view;
+	ecc->kill_view  = ect_kill_view;
+	ecc->realize    = ect_realize;
+	ecc->unrealize  = ect_unrealize;
+	ecc->draw       = ect_draw;
+	ecc->event      = ect_event;
+	ecc->height     = ect_height;
+	ecc->enter_edit = ect_enter_edit;
+	ecc->leave_edit = ect_leave_edit;
+
+	parent_class = gtk_type_class (PARENT_TYPE);
+
+	/*
+	 * Create our pixbuf for expanding/unexpanding
+	 */
+	tree_expanded_pixbuf = gdk_pixbuf_new_from_xpm_data(tree_expanded_xpm);
+	tree_unexpanded_pixbuf = gdk_pixbuf_new_from_xpm_data(tree_unexpanded_xpm);
+}
+
+E_MAKE_TYPE(e_cell_tree, "ECellTree", ECellTree, e_cell_tree_class_init, NULL, PARENT_TYPE);
+
+void
+e_cell_tree_construct (ECellTree *ect,
+		       gboolean draw_lines,
+		       ECell *subcell)
+{		       
+	ect->subcell = subcell;
+	ect->draw_lines = draw_lines;
+}
+
+
+ECell *
+e_cell_tree_new (ETableModel *etm,
+		 gboolean draw_lines,
+		 ECell *subcell)
+{
+	ECellTree *ect = gtk_type_new (e_cell_tree_get_type ());
+
+	e_cell_tree_construct (ect, draw_lines, subcell);
+
+	return (ECell *) ect;
+}
diff --git a/widgets/table/e-cell-tree.h b/widgets/table/e-cell-tree.h
new file mode 100644
index 0000000000..6bd842b39e
--- /dev/null
+++ b/widgets/table/e-cell-tree.h
@@ -0,0 +1,42 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/* ECellTree - Tree item for e-table.
+ * Copyright (C) 2000 Helix Code, Inc.
+ * Author: Chris Toshok <toshok@helixcode.com>
+ *
+ */
+#ifndef _E_CELL_TREE_H_
+#define _E_CELL_TREE_H_
+
+#include <libgnomeui/gnome-canvas.h>
+#include "e-cell.h"
+
+#define E_CELL_TREE_TYPE        (e_cell_tree_get_type ())
+#define E_CELL_TREE(o)          (GTK_CHECK_CAST ((o), E_CELL_TREE_TYPE, ECellTree))
+#define E_CELL_TREE_CLASS(k)    (GTK_CHECK_CLASS_CAST((k), E_CELL_TREE_TYPE, ECellTreeClass))
+#define E_IS_CELL_TREE(o)       (GTK_CHECK_TYPE ((o), E_CELL_TREE_TYPE))
+#define E_IS_CELL_TREE_CLASS(k) (GTK_CHECK_CLASS_TYPE ((k), E_CELL_TREE_TYPE))
+
+typedef struct {
+	ECell parent;
+
+	gboolean draw_lines;
+
+	GdkPixbuf   *expanded_image;
+	GdkPixbuf   *unexpanded_image;
+
+	ECell *subcell;
+} ECellTree;
+
+typedef struct {
+	ECellClass parent_class;
+} ECellTreeClass;
+
+GtkType    e_cell_tree_get_type (void);
+ECell     *e_cell_tree_new      (ETableModel *model, gboolean draw_lines,
+				 ECell *subcell);
+void       e_cell_tree_construct (ECellTree *ect, gboolean draw_lines,
+				  ECell *subcell);
+
+#endif /* _E_CELL_TREE_H_ */
+
+
diff --git a/widgets/table/e-tree-example-1.c b/widgets/table/e-tree-example-1.c
new file mode 100644
index 0000000000..e5563a8071
--- /dev/null
+++ b/widgets/table/e-tree-example-1.c
@@ -0,0 +1,243 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/* This code is GPL. */
+#include <stdio.h>
+#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"
+#include "e-cell-text.h"
+#include "e-cell-tree.h"
+#include "e-cell-checkbox.h"
+#include "e-table.h"
+
+#include <gdk-pixbuf/gdk-pixbuf.h>
+
+#define ROWS 10
+#define COLS 4
+
+#define IMPORTANCE_COLUMN 4
+#define COLOR_COLUMN 5
+
+/*
+ * Here we define the initial layout of the table.  This is an xml
+ * format that allows you to change the initial ordering of the
+ * columns or to do sorting or grouping initially.  This specification
+ * shows all 5 columns, but moves the importance column nearer to the
+ * front.  It also sorts by the "Full Name" column (ascending.)
+ * Sorting and grouping take the model column as their arguments
+ * (sorting is specified by the "column" argument to the leaf elemnt.
+ */
+
+#define INITIAL_SPEC "<ETableSpecification>                    	       \
+	<columns-shown>                  			       \
+		<column> 0 </column>     			       \
+		<column> 4 </column>     			       \
+		<column> 1 </column>     			       \
+		<column> 2 </column>     			       \
+		<column> 3 </column>     			       \
+	</columns-shown>                 			       \
+	<grouping></grouping>                                         \
+</ETableSpecification>"
+
+/*
+ * Virtual Column list:
+ * 0   Subject
+ * 1   Full Name
+ * 2   Email
+ * 3   Date
+ */
+char *headers [COLS] = {
+  "Subject",
+  "Full Name",
+  "Email",
+  "Date"
+};
+
+/*
+ * ETableSimple callbacks
+ * These are the callbacks that define the behavior of our custom model.
+ */
+
+/*
+ * Since our model is a constant size, we can just return its size in
+ * the column and row count fields.
+ */
+
+/* This function returns the number of columns in our ETableModel. */
+static int
+my_col_count (ETableModel *etc, void *data)
+{
+	return COLS;
+}
+
+/* This function returns the value at a particular point in our ETableModel. */
+static void *
+my_value_at (ETreeModel *etc, GNode *node, int col, void *data)
+{
+	switch (col) {
+	case 0: return "Re: Two things";
+	case 1: return "Chris Toshok";
+	case 2: return "toshok@helixcode.com";
+	case 3: return "Jun 07 2000";
+	default: return NULL;
+	}
+}
+
+/* This function sets the value at a particular point in our ETableModel. */
+static void
+my_set_value_at (ETableModel *etc, GNode *node, int col, const void *val, void *data)
+{
+}
+
+/* This function returns whether a particular cell is editable. */
+static gboolean
+my_is_editable (ETableModel *etc, GNode *node, int col, void *data)
+{
+	return FALSE;
+}
+
+/* This function duplicates the value passed to it. */
+static void *
+my_duplicate_value (ETableModel *etc, int col, const void *value, void *data)
+{
+	return g_strdup (value);
+}
+
+/* This function frees the value passed to it. */
+static void
+my_free_value (ETableModel *etc, int col, void *value, void *data)
+{
+	g_free (value);
+}
+
+/* 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)
+{
+}
+
+/* We create a window containing our new tree. */
+static void
+create_tree (void)
+{
+	GtkWidget *e_table, *window, *frame;
+	ECell *cell_left_just;
+	ECell *cell_tree;
+	ETableHeader *e_table_header;
+	int i, j;
+	ETreeModel *e_tree_model = NULL;
+	GNode *root_node;
+
+	/* create a root node with 5 children */
+	root_node = g_node_new (NULL);
+	for (i = 0; i < 5; i++){
+		GNode *n = g_node_insert (root_node, 0, g_node_new(NULL));
+		for (j = 0; j < 5; j ++) {
+			g_node_insert (n, 0, g_node_new(NULL));
+		}
+	}
+
+	/* 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
+	 * list of possible columns in the view.  The second use is
+	 * completely internal.  Many of the ETableHeader functions are
+	 * for that purpose.  The only functions we really need are
+	 * e_table_header_new and e_table_header_add_col.
+	 *
+	 * First we create the header.
+	 */
+	e_table_header = e_table_header_new ();
+	
+	/*
+	 * Next we have to build renderers for all of the columns.
+	 * Since all our columns are text columns, we can simply use
+	 * the same renderer over and over again.  If we had different
+	 * types of columns, we could use a different renderer for
+	 * each column.
+	 */
+	cell_left_just = e_cell_text_new (E_TABLE_MODEL(e_tree_model), NULL, GTK_JUSTIFY_LEFT);
+	
+	/* 
+	 * This renderer is used for the tree column (the leftmost one), and
+	 * has as its subcell renderer the text renderer.  this means that
+	 * text is displayed to the right of the tree pipes.
+	 */
+	cell_tree = e_cell_tree_new (E_TABLE_MODEL(e_tree_model), TRUE, cell_left_just);
+
+	/*
+	 * Next we create a column object for each view column and add
+	 * them to the header.  We don't create a column object for
+	 * the importance column since it will not be shown.
+	 */
+	for (i = 0; i < COLS; i++) {
+		/* Create the column. */
+		ETableCol *ecol = e_table_col_new (
+						   i, headers [i],
+						   80, 20,
+						   i == 0 ? cell_tree
+						   : cell_left_just,
+						   g_str_compare, TRUE);
+		/* Add it to the header. */
+		e_table_header_add_column (e_table_header, ecol, i);
+	}
+
+	/*
+	 * Here we create a window for our new table.  This window
+	 * will get shown and the person will be able to test their
+	 * item.
+	 */
+	window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+
+	/* This frame is simply to get a bevel around our table. */
+	frame = gtk_frame_new (NULL);
+
+	/*
+	 * Here we create the table.  We give it the three pieces of
+	 * the table we've created, the header, the model, and the
+	 * initial layout.  It does the rest.
+	 */
+	e_table = e_table_new (e_table_header, E_TABLE_MODEL(e_tree_model), INITIAL_SPEC);
+
+	if (!e_table) printf ("BAH!");
+
+	/* Build the gtk widget hierarchy. */
+	gtk_container_add (GTK_CONTAINER (frame), e_table);
+	gtk_container_add (GTK_CONTAINER (window), frame);
+
+	/* Size the initial window. */
+	gtk_widget_set_usize (window, 200, 200);
+
+	/* Show it all. */
+	gtk_widget_show_all (window);
+}
+
+/* This is the main function which just initializes gnome and call our create_tree function */
+
+int
+main (int argc, char *argv [])
+{
+	gnome_init ("TableExample", "TableExample", argc, argv);
+	e_cursors_init ();
+
+	gtk_widget_push_visual (gdk_rgb_get_visual ());
+	gtk_widget_push_colormap (gdk_rgb_get_cmap ());
+
+	create_tree ();
+	
+	gtk_main ();
+
+	e_cursors_shutdown ();
+	return 0;
+}
+
diff --git a/widgets/table/e-tree-gnode.c b/widgets/table/e-tree-gnode.c
new file mode 100644
index 0000000000..ce53751b56
--- /dev/null
+++ b/widgets/table/e-tree-gnode.c
@@ -0,0 +1,210 @@
+/* -*- 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
new file mode 100644
index 0000000000..56e6b19b80
--- /dev/null
+++ b/widgets/table/e-tree-gnode.h
@@ -0,0 +1,37 @@
+/* -*- 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
new file mode 100644
index 0000000000..d67e59f3f5
--- /dev/null
+++ b/widgets/table/e-tree-model.c
@@ -0,0 +1,316 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * e-tree-model.c: a Tree Model
+ *
+ * Author:
+ *   Chris Toshok (toshok@helixcode.com)
+ *
+ * Adapted from the gtree code and ETableModel.
+ *
+ * (C) 2000 Helix Code, Inc.
+ */
+#include <config.h>
+#include <gtk/gtksignal.h>
+#include "e-util/e-util.h"
+#include "e-tree-model.h"
+
+#define ETM_CLASS(e) ((ETreeModelClass *)((GtkObject *)e)->klass)
+
+#define PARENT_TYPE E_TABLE_MODEL_TYPE
+
+static ETableModel *e_tree_model_parent_class;
+
+/* virtual methods */
+
+static ETreePath*
+etree_get_root (ETreeModel *etm)
+{
+	/* shouldn't be called */
+	g_assert(0);
+	return NULL;
+}
+
+static void*
+etree_value_at (ETreeModel *etm, ETreePath* node, int col)
+{
+	/* shouldn't be called */
+	g_assert(0);
+	return NULL;
+}
+
+static void
+etree_set_value_at (ETreeModel *etm, ETreePath* node, int col, const void *val)
+{
+	/* shouldn't be called */
+	g_assert(0);
+}
+
+static gboolean
+etree_is_editable (ETreeModel *etm, ETreePath* node, int col)
+{
+	/* shouldn't be called */
+	g_assert(0);
+	return FALSE;
+}
+
+static gboolean
+etree_is_expanded (ETreeModel *etm, ETreePath* node)
+{
+	/* shouldn't be called */
+	g_assert(0);
+	return FALSE;
+}
+
+static guint
+etree_get_children (ETreeModel *etm, ETreePath* node, ETreePath ***paths)
+{
+	/* shouldn't be called */
+	g_assert(0);
+	return 0;
+}
+
+static void
+etree_release_paths (ETreeModel *etm, ETreePath **paths, guint num_paths)
+{
+	/* shouldn't be called */
+	g_assert(0);
+}
+
+static void
+etree_set_expanded (ETreeModel *etm, ETreePath* node, gboolean expanded)
+{
+	/* shouldn't be called */
+	g_assert(0);
+}
+
+static void
+etree_destroy (GtkObject *object)
+{
+}
+
+guint
+e_tree_model_node_num_visible_descendents (ETreeModel *etm, ETreePath *node)
+{
+	int count = 1;
+	if (e_tree_model_node_is_expanded (etm, node)) {
+		ETreePath **paths;
+		int i;
+		int num_paths;
+
+		num_paths = e_tree_model_node_get_children (etm, node, &paths);
+
+		for (i = 0; i < num_paths; i ++)
+			count += e_tree_model_node_num_visible_descendents(etm, paths[i]);
+
+		e_tree_model_release_paths (etm, paths, num_paths);
+	}
+
+	return count;
+}
+
+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)));
+}
+
+static void *
+etable_value_at (ETableModel *etm, int col, int row)
+{
+	ETreeModel *etree = E_TREE_MODEL(etm);
+	ETreeModelClass *et_class = ETM_CLASS(etm);
+	ETreePath* node = e_tree_model_node_at_row (etree, row);
+
+	g_return_val_if_fail (node, NULL);
+
+	if (col == -1)
+		return node;
+	else if (col == -2)
+		return etm;
+	else
+		return et_class->value_at (etree, node, col);
+}
+
+static void
+etable_set_value_at (ETableModel *etm, int col, int row, const void *val)
+{
+	ETreeModel *etree = E_TREE_MODEL(etm);
+	ETreeModelClass *et_class = ETM_CLASS(etm);
+	ETreePath* node = e_tree_model_node_at_row (etree, row);
+
+	g_return_if_fail (node);
+
+	et_class->set_value_at (etree, node, col, val);
+}
+
+static gboolean
+etable_is_cell_editable (ETableModel *etm, int col, int row)
+{
+	ETreeModel *etree = E_TREE_MODEL(etm);
+	ETreeModelClass *et_class = ETM_CLASS(etm);
+	ETreePath* node = e_tree_model_node_at_row (etree, row);
+
+	g_return_val_if_fail (node, FALSE);
+
+	return et_class->is_editable (etree, node, col);
+}
+
+static void
+e_tree_model_class_init (GtkObjectClass *klass)
+{
+	ETableModelClass *table_class = (ETableModelClass *) klass;
+	ETreeModelClass *tree_class = (ETreeModelClass *) klass;
+
+	e_tree_model_parent_class = gtk_type_class (PARENT_TYPE);
+	
+	klass->destroy = etree_destroy;
+
+	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
+	table_class->duplicate_value  = etable_duplicate_value;
+	table_class->free_value       = etable_free_value;
+	table_class->initialize_value = etable_initialize_value;
+	table_class->value_is_empty   = etable_value_is_empty;
+	table_class->thaw             = etable_thaw;
+#endif
+
+	tree_class->get_root          = etree_get_root;
+	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->set_expanded      = etree_set_expanded;
+}
+
+E_MAKE_TYPE(e_tree_model, "ETreeModel", ETreeModel, e_tree_model_class_init, NULL, PARENT_TYPE)
+
+/* signals */
+
+ETreeModel *
+e_tree_model_new ()
+{
+	ETreeModel *et;
+
+	et = gtk_type_new (e_tree_model_get_type ());
+
+	return et;
+}
+
+static ETreePath *
+e_tree_model_node_at_row_1 (ETreeModel *etree, int *row, ETreePath *node)
+{
+	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;
+
+		num_children = e_tree_model_node_get_children (etree, node, &paths);
+
+		for (i = 0; i < num_children; i ++) {
+			ETreePath *p;
+
+			(*row) --;
+
+			p = e_tree_model_node_at_row_1 (etree, row, paths[i]);
+
+			if (p) {
+				ret = p;
+				break;
+			}
+		}
+
+		/* 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)
+{
+	return ETM_CLASS(etree)->get_root(etree);
+}
+
+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)
+{
+	return ETM_CLASS(etree)->get_next(etree, node);
+}
+
+ETreePath *
+e_tree_model_node_get_prev (ETreeModel *etree, ETreePath *node)
+{
+	return ETM_CLASS(etree)->get_prev(etree, node);
+}
+
+guint
+e_tree_model_node_depth (ETreeModel *etree, ETreePath *path)
+{
+	return g_list_length (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);
+}
+
+gboolean
+e_tree_model_node_is_root (ETreeModel *etree, ETreePath *path)
+{
+	return (e_tree_model_node_depth (etree, path) == 0);
+}
+
+gboolean
+e_tree_model_node_is_expandable (ETreeModel *etree, ETreePath *path)
+{
+	return (e_tree_model_node_get_children (etree, path, NULL) > 0);
+}
+
+gboolean
+e_tree_model_node_is_expanded (ETreeModel *etree, ETreePath *path)
+{
+	return ETM_CLASS(etree)->is_expanded (etree, path);
+}
+
+void
+e_tree_model_node_set_expanded (ETreeModel *etree, ETreePath *path, gboolean expanded)
+{
+	ETM_CLASS(etree)->set_expanded (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);
+}
+
+void
+e_tree_model_release_paths (ETreeModel *etree, ETreePath **paths, guint num_paths)
+{
+	ETM_CLASS(etree)->release_paths (etree, paths, num_paths);
+}
+
diff --git a/widgets/table/e-tree-model.h b/widgets/table/e-tree-model.h
new file mode 100644
index 0000000000..6924f3ea0e
--- /dev/null
+++ b/widgets/table/e-tree-model.h
@@ -0,0 +1,72 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+#ifndef _E_TREE_MODEL_H_
+#define _E_TREE_MODEL_H_
+
+#include "e-table-model.h"
+
+#define E_TREE_MODEL_TYPE        (e_tree_model_get_type ())
+#define E_TREE_MODEL(o)          (GTK_CHECK_CAST ((o), E_TREE_MODEL_TYPE, ETreeModel))
+#define E_TREE_MODEL_CLASS(k)    (GTK_CHECK_CLASS_CAST((k), E_TREE_MODEL_TYPE, ETreeModelClass))
+#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 struct {
+	ETableModel base;
+
+	ETableModel *source;
+
+	ETreePath *root_node;
+
+	GArray  *array;
+
+} ETreeModel;
+
+typedef struct {
+	ETableModelClass parent_class;
+
+	/*
+	 * Virtual methods
+	 */
+	ETreePath *(*get_root)      (ETreeModel *etm);
+
+	ETreePath *(*get_next)      (ETreeModel *etm, ETreePath* node);
+	ETreePath *(*get_prev)      (ETreeModel *etm, ETreePath* node);
+
+	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
+	 */
+
+} ETreeModelClass;
+
+GtkType     e_tree_model_get_type (void);
+
+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);
+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);
+
+#endif /* _E_TREE_MODEL_H */
diff --git a/widgets/table/tree-expanded.xpm b/widgets/table/tree-expanded.xpm
new file mode 100644
index 0000000000..fc748953eb
--- /dev/null
+++ b/widgets/table/tree-expanded.xpm
@@ -0,0 +1,22 @@
+/* XPM */
+static char * tree_expanded_xpm[] = {
+"16 16 3 1",
+" 	c None",
+".	c #000000",
+"+	c #FFFFFF",
+"                ",
+"                ",
+"                ",
+"                ",
+"    .........   ",
+"    .+++++++.   ",
+"    .+++++++.   ",
+"    .+++++++.   ",
+"    .+.....+.   ",
+"    .+++++++.   ",
+"    .+++++++.   ",
+"    .+++++++.   ",
+"    .........   ",
+"                ",
+"                ",
+"                "};
diff --git a/widgets/table/tree-unexpanded.xpm b/widgets/table/tree-unexpanded.xpm
new file mode 100644
index 0000000000..0dfb12a0a5
--- /dev/null
+++ b/widgets/table/tree-unexpanded.xpm
@@ -0,0 +1,22 @@
+/* XPM */
+static char * tree_unexpanded_xpm[] = {
+"16 16 3 1",
+" 	c None",
+".	c #000000",
+"+	c #FFFFFF",
+"                ",
+"                ",
+"                ",
+"                ",
+"    .........   ",
+"    .+++++++.   ",
+"    .+++.+++.   ",
+"    .+++.+++.   ",
+"    .+.....+.   ",
+"    .+++.+++.   ",
+"    .+++.+++.   ",
+"    .+++++++.   ",
+"    .........   ",
+"                ",
+"                ",
+"                "};
-- 
cgit v1.2.3