다음 이전 차례

10. 리스트 widget

GtkList widget은 GtkListItem형의 widget들 위한 수직의 컨테이너처럼 작동하도록 설계되었다.

하나의 GtkList widget은 이벤트를 받을 자신만의 윈도와, 보통 white인 자신만의 background 색을 가지고 있다. GtkContainer로부터 파생되었기에 GTK_CONTAINER(List) 매크로를 이용해 다룰 수 있는데, 이것에 대해서는 GtkContainer widget에 대한 부분을 참고하기 바란다.

GtkList widget의 구조체 정의에는 훨씬 더 흥미로운 하나의 필드가 있다. 바로 이것이다.

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

GtkList의 selection 필드는 현재 선택된 모든 아이템들의 연결리스트를 가리키고 있고, 만약 선택된 게 없다면 'NULL'이다. 그래서 우리는 현재의 선택된 것을 알기위해 GTK_LIST()->selection 필드를 참조하지만, 그 내부의 필드들이 gtk_list*() 함수들로써 유지되고 있으므로 그것을 변형해서는 안된다. GtkList의 selection_mode는 GtkList 즉 GTK_LIST()->selection 필드의 것들에 대한 선택 속성을 결정한다.

selection_mode는 이것들 중 하나가 될 것이다.

디폴트는 GTK_SELECTION_MULTIPLE이다.

10.1 시그널

void GtkList::selection_changed (GtkList *LIST)

이 시그널은 GtkList의 selection 필드가 변화했을 때마다 요청된다. 이것은 GtkList의 child가 선택되거나 선택이 해제되었을 때 발생한다.

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

이 시그널은 GtkList의 child가 선택되려고 할 때 발생한다. 이것은 주로 gtk_list_select_item()나 gtk_list_select_child()를 호출할 때, 버튼을 눌렀을 때 등에 발생하며, 때때로 child들이 GtkList에 더해지거나 삭제되었을 때 다른 원인들에 의해 간접적으로 발생하기도 한다.

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

이것은 GtkList의 child가 선택 해제될 때 요청된다. 이것은 주로 gtk_list_ unselect_item()이나 gtk_list_unselect_child()함수를 부를 때나 버튼을 눌렀을 때 발생하며, 역시 GtkList에 child들이 더해지거나 삭제될 때 또다른 원인에 의해 간접적으로 발생한다.

10.2 함수

guint gtk_list_get_type (void)

이것은 GtkList형의 식별자를 리턴한다.

GtkWidget* gtk_list_new (void)

이것은 새로운 GtkList object를 만든다. 이 새 widget 은 GtkWidget object를 향한 포인터의 형태로 리턴된다. 실패했을 경우엔 'NULL'을 리턴한다.

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

POSITION에서 시작해서, 리스트 아이템을 LIST에 삽입한다. ITEMS는 각 노드의 데이터 포인터가 새롭게 만들어진 GtkListItem를 가리키도록 되어있는, 이중의 연결리스트다. ITEMS의 GList노드들은 LIST에 의해 취해진다.

void gtk_list_append_items (GtkList *LIST, GList *ITEMS)

LIST의 마지막에 gtk_list_insert_items() 처럼 리스트 아이템을 삽입한다. ITEMS의 GList 노드들은 LIST에 의해 취해진다.

void gtk_list_prepend_items (GtkList *LIST, GList *ITEMS)

LIST의 맨 첫부분에 gtk_list_insert_items() 처럼 리스트 아이템을 삽입한다. 역시 ITEMS의 GList 노드들은 LIST에 의해 취해진다.

void gtk_list_remove_items (GtkList *LIST, GList *ITEMS)

LIST로부터 리스트 아이템을 제거한다. ITEMS는 각 노드의 데이타 포인터가 LIST의 직계 child를 가리키도록 되어 있는 이중 연결리스트다. 계속해서 g_list_free(ITEMS)를 부르는 것은 호출하는 사람의 책임이다. 또한 호출자는 리스트 아이템 그 자체도 파괴해 주어야 한다.

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

LIST로부터 리스트 아이템을 삭제하고 파괴해준다. LIST에서의 현재 위치가 START에서 END 사이로 설정된 영역에 있는 widget이 영향을 받을 것이다.

