Merge from opensource
[platform/core/uifw/ise-engine-hangul.git] / src / scim_hangul_imengine.cpp
1 /** @file scim_hangul_imengine.cpp
2  */
3
4 /*
5  * Smart Common Input Method
6  *
7  * Copyright (C) 2004-2006 Choe Hwanjin
8  * Copyright (c) 2004-2006 James Su <suzhe@tsinghua.org.cn>
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
23  *
24  * $Id: scim_hangul_imengine.cpp,v 1.34 2007/05/27 13:08:07 hwanjin Exp $
25  */
26
27 #define Uses_SCIM_UTILITY
28 #define Uses_SCIM_IMENGINE
29 #define Uses_SCIM_LOOKUP_TABLE
30 #define Uses_SCIM_CONFIG_BASE
31
32 #ifdef HAVE_CONFIG_H
33   #include <config.h>
34 #endif
35
36 #include <cstring>
37 #include <unistd.h>
38 #include <scim.h>
39 #include "scim_hangul_imengine.h"
40
41 #ifdef HAVE_GETTEXT
42   #include <libintl.h>
43   #define _(String) dgettext(GETTEXT_PACKAGE,String)
44   #define N_(String) (String)
45 #else
46   #define _(String) (String)
47   #define N_(String) (String)
48   #define bindtextdomain(Package,Directory)
49   #define textdomain(domain)
50   #define bind_textdomain_codeset(domain,codeset)
51 #endif
52
53 #define scim_module_init hangul_LTX_scim_module_init
54 #define scim_module_exit hangul_LTX_scim_module_exit
55 #define scim_imengine_module_init hangul_LTX_scim_imengine_module_init
56 #define scim_imengine_module_create_factory hangul_LTX_scim_imengine_module_create_factory
57
58 #define SCIM_IMENGINE_HANGUL "/IMEngine/Hangul"
59
60 #define SCIM_CONFIG_PREFIX SCIM_IMENGINE_HANGUL
61
62 #define SCIM_CONFIG_SHOW_CANDIDATE_COMMENT      SCIM_CONFIG_PREFIX "/ShowCandidateComment"
63 #define SCIM_CONFIG_HANGUL_KEY                  SCIM_CONFIG_PREFIX "/HangulKey"
64 #define SCIM_CONFIG_HANJA_KEY                   SCIM_CONFIG_PREFIX "/HanjaKey"
65 #define SCIM_CONFIG_HANJA_MODE_KEY              SCIM_CONFIG_PREFIX "/HanjaModeKey"
66 #define SCIM_CONFIG_LAYOUT                      SCIM_CONFIG_PREFIX "/KeyboardLayout"
67 #define SCIM_CONFIG_USE_ASCII_MODE              SCIM_CONFIG_PREFIX "/UseAsciiMode"
68 #define SCIM_CONFIG_COMMIT_BY_WORD              SCIM_CONFIG_PREFIX "/CommitByWord"
69 #define SCIM_CONFIG_HANJA_MODE                  SCIM_CONFIG_PREFIX "/HanjaMode"
70 #define SCIM_CONFIG_AUTO_REORDER                SCIM_CONFIG_PREFIX "/AutoReorder"
71
72 #define SCIM_CONFIG_PANEL_LOOKUP_TABLE_VERTICAL "/Panel/Gtk/LookupTableVertical"
73
74 #define SCIM_PROP_PREFIX         "/IMEngine/Hangul"
75 #define SCIM_PROP_HANGUL_MODE    SCIM_PROP_PREFIX "/HangulMode"
76 #define SCIM_PROP_HANJA_MODE     SCIM_PROP_PREFIX "/HanjaMode"
77
78 #ifndef SCIM_HANGUL_ICON_FILE
79     #define SCIM_HANGUL_ICON_FILE           (SCIM_ICONDIR "/scim-hangul.png")
80 #endif
81
82 #define SCIM_HANGUL_ICON_ON      SCIM_ICONDIR "/scim-hangul-on.png"
83 #define SCIM_HANGUL_ICON_OFF     SCIM_ICONDIR "/scim-hangul-off.png"
84
85 static ConfigPointer _scim_config (0);
86
87 static Property hangul_mode(SCIM_PROP_HANGUL_MODE, "");
88 static Property hanja_mode(SCIM_PROP_HANJA_MODE, "");
89
90 extern "C" {
91     void scim_module_init (void)
92     {
93         bindtextdomain (GETTEXT_PACKAGE, SCIM_HANGUL_LOCALEDIR);
94         bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8");
95     }
96
97     void scim_module_exit (void)
98     {
99         _scim_config.reset ();
100     }
101
102     uint32 scim_imengine_module_init (const ConfigPointer &config)
103     {
104         SCIM_DEBUG_IMENGINE(1) << "Initialize Hangul Engine\n";
105
106         _scim_config = config;
107
108         return 1;
109     }
110
111     IMEngineFactoryPointer scim_imengine_module_create_factory (uint32 engine)
112     {
113         HangulFactory *factory = 0;
114
115         try {
116             factory = new HangulFactory (_scim_config);
117         } catch (...) {
118             delete factory;
119             factory = 0;
120         }
121
122         return factory;
123     }
124 }
125
126 HangulFactory::HangulFactory (const ConfigPointer &config)
127 {
128     m_uuid = "d75857a5-4148-4745-89e2-1da7ddaf70a9";
129     m_name = _("Korean");
130     m_config = config;
131     m_keyboard_layout = "2";
132     m_show_candidate_comment = true;
133     m_use_ascii_mode = false;
134     m_commit_by_word = false;
135     m_auto_reorder = true;
136
137     m_hanja_table = hanja_table_load(NULL);
138     m_symbol_table = NULL;
139
140     std::string symbol_file = getenv("HOME");
141     symbol_file += "/.scim/hangul/symbol.txt";
142     if (access(symbol_file.c_str(), R_OK) == 0)
143         m_symbol_table = hanja_table_load(symbol_file.c_str());
144     if (m_symbol_table == NULL) {
145         symbol_file = SCIM_HANGUL_DATADIR "/symbol.txt";
146         if (access(symbol_file.c_str(), R_OK) == 0)
147             m_symbol_table = hanja_table_load(symbol_file.c_str());
148     }
149
150     set_languages ("ko");
151
152     reload_config(m_config);
153
154     m_reload_signal_connection = m_config->signal_connect_reload(slot(this, &HangulFactory::reload_config));
155 }
156
157 HangulFactory::~HangulFactory ()
158 {
159     m_reload_signal_connection.disconnect();
160     if (m_hanja_table != NULL)
161         hanja_table_delete(m_hanja_table);
162
163     if (m_symbol_table != NULL)
164         hanja_table_delete(m_symbol_table);
165 }
166
167 WideString
168 HangulFactory::get_name () const
169 {
170     return utf8_mbstowcs (m_name);
171 }
172
173 WideString
174 HangulFactory::get_authors () const
175 {
176     return utf8_mbstowcs (String (_("Copyright (C) 2006 Choe Hwanjin <choe.hwanjin@gmail.com>")));
177 }
178
179 WideString
180 HangulFactory::get_credits () const
181 {
182     return WideString ();
183 }
184
185 WideString
186 HangulFactory::get_help () const
187 {
188     const char *header =
189         _("Key bindings:\n");
190
191     String hangul_keys;
192     String hanja_keys;
193     scim_key_list_to_string(hangul_keys, m_hangul_keys);
194     scim_key_list_to_string(hanja_keys, m_hanja_keys);
195
196     char paragraph1[512];
197     char paragraph2[512];
198
199     snprintf(paragraph1, sizeof(paragraph1),
200         _("  Hangul key: %s\n"
201           "    This key binding is to switch the input mode between the ASCII input \n"
202           "    mode and the hangul input mode.\n"), hangul_keys.c_str());
203     snprintf(paragraph2, sizeof(paragraph2),
204         _("  Hanja key: %s\n"
205           "    This key binding is to convert a hangul character to a hanja character.\n"), hanja_keys.c_str());
206
207     return utf8_mbstowcs (header)
208         + utf8_mbstowcs (paragraph1)
209         + utf8_mbstowcs (paragraph2);
210 }
211
212 String
213 HangulFactory::get_uuid () const
214 {
215     return m_uuid;
216 }
217
218 String
219 HangulFactory::get_icon_file () const
220 {
221     return String (SCIM_HANGUL_ICON_FILE);
222 }
223
224 void
225 HangulFactory::reload_config(const ConfigPointer &config)
226 {
227     if (config.null())
228         return;
229
230     m_show_candidate_comment = config->read(String(SCIM_CONFIG_SHOW_CANDIDATE_COMMENT),
231                                             m_show_candidate_comment);
232
233     m_keyboard_layout = config->read(String(SCIM_CONFIG_LAYOUT), String("2"));
234
235     m_use_ascii_mode = config->read(String(SCIM_CONFIG_USE_ASCII_MODE),
236                                     false);
237     m_commit_by_word = config->read(String(SCIM_CONFIG_COMMIT_BY_WORD),
238                                     false);
239     m_hanja_mode = config->read(String(SCIM_CONFIG_HANJA_MODE),
240                                     false);
241     m_auto_reorder = config->read(String(SCIM_CONFIG_AUTO_REORDER),
242                                     true);
243
244     String str;
245     str = config->read(String(SCIM_CONFIG_HANGUL_KEY),
246                        String("Hangul,Shift+space"));
247     scim_string_to_key_list(m_hangul_keys, str);
248
249     str = config->read(String (SCIM_CONFIG_HANJA_KEY),
250                        String ("Hangul_Hanja,F9"));
251     scim_string_to_key_list(m_hanja_keys, str);
252
253     str = config->read(String (SCIM_CONFIG_HANJA_MODE_KEY),
254                        String (""));
255     scim_string_to_key_list(m_hanja_mode_keys, str);
256
257     m_lookup_table_vertical = config->read(String(SCIM_CONFIG_PANEL_LOOKUP_TABLE_VERTICAL),
258                                            false);
259 }
260
261 IMEngineInstancePointer
262 HangulFactory::create_instance (const String &encoding, int id)
263 {
264     SCIM_DEBUG_IMENGINE(1) << "create_instance: HangulInstance.\n";
265     return new HangulInstance (this, encoding, id);
266 }
267
268 HangulInstance::HangulInstance (HangulFactory *factory,
269                                 const String  &encoding,
270                                 int            id)
271     : IMEngineInstanceBase (factory, encoding, id),
272       m_factory (factory),
273       m_prev_key (0,0),
274       m_output_mode (OUTPUT_MODE_SYLLABLE)
275 {
276     m_hic = hangul_ic_new(factory->m_keyboard_layout.c_str());
277     hangul_ic_connect_callback (m_hic, "transition", (void *)on_transition, this);
278
279     char label[16];
280     std::vector <WideString> labels;
281
282     for (int i = 1; i < 10; ++i) {
283         snprintf (label, sizeof(label), "%d", i);
284         labels.push_back (utf8_mbstowcs (label));
285     }
286
287     m_lookup_table.set_candidate_labels (labels);
288
289     m_hangul_mode = true;
290 }
291
292 HangulInstance::~HangulInstance ()
293 {
294 }
295
296 bool
297 HangulInstance::candidate_key_event (const KeyEvent &key)
298 {
299     switch (key.code) {
300         case SCIM_KEY_Return:
301         case SCIM_KEY_KP_Enter:
302             select_candidate (m_lookup_table.get_cursor_pos_in_current_page ());
303             break;
304         case SCIM_KEY_KP_Subtract:
305             m_lookup_table.cursor_up ();
306             update_lookup_table (m_lookup_table);
307             hangul_update_aux_string ();
308             break;
309         case SCIM_KEY_space:
310             if (is_hanja_mode())
311                 return false;
312         case SCIM_KEY_KP_Add:
313             m_lookup_table.cursor_down ();
314             update_lookup_table (m_lookup_table);
315             hangul_update_aux_string ();
316             break;
317         case SCIM_KEY_Page_Up:
318             lookup_table_page_up();
319             break;
320         case SCIM_KEY_Page_Down:
321         case SCIM_KEY_KP_Tab:
322             lookup_table_page_down();
323             break;
324         case SCIM_KEY_h:
325             if (is_hanja_mode())
326                 return false;
327         case SCIM_KEY_Left:
328             if (m_factory->m_lookup_table_vertical) {
329                 lookup_table_page_up();
330             } else {
331                 m_lookup_table.cursor_up ();
332                 update_lookup_table (m_lookup_table);
333                 hangul_update_aux_string ();
334             }
335             break;
336         case SCIM_KEY_l:
337             if (is_hanja_mode())
338                 return false;
339         case SCIM_KEY_Right:
340             if (m_factory->m_lookup_table_vertical) {
341                 lookup_table_page_down();
342             } else {
343                 m_lookup_table.cursor_down ();
344                 update_lookup_table (m_lookup_table);
345                 hangul_update_aux_string ();
346             }
347             break;
348         case SCIM_KEY_k:
349             if (is_hanja_mode())
350                 return false;
351         case SCIM_KEY_Up:
352             if (m_factory->m_lookup_table_vertical) {
353                 m_lookup_table.cursor_up ();
354                 update_lookup_table (m_lookup_table);
355                 hangul_update_aux_string ();
356             } else {
357                 lookup_table_page_up();
358             }
359             break;
360         case SCIM_KEY_j:
361             if (is_hanja_mode())
362                 return false;
363         case SCIM_KEY_Down:
364             if (m_factory->m_lookup_table_vertical) {
365                 m_lookup_table.cursor_down ();
366                 update_lookup_table (m_lookup_table);
367                 hangul_update_aux_string ();
368             } else {
369                 lookup_table_page_down();
370             }
371             break;
372         case SCIM_KEY_Escape:
373             delete_candidates ();
374             break;
375         case SCIM_KEY_1:
376         case SCIM_KEY_2:
377         case SCIM_KEY_3:
378         case SCIM_KEY_4:
379         case SCIM_KEY_5:
380         case SCIM_KEY_6:
381         case SCIM_KEY_7:
382         case SCIM_KEY_8:
383         case SCIM_KEY_9:
384             select_candidate (key.code - SCIM_KEY_1);
385             break;
386         default:
387             return !is_hanja_mode();
388     }
389
390     return true;
391 }
392
393 bool
394 HangulInstance::process_key_event (const KeyEvent& rawkey)
395 {
396     SCIM_DEBUG_IMENGINE(1) << "process_key_event.\n";
397
398     KeyEvent key = rawkey.map_to_layout(SCIM_KEYBOARD_Default);
399
400     m_prev_key = key;
401
402     if (use_ascii_mode() && !is_hangul_mode()) {
403         if (is_hangul_key(key)) {
404             toggle_hangul_mode();
405             return true;
406         }
407
408         return false;
409     }
410
411     /* ignore key release. */
412     if (key.is_key_release ())
413         return false;
414
415     /* mode change */
416     if (use_ascii_mode() && is_hangul_key(key)) {
417         toggle_hangul_mode();
418         return true;
419     }
420
421     /* hanja mode */
422     if (is_hanja_mode_key (key)) {
423         toggle_hanja_mode();
424     }
425
426     /* toggle candidate table */
427     if (is_hanja_key (key)) {
428         if (is_hanja_mode()) {
429             update_candidates ();
430         } else {
431             if (m_lookup_table.number_of_candidates ())
432                 delete_candidates ();
433             else
434                 update_candidates ();
435         }
436
437         return true;
438     }
439
440     /* ignore shift keys */
441     if (key.code == SCIM_KEY_Shift_L || key.code == SCIM_KEY_Shift_R)
442         return false;
443
444     /* flush on modifier-on keys */
445     if (key.is_control_down() || key.is_alt_down()) {
446         flush ();
447         return false;
448     }
449
450     /* candidate keys */
451     if (m_lookup_table.number_of_candidates ()) {
452         if (candidate_key_event(key))
453             return true;
454     }
455
456     /* change to ascii mode on ESCAPE key, for vi users.
457      * We should process this key after processing candidate keys,
458      * or input mode will be changed to non-hangul mode when the user presses
459      * escape key to close candidate window. */
460     if (use_ascii_mode() && !is_hanja_mode()) {
461         if (key.code == SCIM_KEY_Escape) {
462             toggle_hangul_mode();
463         }
464     }
465
466     /* backspace */
467     if (is_backspace_key(key)) {
468         bool ret = hangul_ic_backspace(m_hic);
469         if (ret) {
470             hangul_update_preedit_string ();
471         } else if (m_preedit.length() > 0) {
472             ret = true;
473             m_preedit.erase(m_preedit.length() - 1, 1);
474             hangul_update_preedit_string();
475         } else {
476             if (m_surrounding_text.length() > 0) {
477                 m_surrounding_text.erase(m_surrounding_text.length() - 1, 1);
478                 if (m_surrounding_text.empty()) {
479                     delete_candidates();
480                     return ret;
481                 }
482             }
483         }
484
485         if (is_hanja_mode() && m_lookup_table.number_of_candidates()) {
486             update_candidates();
487         }
488
489         return ret;
490     }
491
492     if (key.code >= SCIM_KEY_exclam && key.code <= SCIM_KEY_asciitilde) {
493         /* main hangul composing process */
494         int ascii = key.get_ascii_code();
495         if (key.is_caps_lock_down()) {
496             if (isupper(ascii))
497                 ascii = tolower(ascii);
498             else if (islower(ascii))
499                 ascii = toupper(ascii);
500         }
501
502         bool ret = hangul_ic_process(m_hic, ascii);
503
504         WideString wstr;
505         wstr = get_commit_string ();
506         if (wstr.length ()) {
507             /* Before commit, we set preedit string to null to work arround
508              * some buggy IM implementation, ex) Qt, Evolution */
509             hide_preedit_string ();
510             if (is_hanja_mode() || m_factory->m_commit_by_word) {
511                 m_preedit += wstr;
512             } else {
513                 commit_string(wstr);
514             }
515         }
516
517         if (is_hanja_mode() || m_factory->m_commit_by_word) {
518             if (hangul_ic_is_empty(m_hic)) {
519                 flush();
520             }
521         }
522
523         hangul_update_preedit_string ();
524
525         if (is_hanja_mode()) {
526             update_candidates();
527         }
528
529         return ret;
530     }
531
532     flush();
533     return false;
534 }
535
536 void
537 HangulInstance::move_preedit_caret (unsigned int pos)
538 {
539 }
540
541 void
542 HangulInstance::select_candidate (unsigned int index)
543 {
544     SCIM_DEBUG_IMENGINE(2) << "select_candidate.\n";
545
546     if ((int)index >= m_lookup_table.get_current_page_size ())
547         return;
548
549     WideString candidate = m_lookup_table.get_candidate_in_current_page(index);
550
551     WideString commit_str = candidate;
552     WideString preedit = get_preedit_string();
553     if (is_hanja_mode() || m_factory->m_commit_by_word) {
554         // prefix method
555         int len = m_surrounding_text.length();
556         if (len > 0)
557             delete_surrounding_text(-len, len);
558         if (candidate.length() <= m_surrounding_text.length()) {
559             len = m_surrounding_text.length() - candidate.length();
560             commit_str.append(m_surrounding_text, candidate.length(), len);
561             m_surrounding_text.erase(0, candidate.length());
562         } else if (candidate.length() <= m_surrounding_text.length() + preedit.length()) {
563             len = candidate.length() - m_surrounding_text.length();
564             if (len > (int)m_preedit.length()) {
565                 m_preedit.clear();
566                 hangul_ic_reset(m_hic);
567             } else {
568                 m_preedit.erase(0, len);
569             }
570             m_surrounding_text.clear();
571         } else {
572             m_preedit.clear();
573             hangul_ic_reset(m_hic);
574             m_surrounding_text.clear();
575         }
576     } else {
577         // suffix method
578         if (candidate.length() > preedit.length()) {
579             int len = candidate.length() - preedit.length();
580             delete_surrounding_text(-len, len);
581         }
582         hangul_ic_reset(m_hic);
583         m_surrounding_text.clear();
584     }
585
586     commit_string(commit_str);
587     hangul_update_preedit_string ();
588
589     if (is_hanja_mode()) {
590         update_candidates();
591     } else {
592         delete_candidates();
593     }
594 }
595
596 void
597 HangulInstance::update_lookup_table_page_size (unsigned int page_size)
598 {
599     SCIM_DEBUG_IMENGINE(2) << "update_lookup_table_page_size.\n";
600
601     m_lookup_table.set_page_size (page_size);
602 }
603
604 void
605 HangulInstance::lookup_table_page_up ()
606 {
607     if (!m_lookup_table.number_of_candidates () || !m_lookup_table.get_current_page_start ())
608         return;
609
610     SCIM_DEBUG_IMENGINE(2) << "lookup_table_page_up.\n";
611
612     m_lookup_table.page_up ();
613
614     update_lookup_table (m_lookup_table);
615
616     hangul_update_aux_string ();
617 }
618
619 void
620 HangulInstance::lookup_table_page_down ()
621 {
622     if (m_lookup_table.number_of_candidates () <= 0 ||
623         m_lookup_table.get_current_page_start () + m_lookup_table.get_current_page_size () >=
624           (int)m_lookup_table.number_of_candidates ())
625         return;
626
627     SCIM_DEBUG_IMENGINE(2) << "lookup_table_page_down.\n";
628
629     m_lookup_table.page_down ();
630
631     update_lookup_table (m_lookup_table);
632
633     hangul_update_aux_string ();
634 }
635
636 void
637 HangulInstance::reset()
638 {
639     SCIM_DEBUG_IMENGINE(2) << "reset.\n";
640     flush();
641 }
642
643 void
644 HangulInstance::flush()
645 {
646     SCIM_DEBUG_IMENGINE(2) << "flush.\n";
647
648     hide_preedit_string();
649
650     WideString wstr = m_preedit;
651     const ucschar *str = hangul_ic_flush(m_hic);
652     while (*str != 0)
653         wstr.push_back (*str++);
654
655     if (wstr.length())
656         commit_string(wstr);
657
658     delete_candidates ();
659     m_preedit.clear();
660 }
661
662 void
663 HangulInstance::focus_in ()
664 {
665     SCIM_DEBUG_IMENGINE(2) << "focus_in.\n";
666
667     register_all_properties();
668
669     hangul_ic_select_keyboard(m_hic, m_factory->m_keyboard_layout.c_str());
670
671     if (m_lookup_table.number_of_candidates ()) {
672         update_lookup_table (m_lookup_table);
673         show_lookup_table ();
674     } else {
675         hide_lookup_table ();
676     }
677
678     hangul_update_aux_string ();
679 }
680
681 void
682 HangulInstance::focus_out ()
683 {
684     SCIM_DEBUG_IMENGINE(2) << "focus_out.\n";
685     flush();
686 }
687
688 void
689 HangulInstance::trigger_property (const String &property)
690 {
691     SCIM_DEBUG_IMENGINE(2) << "trigger_property.\n";
692     if (property == SCIM_PROP_HANGUL_MODE) {
693         toggle_hangul_mode();
694     } else if (property == SCIM_PROP_HANJA_MODE) {
695         toggle_hanja_mode();
696     }
697 }
698
699 String
700 HangulInstance::get_candidate_string()
701 {
702     int cursor = 0;
703     if (m_surrounding_text.empty())
704         get_surrounding_text(m_surrounding_text, cursor, 10, 0);
705
706     int i;
707     for (i = m_surrounding_text.length() - 1; i >= 0; i--) {
708         if (!hangul_is_syllable(m_surrounding_text[i]))
709             break;
710     }
711     if (i >= 0)
712         m_surrounding_text.erase(0, i + 1);
713
714     return utf8_wcstombs(m_surrounding_text + get_preedit_string());
715 }
716
717 void
718 HangulInstance::update_candidates ()
719 {
720     m_lookup_table.clear ();
721     m_candidate_comments.clear ();
722
723     HanjaList* list = NULL;
724
725     // search for symbol character
726     // key string for symbol character is like:
727     //  'ㄱ', 'ㄴ', 'ㄷ', etc
728     WideString preeditw = get_preedit_string();
729     if (preeditw.length() == 1) {
730         String key = utf8_wcstombs(preeditw);
731         list = hanja_table_match_suffix(m_factory->m_symbol_table, key.c_str());
732     }
733
734     // search for hanja
735     if (list == NULL) {
736         String str = get_candidate_string();
737         SCIM_DEBUG_IMENGINE(1) << "candidate string: " << str << "\n";
738
739         if (str.length() > 0) {
740             if (is_hanja_mode() || m_factory->m_commit_by_word) {
741                 list = hanja_table_match_prefix(m_factory->m_hanja_table,
742                                                     str.c_str());
743             } else {
744                 list = hanja_table_match_suffix(m_factory->m_hanja_table,
745                                                     str.c_str());
746             }
747         }
748     }
749
750     if (list != NULL) {
751         int n = hanja_list_get_size(list);
752         for (int i = 0; i < n; ++i) {
753             const char* value = hanja_list_get_nth_value(list, i);
754             const char* comment = hanja_list_get_nth_comment(list, i);
755             WideString candidate = utf8_mbstowcs(value, -1);
756             m_lookup_table.append_candidate(candidate);
757             m_candidate_comments.push_back(String(comment));
758         }
759
760         m_lookup_table.set_page_size (9);
761         m_lookup_table.show_cursor ();
762
763         update_lookup_table (m_lookup_table);
764         show_lookup_table ();
765
766         hangul_update_aux_string ();
767
768         hanja_list_delete(list);
769     }
770
771     if (m_lookup_table.number_of_candidates() <= 0) {
772         delete_candidates();
773     }
774 }
775
776 void
777 HangulInstance::delete_candidates ()
778 {
779     m_surrounding_text.clear();
780     m_lookup_table.clear ();
781     m_candidate_comments.clear ();
782     hide_lookup_table ();
783     hide_aux_string ();
784 }
785
786 void
787 HangulInstance::hangul_update_aux_string ()
788 {
789     if (!m_factory->m_show_candidate_comment || !m_lookup_table.number_of_candidates ()) {
790         hide_aux_string ();
791         return;
792     }
793
794     size_t cursor = m_lookup_table.get_cursor_pos ();
795
796     if (cursor >= m_candidate_comments.size ()) {
797         hide_aux_string ();
798         return;
799     }
800
801     update_aux_string (m_lookup_table.get_candidate (cursor) + utf8_mbstowcs (String (" : ") + m_candidate_comments [cursor]));
802     show_aux_string ();
803 }
804
805 void
806 HangulInstance::hangul_update_preedit_string ()
807 {
808     WideString wstr = get_preedit_string ();
809
810     if (wstr.length ()) {
811         AttributeList attrs;
812         attrs.push_back(Attribute(0, m_preedit.length(), SCIM_ATTR_DECORATE, SCIM_ATTR_DECORATE_UNDERLINE));
813         attrs.push_back(Attribute(m_preedit.length(), wstr.length() - m_preedit.length(), SCIM_ATTR_DECORATE, SCIM_ATTR_DECORATE_UNDERLINE));
814         show_preedit_string ();
815         update_preedit_string (wstr, attrs);
816         update_preedit_caret (wstr.length());
817     } else {
818         hide_preedit_string ();
819     }
820 }
821
822 bool
823 HangulInstance::match_key_event (const KeyEventList &keys, const KeyEvent &key) const
824 {
825     KeyEventList::const_iterator kit;
826
827     for (kit = keys.begin (); kit != keys.end (); ++kit) {
828         if (!key.is_key_release()) {
829             if (key.code == kit->code) {
830                 int mask = key.mask;
831                 // we should ignore capslock and numlock
832                 mask &= ~SCIM_KEY_CapsLockMask;
833                 mask &= ~SCIM_KEY_NumLockMask;
834                 if (mask == kit->mask)
835                     return true;
836             }
837         }
838     }
839     return false;
840 }
841
842 void
843 HangulInstance::toggle_hangul_mode()
844 {
845     m_hangul_mode = !m_hangul_mode;
846     flush();
847
848     if (m_hangul_mode) {
849         hangul_mode.set_label("한");
850     } else {
851         hangul_mode.set_label("A");
852     }
853
854     update_property(hangul_mode);
855 }
856
857 void
858 HangulInstance::toggle_hanja_mode()
859 {
860     m_factory->m_hanja_mode = !m_factory->m_hanja_mode;
861
862     if (m_factory->m_hanja_mode) {
863         hanja_mode.set_icon(SCIM_HANGUL_ICON_ON);
864     } else {
865         hanja_mode.set_icon(SCIM_HANGUL_ICON_OFF);
866     }
867
868     update_property(hanja_mode);
869
870     m_factory->m_config->write(String(SCIM_CONFIG_HANJA_MODE), m_factory->m_hanja_mode);
871 }
872
873 void
874 HangulInstance::register_all_properties()
875 {
876     PropertyList proplist;
877
878     if (use_ascii_mode()) {
879         if (m_hangul_mode) {
880             hangul_mode.set_label("한");
881         } else {
882             hangul_mode.set_label("A");
883         }
884         proplist.push_back(hangul_mode);
885     }
886
887     if (m_factory->m_hanja_mode) {
888         hanja_mode.set_icon(SCIM_HANGUL_ICON_ON);
889     } else {
890         hanja_mode.set_icon(SCIM_HANGUL_ICON_OFF);
891     }
892     hanja_mode.set_label(_("Hanja Lock"));
893     proplist.push_back(hanja_mode);
894
895     register_properties(proplist);
896 }
897
898 bool
899 HangulInstance::on_transition (HangulInputContext     *hic,
900                                ucschar                 c,
901                                const ucschar          *preedit,
902                                void                   *data)
903 {
904     HangulInstance *self = reinterpret_cast<HangulInstance*>(data);
905
906     if (!self->m_factory->m_auto_reorder) {
907         if (hangul_is_choseong (c)) {
908             if (hangul_ic_has_jungseong (hic) || hangul_ic_has_jongseong (hic))
909                 return false;
910         }
911
912         if (hangul_is_jungseong (c)) {
913             if (hangul_ic_has_jongseong (hic))
914                 return false;
915         }
916     }
917
918     return true;
919 }
920
921 // vim: sts=4 sw=4 nowrap ai expandtab