FoundationsofGTKDevelopment:Chapter 04

From 흡혈양파의 번역工房
Jump to navigation Jump to search
제 4 장 기본 위젯

기본 위젯

지금까지는 GtkButton을 제외하곤 사용자 상호작용이 가능하도록 설계된 위젯에 대해서는 학습한 내용이 없다. 하지만 이번 장부터는 사용자가 선택하고, 설정을 변경하며, 정보를 입력하도록 해주는 다양한 형태의 위젯을 다룰 것이다.


이러한 위젯으로는 스톡 버튼, 토글 버튼, 체크 버튼, 라디오 버튼, 색상 선택 버튼, 파일 선택자 버튼, 폰트 선택 버튼, 텍스트 엔트리, 숫자 선택 버튼이 있다.


본 장의 마지막에 실린 연습문제는 큰 규모의 애플리케이션에 이러한 위젯을 여러 개 결합할 수 있는 기회를 제공할 것이다.


이번 장에서 익히게 될 주제는 다음과 같다.

  • 스톡 항목이 있는 클릭 가능한 버튼을 사용하는 방법
  • 체크 버튼과 라디오 버튼을 포함해 토글 타입의 버튼을 사용하는 방법
  • 1행의 자유 형식(free-form) 텍스트 입력에 엔트리 위젯을 사용하는 방법
  • 정수나 부동 소수점 수 선택에 스핀 버튼을 사용하는 방법
  • 이용 가능한 특수화 버튼의 유형


스톡 항목 이용하기

GTK+에서 애플리케이션을 생성하면서 문득 동일한 버튼 및 메뉴 항목을 여러 애플리케이션에 걸쳐 사용하고 있다는 사실을 눈치채기 시작할 것이다. 이 때문에 GTK+는 자주 사용하는 메뉴 항목과 버튼을 수용하는 문자열/이미지 쌍인 스톡(stock) 항목을 제공한다.


GTK+는 gtk_button_new_from_stock()을 제공하는데, 이는 미리 정의된 스톡 항목을 이용해 새 버튼을 생성한다. 각 스톡 항목은 버튼에 적용된 연상기호 라벨과 이미지를 포함한다. 스톡 항목의 전체 리스트는 부록 D에서 찾을 수 있다. 각 항목은 수많은 애플리케이션에서 사용되기 때문에 GTK+에 포함되어 있다.


부록 D는 GTK+ 2.10에서 이용 가능한 모든 스톡 아이콘을 포함하지만 애플리케이션을 실행하다보면 아이콘이 자신의 시스템에서 다르게 표시됨을 눈치챌 것이다. 이것은 이용 가능한 스톡 항목들을 항상 보유하고는 있지만 기본 이미지는 사용자의 테마 선택이나 개발자의 선택에 따라 대체될 수 있기 때문이다.


Gtkd note.png gtk_button_new_from_stock() 또는 GTK+에 포함된 다른 스톡 검색 함수에 제공된 스톡 항목을 찾을 수 없는 경우 연상기호 라벨로서 취급될 것이다. 이러한 방법을 통해 버튼이 예측할 수 없는 방식으로 렌더링되는 것을 막을 수 있다.


자신만의 스톡 아이콘을 정의하는 것도 가능하지만 이는 메뉴와 툴바를 주제로 하는 제 9장에서 다룰 것이다. GTK_STOCK_CLOSE 스톡 항목을 이용한 버튼의 예제를 그림 4-1에 소개하겠다.

그림 4-1. GTK_STOCK_CLOSE 스톡 항목


각 스톡 항목은 그것의 문자열 값이나 매크로 정의에 의해 참조될 수 있다. 예를 들어, 리스팅 4-1에서 사용된 close 스톡 항목은 gtk-close 또는 GTK_STOCK_CLOSE로 참조될 수 있다. 하지만 전처리기 지시어(preprocessor directives)는 문자열 값의 편리한 alias에 불과하므로 두 가지 식별자를 모두 학습할 이유는 없다.


Gtkd tip.png 지원되지 않는 항목은 개발자가 코드를 컴파일할 때 플래그가 지정(flagged)되기 때문에 항상 전처리기 지시어를 사용해야 한다. 스톡 항목의 문자열을 이용하는 경우 컴파일러는 오류를 표시(flag)하지 않으므로 유효하지 않은 아이콘이 표시될 것이다.


리스팅 4-1. 스톡 항목 (stockitems.c)

button = gtk_button_new_from_stock (GTK_STOCK_CLOSE);

g_signal_connect_swapped (G_OBJECT (button), "clicked",
        G_CALLBACK (gtk_widget_destroy),
        (gpointer) window);


GTK+ 2.10 배포판부터는 98개의 스톡 항목을 제공하고 있다. 이러한 항목의 리스트는 부록 D에 실려 있다. 제 9장에서 메뉴와 툴바를 다루면서 이러한 스톡 항목을 다시 사용해보겠다.


GTK+ 2.0 배포판부터 일부 스톡 항목이 추가되었기 때문에 가장 최신 버전의 GTK+를 실행하지 않으면 몇 가지 항목을 이용할 수 없음을 기억해야 한다. 이는 새로운 애플리케이션을 생성 시 유념해야 한다. 애플리케이션의 사용자들은 최신 GTK+ 버전을 사용하지 않을지도 모르기 때문이다!


토글 버튼

GtkToggleButton 위젯은 클릭 시 활성화(active) 또는 비활성화(inactive) 상태를 보유하는 GtkButton 타입이다. 활성화되면 누른 상태로 표시된다. 활성화된 토글 버튼을 클릭하면 일반 상태로 돌아갈 것이다. GtkToggleButton에서 파생된 위젯에는 두 가지, GtkCheckButton과 GtkRadioButton이 있다.


세 가지 함수 중 하나를 이용해 새 GtkToggleButton을 생성할 수 있다. 빈 토글 버튼을 생성하려면 gtk_toggle_button_new()를 이용한다. 기본적으로 토글 버튼이 라벨을 포함하길 원한다면 gtk_toggle_button_new_with_label()을 이용한다. 마지막으로 GtkToggleButton은 gtk_toggle_button_new_with_mnemonic()을 이용해 연상기호 라벨을 지원하기도 한다.


그림 4-2는 gtk_toggle_button_new_with_mnemonic() initializer를 호출함으로써 두 개의 연상기호 라벨과 함께 생성된 두 개의 GtkToggleButton 위젯을 보여준다. 스크린샷의 위젯은 리스팅 4-2에 실린 코드로 생성되었다.

그림 4-2. 두 개의 GtkToggleButton 위젯


리스팅 4-2의 예제에서 하나의 토글 버튼이 활성화되면 다른 하나는 이용할 수 없게 된다. 이를 sensitive로 만드는 유일한 방법은 원본 토글 버튼을 비활성화하는 것이다.


리스팅 4-2. 토글 버튼 이용하기 (togglebuttons.c)

#include <gtk/gkt.h>

static void button_toggled (GtkToggleButton*, GtkWidget*);

int main (int argc,
        char *argv[])
{
    GtkWidget *window, *vbox, *toggle1, *toggle2;

    gtk_init (&argc, &argv);

    window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
    gtk_window_set_title (GTK_WINDOW (window), "Toggle Buttons");
    gtk_container_set_border_width (GTK_CONTAINER (window), 10);

    vbox = gtk_vbox_new (TRUE, 5);
    toggle1 = gtk_toggle_button_new_with_mnemonic ("_Deactivate the other one!");
    toggle2 = gtk_toggle_button_new_with_mnemonic ("_No! Deactivate that one!");
    g_signal_connect (G_OBJECT (toggle1), "toggled",
        G_CALLBACK (button_toggled),
        (gpointer) toggle2);
    g_signal_connect (G_OBJECT (toggle2), "toggled",
        G_CALLBACK (button_toggled),
        (gpointer) toggle1);

    gtk_box_pack_start_defaults (GTK_BOX (vbox), toggle1);
    gtk_box_pack_start_defaults (GTK_BOX (vbox), toggle2);

    gtk_container_add (GTK_CONTAINER (window), vbox);
    gtk_widget_show_all (window),

    gtk_main ();
    return 0;
}

/* If the toggle button was activated, set the other as disabled. Otherwise,
* enable the other toggle button. */
static void
button_toggled (GtkToggleButton *toggle,
        GtkWidget *other_toggle)
{
    if (gtk_toggle_button_get_active (toggle))
        gtk_widget_set_sensitive (other_toggle, FALSE);
    else
        gtk_widget_set_sensitive (other_toggle, TRUE);
}


GtkToggleButton 클래스가 추가한 유일한 시그널은 toggled로, 사용자가 버튼을 활성화하거나 비활성화할 때 방출된다. 해당 시그널은 리스팅 4-2에서 하나의 버튼이 나머지 버튼을 이용할 수 없도록 만들기 위해 트리거한다.


리스팅 4-2에는 또 다른 정보, 즉 다수의 위젯이 동일한 콜백 함수를 사용할 수 있다는 정보가 표시되었다. 각 토글 버튼마다 굳이 구분된 콜백 함수를 생성할 필요는 없었는데, 이는 각각이 동일한 기능을 필요로 했기 때문이다. 하나의 시그널을 다수의 콜백 함수로 연결하는 것도 가능하지만 권하진 않는다. 대신 하나의 콜백 함수에 전체 기능을 구현하면 된다.


위젯 플래그 관리하기

한 가지 중요한 위젯의 특성으로, disabled 또는 inactive되는 기능을 들 수 있다. 이는 sensitive 프로퍼티에 의해 관리되는데, gtk_widget_set_sensitive()를 이용해 FALSE로 설정하면 위젯을 비활성화시킬 것이다.

gtk_widget_set_sensitive().

gtk_widget_set_sensitive (other_toggle, FALSE);