void gtk_list_select_item (GtkList *LIST, gint ITEM)

LIST에서의 현재 위치를 통해 지정된 리스트 아이템을 위해 GtkList::select_ child 시그널을 요청한다.

void gtk_list_unselect_item (GtkList *LIST, gint ITEM)

위와 마찬가지 경우에 GtkList::unselect_child 시그널을 요청한다.

void gtk_list_select_child (GtkList *LIST, GtkWidget *CHILD)

주어진 CHILD를 위해 GtkList::select_child 시그널을 요청한다.

void gtk_list_unselect_child (GtkList *LIST, GtkWidget *CHILD)

주어진 CHILD를 위해 GtkList::unselect_child 시그널을 요청한다.

gint gtk_list_child_position (GtkList *LIST, GtkWidget *CHILD)

LIST에서의 CHILD의 위치를 리턴한다. '-1'은 실패했을 때의 리턴값이다.

void gtk_list_set_selection_mode (GtkList *LIST, GtkSelectionMode MODE)

선택 모드 MODE로 LIST를 세팅한다. 모드는 GTK_SELECTION_SINGLE, GTK_ SELECTION_BROWSE, GTK_SELECTION_MULTIPLE, GTK_SELECTION_EXTENDED 중에서 하나가 된다.

GtkList* GTK_LIST (gpointer OBJ)

대부분 포인터를 GtkList* 형으로 캐스트한다. 더 자세히 알려면 일반적인 매크로를 참조하라.

GtkListClass* GTK_LIST_CLASS (gpointer CLASS)

대부분의 포인터를 GtkListClass* 형으로 캐스트한다. 일반적인 매크로를 참조하라.

gint GTK_IS_LIST (gpointer OBJ)

어떤 대부분의 포인터가 GtkList object를 참조하는지 결정한다. 자세히 알려면 일반적인 매크로를 참조하라.

10.3 예제

이번 예제는 GtkList의 선택을 변화시키는 것을 보여줄 것이다. 그리고 리스트 아이템들을 마우스 오른쪽 버튼으로 선택함으로써 그들을 잡아둘 수 있게 할 것이다.

/* 이 프로그램을 컴파일하려면 이렇게 한다.
 * $ gcc -I/usr/local/include/ -lgtk -lgdk -lglib -lX11 -lm -Wall main.c
 */

/* GTK+의 헤더를 include한다.
 * printf()함수 때문에 <stdio.h>도 include한다.
 */
#include        <gtk/gtk.h>
#include        <stdio.h>

/* 이것은 리스트 아이템에 데이터를 저장하기 위한 데이터 식별자다. */
const   gchar   *list_item_data_key="list_item_data";

/* 우리가 GtkList widget에 연결시킬 시그널 핸들러들의 함수원형. */
static  void    sigh_print_selection    (GtkWidget      *gtklist,
                                         gpointer       func_data);
static  void    sigh_button_event       (GtkWidget      *gtklist,
                                         GdkEventButton *event,
                                         GtkWidget      *frame);

