Previous Next Contents

12. List物件

GtkList物件被设计成是个vertical container, 而在其中的物件必须是GtkListItem.

GtkList物件有其自己的视窗用来接取事件, 而其背景色一般是白色的. 由於它是由GtkContainer而来, 您也可以用GTK_CONTAINER(List)巨集来处理. 请见GtkContainer物件一章. 您应该已经熟悉GList的用法, 及其相关函数g_list_*(), 这样您才不会在此遭遇到问题.

在GtkList物件有一栏对我们来说很重要.

struct _GtkList
{
  [...]
  GList *selection;
  guint selection_mode;
  [...]
}; 

GtkList的selection栏指向一个所有items的link list, 其中记录所有被记录的项目, 若为`NULL'则selection为空的. 因此要知道目前的selection, 我们可以读取GTK_LIST()->selection一栏. 但不要修改它们, 因为它们是由内部所维护.

GtkList的selection_mode决定selection的机制, 而GTK_LIST()->selection栏的内容为:

selection_mode可以是以下其中一种:

内定为GTK_SELECTION_MULTIPLE.

12.1 信号

void GtkList::selection_changed (GtkList *LIST)

当selection区域改变的时候, 这个信号会被触发. 这会在当GtkList的子物件被select或unselect时发生.

void GtkList::select_child (GtkList *LIST, GtkWidget *CHILD)

当GtkList的子物件被select时, 这个信号会被触发. 这一般在gtk_list_select_item(), gtk_list_select_child(), 按钮被按下及有时间接触发或有子物件新增或移除时发生.

void GtkList::unselect_child (GtkList *LIST, GtkWidget *CHILD)

当GtkList的子物件被unselect时, 这个信号会被触发. 这一般在gtk_list_unselect_item(), gtk_list_unselect_child(), 按钮被按下及有时间接触发或有子物件新增或移除时发生.

12.2 函数

guint gtk_list_get_type (void)

返回`GtkList' type identifier.

GtkWidget* gtk_list_new (void)

产生新的`GtkList' object. 新的物件其返回值为`GtkWidget' object的指标. `NULL'表示失败.

void gtk_list_insert_items (GtkList *LIST, GList *ITEMS, gint POSITION)

插入list items到LIST里面, 由POSITION开始. ITEMS是双向链结串列. 每个项目要指向一个产生出来的GtkListItem.

void gtk_list_append_items (GtkList *LIST, GList *ITEMS)

就像gtk_list_insert_items()一样插入ITEMS到LIST後面.

void gtk_list_prepend_items (GtkList *LIST, GList *ITEMS)

就如gtk_list_insert_items()一样插入ITEMS到LIST前面.

void gtk_list_remove_items (GtkList *LIST, GList *ITEMS)

从LIST中移除list items. ITEMS是双向链结串列, 每个node要指向child. 设计者要自行呼叫g_list_free(ITEMS). 设计者也要自行处理掉list items.

void gtk_list_clear_items (GtkList *LIST, gint START, gint END)

从LIST中移除并销毁list items.

void gtk_list_select_item (GtkList *LIST, gint ITEM)

透过在LIST中目前的位置,触发GtkList::select_child信号给指定的list item.

void gtk_list_unselect_item (GtkList *LIST, gint ITEM)

透过在LIST中目前的位置,触发GtkList::unselect_child信号给指定的list item.

void gtk_list_select_child (GtkList *LIST, GtkWidget *CHILD)

触发GtkList::select_child信号给指定的CHILD.

void gtk_list_unselect_child (GtkList *LIST, GtkWidget *CHILD)

触发GtkList::unselect_child信号给指定的CHILD.

gint gtk_list_child_position (GtkList *LIST, GtkWidget *CHILD)

返回CHILD在LIST中的位置. `-1'为失败.

void gtk_list_set_selection_mode (GtkList *LIST, GtkSelectionMode MODE)

设定LIST到选择模式MODE, 可以是GTK_SELECTION_SINGLE, GTK_SELECTION_BROWSE, GTK_SELECTION_MULTIPLE 或 GTK_SELECTION_EXTENDED.

GtkList* GTK_LIST (gpointer OBJ)

传一个generic pointer到`GtkList*'. *Note Standard Macros::, for more info.