감도(sensitivity)는 사실 GtkWidgetFlags 목록에서 제공하는 많은 위젯 플래그 중 하나에 불과하다. 아래에 열거된 위젯 플래그들은 GTK_WIDGET_SET_FLAGS()를 이용해 설정하고, GTK_WIDGET_USET_FLAGS()를 이용해 비활성화할 수 있다. GTK_WIDGET_FLAGS()를 이용해 위젯에 설정된 플래그 리스트를 얻을 수도 있다.

  • GTK_TOPLEVEL: 위젯이 부모 위젯을 갖지 않는다. 이는 주로 창이나 메뉴와 같은 위젯을 대상으로 설정된다. 해당 플래그는 최상위 수준의 위젯의 수명(lifetime)에 걸쳐 항상 설정되어야 한다.
  • GTK_NO_WINDOW: 위젯이 고유의 GdkWindow를 갖고 있지 않아 부모의 GdkWindow로 그리기(drawing)가 이루어진다. 해당 플래그를 이용하면 위젯이 GDK 이벤트를 포착하기 위해 GtkEventBox를 필요로 하는지 검사할 수 있다.
  • GTK_REALIZED: gtk_widget_realize()를 이용해 위젯이 실현되었다. 위젯을 실현취소(unrealize) 할 경우 해당 플래그는 자동으로 unset될 것이다.
  • GTK_MAPPED: gtk_widget_map()을 이용해 위젯이 매핑되었다. 기본적으로 부모 위젯이 visible하면 해당 위젯이 사용자에게 표시된다는 의미다.
  • GTK_VISIBLE: 해당 프래그는 사용자가 위젯을 볼 수 있다는 의미는 아니지만 사용자가 부모 위젯을 볼 수 있을 경우에만 자식 위젯도 볼 수 있을 것이란 의미다.
  • GTK_SENSITIVE: 위젯이 사용자와 상호작용이 가능하고, 버튼이나 키누름 이벤트와 같은 특정 이벤트를 수신할 수 있다.
  • GTK_PARENT_SENSITIVE: 위젯이 sensitive로 설정되기 위해서는 위젯의 부모도 sensitive해야 한다. 따라서 GTK_SENSITIVE는 해당 프로퍼티에 따라 좌우된다.
  • GTK_CAN_FOCUS: 요청 시 위젯은 포커스를 잡을 수 있다.
  • GTK_HAS_FOCUS: 위젯에 포커스가 있으며 gtk_widget_grab_focus()를 이용해 설정 가능하다. 해당 프로퍼티는 GTK_CAN_FOCUS에 의존한다.
  • GTK_CAN_DEFAULT: 위젯이 창의 기본 위젯이 될 수 있다.
  • GTK_HAS_DEFAULT: 위젯이 창의 기본 위젯이다. gtk_widget_grab_default()를 이용해 기본 위젯을 설정할 수 있다.
  • GTK_HAS_GRAB: 위젯이 grab 위젯의 스택에 위치하고, 이벤트를 수신하는 데 대한 개인설정(preference)을 표시한다.
  • GTK_RC_STYLE: GTK+가 자원(RC) 정의에서 위젯의 스타일을 검색하였다. 위젯에 어떤 스타일도 발견할 수 없는 경우 even으로 설정 가능하다.
  • GTK_COMPOSITE_CHILD: 해당 위젯은 그 부모 위젯의 구현에 관한 세부 내용을 제공하고, 사용자에게 표시되어선 안 된다.
  • GTK_APP_PAINTABLE: 설정 시 애플리케이션은 위젯에 그리기가 가능해진다. 이는 GTK+가 현재 내용을 덮어쓰는 것을 예방한다.
  • GTK_RECEIVES_DEFAULT: 설정 시 위젯이 창의 기본 위젯에 해당하지 않더라도 자동으로 기본 액션을 수신할 것이다.
  • GTK_DOUBLE_BUFFERED: 위젯이 사용자에게 노출되면 이중 버퍼링(double-buffered)되어야 한다. 이는 사용자를 위해 창이 하나의 단계에서 업데이트되도록 도와주기 때문에 겉으로 보기에 더 순조로워 보인다.
  • GTK_NO_SHOW_ALL: 이 플래그를 설정할 경우, gtk_widget_show_all()을 호출해도 위젯에 영향을 미치지 않을 것이다. 위젯을 수동으로 표시할 필요가 있다. 이는 위젯이 나머지 애플리케이션과 함께 표시되는 것을 막는다.


toggled 시그널이 방출되면 토글 버튼이 활성화되었는지 확인하길 원할 것인데, 위젯이 활성화될 때나 비활성화될 때 모두 시그널이 방출되기 때문이다. 이는 gtk_toggle_button_get_active()를 이용해 실행이 가능하다. 버튼이 활성화된 경우 TRUE가 리턴되고, 비활성화된 경우 FALSE를 리턴한다. 토글 버튼의 현재 상태는 gtk_toggle_button_set_active()를 이용해 설정하는 수도 있다.

void gtk_toggle_button_set_active (GtkToggleButton *toggle,
        gboolean active);


체크 버튼

대부분의 경우 GtkToggleButton 위젯의 사용을 원치 않을 것인데, 일반 GtkButton 버튼과 정확히 동일하게 생겼기 때문이다. 대신 GTK+는 디스플레이 텍스트 옆에 별개의 토글을 위치시키는 GtkCheckButton 위젯을 생성한다. GtkCheckButton은 GtkToggleButton 클래스에서 파생된다. 해당 위젯의 두 예를 그림 4-3에서 확인할 수 있다.

그림 4-3. 체크 버튼


토글 버튼과 마찬가지로 GtkCheckButton 초기화에 3개의 함수가 제공되는데, gtk_check_button_new(), gtk_check_button_new_with_label(), gtk_check_button_new_with_mnemonic()이 그것들이다. GtkCheckButton은 또한 중요한 toggled 시그널을 상속하는데, 리스팅 4-3에서 사용되었다.


리스팅 4-3. 체크 버튼 상호작용 (checkbuttons.c)

#include <gtk/gtk.h>

static void check_toggled (GtkToggleButton*, GtkWidget*);

int main (int argc,
        char *argv[])
{
    GtkWidget *window, *vbox, *check1, *check2, *close;

    gtk_init (&argc, &argv);

    window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
    gtk_window_set_title (GTK_WINDOW (window), "Check Buttons");
    gtk_container_set_border_width (GTK_CONTAINER (window), 10);

    check1 = gtk_check_button_new_with_label ("I am the main option.");
    check2 = gtk_check_button_new_with_label ("I rely on the other guy.");

    /* Only enable the second check button when the first is enabled. */
    gtk_widget_set_sensitive (check2, FALSE);
    g_signal_connect (G_OBJECT (check), "toggled",
        G_CALLBACK (check_toggled),
        (gpointer) check2);

    close = gtk_button_new_from_stock (GTK_STOCK_CLOSE);
    g_signal_connect_swapped (G_OBJECT (close), "clicked",
        G_CALLBACK (gtk_widget_destroy),
        (gpointer) window);

    vbox = gtk_vbox_new (FALSE, 5);
    gtk_box_pack_start (GTK_BOX (vbox), check1, FALSE, TRUE, 0);
    gtk_box_pack_start (GTK_BOX (vbox), check2, FALSE, TRUE, 0);
    gtk_box_pack_start (GTK_BOX (vbox), close, FALSE, TRUE, 0);

    gtk_container_add (GTK_CONTAINER (window), table);
    gtk_widget_show_all (window);

    gtk_main ();
    return 0;
}

/* If the main check button is active, enable the other. Otherwise, disable
* the supplementary check button. */
static void
check_toggled (GtkToggleButton *check1,
        GtkWidget *check2)
{
    if (gtk_toggle_button_get_active (check1))
        gtk_widget_set_sensitive (check2, TRUE);
    else
        gtk_widget_set_sensitive (check2, FALSE);
}


체크 박스와 관련해 초기화 메서드를 제외한 모든 기능은 GtkToggleButton 클래스와 그 조상 클래스에서 구현된다. GtkCheckButotn은 편의(convenience) 위젯에 불과하여 표준 GtkButton 위젯과 그래픽 차이만 제공할 뿐이다.


라디오 버튼

GtkToggleButton에서 파생된 두 번째 위젯 타입은 라디오 버튼 위젯이다. 사실 GtkRadioButton은 GtkCheckButton에서 파생된다. 라디오 버튼은 일반적으로 그룹화가 가능한 토글이다.


그룹에서 라디오 버튼 하나를 선택하면 나머지는 모두 선택해제될 것이다. 그룹은 한 번에 다수의 라디오 버튼을 선택하는 것을 금한다. 이는 하나의 버튼만 선택되어야 하는 곳에 다수의 옵션을 제공할 수 있도록 해준다.


Gtkd note.png 라디오 버튼을 선택해제하는 버튼은 GTK+에서 제공하지 않으므로 하나의 라디오 버튼으로 이루어진 그룹은 바람직하지 않다. 사용자가 옵션을 선택해제할 수 없기 때문이다! 하나의 버튼만 필요한 경우 GtkCheckButton이나 GtkToggleButton 위젯을 사용해야 한다.


라디오 버튼은 라벨 위젯의 면에서 구분된 원형 토글로서 그려지기 때문에 다른 타입의 토글 버튼과 구별할 수 있다. 라디오 버튼은 GtkCheckButton과 같은 토글로서 그리는 것도 가능하지만 사용자를 혼란스럽고 당황스럽게 만들 수 있기 때문에 실행해서는 안 된다. 수직 박스에 위치한 4개의 라디오 버튼 그룹은 그림 4-4에 보이는 바와 같다.

그림 4-4. 라디오 버튼


라디오 버튼이 올바로 작동하기 위해서는 모두 그룹 내 다른 라디오 버튼으로 참조되어야 한다. 그렇지 않을 경우 모든 버튼은 독립적인 토글 버튼으로서 행동할 것이다. 다수의 라디오 버튼을 사용하는 방법의 예는 리스팅 4-4에서 확인할 수 있다.


리스팅 4-4. 이기적인 토글 버튼 (radiobuttons.c)

#include <gtk/gtk.h>

