1 /***************************************************************************
2 * Copyright (C) 2010~2011 by CSSlayer *
3 * wengxt@gmail.com *
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 of the License, or *
8 * (at your option) 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 *
17 * Free Software Foundation, Inc., *
18 * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. *
19 ***************************************************************************/
20
21 #include <memory>
22 #include <sstream>
23 #include <stdlib.h>
24 #include <stdio.h>
25 #include <string.h>
26 #include <errno.h>
27 #include <pinyin.h>
28 #include <fcitx/ime.h>
29 #include <fcitx-config/hotkey.h>
30 #include <fcitx-config/xdg.h>
31 #include <fcitx-utils/log.h>
32 #include <fcitx-config/fcitx-config.h>
33 #include <fcitx-utils/utils.h>
34 #include <fcitx/instance.h>
35 #include <fcitx/keys.h>
36 #include <fcitx/module.h>
37 #include <fcitx/context.h>
38 #include <fcitx/module/punc/fcitx-punc.h>
39 #include <string>
40 #include <libintl.h>
41
42 #include "config.h"
43 #include "eim.h"
44 #include "enummap.h"
45 #include "bus.h"
46 #include "common.h"
47 #include "utils.h"
48
49 #define FCITX_LIBPINYIN_MAX(x, y) ((x) > (y)? (x) : (y))
50 #define FCITX_LIBPINYIN_MIN(x, y) ((x) < (y)? (x) : (y))
51 extern "C" {
52 FCITX_DEFINE_PLUGIN(fcitx_libpinyin, ime2, FcitxIMClass2) = {
53 FcitxLibPinyinCreate,
54 FcitxLibPinyinDestroy,
55 FcitxLibPinyinReloadConfig,
56 NULL,
57 NULL,
58 NULL,
59 NULL,
60 NULL,
61 };
62 }
63
64 typedef struct _FcitxLibPinyinCandWord {
65 boolean ispunc;
66 int idx;
67 } FcitxLibPinyinCandWord;
68
69 CONFIG_DEFINE_LOAD_AND_SAVE(FcitxLibPinyinConfig, FcitxLibPinyinConfig, "fcitx-libpinyin")
70
71 static void FcitxLibPinyinReconfigure(FcitxLibPinyinAddonInstance* libpinyin);
72 static void FcitxLibPinyinSave(void* arg);
73 static bool LibPinyinCheckZhuyinKey(FcitxKeySym sym, FCITX_ZHUYIN_LAYOUT layout, boolean useTone) ;
74
75 static const char* input_keys [] = {
76 "1qaz2wsxedcrfv5tgbyhnujm8ik,9ol.0p;/-", /* standard kb */
77 "1234567890-qwertyuiopasdfghjkl;zxcvbn", /* IBM */
78 "2wsx3edcrfvtgb6yhnujm8ik,9ol.0p;/-['=", /* Gin-yieh */
79 "bpmfdtnlvkhg7c,./j;'sexuaorwiqzy890-=", /* ET */
80 0
81 };
82
83 static const char* tone_keys [] = {
84 "7634 ", /* standard kb */
85 "/m,. ", /* IBM */
86 "1qaz ", /* Gin-yieh */
87 "1234 ", /* ET */
88 0
89 };
90
91 static const FcitxKeyState candidateModifierMap[] = {
92 FcitxKeyState_Ctrl,
93 FcitxKeyState_Alt,
94 FcitxKeyState_Shift,
95 };
96
97 static const FcitxHotkey FCITX_LIBPINYIN_SHIFT_ENTER[2] = {
98 {NULL, FcitxKey_Return, FcitxKeyState_Shift},
99 {NULL, FcitxKey_None, FcitxKeyState_None}
100 };
101
LibPinyinCheckZhuyinKey(FcitxKeySym sym,FCITX_ZHUYIN_LAYOUT layout,boolean useTone)102 bool LibPinyinCheckZhuyinKey(FcitxKeySym sym, FCITX_ZHUYIN_LAYOUT layout, boolean useTone)
103 {
104 char key = sym & 0xff;
105 const char* keys = input_keys[layout];
106 const char* tones = tone_keys[layout];
107 if (strchr(keys, key))
108 return true;
109
110 if (useTone && strchr(tones, key))
111 return true;
112
113 return false;
114 }
115
offset() const116 int FcitxLibPinyin::offset() const
117 {
118 if (!m_fixedString.empty()) {
119 return m_fixedString.back().first;
120 }
121 return 0;
122 }
123
pinyinOffset() const124 int FcitxLibPinyin::pinyinOffset() const
125 {
126 if (!m_fixedString.empty()) {
127 return m_fixedString.back().second;
128 }
129 return 0;
130 }
131
132 /**
133 * @brief Reset the status.
134 *
135 **/
FcitxLibPinyinReset(void * arg)136 void FcitxLibPinyinReset(void* arg)
137 {
138 FcitxLibPinyin* libpinyin = (FcitxLibPinyin*) arg;
139 libpinyin->reset();
140 }
141
reset()142 void FcitxLibPinyin::reset() {
143 m_buf.clear();
144 m_cursorPos = 0;
145 m_parsedLen = 0;
146 m_fixedString.clear();
147 if (m_inst) {
148 pinyin_reset(m_inst);
149 }
150 }
151
parse(const char * str)152 size_t FcitxLibPinyin::parse(const char* str)
153 {
154 switch (m_type) {
155 case LPT_Pinyin:
156 return pinyin_parse_more_full_pinyins(m_inst, str);
157 case LPT_Shuangpin:
158 return pinyin_parse_more_double_pinyins(m_inst, str);
159 case LPT_Zhuyin:
160 return pinyin_parse_more_chewings(m_inst, str);
161 }
162 return 0;
163 }
164
165 /**
166 * @brief Process Key Input and return the status
167 *
168 * @param keycode keycode from XKeyEvent
169 * @param state state from XKeyEvent
170 * @param count count from XKeyEvent
171 * @return INPUT_RETURN_VALUE
172 **/
FcitxLibPinyinDoInput(void * arg,FcitxKeySym sym,unsigned int state)173 INPUT_RETURN_VALUE FcitxLibPinyinDoInput(void* arg, FcitxKeySym sym, unsigned int state)
174 {
175 FcitxLibPinyin* libpinyin = (FcitxLibPinyin*) arg;
176 return libpinyin->doInput(sym, state);
177 }
178
doInput(FcitxKeySym sym,unsigned int state)179 INPUT_RETURN_VALUE FcitxLibPinyin::doInput(FcitxKeySym sym, unsigned int state) {
180 FcitxLibPinyinConfig* config = &m_owner->config;
181 FcitxInstance* instance = m_owner->owner;
182 FcitxInputState* input = FcitxInstanceGetInputState(instance);
183
184 if (FcitxHotkeyIsHotKeySimple(sym, state)) {
185 /* there is some special case that ';' is used */
186 if (FcitxHotkeyIsHotKeyLAZ(sym, state)
187 || sym == '\''
188 || (FcitxHotkeyIsHotKey(sym, state, FCITX_SEMICOLON) && m_type == LPT_Shuangpin && (config->spScheme == FCITX_SHUANG_PIN_MS || config->spScheme == FCITX_SHUANG_PIN_ZIGUANG))
189 || (m_type == LPT_Zhuyin && LibPinyinCheckZhuyinKey(sym, config->zhuyinLayout, config->useTone))
190 ) {
191 if (m_buf.size() == 0 && (sym == '\'' || sym == ';'))
192 return IRV_TO_PROCESS;
193
194 if (m_buf.size() < MAX_PINYIN_INPUT) {
195 m_buf.insert(m_cursorPos, 1, (char)(sym & 0xff));
196 m_cursorPos ++;
197
198 m_parsedLen =parse(m_buf.c_str());
199
200 if (pinyin_get_parsed_input_length(m_inst) == 0 && m_buf.size() == 1 && m_type != LPT_Shuangpin
201 && !(m_type == LPT_Pinyin && !m_owner->config.incomplete)
202 && !(m_type == LPT_Zhuyin && !m_owner->config.zhuyinIncomplete)) {
203 reset();
204 return IRV_TO_PROCESS;
205 }
206 return IRV_DISPLAY_CANDWORDS;
207 } else
208 return IRV_DO_NOTHING;
209 }
210 }
211
212 if (FcitxHotkeyIsHotKey(sym, state, FCITX_SPACE)
213 || (m_type == LPT_Zhuyin && FcitxHotkeyIsHotKey(sym, state, FCITX_ENTER))) {
214 size_t len = m_buf.size();
215 if (len == 0)
216 return IRV_TO_PROCESS;
217
218 return FcitxCandidateWordChooseByIndex(FcitxInputStateGetCandidateList(input), 0);
219 }
220
221 if ((m_type != LPT_Zhuyin && FcitxHotkeyIsHotKey(sym, state, FCITX_ENTER))
222 || (m_type == LPT_Zhuyin && FcitxHotkeyIsHotKey(sym, state, FCITX_LIBPINYIN_SHIFT_ENTER))) {
223 if (m_buf[0] == 0)
224 return IRV_TO_PROCESS;
225
226 const std::string sentence = this->sentence(0);
227 if (!sentence.empty()) {
228 int offset = this->offset();
229 int hzlen = 0;
230 if (fcitx_utf8_strlen(sentence.c_str()) > offset) {
231 hzlen = fcitx_utf8_get_nth_char(sentence.c_str(), offset) - sentence.c_str();
232 } else {
233 hzlen = sentence.size();
234 }
235
236 int start = pinyinOffset();
237 int pylen = m_buf.size() - start;
238
239 if (pylen < 0) {
240 pylen = 0;
241 }
242
243 char* buf = (char*) fcitx_utils_malloc0((hzlen + pylen + 1) * sizeof(char));
244 strncpy(buf, sentence.c_str(), hzlen);
245 if (pylen) {
246 strcpy(&buf[hzlen], &m_buf[start]);
247 }
248 buf[hzlen + pylen] = '\0';
249 FcitxInstanceCommitString(instance, FcitxInstanceGetCurrentIC(instance), buf);
250 free(buf);
251 } else {
252 FcitxInstanceCommitString(instance, FcitxInstanceGetCurrentIC(instance), m_buf.c_str());
253 }
254
255 return IRV_CLEAN;
256 }
257
258 if (FcitxHotkeyIsHotKey(sym, state, FCITX_BACKSPACE) || FcitxHotkeyIsHotKey(sym, state, FCITX_DELETE)) {
259 if (m_buf.size() > 0) {
260 if (offset() != 0 && FcitxHotkeyIsHotKey(sym, state, FCITX_BACKSPACE)) {
261 m_fixedString.pop_back();
262 pinyin_clear_constraint(m_inst, pinyinOffset());
263 } else {
264 if (FcitxHotkeyIsHotKey(sym, state, FCITX_BACKSPACE)) {
265 if (m_cursorPos > 0) {
266 m_cursorPos -- ;
267 } else {
268 return IRV_DO_NOTHING;
269 }
270 }
271 size_t len = m_buf.size();
272 if (m_cursorPos == (int)len)
273 return IRV_DO_NOTHING;
274 m_buf.erase(m_cursorPos, 1);
275 if (m_buf.empty())
276 return IRV_CLEAN;
277 else
278 m_parsedLen = parse(m_buf.c_str());
279 }
280 return IRV_DISPLAY_CANDWORDS;
281 } else
282 return IRV_TO_PROCESS;
283 } else {
284 if (m_buf.size() > 0) {
285 if (FcitxHotkeyIsHotKey(sym, state, FCITX_LEFT)) {
286 if (m_cursorPos > 0) {
287 if (m_cursorPos == pinyinOffset()) {
288 m_fixedString.pop_back();
289 pinyin_clear_constraint(m_inst, offset());
290 return IRV_DISPLAY_CANDWORDS;
291 } else {
292 m_cursorPos--;
293 return IRV_DISPLAY_CANDWORDS;
294 }
295 }
296
297 return IRV_DO_NOTHING;
298 } else if (FcitxHotkeyIsHotKey(sym, state, FCITX_RIGHT)) {
299 size_t len = m_buf.size();
300 if (m_cursorPos < (int) len) {
301 m_cursorPos ++ ;
302 return IRV_DISPLAY_CANDWORDS;
303 }
304 return IRV_DO_NOTHING;
305 } else if (FcitxHotkeyIsHotKey(sym, state, FCITX_HOME)) {
306 int offset = pinyinOffset();
307 if (m_cursorPos != offset) {
308 m_cursorPos = offset;
309 return IRV_DISPLAY_CANDWORDS;
310 }
311 return IRV_DO_NOTHING;
312 } else if (FcitxHotkeyIsHotKey(sym, state, FCITX_END)) {
313 size_t len = m_buf.size();
314 if (m_cursorPos != (int) len) {
315 m_cursorPos = len ;
316 return IRV_DISPLAY_CANDWORDS;
317 }
318 return IRV_DO_NOTHING;
319 }
320 } else {
321 return IRV_TO_PROCESS;
322 }
323 }
324 return IRV_TO_PROCESS;
325 }
326
load()327 void FcitxLibPinyin::load()
328 {
329 if (m_inst != NULL)
330 return;
331
332 FcitxLibPinyinAddonInstance* libpinyinaddon = m_owner;
333
334 if (m_type == LPT_Zhuyin && m_owner->zhuyin_context == NULL) {
335 char* user_path = FcitxLibPinyinGetUserPath(libpinyinaddon->config.bSimplifiedDataForZhuyin ? LPLT_Simplified : LPLT_Traditional);
336 char* syspath = FcitxLibPinyinGetSysPath(libpinyinaddon->config.bSimplifiedDataForZhuyin ? LPLT_Simplified : LPLT_Traditional);
337 libpinyinaddon->zhuyin_context = pinyin_init(syspath, user_path);
338 free(user_path);
339 free(syspath);
340 }
341
342 if (m_type != LPT_Zhuyin && m_owner->pinyin_context == NULL) {
343 char* user_path = FcitxLibPinyinGetUserPath(libpinyinaddon->config.bTraditionalDataForPinyin ? LPLT_Traditional : LPLT_Simplified);
344 char* syspath = FcitxLibPinyinGetSysPath(libpinyinaddon->config.bTraditionalDataForPinyin ? LPLT_Traditional : LPLT_Simplified);
345 libpinyinaddon->pinyin_context = pinyin_init(syspath, user_path);
346 free(user_path);
347 free(syspath);
348 }
349
350 if (m_type == LPT_Zhuyin)
351 m_inst = pinyin_alloc_instance(libpinyinaddon->zhuyin_context);
352 else
353 m_inst = pinyin_alloc_instance(libpinyinaddon->pinyin_context);
354
355 FcitxLibPinyinReconfigure(libpinyinaddon);
356 }
357
FcitxLibPinyinInit(void * arg)358 boolean FcitxLibPinyinInit(void* arg)
359 {
360 FcitxLibPinyin* libpinyin = (FcitxLibPinyin*) arg;
361 libpinyin->init();
362 return true;
363 }
364
init()365 void FcitxLibPinyin::init() {
366 FcitxInstanceSetContext(m_owner->owner, CONTEXT_IM_KEYBOARD_LAYOUT, "us");
367 if (m_type == LPT_Zhuyin) {
368 FcitxInstanceSetContext(m_owner->owner, CONTEXT_ALTERNATIVE_PREVPAGE_KEY, m_owner->config.hkPrevPage);
369 FcitxInstanceSetContext(m_owner->owner, CONTEXT_ALTERNATIVE_NEXTPAGE_KEY, m_owner->config.hkNextPage);
370 }
371
372 load();
373 }
374
updatePreedit(const std::string & sentence)375 void FcitxLibPinyin::updatePreedit(const std::string &sentence)
376 {
377 FcitxInstance* instance = m_owner->owner;
378 FcitxInputState* input = FcitxInstanceGetInputState(instance);
379 int offset = this->offset();
380
381 #if 0
382 if (m_type == LPT_Pinyin) {
383 const gchar* raw_full_pinyin;
384 pinyin_get_raw_full_pinyin(m_inst, &raw_full_pinyin);
385 int libpinyinLen = strlen(raw_full_pinyin);
386 int fcitxLen = m_buf.size();
387 if (fcitxLen != libpinyinLen) {
388 strcpy(m_buf, raw_full_pinyin);
389 m_cursorPos += libpinyinLen - fcitxLen;
390 }
391 }
392 #endif
393
394 int pyoffset = pinyinOffset();
395 if (pyoffset > m_cursorPos)
396 m_cursorPos = pyoffset;
397
398 int hzlen = 0;
399 if (fcitx_utf8_strlen(sentence.c_str()) > offset) {
400 hzlen = fcitx_utf8_get_nth_char(sentence.c_str(), offset) - sentence.c_str();
401 } else {
402 hzlen = sentence.size();
403 }
404
405 if (hzlen > 0) {
406 char* buf = (char*) fcitx_utils_malloc0((hzlen + 1) * sizeof(char));
407 strncpy(buf, sentence.c_str(), hzlen);
408 buf[hzlen] = 0;
409 FcitxMessagesAddMessageAtLast(FcitxInputStateGetPreedit(input), MSG_INPUT, "%s", buf);
410 free(buf);
411 }
412
413 int charcurpos = hzlen;
414
415 int lastpos = pyoffset;
416 int curoffset = pyoffset;
417
418 PinyinKey* pykey = NULL;
419 PinyinKeyPos* pykeypos = NULL;
420 FcitxMessagesAddMessageAtLast(FcitxInputStateGetPreedit(input), MSG_CODE, "");
421 for (int i = pinyinOffset(); i < m_parsedLen; ) {
422 if (!pinyin_get_pinyin_key(m_inst, i, &pykey)) {
423 break;
424 }
425 pinyin_get_pinyin_key_rest(m_inst, i, &pykeypos);
426
427 guint16 rawBegin = 0, rawEnd = 0;
428 pinyin_get_pinyin_key_rest_positions(m_inst, pykeypos, &rawBegin, &rawEnd);
429 if (lastpos > 0) {
430 FcitxMessagesMessageConcatLast(FcitxInputStateGetPreedit(input), " ");
431 if (curoffset < m_cursorPos)
432 charcurpos ++;
433 for (int j = lastpos; j < rawBegin; j ++) {
434 char temp[2] = {'\0', '\0'};
435 temp[0] = m_buf[j];
436 FcitxMessagesMessageConcatLast(FcitxInputStateGetPreedit(input), temp);
437 if (curoffset < m_cursorPos) {
438 curoffset ++;
439 charcurpos ++;
440 }
441 }
442 if (lastpos < rawBegin) {
443 FcitxMessagesMessageConcatLast(FcitxInputStateGetPreedit(input), " ");
444 if (curoffset < m_cursorPos)
445 charcurpos ++;
446 }
447 }
448 lastpos = rawEnd;
449
450 bool breakAhead = false;
451 switch (m_type) {
452 case LPT_Pinyin: {
453 gchar* pystring;
454 pinyin_get_pinyin_string(m_inst, pykey, &pystring);
455 if (!pystring) {
456 breakAhead = true;
457 break;
458 }
459 FcitxMessagesMessageConcatLast(FcitxInputStateGetPreedit(input), pystring);
460 size_t pylen = strlen(pystring);
461 if (curoffset + pylen < m_cursorPos) {
462 curoffset += pylen;
463 charcurpos += pylen;
464 } else {
465 charcurpos += m_cursorPos - curoffset;
466 curoffset = m_cursorPos;
467 }
468 g_free(pystring);
469 break;
470 }
471 case LPT_Shuangpin: {
472 guint16 pykeyposLen = 0;
473 pinyin_get_pinyin_key_rest_length(m_inst, pykeypos, &pykeyposLen);
474 if (pykeyposLen == 2) {
475 const gchar* initial = NULL;
476 gchar* middle_final = NULL, *_initial = NULL;
477 pinyin_get_pinyin_strings(m_inst, pykey, &_initial, &middle_final);
478 if (!_initial[0])
479 initial = "'";
480 else
481 initial = _initial;
482 if (curoffset + 1 <= m_cursorPos) {
483 curoffset += 1;
484 charcurpos += strlen(initial);
485 }
486 FcitxMessagesMessageConcatLast(FcitxInputStateGetPreedit(input), initial);
487
488 if (curoffset + 1 <= m_cursorPos) {
489 curoffset += 1;
490 charcurpos += strlen(middle_final);
491 }
492 FcitxMessagesMessageConcatLast(FcitxInputStateGetPreedit(input), middle_final);
493 g_free(_initial);
494 g_free(middle_final);
495 } else if (pykeyposLen == 1) {
496 gchar* pystring;
497 pinyin_get_pinyin_string(m_inst, pykey, &pystring);
498 if (curoffset + 1 <= m_cursorPos) {
499 curoffset += 1;
500 charcurpos += strlen(pystring);
501 }
502 FcitxMessagesMessageConcatLast(FcitxInputStateGetPreedit(input), pystring);
503 g_free(pystring);
504 }
505 break;
506 }
507 case LPT_Zhuyin: {
508 guint16 pykeyposLen = 0;
509 pinyin_get_pinyin_key_rest_length(m_inst, pykeypos, &pykeyposLen);
510 gchar* pystring;
511 pinyin_get_zhuyin_string(m_inst, pykey, &pystring);
512 // libpinyin would give us null when it is something like "xi'"
513 if (!pystring) {
514 breakAhead = true;
515 break;
516 }
517 FcitxMessagesMessageConcatLast(FcitxInputStateGetPreedit(input), pystring);
518
519 if (curoffset + pykeyposLen <= m_cursorPos) {
520 curoffset += pykeyposLen;
521 charcurpos += strlen(pystring);
522 } else {
523 int diff = m_cursorPos - curoffset;
524 curoffset = m_cursorPos;
525 size_t len = fcitx_utf8_strlen(pystring);
526
527 if (diff > len)
528 charcurpos += strlen(pystring);
529 else {
530 charcurpos += fcitx_utf8_get_nth_char(pystring, diff) - pystring;
531 }
532 }
533 g_free(pystring);
534 break;
535 }
536 }
537 if (breakAhead) {
538 break;
539 }
540 // assertion would happen if pinyin string is sth like "xi'"
541 // so we use breakAhead to track such case. It's ugly but also works.
542 size_t nexti;
543 if (pinyin_get_right_pinyin_offset(m_inst, i, &nexti)) {
544 i = nexti;
545 } else {
546 break;
547 }
548 }
549
550 int buflen = m_buf.size();
551
552 if (lastpos < buflen) {
553 if (FcitxMessagesGetMessageCount(FcitxInputStateGetPreedit(input))) {
554 FcitxMessagesMessageConcatLast(FcitxInputStateGetPreedit(input), " ");
555 if (lastpos < m_cursorPos)
556 charcurpos ++;
557 }
558
559 for (int i = lastpos; i < buflen; i ++) {
560 char temp[2] = {'\0', '\0'};
561 temp[0] = m_buf[i];
562 FcitxMessagesMessageConcatLast(FcitxInputStateGetPreedit(input), temp);
563 if (lastpos < m_cursorPos) {
564 charcurpos ++;
565 lastpos++;
566 }
567 }
568 }
569 FcitxInputStateSetCursorPos(input, charcurpos);
570 }
571
sentence(guint8 index)572 std::string FcitxLibPinyin::sentence(guint8 index)
573 {
574 char* sentence = NULL;
575 pinyin_get_sentence(m_inst, index, &sentence);
576 std::string result = sentence ? sentence : "";
577 g_free(sentence);
578 return result;
579 }
580
581 /**
582 * @brief function DoInput has done everything for us.
583 *
584 * @param searchMode
585 * @return INPUT_RETURN_VALUE
586 **/
FcitxLibPinyinGetCandWords(void * arg)587 INPUT_RETURN_VALUE FcitxLibPinyinGetCandWords(void* arg)
588 {
589 FcitxLibPinyin* libpinyin = (FcitxLibPinyin*)arg;
590 return libpinyin->getCandWords();
591 }
592
getCandWords()593 INPUT_RETURN_VALUE FcitxLibPinyin::getCandWords() {
594 FcitxInstance* instance = m_owner->owner;
595 FcitxInputState* input = FcitxInstanceGetInputState(instance);
596 FcitxGlobalConfig* config = FcitxInstanceGetGlobalConfig(m_owner->owner);
597 FcitxLibPinyinConfig* pyConfig = &m_owner->config;
598 struct _FcitxCandidateWordList* candList = FcitxInputStateGetCandidateList(input);
599 FcitxCandidateWordSetPageSize(candList, config->iMaxCandWord);
600 FcitxUICloseInputWindow(instance);
601 strcpy(FcitxInputStateGetRawInputBuffer(input), m_buf.c_str());
602 FcitxInputStateSetRawInputBufferSize(input, m_buf.size());
603 FcitxInputStateSetShowCursor(input, true);
604 FcitxInputStateSetClientCursorPos(input, 0);
605
606 if (m_type == LPT_Zhuyin) {
607 FcitxKeyState state = candidateModifierMap[pyConfig->candidateModifiers];
608 FcitxCandidateWordSetChooseAndModifier(candList, "1234567890", state);
609 } else
610 FcitxCandidateWordSetChoose(candList, "1234567890");
611
612 /* add punc */
613 if (m_type == LPT_Zhuyin
614 && m_buf.size() == 1
615 && LibPinyinCheckZhuyinKey((FcitxKeySym) m_buf[0], pyConfig->zhuyinLayout, pyConfig->useTone)
616 && (m_buf[0] >= ' ' && m_buf[0] <= '\x7e') /* simple */
617 && !(m_buf[0] >= 'a' && m_buf[0] <= 'z') /* not a-z */
618 && !(m_buf[0] >= 'A' && m_buf[0] <= 'Z') /* not A-Z /*/
619 && !(m_buf[0] >= '0' && m_buf[0] <= '9') /* not digit */
620 ) {
621 int c = m_buf[0];
622 char* result = FcitxPuncGetPunc(instance, &c);
623 if (result) {
624 FcitxCandidateWord candWord;
625 FcitxLibPinyinCandWord* pyCand = (FcitxLibPinyinCandWord*) fcitx_utils_malloc0(sizeof(FcitxLibPinyinCandWord));
626 pyCand->ispunc = true;
627 candWord.callback = FcitxLibPinyinGetCandWord;
628 candWord.extraType = MSG_OTHER;
629 candWord.owner = this;
630 candWord.priv = pyCand;
631 candWord.strExtra = NULL;
632 candWord.strWord = strdup(result);
633 candWord.wordType = MSG_OTHER;
634
635 FcitxCandidateWordAppend(FcitxInputStateGetCandidateList(input), &candWord);
636 }
637 }
638 pinyin_guess_sentence(m_inst);
639 const std::string sentence = this->sentence(0);
640 if (!sentence.empty()) {
641 updatePreedit(sentence.c_str());
642
643 FcitxMessagesAddMessageAtLast(FcitxInputStateGetClientPreedit(input), MSG_INPUT, "%s", sentence.c_str());
644 if (m_buf.size() >= m_parsedLen) {
645 FcitxMessagesAddMessageAtLast(FcitxInputStateGetClientPreedit(input), MSG_INPUT, "%s", m_buf.substr(m_parsedLen).c_str());
646 }
647 } else {
648 FcitxInputStateSetCursorPos(input, m_cursorPos);
649 FcitxMessagesAddMessageAtLast(FcitxInputStateGetClientPreedit(input), MSG_INPUT, "%s", m_buf.c_str());
650 FcitxMessagesAddMessageAtLast(FcitxInputStateGetPreedit(input), MSG_INPUT, "%s", m_buf.c_str());
651 }
652
653 if (pinyinOffset() < m_parsedLen) {
654 pinyin_guess_candidates(m_inst, pinyinOffset(), FcitxLibPinyinTransSortOption(pyConfig->sort));
655 guint candidateLen = 0;
656 pinyin_get_n_candidate(m_inst, &candidateLen);
657 int i = 0;
658 for (i = 0 ; i < candidateLen; i ++) {
659 lookup_candidate_t* token = NULL;
660 pinyin_get_candidate(m_inst, i, &token);
661 FcitxCandidateWord candWord;
662 FcitxLibPinyinCandWord* pyCand = (FcitxLibPinyinCandWord*) fcitx_utils_malloc0(sizeof(FcitxLibPinyinCandWord));
663 pyCand->ispunc = false;
664 pyCand->idx = i;
665 candWord.callback = FcitxLibPinyinGetCandWord;
666 candWord.extraType = MSG_OTHER;
667 candWord.owner = this;
668 candWord.priv = pyCand;
669 candWord.strExtra = NULL;
670
671 const gchar* phrase_string = NULL;
672 pinyin_get_candidate_string(m_inst, token, &phrase_string);
673 candWord.strWord = strdup(phrase_string);
674 candWord.wordType = MSG_OTHER;
675
676 FcitxCandidateWordAppend(FcitxInputStateGetCandidateList(input), &candWord);
677 }
678 } else {
679 FcitxCandidateWord candWord;
680 FcitxLibPinyinCandWord* pyCand = (FcitxLibPinyinCandWord*) fcitx_utils_malloc0(sizeof(FcitxLibPinyinCandWord));
681 pyCand->ispunc = false;
682 pyCand->idx = -1;
683 candWord.callback = FcitxLibPinyinGetCandWord;
684 candWord.extraType = MSG_OTHER;
685 candWord.owner = this;
686 candWord.priv = pyCand;
687 candWord.strExtra = NULL;
688 std::string cand;
689 if (m_buf.size() >= m_parsedLen) {
690 cand += m_buf.substr(m_parsedLen);
691 }
692
693 candWord.strWord = strdup(cand.c_str());
694 candWord.wordType = MSG_OTHER;
695
696 FcitxCandidateWordAppend(FcitxInputStateGetCandidateList(input), &candWord);
697 }
698
699
700 return IRV_DISPLAY_CANDWORDS;
701 }
702
703 /**
704 * @brief get the candidate word by index
705 *
706 * @param iIndex index of candidate word
707 * @return the string of canidate word
708 **/
FcitxLibPinyinGetCandWord(void * arg,FcitxCandidateWord * candWord)709 INPUT_RETURN_VALUE FcitxLibPinyinGetCandWord(void* arg, FcitxCandidateWord* candWord)
710 {
711 FcitxLibPinyin* libpinyin = (FcitxLibPinyin*)arg;
712 return libpinyin->getCandWord(candWord);
713 }
714
getCandWord(FcitxCandidateWord * candWord)715 INPUT_RETURN_VALUE FcitxLibPinyin::getCandWord(FcitxCandidateWord* candWord) {
716 FcitxLibPinyinCandWord* pyCand = (FcitxLibPinyinCandWord*) candWord->priv;
717 FcitxInstance* instance = m_owner->owner;
718 FcitxInputState* input = FcitxInstanceGetInputState(instance);
719
720 if (pyCand->ispunc) {
721 strcpy(FcitxInputStateGetOutputString(input), candWord->strWord);
722 return IRV_COMMIT_STRING;
723 } else if (pyCand->idx < 0) {
724 strcpy(FcitxInputStateGetOutputString(input), (sentence(0) + candWord->strWord).c_str());
725 return IRV_COMMIT_STRING;
726 } else {
727 guint candidateLen = 0;
728 pinyin_get_n_candidate(m_inst, &candidateLen);
729 if (candidateLen <= pyCand->idx)
730 return IRV_TO_PROCESS;
731 lookup_candidate_t* cand = NULL;
732 pinyin_get_candidate(m_inst, pyCand->idx, &cand);
733 int newOffset = pinyin_choose_candidate(m_inst, pinyinOffset(), cand);
734 if (newOffset != pinyinOffset()) {
735 const gchar* phrase_string = NULL;
736 pinyin_get_candidate_string(m_inst, cand, &phrase_string);
737 m_fixedString.push_back(std::make_pair(offset() + fcitx_utf8_strlen(phrase_string), newOffset));
738 }
739
740 if (pinyinOffset() == m_parsedLen) {
741 if (m_parsedLen == m_buf.size()) {
742 pinyin_guess_sentence(m_inst);
743 const std::string sentence = this->sentence(0);
744 if (!sentence.empty()) {
745 strcpy(FcitxInputStateGetOutputString(input), sentence.c_str());
746 pinyin_train(m_inst, 0);
747 } else
748 strcpy(FcitxInputStateGetOutputString(input), "");
749
750 return IRV_COMMIT_STRING;
751 }
752 }
753
754 int pyoffset = pinyinOffset();
755 if (pyoffset > m_cursorPos)
756 m_cursorPos = pyoffset;
757
758 return IRV_DISPLAY_CANDWORDS;
759 }
760 return IRV_TO_PROCESS;
761 }
762
FcitxLibPinyin(FcitxLibPinyinAddonInstance * libpinyinaddon,LIBPINYIN_TYPE type)763 FcitxLibPinyin::FcitxLibPinyin(FcitxLibPinyinAddonInstance* libpinyinaddon, LIBPINYIN_TYPE type) :
764 m_inst(NULL), m_type(type), m_owner(libpinyinaddon)
765 {
766 }
767
~FcitxLibPinyin()768 FcitxLibPinyin::~FcitxLibPinyin()
769 {
770 if (m_inst)
771 pinyin_free_instance(m_inst);
772 }
773
LibPinyinSavePinyinWord(void * arg,FcitxModuleFunctionArg args)774 void* LibPinyinSavePinyinWord(void* arg, FcitxModuleFunctionArg args)
775 {
776 FcitxLibPinyinAddonInstance* libpinyinaddon = (FcitxLibPinyinAddonInstance*) arg;
777 FcitxIM* im = FcitxInstanceGetCurrentIM(libpinyinaddon->owner);
778 pinyin_context_t* context = NULL;
779 if (strcmp(im->uniqueName, "pinyin-libpinyin") == 0 ||
780 strcmp(im->uniqueName, "shuangpin-libpinyin") == 0) {
781 context = libpinyinaddon->pinyin_context;
782 }
783 if (!context)
784 return NULL;
785
786 FcitxLibPinyin* libpinyin = (FcitxLibPinyin*) im->klass;
787 libpinyin->savePinyinWord(static_cast<const char*>(args.args[0]));
788
789 return NULL;
790 }
791
savePinyinWord(const char * str)792 void FcitxLibPinyin::savePinyinWord(const char* str) {
793 // valid non empty utf8 string
794 if (fcitx_utf8_check_string(str) && *str) {
795 const char *s = str;
796 while (*s) {
797 uint32_t chr;
798
799 s = fcitx_utf8_get_char(s, &chr);
800 // ok ok, let's filter out ascii
801 if (chr < 256) {
802 return;
803 }
804 }
805 pinyin_remember_user_input(m_inst, str, -1);
806 }
807 }
808
809 /**
810 * @brief initialize the extra input method
811 *
812 * @param arg
813 * @return successful or not
814 **/
FcitxLibPinyinCreate(FcitxInstance * instance)815 void* FcitxLibPinyinCreate(FcitxInstance* instance)
816 {
817 FcitxLibPinyinAddonInstance* libpinyinaddon = (FcitxLibPinyinAddonInstance*) fcitx_utils_malloc0(sizeof(FcitxLibPinyinAddonInstance));
818 bindtextdomain("fcitx-libpinyin", LOCALEDIR);
819 bind_textdomain_codeset("fcitx-libpinyin", "UTF-8");
820 libpinyinaddon->owner = instance;
821 FcitxAddon* addon = FcitxAddonsGetAddonByName(FcitxInstanceGetAddons(instance), "fcitx-libpinyin");
822
823 if (!FcitxLibPinyinConfigLoadConfig(&libpinyinaddon->config)) {
824 free(libpinyinaddon);
825 return NULL;
826 }
827
828 libpinyinaddon->pinyin = new FcitxLibPinyin(libpinyinaddon, LPT_Pinyin);
829 libpinyinaddon->shuangpin = new FcitxLibPinyin(libpinyinaddon, LPT_Shuangpin);
830 libpinyinaddon->zhuyin = new FcitxLibPinyin(libpinyinaddon, LPT_Zhuyin);
831
832 FcitxLibPinyinReconfigure(libpinyinaddon);
833
834 FcitxInstanceRegisterIM(instance,
835 libpinyinaddon->pinyin,
836 "pinyin-libpinyin",
837 _("Pinyin (LibPinyin)"),
838 "pinyin-libpinyin",
839 FcitxLibPinyinInit,
840 FcitxLibPinyinReset,
841 FcitxLibPinyinDoInput,
842 FcitxLibPinyinGetCandWords,
843 NULL,
844 FcitxLibPinyinSave,
845 NULL,
846 NULL,
847 5,
848 libpinyinaddon->config.bTraditionalDataForPinyin ? "zh_TW" : "zh_CN"
849 );
850
851 FcitxInstanceRegisterIM(instance,
852 libpinyinaddon->shuangpin,
853 "shuangpin-libpinyin",
854 _("Shuangpin (LibPinyin)"),
855 "shuangpin-libpinyin",
856 FcitxLibPinyinInit,
857 FcitxLibPinyinReset,
858 FcitxLibPinyinDoInput,
859 FcitxLibPinyinGetCandWords,
860 NULL,
861 FcitxLibPinyinSave,
862 NULL,
863 NULL,
864 5,
865 libpinyinaddon->config.bTraditionalDataForPinyin ? "zh_TW" : "zh_CN"
866 );
867
868 FcitxInstanceRegisterIM(instance,
869 libpinyinaddon->zhuyin,
870 "zhuyin-libpinyin",
871 _("Bopomofo (LibPinyin)"),
872 "bopomofo",
873 FcitxLibPinyinInit,
874 FcitxLibPinyinReset,
875 FcitxLibPinyinDoInput,
876 FcitxLibPinyinGetCandWords,
877 NULL,
878 FcitxLibPinyinSave,
879 NULL,
880 NULL,
881 5,
882 libpinyinaddon->config.bSimplifiedDataForZhuyin ? "zh_CN" : "zh_TW"
883 );
884
885 FcitxModuleAddFunction(addon, LibPinyinSavePinyinWord);
886
887 libpinyinaddon->bus = new FcitxLibPinyinBus(libpinyinaddon);
888
889 return libpinyinaddon;
890 }
891
892 /**
893 * @brief Destroy the input method while unload it.
894 *
895 * @return int
896 **/
FcitxLibPinyinDestroy(void * arg)897 void FcitxLibPinyinDestroy(void* arg)
898 {
899 FcitxLibPinyinAddonInstance* libpinyin = (FcitxLibPinyinAddonInstance*) arg;
900 delete libpinyin->pinyin;
901 delete libpinyin->zhuyin;
902 delete libpinyin->shuangpin;
903
904 if (libpinyin->pinyin_context)
905 pinyin_fini(libpinyin->pinyin_context);
906 if (libpinyin->zhuyin_context)
907 pinyin_fini(libpinyin->zhuyin_context);
908
909 delete libpinyin->bus;
910
911 free(libpinyin);
912 }
913
FcitxLibPinyinReconfigure(FcitxLibPinyinAddonInstance * libpinyinaddon)914 void FcitxLibPinyinReconfigure(FcitxLibPinyinAddonInstance* libpinyinaddon)
915 {
916 FcitxLibPinyinConfig* config = &libpinyinaddon->config;
917
918 if (libpinyinaddon->zhuyin_context) {
919 pinyin_set_zhuyin_scheme(libpinyinaddon->zhuyin_context, FcitxLibPinyinTransZhuyinLayout(config->zhuyinLayout));
920
921 for (int i = 0; i <= FCITX_ZHUYIN_DICT_LAST; i++) {
922 if (config->dict_zhuyin[i]) {
923 pinyin_load_addon_phrase_library(libpinyinaddon->zhuyin_context, FcitxLibPinyinTransZhuyinDictionary(static_cast<FCITX_ZHUYIN_DICTIONARY>(i)));
924 } else {
925 pinyin_unload_addon_phrase_library(libpinyinaddon->zhuyin_context, FcitxLibPinyinTransZhuyinDictionary(static_cast<FCITX_ZHUYIN_DICTIONARY>(i)));
926 }
927 }
928 }
929 if (libpinyinaddon->pinyin_context) {
930 pinyin_set_double_pinyin_scheme(libpinyinaddon->pinyin_context, FcitxLibPinyinTransShuangpinScheme(config->spScheme));
931 for (int i = 0; i <= FCITX_DICT_LAST; i++) {
932 if (config->dict[i]) {
933 pinyin_load_addon_phrase_library(libpinyinaddon->pinyin_context, FcitxLibPinyinTransDictionary(static_cast<FCITX_DICTIONARY>(i)));
934 } else {
935 pinyin_unload_addon_phrase_library(libpinyinaddon->pinyin_context, FcitxLibPinyinTransDictionary(static_cast<FCITX_DICTIONARY>(i)));
936 }
937 }
938 }
939 pinyin_option_t settings = 0;
940 settings |= DYNAMIC_ADJUST;
941 settings |= USE_DIVIDED_TABLE | USE_RESPLIT_TABLE;
942 for (int i = 0; i <= FCITX_CR_LAST; i ++) {
943 if (config->cr[i])
944 settings |= FcitxLibPinyinTransCorrection((FCITX_CORRECTION) i);
945 }
946 for (int i = 0; i <= FCITX_AMB_LAST; i ++) {
947 if (config->amb[i])
948 settings |= FcitxLibPinyinTransAmbiguity((FCITX_AMBIGUITY) i);
949 }
950
951 if (config->incomplete) {
952 settings |= PINYIN_INCOMPLETE;
953 }
954
955 if (config->zhuyinIncomplete) {
956 settings |= ZHUYIN_INCOMPLETE;
957 }
958
959 if (config->useTone) {
960 settings |= USE_TONE;
961 }
962 settings |= IS_PINYIN;
963 settings |= IS_ZHUYIN;
964 if (libpinyinaddon->pinyin_context)
965 pinyin_set_options(libpinyinaddon->pinyin_context, settings);
966 if (libpinyinaddon->zhuyin_context)
967 pinyin_set_options(libpinyinaddon->zhuyin_context, settings);
968 }
969
FcitxLibPinyinReloadConfig(void * arg)970 void FcitxLibPinyinReloadConfig(void* arg)
971 {
972 FcitxLibPinyinAddonInstance* libpinyinaddon = (FcitxLibPinyinAddonInstance*) arg;
973 FcitxLibPinyinConfigLoadConfig(&libpinyinaddon->config);
974 FcitxLibPinyinReconfigure(libpinyinaddon);
975 }
976
FcitxLibPinyinSave(void * arg)977 void FcitxLibPinyinSave(void* arg)
978 {
979 FcitxLibPinyin* libpinyin = (FcitxLibPinyin*) arg;
980 libpinyin->save();
981 }
982
save()983 void FcitxLibPinyin::save() {
984 if (m_owner->zhuyin_context)
985 pinyin_save(m_owner->zhuyin_context);
986 if (m_owner->pinyin_context)
987 pinyin_save(m_owner->pinyin_context);
988 }
989
clearData(int type)990 void FcitxLibPinyin::clearData(int type)
991 {
992 FcitxLibPinyinAddonInstance* libpinyinaddon = m_owner;
993 reset();
994
995 pinyin_context_t* context = m_type != LPT_Zhuyin ? libpinyinaddon->pinyin_context : libpinyinaddon->zhuyin_context;
996 if (!context) {
997 return;
998 }
999
1000 switch (type) {
1001 case 0:
1002 pinyin_mask_out(context, PHRASE_INDEX_LIBRARY_MASK, PHRASE_INDEX_MAKE_TOKEN(USER_DICTIONARY, null_token));
1003 pinyin_mask_out(context, PHRASE_INDEX_LIBRARY_MASK, PHRASE_INDEX_MAKE_TOKEN(ADDON_DICTIONARY, null_token));
1004 break;
1005 case 1:
1006 pinyin_mask_out(context, PHRASE_INDEX_LIBRARY_MASK, PHRASE_INDEX_MAKE_TOKEN(NETWORK_DICTIONARY, null_token));
1007 break;
1008 case 2:
1009 pinyin_mask_out(context, 0, 0);
1010 break;
1011 }
1012
1013 pinyin_train(m_inst, 0);
1014 pinyin_save(context);
1015 }
1016
import()1017 void FcitxLibPinyin::import()
1018 {
1019 FcitxLibPinyinAddonInstance* libpinyinaddon = m_owner;
1020 reset();
1021 load();
1022
1023 LIBPINYIN_LANGUAGE_TYPE langType = m_type == LPT_Zhuyin ?
1024 (libpinyinaddon->config.bSimplifiedDataForZhuyin ? LPLT_Simplified : LPLT_Traditional)
1025 : (libpinyinaddon->config.bTraditionalDataForPinyin ? LPLT_Traditional : LPLT_Simplified);
1026
1027 pinyin_context_t* context = m_type != LPT_Zhuyin ? libpinyinaddon->pinyin_context : libpinyinaddon->zhuyin_context;
1028 if (!context)
1029 return;
1030
1031 const char* path = langType == LPLT_Simplified ? "libpinyin/importdict" : "libpinyin/importdict_zhuyin";
1032
1033 pinyin_mask_out(context, PHRASE_INDEX_LIBRARY_MASK, PHRASE_INDEX_MAKE_TOKEN(NETWORK_DICTIONARY, null_token));
1034
1035 import_iterator_t* iter = pinyin_begin_add_phrases(context, NETWORK_DICTIONARY);
1036 if (iter == NULL) {
1037 return;
1038 }
1039
1040 FcitxStringHashSet* sset = FcitxXDGGetFiles(path, NULL, ".txt");
1041 HASH_FOREACH(str, sset, FcitxStringHashSet) {
1042 /* user phrase library should be already loaded here. */
1043 FILE* dictfile = FcitxXDGGetFileWithPrefix(path, str->name, "r", NULL);
1044 if (NULL == dictfile) {
1045 continue;
1046 }
1047
1048 char* linebuf = NULL;
1049 size_t size = 0;
1050 while (getline(&linebuf, &size, dictfile) != -1) {
1051 if (0 == strlen(linebuf))
1052 continue;
1053
1054 if ('\n' == linebuf[strlen(linebuf) - 1]) {
1055 linebuf[strlen(linebuf) - 1] = '\0';
1056 }
1057
1058 gchar** items = g_strsplit_set(linebuf, " \t", 3);
1059 guint len = g_strv_length(items);
1060 do {
1061
1062 gchar* phrase = NULL, * pinyin = NULL;
1063 gint count = -1;
1064 if (2 == len || 3 == len) {
1065 phrase = items[0];
1066 pinyin = items[1];
1067 if (3 == len)
1068 count = atoi(items[2]);
1069 } else {
1070 break;
1071 }
1072
1073 if (!fcitx_utf8_check_string(phrase)) {
1074 continue;
1075 }
1076
1077 pinyin_iterator_add_phrase(iter, phrase, pinyin, count);
1078 } while (0);
1079
1080 g_strfreev(items);
1081 }
1082 free(linebuf);
1083 fclose(dictfile);
1084 }
1085
1086 pinyin_end_add_phrases(iter);
1087
1088 if (m_inst) {
1089 pinyin_train(m_inst, 0);
1090 }
1091 pinyin_save(context);
1092 }
1093
1094 // kate: indent-mode cstyle; indent-width 4; replace-tabs on;
1095