/* 사용자 인터페이스를 세팅하는 main 함수 */

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];

                /* gtk+(그리고 결과적으로 gdk도)를 초기화한다. */

                gtk_init(&argc, &argv);

                /* 모든 widget을 넣어둘 윈도를 만든다.
                 * 윈도 매니저의 close-window-event를 다루기 위해 gtk_main_quit()
                 * 함수를 윈도의 "destroy" 이벤트에 연결시킨다. */
                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);

                /* 윈도 내부에 widget들을 수직으로 정렬할 박스가 있어야 한다. */
                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);
                /* GtkList widget을 넣어두고자 하는 스크롤된 윈도다. */
                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);

                /* GtkList widget을 만든다.  selection이 변할 때마다 그 선택된
                 * 아이템을 프린트해 보여주기 위해 sigh_print_selection()
                 * 시그널 핸들러 함수를 GtkList의 "selection_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);

                /* 리스트 아이템을 가두어 둘 "Prison"을 만든다. ;) */
                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);

                /* 리스트 아이템의 "arresting"을 다룰 GtkList에 sigh_button_event()
                 * 시그널 핸들러를 연결시킨다. */
                gtk_signal_connect(GTK_OBJECT(gtklist),
                                                                                         "button_release_event",
                                                                                         GTK_SIGNAL_FUNC(sigh_button_event),
                                                                                         frame);

                /* Separator를 만든다. */
                separator=gtk_hseparator_new();
                gtk_container_add(GTK_CONTAINER(vbox), separator);
                gtk_widget_show(separator);

                /* 끝으로 버튼을 만들고 그것의 "clicked" 시그널을 윈도의 파괴에
                 * 연결시킨다. */
                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));

                /* 이제 각각 라벨을 가지고 있는 5개의 리스트 아이템을 만들고,
                 * gtk_container_add()로써 그들을 GtkList에 첨가한다.
                 * 또한 라벨의 텍스트 문자열을 조사하고 그것을 각 리스트 아이템의
                 * list_item_data_key에 결합시킨다. */
                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);
                }
                /* 여기서는, gtk_list_item_new_with_label()을 이용해서
                 * 다른 5개의 라벨을 만든다.
                 * 이번에는 라벨들에 대한 포인터가 없으므로 라벨의 텍스트
                 * 문자열을 조사할 수 없고, 그러므로 각 리스트 아이템의
                 * list_item_data_key를 똑같은 텍스트 문자열에 결합한다.
                 * 리스트 아이템을 추가하기 위해서 우리는 그들을 이중 연결
                 * 리스트(GList)에 밀어넣고, gtk_list_append_items()를 
                 * 호출해준다.  우리가 아이템들을 이중 연결 리스트에 밀어넣기
                 * 위해 g_list_prepend()를 사용하기 때문에, 그들의 순서는
                 * 뒤로 이어지는(descending) 것이 된다.  반대로 g_list_append()
                 * 를 이용했다면 앞에서 붙여지는(ascending)이 될 것이다. */
                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);

                /* 끝으로 우리는 윈도를 보고 싶어 한다, 아닌가? ;) */
                gtk_widget_show(window);

                /* GTK의 main 이벤트 루프를 시동한다. */
                gtk_main();

                /* gtk_main_quit()이 호출되면 제어는 여기로 온다. */
                return 0;
}

/* GtkList의 버튼 press/release 이벤트들과 연결된 시그널
 * 핸들러 함수들이다. */
void
sigh_button_event       (GtkWidget      *gtklist,
                         GdkEventButton *event,
                         GtkWidget      *frame)
{
                /* 세번째, 즉 가장 오른쪽에 있는 마우스 버튼이 놓여졌을 때만
                 * 어떤 일을 하도록 한다. */
                if (event->type==GDK_BUTTON_RELEASE &&
                                event->button==3) {
                                GList           *dlist, *free_list;
                                GtkWidget       *new_prisoner;

                                /* 현재 선택된 아이템을 가져와서 우리의 다음 prisoner로
                                 * 한다. */
                                dlist=GTK_LIST(gtklist)->selection;
                                if (dlist)
                                                                new_prisoner=GTK_WIDGET(dlist->data);
                                else
                                                                new_prisoner=NULL;

                                /* 이미 사로잡은 리스트 아이템을 조사하고, 그들을
                                 * 다시 리스트로 돌려준다.
                                 * gtk_container_children()이 리턴하는 이중 연결 리스트를
                                 * 꼭 해제(free)해줘야 함을 기억하라. */
                                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);

                                /* 새로 prisoner를 가지게 되면, GtkList로부터 그것을
                                 * 제거하고 "Prison" 프레임으로 밀어넣는다.
                                 * 아이템은 먼저 unselect되어야 한다. */
                                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);
                                }
                }
}

/* 이것은 GtkList가 "selection_changed" 시그널을 발생시키면
 * 호출되는 시그널 핸들러다. */
