ui/gtk/model.c

changeset 0
804d8803eade
equal deleted inserted replaced
-1:000000000000 0:804d8803eade
1 /*
2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3 *
4 * Copyright 2017 Olaf Wintermann. All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are met:
8 *
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 *
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.
15 *
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.
27 */
28
29 #include <stdio.h>
30 #include <stdlib.h>
31
32 #include "model.h"
33 #include "image.h"
34 #include "toolkit.h"
35
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))
40
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);
44
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);
47
48 static GObjectClass list_model_class;
49 static const GTypeInfo list_model_info = {
50 sizeof(GObjectClass),
51 NULL,
52 NULL,
53 (GClassInitFunc)list_model_class_init,
54 NULL,
55 NULL,
56 sizeof(UiListModel),
57 0,
58 (GInstanceInitFunc)list_model_init
59 };
60 static const GInterfaceInfo list_model_interface_info = {
61 (GInterfaceInitFunc)list_model_interface_init,
62 NULL,
63 NULL
64 };
65 static GType list_model_type;
66
67 static const GInterfaceInfo list_model_dnd_dest_interface_info = {
68 (GInterfaceInitFunc)list_model_dnd_dest_interface_init,
69 NULL,
70 NULL
71 };
72 static const GInterfaceInfo list_model_dnd_src_interface_info = {
73 (GInterfaceInitFunc)list_model_dnd_src_interface_init,
74 NULL,
75 NULL
76 };
77
78 void ui_list_init() {
79 list_model_type = g_type_register_static(
80 G_TYPE_OBJECT,
81 "UiListModel",
82 &list_model_info,
83 (GTypeFlags)0);
84 g_type_add_interface_static(
85 list_model_type,
86 GTK_TYPE_TREE_MODEL,
87 &list_model_interface_info);
88 g_type_add_interface_static(
89 list_model_type,
90 GTK_TYPE_TREE_DRAG_DEST,
91 &list_model_dnd_dest_interface_info);
92 g_type_add_interface_static(
93 list_model_type,
94 GTK_TYPE_TREE_DRAG_SOURCE,
95 &list_model_dnd_src_interface_info);
96 }
97
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;
101
102 }
103
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;
117 }
118
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;
122 }
123
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;
128 }
129
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();
135 }
136
137 static GType ui_gtk_type(UiModelType type) {
138 switch(type) {
139 default: break;
140 case UI_STRING: return G_TYPE_STRING;
141 case UI_INTEGER: return G_TYPE_INT;
142 }
143 return G_TYPE_INVALID;
144 }
145
146 static void ui_model_set_value(GType type, void *data, GValue *value) {
147 switch(type) {
148 default: break;
149 case G_TYPE_OBJECT: {
150 value->g_type = G_TYPE_OBJECT;
151 g_value_set_object(value, data);
152 return;
153 }
154 case G_TYPE_STRING: {
155 value->g_type = G_TYPE_STRING;
156 g_value_set_string(value, data);
157 return;
158 }
159 case G_TYPE_INT: {
160 value->g_type = G_TYPE_INT;
161 int *i = data;
162 g_value_set_int(value, *i);
163 return;
164 }
165 }
166 value->g_type = G_TYPE_INVALID;
167 }
168
169 UiListModel* ui_list_model_new(UiObject *obj, UiVar *var, UiModel *info) {
170 UiListModel *model = g_object_new(list_model_type, NULL);
171 model->obj = obj;
172 model->model = info;
173 model->var = var;
174 model->columntypes = calloc(sizeof(GType), 2 * info->columns);
175 int ncol = 0;
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;
180 ncol++;
181 model->columntypes[ncol] = G_TYPE_STRING;
182 } else {
183 model->columntypes[ncol] = ui_gtk_type(info->types[i]);
184 }
185 ncol++;
186 }
187 model->numcolumns = ncol;
188 return model;
189 }
190
191 void ui_list_model_dispose(GObject *obj) {
192
193 }
194
195 void ui_list_model_finalize(GObject *obj) {
196
197 }
198
199
200 GtkTreeModelFlags ui_list_model_get_flags(GtkTreeModel *tree_model) {
201 return (GTK_TREE_MODEL_LIST_ONLY | GTK_TREE_MODEL_ITERS_PERSIST);
202 }
203
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;
208 }
209
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];
215 }
216
217 gboolean ui_list_model_get_iter(
218 GtkTreeModel *tree_model,
219 GtkTreeIter *iter,
220 GtkTreePath *path)
221 {
222 g_assert(IS_UI_LIST_MODEL(tree_model));
223 UiListModel *model = UI_LIST_MODEL(tree_model);
224 UiList *list = model->var->value;
225
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);
230
231 // get row
232 gint *indices = gtk_tree_path_get_indices(path);
233 gint row = indices[0];
234
235 // check row
236 if(row == 0) {
237 // we don't need to count if the first element is requested
238 if(list->first(list) == NULL) {
239 return FALSE;
240 }
241 } else if(row >= list->count(list)) {
242 return FALSE;
243 }
244
245 // the UiList has an integrated iterator
246 // we only get a value to adjust it
247 void *val = NULL;
248 if(row == 0) {
249 val = list->first(list);
250 } else {
251 val = list->get(list, row);
252 }
253
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;
258
259 return val ? TRUE : FALSE;
260 }
261
262 GtkTreePath* ui_list_model_get_path(
263 GtkTreeModel *tree_model,
264 GtkTreeIter *iter)
265 {
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);
269
270 UiListModel *model = UI_LIST_MODEL(tree_model);
271
272 GtkTreePath *path = gtk_tree_path_new();
273 gtk_tree_path_append_index(path, (int)(intptr_t)iter->user_data2); // list->index
274
275 return path;
276 }
277
278 void ui_list_model_get_value(
279 GtkTreeModel *tree_model,
280 GtkTreeIter *iter,
281 gint column,
282 GValue *value)
283 {
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);
287
288 UiListModel *model = UI_LIST_MODEL(tree_model);
289 UiList *list = model->var->value;
290
291 g_return_if_fail(column < model->numcolumns);
292
293 // TODO: return correct value from column
294
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) {
302 UiImage *img = data;
303 ui_model_set_value(model->columntypes[column], img->pixbuf, value);
304 } else {
305 ui_model_set_value(model->columntypes[column], data, value);
306 }
307 } else {
308 value->g_type = G_TYPE_INVALID;
309 }
310 }
311
312 gboolean ui_list_model_iter_next(
313 GtkTreeModel *tree_model,
314 GtkTreeIter *iter)
315 {
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);
319
320 if(!iter->user_data) {
321 return FALSE;
322 }
323
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;
331 index++;
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;
336 }
337
338 gboolean ui_list_model_iter_children(
339 GtkTreeModel *tree_model,
340 GtkTreeIter *iter,
341 GtkTreeIter *parent)
342 {
343 g_return_val_if_fail(IS_UI_LIST_MODEL(tree_model), FALSE);
344
345 UiListModel *model = UI_LIST_MODEL(tree_model);
346 UiList *list = model->var->value;
347
348 if(parent) {
349 return FALSE;
350 }
351
352 /*
353 * a list element has no children
354 * we set the iter to the first element
355 */
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;
361
362 return val ? TRUE : FALSE;
363 }
364
365 gboolean ui_list_model_iter_has_child(
366 GtkTreeModel *tree_model,
367 GtkTreeIter *iter)
368 {
369 return FALSE;
370 }
371
372 gint ui_list_model_iter_n_children(
373 GtkTreeModel *tree_model,
374 GtkTreeIter *iter)
375 {
376 g_assert(IS_UI_LIST_MODEL(tree_model));
377
378 if(!iter) {
379 // return number of rows
380 UiListModel *model = UI_LIST_MODEL(tree_model);
381 UiList *list = model->var->value;
382 return list->count(list);
383 }
384
385 return 0;
386 }
387
388 gboolean ui_list_model_iter_nth_child(
389 GtkTreeModel *tree_model,
390 GtkTreeIter *iter,
391 GtkTreeIter *parent,
392 gint n)
393 {
394 g_return_val_if_fail(IS_UI_LIST_MODEL(tree_model), FALSE);
395
396 if(parent) {
397 return FALSE;
398 }
399
400 UiListModel *model = UI_LIST_MODEL(tree_model);
401 UiList *list = model->var->value;
402
403 // check n
404 if(n == 0) {
405 // we don't need to count if the first element is requested
406 if(list->first(list) == NULL) {
407 return FALSE;
408 }
409 } else if(n >= list->count(list)) {
410 return FALSE;
411 }
412
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;
418
419 return val ? TRUE : FALSE;
420 }
421
422 gboolean ui_list_model_iter_parent(
423 GtkTreeModel *tree_model,
424 GtkTreeIter *iter,
425 GtkTreeIter *child)
426 {
427 return FALSE;
428 }
429
430 // ****** dnd ******
431
432 gboolean ui_list_model_drag_data_received(
433 GtkTreeDragDest *drag_dest,
434 GtkTreePath *dest_path,
435 GtkSelectionData *selection_data)
436 {
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];
442 UiEvent e;
443 e.obj = model->obj;
444 e.window = e.obj->window;
445 e.document = e.obj->ctx->document;
446 e.eventdata = NULL;
447 e.intval = 0;
448 UiSelection s;
449 s.data = selection_data;
450 model->model->drop(&e, &s, model->var->value, row);
451 }
452 return TRUE;
453 }
454
455 gboolean ui_list_model_row_drop_possible(
456 GtkTreeDragDest *drag_dest,
457 GtkTreePath *dest_path,
458 GtkSelectionData *selection_data)
459 {
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];
465 UiEvent e;
466 e.obj = model->obj;
467 e.window = e.obj->window;
468 e.document = e.obj->ctx->document;
469 e.eventdata = NULL;
470 e.intval = 0;
471 UiSelection s;
472 s.data = selection_data;
473 return model->model->candrop(&e, &s, model->var->value, row);
474 }
475 return TRUE;
476 }
477
478 gboolean ui_list_model_row_draggable(
479 GtkTreeDragSource *drag_source,
480 GtkTreePath *path)
481 {
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];
487 UiEvent e;
488 e.obj = model->obj;
489 e.window = e.obj->window;
490 e.document = e.obj->ctx->document;
491 e.eventdata = NULL;
492 e.intval = 0;
493 return model->model->candrag(&e, model->var->value, row);
494 }
495 return TRUE;
496 }
497
498 gboolean ui_list_model_drag_data_get(
499 GtkTreeDragSource *drag_source,
500 GtkTreePath *path,
501 GtkSelectionData *selection_data)
502 {
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];
508 UiEvent e;
509 e.obj = model->obj;
510 e.window = e.obj->window;
511 e.document = e.obj->ctx->document;
512 e.eventdata = NULL;
513 e.intval = 0;
514 UiSelection s;
515 s.data = selection_data;
516 model->model->data_get(&e, &s, model->var->value, row);
517 }
518 return TRUE;
519 }
520
521 gboolean ui_list_model_drag_data_delete(
522 GtkTreeDragSource *drag_source,
523 GtkTreePath *path)
524 {
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];
530 UiEvent e;
531 e.obj = model->obj;
532 e.window = e.obj->window;
533 e.document = e.obj->ctx->document;
534 e.eventdata = NULL;
535 e.intval = 0;
536 model->model->data_delete(&e, model->var->value, row);
537 }
538 return TRUE;
539 }

mercurial