customize treeview sort

本文介绍了一个使用GTK+库实现的自定义GtkTreeModel模型。该模型通过定义CPerson结构来存储姓名和年龄数据,并提供了添加记录的方法。此外,还实现了模型的排序功能,允许按姓名或年龄对记录进行排序。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

 
person.h

#ifndef _MODEL_H
#define _MODEL_H


#include 
#include 
#include 

#define PERSON_TYPE   (  person_get_type() )
#define PERSON(obj)     ( G_TYPE_CHECK_INSTANCE_CAST((obj), PERSON_TYPE, CPerson) )
#define PERSON_CLASS(klass)  ( G_TYPE_CHECK_CLASS_CAST((klass), PERSON_TYPE, CPersonClass ) )
#define IS_PERSON(obj)      ( G_TYPE_CHECK_INSTANCE_TYPE((obj), PERSON_TYPE) )
#define IS_PERSONCALSS(klass)    ( G_TYPE_CHECK_CLASS_TYPE((klass), PERSON_TYPE) )
#define PERSON_GET_CLASS(obj)   ( G_TYPE_INSTANCE_GET_CLASS(), PERSON_TYPE, CPersonClass )

enum
{
    PERSON_COL_RECORD = 0,
    PERSON_COL_NAME,
    PERSON_COL_AGE,
    PERSON_COL_ALL,
};

typedef struct _PersonNode PersonNode;
typedef struct _CPerson CPerson;
typedef struct _CPersonClass CPersonClass;
struct _PersonNode
{
    gchar* name;
    gint age;
};

struct _CPerson
{
    GObject parent;
    
    gint row_n;
    GList* row;

    gint col_n;
    GType col_type[PERSON_COL_ALL];

    GtkSortType type;
    gint sort_id;
    
    gint stamp;
};
struct _CPersonClass
{
    GObjectClass parent_class;
    
};


/* all of internal interface */
GType person_get_type(void);
/*{@ property definition for person 
  */
#define  PERSON_PROPERTY_INDEX_NAME 1
/*@} end definition 
  */
  
/* print function name*/
#define PERSON_PRINT_SELF() g_printf("%s/n",__FUNCTION__);

/* user interface */
CPerson* person_new();
void person_append_record( CPerson* person, gchar* name, gint age );
void person_preappend_record( CPerson* person, gchar* name, gint age );

/*
  *  sort 
  */

enum{

    SORT_PERSON_NONE,
    SORT_PERSON_NAME,
    SORT_PERSON_AGE,
    
};


#endif/* person.h*/

person.c
#include "model.h"
#include 
#include 
#include 


static void person_init( CPerson* person)
{
    PERSON_PRINT_SELF();
    person->col_n = PERSON_COL_ALL;
    person->col_type[PERSON_COL_RECORD] = G_TYPE_POINTER;
    person->col_type[PERSON_COL_NAME] = G_TYPE_STRING;
    person->col_type[PERSON_COL_AGE] = G_TYPE_INT;
    person->row = NULL;
    person->row_n = 0;
    person->sort_id = 0;
    person->type = 0;
    person->stamp = g_random_int();
}

static void class_init(CPersonClass* klass)
{
    GObjectClass* obj_class = G_OBJECT_CLASS(klass); 
}

static GtkTreeModelFlags _get_flags(GtkTreeModel* tree_model)
{
    g_return_val_if_fail( IS_PERSON(tree_model),  (GtkTreeModelFlags)0 );
    return GTK_TREE_MODEL_ITERS_PERSIST | GTK_TREE_MODEL_LIST_ONLY;
}

static gint _get_n_columns( GtkTreeModel* tree_model)
{
       g_return_val_if_fail( IS_PERSON(tree_model), 0);
       g_print("%s num=%d/n", __FUNCTION__, PERSON(tree_model)->col_n);
       return PERSON(tree_model)->col_n;
}

static GType _get_column_type(GtkTreeModel* tree_model, gint index)
{
    g_return_val_if_fail( IS_PERSON(tree_model), 0);
    g_return_val_if_fail( index < PERSON(tree_model)->col_n && index >=0, 0);
    g_print("%s %dth type=%d/n", __FUNCTION__,index, PERSON(tree_model)->col_type[index]);
    return PERSON(tree_model)->col_type[index];
}

