a93cd12cdd7213c20611be60f96209e5cf48ee21
[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_lookup_table_always_on (false),
465       m_inputing_caret (0),
466       m_inputing_key (0),
467       m_iconv (encoding)
468 {
469     m_full_width_letter [0] = m_factory->m_table.is_def_full_width_letter ();
470     m_full_width_letter [1] = false;
471
472     m_full_width_punct [0] = m_factory->m_table.is_def_full_width_punct ();
473     m_full_width_punct [1] = false;
474
475     char buf [2] = { 0, 0 };
476
477     std::vector <KeyEvent>   keys = m_factory->m_table.get_select_keys ();
478     std::vector <WideString> labels;
479
480     for (size_t i = 0; i < keys.size (); ++i) {
481         buf [0] = keys [i].get_ascii_code ();
482         labels.push_back (utf8_mbstowcs (buf));
483     }
484
485     m_lookup_table.set_candidate_labels (labels);
486     m_lookup_table.set_page_size        (keys.size ());
487     m_lookup_table.show_cursor ();
488
489     AttributeList attrs;
490     for (int i = 0;i < sizeof(g_common_symbol)/sizeof(char);i++) {
491         char _str[2]={g_common_symbol[i],0};
492         m_common_lookup_table.append_candidate (utf8_mbstowcs ((const char*)_str),attrs);
493     }
494 }
495
496 TableInstance::~TableInstance ()
497 {
498 }
499
500 bool
501 TableInstance::process_key_event (const KeyEvent& rawkey)
502 {
503     KeyEvent key = rawkey.map_to_layout (m_factory->m_table.get_keyboard_layout ());
504
505     bool ret = false;
506
507     if (!m_focused) return false;
508
509     // capture the mode switch key events
510     if (match_key_event (m_factory->m_mode_switch_keys, key) ||
511         match_key_event (m_factory->m_table.get_mode_switch_keys (), key)) {
512         m_forward = !m_forward;
513         refresh_status_property ();
514         refresh_letter_property ();
515         refresh_punct_property ();
516         reset ();
517         ret = true;
518     }
519
520     // toggle full width punctuation mode
521     else if (match_key_event (m_factory->m_full_width_punct_keys, key) ||
522              match_key_event (m_factory->m_table.get_full_width_punct_keys (), key)) {
523         trigger_property (SCIM_PROP_PUNCT);
524         ret = true;
525     }
526
527     // toggle full width letter mode
528     else if (match_key_event (m_factory->m_full_width_letter_keys, key) ||
529              match_key_event (m_factory->m_table.get_full_width_letter_keys (), key)) {
530         trigger_property (SCIM_PROP_LETTER);
531         ret = true;
532     }
533
534     // discard the key release event.
535     else if (key.is_key_release ()) {
536         ret = true;
537     }
538
539     // process the key press event, if not in forward mode.
540     else if (!m_forward) {
541         // First reset add phrase mode.
542         if (m_add_phrase_mode > 1) {
543             m_add_phrase_mode = 0;
544             refresh_aux_string ();
545         }
546
547         //reset key
548         if (key.code == SCIM_KEY_Escape && key.mask == 0) {
549             if (m_inputted_keys.size () == 0 && m_add_phrase_mode != 1)
550                 ret = false;
551             else {
552                 reset ();
553                 ret = true;
554             }
555         }
556
557         //caret left
558         else if (key.code == SCIM_KEY_Left && key.mask == 0)
559             ret = caret_left ();
560
561         //caret right
562         else if (key.code == SCIM_KEY_Right && key.mask == 0)
563             ret = caret_right ();
564
565         //caret home 
566         else if (key.code == SCIM_KEY_Home && key.mask == 0)
567             ret = caret_home ();
568
569         //caret end
570         else if (key.code == SCIM_KEY_End && key.mask == 0)
571             ret = caret_end ();
572
573         //lookup table cursor up
574         else if (key.code == SCIM_KEY_Up && key.mask == 0)
575             ret = lookup_cursor_up ();
576
577         //lookup table cursor down
578         else if (key.code == SCIM_KEY_Down && key.mask == 0)
579             ret = lookup_cursor_down ();
580
581         //lookup table cursor up to longer phrase
582         else if (key.code == SCIM_KEY_Up && key.mask == SCIM_KEY_ControlMask &&
583                  m_factory->m_long_phrase_first && !m_factory->m_user_phrase_first)
584             ret = lookup_cursor_up_to_longer ();
585
586         //lookup table cursor down
587         else if (key.code == SCIM_KEY_Down && key.mask == SCIM_KEY_ControlMask &&
588                  m_factory->m_long_phrase_first && !m_factory->m_user_phrase_first)
589             ret = lookup_cursor_down_to_shorter ();
590
591         //backspace key
592         else if (key.code == SCIM_KEY_BackSpace && key.mask == 0)
593             ret = erase ();
594
595         //delete key
596         else if (key.code == SCIM_KEY_Delete && key.mask == 0)
597             ret = erase (false);
598
599         //add new phrase
600         else if (!m_inputted_keys.size () && m_last_committed.length () &&
601             match_key_event (m_factory->m_add_phrase_keys, key)) {
602             m_add_phrase_mode = 1;
603             refresh_aux_string ();
604             ret = true;
605         }
606
607         //delete phrase
608         else if (match_key_event (m_factory->m_del_phrase_keys, key)) {
609             if (delete_phrase ())
610                 ret = true;
611         }
612
613         // other situation
614         else {
615             ret = false;
616
617             //select lookup table
618             int pos = m_factory->m_table.get_select_key_pos (key);
619
620             // If there is a new empty key (a split char was inserted),
621             // then try to select lookup table first.
622             // Otherwise try to insert the char first.
623             if (m_inputted_keys.size () && !m_inputted_keys [m_inputing_key].length ()) {
624                 if (pos >= 0 && pos < m_lookup_table.get_current_page_size ())
625                     ret = lookup_select (pos);
626
627                 // insert char
628                 if (!ret && (key.mask & (~ (SCIM_KEY_ShiftMask + SCIM_KEY_CapsLockMask))) == 0)
629                     ret = insert (key.get_ascii_code ());
630             } else {
631                 // insert char
632                 if ((key.mask & (~ (SCIM_KEY_ShiftMask + SCIM_KEY_CapsLockMask))) == 0 &&
633                     test_insert (key.get_ascii_code ()))
634                     ret = insert (key.get_ascii_code ());
635
636                 if (!ret && pos >= 0 && pos < m_lookup_table.get_current_page_size ())
637                     ret = lookup_select (pos);
638
639                 // insert char finally.
640                 if (!ret && (key.mask & (~ (SCIM_KEY_ShiftMask + SCIM_KEY_CapsLockMask))) == 0)
641                     ret = insert (key.get_ascii_code ());
642             }
643
644             //lookup table page up
645             if (!ret && match_key_event (m_factory->m_table.get_page_up_keys (), key))
646                 ret = lookup_page_up ();
647
648             //lookup table page down
649             if (!ret && match_key_event (m_factory->m_table.get_page_down_keys (), key))
650                 ret = lookup_page_down ();
651
652             //space hit
653             if (!ret && match_key_event (m_factory->m_table.get_commit_keys (), key))
654                 ret = space_hit ();
655
656             //enter hit
657             if (!ret && match_key_event (m_factory->m_table.get_forward_keys (), key))
658                 ret = enter_hit ();
659         }
660     }
661
662     if (!ret && (key.mask & (~ (SCIM_KEY_ShiftMask + SCIM_KEY_CapsLockMask))) == 0 &&
663         key.get_ascii_code ())
664         ret = post_process (key.get_ascii_code ());
665
666     m_prev_key = key;
667
668     return ret;
669 }
670
671 void
672 TableInstance::select_candidate (unsigned int item)
673 {
674
675     if (m_inputted_keys.size () == 0
676         && m_lookup_table.number_of_candidates() == 0
677         && item < sizeof(g_common_symbol)/sizeof(char)) {
678         char _str[2]={g_common_symbol[item],0};
679         commit_string(utf8_mbstowcs(_str));
680         return;
681     }
682     lookup_select (item);
683 }
684
685 void
686 TableInstance::update_lookup_table_page_size (unsigned int page_size)
687 {
688     if (page_size > 0)
689         m_lookup_table.set_page_size (page_size);
690 }
691
692 void
693 TableInstance::lookup_table_page_up ()
694 {
695     lookup_page_up ();
696 }
697
698 void
699 TableInstance::lookup_table_page_down ()
700 {
701     lookup_page_down ();
702 }
703
704 void
705 TableInstance::move_preedit_caret (unsigned int pos)
706 {
707     uint32 len = 0;
708     size_t i;
709
710     for (i=0; i<m_converted_strings.size (); ++i) {
711         if (pos >= len && pos < len + m_converted_strings [i].length ()) {
712             m_inputing_key = i;
713             m_inputing_caret = m_inputted_keys [i].length ();
714
715             m_converted_strings.erase (m_converted_strings.begin () + i, m_converted_strings.end ());
716             m_converted_indexes.erase (m_converted_indexes.begin () + i, m_converted_indexes.end ());
717
718             refresh_lookup_table ();
719             refresh_preedit ();
720             refresh_aux_string ();
721
722             return;
723         }
724         len += m_converted_strings [i].length ();
725     }
726
727     if (m_factory->m_table.is_auto_fill () &&
728         m_inputing_key == m_inputted_keys.size () - 1 &&
729         m_inputing_caret == m_inputted_keys [m_inputing_key].length () &&
730         m_converted_strings.size () == m_inputing_key &&
731         m_lookup_table.number_of_candidates ()) {
732
733         uint32 offset = m_lookup_table_indexes [m_lookup_table.get_cursor_pos ()];
734         size_t phlen  = m_factory->m_table.get_phrase_length (offset);
735
736         if (pos >= len && pos < len + phlen) {
737             m_inputing_caret = 0;
738             refresh_lookup_table (true, false);
739             refresh_preedit ();
740             return;
741         }
742     } else {
743         if (m_converted_strings.size ()) {
744             ++len;
745             if (pos < len) ++pos;
746         }
747
748         for (i=m_converted_strings.size (); i<m_inputted_keys.size (); ++i) {
749             if (pos >= len && pos <= len + m_inputted_keys [i].length ()) {
750                 m_inputing_key = i;
751                 m_inputing_caret = pos - len;
752
753                 refresh_lookup_table (true, false);
754                 refresh_preedit ();
755                 refresh_aux_string ();
756                 return;
757             }
758
759             len += (m_inputted_keys [i].length () +1);
760         }
761     }
762 }
763
764 void
765 TableInstance::reset ()
766 {
767     if (m_inputted_keys.size () && m_preedit_string.size()) {
768         commit_string (m_preedit_string);
769     }
770     m_double_quotation_state = false;
771     m_single_quotation_state = false;
772
773     m_lookup_table.clear ();
774
775     std::vector<String> ().swap (m_inputted_keys);
776
777     std::vector<WideString> ().swap (m_converted_strings);
778
779     std::vector<uint32> ().swap (m_converted_indexes);
780
781     std::vector<uint32> ().swap (m_lookup_table_indexes);
782
783     m_add_phrase_mode = 0;
784
785     m_last_committed = WideString ();
786
787     m_inputing_caret = 0;
788     m_inputing_key = 0;
789
790     m_iconv.set_encoding (get_encoding ());
791     if (m_lookup_table_always_on) {
792         refresh_lookup_table (true, false);
793     } else {
794         hide_lookup_table();
795     }
796     hide_preedit_string ();
797     hide_aux_string ();
798 }
799
800 void
801 TableInstance::focus_in ()
802 {
803     m_focused = true;
804
805     if (m_add_phrase_mode != 1) {
806         m_last_committed = WideString ();
807         m_add_phrase_mode = 0;
808     }
809
810     //refresh_lookup_table (true, false);
811     refresh_preedit ();
812     refresh_aux_string ();
813     initialize_properties ();
814 }
815
816 void
817 TableInstance::focus_out ()
818 {
819     m_focused = false;
820     reset ();
821 }
822
823 void
824 TableInstance::trigger_property (const String &property)
825 {
826     if (property == SCIM_PROP_STATUS) {
827         m_forward = !m_forward;
828         refresh_status_property ();
829         refresh_letter_property ();
830         refresh_punct_property ();
831         reset ();
832     } else if (property == SCIM_PROP_LETTER && m_factory->m_table.is_use_full_width_letter ()) {
833         m_full_width_letter [m_forward?1:0] =
834             !m_full_width_letter [m_forward?1:0];
835         refresh_letter_property ();
836     } else if (property == SCIM_PROP_PUNCT && m_factory->m_table.is_use_full_width_punct ()) {
837         m_full_width_punct [m_forward?1:0] = 
838             !m_full_width_punct [m_forward?1:0];
839         refresh_punct_property ();
840     }
841 }
842  
843 void
844 TableInstance::set_layout (unsigned int layout)
845 {
846     switch (layout)
847     {
848         case ECORE_IMF_INPUT_PANEL_LAYOUT_NORMAL:
849         case ECORE_IMF_INPUT_PANEL_LAYOUT_NUMBER:
850         case ECORE_IMF_INPUT_PANEL_LAYOUT_EMAIL:
851         case ECORE_IMF_INPUT_PANEL_LAYOUT_URL:
852             refresh_lookup_table (true, false);
853             m_lookup_table_always_on = true;
854             break;
855         case ECORE_IMF_INPUT_PANEL_LAYOUT_PHONENUMBER:
856         case ECORE_IMF_INPUT_PANEL_LAYOUT_IP:
857         case ECORE_IMF_INPUT_PANEL_LAYOUT_MONTH:
858         case ECORE_IMF_INPUT_PANEL_LAYOUT_NUMBERONLY:
859             hide_lookup_table ();
860             m_lookup_table_always_on = false;
861             break;
862     }
863 }
864
865 void
866 TableInstance::initialize_properties ()
867 {
868     PropertyList proplist;
869
870     proplist.push_back (m_factory->m_status_property);
871
872     if (m_factory->m_table.is_use_full_width_letter ())
873         proplist.push_back (m_factory->m_letter_property);
874
875     if (m_factory->m_table.is_use_full_width_punct ())
876         proplist.push_back (m_factory->m_punct_property);
877
878     register_properties (proplist);
879
880     refresh_status_property ();
881     refresh_letter_property ();
882     refresh_punct_property ();
883 }
884
885 void
886 TableInstance::refresh_status_property ()
887 {
888     if (m_focused) {
889         if (m_forward)
890             m_factory->m_status_property.set_label (_("En"));
891         else
892             m_factory->m_status_property.set_label (utf8_wcstombs (m_factory->m_table.get_status_prompt ()));
893
894         update_property (m_factory->m_status_property);
895     }
896 }
897
898 void
899 TableInstance::refresh_letter_property ()
900 {
901     if (m_focused && m_factory->m_table.is_use_full_width_letter ()) {
902         m_factory->m_letter_property.set_icon (
903             m_full_width_letter [m_forward?1:0] ? SCIM_FULL_LETTER_ICON : SCIM_HALF_LETTER_ICON);
904         update_property (m_factory->m_letter_property);
905     }
906 }
907
908 void
909 TableInstance::refresh_punct_property ()
910 {
911     if (m_focused && m_factory->m_table.is_use_full_width_punct ()) {
912         m_factory->m_punct_property.set_icon (
913             m_full_width_punct [m_forward?1:0] ? SCIM_FULL_PUNCT_ICON : SCIM_HALF_PUNCT_ICON);
914         update_property (m_factory->m_punct_property);
915     }
916 }
917
918 bool
919 TableInstance::caret_left ()
920 {
921     if (m_inputted_keys.size ()) {
922         if (m_inputing_caret > 0) {
923             --m_inputing_caret;
924             refresh_lookup_table (true, false);
925         } else if (m_inputing_key > 0) {
926             --m_inputing_key;
927             m_inputing_caret = m_inputted_keys [m_inputing_key].length ();
928
929             if (m_converted_strings.size () > m_inputing_key) {
930                 m_converted_strings.pop_back ();
931                 m_converted_indexes.pop_back ();
932                 refresh_lookup_table ();
933             } else {
934                 refresh_lookup_table (true, false);
935             }
936         } else {
937             return caret_end ();
938         }
939
940         refresh_preedit ();
941         refresh_aux_string ();
942         return true;
943     }
944     return false;
945 }
946
947 bool
948 TableInstance::caret_right ()
949 {
950     if (m_inputted_keys.size ()) {
951         if (m_inputing_caret < m_inputted_keys [m_inputing_key].size ()) {
952             ++m_inputing_caret;
953         } else if (m_inputing_key < m_inputted_keys.size () - 1) {
954             ++m_inputing_key;
955             m_inputing_caret = 0;
956         } else {
957             return caret_home ();
958         }
959         refresh_lookup_table (true, false);
960         refresh_preedit ();
961         refresh_aux_string ();
962         return true;
963     }
964     return false;
965 }
966
967 bool
968 TableInstance::caret_home ()
969 {
970     if (m_inputted_keys.size ()) {
971         m_inputing_key = 0;
972         m_inputing_caret = 0;
973
974         if (m_converted_strings.size ()) {
975             m_converted_strings.clear ();
976             m_converted_indexes.clear ();
977             refresh_lookup_table ();
978         } else {
979             refresh_lookup_table (true, false);
980         }
981
982         refresh_preedit ();
983         refresh_aux_string ();
984         return true;
985     }
986     return false;
987 }
988
989 bool
990 TableInstance::caret_end ()
991 {
992     if (m_inputted_keys.size ()) {
993         m_inputing_key = m_inputted_keys.size () - 1;
994         m_inputing_caret = m_inputted_keys [m_inputing_key].length ();
995
996         refresh_lookup_table (true, false);
997         refresh_preedit ();
998         refresh_aux_string ();
999         return true;
1000     }
1001     return false;
1002 }
1003
1004 bool
1005 TableInstance::test_insert (char key)
1006 {
1007     if (m_factory->m_table.is_valid_char (key)) {
1008         String newkey;
1009         if (m_inputted_keys.size ()) {
1010             newkey = m_inputted_keys [m_inputing_key];
1011             newkey.insert (newkey.begin () + m_inputing_caret, key);
1012         } else {
1013             newkey.push_back (key);
1014         }
1015
1016         return m_factory->m_table.is_defined_key (newkey);
1017     }
1018     return false;
1019 }
1020
1021 bool
1022 TableInstance::insert (char ch)
1023 {
1024     if (!ch) return false;
1025
1026     String newkey;
1027     uint32 old_inputing_key = m_inputing_key;
1028     bool insert_ok = false;
1029
1030     if (m_inputted_keys.size () - m_converted_strings.size () >
1031         SCIM_TABLE_MAX_INPUTTED_KEYS)
1032         return false;
1033
1034     // If current inputing key is empty, then the last inputing key is the previous key.
1035     if (m_inputted_keys.size () && m_inputing_key && !m_inputted_keys [m_inputing_key].length ())
1036         -- old_inputing_key;
1037
1038     if (m_factory->m_table.is_split_char (ch)) {
1039         // split char is invalid during add phrase mode.
1040         if (m_add_phrase_mode == 1)
1041             return true;
1042         else if (m_inputted_keys.size () == 0)
1043             return false;
1044         else if (m_inputing_key == m_inputted_keys.size () -1 &&
1045                 m_inputted_keys [m_inputing_key].length () &&
1046                 m_inputing_caret == m_inputted_keys [m_inputing_key].length ()) {
1047             ++m_inputing_key;
1048             m_inputing_caret = 0;
1049             m_inputted_keys.insert (m_inputted_keys.begin () + m_inputing_key, String ());
1050         } else {
1051             return false;
1052         }
1053
1054         insert_ok = true;
1055     } else if (m_factory->m_table.is_valid_char (ch)) {
1056         if (m_add_phrase_mode == 1) {
1057             m_inputing_key = 0;
1058             if (!m_inputted_keys.size ()) {
1059                 m_inputted_keys.push_back (String (""));
1060                 m_inputing_caret = 0;
1061             } else if (m_inputted_keys [0].length () >= m_factory->m_table.get_max_key_length ()) {
1062                 return true;
1063             }
1064
1065             m_inputted_keys [0].insert (m_inputted_keys [0].begin () + m_inputing_caret, ch);
1066             ++m_inputing_caret;
1067             insert_ok = true;
1068         } else if (m_inputted_keys.size ()) {
1069             newkey = m_inputted_keys [m_inputing_key];
1070             newkey.insert (newkey.begin () + m_inputing_caret, ch);
1071
1072             if ((m_factory->m_table.is_auto_split () == false ||
1073                  m_factory->m_table.is_defined_key (newkey)) &&
1074                 newkey.length () <= m_factory->m_table.get_max_key_length ()) {
1075                 m_inputted_keys [m_inputing_key] = newkey;
1076                 ++m_inputing_caret;
1077                 insert_ok = true;
1078             } else if (m_inputing_caret == m_inputted_keys [m_inputing_key].length ()) {
1079                 newkey = String ();
1080                 newkey.push_back (ch);
1081
1082                 if (m_factory->m_table.is_defined_key (newkey)) {
1083                     ++m_inputing_key;
1084                     m_inputing_caret = 1;
1085                     m_inputted_keys.insert (m_inputted_keys.begin () + m_inputing_key, newkey);
1086                     insert_ok = true;
1087                 }
1088             } else if (m_inputing_caret == 0) {
1089                 newkey = String ();
1090                 newkey.push_back (ch);
1091
1092                 if (m_factory->m_table.is_defined_key (newkey)) {
1093                     m_inputing_caret = 1;
1094                     m_inputted_keys.insert (m_inputted_keys.begin () + m_inputing_key, newkey);
1095                     insert_ok = true;
1096                 }
1097             }
1098         } else if (!m_factory->m_table.is_multi_wildcard_char (ch)) {
1099             newkey = String ();
1100             newkey.push_back (ch);
1101
1102             if (m_factory->m_table.is_defined_key (newkey)) {
1103                 m_inputted_keys.push_back (newkey);
1104                 m_inputing_key = 0;
1105                 m_inputing_caret = 1; 
1106                 insert_ok = true;
1107             }
1108         }
1109     }
1110
1111     if (insert_ok) {
1112         //Do some extra work in normal mode.
1113         if (m_add_phrase_mode != 1) {
1114             //auto select
1115             if (m_factory->m_table.is_auto_select () &&
1116                 m_converted_strings.size () == old_inputing_key &&
1117                 old_inputing_key + 1 == m_inputing_key &&
1118                 m_lookup_table.number_of_candidates () &&
1119                 m_inputted_keys [m_inputing_key].length ()) {
1120                 lookup_to_converted (m_lookup_table.get_cursor_pos ());
1121             }
1122
1123             //discard invalid key
1124             if (m_factory->m_table.is_discard_invalid_key () &&
1125                 m_converted_strings.size () == old_inputing_key &&
1126                 old_inputing_key + 1 == m_inputing_key &&
1127                 m_lookup_table.number_of_candidates () == 0 &&
1128                 m_inputted_keys [m_inputing_key].length ()) {
1129                 m_inputted_keys.erase (m_inputted_keys.begin () + old_inputing_key);
1130                 m_inputing_key --;
1131             }
1132
1133             if (m_converted_strings.size () == m_inputing_key) {
1134                 refresh_lookup_table (false, true);
1135
1136                 // If auto commit is true, then do auto select when
1137                 // there is only one candidate for this key.
1138                 if (m_lookup_table.number_of_candidates () == 1 &&
1139                     m_factory->m_table.is_auto_commit () &&
1140                     !m_factory->m_table.is_defined_key (
1141                         m_inputted_keys [m_inputing_key],
1142                         GT_SEARCH_ONLY_LONGER)) {
1143                     lookup_to_converted (m_lookup_table.get_cursor_pos ());
1144                     refresh_lookup_table ();
1145                 } else {
1146                     refresh_lookup_table (true, false);
1147                 }
1148             }
1149
1150             if (m_inputted_keys.size () > SCIM_TABLE_MAX_INPUTTED_KEYS ||
1151                 m_factory->m_table.is_auto_commit ())
1152                 commit_converted ();
1153
1154             // If it's a key end char, then append an empty key.
1155             if (m_factory->m_table.is_key_end_char (ch) &&
1156                 m_inputing_key == m_inputted_keys.size () -1 &&
1157                 m_inputted_keys [m_inputing_key].length () &&
1158                 m_inputing_caret == m_inputted_keys [m_inputing_key].length ()) {
1159                 ++m_inputing_key;
1160                 m_inputing_caret = 0;
1161                 m_inputted_keys.insert (m_inputted_keys.begin () + m_inputing_key, String ());
1162             }
1163         }
1164
1165         refresh_preedit ();
1166         refresh_aux_string ();
1167
1168         return true;
1169     }
1170
1171     return false;
1172 }
1173
1174 bool
1175 TableInstance::erase (bool backspace)
1176 {
1177     if (m_inputted_keys.size ()) {
1178         if (backspace && (m_inputing_key > 0 || m_inputing_caret > 0)) {
1179             if (m_inputing_caret > 0) {
1180                 --m_inputing_caret;
1181                 m_inputted_keys [m_inputing_key].erase (m_inputing_caret, 1);
1182             } else {
1183                 if (m_inputted_keys [m_inputing_key].length () == 0)
1184                     m_inputted_keys.erase (m_inputted_keys.begin () + m_inputing_key);
1185
1186                 --m_inputing_key;
1187                 m_inputing_caret = m_inputted_keys [m_inputing_key].length ();
1188
1189                 if (m_inputing_caret > 0) {
1190                     --m_inputing_caret;
1191                     m_inputted_keys [m_inputing_key].erase (m_inputing_caret, 1);
1192                 }
1193             }
1194
1195             if (m_inputted_keys [m_inputing_key].length () == 0) {
1196                 m_inputted_keys.erase (m_inputted_keys.begin () + m_inputing_key);
1197
1198                 if (m_inputing_key > 0) {
1199                     --m_inputing_key;
1200                     m_inputing_caret = m_inputted_keys [m_inputing_key].length ();
1201                 }
1202             }
1203         } else if (!backspace) {
1204             if (m_inputing_caret < m_inputted_keys [m_inputing_key].length ()) {
1205                 m_inputted_keys [m_inputing_key].erase (m_inputing_caret, 1);
1206             }
1207             if (m_inputted_keys [m_inputing_key].length () == 0) {
1208                 m_inputted_keys.erase (m_inputted_keys.begin () + m_inputing_key);
1209             }
1210             if (m_inputing_key == m_inputted_keys.size () && m_inputing_key > 0) {
1211                 --m_inputing_key;
1212                 m_inputing_caret = m_inputted_keys [m_inputing_key].length ();
1213             }
1214         } else {
1215             return true;
1216         }
1217
1218         if (m_inputted_keys.size () == 1 && m_inputted_keys [0].length () == 0) {
1219             m_inputted_keys.clear ();
1220             m_inputing_key = 0;
1221             m_inputing_caret = 0;
1222         }
1223
1224         if (m_add_phrase_mode != 1) {
1225             if (m_converted_strings.size () > m_inputing_key) {
1226                 m_converted_strings.erase (m_converted_strings.begin () + m_inputing_key, m_converted_strings.end ());
1227                 m_converted_indexes.erase (m_converted_indexes.begin () + m_inputing_key, m_converted_indexes.end ());
1228             }
1229             refresh_lookup_table ();
1230         }
1231
1232         refresh_preedit ();
1233         refresh_aux_string ();
1234         return true;
1235     }
1236     return false;
1237 }
1238
1239 bool
1240 TableInstance::space_hit ()
1241 {
1242     if (m_inputted_keys.size ()) {
1243         if (m_add_phrase_mode == 1) {
1244             if (m_factory->m_table.add_phrase (m_inputted_keys [0], m_last_committed)) {
1245                 m_add_phrase_mode = 2;
1246                 m_factory->refresh (true);
1247             } else {
1248                 m_add_phrase_mode = 3;
1249             }
1250
1251             m_inputted_keys.clear ();
1252             m_last_committed = WideString ();
1253             m_inputing_caret = m_inputing_key = 0;
1254         } else {
1255             if (m_converted_strings.size () == 0 && m_lookup_table.number_of_candidates () == 0)
1256                 return true;
1257
1258             if (m_lookup_table.number_of_candidates () && m_converted_strings.size () < m_inputted_keys.size ()) {
1259                 lookup_to_converted (m_lookup_table.get_cursor_pos ());
1260                 refresh_lookup_table ();
1261             }
1262
1263             if (m_converted_strings.size () == m_inputted_keys.size () ||
1264                 (m_converted_strings.size () == m_inputted_keys.size () - 1 &&
1265                  m_inputted_keys [m_inputing_key].length () == 0))
1266                 commit_converted ();
1267         }
1268
1269         refresh_preedit ();
1270         refresh_aux_string ();
1271
1272         return true;
1273     }
1274     return false;
1275 }
1276
1277 bool
1278 TableInstance::enter_hit ()
1279 {
1280     if (m_inputted_keys.size ()) {
1281         if (m_add_phrase_mode == 1) {
1282             if (m_factory->m_table.add_phrase (m_inputted_keys [0], m_last_committed)) {
1283                 m_add_phrase_mode = 2;
1284                 m_factory->refresh (true);
1285             } else {
1286                 m_add_phrase_mode = 3;
1287             }
1288
1289             m_inputted_keys.clear ();
1290             m_last_committed = WideString ();
1291             m_inputing_caret = m_inputing_key = 0;
1292
1293             refresh_preedit ();
1294             refresh_aux_string ();
1295         } else {
1296             reset ();
1297         }
1298         return true;
1299     }
1300
1301     m_last_committed = WideString ();
1302
1303     return false;
1304 }
1305
1306 bool
1307 TableInstance::lookup_cursor_up ()
1308 {
1309     if (m_inputted_keys.size () && m_lookup_table.number_of_candidates ()) {
1310         m_lookup_table.cursor_up ();
1311         refresh_lookup_table (true, false);
1312         refresh_preedit ();
1313         refresh_aux_string ();
1314         return true;
1315     }
1316     return false;
1317 }
1318
1319 bool
1320 TableInstance::lookup_cursor_down ()
1321 {
1322     if (m_inputted_keys.size () && m_lookup_table.number_of_candidates ()) {
1323         m_lookup_table.cursor_down ();
1324         refresh_lookup_table (true, false);
1325         refresh_preedit ();
1326         refresh_aux_string ();
1327         return true;
1328     }
1329     return false;
1330 }
1331
1332 bool
1333 TableInstance::lookup_cursor_up_to_longer ()
1334 {
1335     if (m_inputted_keys.size () && m_lookup_table.number_of_candidates ()) {
1336         //Get current lookup table cursor
1337         uint32 cursor = m_lookup_table.get_cursor_pos ();
1338         //Get current phrase length
1339         uint32 curlen = m_factory->m_table.get_phrase_length (m_lookup_table_indexes [cursor]);
1340
1341         do {
1342             m_lookup_table.cursor_up ();
1343             cursor = m_lookup_table.get_cursor_pos ();
1344             if (curlen < m_factory->m_table.get_phrase_length (m_lookup_table_indexes [cursor]))
1345                 break;
1346         } while (cursor);
1347
1348         refresh_lookup_table (true, false);
1349         refresh_preedit ();
1350         refresh_aux_string ();
1351         return true;
1352     }
1353     return false;
1354 }
1355
1356 bool
1357 TableInstance::lookup_cursor_down_to_shorter ()
1358 {
1359     if (m_inputted_keys.size () && m_lookup_table.number_of_candidates ()) {
1360         uint32 entries = m_lookup_table.number_of_candidates ();
1361         //Get current lookup table cursor
1362         uint32 cursor = m_lookup_table.get_cursor_pos ();
1363         //Get current phrase length
1364         uint32 curlen = m_factory->m_table.get_phrase_length (m_lookup_table_indexes [cursor]);
1365
1366         do {
1367             m_lookup_table.cursor_down ();
1368             cursor = m_lookup_table.get_cursor_pos ();
1369             if (curlen > m_factory->m_table.get_phrase_length (m_lookup_table_indexes [cursor]))
1370                 break;
1371         } while (cursor < entries - 1);
1372
1373         refresh_lookup_table (true, false);
1374         refresh_preedit ();
1375         refresh_aux_string ();
1376         return true;
1377     }
1378     return false;
1379 }
1380
1381 bool
1382 TableInstance::lookup_page_up ()
1383 {
1384     if (m_inputted_keys.size () &&
1385          m_lookup_table.get_current_page_size () <
1386          m_lookup_table.number_of_candidates ()) {
1387
1388         m_lookup_table.page_up ();
1389         refresh_lookup_table (true, false);
1390         refresh_preedit ();
1391         refresh_aux_string ();
1392         return true;
1393     }
1394     return false;
1395 }
1396
1397 bool
1398 TableInstance::lookup_page_down ()
1399 {
1400     if (m_inputted_keys.size () && 
1401          m_lookup_table.get_current_page_size () <
1402          m_lookup_table.number_of_candidates ()) {
1403
1404         if (!m_lookup_table.page_down ())
1405             while (m_lookup_table.page_up ()) NULL;
1406
1407         refresh_lookup_table (true, false);
1408         refresh_preedit ();
1409         refresh_aux_string ();
1410         return true;
1411     }
1412     return false;
1413 }
1414
1415 bool
1416 TableInstance::lookup_select (int index)
1417 {
1418     if (m_inputted_keys.size ()) {
1419         if (m_lookup_table.number_of_candidates () == 0)
1420             return true;
1421
1422         index += m_lookup_table.get_current_page_start ();
1423
1424         lookup_to_converted (index);
1425
1426         if (m_converted_strings.size () == m_inputted_keys.size () ||
1427             (m_converted_strings.size () == m_inputted_keys.size () - 1 &&
1428              m_inputted_keys [m_inputing_key].length () == 0))
1429             commit_converted ();
1430
1431         refresh_lookup_table ();
1432         refresh_preedit ();
1433         refresh_aux_string ();
1434
1435         return true;
1436     }
1437
1438     return false;
1439 }
1440
1441 bool
1442 TableInstance::post_process (char key)
1443 {
1444     // Auto select and commit the candidate item when an invalid key is pressed.
1445     if (m_factory->m_table.is_auto_commit () &&
1446         m_converted_strings.size () == m_inputing_key &&
1447         m_inputing_key + 1 == m_inputted_keys.size () &&
1448         m_inputing_caret == m_inputted_keys [m_inputing_key].length () &&
1449         m_lookup_table.number_of_candidates ()) {
1450
1451         lookup_to_converted (m_lookup_table.get_cursor_pos ());
1452         commit_converted ();
1453
1454         refresh_lookup_table ();
1455         refresh_preedit ();
1456         refresh_aux_string ();
1457     }
1458
1459     if (m_inputted_keys.size ()) return true;
1460
1461     if ((ispunct (key) && m_full_width_punct [m_forward?1:0]) ||
1462         ((isalnum (key) || key == 0x20) && m_full_width_letter [m_forward?1:0])) {
1463         WideString str;
1464         if (key == '.')
1465             str.push_back (0x3002);
1466         else if (key == '\\')
1467             str.push_back (0x3001);
1468         else if (key == '^') {
1469             str.push_back (0x2026);
1470             str.push_back (0x2026);
1471         } else if (key == '\"') {
1472             if (!m_double_quotation_state)
1473                 str.push_back (0x201c);
1474             else
1475                 str.push_back (0x201d);
1476             m_double_quotation_state = !m_double_quotation_state;
1477         } else if (key == '\'') {
1478             if (!m_single_quotation_state)
1479                 str.push_back (0x2018);
1480             else
1481                 str.push_back (0x2019);
1482             m_single_quotation_state = !m_single_quotation_state;
1483         } else {
1484             str.push_back (scim_wchar_to_full_width (key));
1485         }
1486
1487         commit_string (str);
1488
1489         m_last_committed = WideString ();
1490
1491         return true;
1492     }
1493
1494     return false;
1495 }
1496
1497 bool
1498 TableInstance::delete_phrase ()
1499 {
1500     if (m_lookup_table.number_of_candidates ()) {
1501         int pos       = m_lookup_table.get_cursor_pos ();
1502         uint32 offset = m_lookup_table_indexes [pos];
1503
1504         if (m_factory->m_table.delete_phrase (offset)) {
1505             m_factory->refresh (true);
1506             refresh_lookup_table ();
1507         }
1508         return true;
1509     }
1510     return false;
1511 }
1512
1513 void
1514 TableInstance::lookup_to_converted (int index)
1515 {
1516     if (index < 0 || index >= m_lookup_table.number_of_candidates ())
1517         return;
1518
1519     uint32 offset  = m_lookup_table_indexes [index];
1520     WideString str = m_factory->m_table.get_phrase (offset);
1521
1522     m_converted_strings.push_back (str);
1523     m_converted_indexes.push_back (offset);
1524
1525     if (m_inputing_key < m_converted_strings.size ()) {
1526         m_inputing_key = m_converted_strings.size ();
1527         if (m_inputing_key >= m_inputted_keys.size ())
1528             m_inputted_keys.push_back (String (""));
1529         m_inputing_caret = 0;
1530     }
1531 }
1532
1533 void
1534 TableInstance::commit_converted ()
1535 {
1536     if (m_converted_strings.size ()) {
1537         WideString res;
1538
1539         for (size_t i=0; i<m_converted_strings.size (); ++i)
1540             res += m_converted_strings [i];
1541
1542         // Hide preedit string before committing string,
1543         // to prevent some buggy clients from inserting the string into wrong place.
1544         // Preedit string will be refreshed after return from commit_converted ().
1545         hide_preedit_string ();
1546         commit_string (res);
1547
1548         if (utf8_wcstombs (m_last_committed).length () >= 255)
1549             m_last_committed = WideString ();
1550
1551         m_last_committed += res;
1552
1553         m_inputted_keys.erase (m_inputted_keys.begin (), m_inputted_keys.begin () + m_converted_strings.size ());
1554         m_inputing_key -= m_converted_strings.size ();
1555
1556         if (m_inputted_keys.size () == 1 && m_inputted_keys [0].length () == 0) {
1557             m_inputted_keys.clear ();
1558             m_inputing_key = 0;
1559             m_inputing_caret = 0;
1560         }
1561
1562         if (m_inputted_keys.size ()) {
1563             m_inputing_key = m_inputted_keys.size () - 1;
1564             m_inputing_caret = m_inputted_keys [m_inputing_key].length ();
1565         }
1566
1567         if (m_factory->m_table.is_dynamic_adjust ()){
1568             for (size_t i = 0; i < m_converted_indexes.size (); ++i) {
1569                 uint32 freq = m_factory->m_table.get_phrase_frequency (m_converted_indexes [i]);
1570                 if (freq < SCIM_GT_MAX_PHRASE_FREQ) {
1571                     uint32 delta = ((SCIM_GT_MAX_PHRASE_FREQ - freq) >> SCIM_GT_PHRASE_FREQ_DELTA_SHIFT);
1572                     freq += (delta ? delta : 1);
1573                     m_factory->m_table.set_phrase_frequency (m_converted_indexes [i], freq);
1574                 }
1575             }
1576             m_factory->refresh (false);
1577         }
1578
1579         m_converted_strings.clear ();
1580         m_converted_indexes.clear ();
1581     }
1582 }
1583
1584 void
1585 TableInstance::refresh_preedit ()
1586 {
1587     WideString preedit_string;
1588     int start = 0;
1589     int length = 0;
1590     int caret = 0;
1591     int end = 0;
1592     size_t i;
1593
1594     if (m_inputted_keys.size () == 0) {
1595         hide_preedit_string ();
1596         return;
1597     }
1598  
1599     for (i = 0; i<m_converted_strings.size (); ++i)
1600         preedit_string += m_converted_strings [i];
1601
1602     int inputted_keys = m_inputted_keys.size ();
1603
1604     if (m_inputted_keys [inputted_keys - 1].length () == 0)
1605         --inputted_keys;
1606
1607     // Fill the preedit string.
1608     if (m_factory->m_table.is_auto_fill () &&
1609         m_converted_strings.size () == inputted_keys - 1 &&
1610         m_inputing_caret == m_inputted_keys [m_inputing_key].length () &&
1611         m_lookup_table.number_of_candidates ()) {
1612
1613         uint32 offset = m_lookup_table_indexes [m_lookup_table.get_cursor_pos ()];
1614         WideString str = m_factory->m_table.get_phrase (offset);
1615
1616         start = preedit_string.length ();
1617         preedit_string += str;
1618         length = str.length ();
1619         caret = preedit_string.length ();
1620     } else {
1621         i = m_converted_strings.size ();
1622         caret = start = preedit_string.length ();
1623
1624         for (i = m_converted_strings.size (); i < inputted_keys; ++i) {
1625             if (m_factory->m_table.is_show_key_prompt ()) {
1626                 preedit_string += m_factory->m_table.get_key_prompt (m_inputted_keys [i]);
1627                 if (i == m_inputing_key)
1628                     caret += (m_factory->m_table.get_key_prompt (m_inputted_keys [i].substr (0, m_inputing_caret))).length ();
1629             } else {
1630                 preedit_string += utf8_mbstowcs (m_inputted_keys [i]);
1631                 if (i == m_inputing_key)
1632                     caret += m_inputing_caret;
1633             }
1634
1635             if (i == m_converted_strings.size ())
1636                 length = preedit_string.length () - start;
1637
1638             if (i < inputted_keys - 1)
1639                 preedit_string.push_back ((ucs4_t)' ');
1640
1641             if (i < m_inputing_key)
1642                 caret = preedit_string.length ();
1643         }
1644     }
1645     m_preedit_string = preedit_string;
1646     if (preedit_string.length () == 0) {
1647         hide_preedit_string ();
1648         return;
1649     }
1650
1651     AttributeList attrs;
1652
1653     if (length)
1654     {
1655         if (start)
1656             attrs.push_back (Attribute(0, start, SCIM_ATTR_DECORATE, SCIM_ATTR_DECORATE_UNDERLINE));
1657         attrs.push_back (Attribute(start, length, SCIM_ATTR_DECORATE, SCIM_ATTR_DECORATE_HIGHLIGHT));
1658         end = start+length;
1659         if (end < preedit_string.length())
1660             attrs.push_back (Attribute(end, preedit_string.length()- end, SCIM_ATTR_DECORATE, SCIM_ATTR_DECORATE_UNDERLINE));
1661     }
1662     update_preedit_string (preedit_string, attrs);
1663     update_preedit_caret (caret);
1664
1665     show_preedit_string ();
1666 }
1667
1668 void
1669 TableInstance::refresh_lookup_table (bool show, bool refresh)
1670 {
1671     m_lookup_table.set_page_size (m_factory->m_table.get_select_keys ().size ());
1672
1673     if (refresh) {
1674         std::vector <uint32> phrases;
1675         WideString str;
1676
1677         m_lookup_table.clear ();
1678         m_lookup_table_indexes.clear ();
1679
1680         if (m_converted_strings.size () < m_inputted_keys.size ()) {
1681
1682             String key = m_inputted_keys [m_converted_strings.size ()];
1683
1684             if (key.length () &&
1685                 m_factory->m_table.find (phrases,
1686                                          key,
1687                                          m_factory->m_user_phrase_first,
1688                                          m_factory->m_long_phrase_first)) {
1689
1690                 bool show_full_hint = m_factory->m_table.is_wildcard_key (key);
1691                 std::set<WideString> candiadtes;
1692                 for (size_t i = 0; i < phrases.size (); ++i) {
1693                     str = m_factory->m_table.get_phrase (phrases [i]);
1694
1695                     if (m_iconv.test_convert (str)) {
1696                         if (m_factory->m_show_key_hint) {
1697                             String hint = m_factory->m_table.get_key (phrases [i]);
1698
1699                             if (show_full_hint)
1700                                 str += utf8_mbstowcs (hint);
1701                             else if (hint.length () > key.length ())
1702                                 str += utf8_mbstowcs (hint.substr (key.length ()));
1703                         }
1704 #if 0
1705                         AttributeList attrs;
1706
1707                         if (m_factory->m_table.is_user_phrase (phrases [i]))
1708                             attrs.push_back (Attribute (0, str.length (), SCIM_ATTR_FOREGROUND, SCIM_RGB_COLOR(32, 32, 255)));
1709
1710                         m_lookup_table.append_candidate (str, attrs);
1711 #endif
1712                         if (candiadtes.find (str) != candiadtes.end())
1713                             continue;
1714                         candiadtes.insert (str);
1715                         m_lookup_table.append_candidate (str);
1716                         m_lookup_table_indexes.push_back (phrases [i]);
1717                     }
1718                 }
1719             }
1720         }
1721     }
1722
1723     if (show) {
1724         if (m_lookup_table.number_of_candidates () &&
1725             (m_factory->m_table.is_always_show_lookup () ||
1726              m_inputing_key < m_inputted_keys.size () - 1 ||
1727              m_inputing_caret < m_inputted_keys [m_inputing_key].length () ||
1728              m_converted_strings.size () < m_inputted_keys.size () - 1)) {
1729             update_lookup_table (m_lookup_table);
1730         } else {
1731             if(m_inputted_keys.size ())
1732             {
1733                 m_lookup_table.clear ();
1734                 update_lookup_table (m_lookup_table);
1735             }
1736             else
1737                 update_lookup_table (m_common_lookup_table);
1738         }
1739         show_lookup_table ();
1740     }
1741 }
1742
1743 void
1744 TableInstance::refresh_aux_string ()
1745 {
1746     WideString    prompt;
1747     AttributeList attributes;
1748
1749     if (m_add_phrase_mode == 1) {
1750         prompt = utf8_mbstowcs (_("Input a key string for phrase: ")) + m_last_committed;
1751     } else if (m_add_phrase_mode == 2) {
1752         prompt = utf8_mbstowcs (_("Success."));
1753         attributes.push_back (Attribute (0, prompt.length (), SCIM_ATTR_FOREGROUND, SCIM_RGB_COLOR(32, 255, 32)));
1754     } else if (m_add_phrase_mode == 3) {
1755         prompt = utf8_mbstowcs (_("Failed."));
1756         attributes.push_back (Attribute (0, prompt.length (), SCIM_ATTR_FOREGROUND, SCIM_RGB_COLOR(255, 32, 32)));
1757     } else {
1758         if (!m_factory->m_show_prompt || m_inputted_keys.size () == 0) {
1759             hide_aux_string ();
1760             return;
1761         }
1762
1763         if (!m_factory->m_table.is_show_key_prompt ())
1764             prompt = m_factory->m_table.get_key_prompt (m_inputted_keys [m_inputing_key]);
1765
1766         if (m_lookup_table.number_of_candidates () && ! m_factory->m_show_key_hint) {
1767             prompt += utf8_mbstowcs (" <");
1768             unsigned int att_start = prompt.length ();
1769
1770             if (m_factory->m_table.is_show_key_prompt ())
1771                 prompt += m_factory->m_table.get_key_prompt (m_factory->m_table.get_key (
1772                             m_lookup_table_indexes [m_lookup_table.get_cursor_pos ()]));
1773             else
1774                 prompt += utf8_mbstowcs (m_factory->m_table.get_key (
1775                             m_lookup_table_indexes [m_lookup_table.get_cursor_pos ()]));
1776
1777             unsigned int att_length = prompt.length () - att_start;
1778             prompt += utf8_mbstowcs (">");
1779             attributes.push_back (Attribute (att_start, att_length, SCIM_ATTR_FOREGROUND, SCIM_RGB_COLOR(128, 128, 255)));
1780         }
1781     }
1782
1783     if (prompt.length ()) {
1784         update_aux_string (prompt, attributes);
1785         show_aux_string ();
1786     } else {
1787         hide_aux_string ();
1788     }
1789 }
1790
1791 bool
1792 TableInstance::match_key_event (const std::vector<KeyEvent>& keyvec,
1793                                       const KeyEvent& key)
1794 {
1795     std::vector<KeyEvent>::const_iterator kit; 
1796
1797     for (kit = keyvec.begin (); kit != keyvec.end (); ++kit) {
1798         if (key.code == kit->code && key.mask == kit->mask)
1799             if (!(key.mask & SCIM_KEY_ReleaseMask) || m_prev_key.code == key.code)
1800                 return true;
1801     }
1802     return false;
1803 }
1804 /*
1805 vi:ts=4:nowrap:ai:expandtab
1806 */