ui/gtk/model.c

Wed, 21 Jan 2015 20:38:21 +0100

author
Olaf Wintermann <olaf.wintermann@gmail.com>
date
Wed, 21 Jan 2015 20:38:21 +0100
changeset 77
bc0ed99e49c7
parent 42
29b2821d1262
child 124
80609f9675f1
permissions
-rw-r--r--

fixed memory allocation bug

/*
 * 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();
}

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(UiModelType type, void *data, GValue *value) {
    switch(type) {
        case UI_STRING: {
            value->g_type = G_TYPE_STRING;
            g_value_set_string(value, data);
            return;
        }
        case UI_INTEGER: {
            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(UiListPtr *list, UiModelInfo *info) {
    UiListModel *model = g_object_new(list_model_type, NULL);
    model->info = info;
    model->list = list;
    model->columntypes = malloc(sizeof(GType));
    model->numcolumns = info->columns;
    for(int i=0;i<info->columns;i++) {
        model->columntypes[i] = ui_gtk_type(info->types[i]);
    }
    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->info->getvalue) {
        void *data = model->info->getvalue(iter->user_data3, column);
        ui_model_set_value(model->info->types[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);
    
    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;
}

mercurial