add existing code (build system, libs, initial mizucp code)
[mizunara.git] / ui / gtk / model.c
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 }