int main (int argv,
        char *argv[]
{
    GtkWidget *window, *vbox, *radio1, *radio2, *radio3;

    gtk_init (&argv, &argv);

    window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
    gtk_window_set_title (GTK_WINDOW (window), "Radio Buttons");
    gtk_container_set_border_width (GTK_CONTAINER (window), 10);

    /* Create three radio buttons where the second two join radio1's group. */
    radio1 = gtk_radio_button_new_with_label (NULL, "I want to be clicked!");
    radio2 = gtk_radio_button_new_with_label_from_widget (GTK_RADIO_BUTTON (radio1), "Click me instead!");
    radio3 = gtk_radio_button_new_with_label_from_widget (GTK_RADIO_BUTTON (radio1), "No! Click me!");

    /* Note : The radio button you create the new widget from does not matter as
    * long as it is already a member of the group! */
    radio4 = gtk_radio_button_new_with_label_from_widget (GTK_RADIO_BUTTON (radio3), "No! Clickme instead!");

    vbox = gtk_vbox_new (FALSE, 5);
    gtk_box_pack_start_defaults (GTK_BOX (vbox), radio1);
    gtk_box_pack_start_defaults (GTK_BOX (vbox), radio2);
    gtk_box_pack_start_defaults (GTK_BOX (vbox), radio3);
    gtk_box_pack_start_defaults (GTK_BOX (vbox), radio4);

    gtk_container_add (GTK_CONTAINER (window), vbox);
    gtk_widget_show_all (window);

    gtk_main ();
    return 0;
}


그룹에서 첫 번째 라디오 버튼은 아래 세 가지 함수 중 하나를 이용해 생성이 가능하다. 하지만 GtkLabel 위젯을 자식으로 사용하길 원한다면 연상기호 위젯을 사용하는 것도 가능하기 때문에 토글을 키보드에서 활성화시킬 수 있다.

GtkWidget* gtk_radio_button_new (GSList *group);
GtkWidget* gtk_radio_button_new_with_label (GSList *group,
        const gchar *label);
GtkWidget* gtk_radio_button_new_with_mnemonic (GSList *group,
        const gchar *label);


각 호출마다 라디오 그룹에 대해 NULL이 명시되어 있음을 눈치챌 것이다. 라디오 버튼의 그룹을 간단하게 생성하기 위해서는 이들을 그룹 내 다른 위젯으로 연관시켜야 하기 때문이다. 해당 방법을 이용하면 단일 연결된 리스트가 있는 GLIB의 사용을 피할 수 있는데, 리스트가 자동으로 생성 및 관리될 것이기 때문이다. (GSList 데이터 구조는 제 5장과 6장에서 후에 다룰 것이다.)


라벨 없이 라디오 버튼을 포함해 어떤 타입의 토글 버튼이든 생성할 수 있는데, 이런 경우 gtk_container_add()를 이용해 고유의 위젯을 추가할 수 있을 것이다. 프로그램적으로 정의된 라벨이나 연상기호 라벨이 있는 라디오 버튼도 생성 가능하다.


나머지 라디오 버튼을 생성하는 가장 쉬운 방법은 다음 3개의 _from_widget() 중 하나를 이용하면 된다. 첫 번째 라디오 버튼을 생성하는 방법과 비슷하게 이 버튼들 또한 라벨, 연상기호 라벨과 함께 생성되거나 초기 자식 위젯이 없이 생성될 수 있다.

GtkWidget* gtk_radio_button_new_from_widget (GtkRadioButton *group);
GtkWidget* gtk_radio_button_new_with_label_from_widget (GtkRadioButton *group,
        const gchar *label);
GtkWidget* gtk_radio_button_new_with_mnemonic_from_widget (GtkRadioButton *group,
        const gchar *label);


이미 존재하는 라디오 버튼에 초기화 함수를 참조하면 이들 중 하나를 생성한다. GTK+는 명시된 위젯으로부터 그룹으로 새 라디오 버튼을 추가할 것이다. 이 때문에 바람직한 라디오 그룹 내에 이미 존재하는 위젯으로만 참조해야 한다.


마지막으로, 그룹 내 모든 라디오 버튼은 toggled 시그널로 연결되어야 한다. 라디오 버튼이 선택되면 두 개의 버튼만 toggled 시그널을 방출할 것인데, 하나는 선택되고 하나는 선택해제될 것이기 때문이다. 라디오 버튼을 모두 toggled로 연결하지 않으면 모든 라디오 버튼 시그널을 포착할 수 없을 것이다.


텍스트 엔트리

GtkEntry 위젯은 단일 행으로 된 자유 형식의 텍스트 엔트리 위젯이다. 이는 일반적인 방식으로 구현되므로 많은 타입의 해답에 들어맞도록 만들 수 있다. 텍스트 엔트리, 비밀번호 엔트리, 심지어 숫자 선택에도 사용 가능하다.


GtkEntry는 GtkEditable 인터페이스도 구현하는데, 이것은 텍스트의 선택을 처리하도록 생성된 수많은 함수를 제공한다. 그림 4-5에 GtkEntry 위젯의 사용 예를 실었다. 해당 텍스트 엔트리는 비밀번호 엔트리에 사용된다.

그림 4-5. 비밀번호 텍스트 엔트리


Gtkd note.png GtkEditable은 인터페이스라고 불리는 특별한 타입의 객체다. 인터페이스는 다수의 위젯이 구현하는 APIs의 집합으로서, 일관성을 위해 사용된다. 인터페이스의 구현 방법과 개발자의 위젯에서 이를 활용하는 방법은 제 11장에서 다룰 것이다.


GtkEntry 위젯은 모든 텍스트를 표준 문자열로 간주한다. 일반 텍스트와 비밀번호의 유일한 차이는 비가시성(invisibility) 문자라고 불리는 특수 문자가 비밀번호 내용 대신 표시된다는 점이다. 리스팅 4-5는 비밀번호 엔트리에 GtkEntry 위젯을 사용하는 방법을 보여준다. 일반 텍스트 엔트리에 GtkEntry 위젯을 사용하길 원할 경우 visibility만 켜면 된다.


리스팅 4-5. 사용자 정보 검색하기 (entries.c)

#include <gtk/gtk.h>

int main (int argc,
        char *argv[])
{
    GtkWidget *window, *vbox, *hbox, *question, *label, *pass;

    gtk_init (&argc, &argv);

    window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
    gtk_window_set_title (GTK_WINDOW (window), "Password?");
    gtk_container_set_border_width (GTK_CONTAINER (window), 10);

    str = g_strconcat ("What is the password for ", g_get_user_name(), "?", NULL);
    question = gtk_label_new (str);
    label = gtk_label_new ("Password:");

    /* Create a new GtkEntry widget and hide its content from view. */
    pass = gtk_entry_new ();
    gtk_entry_set_visibility (GTK_ENTRY (pass), FALSE);
    gtk_entry_set_invisible_char (GTK_ENTRY (pass), '*');

    hbox = gtk_hbox_new (FALSE, 5);
    gtk_box_pack_start_defaults (GTK_BOX (hbox), label);
    gtk_box_pack_start_defaults (GTK_BOX (hbox), pass);

    vbox = gtk_vbox_new (FALSE, 5);
    gtk_box_pack_start_defaults (GTK_BOX (vbox), question);
    gtk_box_pack_start_defaults (GTK_BOX (vbox), hbox);

    gtk_container_add (GTK_CONTAINER (window), vbox);
7gtk_widget_show_all (window);

    gtk_main ();
    return 0;
}


엔트리 프로퍼티

GtkEntry 위젯은 매우 유연한데, 그 이유는 가장 많은 사례에서 사용되도록 설계되었기 때문이다. 클래스가 제공하는 광범위한 프로퍼티 집합에서 찾아볼 수 있다. 그 중에서 가장 중요한 사용의 예제를 이번 절에 포함시켰다. 프로퍼티 전체 리스트는 부록 A를 참고한다.


GtkEntry 위젯에서 텍스트가 항상 편집 가능해야 하는 것은 아니다. gtk_editable_set_editable()을 이용해 엔트리를 편집 불가하게 설정하면 사용자는 텍스트를 편집할 수 없을 것이다. 하지만 사용자는 여전히 GtkEntry 위젯의 선택 및 복사 기능은 사용할 수 있을 것인데, 이 프로퍼티는 위젯을 insensitive하게 설정하는 것과 동일한 효과를 갖기 때문이다.

void gtk_editable_set_editable (GtkEditable *editable,
        gboolean is_editable);


종종 값의 문자열 제한 때문에 엔트리 위젯에 입력한 자유 형식의 텍스트 길이를 제한하길 원하는 경우가 있을 것이다. 아래 함수 프로토타입에서 gtk_entry_set_max_length()는 엔트리의 텍스트를 max_length 문자로 제한한다. 이는 사용자명, 비밀번호, 또는 길이에 민감한 정보의 길이를 제한할 때 유용할 것이다.

void gtk_entry_set_max_length (GtkEntry *entry,
        gint max_length);


비가시성 문자는 GTK+에서 비밀번호 엔트리 사용을 가능하게 한다. 비가시성 문자는 엔트리에 실제 비밀번호 내용을 대체하게 될 문자로, gtk_entry_set_invisible_char()를 이용해 설정 가능하다. 엔트리에 대한 기본값은 별표이다.

void
 gtk_entry_set_invisible_char (GtkEntry *entry,
        gunichar inv_char);
void gtk_entry_set_visibility (GtkEntry *entry,
        gboolean visible);


비가시성 문자를 명시한 후에는 gtk_entry_set_visibility()를 이용해 visibility를 FALSE로 설정하여 입력된 텍스트를 모두 숨길 수 있다. 숨겨져 있더라도 엔트리의 실제 내용은 프로그램적으로 검색이 가능하다.


GtkEntry 위젯으로 텍스트 삽입하기

GtkEntry 위젯으로 텍스트를 삽입하는 방법에는 여러 가지가 있다. 그 중 가장 간단한 방법은 gtk_entry_set_text()를 이용하는 방법인데, 이를 사용하면 텍스트 엔트리 내용을 주어진 문자열로 오버라이드할 것이다. 하지만 위젯이 표시하는 현재 텍스트를 더 이상 신경 쓰지 않을 때에만 유용하다.

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


GtkEntry가 표시하는 현재 텍스트는 gtk_entry_get_text()를 이용해 검색이 가능하다. 이 문자열은 위젯에 의해 내부적으로 사용되며 어떤 방식으로든 "절대로" free되거나 수정되어선 안 된다.


gtk_editable_insert_text()를 이용해 GtkEntry 위젯으로 텍스트를 삽입하는 방법도 있다. 해당 함수는 삽입해야 할 텍스트의 길이를 바이트로 수락하고, 텍스트가 삽입되어야 할 위치를 수락한다.

void gtk_editable_insert_text (GtkEditable *editable,
        const gchar *text,
        gint length_of_text,
        gint *position);


텍스트를 앞에 추가(prepend)하거나 뒤에 추가(append)하기 위한 함수들도 있지만 꼭 필요한 것은 아닌데, gtk_editable_insert_text()로 각각 0과 -1의 위치를 제공함으로써 해당 함수들을 실행할 수 있기 때문이다.


GtkEntry Text 조작하기

gtk_editable_delete_text()를 이용하면 텍스트 엔트리에서 특정 내용을 쉽게 삭제할 수 있다. 이는 명시된 두 위치 사이의 모든 텍스트를 제거하지만 끝 위치에 있는 문자는 제거하지 않을 것이다.

void gtk_editable_delete_text (GtkEditable *editable,
        gint start_pos,
        gint end_pos);


gtk_editable_delete_text()를 이용 시 개발자가 위치를 명시하는 순서는 중요하지 않다. 그리고 개발자가 끝 위치를 -1으로 명시하면 텍스트의 시작부터 끝 위치까지 문자들이 삭제될 것이다.


텍스트의 특정 영역을 자동 선택되도록 만들고 싶다면 gtk_editable_select_region()을 이용할 수 있다. 텍스트를 삭제할 때와 마찬가지로 끝 위치에 -1을 명시하면 내용 시작부터 끝까지 모든 텍스트를 선택할 것이다. 수동 및 자동 선택은 아래와 같이 몇 가지 함수를 가능하게 한다.

void gtk_editable_select_region (GtkEditable *editable,
        gint start_pos,
        gint end_pos);


텍스트를 선택할 수 있으니 이제 선택내용을 삭제할 수 있게 된다면 유용하겠다. 이는 gtk_editable_delete_selection()을 이용하면 쉽게 가능하다. 해당 함수는 선택된 텍스트를 모두 삭제하고, 선택되지 않은 텍스트를 모두 남겨둘 것이다.

void gtk_editable_delete_selection (GtkEditable *editable);


위젯의 텍스트 내용을 모두 검색하는 기능에 더해 gtk_editable_get_chars()를 이용하면 텍스트의 선택내용만 검색할 수 있다. 이는 명시된 문자열의 복사본을 리턴할 것인데, 사용을 마치면 g_free()를 이용해 해제(free)시켜야만 한다.

gchar* gtk_editable_get_chars (GtkEditable *editable,
        gint start_pos,
        gint end_pos);


아래 3개의 함수는 다양한 클립보드 함수를 실행한다. 기본적으로는 엔트리에 자르기(Ctrl+X), 복사하기(Crtl+V), 붙여넣기(Ctrl+V)를 내장시키는 키보드 가속기들이 있다. 따라서 보통 GtkEntry 위젯을 사용할 때는 클립보드 기능을 구현할 필요가 없다.

void gtk_editable_cut_clipboard (GtkEditable *editable);
void gtk_editable_copy_clipboard (GtkEditable *editable);
void gtk_editable_paste_clipboard (GtkEditable *editable);


스핀 버튼

GtkSpinButton 위젯은 숫자 선택 위젯으로 정수와 부동 소수점 수를 처리할 수 있다. GtkEntry에서 파생되므로 GtkSpinButton은 그 함수와 시그널을 모두 상속한다.

Adjustment

GtkSpinButton 위젯을 다루기 전에 GtkAdjustment 클래스를 이해해야 한다. GtkAdjustment 는 GTK+에서 위젯으로 간주되지 않는 몇 안되는 클래스들 중 하나인데, GtkObject에서 직접 파생되기 때문이다. 스핀 버튼, 뷰 포트, GtkRange에서 파생된 다수의 위젯을 포함해 여러 위젯에 사용된다.


새로운 adjustment는 gtk_adjustment_new()를 이용해 생성되지만 초기화 시 GTK_ADJUSTMENT()를 이용해 형변환(cast)되는 것이 보통인데, GtkObject로서 저장공간은 실용적이지 않기 때문이다. 위젯으로 추가되고 나면 adjustment의 메모리 관리는 위젯이 관리하므로 객체의 이러한 측면에 대해서는 염려하지 않아도 된다.

GtkObject* gtk_adjustment_new (gdouble initial_value,
        gdouble lower_range,
        gdouble upper_range,
        gdouble step_increment,
        gdouble page_increment,
        gdouble page_size);


새로운 adjustment는 6개의 매개변수로 초기화되며, 그 리스트는 아래와 같다.

  • initial_value: 초기화될 때 adjustment에 의해 값이 보관된다. 이는 GtkAdjustment 클래스의 value 프로퍼티에 해당한다.
  • lower_range: adjustment가 보유하도록 허용하는 최소값이다. 이는 GtkAdjustment 클래스의 lower 프로퍼티에 해당한다.
  • upper_range: adjustment가 보유하도록 허용하는 최대값이다. 이는 GtkAdjustment 클래스의 upper 프로퍼티에 해당한다.
  • step_increment: 최소의 변경을 가능하게 만드는 increment. 1부터 10까지 모든 정수를 세고 싶다면 increment는 1로 설정될 것이다.
  • page_size: 페이지의 크기. GtkSpinButton에서 이 값은 별로 사용되지 않으므로 page_increment와 같은 값 또는 0으로 설정되어야 한다.


GtkAdjustment 클래스가 제공하는 유용한 시그널로 2개가 있는데, changed와 value-changed이다. changed 시그널은 adjustment 프로퍼티 중 value 프로퍼티를 제외하고 하나 또는 그 이상이 수정될 때 방출된다. value-changed 시그널은 adjustment의 현재 값이 수정되면 방출된다.


스핀 버튼 예제

스핀 버튼 위젯은 사용자가 위, 아래 방향 화살표로 증가, 감소시킴으로써 정수나 부동 소수점 수를 선택하도록 해준다. 사용자는 키보드를 이용해 값을 입력할 수 있으며, 범위를 벗어날 경우 가장 가까운 허용 값으로 표시될 것이다. 그림 4-6는 정수와 부동 소수점을 표시하는 두 개의 스핀 버튼의 실행 모습을 보여준다.

그림 4-6. 스핀 버튼


앞서 언급하였듯 스핀 버튼은 정수 또는 부동 소수점 수를 표시하는 데에 사용할 수 있다. 실제로 숫자는 gdouble 값으로서 보관된다. 스핀 버튼은 숫자를 올바른 소수 자리수로 올·내림하는 일을 처리한다. 리스팅 4-6은 정수와 부동 소수점 수의 스핀 버튼을 모두 생성한다.


리스팅 4-6. 정수와 부동 소수점 수 선택 (spinbuttons.c)

#include <gtk/gtk.h>

int main (int argc,
        char *argv[])
{
    GtkWidget *window, *spin_int, *spin_float, *vbox;
    GtkAdjustment *integer, *float_pt;

    gtk_init (&argc, &argv);

    window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
    gtk_window_set_title (GTK_WINDOW (window), "Spin Buttons");
    gtk_container_set_border_width (GTK_CONTAINER (window), 10);
    gtk_widget_set_size_request (window, 150, 100);

    /* Create two new adjustments. The first spans between 0 and 10, starting at 5 and
    * moves in increments of 1. The second spans between 0 and 1, starting at 0.5 and
    * moves in increments of 0.1. */
    integer = GTK_ADJUSTMENT (gtk_adjustment_new (5.0, 0.0, 10.0, 1.0, 2.0, 2.0));
    float_pt = GTK_ADJUSTMENT (gtk_adjustment_new (0.5, 0.0, 1.0, 0.1, 0.5, 0.5));

    /* Create two new spin buttons. The first will display no decimal places and the
    * second will display one decimal place. */
    spin_int = gtk_spin_button_new (integer, 1.0, 0);
    spin_float = gtk_spin_button_new (float_pt, 0.1, 1);

    vbox = gtk_vbox_new (FALSE, 5);
    gtk_box_pack_start_defaults (GTK_BOX (vbox), spin_int);
    gtk_box_pack_start_defaults (GTK_BOX (vbox), spin_float);

    gtk_container_add (GTK_CONTAINER (window), vbox);
    gtk_widget_show_all (window);

    gtk_main ();
    return 0;
}


스핀 버튼을 생성하기 전에는 adjustment를 생성해야 한다. NULL adjustment로 스핀 버튼을 초기화할 수도 있지만 insensitive로 설정될 것이다.


자신의 adjustment를 초기화한 후에는 gtk_spin_button_new()를 이용해 새 스핀 버튼을 생성할 수 있다. 초기화 함수에서 또 다른 두 개의 매개변수는 표시할 소수 자리수와 스핀 버튼의 이동 속도(climb rate)를 명시한다. 이동 속도는 화살표 버튼을 누르면 증가하거나 감소되는 값을 의미한다.

GtkWidget *gtk_spin_button_new (GtkAdjustment *adjustment,
        gdouble climb_rate,
        guint digits);


대안적인 방법으로 gtk_spin_button_new_with_range()를 이용해 새 스핀 버튼을 생성할 수도 있는데, 이는 개발자가 명시하는 최소, 최대, 단계(step) 값을 기반으로 새 adjustment를 자동으로 생성할 것이다. 초기 값은 기본적으로 step_increment 에 10을 곱한 페이지 증가(page increment)에 최소 값을 더한 값으로 설정된다. 위젯의 정밀도는 step_increment의 값으로 자동 설정된다.

GtkWidget* gtk_spin_button_new_with_range (gdouble minimum_value,
        gdouble maximum_value,
        gdouble step_increment);


gtk_spin_button_set_digits()를 호출하면 스핀 오프의 새 정밀도를 설정하고, gtk_spin_button_set_value()를 이용하면 새 값을 설정할 수 있다. 값이 스핀 버튼의 범위를 벗어나면 자동으로 수정될 것이다.

void gtk_spin_button_set_value (GtkSpinButton *spin_button,
        gdouble value);


수평 및 수직 스케일

스케일(scale)이라 불리는 또 다른 타입의 위젯은 정수나 부동 소수점 수를 선택하는 수평 또는 수직 슬라이더를 제공하도록 해준다. GtkHScale은 수평적 스케일 위젯이고, GtkVScale은 수직적 스케일 위젯이다. 두 클래스 모두 GtkScale에서 파생되어 프로퍼티, 시그널, 함수를 제공한다.


GtkScale 위젯의 기능은 GtkSpinButton과 크게 다르지 않다. 값은 슬라이더를 이동시켜 선택되기 때문에 사용자가 값을 입력하지 못하도록 제한할 때 종종 사용된다. 그림 4-7은 두 개의 수평적 스케일 위젯의 스크린샷이다.

그림 4-7. 수평적 스케일 위젯


스케일은 슬라이더를 이용해 숫자를 선택한다는 점을 제외하면 스핀 버튼과 기본적으로 동일한 기능을 제공한다. 위젯 간 유사점을 표시하기 위해 리스팅 4-7은 리스팅 4-6과 동일한 기능을 구현하는데, 두 개의 슬라이더는 사용자가 정수와 부동 소수점 수를 선택하도록 해준다.


리스팅 4-7. 스케일을 이용한 정수 및 부동 소수점 수 선택 (scales.c)

#include <gtk/gtk.h>

int main (int argc,
        char *argv[])
{
    GtkWidget *window, *scale_int, *scale_float, *vbox;

    gtk_init (&argc, &argv);

    window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
    gtk_window_set_title (GTK_WINDOW (window), "Scales");
    gtk_container_set_border_width (GTK_CONTAINER (window), 10);
    gtk_widget_set_size_request (window, 250, -1);

    /* Create a scale that scrolls integers and one that scrolls floating point. */
    scale_int = gtk_hscale_new_with_range (0.0, 10.0, 1.0);
    scale_float = gtk_hscale_new_with_range (0.0, 1.0, 0.1);

    /* Set the number of decimal places to display for each widget. */
    gtk_scale_set_digits (GTK_SCALE (scale_int), 0);
    gtk_scale_set_digits (GTK_SCALE (scale_float), 1);

    /* Set the position of the value with respect to the widget. */
    gtk_scale_set_value_pos (GTK_SCALE (scale_int), GTK_POS_RIGHT);
    gtk_scale_set_value_pos (GTK_SCALE (scale_float), GTK_POS_LEFT);

    vbox = gtk_vbox_new (FALSE, 5);
    gtk_box_pack_start_defaults (GTK_BOX (vbox), scale_int);
    gtk_box_pack_start_defaults (GTK_BOX (vbox), scale_float);

    gtk_container_add (GTK_CONTAINER (window), vbox);
    gtk_widget_show_all (window);

    gtk_main ();
    return 0;
}


새로운 스케일 위젯을 생성하는 방법에는 두 가지가 있다. 첫 번째는 gtk_hscale_new() 또는 gtk_vscale_new()를 이용하는 방식으로, 이는 스케일이 어떻게 작동할 것인지 정의하는 GtkAdjustment를 수락한다.

GtkWidget *gtk_hscale_new (GtkAdjustment *adjustment);


위의 방법이 아니라면, gtk_hscale_new_with_range() 또는 gtk_vscale_new_with_range()를 이용해 스케일을 생성하는 방법도 있다. 해당 함수는 스케일의 최소값, 최대값, 단계 증가를 수락한다.

GtkWidget *gtk_hscale_new_with_range (gdouble minimum,
        gdouble maximum,
        gdouble step);


스케일의 값은 항상 gdouble로서 보관되기 때문에 자신이 원하는 기본값이 아닐 경우 gtk_scale_set_digits()를 이용해 표시하고자 하는 소수 자리수를 정의해야 할 것이다. 기본 소수 자리수는 단계 증가에 제공되는 소수 자리수를 기반으로 계산된다. 예를 들어, 단계 증가가 0.01일 경우 기본값으로 소수 둘째자리까지 표시될 것이다.

void gtk_scale_set_digits (GtkScale *scale,
        gint digits);


사용하는 스케일 위젯의 타입에 따라 값이 표시되는 위치를 변경하고 싶은 경우에는 gtk_scale_set_value_pos()를 이용한다. 위치는 GtkPositionType 목록에서 정의되는데, GTK_POST_LEFT, GTK_POS_RIGHT, GTK_POS_TOP, GTK_POS_BOTTOM이 있다. 사용자에게 값을 모두 숨기려면 gtk_scale_set_draw_value()를 이용할 수도 있다.

void gtk_scale_set_value_pos (GtkScale *scale,
        GtkPositionType pos);


GtkScale은 GtkRange라고 불리는 위젯에서 파생된다. 해당 위젯은 추상적 타입으로, adjustment를 처리하는 기능을 제공한다. 이 때문에 스케일의 현재 값을 검색하려면 gtk_range_get_value()를 이용해야 한다. GtkRange는 사용자가 스케일 위치를 변경할 때 방출되는 value-changed 시그널과도 제공한다.


위젯 스타일

다음 여러 절에서는 위젯 스타일 프로퍼티를 편집할 것이므로 GtkStyle 구조와 자원 파일에 대해 학습하기에 알맞은 때가 되었다. 자원 파일은 향후 맞춤설정을 허용하기 위해 런타임 시 자신의 애플리케이션에 로딩 및 적용할 수 있는 스타일 설정의 외부 컬렉션이다.

GtkStyle 구조

GtkWidget은 5개의 public 멤버를 가지는데, 이들을 아래 코드 조각에 표시하였다. 스타일 정보, 크기 요청, 크기 할당, 화면에 위젯을 그리는 데에 사용되는 GdkWindow, 부모 위젯에 대한 포인터가 해당한다.

typedef struct
{
    GtkStyle *style;
    GtkRequisition requisition;
    GtkAllocation allocation;
    GdkWindow *window;
    GtkWidget *parent;
} GtkWidget;


GtkStyle 구조는 위젯에 관한 드로잉(drawing) 정보를 저장한다. 구조의 내용은 아래와 같다.

typedef struct
{
    GdkColor fg[5] /* The foreground color for most widgets. */
    GdkColor bg[5] /* The background color for most widgets. */
    GdkColor light[5] /* Lighter colors used for creating widget shadows. */
    GdkColor dark[5] /* Darker colors used for creating widget shadows. */
    GdkColor mid[5] /* The color midway between light and dark. */
    GdkColor text[5] /* The text color for most text widgets. */
    GdkColor base[5] /* The background color used for text-editing widgets. */
    GdkColor text_aa[5]; /* Used for anti-aliased text colors. */
    GdkColor black, white; /* Colors that represent "Black" and "White". */

    PangoFontDescription *font_desc; /* The default text font. */
    gint xthickness, ythickness; /* Thickness of lines. */
    GdkPixmap *bg_pixmap[5]; /* Background image to use for a widget. */

    /* Graphics contexts that hold drawing properties for each color and state. */
    GdkGC *fg_gc [5], *bg_gc [5], *light_gc[5], *dark_gc[5], *mid_gc[5], *text_gc[5],
    *base_gc[5], *text_aa_gc[5];
    GdkGC *black_gc, *white_gc;
} GtkStyle;


GtkStyle 구조에는 많은 객체들이 있다. 각 객체는 사용자의 스타일에 따라 설정되는 기본값을 갖게 될 것이므로, 이 값을 오버라이드하는 것이 항상 바람직한 것은 아니다. 하지만 필요하다면 위젯의 GtkStyle의 편집을 통해 위젯의 표시 방법을 가장 간단하게 변경할 수 있다.


스타일 프로퍼티 중 다수는 파일 요소로 구성된 배열임을 눈치챌 것이다. 이는 각 요소들이 아래에 소개된 다섯 가지의 위젯 상태 중 하나에 대해 서로 다른 값을 가질 수 있기 때문이다.

  • GTK_STATE_NORMAL: 정상 작동 중인 위젯.
  • GTK_STATE_ACTIVE: 토글을 누름해제할 때와 같은 활성(active) 위젯.
  • GTK_STATE_PRELIGHT: 마우스 포인터가 위젯 위에 있을 때 위젯으로, 버튼 클릭에 반응할 것이다.
  • GTK_STATE_SELECTED: 위젯 또는 그 텍스트를 선택할 때 위젯.
  • GTK_STATE_INSENSITIVE: 위젯이 비활성화되고 사용자에게 반응하지 않을 것이다.


자원 파일

GTK+는 애플리케이션이 자원 파일(RC 파일)이라 불리는 사용자 정의 스타일을 사용할 수 있는 방도를 제공한다. RC 파일은 사용자가 위젯 타입이나 개별적 위젯에 대한 스타일을 정의하도록 해주어, 사용자의 개인설정에 맞도록 변경할 수 있다. 이는 주로 다른 애플리케이션과 함께 사용자의 홈 디렉터리로 보관되므로 사용자는 설정을 변경할 권한을 갖는다.


자원 파일을 로딩하기 위해서는 애플리케이션을 로딩 시 gtk_rc_parse()를 호출해야 한다. 그러면 적절한 모든 위젯에 자동으로 스타일을 적용할 것이다.

void gtk_rc_parse (const gchar *filename);


또 RC 파일로부터 직접 위젯을 참조하고 싶다면 gtk_widget_set_name()을 이용해 위젯에 대한 유일한 이름을 설정할 필요가 있다. 해당 이름은 위젯의 스타일과 그 자식들의 스타일을 설정하기 위해 RC 파일에서 사용될 것이다.


리스팅 4-8에서는 간단한 RC 파일의 예제를 제시하였다. 이 예제에서는 각각이 수많은 프로퍼티를 포함하는 위젯 스타일이 다수 생성된다.


리스팅 4-8. 위젯 스타일 정의하기 (.gtkrc)

style "widgets"
{
    xthickness = 2
    ythickness = 2

    fg[ACTIVE] = "#FFFFFF"
    fg[SELECTED] = "#003366"
    fg[NORMAL] = "#CCCCCC"
    fg[PRELIGHT] = "#FFFFFF"
    fg[INSENSITIVE] = "#999999"

    bg[ACTIVE] = "#003366"
    bg[SELECTED] = "#FFFFFF"
    bg[NORMAL] = "#666666"
    bg[PRELIGHT] = "#003366"
    bg[INSENSITIVE] = "#666666"
}

style "labels" = "widgets" {
    font_name = "Sans Bold 14"
}

style "buttons" = "widgets" {
    GtkButton::inner-border = { 10, 10, 10, 10 }
}

style "checks" = "buttons" {
    GtkCheckButton::indicator-size = 25
}

class "GtkWindow" style "widgets"
class "GtkLabel" style "labels"
class "GtkCheckButton" style "checks"
class "Gtk*Button" style "buttons"


그림 4-8은 리스팅 4-8에 표시된 RC 파일을 활용하는 애플리케이션을 보여준다. 색상과 글꼴은 앞 장에 실린 예제들과 다르다.

그림 4-8. .gtkrc를 이용한 애플리케이션 예제


RC 파일의 모든 위젯에 이용 가능한 표준 스타일을 살펴보려면 부록 C를 참고해야 한다. 이번 절에서는 자신만의 애플리케이션에서 그러한 스타일을 적용하는 방법을 가르칠 것이다.


스타일은 앞의 예제에 표시된 class 지시어를 이용해 위젯 타입에 의해 적용될 수 있다. 이번 예제에서는 button 스타일이 모든 Gtk*Button* 위젯으로 적용되는데, 별표는 와일드카드(wildcard)로 사용된다. 이는 일치하는 클래스명을 가진 애플리케이션 내의 모든 위젯에 적용된다.

class "Gtk*Button" style "buttons"


위젯 스타일을 적용하기 위한 두 번째 방법은 widget 지시어를 이용해 계층구조 패턴을 기반으로 한다. 이 예제는 GtkButton 타입으로 된 widgetname의 모든 간접 및 직접 자식들에게 stylename 스타일을 적용한다.

widget "widgetname.*.GtkButton" style "stylename"


0개 또는 그 이상의 문자에 일치하는 별표 와일드카드 외에 물음표 와일드카드를 이용하면 어떤 문자든 하나 또는 이상으로 매칭할 수 있다. 또 위젯 계층구조는 마침표(period)를 이용해 표시되는데, 마침표의 우측에 있는 위젯은 좌측에 위치한 위젯의 자식이다.


widget 지시어에서 발생하는 문제는, 위젯에 대한 이름이 명시되는 경우 클래스명 대신 그 이름이 사용되어야만 한다는 데에 있다. 위젯 클래스만 사용하길 원한다면 widget_class 지시어를 사용할 수 있다. 이는 모든 위젯명을 무시하고, 명시된 패턴을 따르는 모든 위젯에 스타일을 적용하도록 해준다.

widget_class "GtkWindow.*.GtkLabel" style "stylename"


기본 스타일 지시어 외에 RC 파일에서 지원되는 최상위 수준 지시어를 아래 목록에서 표시한다.

  • include: 다른 자원 파일을 포함한다. 절대 파일명과 상대 파일명 중 하나를 명시할 수 있다.
  • module_path: 콜론으로 구분된 경로 리스트로, RC 파일이 참조하는 테마 엔진을 검색할 것이다.
  • *pixmap_path: 콜론으로 구분된 경로 리스트로, RC 파일이 참조하는 테마 엔진을 검색할 것이다.


애플리케이션에서 RC 파일을 이용할 계획이라면 사용자에게 예제 파일을 제공해야 함을 명심한다. pound (#) 부호를 이용해 RC 파일에 주석을 추가하여 사용자가 내용을 편집하도록 도와줄 수 있다.


이번 절은 RC 파일의 매우 기초적인 내용만 소개하였다. 더 많은 정보는 부록 C를 참조해야 한다. GTK+와 함께 테마와 RC 파일을 학습하는 데 도움이 되는 자원은 http://art.gnome.org에서 찾을 수 있다.


추가 버튼

GtkButton 위젯은 자신만의 커스텀 버튼을 생성하도록 해주지만 GTK+는 개발자가 마음대로 이용할 수 있도록 3개의 추가 버튼 위젯, 즉 색상 선택 버튼, 파일 선택자 버튼, 글꼴 선택 버튼을 제공한다.


아래의 각 절은 세 가지 위젯을 다루면서 GdkColor 구조, 파일 필터, Pango 글꼴처럼 중요한 개념들도 함께 다룰 것이다. 이러한 개념들은 이후 다른 여러 장에서 사용될 것이기 때문에 지금 알아두면 유용할 것이다.


색상 버튼

GtkColorButton 위젯은 사용자들이 특정 색상을 선택할 수 있도록 개발자에게 간단한 방도를 제공한다. 이러한 색상은 6자리의 16진 값이나 RGB 값으로 명시할 수 있다. 색상 자체는 버튼의 자식 위젯으로 설정된 직사각형 블록에 선택된 색상을 표시한다. 이 예는 그림 4-9에서 확인할 수 있다.

그림 4-9. 색상 선택 대화상자


GtkColorButton 예제

색상 버튼을 클릭하면 사용자가 색상 휠에서 선택내용을 확인하거나 색상값을 입력하도록 해주는 대화상자가 열린다. 색상 휠이 제공되면 사용자는 색상의 수치값을 꼭 알 필요가 없다. 리스팅 4-9는 애플리케이션에서 GtkColorButton 위젯을 사용하는 방법을 보여준다.


리스팅 4-9. 색상 버튼과 GdkColors (colorbuttons.c)

#include <gtk/gtk.h>

static void color_changed (GtkColorButton*, GtkWidget*);

int main (int argc,
        char *argv[])
{
    GtkWidget *window, *button, *label, *hbox;
    GdkColor color;

    gtk_init (&argc, &argv);

    window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
    gtk_window_set_title (GTK_WINDOW (window), "Color Button");
    gtk_container_set_border_width (GTK_CONTAINER (window), 10);

    /* Set the initial color as #003366 and set the dialog title. */
    gdk_color_parse ("#003366", &color);
    button = gtk_color_button_new_with_color (&color);
    gtk_color_button_set_title (GTK_COLOR_BUTTON (button), "Select a Color");

    label = gtk_label_new ("Look at my color!");
    gtk_widget_modify_fg (label, GTK_STATE_NORMAL, &color);

    g_signal_connect (G_OBJECT (button), "color_set",
        G_CALLBACK (color_changed),
        (gpointer) label);

    hbox = gtk_hbox_new (FALSE, 5);
    gtk_box_pack_start_defaults (GTK_BOX (hbox), button);
    gtk_box_pack_start_defaults (GTK_BOX (hbox), label);

    gtk_container_add (GTK_CONTAINER (window), hbox);
    gtk_widget_show_all (window);

    gtk_main ();
    return 0;
}

/* Retrieve the selected color and set it as the GtkLabel's foreground color. */

static void
color_changed (GtkColorButton *button,
        GtkWidget *label)
{
    GdkColor color;
    gtk_color_button_get_color (button, &color);
    gtk_widget_modify_fg (label, GTK_STATE_NORMAL, &color);
}


대부분의 경우 초기 색상 값으로 된 GtkColorButton을 생성하길 원할 것인데, 이는 gtk_color_button_new_with_color()에 GdkColor 객체를 명시함으로써 이루어진다. 아무 것도 제공되지 않을 경우 알파 옵션이 비활성화된 불투명한 검정색이 기본색이 된다.


색상을 GdkColor에 보관하기

GdkColor는 아래 코드 조각에 표시된 바와 같이 색상에 대한 적색, 녹색, 청색을 보관하는 구조다. pixel 객체는 색상이 색상 맵에 할당될 때 그 색인을 자동으로 보관하므로, 개발자는 보통 이 값을 수정할 필요가 없다.

struct GdkColor
{
    guint32 pixel;
    guint16 red;
    guint16 green;
    guint16 blue;
};


새로운 GdkColor 객체를 생성한 후 색상에 대한 적색, 녹색, 청색 값을 이미 알고 있다면 다음 방법을 이용해 명시할 수 있다. 적색, 녹색, 청색은 0-65,535 범위의 부호가 없는 정수 값으로서 보관되는데, 65,535는 전체 색상 강도를 나타낸다. 가령, 아래의 색상은 흰색을 나타낸다.

color.red = 65535;
color.green = 65535;
color.blue = 65535;


대부분은 색상에 대해 6자리로 된 16진 값에 익숙할 것인데, 가령 #FFFFF는 흰색을 나타낸다. 따라서 GDK는 16진 색상을 올바른 RGB 값으로 파싱하는 gdk_color_parse()를 제공한다. 해당 함수는 리스팅 4-9에서 사용되었다.

gboolean gdk_color_parse (const gchar *color_string,
        GdkColor *color);


색상 버튼 이용하기

초기 값을 설정한 후에는 gtk_color_button_set_title()을 이용해 색상 선택 대화상자에 주어질 제목을 선택할 수 있다. 기본적으로 제목은 "Pick a Color"로 주어지는데, 제목이 괜찮다면 굳이 이 값을 변경할 필요는 없다.

void gtk_color_button_set_title (GtkColorButton *button,
         const gchar *title);


다음 장에서 더 다루게 될 색상 선택 대화상자는 사용자가 버튼을 클릭하면 표시된다. 이는 선택된 색상을 사용자가 변경할 수 있도록 해준다. 색상 선택 대화상자는 그림 4-9에서 살펴볼 수 있다.


색상 값이 변경되면 위젯에 대해 color-set 시그널이 방출된다. 리스팅 4-5에서는 시그널이 잡히고, gtk_widget_modify_fg()를 이용해 GtkLabel의 전경색이 다음과 같이 변경된다.

gtk_color_button_get_color (button, &color);
gtk_widget_modify_fg (label, GTK_STATE_NORMAL, &color);


리스팅 4-9에서 전경색은 일반 위젯 상태로 설정되었으며, 모든 라벨이 선택가능하지 않은 이상 대체적으로 그 상태일 것이다. gtk_widget_modify_fg()에서 이용할 수 있는 GtkStateType 목록에 대한 옵션으로는 다섯 가지가 있는데, 이는 "위젯 스타일" 절에서 제시하였다. NULL 색상을 전달하면 위젯의 전경색을 기본값으로 리셋할 수 있다.


파일 선택자 버튼

GtkFileChooserButton 위젯은 개발자가 사용자에게 파일이나 폴더를 선택할 것을 요청하는 방법을 제공한다. 해당 위젯은 GtkFileChooser 인터페이스의 기능을 구현하는데, 바로 GTK+가 제공하는 파일 선택 프레임워크다. 그림 4-10는 폴더를 선택하도록 설정된 파일 선택자와 파일을 선택하도록 설정된 버튼을 보여준다.

그림 4-10. 파일 선택자 버튼


사용자가 GtkFileChooserButton을 클릭하면 GtkFileChooserDialog의 인스턴스가 열리고, 개발자가 생성한 버튼의 타입에 따라 사용자가 하나의 파일이나 폴더를 브라우징 및 선택하도록 해준다.


Gtkd note.png 제 5장을 학습하기 전에는 GtkFileChooserDialog 위젯을 어떻게 사용하는지 학습하지 않을 것이지만 현 시점에서 이를 이용해 직접 인터페이스와 접속할 필요는 없는데, GtkFileChooserButton이 대화상자와의 모든 상호작용을 처리할 것이기 때문이다.


GtkFileChooserButton 예제

현재 선택된 파일, 현재 폴더, 파일 선택창 제목과 같은 기본 설정을 변경할 수도 있다. 리스팅 4-10은 파일 선택자 버튼의 두 가지 타입을 모두 어떻게 사용하는지를 보여준다.


리스팅 4-10. 파일 선택자 버튼 이용하기 (filechooserbuttons.c)

#include <gtk/gtk.h>

static void folder_changed (GtkFileChooser*, GtkFileChooser*);
static void file_changed (GtkFileChooser*, GtkLabel*);

int main (int argc,
        char *argv[])
{
    GtkWidget *window, *chooser1, *chooser2, label, *vbox;
    GtkFileFilter *filter;

    gtk_init (&argc, &argv);

    window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
    gtk_window_set_title (GTK_WINDOW (window), "File Chooser Button");
    gtk_container_set_border_width (GTK_CONTAINER (window), 10);

    label = gtk_label_new ("");

    /* Create two buttons, one to select a folder and one to select a file. */
    chooser1 = gtk_file_chooser_button_new ("Chooser a Folder",
        GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER);
    chooser2 = gtk_file_chooser_button_new ("Chooser a Folder",
        GTK_FILE_CHOOSER_ACTION_OPEN);

    /* Monitor when the selected folder or file are changed. */
    g_signal_connect (G_OBJECT (chooser1), "selection_changed",
        G_CALLBACK (folder_changed),
        (gpointer) chooser2);
    g_signal_connect (G_OBJECT (chooser2), "selection_changed",
        G_CALLBACK (file_changed),
        (gpointer)
 label);

    /* Set both file chooser buttons to the location of the user's home directory. */
    gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (chooser1),
        g_get_home_dir());
    gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (chooser2),
        g_get_home_dir());

    /* Provide a filter to show all files and one to show only 3 types of images. */
    filter1 = gtk_file_filter_new ();
    filter2 = gtk_file_filter_new ();
    gtk_file_filter_set_name (filter1, "Image Files");
    gtk_file_filter_set_name (filter2, "All Files");
    gtk_file_filter_add_pattern (filter1, "*.png");
    gtk_file_filter_add_pattern (filter1, "*.jpg");
    gtk_file_filter_add_pattern (filter1, "*.gif");
    gtk_file_filter_add_pattern (filter2, "*");

    /* Add both the filters to the file chooser button that selects files. */
    gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (chooser2), filter1);
    gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (chooser2), filter2);

    vbox = gtk_vbox_new (FALSE, 5);
    gtk_box_pack_start_defaults (GTK_BOX (vbox), chooser1);
    gtk_box_pack_start_defaults (GTK_BOX (vbox), chooser2);
    gtk_box_pack_start_defaults (GTK_BOX (vbox), label);

    gtk_container_add (GTK_CONTAINER (window), vbox);
    gtk_widget_show_all (window);

    gtk_main ();
    return 0;
}

