Tizen 2.1 base
[framework/uifw/ise-engine-anthy.git] / src / scim_anthy_preedit.cpp
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  *  Copyright (C) 2004-2005 Takuro Ashie
4  *
5  *  This program is free software; you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation; either version 2, or (at your option)
8  *  any later version.
9  *
10  *  This program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License
16  *  along with this program; if not, write to the Free Software
17  *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18  */
19
20 #ifdef HAVE_CONFIG_H
21   #include <config.h>
22 #endif
23
24 #include <string.h>
25
26 #include "scim_anthy_factory.h"
27 #include "scim_anthy_imengine.h"
28 #include "scim_anthy_preedit.h"
29 #include "scim_anthy_utils.h"
30
31 using namespace scim_anthy;
32
33 static ConvRule *get_period_rule              (TypingMethod method,
34                                                PeriodStyle  period);
35 static ConvRule *get_comma_rule               (TypingMethod method,
36                                                CommaStyle   period);
37
38 Preedit::Preedit (AnthyInstance &anthy)
39     : m_anthy              (anthy),
40       //m_key2kana_tables    (tables),
41       m_reading            (anthy),
42       m_conversion         (m_anthy, m_reading),
43       m_input_mode         (SCIM_ANTHY_MODE_HIRAGANA)
44 {
45 }
46
47 Preedit::~Preedit ()
48 {
49 }
50
51
52 /*
53  * getting status
54  */
55 unsigned int
56 Preedit::get_length (void)
57 {
58     if (is_converting ())
59         return m_conversion.get_length ();
60     else
61         return m_reading.get_length ();
62
63     return 0;
64 }
65
66 WideString
67 Preedit::get_string (void)
68 {
69     if (is_converting ()) {
70         return m_conversion.get ();
71     } else if (!m_source.empty ()) {
72         return m_source;
73     } else {
74         WideString widestr;
75         switch (m_input_mode) {
76         case SCIM_ANTHY_MODE_KATAKANA:
77             util_convert_to_katakana (widestr, m_reading.get ());
78             return widestr;
79
80         case SCIM_ANTHY_MODE_HALF_KATAKANA:
81             util_convert_to_katakana (widestr, m_reading.get (), true);
82             return widestr;
83
84         case SCIM_ANTHY_MODE_LATIN:
85             return utf8_mbstowcs (m_reading.get_raw ());
86
87         case SCIM_ANTHY_MODE_WIDE_LATIN:
88             util_convert_to_wide (widestr, m_reading.get_raw ());
89             return widestr;
90
91         case SCIM_ANTHY_MODE_HIRAGANA:
92         default:
93             return m_reading.get ();
94         }
95     }
96
97     return WideString ();
98 }
99
100 AttributeList
101 Preedit::get_attribute_list (void)
102 {
103     if (is_converting ())
104     {
105         return m_conversion.get_attribute_list ();
106     } else {
107         AttributeList attrs;
108         util_create_attributes (attrs, 0, get_length (),
109                                 m_anthy.get_factory()->m_preedit_style,
110                                 m_anthy.get_factory()->m_preedit_fg_color,
111                                 m_anthy.get_factory()->m_preedit_bg_color);
112         return attrs;
113     }
114 }
115
116 Reading &
117 Preedit::get_reading (void)
118 {
119     return m_reading;
120 }
121
122 bool
123 Preedit::is_preediting (void)
124 {
125     if (m_reading.get_length () > 0 ||
126         m_conversion.is_converting () ||
127         !m_source.empty ())
128     {
129         return true;
130     } else {
131         return false;
132     }
133 }
134
135 bool
136 Preedit::is_converting (void)
137 {
138     return m_conversion.is_converting ();
139 }
140
141 bool
142 Preedit::is_predicting (void)
143 {
144     return m_conversion.is_predicting ();
145 }
146
147 bool
148 Preedit::is_reconverting (void)
149 {
150     return !m_source.empty ();
151 }
152
153
154 /*
155  * manipulating the preedit string
156  */
157 bool
158 Preedit::can_process_key_event (const KeyEvent & key)
159 {
160     return m_reading.can_process_key_event (key);
161 }
162
163 bool
164 Preedit::process_key_event (const KeyEvent & key)
165 {
166     if (!m_reading.can_process_key_event (key))
167         return false;
168
169     bool retval = m_reading.process_key_event (key);
170
171     if (m_input_mode == SCIM_ANTHY_MODE_LATIN ||
172         m_input_mode == SCIM_ANTHY_MODE_WIDE_LATIN)
173     {
174         return true;
175     }
176
177     // auto convert
178     unsigned int len = m_reading.get_length ();
179     if (len > 0) {
180         String str;
181         str = m_reading.get_raw (len - 1, 1);
182         if (is_comma_or_period (str)) {
183             if (m_anthy.get_factory()->m_behavior_on_period == "Convert" &&
184                 get_length () > 1)
185             {
186                 convert ();
187             } else if (m_anthy.get_factory()->m_behavior_on_period == "Commit") {
188                 return true;
189             }
190         }
191     }
192
193     return retval;
194 }
195
196 bool
197 Preedit::append (const KeyEvent & key,
198                  const String   & string)
199 {
200     return m_reading.append (key, string);
201 }
202
203 void
204 Preedit::erase (bool backward)
205 {
206     if (m_reading.get_length () <= 0)
207         return;
208
209     // cancel conversion
210     revert ();
211
212     // erase
213     TypingMethod method = get_typing_method ();
214     bool allow_split
215         = method == SCIM_ANTHY_TYPING_METHOD_ROMAJI &&
216           m_anthy.get_factory()->m_romaji_allow_split;
217     if (backward && m_reading.get_caret_pos () == 0)
218         return;
219     if (!backward && m_reading.get_caret_pos () >= m_reading.get_length ())
220         return;
221     if (backward)
222         m_reading.move_caret (-1, allow_split);
223     m_reading.erase (m_reading.get_caret_pos (), 1, allow_split);
224 }
225
226 void
227 Preedit::finish (void)
228 {
229     m_reading.finish ();
230 }
231
232
233 /*
234  * manipulating conversion string
235  */
236 void
237 Preedit::convert (CandidateType type, bool single_segment)
238 {
239     if (m_source.empty ())
240         m_conversion.convert (type, single_segment);
241     else
242         m_conversion.convert (m_source, single_segment);
243 }
244
245 void
246 Preedit::convert (const WideString &source, bool single_segment)
247 {
248     m_conversion.convert (source, single_segment);
249     m_source = source;
250 }
251
252 void
253 Preedit::revert (void)
254 {
255     m_conversion.clear ();
256 }
257
258 void
259 Preedit::commit (int segment_id, bool learn)
260 {
261     if (m_conversion.is_converting ())
262         m_conversion.commit (segment_id, learn);
263     if (!m_conversion.is_converting ())
264         clear ();
265 }
266
267 int
268 Preedit::get_nr_segments (void)
269 {
270     return m_conversion.get_nr_segments ();
271 }
272
273 WideString
274 Preedit::get_segment_string (int segment_id)
275 {
276     return m_conversion.get_segment_string (segment_id);
277 }
278
279 int
280 Preedit::get_selected_segment (void)
281 {
282     return m_conversion.get_selected_segment ();
283 }
284
285 void
286 Preedit::select_segment (int segment_id)
287 {
288     m_conversion.select_segment (segment_id);
289 }
290
291 int
292 Preedit::get_segment_size (int segment_id)
293 {
294     return m_conversion.get_segment_size (segment_id);
295 }
296
297 void
298 Preedit::resize_segment (int relative_size, int segment_id)
299 {
300     m_conversion.resize_segment (relative_size, segment_id);
301 }
302
303
304 /*
305  * candidates for a segment
306  */
307 void
308 Preedit::get_candidates (CommonLookupTable &table, int segment_id)
309 {
310     m_conversion.get_candidates (table, segment_id);
311 }
312
313 int
314 Preedit::get_selected_candidate (int segment_id)
315 {
316     return m_conversion.get_selected_candidate (segment_id);
317 }
318
319 void
320 Preedit::select_candidate (int candidate_id, int segment_id)
321 {
322     m_conversion.select_candidate (candidate_id, segment_id);
323 }
324
325
326 /*
327  * manipulating the caret
328  */
329 unsigned int
330 Preedit::get_caret_pos (void)
331 {
332     if (is_converting ()) {
333         return m_conversion.get_segment_position ();
334     } else {
335         if (get_input_mode () == SCIM_ANTHY_MODE_HALF_KATAKANA) {
336             // FIXME! It's ad-hoc
337             WideString substr;
338             substr = m_reading.get (0, m_reading.get_caret_pos (),
339                                     SCIM_ANTHY_STRING_HALF_KATAKANA);
340             return substr.length ();
341         } else {
342             return m_reading.get_caret_pos ();
343         }
344     }
345 }
346
347 void
348 Preedit::set_caret_pos (unsigned int pos)
349 {
350     if (is_converting ())
351         return;
352
353     m_reading.set_caret_pos (pos);
354 }
355
356 void
357 Preedit::move_caret (int step)
358 {
359     if (is_converting ())
360         return;
361
362     TypingMethod method = get_typing_method ();
363     bool allow_split
364         = method == SCIM_ANTHY_TYPING_METHOD_ROMAJI &&
365           m_anthy.get_factory()->m_romaji_allow_split;
366
367     m_reading.move_caret (step, allow_split);
368 }
369
370 void
371 Preedit::predict (void)
372 {
373     m_conversion.predict ();
374 }
375
376
377 /*
378  * clear all string
379  */
380 void
381 Preedit::clear (int segment_id)
382 {
383     // FIXME! We should add implementation not only for conversion string but
384     // also for reading string.
385
386     if (!is_converting ()) {
387         m_reading.clear ();
388         m_conversion.clear ();
389         m_source = WideString ();
390         return;
391     }
392
393     m_conversion.clear (segment_id);
394     if (m_conversion.get_nr_segments () <= 0) {
395         m_reading.clear ();
396         m_source = WideString ();
397     }
398 }
399
400
401 /*
402  * preference
403  */
404 void
405 Preedit::set_input_mode (InputMode mode)
406 {
407     m_input_mode = mode;
408 }
409
410 InputMode
411 Preedit::get_input_mode (void)
412 {
413     return m_input_mode;
414 }
415
416 void
417 Preedit::set_typing_method (TypingMethod method)
418 {
419     m_reading.set_typing_method (method);
420 }
421
422 TypingMethod
423 Preedit::get_typing_method (void)
424 {
425     return m_reading.get_typing_method ();
426 }
427
428 void
429 Preedit::set_period_style (PeriodStyle style)
430 {
431     m_reading.set_period_style (style);
432 }
433
434 PeriodStyle
435 Preedit::get_period_style (void)
436 {
437     return m_reading.get_period_style ();
438 }
439
440 void
441 Preedit::set_comma_style (CommaStyle style)
442 {
443     m_reading.set_comma_style (style);
444 }
445
446 CommaStyle
447 Preedit::get_comma_style (void)
448 {
449     return m_reading.get_comma_style ();
450 }
451
452 void
453 Preedit::set_bracket_style (BracketStyle style)
454 {
455     m_reading.set_bracket_style (style);
456 }
457
458 BracketStyle
459 Preedit::get_bracket_style (void)
460 {
461     return m_reading.get_bracket_style ();
462 }
463
464 void
465 Preedit::set_slash_style (SlashStyle style)
466 {
467     m_reading.set_slash_style (style);
468 }
469
470 SlashStyle
471 Preedit::get_slash_style (void)
472 {
473     return m_reading.get_slash_style ();
474 }
475
476 void
477 Preedit::set_symbol_width (bool half)
478 {
479     m_reading.set_symbol_width (half);
480 }
481
482 bool
483 Preedit::get_symbol_width (void)
484 {
485     return m_reading.get_symbol_width ();
486 }
487
488 void
489 Preedit::set_number_width (bool half)
490 {
491     m_reading.set_number_width (half);
492 }
493
494 bool
495 Preedit::get_number_width (void)
496 {
497     return m_reading.get_number_width ();
498 }
499
500 void
501 Preedit::set_pseudo_ascii_mode (int mode)
502 {
503     m_reading.set_pseudo_ascii_mode (mode);
504 }
505
506 bool
507 Preedit::is_pseudo_ascii_mode (void)
508 {
509     return m_reading.is_pseudo_ascii_mode ();
510 }
511
512 void
513 Preedit::reset_pseudo_ascii_mode (void)
514 {
515     m_reading.reset_pseudo_ascii_mode ();
516 }
517
518 void
519 Preedit::set_dict_encoding (String type)
520 {
521     m_conversion.set_dict_encoding (type);
522 }
523
524 bool
525 Preedit::is_comma_or_period (const String & str)
526 {
527     TypingMethod typing = get_typing_method ();
528     PeriodStyle  period = get_period_style ();
529     CommaStyle   comma  = get_comma_style ();
530
531     ConvRule *period_rule = get_period_rule (typing, period);
532     ConvRule *comma_rule  = get_comma_rule  (typing, comma);
533
534     for (unsigned int i = 0; period_rule && period_rule[i].string; i++) {
535         if (period_rule[i].string &&
536             !strcmp (period_rule[i].string, str.c_str ()))
537         {
538             return true;
539         }
540     }
541     for (unsigned int i = 0; comma_rule && comma_rule[i].string; i++) {
542         if (comma_rule[i].string &&
543             !strcmp (comma_rule[i].string, str.c_str ()))
544         {
545             return true;
546         }
547     }
548
549     return false;
550 }
551
552
553 /*
554  * utilities
555  */
556 static ConvRule *
557 get_period_rule (TypingMethod method, PeriodStyle period)
558 {
559     switch (method) {
560     case SCIM_ANTHY_TYPING_METHOD_KANA:
561         switch (period) {
562         case SCIM_ANTHY_PERIOD_WIDE:
563             return scim_anthy_kana_wide_period_rule;
564         case SCIM_ANTHY_PERIOD_HALF:
565             return scim_anthy_kana_half_period_rule;
566         case SCIM_ANTHY_PERIOD_JAPANESE:
567         default:
568             return scim_anthy_kana_ja_period_rule;
569         };
570         break;
571
572     case SCIM_ANTHY_TYPING_METHOD_ROMAJI:
573     default:
574         switch (period) {
575         case SCIM_ANTHY_PERIOD_WIDE:
576             return scim_anthy_romaji_wide_period_rule;
577         case SCIM_ANTHY_PERIOD_HALF:
578             return scim_anthy_romaji_half_period_rule;
579         case SCIM_ANTHY_PERIOD_JAPANESE:
580         default:
581             return scim_anthy_romaji_ja_period_rule;
582         };
583         break;
584     };
585
586     return NULL;
587 }
588
589 static ConvRule *
590 get_comma_rule (TypingMethod method, CommaStyle period)
591 {
592     switch (method) {
593     case SCIM_ANTHY_TYPING_METHOD_KANA:
594         switch (period) {
595         case SCIM_ANTHY_PERIOD_WIDE:
596             return scim_anthy_kana_wide_comma_rule;
597         case SCIM_ANTHY_PERIOD_HALF:
598             return scim_anthy_kana_half_comma_rule;
599         case SCIM_ANTHY_PERIOD_JAPANESE:
600         default:
601             return scim_anthy_kana_ja_comma_rule;
602         };
603         break;
604
605     case SCIM_ANTHY_TYPING_METHOD_ROMAJI:
606     default:
607         switch (period) {
608         case SCIM_ANTHY_PERIOD_WIDE:
609             return scim_anthy_romaji_wide_comma_rule;
610         case SCIM_ANTHY_PERIOD_HALF:
611             return scim_anthy_romaji_half_comma_rule;
612         case SCIM_ANTHY_PERIOD_JAPANESE:
613         default:
614             return scim_anthy_romaji_ja_comma_rule;
615         };
616         break;
617     };
618
619     return NULL;
620 }
621 /*
622 vi:ts=4:nowrap:ai:expandtab
623 */