diff -r 000000000000 -r 804d8803eade ui/gtk/model.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ui/gtk/model.c Wed Dec 09 11:32:01 2020 +0100 @@ -0,0 +1,539 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2017 Olaf Wintermann. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +#include "model.h" +#include "image.h" +#include "toolkit.h" + +#define IS_UI_LIST_MODEL(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj), list_model_type)) +#define UI_LIST_MODEL(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj), list_model_type, UiListModel)) + +static void list_model_class_init(GObjectClass *cl, gpointer data); +static void list_model_interface_init(GtkTreeModelIface *i, gpointer data); +static void list_model_init(UiListModel *instance, GObjectClass *cl); + +static void list_model_dnd_dest_interface_init(GtkTreeDragDestIface *i, gpointer data); +static void list_model_dnd_src_interface_init(GtkTreeDragSourceIface *i, gpointer data); + +static GObjectClass list_model_class; +static const GTypeInfo list_model_info = { + sizeof(GObjectClass), + NULL, + NULL, + (GClassInitFunc)list_model_class_init, + NULL, + NULL, + sizeof(UiListModel), + 0, + (GInstanceInitFunc)list_model_init +}; +static const GInterfaceInfo list_model_interface_info = { + (GInterfaceInitFunc)list_model_interface_init, + NULL, + NULL +}; +static GType list_model_type; + +static const GInterfaceInfo list_model_dnd_dest_interface_info = { + (GInterfaceInitFunc)list_model_dnd_dest_interface_init, + NULL, + NULL +}; +static const GInterfaceInfo list_model_dnd_src_interface_info = { + (GInterfaceInitFunc)list_model_dnd_src_interface_init, + NULL, + NULL +}; + +void ui_list_init() { + list_model_type = g_type_register_static( + G_TYPE_OBJECT, + "UiListModel", + &list_model_info, + (GTypeFlags)0); + g_type_add_interface_static( + list_model_type, + GTK_TYPE_TREE_MODEL, + &list_model_interface_info); + g_type_add_interface_static( + list_model_type, + GTK_TYPE_TREE_DRAG_DEST, + &list_model_dnd_dest_interface_info); + g_type_add_interface_static( + list_model_type, + GTK_TYPE_TREE_DRAG_SOURCE, + &list_model_dnd_src_interface_info); +} + +static void list_model_class_init(GObjectClass *cl, gpointer data) { + cl->dispose = ui_list_model_dispose; + cl->finalize = ui_list_model_finalize; + +} + +static void list_model_interface_init(GtkTreeModelIface *i, gpointer data) { + i->get_flags = ui_list_model_get_flags; + i->get_n_columns = ui_list_model_get_n_columns; + i->get_column_type = ui_list_model_get_column_type; + i->get_iter = ui_list_model_get_iter; + i->get_path = ui_list_model_get_path; + i->get_value = ui_list_model_get_value; + i->iter_next = ui_list_model_iter_next; + i->iter_children = ui_list_model_iter_children; + i->iter_has_child = ui_list_model_iter_has_child; + i->iter_n_children = ui_list_model_iter_n_children; + i->iter_nth_child = ui_list_model_iter_nth_child; + i->iter_parent = ui_list_model_iter_parent; +} + +static void list_model_dnd_dest_interface_init(GtkTreeDragDestIface *i, gpointer data) { + i->drag_data_received = ui_list_model_drag_data_received; + i->row_drop_possible = ui_list_model_row_drop_possible; +} + +static void list_model_dnd_src_interface_init(GtkTreeDragSourceIface *i, gpointer data) { + i->drag_data_delete = ui_list_model_drag_data_delete; + i->drag_data_get = ui_list_model_drag_data_get; + i->row_draggable = ui_list_model_row_draggable; +} + +static void list_model_init(UiListModel *instance, GObjectClass *cl) { + instance->columntypes = NULL; + instance->var = NULL; + instance->numcolumns = 0; + instance->stamp = g_random_int(); +} + +static GType ui_gtk_type(UiModelType type) { + switch(type) { + default: break; + case UI_STRING: return G_TYPE_STRING; + case UI_INTEGER: return G_TYPE_INT; + } + return G_TYPE_INVALID; +} + +static void ui_model_set_value(GType type, void *data, GValue *value) { + switch(type) { + default: break; + case G_TYPE_OBJECT: { + value->g_type = G_TYPE_OBJECT; + g_value_set_object(value, data); + return; + } + case G_TYPE_STRING: { + value->g_type = G_TYPE_STRING; + g_value_set_string(value, data); + return; + } + case G_TYPE_INT: { + value->g_type = G_TYPE_INT; + int *i = data; + g_value_set_int(value, *i); + return; + } + } + value->g_type = G_TYPE_INVALID; +} + +UiListModel* ui_list_model_new(UiObject *obj, UiVar *var, UiModel *info) { + UiListModel *model = g_object_new(list_model_type, NULL); + model->obj = obj; + model->model = info; + model->var = var; + model->columntypes = calloc(sizeof(GType), 2 * info->columns); + int ncol = 0; + for(int i=0;icolumns;i++) { + UiModelType type = info->types[i]; + if(type == UI_ICON_TEXT) { + model->columntypes[ncol] = G_TYPE_OBJECT; + ncol++; + model->columntypes[ncol] = G_TYPE_STRING; + } else { + model->columntypes[ncol] = ui_gtk_type(info->types[i]); + } + ncol++; + } + model->numcolumns = ncol; + return model; +} + +void ui_list_model_dispose(GObject *obj) { + +} + +void ui_list_model_finalize(GObject *obj) { + +} + + +GtkTreeModelFlags ui_list_model_get_flags(GtkTreeModel *tree_model) { + return (GTK_TREE_MODEL_LIST_ONLY | GTK_TREE_MODEL_ITERS_PERSIST); +} + +gint ui_list_model_get_n_columns(GtkTreeModel *tree_model) { + g_return_val_if_fail(IS_UI_LIST_MODEL(tree_model), 0); + UiListModel *model = UI_LIST_MODEL(tree_model); + return model->numcolumns; +} + +GType ui_list_model_get_column_type(GtkTreeModel *tree_model, gint index) { + g_return_val_if_fail(IS_UI_LIST_MODEL(tree_model), G_TYPE_INVALID); + UiListModel *model = UI_LIST_MODEL(tree_model); + g_return_val_if_fail(index < model->numcolumns, G_TYPE_INVALID); + return model->columntypes[index]; +} + +gboolean ui_list_model_get_iter( + GtkTreeModel *tree_model, + GtkTreeIter *iter, + GtkTreePath *path) +{ + g_assert(IS_UI_LIST_MODEL(tree_model)); + UiListModel *model = UI_LIST_MODEL(tree_model); + UiList *list = model->var->value; + + // check the depth of the path + // a list must have a depth of 1 + gint depth = gtk_tree_path_get_depth(path); + g_assert(depth == 1); + + // get row + gint *indices = gtk_tree_path_get_indices(path); + gint row = indices[0]; + + // check row + if(row == 0) { + // we don't need to count if the first element is requested + if(list->first(list) == NULL) { + return FALSE; + } + } else if(row >= list->count(list)) { + return FALSE; + } + + // the UiList has an integrated iterator + // we only get a value to adjust it + void *val = NULL; + if(row == 0) { + val = list->first(list); + } else { + val = list->get(list, row); + } + + iter->stamp = model->stamp; + iter->user_data = list->iter; + iter->user_data2 = (gpointer)(intptr_t)row; // list->index + iter->user_data3 = val; + + return val ? TRUE : FALSE; +} + +GtkTreePath* ui_list_model_get_path( + GtkTreeModel *tree_model, + GtkTreeIter *iter) +{ + g_return_val_if_fail(IS_UI_LIST_MODEL(tree_model), NULL); + g_return_val_if_fail(iter != NULL, NULL); + g_return_val_if_fail(iter->user_data != NULL, NULL); + + UiListModel *model = UI_LIST_MODEL(tree_model); + + GtkTreePath *path = gtk_tree_path_new(); + gtk_tree_path_append_index(path, (int)(intptr_t)iter->user_data2); // list->index + + return path; +} + +void ui_list_model_get_value( + GtkTreeModel *tree_model, + GtkTreeIter *iter, + gint column, + GValue *value) +{ + g_return_if_fail(IS_UI_LIST_MODEL(tree_model)); + g_return_if_fail(iter != NULL); + g_return_if_fail(iter->user_data != NULL); + + UiListModel *model = UI_LIST_MODEL(tree_model); + UiList *list = model->var->value; + + g_return_if_fail(column < model->numcolumns); + + // TODO: return correct value from column + + //value->g_type = G_TYPE_STRING; + list->iter = iter->user_data; + //list->index = (int)(intptr_t)iter->user_data2; + //list->current = iter->user_data3; + if(model->model->getvalue) { + void *data = model->model->getvalue(iter->user_data3, column); + if(model->columntypes[column] == G_TYPE_OBJECT) { + UiImage *img = data; + ui_model_set_value(model->columntypes[column], img->pixbuf, value); + } else { + ui_model_set_value(model->columntypes[column], data, value); + } + } else { + value->g_type = G_TYPE_INVALID; + } +} + +gboolean ui_list_model_iter_next( + GtkTreeModel *tree_model, + GtkTreeIter *iter) +{ + g_return_val_if_fail(IS_UI_LIST_MODEL(tree_model), FALSE); + g_return_val_if_fail(iter != NULL, FALSE); + //g_return_val_if_fail(iter->user_data != NULL, FALSE); + + if(!iter->user_data) { + return FALSE; + } + + UiListModel *model = UI_LIST_MODEL(tree_model); + UiList *list = model->var->value; + list->iter = iter->user_data; + //list->index = (int)(intptr_t)iter->user_data2; + void *val = list->next(list); + iter->user_data = list->iter; + intptr_t index = (intptr_t)iter->user_data2; + index++; + //iter->user_data2 = (gpointer)(intptr_t)list->index; + iter->user_data2 = (gpointer)index; + iter->user_data3 = val; + return val ? TRUE : FALSE; +} + +gboolean ui_list_model_iter_children( + GtkTreeModel *tree_model, + GtkTreeIter *iter, + GtkTreeIter *parent) +{ + g_return_val_if_fail(IS_UI_LIST_MODEL(tree_model), FALSE); + + UiListModel *model = UI_LIST_MODEL(tree_model); + UiList *list = model->var->value; + + if(parent) { + return FALSE; + } + + /* + * a list element has no children + * we set the iter to the first element + */ + void *val = list->first(list); + iter->stamp = model->stamp; + iter->user_data = list->iter; + iter->user_data2 = (gpointer)0; + iter->user_data3 = val; + + return val ? TRUE : FALSE; +} + +gboolean ui_list_model_iter_has_child( + GtkTreeModel *tree_model, + GtkTreeIter *iter) +{ + return FALSE; +} + +gint ui_list_model_iter_n_children( + GtkTreeModel *tree_model, + GtkTreeIter *iter) +{ + g_assert(IS_UI_LIST_MODEL(tree_model)); + + if(!iter) { + // return number of rows + UiListModel *model = UI_LIST_MODEL(tree_model); + UiList *list = model->var->value; + return list->count(list); + } + + return 0; +} + +gboolean ui_list_model_iter_nth_child( + GtkTreeModel *tree_model, + GtkTreeIter *iter, + GtkTreeIter *parent, + gint n) +{ + g_return_val_if_fail(IS_UI_LIST_MODEL(tree_model), FALSE); + + if(parent) { + return FALSE; + } + + UiListModel *model = UI_LIST_MODEL(tree_model); + UiList *list = model->var->value; + + // check n + if(n == 0) { + // we don't need to count if the first element is requested + if(list->first(list) == NULL) { + return FALSE; + } + } else if(n >= list->count(list)) { + return FALSE; + } + + void *val = list->get(list, n); + iter->stamp = model->stamp; + iter->user_data = list->iter; + iter->user_data2 = (gpointer)(intptr_t)n; // list->index + iter->user_data3 = val; + + return val ? TRUE : FALSE; +} + +gboolean ui_list_model_iter_parent( + GtkTreeModel *tree_model, + GtkTreeIter *iter, + GtkTreeIter *child) +{ + return FALSE; +} + +// ****** dnd ****** + +gboolean ui_list_model_drag_data_received( + GtkTreeDragDest *drag_dest, + GtkTreePath *dest_path, + GtkSelectionData *selection_data) +{ + //printf("drag received\n"); + UiListModel *model = UI_LIST_MODEL(drag_dest); + if(model->model->drop) { + gint *indices = gtk_tree_path_get_indices(dest_path); + gint row = indices[0]; + UiEvent e; + e.obj = model->obj; + e.window = e.obj->window; + e.document = e.obj->ctx->document; + e.eventdata = NULL; + e.intval = 0; + UiSelection s; + s.data = selection_data; + model->model->drop(&e, &s, model->var->value, row); + } + return TRUE; +} + +gboolean ui_list_model_row_drop_possible( + GtkTreeDragDest *drag_dest, + GtkTreePath *dest_path, + GtkSelectionData *selection_data) +{ + //printf("row_drop_possible\n"); + UiListModel *model = UI_LIST_MODEL(drag_dest); + if(model->model->candrop) { + gint *indices = gtk_tree_path_get_indices(dest_path); + gint row = indices[0]; + UiEvent e; + e.obj = model->obj; + e.window = e.obj->window; + e.document = e.obj->ctx->document; + e.eventdata = NULL; + e.intval = 0; + UiSelection s; + s.data = selection_data; + return model->model->candrop(&e, &s, model->var->value, row); + } + return TRUE; +} + +gboolean ui_list_model_row_draggable( + GtkTreeDragSource *drag_source, + GtkTreePath *path) +{ + //printf("row_draggable\n"); + UiListModel *model = UI_LIST_MODEL(drag_source); + if(model->model->candrag) { + gint *indices = gtk_tree_path_get_indices(path); + gint row = indices[0]; + UiEvent e; + e.obj = model->obj; + e.window = e.obj->window; + e.document = e.obj->ctx->document; + e.eventdata = NULL; + e.intval = 0; + return model->model->candrag(&e, model->var->value, row); + } + return TRUE; +} + +gboolean ui_list_model_drag_data_get( + GtkTreeDragSource *drag_source, + GtkTreePath *path, + GtkSelectionData *selection_data) +{ + //printf("drag_data_get\n"); + UiListModel *model = UI_LIST_MODEL(drag_source); + if(model->model->data_get) { + gint *indices = gtk_tree_path_get_indices(path); + gint row = indices[0]; + UiEvent e; + e.obj = model->obj; + e.window = e.obj->window; + e.document = e.obj->ctx->document; + e.eventdata = NULL; + e.intval = 0; + UiSelection s; + s.data = selection_data; + model->model->data_get(&e, &s, model->var->value, row); + } + return TRUE; +} + +gboolean ui_list_model_drag_data_delete( + GtkTreeDragSource *drag_source, + GtkTreePath *path) +{ + //printf("drag_data_delete\n"); + UiListModel *model = UI_LIST_MODEL(drag_source); + if(model->model->data_get) { + gint *indices = gtk_tree_path_get_indices(path); + gint row = indices[0]; + UiEvent e; + e.obj = model->obj; + e.window = e.obj->window; + e.document = e.obj->ctx->document; + e.eventdata = NULL; + e.intval = 0; + model->model->data_delete(&e, model->var->value, row); + } + return TRUE; +}