/* When a folder is selected, use that as the new location of the other chooser. */
static void
folder_changed (GtkFileChooser *chooser1,
        GtkFileChooser *chooser2)
{
    gchar *folder = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (chooser1));
    gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (chooser2), folder);
}

/* When a file is selected, display the full path in the GtkLabel widget. */
static void
file_changed (GtkFileChooser *chooser2,
        GtkLabel *label)
{
    gchar *file = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (chooser2));
    gtk_label_set_text (label, file);
}


파일 선택자 버튼 위젯은 gtk_file_chooser_button_new()를 이용해 생성된다. 해당 위젯은 두 가지 목적에 부응하는데, 바로 단일 파일을 선택하거나 단일 폴더를 선택하는 것이다. 생성 가능한 파일 선택자의 유형에는 네 가지가 있지만 (나머지 두 가지는 제 5장에서 다루겠다) 파일 선택자 버튼은 GTK_FILE_CHOOSE_ACTION_OPEN과 GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER만 지원한다.

  • GTK_FILE_CHOOSER_ACTION_OPEN: 사용자는 시스템에 이미 존재하는 단일 파일을 선택할 수 있을 것이다. 당신은 이러한 액션 타입으로 필터를 제공하여 특정 파일 패턴만 사용자에게 표시되도록 할 수 있다.
  • GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER: 사용자는 시스템에 이미 존재하는 단일 폴더를 선택할 수 있을 것이다.


