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