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