change things so we focus the cell and select the row, and also dispatch

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

	* e-table-item.c (eti_event): change things so we focus the cell
	and select the row, and also dispatch the event to that row/cell.
	This fixes the problem with the tree where you had to click twice
	to activate the tree controls.

	* Makefile.am (libetable_a_SOURCES): remove e-tree-gnode.* and add
	e-tree-simple.*
	(icons): add tree-expanded.xpm and tree-unexpanded.xpm

	* e-cell-tree.c (ect_enter_edit): defer to subcell's view.
	(ect_leave_edit): defer to subcell's view.
	(visible_depth_of_node): visual depth, taking into account that
	the root node of the model might not be displayed.
	(offset_of_node): return the offset used to shift the subcell
	over.
	(ect_draw): don't draw vertical lines if we're the leftmode node
	(a visual root node).  also, don't shift x2 by the subcell offset,
	so we get ellipses like we're supposed to.
	(ect_event): remove GDK_BUTTON_RELEASE from the list of events
	that we care about.

	* e-tree-example-1.c: lots of changes, a more dynamic UI so we can
	test tree model api stuff.

	* e-tree-gnode.c, e-tree-gnode.c: removed files, since their guts
	have been rolled into e-tree-model.c

	* e-tree-model.c, e-tree-model.h: substantially changed.  too much
	to really describe here.  this should really be considered the
	first revision of these files :)

	* e-tree-simple.c, e-tree-simple.h: analogous to e-table-simple, a
	subclass that allows the use of function pointers.

svn path=/trunk/; revision=3519
This commit is contained in:
Chris Toshok 2000-06-11 04:20:56 +00:00 committed by Chris Toshok
parent 835ec00d01
commit aac3f2c8b6
20 changed files with 1746 additions and 876 deletions

View File

@ -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> 2000-06-11 Christopher James Lahey <clahey@helixcode.com>
* e-table-model.c: Small syntactic changes. * e-table-model.c: Small syntactic changes.

View File

@ -76,10 +76,10 @@ libetable_a_SOURCES = \
e-table-subset-variable.h \ e-table-subset-variable.h \
e-table-text-model.c \ 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.c \
e-tree-model.h e-tree-model.h \
e-tree-simple.c \
e-tree-simple.h
noinst_PROGRAMS = \ noinst_PROGRAMS = \
table-test table-example-1 table-example-2 table-size-test tree-example-1 table-test table-example-1 table-example-2 table-size-test tree-example-1
@ -159,7 +159,9 @@ icons = \
add-col.xpm \ add-col.xpm \
check-empty.xpm \ check-empty.xpm \
check-filled.xpm \ check-filled.xpm \
remove-col.xpm remove-col.xpm \
tree-expanded.xpm \
tree-unexpanded.xpm
EXTRA_DIST = \ EXTRA_DIST = \
sample.table \ sample.table \

View File

