다음 이전 차례

8. 다양한 widget들

8.1 라벨(label)

라벨은 GTK에서 자주 쓰이고 비교적 간단한 것이다. 이들은 관련된 X윈도가 없으므로 시그널을 발생하지 않는다. 만약 시그널을 잡아내거나 클리핑을 할 목적이라면 EventBox widget을 이용하라.

새로운 라벨을 만들기 위해 이것을 이용한다.

GtkWidget* gtk_label_new (char *str);

하나의 인자는 우리가 나타내고자 하는 라벨의 문자열이다.

라벨을 만든 이후에 이 라벨의 텍스트를 바꾸려면 이것을 이용한다.

void gtk_label_set (GtkLabel  *label,
                    char      *str);

첫번째 인자는 이전에 만들어져 있는 라벨(GTK_LABEL()매크로로써 캐스트됨) 이고, 두번째는 새로운 문자열이다.

새로운 라벨을 위한 공간은 필요할 경우에 자동적으로 조절된다.

현재의 문자열을 되찾고 싶다면 이것을 이용한다.

void gtk_label_get (GtkLabel  *label,
                    char     **str);

첫번째 인자는 만들어졌던 라벨이고, 두번째는 되살리고자 하는 문자열이다.

8.2 풍선 도움말(tooltip widget)

마우스포인터를 어떤 버튼이나 다른 widget 위에 몇 초 머무르게 하면 작은 텍스트 문자열이 튀어나오는 경우가 있다. 이것은 간단한 것이며, 그래서 여기서 예제없이 설명하겠다. 실제로 코드를 보고싶다면 GDK와 함께 배포되는 testgtk.c 프로그램을 참조하라.

라벨 등 어떤 widget에는 이 tooltip이 쓰이지 않는다.

우리가 처음 이용할 함수는 새로운 tooltip을 만드는 것이다. 이것은 주어진 함수에서 한번만 해주면 된다. 이 함수가 리턴하는 GtkTooltip은 다중의 tooltip들을 만드는데도 이용될 수 있다.

GtkTooltips *gtk_tooltips_new (void);

일단 새로운 tooltip과 그것을 사용할 widget을 만들었으면, 그것을 세팅하기 위해 이 함수를 이용하라.

void gtk_tooltips_set_tip (GtkTooltips *tooltips,
                           GtkWidget   *widget,
                           const gchar *tip_text,
                           const gchar *tip_private);

첫번째 인자는 우리가 만든 tooltip이고, 다음은 이 tooltip을 포함하게 될 widget이다. 세번째 인자인 텍스트는 우리가 tooltip에서 말하고자 하는 것이다. 마지막 인자는 NULL로 줄 수 있다.

이것은 짧은 예다.

GtkTooltips *tooltips;
GtkWidget *button;
...
tooltips = gtk_tooltips_new ();
button = gtk_button_new_with_label ("button 1");
...
gtk_tooltips_set_tip (tooltips, button, "This is button 1", NULL);

Tooltip에 쓰이는 다른 함수들도 있다. 여기서 그들을 간단히 소개하겠다.

void gtk_tooltips_destroy    (GtkTooltips *tooltips);

만들어진 tooltip을 제거한다.

void gtk_tooltips_enable     (GtkTooltips *tooltips);

Disable로 설정된 tooltip을 enable한다.

void gtk_tooltips_disable    (GtkTooltips *tooltips);

Enable로 설정된 tooltip을 disable한다.

void gtk_tooltips_set_delay  (GtkTooltips *tooltips,
                              gint         delay);

Tooltip이 튀어오르기 위해 얼마나 마우스포인터를 widget위에 머무르게 해야하는 지를, millisecond단위로 세팅한다. 디폴트로 1000millisecond, 즉 1초다.

void gtk_tooltips_set_tips (GtkTooltips *tooltips,
                            GtkWidget   *widget,
                            gchar    *tips_text);

이미 만들어진 tooltip의 텍스트 내용을 바꾼다.

void gtk_tooltips_set_colors (GtkTooltips *tooltips,
                              GdkColor    *background,
                              GdkColor    *foreground);

Tooltip의 표현색과 배경색을 바꾼다. 어떻게 색깔을 설정하는지에 대해서는 모르겠다.

Tooltip에 관련된 함수는 이것이 전부다. 더이상 알 것도 없다. :)

8.3 진행막대(progress bar)

진행막대는 작업의 상황을 나타내기 위해 쓰인다. 이제 코드를 보면 알겠지만, 이것은 꽤 간단하다. 그러나 먼저 새로운 진행막대를 만들어주는 함수를 살펴 보는 것으로 시작하자.

GtkWidget *gtk_progress_bar_new (void);

이제 진행막대가 만들어졌고 우리는 그것을 이용할 수 있다.

void gtk_progress_bar_update (GtkProgressBar *pbar, gfloat percentage);

첫번째 인자는 동작시킬 진행막대가 되고, 두번째 인자는 '완료된' 분량을 나타낸다. 이것은 실제 숫자로 0부터 1까지고, 0부터 100퍼센트를 의미하는 것이다.

진행막대는 보통 타임아웃이나 또는 멀티태스킹하는 착각을 일으키게 하는 함수들과 함께 쓰인다. (section 타임아웃, 그리고 I/O와 Idle 함수들을 참조하라.) 모든 경우에 gtk_progress_bar_update함수가 동일한 방식으로 쓰이게 된다.

이것은 타임아웃을 이용해 업데이트되는 진행막대를 보인 예제다. 이것은 또한 진행막대를 리셋, 즉 초기화하는 방법도 보여줄 것이다.

/* progressbar.c */

#include <gtk/gtk.h>

static int ptimer = 0;
int pstat = TRUE;

/* 이 함수는 진행막대를 증가시키고 업데이트한다.  또 pstat가 FALSE로 되면
 * 진행막대를 리셋, 즉 초기화한다. */
gint progress (gpointer data)
{
                gfloat pvalue;

                /* 진행막대의 현재값을 알아낸다. */
                pvalue = GTK_PROGRESS_BAR (data)->percentage;

                if ((pvalue >= 1.0) || (pstat == FALSE)) {
                                pvalue = 0.0;
                                pstat = TRUE;
                }
                pvalue += 0.01;

                gtk_progress_bar_update (GTK_PROGRESS_BAR (data), pvalue);

                return TRUE;
}

/* 이 함수는 진행막대의 리셋을 위한 시그널을 낸다. */
void progress_r (void)
{
                pstat = FALSE;
}

void destroy (GtkWidget *widget, gpointer data)
{
                gtk_main_quit ();
}