void
sigh_print_selection    (GtkWidget      *gtklist,
                                         gpointer       func_data)
{
                GList   *dlist;

                /* GtkList의 선택된 아이템의 이중 연결 리스트를 가져온다.
                 * 이것을 read-only로 다뤄야 함을 잊지 말자! */
                dlist=GTK_LIST(gtklist)->selection;

                /* 선택된 아이템이 없을 때는 아무 작업도 할 게 없고 그걸
                 * 사용자에게 알려준다. */
                if (!dlist) {
                                g_print("Selection cleared\n");
                                return;
                }
                /* 뭔가를 선택했고 그것을 프린트한다. */
                g_print("The selection is a ");

                /* 이중 연결 리스트에서 리스트 아이템을 취하고
                 * list_item_data_key와 관련된 데이터를 조사한다.
                 * 그리고 나서 그걸 프린트한다. */
                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");
}

10.4 리스트 아이템 widget

GtkListItem widget은 하나의 child를 넣어둘 수 있는 컨테이너처럼 작동하도록 설계되었으며, GtkList widget이 그의 child들을 위해 그러하듯이 이것 역시 selection/deselection 함수들을 제공하고 있다.

GtkListItem은 이벤트를 받기 위해서 자신만의 윈도를 가지고 있고, 또한 보통 white인 자신만의 background 색깔도 가지고 있다.

이것이 GtkItem으로부터 직접적으로 파생되었기 때문에 역시 GTK_ITEM( ListItem) 매크로로 다루어질 수 있으며, 자세한 것은 GtkItem widget을 참조하면 될 것이다. 대개 GtkListItem은 식별되기 위한 라벨을 가지고 있다, 예를 들면 GtkList에 있는 filename처럼. 따라서 convenient 함수 gtk_list_item_new_with _label()이 제공된다. 같은 효과를 자신만의 GtkLabel을 만듦으로써 얻을 수 있다. GtkListItem에 뒤이어지는 컨테이너와 함께 xalign=0, yalign=0.5로 세팅하면 된다.

GtkListItem에 꼭 GtkLabel을 더해 주는 것은 아니며, 우리는 GtkVBox 또는 GtkArrow 등을 더해줄 수도 있다.

10.5 시그널

GtkListItem은 자신만의 시그널을 새로 만들지는 않고, GtkItem의 시그널을 상속받는다. 자세한 것은 GtkItem에 대해서 참조하라.

10.6 함수

guint gtk_list_item_get_type (void)

GtkListItem형의 식별자를 리턴한다.

GtkWidget* gtk_list_item_new (void)

새로운 GtkListItem object를 만든다. 이 새로운 widget은 GtkWidget object 를 향한 포인터로서 리턴된다. 'NULL'은 실패했을 경우의 리턴값이다.

GtkWidget* gtk_list_item_new_with_label (gchar *LABEL)

하나뿐인 child로 하나의 GtkLabel을 가지며 GtkListItem object를 만든다. 이 새로운 widget은 GtkWidget object를 향한 포인터로서 리턴된다. 'NULL'은 실패했을 때의 리턴값이다.

void gtk_list_item_select (GtkListItem *LIST_ITEM)

이것은 근본적으로, GtkItem::select 시그널을 발생할 gtk_item_select를 호출하는 데 있어서의 wrapper다(GTK_ITEM(list_item)). 자세한 것은 GtkItem을 참조하라.

void gtk_list_item_deselect (GtkListItem *LIST_ITEM)

이것은 GtkItem::deselect 시그널을 발생할 gtk_item_deselect를 호출하는 데 있어서의 wrapper다(GTK_ITEM(list_item)). 역시 자세한 것은 GtkItem을 참조 하라.

GtkListItem* GTK_LIST_ITEM (gpointer OBJ)

대부분의 포인터를 GtkListItem*으로 캐스트한다. 자세한 것은 일반적인 매크로를 참조하라.

GtkListItemClass* GTK_LIST_ITEM_CLASS (gpointer CLASS)

대부분의 포인터를 GtkListItemClass*로 캐스트한다. 역시 일반적인 매크로에 대한 부분을 참조하라.

gint GTK_IS_LIST_ITEM (gpointer OBJ)

어떤 포인터가 GtkListItem object를 참조하고 있는지를 결정한다. 자세한 것은 일반적인 매크로에 대해서 참조하라.

10.7 예제

GtkList에 대한 예제를 참조하라. 그것은 GtkListItem의 이용법에 대해서도 잘 다루고 있다.


다음 이전 차례