2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
4 * Copyright 2017 Olaf Wintermann. All rights reserved.
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 * POSSIBILITY OF SUCH DAMAGE.
36 #define IS_UI_LIST_MODEL(obj) \
37 (G_TYPE_CHECK_INSTANCE_TYPE((obj), list_model_type))
38 #define UI_LIST_MODEL(obj) \
39 (G_TYPE_CHECK_INSTANCE_CAST((obj), list_model_type, UiListModel))
41 static void list_model_class_init(GObjectClass *cl, gpointer data);
42 static void list_model_interface_init(GtkTreeModelIface *i, gpointer data);
43 static void list_model_init(UiListModel *instance, GObjectClass *cl);
45 static void list_model_dnd_dest_interface_init(GtkTreeDragDestIface *i, gpointer data);
46 static void list_model_dnd_src_interface_init(GtkTreeDragSourceIface *i, gpointer data);
48 static GObjectClass list_model_class;
49 static const GTypeInfo list_model_info = {
53 (GClassInitFunc)list_model_class_init,
58 (GInstanceInitFunc)list_model_init
60 static const GInterfaceInfo list_model_interface_info = {
61 (GInterfaceInitFunc)list_model_interface_init,
65 static GType list_model_type;
67 static const GInterfaceInfo list_model_dnd_dest_interface_info = {
68 (GInterfaceInitFunc)list_model_dnd_dest_interface_init,
72 static const GInterfaceInfo list_model_dnd_src_interface_info = {
73 (GInterfaceInitFunc)list_model_dnd_src_interface_init,
79 list_model_type = g_type_register_static(
84 g_type_add_interface_static(
87 &list_model_interface_info);
88 g_type_add_interface_static(
90 GTK_TYPE_TREE_DRAG_DEST,
91 &list_model_dnd_dest_interface_info);
92 g_type_add_interface_static(
94 GTK_TYPE_TREE_DRAG_SOURCE,
95 &list_model_dnd_src_interface_info);
98 static void list_model_class_init(GObjectClass *cl, gpointer data) {
99 cl->dispose = ui_list_model_dispose;
100 cl->finalize = ui_list_model_finalize;
104 static void list_model_interface_init(GtkTreeModelIface *i, gpointer data) {
105 i->get_flags = ui_list_model_get_flags;
106 i->get_n_columns = ui_list_model_get_n_columns;
107 i->get_column_type = ui_list_model_get_column_type;
108 i->get_iter = ui_list_model_get_iter;
109 i->get_path = ui_list_model_get_path;
110 i->get_value = ui_list_model_get_value;
111 i->iter_next = ui_list_model_iter_next;
112 i->iter_children = ui_list_model_iter_children;
113 i->iter_has_child = ui_list_model_iter_has_child;
114 i->iter_n_children = ui_list_model_iter_n_children;
115 i->iter_nth_child = ui_list_model_iter_nth_child;
116 i->iter_parent = ui_list_model_iter_parent;
119 static void list_model_dnd_dest_interface_init(GtkTreeDragDestIface *i, gpointer data) {
120 i->drag_data_received = ui_list_model_drag_data_received;
121 i->row_drop_possible = ui_list_model_row_drop_possible;
124 static void list_model_dnd_src_interface_init(GtkTreeDragSourceIface *i, gpointer data) {
125 i->drag_data_delete = ui_list_model_drag_data_delete;
126 i->drag_data_get = ui_list_model_drag_data_get;
127 i->row_draggable = ui_list_model_row_draggable;
130 static void list_model_init(UiListModel *instance, GObjectClass *cl) {
131 instance->columntypes = NULL;
132 instance->var = NULL;
133 instance->numcolumns = 0;
134 instance->stamp = g_random_int();
137 static GType ui_gtk_type(UiModelType type) {
140 case UI_STRING: return G_TYPE_STRING;
141 case UI_INTEGER: return G_TYPE_INT;
143 return G_TYPE_INVALID;
146 static void ui_model_set_value(GType type, void *data, GValue *value) {
149 case G_TYPE_OBJECT: {
150 value->g_type = G_TYPE_OBJECT;
151 g_value_set_object(value, data);
154 case G_TYPE_STRING: {
155 value->g_type = G_TYPE_STRING;
156 g_value_set_string(value, data);
160 value->g_type = G_TYPE_INT;
162 g_value_set_int(value, *i);
166 value->g_type = G_TYPE_INVALID;
169 UiListModel* ui_list_model_new(UiObject *obj, UiVar *var, UiModel *info) {
170 UiListModel *model = g_object_new(list_model_type, NULL);
174 model->columntypes = calloc(sizeof(GType), 2 * info->columns);
176 for(int i=0;i<info->columns;i++) {
177 UiModelType type = info->types[i];
178 if(type == UI_ICON_TEXT) {
179 model->columntypes[ncol] = G_TYPE_OBJECT;
181 model->columntypes[ncol] = G_TYPE_STRING;
183 model->columntypes[ncol] = ui_gtk_type(info->types[i]);
187 model->numcolumns = ncol;
191 void ui_list_model_dispose(GObject *obj) {
195 void ui_list_model_finalize(GObject *obj) {
200 GtkTreeModelFlags ui_list_model_get_flags(GtkTreeModel *tree_model) {
201 return (GTK_TREE_MODEL_LIST_ONLY | GTK_TREE_MODEL_ITERS_PERSIST);
204 gint ui_list_model_get_n_columns(GtkTreeModel *tree_model) {
205 g_return_val_if_fail(IS_UI_LIST_MODEL(tree_model), 0);
206 UiListModel *model = UI_LIST_MODEL(tree_model);
207 return model->numcolumns;
210 GType ui_list_model_get_column_type(GtkTreeModel *tree_model, gint index) {
211 g_return_val_if_fail(IS_UI_LIST_MODEL(tree_model), G_TYPE_INVALID);
212 UiListModel *model = UI_LIST_MODEL(tree_model);
213 g_return_val_if_fail(index < model->numcolumns, G_TYPE_INVALID);
214 return model->columntypes[index];
217 gboolean ui_list_model_get_iter(
218 GtkTreeModel *tree_model,
222 g_assert(IS_UI_LIST_MODEL(tree_model));
223 UiListModel *model = UI_LIST_MODEL(tree_model);
224 UiList *list = model->var->value;
226 // check the depth of the path
227 // a list must have a depth of 1
228 gint depth = gtk_tree_path_get_depth(path);
229 g_assert(depth == 1);
232 gint *indices = gtk_tree_path_get_indices(path);
233 gint row = indices[0];
237 // we don't need to count if the first element is requested
238 if(list->first(list) == NULL) {
241 } else if(row >= list->count(list)) {
245 // the UiList has an integrated iterator
246 // we only get a value to adjust it
249 val = list->first(list);
251 val = list->get(list, row);
254 iter->stamp = model->stamp;
255 iter->user_data = list->iter;
256 iter->user_data2 = (gpointer)(intptr_t)row; // list->index
257 iter->user_data3 = val;
259 return val ? TRUE : FALSE;
262 GtkTreePath* ui_list_model_get_path(
263 GtkTreeModel *tree_model,
266 g_return_val_if_fail(IS_UI_LIST_MODEL(tree_model), NULL);
267 g_return_val_if_fail(iter != NULL, NULL);
268 g_return_val_if_fail(iter->user_data != NULL, NULL);
270 UiListModel *model = UI_LIST_MODEL(tree_model);
272 GtkTreePath *path = gtk_tree_path_new();
273 gtk_tree_path_append_index(path, (int)(intptr_t)iter->user_data2); // list->index
278 void ui_list_model_get_value(
279 GtkTreeModel *tree_model,
284 g_return_if_fail(IS_UI_LIST_MODEL(tree_model));
285 g_return_if_fail(iter != NULL);
286 g_return_if_fail(iter->user_data != NULL);
288 UiListModel *model = UI_LIST_MODEL(tree_model);
289 UiList *list = model->var->value;
291 g_return_if_fail(column < model->numcolumns);
293 // TODO: return correct value from column
295 //value->g_type = G_TYPE_STRING;
296 list->iter = iter->user_data;
297 //list->index = (int)(intptr_t)iter->user_data2;
298 //list->current = iter->user_data3;
299 if(model->model->getvalue) {
300 void *data = model->model->getvalue(iter->user_data3, column);
301 if(model->columntypes[column] == G_TYPE_OBJECT) {
303 ui_model_set_value(model->columntypes[column], img->pixbuf, value);
305 ui_model_set_value(model->columntypes[column], data, value);
308 value->g_type = G_TYPE_INVALID;
312 gboolean ui_list_model_iter_next(
313 GtkTreeModel *tree_model,
316 g_return_val_if_fail(IS_UI_LIST_MODEL(tree_model), FALSE);
317 g_return_val_if_fail(iter != NULL, FALSE);
318 //g_return_val_if_fail(iter->user_data != NULL, FALSE);
320 if(!iter->user_data) {
324 UiListModel *model = UI_LIST_MODEL(tree_model);
325 UiList *list = model->var->value;
326 list->iter = iter->user_data;
327 //list->index = (int)(intptr_t)iter->user_data2;
328 void *val = list->next(list);
329 iter->user_data = list->iter;
330 intptr_t index = (intptr_t)iter->user_data2;
332 //iter->user_data2 = (gpointer)(intptr_t)list->index;
333 iter->user_data2 = (gpointer)index;
334 iter->user_data3 = val;
335 return val ? TRUE : FALSE;
338 gboolean ui_list_model_iter_children(
339 GtkTreeModel *tree_model,
343 g_return_val_if_fail(IS_UI_LIST_MODEL(tree_model), FALSE);
345 UiListModel *model = UI_LIST_MODEL(tree_model);
346 UiList *list = model->var->value;
353 * a list element has no children
354 * we set the iter to the first element
356 void *val = list->first(list);
357 iter->stamp = model->stamp;
358 iter->user_data = list->iter;
359 iter->user_data2 = (gpointer)0;
360 iter->user_data3 = val;
362 return val ? TRUE : FALSE;
365 gboolean ui_list_model_iter_has_child(
366 GtkTreeModel *tree_model,
372 gint ui_list_model_iter_n_children(
373 GtkTreeModel *tree_model,
376 g_assert(IS_UI_LIST_MODEL(tree_model));
379 // return number of rows
380 UiListModel *model = UI_LIST_MODEL(tree_model);
381 UiList *list = model->var->value;
382 return list->count(list);
388 gboolean ui_list_model_iter_nth_child(
389 GtkTreeModel *tree_model,
394 g_return_val_if_fail(IS_UI_LIST_MODEL(tree_model), FALSE);
400 UiListModel *model = UI_LIST_MODEL(tree_model);
401 UiList *list = model->var->value;
405 // we don't need to count if the first element is requested
406 if(list->first(list) == NULL) {
409 } else if(n >= list->count(list)) {
413 void *val = list->get(list, n);
414 iter->stamp = model->stamp;
415 iter->user_data = list->iter;
416 iter->user_data2 = (gpointer)(intptr_t)n; // list->index
417 iter->user_data3 = val;
419 return val ? TRUE : FALSE;
422 gboolean ui_list_model_iter_parent(
423 GtkTreeModel *tree_model,
432 gboolean ui_list_model_drag_data_received(
433 GtkTreeDragDest *drag_dest,
434 GtkTreePath *dest_path,
435 GtkSelectionData *selection_data)
437 //printf("drag received\n");
438 UiListModel *model = UI_LIST_MODEL(drag_dest);
439 if(model->model->drop) {
440 gint *indices = gtk_tree_path_get_indices(dest_path);
441 gint row = indices[0];
444 e.window = e.obj->window;
445 e.document = e.obj->ctx->document;
449 s.data = selection_data;
450 model->model->drop(&e, &s, model->var->value, row);
455 gboolean ui_list_model_row_drop_possible(
456 GtkTreeDragDest *drag_dest,
457 GtkTreePath *dest_path,
458 GtkSelectionData *selection_data)
460 //printf("row_drop_possible\n");
461 UiListModel *model = UI_LIST_MODEL(drag_dest);
462 if(model->model->candrop) {
463 gint *indices = gtk_tree_path_get_indices(dest_path);
464 gint row = indices[0];
467 e.window = e.obj->window;
468 e.document = e.obj->ctx->document;
472 s.data = selection_data;
473 return model->model->candrop(&e, &s, model->var->value, row);
478 gboolean ui_list_model_row_draggable(
479 GtkTreeDragSource *drag_source,
482 //printf("row_draggable\n");
483 UiListModel *model = UI_LIST_MODEL(drag_source);
484 if(model->model->candrag) {
485 gint *indices = gtk_tree_path_get_indices(path);
486 gint row = indices[0];
489 e.window = e.obj->window;
490 e.document = e.obj->ctx->document;
493 return model->model->candrag(&e, model->var->value, row);
498 gboolean ui_list_model_drag_data_get(
499 GtkTreeDragSource *drag_source,
501 GtkSelectionData *selection_data)
503 //printf("drag_data_get\n");
504 UiListModel *model = UI_LIST_MODEL(drag_source);
505 if(model->model->data_get) {
506 gint *indices = gtk_tree_path_get_indices(path);
507 gint row = indices[0];
510 e.window = e.obj->window;
511 e.document = e.obj->ctx->document;
515 s.data = selection_data;
516 model->model->data_get(&e, &s, model->var->value, row);
521 gboolean ui_list_model_drag_data_delete(
522 GtkTreeDragSource *drag_source,
525 //printf("drag_data_delete\n");
526 UiListModel *model = UI_LIST_MODEL(drag_source);
527 if(model->model->data_get) {
528 gint *indices = gtk_tree_path_get_indices(path);
529 gint row = indices[0];
532 e.window = e.obj->window;
533 e.document = e.obj->ctx->document;
536 model->model->data_delete(&e, model->var->value, row);