gtk_file_chooser_button_new() 내의 또 다른 매개변수는 사용자가 버튼을 클릭할 때 표시되는 파일 선택자 대화상자의 제목을 설정하도록 해준다. 기본 제목은 "Select A File"이며 GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER를 이용하면 제목이 리셋된다.


GtkFileChooser

GtkFileChooserButton 위젯은 GtkFileChooser 인터페이스가 제공하는 기능의 구현이다. 즉, 버튼이 GtkFileChooser로부터 파생되지는 않았지만 GTK_FILE_CHOOSER()로 형변환할 경우 파일 선택자로서 취급할 수 있다는 의미다. 리스팅 4-10에 열거된 함수들 중 소수만이 GtkFileChooser가 제공한 함수들을 활용한다는 사실을 인지할 것이다.


리스팅 4-10에서 gtk_file_chooser_set_current_folder()는 각 파일 선택자 버튼의 현재 폴더를 사용자의 홈 디렉터리로 설정하는 데에 사용되었다. 이러한 폴더의 내용은 사용자가 처음으로 파일 선택자 버튼을 클릭할 때 표시되겠지만, 다른 방법을 통해 그 내용이 변경된 경우라면 제외된다. 폴더가 성공적으로 변경되면 해당 함수는 TRUE를 리턴할 것이다.

