FoundationsofGTKDevelopment:Appendix F

From 흡혈양파의 번역工房
Jump to navigation Jump to search
부록 F 연습문제 해답과 힌트

연습문제 해답과 힌트

마지막 부록에서는 이 책에 실린 연습문제의 해답을 싣겠지만 전체적인 코드는 www.gtkbook.com에서 다운로드할 수 있다. 이 부록을 통해 연습문제에서 막히는 부분이 생기면 코드를 살펴보기 전에 문제를 해결할 수 있는 도구를 제공하고자 한다. 그리고 나서 다운로드한 해답을 참고하여 연습문제에서 소개한 애플리케이션을 필자가 어떻게 구현하는지 확인하도록 한다.


Gtkd note.png 연습문제가 복잡해지면서 자신이 구현한 해답과 필자의 해답이 상당히 다를 수도 있다. 애플리케이션이 성공적으로 작동한다 하더라도 다운로드한 해답과 자신의 해답을 비교해보길 바란다.


연습문제 2-1. 이벤트와 프로퍼티 사용하기

이 연습문제의 해답은 제 2장에 걸쳐 소개한 연습문제들과 매우 유사할 것이다. 우선 각자의 애플리케이션은 모든 GTK+ 애플리케이션에서 필요로 하는 다음 네 가지 기본 단계를 포함해야 한다.

  1. gtk_init()를 이용해 GTK+ 초기화하기.
  2. 최상위 수준의 GtkWindow 위젯 생성하기.
  3. 사용자에게 GtkWindow 위젯 표시하기.
  4. gtk_main()을 이용해 메인 루프로 이동하기.


네 가지 기본 단계에 더해 GtkLabel 위젯을 최상위 수준의 창으로 추가하는 일도 해야 한다. 이 라벨 위젯은 gtk_label_set_selectable()을 이용해 선택 가능하게(selectable) 설정할 수 있다. 다음으로, GtkWindow 위젯을 key-press-event 시그널로 연결해야 하는데, 이 시그널은 창에 포커스가 있을 때 사용자가 키를 누를 때마다 호출될 것이다.


Gtkd note.png key-press-event가 GtkLabel 위젯으로 연결되어 있다면 작동하지 않을 것이다! 라벨 위젯은 고유의 GdkWindow를 갖고 있지 않으므로 GDK 이벤트를 수신할 수 없음을 제 3장의 해답에서 학습할 것이다.


key-press-event 콜백 함수에서는 라벨이 현재 첫 번째 이름을 표시하는지 아니면 마지막 이름을 표시하는지 결정하기 위해 g_ascii_strcasecmp()를 사용할 수 있다. 이에 따라 창과 라벨 텍스트가 바뀌어야 한다. 애플리케이션이 계속해서 key-press-event를 처리할 것이라면 FALSE를 리턴해야 한다.


첫 애플리케이션 생성 단계에서 마지막으로 할 일은 최상위 수준의 창을 destroy 시그널로 연결하는 것이다. destroy 시그널의 콜백 함수에서 gtk_main_quit()을 호출하면 애플리케이션이 종료될 것이다. delete-event 가 발생할 때마다 창이 소멸되길 원하므로 delete-event 시그널을 사용할 필요가 없다.


연습문제 2-2. GObject 프로퍼티 시스템

이번 문제는 연습문제 2-1과 매우 비슷한데, 차이점이 있다면 프로퍼티를 변경하기 위해 GObject 라이브러리가 제공한 함수를 이용해야 한다는 것이다. 예를 들어, main() 함수에서 GtkWindow 위젯의 제목, 너비, 높이, 크기 조정 가능 기능은 g_object_set()을 이용해 설정되어야 한다.


뿐만 아니라 key-press-event 콜백 함수에서는 g_object_get()과 g_object_set()을 이용해 GtkWindow의 title 프로퍼티, GtkLabel의 label 프로퍼티와 상호작용해야 한다.


그 외에 창의 title 프로퍼티가 변경되면 알림을 제공하도록 지시하였다. 창을 notify::title 시그널로 연결하면 주어진 프로퍼티로 된 값을 감시하여 이를 확보할 수 있다. 이후 g_message()를 이용해 새로운 창 제목을 표준 출력으로 출력할 수 있다. Terminal 에뮬레이터로부터 애플리케이션을 시작할 경우 terminal 출력에서 메시지를 확인할 수 있을 것이다.