GtkListClass* GTK_LIST_CLASS (gpointer CLASS)

传一个generic pointer到`GtkListClass*'. *Note Standard Macros::, for more info.

gint GTK_IS_LIST (gpointer OBJ)

决定是否一个generic pointer对应到`GtkList' object. *Note Standard Macros::, for more info.

12.3 范例

以下是个范例程式, 将会列出GtkList的选择改变, 并让您用滑鼠右键"逮捕"list items.

/* compile this program with:
 * $ gcc -I/usr/local/include/ -lgtk -lgdk -lglib -lX11 -lm -Wall main.c
 */

/* include the gtk+ header files
 * include stdio.h, we need that for the printf() function
 */
#include        <gtk/gtk.h>
#include        <stdio.h>

/* this is our data identification string to store
 * data in list items
 */
const   gchar   *list_item_data_key="list_item_data";


/* prototypes for signal handler that we are going to connect
 * to the GtkList widget
 */
static  void    sigh_print_selection    (GtkWidget      *gtklist,
                                         gpointer       func_data);
static  void    sigh_button_event       (GtkWidget      *gtklist,
                                         GdkEventButton *event,
                                         GtkWidget      *frame);


/* main function to set up the user interface */

gint main (int argc, gchar *argv[])
{                                  
    GtkWidget       *separator;
    GtkWidget       *window;
    GtkWidget       *vbox;
    GtkWidget       *scrolled_window;
    GtkWidget       *frame;
    GtkWidget       *gtklist;
    GtkWidget       *button;
    GtkWidget       *list_item;
    GList           *dlist;
    guint           i;
    gchar           buffer[64];
    
    
    /* initialize gtk+ (and subsequently gdk) */

    gtk_init(&argc, &argv);
    
    
    /* create a window to put all the widgets in
     * connect gtk_main_quit() to the "destroy" event of
     * the window to handle window manager close-window-events
     */
    window=gtk_window_new(GTK_WINDOW_TOPLEVEL);
    gtk_window_set_title(GTK_WINDOW(window), "GtkList Example");
    gtk_signal_connect(GTK_OBJECT(window),
                       "destroy",
                       GTK_SIGNAL_FUNC(gtk_main_quit),
                       NULL);
    
    
    /* inside the window we need a box to arrange the widgets
     * vertically */
    vbox=gtk_vbox_new(FALSE, 5);
    gtk_container_border_width(GTK_CONTAINER(vbox), 5);
    gtk_container_add(GTK_CONTAINER(window), vbox);
    gtk_widget_show(vbox);
    
    /* this is the scolled window to put the GtkList widget inside */
    scrolled_window=gtk_scrolled_window_new(NULL, NULL);
    gtk_widget_set_usize(scrolled_window, 250, 150);
    gtk_container_add(GTK_CONTAINER(vbox), scrolled_window);
    gtk_widget_show(scrolled_window);
    
    /* create the GtkList widget
     * connect the sigh_print_selection() signal handler
     * function to the "selection_changed" signal of the GtkList
     * to print out the selected items each time the selection
     * has changed */
    gtklist=gtk_list_new();
    gtk_container_add(GTK_CONTAINER(scrolled_window), gtklist);
    gtk_widget_show(gtklist);
    gtk_signal_connect(GTK_OBJECT(gtklist),
                       "selection_changed",
                       GTK_SIGNAL_FUNC(sigh_print_selection),
                       NULL);
    
    /* we create a "Prison" to put a list item in ;)
     */
    frame=gtk_frame_new("Prison");
    gtk_widget_set_usize(frame, 200, 50);
    gtk_container_border_width(GTK_CONTAINER(frame), 5);
    gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_OUT);
    gtk_container_add(GTK_CONTAINER(vbox), frame);
    gtk_widget_show(frame);
    
    /* connect the sigh_button_event() signal handler to the GtkList
     * wich will handle the "arresting" of list items
     */
    gtk_signal_connect(GTK_OBJECT(gtklist),
                       "button_release_event",
                       GTK_SIGNAL_FUNC(sigh_button_event),
                       frame);
    
    /* create a separator
     */
    separator=gtk_hseparator_new();
    gtk_container_add(GTK_CONTAINER(vbox), separator);
    gtk_widget_show(separator);
    
    /* finaly create a button and connect it愀 "clicked" signal
     * to the destroyment of the window
     */
    button=gtk_button_new_with_label("Close");
    gtk_container_add(GTK_CONTAINER(vbox), button);
    gtk_widget_show(button);
    gtk_signal_connect_object(GTK_OBJECT(button),
                              "clicked",
                              GTK_SIGNAL_FUNC(gtk_widget_destroy),
                              GTK_OBJECT(window));
    
    
    /* now we create 5 list items, each having it愀 own
     * label and add them to the GtkList using gtk_container_add()
     * also we query the text string from the label and
     * associate it with the list_item_data_key for each list item
     */
    for (i=0; i<5; i++) {
        GtkWidget       *label;
        gchar           *string;
        
        sprintf(buffer, "ListItemContainer with Label #%d", i);
        label=gtk_label_new(buffer);
        list_item=gtk_list_item_new();
        gtk_container_add(GTK_CONTAINER(list_item), label);
        gtk_widget_show(label);
        gtk_container_add(GTK_CONTAINER(gtklist), list_item);
        gtk_widget_show(list_item);
        gtk_label_get(GTK_LABEL(label), &string);
        gtk_object_set_data(GTK_OBJECT(list_item),
                            list_item_data_key,
                            string);
    }
    /* here, we are creating another 5 labels, this time
     * we use gtk_list_item_new_with_label() for the creation
     * we can憩 query the text string from the label because
     * we don憩 have the labels pointer and therefore
     * we just associate the list_item_data_key of each
     * list item with the same text string
     * for adding of the list items we put them all into a doubly
     * linked list (GList), and then add them by a single call to
     * gtk_list_append_items()
     * because we use g_list_prepend() to put the items into the
     * doubly linked list, their order will be descending (instead
     * of ascending when using g_list_append())
     */
    dlist=NULL;
    for (; i<10; i++) {
        sprintf(buffer, "List Item with Label %d", i);
        list_item=gtk_list_item_new_with_label(buffer);
        dlist=g_list_prepend(dlist, list_item);
        gtk_widget_show(list_item);
        gtk_object_set_data(GTK_OBJECT(list_item),
                            list_item_data_key,
                            "ListItem with integrated Label");
    }
    gtk_list_append_items(GTK_LIST(gtklist), dlist);
    
    /* finaly we want to see the window, don憩 we? ;)
     */
    gtk_widget_show(window);
    
    /* fire up the main event loop of gtk
     */
    gtk_main();
    
    /* we get here after gtk_main_quit() has been called which
     * happens if the main window gets destroyed
     */
    return 0;
}

