--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ui/gtk/model.c Sun May 11 10:41:29 2014 +0200 @@ -0,0 +1,331 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2014 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" + +#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 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; + +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); +} + +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_init(UiListModel *instance, GObjectClass *cl) { + instance->columntypes = NULL; + instance->list = NULL; + instance->numcolumns = 0; + instance->stamp = g_random_int(); +} + +UiListModel* ui_list_model_new(UiListPtr *list, ui_model_getvalue_f getvalue) { + UiListModel *model = g_object_new(list_model_type, NULL); + model->list = list; + model->getvalue = getvalue; + model->columntypes = malloc(sizeof(GType)); + model->numcolumns = 1; + model->columntypes[0] = G_TYPE_STRING; + 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->list->list; + + // 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); + UiList *list = model->list->list; + + 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->list->list; + + 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->getvalue) { + char *str = model->getvalue(iter->user_data3, column); + g_value_set_string(value, str); + } else { + g_value_set_string(value, iter->user_data3); + } +} + +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); + + UiListModel *model = UI_LIST_MODEL(tree_model); + UiList *list = model->list->list; + 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->list->list; + + 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->list->list; + 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->list->list; + + // 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; +}