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