int main (int argc, char *argv[])
{
                GtkWidget *window;
                GtkWidget *button;
                GtkWidget *label;
                GtkWidget *table;
                GtkWidget *pbar;

                gtk_init (&argc, &argv);

                window = gtk_window_new (GTK_WINDOW_TOPLEVEL);

                gtk_signal_connect (GTK_OBJECT (window), "delete_event",
                            GTK_SIGNAL_FUNC (destroy), NULL);

                gtk_container_border_width (GTK_CONTAINER (window), 10);

                table = gtk_table_new(3,2,TRUE);
                gtk_container_add (GTK_CONTAINER (window), table);

                label = gtk_label_new ("Progress Bar Example");
                gtk_table_attach_defaults(GTK_TABLE(table), label, 0,2,0,1);
                gtk_widget_show(label);

                /* 새로운 진행막대를 만들고, 그것을 테이블에 패킹하여 보여준다. */
                pbar = gtk_progress_bar_new ();
                gtk_table_attach_defaults(GTK_TABLE(table), pbar, 0,2,1,2);
                gtk_widget_show (pbar);

                /* 진행막대의 자동 업데이트를 위한 timeout을 설정한다. */
                ptimer = gtk_timeout_add (100, progress, pbar);

                /* 이 버튼이 진행막대를 리셋하는 시그널을 위한 것이다. */
                button = gtk_button_new_with_label ("Reset");
                gtk_signal_connect (GTK_OBJECT (button), "clicked",
                            GTK_SIGNAL_FUNC (progress_r), NULL);
                gtk_table_attach_defaults(GTK_TABLE(table), button, 0,1,2,3);
                gtk_widget_show(button);

                button = gtk_button_new_with_label ("Cancel");
                gtk_signal_connect (GTK_OBJECT (button), "clicked",
                            GTK_SIGNAL_FUNC (destroy), NULL);

                gtk_table_attach_defaults(GTK_TABLE(table), button, 1,2,2,3);
                gtk_widget_show (button);

                gtk_widget_show(table);
                gtk_widget_show(window);

                gtk_main ();

                return 0;
}

이 작은 프로그램에는 진행막대의 일반적인 동작과 관련된 네 개의 영역이 있다. 그들이 쓰여진 순서에 따라 알아보도록 하자.

pbar = gtk_progress_bar_new ();

이 코드는 pbar라는 이름의 새로운 진행막대를 만들고 있다.

ptimer = gtk_timeout_add (100, progress, pbar);

이 코드에서 일정한 시간간격의 타임아웃을 이용하는데, 진행막대의 이용에 꼭 타임아웃을 써야 되는 것은 아니다.

pvalue = GTK_PROGRESS_BAR (data)->percentage;

여기서 pvalue를 향한 percentage bar의 현재 값을 지정해 주고 있다.

gtk_progress_bar_update (GTK_PROGRESS_BAR (data), pvalue);

끝으로, pvalue의 값에 의해 진행막대를 업데이트한다.

그리고 진행막대에 대해 알 것은 이것이 전부다, 즐겨보기를 !

8.4 대화상자

대화상자 widget은 매우 간단한 것인데, 실제로 이것은 몇가지가 미리 패킹되어 있는 하나의 윈도일 뿐이다. Dialog를 위한 구조체는 이것이다.

struct GtkDialog
{
      GtkWindow window;

      GtkWidget *vbox;
      GtkWidget *action_area;
};

보다시피 이것은 단순히 윈도를 만들고, 그리고는 vbox를 맨 위로 패킹하고, 다음으로 separator를, 그리고 나서 "action_area"를 위한 hbox를 패킹한다.

대화상자widget은 사용자에게 팝업 메시지를 보이거나 하는 등의 목적으로 쓰일 수 있다. 이것은 정말 기본적인 것으로, 대화박스를 위한 함수는 이것 하나 뿐이다.

GtkWidget* gtk_dialog_new (void);

그래서 새로운 대화박스를 만들려면 이렇게 한다.

GtkWidget *window;
window = gtk_dialog_new ();

이것은 대화상자를 만들 것이고, 어떻게 이용할지는 각자에게 달려있다. 우리는 이런 식으로 해서 action_area 안에 버튼을 패킹할 수 있다.

button = ...
gtk_box_pack_start (GTK_BOX (GTK_DIALOG (window)->action_area), button,
                    TRUE, TRUE, 0);
gtk_widget_show (button);

그리고 우리는 패킹에 의하여 vbox 영역에 라벨 등을 추가할 수 있다. 아래와 같이 해보자.

label = gtk_label_new ("Dialogs are groovy");
gtk_box_pack_start (GTK_BOX (GTK_DIALOG (window)->vbox), label, TRUE,
                    TRUE, 0);
gtk_widget_show (label);

대화상자를 이용한 예제에서처럼, 우리는 action_area에 Cancel과 Ok의 두 버튼을, 그리고 vbox영역엔 사용자에게 물어보거나 에러메시지를 내거나 하는 목적의 라벨을 만들 수 있을 것이다. 그리고 나서 사용자의 선택에 따라 작동하도록 버튼 각각에 서로 다른 시그널을 설정해 둘 수 있다.

8.5 픽스맵

픽스맵은 그림을 포함하고 있는 자료 구조이다. 이런 그림은 여러가지로 쓰일 수 있는데, 가장 눈에 띄는 이용은 X윈도 데스크톱의 아이콘이나 커서일 것이다. 비트맵이란 2색의 픽스맵이다.

GTK에서 픽스맵을 이용하기 위해, 우리는 먼저 GDK 수준의 함수들로써 GdkPixmap 구조체를 만들어야 한다. 픽스맵은 메모리의 자료 혹은 파일로부터 읽어들인 자료로부터 만들어질 수 있다. 우리는 픽스맵을 만들기 위한 각각의 함수를 지금부터 살펴볼 것이다.

GdkPixmap *gdk_bitmap_create_from_data( GdkWindow *window,
                                        gchar     *data,
                                        gint      width,
                                        gint      height );

이 함수는 메모리의 데이터로 2색의 single-plane 픽스맵을 만들기 위한 것으로, 데이터의 각각의 비트는 픽셀의 on/off 여부를 나타낸다. width/height는 픽셀 단위다. 픽스맵 리소스는 그것이 보여질 스크린의 배경에서만 의미가 있으므로, GdkWindow 형 포인터는 현재의 윈도를 가리키게 된다.

GdkPixmap* gdk_pixmap_create_from_data( GdkWindow  *window,
                                        gchar      *data,
                                        gint        width,
                                        gint        height,
                                        gint        depth,
                                        GdkColor   *fg,
                                        GdkColor   *bg );

이것은 주어진 비트맵 데이타로부터 임의의 depth(색의 갯수)를 가진 픽스맵을 만들 때 쓰인다. fg와 bg는 이용할 foreground 및 background 색깔이다.

GdkPixmap* gdk_pixmap_create_from_xpm( GdkWindow  *window,
                                       GdkBitmap **mask,
                                       GdkColor   *transparent_color,
                                       const gchar *filename );

XPM 포맷은 X윈도시스템에서 읽어들일 수 있는 하나의 픽스맵 형태다. 이것은 널리 쓰이고 있으며 이미지 파일을 이 포맷으로 만들어 주는 수많은 유틸리티들이 있다. 파일이름으로 불리어진 이 파일은 그 포맷의 이미지를 포함하고 있어야 하고, 그것은 픽스맵 구조체 안으로 로드된다. 그 픽스맵의 어떤 비트가 불투명 해야 하는지는 mask가 결정한다. 나머지 모든 비트들은 transparent_color에 의해 정해진 색깔을 가지게 된다. 이것을 이용하는 예제가 아래에 뒤따를 것이다.

GdkPixmap* gdk_pixmap_create_from_xpm_d (GdkWindow  *window,
                                         GdkBitmap **mask,
                                         GdkColor   *transparent_color,
                                         gchar     **data);

작은 이미지는 XPM 포맷의 데이터로서 프로그램 내부에 포함되어 있을 수가 있다. 어떤 픽스맵은 파일로부터 데이터를 읽어들이는 대신 이런 데이터를 이용해 만들어진다. 이 데이터의 예는 이런 것이다.

