fix macro key: ignore case of key when add
[platform/core/uifw/ise-engine-unikey.git] / src / scim_unikey_setup.cpp
1 /**
2    Scim-Unikey Input Method
3
4    Copyright (C) 2008-2009 Ubuntu-VN <http://www.ubuntu-vn.org>
5    Author: Le Quoc Tuan <mr.lequoctuan@gmail.com>
6    Home: http://scim-unikey.googlecode.com
7    License: GNU LESSER GENERAL PUBLIC LICENSE v2.1
8 */
9
10 #define Uses_SCIM_CONFIG_BASE
11
12 #include <libintl.h>
13 #define _(String) dgettext(PACKAGE_NAME,String)
14
15 #include <iostream>
16 #include <gtk/gtk.h>
17 #include <string.h>
18
19 #include <scim.h>
20 #include "scim_unikey_const.h"
21 #include "scim_unikey_utils.h"
22 #include "keycons.h"
23 #include "mactab.h"
24 #include "scimkeyselection.h"
25
26 using namespace scim;
27
28 #define scim_module_init unikey_setup_LTX_scim_module_init
29 #define scim_module_exit unikey_setup_LTX_scim_module_exit
30
31 #define scim_setup_module_create_ui       unikey_setup_LTX_scim_setup_module_create_ui
32 #define scim_setup_module_get_category    unikey_setup_LTX_scim_setup_module_get_category
33 #define scim_setup_module_get_name        unikey_setup_LTX_scim_setup_module_get_name
34 #define scim_setup_module_get_description unikey_setup_LTX_scim_setup_module_get_description
35 #define scim_setup_module_load_config     unikey_setup_LTX_scim_setup_module_load_config
36 #define scim_setup_module_save_config     unikey_setup_LTX_scim_setup_module_save_config
37 #define scim_setup_module_query_changed   unikey_setup_LTX_scim_setup_module_query_changed
38
39 static bool    __have_changed                       = false;
40
41 static bool    __unikey_preedit                     = SCIM_IMENGINE_UNIKEY_PREEDIT_DEF;
42 static bool    __unikey_freemarking                 = SCIM_IMENGINE_UNIKEY_FREEMARKING_DEF;
43 static bool    __unikey_modernstyle                 = SCIM_IMENGINE_UNIKEY_MODERNSTYLE_DEF;
44 static bool    __unikey_macroenabled                = SCIM_IMENGINE_UNIKEY_MACROENABLED_DEF;
45 static bool    __unikey_spellcheckenabled           = SCIM_IMENGINE_UNIKEY_SPELLCHECKENABLED_DEF;
46 static bool    __unikey_autononvnrestore            = SCIM_IMENGINE_UNIKEY_AUTONONVNRESTORE_DEF;
47 static bool    __unikey_codertelex                  = SCIM_IMENGINE_UNIKEY_CODERTELEX_DEF;
48 static bool    __unikey_processwatwordbegin       = SCIM_IMENGINE_UNIKEY_PROCESSWATWORDBEGIN_DEF;
49
50 static GtkTooltips  *__widget_tooltips              = 0;
51 static GtkWidget    *__widget_preedit               = 0;
52 static GtkWidget    *__widget_preedit_skey          = 0;
53 static GtkWidget    *__widget_freemarking           = 0;
54 static GtkWidget    *__widget_modernstyle           = 0;
55 static GtkWidget    *__widget_macroenabled          = 0;
56 static GtkWidget    *__widget_spellcheckenabled     = 0;
57 static GtkWidget    *__widget_autononvnrestore      = 0;
58 static GtkWidget    *__widget_codertelex            = 0;
59 static GtkWidget    *__widget_processwatwordbegin   = 0;
60 static CMacroTable  __macStore;
61
62 static GtkWidget* create_setup_window();
63 static void load_config(const ConfigPointer &config);
64 static void save_config(const ConfigPointer &config);
65 static bool query_changed();
66
67 static void setup_widget_value();
68 static GtkListStore* create_and_fill_list_store();
69
70 static void on_default_toggle_button_toggled(GtkToggleButton *togglebutton, gpointer user_data);
71 static void on_macrotable_button_clicked(GtkButton *button, gpointer user_data);
72 static void on_key_edited (GtkCellRendererText *celltext, const gchar *string_path, const gchar *new_text, gpointer data);
73 static void on_replace_edited (GtkCellRendererText *celltext, const gchar *string_path, const gchar *new_text, gpointer data);
74 static void on_end_macro_table(GtkListStore *liststore);
75 static void on_del_macro_clicked(GtkButton *button, gpointer user_data);
76 static void on_delall_macro_clicked(GtkButton *button, gpointer user_data);
77 static gboolean iter_2_macro(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data);
78 static void on_hotkey_button_clicked(GtkButton *button, gpointer user_data);
79
80 extern "C"
81 {
82     void scim_module_init(void)
83     {
84     }
85
86     void scim_module_exit(void)
87     {
88     }
89
90     GtkWidget * scim_setup_module_create_ui (void)
91     {
92         return create_setup_window();
93     }
94
95     String scim_setup_module_get_category (void)
96     {
97         return String("IMEngine");
98     }
99
100     String scim_setup_module_get_name(void)
101     {
102         return String(_("Unikey Setup"));
103     }
104
105     String scim_setup_module_get_description(void)
106     {
107         return String("Setup scim-unikey v" SCIM_UNIKEY_VERSION " IMEngine.");
108     }
109
110     void scim_setup_module_load_config(const ConfigPointer &config)
111     {
112         load_config(config);
113     }
114
115     void scim_setup_module_save_config(const ConfigPointer &config)
116     {
117         save_config(config);
118     }
119
120     bool scim_setup_module_query_changed()
121     {
122         return query_changed();
123     }
124 } // extern "C"
125
126 static GtkWidget* create_setup_window()
127 {
128     static GtkWidget *window = 0;
129     if (!window)
130     {
131         gtk_window_set_default_icon_from_file(SCIM_ICONDIR SCIM_UNIKEY_ICON_FILENAME, NULL);
132         __widget_tooltips = gtk_tooltips_new();
133
134 // Create the toplevel box.
135         window = gtk_hbox_new(false, 0);
136
137 // create vbox
138         GtkWidget *vbox0 = gtk_vbox_new(false, 0);
139         gtk_box_pack_start(GTK_BOX(window), vbox0, false, false, 0);
140
141 // create Common Frame
142         GtkWidget *fr = gtk_frame_new(_("Unikey option"));
143         gtk_box_pack_start(GTK_BOX(vbox0), fr, false, false, 0);
144
145         GtkWidget *vbox = gtk_vbox_new(false, 5);
146         gtk_container_add(GTK_CONTAINER(fr), vbox);
147         gtk_container_set_border_width(GTK_CONTAINER(fr), 5);
148
149 // create spellcheck checkbox
150         __widget_spellcheckenabled = gtk_check_button_new_with_label(_("Enable spell check"));
151         gtk_box_pack_start(GTK_BOX(vbox), __widget_spellcheckenabled, false, false, 0);
152         gtk_container_set_border_width(GTK_CONTAINER(__widget_spellcheckenabled), 5);
153         g_signal_connect(__widget_spellcheckenabled, "toggled", G_CALLBACK(on_default_toggle_button_toggled), &__unikey_spellcheckenabled);
154
155         gtk_tooltips_set_tip (__widget_tooltips, __widget_spellcheckenabled,
156                               _("If enable, you can decrease mistake when typing"), NULL);
157
158 // create autononvnrestore checkbox
159         __widget_autononvnrestore = gtk_check_button_new_with_label(_("Auto restore keys with invalid words"));
160         gtk_box_pack_start(GTK_BOX(vbox), __widget_autononvnrestore, false, false, 0);
161         gtk_container_set_border_width(GTK_CONTAINER(__widget_autononvnrestore), 5);
162         g_signal_connect(__widget_autononvnrestore, "toggled", G_CALLBACK(on_default_toggle_button_toggled), &__unikey_autononvnrestore);
163
164         gtk_tooltips_set_tip (__widget_tooltips, __widget_autononvnrestore,
165                               _("When typing a word not in Vietnamese,\n"
166                               "it will auto restore keystroke into orginal"), NULL);
167
168 // create modernstyle checkbox
169         __widget_modernstyle = gtk_check_button_new_with_label(_("Use oà, uý (instead of òa, úy)"));
170         gtk_box_pack_start(GTK_BOX(vbox), __widget_modernstyle, false, false, 0);
171         gtk_container_set_border_width(GTK_CONTAINER(__widget_modernstyle), 5);
172         g_signal_connect(__widget_modernstyle, "toggled", G_CALLBACK(on_default_toggle_button_toggled), &__unikey_modernstyle);
173
174 // create freemarking checkbox
175         __widget_freemarking = gtk_check_button_new_with_label(_("Allow type with more freedom"));
176         gtk_box_pack_start(GTK_BOX(vbox), __widget_freemarking, false, false, 0);
177         gtk_container_set_border_width(GTK_CONTAINER(__widget_freemarking), 5);
178         g_signal_connect(__widget_freemarking, "toggled", G_CALLBACK(on_default_toggle_button_toggled), &__unikey_freemarking);
179
180 /*
181 // create preedit frame
182 */
183         GtkWidget *prefr = gtk_frame_new(_("Typing mode"));
184         gtk_container_set_border_width(GTK_CONTAINER(prefr), 5);
185         gtk_box_pack_start(GTK_BOX(vbox0), prefr, false, false, 0);
186
187         vbox = gtk_vbox_new(false, 5);
188         gtk_container_add(GTK_CONTAINER(prefr), vbox);
189
190 // create preedit switch key
191         GtkWidget *psbox = gtk_hbox_new(false, 0);
192         gtk_box_pack_start(GTK_BOX(vbox), psbox, false, false, 0);
193
194         GtkWidget *lpskey = gtk_label_new(_("Mode switch key:"));
195         gtk_box_pack_start(GTK_BOX(psbox), lpskey, false, false, 5);
196
197         __widget_preedit_skey = gtk_entry_new();
198         gtk_entry_set_editable(GTK_ENTRY(__widget_preedit_skey), false);
199         gtk_box_pack_start(GTK_BOX(psbox), __widget_preedit_skey, true, false, 0);
200
201         GtkWidget *bpskey = gtk_button_new_with_label("...");
202         gtk_box_pack_start(GTK_BOX(psbox), bpskey, true, true, 0);
203         gtk_container_set_border_width(GTK_CONTAINER(bpskey), 5);
204         g_signal_connect(bpskey, "clicked", G_CALLBACK(on_hotkey_button_clicked), __widget_preedit_skey);
205
206
207 // create preedit checkbox
208         __widget_preedit = gtk_check_button_new_with_label(_("Preedit is default"));
209         gtk_box_pack_start(GTK_BOX(vbox), __widget_preedit, false, false, 0);
210         gtk_container_set_border_width(GTK_CONTAINER(__widget_preedit), 5);
211         g_signal_connect(__widget_preedit, "toggled", G_CALLBACK(on_default_toggle_button_toggled), &__unikey_preedit);
212
213         gtk_tooltips_set_tip (__widget_tooltips, __widget_preedit,
214                               _("This option is best for most application\n"
215                               "But you may don't like it because it have an underline when typing"), NULL);
216
217
218
219 //
220         vbox = gtk_vbox_new(false, 0);
221         gtk_box_pack_start(GTK_BOX(window), vbox, true, true, 0);
222
223 // right frame 1
224         fr = gtk_frame_new(_("Macro option"));
225         gtk_box_pack_start(GTK_BOX(vbox), fr, false, true, 0);
226         gtk_container_set_border_width(GTK_CONTAINER(fr), 5);
227
228         // (frame 2)
229         GtkWidget *fr2 = gtk_frame_new(_("Telex option"));
230         gtk_box_pack_start(GTK_BOX(vbox), fr2, false, false, 0);
231         gtk_container_set_border_width(GTK_CONTAINER(fr2), 5);
232
233         vbox = gtk_vbox_new(false, 5);
234         gtk_container_add(GTK_CONTAINER(fr), vbox);
235
236 // create macroenabled checkbox
237         __widget_macroenabled = gtk_check_button_new_with_label(_("Enable Macro"));
238         gtk_box_pack_start(GTK_BOX(vbox), __widget_macroenabled, false, false, 0);
239         gtk_container_set_border_width(GTK_CONTAINER(__widget_macroenabled), 5);
240         g_signal_connect(__widget_macroenabled, "toggled", G_CALLBACK(on_default_toggle_button_toggled), &__unikey_macroenabled);
241
242 // create macroedit button
243         GtkWidget* __widget_macrotable = gtk_button_new_with_label(_("Macro Table"));
244         gtk_box_pack_start(GTK_BOX(vbox), __widget_macrotable, false, false, 0);
245         gtk_container_set_border_width(GTK_CONTAINER(__widget_macrotable), 5);
246         g_signal_connect(__widget_macrotable, "clicked", G_CALLBACK(on_macrotable_button_clicked), NULL);
247
248         gtk_tooltips_set_tip (__widget_tooltips, __widget_macrotable,
249                               _("Edit the macro table for Macro function"), NULL);
250
251
252 // right frame 2
253         vbox = gtk_vbox_new(false, 5);
254         gtk_container_add(GTK_CONTAINER(fr2), vbox);
255
256 // create process w at word begin checkbox
257         __widget_processwatwordbegin = gtk_check_button_new_with_label(_("Process W at word begin"));
258         gtk_box_pack_start(GTK_BOX(vbox), __widget_processwatwordbegin, false, false, 0);
259         gtk_container_set_border_width(GTK_CONTAINER(__widget_processwatwordbegin), 5);
260         g_signal_connect(__widget_processwatwordbegin, "toggled", G_CALLBACK(on_default_toggle_button_toggled), &__unikey_processwatwordbegin);
261
262         gtk_tooltips_set_tip (__widget_tooltips, __widget_processwatwordbegin,
263                               _("If enable, type W at begin\n"
264                               "of word will change to Ư."), NULL);
265
266 // create coder telex checkbox
267         __widget_codertelex = gtk_check_button_new_with_label(_("Not use [,],{,} on Telex"));
268         gtk_box_pack_start(GTK_BOX(vbox), __widget_codertelex, false, false, 0);
269         gtk_container_set_border_width(GTK_CONTAINER(__widget_codertelex), 5);
270         g_signal_connect(__widget_codertelex, "toggled", G_CALLBACK(on_default_toggle_button_toggled), &__unikey_codertelex);
271
272         gtk_tooltips_set_tip (__widget_tooltips, __widget_codertelex,
273                               _("Not use [,],{,} for map on Telex"), NULL);
274
275
276         setup_widget_value();
277         gtk_widget_show_all(window);
278     }
279     return window;
280 }
281
282 static void setup_widget_value()
283 {
284     if (__widget_preedit)
285         gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(__widget_preedit), __unikey_preedit);
286
287     if (__widget_spellcheckenabled)
288         gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(__widget_spellcheckenabled), __unikey_spellcheckenabled);
289
290     if (__widget_autononvnrestore)
291         gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(__widget_autononvnrestore), __unikey_autononvnrestore);
292
293     if (__widget_modernstyle)
294         gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(__widget_modernstyle), __unikey_modernstyle);
295
296     if (__widget_freemarking)
297         gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(__widget_freemarking), __unikey_freemarking);
298
299     if (__widget_macroenabled)
300         gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(__widget_macroenabled), __unikey_macroenabled);
301
302     if (__widget_codertelex)
303         gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(__widget_codertelex), __unikey_codertelex);
304
305     if (__widget_processwatwordbegin)
306         gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(__widget_processwatwordbegin), __unikey_processwatwordbegin);
307 }
308
309 static void load_config(const ConfigPointer &config)
310 {
311     if (!config.null())
312     {
313         bool t;
314         t = config->read(SCIM_IMENGINE_UNIKEY_PREEDIT, &__unikey_preedit);
315         if (!t) __unikey_preedit = SCIM_IMENGINE_UNIKEY_PREEDIT_DEF;
316
317         String s;        
318         t = config->read(SCIM_IMENGINE_UNIKEY_PREEDIT_SWITCH_KEY, &s);
319         gtk_entry_set_text(GTK_ENTRY(__widget_preedit_skey), t?s.c_str():SCIM_IMENGINE_UNIKEY_PREEDIT_SWITCH_KEY_DEF);
320
321         t = config->read(SCIM_IMENGINE_UNIKEY_FREEMARKING, &__unikey_freemarking);
322         if (!t) __unikey_freemarking = SCIM_IMENGINE_UNIKEY_FREEMARKING_DEF;
323
324         t = config->read(SCIM_IMENGINE_UNIKEY_MODERNSTYLE, &__unikey_modernstyle);
325         if (!t) __unikey_modernstyle = SCIM_IMENGINE_UNIKEY_MODERNSTYLE_DEF;
326
327         t = config->read(SCIM_IMENGINE_UNIKEY_MACROENABLED, &__unikey_macroenabled);
328         if (!t) __unikey_macroenabled = SCIM_IMENGINE_UNIKEY_MACROENABLED_DEF;
329
330         t = config->read(SCIM_IMENGINE_UNIKEY_SPELLCHECKENABLED, &__unikey_spellcheckenabled);
331         if (!t) __unikey_spellcheckenabled = SCIM_IMENGINE_UNIKEY_SPELLCHECKENABLED_DEF;
332
333         t = config->read(SCIM_IMENGINE_UNIKEY_AUTONONVNRESTORE, &__unikey_autononvnrestore);
334         if (!t) __unikey_autononvnrestore = SCIM_IMENGINE_UNIKEY_AUTONONVNRESTORE_DEF;
335
336         t = config->read(SCIM_IMENGINE_UNIKEY_CODERTELEX, &__unikey_codertelex);
337         if (!t) __unikey_codertelex = SCIM_IMENGINE_UNIKEY_CODERTELEX_DEF;
338
339         t = config->read(SCIM_IMENGINE_UNIKEY_PROCESSWATWORDBEGIN, &__unikey_processwatwordbegin);
340         if (!t) __unikey_processwatwordbegin = SCIM_IMENGINE_UNIKEY_PROCESSWATWORDBEGIN_DEF;
341
342         __macStore.init();
343         __macStore.loadFromFile(getMacroFile());
344
345         setup_widget_value();
346         __have_changed = false;
347     }
348 }
349
350 static void save_config(const ConfigPointer &config)
351 {
352     if (!config.null())
353     {
354         config->write(SCIM_IMENGINE_UNIKEY_PREEDIT, __unikey_preedit);
355         String s = String(gtk_entry_get_text(GTK_ENTRY(__widget_preedit_skey)));
356         config->write(SCIM_IMENGINE_UNIKEY_PREEDIT_SWITCH_KEY, s);
357         config->write(SCIM_IMENGINE_UNIKEY_FREEMARKING, __unikey_freemarking);
358         config->write(SCIM_IMENGINE_UNIKEY_MODERNSTYLE, __unikey_modernstyle);
359         config->write(SCIM_IMENGINE_UNIKEY_MACROENABLED, __unikey_macroenabled);
360         config->write(SCIM_IMENGINE_UNIKEY_SPELLCHECKENABLED, __unikey_spellcheckenabled);
361         config->write(SCIM_IMENGINE_UNIKEY_AUTONONVNRESTORE, __unikey_autononvnrestore);
362         config->write(SCIM_IMENGINE_UNIKEY_AUTONONVNRESTORE, __unikey_autononvnrestore);
363         config->write(SCIM_IMENGINE_UNIKEY_CODERTELEX, __unikey_codertelex);
364         config->write(SCIM_IMENGINE_UNIKEY_PROCESSWATWORDBEGIN, __unikey_processwatwordbegin);
365
366         s = String(getMacroFile());
367         FILE *f = fopen(s.c_str(), "wt");
368         if (f == NULL)
369         {
370             s = s.substr(0, s.rfind('/'));
371             int tmp=system((String("mkdir ") + s).c_str());
372         }
373         else
374             fclose(f);
375
376         __macStore.writeToFile(getMacroFile());
377
378         __have_changed = false;
379     }
380 }
381
382 static bool query_changed()
383 {
384     return __have_changed;
385 }
386
387 static void on_default_toggle_button_toggled(GtkToggleButton *togglebutton, gpointer user_data)
388 {
389     bool *toggle = static_cast<bool*> (user_data);
390
391     if (toggle)
392     {
393         *toggle = gtk_toggle_button_get_active (togglebutton);
394         __have_changed = true;
395     }
396 }
397
398 enum {COL_KEY = 0, COL_REPLACE, NUM_COLS};
399
400 static void on_macrotable_button_clicked(GtkButton *button, gpointer user_data)
401 {
402     GtkWidget           *dialog;
403     GtkWidget           *treeview;
404     GtkTreeModel        *model;
405     GtkTreeViewColumn   *col;
406     GtkWidget           *contentarea;
407     GtkWidget           *hbox, *vbox;
408     GtkCellRenderer     *renderer;
409
410 // create main dialog
411     dialog = gtk_dialog_new();
412     gtk_window_set_default_size(GTK_WINDOW(dialog), 600, 300);
413     gtk_window_set_title(GTK_WINDOW(dialog), _("Macro table definition"));
414     gtk_dialog_add_buttons(GTK_DIALOG(dialog),
415                            GTK_STOCK_OK, GTK_RESPONSE_OK,
416                            GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
417                            NULL);
418
419     //contentarea = gtk_dialog_get_content_area(GTK_DIALOG(dialog)); // only for GTK >= 2.14
420     contentarea = GTK_DIALOG(dialog)->vbox;
421
422     hbox = gtk_hbox_new (false, 5);
423     gtk_container_add(GTK_CONTAINER(contentarea), hbox);
424
425 // create scroll window and tree view
426     model = GTK_TREE_MODEL(create_and_fill_list_store());
427
428     treeview = gtk_tree_view_new_with_model (model);
429
430     GtkWidget *scroll = gtk_scrolled_window_new(NULL, NULL);
431     gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
432
433     gtk_box_pack_start(GTK_BOX(hbox), scroll, true, true, 0);
434     gtk_container_add(GTK_CONTAINER(scroll), treeview);
435
436 // create key column
437     renderer = gtk_cell_renderer_text_new();
438     g_object_set(renderer, "editable", true, NULL);
439     g_object_set(renderer, "width-chars", MAX_MACRO_KEY_LEN+4, NULL);
440     g_signal_connect(renderer, "edited", G_CALLBACK(on_key_edited), model);
441     col = gtk_tree_view_column_new_with_attributes(_("Word"), renderer, "text", COL_KEY, NULL);
442     gtk_tree_view_append_column (GTK_TREE_VIEW(treeview), col);
443
444 // create replace column
445     renderer = gtk_cell_renderer_text_new();
446     g_object_set(renderer, "editable", true, NULL);
447     //g_object_set(renderer, "width-chars", MAX_MACRO_KEY_LEN*3, NULL);
448     g_signal_connect(renderer, "edited", G_CALLBACK(on_replace_edited), model);
449     col = gtk_tree_view_column_new_with_attributes(_("Replace with"), renderer, "text", COL_REPLACE, NULL);
450     gtk_tree_view_append_column (GTK_TREE_VIEW(treeview), col);
451
452     vbox = gtk_vbox_new (false, 0);
453     gtk_box_pack_end(GTK_BOX(hbox), vbox, false, false, 6);
454
455 // create Del button
456     GtkWidget* btDel = gtk_button_new_with_label (_("Delete"));
457     gtk_box_pack_start(GTK_BOX(vbox), btDel, false, true, 3);
458     g_signal_connect(btDel, "clicked", G_CALLBACK(on_del_macro_clicked), treeview);
459
460 // create DelAll button
461     GtkWidget *btDelAll = gtk_button_new_with_label (_("Delete All"));
462     gtk_box_pack_start(GTK_BOX(vbox), btDelAll, false, true, 3);
463     g_signal_connect(btDelAll, "clicked", G_CALLBACK(on_delall_macro_clicked), model);
464
465     gtk_widget_show_all(dialog);
466     gint result = gtk_dialog_run(GTK_DIALOG(dialog));
467
468 // save Macro Table
469     if (result == GTK_RESPONSE_OK)
470     {
471         __macStore.init();
472         gtk_tree_model_foreach(model, iter_2_macro, NULL);
473
474         __have_changed = true;
475     }
476
477     g_object_unref(model);
478     gtk_widget_destroy(dialog);
479 }
480
481 static void on_end_macro_table(GtkListStore *liststore)
482 {
483     GtkTreeIter     iter;
484     gchar           *lastkey;
485     gint n;
486
487     n = gtk_tree_model_iter_n_children(GTK_TREE_MODEL(liststore), NULL);
488     if (n)
489     {
490         gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(liststore), &iter, NULL, n-1);
491     }
492     else
493     {
494         gtk_list_store_append(liststore, &iter);
495         gtk_list_store_set(liststore, &iter, COL_KEY, "...", COL_REPLACE, "...", -1);
496         return;
497     }
498
499     gtk_tree_model_get(GTK_TREE_MODEL(liststore), &iter, COL_KEY, &lastkey, -1);
500     if (strcmp(lastkey, "...") || n==0)
501     {
502         gtk_list_store_append(liststore, &iter);
503         gtk_list_store_set(liststore, &iter, COL_KEY, "...", COL_REPLACE, "...", -1);
504     }
505 }
506
507 static void on_del_macro_clicked(GtkButton *button, gpointer user_data)
508 {
509     GtkTreeView     *treeview;
510     GtkListStore    *liststore;
511     GtkTreeSelection*tselect;
512     GtkTreeIter     iter;
513     gchar           *lastkey;
514
515     treeview = GTK_TREE_VIEW(user_data);
516     tselect = gtk_tree_view_get_selection(treeview);
517
518     if (gtk_tree_selection_get_selected(tselect, NULL, &iter))
519     {
520         liststore = GTK_LIST_STORE(gtk_tree_view_get_model(treeview));
521         gtk_tree_model_get(GTK_TREE_MODEL(liststore), &iter, COL_KEY, &lastkey, -1);
522         if (strcmp(lastkey, "..."))
523             gtk_list_store_remove(liststore, &iter);
524     }
525 }
526
527 static void on_delall_macro_clicked(GtkButton *button, gpointer user_data)
528 {
529     GtkListStore    *liststore;
530     GtkTreeIter     iter;
531
532     liststore = GTK_LIST_STORE(user_data);
533
534     gtk_list_store_clear(liststore);
535     on_end_macro_table(liststore);
536 }
537
538 static GtkListStore* create_and_fill_list_store()
539 {
540     GtkListStore*   liststore;
541     GtkTreeIter     iter;
542     FILE            *f;
543     char            key[MAX_MACRO_KEY_LEN*3], replace[MAX_MACRO_TEXT_LEN*3];
544     UKBYTE          *p;
545     int             i, inLen, maxOutLen, ret;
546
547     liststore = gtk_list_store_new (NUM_COLS, G_TYPE_STRING, G_TYPE_STRING);
548
549 // add key and replace to liststore
550     for (int i=0 ; i < __macStore.getCount() ; i++)
551     {
552         p = (UKBYTE *)__macStore.getKey(i);
553         inLen = -1;
554         maxOutLen = sizeof(key);
555         ret = VnConvert(CONV_CHARSET_VNSTANDARD, CONV_CHARSET_UNIUTF8,
556                         (UKBYTE *) p, (UKBYTE *)key,
557                         &inLen, &maxOutLen);
558         if (ret != 0)
559             continue;
560
561         p = (UKBYTE *)__macStore.getText(i);
562         inLen = -1;
563         maxOutLen = sizeof(replace);
564         ret = VnConvert(CONV_CHARSET_VNSTANDARD, CONV_CHARSET_UNIUTF8,
565                         p, (UKBYTE *)replace,
566                         &inLen, &maxOutLen);
567         if (ret != 0)
568             continue;
569
570         gtk_list_store_append(liststore, &iter);
571         gtk_list_store_set(liststore, &iter, COL_KEY, key, COL_REPLACE, replace, -1);
572     }
573
574     on_end_macro_table(liststore);
575     return liststore;
576 }
577
578 static void on_key_edited (GtkCellRendererText *celltext, const gchar *string_path, const gchar *new_text, gpointer data)
579 {
580     GtkTreeModel    *model;
581     GtkTreeIter     iter;
582     gchar           *curkey, *oldkey, *oldreplace;
583     bool            b;
584
585     model = GTK_TREE_MODEL (data);
586
587     if (!strcmp(new_text, "..."))
588         return;
589
590     b = gtk_tree_model_get_iter_first(model, &iter);
591     while (b)
592     {
593         gtk_tree_model_get(model, &iter, COL_KEY, &curkey, -1);
594         if (!strcasecmp(curkey, new_text))
595             return;
596
597         b = gtk_tree_model_iter_next(model, &iter);
598     }
599     gtk_tree_model_get_iter_from_string(model, &iter, string_path);
600     gtk_tree_model_get(model, &iter, COL_KEY, &oldkey, COL_REPLACE, &oldreplace, -1);
601
602     gtk_list_store_set(GTK_LIST_STORE(model), &iter, COL_KEY, new_text, -1);
603     if (!strcmp(oldkey, "..."))
604         gtk_list_store_set(GTK_LIST_STORE(model), &iter, COL_REPLACE, _("(replace text)"));
605
606     on_end_macro_table(GTK_LIST_STORE(model));
607 }
608
609 static void on_replace_edited (GtkCellRendererText *celltext, const gchar *string_path, const gchar *new_text, gpointer data)
610 {
611     GtkTreeModel    *model;
612     GtkTreeIter     iter;
613     gchar           *oldkey;
614
615     model = GTK_TREE_MODEL (data);
616     gtk_tree_model_get_iter_from_string(model, &iter, string_path);
617
618     gtk_tree_model_get(model, &iter, COL_KEY, &oldkey, -1);
619     if (strcmp(oldkey, "..."))
620         gtk_list_store_set(GTK_LIST_STORE(model), &iter, COL_REPLACE, new_text, -1);
621 }
622
623 static gboolean iter_2_macro(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data)
624 {
625     gchar   *key, *replace;
626     GtkTreeIter c;
627
628     c = *iter;
629
630     if (!gtk_tree_model_iter_next(model, &c))
631         return true;
632     gtk_tree_model_get(model, iter, COL_KEY, &key, COL_REPLACE, &replace, -1);
633
634     __macStore.addItem(key, replace, CONV_CHARSET_XUTF8);
635
636     return false;
637 }
638
639 static void on_hotkey_button_clicked (GtkButton *button, gpointer data)
640 {
641     GtkWidget *wid = (GtkWidget*)data;
642
643     GtkWidget *dialog = scim_key_selection_dialog_new("Edit hotkey");
644     gint r;
645     const gchar *hotkeys = gtk_entry_get_text(GTK_ENTRY(wid));
646     
647     if (hotkeys)
648     {
649         scim_key_selection_dialog_set_keys(
650             SCIM_KEY_SELECTION_DIALOG(dialog), hotkeys);
651     }
652
653     r = gtk_dialog_run(GTK_DIALOG(dialog));
654
655     if (r == GTK_RESPONSE_OK)
656     {
657         const gchar *newkeys = scim_key_selection_dialog_get_keys(SCIM_KEY_SELECTION_DIALOG(dialog));
658         gtk_entry_set_text(GTK_ENTRY(wid), newkeys?newkeys:"");
659         __have_changed = true;
660     }
661
662     gtk_widget_destroy(dialog);
663 }