From 24f5a91ff42031d65b64be724bc0237af96d6c05 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 29 Jul 2014 03:07:11 -0400 Subject: Wrote the GTK+ Table model code. Mostly working; now it just needs an update mechanism... --- redo/gtk_unix.h | 2 +- redo/table_unix.c | 181 +++++++++++++++++++++++++++++++++++++++++++++-------- redo/table_unix.go | 44 ++++++++++++- 3 files changed, 196 insertions(+), 31 deletions(-) diff --git a/redo/gtk_unix.h b/redo/gtk_unix.h index 874e0f2..48b9918 100644 --- a/redo/gtk_unix.h +++ b/redo/gtk_unix.h @@ -23,7 +23,7 @@ Thanks to desrt in irc.gimp.net/#gtk+ #include /* table_unix.c */ -extern void tableAppendColumn(GtkTreeView *, gchar *); +extern void tableAppendColumn(GtkTreeView *, gint, gchar *); typedef struct goTableModel goTableModel; typedef struct goTableModelClass goTableModelClass; struct goTableModel { diff --git a/redo/table_unix.c b/redo/table_unix.c index 73902e1..0bd84de 100644 --- a/redo/table_unix.c +++ b/redo/table_unix.c @@ -3,18 +3,27 @@ #include "gtk_unix.h" #include "_cgo_export.h" -void tableAppendColumn(GtkTreeView *table, gchar *name) +void tableAppendColumn(GtkTreeView *table, gint index, gchar *name) { GtkTreeViewColumn *col; GtkCellRenderer *renderer; renderer = gtk_cell_renderer_text_new(); col = gtk_tree_view_column_new_with_attributes(name, renderer, - /* TODO */ + "text", index, NULL); gtk_tree_view_append_column(table, col); } +/* +how our GtkTreeIters are stored: + stamp: either GOOD_STAMP or BAD_STAMP + user_data: row index +TODO verify range of gint +*/ +#define GOOD_STAMP 0x1234 +#define BAD_STAMP 0x5678 + static void goTableModel_initGtkTreeModel(GtkTreeModelIface *); G_DEFINE_TYPE_WITH_CODE(goTableModel, goTableModel, G_TYPE_OBJECT, @@ -42,35 +51,153 @@ static GtkTreeModelFlags goTableModel_get_flags(GtkTreeModel *model) return GTK_TREE_MODEL_LIST_ONLY; } -static void goTableModel_initGtkTreeModel(GtkTreeModelIface *interface) +/* get_n_columns in Go */ + +static GType goTableModel_get_column_type(GtkTreeModel *model, gint column) +{ + /* TODO change when we get more column types */ + return G_TYPE_STRING; +} + +static gboolean goTableModel_get_iter(GtkTreeModel *model, GtkTreeIter *iter, GtkTreePath *path) +{ + goTableModel *t = (goTableModel *) model; + 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 >= goTableModel_getRowCount(t->gotable)) + goto bad; + iter->stamp = GOOD_STAMP; + iter->user_data = (gpointer) index; + return TRUE; +bad: + iter->stamp = BAD_STAMP; + return FALSE; +} + +static GtkTreePath *goTableModel_get_path(GtkTreeModel *model, GtkTreeIter *iter) +{ + if (iter->stamp != GOOD_STAMP) + return NULL; /* TODO is this right? */ + return gtk_tree_path_new_from_indices((gint) iter->user_data, -1); +} + +static void goTableModel_get_value(GtkTreeModel *model, GtkTreeIter *iter, gint column, GValue *value) +{ + goTableModel *t = (goTableModel *) model; + gchar *str; + + /* TODO what if iter is invalid? */ + /* we (actually cgo) allocated str with malloc(), not g_malloc(), so let's free it explicitly and give the GValue a copy to be safe */ + str = goTableModel_do_get_value(t->gotable, (gint) iter->user_data, column); + g_value_set_string(value, str); + free(str); +} + +static gboolean goTableModel_iter_next(GtkTreeModel *model, GtkTreeIter *iter) +{ + goTableModel *t = (goTableModel *) model; + gint index; + + if (iter->stamp != GOOD_STAMP) + return FALSE; /* TODO correct? */ + index = (gint) iter->user_data; + index++; + iter->user_data = (gpointer) index; + if (index >= goTableModel_getRowCount(t->gotable)) { + iter->stamp = BAD_STAMP; + return FALSE; + } + return TRUE; +} + +static gboolean goTableModel_iter_previous(GtkTreeModel *model, GtkTreeIter *iter) +{ + goTableModel *t = (goTableModel *) model; + gint index; + + if (iter->stamp != GOOD_STAMP) + return FALSE; /* TODO correct? */ + index = (gint) iter->user_data; + index--; + iter->user_data = (gpointer) index; + if (index < 0) { + iter->stamp = BAD_STAMP; + return FALSE; + } + return TRUE; +} + +static gboolean goTableModel_iter_children(GtkTreeModel *model, GtkTreeIter *child, GtkTreeIter *parent) +{ + goTableModel *t = (goTableModel *) model; + + if (parent == NULL && goTableModel_getRowCount(t->gotable) > 0) { + child->stamp = GOOD_STAMP; + child->user_data = 0; + return TRUE; + } + child->stamp = BAD_STAMP; + return FALSE; +} + +static gboolean goTableModel_iter_has_child(GtkTreeModel *model, GtkTreeIter *iter) +{ + return FALSE; +} + +static gint goTableModel_iter_n_children(GtkTreeModel *model, GtkTreeIter *iter) +{ + goTableModel *t = (goTableModel *) model; + + if (iter == NULL) + return goTableModel_getRowCount(t->gotable); + return 0; +} + +static gboolean goTableModel_iter_nth_child(GtkTreeModel *model, GtkTreeIter *child, GtkTreeIter *parent, gint n) { - GtkTreeModelIface *chain; + goTableModel *t = (goTableModel *) model; + + if (parent == NULL && n >= 0 && n < goTableModel_getRowCount(t->gotable)) { + child->stamp = GOOD_STAMP; + child->user_data = (gpointer) n; + return TRUE; + } + child->stamp = BAD_STAMP; + return FALSE; +} - chain = (GtkTreeModelIface *) g_type_interface_peek_parent(interface); +static gboolean goTableModel_iter_parent(GtkTreeModel *model, GtkTreeIter *parent, GtkTreeIter *child) +{ + parent->stamp = BAD_STAMP; + return FALSE; +} + +/* end of interface definitions */ + +static void goTableModel_initGtkTreeModel(GtkTreeModelIface *interface) +{ + /* don't chain; we have nothing to chain to */ #define DEF(x) interface->x = goTableModel_ ## x; -#define CHAIN(x) interface->x = chain->x; - /* signals */ - CHAIN(row_changed) - CHAIN(row_inserted) - CHAIN(row_has_child_toggled) - CHAIN(row_deleted) - CHAIN(rows_reordered) - /* vtable */ DEF(get_flags) - CHAIN(get_n_columns) - CHAIN(get_column_type) - CHAIN(get_iter) - CHAIN(get_path) - CHAIN(get_value) - CHAIN(iter_next) - CHAIN(iter_previous) - CHAIN(iter_children) - CHAIN(iter_has_child) - CHAIN(iter_n_children) - CHAIN(iter_nth_child) - CHAIN(iter_parent) - CHAIN(ref_node) - CHAIN(unref_node) + DEF(get_n_columns) + DEF(get_column_type) + DEF(get_iter) + DEF(get_path) + DEF(get_value) + DEF(iter_next) + DEF(iter_previous) + DEF(iter_children) + DEF(iter_has_child) + DEF(iter_n_children) + DEF(iter_nth_child) + DEF(iter_parent) + /* no need for ref_node and unref_node */ } static GParamSpec *goTableModelProperties[2]; diff --git a/redo/table_unix.go b/redo/table_unix.go index 049945c..99a8a01 100644 --- a/redo/table_unix.go +++ b/redo/table_unix.go @@ -3,7 +3,7 @@ package ui import ( -// "fmt" + "fmt" "reflect" "unsafe" ) @@ -20,6 +20,12 @@ type table struct { scrollc *C.GtkContainer scrollwindow *C.GtkScrolledWindow + + model *C.goTableModel + modelgtk *C.GtkTreeModel + + // stuff required by GtkTreeModel + nColumns C.gint } func finishNewTable(b *tablebase, ty reflect.Type) Table { @@ -37,12 +43,17 @@ func finishNewTable(b *tablebase, ty reflect.Type) Table { // give the scrolled window a border (thanks to jlindgren in irc.gimp.net/#gtk+) C.gtk_scrolled_window_set_shadow_type(t.scrollwindow, C.GTK_SHADOW_IN) C.gtk_container_add(t.scrollc, t.treewidget) - // TODO model + model := C.newTableModel(unsafe.Pointer(t)) + t.model = model + t.modelgtk = (*C.GtkTreeModel)(unsafe.Pointer(model)) + C.gtk_tree_view_set_model(t.treeview, t.modelgtk) for i := 0; i < ty.NumField(); i++ { cname := togstr(ty.Field(i).Name) - C.tableAppendColumn(t.treeview, cname) + C.tableAppendColumn(t.treeview, C.gint(i), cname) freegstr(cname) // free now (not deferred) to conserve memory } + // and for some GtkTreeModel boilerplate + t.nColumns = C.gint(ty.NumField()) return t } @@ -62,3 +73,30 @@ func (t *table) Unlock() { defer t.RUnlock() // TODO } + +//export goTableModel_get_n_columns +func goTableModel_get_n_columns(model *C.GtkTreeModel) C.gint { + tm := (*C.goTableModel)(unsafe.Pointer(model)) + t := (*table)(tm.gotable) + return t.nColumns +} + +//export goTableModel_do_get_value +func goTableModel_do_get_value(data unsafe.Pointer, row C.gint, col C.gint) *C.gchar { + t := (*table)(data) + t.RLock() + defer t.RUnlock() + d := reflect.Indirect(reflect.ValueOf(t.data)) + datum := d.Index(int(row)).Field(int(col)) + s := fmt.Sprintf("%v", datum) + return togstr(s) +} + +//export goTableModel_getRowCount +func goTableModel_getRowCount(data unsafe.Pointer) C.gint { + t := (*table)(data) + t.RLock() + defer t.RUnlock() + d := reflect.Indirect(reflect.ValueOf(t.data)) + return C.gint(d.Len()) +} -- cgit v1.2.3