/* this is the signal handler that got connected to button
 * press/release events of the GtkList
 */
void
sigh_button_event       (GtkWidget      *gtklist,
                         GdkEventButton *event,
                         GtkWidget      *frame)
{
    /* we only do something if the third (rightmost mouse button
     * was released
     */
    if (event->type==GDK_BUTTON_RELEASE &&
        event->button==3) {
        GList           *dlist, *free_list;
        GtkWidget       *new_prisoner;
        
        /* fetch the currently selected list item which
         * will be our next prisoner ;)
         */
        dlist=GTK_LIST(gtklist)->selection;
        if (dlist)
                new_prisoner=GTK_WIDGET(dlist->data);
        else
                new_prisoner=NULL;
        
        /* look for already prisoned list items, we
         * will put them back into the list
         * remember to free the doubly linked list that
         * gtk_container_children() returns
         */
        dlist=gtk_container_children(GTK_CONTAINER(frame));
        free_list=dlist;
        while (dlist) {
            GtkWidget       *list_item;
            
            list_item=dlist->data;
            
            gtk_widget_reparent(list_item, gtklist);
            
            dlist=dlist->next;
        }
        g_list_free(free_list);
        
        /* if we have a new prisoner, remove him from the
         * GtkList and put him into the frame "Prison"
         * we need to unselect the item before
         */
        if (new_prisoner) {
            GList   static_dlist;
            
            static_dlist.data=new_prisoner;
            static_dlist.next=NULL;
            static_dlist.prev=NULL;
            
            gtk_list_unselect_child(GTK_LIST(gtklist),
                                    new_prisoner);
            gtk_widget_reparent(new_prisoner, frame);
        }
    }
}