/* XPM */
static const char * xpm_data[] = {
"16 16 3 1",
"       c None",
".      c #000000000000",
"X      c #FFFFFFFFFFFF",
"                ",
"   ......       ",
"   .XXX.X.      ",
"   .XXX.XX.     ",
"   .XXX.XXX.    ",
"   .XXX.....    ",
"   .XXXXXXX.    ",
"   .XXXXXXX.    ",
"   .XXXXXXX.    ",
"   .XXXXXXX.    ",
"   .XXXXXXX.    ",
"   .XXXXXXX.    ",
"   .XXXXXXX.    ",
"   .........    ",
"                ",
"                "};

void gdk_pixmap_destroy( GdkPixmap  *pixmap );

우리가 어떤 픽스맵을 이용했고 또 당분간 다시 이용할 필요가 없을 경우, 이 리소스를 gdk_pixmap_destory로 메모리에 반납해주는 것도 좋은 생각이다. 픽스맵은 중요한 리소스로 간주되어야 한다.

우리가 일단 픽스맵을 만들면, 그것을 GTK widget처럼 보여줄 수 있다. 우리는 GDK 픽스맵을 포함시키기 위해서 픽스맵widget을 만들어야만 한다. 이것을 이용 해서 그렇게 할 수 있다.

GtkWidget* gtk_pixmap_new( GdkPixmap  *pixmap,
                           GdkBitmap  *mask );

또다른 픽스맵widget 함수들은 다음과 같다.

guint gtk_pixmap_get_type( void );
void  gtk_pixmap_set( GtkPixmap  *pixmap,
                      GdkPixmap  *val,
                      GdkBitmap  *mask);
void  gtk_pixmap_get( GtkPixmap  *pixmap,
                      GdkPixmap **val,
                      GdkBitmap **mask);

gtk_pixmap_set은 widget이 현재 다루고 있는 픽스맵을 변화시키기 위해 이용한다. 인자 val은 GDK를 이용해 만들어진 픽스맵이다.

이 예제는 버튼 안에 픽스맵을 넣는 예제이다.

#include <gtk/gtk.h>

/* Open-File 아이콘을 위한 XPM 데이터 */
static const char * xpm_data[] = {
"16 16 3 1",
"       c None",
".      c #000000000000",
"X      c #FFFFFFFFFFFF",
"                ",
"   ......       ",
"   .XXX.X.      ",
"   .XXX.XX.     ",
"   .XXX.XXX.    ",
"   .XXX.....    ",
"   .XXXXXXX.    ",
"   .XXXXXXX.    ",
"   .XXXXXXX.    ",
"   .XXXXXXX.    ",
"   .XXXXXXX.    ",
"   .XXXXXXX.    ",
"   .XXXXXXX.    ",
"   .........    ",
"                ",
"                "};

/* 이것이 호출되면(delete_event 시그널을 통해) 어플이 종료된다. */
void close_application( GtkWidget *widget, GdkEvent *event, gpointer data )
{
                gtk_main_quit();
}

/* 버튼이 클릭되면 호출되어 메시지를 프린트한다. */
void button_clicked( GtkWidget *widget, gpointer data )
{
                printf( "button clicked\n" );
}

int main( int argc, char *argv[] )
{
                /* GtkWidget은 widget을 위한 기억장소 타입이다. */ 
                GtkWidget *window, *pixmapwid, *button;
                GdkPixmap *pixmap;
                GdkBitmap *mask;
                GtkStyle *style;

                /* main 윈도를 만들고, 어플을 끝내기 위한 delete_event 시그널을
                 * 연결시켜 둔다. */
                gtk_init( &argc, &argv );
                window = gtk_window_new( GTK_WINDOW_TOPLEVEL );
                gtk_signal_connect( GTK_OBJECT (window), "delete_event",
                            GTK_SIGNAL_FUNC (close_application), NULL );
                gtk_container_border_width( GTK_CONTAINER (window), 10 );
                gtk_widget_show( window );

                /* gdk함수로 픽스맵을 만들기 위한 것이다. */
                style = gtk_widget_get_style( window );
                pixmap = gdk_pixmap_create_from_xpm_d( window->window,  &mask,
                                              &style->bg[GTK_STATE_NORMAL],
                                              (gchar **)xpm_data );

                /* 픽스맵을 다룰 pixmap widget이다. */
                pixmapwid = gtk_pixmap_new( pixmap, mask );
                gtk_widget_show( pixmapwid );

                /* pixmap widget을 가지게 되는 버튼이다. */
                button = gtk_button_new();
                gtk_container_add( GTK_CONTAINER(button), pixmapwid );
                gtk_container_add( GTK_CONTAINER(window), button );
                gtk_widget_show( button );

                gtk_signal_connect( GTK_OBJECT(button), "clicked",
                            GTK_SIGNAL_FUNC(button_clicked), NULL );

                /* 윈도를 보인다. */
                gtk_main ();

                return 0;
}

현재 디렉토리의 icon0.xpm 이라는 XPM 데이터파일을 불러오기 위해서, 우리는 픽스맵을 만들어야 한다.

/* 파일로부터 픽스맵을 불러온다. */
pixmap = gdk_pixmap_create_from_xpm( window->window, &mask,
                                    &style->bg[GTK_STATE_NORMAL],
                                    "./icon0.xpm" );
pixmapwid = gtk_pixmap_new( pixmap, mask );
gtk_widget_show( pixmapwid );
gtk_container_add( GTK_CONTAINER(window), pixmapwid );

픽스맵을 이용할 때 단점 하나는 이미지에 관계없이 모든 대상이 직사각형 이라는 것이다. 우리는 데스크톱과 어플에서 아이콘이 좀더 자연스러운 모양을 가지도록 하고싶다. 예를들어 어떤 게임 인터페이스에서, 우리는 누를 버튼이 둥근 모양을 가지도록 하고 싶어한다. 이렇게 하기 위해서는 특정한 모양을 갖춘 윈도를 이용한다.

특정한 모양을 갖춘 윈도란 간단히 배경을 이루는 픽셀들이 투명한 것을 말한다. 배경 이미지가 여러 색을 띠고 있다면, 이렇게 우리는 우리의 아이콘을 둘러싼 들어맞지 않는 경계인 직사각형으로 겹쳐 그리지 않는다. 이번 예제는 이런 식으로 해서 데스크톱에 수레바퀴 이미지를 보여준다.

/* wheelbarrow.c */

#include <gtk/gtk.h>

