Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- glib, gtk 팝업 사전 만들기 7화
- 6화에 파일 올렸는데, 중간 중간 짤려먹어서 파일만 다시 올려봄.
- 이것도 짤리면 말구~
- ----------
- tiandict.c
- ----------
- /* -*- Mode: C; indent-tabs-mode: nil; c-basic-offset: 2; tab-width: 2 -*- */
- #include <stdlib.h>
- #include <gmodule.h>
- #include <gtk/gtk.h>
- #include <ndbm.h>
- #include <fcntl.h>
- #define TIAN_WIDTH 300
- #define TIAN_HEIGHT 100
- typedef struct _Tiandict
- {
- DBM *dbm;
- GtkWidget *window;
- GtkTextBuffer *text_buffer; /* 요거는 GtkTextView 용도 */
- GtkClipboard *clipboard;
- gulong handler_id;
- /* 요거는 offset 맞추기 위한 변수, GUI 프로그래밍하다보면 offset, 좌표 맞추는 일이 자주 발생함. */
- int titlebar_height;
- } Tiandict;
- void
- on_owner_change (GtkClipboard *clipboard,
- GdkEvent *event,
- Tiandict *dict)
- {
- GdkWindow *gdk_window;
- GdkSeat *seat;
- GdkDevice *device;
- gchar *text;
- gint x;
- gint y;
- text = gtk_clipboard_wait_for_text (clipboard);
- if (text == NULL)
- return;
- gtk_window_set_title (GTK_WINDOW (dict->window), text);
- gdk_window = gtk_widget_get_window (GTK_WIDGET (dict->window));
- seat = gdk_display_get_default_seat (gdk_display_get_default ());
- device = gdk_seat_get_pointer (seat);
- if (gdk_window && device)
- {
- gdk_device_get_position (device, NULL, &x, &y);
- /* 아래는 적절한 위치로 이동하기 위해 좌표를 좀 조절함 */
- gtk_window_move (GTK_WINDOW (dict->window), x - 25,
- y + dict->titlebar_height);
- gtk_window_set_keep_above (GTK_WINDOW (dict->window), TRUE);
- gtk_window_set_keep_above (GTK_WINDOW (dict->window), FALSE);
- }
- /* 아래는 db 에서 데이터 인출해오는 코드, 매우 간단함. */
- datum key;
- datum content;
- key.dptr = text;
- key.dsize = strlen (text) + 1;
- content = dbm_fetch (dict->dbm, key);
- if (dbm_error (dict->dbm))
- perror (NULL);
- if (content.dptr)
- gtk_text_buffer_set_text (dict->text_buffer, content.dptr, -1);
- else
- gtk_text_buffer_set_text (dict->text_buffer, "찾는 단어가 없습니다.", -1);
- g_free (text);
- }
- gboolean
- on_enter_notify_event (GtkWidget *widget,
- GdkEvent *event,
- Tiandict *dict)
- {
- if (dict->handler_id)
- {
- g_signal_handler_disconnect (dict->clipboard, dict->handler_id);
- dict->handler_id = 0;
- }
- return FALSE;
- }
- gboolean
- on_leave_notify_event (GtkWidget *widget,
- GdkEvent *event,
- Tiandict *dict)
- {
- if (!dict->handler_id)
- dict->handler_id = g_signal_connect (dict->clipboard, "owner-change",
- (GCallback) on_owner_change, dict);
- return FALSE;
- }
- static void
- on_destroy (GtkWidget *widget,
- gpointer user_data)
- {
- gtk_main_quit ();
- }
- /* 이 함수는 실제로 저장하는 함수임 */
- static void
- on_save_button_clicked (GtkButton *button,
- Tiandict *dict)
- {
- /* 아래 변수는 포인터가 아니잖아. 함수에 포인터로 넘길 때, &iter1 이렇게 함
- 아래처럼 변수를 선언하면 malloc, free 안 써도 됨. 함수 내에서 한번 쓰고 버리는 함수들은
- 이렇게 선언하면 됨 */
- GtkTextIter iter1;
- GtkTextIter iter2;
- gchar *text;
- /* text buffer 에서 문자열 가져오는 작업임. api 문서 보면 다 나옴.
- 문서를 얼마나 빨리 찾냐가 기술임 ㅋㅋㅋㅋ */
- gtk_text_buffer_get_start_iter (dict->text_buffer, &iter1);
- gtk_text_buffer_get_end_iter (dict->text_buffer, &iter2);
- text = gtk_text_buffer_get_text (dict->text_buffer, &iter1, &iter2, TRUE);
- /* text 가 NULL 이면 프로그램 죽음. 그거 방지하려고 넣는 코드.
- text[0] 이거는 뭐하는 거냐면
- text = ""; 이렇게 하면 text[0] 여기에 0('\0', NULL) 값 들어있는거임.
- 문자열은 문자열인데 strlen (text) 하면 0 나오는 문자열이거든. 그거 저장할 필요 없잖아 */
- if (text && text[0])
- {
- datum key;
- datum content;
- /* 마우스로 더블 클릭하면 PRIMARY Atom 에 저장된다고 했잖어. 그걸 GtkClipboard 이라는 걸로
- 인출해오고, 인출 후에 set_title () 했거덩. 그래서 title 로부터 key 되는 단어를 가져오는
- 거임. 별도로 변수 만들기 귀찮거덩. 있는거 그냥 쓰자 */
- key.dptr = (char *) gtk_window_get_title (GTK_WINDOW (dict->window));
- /* 문자열은 NULL 바이트까지 저장하는게 나중에 써먹기 좋아. */
- key.dsize = strlen (key.dptr) + 1;
- content.dptr = text;
- content.dsize = strlen (content.dptr) + 1;
- /* 저장하는 함수. 예외처리해야 하는데 귀찮아서 ㅋㅋㅋ */
- dbm_store (dict->dbm, key, content, DBM_REPLACE);
- }
- /* text[0] 에 0 들어 있을 경우도 해제되는거임. */
- if (text) /* if (text != NULL) 이거랑 같은 코드. 일부러 길게 칠 필요 없거덩 */
- g_free (text);
- }
- Tiandict *tian_dict_new ()
- {
- Tiandict *dict;
- GtkWidget *sw; /* 이거는 scrolled window, 길어서 그냥 sw */
- GtkWidget *editor;
- GtkWidget *titlebar; /* GtkHeader */
- GtkWidget *save_button; /* GtkButton */
- gchar *path1, *path2; /* 패스1은 디렉토리 만들 때 써먹을 거임 */
- dict = g_slice_new (Tiandict);
- path1 = g_build_path (G_DIR_SEPARATOR_S, g_get_user_config_dir (),
- "tiandict", NULL);
- path2 = g_build_path (G_DIR_SEPARATOR_S, g_get_user_config_dir (),
- "tiandict", "dict.db", NULL);
- g_mkdir_with_parents (path1, 0700); /* 디렉토리 한방에 만드는거 mkdir -p */
- dict->dbm = dbm_open (path2, O_RDWR | O_CREAT | O_SYNC, S_IRUSR | S_IWUSR);
- if (!dict->dbm)
- {
- /* glib 에 g_log, g_info, g_message, g_warning, g_critical, g_debug
- 이런 함수들 있음. 참고로 logging 도 됨 */
- g_critical ("Can't open %s", path2);
- exit (EXIT_FAILURE);
- }
- gtk_init (NULL, NULL);
- /* GtkButton */
- /* button 은 이렇게 만드는데, icon 이름에,
- /usr/share/icons/Adwaita/22x22/legacy/document-save.png
- document-save 요 부분만 넣으면 됨. 각종 사이즈, 각종 theme 별로,
- document-save 아이콘이 엄청 나게 많음.
- /usr/share/icons/ 디렉토리에 있는 거니까.. 고민하지 말라구. */
- save_button = gtk_button_new_from_icon_name ("document-save",
- GTK_ICON_SIZE_SMALL_TOOLBAR);
- /* 전편에 설명했지? 버튼 클릭하면 clicked 신호가 발생하는데,
- on_save_button_clicked 함수 실행하라는 얘기임. user_data 로 dict 넘어가는거구 */
- g_signal_connect (save_button, "clicked",
- G_CALLBACK (on_save_button_clicked), dict);
- /* GtkHeaderBar */
- titlebar = gtk_header_bar_new ();
- gtk_header_bar_set_decoration_layout (GTK_HEADER_BAR (titlebar),
- "menu:minimize,close");
- gtk_header_bar_set_show_close_button (GTK_HEADER_BAR (titlebar), TRUE);
- /* gtk_container_add (GTK_HEADER_BAR (titlebar), aoeuao) 이런 함수도 있지만,
- 아래처럼 직접 제공하면 그걸 써!!.
- pack_start, pack_end 함수 많이 보게 될 텐데,
- pack_start 는 시작부분(왼쪽이겠지?)부터 패킹하라는 얘기. 쳐넣으라는 얘기.
- pack_end 는 끝에서부터 (오른쪽이겠지?) 패킹하라는 얘기. 쳐넣으라는 얘기.
- 이런 짓을 왜하지? 이러지 말구, 창 최대화하면 그게 layout 에 영향을 주거덩.
- 왜 필요한지 사이즈, 간격 맞추다보면 별 뻘짓을 다하게 될꺼다 흠핫핫핫 ㅋㅋ */
- gtk_header_bar_pack_start (GTK_HEADER_BAR (titlebar), save_button);
- /* GtkTextView */
- editor = gtk_text_view_new ();
- /* 이거는 줄바꿈, 편집기에 이렇거 많이 보이지? */
- gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (editor), GTK_WRAP_WORD_CHAR);
- /* 이거는 버퍼 얻어오는거. 나중에 해제 시키지 말구 */
- dict->text_buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (editor));
- gtk_text_buffer_set_text (dict->text_buffer,
- "티엔 사전입니다. 마우스로 더블 클릭하면 단어에 대한 뜻이 나옵니다. 찾는 단어가 없을 때, "
- "이 곳에 단어에 대한 의미를 입력후에 위에 제목 줄에 저장 버튼 누르시면 되겠습니다.", -1);
- /* GtkScrolledWindow */
- /* 옵션 안 주면 스크롤바가 알아서 다 자동으로 됨 */
- /* 스코롤러 정책 옵션 바꿀려면 devhelp 로 gtk_scrolled 입력하고 검색하면 되구.
- GUI 프로그래밍은 api 검색 능력이 곧 실력이야. 진짜임!!! */
- sw = gtk_scrolled_window_new (NULL, NULL);
- gtk_container_add (GTK_CONTAINER (sw), editor);
- /* GtkWindow */
- dict->window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
- gtk_window_set_titlebar (GTK_WINDOW (dict->window), titlebar);
- gtk_window_set_default_size (GTK_WINDOW (dict->window), TIAN_WIDTH, TIAN_HEIGHT);
- gtk_window_set_position (GTK_WINDOW (dict->window), GTK_WIN_POS_CENTER);
- gtk_window_set_gravity (GTK_WINDOW (dict->window), GDK_GRAVITY_CENTER);
- gtk_window_set_title (GTK_WINDOW (dict->window), "Tiandict");
- gtk_container_add (GTK_CONTAINER (dict->window), sw);
- /* GtkClipboard */
- dict->clipboard = gtk_clipboard_get (GDK_SELECTION_PRIMARY);
- dict->handler_id = g_signal_connect (dict->clipboard, "owner-change",
- (GCallback) on_owner_change, dict);
- g_signal_connect (dict->window, "destroy", (GCallback) on_destroy, NULL);
- g_signal_connect (sw, "enter-notify-event", (GCallback) on_enter_notify_event, dict);
- g_signal_connect (sw, "leave-notify-event", (GCallback) on_leave_notify_event, dict);
- gtk_widget_show_all (dict->window);
- /* 요거는 진짜 설명이 필요함. 위에 gtk_header_bar_new() 했잖아. 그때는 height 를
- 알 수 없어. 그걸 나중에 allocate 한 후에 알 수 있거든.
- gtk_widget_show_all gtk_widget_show 한 이후에는 확실히 알 수 있지.
- 그거 모르면 엄청 해멜거다.. ㅋㅋㅋ "draw" 신호가 발생하거든.
- 그 신호에 g_signal_connect 로 연결하고, 콜백에서 아래 함수 실행해도 크기 알 수 있지
- 이거 진짜 노하우야... 잘 기억해둬. 나 옛날에 이거 몰라서 한 두달 삽질했다 !!!!
- 그 옛날이 벌써 4년전이네 ㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋ
- */
- dict->titlebar_height = gtk_widget_get_allocated_height (titlebar);
- g_free (path1);
- g_free (path2);
- return dict;
- }
- void tian_dict_free (Tiandict *dict)
- {
- dbm_close (dict->dbm);
- g_slice_free (Tiandict, dict);
- }
- int main (int argc, char **argv)
- {
- Tiandict *dict;
- dict = tian_dict_new ();
- gtk_main ();
- tian_dict_free (dict);
- return EXIT_SUCCESS;
- }
- ---------
- configure
- ---------
- #!/bin/sh
- # default variables
- prefix="/usr/local"
- EXTRA_CFLAGS="-Wall -Werror"
- EXTRA_LDFLAGS="-Wl,-z,relro"
- hardening="-D_FORTIFY_SOURCE=2 -O1"
- for i in "$@"
- do
- case $i in
- --disable-hardening)
- hardening=""
- ;;
- --libdir=*)
- libdir="${i#*=}"
- ;;
- --prefix=*)
- prefix="${i#*=}"
- ;;
- --help|*)
- echo "Usage: ./configure [OPTION]..."
- echo " --help Show help messages"
- echo " --disable-hardening Disable hardening"
- echo " --libdir=PATH Set PATH to libdir"
- echo " --prefix=DIRECTORY Set DIRECTORY to prefix"
- exit 1
- ;;
- esac
- done
- [ -z $libdir ] && libdir="$prefix/lib"
- eval libdir=$libdir
- EXTRA_CFLAGS=$EXTRA_CFLAGS" $hardening"
- errmsg=""
- # check required packages
- if ! pkg-config --exists glib-2.0; then errmsg+=" glib-2.0"; fi
- if ! pkg-config --exists gobject-2.0; then errmsg+=" gobject-2.0"; fi
- if ! pkg-config --exists gtk+-3.0; then errmsg+=" gtk+-3.0"; fi
- if [ ! -f /usr/lib/libgdbm_compat.so.4 ] &&
- [ ! -f /usr/lib/x86_64-linux-gnu/libgdbm_compat.so.4 ]
- then errmsg+=" libgdbm_compat.so.4"
- fi
- # print error messages
- if test -n "$errmsg"; then
- echo "Not found: $errmsg"
- exit 1
- fi
- # generate config.mk
- cat > config.mk <<- EOF
- EXTRA_CFLAGS = $EXTRA_CFLAGS
- EXTRA_LDFLAGS = $EXTRA_LDFLAGS
- VERSION = 2020.03.07
- CC = cc
- top_srcdir = $(pwd)
- prefix = $prefix
- libdir = $libdir
- datadir = $prefix/share
- GETTEXT_PACKAGE = tiandict
- TARGET_LOCALE_DIR = $prefix/share/locale
- MKDIR_P = mkdir -p
- EOF
- echo "Generated config.mk"
- echo "----------------------------------------------------------------"
- cat config.mk
- echo "----------------------------------------------------------------"
- --------
- Makefile
- --------
- include ./config.mk
- TARGET = tiandict
- TARGET_DEPS_CFLAGS = `pkg-config --cflags glib-2.0 gobject-2.0 gtk+-3.0`
- TARGET_DEPS_LIBS = `pkg-config --libs glib-2.0 gobject-2.0 gtk+-3.0` -lgdbm_compat
- SOURCES = tiandict.c
- CFLAGS = \
- $(TARGET_DEPS_CFLAGS) \
- $(EXTRA_CFLAGS) \
- -DVERSION=\"$(VERSION)\" \
- -DTARGET_LOCALE_DIR=\"$(TARGET_LOCALE_DIR)\"
- LDFLAGS = $(TARGET_DEPS_LIBS) $(EXTRA_LDFLAGS)
- all: $(TARGET)
- $(TARGET): $(SOURCES) Makefile
- $(CC) $(CFLAGS) $(SOURCES) \
- -Wl,--as-needed $(LDFLAGS) -o $(TARGET)
- clean:
- rm -f $(TARGET) config.mk
- install:
- install -D -m 755 $(TARGET) -t $(DESTDIR)$(prefix)/bin
- uninstall:
- rm -f $(DESTDIR)$(prefix)/bin/$(TARGET)
- -rmdir -p $(DESTDIR)$(prefix)/bin
Advertisement
Add Comment
Please, Sign In to add comment