forked from andlabs/ui
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathtable_unix.c
270 lines (229 loc) · 7.2 KB
/
table_unix.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
// +build !windows,!darwin
// 29 july 2014
#include "gtk_unix.h"
#include "_cgo_export.h"
void tableAppendColumn(GtkTreeView *table, gint index, gchar *name, GtkCellRenderer *renderer, gchar *attribute)
{
GtkTreeViewColumn *col;
col = gtk_tree_view_column_new_with_attributes(name, renderer,
attribute, index,
NULL);
// allow columns to be resized
gtk_tree_view_column_set_resizable(col, TRUE);
gtk_tree_view_append_column(table, col);
}
/*
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() trick.
*/
#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 void goTableModel_initGtkTreeModel(GtkTreeModelIface *);
G_DEFINE_TYPE_WITH_CODE(goTableModel, goTableModel, G_TYPE_OBJECT,
G_IMPLEMENT_INTERFACE(GTK_TYPE_TREE_MODEL, goTableModel_initGtkTreeModel))
static void goTableModel_init(goTableModel *t)
{
// do nothing
}
static void goTableModel_dispose(GObject *obj)
{
G_OBJECT_CLASS(goTableModel_parent_class)->dispose(obj);
}
static void goTableModel_finalize(GObject *obj)
{
G_OBJECT_CLASS(goTableModel_parent_class)->finalize(obj);
}
// and now for the interface function definitions
static GtkTreeModelFlags goTableModel_get_flags(GtkTreeModel *model)
{
return GTK_TREE_MODEL_LIST_ONLY;
}
// get_n_columns and get_column_type in Go
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 = TO(index);
return TRUE;
bad:
iter->stamp = BAD_STAMP;
return FALSE;
}
static GtkTreePath *goTableModel_get_path(GtkTreeModel *model, 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);
}
static void goTableModel_get_value(GtkTreeModel *model, GtkTreeIter *iter, gint column, GValue *value)
{
goTableModel *t = (goTableModel *) model;
if (iter->stamp != GOOD_STAMP)
return; // this is what both GtkListStore and GtkTreeStore do
goTableModel_do_get_value(t->gotable, FROM(iter->user_data), column, value);
}
static gboolean goTableModel_iter_next(GtkTreeModel *model, GtkTreeIter *iter)
{
goTableModel *t = (goTableModel *) model;
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 >= goTableModel_getRowCount(t->gotable)) {
iter->stamp = BAD_STAMP;
return FALSE;
}
iter->user_data = TO(index);
return TRUE;
}
static gboolean goTableModel_iter_previous(GtkTreeModel *model, GtkTreeIter *iter)
{
goTableModel *t = (goTableModel *) model;
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 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 = TO(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)
{
// don't chain; we have nothing to chain to
#define DEF(x) interface->x = goTableModel_ ## x;
DEF(get_flags)
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];
static void goTableModel_set_property(GObject *obj, guint id, const GValue *value, GParamSpec *pspec)
{
goTableModel *t = (goTableModel *) obj;
if (id == 1) {
t->gotable = (void *) g_value_get_pointer(value);
return;
}
G_OBJECT_WARN_INVALID_PROPERTY_ID(t, id, pspec);
}
static void goTableModel_get_property(GObject *obj, guint id, GValue *value, GParamSpec *pspec)
{
G_OBJECT_WARN_INVALID_PROPERTY_ID((goTableModel *) obj, id, pspec);
}
static void goTableModel_class_init(goTableModelClass *class)
{
G_OBJECT_CLASS(class)->dispose = goTableModel_dispose;
G_OBJECT_CLASS(class)->finalize = goTableModel_finalize;
G_OBJECT_CLASS(class)->set_property = goTableModel_set_property;
G_OBJECT_CLASS(class)->get_property = goTableModel_get_property;
goTableModelProperties[1] = g_param_spec_pointer(
"gotable",
"go-table",
"Go-side *table value",
G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
g_object_class_install_properties(G_OBJECT_CLASS(class), 2, goTableModelProperties);
}
goTableModel *newTableModel(void *gotable)
{
return (goTableModel *) g_object_new(goTableModel_get_type(), "gotable", (gpointer) gotable, NULL);
}
// somewhat naive, but the only alternatives seem to be unloading/reloading the model (or the view!), which is bleh
void tableUpdate(goTableModel *t, gint old, gint new)
{
gint i;
gint nUpdate;
GtkTreePath *path;
GtkTreeIter iter;
iter.stamp = GOOD_STAMP;
// first, append extra items
if (old < new) {
for (i = old; i < new; i++) {
path = gtk_tree_path_new_from_indices(i, -1);
iter.user_data = TO(i);
g_signal_emit_by_name(t, "row-inserted", path, &iter);
}
nUpdate = old;
} else
nUpdate = new;
// next, update existing items
for (i = 0; i < nUpdate; i++) {
path = gtk_tree_path_new_from_indices(i, -1);
iter.user_data = TO(i);
g_signal_emit_by_name(t, "row-changed", path, &iter);
}
// finally, remove deleted items
if (old > new)
for (i = new; i < old; i++) {
// note that we repeatedly remove the row at index new, as that changes with each removal; NOT i
path = gtk_tree_path_new_from_indices(new, -1);
// row-deleted has no iter
g_signal_emit_by_name(t, "row-deleted", path);
}
}