More uiTable work. I'm not too happy anymore...
This commit is contained in:
parent
66cd3542bc
commit
056db88de0
|
@ -70,6 +70,3 @@ extern void fallbackScale(uiDrawMatrix *, double, double, double, double);
|
||||||
extern void fallbackMultiply(uiDrawMatrix *, uiDrawMatrix *);
|
extern void fallbackMultiply(uiDrawMatrix *, uiDrawMatrix *);
|
||||||
extern void fallbackTransformPoint(uiDrawMatrix *, double *, double *);
|
extern void fallbackTransformPoint(uiDrawMatrix *, double *, double *);
|
||||||
extern void fallbackTransformSize(uiDrawMatrix *, double *, double *);
|
extern void fallbackTransformSize(uiDrawMatrix *, double *, double *);
|
||||||
|
|
||||||
// each table implementation provides this
|
|
||||||
extern void tableNotify(uiTable *, uiTableNotification, intmax_t, intmax_t);
|
|
||||||
|
|
3
ui.h
3
ui.h
|
@ -541,7 +541,6 @@ struct uiTableModelSpec {
|
||||||
};
|
};
|
||||||
|
|
||||||
enum uiTableNotification {
|
enum uiTableNotification {
|
||||||
uiTableNumRowsChanged,
|
|
||||||
uiTableRowInserted,
|
uiTableRowInserted,
|
||||||
uiTableRowDeleted,
|
uiTableRowDeleted,
|
||||||
uiTableCellChanged,
|
uiTableCellChanged,
|
||||||
|
@ -556,6 +555,8 @@ _UI_EXTERN void *uiTableModelFromString(const char *str);
|
||||||
|
|
||||||
struct uiTableColumnParams {
|
struct uiTableColumnParams {
|
||||||
const char *Name;
|
const char *Name;
|
||||||
|
// TODO make this unnecessary
|
||||||
|
uiTableColumnType Type;
|
||||||
int Mutable; // TODO move to the model?
|
int Mutable; // TODO move to the model?
|
||||||
intmax_t ValueColumn;
|
intmax_t ValueColumn;
|
||||||
// TODO background color
|
// TODO background color
|
||||||
|
|
|
@ -24,6 +24,7 @@ CFILES += \
|
||||||
unix/stddialogs.c \
|
unix/stddialogs.c \
|
||||||
unix/tab.c \
|
unix/tab.c \
|
||||||
unix/table.c \
|
unix/table.c \
|
||||||
|
unix/tablemodel.c \
|
||||||
unix/text.c \
|
unix/text.c \
|
||||||
unix/util.c \
|
unix/util.c \
|
||||||
unix/window.c
|
unix/window.c
|
||||||
|
|
326
unix/table.c
326
unix/table.c
|
@ -1,294 +1,88 @@
|
||||||
// 18 october 2015
|
// 18 october 2015
|
||||||
#include "uipriv_unix.h"
|
#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 {
|
struct uiTable {
|
||||||
uiUnixControl c;
|
uiUnixControl c;
|
||||||
GtkWidget *widget;
|
GtkWidget *widget;
|
||||||
GtkContainer *container;
|
GtkContainer *scontainer;
|
||||||
GtkScrolledWindow *sw;
|
GtkScrolledWindow *sw;
|
||||||
GtkWidget *treeWidget;
|
GtkWidget *treeWidget;
|
||||||
GtkTreeView *treeview;
|
GtkTreeView *treeview;
|
||||||
GtkTreeSelection *selection;
|
GtkTreeSelection *selection;
|
||||||
tableGTKModel *model;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct tableGTKModel {
|
|
||||||
GObject parent_instance;
|
|
||||||
uiTableModel *model;
|
uiTableModel *model;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct tableGTKModelClass {
|
uiUnixDefineControl(
|
||||||
GObjectClass parent_class;
|
uiTable, // type name
|
||||||
};
|
uiTableType // type function
|
||||||
|
)
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
void uiTableSetModel(uiTable *t, uiTableModel *m)
|
void uiTableSetModel(uiTable *t, uiTableModel *m)
|
||||||
{
|
{
|
||||||
if (t->model != NULL)
|
t->model = m;
|
||||||
(*(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?
|
|
||||||
gtk_tree_view_set_model(t->treeview, GTK_TREE_MODEL(t->moel));
|
gtk_tree_view_set_model(t->treeview, GTK_TREE_MODEL(t->moel));
|
||||||
}
|
}
|
||||||
|
|
||||||
void uiTableAppendColumn(uiTable *t, uiTableColumnParams *p)
|
void uiTableAppendColumn(uiTable *t, uiTableColumnParams *p)
|
||||||
{
|
{
|
||||||
|
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
|
// 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 *uiNewTable(void)
|
||||||
{
|
{
|
||||||
uiTable *t;
|
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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
Loading…
Reference in New Issue