/* XPM */
static char * WheelbarrowFull_xpm[] = {
"48 48 64 1",
"       c None",
".      c #DF7DCF3CC71B",
"X      c #965875D669A6",
"o      c #71C671C671C6",
"O      c #A699A289A699",
"+      c #965892489658",
"@      c #8E38410330C2",
"#      c #D75C7DF769A6",
"$      c #F7DECF3CC71B",
"%      c #96588A288E38",
"&      c #A69992489E79",
"*      c #8E3886178E38",
"=      c #104008200820",
"-      c #596510401040",
";      c #C71B30C230C2",
":      c #C71B9A699658",
">      c #618561856185",
",      c #20811C712081",
"<      c #104000000000",
"1      c #861720812081",
"2      c #DF7D4D344103",
"3      c #79E769A671C6",
"4      c #861782078617",
"5      c #41033CF34103",
"6      c #000000000000",
"7      c #49241C711040",
"8      c #492445144924",
"9      c #082008200820",
"0      c #69A618611861",
"q      c #B6DA71C65144",
"w      c #410330C238E3",
"e      c #CF3CBAEAB6DA",
"r      c #71C6451430C2",
"t      c #EFBEDB6CD75C",
"y      c #28A208200820",
"u      c #186110401040",
"i      c #596528A21861",
"p      c #71C661855965",
"a      c #A69996589658",
"s      c #30C228A230C2",
"d      c #BEFBA289AEBA",
"f      c #596545145144",
"g      c #30C230C230C2",
"h      c #8E3882078617",
"j      c #208118612081",
"k      c #38E30C300820",
"l      c #30C2208128A2",
"z      c #38E328A238E3",
"x      c #514438E34924",
"c      c #618555555965",
"v      c #30C2208130C2",
"b      c #38E328A230C2",
"n      c #28A228A228A2",
"m      c #41032CB228A2",
"M      c #104010401040",
"N      c #492438E34103",
"B      c #28A2208128A2",
"V      c #A699596538E3",
"C      c #30C21C711040",
"Z      c #30C218611040",
"A      c #965865955965",
"S      c #618534D32081",
"D      c #38E31C711040",
"F      c #082000000820",
"                                                ",
"          .XoO                                  ",
"         +@#$%o&                                ",
"         *=-;#::o+                              ",
"           >,<12#:34                            ",
"             45671#:X3                          ",
"               +89<02qwo                        ",
"e*                >,67;ro                       ",
"ty>                 459@>+&&                    ",
"$2u+                  ><ipas8*                  ",
"%$;=*                *3:.Xa.dfg>                ",
"Oh$;ya             *3d.a8j,Xe.d3g8+             ",
" Oh$;ka          *3d$a8lz,,xxc:.e3g54           ",
"  Oh$;kO       *pd$%svbzz,sxxxxfX..&wn>         ",
"   Oh$@mO    *3dthwlsslszjzxxxxxxx3:td8M4       ",
"    Oh$@g& *3d$XNlvvvlllm,mNwxxxxxxxfa.:,B*     ",
"     Oh$@,Od.czlllllzlmmqV@V#V@fxxxxxxxf:%j5&   ",
"      Oh$1hd5lllslllCCZrV#r#:#2AxxxxxxxxxcdwM*  ",
"       OXq6c.%8vvvllZZiqqApA:mq:Xxcpcxxxxxfdc9* ",
"        2r<6gde3bllZZrVi7S@SV77A::qApxxxxxxfdcM ",
"        :,q-6MN.dfmZZrrSS:#riirDSAX@Af5xxxxxfevo",
"         +A26jguXtAZZZC7iDiCCrVVii7Cmmmxxxxxx%3g",
"          *#16jszN..3DZZZZrCVSA2rZrV7Dmmwxxxx&en",
"           p2yFvzssXe:fCZZCiiD7iiZDiDSSZwwxx8e*>",
"           OA1<jzxwwc:$d%NDZZZZCCCZCCZZCmxxfd.B ",
"            3206Bwxxszx%et.eaAp77m77mmmf3&eeeg* ",
"             @26MvzxNzvlbwfpdettttttttttt.c,n&  ",
"             *;16=lsNwwNwgsvslbwwvccc3pcfu<o    ",
"              p;<69BvwwsszslllbBlllllllu<5+     ",
"              OS0y6FBlvvvzvzss,u=Blllj=54       ",
"               c1-699Blvlllllu7k96MMMg4         ",
"               *10y8n6FjvllllB<166668           ",
"                S-kg+>666<M<996-y6n<8*          ",
"                p71=4 m69996kD8Z-66698&&        ",
"                &i0ycm6n4 ogk17,0<6666g         ",
"                 N-k-<>     >=01-kuu666>        ",
"                 ,6ky&      &46-10ul,66,        ",
"                 Ou0<>       o66y<ulw<66&       ",
"                  *kk5       >66By7=xu664       ",
"                   <<M4      466lj<Mxu66o       ",
"                   *>>       +66uv,zN666*       ",
"                              566,xxj669        ",
"                              4666FF666>        ",
"                               >966666M         ",
"                                oM6668+         ",
"                                  *4            ",
"                                                ",
"                                                "};

/* 이것이 호출되면(delete_event 시그널을 통해) 어플이 종료된다. */
void close_application( GtkWidget *widget, GdkEvent *event, gpointer data )
{
                gtk_main_quit();
}

int main (int argc, char *argv[])
{
                GtkWidget *window, *pixmap, *fixed;
                GdkPixmap *gdk_pixmap;
                GdkBitmap *mask;
                GtkStyle *style;
                GdkGC *gc;

                /* main윈도를 만들고, 어플을 끝내기 위한 delete_event 시그널을 거기에
                 * 연결시켜 둔다.  main 윈도는 우리가 popup만 되도록 만들었기 때문에
                 * 타이틀바를 가지지 않을 것이다. */
                gtk_init (&argc, &argv);
                window = gtk_window_new( GTK_WINDOW_POPUP );
                gtk_signal_connect (GTK_OBJECT (window), "delete_event",
                            GTK_SIGNAL_FUNC (close_application), NULL);
                gtk_widget_show (window);

                /* 픽스맵과 pixmap widget을 위한 것이다. */
                style = gtk_widget_get_default_style();
                gc = style->black_gc;
                gdk_pixmap = gdk_pixmap_create_from_xpm_d( window->window, &mask,
                                              &style->bg[GTK_STATE_NORMAL],
                                              WheelbarrowFull_xpm );
                pixmap = gtk_pixmap_new( gdk_pixmap, mask );
                gtk_widget_show( pixmap );

                /* 픽스맵을 보이기 위해, 우리는 픽스맵을 놓아둘 fixed widget을
                 * 이용한다. */
                fixed = gtk_fixed_new();
                gtk_widget_set_usize( fixed, 200, 200 );
                gtk_fixed_put( GTK_FIXED(fixed), pixmap, 0, 0 );
                gtk_container_add( GTK_CONTAINER(window), fixed );
                gtk_widget_show( fixed );

                /* 이것은 이미지 자신을 제외한 다른 모든 것을 은폐한다. */
                gtk_widget_shape_combine_mask( window, mask, 0, 0 );

                /* 윈도를 보인다. */
                gtk_widget_set_uposition( window, 20, 400 );
                gtk_widget_show( window );
                gtk_main ();

                return 0;
}

수레바퀴 이미지를 더 섬세하게 만들기 위해, 우리는 버튼이 눌려진 이벤트의 시그널이 수레바퀴에 어떤 동작을 하도록 엮어줄 수 있다. 여기 보이는 몇 줄의 코드는 마우스버튼이 눌려졌을 때 어플이 끝나도록 해준다.

gtk_widget_set_events( window,
                       gtk_widget_get_events( window ) |
                       GDK_BUTTON_PRESS_MASK );

gtk_signal_connect( GTK_OBJECT(window), "button_press_event",
                    GTK_SIGNAL_FUNC(close_application), NULL );

8.6 룰러(ruler)

룰러 widget은 주어진 한 윈도에서 마우스 포인터의 위치를 가리키는데 쓰인다. 윈도는 폭을 가로지르는 수평 룰러와 높이를 가로지르는 수직룰러를 가질 수 있다. 룰러 위의 조그만 삼각형 표시기(indicator)가 룰러에 대한 포인터의 정확한 위치를 보여준다.