연습문제 3-1. 다수의 컨테이너 사용하기

이번 연습문제는 GtkNotebook, GtkVBox, GtkHBox 를 포함해 제 3장에서 다룬 다양한 컨테이너 위젯을 사용하는 경험이 되었을 것이다. 이번에는 이 세 가지 컨테이너를 하나씩 분석해보자.


GtkNotebook 컨테이너 위젯에는 4개의 탭이 포함되어 있어야 한다. 노트북의 각 탭은 라벨 위젯과 자식 위젯에 연관된다. gtk_notebook_append_page() 함수를 이용하면 새로운 페이지를 노트북으로 추가할 수 있다. 각 탭은 clicked 시그널로 연결된 GtkButton 위젯을 포함해야 한다. 버튼을 클릭하면 노트북은 다음 페이지로 넘어가고, 마지막 페이지에 도달하면 처음으로 되돌아온다(wrap around). 각 clicked 시그널을 동일한 콜백 함수로 연결하면 이를 구현할 수 있다.


다운로드한 해답에 소개된 next_tab()이라고 불리는 콜백 함수에서는 먼저 페이지 번호를 확인할 필요가 있다. 페이지 번호가 3보다 적으면 gtk_notebook_next_page()를 호출해 다음 페이지로 넘어가면 된다. 그 외의 경우 gtk_notebook_set_page()를 이용해 페이지 번호를 0으로 설정할 수 있다. 노트북의 이전 페이지로 이동 시에도 동일한 방법을 이용하면 된다.


다음 컨테이너 위젯은 두 개의 버튼이 있는 GtkHBox다. 첫 번째 버튼을 누르면 GtkNotebook 컨테이너에서 이전 페이지로 이동할 것이다. 앞서 언급하였듯 다음 페이지로 이동하기 위해 사용했던 방법을 역으로(reversed) 이용하면 이전 페이지로 이동할 수 있다. 두 번째 버튼을 클릭하면 창을 닫고 애플리케이션을 종료한다. 이러한 버튼들을 gtk_box_pack_end()를 이용해 패킹하면 수평 박스의 좌측면 대신 우측면에 나타난다.


애플리케이션에서 마지막 컨테이너 위젯인 GtkVBox위젯은 GtkNotebook과 GtkHBox 위젯을 보유해야 한다. 수직 박스를 최상위 수준의 GtkWindow 위젯으로 패킹하면 애플리케이션의 사용자 인터페이스가 완성된다.


연습문제 3-2. 좀 더 복잡한 컨테이너

이번 연습문제 해답은 바로 앞에서 소개한 해답과 매우 비슷하다. 차이점이 있다면 gtk_notebook_set_show_tabs()를 이용해 GtkNotebook 탭을 숨겨야 한다는 점이다. 이후 GtkExpander 컨테이너를 각 GtkButon 위젯과 노트북 탭 사이에 위치시켜야 한다. 이는 각 탭에서 발견되는 버튼을 개발자가 표시하고 숨길 수 있도록 해준다. 익스팬더의 라벨을 이용해서 현재 어떤 탭이 표시되는지 확인할 수도 있다.


앞의 해답과 비교 시 또 다른 점은 노트북과 수평 박스를 패킹하는 데에 GtkVBox 위젯을 사용하지 않고 GtkVPaned 위젯을 사용해야 한다는 점이다. 이 컨테이너는 두 위젯 간 수평 구분자를 드래그함으로써 두 자식에 할당되는 공간의 재분배를 가능하게 해준다.


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

이번 연습문제에서는 제 4장에서 학습한 여러 위젯을 사용해야 하는데, 스톡 버튼 GtkEntry와 GtkFileChooserButton을 예로 들 수 있겠다. 이 연습문제의 목적은 GLib에서 만든 함수를 이용해 선택된 파일을 재명명하는 기능을 사용자에게 제공하는 데에 있다.


