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