gboolean _get_iter(GtkTreeModel* tree_model, GtkTreeIter* iter, GtkTreePath* path)
{
    CPerson* pm =0;
    gint * indice = 0, depth = 0;
    gint n = 0;
    PersonNode* node;

    g_assert(IS_PERSON(tree_model));
    g_assert(path!=NULL);
    pm = PERSON(tree_model);
    
    indice = gtk_tree_path_get_indices(path);
    depth = gtk_tree_path_get_depth(path);
    g_assert(depth==1);

    n = indice[0];
    if( n >= PERSON(tree_model)->row_n || n < 0)
        return FALSE;

    node = (PersonNode*)g_list_nth_data(PERSON(tree_model)->row, n);
    g_assert(node);
   
    iter->stamp = pm->stamp;
    iter->user_data = node;
    iter->user_data2 = NULL;
    iter->user_data3 = NULL;
   
    return TRUE;
    
    
}

static GtkTreePath* _get_path(GtkTreeModel* tree_model, GtkTreeIter* iter)
{
    GtkTreePath* path = 0;
    PersonNode* node = 0;
    CPerson* pm = 0;

    
    g_return_val_if_fail( IS_PERSON(tree_model), NULL);
    g_return_val_if_fail( iter, NULL);
    g_return_val_if_fail( iter->user_data, NULL);

    pm = PERSON(tree_model);
    node = (PersonNode*)iter->user_data;

    path = gtk_tree_path_new();
    gtk_tree_path_append_index(path, g_list_index(pm->row, node) );
    return path;
    
}

static void _get_value(GtkTreeModel* model, GtkTreeIter* iter, gint column, GValue* value)
{
        CPerson* pm;
        PersonNode* node;
        g_return_if_fail( IS_PERSON(model));
        g_return_if_fail( iter);
        g_return_if_fail(column< PERSON(model)->col_n && column >=0);

        g_value_init(value, PERSON(model)->col_type[column]);

        node = (PersonNode*) iter->user_data;
        switch(column)
            {
            case PERSON_COL_NAME:
                g_value_set_string(value, node->name);
                break;
            case PERSON_COL_AGE:
                g_value_set_int(value, node->age);
                break;
             default:
                break;
        }
        PERSON_PRINT_SELF();

}

static gboolean _iter_next(GtkTreeModel* model,  GtkTreeIter* iter)
{
    CPerson* pm;
    PersonNode* node, *next;
    
    g_return_val_if_fail( IS_PERSON(model), FALSE);
    g_return_val_if_fail( iter&&iter->user_data, FALSE);

    pm = PERSON(model);
    node = (PersonNode*) iter->user_data;
  
    next = (PersonNode*)g_list_nth_data(pm->row, g_list_index(pm->row, node)+1);    
    g_print("free iter %d/n", g_list_index(pm->row, node));
    if(!next)
        return FALSE;
    iter->stamp = pm->stamp;
    iter->user_data = next;
    
    return TRUE; 
    
}