처음으로 할 일은 자신의 사용자 인터페이스를 구성해야 하는데, 세 개의 상호작용 위젯을 포함하도록 해야 한다. 첫 번째 위젯은 파일 선택자 버튼으로, gtk_file_chooser_button_new()를 이용해 생성된다. 선택자의 액션은 GTK_FILE_CHOOSER_ACTION_OPEN으로 설정되어야 한다. 그러면 개발자는 하나의 파일만 선택할 수 있다. gtk_file_chooser_set_current_folder() 함수를 이용하면 파일 선택자 버튼의 현재 폴더를 g_get_home_dir()에서 찾을 수 있는 사용자의 홈 디렉터리로 설정한다.


GtkFileChooserButton 위젯은 selection-changed 시그널로 연결되어야 한다. 그 콜백 함수에서 개발자는 파일이 재명명 가능한 파일인지 검사할 필요가 있다. 이러한 검증은 g_access()라고 불리는 GLib 함수를 통해 이루어진다. 애플리케이션에서 아래와 같은 호출을 이용할 수 있겠다.

gint mode = g_access (fn, W_OK);


파일로 접근할 수 없거나 현재 사용자가 파일을 변경한 경우 GtkEntry와 GtkButton 위젯이 비활성화되어야 한다. 이는 mode와 같은 opposite Boolean 값을 gtk_widget_set_sensitive()로 전송함으로써 이루어진다.


다음으로 사용할 위젯은 GtkEntry로, 사용자가 위젯에 대해 새로운 이름을 입력하도록 해준다. 파일명을 재명명하면 GtkFileChooserButton의 위치 뒤에 붙을 것이기 때문에 위치를 제외한 파일명이 새 이름에 해당한다. 마지막 위젯, GtkButton을 클릭하면 재명명 함수가 호출될 것이다.


버튼의 콜백 함수에서 개발자는 파일 선택자 버튼으로부터 현재 파일과 위치를 검색할 필요가 있다. GtkEntry 위젯의 내용을 비롯해 위치를 이용하면 파일에 대한 새로운 절대 경로를 만들 수 있다. 마지막으로 해야 할 일은 g_rename() 함수를 이용해 파일을 재명명하는 것이다. g_rename()이 작동하려면 <glib/gstdio.h>를 포함시켜야 함을 주목하라!


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

이번 문제는 앞의 연습문제와는 매우 상이하며, GtkCheckButton, GtkSpinButton, GtkHScale 위젯을 연습하도록 해준다. 체크 버튼이 활성화되면 스핀 버튼과 수평적 스케일의 값이 동기화될 것이다. 활성화되지 않으면 이 값들은 서로 무관하게 이동할 수 있다.


이를 구현하기 위해서는 먼저 각 범위 위젯에 하나씩, 총 두 개의 동일한 조정(adjustment)을 생성해야 한다. 이 연습문제에서는 애플리케이션이 시작되면 토글 버튼이 활성화되므로 값이 즉시 동기화될 것이다.


다음으로 각 범위 위젯을 value-changed 시그널에 대한 동일한 콜백 함수로 연결한다. 이 함수에서는 먼저 스핀 버튼과 스케일의 현재 값을 검색한다. 토글 버튼이 활성화되면 이 값들이 비교된다. value-changed 시그널이 반복되어 발생하지 않도록 값이 다를 때만 액션을 취한다.


마지막으로, 콜백 함수는 어떤 타입의 위젯이 새로운 값을 보유하는지 알아내기 위해 GTK_IS_SPIN_BUTTON()을 이용할 수 있다. 테스트 결과를 바탕으로 다른 위젯은 새로운 값을 부여 받아야 한다.


연습문제 5-1. 파일 선택자 대화상자 구현하기

제 5장에 유일하게 실린 연습문제에서는 GtkFileChooserWidget 위젯을 GtkDialog 위젯에 포함시킴으로써 네 가지 타입의 파일 선택자 대화상자를 재생성하는 것이 목적이다. 각 결과는 간단히 표준 출력으로 인쇄할 수 있다.


애플리케이션 메인 창에는 GtkFileChooser 액션 타입마다 하나씩, 총 4개의 버튼이 포함되어 있을 것이며, GTK_FILE_CHOOSER_ACTION_OPEN 액션을 이용하면 다수의 파일을 선택할 수 있을 것이다. 이 버튼들은 수직 박스에 패킹한 다음 최상위 수준 창으로 패킹 가능하다.


