Wrote the GTK+ Table model code. Mostly working; now it just needs an update mechanism...
This commit is contained in:
parent
92539e10e9
commit
24f5a91ff4
|
@ -23,7 +23,7 @@ Thanks to desrt in irc.gimp.net/#gtk+
|
||||||
#include <gtk/gtk.h>
|
#include <gtk/gtk.h>
|
||||||
|
|
||||||
/* table_unix.c */
|
/* table_unix.c */
|
||||||
extern void tableAppendColumn(GtkTreeView *, gchar *);
|
extern void tableAppendColumn(GtkTreeView *, gint, gchar *);
|
||||||
typedef struct goTableModel goTableModel;
|
typedef struct goTableModel goTableModel;
|
||||||
typedef struct goTableModelClass goTableModelClass;
|
typedef struct goTableModelClass goTableModelClass;
|
||||||
struct goTableModel {
|
struct goTableModel {
|
||||||
|
|
|
@ -3,18 +3,27 @@
|
||||||
#include "gtk_unix.h"
|
#include "gtk_unix.h"
|
||||||
#include "_cgo_export.h"
|
#include "_cgo_export.h"
|
||||||
|
|
||||||
void tableAppendColumn(GtkTreeView *table, gchar *name)
|
void tableAppendColumn(GtkTreeView *table, gint index, gchar *name)
|
||||||
{
|
{
|
||||||
GtkTreeViewColumn *col;
|
GtkTreeViewColumn *col;
|
||||||
GtkCellRenderer *renderer;
|
GtkCellRenderer *renderer;
|
||||||
|
|
||||||
renderer = gtk_cell_renderer_text_new();
|
renderer = gtk_cell_renderer_text_new();
|
||||||
col = gtk_tree_view_column_new_with_attributes(name, renderer,
|
col = gtk_tree_view_column_new_with_attributes(name, renderer,
|
||||||
/* TODO */
|
"text", index,
|
||||||
NULL);
|
NULL);
|
||||||
gtk_tree_view_append_column(table, col);
|
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 *);
|
static void goTableModel_initGtkTreeModel(GtkTreeModelIface *);
|
||||||
|
|
||||||
G_DEFINE_TYPE_WITH_CODE(goTableModel, goTableModel, G_TYPE_OBJECT,
|
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;
|
return GTK_TREE_MODEL_LIST_ONLY;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 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)
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
static void goTableModel_initGtkTreeModel(GtkTreeModelIface *interface)
|
||||||
{
|
{
|
||||||
GtkTreeModelIface *chain;
|
/* don't chain; we have nothing to chain to */
|
||||||
|
|
||||||
chain = (GtkTreeModelIface *) g_type_interface_peek_parent(interface);
|
|
||||||
#define DEF(x) interface->x = goTableModel_ ## x;
|
#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)
|
DEF(get_flags)
|
||||||
CHAIN(get_n_columns)
|
DEF(get_n_columns)
|
||||||
CHAIN(get_column_type)
|
DEF(get_column_type)
|
||||||
CHAIN(get_iter)
|
DEF(get_iter)
|
||||||
CHAIN(get_path)
|
DEF(get_path)
|
||||||
CHAIN(get_value)
|
DEF(get_value)
|
||||||
CHAIN(iter_next)
|
DEF(iter_next)
|
||||||
CHAIN(iter_previous)
|
DEF(iter_previous)
|
||||||
CHAIN(iter_children)
|
DEF(iter_children)
|
||||||
CHAIN(iter_has_child)
|
DEF(iter_has_child)
|
||||||
CHAIN(iter_n_children)
|
DEF(iter_n_children)
|
||||||
CHAIN(iter_nth_child)
|
DEF(iter_nth_child)
|
||||||
CHAIN(iter_parent)
|
DEF(iter_parent)
|
||||||
CHAIN(ref_node)
|
/* no need for ref_node and unref_node */
|
||||||
CHAIN(unref_node)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static GParamSpec *goTableModelProperties[2];
|
static GParamSpec *goTableModelProperties[2];
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
package ui
|
package ui
|
||||||
|
|
||||||
import (
|
import (
|
||||||
// "fmt"
|
"fmt"
|
||||||
"reflect"
|
"reflect"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
)
|
)
|
||||||
|
@ -20,6 +20,12 @@ type table struct {
|
||||||
|
|
||||||
scrollc *C.GtkContainer
|
scrollc *C.GtkContainer
|
||||||
scrollwindow *C.GtkScrolledWindow
|
scrollwindow *C.GtkScrolledWindow
|
||||||
|
|
||||||
|
model *C.goTableModel
|
||||||
|
modelgtk *C.GtkTreeModel
|
||||||
|
|
||||||
|
// stuff required by GtkTreeModel
|
||||||
|
nColumns C.gint
|
||||||
}
|
}
|
||||||
|
|
||||||
func finishNewTable(b *tablebase, ty reflect.Type) Table {
|
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+)
|
// 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_scrolled_window_set_shadow_type(t.scrollwindow, C.GTK_SHADOW_IN)
|
||||||
C.gtk_container_add(t.scrollc, t.treewidget)
|
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++ {
|
for i := 0; i < ty.NumField(); i++ {
|
||||||
cname := togstr(ty.Field(i).Name)
|
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
|
freegstr(cname) // free now (not deferred) to conserve memory
|
||||||
}
|
}
|
||||||
|
// and for some GtkTreeModel boilerplate
|
||||||
|
t.nColumns = C.gint(ty.NumField())
|
||||||
return t
|
return t
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -62,3 +73,30 @@ func (t *table) Unlock() {
|
||||||
defer t.RUnlock()
|
defer t.RUnlock()
|
||||||
// TODO
|
// 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())
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue