From 056db88de067d2a0ed42484807099a4d19cc4b99 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 18 Oct 2015 18:55:06 -0400 Subject: [PATCH] More uiTable work. I'm not too happy anymore... --- common/uipriv.h | 3 - ui.h | 3 +- unix/GNUmakeinc.mk | 1 + unix/table.c | 328 +++++++++------------------------------------ unix/tablemodel.c | 302 +++++++++++++++++++++++++++++++++++++++++ 5 files changed, 366 insertions(+), 271 deletions(-) create mode 100644 unix/tablemodel.c diff --git a/common/uipriv.h b/common/uipriv.h index bb7f7baa..a8cf9273 100644 --- a/common/uipriv.h +++ b/common/uipriv.h @@ -70,6 +70,3 @@ extern void fallbackScale(uiDrawMatrix *, double, double, double, double); extern void fallbackMultiply(uiDrawMatrix *, uiDrawMatrix *); extern void fallbackTransformPoint(uiDrawMatrix *, double *, double *); extern void fallbackTransformSize(uiDrawMatrix *, double *, double *); - -// each table implementation provides this -extern void tableNotify(uiTable *, uiTableNotification, intmax_t, intmax_t); diff --git a/ui.h b/ui.h index 68d79e3b..205a1653 100644 --- a/ui.h +++ b/ui.h @@ -541,7 +541,6 @@ struct uiTableModelSpec { }; enum uiTableNotification { - uiTableNumRowsChanged, uiTableRowInserted, uiTableRowDeleted, uiTableCellChanged, @@ -556,6 +555,8 @@ _UI_EXTERN void *uiTableModelFromString(const char *str); struct uiTableColumnParams { const char *Name; + // TODO make this unnecessary + uiTableColumnType Type; int Mutable; // TODO move to the model? intmax_t ValueColumn; // TODO background color diff --git a/unix/GNUmakeinc.mk b/unix/GNUmakeinc.mk index 7149970b..f6ecd1bc 100644 --- a/unix/GNUmakeinc.mk +++ b/unix/GNUmakeinc.mk @@ -24,6 +24,7 @@ CFILES += \ unix/stddialogs.c \ unix/tab.c \ unix/table.c \ + unix/tablemodel.c \ unix/text.c \ unix/util.c \ unix/window.c diff --git a/unix/table.c b/unix/table.c index 2a1f69a1..80656c93 100644 --- a/unix/table.c +++ b/unix/table.c @@ -1,294 +1,88 @@ // 18 october 2015 #include "uipriv_unix.h" -#define tableGTKModelType (tableGTKModel_get_type()) -#define tableGTKModel(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), tableGTKModelType, tableGTKModel)) -#define isAreaWidget(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), tableGTKModelType)) -#define tableGTKModelClass(class) (G_TYPE_CHECK_CLASS_CAST((class), tableGTKModelType, tableGTKModelClass)) -#define isAreaWidgetClass(class) (G_TYPE_CHECK_CLASS_TYPE((class), tableGTKModel)) -#define getAreaWidgetClass(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), tableGTKModelType, tableGTKModelClass)) - -typedef struct tableGTKModel tableGTKModel; -typedef struct tableGTKModelClass tableGTKModelClass; - struct uiTable { uiUnixControl c; GtkWidget *widget; - GtkContainer *container; + GtkContainer *scontainer; GtkScrolledWindow *sw; GtkWidget *treeWidget; GtkTreeView *treeview; GtkTreeSelection *selection; - tableGTKModel *model; -}; - -struct tableGTKModel { - GObject parent_instance; uiTableModel *model; }; -struct tableGTKModelClass { - GObjectClass parent_class; -}; - -static void tableGTKModel_treeModel_init(GtkTreeModel *); - -G_DEFINE_TYPE_WITH_CODE(tableGTKModel, tableGTKModel, G_TYPE_OBJECT, - G_IMPLEMENT_INTERFACE(GTK_TYPE_TREE_MODEL, tableGTKModel_treeModel_init)) - -static void tableGTKModel_init(tableGTKModel *m) -{ - // do nothing -} - -static void tableGTKModel_dispose(GObject *obj) -{ - G_OBJECT_CLASS(tableGTKModel_parent_class)->dispose(obj); -} - -static void tableGTKModel_finalize(GObject *obj) -{ - G_OBJECT_CLASS(tableGTKModel_parent_class)->finalize(obj); -} - -static GtkTreeModelFlags tableGTKModel_get_flags(GtkTreeModel *mb) -{ - return GTK_TREE_MODEL_LIST_ONLY; -} - -static gint tableGTKModel_get_n_columns(GtkTreeModel *mb) -{ - tableGTKModel *m = tableGTKModel(mb); - - return (*(m->model->NumColumns))(m->model); -} - -static GType tableGTKModel_get_column_type(GtkTreeModel *mb, gint index) -{ - tableGTKModel *m = tableGTKModel(mb); - uiTableColumnType type; - - type = (*(m->model->ColumnType))(m->model, index); - switch (type) { - case uiTableColumnText: - return G_TYPE_STRING; -//TODO case uiTableColumnImage: -//TODO return GDK_TYPE_PIXBUF; - case uiTableColumnCheckbox: - return G_TYPE_BOOLEAN; - } - complain("unknown column type %d in tableGTKModel_get_column_type()", type); - return G_TYPE_INVALID; // make compiler happy -} - -/* -how our GtkTreeIters are stored: - stamp: either GOOD_STAMP or BAD_STAMP - user_data: row index -Thanks to Company in irc.gimp.net/#gtk+ for suggesting the GSIZE_TO_POINTER() t -rick. -*/ -#define GOOD_STAMP 0x1234 -#define BAD_STAMP 0x5678 -#define FROM(x) ((gint) GPOINTER_TO_SIZE((x))) -#define TO(x) GSIZE_TO_POINTER((gsize) (x)) - -static gboolean tableGTKModel_get_iter(GtkTreeModel *mb, GtkTreeIter *iter, GtkTreePath *path) -{ - tableGTKModel *m = tableGTKModel(mb); - gint index; - - if (gtk_tree_path_get_depth(path) != 1) - goto bad; - index = gtk_tree_path_get_indices(path)[0]; - if (index < 0) - goto bad; - if (index >= (*(m->model->NumRows))(m->model)) - goto bad; - iter->stamp = GOOD_STAMP; - iter->user_data = TO(index); - return TRUE; -bad: - iter->stamp = BAD_STAMP; - return FALSE; -} - -static GtkTreePath *tableGTKModel_get_path(GtkTreeModel *mb, GtkTreeIter *iter) -{ - // note: from this point forward, the GOOD_STAMP checks ensure that the index stored in iter is nonnegative - if (iter->stamp != GOOD_STAMP) - return NULL; // this is what both GtkListStore and GtkTreeStore do - return gtk_tree_path_new_from_indices(FROM(iter->user_data), -1); -} - -void *uiTableModelFromString(const char *str) -{ - return g_strdup(str); -} - -#define toBool(v) ((int) (v)) -#define toStr(v) ((const char *) (v)) - -static void tableGTKModel_get_value(GtkTreeModel *mb, GtkTreeIter *iter, gint column, GValue *value) -{ - tableGTKModel *m = tableGTKModel(mb); - void *v; - uiTableColumnType type; - - if (iter->stamp != GOOD_STAMP) - return; // this is what both GtkListStore and GtkTreeStore do - v = (*(m->model->CellValue))(m->model, FROM(iter->user_data), column); - type = (*(m->model->ColumnType))(m->model, column); - switch (type) { - case uiTableColumnText: - g_value_init(value, G_TYPE_STRING); - g_value_take_string(value, toStr(v)); - // the GValue now manages the memory of the string that was g_strdup()'d before - return; -//TODO case uiTableColumnImage: - // TODO - case uiTableColumnCheckbox: - g_value_init(value, G_TYPE_BOOLEAN); - g_value_set_boolean(value, toBool(v)); - return; - } - complain("unknown column type %d in tableGTKModel_get_value()", type); -} - -static gboolean tableGTKModel_iter_next(GtkTreeModel *mb, GtkTreeIter *iter) -{ - tableGTKModel *m = tableGTKModel(mb); - gint index; - - if (iter->stamp != GOOD_STAMP) - return FALSE; // this is what both GtkListStore and GtkTreeStore do - index = FROM(iter->user_data); - index++; - if (index >= (*(m->model->NumRows))(m->model)) { - iter->stamp = BAD_STAMP; - return FALSE; - } - iter->user_data = TO(index); - return TRUE; -} - -static gboolean tableGTKModel_iter_previous(GtkTreeModel *mb, GtkTreeIter *iter) -{ - tableGTKModel *m = tableGTKModel(mb); - gint index; - - if (iter->stamp != GOOD_STAMP) - return FALSE; // this is what both GtkListStore and GtkTreeStore do - index = FROM(iter->user_data); - if (index <= 0) { - iter->stamp = BAD_STAMP; - return FALSE; - } - index--; - iter->user_data = TO(index); - return TRUE; -} - -static gboolean tableGTKModel_iter_children(GtkTreeModel *mb, GtkTreeIter *iter, GtkTreeIter *parent) -{ - tableGTKModel *m = tableGTKModel(mb); - - if (parent == NULL && (*(m->model->NumRows))(m->model) > 0) { - child->stamp = GOOD_STAMP; - child->user_data = 0; - return TRUE; - } - child->stamp = BAD_STAMP; - return FALSE; -} - -static gboolean tableGTKModel_iter_has_child(GtkTreeModel *mb, GtkTreeIter *iter) -{ - return FALSE; -} - -static gint tableGTKModel_iter_n_children(GtkTreeModel *mb, GtkTreeIter *iter) -{ - tableGTKModel *m = tableGTKModel(mb); - - if (iter == NULL) - return (*(m->model->NumRows))(m->model); - return 0; -} - -static gboolean tableGTKModel_iter_nth_child(GtkTreeModel *mb, GtkTreeIter *iter, GtkTreeIter *parent, gint n) -{ - tableGTKModel *m = tableGTKModel(mb); - - if (parent == NULL && n >= 0 && n < (*(m->model->NumRows))(m->model)) { - child->stamp = GOOD_STAMP; - child->user_data = TO(n); - return TRUE; - } - child->stamp = BAD_STAMP; - return FALSE; -} - -static gboolean tableGTKModel_iter_parent(GtkTreeModel *mb, GtkTreeIter *iter, GtkTreeIter *child) -{ - parent->stamp = BAD_STAMP; - return FALSE; -} - -static void tableGTKModel_class_init(tableGTKModelClass *class) -{ - G_OBJECT_CLASS(class)->dispose = tableGTKModel_dispose; - G_OBJECT_CLASS(class)->finalize = tableGTKModel_finalize; -} - -static void tableGTKModel_treeModel_init(GtkTreeModel *iface) -{ - iface->get_flags = tableGTKModel_get_flags; - iface->get_n_columns = tableGTKModel_get_n_columns; - iface->get_column_type = tableGTKModel_get_column_type; - iface->get_iter = tableGTKModel_get_iter; - iface->get_path = tableGTKModel_get_path; - iface->get_value = tableGTKModel_get_value; - iface->iter_next = tableGTKModel_iter_next; - iface->iter_previous = tableGTKModel_iter_previous; - iface->iter_children = tableGTKModel_iter_children; - iface->iter_has_child = tableGTKModel_iter_has_child; - iface->iter_n_children = tableGTKModel_iter_n_children; - iface->iter_nth_child = tableGTKModel_iter_nth_child; - iface->iter_parent = tableGTKModel_iter_parent; - // no need for ref_node or unref_node -} - -static void newGTKModel(uiTableModel *model) -{ - tableGTKModel *m; - - m = tableGTKModel(g_object_new(tableGTKModelType, NULL)); - m->model = model; - return m; -} - -void tableNotify(uiTable *t, uiTableNotification n, intmax_t row, intmax_t column) -{ - // TODO -} +uiUnixDefineControl( + uiTable, // type name + uiTableType // type function +) void uiTableSetModel(uiTable *t, uiTableModel *m) { - if (t->model != NULL) - (*(t->model->model->Unsubscribe))(t->model->model, t); - t->model = newGTKModel(m); - (*(t->model->model->Subscribe))(t->model->model, t); - // TODO will this free the old model? + t->model = m; gtk_tree_view_set_model(t->treeview, GTK_TREE_MODEL(t->moel)); } void uiTableAppendColumn(uiTable *t, uiTableColumnParams *p) { - // TODO + GtkTreeViewColumn *col; + GtkCellRenderer *r; + const char *attribute; + const char *mutableAttr; + gboolean mutable; + + switch (p->Type) { + case uiTableColumnText: + r = gtk_cell_renderer_text_new(); + attribute = "text"; + mutableAttr = "editable"; + break; +//TODO case uiTableColumnImage: + // TODO + case uiTableColumnCheckbox: + r = gtk_cell_renderer_toggle_new(); + attribute = "active"; + mutableAttr = "activatable"; + break; + default: + complain("unknown table column type %d in uiTableAppendColumn()", p->Type); + } + mutable = FALSE; + if (p->Mutable) + mutable = TRUE; + g_object_set(r, + mutableAttr, mutable, + NULL); + col = gtk_tree_view_column_new_with_attributes(p->Name, r, + attribute, p->ValueColumn, + NULL); + // allow columns to be resized + gtk_tree_view_column_set_resizable(col, TRUE); + gtk_tree_view_append_column(t->treeviw, col); } uiTable *uiNewTable(void) { uiTable *t; + + t = (uiTable *) uiNewControl(uiTableType()); + + t->widget = gtk_scrolled_window_new(NULL, NULL); + t->scontainer = GTK_CONTAINER(t->widget); + t->sw = GTK_SCROLLED_WINDOW(t->widget); + + t->treeWidget = gtk_tree_view_new(); + t->treeview = GTK_TREE_VIEW(t->treeWidget); + + t->selection = gtk_tree_view_get_selection(t->treeview); + + // give a border and add the table + gtk_scrolled_window_set_shadow_type(t->sw, GTK_SHADOW_IN); + gtk_container_add(t->scontainer, t->treeWidget); + // and make the table visible; only the scrolled window's visibility is controlled by libui + gtk_widget_show(t->treeWidget); + + uiUnixFinishNewControl(t, uiTable); + + return t; } diff --git a/unix/tablemodel.c b/unix/tablemodel.c new file mode 100644 index 00000000..a0a70ebf --- /dev/null +++ b/unix/tablemodel.c @@ -0,0 +1,302 @@ +// 18 october 2015 +#include "uipriv_unix.h" + +// On GTK+, uiTableModel is a GtkTreeModel. + +#define uiTableModelType (uiTableModel_get_type()) +#define uiTableModel(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), uiTableModelType, uiTableModel)) +#define isAreaWidget(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), uiTableModelType)) +#define uiTableModelClass(class) (G_TYPE_CHECK_CLASS_CAST((class), uiTableModelType, uiTableModelClass)) +#define isAreaWidgetClass(class) (G_TYPE_CHECK_CLASS_TYPE((class), uiTableModel)) +#define getAreaWidgetClass(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), uiTableModelType, uiTableModelClass)) + +typedef struct uiTableModel uiTableModel; +typedef struct uiTableModelClass uiTableModelClass; + +struct uiTableModel { + GObject parent_instance; + uiTableModelSpec *spec; + void *mData; + intmax_t nColumns; + GType *coltypes; +}; + +struct uiTableModelClass { + GObjectClass parent_class; +}; + +static void uiTableModel_treeModel_init(GtkTreeModel *); + +G_DEFINE_TYPE_WITH_CODE(uiTableModel, uiTableModel, G_TYPE_OBJECT, + G_IMPLEMENT_INTERFACE(GTK_TYPE_TREE_MODEL, uiTableModel_treeModel_init)) + +static void uiTableModel_init(uiTableModel *m) +{ + // do nothing +} + +static void uiTableModel_dispose(GObject *obj) +{ + G_OBJECT_CLASS(uiTableModel_parent_class)->dispose(obj); +} + +static void uiTableModel_finalize(GObject *obj) +{ + uiTableModel *m = uiTableModel(obj); + + uiFree(m->coltypes); + G_OBJECT_CLASS(uiTableModel_parent_class)->finalize(obj); +} + +static GtkTreeModelFlags uiTableModel_get_flags(GtkTreeModel *mb) +{ + return GTK_TREE_MODEL_LIST_ONLY; +} + +static gint uiTableModel_get_n_columns(GtkTreeModel *mb) +{ + uiTableModel *m = uiTableModel(mb); + + return m->nColumns; +} + +static GType uiTableModel_get_column_type(GtkTreeModel *mb, gint index) +{ + uiTableModel *m = uiTableModel(mb); + + return m->coltypes[index]; +} + +/* +how our GtkTreeIters are stored: + stamp: either GOOD_STAMP or BAD_STAMP + user_data: row index +Thanks to Company in irc.gimp.net/#gtk+ for suggesting the GSIZE_TO_POINTER() t +rick. +*/ +#define GOOD_STAMP 0x1234 +#define BAD_STAMP 0x5678 +#define FROM(x) ((gint) GPOINTER_TO_SIZE((x))) +#define TO(x) GSIZE_TO_POINTER((gsize) (x)) + +#define numRows(m) ((*((m)->spec->NumRows))((m), (m)->mData)) +#define cellValue(m, row, col) ((*((m)->spec->CellValue))((m), (m)->mData, row, column)) + +static gboolean uiTableModel_get_iter(GtkTreeModel *mb, GtkTreeIter *iter, GtkTreePath *path) +{ + uiTableModel *m = uiTableModel(mb); + gint index; + + if (gtk_tree_path_get_depth(path) != 1) + goto bad; + index = gtk_tree_path_get_indices(path)[0]; + if (index < 0) + goto bad; + if (index >= numRows(m)) + goto bad; + iter->stamp = GOOD_STAMP; + iter->user_data = TO(index); + return TRUE; +bad: + iter->stamp = BAD_STAMP; + return FALSE; +} + +static GtkTreePath *uiTableModel_get_path(GtkTreeModel *mb, GtkTreeIter *iter) +{ + // note: from this point forward, the GOOD_STAMP checks ensure that the index stored in iter is nonnegative + if (iter->stamp != GOOD_STAMP) + return NULL; // this is what both GtkListStore and GtkTreeStore do + return gtk_tree_path_new_from_indices(FROM(iter->user_data), -1); +} + +void *uiTableModelFromString(const char *str) +{ + return g_strdup(str); +} + +#define toBool(v) ((int) (v)) +#define toStr(v) ((const char *) (v)) + +static void uiTableModel_get_value(GtkTreeModel *mb, GtkTreeIter *iter, gint column, GValue *value) +{ + uiTableModel *m = uiTableModel(mb); + void *v; + GType type; + + if (iter->stamp != GOOD_STAMP) + return; // this is what both GtkListStore and GtkTreeStore do + v = cellValue(m, FROM(iter->user_data), column); + type = m->coltypes[column]; + g_value_init(value, type); + if (type == G_TYPE_STRING) + g_value_take_string(value, toStr(v)); + // the GValue now manages the memory of the string that was g_strdup()'d before + // TODO image + else if (type == G_TYPE_BOOLEAN) + g_value_set_boolean(value, toBool(v)); + else + complain("unknown GType in uiTableModel_get_value()"); +} + +static gboolean uiTableModel_iter_next(GtkTreeModel *mb, GtkTreeIter *iter) +{ + uiTableModel *m = uiTableModel(mb); + gint index; + + if (iter->stamp != GOOD_STAMP) + return FALSE; // this is what both GtkListStore and GtkTreeStore do + index = FROM(iter->user_data); + index++; + if (index >= numRows(m)) { + iter->stamp = BAD_STAMP; + return FALSE; + } + iter->user_data = TO(index); + return TRUE; +} + +static gboolean uiTableModel_iter_previous(GtkTreeModel *mb, GtkTreeIter *iter) +{ + uiTableModel *m = uiTableModel(mb); + gint index; + + if (iter->stamp != GOOD_STAMP) + return FALSE; // this is what both GtkListStore and GtkTreeStore do + index = FROM(iter->user_data); + if (index <= 0) { + iter->stamp = BAD_STAMP; + return FALSE; + } + index--; + iter->user_data = TO(index); + return TRUE; +} + +static gboolean uiTableModel_iter_children(GtkTreeModel *mb, GtkTreeIter *iter, GtkTreeIter *parent) +{ + uiTableModel *m = uiTableModel(mb); + + if (parent == NULL && numRows(m) > 0) { + child->stamp = GOOD_STAMP; + child->user_data = 0; + return TRUE; + } + child->stamp = BAD_STAMP; + return FALSE; +} + +static gboolean uiTableModel_iter_has_child(GtkTreeModel *mb, GtkTreeIter *iter) +{ + return FALSE; +} + +static gint uiTableModel_iter_n_children(GtkTreeModel *mb, GtkTreeIter *iter) +{ + uiTableModel *m = uiTableModel(mb); + + if (iter == NULL) + return numRows(m); + return 0; +} + +static gboolean uiTableModel_iter_nth_child(GtkTreeModel *mb, GtkTreeIter *iter, GtkTreeIter *parent, gint n) +{ + uiTableModel *m = uiTableModel(mb); + + if (parent == NULL && n >= 0 && n < numRows(m)) { + child->stamp = GOOD_STAMP; + child->user_data = TO(n); + return TRUE; + } + child->stamp = BAD_STAMP; + return FALSE; +} + +static gboolean uiTableModel_iter_parent(GtkTreeModel *mb, GtkTreeIter *iter, GtkTreeIter *child) +{ + parent->stamp = BAD_STAMP; + return FALSE; +} + +static void uiTableModel_class_init(uiTableModelClass *class) +{ + G_OBJECT_CLASS(class)->dispose = uiTableModel_dispose; + G_OBJECT_CLASS(class)->finalize = uiTableModel_finalize; +} + +static void uiTableModel_treeModel_init(GtkTreeModel *iface) +{ + iface->get_flags = uiTableModel_get_flags; + iface->get_n_columns = uiTableModel_get_n_columns; + iface->get_column_type = uiTableModel_get_column_type; + iface->get_iter = uiTableModel_get_iter; + iface->get_path = uiTableModel_get_path; + iface->get_value = uiTableModel_get_value; + iface->iter_next = uiTableModel_iter_next; + iface->iter_previous = uiTableModel_iter_previous; + iface->iter_children = uiTableModel_iter_children; + iface->iter_has_child = uiTableModel_iter_has_child; + iface->iter_n_children = uiTableModel_iter_n_children; + iface->iter_nth_child = uiTableModel_iter_nth_child; + iface->iter_parent = uiTableModel_iter_parent; + // no need for ref_node or unref_node +} + +uiTableModel *uiNewTableModel(uintmax_t nCols, uiTableColumnType *types, uiTableModelSpec *spec, void *mData) +{ + uiTableModel *m; + + m = uiTableModel(g_object_new(uiTableModelType, NULL)); + m->spec = spec; + m->mData = mData; + m->nColumns = nCols; + m->coltypes = (GType *) uiAlloc(m->nColumns * sizeof (GType), "GType[]"); + for (i = 0; i < m->nColumns; i++) + switch (types[i]) { + case uiTableColumnText: + m->coltypes[i] = G_TYPE_STRING; + break; +//TODO case uiTableColumnImage: + // TODO + case uiTableColumnCheckbox: + m->coltypes[i] = G_TYPE_BOOLEAN; + break; + default: + complain("unknown column type %d in uiNewTableModel()", types[i]); + } + return m; +} + +// TODO ensure no tables are subscribed +void uiFreeTableModel(uiTableModel *m) +{ + g_object_unref(m); +} + +void uiTableModelNotify(uiTableModel *m, uiTableNotification notification, intmax_t row, intmax_t column) +{ + GtkTreeModel *model = GTK_TREE_MODEL(m); + GtkTreePath *path; + GtkTreeIter iter; + + path = gtk_tree_path_new_from_indices(row, -1); + switch (notification) { + case uiTableRowInserted: + if (gtk_tree_model_get_iter(model, &iter, path) == FALSE) + complain("invalid row given to row inserted in uiTableModelNotify()"); + gtk_tree_model_row_inserted(model, path, &iter); + break; + case uiTableRowDeleted: + gtk_tree_model_row_deleted(model, path); + break; + case uiTableCellChanged: + if (gtk_tree_model_get_iter(model, &iter, path) == FALSE) + complain("invalid row given to row changed in uiTableModelNotify()"); + gtk_tree_model_row_changed(model, path, &iter); + break; + default: + complain("unknown uiTable notification %d in uiTableModelNotify()", notification); + } + gtk_tree_path_free(path); +}