각 콜백 함수는 동일한 패턴을 따른다. 먼저 GtkDialog 위젯을 생성하고, gtk_box_pack_start()를 이용해 대화상자의 vbox member를 패킹함으로써 대화상자의 액션 영역 위에 GtkFileChooserWidget을 패킹한다.


그 다음으로 gtk_dialog_run()을 이용해 대화상자를 실행한다. 리턴 결과가 액션의 수락과 연관된 응답일 경우 개발자는 발생하는 결과를 g_print()를 호출해 출력해야 한다. 가령 개발자는 파일이 저장될 것이라던지, 폴더가 생성되었다던지, 파일이 열릴 것이라던지, 폴더가 선택되었다던지 등의 정보를 사용자에게 알려야 할 것이다. GTK_FILE_CHOOSER_ACTION_OPEN 액션의 경우 선택된 모든 파일을 출력해야 한다.


연습문제 6-1. 파일로 작업하기

이번 연습문제에서는 제 6장에서 파일 조작에 관해 학습한 내용을 앞 장에 걸쳐 학습한 위젯과 통합시키는 데에 목적이 있다. 이 연습문제에서 사용되는 사용자 인터페이스는 3개의 위젯, 즉 GtkEntry, GtkFileChooserButton, GtkButton을 포함해야 한다.


GtkEntry 위젯은 사용자가 시스템 상에 파일로 저장시킬 한 행의 텍스트를 입력하도록 허용할 것이다. 파일의 위치는 GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER의 액션을 이용해 GtkFileChooserButton 위젯에서 선택된다. 마지막으로 GtkButton 위젯을 클릭하면 파일을 저장하기 시작할 것이다. 다운로드한 해답에서는 사용자가 버튼을 클릭하면 선택된 위치에 arbitrary_file 이란 파일로 텍스트가 저장될 것이다.


버튼의 콜백 함수에서 개발자는 선택된 위치로부터 파일 경로를 비롯해 원하는 파일명을 만들 수 있다. 그리고 나서 g_file_set_contents()를 이용해 GtkEntry 위젯의 내용을 파일로 저장 가능하다. 파일을 쓸 때 오류가 발생하면 제 6장에서 다룬 메시지 보고 시스템을 이용해 사용자에게 보고해야 한다. 예를 들어, 사용자가 선택된 위치로 쓰기 접근성을 갖고 있지 않다면 쓰기 연산은 실패할 것이다.


연습문제 6-2. Timeout 함수

이 연습문제에서는 타이머를 생성하기 위해 timeout 함수를 이용하는데 그 과정이 매우 간단하다. 먼저 두 개의 위젯을 생성해야 하는데, 현재 계수를 출력하는 GtkLabel과 클릭 시 계수를 0으로 리셋하는 GtkButton이 해당한다. Timeout은 다음과 같이 g_timeout_add_full()을 이용해 생성되어야 한다.

g_timeout_add_full (G_PRIORITY_DEFAULT, 1000,
        (GSourceFunc) timeout_function,
        (gpointer) widget, NULL);


위의 timeout 함수는 1,000 밀리초마다 timeout_function()을 호출할 것이다. 함수 내에서 초는 증가하고 라벨은 업데이트된다.


두 번째 작업은 GTimer를 이용해 타이머 생성을 재구현하는 일이다. 앞의 해답에 두 번째 라벨을 위치시켜 계수를 비교할 수 있도록 해보라. Timeout은 정확한 시간을 유지하는 GTimer보다 느리게 계수함을 눈치챌 것이다. 이는 timeout_function()이 1,000 밀리초마다 호출되는 데다가 함수를 실행하는 데에 소요되는 시간까지 합쳐지기 때문이다! Timeout의 다음 계수 시간(counting period)은 앞의 호출이 완료될 때까지 시작하지 않으므로 함수 호출의 중복을 피할 수 있다. 연습문제에서 알 수 있듯이 시간을 추적하는 데에는 절대 timeout 함수를 사용해선 안 되는 이유다.


연습문제 7-1. 텍스트 에디터

이번 연습문제에서는 처음으로 텍스트 에디터 애플리케이션을 살펴볼 것이다. 여기서는 텍스트 에디터의 기능을 모두 구현할 것을 요구한다.


Gtkd note.png 다운로드한 해답에는 텍스트 에디터의 기본 기능만 포함되어 있다. 문제가 생기면 작업을 시작할 수 있도록 준비시키는 것이 목적이다. 하지만 제공된 해답을 능가하여 텍스트 에디터 구현을 확장할 것을 권한다.


텍스트 에디터에는 다수의 콜백 함수가 구현되고 있다. 새로운 파일을 생성하고, 기존 파일을 열고, 파일을 저장하고, 선택된 텍스트의 자름, 복사, 붙여넣기를 실행하고, 문서에서 텍스트를 검색하는 기능을 예로 들 수 있겠다.


새로운 문서를 생성하기 위해서는 애플리케이션이 GtkMessageDialog 위젯과 계속 작업할 것인지 사용자에게 물어보아야 한다. 사용자가 계속 사용하기로 선택하면 다운로드한 해답에서와 같이 GtkTextBuffer 객체를 제거하고 대화상자를 소멸한다. 그렇지 않으면 대화상자만 소멸된다.


제공된 해답에서는 문서를 열면 사용자에게 확인을 요청하지 않는다는 사실을 눈치챌 것인데, GtkFileChooserDialog 위젯에서 연산을 취소하는 일은 쉽기 때문이다. 파일 선택자 대화상자에는 GTK_FILE_CHOOSER_ACTION_OPEN이란 액션 타입이 있다. g_file_get_contents()를 이용하면 파일을 선택 시 그 내용이 읽히고 텍스트 버퍼로 적힌다. 연습문제 해답에서 파일을 저장하기 위해 버튼을 누를 때마다 새로운 파일명을 묻는다. 텍스트를 선택된 파일로 저장하기 위해서는 g_file_set_contents()를 호출한다.


클립보드 함수는 제 7장의 클립보드 예제에서 제공된 함수와 매우 유사하다. 자르기, 복사하기, 붙여넣기 액션에 내장된 텍스트 버퍼 함수를 사용한다. 이들은 기본 클립보드, GDK_SELECTION_CLIPBOARD에서 실행된다.


마지막 콜백 함수는 현재 텍스트에서 대·소문자에 민감한 문자열을 검색한다. 이에 사용된 해답은 제 7장의 리스팅 7-6에 표시된 함수와 유사하므로 추가 정보는 본문의 내용을 참고한다.


연습문제 8-1. 파일 브라우저

이번 연습문제에서는 매우 간단한 파일 브라우저를 구현할 것이다. 이는 사용자가 시스템의 파일 구조체를 살펴보고 파일과 폴더를 구별할 수 있도록 해준다. 여기서는 GtkTreeView 위젯의 사용 경험을 쌓는 것이 목적이다. 이는 13장에서 좀 더 기능적인 파일 브라우저로 더 많이 확장될 것이다.


첫 번째 단계는 하나의 열만 포함하는 트리 뷰를 설정하는 것이다. 이 열은 두 개의 셀 렌더러를 포함할 것인데, 하나는 GdkPixbuf용이고 나머지 하나는 파일명이나 폴더명에 사용될 것이므로 제 8장에서 논한 트리 뷰 열 생성 방법을 확장시켜 사용해야 할 것이다. 첫 번째 셀 렌더러는 GtkCellRendererPixbuf를, 두 번째는 GtkCellRendererText를 이용해야 한다.


트리 모델인 GtkListStore는 GDK_TYPE_PIXBUF와 G_TYPE_STRING 타입으로 된 두 개의 열과 함께 생성된다. 리스트 저장소(list store)는 트리 뷰로 추가된 다음에 g_object_unref()를 이용해 참조해제해야만 트리 뷰 위젯과 함께 소멸될 것이다.


다운로드한 해답에서는 트리 뷰가 생성된 다음에 populate_tree_model() 함수가 호출되어 시작 시 파일 시스템의 루트 폴더를 표시한다. 파일 브라우저가 표시한 현재 경로는 current_path라는 전역적 연결 리스트에 저장된다. 리스트가 비어 있다면 루트 폴더가 표시된다. 비어 있지 않은 경우 리스트의 내용에서 경로가 만들어지고, 트리 모델에 ".." 디렉터리 엔트리가 추가된다.


그 다음으로 GDir을 이용해 디렉터리의 내용을 살펴보고, 각 파일이나 폴더를 트리 모델로 추가한다. 각각이 파일인지 폴더인지 확인하기 위해 G_FILE_TEST_IS_DIR과 함께 g_file_test()를 이용하면 결과에 따라 올바른 아이콘이 표시된다.


마지막 단계는 디렉터리 이동을 처리하는 일인데, GtkTreeView의 row-activated 시그널이 사용된다. 선택내용이 ".." 엔트리인 경우 경로의 마지막 요소가 제거되고, 트리 모델이 다시 채워진다(repopulated). 그 외의 경우는 현재 위치와 선택내용에서 새로운 경로가 만들어진다. 선택내용이 폴더라면 트리 모델은 새로운 디렉터리에서 다시 채워진다. 반대로 선택내용이 파일이라면 액션이 무시되고 어떤 일도 실행되지 않는다.


연습문제 9-1. 툴바

이번 연습문제는 연습문제 7-1을 약간 수정하여 면을 따라 위치한 버튼을 GtkUIManager를 이용해 생성된 GtkToolbar로 대체한다. 아래 UI 파일을 이용해 툴바를 이용할 수 있다.

<ui>
    <toolbar name="Toolbar">
        <toolitem name="FileNew" action="New"/>
        <toolitem name="FileOpen" action="Open"/>
        <toolitem name="FileSave" action="Save"/>
        <separator/>
        <toolitem name="EditCut" action="Cut"/>
        <toolitem name="EditCopy" action="Copy"/>
        <toolitem name="EditPaste" action="Paste"/>
    </toolbar>
</ui>


애플리케이션에서 다음으로 해야 할 일은 UI 파일 내 각 툴바 항목과 연관될 GtkActionEntry 객체의 배열을 생성하는 것이다. 이러한 액션들은 GtkActionGroup 객체에서 조직되고, 툴바는 GtkUIManager 객체를 이용해 생성된다. 나머지 텍스트 에디터 구현 부분은 연습문제 7-1과 동일하다.


연습문제 9-2. 메뉴 표시줄

이 또한 연습문제 7-1을 수정하여 면을 따라 위치한 버튼을 GtkUIManager로 생성한 GtkMenuBar 위젯으로 대체한다. 툴바는 아래 UI 파일을 이용해 생성할 수 있다.

<ui>
    <menubar name="MenuBar">
        <menu name="FileMenu" action="File">
            <menuitem name="FileNew" action="New"/>
            <menuitem name="FileOpen" action="Open"/>
            <menuitem name="FileSave" action="Save"/>
        </menu>
        <menu name="EditMenu" action="Edit">
            <menuitem name="EditCut" action="Cut"/>
            <menuitem name="EditCopy" action="Copy"/>
            <menuitem name="EditPaste" action="Paste"/>
        </menu>
    </menubar>
</ui>


애플리케이션에서 다음으로 해야 할 일은 UI 파일 내 각 툴바 항목과 연관될 GtkActionEntry 객체의 배열을 생성하는 것이다. 이러한 액션들은 GtkActionGroup 객체에서 조직되고, 메뉴 표시줄은 GtkUIManager 객체를 이용해 생성된다. 나머지 텍스트 에디터 구현 부분은 연습문제 7-1과 동일하다.


연습문제 10-1. Glade 텍스트 에디터

이번에도 연습문제 7-1을 확장시키되 사용자 인터페이스 전체를 Glade에서 다시 디자인하도록 요구한다. 버튼을 사용하는 대신 텍스트 편집 함수를 위한 툴바를 구현해야 한다. 다음으로 Libglade를 이용해 그래픽 사용자 인터페이스를 로딩하고 필요한 시그널을 연결할 수 있다. 그림 F-1에는 툴바를 이용해 생성한 애플리케이션의 스크린샷을 실었다.

그림 F-1. Glade에서 디자인한 툴바가 있는 텍스트 에디터 애플리케이션


연습문제 10-2. 메뉴가 있는 Glade 텍스트 에디터

