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