1 /** @file scim_rawcode_imengine.cpp
2  * implementation of class RawCodeInstance.
3  */
4 
5 /*
6  * Smart Common Input Method
7  *
8  * Copyright (c) 2002-2005 James Su <suzhe@tsinghua.org.cn>
9  *
10  *
11  * This library is free software; you can redistribute it and/or
12  * modify it under the terms of the GNU Lesser General Public
13  * License as published by the Free Software Foundation; either
14  * version 2 of the License, or (at your option) any later version.
15  *
16  * This library is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  * GNU Lesser General Public License for more details.
20  *
21  * You should have received a copy of the GNU Lesser General Public
22  * License along with this program; if not, write to the
23  * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
24  * Boston, MA  02111-1307  USA
25  *
26  * $Id: scim_rawcode_imengine.cpp,v 1.7.2.7 2007/04/10 07:47:18 suzhe Exp $
27  *
28  */
29 
30 #define Uses_SCIM_IMENGINE
31 #define Uses_SCIM_ICONV
32 #define Uses_C_STRING
33 #define Uses_SCIM_CONFIG_BASE
34 #define Uses_SCIM_CONFIG_PATH
35 #include "scim_private.h"
36 #include "scim.h"
37 #include "scim_rawcode_imengine.h"
38 
39 #define scim_module_init rawcode_LTX_scim_module_init
40 #define scim_module_exit rawcode_LTX_scim_module_exit
41 #define scim_imengine_module_init rawcode_LTX_scim_imengine_module_init
42 #define scim_imengine_module_create_factory rawcode_LTX_scim_imengine_module_create_factory
43 
44 #define SCIM_CONFIG_IMENGINE_RAWCODE_LOCALES   "/IMEngine/RawCode/Locales"
45 #define SCIM_PROP_RAWCODE_ENCODING             "/IMEngine/RawCode/Encoding"
46 
47 #define SCIM_RAWCODE_ICON_FILE                 (SCIM_ICONDIR "/rawcode.png")
48 
49 using namespace scim;
50 
51 static Pointer <RawCodeFactory> __rawcode_factory;
52 
53 static String __rawcode_locales;
54 
55 static std::vector<String> __rawcode_encodings;
56 
57 extern "C" {
scim_module_init(void)58     void scim_module_init (void)
59     {
60         __rawcode_locales = String (
61             "zh_CN.GB18030,zh_CN.GBK,zh_CN.GB2312,zh_TW,zh_TW.EUC-TW,zh_HK,"
62             "ja_JP,ja_JP.sjis,ko_KR,en_US.UTF-8");
63     }
64 
scim_module_exit(void)65     void scim_module_exit (void)
66     {
67         __rawcode_factory.reset ();
68     }
69 
scim_imengine_module_init(const ConfigPointer & config)70     unsigned int scim_imengine_module_init (const ConfigPointer &config)
71     {
72         if (!config.null ()) {
73             String locales = config->read (String (SCIM_CONFIG_IMENGINE_RAWCODE_LOCALES), String ("default"));
74             if (locales != "default")
75                 __rawcode_locales = locales;
76         }
77 
78         std::vector <String> locale_list;
79 
80         scim_split_string_list (locale_list, __rawcode_locales);
81 
82         for (size_t i=0; i<locale_list.size (); ++i) {
83             locale_list [i] = scim_validate_locale (locale_list [i]);
84             if (locale_list [i].length ())
85                 __rawcode_encodings.push_back (scim_get_locale_encoding (locale_list [i]));
86         }
87 
88         std::sort (__rawcode_encodings.begin (), __rawcode_encodings.end ());
89         __rawcode_encodings.erase (std::unique (__rawcode_encodings.begin (), __rawcode_encodings.end ()), __rawcode_encodings.end ());
90 
91         return 1;
92     }
93 
scim_imengine_module_create_factory(unsigned int factory)94     IMEngineFactoryPointer scim_imengine_module_create_factory (unsigned int factory)
95     {
96         String languages;
97 
98         if (factory != 0) return NULL;
99 
100         if (__rawcode_factory.null ())
101             __rawcode_factory = new RawCodeFactory ();
102 
103         return __rawcode_factory;
104     }
105 }
106 
107 // implementation of RawCode
RawCodeFactory()108 RawCodeFactory::RawCodeFactory ()
109 {
110     set_locales (__rawcode_locales);
111 }
112 
~RawCodeFactory()113 RawCodeFactory::~RawCodeFactory ()
114 {
115 }
116 
117 WideString
get_name() const118 RawCodeFactory::get_name () const
119 {
120     return utf8_mbstowcs (_("RAW CODE"));
121 }
122 
123 WideString
get_authors() const124 RawCodeFactory::get_authors () const
125 {
126     return utf8_mbstowcs (String (
127                 _("(C) 2002-2006 James Su <suzhe@tsinghua.org.cn>")));
128 }
129 
130 WideString
get_credits() const131 RawCodeFactory::get_credits () const
132 {
133     return WideString ();
134 }
135 
136 WideString
get_help() const137 RawCodeFactory::get_help () const
138 {
139     return utf8_mbstowcs (String (_(
140                             "Hot Keys:\n\n"
141                             "  Control+u:\n"
142                             "    switch between Multibyte encoding and Unicode.\n\n"
143                             "  Esc:\n"
144                             "    reset the input method.\n")));
145 }
146 
147 String
get_uuid() const148 RawCodeFactory::get_uuid () const
149 {
150     return String ("6e029d75-ef65-42a8-848e-332e63d70f9c");
151 }
152 
153 String
get_icon_file() const154 RawCodeFactory::get_icon_file () const
155 {
156     return String (SCIM_RAWCODE_ICON_FILE);
157 }
158 
159 String
get_language() const160 RawCodeFactory::get_language () const
161 {
162     return scim_validate_language ("other");
163 }
164 
165 IMEngineInstancePointer
create_instance(const String & encoding,int id)166 RawCodeFactory::create_instance (const String& encoding, int id)
167 {
168     return new RawCodeInstance (this, encoding, id);
169 }
170 
171 int
get_maxlen(const String & encoding)172 RawCodeFactory::get_maxlen (const String &encoding)
173 {
174     if (encoding == "UTF-8") return 4;
175     if (encoding == "Unicode") return 0;
176 
177     std::vector <String> locales;
178 
179     scim_split_string_list (locales, get_locales ());
180 
181     for (size_t i=0; i<locales.size (); ++i)
182         if (scim_get_locale_encoding (locales [i]) == encoding)
183             return scim_get_locale_maxlen (locales [i]);
184 
185     return 0;
186 }
187 
188 // implementation of RawCodeInstance
RawCodeInstance(RawCodeFactory * factory,const String & encoding,int id)189 RawCodeInstance::RawCodeInstance (RawCodeFactory *factory,
190                                   const String& encoding,
191                                   int id)
192     : IMEngineInstanceBase (factory, encoding, id),
193       m_factory (factory)
194 {
195     if (!m_client_iconv.set_encoding (encoding))
196         m_client_iconv.set_encoding ("UTF-8");
197 
198     set_working_encoding ("Unicode");
199 }
200 
~RawCodeInstance()201 RawCodeInstance::~RawCodeInstance ()
202 {
203 }
204 
205 bool
process_key_event(const KeyEvent & key)206 RawCodeInstance::process_key_event (const KeyEvent& key)
207 {
208     if (key.is_key_release ()) return true;
209 
210     // switch between unicode and native code mode
211     if ((key.code == SCIM_KEY_u || key.code == SCIM_KEY_U) && key.is_control_down ()) {
212         if (m_unicode)
213             set_working_encoding (get_encoding ());
214         else
215             set_working_encoding ("Unicode");
216 
217         reset ();
218         return true;
219     }
220 
221     //reset key
222     if (key.code == SCIM_KEY_Escape && key.mask == 0) {
223         reset ();
224         return true;
225     }
226 
227     //delete key
228     if (key.code == SCIM_KEY_BackSpace && key.mask == 0 &&
229         m_preedit_string.size () != 0) {
230         m_preedit_string.erase (m_preedit_string.length () - 1, 1);
231         update_preedit_string (m_preedit_string);
232         update_preedit_caret (m_preedit_string.length ());
233         process_preedit_string ();
234         return true;
235     }
236 
237     // valid keys
238     if (((key.code >= SCIM_KEY_0 && key.code <= SCIM_KEY_9) ||
239          (key.code >= SCIM_KEY_A && key.code <= SCIM_KEY_F) ||
240          (key.code >= SCIM_KEY_a && key.code <= SCIM_KEY_f)) &&
241         (key.mask == 0 || key.is_shift_down ()) &&
242         m_preedit_string.length () < m_max_preedit_len) {
243 
244         if (m_preedit_string.length () == 0)
245             show_preedit_string ();
246 
247         ucs4_t ascii = (ucs4_t) tolower (key.get_ascii_code ());
248         m_preedit_string.push_back (ascii);
249         update_preedit_string (m_preedit_string);
250         update_preedit_caret (m_preedit_string.length ());
251         process_preedit_string ();
252         return true;
253     }
254 
255     // commit key
256     if (key.code == SCIM_KEY_space && key.mask == 0 && m_preedit_string.length ()
257         && m_lookup_table.number_of_candidates ()) {
258         WideString str = m_lookup_table.get_candidate_label (0);
259         // It's already a complete char, commit it.
260         if (str.length () && str [0] == 0x20) {
261             commit_string (m_lookup_table.get_candidate_in_current_page (0));
262             reset ();
263             return true;
264         }
265     }
266 
267     //page up key.
268     if ((key.code == SCIM_KEY_comma || key.code == SCIM_KEY_minus || key.code == SCIM_KEY_bracketleft
269          || key.code == SCIM_KEY_Page_Up) && key.mask == 0)
270         lookup_table_page_up ();
271 
272     //page down key.
273     if ((key.code == SCIM_KEY_period || key.code == SCIM_KEY_equal || key.code == SCIM_KEY_bracketright
274          || key.code == SCIM_KEY_Page_Down) && key.mask == 0)
275         lookup_table_page_down ();
276 
277     //other keys is not allowed when preediting
278     if (m_preedit_string.length ())
279         return true;
280 
281     return false;
282 }
283 
284 void
select_candidate(unsigned int item)285 RawCodeInstance::select_candidate (unsigned int item)
286 {
287     WideString label = m_lookup_table.get_candidate_label (item);
288     KeyEvent key ((int) label [0], 0);
289     process_key_event (key);
290 }
291 
292 void
update_lookup_table_page_size(unsigned int page_size)293 RawCodeInstance::update_lookup_table_page_size (unsigned int page_size)
294 {
295     if (page_size > 0)
296         m_lookup_table.set_page_size (page_size);
297 }
298 
299 void
lookup_table_page_up()300 RawCodeInstance::lookup_table_page_up ()
301 {
302     if (m_preedit_string.length () && m_lookup_table.number_of_candidates ()) {
303         m_lookup_table.page_up ();
304         m_lookup_table.set_page_size (m_lookup_table.number_of_candidates ());
305 
306         m_lookup_table.set_candidate_labels (
307             std::vector <WideString> (
308                 m_lookup_table_labels.begin () + m_lookup_table.get_current_page_start (),
309                 m_lookup_table_labels.end ()));
310 
311         update_lookup_table (m_lookup_table);
312     }
313 }
314 
315 void
lookup_table_page_down()316 RawCodeInstance::lookup_table_page_down ()
317 {
318     if (m_preedit_string.length () && m_lookup_table.number_of_candidates ()) {
319         m_lookup_table.page_down ();
320         m_lookup_table.set_page_size (m_lookup_table.number_of_candidates ());
321 
322         m_lookup_table.set_candidate_labels (
323             std::vector <WideString> (
324                 m_lookup_table_labels.begin () + m_lookup_table.get_current_page_start (),
325                 m_lookup_table_labels.end ()));
326 
327         update_lookup_table (m_lookup_table);
328     }
329 }
330 
331 void
move_preedit_caret(unsigned int)332 RawCodeInstance::move_preedit_caret (unsigned int /*pos*/)
333 {
334 }
335 
336 void
reset()337 RawCodeInstance::reset ()
338 {
339     if (!m_client_iconv.set_encoding (get_encoding ()))
340         m_client_iconv.set_encoding ("UTF-8");
341 
342     m_preedit_string = WideString ();
343 
344     m_lookup_table.clear ();
345 
346     hide_lookup_table ();
347     hide_preedit_string ();
348 }
349 
350 void
set_working_encoding(const String & encoding)351 RawCodeInstance::set_working_encoding (const String &encoding)
352 {
353     unsigned int maxlen = m_factory->get_maxlen (encoding);
354 
355     if (maxlen && encoding != "Unicode" && m_working_iconv.set_encoding (encoding)){
356         m_unicode = false;
357         m_max_preedit_len = maxlen * 2;
358         m_working_encoding = encoding;
359     } else {
360         m_unicode = true;
361         m_working_encoding = "Unicode";
362         m_max_preedit_len = 6;
363     }
364 
365     refresh_encoding_property ();
366 }
367 
368 void
focus_in()369 RawCodeInstance::focus_in ()
370 {
371     initialize_properties ();
372 
373     if (m_preedit_string.length ()) {
374         update_preedit_string (m_preedit_string);
375         update_preedit_caret (m_preedit_string.length ());
376         show_preedit_string ();
377         if (m_lookup_table.number_of_candidates ()) {
378             update_lookup_table (m_lookup_table);
379             show_lookup_table ();
380         }
381     }
382 }
383 
384 void
focus_out()385 RawCodeInstance::focus_out ()
386 {
387     reset ();
388 }
389 
390 void
trigger_property(const String & property)391 RawCodeInstance::trigger_property (const String &property)
392 {
393     if (property.substr (0, strlen (SCIM_PROP_RAWCODE_ENCODING)) == SCIM_PROP_RAWCODE_ENCODING) {
394         set_working_encoding (property.substr (strlen (SCIM_PROP_RAWCODE_ENCODING) + 1));
395         reset ();
396     }
397 }
398 
399 void
initialize_properties()400 RawCodeInstance::initialize_properties ()
401 {
402     PropertyList proplist;
403 
404     proplist.push_back (Property (SCIM_PROP_RAWCODE_ENCODING,
405                                   _(m_working_encoding.c_str ()),
406                                   "",
407                                   _("The status of the current input method. Click to change it.")));
408 
409     proplist.push_back (Property (String (SCIM_PROP_RAWCODE_ENCODING) + String ("/Unicode"), _("Unicode")));
410 
411     for (size_t i = 0; i < __rawcode_encodings.size (); ++i)
412         proplist.push_back (Property (String (SCIM_PROP_RAWCODE_ENCODING) + String ("/") + __rawcode_encodings [i], _(__rawcode_encodings [i].c_str ())));
413 
414     register_properties (proplist);
415 }
416 
417 void
refresh_encoding_property()418 RawCodeInstance::refresh_encoding_property ()
419 {
420     update_property (Property (SCIM_PROP_RAWCODE_ENCODING,
421                                _(m_working_encoding.c_str ()),
422                                "",
423                                _("The status of the current input method. Click to change it.")));
424 }
425 
426 void
process_preedit_string()427 RawCodeInstance::process_preedit_string ()
428 {
429     if (m_preedit_string.length () == 0) {
430         hide_preedit_string ();
431         hide_lookup_table ();
432         return;
433     }
434 
435     if (m_unicode) {
436         size_t maxlen = 6;
437 
438         if (m_preedit_string.length () > 0) {
439             if (m_preedit_string [0] == '0')
440                 maxlen = 4;
441             else if (m_preedit_string [0] == '1')
442                 maxlen = 6;
443             else
444                 maxlen = 5;
445         }
446 
447         if (m_preedit_string.length () >= 3 && m_preedit_string.length () < maxlen && create_lookup_table () > 0) {
448             update_lookup_table (m_lookup_table);
449         } else if (m_preedit_string.length () == maxlen) {
450             WideString str;
451             ucs4_t code = get_unicode_value (m_preedit_string);
452 
453             m_preedit_string = WideString ();
454             m_lookup_table.clear ();
455             hide_preedit_string ();
456 
457             // If code is valid under current encoding,
458             // then commit it.
459             if (m_client_iconv.test_convert (&code, 1) && code > 0 && code < 0x10FFFF) {
460                 str.push_back (code);
461                 commit_string (str);
462             }
463         } else if (m_lookup_table.number_of_candidates ()){
464             m_lookup_table.clear ();
465         }
466     } else {
467         String code = get_multibyte_string (m_preedit_string);
468         WideString wstr;
469 
470         // convert ok, then commit.
471         if (m_working_iconv.convert (wstr, code) && wstr.length () > 0 && wstr [0] >= 128 && m_client_iconv.test_convert (wstr)) {
472             m_preedit_string = WideString ();
473             m_lookup_table.clear ();
474 
475             hide_preedit_string ();
476             commit_string (wstr);
477         } else if (create_lookup_table () > 0) {
478             update_lookup_table (m_lookup_table);
479         }
480     }
481 
482     if (m_lookup_table.number_of_candidates ())
483         show_lookup_table ();
484     else
485         hide_lookup_table ();
486 }
487 
ascii_to_hex(int ascii)488 inline static int ascii_to_hex (int ascii)
489 {
490     if (ascii >= '0' && ascii <= '9')
491         return ascii - '0';
492     else if (ascii >= 'a' && ascii <= 'f')
493         return ascii - 'a' + 10;
494     else if (ascii >= 'A' && ascii <= 'F')
495         return ascii - 'A' + 10;
496     return 0;
497 }
hex_to_ascii(int hex)498 inline static int hex_to_ascii (int hex)
499 {
500     hex %= 16;
501 
502     if (hex >= 0 && hex <= 9)
503         return hex + '0';
504 
505     return hex - 10 + 'a';
506 }
507 
508 String
get_multibyte_string(const WideString & preedit)509 RawCodeInstance::get_multibyte_string (const WideString & preedit)
510 {
511     String str;
512     char ch = 0;
513 
514     if (preedit.length () == 0)
515         return str;
516 
517     for (size_t i=0; i<preedit.length (); ++i) {
518         if (i % 2 == 0)
519             ch = (char) ascii_to_hex ((int) preedit [i]) & 0x0f;
520         else {
521             ch = (ch << 4) | ((char) ascii_to_hex ((int) preedit [i]) & 0x0f);
522             str.push_back (ch);
523             ch = 0;
524         }
525     }
526 
527     if (ch != 0)
528         str.push_back (ch);
529 
530     return str;
531 }
532 
533 ucs4_t
get_unicode_value(const WideString & preedit)534 RawCodeInstance::get_unicode_value (const WideString &preedit)
535 {
536     ucs4_t code = 0;
537     for (size_t i=0; i<preedit.length (); ++i) {
538         code = (code << 4) | (ascii_to_hex ((int) preedit [i]) & 0x0f);
539     }
540     return code;
541 }
542 
543 int
create_lookup_table()544 RawCodeInstance::create_lookup_table ()
545 {
546     String mbs_code;
547     ucs4_t ucs_code;
548     WideString trail;
549     WideString wstr;
550 
551     m_lookup_table.clear ();
552     m_lookup_table_labels.clear ();
553 
554     trail.push_back (0x20);
555 
556     if (m_unicode) {
557         ucs_code = get_unicode_value (m_preedit_string);
558         if (m_client_iconv.test_convert (&ucs_code, 1) && ucs_code > 0 && ucs_code < 0x10FFFF) {
559             m_lookup_table_labels.push_back (trail);
560             m_lookup_table.append_candidate (ucs_code);
561         }
562     }
563 
564     for (int i=0; i<16; ++i) {
565         trail [0] = (ucs4_t) hex_to_ascii (i);
566         if (m_unicode) {
567             ucs_code = get_unicode_value (m_preedit_string + trail);
568             if (m_client_iconv.test_convert (&ucs_code, 1) && ucs_code > 0 && ucs_code < 0x10FFFF) {
569                 m_lookup_table_labels.push_back (trail);
570                 m_lookup_table.append_candidate (ucs_code);
571             }
572         } else {
573             mbs_code = get_multibyte_string (m_preedit_string + trail);
574             if (m_working_iconv.convert (wstr, mbs_code) && wstr.length () > 0 && wstr [0] >= 128 && m_client_iconv.test_convert (wstr)) {
575                 m_lookup_table_labels.push_back (trail);
576                 m_lookup_table.append_candidate (wstr);
577             }
578         }
579     }
580 
581     m_lookup_table.set_page_size (m_lookup_table_labels.size ());
582     m_lookup_table.set_candidate_labels (m_lookup_table_labels);
583 
584     return m_lookup_table_labels.size ();
585 }
586 /*
587 vi:ts=4:nowrap:ai:expandtab
588 */
589