이 또한 연습문제 7-1을 확장시켜 사용자 인터페이스를 모두 Glade에서 다시 디자인하도록 요한다. 하지만 이번에는 버튼을 이용하는 대신 텍스트 편집 함수를 위한 메뉴 표시줄을 구현해야 한다. 그리고 나서 Libglade를 이용해 그래픽 사용자 인터페이스를 로딩하고 필요한 시그널을 연결한다. 그림 F-2에는 메뉴 표시줄을 이용하는 애플리케이션의 스크린샷이 실려 있다.

그림 F-2. Glade에서 디자인한 메뉴 표시줄이 있는 텍스트 에디터 애플리케이션


연습문제 11-1. MyMarquee 확장하기

이번 연습문제에서는 제 11장에서 만든 MyMarquee 위젯을 확장할 것이다. 이번 절에는 연습문제에서 필요로 하는 추가 기능을 구현하는 것과 관련된 팁이 많이 포함되어 있다.


첫 번째 확장은 위젯 주위에 테두리를 추가하는 것이다. 이는 gdk_draw_rectangle() 함수를 이용하면 된다. 아래 함수는 주어진 width와 height로 된 직사각형의 테두리를 (x,y)에 상단 좌측 모서리가 위치하도록 그린다.

void gdk_draw_rectangle (GdkDrawable *drawable,
        GdkGC *gc,
        gboolean filled,
        gint x,
        gint y,
        gint width,
        gint height);


그 다음으로 여러 개의 메시지를 스크롤하는 기능을 제공하고자 한다. 이를 위해서는 우선 메시지를 private한 연결 리스트로 저장할 필요가 있다. 다음으로 메시지를 추가 및 저장하기 위한 함수들을 제공해야 한다. 메시지가 위젯의 경계를 벗어나도록 스크롤되면 리스트에서 다음 메시지의 스크롤이 시작해야 한다.


그리고 나면 메시지를 왼쪽 또는 오른쪽 방향으로 스크롤할 수 있는 기능을 제공할 필요가 있다. 이는 개발자의 슬라이드 함수에서 처리되는데, 함수가 호출될 때마다 올바른 방향으로 speed 픽셀을 이동할 것이다. 여기서 주의해야 할 점은 메시지가 스크롤 방향에서 명시한 방향으로 위젯을 벗어나서 스크롤될 수 있다는 점이다.


마지막으로, 마우스 포인터가 위젯 위에 있으면 메시지는 스크롤을 중단해야 한다. 이는 기본 enter-notify-event와 leave-notify-event 콜백 함수를 오버라이드하여 실행된다. 메시지가 스크롤되어야 하는지 여부를 명시하기 위해서는 boolean 플래그를 사용해야 한다. 이를 호출하는 동안 메시지의 제거 여부를 결정하려면 my_marquee_slide()를 확인하면 된다.


연습문제 12-1. 전체 텍스트 에디터 생성하기

마지막으로 제시된 텍스트 에디터 연습문제는 10-1의 확장에 해당한다. 여기서는 추가로 두 개의 기능을 더해야 한다. 첫 번째는 인쇄 지원으로, 사용자가 GtkTextBuffer 위젯에 현재 텍스트를 인쇄하도록 허용하는 기능이다. 이 연습문제에 다운로드한 해답에 실린 인쇄 지원은 제 12장에 소개된 예제와 매우 유사하므로 해답의 도출 과정에 관한 추가 정보는 본문에 실린 설명을 확인하도록 한다.


두 번째 추가 기능은 Open 툴바 항목에 대한 최근 파일 선택자 메뉴에 해당한다. 이를 생성하기 위해서는 Open 툴바 항목을 GtkMenuToolItem 위젯으로 변환해야 한다. gtk_recent_manager_get_default()로 생성된 기본 최근 관리자를 이용하면 최근 파일을 제공할 수 있다. 그 다음, gtk_recent_choose_menu_new_for_manager()를 이용해 최근 파일 선택자 메뉴를 생성할 수 있다. 이 메뉴는 Open 메뉴 툴 버튼의 GtkMenu로 추가되어야 한다. 마지막으로 selection-done 시그널을 이용해 어떤 메뉴가 선택되었는지, 어떤 파일을 열어야 하는지 알아낼 수 있다.


Notes