1 /*
2  * Copyright (C) 2020 The HIME team, Taiwan
3  * Copyright (C) 2005-2011 Edward Der-Hua Liu, Hsin-Chu, Taiwan
4  * Copyright (C) 2012 Favonia <favonia@gmail.com>
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation version 2.1
9  * of the License.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
19  */
20 
21 #include "hime.h"
22 
23 #include "gtab.h"
24 #include "hime-conf.h"
25 
26 struct {
27     char *keystr;
28     int keynum;
29 } imkeys[] = {
30     {"Control-Space", Control_Space},
31     {"Shift-Space", Shift_Space},
32     {"Alt-Space", Alt_Space},
33     {"Windows-Space", Windows_Space},
34     {NULL, 0},
35 };
36 
37 extern char *default_input_method_str;
38 
39 /* XXX UI states hold uncommited preference.
40  * That's why we need these global variables. */
41 static GtkWidget *gtablist_widget = NULL;
42 static GtkWidget *vbox;
43 static GtkWidget *sw;
44 static GtkWidget *treeview;
45 static GtkWidget *check_button_phonetic_speak, *opt_speaker_opts, *check_button_hime_bell_off;
46 static GtkWidget *opt_im_toggle_keys, *check_button_hime_remote_client,
47     *check_button_hime_shift_space_eng_full,
48     *check_button_hime_init_im_enabled,
49     *check_button_hime_init_full_mode,
50     *check_button_hime_eng_phrase_enabled,
51     *check_button_hime_win_sym_click_close,
52     *check_button_hime_punc_auto_send;
53 
54 static GtkWidget *check_button_hime_single_state;
55 extern gboolean button_order;
56 
57 char *pho_speaker[16];
58 int pho_speakerN;
59 
60 typedef struct
61 {
62     gchar *name;
63     GdkPixbuf *icon;
64     gchar *key;
65     gchar *file;
66     gboolean cycle;
67     gboolean default_inmd;
68     gboolean use;
69     gboolean editable;
70     INMD *pinmd;
71 } Item;
72 
73 enum {
74     COLUMN_NAME,
75     COLUMN_ICON,
76     COLUMN_KEY,
77     COLUMN_FILE,
78     COLUMN_CYCLE,
79     COLUMN_DEFAULT_INMD,
80     COLUMN_USE,
81     COLUMN_EDITABLE,
82     COLUMN_PINMD,
83     NUM_COLUMNS
84 };
85 
86 static GArray *articles = NULL;
87 int hime_switch_keys_lookup (int key);
88 
89 #if 0
90 /* unused */
91 static int qcmp_key(const void *aa, const void *bb)
92 {
93   Item *a=(Item *)aa, *b=(Item *)bb;
94 
95   return hime_switch_keys_lookup(a->key[0]) - hime_switch_keys_lookup(b->key[0]);
96 }
97 #endif
98 
99 extern char *TableDir;
100 void get_icon_path (char *iconame, char fname[]);
101 
102 static void
add_items(void)103 add_items (void) {
104     Item foo;
105 
106     g_return_if_fail (articles != NULL);
107 
108     load_gtab_list (FALSE);
109 
110     int i;
111     for (i = 0; i < inmdN; i++) {
112         INMD *pinmd = &inmd[i];
113         char *name = pinmd->cname;
114         if (!name)
115             continue;
116 
117         char key[2];
118         char *file = pinmd->filename;
119         char *icon = pinmd->icon;
120 
121         key[0] = pinmd->key_ch;
122         key[1] = 0;
123 
124         foo.name = g_strdup (name);
125         char icon_path[128];
126         get_icon_path (icon, icon_path);
127         GError *err = NULL;
128         foo.icon = gdk_pixbuf_new_from_file (icon_path, &err);
129         foo.key = g_strdup (key);
130         foo.file = g_strdup (file);
131         //    dbg("%d] %d\n",i,pinmd->in_cycle);
132         foo.default_inmd = default_input_method == i;
133         foo.use = !pinmd->disabled;
134         foo.cycle = pinmd->in_cycle && foo.use;
135         foo.editable = FALSE;
136         foo.pinmd = pinmd;
137         g_array_append_vals (articles, &foo, 1);
138     }
139 
140     //  g_array_sort (articles,qcmp_key);
141 }
142 
143 static GtkTreeModel *
create_model(void)144 create_model (void) {
145     gint i = 0;
146     GtkListStore *model;
147     GtkTreeIter iter;
148 
149     /* create array */
150     articles = g_array_sized_new (FALSE, FALSE, sizeof (Item), 1);
151 
152     add_items ();
153 
154     /* create list store */
155     model = gtk_list_store_new (NUM_COLUMNS, G_TYPE_STRING, GDK_TYPE_PIXBUF,
156                                 G_TYPE_STRING,
157                                 G_TYPE_STRING, G_TYPE_BOOLEAN, G_TYPE_BOOLEAN,
158                                 G_TYPE_BOOLEAN,
159                                 G_TYPE_BOOLEAN, G_TYPE_POINTER);
160 
161     /* add items */
162     for (i = 0; i < articles->len; i++) {
163         gtk_list_store_append (model, &iter);
164 
165         gtk_list_store_set (model, &iter,
166                             COLUMN_NAME,
167                             g_array_index (articles, Item, i).name,
168                             COLUMN_ICON,
169                             g_array_index (articles, Item, i).icon,
170                             COLUMN_KEY,
171                             g_array_index (articles, Item, i).key,
172                             COLUMN_FILE,
173                             g_array_index (articles, Item, i).file,
174                             COLUMN_CYCLE,
175                             g_array_index (articles, Item, i).cycle,
176                             COLUMN_DEFAULT_INMD,
177                             g_array_index (articles, Item, i).default_inmd,
178                             COLUMN_USE,
179                             g_array_index (articles, Item, i).use,
180                             COLUMN_EDITABLE,
181                             g_array_index (articles, Item, i).editable,
182                             COLUMN_PINMD,
183                             g_array_index (articles, Item, i).pinmd,
184                             -1);
185     }
186 
187     return GTK_TREE_MODEL (model);
188 }
189 
190 extern char gtab_list[];
191 
save_gtab_list()192 static void save_gtab_list () {
193     char ttt[128];
194     get_hime_user_fname (gtab_list, ttt);
195 
196     FILE *fp;
197 
198     if ((fp = fopen (ttt, "w")) == NULL)
199         p_err ("cannot write to %s\n", ttt);
200 
201     int i;
202     for (i = 0; i < inmdN; i++) {
203         INMD *pinmd = &inmd[i];
204         char *name = pinmd->cname;
205         if (!name)
206             continue;
207 
208         char *file = pinmd->filename;
209         char *icon = pinmd->icon;
210         char *disabled = pinmd->disabled ? "!" : "";
211 
212         fprintf (fp, "%s%s %c %s %s\n", disabled, name, pinmd->key_ch, file, icon);
213     }
214 
215     fclose (fp);
216 }
217 
save_gtablist_conf()218 void save_gtablist_conf () {
219     if (gtablist_widget == NULL) {
220         fprintf (stderr, "save_gtablist_conf: gtablist_widget is NULL!\n");
221         return;
222     }
223 
224     GtkTreeModel *model = gtk_tree_view_get_model ((GtkTreeView *) treeview);
225 
226     GtkTreeIter iter;
227     if (!gtk_tree_model_get_iter_first (model, &iter))
228         return;
229 
230     do {
231         char *tkey;
232         gtk_tree_model_get (model, &iter, COLUMN_KEY, &tkey, -1);
233         gboolean cycle, default_inmd, use;
234         gtk_tree_model_get (model, &iter, COLUMN_CYCLE, &cycle, -1);
235         gtk_tree_model_get (model, &iter, COLUMN_DEFAULT_INMD, &default_inmd, -1);
236         gtk_tree_model_get (model, &iter, COLUMN_USE, &use, -1);
237         INMD *pinmd;
238         gtk_tree_model_get (model, &iter, COLUMN_PINMD, &pinmd, -1);
239         pinmd->in_cycle = cycle;
240         pinmd->disabled = !use;
241     } while (gtk_tree_model_iter_next (model, &iter));
242 
243     dbg ("default_input_method_str %s\n", default_input_method_str);
244     save_hime_conf_str (DEFAULT_INPUT_METHOD, default_input_method_str);
245 
246     int idx;
247     idx = gtk_combo_box_get_active (GTK_COMBO_BOX (opt_im_toggle_keys));
248     save_hime_conf_int (HIME_IM_TOGGLE_KEYS, imkeys[idx].keynum);
249 
250     free (hime_str_im_cycle);
251 
252     int i;
253     char tt[512];
254     int ttN = 0;
255     for (i = 0; i < inmdN; i++) {
256         if (inmd[i].in_cycle) {
257             dbg ("in %d %c\n", i, inmd[i].key_ch);
258             tt[ttN++] = inmd[i].key_ch;
259         }
260     }
261     tt[ttN] = 0;
262     hime_str_im_cycle = strdup (tt);
263     save_hime_conf_str (HIME_STR_IM_CYCLE, hime_str_im_cycle);
264     dbg ("hime_str_im_cycle ttN:%d '%s' '%s'\n", ttN, hime_str_im_cycle, tt);
265 
266     save_hime_conf_int (HIME_REMOTE_CLIENT,
267                         gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (check_button_hime_remote_client)));
268     save_hime_conf_int (HIME_SHIFT_SPACE_ENG_FULL,
269                         gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (check_button_hime_shift_space_eng_full)));
270 
271     save_hime_conf_int (HIME_INIT_IM_ENABLED,
272                         gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (check_button_hime_init_im_enabled)));
273 
274     save_hime_conf_int (HIME_INIT_FULL_MODE,
275                         gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (check_button_hime_init_full_mode)));
276 
277     save_hime_conf_int (HIME_ENG_PHRASE_ENABLED,
278                         gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (check_button_hime_eng_phrase_enabled)));
279 
280     save_hime_conf_int (HIME_WIN_SYM_CLICK_CLOSE,
281                         gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (check_button_hime_win_sym_click_close)));
282 
283     save_hime_conf_int (HIME_BELL_OFF,
284                         gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (check_button_hime_bell_off)));
285 
286     save_hime_conf_int (HIME_PUNC_AUTO_SEND,
287                         gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (check_button_hime_punc_auto_send)));
288     save_hime_conf_int (HIME_SINGLE_STATE,
289                         gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (check_button_hime_single_state)));
290     if (opt_speaker_opts) {
291         idx = gtk_combo_box_get_active (GTK_COMBO_BOX (opt_speaker_opts));
292         save_hime_conf_str (PHONETIC_SPEAK_SEL, pho_speaker[idx]);
293     }
294 
295     save_gtab_list ();
296 
297     save_hime_conf_int (PHONETIC_SPEAK,
298                         gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (check_button_phonetic_speak)));
299 
300     save_omni_config ();
301     /* caleb- did not found where "reload" is used.
302    * caleb- think the send_hime_message() here does nothing.
303    */
304     send_hime_message (GDK_DISPLAY (), "reload");
305 }
306 
destroy_gtablist_widget()307 void destroy_gtablist_widget () {
308     gtk_widget_destroy (gtablist_widget);
309     gtablist_widget = NULL;
310 }
311 
312 int hime_switch_keys_lookup (int key);
313 #if 1
toggled(GtkCellRendererToggle * cell,gchar * path_string,gpointer data)314 static gboolean toggled (GtkCellRendererToggle *cell, gchar *path_string, gpointer data) {
315     GtkTreeModel *model = GTK_TREE_MODEL (data);
316     GtkTreeIter iter;
317     GtkTreePath *path = gtk_tree_path_new_from_string (path_string);
318     gboolean cycle;
319 
320     dbg ("toggled\n");
321 
322     gtk_tree_model_get_iter (model, &iter, path);
323     gtk_tree_model_get (model, &iter, COLUMN_CYCLE, &cycle, -1);
324 
325     cycle ^= 1;
326     gtk_list_store_set (GTK_LIST_STORE (model), &iter, COLUMN_CYCLE, cycle, -1);
327 
328     if (cycle) {
329         gtk_list_store_set (GTK_LIST_STORE (model), &iter, COLUMN_USE, TRUE, -1);
330     }
331 
332     gtk_tree_path_free (path);
333 
334     return TRUE;
335 }
336 #endif
337 
clear_col_default_inmd(GtkTreeModel * model)338 static void clear_col_default_inmd (GtkTreeModel *model) {
339     GtkTreeIter iter;
340 
341     if (!gtk_tree_model_get_iter_first (model, &iter))
342         return;
343 
344     do {
345         char *tkey;
346         gtk_tree_model_get (model, &iter, COLUMN_KEY, &tkey, -1);
347         gtk_list_store_set (GTK_LIST_STORE (model), &iter, COLUMN_DEFAULT_INMD, 0, -1);
348 
349     } while (gtk_tree_model_iter_next (model, &iter));
350 }
351 
toggled_default_inmd(GtkCellRendererToggle * cell,gchar * path_string,gpointer data)352 static gboolean toggled_default_inmd (GtkCellRendererToggle *cell, gchar *path_string, gpointer data) {
353     GtkTreeModel *model = GTK_TREE_MODEL (data);
354     clear_col_default_inmd (model);
355     GtkTreeIter iter;
356     GtkTreePath *path = gtk_tree_path_new_from_string (path_string);
357 
358     //  dbg("toggled_default_inmd\n");
359     gtk_tree_model_get_iter (model, &iter, path);
360     char *key;
361     gtk_tree_model_get (model, &iter, COLUMN_KEY, &key, -1);
362     char *file;
363     gtk_tree_model_get (model, &iter, COLUMN_FILE, &file, -1);
364     char tt[128];
365     snprintf (tt, sizeof (tt), "%s %s", key, file);
366     free (default_input_method_str);
367     default_input_method_str = strdup (tt);
368     dbg ("default_input_method_str %s\n", default_input_method_str);
369     //  default_input_method = hime_switch_keys_lookup(key[0]);
370 
371     gtk_list_store_set (GTK_LIST_STORE (model), &iter, COLUMN_DEFAULT_INMD, TRUE, -1);
372     gtk_list_store_set (GTK_LIST_STORE (model), &iter, COLUMN_USE, TRUE, -1);
373 
374     gtk_tree_path_free (path);
375 
376     return TRUE;
377 }
378 
toggled_use(GtkCellRendererToggle * cell,gchar * path_string,gpointer data)379 static gboolean toggled_use (GtkCellRendererToggle *cell, gchar *path_string, gpointer data) {
380     GtkTreeModel *model = GTK_TREE_MODEL (data);
381     //  clear_all(model);
382     GtkTreeIter iter;
383     GtkTreePath *path = gtk_tree_path_new_from_string (path_string);
384 
385     //  dbg("toggled_default_inmd\n");
386     gtk_tree_model_get_iter (model, &iter, path);
387     gboolean cycle, default_inmd, use;
388     gtk_tree_model_get (model, &iter, COLUMN_CYCLE, &cycle, -1);
389     gtk_tree_model_get (model, &iter, COLUMN_DEFAULT_INMD, &default_inmd, -1);
390     gtk_tree_model_get (model, &iter, COLUMN_USE, &use, -1);
391     use = !use;
392     gboolean must_on = default_inmd;
393     dbg ("toggle %d %d %d\n", cycle, default_inmd, use);
394 
395     if (must_on && !use) {
396         return TRUE;
397     }
398 
399     gtk_list_store_set (GTK_LIST_STORE (model), &iter, COLUMN_USE, use, -1);
400     if (!use)
401         gtk_list_store_set (GTK_LIST_STORE (model), &iter, COLUMN_CYCLE, FALSE, -1);
402 
403     gtk_tree_path_free (path);
404 
405     return TRUE;
406 }
407 
408 static void
add_columns(GtkTreeView * treeview)409 add_columns (GtkTreeView *treeview) {
410     GtkCellRenderer *renderer;
411     GtkTreeModel *model = gtk_tree_view_get_model (treeview);
412 
413     /* name column */
414 
415     renderer = gtk_cell_renderer_text_new ();
416     g_object_set_data (G_OBJECT (renderer), "column", (gint *) COLUMN_NAME);
417     gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (treeview),
418                                                  -1, _ ("Name"), renderer,
419                                                  "text", COLUMN_NAME,
420                                                  "editable", COLUMN_EDITABLE,
421                                                  NULL);
422 
423     renderer = gtk_cell_renderer_pixbuf_new ();
424     g_object_set_data (G_OBJECT (renderer), "column", (gint *) COLUMN_ICON);
425     gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (treeview),
426                                                  -1, "圖示", renderer,
427                                                  "pixbuf", COLUMN_ICON,
428                                                  //                                               "editable", COLUMN_EDITABLE,
429                                                  NULL);
430 
431     renderer = gtk_cell_renderer_text_new ();
432     g_object_set_data (G_OBJECT (renderer), "column", (gint *) COLUMN_KEY);
433 
434     gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (treeview),
435                                                  -1, _ ("Ctrl-Alt-Toggle Key"), renderer,
436                                                  "text", COLUMN_KEY,
437                                                  "editable", COLUMN_EDITABLE,
438                                                  NULL);
439 
440     // cycle column
441     renderer = gtk_cell_renderer_toggle_new ();
442     g_signal_connect (G_OBJECT (renderer), "toggled",
443                       G_CALLBACK (toggled), model);
444 
445     g_object_set (G_OBJECT (renderer), "xalign", 0.0, NULL);
446 
447     gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (treeview), -1,
448                                                  _ ("Ctrl-Shift selections"),
449                                                  renderer, "active", COLUMN_CYCLE,
450                                                  NULL);
451 
452     // default_inmd column
453     renderer = gtk_cell_renderer_toggle_new ();
454     gtk_cell_renderer_toggle_set_radio (GTK_CELL_RENDERER_TOGGLE (renderer), TRUE);
455     g_signal_connect (G_OBJECT (renderer), "toggled",
456                       G_CALLBACK (toggled_default_inmd), model);
457 
458     g_object_set (G_OBJECT (renderer), "xalign", 0.0, NULL);
459 
460     gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (treeview),
461                                                  -1, _ ("Default"),
462 
463                                                  renderer,
464                                                  "active", COLUMN_DEFAULT_INMD,
465                                                  NULL);
466 
467     // use
468     renderer = gtk_cell_renderer_toggle_new ();
469     g_signal_connect (G_OBJECT (renderer), "toggled",
470                       G_CALLBACK (toggled_use), model);
471 
472     g_object_set (G_OBJECT (renderer), "xalign", 0.0, NULL);
473 
474     gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (treeview),
475                                                  -1, _ ("Enable"),
476                                                  renderer,
477                                                  "active", COLUMN_USE,
478                                                  NULL);
479 
480     renderer = gtk_cell_renderer_text_new ();
481     g_object_set_data (G_OBJECT (renderer), "column", (gint *) COLUMN_FILE);
482 
483     gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (treeview),
484                                                  -1, _ ("File Name"), renderer,
485                                                  "text", COLUMN_FILE,
486                                                  "editable", COLUMN_EDITABLE,
487                                                  NULL);
488 }
489 
set_selection_by_key(int key)490 void set_selection_by_key (int key) {
491     if (!treeview)
492         return;
493 
494     GtkTreeSelection *selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview));
495     GtkTreeModel *model = gtk_tree_view_get_model (GTK_TREE_VIEW (treeview));
496     GtkTreeIter iter;
497     gboolean found = FALSE;
498 
499     if (!gtk_tree_model_get_iter_first (model, &iter))
500         return;
501 
502     do {
503         char *tkey;
504 
505         gtk_tree_model_get (model, &iter, COLUMN_KEY, &tkey, -1);
506 
507         if (atoi (tkey) == key) {
508             found = TRUE;
509             break;
510         }
511     } while (gtk_tree_model_iter_next (model, &iter));
512 
513     if (found)
514         gtk_tree_selection_select_iter (selection, &iter);
515 }
516 
create_im_toggle_keys()517 static GtkWidget *create_im_toggle_keys () {
518 
519     GtkWidget *hbox = gtk_hbox_new (FALSE, 1);
520     GtkWidget *label = gtk_label_new (_ ("Toggle input window"));
521     gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
522 
523     opt_im_toggle_keys = gtk_combo_box_text_new ();
524     gtk_box_pack_start (GTK_BOX (hbox), opt_im_toggle_keys, FALSE, FALSE, 0);
525 
526     int i, current_idx = 0;
527 
528     for (i = 0; imkeys[i].keystr; i++) {
529         if (imkeys[i].keynum == hime_im_toggle_keys)
530             current_idx = i;
531         gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (opt_im_toggle_keys), imkeys[i].keystr);
532     }
533 
534     gtk_combo_box_set_active (GTK_COMBO_BOX (opt_im_toggle_keys), current_idx);
535 
536     return hbox;
537 }
538 
539 int get_current_speaker_idx ();
540 
create_speaker_opts()541 static GtkWidget *create_speaker_opts () {
542     GtkWidget *hbox = gtk_hbox_new (FALSE, 1);
543 
544     opt_speaker_opts = gtk_combo_box_text_new ();
545     gtk_box_pack_start (GTK_BOX (hbox), opt_speaker_opts, FALSE, FALSE, 0);
546 
547     int i;
548     int current_idx = get_current_speaker_idx ();
549 
550     for (i = 0; i < pho_speakerN; i++) {
551         if (imkeys[i].keynum == hime_im_toggle_keys)
552             current_idx = i;
553         gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (opt_speaker_opts), pho_speaker[i]);
554     }
555 
556     gtk_combo_box_set_active (GTK_COMBO_BOX (opt_speaker_opts), current_idx);
557 
558     return hbox;
559 }
560 
561 #include <dirent.h>
562 
create_gtablist_widget()563 GtkWidget *create_gtablist_widget () {
564     if (gtablist_widget != NULL)
565         fprintf (stderr, "create_gtablist_widget: gtablist_widget was not NULL!\n");
566 
567     load_settings ();
568 
569     GtkWidget *box = gtk_vbox_new (FALSE, 0);
570     gtk_orientable_set_orientation (GTK_ORIENTABLE (box), GTK_ORIENTATION_VERTICAL);
571     gtablist_widget = box;
572 
573     vbox = gtk_vbox_new (FALSE, 0);
574     gtk_orientable_set_orientation (GTK_ORIENTABLE (vbox), GTK_ORIENTATION_VERTICAL);
575     gtk_box_pack_start (GTK_BOX (box), vbox, TRUE, TRUE, 0);
576 
577     sw = gtk_scrolled_window_new (NULL, NULL);
578     gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (sw),
579                                          GTK_SHADOW_ETCHED_IN);
580     gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw),
581                                     GTK_POLICY_NEVER,
582                                     GTK_POLICY_AUTOMATIC);
583     gtk_box_pack_start (GTK_BOX (vbox), sw, TRUE, TRUE, 0);
584 
585     /* create model */
586     GtkTreeModel *model = create_model ();
587 
588     /* create tree view */
589     treeview = gtk_tree_view_new_with_model (model);
590     gtk_widget_set_hexpand (treeview, TRUE);
591     gtk_widget_set_vexpand (treeview, TRUE);
592     g_object_unref (G_OBJECT (model));
593     gtk_tree_view_set_rules_hint (GTK_TREE_VIEW (treeview), TRUE);
594 
595     GtkTreeSelection *tree_selection =
596         gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview));
597     gtk_tree_selection_set_mode (tree_selection, GTK_SELECTION_SINGLE);
598 
599     add_columns (GTK_TREE_VIEW (treeview));
600 
601     gtk_container_add (GTK_CONTAINER (sw), treeview);
602 
603     {
604         // Trying to get correct size of dialog_data->treeview, then put it into a gtk_scrolled_window
605         /* XXX Favonia: is this necessary? */
606         gtk_widget_show_all (gtablist_widget);
607 
608         GtkRequisition requisition;
609         gtk_widget_get_child_requisition (treeview, &requisition);
610         gtk_widget_set_size_request (vbox, requisition.width, 240);
611     }
612 
613     vbox = gtk_vbox_new (FALSE, 0);
614     gtk_orientable_set_orientation (GTK_ORIENTABLE (vbox), GTK_ORIENTATION_VERTICAL);
615     gtk_box_pack_start (GTK_BOX (box), vbox, FALSE, FALSE, 0);
616 
617     gtk_box_pack_start (GTK_BOX (vbox), create_im_toggle_keys (), FALSE, FALSE, 0);
618 
619     GtkWidget *hbox_hime_remote_client = gtk_hbox_new (FALSE, 10);
620     gtk_box_pack_start (GTK_BOX (vbox), hbox_hime_remote_client, FALSE, FALSE, 0);
621     check_button_hime_remote_client = gtk_check_button_new_with_label (_ ("Remote client support (port 9999-)"));
622     gtk_box_pack_start (GTK_BOX (hbox_hime_remote_client), check_button_hime_remote_client, FALSE, FALSE, 0);
623     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (check_button_hime_remote_client),
624                                   hime_remote_client);
625 
626     GtkWidget *hbox_hime_init_im_enabled = gtk_hbox_new (FALSE, 10);
627     gtk_box_pack_start (GTK_BOX (vbox), hbox_hime_init_im_enabled, FALSE, FALSE, 0);
628     check_button_hime_init_im_enabled = gtk_check_button_new_with_label (_ ("Toggle On inputing mode by default (non-XIM only)"));
629     gtk_box_pack_start (GTK_BOX (hbox_hime_init_im_enabled), check_button_hime_init_im_enabled, FALSE, FALSE, 0);
630     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (check_button_hime_init_im_enabled),
631                                   hime_init_im_enabled);
632 
633     GtkWidget *hbox_hime_init_full_mode = gtk_hbox_new (FALSE, 10);
634     gtk_box_pack_start (GTK_BOX (vbox), hbox_hime_init_full_mode, FALSE, FALSE, 0);
635     check_button_hime_init_full_mode = gtk_check_button_new_with_label (_ ("Toggle Full-shape mode by default"));
636     gtk_box_pack_start (GTK_BOX (hbox_hime_init_full_mode), check_button_hime_init_full_mode, FALSE, FALSE, 0);
637     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (check_button_hime_init_full_mode),
638                                   hime_init_full_mode);
639 
640     GtkWidget *hbox_hime_shift_space_eng_full = gtk_hbox_new (FALSE, 10);
641     gtk_box_pack_start (GTK_BOX (vbox), hbox_hime_shift_space_eng_full, FALSE, FALSE, 0);
642     check_button_hime_shift_space_eng_full = gtk_check_button_new_with_label (_ ("Toggle half-/double-width with Shift-Space"));
643     gtk_box_pack_start (GTK_BOX (hbox_hime_shift_space_eng_full), check_button_hime_shift_space_eng_full, FALSE, FALSE, 0);
644     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (check_button_hime_shift_space_eng_full),
645                                   hime_shift_space_eng_full);
646 
647     GtkWidget *hbox_hime_single_state = gtk_hbox_new (FALSE, 10);
648     gtk_box_pack_start (GTK_BOX (vbox), hbox_hime_single_state, FALSE, FALSE, 0);
649     check_button_hime_single_state = gtk_check_button_new_with_label (_ ("Share input method state for all processes"));
650     gtk_box_pack_start (GTK_BOX (hbox_hime_single_state), check_button_hime_single_state, FALSE, FALSE, 0);
651     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (check_button_hime_single_state),
652                                   hime_single_state);
653 
654     GtkWidget *hbox_hime_eng_phrase_enabled = gtk_hbox_new (FALSE, 10);
655     gtk_box_pack_start (GTK_BOX (vbox), hbox_hime_eng_phrase_enabled, FALSE, FALSE, 0);
656     check_button_hime_eng_phrase_enabled = gtk_check_button_new_with_label (_ ("Enable Alt-Shift phrases in alphabet-numeric mode"));
657     gtk_box_pack_start (GTK_BOX (hbox_hime_eng_phrase_enabled), check_button_hime_eng_phrase_enabled, FALSE, FALSE, 0);
658     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (check_button_hime_eng_phrase_enabled),
659                                   hime_eng_phrase_enabled);
660 
661     GtkWidget *hbox_phonetic_speak = gtk_hbox_new (FALSE, 10);
662     gtk_box_pack_start (GTK_BOX (vbox), hbox_phonetic_speak, FALSE, FALSE, 0);
663     check_button_phonetic_speak = gtk_check_button_new_with_label (_ ("Enable text-to-speech"));
664     gtk_box_pack_start (GTK_BOX (hbox_phonetic_speak), check_button_phonetic_speak, FALSE, FALSE, 0);
665     gtk_toggle_button_set_active (
666         GTK_TOGGLE_BUTTON (check_button_phonetic_speak), phonetic_speak);
667 
668     GtkWidget *hbox_hime_win_sym_click_close = gtk_hbox_new (FALSE, 10);
669     gtk_box_pack_start (GTK_BOX (vbox), hbox_hime_win_sym_click_close, FALSE, FALSE, 0);
670     check_button_hime_win_sym_click_close = gtk_check_button_new_with_label (_ ("Close symbols window after selection"));
671     gtk_box_pack_start (GTK_BOX (hbox_hime_win_sym_click_close), check_button_hime_win_sym_click_close, FALSE, FALSE, 0);
672     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (check_button_hime_win_sym_click_close),
673                                   hime_win_sym_click_close);
674 
675     GtkWidget *hbox_hime_bell_off = gtk_hbox_new (FALSE, 10);
676     gtk_box_pack_start (GTK_BOX (vbox), hbox_hime_bell_off, FALSE, FALSE, 0);
677     check_button_hime_bell_off = gtk_check_button_new_with_label (_ ("Disable beep"));
678     gtk_box_pack_start (GTK_BOX (hbox_hime_bell_off), check_button_hime_bell_off, FALSE, FALSE, 0);
679     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (check_button_hime_bell_off),
680                                   hime_bell_off);
681 
682     GtkWidget *hbox_hime_punc_auto_send = gtk_hbox_new (FALSE, 10);
683     gtk_box_pack_start (GTK_BOX (vbox), hbox_hime_punc_auto_send, FALSE, FALSE, 0);
684     check_button_hime_punc_auto_send = gtk_check_button_new_with_label (_ ("Auto-commit words ended with punctuation"));
685     gtk_box_pack_start (GTK_BOX (hbox_hime_punc_auto_send), check_button_hime_punc_auto_send, FALSE, FALSE, 0);
686     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (check_button_hime_punc_auto_send),
687                                   hime_punc_auto_send);
688 
689     DIR *dir;
690     if ((dir = opendir (HIME_OGG_DIR "/ㄧ"))) {
691         struct dirent *dire;
692 
693         pho_speakerN = 0;
694         while ((dire = readdir (dir))) {
695             char *name = dire->d_name;
696 
697             if (name[0] == '.')
698                 continue;
699             pho_speaker[pho_speakerN++] = strdup (name);
700         }
701         closedir (dir);
702 
703         dbg ("pho_speakerN:%d\n", pho_speakerN);
704     }
705 
706     if (pho_speakerN) {
707         GtkWidget *labelspeaker = gtk_label_new (_ ("speaker"));
708         gtk_box_pack_start (GTK_BOX (hbox_phonetic_speak), labelspeaker, FALSE, FALSE, 0);
709         gtk_container_add (GTK_CONTAINER (hbox_phonetic_speak), create_speaker_opts ());
710     }
711 
712     return gtablist_widget;
713 }
714