@ -49,6 +49,21 @@ static ECellClass *parent_class;
static GdkPixbuf *tree_expanded_pixbuf, *tree_unexpanded_pixbuf; 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* static ETreePath*
e_cell_tree_get_node (ETreeModel *tree_model, int row) e_cell_tree_get_node (ETreeModel *tree_model, int row)
{ {
@ -136,7 +151,6 @@ ect_unrealize (ECellView *ecv)
(* parent_class->unrealize) (ecv); (* parent_class->unrealize) (ecv);
} }
#define INDENT_AMOUNT 16
/* /*
* ECell::draw method * ECell::draw method
*/ */
@ -167,7 +181,7 @@ ect_draw (ECellView *ecell_view, GdkDrawable *drawable,
*/ */
node = e_cell_tree_get_node (tree_model, row); 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); expandable = e_tree_model_node_is_expandable (tree_model, node);
expanded = e_tree_model_node_is_expanded (tree_model, node); expanded = e_tree_model_node_is_expanded (tree_model, node);
subcell_offset = offset; subcell_offset = offset;
@ -196,15 +210,8 @@ ect_draw (ECellView *ecell_view, GdkDrawable *drawable,
rect.x, rect.y, rect.width, rect.height); rect.x, rect.y, rect.width, rect.height);
gdk_gc_set_foreground (tree_view->gc, foreground); gdk_gc_set_foreground (tree_view->gc, foreground);
/* draw our lines */
if (E_CELL_TREE(tree_view->cell_view.ecell)->draw_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, gdk_draw_line (drawable, tree_view->gc,
rect.x + offset - INDENT_AMOUNT / 2 + 1, rect.x + offset - INDENT_AMOUNT / 2 + 1,
@ -212,12 +219,22 @@ ect_draw (ECellView *ecell_view, GdkDrawable *drawable,
rect.x + offset, rect.x + offset,
rect.y + rect.height / 2); 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 /* now traverse back up to the root of the tree, checking at
each level if the node has siblings, and drawing the each level if the node has siblings, and drawing the
correct vertical pipe for it's configuration. */ correct vertical pipe for it's configuration. */
node = e_tree_model_node_get_parent (tree_model, node); node = e_tree_model_node_get_parent (tree_model, node);
offset -= INDENT_AMOUNT; 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)) { if (e_tree_model_node_get_next(tree_model, node)) {
gdk_draw_line (drawable, tree_view->gc, gdk_draw_line (drawable, tree_view->gc,
rect.x + offset - INDENT_AMOUNT / 2, rect.x + offset - INDENT_AMOUNT / 2,
@ -255,7 +272,7 @@ ect_draw (ECellView *ecell_view, GdkDrawable *drawable,
subcell_offset pixels */ subcell_offset pixels */
e_cell_draw (tree_view->subcell_view, drawable, e_cell_draw (tree_view->subcell_view, drawable,
model_col, view_col, row, selected, 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; ECellTreeView *tree_view = (ECellTreeView *) ecell_view;
switch (event->type) { switch (event->type) {
case GDK_BUTTON_PRESS: case GDK_BUTTON_PRESS: {
case GDK_BUTTON_RELEASE: {
/* if the event happened in our area of control (and /* if the event happened in our area of control (and
we care about it), handle it. */ we care about it), handle it. */
ETreeModel *tree_model = e_cell_tree_get_tree_model (ecell_view->e_table_model, row); 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); 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. */ /* 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 (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; return TRUE;
} }
else if (event->button.x < (offset - INDENT_AMOUNT))
return TRUE;
} }
default: default:
/* otherwise, pass it off to our subcell_view */ /* 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 * static void *
ect_enter_edit (ECellView *ecell_view, int model_col, int view_col, int row) 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 static void
ect_leave_edit (ECellView *ecell_view, int model_col, int view_col, int row, void *edit_context) 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);
} }
/* /*

View File

@ -1197,6 +1197,14 @@ eti_event (GnomeCanvasItem *item, GdkEvent *e)
if (!find_cell (eti, e->button.x, e->button.y, &col, &row, &x1, &y1)) if (!find_cell (eti, e->button.x, e->button.y, &col, &row, &x1, &y1))
return TRUE; 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){ if (eti->cursor_row == row && eti->cursor_col == col){
ecol = e_table_header_get_column (eti->header, 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->button.y = y1;
e_cell_event (ecell_view, e, ecol->col_idx, col, row); 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; break;
case 3: case 3:

View File

@ -4,7 +4,6 @@
#include <string.h> #include <string.h>
#include <gnome.h> #include <gnome.h>
#include "e-util/e-cursors.h" #include "e-util/e-cursors.h"
#include "e-tree-gnode.h"
#include "e-table-header.h" #include "e-table-header.h"
#include "e-table-header-item.h" #include "e-table-header-item.h"
#include "e-table-item.h" #include "e-table-item.h"
@ -12,6 +11,7 @@
#include "e-cell-tree.h" #include "e-cell-tree.h"
#include "e-cell-checkbox.h" #include "e-cell-checkbox.h"
#include "e-table.h" #include "e-table.h"
#include "e-tree-simple.h"
#include <gdk-pixbuf/gdk-pixbuf.h> #include <gdk-pixbuf/gdk-pixbuf.h>
@ -56,6 +56,8 @@ char *headers [COLS] = {
"Date" "Date"
}; };
GtkWidget *e_table;
/* /*
* ETableSimple callbacks * ETableSimple callbacks
* These are the callbacks that define the behavior of our custom model. * 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; 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 * 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) { 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 1: return "Chris Toshok";
case 2: return "toshok@helixcode.com"; case 2: return "toshok@helixcode.com";
case 3: return "Jun 07 2000"; 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 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. */ /* This function returns whether a particular cell is editable. */
static gboolean 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)
{
return g_strdup (value);
}
/* This function frees the value passed to it. */
static void static void
my_free_value (ETableModel *etc, int col, void *value, void *data) toggle_root (GtkButton *button, gpointer data)
{ {
g_free (value); 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));
} }
/* This function is for when the model is unfrozen. This can mostly
be ignored for simple models. */
static void static void
my_thaw (ETableModel *etc, void *data) 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)
{
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);
}
static void
expand_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, TRUE);
}
static void
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. */ /* We create a window containing our new tree. */
static void static void
create_tree (void) create_tree (void)
{ {
GtkWidget *e_table, *window, *frame; GtkWidget *window, *frame, *button, *vbox;
ECell *cell_left_just; ECell *cell_left_just;
ECell *cell_tree; ECell *cell_tree;
ETableHeader *e_table_header; ETableHeader *e_table_header;
int i, j; int i, j;
ETreeModel *e_tree_model = NULL; 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 */ /* 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++){ 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 ++) { 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 * Next we create a header. The ETableHeader is used in two
* different way. The first is the full_header. This is the * 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); window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
/* This frame is simply to get a bevel around our table. */ /* This frame is simply to get a bevel around our table. */
vbox = gtk_vbox_new (FALSE, 0);
frame = gtk_frame_new (NULL); frame = gtk_frame_new (NULL);
/* /*
@ -213,8 +312,34 @@ create_tree (void)
/* Build the gtk widget hierarchy. */ /* Build the gtk widget hierarchy. */
gtk_container_add (GTK_CONTAINER (frame), e_table); 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. */ /* Size the initial window. */
gtk_widget_set_usize (window, 200, 200); gtk_widget_set_usize (window, 200, 200);

View File

@ -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;
}

View File

@ -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_ */

View File

@ -20,21 +20,197 @@
static ETableModel *e_tree_model_parent_class; 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 */ /* virtual methods */
static void
etree_destroy (GtkObject *object)
{
/* XXX lots of stuff to free here */
}
static ETreePath* static ETreePath*
etree_get_root (ETreeModel *etm) etree_get_root (ETreeModel *etm)
{ {
/* shouldn't be called */ return etm->root;
g_assert(0);
return NULL;
} }
static ETreePath*
etree_get_parent (ETreeModel *etm, ETreePath *path)
{
g_return_val_if_fail (path, NULL);
return path->parent;
}
static ETreePath*
etree_get_next (ETreeModel *etm, ETreePath *node)
{
g_return_val_if_fail (node, NULL);
return g_node_next_sibling(node);
}
static ETreePath*
etree_get_prev (ETreeModel *etm, ETreePath *node)
{
g_return_val_if_fail (node, NULL);
return g_node_prev_sibling (node);
}
static guint
etree_get_children (ETreeModel *etm, ETreePath* node, ETreePath ***paths)
{
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 gboolean
etree_is_expanded (ETreeModel *etm, ETreePath* node)
{
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)
{
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_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);
}
}
static ETreePath *
etree_node_at_row (ETreeModel *etree, int row)
{
GNode *node = g_array_index (etree->row_array, GNode*, row);
return node;
}
/* ETable analogs */
static void* static void*
etree_value_at (ETreeModel *etm, ETreePath* node, int col) etree_value_at (ETreeModel *etm, ETreePath* node, int col)
{ {
/* shouldn't be called */ /* shouldn't be called */
g_assert(0); g_assert (0);
return NULL; return NULL;
} }
@ -42,7 +218,7 @@ static void
etree_set_value_at (ETreeModel *etm, ETreePath* node, int col, const void *val) etree_set_value_at (ETreeModel *etm, ETreePath* node, int col, const void *val)
{ {
/* shouldn't be called */ /* shouldn't be called */
g_assert(0); g_assert (0);
} }
static gboolean static gboolean
@ -53,65 +229,13 @@ etree_is_editable (ETreeModel *etm, ETreePath* node, int col)
return FALSE; return FALSE;
} }
static gboolean
etree_is_expanded (ETreeModel *etm, ETreePath* node) /* ETable virtual functions we map */
{
/* 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 static int
etable_row_count (ETableModel *etm) 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 * static void *
@ -155,6 +279,7 @@ etable_is_cell_editable (ETableModel *etm, int col, int row)
return et_class->is_editable (etree, node, col); return et_class->is_editable (etree, node, col);
} }
static void static void
e_tree_model_class_init (GtkObjectClass *klass) e_tree_model_class_init (GtkObjectClass *klass)
{ {
@ -165,11 +290,38 @@ e_tree_model_class_init (GtkObjectClass *klass)
klass->destroy = etree_destroy; 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->row_count = etable_row_count;
table_class->value_at = etable_value_at; table_class->value_at = etable_value_at;
table_class->set_value_at = etable_set_value_at; table_class->set_value_at = etable_set_value_at;
table_class->is_cell_editable = etable_is_cell_editable; table_class->is_cell_editable = etable_is_cell_editable;
#if 0 #if 0
/* XX need to pass these through */
table_class->duplicate_value = etable_duplicate_value; table_class->duplicate_value = etable_duplicate_value;
table_class->free_value = etable_free_value; table_class->free_value = etable_free_value;
table_class->initialize_value = etable_initialize_value; table_class->initialize_value = etable_initialize_value;
@ -178,19 +330,68 @@ e_tree_model_class_init (GtkObjectClass *klass)
#endif #endif
tree_class->get_root = etree_get_root; 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->value_at = etree_value_at;
tree_class->set_value_at = etree_set_value_at; tree_class->set_value_at = etree_set_value_at;
tree_class->is_editable = etree_is_editable; tree_class->is_editable = etree_is_editable;
tree_class->get_children = etree_get_children; tree_class->get_children = etree_get_children;
tree_class->release_paths = etree_release_paths;
tree_class->is_expanded = etree_is_expanded; 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 = 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) E_MAKE_TYPE(e_tree_model, "ETreeModel", ETreeModel, e_tree_model_class_init, NULL, PARENT_TYPE)
/* signals */ /* 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 * ETreeModel *
e_tree_model_new () e_tree_model_new ()
@ -202,40 +403,6 @@ e_tree_model_new ()
return et; 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 * ETreePath *
e_tree_model_get_root (ETreeModel *etree) e_tree_model_get_root (ETreeModel *etree)
{ {
@ -245,10 +412,48 @@ e_tree_model_get_root (ETreeModel *etree)
ETreePath * ETreePath *
e_tree_model_node_at_row (ETreeModel *etree, int row) 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 ETM_CLASS(etree)->node_at_row (etree, row);
return e_tree_model_node_at_row_1 (etree, &row, ETM_CLASS(etree)->get_root(etree));
} }
int
e_tree_model_row_of_node (ETreeModel *etree, ETreePath *node)
{
int i;
for (i = 0; i < etree->row_array->len; i ++)
if (g_array_index (etree->row_array, GNode*, i) == node)
return i;
return -1;
}
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));
}
}
}
gboolean
e_tree_model_root_node_is_visible (ETreeModel *etm)
{
return etm->root_visible;
}
ETreePath * ETreePath *
e_tree_model_node_get_next (ETreeModel *etree, ETreePath *node) 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 guint
e_tree_model_node_depth (ETreeModel *etree, ETreePath *path) e_tree_model_node_depth (ETreeModel *etree, ETreePath *path)
{ {
return g_list_length (path) - 1; return g_node_depth (path) - 1;
} }
ETreePath * ETreePath *
e_tree_model_node_get_parent (ETreeModel *etree, ETreePath *path) e_tree_model_node_get_parent (ETreeModel *etree, ETreePath *path)
{ {
g_return_val_if_fail (path, NULL); return ETM_CLASS(etree)->get_parent(etree, path);
if (path->next == NULL)
return NULL;
else
return g_list_copy (path->next);
} }
gboolean gboolean
@ -296,21 +496,190 @@ e_tree_model_node_is_expanded (ETreeModel *etree, ETreePath *path)
return ETM_CLASS(etree)->is_expanded (etree, 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 void
e_tree_model_node_set_expanded (ETreeModel *etree, ETreePath *path, gboolean expanded) e_tree_model_node_set_expanded (ETreeModel *etree, ETreePath *path, gboolean expanded)
{ {
ETM_CLASS(etree)->set_expanded (etree, path, 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 guint
e_tree_model_node_get_children (ETreeModel *etree, ETreePath *path, ETreePath ***paths) e_tree_model_node_get_children (ETreeModel *etree, ETreePath *path, ETreePath ***paths)
{ {
return ETM_CLASS(etree)->get_children (etree, path, paths); return ETM_CLASS(etree)->get_children (etree, path, paths);
} }
void guint
e_tree_model_release_paths (ETreeModel *etree, ETreePath **paths, guint num_paths) e_tree_model_node_num_visible_descendents (ETreeModel *etm, ETreePath *node)
{ {
ETM_CLASS(etree)->release_paths (etree, paths, num_paths); 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_node_set_data (ETreeModel *etm, ETreePath *node, gpointer node_data)
{
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);
}
}
}

View File

@ -10,18 +10,13 @@
#define E_IS_TREE_MODEL(o) (GTK_CHECK_TYPE ((o), E_TREE_MODEL_TYPE)) #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)) #define E_IS_TREE_MODEL_CLASS(k) (GTK_CHECK_CLASS_TYPE ((k), E_TREE_MODEL_TYPE))
typedef gpointer ETreePathItem; typedef GNode ETreePath;
typedef GList ETreePath;
typedef struct { typedef struct {
ETableModel base; ETableModel base;
GNode *root;
ETableModel *source; gboolean root_visible;
GArray *row_array; /* used in the mapping between ETable and our tree */
ETreePath *root_node;
GArray *array;
} ETreeModel; } ETreeModel;
typedef struct { typedef struct {
@ -32,41 +27,75 @@ typedef struct {
*/ */
ETreePath *(*get_root) (ETreeModel *etm); ETreePath *(*get_root) (ETreeModel *etm);
ETreePath *(*get_parent) (ETreeModel *etm, ETreePath* node);
ETreePath *(*get_next) (ETreeModel *etm, ETreePath* node); ETreePath *(*get_next) (ETreeModel *etm, ETreePath* node);
ETreePath *(*get_prev) (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 *(*value_at) (ETreeModel *etm, ETreePath* node, int col);
void (*set_value_at) (ETreeModel *etm, ETreePath* node, int col, const void *val); void (*set_value_at) (ETreeModel *etm, ETreePath* node, int col, const void *val);
gboolean (*is_editable) (ETreeModel *etm, ETreePath* node, int col); 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 * 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; } ETreeModelClass;
GtkType e_tree_model_get_type (void); GtkType e_tree_model_get_type (void);
void e_tree_model_construct (ETreeModel *etree);
ETreeModel *e_tree_model_new (void); ETreeModel *e_tree_model_new (void);
/* operations on "nodes" in the tree */ /* tree traversal operations */
ETreePath * e_tree_model_get_root (ETreeModel *etree); 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_parent (ETreeModel *etree, ETreePath *path);
ETreePath *e_tree_model_node_get_next (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); 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); /* node operations */
gboolean e_tree_model_node_is_expanded (ETreeModel *etree, ETreePath *path); ETreePath *e_tree_model_node_insert (ETreeModel *etree, ETreePath *parent, int position, gpointer node_data);
guint e_tree_model_node_get_children (ETreeModel *etree, ETreePath *path, ETreePath ***paths); ETreePath *e_tree_model_node_insert_before (ETreeModel *etree, ETreePath *parent, ETreePath *sibling, gpointer node_data);
void e_tree_model_release_paths (ETreeModel *etree, ETreePath **paths, guint num_paths); gpointer e_tree_model_node_remove (ETreeModel *etree, ETreePath *path);
guint e_tree_model_node_num_visible_descendents (ETreeModel *etm, ETreePath *node);
/* 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 */ #endif /* _E_TREE_MODEL_H */

View File

@ -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;
}

View File

@ -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_ */

View File

@ -49,6 +49,21 @@ static ECellClass *parent_class;
static GdkPixbuf *tree_expanded_pixbuf, *tree_unexpanded_pixbuf; 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* static ETreePath*
e_cell_tree_get_node (ETreeModel *tree_model, int row) e_cell_tree_get_node (ETreeModel *tree_model, int row)
{ {
@ -136,7 +151,6 @@ ect_unrealize (ECellView *ecv)
(* parent_class->unrealize) (ecv); (* parent_class->unrealize) (ecv);
} }
#define INDENT_AMOUNT 16
/* /*
* ECell::draw method * ECell::draw method
*/ */
@ -167,7 +181,7 @@ ect_draw (ECellView *ecell_view, GdkDrawable *drawable,
*/ */
node = e_cell_tree_get_node (tree_model, row); 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); expandable = e_tree_model_node_is_expandable (tree_model, node);
expanded = e_tree_model_node_is_expanded (tree_model, node); expanded = e_tree_model_node_is_expanded (tree_model, node);
subcell_offset = offset; subcell_offset = offset;
@ -196,15 +210,8 @@ ect_draw (ECellView *ecell_view, GdkDrawable *drawable,
rect.x, rect.y, rect.width, rect.height); rect.x, rect.y, rect.width, rect.height);
gdk_gc_set_foreground (tree_view->gc, foreground); gdk_gc_set_foreground (tree_view->gc, foreground);
/* draw our lines */
if (E_CELL_TREE(tree_view->cell_view.ecell)->draw_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, gdk_draw_line (drawable, tree_view->gc,
rect.x + offset - INDENT_AMOUNT / 2 + 1, rect.x + offset - INDENT_AMOUNT / 2 + 1,
@ -212,12 +219,22 @@ ect_draw (ECellView *ecell_view, GdkDrawable *drawable,
rect.x + offset, rect.x + offset,
rect.y + rect.height / 2); 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 /* now traverse back up to the root of the tree, checking at
each level if the node has siblings, and drawing the each level if the node has siblings, and drawing the
correct vertical pipe for it's configuration. */ correct vertical pipe for it's configuration. */
node = e_tree_model_node_get_parent (tree_model, node); node = e_tree_model_node_get_parent (tree_model, node);
offset -= INDENT_AMOUNT; 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)) { if (e_tree_model_node_get_next(tree_model, node)) {
gdk_draw_line (drawable, tree_view->gc, gdk_draw_line (drawable, tree_view->gc,
rect.x + offset - INDENT_AMOUNT / 2, rect.x + offset - INDENT_AMOUNT / 2,
@ -255,7 +272,7 @@ ect_draw (ECellView *ecell_view, GdkDrawable *drawable,
subcell_offset pixels */ subcell_offset pixels */
e_cell_draw (tree_view->subcell_view, drawable, e_cell_draw (tree_view->subcell_view, drawable,
model_col, view_col, row, selected, 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; ECellTreeView *tree_view = (ECellTreeView *) ecell_view;
switch (event->type) { switch (event->type) {
case GDK_BUTTON_PRESS: case GDK_BUTTON_PRESS: {
case GDK_BUTTON_RELEASE: {
/* if the event happened in our area of control (and /* if the event happened in our area of control (and
we care about it), handle it. */ we care about it), handle it. */
ETreeModel *tree_model = e_cell_tree_get_tree_model (ecell_view->e_table_model, row); 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); 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. */ /* 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 (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; return TRUE;
} }
else if (event->button.x < (offset - INDENT_AMOUNT))
return TRUE;
} }
default: default:
/* otherwise, pass it off to our subcell_view */ /* 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 * static void *
ect_enter_edit (ECellView *ecell_view, int model_col, int view_col, int row) 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 static void
ect_leave_edit (ECellView *ecell_view, int model_col, int view_col, int row, void *edit_context) 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);
} }
/* /*

View File

@ -1197,6 +1197,14 @@ eti_event (GnomeCanvasItem *item, GdkEvent *e)
if (!find_cell (eti, e->button.x, e->button.y, &col, &row, &x1, &y1)) if (!find_cell (eti, e->button.x, e->button.y, &col, &row, &x1, &y1))
return TRUE; 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){ if (eti->cursor_row == row && eti->cursor_col == col){
ecol = e_table_header_get_column (eti->header, 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->button.y = y1;
e_cell_event (ecell_view, e, ecol->col_idx, col, row); 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; break;
case 3: case 3:

View File

@ -4,7 +4,6 @@
#include <string.h> #include <string.h>
#include <gnome.h> #include <gnome.h>
#include "e-util/e-cursors.h" #include "e-util/e-cursors.h"
#include "e-tree-gnode.h"
#include "e-table-header.h" #include "e-table-header.h"
#include "e-table-header-item.h" #include "e-table-header-item.h"
#include "e-table-item.h" #include "e-table-item.h"
@ -12,6 +11,7 @@
#include "e-cell-tree.h" #include "e-cell-tree.h"
#include "e-cell-checkbox.h" #include "e-cell-checkbox.h"
#include "e-table.h" #include "e-table.h"
#include "e-tree-simple.h"
#include <gdk-pixbuf/gdk-pixbuf.h> #include <gdk-pixbuf/gdk-pixbuf.h>
@ -56,6 +56,8 @@ char *headers [COLS] = {
"Date" "Date"
}; };
GtkWidget *e_table;
/* /*
* ETableSimple callbacks * ETableSimple callbacks
* These are the callbacks that define the behavior of our custom model. * 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; 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 * 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) { 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 1: return "Chris Toshok";
case 2: return "toshok@helixcode.com"; case 2: return "toshok@helixcode.com";
case 3: return "Jun 07 2000"; 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 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. */ /* This function returns whether a particular cell is editable. */
static gboolean 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)
{
return g_strdup (value);
}
/* This function frees the value passed to it. */
static void static void
my_free_value (ETableModel *etc, int col, void *value, void *data) toggle_root (GtkButton *button, gpointer data)
{ {
g_free (value); 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));
} }
/* This function is for when the model is unfrozen. This can mostly
be ignored for simple models. */
static void static void
my_thaw (ETableModel *etc, void *data) 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)
{
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);
}
static void
expand_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, TRUE);
}
static void
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. */ /* We create a window containing our new tree. */
static void static void
create_tree (void) create_tree (void)
{ {
GtkWidget *e_table, *window, *frame; GtkWidget *window, *frame, *button, *vbox;
ECell *cell_left_just; ECell *cell_left_just;
ECell *cell_tree; ECell *cell_tree;
ETableHeader *e_table_header; ETableHeader *e_table_header;
int i, j; int i, j;
ETreeModel *e_tree_model = NULL; 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 */ /* 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++){ 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 ++) { 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 * Next we create a header. The ETableHeader is used in two
* different way. The first is the full_header. This is the * 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); window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
/* This frame is simply to get a bevel around our table. */ /* This frame is simply to get a bevel around our table. */
vbox = gtk_vbox_new (FALSE, 0);
frame = gtk_frame_new (NULL); frame = gtk_frame_new (NULL);
/* /*
@ -213,8 +312,34 @@ create_tree (void)
/* Build the gtk widget hierarchy. */ /* Build the gtk widget hierarchy. */
gtk_container_add (GTK_CONTAINER (frame), e_table); 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. */ /* Size the initial window. */
gtk_widget_set_usize (window, 200, 200); gtk_widget_set_usize (window, 200, 200);

View File

@ -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;
}

View File

@ -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_ */

View File

@ -20,21 +20,197 @@
static ETableModel *e_tree_model_parent_class; 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 */ /* virtual methods */
static void
etree_destroy (GtkObject *object)
{
/* XXX lots of stuff to free here */
}
static ETreePath* static ETreePath*
etree_get_root (ETreeModel *etm) etree_get_root (ETreeModel *etm)
{ {
/* shouldn't be called */ return etm->root;
g_assert(0);
return NULL;
} }
static ETreePath*
etree_get_parent (ETreeModel *etm, ETreePath *path)
{
g_return_val_if_fail (path, NULL);
return path->parent;
}
static ETreePath*
etree_get_next (ETreeModel *etm, ETreePath *node)
{
g_return_val_if_fail (node, NULL);
return g_node_next_sibling(node);
}
static ETreePath*
etree_get_prev (ETreeModel *etm, ETreePath *node)
{
g_return_val_if_fail (node, NULL);
return g_node_prev_sibling (node);
}
static guint
etree_get_children (ETreeModel *etm, ETreePath* node, ETreePath ***paths)
{
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 gboolean
etree_is_expanded (ETreeModel *etm, ETreePath* node)
{
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)
{
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_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);
}
}
static ETreePath *
etree_node_at_row (ETreeModel *etree, int row)
{
GNode *node = g_array_index (etree->row_array, GNode*, row);
return node;
}
/* ETable analogs */
static void* static void*
etree_value_at (ETreeModel *etm, ETreePath* node, int col) etree_value_at (ETreeModel *etm, ETreePath* node, int col)
{ {
/* shouldn't be called */ /* shouldn't be called */
g_assert(0); g_assert (0);
return NULL; return NULL;
} }
@ -42,7 +218,7 @@ static void
etree_set_value_at (ETreeModel *etm, ETreePath* node, int col, const void *val) etree_set_value_at (ETreeModel *etm, ETreePath* node, int col, const void *val)
{ {
/* shouldn't be called */ /* shouldn't be called */
g_assert(0); g_assert (0);
} }
static gboolean static gboolean
@ -53,65 +229,13 @@ etree_is_editable (ETreeModel *etm, ETreePath* node, int col)
return FALSE; return FALSE;
} }
static gboolean
etree_is_expanded (ETreeModel *etm, ETreePath* node) /* ETable virtual functions we map */
{
/* 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 static int
etable_row_count (ETableModel *etm) 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 * static void *
@ -155,6 +279,7 @@ etable_is_cell_editable (ETableModel *etm, int col, int row)
return et_class->is_editable (etree, node, col); return et_class->is_editable (etree, node, col);
} }
static void static void
e_tree_model_class_init (GtkObjectClass *klass) e_tree_model_class_init (GtkObjectClass *klass)
{ {
@ -165,11 +290,38 @@ e_tree_model_class_init (GtkObjectClass *klass)
klass->destroy = etree_destroy; 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->row_count = etable_row_count;
table_class->value_at = etable_value_at; table_class->value_at = etable_value_at;
table_class->set_value_at = etable_set_value_at; table_class->set_value_at = etable_set_value_at;
table_class->is_cell_editable = etable_is_cell_editable; table_class->is_cell_editable = etable_is_cell_editable;
#if 0 #if 0
/* XX need to pass these through */
table_class->duplicate_value = etable_duplicate_value; table_class->duplicate_value = etable_duplicate_value;
table_class->free_value = etable_free_value; table_class->free_value = etable_free_value;
table_class->initialize_value = etable_initialize_value; table_class->initialize_value = etable_initialize_value;
@ -178,19 +330,68 @@ e_tree_model_class_init (GtkObjectClass *klass)
#endif #endif
tree_class->get_root = etree_get_root; 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->value_at = etree_value_at;
tree_class->set_value_at = etree_set_value_at; tree_class->set_value_at = etree_set_value_at;
tree_class->is_editable = etree_is_editable; tree_class->is_editable = etree_is_editable;
tree_class->get_children = etree_get_children; tree_class->get_children = etree_get_children;
tree_class->release_paths = etree_release_paths;
tree_class->is_expanded = etree_is_expanded; 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 = 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) E_MAKE_TYPE(e_tree_model, "ETreeModel", ETreeModel, e_tree_model_class_init, NULL, PARENT_TYPE)
/* signals */ /* 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 * ETreeModel *
e_tree_model_new () e_tree_model_new ()
@ -202,40 +403,6 @@ e_tree_model_new ()
return et; 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 * ETreePath *
e_tree_model_get_root (ETreeModel *etree) e_tree_model_get_root (ETreeModel *etree)
{ {
@ -245,10 +412,48 @@ e_tree_model_get_root (ETreeModel *etree)
ETreePath * ETreePath *
e_tree_model_node_at_row (ETreeModel *etree, int row) 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 ETM_CLASS(etree)->node_at_row (etree, row);
return e_tree_model_node_at_row_1 (etree, &row, ETM_CLASS(etree)->get_root(etree));
} }
int
e_tree_model_row_of_node (ETreeModel *etree, ETreePath *node)
{
int i;
for (i = 0; i < etree->row_array->len; i ++)
if (g_array_index (etree->row_array, GNode*, i) == node)
return i;
return -1;
}
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));
}
}
}
gboolean
e_tree_model_root_node_is_visible (ETreeModel *etm)
{
return etm->root_visible;
}
ETreePath * ETreePath *
e_tree_model_node_get_next (ETreeModel *etree, ETreePath *node) 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 guint
e_tree_model_node_depth (ETreeModel *etree, ETreePath *path) e_tree_model_node_depth (ETreeModel *etree, ETreePath *path)
{ {
return g_list_length (path) - 1; return g_node_depth (path) - 1;
} }
ETreePath * ETreePath *
e_tree_model_node_get_parent (ETreeModel *etree, ETreePath *path) e_tree_model_node_get_parent (ETreeModel *etree, ETreePath *path)
{ {
g_return_val_if_fail (path, NULL); return ETM_CLASS(etree)->get_parent(etree, path);
if (path->next == NULL)
return NULL;
else
return g_list_copy (path->next);
} }
gboolean gboolean
@ -296,21 +496,190 @@ e_tree_model_node_is_expanded (ETreeModel *etree, ETreePath *path)
return ETM_CLASS(etree)->is_expanded (etree, 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 void
e_tree_model_node_set_expanded (ETreeModel *etree, ETreePath *path, gboolean expanded) e_tree_model_node_set_expanded (ETreeModel *etree, ETreePath *path, gboolean expanded)
{ {
ETM_CLASS(etree)->set_expanded (etree, path, 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 guint
e_tree_model_node_get_children (ETreeModel *etree, ETreePath *path, ETreePath ***paths) e_tree_model_node_get_children (ETreeModel *etree, ETreePath *path, ETreePath ***paths)
{ {
return ETM_CLASS(etree)->get_children (etree, path, paths); return ETM_CLASS(etree)->get_children (etree, path, paths);
} }
void guint
e_tree_model_release_paths (ETreeModel *etree, ETreePath **paths, guint num_paths) e_tree_model_node_num_visible_descendents (ETreeModel *etm, ETreePath *node)
{ {
ETM_CLASS(etree)->release_paths (etree, paths, num_paths); 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_node_set_data (ETreeModel *etm, ETreePath *node, gpointer node_data)
{
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);
}
}
}

View File

@ -10,18 +10,13 @@
#define E_IS_TREE_MODEL(o) (GTK_CHECK_TYPE ((o), E_TREE_MODEL_TYPE)) #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)) #define E_IS_TREE_MODEL_CLASS(k) (GTK_CHECK_CLASS_TYPE ((k), E_TREE_MODEL_TYPE))
typedef gpointer ETreePathItem; typedef GNode ETreePath;
typedef GList ETreePath;
typedef struct { typedef struct {
ETableModel base; ETableModel base;
GNode *root;
ETableModel *source; gboolean root_visible;
GArray *row_array; /* used in the mapping between ETable and our tree */
ETreePath *root_node;
GArray *array;
} ETreeModel; } ETreeModel;
typedef struct { typedef struct {
@ -32,41 +27,75 @@ typedef struct {
*/ */
ETreePath *(*get_root) (ETreeModel *etm); ETreePath *(*get_root) (ETreeModel *etm);
ETreePath *(*get_parent) (ETreeModel *etm, ETreePath* node);
ETreePath *(*get_next) (ETreeModel *etm, ETreePath* node); ETreePath *(*get_next) (ETreeModel *etm, ETreePath* node);
ETreePath *(*get_prev) (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 *(*value_at) (ETreeModel *etm, ETreePath* node, int col);
void (*set_value_at) (ETreeModel *etm, ETreePath* node, int col, const void *val); void (*set_value_at) (ETreeModel *etm, ETreePath* node, int col, const void *val);
gboolean (*is_editable) (ETreeModel *etm, ETreePath* node, int col); 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 * 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; } ETreeModelClass;
GtkType e_tree_model_get_type (void); GtkType e_tree_model_get_type (void);
void e_tree_model_construct (ETreeModel *etree);
ETreeModel *e_tree_model_new (void); ETreeModel *e_tree_model_new (void);
/* operations on "nodes" in the tree */ /* tree traversal operations */
ETreePath * e_tree_model_get_root (ETreeModel *etree); 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_parent (ETreeModel *etree, ETreePath *path);
ETreePath *e_tree_model_node_get_next (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); 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); /* node operations */
gboolean e_tree_model_node_is_expanded (ETreeModel *etree, ETreePath *path); ETreePath *e_tree_model_node_insert (ETreeModel *etree, ETreePath *parent, int position, gpointer node_data);
guint e_tree_model_node_get_children (ETreeModel *etree, ETreePath *path, ETreePath ***paths); ETreePath *e_tree_model_node_insert_before (ETreeModel *etree, ETreePath *parent, ETreePath *sibling, gpointer node_data);
void e_tree_model_release_paths (ETreeModel *etree, ETreePath **paths, guint num_paths); gpointer e_tree_model_node_remove (ETreeModel *etree, ETreePath *path);
guint e_tree_model_node_num_visible_descendents (ETreeModel *etm, ETreePath *node);
/* 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 */ #endif /* _E_TREE_MODEL_H */

View File

@ -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;
}

View File

@ -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_ */