52dff7ea27ef3d72a08b3f30d67fd7d4fe760065
[platform/core/uifw/ise-engine-unikey.git] / src / scim_unikey_imengine.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_IMENGINE
11 #define Uses_SCIM_ICONV
12 #define Uses_SCIM_CONFIG_BASE
13 #define Uses_SCIM_CONFIG_PATH
14
15 #ifdef HAVE_CONFIG_H
16 #include "config.h"
17 #endif
18
19 #include <libintl.h>
20 #define _(String) dgettext(PACKAGE_NAME,String)
21
22 #include <scim.h>
23 #include "scim_unikey_const.h"
24 #include "scim_unikey_utils.h"
25 #include "scim_unikey_imengine.h"
26
27 #include <string.h>
28 #include "unikey.h"
29 #include "vnconv.h"
30
31 #define scim_module_init                            unikey_LTX_scim_module_init
32 #define scim_module_exit                            unikey_LTX_scim_module_exit
33 #define scim_imengine_module_init                   unikey_LTX_scim_imengine_module_init
34 #define scim_imengine_module_create_factory         unikey_LTX_scim_imengine_module_create_factory
35
36 static unsigned char WordBreakSyms[] =
37 {
38     ',', ';', ':', '.', '\"', '\'', '!', '?', ' ',
39     '<', '>', '=', '+', '-', '*', '/', '\\',
40     '_', '~', '`', '@', '#', '$', '%', '^', '&', '(', ')', '{', '}', '[', ']',
41     '|'
42 };
43
44 static unsigned char WordAutoCommit[] =
45 {
46     '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
47     'b', 'c', 'f', 'g', 'h', 'j', 'k', 'l', 'm', 'n',
48     'p', 'q', 'r', 's', 't', 'v', 'x', 'z',
49     'B', 'C', 'F', 'G', 'H', 'J', 'K', 'L', 'M', 'N',
50     'P', 'Q', 'R', 'S', 'T', 'V', 'X', 'Z'
51 };
52
53 const String          Unikey_IMNames[]    = {"Telex", "Vni", "STelex", "STelex2"};
54 const UkInputMethod   Unikey_IM[]         = {UkTelex, UkVni, UkSimpleTelex, UkSimpleTelex2};
55 const unsigned int    NUM_INPUTMETHOD     = sizeof(Unikey_IM)/sizeof(Unikey_IM[0]);
56
57 const String          Unikey_OCNames[]    = {"Unicode",
58                                              "TCVN3",
59                                              "VNI Win",
60                                              "VIQR",
61                                              "CString",
62                                              "NCR Decimal",
63                                              "NCR Hex"};
64
65 const unsigned int    Unikey_OC[]         = {CONV_CHARSET_XUTF8,
66                                              CONV_CHARSET_TCVN3,
67                                              CONV_CHARSET_VNIWIN,
68                                              CONV_CHARSET_VIQR,
69                                              CONV_CHARSET_UNI_CSTRING,
70                                              CONV_CHARSET_UNIREF,
71                                              CONV_CHARSET_UNIREF_HEX};
72 const unsigned int    NUM_OUTPUTCHARSET   = sizeof(Unikey_OC)/sizeof(Unikey_OC[0]);
73
74 ConfigPointer   __config;
75
76 using namespace scim;
77
78 extern "C"
79 {
80     void scim_module_init(void)
81     {
82         UnikeySetup();
83     }
84
85     void scim_module_exit(void)
86     {
87         UnikeyCleanup();
88     }
89
90     unsigned int scim_imengine_module_init(ConfigPointer &config)
91     {
92         if (config.null())
93             return 0;
94         __config = config;
95
96         return 1;
97     }
98
99     IMEngineFactoryPointer scim_imengine_module_create_factory(unsigned int index)
100     {
101         return new UnikeyFactory();
102     }
103 }
104
105 UnikeyFactory::UnikeyFactory()
106 {
107     set_languages("vi_VN");
108 }
109 UnikeyFactory::~UnikeyFactory()
110 {
111 }
112 WideString UnikeyFactory::get_name() const
113 {
114     return utf8_mbstowcs("Unikey");
115 }
116 String UnikeyFactory::get_uuid() const
117 {
118     return String(String("16ef5139-de02-494f-8d98-ddfcd60bbae1"));
119 }
120 WideString UnikeyFactory::get_authors() const
121 {
122     return utf8_mbstowcs("Le Quoc Tuan <mr.lequoctuan@gmail.com>");
123 }
124 WideString UnikeyFactory::get_credits() const
125 {
126     return utf8_mbstowcs(String("Scim-Unikey Input Method\n"
127                                 "Version: " SCIM_UNIKEY_VERSION "\n"
128                                 "Copyright © 2008-2009 Ubuntu-VN\n"
129                                 "http://www.ubuntu-vn.org\n\n"
130                                 "Thanks to Pham Kim Long for ukengine")
131         );
132 }
133 WideString UnikeyFactory::get_help() const
134 {
135     return utf8_mbstowcs(_("This IME work best when environment variable are\n"
136                            "- GTK_IM_MODULE=scim-bridge\n"
137                            "- QT_IM_MODULE=xim\n"
138                            " If you use preedit, you don't not need that\n\n"
139                            
140                            "Some time, in some application, you \"must\"\n"
141                            "enable Preedit to type best\n\n"
142
143                            "In some application, scim can't enable,\n"
144                            "this is not a bug of scim-unikey.\n"
145                            "You can try this in terminal: app_name | scim\n\n"
146
147                            "This IME come into being not intend to\n"
148                            "replace scim-m17n, just a choose, I recommend\n"
149                            "use this combine with scim-m17n\n\n"
150
151                            "For other support goto:\n"
152                            "  http://forum.ubuntu-vn.org/viewforum.php?f=85"));
153 }
154 String UnikeyFactory::get_icon_file() const
155 {
156     return String(SCIM_ICONDIR SCIM_UNIKEY_ICON_FILENAME);
157 }
158
159 IMEngineInstancePointer UnikeyFactory::create_instance(const String & encoding, int id)
160 {
161     return new UnikeyInstance(this, encoding, id);
162 }
163
164 UnikeyInstance::UnikeyInstance(UnikeyFactory *factory, const String &encoding, int id)
165     :IMEngineInstanceBase(factory, encoding, id),
166      m_auto_commit(false)
167 {
168     static bool t, o;  //temp variable
169
170     m_preeditstring.clear();
171
172     CreateDefaultUnikeyOptions(&m_ukopt);
173
174     /* Read config
175        if can't read config, set it to default value*/
176
177     t = __config->read(SCIM_IMENGINE_UNIKEY_INPUTMETHOD, &m_im);
178     if (!t) m_im = 0;
179
180     t = __config->read(SCIM_IMENGINE_UNIKEY_OUTPUTCHARSET, &m_oc);
181     if (!t) m_oc = 0;
182
183     t = __config->read(SCIM_IMENGINE_UNIKEY_PREEDIT, &m_preedit);
184     if (!t) m_preedit = SCIM_IMENGINE_UNIKEY_PREEDIT_DEF;
185
186     // read preedit switch key
187     m_preeditskey.clear();
188     String s;
189     int i = 0, j;
190     t = __config->read(SCIM_IMENGINE_UNIKEY_PREEDIT_SWITCH_KEY, &s);
191     if (!t) s=SCIM_IMENGINE_UNIKEY_PREEDIT_SWITCH_KEY_DEF;
192     if (s.length())
193     {
194         while (true)
195         {
196             j=s.find(',', i);
197             if (j!=String::npos)
198                 m_preeditskey.push_back(KeyEvent(s.substr(i, j-i)));
199             else
200                 break;
201             i = j+1;
202         }
203         m_preeditskey.push_back(KeyEvent(s.substr(i)));
204     } // end read preedit switch key
205
206     t = __config->read(SCIM_IMENGINE_UNIKEY_PROCESSWATWORDBEGIN, &o);
207     m_process_w_AtBeginWord = t?o:SCIM_IMENGINE_UNIKEY_PROCESSWATWORDBEGIN_DEF;
208
209 // Unikey Options
210     t = __config->read(SCIM_IMENGINE_UNIKEY_FREEMARKING, &o);
211     m_ukopt.freeMarking = t?o:SCIM_IMENGINE_UNIKEY_FREEMARKING_DEF;
212
213     t = __config->read(SCIM_IMENGINE_UNIKEY_MODERNSTYLE, &o);
214     m_ukopt.modernStyle = t?o:SCIM_IMENGINE_UNIKEY_MODERNSTYLE_DEF;
215
216     t = __config->read(SCIM_IMENGINE_UNIKEY_MACROENABLED, &o);
217     m_ukopt.macroEnabled = t?o:SCIM_IMENGINE_UNIKEY_MACROENABLED_DEF;
218
219     t = __config->read(SCIM_IMENGINE_UNIKEY_SPELLCHECKENABLED, &o);
220     m_ukopt.spellCheckEnabled = t?o:SCIM_IMENGINE_UNIKEY_SPELLCHECKENABLED_DEF;
221
222     t = __config->read(SCIM_IMENGINE_UNIKEY_AUTONONVNRESTORE, &o);
223     m_ukopt.autoNonVnRestore = t?o:SCIM_IMENGINE_UNIKEY_AUTONONVNRESTORE_DEF;
224
225     UnikeySetOptions(&m_ukopt);
226
227     if (m_ukopt.macroEnabled)
228     {
229         UnikeyLoadMacroTable(getMacroFile());
230     }
231
232     reset();
233 }
234
235 UnikeyInstance::~UnikeyInstance()
236 {
237 }
238
239 void UnikeyInstance::focus_in()
240 {
241     register_properties(CreatePropertyList());
242
243     UnikeySetInputMethod(Unikey_IM[m_im]);
244     UnikeySetOutputCharset(Unikey_OC[m_oc]);
245     UnikeySetOptions(&m_ukopt);
246 }
247
248 void UnikeyInstance::focus_out()
249 {
250     reset();
251 }
252
253 void UnikeyInstance::reset()
254 {
255     UnikeyResetBuf();
256
257     m_lastkey_with_shift = false;
258
259     if (m_preedit)
260     {
261         if (m_preeditstring.length())
262         {
263             commit_string(m_preeditstring);
264             hide_preedit_string();
265             m_preeditstring.clear();
266         }
267         m_auto_commit = false;
268     }
269 }
270
271 void UnikeyInstance::Unikey_send_backspace(int nBackspace)
272 {
273     static WideString ws;
274     static int n;
275
276     //  if surrounding text was provided, use it instead of send backspace
277     if (get_surrounding_text(ws, n, nBackspace, 0))
278     {
279         // for type in Auto Complete in OpenOffice
280         // Hope this not rise bugs in others application
281         // not use SCIM_KEY_NullKey, because GTK application crash when GTK_IM_MODULE=scim
282
283         forward_key_event(SCIM_KEY_VoidSymbol);
284
285         delete_surrounding_text(-ws.length(), ws.length());
286     }
287     else
288     {
289         for (int i=0; i < nBackspace; i++)
290             forward_key_event(SCIM_KEY_BackSpace);
291     }
292 }
293
294 void UnikeyInstance::Unikey_update_preedit_string(const WideString s, const bool visible)
295 {
296     AttributeList attlist;
297     Attribute att;
298
299     // underline preedit string
300     att = Attribute(0, s.length(), SCIM_ATTR_DECORATE, SCIM_ATTR_DECORATE_UNDERLINE);
301     attlist.push_back(att);
302
303     if (m_ukopt.spellCheckEnabled==1 && UnikeyLastWordIsNonVn())
304     {
305         // red preedit string
306         att = Attribute(0, s.length(), SCIM_ATTR_FOREGROUND, 0xff0000);
307         attlist.push_back(att);
308     }
309
310     update_preedit_string(s, attlist);
311     update_preedit_caret(s.length());
312
313     if (visible == true)
314     {
315         show_preedit_string();
316     }
317     else
318     {
319         hide_preedit_string();
320     }
321 }
322
323 bool UnikeyInstance::process_key_event(const KeyEvent& key)
324 {
325     bool tmp;
326
327     for (int i = 0; i<m_preeditskey.size(); i++)
328     {
329         if (key==m_preeditskey.at(i))
330         {
331             reset();
332             m_preedit=!m_preedit;
333             register_properties(CreatePropertyList());
334             return true;
335         }
336     }
337
338     tmp = m_preedit?Unikey_process_key_event_preedit(key)
339         :Unikey_process_key_event_direct(key);
340
341     if ((key.code >= SCIM_KEY_space && key.code <= SCIM_KEY_asciitilde)
342         || (key.code >= SCIM_KEY_KP_Multiply && key.code <= SCIM_KEY_KP_9))
343     {
344         m_lastkey_with_shift = key.is_shift_down();
345     }
346     else
347     {
348         m_lastkey_with_shift = false;
349     }
350
351     return tmp;
352 }
353
354 /** This method not use preedit string */
355 bool UnikeyInstance::Unikey_process_key_event_direct(const KeyEvent& key)
356 {
357     if (key.is_key_release())
358     {
359         return false;
360     }
361
362     if (key.is_control_down() || key.mask & SCIM_KEY_AltMask)
363     {
364         reset();
365         return false;
366     }
367
368     if (key.code >= SCIM_KEY_Shift_L && key.code <= SCIM_KEY_Hyper_R)
369     {
370         return false;
371     }
372
373     if (key.code == SCIM_KEY_BackSpace)
374     {
375         UnikeyBackspacePress();     // xu ly phim backspace
376         if (UnikeyBackspaces==0)    // neu ukengine bao khong can xoa ky tu nao,
377             return false;  // thi tra lai backspace cho chuong trinh khach
378         else
379         {
380             Unikey_send_backspace(UnikeyBackspaces);
381
382             // append key that need change tone pos after press backspace
383             if (UnikeyBufChars)
384             {
385                 if (Unikey_OC[m_oc] == CONV_CHARSET_XUTF8)
386                     commit_string(utf8_mbstowcs((const char*)UnikeyBuf, UnikeyBufChars));  // send the solved string to client
387                 else
388                 {
389                     static unsigned char buf[1024];
390                     int bufSize=sizeof(buf)/sizeof(buf[0]);
391
392                     latinToUtf(buf, UnikeyBuf, UnikeyBufChars, &bufSize);
393                     commit_string(utf8_mbstowcs((const char*)buf, sizeof(buf)/sizeof(buf[0]) - bufSize));
394                 }
395             }
396
397             return true;
398         }
399     }
400
401     if (   (key.code >= SCIM_KEY_space && key.code <= SCIM_KEY_asciitilde)
402            || (key.code >= SCIM_KEY_KP_Multiply && key.code <= SCIM_KEY_KP_9)
403         )
404     {
405         UnikeySetCapsState(key.mask & SCIM_KEY_ShiftMask,
406                            key.mask & SCIM_KEY_CapsLockMask);
407
408         // shift + space to restore keystroke
409         if (m_lastkey_with_shift == false
410             && key.mask & SCIM_KEY_ShiftMask
411             && key.code == SCIM_KEY_space
412             && !UnikeyAtWordBeginning())
413         {
414             UnikeyRestoreKeyStrokes();
415             if (UnikeyBackspaces == 0)
416             {
417                 UnikeyPutChar(key.code);
418             }
419         }
420
421         else if ((key.code >= SCIM_KEY_KP_0 &&
422                   key.code <= SCIM_KEY_KP_9))
423         {
424             UnikeyPutChar(key.code);
425         }
426
427         else if ((Unikey_IM[m_im] == UkTelex || Unikey_IM[m_im] == UkSimpleTelex2)
428                  && m_process_w_AtBeginWord == false
429                  && UnikeyAtWordBeginning()
430                  && (key.code == SCIM_KEY_w || key.code == SCIM_KEY_W))
431         {
432             UnikeyPutChar(key.code);
433         }
434
435         else
436         {
437             UnikeyFilter(key.code);
438         }
439
440         if (UnikeyBackspaces)
441         {
442             Unikey_send_backspace(UnikeyBackspaces);
443         }
444
445         if (UnikeyBufChars)
446         {
447             if (Unikey_OC[m_oc] == CONV_CHARSET_XUTF8)
448             {
449                 commit_string(utf8_mbstowcs((const char*)UnikeyBuf, UnikeyBufChars));  // send the solved string to client
450             }
451             else
452             {
453                 static unsigned char buf[1024];
454                 int bufSize=sizeof(buf)/sizeof(buf[0]);
455
456                 latinToUtf(buf, UnikeyBuf, UnikeyBufChars, &bufSize);
457                 commit_string(utf8_mbstowcs((const char*)buf, sizeof(buf)/sizeof(buf[0]) - bufSize));
458             }
459         }
460         else
461         {
462             forward_key_event(key);
463             return true;
464         }
465
466         return true;
467     }
468
469     // else
470     reset();
471     return false;
472 }
473
474 /** This method use preedit string */
475 bool UnikeyInstance::Unikey_process_key_event_preedit(const KeyEvent& key)
476 {
477     static int i;
478
479     if (key.code == SCIM_KEY_Tab
480         || key.mask & SCIM_KEY_ControlMask
481         || key.mask & SCIM_KEY_AltMask)
482     {
483         if (m_preeditstring.length())
484         {
485             commit_string(m_preeditstring);
486             hide_preedit_string();
487             m_preeditstring.clear();
488         }
489         reset();
490         return false;
491     }
492
493     if (key.is_key_release())
494         return false;
495
496     if (key.code == SCIM_KEY_Tab
497              || key.code == SCIM_KEY_Return
498              || key.code == SCIM_KEY_Delete
499              || key.code == SCIM_KEY_KP_Enter
500              || (key.code >= SCIM_KEY_Home && key.code <= SCIM_KEY_Insert)
501              || (key.code >= SCIM_KEY_KP_Home && key.code <= SCIM_KEY_KP_Delete))
502     {
503         if (m_preeditstring.length())
504         {
505             commit_string(m_preeditstring);
506             hide_preedit_string();
507             m_preeditstring.clear();
508         }
509
510         reset();
511         return false;
512     }
513
514     else if (key.code >= SCIM_KEY_Shift_L && key.code <= SCIM_KEY_Hyper_R)
515         return false;
516
517     else if (key.code == SCIM_KEY_BackSpace)
518     {
519         UnikeyBackspacePress();     // process Backspace
520
521         if (UnikeyBackspaces==0 || m_preeditstring.empty())    // if Unikey tell no Backspace
522         {
523             reset();
524             return false;  // Return Backspace to client
525         }
526         else
527         {
528             static int len;
529
530             len = m_preeditstring.length();
531             if (len <= UnikeyBackspaces)
532             {
533                 m_preeditstring.clear();
534                 hide_preedit_string();
535                 m_auto_commit = true;
536             }
537             else
538             {
539                 m_preeditstring.erase(len - UnikeyBackspaces, UnikeyBackspaces);
540                 Unikey_update_preedit_string(m_preeditstring, true);
541             }
542
543             // append key that need change tone pos after press backspace
544             if (UnikeyBufChars)
545             {
546                 if (Unikey_OC[m_oc] == CONV_CHARSET_XUTF8)
547                     m_preeditstring.append(utf8_mbstowcs((const char*)UnikeyBuf, UnikeyBufChars));
548                 else
549                 {
550                     static unsigned char buf[1024];
551                     int bufSize=sizeof(buf)/sizeof(buf[0]);
552
553                     latinToUtf(buf, UnikeyBuf, UnikeyBufChars, &bufSize);
554                     m_preeditstring.append(utf8_mbstowcs((const char*)buf, sizeof(buf)/sizeof(buf[0])-bufSize));
555                 }
556
557                 m_auto_commit = false;
558                 Unikey_update_preedit_string(m_preeditstring, true);
559             }
560         }
561
562         return true;
563     }
564
565     else if (   (key.code >= SCIM_KEY_space && key.code <= SCIM_KEY_asciitilde)
566                 || (key.code >= SCIM_KEY_KP_Multiply && key.code <= SCIM_KEY_KP_9)
567         )
568     {
569         UnikeySetCapsState(key.mask & SCIM_KEY_ShiftMask, key.mask & SCIM_KEY_CapsLockMask);
570
571         // auto commit char: commit char that never change later in preedit string
572         // if macro enabled, then not auto commit. Because macro may change any word
573         if (m_ukopt.macroEnabled==0 && (UnikeyAtWordBeginning() || m_auto_commit))
574         {
575             for (i = 0; i < sizeof(WordAutoCommit); i++)
576                 if (key.code == WordAutoCommit[i])
577                 {
578                     UnikeyPutChar(key.code);
579                     m_auto_commit = true;
580                     forward_key_event(key); // forward keyevent instead of return false
581                     return true;
582                 }
583         } // end auto commit char
584
585         if ((Unikey_IM[m_im] == UkTelex || Unikey_IM[m_im] == UkSimpleTelex2)
586                  && m_process_w_AtBeginWord == false
587                  && UnikeyAtWordBeginning()
588                  && (key.code == SCIM_KEY_w || key.code == SCIM_KEY_W))
589         {
590             UnikeyPutChar(key.code);
591             if (m_ukopt.macroEnabled==0)
592             {
593                 forward_key_event(key);
594             }
595             else
596             {
597                 m_preeditstring.push_back(key.code);
598                 Unikey_update_preedit_string(m_preeditstring, true);
599             }
600
601             m_auto_commit = true;
602             
603             return true;
604         }
605
606         m_auto_commit = false;
607
608         if (m_lastkey_with_shift == false
609             && key.mask & SCIM_KEY_ShiftMask
610             && key.code == SCIM_KEY_space
611             && !UnikeyAtWordBeginning())
612         {
613             UnikeyRestoreKeyStrokes();
614         }
615
616         else if (key.code >= SCIM_KEY_KP_Multiply && key.code <= SCIM_KEY_KP_9)
617         {
618             UnikeyPutChar(key.code);
619         }
620
621         else
622         {
623             UnikeyFilter(key.code);
624         }
625
626         if (UnikeyBackspaces > 0)
627         {
628             static int len;
629
630             len = m_preeditstring.length();
631
632             if (len <= UnikeyBackspaces)
633                 m_preeditstring.clear();
634             else
635                 m_preeditstring.erase(len - UnikeyBackspaces, UnikeyBackspaces);
636         }
637
638         if (UnikeyBufChars > 0)
639         {
640             if (Unikey_OC[m_oc] == CONV_CHARSET_XUTF8)
641                 m_preeditstring.append(utf8_mbstowcs((const char*)UnikeyBuf, UnikeyBufChars));
642             else
643             {
644                 static unsigned char buf[1024];
645                 int bufSize=sizeof(buf)/sizeof(buf[0]);
646
647                 latinToUtf(buf, UnikeyBuf, UnikeyBufChars, &bufSize);
648                 m_preeditstring.append(utf8_mbstowcs((const char*)buf, sizeof(buf)/sizeof(buf[0])-bufSize));
649             }
650         }
651         else
652         {    
653             m_preeditstring.push_back(key.get_unicode_code());
654         }
655
656         if (m_preeditstring.length())
657         {
658             if (key.code >= SCIM_KEY_KP_Multiply && key.code <= SCIM_KEY_KP_9)
659             {
660                 commit_string(m_preeditstring);
661                 hide_preedit_string();
662                 m_preeditstring.clear();
663                 reset();
664                 return true;
665             }
666             else
667             {
668                 for (i=0; i < sizeof(WordBreakSyms); i++)
669                     if (WordBreakSyms[i] == m_preeditstring[m_preeditstring.length()-1] && key.code == WordBreakSyms[i])
670                     {
671                         commit_string(m_preeditstring);
672                         hide_preedit_string();
673                         m_preeditstring.clear();
674                         reset();
675                         return true;
676                     }
677             }
678         }
679
680         Unikey_update_preedit_string(m_preeditstring, true);
681         return true;
682     }
683
684     // else (key non process)
685     reset();
686     return false;
687 }
688
689 PropertyList UnikeyInstance::CreatePropertyList()
690 {
691     PropertyList props;
692     int i;
693
694     Property prop("", "", "", "");
695
696 // input method
697     prop.set_key("/Unikey/InputMethod");
698     prop.set_label(Unikey_IMNames[m_im]);
699     prop.set_icon("");
700     prop.set_tip(_("Choose input method"));
701     props.push_back(prop);
702
703     for (i=0; i<NUM_INPUTMETHOD; i++)
704     {
705         prop.set_key(String("/Unikey/InputMethod/") + Unikey_IMNames[i] + String(m_im==i?"-Checked":""));
706         prop.set_label(Unikey_IMNames[i]);
707         prop.set_icon(m_im==i?SCIM_ICONDIR SCIM_UNIKEY_ICON_CHECK:"");
708         prop.set_tip("");
709         props.push_back(prop);
710     }
711
712 // output charset
713
714     prop.set_key("/Unikey/OutputCharset");
715     prop.set_label(Unikey_OCNames[m_oc]);
716     prop.set_icon("");
717     prop.set_tip(_("Choose output charset"));
718     props.push_back(prop);
719
720     for (i=0; i<NUM_OUTPUTCHARSET; i++)
721     {
722         prop.set_key(String("/Unikey/OutputCharset/") + Unikey_OCNames[i] + String(m_oc==i?"-Checked":""));
723         prop.set_label(Unikey_OCNames[i]);
724         prop.set_icon(m_oc==i?SCIM_ICONDIR SCIM_UNIKEY_ICON_CHECK:"");
725         prop.set_tip("");
726         props.push_back(prop);
727     }
728
729 // unikey options
730     // menu
731     prop.set_key("/Unikey/Options");
732     prop.set_label(_("Options"));
733     prop.set_icon(SCIM_ICONDIR SCIM_UNIKEY_ICON_CONFIGURE);
734     prop.set_tip(_("Configure Unikey here"));
735     props.push_back(prop);
736
737     // spellcheck
738     prop.set_key(m_ukopt.spellCheckEnabled?
739                  "/Unikey/Options/SpellCheck/Disable":"/Unikey/Options/SpellCheck/Enable");
740     prop.set_label(_("Enable spell check"));
741     prop.set_icon(m_ukopt.spellCheckEnabled?SCIM_ICONDIR SCIM_UNIKEY_ICON_CHECK:"");
742     prop.set_tip(_("If enable, you can decrease mistake when typing"));
743     props.push_back(prop);
744     
745     // auto restore keystroke
746     prop.set_key(m_ukopt.autoNonVnRestore?
747                  "/Unikey/Options/AutoRestoreKeys/Disable":"/Unikey/Options/AutoRestoreKeys/Enable");
748     prop.set_label(_("Auto restore keys with invalid words"));
749     prop.set_icon(m_ukopt.autoNonVnRestore?SCIM_ICONDIR SCIM_UNIKEY_ICON_CHECK:"");
750     prop.set_tip(_("When typing a word not in Vietnamese,\n"
751                    "it will auto restore keystroke into original"));
752     props.push_back(prop);
753
754     // modern style
755     prop.set_key(m_ukopt.modernStyle?
756                  "/Unikey/Options/ModernStyle/Disable":"/Unikey/Options/ModernStyle/Enable");
757     prop.set_label(_("Use oà, uý (instead of òa, úy)"));
758     prop.set_icon(m_ukopt.modernStyle?SCIM_ICONDIR SCIM_UNIKEY_ICON_CHECK:"");
759     prop.set_tip("");
760     props.push_back(prop);
761
762     // freeMarking
763     prop.set_key(m_ukopt.freeMarking?
764                  "/Unikey/Options/FreeMarking/Disable":"/Unikey/Options/FreeMarking/Enable");
765     prop.set_label(_("Allow type with more freedom"));
766     prop.set_icon(m_ukopt.freeMarking?SCIM_ICONDIR SCIM_UNIKEY_ICON_CHECK:"");
767     prop.set_tip("");
768     props.push_back(prop);
769
770     // macro
771     prop.set_key(m_ukopt.macroEnabled?
772                  "/Unikey/Options/EnabledMacro/Disable":"/Unikey/Options/EnabledMacro/Enable");
773     prop.set_label(_("Enable Macro"));
774     prop.set_icon(m_ukopt.macroEnabled?SCIM_ICONDIR SCIM_UNIKEY_ICON_CHECK:"");
775     prop.set_tip("");
776     props.push_back(prop);
777
778     // preedit
779     prop.set_key(m_preedit?"/Unikey/Options/Preedit/Disable":"/Unikey/Options/Preedit/Enable");
780     prop.set_label(_("Enable PreEdit"));
781     prop.set_icon(m_preedit?SCIM_ICONDIR SCIM_UNIKEY_ICON_CHECK:"");
782     prop.set_tip(_("This option is best for most application\n"
783                    "But you may don't like it because it have an underline when typing"));
784     props.push_back(prop);
785
786     // process w at begin
787     prop.set_key(m_process_w_AtBeginWord?
788                  "/Unikey/Options/ProcessWAtBegin/Disable":"/Unikey/Options/ProcessWAtBegin/Enable");
789     prop.set_label(_("Process W at word begin"));
790     prop.set_icon(m_process_w_AtBeginWord?SCIM_ICONDIR SCIM_UNIKEY_ICON_CHECK:"");
791     prop.set_tip(_("If enable, type W at begin\n"
792                    "of word will change to Ư."));
793     props.push_back(prop);
794
795     return props;
796 }
797
798 void UnikeyInstance::trigger_property(const String &property)
799 {
800     bool change = false;
801     int i;
802
803 // input method
804     if (!property.compare(0, strlen("/Unikey/InputMethod/"), "/Unikey/InputMethod/"))
805     {
806         for (i=0; i<NUM_INPUTMETHOD; i++)
807             if (!property.compare(strlen("/Unikey/InputMethod/"),
808                                   property.length() - strlen("/Unikey/InputMethod/"),
809                                   Unikey_IMNames[i]))
810             {
811                 m_im = i;
812                 __config->write(SCIM_IMENGINE_UNIKEY_INPUTMETHOD, m_im);
813                 change = true;
814                 break;
815             }
816     }
817
818 // output charset
819     else if (!property.compare(0, strlen("/Unikey/OutputCharset/"), "/Unikey/OutputCharset/"))
820     {
821         for (i=0; i<NUM_OUTPUTCHARSET; i++)
822             if (!property.compare(strlen("/Unikey/OutputCharset/"),
823                                   property.length() - strlen("/Unikey/OutputCharset/"),
824                                   Unikey_OCNames[i]))
825             {
826                 m_oc = i;
827                 __config->write(SCIM_IMENGINE_UNIKEY_OUTPUTCHARSET, m_oc);
828                 change = true;
829                 break;
830             }
831     }
832
833 // spellcheck
834     else if (property == "/Unikey/Options/SpellCheck/Enable")
835     {
836         m_ukopt.spellCheckEnabled = true;
837         __config->write(SCIM_IMENGINE_UNIKEY_SPELLCHECKENABLED, true);
838         change = true;
839     }
840     else if (property == "/Unikey/Options/SpellCheck/Disable")
841     {
842         m_ukopt.spellCheckEnabled = false;
843         __config->write(SCIM_IMENGINE_UNIKEY_SPELLCHECKENABLED, false);
844         change = true;
845     }
846
847 // auto restore keystroke
848     else if (property == "/Unikey/Options/AutoRestoreKeys/Enable")
849     {
850         m_ukopt.autoNonVnRestore = true;
851         __config->write(SCIM_IMENGINE_UNIKEY_AUTONONVNRESTORE, true);
852         change = true;
853     }
854     else if (property == "/Unikey/Options/AutoRestoreKeys/Disable")
855     {
856         m_ukopt.autoNonVnRestore = false;
857         __config->write(SCIM_IMENGINE_UNIKEY_AUTONONVNRESTORE, false);
858         change = true;
859     }
860
861 // modern style
862     else if (property == "/Unikey/Options/ModernStyle/Enable")
863     {
864         m_ukopt.modernStyle = true;
865         __config->write(SCIM_IMENGINE_UNIKEY_MODERNSTYLE, true);
866         change = true;
867     }
868     else if (property == "/Unikey/Options/ModernStyle/Disable")
869     {
870         m_ukopt.modernStyle = false;
871         __config->write(SCIM_IMENGINE_UNIKEY_MODERNSTYLE, false);
872         change = true;
873     }
874
875 // free Marking
876     else if (property == "/Unikey/Options/FreeMarking/Enable")
877     {
878         m_ukopt.freeMarking = true;
879         __config->write(SCIM_IMENGINE_UNIKEY_FREEMARKING, true);
880         change = true;
881     }
882     else if (property == "/Unikey/Options/FreeMarking/Disable")
883     {
884         m_ukopt.freeMarking = false;
885         __config->write(SCIM_IMENGINE_UNIKEY_FREEMARKING, false);
886         change = true;
887     }
888 // macro 
889     else if (property == "/Unikey/Options/EnabledMacro/Enable")
890     {
891         m_ukopt.macroEnabled = true;
892         UnikeyLoadMacroTable(getMacroFile());
893         __config->write(SCIM_IMENGINE_UNIKEY_MACROENABLED, true);
894         change = true;
895     }
896     else if (property == "/Unikey/Options/EnabledMacro/Disable")
897     {
898         m_ukopt.macroEnabled = false;
899         __config->write(SCIM_IMENGINE_UNIKEY_MACROENABLED, false);
900         change = true;
901     }
902
903 // preedit
904     else if (property == "/Unikey/Options/Preedit/Enable")
905     {
906         m_preedit = true;
907         __config->write(SCIM_IMENGINE_UNIKEY_PREEDIT, true);
908         change = true;
909     }
910     else if (property == "/Unikey/Options/Preedit/Disable")
911     {
912         m_preedit = false;
913         __config->write(SCIM_IMENGINE_UNIKEY_PREEDIT, false);
914         change = true;
915     }
916
917 // process w at begin
918     else if (property == "/Unikey/Options/ProcessWAtBegin/Enable")
919     {
920         m_process_w_AtBeginWord = true;
921         __config->write(SCIM_IMENGINE_UNIKEY_PROCESSWATWORDBEGIN, true);
922         change = true;
923     }
924     else if (property == "/Unikey/Options/ProcessWAtBegin/Disable")
925     {
926         m_process_w_AtBeginWord = false;
927         __config->write(SCIM_IMENGINE_UNIKEY_PROCESSWATWORDBEGIN, false);
928         change = true;
929     }
930
931     if (change)
932     {
933         __config->flush();
934         focus_out();
935         focus_in();
936     }
937 }