룰러는 반드시 미리 먼저 만들어져야만 한다. 수평 및 수직 룰러는 다음 함수들을 이용해서 만들어진다.

GtkWidget *gtk_hruler_new(void);    /* 수평 룰러 */
GtkWidget *gtk_vruler_new(void);    /* 수직 룰러 */

룰러가 한번 만들어지면 측정단위를 정의할 수 있다. 룰러의 측정단위는 GTK_PIXELS나 GTK_INCHES, GTK_CENTIMETERS 중의 하나가 된다.

void gtk_ruler_set_metric( GtkRuler        *ruler,
                           GtkMetricType   metric );

기본 설정 단위는 GTK_PIXELS이다.

gtk_ruler_set_metric( GTK_RULER(ruler), GTK_PIXELS );

또다른 중요한 룰러의 특성은 어떻게 크기 단위를 나타내느냐 하는 점과 표시기가 처음 어디에 놓이냐하는 점이다. 이들은 다음 함수로 결정한다.

void  gtk_ruler_set_range  (GtkRuler       *ruler,
                            gfloat          lower,
                            gfloat          upper,
                            gfloat          position,
                            gfloat          max_size);

인자 lower와 upper는 룰러의 범위를 정의하고 max_size는 출력 가능한 가장 큰 수를 설정한다. Position은 룰러 내의 표시기 초기 위치이다.

800 픽셀의 수직 룰러는 이렇게 된다.

gtk_ruler_set_range( GTK_RULER(vruler), 0, 800, 0, 800);

0부터 800까지 매 100 픽셀마다 표지(marking)가 출력될 것이다. 만일 룰러의 범위를 7부터 16으로 바꾸려면 다음과 같이 한다.

gtk_ruler_set_range( GTK_RULER(vruler), 7, 16, 0, 20);

룰러의 표시기는 포인터의 상대적인 위치를 가리키는 조그만 삼각형 표지이다. 만일 룰러가 마우스 포인터를 따르게 하고 싶다면 motion_notify_event 시그널이 룰러의 motion_notify_event method와 연결되어야만 한다. 특정 윈도 영역 내의 모든 마우스 움직임을 따르게 하기 위해 다음과 같이 한다.

#define EVENT_METHOD(i, x) GTK_WIDGET_CLASS(GTK_OBJECT(i)->klass)->x

gtk_signal_connect_object( GTK_OBJECT(area), "motion_notify_event",
         (GtkSignalFunc)EVENT_METHOD(ruler, motion_notify_event),
         GTK_OBJECT(ruler) );

다음 예제는 수평 룰러를 위에 수직 룰러를 왼쪽에 가진 drawing area를 만든다. 이 drawing area는 600 픽셀의 폭과 400 픽셀의 높이를 가진다. 수평 룰러는 7부터 13의 범위에 매 100 픽셀마다 표지를 하고 수직 루러는 0부터 400의 범위에 마찬가지로 매 100 픽셀마다 표지를 한다. 룰러들과 drawing area의 위치 설정은 테이블을 사용한다.

/* rulers.c */

#include <gtk/gtk.h>

#define EVENT_METHOD(i, x) GTK_WIDGET_CLASS(GTK_OBJECT(i)->klass)->x

#define XSIZE  600
#define YSIZE  400

/* 닫기 버튼(close button)이 눌러지면 이 함수가 불린다.
 */
void close_application( GtkWidget *widget, gpointer data ) {
    gtk_main_quit();
}


/* 메인 함수
 */
int main( int argc, char *argv[] ) {
    GtkWidget *window, *table, *area, *hrule, *vrule;

    /* gtk를 초기화하고 메인 윈도를 만든다.*/
    gtk_init( &argc, &argv );

    window = gtk_window_new( GTK_WINDOW_TOPLEVEL );
    gtk_signal_connect (GTK_OBJECT (window), "delete_event",
            GTK_SIGNAL_FUNC( close_application ), NULL);
    gtk_container_border_width (GTK_CONTAINER (window), 10);

    /* 룰러와 drawing area를 놓은 테이블을 만든다 */
    table = gtk_table_new( 3, 2, FALSE );
    gtk_container_add( GTK_CONTAINER(window), table );

    area = gtk_drawing_area_new();
    gtk_drawing_area_size( (GtkDrawingArea *)area, XSIZE, YSIZE );
    gtk_table_attach( GTK_TABLE(table), area, 1, 2, 1, 2,
                      GTK_EXPAND|GTK_FILL, GTK_FILL, 0, 0 );
    gtk_widget_set_events( area, GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK );

    /* 수평 룰러는 가장 위에 놓인다.  마우스가 drawing area 위를 움직이면,
       motion_notify_event 가 룰러의 적절한 이벤트 핸들러에게 전달된다.
     */
    hrule = gtk_hruler_new();
    gtk_ruler_set_metric( GTK_RULER(hrule), GTK_PIXELS );
    gtk_ruler_set_range( GTK_RULER(hrule), 7, 13, 0, 20 );
    gtk_signal_connect_object( GTK_OBJECT(area), "motion_notify_event",
                               (GtkSignalFunc)EVENT_METHOD(hrule, motion_notify_event),
                               GTK_OBJECT(hrule) );
    /*  GTK_WIDGET_CLASS(GTK_OBJECT(hrule)->klass)->motion_notify_event, */
    gtk_table_attach( GTK_TABLE(table), hrule, 1, 2, 0, 1,
                      GTK_EXPAND|GTK_SHRINK|GTK_FILL, GTK_FILL, 0, 0 );
    
    /* 수직 룰러는 제일 왼쪽에 놓인다.  마우스가 drawing area 위를 움직이면,,
       motion_notify_event 가 룰러의 적절한 이벤트 핸들러에게 전달된다.
     */
    vrule = gtk_vruler_new();
    gtk_ruler_set_metric( GTK_RULER(vrule), GTK_PIXELS );
    gtk_ruler_set_range( GTK_RULER(vrule), 0, YSIZE, 10, YSIZE );
    gtk_signal_connect_object( GTK_OBJECT(area), "motion_notify_event",
                               (GtkSignalFunc)
                                  GTK_WIDGET_CLASS(GTK_OBJECT(vrule)->klass)->motion_notify_event,
                               GTK_OBJECT(vrule) );
    gtk_table_attach( GTK_TABLE(table), vrule, 0, 1, 1, 2,
                      GTK_FILL, GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0 );

    /* 이제 모든 것을 보여준다 */
    gtk_widget_show( area );
    gtk_widget_show( hrule );
    gtk_widget_show( vrule );
    gtk_widget_show( table );
    gtk_widget_show( window );
    gtk_main();

    return 0;
}

8.7 상태표시줄(statusbar)

상태표시줄은 텍스트 메시지를 보여주는데 쓰이는 간단한 widget이다. 이 widget은 텍스트 메시지들을 스택에 보관한다. 따라서 현재의 메시지를 꺼내면 바로 이전의 메시지를 다시 보여주게 된다.

한 어플리케이션의 여러 부분들이 메시지를 표시하는데 같은 상태표시줄을 사용해야하는 경우, 상태표시줄 widget은 여러 '사용자'들을 구분하는데 쓰이는 Context Identifier를 발행한다. 어떤 context를 가졌느냐에 상관없이 스택 제일 위의 메시지가 보여진다. 메시지들은 Context Identifier의 순서가 아니라 나중에 들어간 것이 먼저 나오는 순서로 스택에 쌓인다.

