Guest User

tiandict

a guest
Mar 9th, 2020
140
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C 13.62 KB | None | 0 0
  1. glib, gtk 팝업 사전 만들기 7
  2.  
  3. 6화에 파일 올렸는데, 중간 중간 짤려먹어서 파일만 다시 올려봄.
  4. 이것도 짤리면 말구~
  5.  
  6. ----------
  7. tiandict.c
  8. ----------
  9. /* -*- Mode: C; indent-tabs-mode: nil; c-basic-offset: 2; tab-width: 2 -*- */
  10.  
  11. #include <stdlib.h>
  12. #include <gmodule.h>
  13. #include <gtk/gtk.h>
  14. #include <ndbm.h>
  15. #include <fcntl.h>
  16.  
  17. #define TIAN_WIDTH  300
  18. #define TIAN_HEIGHT 100
  19.  
  20. typedef struct _Tiandict
  21. {
  22.   DBM           *dbm;
  23.   GtkWidget     *window;
  24.   GtkTextBuffer *text_buffer; /* 요거는 GtkTextView 용도 */
  25.   GtkClipboard  *clipboard;
  26.   gulong         handler_id;
  27.   /* 요거는 offset 맞추기 위한 변수, GUI 프로그래밍하다보면 offset, 좌표 맞추는 일이 자주 발생함. */
  28.   int            titlebar_height;
  29. } Tiandict;
  30.  
  31. void
  32. on_owner_change (GtkClipboard *clipboard,
  33.                  GdkEvent     *event,
  34.                  Tiandict     *dict)
  35. {
  36.   GdkWindow *gdk_window;
  37.   GdkSeat   *seat;
  38.   GdkDevice *device;
  39.   gchar     *text;
  40.   gint       x;
  41.   gint       y;
  42.  
  43.   text = gtk_clipboard_wait_for_text (clipboard);
  44.  
  45.   if (text == NULL)
  46.     return;
  47.  
  48.   gtk_window_set_title (GTK_WINDOW (dict->window), text);
  49.  
  50.   gdk_window = gtk_widget_get_window (GTK_WIDGET (dict->window));
  51.   seat       = gdk_display_get_default_seat (gdk_display_get_default ());
  52.   device     = gdk_seat_get_pointer (seat);
  53.  
  54.   if (gdk_window && device)
  55.   {
  56.     gdk_device_get_position (device, NULL, &x, &y);
  57.     /* 아래는 적절한 위치로 이동하기 위해 좌표를 좀 조절함 */
  58.     gtk_window_move (GTK_WINDOW (dict->window), x - 25,
  59.                                                 y + dict->titlebar_height);
  60.     gtk_window_set_keep_above (GTK_WINDOW (dict->window), TRUE);
  61.     gtk_window_set_keep_above (GTK_WINDOW (dict->window), FALSE);
  62.   }
  63.  
  64.   /* 아래는 db 에서 데이터 인출해오는 코드, 매우 간단함. */
  65.   datum key;
  66.   datum content;
  67.  
  68.   key.dptr  = text;
  69.   key.dsize = strlen (text) + 1;
  70.   content = dbm_fetch (dict->dbm, key);
  71.  
  72.   if (dbm_error (dict->dbm))
  73.     perror (NULL);
  74.  
  75.   if (content.dptr)
  76.     gtk_text_buffer_set_text (dict->text_buffer, content.dptr, -1);
  77.   else
  78.     gtk_text_buffer_set_text (dict->text_buffer, "찾는 단어가 없습니다.", -1);
  79.  
  80.   g_free (text);
  81. }
  82.  
  83. gboolean
  84. on_enter_notify_event (GtkWidget *widget,
  85.                        GdkEvent  *event,
  86.                        Tiandict  *dict)
  87. {
  88.   if (dict->handler_id)
  89.   {
  90.     g_signal_handler_disconnect (dict->clipboard, dict->handler_id);
  91.     dict->handler_id = 0;
  92.   }
  93.  
  94.   return FALSE;
  95. }
  96.  
  97. gboolean
  98. on_leave_notify_event (GtkWidget *widget,
  99.                        GdkEvent  *event,
  100.                        Tiandict  *dict)
  101. {
  102.   if (!dict->handler_id)
  103.     dict->handler_id = g_signal_connect (dict->clipboard, "owner-change",
  104.                                          (GCallback) on_owner_change, dict);
  105.  
  106.   return FALSE;
  107. }
  108.  
  109. static void
  110. on_destroy (GtkWidget *widget,
  111.             gpointer   user_data)
  112. {
  113.   gtk_main_quit ();
  114. }
  115.  
  116. /* 이 함수는 실제로 저장하는 함수임 */
  117. static void
  118. on_save_button_clicked (GtkButton *button,
  119.                         Tiandict  *dict)
  120. {
  121.   /* 아래 변수는 포인터가 아니잖아. 함수에 포인터로 넘길 때, &iter1 이렇게 함
  122.      아래처럼 변수를 선언하면 malloc, free 안 써도 됨. 함수 내에서 한번 쓰고 버리는 함수들은
  123.      이렇게 선언하면 됨 */
  124.   GtkTextIter  iter1;
  125.   GtkTextIter  iter2;
  126.   gchar       *text;
  127.  
  128.   /* text buffer 에서 문자열 가져오는 작업임. api 문서 보면 다 나옴.
  129.      문서를 얼마나 빨리 찾냐가 기술임 ㅋㅋㅋㅋ */
  130.   gtk_text_buffer_get_start_iter  (dict->text_buffer, &iter1);
  131.   gtk_text_buffer_get_end_iter    (dict->text_buffer, &iter2);
  132.   text = gtk_text_buffer_get_text (dict->text_buffer, &iter1, &iter2, TRUE);
  133.  
  134.   /* text 가 NULL 이면 프로그램 죽음. 그거 방지하려고 넣는 코드.
  135.      text[0] 이거는 뭐하는 거냐면
  136.      text = ""; 이렇게 하면 text[0] 여기에 0('\0', NULL) 값 들어있는거임.
  137.      문자열은 문자열인데 strlen (text) 하면 0 나오는 문자열이거든. 그거 저장할 필요 없잖아 */
  138.   if (text && text[0])
  139.   {
  140.     datum key;
  141.     datum content;
  142.  
  143.     /* 마우스로 더블 클릭하면 PRIMARY Atom 에 저장된다고 했잖어. 그걸 GtkClipboard 이라는 걸로
  144.        인출해오고, 인출 후에 set_title () 했거덩. 그래서 title 로부터 key 되는 단어를 가져오는
  145.        거임. 별도로 변수 만들기 귀찮거덩. 있는거 그냥 쓰자 */
  146.     key.dptr  = (char *) gtk_window_get_title (GTK_WINDOW (dict->window));
  147.     /* 문자열은 NULL 바이트까지 저장하는게 나중에 써먹기 좋아. */
  148.     key.dsize = strlen (key.dptr) + 1;
  149.  
  150.     content.dptr  = text;
  151.     content.dsize = strlen (content.dptr) + 1;
  152.     /* 저장하는 함수. 예외처리해야 하는데 귀찮아서 ㅋㅋㅋ */
  153.     dbm_store (dict->dbm, key, content, DBM_REPLACE);
  154.   }
  155.  
  156.   /* text[0] 에 0 들어 있을 경우도 해제되는거임. */
  157.   if (text) /* if (text != NULL) 이거랑 같은 코드. 일부러 길게 칠 필요 없거덩 */
  158.     g_free (text);
  159. }
  160.  
  161. Tiandict *tian_dict_new ()
  162. {
  163.   Tiandict  *dict;
  164.   GtkWidget *sw; /* 이거는 scrolled window, 길어서 그냥 sw */
  165.   GtkWidget *editor;
  166.   GtkWidget *titlebar;    /* GtkHeader */
  167.   GtkWidget *save_button; /* GtkButton */
  168.   gchar     *path1, *path2; /* 패스1은 디렉토리 만들 때 써먹을 거임 */
  169.  
  170.   dict = g_slice_new (Tiandict);
  171.   path1 = g_build_path (G_DIR_SEPARATOR_S, g_get_user_config_dir (),
  172.                        "tiandict", NULL);
  173.   path2 = g_build_path (G_DIR_SEPARATOR_S, g_get_user_config_dir (),
  174.                        "tiandict", "dict.db", NULL);
  175.   g_mkdir_with_parents (path1, 0700); /* 디렉토리 한방에 만드는거 mkdir -p */
  176.   dict->dbm = dbm_open (path2, O_RDWR | O_CREAT | O_SYNC, S_IRUSR | S_IWUSR);
  177.  
  178.   if (!dict->dbm)
  179.   {
  180.     /* glib 에 g_log, g_info, g_message, g_warning, g_critical, g_debug
  181.        이런 함수들 있음. 참고로 logging 도 됨 */
  182.     g_critical ("Can't open %s", path2);
  183.     exit (EXIT_FAILURE);
  184.   }
  185.  
  186.   gtk_init (NULL, NULL);
  187.  
  188.   /* GtkButton */
  189.   /* button 은 이렇게 만드는데, icon 이름에,
  190.      /usr/share/icons/Adwaita/22x22/legacy/document-save.png
  191.      document-save 요 부분만 넣으면 됨. 각종 사이즈, 각종 theme 별로,
  192.      document-save 아이콘이 엄청 나게 많음.
  193.      /usr/share/icons/ 디렉토리에 있는 거니까.. 고민하지 말라구. */
  194.   save_button = gtk_button_new_from_icon_name ("document-save",
  195.                                                GTK_ICON_SIZE_SMALL_TOOLBAR);
  196.   /* 전편에 설명했지? 버튼 클릭하면 clicked 신호가 발생하는데,
  197.      on_save_button_clicked 함수 실행하라는 얘기임. user_data 로 dict 넘어가는거구 */
  198.   g_signal_connect (save_button, "clicked",
  199.                     G_CALLBACK (on_save_button_clicked), dict);
  200.  
  201.   /* GtkHeaderBar */
  202.   titlebar = gtk_header_bar_new ();
  203.   gtk_header_bar_set_decoration_layout (GTK_HEADER_BAR (titlebar),
  204.                                         "menu:minimize,close");
  205.   gtk_header_bar_set_show_close_button (GTK_HEADER_BAR (titlebar), TRUE);
  206.   /* gtk_container_add (GTK_HEADER_BAR (titlebar), aoeuao) 이런 함수도 있지만,
  207.      아래처럼 직접 제공하면 그걸 써!!.
  208.      pack_start, pack_end 함수 많이 보게 될 텐데,
  209.      pack_start 는 시작부분(왼쪽이겠지?)부터 패킹하라는 얘기. 쳐넣으라는 얘기.
  210.      pack_end 는 끝에서부터 (오른쪽이겠지?) 패킹하라는 얘기. 쳐넣으라는 얘기.
  211.      이런 짓을 왜하지? 이러지 말구, 창 최대화하면 그게 layout 에 영향을 주거덩.
  212.      왜 필요한지 사이즈, 간격 맞추다보면 별 뻘짓을 다하게 될꺼다 흠핫핫핫 ㅋㅋ */
  213.   gtk_header_bar_pack_start (GTK_HEADER_BAR (titlebar), save_button);
  214.  
  215.   /* GtkTextView */
  216.   editor = gtk_text_view_new ();
  217.   /* 이거는 줄바꿈, 편집기에 이렇거 많이 보이지? */
  218.   gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (editor), GTK_WRAP_WORD_CHAR);
  219.   /* 이거는 버퍼 얻어오는거. 나중에 해제 시키지 말구 */
  220.   dict->text_buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (editor));
  221.   gtk_text_buffer_set_text (dict->text_buffer,
  222.     "티엔 사전입니다. 마우스로 더블 클릭하면 단어에 대한 뜻이 나옵니다. 찾는 단어가 없을 때, "
  223.     "이 곳에 단어에 대한 의미를 입력후에 위에 제목 줄에 저장 버튼 누르시면 되겠습니다.", -1);
  224.  
  225.   /* GtkScrolledWindow */
  226.   /* 옵션 안 주면 스크롤바가 알아서 다 자동으로 됨 */
  227.   /* 스코롤러 정책 옵션 바꿀려면 devhelp 로 gtk_scrolled 입력하고 검색하면 되구.
  228.      GUI 프로그래밍은 api 검색 능력이 곧 실력이야. 진짜임!!! */
  229.   sw = gtk_scrolled_window_new (NULL, NULL);
  230.   gtk_container_add (GTK_CONTAINER (sw), editor);
  231.  
  232.   /* GtkWindow */
  233.   dict->window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
  234.   gtk_window_set_titlebar (GTK_WINDOW (dict->window), titlebar);
  235.   gtk_window_set_default_size (GTK_WINDOW (dict->window), TIAN_WIDTH, TIAN_HEIGHT);
  236.   gtk_window_set_position (GTK_WINDOW (dict->window), GTK_WIN_POS_CENTER);
  237.   gtk_window_set_gravity  (GTK_WINDOW (dict->window), GDK_GRAVITY_CENTER);
  238.   gtk_window_set_title    (GTK_WINDOW (dict->window), "Tiandict");
  239.   gtk_container_add (GTK_CONTAINER (dict->window), sw);
  240.  
  241.   /* GtkClipboard */
  242.   dict->clipboard  = gtk_clipboard_get (GDK_SELECTION_PRIMARY);
  243.   dict->handler_id = g_signal_connect (dict->clipboard, "owner-change",
  244.                                        (GCallback) on_owner_change, dict);
  245.  
  246.   g_signal_connect (dict->window, "destroy", (GCallback) on_destroy, NULL);
  247.   g_signal_connect (sw, "enter-notify-event", (GCallback) on_enter_notify_event, dict);
  248.   g_signal_connect (sw, "leave-notify-event", (GCallback) on_leave_notify_event, dict);
  249.  
  250.   gtk_widget_show_all (dict->window);
  251.  
  252.   /* 요거는 진짜 설명이 필요함. 위에 gtk_header_bar_new() 했잖아. 그때는 height 를
  253.      알 수 없어. 그걸 나중에 allocate 한 후에 알 수 있거든.
  254.      gtk_widget_show_all gtk_widget_show 한 이후에는 확실히 알 수 있지.
  255.      그거 모르면 엄청 해멜거다.. ㅋㅋㅋ "draw" 신호가 발생하거든.
  256.      그 신호에 g_signal_connect 로 연결하고, 콜백에서 아래 함수 실행해도 크기 알 수 있지
  257.      이거 진짜 노하우야... 잘 기억해둬. 나 옛날에 이거 몰라서 한 두달 삽질했다 !!!!
  258.      그 옛날이 벌써 4년전이네 ㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋ
  259.      */
  260.   dict->titlebar_height = gtk_widget_get_allocated_height (titlebar);
  261.  
  262.   g_free (path1);
  263.   g_free (path2);
  264.  
  265.   return dict;
  266. }
  267.  
  268. void tian_dict_free (Tiandict *dict)
  269. {
  270.   dbm_close (dict->dbm);
  271.   g_slice_free (Tiandict, dict);
  272. }
  273.  
  274. int main (int argc, char **argv)
  275. {
  276.   Tiandict *dict;
  277.  
  278.   dict = tian_dict_new ();
  279.  
  280.   gtk_main ();
  281.  
  282.   tian_dict_free (dict);
  283.  
  284.   return EXIT_SUCCESS;
  285. }
  286.  
  287. ---------
  288. configure
  289. ---------
  290. #!/bin/sh
  291.  
  292. # default variables
  293. prefix="/usr/local"
  294. EXTRA_CFLAGS="-Wall -Werror"
  295. EXTRA_LDFLAGS="-Wl,-z,relro"
  296. hardening="-D_FORTIFY_SOURCE=2 -O1"
  297.  
  298. for i in "$@"
  299. do
  300. case $i in
  301.   --disable-hardening)
  302.     hardening=""
  303.     ;;
  304.   --libdir=*)
  305.     libdir="${i#*=}"
  306.     ;;
  307.   --prefix=*)
  308.     prefix="${i#*=}"
  309.     ;;
  310.   --help|*)
  311.     echo "Usage: ./configure [OPTION]..."
  312.     echo "  --help                      Show help messages"
  313.     echo "  --disable-hardening         Disable hardening"
  314.     echo "  --libdir=PATH               Set PATH to libdir"
  315.     echo "  --prefix=DIRECTORY          Set DIRECTORY to prefix"
  316.     exit 1
  317.     ;;
  318. esac
  319. done
  320.  
  321. [ -z $libdir ] && libdir="$prefix/lib"
  322. eval libdir=$libdir
  323.  
  324. EXTRA_CFLAGS=$EXTRA_CFLAGS" $hardening"
  325. errmsg=""
  326.  
  327. # check required packages
  328.  
  329. if ! pkg-config --exists glib-2.0;    then errmsg+=" glib-2.0";    fi
  330. if ! pkg-config --exists gobject-2.0; then errmsg+=" gobject-2.0"; fi
  331. if ! pkg-config --exists gtk+-3.0;    then errmsg+=" gtk+-3.0";    fi
  332. if [ ! -f /usr/lib/libgdbm_compat.so.4 ] &&
  333.    [ ! -f /usr/lib/x86_64-linux-gnu/libgdbm_compat.so.4 ]
  334.   then errmsg+=" libgdbm_compat.so.4"
  335. fi
  336.  
  337. # print error messages
  338.  
  339. if test -n "$errmsg"; then
  340.   echo "Not found: $errmsg"
  341.   exit 1
  342. fi
  343.  
  344. # generate config.mk
  345.  
  346. cat > config.mk <<- EOF
  347. EXTRA_CFLAGS      = $EXTRA_CFLAGS
  348. EXTRA_LDFLAGS     = $EXTRA_LDFLAGS
  349. VERSION           = 2020.03.07
  350. CC                = cc
  351. top_srcdir        = $(pwd)
  352. prefix            = $prefix
  353. libdir            = $libdir
  354. datadir           = $prefix/share
  355. GETTEXT_PACKAGE   = tiandict
  356. TARGET_LOCALE_DIR = $prefix/share/locale
  357. MKDIR_P           = mkdir -p
  358. EOF
  359.  
  360. echo "Generated config.mk"
  361. echo "----------------------------------------------------------------"
  362. cat config.mk
  363. echo "----------------------------------------------------------------"
  364.  
  365.  
  366. --------
  367. Makefile
  368. --------
  369. include ./config.mk
  370.  
  371. TARGET = tiandict
  372.  
  373. TARGET_DEPS_CFLAGS = `pkg-config --cflags glib-2.0 gobject-2.0 gtk+-3.0`
  374. TARGET_DEPS_LIBS   = `pkg-config --libs   glib-2.0 gobject-2.0 gtk+-3.0` -lgdbm_compat
  375.  
  376. SOURCES = tiandict.c
  377. CFLAGS = \
  378.     $(TARGET_DEPS_CFLAGS) \
  379.     $(EXTRA_CFLAGS) \
  380.     -DVERSION=\"$(VERSION)\" \
  381.     -DTARGET_LOCALE_DIR=\"$(TARGET_LOCALE_DIR)\"
  382.  
  383. LDFLAGS = $(TARGET_DEPS_LIBS) $(EXTRA_LDFLAGS)
  384.  
  385. all: $(TARGET)
  386.  
  387. $(TARGET): $(SOURCES) Makefile
  388.     $(CC) $(CFLAGS) $(SOURCES) \
  389.       -Wl,--as-needed $(LDFLAGS) -o $(TARGET)
  390.  
  391. clean:
  392.     rm -f $(TARGET) config.mk
  393.  
  394. install:
  395.     install -D -m 755 $(TARGET) -t $(DESTDIR)$(prefix)/bin
  396.  
  397. uninstall:
  398.     rm -f $(DESTDIR)$(prefix)/bin/$(TARGET)
  399.     -rmdir -p $(DESTDIR)$(prefix)/bin
Advertisement
Add Comment
Please, Sign In to add comment