1 /* -*- Mode: C++; indent-tabs-mode: nil; c-basic-offset: 4  -*- */
2 /*
3  *  Copyright (C) 2004 Jun Mukai
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 #define Uses_SCIM_UTILITY
21 #define Uses_SCIM_LOOKUP_TABLE
22 
23 #include <scim.h>
24 
25 #include "scim_skk_core.h"
26 #include "conv_table.h"
27 
28 using namespace scim_skk;
29 
30 static void convert_char_to_wide         (const char c, WideString &result);
31 static void convert_hiragana_to_katakana (const WideString &hira,
32                                           WideString &kata,
33                                           bool half = false);
34 
35 static const int skk_key_mask = SCIM_KEY_ControlMask | SCIM_KEY_AltMask;
36 
37 
38 
SKKCore(KeyBind * keybind,SKKAutomaton * key2kana,SKKDictionary * dict,History & hist)39 SKKCore::SKKCore      (KeyBind *keybind, SKKAutomaton *key2kana,
40                        SKKDictionary *dict, History &hist)
41     : m_keybind(keybind),
42       m_hist(hist),
43       m_histmgr(hist),
44       m_dict(dict),
45       m_skk_mode(SKK_MODE_HIRAGANA),
46       m_input_mode(INPUT_MODE_DIRECT),
47       m_key2kana(key2kana),
48       m_learning(0),
49       m_commit_flag(false),
50       m_end_flag(false),
51       m_preedit_pos(0),
52       m_commit_pos(0),
53       m_ltable()
54 {
55     std::vector<WideString> result;
56     m_keybind->selection_labels(result);
57     m_ltable.set_page_size(m_keybind->selection_key_length());
58     m_ltable.set_candidate_labels(result);
59     m_ltable.show_cursor(true);
60     clear_preedit();
61     clear_commit();
62     clear_pending(false);
63 }
64 
~SKKCore(void)65 SKKCore::~SKKCore     (void)
66 {
67     clear();
68     if (m_learning)
69         delete m_learning;
70 }
71 
72 WideString &
get_commit_string(void)73 SKKCore::get_commit_string (void)
74 {
75     return m_commitstr;
76 }
77 
78 void
get_preedit_attributes(AttributeList & alist)79 SKKCore::get_preedit_attributes (AttributeList &alist)
80 {
81     alist.clear();
82     switch (m_input_mode) {
83     case INPUT_MODE_CONVERTING:
84         if (m_ltable.visible_table()) {
85             int cpos = m_ltable.get_cursor_pos();
86             int len1 = m_ltable.get_cand(cpos).length();
87             int len2 = m_ltable.get_annot(cpos).length();
88             alist.push_back(Attribute(1, len1, SCIM_ATTR_DECORATE,
89                                       SCIM_ATTR_DECORATE_HIGHLIGHT));
90             if (annot_highlight && len2 > 0)
91                 alist.push_back(Attribute(len1+m_okuristr.length()+2, len2,
92                                           SCIM_ATTR_BACKGROUND,
93                                           annot_bgcolor));
94         } else {
95             int len1 = m_ltable.get_cand_from_vector().length();
96             int len2 = m_ltable.get_annot_from_vector().length();
97             alist.push_back(Attribute(1, len1, SCIM_ATTR_DECORATE,
98                                       SCIM_ATTR_DECORATE_HIGHLIGHT));
99             if (annot_highlight && len2 > 0)
100                 alist.push_back(Attribute(len1+m_okuristr.length()+2, len2,
101                                           SCIM_ATTR_BACKGROUND,
102                                           annot_bgcolor));
103         }
104         break;
105     default:
106         break;
107     }
108 }
109 
110 void
get_preedit_string(WideString & result)111 SKKCore::get_preedit_string (WideString &result)
112 {
113     if (!m_commitstr.empty()) {
114         result += m_commitstr.substr(0, m_commit_pos);
115     }
116 
117     switch (m_input_mode) {
118     case INPUT_MODE_OKURI:
119         result += utf8_mbstowcs("\xE2\x96\xBD");
120         result += m_preeditstr;
121         result += utf8_mbstowcs("*");
122         result += m_okuristr;
123         result += m_pendingstr;
124         break;
125     case INPUT_MODE_PREEDIT:
126         result += utf8_mbstowcs("\xE2\x96\xBD");
127         if (m_skk_mode == SKK_MODE_HIRAGANA) {
128             result += m_preeditstr.substr(0, m_preedit_pos);
129         } else {
130             convert_hiragana_to_katakana(m_preeditstr.substr(0, m_preedit_pos),
131                                          result,
132                                          m_skk_mode == SKK_MODE_HALF_KATAKANA);
133         }
134         result += m_pendingstr;
135         if (m_skk_mode == SKK_MODE_HIRAGANA) {
136             result += m_preeditstr.substr(m_preedit_pos,
137                                           m_preeditstr.length());
138         } else {
139             convert_hiragana_to_katakana(m_preeditstr.substr(m_preedit_pos,
140                                          m_preeditstr.length()),
141                                          result,
142                                          m_skk_mode == SKK_MODE_HALF_KATAKANA);
143         }
144         break;
145     case INPUT_MODE_CONVERTING:
146         result += utf8_mbstowcs("\xE2\x96\xBC");
147         if (m_ltable.visible_table()) {
148             int cpos = m_ltable.get_cursor_pos();
149             result += m_ltable.get_cand(cpos);
150         } else {
151             result += m_ltable.get_cand_from_vector();
152         }
153         if (!m_okuristr.empty()) {
154             result += m_okuristr;
155         }
156         if (annot_view && annot_pos &&
157             !m_ltable.visible_table()) {
158             WideString annot = m_ltable.get_annot_from_vector();
159             if (annot.length() > 0) {
160                 result += utf8_mbstowcs(";");
161                 result += m_ltable.get_annot_from_vector();
162             }
163         }
164         break;
165     case INPUT_MODE_LEARNING:
166         result += utf8_mbstowcs("\xE2\x96\xBC");
167         result += m_preeditstr;
168         if (!m_okuristr.empty()) {
169             result += utf8_mbstowcs("*");
170             result += m_okuristr;
171         }
172         result += utf8_mbstowcs("\xE3\x80\x90");
173         m_learning->get_preedit_string(result);
174         result += utf8_mbstowcs("\xE3\x80\x91");
175         break;
176     case INPUT_MODE_DIRECT:
177         result += m_pendingstr;
178     }
179 
180     if (!m_commitstr.empty()) {
181         result += m_commitstr.substr(m_commit_pos,
182                                      m_commitstr.length() - m_commit_pos);
183     }
184 }
185 
186 void
commit_string(const WideString & str)187 SKKCore::commit_string (const WideString &str)
188 {
189     m_commitstr.insert(m_commit_pos, str);
190     m_commit_pos += str.length();
191     m_commit_flag = true;
192 }
193 
194 void
commit_or_preedit(const WideString & str)195 SKKCore::commit_or_preedit (const WideString &str)
196 {
197     switch (m_input_mode) {
198     case INPUT_MODE_PREEDIT:
199         if (m_preedit_pos < m_preeditstr.length()) {
200             m_preeditstr.insert(m_preedit_pos, str);
201         } else {
202             m_preeditstr += str;
203         }
204         m_preedit_pos += str.length();
205         m_histmgr.clear();
206         break;
207     case INPUT_MODE_OKURI:
208         m_okuristr += str;
209         if (m_pendingstr.empty()) {
210             m_ltable.clear();
211             m_dict->lookup(m_preeditstr+m_okurihead, true, m_ltable);
212             if (m_ltable.empty()) {
213                 set_input_mode(INPUT_MODE_LEARNING);
214                 m_learning = new SKKCore(m_keybind, m_key2kana,
215                                          m_dict, m_hist);
216             } else {
217                 set_input_mode(INPUT_MODE_CONVERTING);
218             }
219         }
220         break;
221     default:
222         if (m_skk_mode == SKK_MODE_KATAKANA ||
223             m_skk_mode == SKK_MODE_HALF_KATAKANA) {
224             WideString katakana;
225             convert_hiragana_to_katakana(str, katakana,
226                                          m_skk_mode == SKK_MODE_HALF_KATAKANA);
227             commit_string(katakana);
228         } else {
229             commit_string(str);
230         }
231         break;
232     }
233 }
234 
235 void
commit_converting(int index)236 SKKCore::commit_converting (int index)
237 {
238     if (!m_ltable.vector_empty() && !m_ltable.visible_table()) {
239         CandEnt cent = m_ltable.get_candent_from_vector();
240         commit_string(cent.cand);
241         commit_string(m_okuristr);
242         if (m_okurihead != 0)
243             m_preeditstr += m_okurihead;
244         m_dict->write(m_preeditstr, cent);
245         m_ltable.clear();
246         clear_preedit();
247         if (m_skk_mode == SKK_MODE_ASCII)
248             set_skk_mode(SKK_MODE_HIRAGANA);
249     } else {
250         if (index < 0) {
251             index = m_ltable.get_cursor_pos();
252         } else {
253             index += m_ltable.get_current_page_start();
254         }
255         WideString cand  = m_ltable.get_cand(index);
256         WideString annot = m_ltable.get_annot(index);
257         WideString cand_orig = m_ltable.get_cand_orig(index);
258         commit_string(cand);
259         commit_string(m_okuristr);
260         if (m_okurihead != 0)
261             m_preeditstr += m_okurihead;
262         m_dict->write(m_preeditstr, CandEnt(cand, annot, cand_orig));
263         m_ltable.clear();
264         clear_preedit();
265         if (m_skk_mode == SKK_MODE_ASCII)
266             set_skk_mode(SKK_MODE_HIRAGANA);
267     }
268 }
269 
270 
271 int
caret_pos(void)272 SKKCore::caret_pos (void)
273 {
274     int base_pos = m_commit_pos + m_pendingstr.length();
275 
276     switch (m_input_mode) {
277     case INPUT_MODE_DIRECT:
278         return base_pos;
279     case INPUT_MODE_PREEDIT:
280         return base_pos + m_preedit_pos + 1;
281     case INPUT_MODE_OKURI:
282         return base_pos + m_preeditstr.length() + 2;
283     case INPUT_MODE_CONVERTING:
284         if (m_ltable.visible_table()) {
285             int cpos = m_ltable.get_cursor_pos();
286             base_pos += m_ltable.get_candidate(cpos).length() + 1;
287         } else {
288             base_pos += m_ltable.get_candidate_from_vector().length() + 1;
289         }
290         if (!m_okuristr.empty())
291             base_pos += m_okuristr.length();
292         return base_pos;
293     case INPUT_MODE_LEARNING:
294         if (!m_okuristr.empty())
295             base_pos += m_okuristr.length() + 1;
296         return base_pos + m_preeditstr.length() + 2 + m_learning->caret_pos();
297     }
298 
299     return base_pos;
300 }
301 
302 void
move_preedit_caret(int pos)303 SKKCore::move_preedit_caret (int pos)
304 {
305     if (pos < 0)
306         return;
307 
308     switch (m_input_mode) {
309     case INPUT_MODE_DIRECT:
310         if (pos <= m_commitstr.length()) {
311             m_commit_pos = pos;
312         }
313         break;
314     case INPUT_MODE_PREEDIT:
315         if (pos < m_commit_pos) {
316             m_commit_pos = pos;
317         } else if (pos > m_commit_pos &&
318                    pos <= m_commit_pos + 1 + m_preeditstr.length()) {
319             m_preedit_pos = pos - m_commit_pos - 1;
320             clear_pending();
321         } else if (pos > m_commit_pos + m_preeditstr.length() + 1 &&
322                    pos <= m_commitstr.length() + m_preeditstr.length() + 1) {
323             m_commit_pos = pos - m_preeditstr.length() - 1;
324         }
325         break;
326     case INPUT_MODE_OKURI:
327         if (pos < m_commit_pos) {
328             m_commit_pos = pos;
329         } else if (pos > m_commit_pos +
330                    m_preeditstr.length() + m_pendingstr.length() + 2 &&
331                    pos <= m_commitstr.length() +
332                    m_preeditstr.length() + m_pendingstr.length() + 2) {
333             m_commit_pos = pos - m_preeditstr.length() - m_pendingstr.length() - 2;
334         }
335         break;
336     case INPUT_MODE_CONVERTING:
337         if (pos < m_commit_pos) {
338             m_commit_pos = pos;
339         } else if (pos > m_commit_pos +
340                    m_ltable.get_candidate_from_vector().length() +
341                    m_okuristr.length() + 1 &&
342                    pos <= m_commitstr.length() +
343                    m_ltable.get_candidate_from_vector().length() +
344                    m_okuristr.length() + 1) {
345             m_commit_pos = pos - m_ltable.get_candidate_from_vector().length() - m_okuristr.length() - 1;
346         }
347         break;
348     case INPUT_MODE_LEARNING:
349         pos -= m_commitstr.length();
350         pos -= m_preeditstr.length() + 2;
351         m_learning->move_preedit_caret(pos);
352         break;
353     }
354 }
355 
356 void
set_skk_mode(SKKMode newmode)357 SKKCore::set_skk_mode (SKKMode newmode)
358 {
359     if (m_learning) {
360         m_learning->set_skk_mode(newmode);
361     } else if (m_skk_mode != newmode) {
362         clear_pending();
363         m_skk_mode = newmode;
364     }
365 }
366 
367 SKKMode
get_skk_mode(void)368 SKKCore::get_skk_mode (void)
369 {
370     if (m_learning) {
371         return m_learning->get_skk_mode();
372     } else {
373         return m_skk_mode;
374     }
375 }
376 
377 void
set_input_mode(InputMode newmode)378 SKKCore::set_input_mode (InputMode newmode)
379 {
380     if (m_learning) {
381         m_learning->set_input_mode(newmode);
382     } else {
383         m_input_mode = newmode;
384     }
385 }
386 
387 InputMode
get_input_mode(void)388 SKKCore::get_input_mode (void)
389 {
390     if (m_learning) {
391         return m_learning->get_input_mode();
392     } else {
393         return m_input_mode;
394     }
395 }
396 
397 
398 void
clear_pending(bool flag)399 SKKCore::clear_pending (bool flag)
400 {
401     if (flag && m_pendingstr == utf8_mbstowcs("n")) {
402         commit_or_preedit(utf8_mbstowcs("\xE3\x82\x93"));
403     }
404     m_pendingstr.clear();
405     m_key2kana->clear();
406 }
407 
408 
409 void
clear_preedit(void)410 SKKCore::clear_preedit (void)
411 {
412     m_preeditstr.clear();
413     m_preedit_pos = 0;
414     m_okuristr.clear();
415     m_okurihead = 0;
416     m_histmgr.clear();
417 }
418 
419 void
clear_commit(void)420 SKKCore::clear_commit (void)
421 {
422     m_commit_flag = false;
423     m_commit_pos = 0;
424     m_commitstr.clear();
425 }
426 
427 void
clear(void)428 SKKCore::clear (void)
429 {
430     clear_pending();
431     clear_preedit();
432     m_ltable.clear();
433     m_commit_flag = false;
434     if (m_learning)
435         m_learning->clear();
436 }
437 
438 bool
action_kakutei(void)439 SKKCore::action_kakutei (void)
440 {
441     switch (m_input_mode) {
442     case INPUT_MODE_DIRECT:
443         if (!(m_skk_mode == SKK_MODE_ASCII ||
444               m_skk_mode == SKK_MODE_WIDE_ASCII) &&
445             m_pendingstr.empty() && m_preeditstr.empty()) {
446             m_end_flag = true;
447             return false;
448         } else {
449             clear_pending();
450         }
451         break;
452     case INPUT_MODE_PREEDIT:
453     case INPUT_MODE_OKURI:
454         set_input_mode(INPUT_MODE_DIRECT);
455         if (m_preeditstr.length() > 0) {
456             if (m_skk_mode == SKK_MODE_KATAKANA ||
457                     m_skk_mode == SKK_MODE_HALF_KATAKANA) {
458                 WideString katakana;
459                 convert_hiragana_to_katakana(m_preeditstr, katakana,
460                                              m_skk_mode == SKK_MODE_HALF_KATAKANA);
461                 commit_string(katakana);
462             } else {
463                 commit_string(m_preeditstr);
464             }
465             if (m_input_mode == INPUT_MODE_PREEDIT) {
466                 m_hist.add_entry(m_preeditstr);
467             }
468             clear_preedit();
469         }
470         clear_pending();
471         break;
472     case INPUT_MODE_CONVERTING:
473         commit_converting();
474         set_input_mode(INPUT_MODE_DIRECT);
475         break;
476     default:
477         break;
478     }
479     if(m_skk_mode == SKK_MODE_ASCII || m_skk_mode == SKK_MODE_WIDE_ASCII) {
480         set_skk_mode(SKK_MODE_HIRAGANA);
481     }
482     return true;
483 }
484 
485 bool
action_cancel(void)486 SKKCore::action_cancel (void)
487 {
488     switch (m_input_mode) {
489     case INPUT_MODE_DIRECT:
490         if (!m_pendingstr.empty()) {
491             clear_pending(false);
492         } else {
493             clear_commit();
494             m_end_flag = true;
495             return false;
496         }
497         break;
498     case INPUT_MODE_PREEDIT:
499     case INPUT_MODE_OKURI:
500         clear_preedit();
501         clear_pending(false);
502         set_input_mode(INPUT_MODE_DIRECT);
503         if (m_skk_mode == SKK_MODE_ASCII) {
504             set_skk_mode(SKK_MODE_HIRAGANA);
505         }
506         break;
507     case INPUT_MODE_CONVERTING:
508         if (!m_okuristr.empty()) {
509             m_preeditstr += m_okuristr;
510             m_preedit_pos += m_okuristr.length();
511             m_okuristr.clear();
512             m_okurihead = 0;
513         }
514         m_ltable.clear();
515         set_input_mode(INPUT_MODE_PREEDIT);
516         break;
517     default:
518         break;
519     }
520     return true;
521 }
522 
523 bool
action_convert(void)524 SKKCore::action_convert (void)
525 {
526     bool retval;
527     switch (m_input_mode) {
528     case INPUT_MODE_CONVERTING:
529         retval = action_nextpage();
530         if (!retval) {
531             set_input_mode(INPUT_MODE_LEARNING);
532             m_learning = new SKKCore(m_keybind, m_key2kana, m_dict, m_hist);
533         }
534         return true;
535     case INPUT_MODE_PREEDIT:
536         clear_pending();
537         m_hist.add_entry(m_preeditstr);
538         m_dict->lookup(m_preeditstr, false, m_ltable);
539         if (m_ltable.empty()) {
540             set_input_mode(INPUT_MODE_LEARNING);
541             m_learning = new SKKCore(m_keybind, m_key2kana, m_dict, m_hist);
542         } else {
543             set_input_mode(INPUT_MODE_CONVERTING);
544         }
545         return true;
546     default:
547         break;
548     }
549 
550     return false;
551 }
552 
553 bool
action_katakana(bool half)554 SKKCore::action_katakana (bool half)
555 {
556     switch (m_input_mode) {
557     case INPUT_MODE_DIRECT:
558         switch (m_skk_mode) {
559         case SKK_MODE_KATAKANA:
560         case SKK_MODE_HALF_KATAKANA:
561             set_skk_mode(SKK_MODE_HIRAGANA);
562             break;
563         default:
564             if (half) {
565                 set_skk_mode(SKK_MODE_HALF_KATAKANA);
566             } else {
567                 set_skk_mode(SKK_MODE_KATAKANA);
568             }
569             break;
570         }
571         clear_pending();
572         return true;
573     case INPUT_MODE_PREEDIT:
574     case INPUT_MODE_OKURI:
575         if (m_preeditstr.length() > 0) {
576             if (m_skk_mode == SKK_MODE_HIRAGANA) {
577                 WideString katakana;
578                 convert_hiragana_to_katakana(m_preeditstr, katakana, false);
579                 commit_string(katakana);
580             } else {
581                 commit_string(m_preeditstr);
582             }
583             if (!m_preeditstr.empty() && m_input_mode == INPUT_MODE_PREEDIT) {
584                 m_hist.add_entry(m_preeditstr);
585             }
586             clear_preedit();
587             clear_pending();
588             set_input_mode(INPUT_MODE_DIRECT);
589         }
590         return true;
591     case INPUT_MODE_CONVERTING:
592         commit_converting();
593         set_input_mode(INPUT_MODE_DIRECT);
594         if (m_skk_mode == SKK_MODE_KATAKANA ||
595                 m_skk_mode == SKK_MODE_HALF_KATAKANA) {
596             set_skk_mode(SKK_MODE_HIRAGANA);
597         } else {
598             set_skk_mode(SKK_MODE_KATAKANA);
599         }
600         return true;
601     default:
602         break;
603     }
604 
605     return false;
606 }
607 
608 bool
action_toggle_case(void)609 SKKCore::action_toggle_case (void)
610 {
611     if (m_input_mode == INPUT_MODE_PREEDIT && m_skk_mode == SKK_MODE_ASCII) {
612         for (WideString::iterator i = m_preeditstr.begin();
613              i != m_preeditstr.end(); i++) {
614             int code = *i;
615             if (islower(code)) {
616                 *i = toupper(code);
617             } else if (isupper(code)) {
618                 *i = tolower(code);
619             }
620         }
621         if (!m_preeditstr.empty()) {
622             m_hist.add_entry(m_preeditstr);
623         }
624         commit_string(m_preeditstr);
625         clear_preedit();
626         clear_pending();
627         set_input_mode(INPUT_MODE_DIRECT);
628         set_skk_mode(SKK_MODE_HIRAGANA);
629         return true;
630     }
631     return false;
632 }
633 
634 bool
action_start_preedit(void)635 SKKCore::action_start_preedit (void)
636 {
637     switch (m_input_mode) {
638     case INPUT_MODE_DIRECT:
639         set_input_mode(INPUT_MODE_PREEDIT);
640         m_preedit_pos = 0;
641         clear_pending();
642         return true;
643     case INPUT_MODE_PREEDIT:
644     case INPUT_MODE_OKURI:
645         if (m_preeditstr.length() > 0) {
646             commit_string(m_preeditstr);
647             clear_preedit();
648         }
649         clear_pending();
650         return true;
651     case INPUT_MODE_CONVERTING:
652         commit_converting();
653         set_input_mode(INPUT_MODE_PREEDIT);
654         return true;
655     default:
656         return false;
657     }
658 }
659 
660 bool
action_completion(void)661 SKKCore::action_completion (void)
662 {
663     if (m_input_mode == INPUT_MODE_PREEDIT) {
664         if (m_histmgr.is_clear()) {
665             m_histmgr.setup_completion(m_preeditstr);
666         } else {
667             m_histmgr.next_cand();
668         }
669         m_histmgr.get_current_candidate(m_preeditstr);
670         m_preedit_pos = m_preeditstr.size();
671         return true;
672     } else {
673         return false;
674     }
675 }
676 
677 bool
action_completion_back(void)678 SKKCore::action_completion_back (void)
679 {
680     if (m_input_mode == INPUT_MODE_PREEDIT) {
681         if (m_histmgr.prev_cand()) {
682             m_histmgr.get_current_candidate(m_preeditstr);
683             m_preedit_pos = m_preeditstr.size();
684             return true;
685         }
686     }
687     return false;
688 }
689 
690 bool
action_prevcand(void)691 SKKCore::action_prevcand (void)
692 {
693     if (m_input_mode == INPUT_MODE_CONVERTING) {
694         bool retval = action_prevpage();
695         if (!retval)
696             action_cancel();
697         return true;
698     } else {
699         return false;
700     }
701 }
702 
703 bool
action_ascii(bool wide)704 SKKCore::action_ascii (bool wide)
705 {
706     switch (m_input_mode) {
707     case INPUT_MODE_PREEDIT:
708     case INPUT_MODE_OKURI:
709         commit_string(m_preeditstr);
710         clear_preedit();
711         set_input_mode(INPUT_MODE_DIRECT);
712         break;
713     case INPUT_MODE_CONVERTING:
714         commit_converting();
715         set_input_mode(INPUT_MODE_DIRECT);
716     default:
717         break;
718     }
719     clear_pending();
720     if (wide) {
721         set_skk_mode(SKK_MODE_WIDE_ASCII);
722     } else {
723         set_skk_mode(SKK_MODE_ASCII);
724     }
725     return true;
726 }
727 
728 bool
action_ascii_convert(void)729 SKKCore::action_ascii_convert (void)
730 {
731     switch (m_input_mode) {
732     case INPUT_MODE_CONVERTING:
733         commit_converting();
734     case INPUT_MODE_DIRECT:
735         set_skk_mode(SKK_MODE_ASCII);
736         set_input_mode(INPUT_MODE_PREEDIT);
737         clear_preedit();
738         clear_pending();
739         return true;
740     default:
741         return false;
742     }
743 }
744 
745 bool
action_backspace(void)746 SKKCore::action_backspace (void)
747 {
748     if (m_pendingstr.empty()) {
749         switch (m_input_mode) {
750         case INPUT_MODE_CONVERTING:
751             set_input_mode(INPUT_MODE_PREEDIT);
752             m_ltable.clear();
753             break;
754         case INPUT_MODE_PREEDIT:
755             if (m_preedit_pos == 0) {
756                 commit_string(m_preeditstr);
757                 action_cancel();
758             } else {
759                 m_preeditstr.erase(m_preedit_pos-1, 1);
760                 m_histmgr.clear();
761                 m_preedit_pos--;
762             }
763             break;
764         case INPUT_MODE_DIRECT:
765             if (m_commit_pos == 0) {
766                 clear_commit();
767                 m_end_flag = true;
768                 return false;
769             } else {
770                 m_commitstr.erase(m_commit_pos-1, 1);
771                 m_commit_pos--;
772             }
773         default:
774             break;
775         }
776     } else {
777         if (m_input_mode == INPUT_MODE_OKURI &&
778             m_pendingstr.length() == 1) {
779             clear_pending();
780             set_input_mode(INPUT_MODE_PREEDIT);
781             m_preedit_pos = m_preeditstr.length();
782         } else {
783             m_pendingstr.erase(m_pendingstr.length()-1);
784             m_key2kana->set_pending(m_pendingstr);
785         }
786     }
787 
788     return true;
789 }
790 
791 bool
action_delete(void)792 SKKCore::action_delete (void)
793 {
794     if (m_pendingstr.empty()) {
795         switch (m_input_mode) {
796         case INPUT_MODE_CONVERTING:
797             set_input_mode(INPUT_MODE_PREEDIT);
798             m_ltable.clear();
799             break;
800         case INPUT_MODE_PREEDIT:
801             if (m_preedit_pos < m_preeditstr.length()) {
802                 m_preeditstr.erase(m_preedit_pos, 1);
803                 m_histmgr.clear();
804             }
805             break;
806         case INPUT_MODE_DIRECT:
807             if (m_commitstr.empty()) {
808                 clear_commit();
809                 m_end_flag = true;
810                 return false;
811             } else if (m_commit_pos < m_commitstr.length()) {
812                 m_commitstr.erase(m_commit_pos, 1);
813             }
814         default:
815             break;
816         }
817     } else {
818         clear_pending();
819     }
820 
821     return true;
822 }
823 
824 bool
action_forward(void)825 SKKCore::action_forward (void)
826 {
827     switch (m_input_mode) {
828     case INPUT_MODE_CONVERTING:
829         if (m_ltable.visible_table()) {
830             if (!m_ltable.cursor_down()) {
831                 set_input_mode(INPUT_MODE_LEARNING);
832                 m_learning = new SKKCore(m_keybind, m_key2kana,
833                                          m_dict, m_hist);
834             }
835             return true;
836         } else {
837             return action_convert();
838         }
839     case INPUT_MODE_DIRECT:
840         clear_pending();
841         if (m_commit_pos < m_commitstr.length()) {
842             m_commit_pos++;
843             return true;
844         } else {
845             return false;
846         }
847     case INPUT_MODE_PREEDIT:
848         clear_pending();
849         m_histmgr.clear();
850         if (m_preedit_pos < m_preeditstr.length()) {
851             m_preedit_pos++;
852         } else if (m_commit_pos < m_commitstr.length()) {
853             m_commit_pos++;
854         } else {
855             return false;
856         }
857         return true;
858     default:
859         break;
860     }
861     return false;
862 }
863 
864 bool
action_backward(void)865 SKKCore::action_backward (void)
866 {
867     switch (m_input_mode) {
868     case INPUT_MODE_CONVERTING:
869         if (m_ltable.visible_table()) {
870             if (!m_ltable.cursor_up()) {
871                 return m_ltable.prev_candidate();
872             } else {
873                 return true;
874             }
875         } else {
876             return action_prevcand();
877         }
878     case INPUT_MODE_DIRECT:
879         clear_pending();
880         m_histmgr.clear();
881         if (m_commit_pos > 0) {
882             m_commit_pos--;
883             return true;
884         } else {
885             return false;
886         }
887     case INPUT_MODE_PREEDIT:
888         clear_pending();
889         if (m_preedit_pos > 0) {
890             m_preedit_pos--;
891         } else if (m_commit_pos > 0) {
892             m_commit_pos--;
893         } else {
894             return false;
895         }
896         return true;
897     default:
898         break;
899     }
900     return false;
901 }
902 
903 bool
action_home(void)904 SKKCore::action_home (void)
905 {
906     switch (m_input_mode) {
907     case INPUT_MODE_CONVERTING:
908         return false;
909     case INPUT_MODE_DIRECT:
910         clear_pending();
911         if (m_commit_pos > 0) {
912             m_commit_pos = 0;
913             return true;
914         } else {
915             return false;
916         }
917     case INPUT_MODE_PREEDIT:
918         clear_pending();
919         m_histmgr.clear();
920         if (m_preedit_pos > 0) {
921             m_preedit_pos = 0;
922         } else if (m_commit_pos > 0) {
923             m_commit_pos = 0;
924         } else {
925             return false;
926         }
927         return true;
928     default:
929         return false;
930     }
931 }
932 
933 bool
action_end(void)934 SKKCore::action_end (void)
935 {
936     switch (m_input_mode) {
937     case INPUT_MODE_CONVERTING:
938         return false;
939     case INPUT_MODE_DIRECT:
940         clear_pending();
941         if (m_commit_pos < m_commitstr.length()) {
942             m_commit_pos = m_commitstr.length();
943             return true;
944         } else {
945             return false;
946         }
947     case INPUT_MODE_PREEDIT:
948         clear_pending();
949         m_histmgr.clear();
950         if (m_preedit_pos < m_preeditstr.length()) {
951             m_preedit_pos = m_preeditstr.length();
952         } else if (m_commit_pos < m_commitstr.length()) {
953             m_commit_pos = m_commitstr.length();
954         } else {
955             return false;
956         }
957         return true;
958     default:
959         return false;
960     }
961 }
962 
963 bool
action_nextpage(void)964 SKKCore::action_nextpage (void)
965 {
966     if (m_input_mode != INPUT_MODE_CONVERTING) return false;
967     if (!m_ltable.visible_table()) {
968         if (!m_ltable.next_candidate()) {
969             return m_ltable.number_of_candidates() != 0;
970         }
971         return true;
972     } else if (m_ltable.number_of_candidates() > 0) {
973         bool retval = m_ltable.page_down();
974         m_ltable.set_page_size(m_keybind->selection_key_length());
975         return retval;
976     }
977     return false;
978 }
979 
980 bool
action_prevpage(void)981 SKKCore::action_prevpage (void)
982 {
983     if (m_input_mode != INPUT_MODE_CONVERTING) return false;
984     if (!m_ltable.visible_table()) {
985         return m_ltable.prev_candidate();
986     } else {
987         bool retval = m_ltable.page_up();
988         m_ltable.set_page_size(m_keybind->selection_key_length());
989         if (!retval)
990             return m_ltable.prev_candidate();
991         else
992             return true;
993     }
994     return false;
995 }
996 
997 void
action_select_index(int i)998 SKKCore::action_select_index (int i)
999 {
1000     commit_converting(i);
1001     set_input_mode(INPUT_MODE_DIRECT);
1002 }
1003 
1004 bool
process_remaining_keybinds(const KeyEvent & key)1005 SKKCore::process_remaining_keybinds (const KeyEvent &key)
1006 {
1007     if (m_keybind->match_katakana_keys(key))
1008         return action_katakana(false);
1009 
1010     if (m_keybind->match_half_katakana_keys(key))
1011         return action_katakana(true);
1012 
1013     if (m_keybind->match_start_preedit_keys(key))
1014         return action_start_preedit();
1015 
1016     if (m_keybind->match_prevcand_keys(key))
1017         return action_prevcand();
1018 
1019     if(m_keybind->match_ascii_keys(key))
1020         return action_ascii(false);
1021 
1022     if(m_keybind->match_wide_ascii_keys(key))
1023         return action_ascii(true);
1024 
1025     if (m_keybind->match_ascii_convert_keys(key))
1026         return action_ascii_convert();
1027 
1028     if (m_keybind->match_backspace_keys(key))
1029         return action_backspace();
1030 
1031     if (m_keybind->match_delete_keys(key))
1032         return action_delete();
1033 
1034     if (m_keybind->match_forward_keys(key))
1035         return action_forward();
1036 
1037     if (m_keybind->match_backward_keys(key))
1038         return action_backward();
1039 
1040     if (m_keybind->match_home_keys(key))
1041         return action_home();
1042 
1043     if (m_keybind->match_end_keys(key))
1044         return action_end();
1045 
1046     if (m_keybind->match_completion_keys(key))
1047         return action_completion();
1048 
1049     if (m_keybind->match_completion_back_keys(key))
1050         return action_completion_back();
1051 
1052     return false;
1053 }
1054 
1055 bool
process_ascii(const KeyEvent & key)1056 SKKCore::process_ascii (const KeyEvent &key)
1057 {
1058     if (m_keybind->match_kakutei_keys(key))
1059         return action_kakutei();
1060 
1061     if (m_keybind->match_cancel_keys(key))
1062         return action_cancel();
1063 
1064     if (m_input_mode == INPUT_MODE_PREEDIT &&
1065         m_keybind->match_convert_keys(key))
1066         return action_convert();
1067 
1068     if (m_input_mode == INPUT_MODE_PREEDIT &&
1069         m_keybind->match_upcase_keys(key))
1070         return action_toggle_case();
1071 
1072     char code = key.get_ascii_code();
1073 
1074     if (!(key.mask & skk_key_mask)) {
1075         if (m_input_mode == INPUT_MODE_DIRECT) {
1076             return false;
1077         } else {
1078             if (isprint(code)) {
1079                 char str[2] = {code, '\0'};
1080                 commit_or_preedit(utf8_mbstowcs(str));
1081                 return true;
1082             } else {
1083                 return process_remaining_keybinds(key);
1084             }
1085         }
1086     }
1087 
1088     return process_remaining_keybinds(key);
1089 }
1090 
1091 bool
process_wide_ascii(const KeyEvent & key)1092 SKKCore::process_wide_ascii (const KeyEvent &key)
1093 {
1094     if (m_keybind->match_kakutei_keys(key))
1095         return action_kakutei();
1096 
1097     if (m_keybind->match_cancel_keys(key))
1098         return action_cancel();
1099 
1100     char code = key.get_ascii_code();
1101 
1102     if (!(key.mask & skk_key_mask) && isprint(code)) {
1103         WideString result;
1104 
1105         convert_char_to_wide(code, result);
1106         commit_string(result);
1107         return true;
1108     }
1109 
1110     return process_remaining_keybinds(key);
1111 }
1112 
1113 bool
process_romakana(const KeyEvent & key)1114 SKKCore::process_romakana (const KeyEvent &key)
1115 {
1116     if (m_keybind->match_kakutei_keys(key))
1117         return action_kakutei();
1118     if (m_keybind->match_cancel_keys(key))
1119         return action_cancel();
1120 
1121     if (m_input_mode == INPUT_MODE_PREEDIT ||
1122         m_input_mode == INPUT_MODE_OKURI)
1123         if (m_keybind->match_convert_keys(key))
1124             return action_convert();
1125 
1126     if (m_pendingstr.empty() && process_remaining_keybinds(key)) {
1127         return true;
1128     }
1129 
1130     unsigned char code = key.get_ascii_code();
1131 
1132     if (!(key.mask & skk_key_mask) && isprint(code)) {
1133         bool d2p = false;  /* direct to preedit flag */
1134         bool p2o = false;  /* preedit to okuri flag */
1135         bool is_pending_clear;
1136         bool retval = false;
1137         WideString result;
1138 
1139         if (isalpha(code) && key.is_shift_down()) {
1140             if (m_input_mode == INPUT_MODE_PREEDIT &&
1141                 !m_preeditstr.empty()) {
1142                 p2o = true;
1143             } else if (m_input_mode == INPUT_MODE_DIRECT) {
1144                 d2p = true;
1145             }
1146         }
1147 
1148         is_pending_clear =
1149             m_key2kana->append(String(1, tolower(code)), result, m_pendingstr);
1150 
1151         if (m_input_mode == INPUT_MODE_OKURI && !m_pendingstr.empty() &&
1152             result.empty()) {
1153             m_okurihead = m_pendingstr[0];
1154         }
1155 
1156         if (d2p) {
1157             /* shift to preedit from direct */
1158             if (m_pendingstr.empty()) {
1159                 set_input_mode(INPUT_MODE_PREEDIT);
1160                 commit_or_preedit(result);
1161             } else {
1162                 commit_or_preedit(result);
1163                 set_input_mode(INPUT_MODE_PREEDIT);
1164             }
1165             retval = true;
1166         } else if (p2o) {
1167             /* shift to okuri from preedit */
1168             m_okurihead = (ucs4_t) tolower(code);
1169             m_preeditstr.erase(m_preedit_pos);
1170             if (m_pendingstr.empty()) {
1171                 set_input_mode(INPUT_MODE_OKURI);
1172                 commit_or_preedit(result);
1173             } else{
1174                 commit_or_preedit(result);
1175                 set_input_mode(INPUT_MODE_OKURI);
1176             }
1177             retval = true;
1178         } else if (result.length() > 0) {
1179             /* otherwise simply commit or add to preedit */
1180             commit_or_preedit(result);
1181             retval = true;
1182         } else if (!m_pendingstr.empty()) {
1183             retval = true ;
1184         }
1185 
1186         if ((is_pending_clear) && process_remaining_keybinds(key)) {
1187             clear_pending();
1188             retval = true;
1189         }
1190 
1191         return retval;
1192     } else {
1193         return process_remaining_keybinds(key);
1194     }
1195 
1196     return false;
1197 }
1198 
1199 bool
process_key_event(const KeyEvent key)1200 SKKCore::process_key_event (const KeyEvent key)
1201 {
1202     if (m_input_mode == INPUT_MODE_CONVERTING) {
1203         if (m_keybind->match_kakutei_keys(key))
1204             return action_kakutei();
1205         if (m_keybind->match_cancel_keys(key))
1206             return action_cancel();
1207         if (m_keybind->match_convert_keys(key))
1208             return action_convert();
1209         if (m_keybind->match_prevcand_keys(key))
1210             return action_prevcand();
1211         if (m_keybind->match_forward_keys(key))
1212             return action_forward();
1213         if (m_keybind->match_backward_keys(key))
1214             return action_backward();
1215         if (m_ltable.visible_table() && m_ltable.number_of_candidates() > 0) {
1216             int index;
1217             if ((index = m_keybind->match_selection_keys(key)) > -1) {
1218                 action_select_index(index);
1219                 return true;
1220             }
1221         }
1222         commit_converting();
1223         set_input_mode(INPUT_MODE_DIRECT);
1224         if (key.code == SCIM_KEY_Return &&
1225             (ignore_return ^ (key.mask & SCIM_KEY_ShiftMask != 0))) {
1226             return true;
1227         }
1228     }
1229 
1230     if (m_input_mode == INPUT_MODE_LEARNING) {
1231         bool retval = m_learning->process_key_event(key);
1232         char code = key.get_ascii_code();
1233         if (key.code == SCIM_KEY_Return || m_learning->m_end_flag) {
1234             if (key.code == SCIM_KEY_Return &&
1235                 (ignore_return ^ (key.mask & SCIM_KEY_ShiftMask != 0)))
1236                 retval = true;
1237             if (m_learning->m_commitstr.empty()) {
1238                 /* learning is canceled */
1239                 delete m_learning;
1240                 m_learning = 0;
1241                 if (m_ltable.empty()) {
1242                     set_input_mode(INPUT_MODE_PREEDIT);
1243                     m_ltable.clear();
1244                     if (!m_okuristr.empty()) {
1245                         m_preeditstr += m_okuristr;
1246                         m_preedit_pos += m_okuristr.length();
1247                         m_okuristr.clear();
1248                         m_okurihead = 0;
1249                     }
1250                 } else {
1251                     if (m_ltable.number_of_candidates() == 0)
1252                         m_ltable.prev_candidate();
1253                     set_input_mode(INPUT_MODE_CONVERTING);
1254                 }
1255                 retval = true;
1256             } else {
1257                 /* learning is committed */
1258                 static const ucs4_t sharp = 0x23; /* code for number sign */
1259                 if (m_learning->m_commitstr.find(sharp) != WideString::npos) {
1260                     WideString result, keystr;
1261                     std::list<WideString> numbers;
1262                     m_dict->extract_numbers(m_preeditstr,
1263                                             numbers, keystr);
1264                     m_dict->number_conversion(numbers,
1265                                               m_learning->m_commitstr,
1266                                               result);
1267                     m_preeditstr.assign(keystr);
1268                     commit_string(result);
1269                 } else {
1270                     commit_string(m_learning->m_commitstr);
1271                 }
1272                 commit_string(m_okuristr);
1273                 if (m_okurihead != 0)
1274                     m_preeditstr += m_okurihead;
1275                 m_dict->write(m_preeditstr,
1276                               CandEnt(m_learning->m_commitstr));
1277                 clear_preedit();
1278                 m_ltable.clear();
1279                 m_learning->clear();
1280                 delete m_learning;
1281                 m_learning = 0;
1282                 set_input_mode(INPUT_MODE_DIRECT);
1283             }
1284         } else if (retval == false &&
1285                    isprint(code) && !(key.mask & skk_key_mask)) {
1286             retval = true;
1287             m_learning->commit_string(utf8_mbstowcs(&code, 1));
1288         }
1289         return retval;
1290     }
1291 
1292     if (m_input_mode == INPUT_MODE_PREEDIT && key.code == SCIM_KEY_Return &&
1293         !(key.mask & skk_key_mask)) {
1294         action_kakutei();
1295         if (ignore_return ^ (key.mask & SCIM_KEY_ShiftMask != 0)) {
1296             return true;
1297         } else {
1298             return false;
1299         }
1300     }
1301 
1302 
1303     switch (m_skk_mode) {
1304     case SKK_MODE_ASCII:
1305         return process_ascii(key);
1306     case SKK_MODE_WIDE_ASCII:
1307         return process_wide_ascii(key);
1308     default:
1309         return process_romakana(key);
1310     }
1311 }
1312 
1313 SKKCandList&
get_lookup_table(void)1314 SKKCore::get_lookup_table (void)
1315 {
1316     if (m_learning)
1317         return m_learning->get_lookup_table();
1318     else
1319         return m_ltable;
1320 }
1321 
1322 bool
lookup_table_visible(void)1323 SKKCore::lookup_table_visible (void)
1324 {
1325     if (m_learning)
1326         return m_learning->lookup_table_visible();
1327     else
1328         return m_ltable.visible_table();
1329 }
1330 
1331 static void
convert_char_to_wide(const char c,WideString & wide)1332 convert_char_to_wide (const char c, WideString & wide)
1333 {
1334     bool found = false;
1335 
1336     for (unsigned int i = 0; wide_table[i].code; i++) {
1337         if (wide_table[i].code[0] == c) {
1338             wide += utf8_mbstowcs (wide_table[i].wide);
1339             found = true;
1340             break;
1341         }
1342     }
1343 
1344 #if 0
1345     if (!found && space && c == ' ') {
1346         wide += utf8_mbstowcs ("\xE3\x80\x80");
1347         found = true;
1348     }
1349 #endif
1350 
1351     if (!found)
1352         wide += utf8_mbstowcs (&c, 1);
1353 }
1354 
1355 static void
convert_hiragana_to_katakana(const WideString & hira,WideString & kata,bool half)1356 convert_hiragana_to_katakana (const WideString & hira, WideString & kata,
1357                               bool half)
1358 {
1359     if (hira.length () < 0)
1360         return;
1361 
1362     for (unsigned int i = 0; i < hira.length (); i++) {
1363         WideString tmpwide;
1364         bool found = false;
1365 
1366         for (unsigned int j = 0; hiragana_katakana_table[j].hiragana; j++) {
1367             tmpwide = utf8_mbstowcs (hiragana_katakana_table[j].hiragana);
1368             if (hira.substr(i, 1) == tmpwide) {
1369                 if (half)
1370                     kata += utf8_mbstowcs (hiragana_katakana_table[j].half_katakana);
1371                 else
1372                     kata += utf8_mbstowcs (hiragana_katakana_table[j].katakana);
1373                 found = true;
1374                 break;
1375             }
1376         }
1377 
1378         if (!found)
1379             kata += hira.substr(i, 1);
1380     }
1381 }
1382