상태표시줄은 다음 함수를 통해 만들어진다.

GtkWidget* gtk_statusbar_new (void);

새로운 Context Identifier는 간단한 context에 관한 설명과 함께 다음 함수를 호출하여 얻을 수 있다.

guint gtk_statusbar_get_context_id (GtkStatusbar *statusbar,
                                    const gchar  *context_description);

상태표시줄을 다루는 다음과 같은 함수들이 있다.

guint    gtk_statusbar_push        (GtkStatusbar *statusbar,
                                    guint          context_id,
                                    gchar         *text);

void     gtk_statusbar_pop         (GtkStatusbar *statusbar)
                                    guint         context_id);
void     gtk_statusbar_remove      (GtkStatusbar *statusbar,
                                    guint         context_id,
                                    guint         message_id); 

먼저 gtk_statusbar_push는 상태표시줄에 새로운 메시지를 추가하는데 쓰인다. 이 함수는 나중에 gtk_statusbar_remove를 호출하는데 쓰일 수 있는 Message Identifier를 리턴한다. gtk_statusbar_remove는 주어진 Message와 Context Identifier의 메시지를 상태표시줄에서 제거한다.

gtk_statusbar_pop은 주어진 Context Identifier의 스택 제일 위 메시지를 꺼내 삭제한다.

다음 예제는 버튼 2개와 상태표시줄 하나를 만든다. 벼튼 하나는 상태표시줄에 새 메시지를 넣고 나머지 다른 버튼은 마지막으로 넣어진 메시지를 제거한다.

/* statusbar.c */

#include <gtk/gtk.h>
#include <glib.h>

GtkWidget *status_bar;

void push_item (GtkWidget *widget, gpointer data)
{
  static int count = 1;
  char buff[20];

  g_snprintf(buff, 20, "Item %d", count++);
  gtk_statusbar_push( GTK_STATUSBAR(status_bar), (guint) &data, buff);

  return;
}

void pop_item (GtkWidget *widget, gpointer data)
{
  gtk_statusbar_pop( GTK_STATUSBAR(status_bar), (guint) &data );
  return;
}

int main (int argc, char *argv[])
{

    GtkWidget *window;
    GtkWidget *vbox;
    GtkWidget *button;

    int context_id;

    gtk_init (&argc, &argv);

    /* 새 윈도를 하나 만든다 */
    window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
    gtk_widget_set_usize( GTK_WIDGET (window), 200, 100);
    gtk_window_set_title(GTK_WINDOW (window), "GTK Statusbar Example");
    gtk_signal_connect(GTK_OBJECT (window), "delete_event",
                       (GtkSignalFunc) gtk_exit, NULL);
 
    vbox = gtk_vbox_new(FALSE, 1);
    gtk_container_add(GTK_CONTAINER(window), vbox);
    gtk_widget_show(vbox);
          
    status_bar = gtk_statusbar_new();      
    gtk_box_pack_start (GTK_BOX (vbox), status_bar, TRUE, TRUE, 0);
    gtk_widget_show (status_bar);

    context_id = gtk_statusbar_get_context_id( GTK_STATUSBAR(status_bar), "Statusbar example");

    button = gtk_button_new_with_label("push item");
    gtk_signal_connect(GTK_OBJECT(button), "clicked",
        GTK_SIGNAL_FUNC (push_item), &context_id);
    gtk_box_pack_start(GTK_BOX(vbox), button, TRUE, TRUE, 2);
    gtk_widget_show(button);              

    button = gtk_button_new_with_label("pop last item");
    gtk_signal_connect(GTK_OBJECT(button), "clicked",
        GTK_SIGNAL_FUNC (pop_item), &context_id);
    gtk_box_pack_start(GTK_BOX(vbox), button, TRUE, TRUE, 2);
    gtk_widget_show(button);              

    /* 다른 모든 것들이 한번에 모두 다 보이도록 하기 위해서
     * 항상 윈도를 제일 마지막에 보여준다. */
    gtk_widget_show(window);

    gtk_main ();

    return 0;
}

8.8 텍스트 입력(text entry)

텍스트 입력 widget은 한줄짜리 텍스트 상자 안에 문자를 타이프해 넣거나 또는 그냥 보여줄 수 있게 해준다. 텍스트는 현 입력 widget의 내용을 추가하거나 또는 완전히 대체하는 함수 호출들에 의해 결정된다.

텍스트 입력 widget을 만드는데는 다음 두 함수를 사용한다.

GtkWidget* gtk_entry_new (void);

GtkWidget* gtk_entry_new_with_max_length (guint16 max);

처음 것은 단순히 새로운 입력 widget을 하나 만든다. 그에 반해 두번째 것은 편집가능한 텍스트의 길이 제한을 가진 입력 widget을 만든다.

현 입력의 텍스트를 변경하는데 쓰이는 몇가지 함수가 존재한다.

void gtk_entry_set_text       (GtkEntry    *entry,
                               const gchar *text);
void gtk_entry_append_text    (GtkEntry    *entry,
                               const gchar *text);
void gtk_entry_prepend_text   (GtkEntry    *entry,
                               const gchar *text);

함수 gtk_entry_set_text은 입력된 내용을 완전히 변경한다. 함수 gtk_entry_append_text와 gtk_entry_prepend_text는 현 내용의 앞 또는 뒤에 원하는 텍스트를 덧붙인다.

다음 함수는 현재의 텍스트 입력 위치를 정한다.

void gtk_entry_set_position   (GtkEntry *entry,
                               gint     position);

입력 widget의 내용은 다음 함수 호출에 의해서 알 수 있다. 이것은 나중에 설명할 callback 함수 안에서 사용하는 것이 유용하다.

gchar* gtk_entry_get_text (GtkEntry *entry);

만일 입력된 내용이 타이핑에 의해서 변경되는 것을 원하지 않는다면 편집이 불가능하도록 상태를 바꿀 수 있다.

void gtk_entry_set_editable (GtkEntry *entry,
                             gboolean editable);

이 함수의 editable 인자에 TRUE나 FALSE를 주어 입력 widget이 편집 가능하게 되거나 또는 가능하지 않도록 변경할 수 있다.

만약 패스워드를 입력할 때처럼 입력하는 텍스트가 보이지 않아야 한다면 boolean flag를 갖는 다음 함수를 사용한다.

void gtk_entry_set_visibility (GtkEntry *entry,
                               gboolean visible);

다음 함수를 써서 텍스트의 일정 부분을 선택(selected)되도록 만들 수 있다. 이 기능은 미리 기본 텍스트를 정해 출력하고 이를 사용자가 새로운 입력을 위해서 간단히 지울 수 있게 하는데 자주 쓰인다.

void gtk_entry_select_region (GtkEntry *entry,
                              gint     start,
                              gint     end);

만약에 사용자가 텍스트를 입력하는 것을 알아채고 싶다면, activatechanged 시그널을 연결할 수 있다. Activate는 사용자가 엔터키를 타이프하면, Changed는 텍스트가 전부 다른 것으로 바뀌면 발생한다.(모든 문자가 입력되었거나 지워졌을 때 등)

다음은 텍스트 입력 widget의 한 예이다.

/* entry.c */

#include <gtk/gtk.h>

void enter_callback(GtkWidget *widget, GtkWidget *entry)
{
  gchar *entry_text;
  entry_text = gtk_entry_get_text(GTK_ENTRY(entry));
  printf("Entry contents: %s\n", entry_text);
}