gboolean gtk_file_chooser_set_current_folder (GtkFileChooser *chooser,
        const gchar *filename);


g_get_home_dir() 함수는 GLib가 제공하는 유틸리티 함수로서, 현재 사용자의 홈 디렉터리를 리턴한다. GLib 내의 대부분 기능과 마찬가지로 해당 함수는 크로스 플랫폼적이다.


이는 파일 선택자 인터페이스의 유용한 특징을 상기시키는데, UNIX에서 실행되든 Windows 머신에서 실행되든 많은 유형의 파일 구조를 확인하는 데에 사용 가능하다는 점이다. 자신의 애플리케이션을 여러 운영체제용으로 컴파일하고자 할 경우 특히 유용하다.


파일 선택자 버튼은 한 번에 하나의 파일 선택만 허용하므로 파일 선택자 버튼의 타입에 따라 현재 선택된 파일이나 폴더를 검색하기 위해서는 gtk_file_chooser_get_filename()을 이용할 수 있다. 선택된 파일이 없다면 함수는 NULL을 리턴할 것이다. 리턴된 문자열에 대한 작업이 완료되면 g_free()를 이용해 해제시켜야 한다.

gchar* gtk_file_chooser_get_filename (GtkFileChooser *chooser);


GtkFileChooser 인터페이스에 대해 이 정도의 정보라면 지금으로선 파일 선택자 버튼을 구현하기에 충분하다. 다음 장에서 GtkFileChooserDialog 위젯을 학습하면서 GtkFileChooser를 좀 더 깊이 다룰 것이다.