static gboolean _iter_children(GtkTreeModel* model,  GtkTreeIter* iter, GtkTreeIter* parent)
{
    CPerson* pm;

    g_return_val_if_fail( IS_PERSON(model), FALSE);
    g_return_val_if_fail( !parent, FALSE);

    pm = PERSON(model);

    if( pm->row_n == 0)
        return FALSE;

    iter->stamp = pm->stamp;
    iter->user_data = g_list_nth_data(pm->row, 0);
    PERSON_PRINT_SELF();
    return TRUE; 
    
}
static gboolean _iter_has_child(GtkTreeModel* model,  GtkTreeIter* iter)
{
    PERSON_PRINT_SELF();
    return FALSE;   
}
static gboolean _iter_n_child(GtkTreeModel* model,  GtkTreeIter* iter)
{
    CPerson* pm;
    PERSON_PRINT_SELF();
    g_return_val_if_fail( IS_PERSON(model), FALSE);
    g_return_val_if_fail( iter==NULL || iter->user_data!=NULL, FALSE);
    pm = PERSON(model);
    if(!iter)
        return pm->row_n;    
    
    return FALSE; 
}
static gboolean _iter_nth_child(GtkTreeModel* model,  GtkTreeIter* iter, GtkTreeIter* parent, gint n)
{
    CPerson* pm;
    PersonNode* node = 0;
    PERSON_PRINT_SELF();
    g_return_val_if_fail( IS_PERSON(model), FALSE);
    g_return_val_if_fail(!parent, FALSE);
    
    pm = PERSON(model);
    
    if( n >= pm->row_n )
        return FALSE;

    g_print("%d/n", n);
    node = g_list_nth_data(pm->row, n);
    
    iter->stamp = pm->stamp;
    iter->user_data = node;
    return TRUE; 
}
static gboolean _iter_parent(GtkTreeModel* model,  GtkTreeIter* iter, GtkTreeIter* child)
{
    return FALSE;
}


static void interface_init(GtkTreeModelIface* iface)
{
    iface->get_flags = _get_flags;
    iface->get_n_columns = _get_n_columns;
    iface->get_column_type = _get_column_type;
    iface->get_iter = _get_iter;
    iface->get_path= _get_path;
    iface->get_value = _get_value;
    iface->iter_next = _iter_next;
    iface->iter_children = _iter_children;
    iface->iter_has_child = _iter_has_child;
    iface->iter_n_children = _iter_n_child;
    iface->iter_nth_child = _iter_nth_child;
    iface->iter_parent = _iter_parent;
    iface->ref_node = NULL;
    iface->unref_node = NULL;
    PERSON_PRINT_SELF();
}
static void sort_interface_init( GtkTreeSortableIface* iface);
GType person_get_type(void)
{
    static GType person_type = 0;
    if(!person_type){
        GTypeInfo info = { sizeof(CPersonClass),
                    /* base init&finalize*/
                    NULL,
                    NULL,
                    /*Class init & finalize*/
                    (GClassInitFunc)class_init,
                    NULL,
                    NULL,
                    sizeof(CPerson),
                    0,
                    (GInstanceInitFunc)person_init 
            };
        person_type = g_type_register_static( G_TYPE_OBJECT, "person", &info, 0);
    
        if(1)
        {
            static const GInterfaceInfo iface_info = { (GInterfaceInitFunc)interface_init, NULL, NULL};
            g_type_add_interface_static( person_type, GTK_TYPE_TREE_MODEL, &iface_info); 
        }
        if(1)
        {
            static const GInterfaceInfo sort_iface_info = { (GInterfaceInitFunc)sort_interface_init, NULL, NULL};
            g_type_add_interface_static( person_type, GTK_TYPE_TREE_SORTABLE, &sort_iface_info); 
        }
    }
    return person_type;
}


/* sort interface */
static void person_model_resort(CPerson* model);
static void _set_sort_column_id( GtkTreeSortable* sortable, gint sort_column_id, GtkSortType order)
{
    CPerson* item = 0;
    PERSON_PRINT_SELF()
    g_return_if_fail(sortable);
    g_return_if_fail(IS_PERSON(sortable));

    item = PERSON(sortable);
    if( item->sort_id==sort_column_id && item->type==order)
        return;

    item->sort_id = sort_column_id;
    item->type = order;
    person_model_resort(PERSON(sortable));
    gtk_tree_sortable_sort_column_changed(sortable);
    
}
static gboolean _get_sort_column_id( GtkTreeSortable* sortable, gint* sort_column_id, GtkSortType* order)
{
    CPerson* item = 0;
    PERSON_PRINT_SELF()
    g_return_val_if_fail(sortable, FALSE);
    g_return_val_if_fail(IS_PERSON(sortable), FALSE);

    item = PERSON(sortable);
    if(sort_column_id)
        *sort_column_id = item->sort_id;
    if(order)
        *order = item->type;
    return TRUE;
}