void entry_toggle_editable (GtkWidget *checkbutton,
                                   GtkWidget *entry)
{
  gtk_entry_set_editable(GTK_ENTRY(entry),
                         GTK_TOGGLE_BUTTON(checkbutton)->active);
}

void entry_toggle_visibility (GtkWidget *checkbutton,
                                   GtkWidget *entry)
{
  gtk_entry_set_visibility(GTK_ENTRY(entry),
                         GTK_TOGGLE_BUTTON(checkbutton)->active);
}

int main (int argc, char *argv[])
{

    GtkWidget *window;
    GtkWidget *vbox, *hbox;
    GtkWidget *entry;
    GtkWidget *button;
    GtkWidget *check;

    gtk_init (&argc, &argv);

    /* create a new window */
    window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
    gtk_widget_set_usize( GTK_WIDGET (window), 200, 100);
    gtk_window_set_title(GTK_WINDOW (window), "GTK Entry");
    gtk_signal_connect(GTK_OBJECT (window), "delete_event",
                       (GtkSignalFunc) gtk_exit, NULL);

    vbox = gtk_vbox_new (FALSE, 0);
    gtk_container_add (GTK_CONTAINER (window), vbox);
    gtk_widget_show (vbox);

    entry = gtk_entry_new_with_max_length (50);
    gtk_signal_connect(GTK_OBJECT(entry), "activate",
                       GTK_SIGNAL_FUNC(enter_callback),
                       entry);
    gtk_entry_set_text (GTK_ENTRY (entry), "hello");
    gtk_entry_append_text (GTK_ENTRY (entry), " world");
    gtk_entry_select_region (GTK_ENTRY (entry),
                             0, GTK_ENTRY(entry)->text_length);
    gtk_box_pack_start (GTK_BOX (vbox), entry, TRUE, TRUE, 0);
    gtk_widget_show (entry);

    hbox = gtk_hbox_new (FALSE, 0);
    gtk_container_add (GTK_CONTAINER (vbox), hbox);
    gtk_widget_show (hbox);
                                  
    check = gtk_check_button_new_with_label("Editable");
    gtk_box_pack_start (GTK_BOX (hbox), check, TRUE, TRUE, 0);
    gtk_signal_connect (GTK_OBJECT(check), "toggled",
                        GTK_SIGNAL_FUNC(entry_toggle_editable), entry);
    gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(check), TRUE);
    gtk_widget_show (check);
    
    check = gtk_check_button_new_with_label("Visible");
    gtk_box_pack_start (GTK_BOX (hbox), check, TRUE, TRUE, 0);
    gtk_signal_connect (GTK_OBJECT(check), "toggled",
                        GTK_SIGNAL_FUNC(entry_toggle_visibility), entry);
    gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(check), TRUE);
    gtk_widget_show (check);
                                   
    button = gtk_button_new_with_label ("Close");
    gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
                               GTK_SIGNAL_FUNC(gtk_exit),
                               GTK_OBJECT (window));
    gtk_box_pack_start (GTK_BOX (vbox), button, TRUE, TRUE, 0);
    GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
    gtk_widget_grab_default (button);
    gtk_widget_show (button);
    
    gtk_widget_show(window);

    gtk_main();
    return(0);
}

8.9 색 선택(color selection)

색선택 widget은, 사실 전혀 놀랍지 않겠지만, 직접 색을 선택하는데 쓰인다. 이 복합적인 widget은 사용자로 하여금 RGB(Red, Green, Blue)와 HSV (Hue, Saturation, Value) 세 요소를 통해 색을 고르게 한다. 이는 슬라이더나 입력창을 이용해서 각 요소에 특정 값을 주게하거나 또는, hue-saturation wheel에서 직접 색을 선택하게 함으로써 이루어진다. 필요에 따라서는 색의 투명도도 정해줄 수 있다.

이 색선택 widget은 외부에서 gtk_color_selection_set_color()을 통해 지정했거나 또는 사용자가 직접 현재 색을 변경할 때마다 "color_changed"이라는 단 한가지의 시그널만을 발생시킨다.

이 색선택 widget이 우리에게 무엇을 넘겨주어야 하는지 보자. 이 widget은 gtk_color_selection와 gtk_color_selection_dialog의 두 형태가 있다.

GtkWidget *gtk_color_selection_new(void);

대개 이 함수만을 직접 쓸 수는 없다. 이것은 나중에 어딘가로 연결을 시켜주어야만 하는 GtkColorSelection widget을 만들 따름이다. 이 GtkColorSelection widget은 GtkVBox widget에서 상속된 것이다.

 
GtkWidget *gtk_color_selection_dialog_new(const gchar *title);

대개 이 함수를 색선택 widget을 만드는 데 사용한다. 이는 GtkColorSelectionDialog라는 GtkDialog에서 상속된 widget을 만든다. 이 widget은 GtkColorSelection, GtkHSeparator와 "Ok", "Cancel", "Help"의 버튼 세개를 가진 GtkHBox들을 포함한 GtkFrame widget으로 이루어져있다. GtkColorSelectionDialog structure에서 "ok_button", "cancel_button", "help_button" widget에 접근해서 그 세 버튼들을 직접 다룰 수 있다. (예 : GTK_COLOR_SELECTION_DIALOG(colorseldialog)->ok_button)

void gtk_color_selection_set_update_policy(GtkColorSelection *colorsel, 
                                           GtkUpdateType policy);

이 함수는 갱신 정책을 정한다. 기본 정책은 사용자가 무엇이든 변경하는 그때마다 바로 즉시 현재의 색을 거기에 따라 바꾸는 GTK_UPDATE_CONTINOUS이다. 성능문제가 발생하면 GTK_UPDATE_DISCONTINOUS나 GTK_UPDATE_DELAYED로 바꾸어 줄 수 있다.

void gtk_color_selection_set_opacity(GtkColorSelection *colorsel,
                                     gint use_opacity);
색선택 widget은 색의 투명도(알파채널이라고도 알려진)를 조정하는 기능도 지원한다. 이 기능은 기본 설정에서는 꺼져있다. 이것을 사용하려면 위의 함수를 use_opacity를 TRUE로 해서 호출한다. 마찬가지로 use_opacity를 FALSE로 해서 호출하면 투명도 조정 기능이 꺼진다.

void gtk_color_selection_set_color(GtkColorSelection *colorsel,
                                   gdouble *color);

색상 array(gdouble)와 함께 위 함수를 호출하여 외부에서 현재 색을 지정해 줄 수 있다. 이 array의 길이는 색 투명도 조정 기능을 켜놓았는지 꺼놓았는지에 따라 달라진다. 위치 0은 빨간색, 1은 녹색, 2는 파란색이며 3은 투명도이다.(투명도는 이 기능을 켜놓았을 때만 의미가 있다. 앞에서 나온 gtk_color_selection_set_opacity()에 대한 부분을 보라.) 모든 값은 0.0과 1.0 사이에 있다.

void gtk_color_selection_get_color(GtkColorSelection *colorsel,
                                   gdouble *color);

만일 'color_changed" 시그널을 받았을 때, 현재 색이 어떤 것이지 알고 싶다면 위의 함수를 사용하면 된다. Color는 색 array이다. 이 색 array에 대한 것은 gtk_color_selection_set_color() 함수에 대한 부분을 보라.

