64c50a1b9500097ebcf432259e29ccc422b65abb
[framework/uifw/ise-engine-tables.git] / src / scim_table_imengine.cpp
1 /** @file scim_table_imengine.cpp
2  * implementation of class TableInstance.
3  */
4
5 /*
6  * Smart Common Input Method
7  * 
8  * Copyright (c) 2002-2005 James Su <suzhe@tsinghua.org.cn>
9  * Copyright (c) 2012-2013 Samsung Electronics Co., Ltd.
10  *
11  * This program is free software; you can redistribute it and/or modify
12  * it under the terms of the GNU General Public License as published by
13  * the Free Software Foundation; either version 2 of the License, or
14  * (at your option) any later version.
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with this program; if not, write to the Free Software
23  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
24  *
25  * $Id: scim_table_imengine.cpp,v 1.12 2006/01/12 08:43:29 suzhe Exp $
26  *
27  * Modifications by Samsung Electronics Co., Ltd.
28  *
29  * 1.Added auto commit feature for mobile user
30  */
31
32 #define Uses_STL_AUTOPTR
33 #define Uses_STL_FUNCTIONAL
34 #define Uses_STL_VECTOR
35 #define Uses_STL_IOSTREAM
36 #define Uses_STL_FSTREAM
37 #define Uses_STL_ALGORITHM
38 #define Uses_STL_MAP
39 #define Uses_STL_UTILITY
40 #define Uses_STL_IOMANIP
41 #define Uses_C_STDIO
42 #define Uses_SCIM_UTILITY
43 #define Uses_SCIM_IMENGINE
44 #define Uses_SCIM_ICONV
45 #define Uses_SCIM_CONFIG_BASE
46 #define Uses_SCIM_CONFIG_PATH
47 #define Uses_SCIM_LOOKUP_TABLE
48
49 #include <dirent.h>
50 #include <sys/types.h>
51 #include <sys/stat.h>
52 #include <unistd.h>
53 #include <scim.h>
54 #include <set>
55
56 #include <Ecore_IMF.h>
57
58 #include "scim_table_imengine.h"
59 #include "scim_table_private.h"
60
61 #define scim_module_init table_LTX_scim_module_init
62 #define scim_module_exit table_LTX_scim_module_exit
63 #define scim_imengine_module_init table_LTX_scim_imengine_module_init
64 #define scim_imengine_module_create_factory table_LTX_scim_imengine_module_create_factory
65
66 #define SCIM_TABLE_SAVE_PERIOD       300
67
68 #define SCIM_TABLE_MAX_TABLE_NUMBER  256
69 #define SCIM_TABLE_MAX_INPUTTED_KEYS  16
70
71 #define SCIM_CONFIG_IMENGINE_TABLE_FULL_WIDTH_PUNCT_KEY   "/IMEngine/Table/FullWidthPunctKey"
72 #define SCIM_CONFIG_IMENGINE_TABLE_FULL_WIDTH_LETTER_KEY  "/IMEngine/Table/FullWidthLetterKey"
73 #define SCIM_CONFIG_IMENGINE_TABLE_MODE_SWITCH_KEY        "/IMEngine/Table/ModeSwitchKey"
74 #define SCIM_CONFIG_IMENGINE_TABLE_ADD_PHRASE_KEY         "/IMEngine/Table/AddPhraseKey"
75 #define SCIM_CONFIG_IMENGINE_TABLE_DEL_PHRASE_KEY         "/IMEngine/Table/DeletePhraseKey"
76 #define SCIM_CONFIG_IMENGINE_TABLE_SHOW_PROMPT            "/IMEngine/Table/ShowPrompt"
77 #define SCIM_CONFIG_IMENGINE_TABLE_USER_TABLE_BINARY      "/IMEngine/Table/UserTableBinary"
78 #define SCIM_CONFIG_IMENGINE_TABLE_USER_PHRASE_FIRST      "/IMEngine/Table/UserPhraseFirst"
79 #define SCIM_CONFIG_IMENGINE_TABLE_LONG_PHRASE_FIRST      "/IMEngine/Table/LongPhraseFirst"
80 #define SCIM_CONFIG_IMENGINE_TABLE_SHOW_KEY_HINT          "/IMEngine/Table/ShowKeyHint"
81
82 #define SCIM_PROP_STATUS                                  "/IMEngine/Table/Status"
83 #define SCIM_PROP_LETTER                                  "/IMEngine/Table/Letter"
84 #define SCIM_PROP_PUNCT                                   "/IMEngine/Table/Punct"
85
86 #define SCIM_FULL_LETTER_ICON                             (SCIM_ICONDIR "/full-letter.png")
87 #define SCIM_HALF_LETTER_ICON                             (SCIM_ICONDIR "/half-letter.png")
88 #define SCIM_FULL_PUNCT_ICON                              (SCIM_ICONDIR "/full-punct.png")
89 #define SCIM_HALF_PUNCT_ICON                              (SCIM_ICONDIR "/half-punct.png")
90
91 #define SCIM_TABLE_ICON_FILE                              (SCIM_ICONDIR "/table.png")
92
93
94 using namespace scim;
95
96 static unsigned int _scim_number_of_tables = 0;
97
98 static Pointer <TableFactory> _scim_table_factories [SCIM_TABLE_MAX_TABLE_NUMBER];
99
100 static std::vector<String> _scim_sys_table_list;
101 static std::vector<String> _scim_user_table_list;
102
103 static ConfigPointer _scim_config;
104 char g_common_symbol[]={'#','$','%','^','&','*','@'};
105
106 static void
107 _get_table_list (std::vector<String> &table_list, const String &path)
108 {
109     table_list.clear ();
110
111     DIR *dir = opendir (path.c_str ());
112     if (dir != NULL) {
113         struct dirent *file = readdir (dir);
114         while (file != NULL) {
115             struct stat filestat;
116             String absfn = path + SCIM_PATH_DELIM_STRING + file->d_name;
117             stat (absfn.c_str (), &filestat);
118
119             if (S_ISREG (filestat.st_mode))
120                 table_list.push_back (absfn);
121
122             file = readdir (dir);
123         }
124         closedir (dir);        
125     }
126 }
127
128 extern "C" {
129     void scim_module_init (void)
130     {
131         bindtextdomain (GETTEXT_PACKAGE, SCIM_TABLE_LOCALEDIR);
132         bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8");
133     }
134
135     void scim_module_exit (void)
136     {
137         for (int i=0; i<_scim_number_of_tables; ++i)
138             _scim_table_factories [i].reset ();
139
140         _scim_config.reset ();
141     }
142
143     unsigned int scim_imengine_module_init (const ConfigPointer &config)
144     {
145         _scim_config = config;
146
147         _get_table_list (_scim_sys_table_list, SCIM_TABLE_SYSTEM_TABLE_DIR);
148         _get_table_list (_scim_user_table_list, scim_get_home_dir () + SCIM_TABLE_USER_TABLE_DIR);
149
150         _scim_number_of_tables = _scim_sys_table_list.size () + _scim_user_table_list.size (); 
151
152         return _scim_number_of_tables; 
153     }
154
155     IMEngineFactoryPointer scim_imengine_module_create_factory (unsigned int index)
156     {
157         if (index >= _scim_number_of_tables) return 0;
158
159         TableFactory *factory = 0;
160
161         try {
162             factory = new TableFactory (_scim_config);
163
164             if (index < _scim_sys_table_list.size ())
165                 factory->load_table (_scim_sys_table_list [index], false);
166             else
167                 factory->load_table (_scim_user_table_list [index - _scim_sys_table_list.size ()], true);
168
169             if (!factory->valid ())
170                 throw IMEngineError ("Table load failed!");
171
172             return IMEngineFactoryPointer (factory);
173
174         } catch (...) {
175             delete factory;
176             return IMEngineFactoryPointer (0);
177         }
178     }
179 }
180
181 // implementation of Table
182 TableFactory::TableFactory (const ConfigPointer &config)
183     : m_config (config),
184       m_is_user_table (false),
185       m_show_prompt (false),
186       m_show_key_hint (false),
187       m_user_table_binary (false),
188       m_user_phrase_first (false),
189       m_long_phrase_first (false),
190       m_last_time ((time_t)0),
191       m_status_property (SCIM_PROP_STATUS, ""),
192       m_letter_property (SCIM_PROP_LETTER, _("Full/Half Letter")),
193       m_punct_property (SCIM_PROP_PUNCT, _("Full/Half Punct"))
194 {
195     init (m_config);
196
197     m_status_property.set_tip (_("The status of the current input method. Click to change it."));
198     m_letter_property.set_tip (_("The input mode of the letters. Click to toggle between half and full."));
199     m_punct_property.set_tip (_("The input mode of the puncutations. Click to toggle between half and full."));
200
201     if (!m_config.null ())
202         m_reload_signal_connection = m_config->signal_connect_reload (slot (this, &TableFactory::init));
203 }
204
205 void
206 TableFactory::init (const ConfigPointer &config)
207 {
208     String str;
209
210     SCIM_DEBUG_IMENGINE (1) << "Load configuration.\n";
211
212     if (!config.null ()) {
213         //Read full width punctuation keys
214         str = config->read (String (SCIM_CONFIG_IMENGINE_TABLE_FULL_WIDTH_PUNCT_KEY), String (""));
215
216         scim_string_to_key_list (m_full_width_punct_keys, str);
217
218         //Read full width letter keys
219         str = config->read (String (SCIM_CONFIG_IMENGINE_TABLE_FULL_WIDTH_LETTER_KEY), String (""));
220
221         scim_string_to_key_list (m_full_width_letter_keys, str);
222
223         //Read mode switch keys
224         str = config->read (String (SCIM_CONFIG_IMENGINE_TABLE_MODE_SWITCH_KEY), String (""));
225
226         scim_string_to_key_list (m_mode_switch_keys, str);
227
228         //Read add phrase keys
229         str = config->read (String (SCIM_CONFIG_IMENGINE_TABLE_ADD_PHRASE_KEY), String ("Control+a,Control+equal"));
230
231         scim_string_to_key_list (m_add_phrase_keys, str);
232
233         //Read delete phrase keys
234         str = config->read (String (SCIM_CONFIG_IMENGINE_TABLE_DEL_PHRASE_KEY), String ("Control+d,Control+minus"));
235
236         scim_string_to_key_list (m_del_phrase_keys, str);
237
238         m_show_prompt = config->read (String (SCIM_CONFIG_IMENGINE_TABLE_SHOW_PROMPT), false);
239
240         m_show_key_hint = config->read (String (SCIM_CONFIG_IMENGINE_TABLE_SHOW_KEY_HINT), false);
241
242         m_user_phrase_first = config->read (String (SCIM_CONFIG_IMENGINE_TABLE_USER_PHRASE_FIRST), false);
243
244         m_long_phrase_first = config->read (String (SCIM_CONFIG_IMENGINE_TABLE_LONG_PHRASE_FIRST), false);
245
246         m_user_table_binary = config->read (String (SCIM_CONFIG_IMENGINE_TABLE_USER_TABLE_BINARY), false);
247     }
248
249     m_last_time = time (NULL);
250 }
251
252 TableFactory::~TableFactory ()
253 {
254     save ();
255     m_reload_signal_connection.disconnect ();
256 }
257
258 WideString
259 TableFactory::get_name () const
260 {
261     return m_table.get_name (scim_get_current_locale ());
262 }
263
264 WideString
265 TableFactory::get_authors () const
266 {
267     return m_table.get_author ();
268 }
269
270 WideString
271 TableFactory::get_credits () const
272 {
273     return WideString ();
274 }
275
276 WideString
277 TableFactory::get_help () const
278 {
279     WideString help;
280
281     std::vector<KeyEvent> keys, keys2;
282
283     String full_width_letter;
284     String full_width_punct;
285     String mode_switch;
286     String add_phrase;
287     String del_phrase;
288
289     keys = m_full_width_letter_keys;
290     keys2 = m_table.get_full_width_letter_keys ();
291     keys.insert (keys.end (), keys2.begin (), keys2.end ());
292     keys.erase (std::unique (keys.begin (), keys.end ()), keys.end ());
293     scim_key_list_to_string (full_width_letter, keys);
294
295     keys = m_full_width_punct_keys;
296     keys2 = m_table.get_full_width_punct_keys ();
297     keys.insert (keys.end (), keys2.begin (), keys2.end ());
298     keys.erase (std::unique (keys.begin (), keys.end ()), keys.end ());
299     scim_key_list_to_string (full_width_punct, keys);
300
301     keys = m_mode_switch_keys;
302     keys2 = m_table.get_mode_switch_keys ();
303     keys.insert (keys.end (), keys2.begin (), keys2.end ());
304     keys.erase (std::unique (keys.begin (), keys.end ()), keys.end ());
305     scim_key_list_to_string (mode_switch, keys);
306
307     scim_key_list_to_string (add_phrase, m_add_phrase_keys);
308     scim_key_list_to_string (del_phrase, m_del_phrase_keys);
309
310     return utf8_mbstowcs (
311         String (_("Hot Keys:\n\n  ")) +
312         full_width_letter + String (":\n") +
313         String (_("    Switch between full/half width letter mode.\n\n  ")) +
314         full_width_punct + String (":\n") +
315         String (_("    Switch between full/half width punctuation mode.\n\n  ")) +
316         mode_switch + String (":\n") +
317         String (_("    Switch between Forward/Input mode.\n\n  ")) +
318         add_phrase + String (":\n") +
319         String (_("    Add a new phrase.\n\n  ")) +
320         del_phrase + String (":\n") +
321         String (_("    Delete the selected phrase.\n\n")) +
322         String (_("  Control+Down:\n    Move lookup cursor to next shorter phrase\n"
323                   "    Only available when LongPhraseFirst option is set.\n\n")) +
324         String (_("  Control+Up:\n    Move lookup cursor to previous longer phrase\n"
325                   "    Only available when LongPhraseFirst option is set.\n\n")) +
326         String (_("  Esc:\n    reset the input method.\n\n\n")) +
327         String (_("How to add a phrase:\n"
328                   "    Input the new phrase as normal, then press the\n"
329                   "  hot key. A hint will be shown to let you input a key\n"
330                   "  for this phrase.\n"
331                   "    Input a key then press the space bar.\n"
332                   "  A hint will be shown to indicate whether\n"
333                   "  the phrase was added sucessfully.\n"))
334         );
335 }
336
337 String
338 TableFactory::get_uuid () const
339 {
340     return m_table.get_uuid ();
341 }
342
343 String
344 TableFactory::get_icon_file () const
345 {
346     String file = m_table.get_icon_file ();
347
348     return file.length () ? file : String (SCIM_TABLE_ICON_FILE);
349 }
350
351 IMEngineInstancePointer
352 TableFactory::create_instance (const String& encoding, int id)
353 {
354     return new TableInstance (this, encoding, id);
355 }
356
357 bool
358 TableFactory::load_table (const String &table_file, bool user_table)
359 {
360     if (!table_file.length ()) return false;
361
362     m_table_filename = table_file;
363     m_is_user_table = user_table;
364
365     if (user_table) {
366         if (!m_table.init ("", m_table_filename, "")) return false;
367     } else {
368         if (!m_table.init (m_table_filename,
369                            get_sys_table_user_file (),
370                            get_sys_table_freq_file ()))
371             return false;
372     }
373
374     set_languages (m_table.get_languages ());
375
376     return m_table.valid ();
377 }
378
379 void
380 TableFactory::refresh (bool rightnow)
381 {
382     time_t cur_time = time (NULL);
383
384     if (rightnow || cur_time < m_last_time || cur_time - m_last_time > SCIM_TABLE_SAVE_PERIOD) {
385         m_last_time = cur_time;
386         save ();
387     }
388 }
389
390 void
391 TableFactory::save ()
392 {
393     if (!m_table.valid () || !m_table.updated ()) return;
394
395     if (m_is_user_table)
396         m_table.save ("", m_table_filename, "", m_user_table_binary);
397     else
398         m_table.save ("", get_sys_table_user_file (), get_sys_table_freq_file (), m_user_table_binary);
399 }
400
401 String
402 TableFactory::get_sys_table_freq_file ()
403 {
404     String fn, tf;
405     String::size_type pos;
406
407     if (m_table_filename.length ()) {
408         pos = m_table_filename.rfind (SCIM_PATH_DELIM);
409
410         if (pos != String::npos)
411             tf = m_table_filename.substr (pos+1);
412         else
413             tf = m_table_filename;
414
415         fn = scim_get_home_dir () + SCIM_TABLE_SYSTEM_UPDATE_TABLE_DIR;
416
417         if (access (fn.c_str (), R_OK | W_OK) != 0) {
418             if (!scim_make_dir (fn))
419                 return String ();
420         }
421
422         fn = fn + SCIM_PATH_DELIM_STRING + tf + ".freq";
423     }
424     return fn;
425 }
426
427 String
428 TableFactory::get_sys_table_user_file ()
429 {
430     String fn, tf;
431     String::size_type pos;
432
433     if (m_table_filename.length ()) {
434         pos = m_table_filename.rfind (SCIM_PATH_DELIM);
435
436         if (pos != String::npos)
437             tf = m_table_filename.substr (pos+1);
438         else
439             tf = m_table_filename;
440
441         fn = scim_get_home_dir () + SCIM_TABLE_SYSTEM_UPDATE_TABLE_DIR;
442
443         if (access (fn.c_str (), R_OK | W_OK) != 0) {
444             if (!scim_make_dir (fn))
445                 return String ();
446         }
447
448         fn = fn + SCIM_PATH_DELIM_STRING + tf + ".user";
449     }
450     return fn;
451 }
452
453
454 // implementation of TableInstance
455 TableInstance::TableInstance (TableFactory *factory,
456                                             const String& encoding,
457                                             int id)
458     : IMEngineInstanceBase (factory, encoding, id),
459       m_factory (factory),
460       m_double_quotation_state (false),
461       m_single_quotation_state (false),
462       m_forward (false),
463       m_focused (false),
464       m_inputing_caret (0),
465       m_inputing_key (0),
466       m_iconv (encoding)
467 {
468     m_full_width_letter [0] = m_factory->m_table.is_def_full_width_letter ();
469     m_full_width_letter [1] = false;
470
471     m_full_width_punct [0] = m_factory->m_table.is_def_full_width_punct ();
472     m_full_width_punct [1] = false;
473
474     char buf [2] = { 0, 0 };
475
476     std::vector <KeyEvent>   keys = m_factory->m_table.get_select_keys ();
477     std::vector <WideString> labels;
478
479     for (size_t i = 0; i < keys.size (); ++i) {
480         buf [0] = keys [i].get_ascii_code ();
481         labels.push_back (utf8_mbstowcs (buf));
482     }
483
484     m_lookup_table.set_candidate_labels (labels);
485     m_lookup_table.set_page_size        (keys.size ());
486     m_lookup_table.show_cursor ();
487
488     AttributeList attrs;
489     for (int i = 0;i < sizeof(g_common_symbol)/sizeof(char);i++) {
490         char _str[2]={g_common_symbol[i],0};
491         m_common_lookup_table.append_candidate (utf8_mbstowcs ((const char*)_str),attrs);
492     }
493 }
494
495 TableInstance::~TableInstance ()
496 {
497 }
498
499 bool
500 TableInstance::process_key_event (const KeyEvent& rawkey)
501 {
502     KeyEvent key = rawkey.map_to_layout (m_factory->m_table.get_keyboard_layout ());
503
504     bool ret = false;
505
506     if (!m_focused) return false;
507
508     // capture the mode switch key events
509     if (match_key_event (m_factory->m_mode_switch_keys, key) ||
510         match_key_event (m_factory->m_table.get_mode_switch_keys (), key)) {
511         m_forward = !m_forward;
512         refresh_status_property ();
513         refresh_letter_property ();
514         refresh_punct_property ();
515         reset ();
516         ret = true;
517     }
518
519     // toggle full width punctuation mode
520     else if (match_key_event (m_factory->m_full_width_punct_keys, key) ||
521              match_key_event (m_factory->m_table.get_full_width_punct_keys (), key)) {
522         trigger_property (SCIM_PROP_PUNCT);
523         ret = true;
524     }
525
526     // toggle full width letter mode
527     else if (match_key_event (m_factory->m_full_width_letter_keys, key) ||
528              match_key_event (m_factory->m_table.get_full_width_letter_keys (), key)) {
529         trigger_property (SCIM_PROP_LETTER);
530         ret = true;
531     }
532
533     // discard the key release event.
534     else if (key.is_key_release ()) {
535         ret = true;
536     }
537
538     // process the key press event, if not in forward mode.
539     else if (!m_forward) {
540         // First reset add phrase mode.
541         if (m_add_phrase_mode > 1) {
542             m_add_phrase_mode = 0;
543             refresh_aux_string ();
544         }
545
546         //reset key
547         if (key.code == SCIM_KEY_Escape && key.mask == 0) {
548             if (m_inputted_keys.size () == 0 && m_add_phrase_mode != 1)
549                 ret = false;
550             else {
551                 reset ();
552                 ret = true;
553             }
554         }
555
556         //caret left
557         else if (key.code == SCIM_KEY_Left && key.mask == 0)
558             ret = caret_left ();
559
560         //caret right
561         else if (key.code == SCIM_KEY_Right && key.mask == 0)
562             ret = caret_right ();
563
564         //caret home 
565         else if (key.code == SCIM_KEY_Home && key.mask == 0)
566             ret = caret_home ();
567
568         //caret end
569         else if (key.code == SCIM_KEY_End && key.mask == 0)
570             ret = caret_end ();
571
572         //lookup table cursor up
573         else if (key.code == SCIM_KEY_Up && key.mask == 0)
574             ret = lookup_cursor_up ();
575
576         //lookup table cursor down
577         else if (key.code == SCIM_KEY_Down && key.mask == 0)
578             ret = lookup_cursor_down ();
579
580         //lookup table cursor up to longer phrase
581         else if (key.code == SCIM_KEY_Up && key.mask == SCIM_KEY_ControlMask &&
582                  m_factory->m_long_phrase_first && !m_factory->m_user_phrase_first)
583             ret = lookup_cursor_up_to_longer ();
584
585         //lookup table cursor down
586         else if (key.code == SCIM_KEY_Down && key.mask == SCIM_KEY_ControlMask &&
587                  m_factory->m_long_phrase_first && !m_factory->m_user_phrase_first)
588             ret = lookup_cursor_down_to_shorter ();
589
590         //backspace key
591         else if (key.code == SCIM_KEY_BackSpace && key.mask == 0)
592             ret = erase ();
593
594         //delete key
595         else if (key.code == SCIM_KEY_Delete && key.mask == 0)
596             ret = erase (false);
597
598         //add new phrase
599         else if (!m_inputted_keys.size () && m_last_committed.length () &&
600             match_key_event (m_factory->m_add_phrase_keys, key)) {
601             m_add_phrase_mode = 1;
602             refresh_aux_string ();
603             ret = true;
604         }
605
606         //delete phrase
607         else if (match_key_event (m_factory->m_del_phrase_keys, key)) {
608             if (delete_phrase ())
609                 ret = true;
610         }
611
612         // other situation
613         else {
614             ret = false;
615
616             //select lookup table
617             int pos = m_factory->m_table.get_select_key_pos (key);
618
619             // If there is a new empty key (a split char was inserted),
620             // then try to select lookup table first.
621             // Otherwise try to insert the char first.
622             if (m_inputted_keys.size () && !m_inputted_keys [m_inputing_key].length ()) {
623                 if (pos >= 0 && pos < m_lookup_table.get_current_page_size ())
624                     ret = lookup_select (pos);
625
626                 // insert char
627                 if (!ret && (key.mask & (~ (SCIM_KEY_ShiftMask + SCIM_KEY_CapsLockMask))) == 0)
628                     ret = insert (key.get_ascii_code ());
629             } else {
630                 // insert char
631                 if ((key.mask & (~ (SCIM_KEY_ShiftMask + SCIM_KEY_CapsLockMask))) == 0 &&
632                     test_insert (key.get_ascii_code ()))
633                     ret = insert (key.get_ascii_code ());
634
635                 if (!ret && pos >= 0 && pos < m_lookup_table.get_current_page_size ())
636                     ret = lookup_select (pos);
637
638                 // insert char finally.
639                 if (!ret && (key.mask & (~ (SCIM_KEY_ShiftMask + SCIM_KEY_CapsLockMask))) == 0)
640                     ret = insert (key.get_ascii_code ());
641             }
642
643             //lookup table page up
644             if (!ret && match_key_event (m_factory->m_table.get_page_up_keys (), key))
645                 ret = lookup_page_up ();
646
647             //lookup table page down
648             if (!ret && match_key_event (m_factory->m_table.get_page_down_keys (), key))
649                 ret = lookup_page_down ();
650
651             //space hit
652             if (!ret && match_key_event (m_factory->m_table.get_commit_keys (), key))
653                 ret = space_hit ();
654
655             //enter hit
656             if (!ret && match_key_event (m_factory->m_table.get_forward_keys (), key))
657                 ret = enter_hit ();
658         }
659     }
660
661     if (!ret && (key.mask & (~ (SCIM_KEY_ShiftMask + SCIM_KEY_CapsLockMask))) == 0 &&
662         key.get_ascii_code ())
663         ret = post_process (key.get_ascii_code ());
664
665     m_prev_key = key;
666
667     return ret;
668 }
669
670 void
671 TableInstance::select_candidate (unsigned int item)
672 {
673
674     if (m_inputted_keys.size () == 0
675         && m_lookup_table.number_of_candidates() == 0
676         && item < sizeof(g_common_symbol)/sizeof(char)) {
677         char _str[2]={g_common_symbol[item],0};
678         commit_string(utf8_mbstowcs(_str));
679         return;
680     }
681     lookup_select (item);
682 }
683
684 void
685 TableInstance::update_lookup_table_page_size (unsigned int page_size)
686 {
687     if (page_size > 0)
688         m_lookup_table.set_page_size (page_size);
689 }
690
691 void
692 TableInstance::lookup_table_page_up ()
693 {
694     lookup_page_up ();
695 }
696
697 void
698 TableInstance::lookup_table_page_down ()
699 {
700     lookup_page_down ();
701 }
702
703 void
704 TableInstance::move_preedit_caret (unsigned int pos)
705 {
706     uint32 len = 0;
707     size_t i;
708
709     for (i=0; i<m_converted_strings.size (); ++i) {
710         if (pos >= len && pos < len + m_converted_strings [i].length ()) {
711             m_inputing_key = i;
712             m_inputing_caret = m_inputted_keys [i].length ();
713
714             m_converted_strings.erase (m_converted_strings.begin () + i, m_converted_strings.end ());
715             m_converted_indexes.erase (m_converted_indexes.begin () + i, m_converted_indexes.end ());
716
717             refresh_lookup_table ();
718             refresh_preedit ();
719             refresh_aux_string ();
720
721             return;
722         }
723         len += m_converted_strings [i].length ();
724     }
725
726     if (m_factory->m_table.is_auto_fill () &&
727         m_inputing_key == m_inputted_keys.size () - 1 &&
728         m_inputing_caret == m_inputted_keys [m_inputing_key].length () &&
729         m_converted_strings.size () == m_inputing_key &&
730         m_lookup_table.number_of_candidates ()) {
731
732         uint32 offset = m_lookup_table_indexes [m_lookup_table.get_cursor_pos ()];
733         size_t phlen  = m_factory->m_table.get_phrase_length (offset);
734
735         if (pos >= len && pos < len + phlen) {
736             m_inputing_caret = 0;
737             refresh_lookup_table (true, false);
738             refresh_preedit ();
739             return;
740         }
741     } else {
742         if (m_converted_strings.size ()) {
743             ++len;
744             if (pos < len) ++pos;
745         }
746
747         for (i=m_converted_strings.size (); i<m_inputted_keys.size (); ++i) {
748             if (pos >= len && pos <= len + m_inputted_keys [i].length ()) {
749                 m_inputing_key = i;
750                 m_inputing_caret = pos - len;
751
752                 refresh_lookup_table (true, false);
753                 refresh_preedit ();
754                 refresh_aux_string ();
755                 return;
756             }
757
758             len += (m_inputted_keys [i].length () +1);
759         }
760     }
761 }
762
763 void
764 TableInstance::reset ()
765 {
766     if (m_inputted_keys.size () && m_preedit_string.size()) {
767         commit_string (m_preedit_string);
768     }
769     m_double_quotation_state = false;
770     m_single_quotation_state = false;
771
772     m_lookup_table.clear ();
773
774     std::vector<String> ().swap (m_inputted_keys);
775
776     std::vector<WideString> ().swap (m_converted_strings);
777
778     std::vector<uint32> ().swap (m_converted_indexes);
779
780     std::vector<uint32> ().swap (m_lookup_table_indexes);
781
782     m_add_phrase_mode = 0;
783
784     m_last_committed = WideString ();
785
786     m_inputing_caret = 0;
787     m_inputing_key = 0;
788
789     m_iconv.set_encoding (get_encoding ());
790     if (!m_forward) {
791         refresh_lookup_table (true, true);
792     } else {
793         hide_lookup_table();
794     }
795     hide_preedit_string ();
796     hide_aux_string ();
797 }
798
799 void
800 TableInstance::focus_in ()
801 {
802     m_focused = true;
803
804     if (m_add_phrase_mode != 1) {
805         m_last_committed = WideString ();
806         m_add_phrase_mode = 0;
807     }
808
809     //refresh_lookup_table (true, false);
810     refresh_preedit ();
811     refresh_aux_string ();
812     initialize_properties ();
813 }
814
815 void
816 TableInstance::focus_out ()
817 {
818     m_focused = false;
819     reset ();
820 }
821
822 void
823 TableInstance::trigger_property (const String &property)
824 {
825     if (property == SCIM_PROP_STATUS) {
826         m_forward = !m_forward;
827         refresh_status_property ();
828         refresh_letter_property ();
829         refresh_punct_property ();
830         reset ();
831     } else if (property == SCIM_PROP_LETTER && m_factory->m_table.is_use_full_width_letter ()) {
832         m_full_width_letter [m_forward?1:0] =
833             !m_full_width_letter [m_forward?1:0];
834         refresh_letter_property ();
835     } else if (property == SCIM_PROP_PUNCT && m_factory->m_table.is_use_full_width_punct ()) {
836         m_full_width_punct [m_forward?1:0] = 
837             !m_full_width_punct [m_forward?1:0];
838         refresh_punct_property ();
839     }
840 }
841  
842 void
843 TableInstance::set_layout (unsigned int layout)
844 {
845     switch (layout)
846     {
847         case ECORE_IMF_INPUT_PANEL_LAYOUT_NORMAL:
848         case ECORE_IMF_INPUT_PANEL_LAYOUT_NUMBER:
849         case ECORE_IMF_INPUT_PANEL_LAYOUT_EMAIL:
850         case ECORE_IMF_INPUT_PANEL_LAYOUT_URL:
851             refresh_lookup_table(true,false);
852             show_lookup_table ();
853             m_forward = false;
854             break;
855         default:
856             hide_lookup_table ();
857             m_forward = true;
858     }
859 }
860
861 void
862 TableInstance::initialize_properties ()
863 {
864     PropertyList proplist;
865
866     proplist.push_back (m_factory->m_status_property);
867
868     if (m_factory->m_table.is_use_full_width_letter ())
869         proplist.push_back (m_factory->m_letter_property);
870
871     if (m_factory->m_table.is_use_full_width_punct ())
872         proplist.push_back (m_factory->m_punct_property);
873
874     register_properties (proplist);
875
876     refresh_status_property ();
877     refresh_letter_property ();
878     refresh_punct_property ();
879 }
880
881 void
882 TableInstance::refresh_status_property ()
883 {
884     if (m_focused) {
885         if (m_forward)
886             m_factory->m_status_property.set_label (_("En"));
887         else
888             m_factory->m_status_property.set_label (utf8_wcstombs (m_factory->m_table.get_status_prompt ()));
889
890         update_property (m_factory->m_status_property);
891     }
892 }
893
894 void
895 TableInstance::refresh_letter_property ()
896 {
897     if (m_focused && m_factory->m_table.is_use_full_width_letter ()) {
898         m_factory->m_letter_property.set_icon (
899             m_full_width_letter [m_forward?1:0] ? SCIM_FULL_LETTER_ICON : SCIM_HALF_LETTER_ICON);
900         update_property (m_factory->m_letter_property);
901     }
902 }
903
904 void
905 TableInstance::refresh_punct_property ()
906 {
907     if (m_focused && m_factory->m_table.is_use_full_width_punct ()) {
908         m_factory->m_punct_property.set_icon (
909             m_full_width_punct [m_forward?1:0] ? SCIM_FULL_PUNCT_ICON : SCIM_HALF_PUNCT_ICON);
910         update_property (m_factory->m_punct_property);
911     }
912 }
913
914 bool
915 TableInstance::caret_left ()
916 {
917     if (m_inputted_keys.size ()) {
918         if (m_inputing_caret > 0) {
919             --m_inputing_caret;
920             refresh_lookup_table (true, false);
921         } else if (m_inputing_key > 0) {
922             --m_inputing_key;
923             m_inputing_caret = m_inputted_keys [m_inputing_key].length ();
924
925             if (m_converted_strings.size () > m_inputing_key) {
926                 m_converted_strings.pop_back ();
927                 m_converted_indexes.pop_back ();
928                 refresh_lookup_table ();
929             } else {
930                 refresh_lookup_table (true, false);
931             }
932         } else {
933             return caret_end ();
934         }
935
936         refresh_preedit ();
937         refresh_aux_string ();
938         return true;
939     }
940     return false;
941 }
942
943 bool
944 TableInstance::caret_right ()
945 {
946     if (m_inputted_keys.size ()) {
947         if (m_inputing_caret < m_inputted_keys [m_inputing_key].size ()) {
948             ++m_inputing_caret;
949         } else if (m_inputing_key < m_inputted_keys.size () - 1) {
950             ++m_inputing_key;
951             m_inputing_caret = 0;
952         } else {
953             return caret_home ();
954         }
955         refresh_lookup_table (true, false);
956         refresh_preedit ();
957         refresh_aux_string ();
958         return true;
959     }
960     return false;
961 }
962
963 bool
964 TableInstance::caret_home ()
965 {
966     if (m_inputted_keys.size ()) {
967         m_inputing_key = 0;
968         m_inputing_caret = 0;
969
970         if (m_converted_strings.size ()) {
971             m_converted_strings.clear ();
972             m_converted_indexes.clear ();
973             refresh_lookup_table ();
974         } else {
975             refresh_lookup_table (true, false);
976         }
977
978         refresh_preedit ();
979         refresh_aux_string ();
980         return true;
981     }
982     return false;
983 }
984
985 bool
986 TableInstance::caret_end ()
987 {
988     if (m_inputted_keys.size ()) {
989         m_inputing_key = m_inputted_keys.size () - 1;
990         m_inputing_caret = m_inputted_keys [m_inputing_key].length ();
991
992         refresh_lookup_table (true, false);
993         refresh_preedit ();
994         refresh_aux_string ();
995         return true;
996     }
997     return false;
998 }
999
1000 bool
1001 TableInstance::test_insert (char key)
1002 {
1003     if (m_factory->m_table.is_valid_char (key)) {
1004         String newkey;
1005         if (m_inputted_keys.size ()) {
1006             newkey = m_inputted_keys [m_inputing_key];
1007             newkey.insert (newkey.begin () + m_inputing_caret, key);
1008         } else {
1009             newkey.push_back (key);
1010         }
1011
1012         return m_factory->m_table.is_defined_key (newkey);
1013     }
1014     return false;
1015 }
1016
1017 bool
1018 TableInstance::insert (char ch)
1019 {
1020     if (!ch) return false;
1021
1022     String newkey;
1023     uint32 old_inputing_key = m_inputing_key;
1024     bool insert_ok = false;
1025
1026     if (m_inputted_keys.size () - m_converted_strings.size () >
1027         SCIM_TABLE_MAX_INPUTTED_KEYS)
1028         return false;
1029
1030     // If current inputing key is empty, then the last inputing key is the previous key.
1031     if (m_inputted_keys.size () && m_inputing_key && !m_inputted_keys [m_inputing_key].length ())
1032         -- old_inputing_key;
1033
1034     if (m_factory->m_table.is_split_char (ch)) {
1035         // split char is invalid during add phrase mode.
1036         if (m_add_phrase_mode == 1)
1037             return true;
1038         else if (m_inputted_keys.size () == 0)
1039             return false;
1040         else if (m_inputing_key == m_inputted_keys.size () -1 &&
1041                 m_inputted_keys [m_inputing_key].length () &&
1042                 m_inputing_caret == m_inputted_keys [m_inputing_key].length ()) {
1043             ++m_inputing_key;
1044             m_inputing_caret = 0;
1045             m_inputted_keys.insert (m_inputted_keys.begin () + m_inputing_key, String ());
1046         } else {
1047             return false;
1048         }
1049
1050         insert_ok = true;
1051     } else if (m_factory->m_table.is_valid_char (ch)) {
1052         if (m_add_phrase_mode == 1) {
1053             m_inputing_key = 0;
1054             if (!m_inputted_keys.size ()) {
1055                 m_inputted_keys.push_back (String (""));
1056                 m_inputing_caret = 0;
1057             } else if (m_inputted_keys [0].length () >= m_factory->m_table.get_max_key_length ()) {
1058                 return true;
1059             }
1060
1061             m_inputted_keys [0].insert (m_inputted_keys [0].begin () + m_inputing_caret, ch);
1062             ++m_inputing_caret;
1063             insert_ok = true;
1064         } else if (m_inputted_keys.size ()) {
1065             newkey = m_inputted_keys [m_inputing_key];
1066             newkey.insert (newkey.begin () + m_inputing_caret, ch);
1067
1068             if ((m_factory->m_table.is_auto_split () == false ||
1069                  m_factory->m_table.is_defined_key (newkey)) &&
1070                 newkey.length () <= m_factory->m_table.get_max_key_length ()) {
1071                 m_inputted_keys [m_inputing_key] = newkey;
1072                 ++m_inputing_caret;
1073                 insert_ok = true;
1074             } else if (m_inputing_caret == m_inputted_keys [m_inputing_key].length ()) {
1075                 newkey = String ();
1076                 newkey.push_back (ch);
1077
1078                 if (m_factory->m_table.is_defined_key (newkey)) {
1079                     ++m_inputing_key;
1080                     m_inputing_caret = 1;
1081                     m_inputted_keys.insert (m_inputted_keys.begin () + m_inputing_key, newkey);
1082                     insert_ok = true;
1083                 }
1084             } else if (m_inputing_caret == 0) {
1085                 newkey = String ();
1086                 newkey.push_back (ch);
1087
1088                 if (m_factory->m_table.is_defined_key (newkey)) {
1089                     m_inputing_caret = 1;
1090                     m_inputted_keys.insert (m_inputted_keys.begin () + m_inputing_key, newkey);
1091                     insert_ok = true;
1092                 }
1093             }
1094         } else if (!m_factory->m_table.is_multi_wildcard_char (ch)) {
1095             newkey = String ();
1096             newkey.push_back (ch);
1097
1098             if (m_factory->m_table.is_defined_key (newkey)) {
1099                 m_inputted_keys.push_back (newkey);
1100                 m_inputing_key = 0;
1101                 m_inputing_caret = 1; 
1102                 insert_ok = true;
1103             }
1104         }
1105     }
1106
1107     if (insert_ok) {
1108         //Do some extra work in normal mode.
1109         if (m_add_phrase_mode != 1) {
1110             //auto select
1111             if (m_factory->m_table.is_auto_select () &&
1112                 m_converted_strings.size () == old_inputing_key &&
1113                 old_inputing_key + 1 == m_inputing_key &&
1114                 m_lookup_table.number_of_candidates () &&
1115                 m_inputted_keys [m_inputing_key].length ()) {
1116                 lookup_to_converted (m_lookup_table.get_cursor_pos ());
1117             }
1118
1119             //discard invalid key
1120             if (m_factory->m_table.is_discard_invalid_key () &&
1121                 m_converted_strings.size () == old_inputing_key &&
1122                 old_inputing_key + 1 == m_inputing_key &&
1123                 m_lookup_table.number_of_candidates () == 0 &&
1124                 m_inputted_keys [m_inputing_key].length ()) {
1125                 m_inputted_keys.erase (m_inputted_keys.begin () + old_inputing_key);
1126                 m_inputing_key --;
1127             }
1128
1129             if (m_converted_strings.size () == m_inputing_key) {
1130                 refresh_lookup_table (false, true);
1131
1132                 // If auto commit is true, then do auto select when
1133                 // there is only one candidate for this key.
1134                 if (m_lookup_table.number_of_candidates () == 1 &&
1135                     m_factory->m_table.is_auto_commit () &&
1136                     !m_factory->m_table.is_defined_key (
1137                         m_inputted_keys [m_inputing_key],
1138                         GT_SEARCH_ONLY_LONGER)) {
1139                     lookup_to_converted (m_lookup_table.get_cursor_pos ());
1140                     refresh_lookup_table ();
1141                 } else {
1142                     refresh_lookup_table (true, false);
1143                 }
1144             }
1145
1146             if (m_inputted_keys.size () > SCIM_TABLE_MAX_INPUTTED_KEYS ||
1147                 m_factory->m_table.is_auto_commit ())
1148                 commit_converted ();
1149
1150             // If it's a key end char, then append an empty key.
1151             if (m_factory->m_table.is_key_end_char (ch) &&
1152                 m_inputing_key == m_inputted_keys.size () -1 &&
1153                 m_inputted_keys [m_inputing_key].length () &&
1154                 m_inputing_caret == m_inputted_keys [m_inputing_key].length ()) {
1155                 ++m_inputing_key;
1156                 m_inputing_caret = 0;
1157                 m_inputted_keys.insert (m_inputted_keys.begin () + m_inputing_key, String ());
1158             }
1159         }
1160
1161         refresh_preedit ();
1162         refresh_aux_string ();
1163
1164         return true;
1165     }
1166
1167     return false;
1168 }
1169
1170 bool
1171 TableInstance::erase (bool backspace)
1172 {
1173     if (m_inputted_keys.size ()) {
1174         if (backspace && (m_inputing_key > 0 || m_inputing_caret > 0)) {
1175             if (m_inputing_caret > 0) {
1176                 --m_inputing_caret;
1177                 m_inputted_keys [m_inputing_key].erase (m_inputing_caret, 1);
1178             } else {
1179                 if (m_inputted_keys [m_inputing_key].length () == 0)
1180                     m_inputted_keys.erase (m_inputted_keys.begin () + m_inputing_key);
1181
1182                 --m_inputing_key;
1183                 m_inputing_caret = m_inputted_keys [m_inputing_key].length ();
1184
1185                 if (m_inputing_caret > 0) {
1186                     --m_inputing_caret;
1187                     m_inputted_keys [m_inputing_key].erase (m_inputing_caret, 1);
1188                 }
1189             }
1190
1191             if (m_inputted_keys [m_inputing_key].length () == 0) {
1192                 m_inputted_keys.erase (m_inputted_keys.begin () + m_inputing_key);
1193
1194                 if (m_inputing_key > 0) {
1195                     --m_inputing_key;
1196                     m_inputing_caret = m_inputted_keys [m_inputing_key].length ();
1197                 }
1198             }
1199         } else if (!backspace) {
1200             if (m_inputing_caret < m_inputted_keys [m_inputing_key].length ()) {
1201                 m_inputted_keys [m_inputing_key].erase (m_inputing_caret, 1);
1202             }
1203             if (m_inputted_keys [m_inputing_key].length () == 0) {
1204                 m_inputted_keys.erase (m_inputted_keys.begin () + m_inputing_key);
1205             }
1206             if (m_inputing_key == m_inputted_keys.size () && m_inputing_key > 0) {
1207                 --m_inputing_key;
1208                 m_inputing_caret = m_inputted_keys [m_inputing_key].length ();
1209             }
1210         } else {
1211             return true;
1212         }
1213
1214         if (m_inputted_keys.size () == 1 && m_inputted_keys [0].length () == 0) {
1215             m_inputted_keys.clear ();
1216             m_inputing_key = 0;
1217             m_inputing_caret = 0;
1218         }
1219
1220         if (m_add_phrase_mode != 1) {
1221             if (m_converted_strings.size () > m_inputing_key) {
1222                 m_converted_strings.erase (m_converted_strings.begin () + m_inputing_key, m_converted_strings.end ());
1223                 m_converted_indexes.erase (m_converted_indexes.begin () + m_inputing_key, m_converted_indexes.end ());
1224             }
1225             refresh_lookup_table ();
1226         }
1227
1228         refresh_preedit ();
1229         refresh_aux_string ();
1230         return true;
1231     }
1232     return false;
1233 }
1234
1235 bool
1236 TableInstance::space_hit ()
1237 {
1238     if (m_inputted_keys.size ()) {
1239         if (m_add_phrase_mode == 1) {
1240             if (m_factory->m_table.add_phrase (m_inputted_keys [0], m_last_committed)) {
1241                 m_add_phrase_mode = 2;
1242                 m_factory->refresh (true);
1243             } else {
1244                 m_add_phrase_mode = 3;
1245             }
1246
1247             m_inputted_keys.clear ();
1248             m_last_committed = WideString ();
1249             m_inputing_caret = m_inputing_key = 0;
1250         } else {
1251             if (m_converted_strings.size () == 0 && m_lookup_table.number_of_candidates () == 0)
1252                 return true;
1253
1254             if (m_lookup_table.number_of_candidates () && m_converted_strings.size () < m_inputted_keys.size ()) {
1255                 lookup_to_converted (m_lookup_table.get_cursor_pos ());
1256                 refresh_lookup_table ();
1257             }
1258
1259             if (m_converted_strings.size () == m_inputted_keys.size () ||
1260                 (m_converted_strings.size () == m_inputted_keys.size () - 1 &&
1261                  m_inputted_keys [m_inputing_key].length () == 0))
1262                 commit_converted ();
1263         }
1264
1265         refresh_preedit ();
1266         refresh_aux_string ();
1267
1268         return true;
1269     }
1270     return false;
1271 }
1272
1273 bool
1274 TableInstance::enter_hit ()
1275 {
1276     if (m_inputted_keys.size ()) {
1277         if (m_add_phrase_mode == 1) {
1278             if (m_factory->m_table.add_phrase (m_inputted_keys [0], m_last_committed)) {
1279                 m_add_phrase_mode = 2;
1280                 m_factory->refresh (true);
1281             } else {
1282                 m_add_phrase_mode = 3;
1283             }
1284
1285             m_inputted_keys.clear ();
1286             m_last_committed = WideString ();
1287             m_inputing_caret = m_inputing_key = 0;
1288
1289             refresh_preedit ();
1290             refresh_aux_string ();
1291         } else {
1292             reset ();
1293         }
1294         return true;
1295     }
1296
1297     m_last_committed = WideString ();
1298
1299     return false;
1300 }
1301
1302 bool
1303 TableInstance::lookup_cursor_up ()
1304 {
1305     if (m_inputted_keys.size () && m_lookup_table.number_of_candidates ()) {
1306         m_lookup_table.cursor_up ();
1307         refresh_lookup_table (true, false);
1308         refresh_preedit ();
1309         refresh_aux_string ();
1310         return true;
1311     }
1312     return false;
1313 }
1314
1315 bool
1316 TableInstance::lookup_cursor_down ()
1317 {
1318     if (m_inputted_keys.size () && m_lookup_table.number_of_candidates ()) {
1319         m_lookup_table.cursor_down ();
1320         refresh_lookup_table (true, false);
1321         refresh_preedit ();
1322         refresh_aux_string ();
1323         return true;
1324     }
1325     return false;
1326 }
1327
1328 bool
1329 TableInstance::lookup_cursor_up_to_longer ()
1330 {
1331     if (m_inputted_keys.size () && m_lookup_table.number_of_candidates ()) {
1332         //Get current lookup table cursor
1333         uint32 cursor = m_lookup_table.get_cursor_pos ();
1334         //Get current phrase length
1335         uint32 curlen = m_factory->m_table.get_phrase_length (m_lookup_table_indexes [cursor]);
1336
1337         do {
1338             m_lookup_table.cursor_up ();
1339             cursor = m_lookup_table.get_cursor_pos ();
1340             if (curlen < m_factory->m_table.get_phrase_length (m_lookup_table_indexes [cursor]))
1341                 break;
1342         } while (cursor);
1343
1344         refresh_lookup_table (true, false);
1345         refresh_preedit ();
1346         refresh_aux_string ();
1347         return true;
1348     }
1349     return false;
1350 }
1351
1352 bool
1353 TableInstance::lookup_cursor_down_to_shorter ()
1354 {
1355     if (m_inputted_keys.size () && m_lookup_table.number_of_candidates ()) {
1356         uint32 entries = m_lookup_table.number_of_candidates ();
1357         //Get current lookup table cursor
1358         uint32 cursor = m_lookup_table.get_cursor_pos ();
1359         //Get current phrase length
1360         uint32 curlen = m_factory->m_table.get_phrase_length (m_lookup_table_indexes [cursor]);
1361
1362         do {
1363             m_lookup_table.cursor_down ();
1364             cursor = m_lookup_table.get_cursor_pos ();
1365             if (curlen > m_factory->m_table.get_phrase_length (m_lookup_table_indexes [cursor]))
1366                 break;
1367         } while (cursor < entries - 1);
1368
1369         refresh_lookup_table (true, false);
1370         refresh_preedit ();
1371         refresh_aux_string ();
1372         return true;
1373     }
1374     return false;
1375 }
1376
1377 bool
1378 TableInstance::lookup_page_up ()
1379 {
1380     if (m_inputted_keys.size () &&
1381          m_lookup_table.get_current_page_size () <
1382          m_lookup_table.number_of_candidates ()) {
1383
1384         m_lookup_table.page_up ();
1385         refresh_lookup_table (true, false);
1386         refresh_preedit ();
1387         refresh_aux_string ();
1388         return true;
1389     }
1390     return false;
1391 }
1392
1393 bool
1394 TableInstance::lookup_page_down ()
1395 {
1396     if (m_inputted_keys.size () && 
1397          m_lookup_table.get_current_page_size () <
1398          m_lookup_table.number_of_candidates ()) {
1399
1400         if (!m_lookup_table.page_down ())
1401             while (m_lookup_table.page_up ()) NULL;
1402
1403         refresh_lookup_table (true, false);
1404         refresh_preedit ();
1405         refresh_aux_string ();
1406         return true;
1407     }
1408     return false;
1409 }
1410
1411 bool
1412 TableInstance::lookup_select (int index)
1413 {
1414     if (m_inputted_keys.size ()) {
1415         if (m_lookup_table.number_of_candidates () == 0)
1416             return true;
1417
1418         index += m_lookup_table.get_current_page_start ();
1419
1420         lookup_to_converted (index);
1421
1422         if (m_converted_strings.size () == m_inputted_keys.size () ||
1423             (m_converted_strings.size () == m_inputted_keys.size () - 1 &&
1424              m_inputted_keys [m_inputing_key].length () == 0))
1425             commit_converted ();
1426
1427         refresh_lookup_table ();
1428         refresh_preedit ();
1429         refresh_aux_string ();
1430
1431         return true;
1432     }
1433
1434     return false;
1435 }
1436
1437 bool
1438 TableInstance::post_process (char key)
1439 {
1440     // Auto select and commit the candidate item when an invalid key is pressed.
1441     if (m_factory->m_table.is_auto_commit () &&
1442         m_converted_strings.size () == m_inputing_key &&
1443         m_inputing_key + 1 == m_inputted_keys.size () &&
1444         m_inputing_caret == m_inputted_keys [m_inputing_key].length () &&
1445         m_lookup_table.number_of_candidates ()) {
1446
1447         lookup_to_converted (m_lookup_table.get_cursor_pos ());
1448         commit_converted ();
1449
1450         refresh_lookup_table ();
1451         refresh_preedit ();
1452         refresh_aux_string ();
1453     }
1454
1455     if (m_inputted_keys.size ()) return true;
1456
1457     if ((ispunct (key) && m_full_width_punct [m_forward?1:0]) ||
1458         ((isalnum (key) || key == 0x20) && m_full_width_letter [m_forward?1:0])) {
1459         WideString str;
1460         if (key == '.')
1461             str.push_back (0x3002);
1462         else if (key == '\\')
1463             str.push_back (0x3001);
1464         else if (key == '^') {
1465             str.push_back (0x2026);
1466             str.push_back (0x2026);
1467         } else if (key == '\"') {
1468             if (!m_double_quotation_state)
1469                 str.push_back (0x201c);
1470             else
1471                 str.push_back (0x201d);
1472             m_double_quotation_state = !m_double_quotation_state;
1473         } else if (key == '\'') {
1474             if (!m_single_quotation_state)
1475                 str.push_back (0x2018);
1476             else
1477                 str.push_back (0x2019);
1478             m_single_quotation_state = !m_single_quotation_state;
1479         } else {
1480             str.push_back (scim_wchar_to_full_width (key));
1481         }
1482
1483         commit_string (str);
1484
1485         m_last_committed = WideString ();
1486
1487         return true;
1488     }
1489
1490     return false;
1491 }
1492
1493 bool
1494 TableInstance::delete_phrase ()
1495 {
1496     if (m_lookup_table.number_of_candidates ()) {
1497         int pos       = m_lookup_table.get_cursor_pos ();
1498         uint32 offset = m_lookup_table_indexes [pos];
1499
1500         if (m_factory->m_table.delete_phrase (offset)) {
1501             m_factory->refresh (true);
1502             refresh_lookup_table ();
1503         }
1504         return true;
1505     }
1506     return false;
1507 }
1508
1509 void
1510 TableInstance::lookup_to_converted (int index)
1511 {
1512     if (index < 0 || index >= m_lookup_table.number_of_candidates ())
1513         return;
1514
1515     uint32 offset  = m_lookup_table_indexes [index];
1516     WideString str = m_factory->m_table.get_phrase (offset);
1517
1518     m_converted_strings.push_back (str);
1519     m_converted_indexes.push_back (offset);
1520
1521     if (m_inputing_key < m_converted_strings.size ()) {
1522         m_inputing_key = m_converted_strings.size ();
1523         if (m_inputing_key >= m_inputted_keys.size ())
1524             m_inputted_keys.push_back (String (""));
1525         m_inputing_caret = 0;
1526     }
1527 }
1528
1529 void
1530 TableInstance::commit_converted ()
1531 {
1532     if (m_converted_strings.size ()) {
1533         WideString res;
1534
1535         for (size_t i=0; i<m_converted_strings.size (); ++i)
1536             res += m_converted_strings [i];
1537
1538         // Hide preedit string before committing string,
1539         // to prevent some buggy clients from inserting the string into wrong place.
1540         // Preedit string will be refreshed after return from commit_converted ().
1541         hide_preedit_string ();
1542         commit_string (res);
1543
1544         if (utf8_wcstombs (m_last_committed).length () >= 255)
1545             m_last_committed = WideString ();
1546
1547         m_last_committed += res;
1548
1549         m_inputted_keys.erase (m_inputted_keys.begin (), m_inputted_keys.begin () + m_converted_strings.size ());
1550         m_inputing_key -= m_converted_strings.size ();
1551
1552         if (m_inputted_keys.size () == 1 && m_inputted_keys [0].length () == 0) {
1553             m_inputted_keys.clear ();
1554             m_inputing_key = 0;
1555             m_inputing_caret = 0;
1556         }
1557
1558         if (m_inputted_keys.size ()) {
1559             m_inputing_key = m_inputted_keys.size () - 1;
1560             m_inputing_caret = m_inputted_keys [m_inputing_key].length ();
1561         }
1562
1563         if (m_factory->m_table.is_dynamic_adjust ()){
1564             for (size_t i = 0; i < m_converted_indexes.size (); ++i) {
1565                 uint32 freq = m_factory->m_table.get_phrase_frequency (m_converted_indexes [i]);
1566                 if (freq < SCIM_GT_MAX_PHRASE_FREQ) {
1567                     uint32 delta = ((SCIM_GT_MAX_PHRASE_FREQ - freq) >> SCIM_GT_PHRASE_FREQ_DELTA_SHIFT);
1568                     freq += (delta ? delta : 1);
1569                     m_factory->m_table.set_phrase_frequency (m_converted_indexes [i], freq);
1570                 }
1571             }
1572             m_factory->refresh (false);
1573         }
1574
1575         m_converted_strings.clear ();
1576         m_converted_indexes.clear ();
1577     }
1578 }
1579
1580 void
1581 TableInstance::refresh_preedit ()
1582 {
1583     WideString preedit_string;
1584     int start = 0;
1585     int length = 0;
1586     int caret = 0;
1587     int end = 0;
1588     size_t i;
1589
1590     if (m_inputted_keys.size () == 0) {
1591         hide_preedit_string ();
1592         return;
1593     }
1594  
1595     for (i = 0; i<m_converted_strings.size (); ++i)
1596         preedit_string += m_converted_strings [i];
1597
1598     int inputted_keys = m_inputted_keys.size ();
1599
1600     if (m_inputted_keys [inputted_keys - 1].length () == 0)
1601         --inputted_keys;
1602
1603     // Fill the preedit string.
1604     if (m_factory->m_table.is_auto_fill () &&
1605         m_converted_strings.size () == inputted_keys - 1 &&
1606         m_inputing_caret == m_inputted_keys [m_inputing_key].length () &&
1607         m_lookup_table.number_of_candidates ()) {
1608
1609         uint32 offset = m_lookup_table_indexes [m_lookup_table.get_cursor_pos ()];
1610         WideString str = m_factory->m_table.get_phrase (offset);
1611
1612         start = preedit_string.length ();
1613         preedit_string += str;
1614         length = str.length ();
1615         caret = preedit_string.length ();
1616     } else {
1617         i = m_converted_strings.size ();
1618         caret = start = preedit_string.length ();
1619
1620         for (i = m_converted_strings.size (); i < inputted_keys; ++i) {
1621             if (m_factory->m_table.is_show_key_prompt ()) {
1622                 preedit_string += m_factory->m_table.get_key_prompt (m_inputted_keys [i]);
1623                 if (i == m_inputing_key)
1624                     caret += (m_factory->m_table.get_key_prompt (m_inputted_keys [i].substr (0, m_inputing_caret))).length ();
1625             } else {
1626                 preedit_string += utf8_mbstowcs (m_inputted_keys [i]);
1627                 if (i == m_inputing_key)
1628                     caret += m_inputing_caret;
1629             }
1630
1631             if (i == m_converted_strings.size ())
1632                 length = preedit_string.length () - start;
1633
1634             if (i < inputted_keys - 1)
1635                 preedit_string.push_back ((ucs4_t)' ');
1636
1637             if (i < m_inputing_key)
1638                 caret = preedit_string.length ();
1639         }
1640     }
1641     m_preedit_string = preedit_string;
1642     if (preedit_string.length () == 0) {
1643         hide_preedit_string ();
1644         return;
1645     }
1646
1647     AttributeList attrs;
1648
1649     if (length)
1650     {
1651         if (start)
1652             attrs.push_back (Attribute(0, start, SCIM_ATTR_DECORATE, SCIM_ATTR_DECORATE_UNDERLINE));
1653         attrs.push_back (Attribute(start, length, SCIM_ATTR_DECORATE, SCIM_ATTR_DECORATE_HIGHLIGHT));
1654         end = start+length;
1655         if (end < preedit_string.length())
1656             attrs.push_back (Attribute(end, preedit_string.length()- end, SCIM_ATTR_DECORATE, SCIM_ATTR_DECORATE_UNDERLINE));
1657     }
1658     update_preedit_string (preedit_string, attrs);
1659     update_preedit_caret (caret);
1660
1661     show_preedit_string ();
1662 }
1663
1664 void
1665 TableInstance::refresh_lookup_table (bool show, bool refresh)
1666 {
1667     m_lookup_table.set_page_size (m_factory->m_table.get_select_keys ().size ());
1668
1669     if (refresh) {
1670         std::vector <uint32> phrases;
1671         WideString str;
1672
1673         m_lookup_table.clear ();
1674         m_lookup_table_indexes.clear ();
1675
1676         if (m_converted_strings.size () < m_inputted_keys.size ()) {
1677
1678             String key = m_inputted_keys [m_converted_strings.size ()];
1679
1680             if (key.length () &&
1681                 m_factory->m_table.find (phrases,
1682                                          key,
1683                                          m_factory->m_user_phrase_first,
1684                                          m_factory->m_long_phrase_first)) {
1685
1686                 bool show_full_hint = m_factory->m_table.is_wildcard_key (key);
1687                 std::set<WideString> candiadtes;
1688                 for (size_t i = 0; i < phrases.size (); ++i) {
1689                     str = m_factory->m_table.get_phrase (phrases [i]);
1690
1691                     if (m_iconv.test_convert (str)) {
1692                         if (m_factory->m_show_key_hint) {
1693                             String hint = m_factory->m_table.get_key (phrases [i]);
1694
1695                             if (show_full_hint)
1696                                 str += utf8_mbstowcs (hint);
1697                             else if (hint.length () > key.length ())
1698                                 str += utf8_mbstowcs (hint.substr (key.length ()));
1699                         }
1700 #if 0
1701                         AttributeList attrs;
1702
1703                         if (m_factory->m_table.is_user_phrase (phrases [i]))
1704                             attrs.push_back (Attribute (0, str.length (), SCIM_ATTR_FOREGROUND, SCIM_RGB_COLOR(32, 32, 255)));
1705
1706                         m_lookup_table.append_candidate (str, attrs);
1707 #endif
1708                         if (candiadtes.find (str) != candiadtes.end())
1709                             continue;
1710                         candiadtes.insert (str);
1711                         m_lookup_table.append_candidate (str);
1712                         m_lookup_table_indexes.push_back (phrases [i]);
1713                     }
1714                 }
1715             }
1716         }
1717     }
1718     if (show) {
1719         if (m_lookup_table.number_of_candidates () &&
1720             (m_factory->m_table.is_always_show_lookup () ||
1721              m_inputing_key < m_inputted_keys.size () - 1 ||
1722              m_inputing_caret < m_inputted_keys [m_inputing_key].length () ||
1723              m_converted_strings.size () < m_inputted_keys.size () - 1)) {
1724             update_lookup_table (m_lookup_table);
1725         } else {        
1726             if (m_inputted_keys.size () &&
1727                 (m_inputing_caret || m_lookup_table.number_of_candidates ()))
1728             {
1729                 m_lookup_table.clear ();
1730                 update_lookup_table (m_lookup_table);
1731             }
1732             else
1733                 update_lookup_table (m_common_lookup_table);
1734         }
1735     }
1736 }
1737
1738 void
1739 TableInstance::refresh_aux_string ()
1740 {
1741     WideString    prompt;
1742     AttributeList attributes;
1743
1744     if (m_add_phrase_mode == 1) {
1745         prompt = utf8_mbstowcs (_("Input a key string for phrase: ")) + m_last_committed;
1746     } else if (m_add_phrase_mode == 2) {
1747         prompt = utf8_mbstowcs (_("Success."));
1748         attributes.push_back (Attribute (0, prompt.length (), SCIM_ATTR_FOREGROUND, SCIM_RGB_COLOR(32, 255, 32)));
1749     } else if (m_add_phrase_mode == 3) {
1750         prompt = utf8_mbstowcs (_("Failed."));
1751         attributes.push_back (Attribute (0, prompt.length (), SCIM_ATTR_FOREGROUND, SCIM_RGB_COLOR(255, 32, 32)));
1752     } else {
1753         if (!m_factory->m_show_prompt || m_inputted_keys.size () == 0) {
1754             hide_aux_string ();
1755             return;
1756         }
1757
1758         if (!m_factory->m_table.is_show_key_prompt ())
1759             prompt = m_factory->m_table.get_key_prompt (m_inputted_keys [m_inputing_key]);
1760
1761         if (m_lookup_table.number_of_candidates () && ! m_factory->m_show_key_hint) {
1762             prompt += utf8_mbstowcs (" <");
1763             unsigned int att_start = prompt.length ();
1764
1765             if (m_factory->m_table.is_show_key_prompt ())
1766                 prompt += m_factory->m_table.get_key_prompt (m_factory->m_table.get_key (
1767                             m_lookup_table_indexes [m_lookup_table.get_cursor_pos ()]));
1768             else
1769                 prompt += utf8_mbstowcs (m_factory->m_table.get_key (
1770                             m_lookup_table_indexes [m_lookup_table.get_cursor_pos ()]));
1771
1772             unsigned int att_length = prompt.length () - att_start;
1773             prompt += utf8_mbstowcs (">");
1774             attributes.push_back (Attribute (att_start, att_length, SCIM_ATTR_FOREGROUND, SCIM_RGB_COLOR(128, 128, 255)));
1775         }
1776     }
1777
1778     if (prompt.length ()) {
1779         update_aux_string (prompt, attributes);
1780         show_aux_string ();
1781     } else {
1782         hide_aux_string ();
1783     }
1784 }
1785
1786 bool
1787 TableInstance::match_key_event (const std::vector<KeyEvent>& keyvec,
1788                                       const KeyEvent& key)
1789 {
1790     std::vector<KeyEvent>::const_iterator kit; 
1791
1792     for (kit = keyvec.begin (); kit != keyvec.end (); ++kit) {
1793         if (key.code == kit->code && key.mask == kit->mask)
1794             if (!(key.mask & SCIM_KEY_ReleaseMask) || m_prev_key.code == key.code)
1795                 return true;
1796     }
1797     return false;
1798 }
1799 /*
1800 vi:ts=4:nowrap:ai:expandtab
1801 */