파일 필터

GtkFileFilter 객체는 파일 선택자에 표시되는 파일을 제한하도록 해준다. 가령, 리스팅 4-10에서 Image Files 필터가 선택되면 사용자는 PNG, JPG, GIF 파일만 보고 선택할 수 있다.


파일 필터는 gtk_file_filter_new()를 이용해 생성된다. 따라서 필터 타입에 표시되는 이름을 설정하려면 gtk_file_filter_set_name()을 이용할 필요가 있겠다. 하나 이상의 필터를 제공할 경우 이러한 이름은 사용자가 필터 간 전환하는 데에 도움이 된다.

GtkFileFilter* gtk_file_filter_new ();
void gtk_file_filter_set_name (GtkFileFilter *filter,
        const gchar *name);


마지막으로 필터를 완료하려면 표시해야 할 파일 타입을 추가해야 한다. 일반적으로는 아래의 코드 조각에 표시된 바와 같이 gtk_file_filter_add_pattern()를 이용하여 이루어진다. 해당 함수는 표시되어야 할 파일명에 대한 포맷을 명시하도록 해준다. 보통은 표시해야 할 파일 확장자를 식별하면 된다. 필터링 함수의 타입에는 와일드카드로 별표 문자를 이용할 수 있다.

void gtk_file_filter_add_pattern (GtkFileFilter *filter,
        const gchar *pattern);


Gtkd tip.png 리스팅 4-10에서와 같이 당신은 All Files 필터를 제공하여 디렉터리 내 모든 파일을 표시하길 원할지도 모른다. 이를 위해서는 하나의 패턴만 와일드카드 문자로 설정된 필터를 생성해야 한다. 해당 필터를 제공하지 않으면 사용자는 다른 필터가 제공한 패턴에 매치하지 않는 파일은 절대로 볼 수 없을 것이다.