다음은 GtkColorSelectionDialog을 사용하는 간단한 예이다. 이 프로그램은 drawing area를 가진 윈도 하나를 만든다. 이것을 클릭하면 color selection dialog가 뜬다. 이를 조정해서 배경색을 바꿀 수 있다.

#include <glib.h>
#include <gdk/gdk.h>
#include <gtk/gtk.h>

GtkWidget *colorseldlg = NULL;
GtkWidget *drawingarea = NULL;

/* 색이 바뀌면 이 함수를 부른다. */

void color_changed_cb (GtkWidget *widget, GtkColorSelection *colorsel)
{
  gdouble color[3];
  GdkColor gdk_color;
  GdkColormap *colormap;

  /* drawing area의 colormap을 얻는다 */

  colormap = gdk_window_get_colormap (drawingarea->window);

  /* 현재 색을 얻는다 */

  gtk_color_selection_get_color (colorsel,color);

  /* unsigned 16 bit 정수(0..65535)로 바꿔서 GdkColor에 넣는다  */

  gdk_color.red = (guint16)(color[0]*65535.0);
  gdk_color.green = (guint16)(color[1]*65535.0);
  gdk_color.blue = (guint16)(color[2]*65535.0);

  /* 색을 할당한다 */

  gdk_color_alloc (colormap, &gdk_color);

  /* 윈도의 배경색을 정한다 */

  gdk_window_set_background (drawingarea->window, &gdk_color);

  /* 윈도를 지운다 */

  gdk_window_clear (drawingarea->window);
}

/* Drawingarea event handler */

gint area_event (GtkWidget *widget, GdkEvent *event, gpointer client_data)
{
  gint handled = FALSE;
  GtkWidget *colorsel;

  /* 버튼이 눌러졌는지 확인한다 */

  if (event->type == GDK_BUTTON_PRESS && colorseldlg == NULL)
    {
      /* Yes, we have an event and there's no colorseldlg yet! */

      handled = TRUE;

      /* color selection dialog를 만든다 */

      colorseldlg = gtk_color_selection_dialog_new("Select background color");

      /* GtkColorSelection widget을 구한다 */

      colorsel = GTK_COLOR_SELECTION_DIALOG(colorseldlg)->colorsel;

      /* "color_changed" 시그널을 widget에 연결한다. */

      gtk_signal_connect(GTK_OBJECT(colorsel), "color_changed",
        (GtkSignalFunc)color_changed_cb, (gpointer)colorsel);

      /* color seclection dialog를 보인다 */

      gtk_widget_show(colorseldlg);
    }

  return handled;
}

/* 윈도를 닫고 핸들러에서 빠져나간다 */

void destroy_window (GtkWidget *widget, gpointer client_data)
{
  gtk_main_quit ();
}

/* Main */

gint main (gint argc, gchar *argv[])
{
  GtkWidget *window;

  /* 툴킷을 초기화하고 gtk와 관련된 명령입력 요소들을 없앤다. */

  gtk_init (&argc,&argv);

  /* toplevel 윈도를 만들고 제목과 policy를 정한다 */

  window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
  gtk_window_set_title (GTK_WINDOW(window), "Color selection test");
  gtk_window_set_policy (GTK_WINDOW(window), TRUE, TRUE, TRUE);

  /* "delete"와  "destroy" 이벤트를 만나면 종료한다 */

  gtk_signal_connect (GTK_OBJECT(window), "delete_event",
    (GtkSignalFunc)destroy_window, (gpointer)window);

  gtk_signal_connect (GTK_OBJECT(window), "destroy",
    (GtkSignalFunc)destroy_window, (gpointer)window);
  
  /* drawingarea를 만든 뒤, 그 크기를 정하고 버튼 이벤트와 연결한다 */

  drawingarea = gtk_drawing_area_new ();

  gtk_drawing_area_size (GTK_DRAWING_AREA(drawingarea), 200, 200);

  gtk_widget_set_events (drawingarea, GDK_BUTTON_PRESS_MASK);

  gtk_signal_connect (GTK_OBJECT(drawingarea), "event", 
    (GtkSignalFunc)area_event, (gpointer)drawingarea);
  
  /* drawingarea를 메인 윈도에 붙이고 둘 다 보여준다. */

  gtk_container_add (GTK_CONTAINER(window), drawingarea);

  gtk_widget_show (drawingarea);
  gtk_widget_show (window);
  
  /* gtk main loop에 들어간다. (다시는 돌아오지 못한다.) */

  gtk_main ();

  /* 까다로운 컴파일러들을 만족시킨다 */

  return 0;
}

8.10 파일 선택(file selection)

파일선택 widget은 파일 대화상자를 보이는 빠르고 간편한 것이다. 이것은 Ok, Cancel, Help 버튼을 가지고 있으며, 프로그래밍 시간을 상당히 줄여준다.

새로운 파일선택 박스를 만들려면 이것을 이용한다.

GtkWidget* gtk_file_selection_new (gchar *title);

파일이름을 세팅하기 위해서 이 함수를 이용한다.

void gtk_file_selection_set_filename (GtkFileSelection *filesel, gchar *filename);

사용자가 키보드 혹은 마우스 클릭으로 입력한 텍스트를 포착하기 위해서는 이 함수를 쓴다.

gchar* gtk_file_selection_get_filename (GtkFileSelection *filesel);

파일선택widget 내부에 포함된 widget들에 대한 포인터들도 있다. 그들은 다음과 같다.

우리가 가장 많이 이용하게 될 것은 ok_button, cancel_button, 그리고 help_button 포인터가 될 것이다.

이것은 testgtk.c 에서 발췌한 것을 목적에 의해 변형한 것이다. 보면 알겠지만, 여기서는 파일선택widget을 하나 만들 뿐이다. Help 버튼이 스크린에 보이겠지만, 아무런 시그널이 연결되어 있지 않으므로 아무 기능이 없을 것이다.

#include <gtk/gtk.h>
/* filesel.c */

/* 선택된 파일 이름을 취해서 그것을 콘솔로 프린트한다. */
void file_ok_sel (GtkWidget *w, GtkFileSelection *fs)
{
    g_print ("%s\n", gtk_file_selection_get_filename (GTK_FILE_SELECTION (fs)));
}

void destroy (GtkWidget *widget, gpointer data)
{
    gtk_main_quit ();
}

int main (int argc, char *argv[])
{
    GtkWidget *filew;

    gtk_init (&argc, &argv);

    /* 파일선택 widget을 하나 만든다. */
    filew = gtk_file_selection_new ("File selection");

    gtk_signal_connect (GTK_OBJECT (filew), "destroy",
                        (GtkSignalFunc) destroy, &filew);
    /* file_ok_sel 함수로 ok_button을 연결시킨다. */
    gtk_signal_connect (GTK_OBJECT (GTK_FILE_SELECTION (filew)->ok_button),
                        "clicked", (GtkSignalFunc) file_ok_sel, filew );

    /* gtk_widget_destroy 함수로 cancel_button을 연결시킨다. */
    gtk_signal_connect_object (GTK_OBJECT (GTK_FILE_SELECTION (filew)->cancel_button),
                               "clicked", (GtkSignalFunc) gtk_widget_destroy,
                               GTK_OBJECT (filew));

    /* 파일이름을 세팅한다. */
    gtk_file_selection_set_filename (GTK_FILE_SELECTION(filew),
                                     "penguin.png");

    gtk_widget_show(filew);
    gtk_main ();
    return 0;
}


다음 이전 차례