static void _set_sort_func(GtkTreeSortable* sortable, gint sort_column_id, GtkTreeIterCompareFunc func, 
                                    gpointer data, GtkDestroyNotify destroy )
{
   PERSON_PRINT_SELF()
    return;
}
static void _set_default_sort_func(GtkTreeSortable* sortable,  GtkTreeIterCompareFunc func, 
                                    gpointer data, GtkDestroyNotify destroy )
{
    PERSON_PRINT_SELF()
    return;
}

static gboolean _has_default_sort_func(GtkTreeSortable* sortable)
{
    PERSON_PRINT_SELF()
    return FALSE;
}

static void sort_interface_init( GtkTreeSortableIface* iface)
{
    PERSON_PRINT_SELF();
    iface->get_sort_column_id = _get_sort_column_id;
    iface->set_sort_column_id = _set_sort_column_id;
    iface->set_sort_func = _set_sort_func; 
    iface->set_default_sort_func = _set_default_sort_func; 
    iface->has_default_sort_func = _has_default_sort_func; 
}


CPerson* person_new()
{
    CPerson* person = 0;
    //if used the gtype system, we'd call this API for init. 
    //g_type_init();
    person = g_object_new(PERSON_TYPE, NULL);
    return person;
}

void person_append_record( CPerson* person, gchar* name, gint age )
{
    GtkTreeModel* model;
    GtkTreePath* path;
    GtkTreeIter iter;
    PersonNode *new1;

    g_return_if_fail( person);
    g_return_if_fail(name);
    

    new1= g_new0(PersonNode, 1);
    new1->name = g_strdup(name);
    new1->age = age;

    person->row = g_list_append(person->row, new1);
    person->row_n++;

    path = gtk_tree_path_new();
    gtk_tree_path_append_index(path, g_list_index(person->row, new1) );
    _get_iter(GTK_TREE_MODEL(person), &iter, path);
    
    gtk_tree_model_row_inserted(GTK_TREE_MODEL(person), path, &iter);
    gtk_tree_path_free(path);
}

void person_preappend_record( CPerson* person, gchar* name, gint age )
{
    GtkTreeModel* model;
    GtkTreePath* path;
    GtkTreeIter iter;
    PersonNode *new1;
    
    g_return_if_fail( person);
    g_return_if_fail(name);
    
    
    person->row_n++;       
    
    new1= g_new0(PersonNode, 1);
    new1->name = g_strdup(name);
    new1->age = age;
    person->row = g_list_prepend(person->row, new1);
    
    path = gtk_tree_path_new();
    gtk_tree_path_append_index(path, 0);
    _get_iter(GTK_TREE_MODEL(person), &iter, path);
    
    gtk_tree_model_row_inserted(GTK_TREE_MODEL(person), path, &iter);
    gtk_tree_path_free(path);
}


static gint person_record_compare(gint sort_id, PersonNode* a, PersonNode* b)
{
    switch(sort_id)
    {
        case SORT_PERSON_NONE:
            return 0;
        case SORT_PERSON_AGE:
            if(a->age == b->age)
                return 0;
            if(a->age > b->age)
                return 1;
            return -1;
        case SORT_PERSON_NAME:
            return g_utf8_collate(a->name, b->name);
        default:
            break;
    }
    return 0;
}

static gint person_qsort_compare_func(gpointer a, gpointer b, CPerson* list)
{
    gint ret = 0;
    
    g_assert( a&&b&&list);

    ret = person_record_compare(list->sort_id, (PersonNode*)a, (PersonNode*)b);

    if(ret!=0 && list->type==GTK_SORT_DESCENDING)
        ret = ret>0?-1:1;

    return ret;
    
}