gtk_file_filter_add_mime_type()를 이용해 Multipurpose Internet Mail Extensions(MIME) 타입을 명시함으로써 필터 패턴을 명시하는 수도 있다. 가령, image/* 는 이미지 MIME 타입에 해당하는 파일을 모두 표시할 것이다. 해당 함수에서 발생할 수 있는 문제는, MIME 타입에 익숙할 필요가 있다는 것이다. 하지만 MIME 타입을 이용 시 장점은 필터에 대한 모든 파일 확장자를 명시할 필요가 없다는 것이다. 이는 구체적인 MIME 범주에 모든 파일을 일반화하도록 허용한다.

void gtk_file_filter_add_mime_type (GtkFileFilter *filter,
        const char *mime_type);


필터를 생성하고 나면 파일 선택자로 추가되어야 하는데, 이 때는 gtk_file_chooser_add_filter()를 이용하면 된다. 필터를 제공하고 나면 기본적으로 처음으로 명시된 필터들이 파일 선택자 내에서 사용될 것이다. 다수의 필터를 명시할 경우 사용자는 타입들 간 전환할 수 있을 것이다.

void gtk_file_chooser_add_filter (GtkFileChooser *chooser,
        GtkFileFilter *filter);


글꼴 버튼

GtkFontButton은 사용자가 사용자의 시스템에 현재 상주하는 글꼴에 상응하는 글꼴 매개변수를 선택하도록 해주는 또 다른 유형의 특수화 버튼이다. 글꼴 옵션은 사용자가 버튼을 클릭하면 표시되는 글꼴 선택 대화상자에서 선택된다. 이러한 옵션으로는 글꼴명, 스타일 옵션, 글꼴 크기가 포함된다. GtkFontButton 위젯 예제는 그림 4-11에 표시하였다.

그림 4-11. 글꼴 선택 버튼


글꼴 버튼 위젯은 gtk_font_button_new_with_font()를 이용해 초기화되는데 이는 초기 글꼴을 명시하도록 해준다. 글꼴은 Family Style Size로 된 문자열로서 제공된다. 각 매개변수는 선택적이며, GtkFontButton 에 대한 기본 글꼴은 Sans 12로서, 스타일 매개변수를 전혀 제공하지 않는다.


"패밀리(Family)"란 "Sans", "Serif", "Arial"과 같은 공식 글꼴명을 나타낸다. 스타일 옵션은 글꼴별로 다양하지만 보통 "이탤릭체(Italic)", "볼드체(Bold)", "볼드 이탤릭체(Bold Italic)"를 포함한다. Regular로 글꼴 스타일을 정하면 어떤 글꼴도 명시되지 않을 것이다. 크기는 표시되어야 할 텍스트의 포인트 크기(point size)로, "12" 또는 "12.5"를 예로 들 수 있겠다.


GtkFontButton 예제

리스팅 4-11은 "Sans Bold 12"의 글꼴로 초기화되는 GtkFontButton 위젯을 생성한다. 버튼에 선택된 글꼴이 변경되면 글꼴 버튼 아래에서 패킹된 GtkLabel 글꼴에 새 글꼴이 적용된다.


리스팅 4-11. 글꼴 선택 버튼 이용하기 (fontbuttons.c)

#include <gtk/gtk.h>

static void font_changed (GtkFontButton*, GtkWidget*);

int main (int argc,
        char *argv[])
{
    GtkWidget *window, *vbox, *button, *label;
    PangoFontDescription *initial_font;

    gtk_init (&argc, &argv);

    window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
    gtk_window_set_title (GTK_WINDOW (window), "Font Button");
    gtk_container_set_border_width (GTK_CONTAINER (window), 10);

    label = gtk_label_new ("Look at the font!");
    initial_font = pango_font_description_from_string ("Sans Bold 12");
    gtk_widget_modify_font (label, initial_font);

    /* Create a new font selection button with the given default font. */
    button = gtk_font_button_new_with_font ("Sans Bold 12");
    gtk_font_button_set_title (GTK_FONT_BUTTON (button), "Choose a Font");

    /* Monitor for changes to the font chosen in the font button. */
    g_signal_connect (G_OBJECT (button), "font_set",
        G_CALLBACK (font_changed),
        (gpointer) label);

    vbox= gtk_vbox_new (FALSE, 5);
    gtk_box_pack_start_defaults (GTK_BOX (vbox), button);
    gtk_box_pack_start_defaults (GTK_BOX (vbox), label);

    gtk_container_add (GTK_CONTAINER (window), vbox);
    gtk_widget_show_all (window);

    gtk_main ();
    return 0;
}

/* When the font is changed, display the font both as the text of a label and as
* the label's physical font. */
static void
font_changed (GtkFontButton *button,
        GtkWidget *label)
{
    const gchar *font, buffer[512];
    PangoFontDescription *desc;

    font = gtk_font_button_get_font_name (button);
    desc = pango_font_description_from_string (font);

    g_snprintf (buffer, sizeof (buffer), "Font: %s", font);
    gtk_label_set_text (GTK_LABEL (label), buffer);
    gtk_widget_modify_font (label, desc);
}


글꼴 선택 버튼 이용하기

리스팅 4-11의 코드는 당신이 실행시킨 PangoFontDescription 타입의 첫 예를 제시한다. PangoFontDescription 구조는 글꼴 스타일 문자열을 파싱하는 데에 사용된다. 아래와 같이 pango_font_description_from_string()을 사용하면 "Sans Bold 12"처럼 글꼴 문자열에서 새로운 글꼴 설명을 생성할 수 있다.

initial_font = pango_font_description_from_string ("Sans Bold 12");
gtk_widget_modify_font (label, initial_font);


글꼴 설명을 생성하고 나면 gtk_widget_modify_font()를 호출하여 위젯의 텍스트에 해당하는 글꼴을 설정할 수 있다. 해당 함수는 위젯의 GtkStyle 프로퍼티가 보관한 글꼴 설명 객체를 편집할 것이다.


리스팅 4-11에서 라벨의 텍스트는 font-set 시그널이 방출될 때 GtkFontButton이 보관한 글꼴로 설정되었다. gtk_font_button_get_font_name()을 이용하면 글꼴 버튼이 저장한 글꼴 설명 문자열 전체를 검색할 수 있는데 이는 라벨이 표시하는 글꼴 문자열을 검색 시 사용된다. 리턴된 문자열은 절대로 수정되거나 해제(free)되어선 안 된다.

const gchar* gtk_font_button_get_font_name (GtkFontButton *button);


리스팅 4-11에서는 새 글꼴 스타일이 GtkLabel에 적용되었다. 하지만 gtk_font_button_set_use_font()와 gtk_font_button_set_use_size()를 TRUE로 설정하면 글꼴 버튼은 그 텍스트를 렌더링할 때 폰트 패밀리와 크기를 사용할 것이다. 이런 경우 사용자가 글꼴 버튼에서 텍스트를 미리볼 수 있도록 해준다. 기본적으로 글꼴 버튼은 꺼져 있다.

void gtk_font_button_set_use_font (GtkFontButton *button,
        gboolean use_font);
void gtk_font_button_set_use_size (GtkFontButton *button,
        gboolean use_size);


자신의 이해도 시험하기

이번 장을 통해 GtkEntry, GtkSpinButton, 다양한 토글 및 버튼 타입을 포함해 많은 기본 위젯을 학습하였다. 이러한 위젯들의 사용법을 연습하기 위해 아래 소개된 두 개의 연습문제를 통해 두 가지 애플리케이션을 생성할 것이다.


연습문제 4-1. 파일 재명명하기

이번 연습문제에서는 GtkFileChooserButton 위젯을 이용하여 사용자가 시스템에서 파일명을 선택하도록 한다. 다음으로, 사용자가 파일에 대해 새로운 이름을 명시하도록 GtkEntry 위젯을 사용하라. (연습문제에서 필요로 하는 파일 유틸리티에 관한 함수는 GLib API 문서에서 찾을 수 있음을 명심한다.)


파일이 성공적으로 재명명되면 사용자가 새 파일을 선택할 때까지 GtkEntry 위젯과 버튼을 비활성화시켜야 한다. 사용자가 선택된 파일을 재명명할 권한을 갖지 않은 경우 GtkEntry 위젯과 버튼은 insensitive로 설정되어야 한다. 해당 연습문제를 완료한 후 부록 F에서 해답을 찾을 수 있다.


이 연습문제는 본 장에서 다룬 두 개의 위젯, GtkEntry와 GtkFileChooserButton을 이용한다. 또 파일의 재명명에 필요한 함수와 기존 파일의 권한에 관한 정보 검색에 필요한 함수를 포함해 GLib이 제공하는 많은 유틸리티 함수들을 사용하도록 요한다.


제 6장에 가서야 GLib를 학습할 것이지만 디렉터리 생성, 파일 권한 변경, 디렉터리 구조 이동하기 등 파일에 관련된 다른 유틸리티 함수들도 경험해보고 싶을 것이다. GLib는 많은 기능을 제공하므로 시간이 충분하다면 API 문서를 살펴보는 것이 유용하겠다.


연습문제 4-2. 스핀 버튼과 스케일

해당 연습문제에서는 3개의 위젯, 즉 스핀 버튼, 수평 스케일, 체크 버튼을 생성한다. 스핀 버튼과 수평 스케일은 동일한 초기값과 범위(bounds)로 설정되어야 한다. 체크 버튼을 선택하면 두 개의 adjustment 위젯이 동일한 값으로 동기화되어야 한다. 즉, 사용자가 하나의 위젯 값을 변경하면 나머지도 동일한 값으로 변경될 것이란 의미다.


두 위젯 모두 정수와 부동 소수점 수를 지원하므로, 이 연습문제는 다양한 소수 자리수로 구현되어야 한다. adjustment와 편의 initializer를 모두 이용해 스핀 버튼과 스케일을 생성하는 연습도 해야 할 것이다.


본 장에서 소개한 위젯의 수가 많기 때문에 연습문제를 통해 모두 사용법을 익힐 수는 없다. 하지만 두 연습문제를 완료했다면 이번 장에서 다루었던 다른 위젯들도 모두 이해했는지 개별적으로 확인해야 한다.


이러한 위젯들 중 다수는 본 저서에 걸쳐, 그리고 향후 자신의 애플리케이션에서도 사용하게 될 것이므로 기본 위젯들을 이용해 이것저것 실험해 볼 것을 권한다. 위젯이 제공하는 기능들 중 본 장에서 다루지 않은 내용도 API 문서를 통해 학습해야 한다.


요약

본 장에서는 사용자와의 주요 상호작용 방법을 제공하는 9개의 새로운 위젯들을 학습하였다.

  • GtkToggleButton: 클릭한 후 활성 또는 비활성 상태를 보유하는 GtkButton 위젯의 타입. 활성화 시 누름 상태로 표시된다.
  • GtkCheckButton: GtkToggleButton에서 파생되고, 표시된 텍스트 옆에 별개의 토글로 그려진다. GtkButton과 구별되도록 해준다.
  • GtkRadioButton: 다수의 라디오 버튼 위젯을 그룹화하여 한 번에 하나의 토글만 활성화할 수 있도록 해준다.
  • GtkEntry: 해당 위젯은 사용자가 하나의 행에 자유 형식의 텍스트를 입력하도록 해준다. 비밀번호 엔트리에도 사용 가능하다.
  • GtkSpinButton: GtkEntry에서 파생된 스핀 버튼은 사용자가 사전 정의된 범위 내에서 정수 또는 부동 소수점 수를 선택 및 입력하도록 해준다.
  • GtkScale: 스핀 버튼과 유사한 이 버튼은 사용자가 수직 또는 수평 슬라이더를 이동시켜 정수 또는 부동 소수점 수를 선택하도록 해준다.
  • GtkColorButton: 특수 타입의 버튼으로서, 사용자가 선택적 알파 값에 더해 특정 색상을 선택하도록 해준다.
  • GtkFileChooserButton: 특수 타입의 버튼으로서, 사용자가 시스템에 이미 존재하는 단일 파일이나 폴더를 선택하도록 해준다.
  • GtkFontButton: 특수 타입의 버튼으로서, 사용자가 폰트 패밀리, 스타일, 크기를 선택하도록 해준다.


다음 장에서는 GtkDialog 클래스를 이용해 자신만의 커스텀 대화상자를 생성하는 방법과 GTK+로 내장되는 여러 가지의 대화상자에 대해 학습할 것이다. 제 5장을 마무리할 때쯤이면 GTK+에서 이용할 수 있는 가장 중요하면서 간단한 위젯들을 어느 정도 이해하게 될 것이다. 그리고 나서 조금 더 복잡한 주제로 넘어가도록 하겠다.


Notes