Sun, 19 Nov 2017 09:00:16 +0100
adds simple drag and drop support (GTK)
/* * 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 <stdio.h> #include <stdlib.h> #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->finalize = ...; // TODO } 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) { 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) { 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;i<info->columns;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; } 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; }