static void person_model_resort(CPerson* model)
{
    GList* list_addr = 0, *temp = 0;
    gint* new = 0, i = 0;
    GtkTreePath *path = 0;
    GtkTreeIter iter ;
    g_return_if_fail(model);
    g_return_if_fail(IS_PERSON(model));
    PERSON_PRINT_SELF()
    if(model->sort_id==0)
        return;
    if(model->row_n==0)
        return;
   
    list_addr = g_list_copy(model->row); 
    new = g_new0(gint, g_list_length(model->row));
    model->row = g_list_sort_with_data(model->row, (GCompareDataFunc)person_qsort_compare_func, model);
    
    temp = model->row;
    while(list_addr)   
    {
         new[i++] = g_list_index(model->row, list_addr->data);
         list_addr = g_list_next(list_addr);
    }
    g_list_free(list_addr);

    gint k = 0;
   
    path = gtk_tree_path_new();
    gtk_tree_model_rows_reordered(GTK_TREE_MODEL(model), path, NULL, new);
    gtk_tree_path_free(path);

    g_free(new);
    
}

main.c
#include 
#include 
#include 
#include "callback.h"
#include "model.h"
struct _GWindow
{
     GtkWidget* window;
     GtkWidget* main_image;
     GtkWidget* sub_image;
     GtkWidget* button_start;
     GtkWidget* button_stop;
     GtkWidget* tree;
};
typedef struct _GWindow GWindow;

void lookup_widget(GladeXML* xml, GWindow* all)
{
    all->window = glade_xml_get_widget(xml, "g_window");
    g_assert(all->window!=NULL);
    all->main_image = glade_xml_get_widget(xml, "g_main_image");
    g_assert(all->main_image!=NULL);
    all->sub_image= glade_xml_get_widget(xml, "g_sub_image");
    g_assert(all->sub_image!=NULL);
    all->button_start= glade_xml_get_widget(xml, "button_start");
    g_assert(all->button_start!=NULL);
    all->button_stop= glade_xml_get_widget(xml, "button_stop");
    g_assert(all->button_stop!=NULL);
    all->tree= glade_xml_get_widget(xml, "treeview");
    g_assert(all->tree!=NULL);
    
}

gboolean timeout(gpointer data)
{
    gtk_widget_queue_draw(GTK_WIDGET(((GWindow*)(data))->main_image));
}

void append_data(CPerson * person)
{
    person_append_record(person, "first", 12 );
    person_append_record(person, "second", 14 );
}

void set_mode(GtkWidget* view)
{
    CPerson* person;
    GtkTreeViewColumn* col;
    GtkCellRenderer* render;
    person = person_new();
    append_data(person);

    gtk_tree_view_set_model(GTK_TREE_VIEW(view), GTK_TREE_MODEL(person));
    
    render = gtk_cell_renderer_text_new();
    col = gtk_tree_view_column_new();
    gtk_tree_view_column_pack_start (col, render, TRUE);
    gtk_tree_view_column_add_attribute (col, render, "text", PERSON_COL_NAME);
    gtk_tree_view_column_set_title (col, "Name");
    gtk_tree_view_append_column(GTK_TREE_VIEW(view),col);
    render = gtk_cell_renderer_text_new();
    col = gtk_tree_view_column_new();
    gtk_tree_view_column_pack_start (col, render, TRUE);
    gtk_tree_view_column_add_attribute (col, render, "text", PERSON_COL_AGE);
    gtk_tree_view_column_set_title (col, "age");
    gtk_tree_view_append_column(GTK_TREE_VIEW(view),col);   
    gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(person), 2, 0);

}
gboolean listview_add(gpointer data)
{
    GtkWidget* tree = GTK_WIDGET(data);
    static gint i = 100;
    static gint sort[] = {0, 1};
    person_preappend_record( PERSON( gtk_tree_view_get_model(tree)), "add", i++);
    gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(gtk_tree_view_get_model(tree)), 1, 0);
    
    return TRUE;
}

gint main(gint argc, gchar* argv[])
{
    
    GladeXML* xml=0;
    GWindow window;
    gint x, y;
    gtk_init(&argc, &argv);

    xml = glade_xml_new("rsc/g.glade",NULL,NULL);
    g_assert(xml!=NULL);
    glade_xml_signal_autoconnect(xml);
    lookup_widget(xml, &window);
    set_mode(window.tree);
 
    g_signal_connect (G_OBJECT (window.window), "delete_event", GTK_SIGNAL_FUNC (gtk_main_quit), NULL);  

    g_timeout_add(3000, listview_add, window.tree);
        
    gtk_main();
}

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值