/* this is the signal handler that gets called if GtkList
 * emits the "selection_changed" signal
 */
void
sigh_print_selection    (GtkWidget      *gtklist,
                         gpointer       func_data)
{
    GList   *dlist;
    
    /* fetch the doubly linked list of selected items
     * of the GtkList, remember to treat this as read-only!
     */
    dlist=GTK_LIST(gtklist)->selection;
    
    /* if there are no selected items there is nothing more
     * to do than just telling the user so
     */
    if (!dlist) {
        g_print("Selection cleared\n");
        return;
    }
    /* ok, we got a selection and so we print it
     */
    g_print("The selection is a ");
    
    /* get the list item from the doubly linked list
     * and then query the data associated with list_item_data_key
     * we then just print it
     */
    while (dlist) {
        GtkObject       *list_item;
        gchar           *item_data_string;
        
        list_item=GTK_OBJECT(dlist->data);
        item_data_string=gtk_object_get_data(list_item,
                                             list_item_data_key);
        g_print("%s ", item_data_string);
        
        dlist=dlist->next;
    }
    g_print("\n");
}

12.4 List Item物件

GtkListItem物件是设计用来做为container的子物件, 用来提供selection/deselection的功能.

GtkListItem有自己的视窗来接收事件并有其自身的背景颜色, 一般是白色的.

因为是由GtkItem而来的, 它也可以用GTK_ITEM(ListItem)巨集. 一般GtkListItem只有一个标签, 用来记录例如一个档名. 另外还有一个很好用的函数gtk_list_item_new_with_label(). 若您不想加GtkLabel到GtkListItem, 也可以加GtkVBox或GtkArrow.

12.5 信号

GtkListItem不产生自己的新的信号, 但它继承GtkItem的信号.

12.6 函数

guint gtk_list_item_get_type (void)

返回`GtkListItem' type identifier.

GtkWidget* gtk_list_item_new (void)

产生新的`GtkListItem' object. 新物件返回一个指标给`GtkWidget'物件. `NULL'表示错误.

GtkWidget* gtk_list_item_new_with_label (gchar *LABEL)

产生新的`GtkListItem'物件, 并带一个标签. 并返回一个`GtkWidget' object. `NULL'表示错误.

void gtk_list_item_select (GtkListItem *LIST_ITEM)

这个函数基本上是将gtk_item_select (GTK_ITEM (list_item))包装起来. 它将会送GtkItem::select信号. *Note GtkItem::, for more info.

void gtk_list_item_deselect (GtkListItem *LIST_ITEM)

这个函数基本上是将gtk_item_deselect (GTK_ITEM (list_item))包装起来. 它将会送GtkItem::deselect信号. *Note GtkItem::, for more info.

GtkListItem* GTK_LIST_ITEM (gpointer OBJ)

传一个generic pointer到`GtkListItem*'. *Note Standard Macros::, for more info.

GtkListItemClass* GTK_LIST_ITEM_CLASS (gpointer CLASS)

传一个generic pointer到`GtkListItemClass*'. *Note Standard Macros::, for more info.

gint GTK_IS_LIST_ITEM (gpointer OBJ)

决定generic pointer是否对照到`GtkListItem' object. *Note Standard Macros::, for more info.

12.7 例子

Please see the GtkList example on this, which covers the usage of a GtkListItem as well.


译注: List物件这一篇本身比较不容易翻译, 因原文本身讲的并不太清楚. 此外, 其结构原本就比较繁琐. 若您在此糟遇问题, 可来信反应. 译者会想办法将其改善.
If you got stuck here, it's mostly not your problem. Don't feel frustration. The List Widget itself is pretty complicated. You may drop me a word if you need. I will try to improve it.
Previous Next Contents