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