1 /**
2 * chewingio.c
3 *
4 * Copyright (c) 1999, 2000, 2001
5 * Lu-chuan Kung and Kang-pen Chen.
6 * All rights reserved.
7 *
8 * Copyright (c) 2004-2008, 2010-2014
9 * libchewing Core Team. See ChangeLog for details.
10 *
11 * See the file "COPYING" for information on usage and redistribution
12 * of this file.
13 */
14
15 /**
16 * @file chewingio.c
17 * @brief Implement basic I/O routines for Chewing manipulation.
18 */
19 #ifdef HAVE_CONFIG_H
20 # include <config.h>
21 #endif
22
23 #include <assert.h>
24 #include <string.h>
25 #include <ctype.h>
26 #include <stdlib.h>
27 #include <stdio.h>
28
29 #include "chewing-utf8-util.h"
30 #include "global.h"
31 #include "bopomofo-private.h"
32 #include "chewingutil.h"
33 #include "userphrase-private.h"
34 #include "choice-private.h"
35 #include "dict-private.h"
36 #include "tree-private.h"
37 #include "pinyin-private.h"
38 #include "private.h"
39 #include "chewingio.h"
40 #include "mod_aux.h"
41 #include "global-private.h"
42 #include "plat_path.h"
43 #include "chewing-private.h"
44 #include "key2pho-private.h"
45
46 #if WITH_SQLITE3
47 # include "chewing-sql.h"
48 #else
49 # include "hash-private.h"
50 #endif
51
52 const char *const kb_type_str[] = {
53 "KB_DEFAULT",
54 "KB_HSU",
55 "KB_IBM",
56 "KB_GIN_YIEH",
57 "KB_ET",
58 "KB_ET26",
59 "KB_DVORAK",
60 "KB_DVORAK_HSU",
61 "KB_DACHEN_CP26",
62 "KB_HANYU_PINYIN",
63 "KB_THL_PINYIN",
64 "KB_MPS2_PINYIN",
65 "KB_CARPALX"
66 };
67
68 const char *const DICT_FILES[] = {
69 DICT_FILE,
70 PHONE_TREE_FILE,
71 NULL,
72 };
73
74 const char *const SYMBOL_TABLE_FILES[] = {
75 SYMBOL_TABLE_FILE,
76 NULL,
77 };
78
79 const char *const EASY_SYMBOL_FILES[] = {
80 SOFTKBD_TABLE_FILE,
81 NULL,
82 };
83
84 const char *const PINYIN_FILES[] = {
85 PINYIN_TAB_NAME,
86 NULL,
87 };
88
chewing_KBStr2Num(const char str[])89 CHEWING_API int chewing_KBStr2Num(const char str[])
90 {
91 int i;
92
93 STATIC_ASSERT(KB_TYPE_NUM == ARRAY_SIZE(kb_type_str));
94 for (i = 0; i < KB_TYPE_NUM; i++) {
95 if (!strcmp(str, kb_type_str[i]))
96 return i;
97 }
98 return KB_DEFAULT;
99 }
100
chooseCandidate(ChewingContext * ctx,int toSelect,int key_buf_cursor)101 static void chooseCandidate(ChewingContext *ctx, int toSelect, int key_buf_cursor)
102 {
103 ChewingData *pgdata = ctx->data;
104
105 if (toSelect) {
106 if (!pgdata->bSelect) {
107 ChoiceInitAvail(pgdata);
108 } else {
109 if (ChoiceHasNextAvail(pgdata))
110 ChoiceNextAvail(pgdata);
111 else /* rollover */
112 ChoiceFirstAvail(pgdata);
113 }
114 } else if (pgdata->symbolKeyBuf[key_buf_cursor]) {
115 /* Open Symbol Choice List */
116 if (pgdata->choiceInfo.isSymbol == WORD_CHOICE) {
117 OpenSymbolChoice(pgdata);
118 }
119 /**
120 * If these's only one candidate list available, ChoiceFirstAvail
121 * will re-open the list, namely turn back to the firt page.
122 * However, it doesn't work for symbols, therefore we
123 * set the page number to 0 directly.
124 */
125 else if (pgdata->bSelect) {
126 pgdata->choiceInfo.pageNo = 0;
127 }
128 } else {
129 /*
130 * The cursor position is not word, nor symbol. The only
131 * possible case is that user just uses ` to open symbol
132 * selection. In this case, when chooseCandidate is called,
133 * libchewing needs to reset pageNo to 0 to do rollover.
134 */
135 if (pgdata->bSelect) {
136 pgdata->choiceInfo.pageNo = 0;
137 }
138 }
139 }
140
NullLogger(void * data UNUSED,int level UNUSED,const char * fmt UNUSED,...)141 static void NullLogger(void *data UNUSED, int level UNUSED, const char *fmt UNUSED, ...)
142 {
143 }
144
allocate_ChewingData(void (* logger)(void * data,int level,const char * fmt,...),void * loggerdata)145 static ChewingData *allocate_ChewingData(void (*logger) (void *data, int level, const char *fmt, ...), void *loggerdata)
146 {
147 static const int DEFAULT_SELKEY[] = { '1', '2', '3', '4', '5', '6', '7', '8', '9', '0' };
148
149 ChewingData *data = ALC(ChewingData, 1);
150
151 if (data) {
152 data->config.candPerPage = MAX_SELKEY;
153 data->config.maxChiSymbolLen = MAX_CHI_SYMBOL_LEN;
154 data->logger = logger;
155 data->loggerData = loggerdata;
156 memcpy(data->config.selKey, DEFAULT_SELKEY, sizeof(data->config.selKey));
157 }
158
159 return data;
160 }
161
chewing_new2(const char * syspath,const char * userpath,void (* logger)(void * data,int level,const char * fmt,...),void * loggerdata)162 CHEWING_API ChewingContext *chewing_new2(const char *syspath,
163 const char *userpath,
164 void (*logger) (void *data, int level, const char *fmt, ...), void *loggerdata)
165 {
166 ChewingContext *ctx;
167 ChewingData *pgdata;
168 int ret;
169 char search_path[PATH_MAX + 1] = {0};
170 char path[PATH_MAX];
171 char *userphrase_path = NULL;
172
173 if (!logger)
174 logger = NullLogger;
175
176 ctx = ALC(ChewingContext, 1);
177
178 if (!ctx)
179 goto error;
180
181 ctx->output = ALC(ChewingOutput, 1);
182
183 if (!ctx->output)
184 goto error;
185
186 pgdata = allocate_ChewingData(logger, loggerdata);
187 if (!pgdata)
188 goto error;
189 ctx->data = pgdata;
190
191 LOG_API("syspath = %d, userpath = %d", syspath, userpath);
192
193 chewing_Reset(ctx);
194
195 if (syspath) {
196 strncpy(search_path, syspath, sizeof(search_path) - 1);
197 } else {
198 ret = get_search_path(search_path, sizeof(search_path));
199 if (ret) {
200 LOG_ERROR("get_search_path returns %d", ret);
201 goto error;
202 }
203 }
204 LOG_VERBOSE("search_path is %s", search_path);
205
206 ret = find_path_by_files(search_path, DICT_FILES, path, sizeof(path));
207 if (ret) {
208 LOG_ERROR("find_path_by_files returns %d", ret);
209 goto error;
210 }
211
212 ret = InitDict(ctx->data, path);
213 if (ret) {
214 LOG_ERROR("InitDict returns %d", ret);
215 goto error;
216 }
217
218 ret = InitTree(ctx->data, path);
219 if (ret) {
220 LOG_ERROR("InitTree returns %d", ret);
221 goto error;
222 }
223
224 if (userpath) {
225 userphrase_path = strdup(userpath);
226 } else {
227 userphrase_path = GetDefaultUserPhrasePath(ctx->data);
228 }
229
230 if (!userphrase_path) {
231 LOG_ERROR("GetUserPhraseStoragePath returns %p", path);
232 goto error;
233 }
234
235 ret = InitUserphrase(ctx->data, userphrase_path);
236 free(userphrase_path);
237
238 if (ret) {
239 LOG_ERROR("InitSql returns %d", ret);
240 goto error;
241 }
242
243 ctx->cand_no = 0;
244
245 ret = find_path_by_files(search_path, SYMBOL_TABLE_FILES, path, sizeof(path));
246 if (ret) {
247 LOG_ERROR("find_path_by_files returns %d", ret);
248 goto error;
249 }
250
251 ret = InitSymbolTable(ctx->data, path);
252 if (ret) {
253 LOG_ERROR("InitSymbolTable returns %d", ret);
254 goto error;
255 }
256
257 ret = find_path_by_files(search_path, EASY_SYMBOL_FILES, path, sizeof(path));
258 if (ret) {
259 LOG_ERROR("find_path_by_files returns %d", ret);
260 goto error;
261 }
262
263 ret = InitEasySymbolInput(ctx->data, path);
264 if (ret) {
265 LOG_ERROR("InitEasySymbolInput returns %d", ret);
266 goto error;
267 }
268
269 ret = find_path_by_files(search_path, PINYIN_FILES, path, sizeof(path));
270 if (ret) {
271 LOG_ERROR("find_path_by_files returns %d", ret);
272 goto error;
273 }
274
275 ret = InitPinyin(ctx->data, path);
276 if (!ret) {
277 LOG_ERROR("InitPinyin returns %d", ret);
278 goto error;
279 }
280
281 return ctx;
282 error:
283 chewing_delete(ctx);
284 return NULL;
285 }
286
chewing_new()287 CHEWING_API ChewingContext *chewing_new()
288 {
289 return chewing_new2(NULL, NULL, NULL, NULL);
290 }
291
chewing_Reset(ChewingContext * ctx)292 CHEWING_API int chewing_Reset(ChewingContext *ctx)
293 {
294 ChewingData *pgdata;
295 ChewingStaticData static_data;
296 ChewingConfigData old_config;
297 void (*logger) (void *data, int level, const char *fmt, ...);
298 void *loggerData;
299
300 if (!ctx) {
301 return -1;
302 }
303 pgdata = ctx->data;
304
305 LOG_API("");
306
307 /* Backup old config and restore it after clearing pgdata structure. */
308 old_config = pgdata->config;
309 static_data = pgdata->static_data;
310 logger = pgdata->logger;
311 loggerData = pgdata->loggerData;
312 memset(pgdata, 0, sizeof(ChewingData));
313 pgdata->config = old_config;
314 pgdata->static_data = static_data;
315 pgdata->logger = logger;
316 pgdata->loggerData = loggerData;
317
318 /* bopomofoData */
319 memset(&(pgdata->bopomofoData), 0, sizeof(BopomofoData));
320
321 /* choiceInfo */
322 memset(&(pgdata->choiceInfo), 0, sizeof(ChoiceInfo));
323
324 pgdata->chiSymbolCursor = 0;
325 pgdata->chiSymbolBufLen = 0;
326 pgdata->nPhoneSeq = 0;
327 memset(pgdata->bUserArrCnnct, 0, sizeof(int) * (MAX_PHONE_SEQ_LEN + 1));
328 memset(pgdata->bUserArrBrkpt, 0, sizeof(int) * (MAX_PHONE_SEQ_LEN + 1));
329 pgdata->bChiSym = CHINESE_MODE;
330 pgdata->bFullShape = HALFSHAPE_MODE;
331 pgdata->bSelect = 0;
332 pgdata->nSelect = 0;
333 pgdata->PointStart = -1;
334 pgdata->PointEnd = 0;
335 pgdata->phrOut.nNumCut = 0;
336 return 0;
337 }
338
chewing_set_KBType(ChewingContext * ctx,int kbtype)339 CHEWING_API int chewing_set_KBType(ChewingContext *ctx, int kbtype)
340 {
341 ChewingData *pgdata;
342
343 if (!ctx) {
344 return -1;
345 }
346 pgdata = ctx->data;
347
348 LOG_API("kbtype = %d", kbtype);
349
350 if (kbtype < KB_TYPE_NUM && kbtype >= 0) {
351 ctx->data->bopomofoData.kbtype = kbtype;
352 return 0;
353 } else {
354 ctx->data->bopomofoData.kbtype = KB_DEFAULT;
355 return -1;
356 }
357 }
358
chewing_get_KBType(const ChewingContext * ctx)359 CHEWING_API int chewing_get_KBType(const ChewingContext *ctx)
360 {
361 const ChewingData *pgdata;
362
363 if (!ctx) {
364 return -1;
365 }
366 pgdata = ctx->data;
367
368 LOG_API("kbtype = %d", ctx->data->bopomofoData.kbtype);
369
370 return ctx->data->bopomofoData.kbtype;
371 }
372
chewing_get_KBString(const ChewingContext * ctx)373 CHEWING_API char *chewing_get_KBString(const ChewingContext *ctx)
374 {
375 const ChewingData *pgdata;
376
377 if (!ctx) {
378 return strdup("");
379 }
380 pgdata = ctx->data;
381
382 LOG_API("KBString = %s", kb_type_str[ctx->data->bopomofoData.kbtype]);
383
384 return strdup(kb_type_str[ctx->data->bopomofoData.kbtype]);
385 }
386
chewing_delete(ChewingContext * ctx)387 CHEWING_API void chewing_delete(ChewingContext *ctx)
388 {
389 if (ctx) {
390 if (ctx->data) {
391 TerminatePinyin(ctx->data);
392 TerminateEasySymbolTable(ctx->data);
393 TerminateSymbolTable(ctx->data);
394 TerminateUserphrase(ctx->data);
395 TerminateTree(ctx->data);
396 TerminateDict(ctx->data);
397 free(ctx->data);
398 }
399
400 if (ctx->output)
401 free(ctx->output);
402 free(ctx);
403 }
404 return;
405 }
406
chewing_free(void * p)407 CHEWING_API void chewing_free(void *p)
408 {
409 free(p);
410 }
411
chewing_set_candPerPage(ChewingContext * ctx,int n)412 CHEWING_API void chewing_set_candPerPage(ChewingContext *ctx, int n)
413 {
414 ChewingData *pgdata;
415
416 if (!ctx) {
417 return;
418 }
419 pgdata = ctx->data;
420
421 LOG_API("n = %d", n);
422
423 if (MIN_SELKEY <= n && n <= MAX_SELKEY && ctx->data->config.selKey[n - 1])
424 ctx->data->config.candPerPage = n;
425 }
426
chewing_get_candPerPage(const ChewingContext * ctx)427 CHEWING_API int chewing_get_candPerPage(const ChewingContext *ctx)
428 {
429 const ChewingData *pgdata;
430
431 if (!ctx) {
432 return -1;
433 }
434 pgdata = ctx->data;
435
436 LOG_API("candPerPage = %d", ctx->data->config.candPerPage);
437
438 return ctx->data->config.candPerPage;
439 }
440
chewing_set_maxChiSymbolLen(ChewingContext * ctx,int n)441 CHEWING_API void chewing_set_maxChiSymbolLen(ChewingContext *ctx, int n)
442 {
443 ChewingData *pgdata;
444
445 if (!ctx) {
446 return;
447 }
448 pgdata = ctx->data;
449
450 LOG_API("n = %d", n);
451
452 if (MIN_CHI_SYMBOL_LEN <= n && n <= MAX_CHI_SYMBOL_LEN)
453 ctx->data->config.maxChiSymbolLen = n;
454 }
455
chewing_get_maxChiSymbolLen(const ChewingContext * ctx)456 CHEWING_API int chewing_get_maxChiSymbolLen(const ChewingContext *ctx)
457 {
458 const ChewingData *pgdata;
459
460 if (!ctx) {
461 return -1;
462 }
463 pgdata = ctx->data;
464
465 LOG_API("maxChiSymbolLen = %d", ctx->data->config.maxChiSymbolLen);
466
467 return ctx->data->config.maxChiSymbolLen;
468 }
469
chewing_set_selKey(ChewingContext * ctx,const int * selkeys,int len)470 CHEWING_API void chewing_set_selKey(ChewingContext *ctx, const int *selkeys, int len)
471 {
472 ChewingData *pgdata;
473
474 if (!ctx) {
475 return;
476 }
477 pgdata = ctx->data;
478
479 LOG_API("");
480
481 if (!selkeys) {
482 return;
483 }
484
485 if (MIN_SELKEY <= len && len <= MAX_SELKEY) {
486 memset(ctx->data->config.selKey, 0, sizeof(ctx->data->config.selKey));
487 memcpy(ctx->data->config.selKey, selkeys, sizeof(*selkeys) * len);
488 }
489 }
490
chewing_get_selKey(const ChewingContext * ctx)491 CHEWING_API int *chewing_get_selKey(const ChewingContext *ctx)
492 {
493 const ChewingData *pgdata;
494 int *selkeys;
495
496 if (!ctx) {
497 return NULL;
498 }
499 pgdata = ctx->data;
500
501 LOG_API("");
502
503 selkeys = ALC(int, MAX_SELKEY);
504 if (selkeys) {
505 memcpy(selkeys, ctx->data->config.selKey, sizeof(*selkeys) * MAX_SELKEY);
506 }
507 return selkeys;
508 }
509
chewing_set_addPhraseDirection(ChewingContext * ctx,int direction)510 CHEWING_API void chewing_set_addPhraseDirection(ChewingContext *ctx, int direction)
511 {
512 ChewingData *pgdata;
513
514 if (!ctx) {
515 return;
516 }
517 pgdata = ctx->data;
518
519 LOG_API("direction = %d", direction);
520
521 if (direction == 0 || direction == 1)
522 ctx->data->config.bAddPhraseForward = direction;
523 }
524
chewing_get_addPhraseDirection(const ChewingContext * ctx)525 CHEWING_API int chewing_get_addPhraseDirection(const ChewingContext *ctx)
526 {
527 const ChewingData *pgdata;
528
529 if (!ctx) {
530 return -1;
531 }
532 pgdata = ctx->data;
533
534 LOG_API("bAddPhraseForward = %d", ctx->data->config.bAddPhraseForward);
535
536 return ctx->data->config.bAddPhraseForward;
537 }
538
chewing_set_spaceAsSelection(ChewingContext * ctx,int mode)539 CHEWING_API void chewing_set_spaceAsSelection(ChewingContext *ctx, int mode)
540 {
541 ChewingData *pgdata;
542
543 if (!ctx) {
544 return;
545 }
546 pgdata = ctx->data;
547
548 LOG_API("mode = %d", mode);
549
550 if (mode == 0 || mode == 1)
551 ctx->data->config.bSpaceAsSelection = mode;
552 }
553
chewing_get_spaceAsSelection(const ChewingContext * ctx)554 CHEWING_API int chewing_get_spaceAsSelection(const ChewingContext *ctx)
555 {
556 const ChewingData *pgdata;
557
558 if (!ctx) {
559 return -1;
560 }
561 pgdata = ctx->data;
562
563 LOG_API("bSpaceAsSelection = %d", ctx->data->config.bSpaceAsSelection);
564
565 return ctx->data->config.bSpaceAsSelection;
566 }
567
chewing_set_escCleanAllBuf(ChewingContext * ctx,int mode)568 CHEWING_API void chewing_set_escCleanAllBuf(ChewingContext *ctx, int mode)
569 {
570 ChewingData *pgdata;
571
572 if (!ctx) {
573 return;
574 }
575 pgdata = ctx->data;
576
577 LOG_API("mode = %d", mode);
578
579 if (mode == 0 || mode == 1)
580 ctx->data->config.bEscCleanAllBuf = mode;
581 }
582
chewing_get_escCleanAllBuf(const ChewingContext * ctx)583 CHEWING_API int chewing_get_escCleanAllBuf(const ChewingContext *ctx)
584 {
585 const ChewingData *pgdata;
586
587 if (!ctx) {
588 return -1;
589 }
590 pgdata = ctx->data;
591
592 LOG_API("bEscCleanAllBuf = %d", ctx->data->config.bEscCleanAllBuf);
593
594 return ctx->data->config.bEscCleanAllBuf;
595 }
596
chewing_set_autoShiftCur(ChewingContext * ctx,int mode)597 CHEWING_API void chewing_set_autoShiftCur(ChewingContext *ctx, int mode)
598 {
599 ChewingData *pgdata;
600
601 if (!ctx) {
602 return;
603 }
604 pgdata = ctx->data;
605
606 LOG_API("mode = %d", mode);
607
608 if (mode == 0 || mode == 1)
609 ctx->data->config.bAutoShiftCur = mode;
610 }
611
chewing_get_autoShiftCur(const ChewingContext * ctx)612 CHEWING_API int chewing_get_autoShiftCur(const ChewingContext *ctx)
613 {
614 const ChewingData *pgdata;
615
616 if (!ctx) {
617 return -1;
618 }
619 pgdata = ctx->data;
620
621 LOG_API("bAutoShiftCur = %d", ctx->data->config.bAutoShiftCur);
622
623 return ctx->data->config.bAutoShiftCur;
624 }
625
chewing_set_easySymbolInput(ChewingContext * ctx,int mode)626 CHEWING_API void chewing_set_easySymbolInput(ChewingContext *ctx, int mode)
627 {
628 ChewingData *pgdata;
629
630 if (!ctx) {
631 return;
632 }
633 pgdata = ctx->data;
634
635 LOG_API("mode = %d", mode);
636
637 if (mode == 0 || mode == 1)
638 ctx->data->config.bEasySymbolInput = mode;
639 }
640
chewing_get_easySymbolInput(const ChewingContext * ctx)641 CHEWING_API int chewing_get_easySymbolInput(const ChewingContext *ctx)
642 {
643 const ChewingData *pgdata;
644
645 if (!ctx) {
646 return -1;
647 }
648 pgdata = ctx->data;
649
650 LOG_API("bEasySymbolInput = %d", ctx->data->config.bEasySymbolInput);
651
652 return ctx->data->config.bEasySymbolInput;
653 }
654
chewing_set_phraseChoiceRearward(ChewingContext * ctx,int mode)655 CHEWING_API void chewing_set_phraseChoiceRearward(ChewingContext *ctx, int mode)
656 {
657 ChewingData *pgdata;
658
659 if (!ctx) {
660 return;
661 }
662 pgdata = ctx->data;
663
664 LOG_API("mode = %d", mode);
665
666 if (mode == 0 || mode == 1)
667 ctx->data->config.bPhraseChoiceRearward = mode;
668 }
669
chewing_get_phraseChoiceRearward(const ChewingContext * ctx)670 CHEWING_API int chewing_get_phraseChoiceRearward(const ChewingContext *ctx)
671 {
672 const ChewingData *pgdata;
673
674 if (!ctx) {
675 return -1;
676 }
677 pgdata = ctx->data;
678
679 LOG_API("bPhraseChoiceRearward = %d", ctx->data->config.bPhraseChoiceRearward);
680
681 return ctx->data->config.bPhraseChoiceRearward;
682 }
683
chewing_set_ChiEngMode(ChewingContext * ctx,int mode)684 CHEWING_API void chewing_set_ChiEngMode(ChewingContext *ctx, int mode)
685 {
686 ChewingData *pgdata;
687
688 if (!ctx) {
689 return;
690 }
691 pgdata = ctx->data;
692
693 LOG_API("mode = %d", mode);
694
695 if (mode == CHINESE_MODE || mode == SYMBOL_MODE) {
696 // remove all data inside buffer as switching mode.
697 BopomofoRemoveAll(&(ctx->data->bopomofoData));
698 MakeOutputWithRtn(ctx->output, ctx->data, KEYSTROKE_ABSORB);
699 ctx->data->bChiSym = mode;
700 }
701 }
702
chewing_get_ChiEngMode(const ChewingContext * ctx)703 CHEWING_API int chewing_get_ChiEngMode(const ChewingContext *ctx)
704 {
705 const ChewingData *pgdata;
706
707 if (!ctx) {
708 return -1;
709 }
710 pgdata = ctx->data;
711
712 LOG_API("bChiSym = %d", ctx->data->bChiSym);
713
714 return ctx->data->bChiSym;
715 }
716
chewing_set_ShapeMode(ChewingContext * ctx,int mode)717 CHEWING_API void chewing_set_ShapeMode(ChewingContext *ctx, int mode)
718 {
719 ChewingData *pgdata;
720
721 if (!ctx) {
722 return;
723 }
724 pgdata = ctx->data;
725
726 LOG_API("mode = %d", mode);
727
728 if (mode == HALFSHAPE_MODE || mode == FULLSHAPE_MODE)
729 ctx->data->bFullShape = mode;
730 }
731
chewing_get_ShapeMode(const ChewingContext * ctx)732 CHEWING_API int chewing_get_ShapeMode(const ChewingContext *ctx)
733 {
734 const ChewingData *pgdata;
735
736 if (!ctx) {
737 return -1;
738 }
739 pgdata = ctx->data;
740
741 LOG_API("ctx->data->bFullShape = %d", ctx->data->bFullShape);
742
743 return ctx->data->bFullShape;
744 }
745
CheckAndResetRange(ChewingData * pgdata)746 static void CheckAndResetRange(ChewingData *pgdata)
747 {
748 if (pgdata->PointStart > -1) {
749 pgdata->PointStart = -1;
750 pgdata->PointEnd = 0;
751 }
752 }
753
SelectCandidate(ChewingData * pgdata,int num)754 static int SelectCandidate(ChewingData *pgdata, int num)
755 {
756 assert(pgdata);
757 assert(pgdata->choiceInfo.pageNo >= 0);
758
759 if (0 <= num && num < pgdata->choiceInfo.nTotalChoice) {
760 if (pgdata->choiceInfo.isSymbol != WORD_CHOICE) {
761 SymbolChoice(pgdata, num);
762 } else {
763 /* change the select interval & selectStr & nSelect */
764 AddSelect(pgdata, num);
765 /* second, call choice module */
766 ChoiceSelect(pgdata, num);
767 /* automatically shift the cursor to next phrase */
768 if (pgdata->config.bAutoShiftCur != 0 &&
769 /* if cursor at end of string, do not shift the cursor. */
770 pgdata->chiSymbolCursor < pgdata->chiSymbolBufLen) {
771 if (pgdata->config.bPhraseChoiceRearward) {
772 ++pgdata->chiSymbolCursor;
773 } else {
774 pgdata->chiSymbolCursor += pgdata->availInfo.avail[pgdata->availInfo.currentAvail].len;
775 }
776 }
777 }
778 return 0;
779 }
780
781 return -1;
782 }
783
DoSelect(ChewingData * pgdata,int num)784 static void DoSelect(ChewingData *pgdata, int num)
785 {
786 assert(pgdata->choiceInfo.pageNo >= 0);
787 if (num >= 0 && num < pgdata->choiceInfo.nChoicePerPage) {
788 num += pgdata->choiceInfo.pageNo * pgdata->choiceInfo.nChoicePerPage;
789 SelectCandidate(pgdata, num);
790 }
791 }
792
chewing_handle_Space(ChewingContext * ctx)793 CHEWING_API int chewing_handle_Space(ChewingContext *ctx)
794 {
795 ChewingData *pgdata;
796
797 if (!ctx) {
798 return -1;
799 }
800 pgdata = ctx->data;
801
802 LOG_API("");
803
804 /*
805 * Use chewing_handle_Default( ctx, ' ' ) to handle space when:
806 * - "space as selection" mode is disable
807 * - mode is not CHINESE_MODE
808 * - has incompleted bopomofo (space is needed to complete it)
809 */
810 if (!pgdata->config.bSpaceAsSelection || pgdata->bChiSym != CHINESE_MODE || BopomofoIsEntering(&ctx->data->bopomofoData)) {
811 return chewing_handle_Default(ctx, ' ');
812 }
813
814 CheckAndResetRange(pgdata);
815
816 /*
817 * space = right when the follogin conditions are true
818 * 1. In select mode
819 * 2. The candidate page is not last page
820 *
821 * Otherwise, space = down
822 */
823 if (pgdata->bSelect && ctx->output->pci->pageNo < ctx->output->pci->nPage - 1) {
824 return chewing_handle_Right(ctx);
825 } else {
826 return chewing_handle_Down(ctx);
827 }
828 return 0;
829 }
830
chewing_handle_Esc(ChewingContext * ctx)831 CHEWING_API int chewing_handle_Esc(ChewingContext *ctx)
832 {
833 ChewingData *pgdata;
834 ChewingOutput *pgo;
835 int keystrokeRtn = KEYSTROKE_ABSORB;
836
837 if (!ctx) {
838 return -1;
839 }
840 pgdata = ctx->data;
841 pgo = ctx->output;
842
843 LOG_API("");
844
845 CheckAndResetRange(pgdata);
846
847 if (!ChewingIsEntering(pgdata)) {
848 keystrokeRtn = KEYSTROKE_IGNORE;
849 } else if (pgdata->bSelect) {
850 ChoiceEndChoice(pgdata);
851 } else if (BopomofoIsEntering(&(pgdata->bopomofoData))) {
852 BopomofoRemoveAll(&(pgdata->bopomofoData));
853 } else if (pgdata->config.bEscCleanAllBuf) {
854 CleanAllBuf(pgdata);
855 pgo->commitBufLen = pgdata->chiSymbolBufLen;
856 }
857
858 MakeOutputWithRtn(pgo, pgdata, keystrokeRtn);
859 return 0;
860 }
861
chewing_handle_Enter(ChewingContext * ctx)862 CHEWING_API int chewing_handle_Enter(ChewingContext *ctx)
863 {
864 ChewingData *pgdata;
865 ChewingOutput *pgo;
866 int nCommitStr;
867 int keystrokeRtn = KEYSTROKE_ABSORB;
868
869 if (!ctx) {
870 return -1;
871 }
872 pgdata = ctx->data;
873 pgo = ctx->output;
874
875 LOG_API("");
876
877 nCommitStr = pgdata->chiSymbolBufLen;
878
879 if (!ChewingIsEntering(pgdata)) {
880 keystrokeRtn = KEYSTROKE_IGNORE;
881 } else if (pgdata->bSelect) {
882 keystrokeRtn = KEYSTROKE_ABSORB | KEYSTROKE_BELL;
883 } else if (pgdata->PointStart > -1) {
884 int buf = pgdata->chiSymbolCursor;
885 int key;
886
887 if (pgdata->PointEnd > 1) {
888 if (!pgdata->config.bAddPhraseForward) {
889 pgdata->chiSymbolCursor = pgdata->PointStart;
890 key = '0' + pgdata->PointEnd;
891 } else {
892 pgdata->chiSymbolCursor = pgdata->PointStart + pgdata->PointEnd;
893 key = '0' + pgdata->PointEnd;
894 }
895
896 chewing_handle_CtrlNum(ctx, key);
897 pgdata->chiSymbolCursor = buf;
898 } else if (pgdata->PointEnd < 1) {
899 if (pgdata->config.bAddPhraseForward)
900 pgdata->chiSymbolCursor = buf - pgdata->PointEnd;
901 key = '0' - pgdata->PointEnd;
902 chewing_handle_CtrlNum(ctx, key);
903 pgdata->chiSymbolCursor = buf;
904 }
905 pgdata->PointStart = -1;
906 pgdata->PointEnd = 0;
907 } else {
908 keystrokeRtn = KEYSTROKE_COMMIT;
909 WriteChiSymbolToCommitBuf(pgdata, pgo, nCommitStr);
910 AutoLearnPhrase(pgdata);
911 CleanAllBuf(pgdata);
912 pgo->commitBufLen = nCommitStr;
913 }
914
915 MakeOutputWithRtn(pgo, pgdata, keystrokeRtn);
916 return 0;
917 }
918
chewing_handle_Del(ChewingContext * ctx)919 CHEWING_API int chewing_handle_Del(ChewingContext *ctx)
920 {
921 ChewingData *pgdata;
922 ChewingOutput *pgo;
923 int keystrokeRtn = KEYSTROKE_ABSORB;
924
925 if (!ctx) {
926 return -1;
927 }
928 pgdata = ctx->data;
929 pgo = ctx->output;
930
931 LOG_API("");
932
933 CheckAndResetRange(pgdata);
934
935 if (!ChewingIsEntering(pgdata)) {
936 keystrokeRtn = KEYSTROKE_IGNORE;
937 }
938
939 if (!pgdata->bSelect) {
940 if (!BopomofoIsEntering(&(pgdata->bopomofoData)) && pgdata->chiSymbolCursor < pgdata->chiSymbolBufLen) {
941 ChewingKillChar(pgdata, pgdata->chiSymbolCursor, NONDECREASE_CURSOR);
942 }
943 CallPhrasing(pgdata, 0);
944 }
945 MakeOutputWithRtn(pgo, pgdata, keystrokeRtn);
946 return 0;
947 }
948
chewing_handle_Backspace(ChewingContext * ctx)949 CHEWING_API int chewing_handle_Backspace(ChewingContext *ctx)
950 {
951 ChewingData *pgdata;
952 ChewingOutput *pgo;
953 int keystrokeRtn = KEYSTROKE_ABSORB;
954
955 if (!ctx) {
956 return -1;
957 }
958 pgdata = ctx->data;
959 pgo = ctx->output;
960
961 LOG_API("");
962
963 CheckAndResetRange(pgdata);
964
965 if (!ChewingIsEntering(pgdata)) {
966 keystrokeRtn = KEYSTROKE_IGNORE;
967 }
968
969 if (!pgdata->bSelect) {
970 if (BopomofoIsEntering(&(pgdata->bopomofoData))) {
971 BopomofoRemoveLast(&(pgdata->bopomofoData));
972 } else if (pgdata->chiSymbolCursor > 0) {
973 ChewingKillChar(pgdata, pgdata->chiSymbolCursor - 1, DECREASE_CURSOR);
974 }
975 CallPhrasing(pgdata, 0);
976 } else if (pgdata->bSelect) {
977 chewing_cand_close(ctx);
978 }
979
980 MakeOutputWithRtn(pgo, pgdata, keystrokeRtn);
981 return 0;
982 }
983
chewing_handle_Up(ChewingContext * ctx)984 CHEWING_API int chewing_handle_Up(ChewingContext *ctx)
985 {
986 ChewingData *pgdata;
987 ChewingOutput *pgo;
988 int keystrokeRtn = KEYSTROKE_ABSORB;
989 int key_buf_cursor;
990
991 if (!ctx) {
992 return -1;
993 }
994 pgdata = ctx->data;
995 pgo = ctx->output;
996
997 LOG_API("");
998
999 CheckAndResetRange(pgdata);
1000
1001 if (!ChewingIsEntering(pgdata)) {
1002 keystrokeRtn = KEYSTROKE_IGNORE;
1003 }
1004
1005 key_buf_cursor = pgdata->chiSymbolCursor;
1006 // FIXME: when pgdata->chiSymbolBufLen == 0, key_buf_cursor will be -1.
1007 if (pgdata->chiSymbolCursor == pgdata->chiSymbolBufLen)
1008 key_buf_cursor--;
1009
1010 /* close candidate list, compared to Down key to open candidate list. */
1011 if (pgdata->bSelect) {
1012 ChoiceEndChoice(pgdata);
1013 }
1014
1015 MakeOutputWithRtn(pgo, pgdata, keystrokeRtn);
1016 return 0;
1017 }
1018
chewing_handle_Down(ChewingContext * ctx)1019 CHEWING_API int chewing_handle_Down(ChewingContext *ctx)
1020 {
1021 ChewingData *pgdata;
1022 ChewingOutput *pgo;
1023 int toSelect = 0;
1024 int keystrokeRtn = KEYSTROKE_ABSORB;
1025 int key_buf_cursor;
1026
1027 if (!ctx) {
1028 return -1;
1029 }
1030 pgdata = ctx->data;
1031 pgo = ctx->output;
1032
1033 LOG_API("");
1034
1035 CheckAndResetRange(pgdata);
1036
1037 if (!ChewingIsEntering(pgdata)) {
1038 keystrokeRtn = KEYSTROKE_IGNORE;
1039 }
1040
1041 key_buf_cursor = pgdata->chiSymbolCursor;
1042 if (pgdata->chiSymbolCursor == pgdata->chiSymbolBufLen && key_buf_cursor > 0)
1043 key_buf_cursor--;
1044
1045 /* see if to select */
1046 if (ChewingIsChiAt(key_buf_cursor, pgdata))
1047 toSelect = 1;
1048
1049 chooseCandidate(ctx, toSelect, key_buf_cursor);
1050
1051 MakeOutputWithRtn(pgo, pgdata, keystrokeRtn);
1052 return 0;
1053 }
1054
1055 /* Add phrase in Hanin Style */
chewing_handle_ShiftLeft(ChewingContext * ctx)1056 CHEWING_API int chewing_handle_ShiftLeft(ChewingContext *ctx)
1057 {
1058 ChewingData *pgdata;
1059 ChewingOutput *pgo;
1060 int keystrokeRtn = KEYSTROKE_ABSORB;
1061
1062 if (!ctx) {
1063 return -1;
1064 }
1065 pgdata = ctx->data;
1066 pgo = ctx->output;
1067
1068 LOG_API("");
1069
1070 if (!ChewingIsEntering(pgdata)) {
1071 keystrokeRtn = KEYSTROKE_IGNORE;
1072 }
1073 if (!pgdata->bSelect) {
1074 /* PointEnd locates (-9, +9) */
1075 if (!BopomofoIsEntering(&(pgdata->bopomofoData)) && pgdata->chiSymbolCursor > 0 && pgdata->PointEnd > -9) {
1076 if (pgdata->PointStart == -1)
1077 pgdata->PointStart = pgdata->chiSymbolCursor;
1078 pgdata->chiSymbolCursor--;
1079 if (ChewingIsChiAt(pgdata->chiSymbolCursor, pgdata)) {
1080 pgdata->PointEnd--;
1081 }
1082 if (pgdata->PointEnd == 0)
1083 pgdata->PointStart = -1;
1084 }
1085 }
1086
1087 MakeOutputWithRtn(pgo, pgdata, keystrokeRtn);
1088 return 0;
1089 }
1090
chewing_handle_Left(ChewingContext * ctx)1091 CHEWING_API int chewing_handle_Left(ChewingContext *ctx)
1092 {
1093 ChewingData *pgdata;
1094 ChewingOutput *pgo;
1095 int keystrokeRtn = KEYSTROKE_ABSORB;
1096
1097 if (!ctx) {
1098 return -1;
1099 }
1100 pgdata = ctx->data;
1101 pgo = ctx->output;
1102
1103 LOG_API("");
1104
1105 if (!ChewingIsEntering(pgdata)) {
1106 keystrokeRtn = KEYSTROKE_IGNORE;
1107 }
1108
1109 if (pgdata->bSelect) {
1110 assert(pgdata->choiceInfo.nPage > 0);
1111 if (pgdata->choiceInfo.pageNo > 0)
1112 pgdata->choiceInfo.pageNo--;
1113 else
1114 pgdata->choiceInfo.pageNo = pgdata->choiceInfo.nPage - 1;
1115 } else {
1116 if (!BopomofoIsEntering(&(pgdata->bopomofoData)) && pgdata->chiSymbolCursor > 0) {
1117 CheckAndResetRange(pgdata);
1118 pgdata->chiSymbolCursor--;
1119 }
1120 }
1121 MakeOutputWithRtn(pgo, pgdata, keystrokeRtn);
1122 return 0;
1123 }
1124
1125 /* Add phrase in Hanin Style */
chewing_handle_ShiftRight(ChewingContext * ctx)1126 CHEWING_API int chewing_handle_ShiftRight(ChewingContext *ctx)
1127 {
1128 ChewingData *pgdata;
1129 ChewingOutput *pgo;
1130 int keystrokeRtn = KEYSTROKE_ABSORB;
1131
1132 if (!ctx) {
1133 return -1;
1134 }
1135 pgdata = ctx->data;
1136 pgo = ctx->output;
1137
1138 LOG_API("");
1139
1140 if (!ChewingIsEntering(pgdata)) {
1141 keystrokeRtn = KEYSTROKE_IGNORE;
1142 }
1143
1144 if (!pgdata->bSelect) {
1145 /* PointEnd locates (-9, +9) */
1146 if (!BopomofoIsEntering(&(pgdata->bopomofoData)) &&
1147 pgdata->chiSymbolCursor < pgdata->chiSymbolBufLen && pgdata->PointEnd < 9) {
1148 if (pgdata->PointStart == -1)
1149 pgdata->PointStart = pgdata->chiSymbolCursor;
1150 if (ChewingIsChiAt(pgdata->chiSymbolCursor, pgdata)) {
1151 pgdata->PointEnd++;
1152 }
1153 pgdata->chiSymbolCursor++;
1154 if (pgdata->PointEnd == 0)
1155 pgdata->PointStart = -1;
1156 }
1157 }
1158
1159 MakeOutputWithRtn(pgo, pgdata, keystrokeRtn);
1160 return 0;
1161 }
1162
chewing_handle_Right(ChewingContext * ctx)1163 CHEWING_API int chewing_handle_Right(ChewingContext *ctx)
1164 {
1165 ChewingData *pgdata;
1166 ChewingOutput *pgo;
1167 int keystrokeRtn = KEYSTROKE_ABSORB;
1168
1169 if (!ctx) {
1170 return -1;
1171 }
1172 pgdata = ctx->data;
1173 pgo = ctx->output;
1174
1175 LOG_API("");
1176
1177 if (!ChewingIsEntering(pgdata)) {
1178 keystrokeRtn = KEYSTROKE_IGNORE;
1179 }
1180
1181 if (pgdata->bSelect) {
1182 if (pgdata->choiceInfo.pageNo < pgdata->choiceInfo.nPage - 1)
1183 pgdata->choiceInfo.pageNo++;
1184 else
1185 pgdata->choiceInfo.pageNo = 0;
1186 } else {
1187 if (!BopomofoIsEntering(&(pgdata->bopomofoData)) && pgdata->chiSymbolCursor < pgdata->chiSymbolBufLen) {
1188 CheckAndResetRange(pgdata);
1189 pgdata->chiSymbolCursor++;
1190 }
1191 }
1192
1193 MakeOutputWithRtn(pgo, pgdata, keystrokeRtn);
1194 return 0;
1195 }
1196
chewing_handle_Tab(ChewingContext * ctx)1197 CHEWING_API int chewing_handle_Tab(ChewingContext *ctx)
1198 {
1199 ChewingData *pgdata;
1200 ChewingOutput *pgo;
1201 int keystrokeRtn = KEYSTROKE_ABSORB;
1202 int all_phrasing = 0;
1203 int cursor;
1204
1205 if (!ctx) {
1206 return -1;
1207 }
1208 pgdata = ctx->data;
1209 pgo = ctx->output;
1210
1211 LOG_API("");
1212
1213 CheckAndResetRange(pgdata);
1214
1215 if (!ChewingIsEntering(pgdata)) {
1216 keystrokeRtn = KEYSTROKE_IGNORE;
1217 }
1218
1219
1220 if (!pgdata->bSelect) {
1221 if (pgdata->chiSymbolCursor == pgdata->chiSymbolBufLen) {
1222 pgdata->phrOut.nNumCut++;
1223 all_phrasing = 1;
1224 } else if (ChewingIsChiAt(pgdata->chiSymbolCursor - 1, pgdata)) {
1225 cursor = PhoneSeqCursor(pgdata);
1226 if (IsPreferIntervalConnted(cursor, pgdata)) {
1227 pgdata->bUserArrBrkpt[cursor] = 1;
1228 pgdata->bUserArrCnnct[cursor] = 0;
1229 } else {
1230 pgdata->bUserArrBrkpt[cursor] = 0;
1231 pgdata->bUserArrCnnct[cursor] = 1;
1232 }
1233 }
1234 CallPhrasing(pgdata, all_phrasing);
1235 }
1236 MakeOutputWithRtn(pgo, pgdata, keystrokeRtn);
1237 return 0;
1238 }
1239
chewing_handle_DblTab(ChewingContext * ctx)1240 CHEWING_API int chewing_handle_DblTab(ChewingContext *ctx)
1241 {
1242 ChewingData *pgdata;
1243 ChewingOutput *pgo;
1244 int keystrokeRtn = KEYSTROKE_ABSORB;
1245 int cursor;
1246
1247 if (!ctx) {
1248 return -1;
1249 }
1250 pgdata = ctx->data;
1251 pgo = ctx->output;
1252
1253 LOG_API("");
1254
1255 CheckAndResetRange(pgdata);
1256
1257 if (!ChewingIsEntering(pgdata)) {
1258 keystrokeRtn = KEYSTROKE_IGNORE;
1259 }
1260
1261 if (!pgdata->bSelect) {
1262 cursor = PhoneSeqCursor(pgdata);
1263 pgdata->bUserArrBrkpt[cursor] = 0;
1264 pgdata->bUserArrCnnct[cursor] = 0;
1265 }
1266 CallPhrasing(pgdata, 0);
1267
1268 MakeOutputWithRtn(pgo, pgdata, keystrokeRtn);
1269 return 0;
1270 }
1271
1272
chewing_handle_Capslock(ChewingContext * ctx)1273 CHEWING_API int chewing_handle_Capslock(ChewingContext *ctx)
1274 {
1275 ChewingData *pgdata;
1276 ChewingOutput *pgo;
1277
1278 if (!ctx) {
1279 return -1;
1280 }
1281 pgdata = ctx->data;
1282 pgo = ctx->output;
1283
1284 LOG_API("");
1285
1286 chewing_set_ChiEngMode(ctx, 1 - chewing_get_ChiEngMode(ctx));
1287 MakeOutputWithRtn(pgo, pgdata, KEYSTROKE_ABSORB);
1288 return 0;
1289 }
1290
chewing_handle_Home(ChewingContext * ctx)1291 CHEWING_API int chewing_handle_Home(ChewingContext *ctx)
1292 {
1293 ChewingData *pgdata;
1294 ChewingOutput *pgo;
1295 int keystrokeRtn = KEYSTROKE_ABSORB;
1296
1297 if (!ctx) {
1298 return -1;
1299 }
1300 pgdata = ctx->data;
1301 pgo = ctx->output;
1302
1303 LOG_API("");
1304
1305 CheckAndResetRange(pgdata);
1306
1307 if (!ChewingIsEntering(pgdata)) {
1308 keystrokeRtn = KEYSTROKE_IGNORE;
1309 } else if (!pgdata->bSelect) {
1310 pgdata->chiSymbolCursor = 0;
1311 }
1312 MakeOutputWithRtn(pgo, pgdata, keystrokeRtn);
1313 return 0;
1314 }
1315
chewing_handle_End(ChewingContext * ctx)1316 CHEWING_API int chewing_handle_End(ChewingContext *ctx)
1317 {
1318 ChewingData *pgdata;
1319 ChewingOutput *pgo;
1320 int keystrokeRtn = KEYSTROKE_ABSORB;
1321
1322 if (!ctx) {
1323 return -1;
1324 }
1325 pgdata = ctx->data;
1326 pgo = ctx->output;
1327
1328 LOG_API("");
1329
1330 CheckAndResetRange(pgdata);
1331
1332 if (!ChewingIsEntering(pgdata)) {
1333 keystrokeRtn = KEYSTROKE_IGNORE;
1334 } else if (!pgdata->bSelect) {
1335 pgdata->chiSymbolCursor = pgdata->chiSymbolBufLen;
1336 }
1337 MakeOutputWithRtn(pgo, pgdata, keystrokeRtn);
1338 return 0;
1339 }
1340
chewing_handle_PageUp(ChewingContext * ctx)1341 CHEWING_API int chewing_handle_PageUp(ChewingContext *ctx)
1342 {
1343 ChewingData *pgdata;
1344 ChewingOutput *pgo;
1345 int keystrokeRtn = KEYSTROKE_ABSORB;
1346
1347 if (!ctx) {
1348 return -1;
1349 }
1350 pgdata = ctx->data;
1351 pgo = ctx->output;
1352
1353 LOG_API("");
1354
1355 CheckAndResetRange(pgdata);
1356
1357 if (!ChewingIsEntering(pgdata)) {
1358 keystrokeRtn = KEYSTROKE_IGNORE;
1359 } else if (!pgdata->bSelect) {
1360 pgdata->chiSymbolCursor = pgdata->chiSymbolBufLen;
1361 } else if (pgdata->bSelect) {
1362 assert(pgdata->choiceInfo.nPage > 0);
1363 if (pgdata->choiceInfo.pageNo > 0)
1364 pgdata->choiceInfo.pageNo--;
1365 else
1366 pgdata->choiceInfo.pageNo = pgdata->choiceInfo.nPage - 1;
1367 }
1368 MakeOutputWithRtn(pgo, pgdata, keystrokeRtn);
1369 return 0;
1370 }
1371
chewing_handle_PageDown(ChewingContext * ctx)1372 CHEWING_API int chewing_handle_PageDown(ChewingContext *ctx)
1373 {
1374 ChewingData *pgdata;
1375 ChewingOutput *pgo;
1376 int keystrokeRtn = KEYSTROKE_ABSORB;
1377
1378 if (!ctx) {
1379 return -1;
1380 }
1381 pgdata = ctx->data;
1382 pgo = ctx->output;
1383
1384 LOG_API("");
1385
1386 CheckAndResetRange(pgdata);
1387
1388 if (!ChewingIsEntering(pgdata)) {
1389 keystrokeRtn = KEYSTROKE_IGNORE;
1390 } else if (!pgdata->bSelect) {
1391 pgdata->chiSymbolCursor = pgdata->chiSymbolBufLen;
1392 } else if (pgdata->bSelect) {
1393 if (pgdata->choiceInfo.pageNo < pgdata->choiceInfo.nPage - 1)
1394 pgdata->choiceInfo.pageNo++;
1395 else
1396 pgdata->choiceInfo.pageNo = 0;
1397 }
1398 MakeOutputWithRtn(pgo, pgdata, keystrokeRtn);
1399 return 0;
1400 }
1401
1402 /* Dvorak <-> Qwerty keyboard layout converter */
dvorak_convert(int key)1403 static int dvorak_convert(int key)
1404 {
1405 const char dkey[] = {
1406 '\'', '\"', ',', '<', '.', '>', 'p', 'P', 'y', 'Y', 'f', 'F', 'g', 'G',
1407 'c', 'C', 'r', 'R', 'l', 'L', '/', '?', '=', '+', '\\', '|',
1408 'a', 'A', 'o', 'O', 'e', 'E', 'u', 'U', 'i', 'I', 'd', 'D', 'h', 'H',
1409 't', 'T', 'n', 'N', 's', 'S', '-', '_',
1410 ';', ':', 'q', 'Q', 'j', 'J', 'k', 'K', 'x', 'X', 'b', 'B', 'm', 'M',
1411 'w', 'W', 'v', 'V', 'z', 'Z'
1412 };
1413 const char qkey[] = {
1414 'q', 'Q', 'w', 'W', 'e', 'E', 'r', 'R', 't', 'T', 'y', 'Y', 'u', 'U',
1415 'i', 'I', 'o', 'O', 'p', 'P', '[', '{', ']', '}', '\\', '|',
1416 'a', 'A', 's', 'S', 'd', 'D', 'f', 'F', 'g', 'G', 'h', 'H', 'j', 'J',
1417 'k', 'K', 'l', 'L', ';', ':', '\'', '\"',
1418 'z', 'Z', 'x', 'X', 'c', 'C', 'v', 'V', 'b', 'B', 'n', 'N', 'm', 'M',
1419 ',', '<', '.', '>', '/', '?'
1420 };
1421 size_t i;
1422
1423 STATIC_ASSERT(ARRAY_SIZE(dkey) == ARRAY_SIZE(qkey));
1424
1425 for (i = 0; i < ARRAY_SIZE(dkey); i++) {
1426 if (key == qkey[i]) {
1427 key = dkey[i];
1428 return key;
1429 }
1430 }
1431 return key;
1432 }
1433
chewing_handle_Default(ChewingContext * ctx,int key)1434 CHEWING_API int chewing_handle_Default(ChewingContext *ctx, int key)
1435 {
1436 ChewingData *pgdata;
1437 ChewingOutput *pgo;
1438 int keystrokeRtn = KEYSTROKE_ABSORB;
1439 int rtn;
1440 int num;
1441 int bQuickCommit = 0;
1442
1443 if (!ctx) {
1444 return -1;
1445 }
1446 pgdata = ctx->data;
1447 pgo = ctx->output;
1448
1449 LOG_API("key = %d", key);
1450
1451 /* Update lifetime */
1452 IncreaseLifeTime(ctx->data);
1453
1454 /* Skip the special key */
1455 if (key & 0xFF00) {
1456 keystrokeRtn = KEYSTROKE_IGNORE;
1457 goto End_KeyDefault;
1458 }
1459
1460 /* We ignore non-printable input */
1461 if (!isprint(key))
1462 goto End_KeyDefault;
1463
1464 CheckAndResetRange(pgdata);
1465
1466 DEBUG_CHECKPOINT();
1467 DEBUG_OUT(" key=%d", key);
1468
1469 /* Dvorak Hsu */
1470 if (pgdata->bopomofoData.kbtype == KB_DVORAK_HSU) {
1471 key = dvorak_convert(key);
1472 }
1473
1474 /* selecting */
1475 if (pgdata->bSelect) {
1476 if (key == ' ')
1477 return chewing_handle_Right(ctx);
1478 /* num starts from 0 */
1479 num = CountSelKeyNum(key, pgdata);
1480 if (num >= 0) {
1481 DoSelect(pgdata, num);
1482 goto End_keyproc;
1483 }
1484
1485 /* Otherwise, use 'j' and 'k' for paging in selection mode */
1486 DEBUG_OUT("\t\tchecking paging key, got '%c'\n", key);
1487 switch (key) {
1488 case 'j':
1489 case 'J':
1490 if (pgdata->chiSymbolCursor > 0) {
1491 if (!ChewingIsEntering(pgdata)) {
1492 keystrokeRtn = KEYSTROKE_IGNORE;
1493 }
1494 CheckAndResetRange(pgdata);
1495 pgdata->chiSymbolCursor--;
1496 if (ChewingIsChiAt(pgdata->chiSymbolCursor, pgdata))
1497 ChoiceInitAvail(pgdata);
1498 else
1499 OpenSymbolChoice(pgdata);
1500
1501 }
1502 goto End_Paging;
1503 case 'k':
1504 case 'K':
1505 if (pgdata->chiSymbolCursor < pgdata->chiSymbolBufLen) {
1506 if (!ChewingIsEntering(pgdata)) {
1507 keystrokeRtn = KEYSTROKE_IGNORE;
1508 }
1509 CheckAndResetRange(pgdata);
1510 pgdata->chiSymbolCursor++;
1511 if (ChewingIsChiAt(pgdata->chiSymbolCursor, pgdata))
1512 ChoiceInitAvail(pgdata);
1513 else
1514 OpenSymbolChoice(pgdata);
1515 }
1516 goto End_Paging;
1517 default:
1518 break;
1519 }
1520 }
1521 /* editing */
1522 else {
1523 if (pgdata->bChiSym == CHINESE_MODE) {
1524 if (pgdata->config.bEasySymbolInput != 0) {
1525 EasySymbolInput(key, pgdata);
1526 goto End_keyproc;
1527 }
1528
1529 rtn = BopomofoPhoInput(pgdata, key);
1530 DEBUG_OUT("\t\tChinese mode key, " "BopomofoPhoInput return value = %d\n", rtn);
1531
1532 if (rtn == BOPOMOFO_KEY_ERROR)
1533 rtn = SpecialSymbolInput(key, pgdata);
1534 switch (rtn) {
1535 case BOPOMOFO_ABSORB:
1536 keystrokeRtn = KEYSTROKE_ABSORB;
1537 break;
1538 case BOPOMOFO_COMMIT:
1539 AddChi(pgdata->bopomofoData.phone, pgdata->bopomofoData.phoneAlt, pgdata);
1540 break;
1541 case BOPOMOFO_NO_WORD:
1542 keystrokeRtn = KEYSTROKE_BELL | KEYSTROKE_ABSORB;
1543 break;
1544 case BOPOMOFO_KEY_ERROR:
1545 case BOPOMOFO_IGNORE:
1546 DEBUG_OUT("\t\tbefore isupper(key),key=%d\n", key);
1547 /* change upper case into lower case */
1548 if (isupper(key))
1549 key = tolower(key);
1550
1551 DEBUG_OUT("\t\tafter isupper(key),key=%d\n", key);
1552
1553 /* see if buffer contains nothing */
1554 if (pgdata->chiSymbolBufLen == 0) {
1555 bQuickCommit = 1;
1556 }
1557
1558 if (pgdata->config.bEasySymbolInput == 0) {
1559 if (pgdata->bFullShape)
1560 rtn = FullShapeSymbolInput(key, pgdata);
1561 else
1562 rtn = SymbolInput(key, pgdata);
1563 }
1564
1565 if (rtn == SYMBOL_KEY_ERROR) {
1566 keystrokeRtn = KEYSTROKE_IGNORE;
1567 /*
1568 * If the key is not a printable symbol,
1569 * then it's wrong to commit it.
1570 */
1571 bQuickCommit = 0;
1572 } else
1573 keystrokeRtn = KEYSTROKE_ABSORB;
1574
1575 break;
1576 default:
1577 goto End_KeyDefault;
1578 }
1579 }
1580 /* English mode */
1581 else {
1582 /* see if buffer contains nothing */
1583 if (pgdata->chiSymbolBufLen == 0) {
1584 bQuickCommit = 1;
1585 }
1586 if (pgdata->bFullShape) {
1587 rtn = FullShapeSymbolInput(key, pgdata);
1588 } else {
1589 rtn = SymbolInput(key, pgdata);
1590 }
1591
1592 if (rtn == SYMBOL_KEY_ERROR) {
1593 keystrokeRtn = KEYSTROKE_IGNORE;
1594 bQuickCommit = 0;
1595 }
1596 }
1597 }
1598
1599 End_keyproc:
1600 if (!bQuickCommit) {
1601 CallPhrasing(pgdata, 0);
1602 if (ReleaseChiSymbolBuf(pgdata, pgo) != 0)
1603 keystrokeRtn = KEYSTROKE_COMMIT;
1604 }
1605 /* Quick commit */
1606 else {
1607 DEBUG_OUT("\t\tQuick commit buf[0]=%c\n", pgdata->preeditBuf[0].char_);
1608 WriteChiSymbolToCommitBuf(pgdata, pgo, 1);
1609 pgdata->chiSymbolBufLen = 0;
1610 pgdata->chiSymbolCursor = 0;
1611 keystrokeRtn = KEYSTROKE_COMMIT;
1612 }
1613
1614 if (pgdata->phrOut.nNumCut > 0) {
1615 int i;
1616
1617 for (i = 0; i < pgdata->phrOut.nDispInterval; i++) {
1618 pgdata->bUserArrBrkpt[pgdata->phrOut.dispInterval[i].from] = 1;
1619 pgdata->bUserArrBrkpt[pgdata->phrOut.dispInterval[i].to] = 1;
1620 }
1621 pgdata->phrOut.nNumCut = 0;
1622 }
1623
1624 End_KeyDefault:
1625 CallPhrasing(pgdata, 0);
1626 End_Paging:
1627 MakeOutputWithRtn(pgo, pgdata, keystrokeRtn);
1628 return 0;
1629 }
1630
chewing_handle_CtrlNum(ChewingContext * ctx,int key)1631 CHEWING_API int chewing_handle_CtrlNum(ChewingContext *ctx, int key)
1632 {
1633 ChewingData *pgdata;
1634 ChewingOutput *pgo;
1635 int keystrokeRtn = KEYSTROKE_ABSORB;
1636 int newPhraseLen;
1637 int i;
1638 uint16_t addPhoneSeq[MAX_PHONE_SEQ_LEN];
1639 char addWordSeq[MAX_PHONE_SEQ_LEN * MAX_UTF8_SIZE + 1];
1640 int phraseState;
1641 int cursor;
1642
1643 if (!ctx) {
1644 return -1;
1645 }
1646 pgdata = ctx->data;
1647 pgo = ctx->output;
1648
1649 LOG_API("");
1650
1651 CheckAndResetRange(pgdata);
1652
1653 if (pgdata->bSelect)
1654 return 0;
1655
1656 CallPhrasing(pgdata, 0);
1657 newPhraseLen = key - '0';
1658
1659 if (key == '0' || key == '1') {
1660 pgdata->bSelect = 1;
1661 pgdata->choiceInfo.oldChiSymbolCursor = pgdata->chiSymbolCursor;
1662
1663 HaninSymbolInput(pgdata);
1664 CallPhrasing(pgdata, 0);
1665 MakeOutputWithRtn(pgo, pgdata, keystrokeRtn);
1666 return 0;
1667 }
1668
1669 cursor = PhoneSeqCursor(pgdata);
1670 if (!pgdata->config.bAddPhraseForward) {
1671 if (newPhraseLen >= 1 && cursor + newPhraseLen - 1 <= pgdata->nPhoneSeq) {
1672 if (NoSymbolBetween(pgdata, cursor, cursor + newPhraseLen)) {
1673 /* Manually add phrase to the user phrase database. */
1674 memcpy(addPhoneSeq, &pgdata->phoneSeq[cursor], sizeof(uint16_t) * newPhraseLen);
1675 addPhoneSeq[newPhraseLen] = 0;
1676
1677 copyStringFromPreeditBuf(pgdata, cursor, newPhraseLen, addWordSeq, sizeof(addWordSeq));
1678
1679 phraseState = UserUpdatePhrase(pgdata, addPhoneSeq, addWordSeq);
1680 SetUpdatePhraseMsg(pgdata, addWordSeq, newPhraseLen, phraseState);
1681
1682 /* Clear the breakpoint between the New Phrase */
1683 for (i = 1; i < newPhraseLen; i++)
1684 pgdata->bUserArrBrkpt[cursor + i] = 0;
1685 }
1686 }
1687 } else {
1688 if (newPhraseLen >= 1 && cursor - newPhraseLen >= 0) {
1689 if (NoSymbolBetween(pgdata, cursor - newPhraseLen, cursor)) {
1690 /* Manually add phrase to the user phrase database. */
1691 memcpy(addPhoneSeq, &pgdata->phoneSeq[cursor - newPhraseLen], sizeof(uint16_t) * newPhraseLen);
1692 addPhoneSeq[newPhraseLen] = 0;
1693
1694 copyStringFromPreeditBuf(pgdata, cursor - newPhraseLen, newPhraseLen, addWordSeq, sizeof(addWordSeq));
1695
1696 phraseState = UserUpdatePhrase(pgdata, addPhoneSeq, addWordSeq);
1697 SetUpdatePhraseMsg(pgdata, addWordSeq, newPhraseLen, phraseState);
1698
1699 /* Clear the breakpoint between the New Phrase */
1700 for (i = 1; i < newPhraseLen; i++)
1701 pgdata->bUserArrBrkpt[cursor - newPhraseLen + i] = 0;
1702 }
1703 }
1704 }
1705 CallPhrasing(pgdata, 0);
1706 MakeOutputWithRtn(pgo, pgdata, keystrokeRtn);
1707 MakeOutputAddMsgAndCleanInterval(pgo, pgdata);
1708 return 0;
1709 }
1710
chewing_handle_ShiftSpace(ChewingContext * ctx)1711 CHEWING_API int chewing_handle_ShiftSpace(ChewingContext *ctx)
1712 {
1713 ChewingData *pgdata;
1714 ChewingOutput *pgo;
1715 int keystrokeRtn = KEYSTROKE_ABSORB;
1716
1717 if (!ctx) {
1718 return -1;
1719 }
1720 pgdata = ctx->data;
1721 pgo = ctx->output;
1722
1723 LOG_API("");
1724
1725 if (!pgdata->bSelect) {
1726 CheckAndResetRange(pgdata);
1727 }
1728
1729 chewing_set_ShapeMode(ctx, 1 - chewing_get_ShapeMode(ctx));
1730
1731 CallPhrasing(pgdata, 0);
1732 MakeOutputWithRtn(pgo, pgdata, keystrokeRtn);
1733 return 0;
1734 }
1735
chewing_handle_Numlock(ChewingContext * ctx,int key)1736 CHEWING_API int chewing_handle_Numlock(ChewingContext *ctx, int key)
1737 {
1738 ChewingData *pgdata;
1739 ChewingOutput *pgo;
1740 int keystrokeRtn = KEYSTROKE_ABSORB;
1741 int rtn;
1742 int QuickCommit = 0;
1743
1744 if (!ctx) {
1745 return -1;
1746 }
1747 pgdata = ctx->data;
1748 pgo = ctx->output;
1749
1750 LOG_API("");
1751
1752 if (!pgdata->bSelect) {
1753 /* If we're not selecting words, we should send out numeric
1754 * characters at once.
1755 */
1756 if (pgdata->chiSymbolBufLen == 0) {
1757 QuickCommit = 1;
1758 }
1759 rtn = SymbolInput(key, pgdata);
1760 /* copied from chewing_handle_Default */
1761 if (rtn == SYMBOL_KEY_ERROR) {
1762 keystrokeRtn = KEYSTROKE_IGNORE;
1763 } else if (QuickCommit) {
1764 WriteChiSymbolToCommitBuf(pgdata, pgo, 1);
1765 pgdata->chiSymbolBufLen = 0;
1766 pgdata->chiSymbolCursor = 0;
1767 keystrokeRtn = KEYSTROKE_COMMIT;
1768 } else { /* Not quick commit */
1769 CallPhrasing(pgdata, 0);
1770 if (ReleaseChiSymbolBuf(pgdata, pgo) != 0)
1771 keystrokeRtn = KEYSTROKE_COMMIT;
1772 }
1773 } else {
1774 /* Otherwise, if we are selecting words, we use numeric keys
1775 * as selkey
1776 * and submit the words.
1777 */
1778 int num = -1;
1779
1780 if (key > '0' && key <= '9')
1781 num = key - '1';
1782 else if (key == '0')
1783 num = 9;
1784 DoSelect(pgdata, num);
1785 }
1786 CallPhrasing(pgdata, 0);
1787 if (ReleaseChiSymbolBuf(pgdata, pgo) != 0)
1788 keystrokeRtn = KEYSTROKE_COMMIT;
1789 MakeOutputWithRtn(pgo, pgdata, keystrokeRtn);
1790 return 0;
1791 }
1792
chewing_get_phoneSeq(const ChewingContext * ctx)1793 CHEWING_API unsigned short *chewing_get_phoneSeq(const ChewingContext *ctx)
1794 {
1795 const ChewingData *pgdata;
1796 uint16_t *seq;
1797
1798 if (!ctx) {
1799 return NULL;
1800 }
1801 pgdata = ctx->data;
1802
1803 LOG_API("");
1804
1805 seq = ALC(uint16_t, ctx->data->nPhoneSeq);
1806 if (seq)
1807 memcpy(seq, ctx->data->phoneSeq, sizeof(uint16_t) * ctx->data->nPhoneSeq);
1808 return seq;
1809 }
1810
chewing_get_phoneSeqLen(const ChewingContext * ctx)1811 CHEWING_API int chewing_get_phoneSeqLen(const ChewingContext *ctx)
1812 {
1813 const ChewingData *pgdata;
1814
1815 if (!ctx) {
1816 return -1;
1817 }
1818 pgdata = ctx->data;
1819
1820 LOG_API("nPhoneSeq = %d", ctx->data->nPhoneSeq);
1821
1822 return ctx->data->nPhoneSeq;
1823 }
1824
chewing_set_logger(ChewingContext * ctx,void (* logger)(void * data,int level,const char * fmt,...),void * data)1825 CHEWING_API void chewing_set_logger(ChewingContext *ctx,
1826 void (*logger) (void *data, int level, const char *fmt, ...), void *data)
1827 {
1828 ChewingData *pgdata;
1829
1830 if (!ctx) {
1831 return;
1832 }
1833 pgdata = ctx->data;
1834
1835 LOG_API("");
1836
1837 if (!logger) {
1838 logger = NullLogger;
1839 data = 0;
1840 }
1841 ctx->data->logger = logger;
1842 ctx->data->loggerData = data;
1843 }
1844
chewing_userphrase_enumerate(ChewingContext * ctx)1845 CHEWING_API int chewing_userphrase_enumerate(ChewingContext *ctx)
1846 {
1847 ChewingData *pgdata;
1848
1849 #if WITH_SQLITE3
1850 int ret;
1851 #endif
1852
1853 if (!ctx) {
1854 return -1;
1855 }
1856
1857 pgdata = ctx->data;
1858
1859 LOG_API("");
1860
1861 #if WITH_SQLITE3
1862 assert(pgdata->static_data.stmt_userphrase[STMT_USERPHRASE_SELECT]);
1863 ret = sqlite3_reset(pgdata->static_data.stmt_userphrase[STMT_USERPHRASE_SELECT]);
1864 if (ret != SQLITE_OK) {
1865 LOG_ERROR("sqlite3_reset returns %d", ret);
1866 return -1;
1867 }
1868 #else
1869 pgdata->static_data.userphrase_enum = FindNextHash(pgdata, NULL);
1870 #endif
1871 return 0;
1872 }
1873
chewing_userphrase_has_next(ChewingContext * ctx,unsigned int * phrase_len,unsigned int * bopomofo_len)1874 CHEWING_API int chewing_userphrase_has_next(ChewingContext *ctx, unsigned int *phrase_len, unsigned int *bopomofo_len)
1875 {
1876 ChewingData *pgdata;
1877
1878 #if WITH_SQLITE3
1879 int ret;
1880 #endif
1881
1882 if (!ctx || !phrase_len || !bopomofo_len) {
1883 return 0;
1884 }
1885 pgdata = ctx->data;
1886
1887 LOG_API("");
1888
1889 #if WITH_SQLITE3
1890 ret = sqlite3_step(pgdata->static_data.stmt_userphrase[STMT_USERPHRASE_SELECT]);
1891 if (ret != SQLITE_ROW) {
1892 if (ret != SQLITE_DONE) {
1893 LOG_ERROR("sqlite3_step returns %d", ret);
1894 }
1895 return 0;
1896 }
1897
1898 *phrase_len = strlen((const char *) sqlite3_column_text(pgdata->static_data.stmt_userphrase[STMT_USERPHRASE_SELECT],
1899 SQL_STMT_USERPHRASE[STMT_USERPHRASE_SELECT].column
1900 [COLUMN_USERPHRASE_PHRASE])) + 1;
1901
1902 *bopomofo_len = GetBopomofoBufLen(sqlite3_column_int(pgdata->static_data.stmt_userphrase[STMT_USERPHRASE_SELECT],
1903 SQL_STMT_USERPHRASE[STMT_USERPHRASE_SELECT].column
1904 [COLUMN_USERPHRASE_LENGTH]));
1905
1906 return 1;
1907 #else
1908 if (pgdata->static_data.userphrase_enum) {
1909 *phrase_len = strlen(pgdata->static_data.userphrase_enum->data.wordSeq) + 1;
1910 *bopomofo_len = BopomofoFromUintArray(NULL, 0, pgdata->static_data.userphrase_enum->data.phoneSeq);
1911 return 1;
1912
1913 }
1914 return 0;
1915 #endif
1916 }
1917
chewing_userphrase_get(ChewingContext * ctx,char * phrase_buf,unsigned int phrase_len,char * bopomofo_buf,unsigned int bopomofo_len)1918 CHEWING_API int chewing_userphrase_get(ChewingContext *ctx,
1919 char *phrase_buf, unsigned int phrase_len,
1920 char *bopomofo_buf, unsigned int bopomofo_len)
1921 {
1922 ChewingData *pgdata;
1923
1924 #if WITH_SQLITE3
1925 const char *phrase;
1926 int length;
1927 int i;
1928 uint16_t phone_array[MAX_PHRASE_LEN + 1] = { 0 };
1929 #endif
1930
1931 if (!ctx || !phrase_buf || !phrase_len || !bopomofo_buf || !bopomofo_len) {
1932 return -1;
1933 }
1934 pgdata = ctx->data;
1935
1936 LOG_API("");
1937
1938 #if WITH_SQLITE3
1939 phrase = (const char *) sqlite3_column_text(pgdata->static_data.stmt_userphrase[STMT_USERPHRASE_SELECT],
1940 SQL_STMT_USERPHRASE[STMT_USERPHRASE_SELECT].column
1941 [COLUMN_USERPHRASE_PHRASE]);
1942 length =
1943 sqlite3_column_int(pgdata->static_data.stmt_userphrase[STMT_USERPHRASE_SELECT],
1944 SQL_STMT_USERPHRASE[STMT_USERPHRASE_SELECT].column[COLUMN_USERPHRASE_LENGTH]);
1945
1946 if (phrase_len < strlen(phrase) + 1) {
1947 LOG_ERROR("phrase_len %d is smaller than %d", phrase_len, strlen(phrase) + 1);
1948 return -1;
1949 }
1950
1951 if (bopomofo_len < GetBopomofoBufLen(length)) {
1952 LOG_ERROR("bopomofo_len %d is smaller than %d", bopomofo_len, GetBopomofoBufLen(length));
1953 return -1;
1954 }
1955
1956 for (i = 0; i < length && i < MAX_PHRASE_LEN; ++i) {
1957 phone_array[i] = sqlite3_column_int(pgdata->static_data.stmt_userphrase[STMT_USERPHRASE_SELECT],
1958 SQL_STMT_USERPHRASE[STMT_USERPHRASE_SELECT].column[COLUMN_USERPHRASE_PHONE_0
1959 + i]);
1960 }
1961
1962 strncpy(phrase_buf, phrase, phrase_len);
1963 BopomofoFromUintArray(bopomofo_buf, bopomofo_len, phone_array);
1964
1965 return 0;
1966 #else
1967 if (pgdata->static_data.userphrase_enum) {
1968 strncpy(phrase_buf, pgdata->static_data.userphrase_enum->data.wordSeq, phrase_len);
1969 phrase_buf[phrase_len - 1] = 0;
1970
1971 BopomofoFromUintArray(bopomofo_buf, bopomofo_len, pgdata->static_data.userphrase_enum->data.phoneSeq);
1972 bopomofo_buf[bopomofo_len - 1] = 0;
1973
1974 pgdata->static_data.userphrase_enum = FindNextHash(pgdata, pgdata->static_data.userphrase_enum);
1975
1976 return 0;
1977 }
1978
1979 return -1;
1980 #endif
1981 }
1982
chewing_userphrase_add(ChewingContext * ctx,const char * phrase_buf,const char * bopomofo_buf)1983 CHEWING_API int chewing_userphrase_add(ChewingContext *ctx, const char *phrase_buf, const char *bopomofo_buf)
1984 {
1985 ChewingData *pgdata;
1986 ssize_t phrase_len;
1987 ssize_t phone_len;
1988 uint16_t *phone_buf = 0;
1989 int ret;
1990
1991 if (!ctx || !phrase_buf || !bopomofo_buf) {
1992 return -1;
1993 }
1994 pgdata = ctx->data;
1995
1996 LOG_API("");
1997
1998 phrase_len = ueStrLen(phrase_buf);
1999 phone_len = UintArrayFromBopomofo(NULL, 0, bopomofo_buf);
2000
2001 if (phrase_len != phone_len) {
2002 return 0;
2003 }
2004
2005 phone_buf = ALC(uint16_t, phone_len + 1);
2006 if (!phone_buf)
2007 return -1;
2008 ret = UintArrayFromBopomofo(phone_buf, phone_len + 1, bopomofo_buf);
2009 if (ret == -1) {
2010 free(phone_buf);
2011 return 0;
2012 }
2013
2014 ret = UserUpdatePhrase(pgdata, phone_buf, phrase_buf);
2015 free(phone_buf);
2016
2017 if (ret == USER_UPDATE_FAIL) {
2018 return 0;
2019 }
2020
2021 return 1;
2022 }
2023
chewing_userphrase_remove(ChewingContext * ctx,const char * phrase_buf,const char * bopomofo_buf)2024 CHEWING_API int chewing_userphrase_remove(ChewingContext *ctx, const char *phrase_buf, const char *bopomofo_buf)
2025 {
2026 ChewingData *pgdata;
2027 ssize_t phone_len;
2028 uint16_t *phone_buf = 0;
2029 int ret;
2030
2031 if (!ctx || !phrase_buf || !bopomofo_buf) {
2032 return -1;
2033 }
2034 pgdata = ctx->data;
2035
2036 LOG_API("");
2037
2038 phone_len = UintArrayFromBopomofo(NULL, 0, bopomofo_buf);
2039 phone_buf = ALC(uint16_t, phone_len + 1);
2040 if (!phone_buf)
2041 return 0;
2042 ret = UintArrayFromBopomofo(phone_buf, phone_len + 1, bopomofo_buf);
2043 if (ret == -1) {
2044 free(phone_buf);
2045 return 0;
2046 }
2047 ret = UserRemovePhrase(pgdata, phone_buf, phrase_buf);
2048 free(phone_buf);
2049
2050 return ret;
2051 }
2052
chewing_userphrase_lookup(ChewingContext * ctx,const char * phrase_buf,const char * bopomofo_buf)2053 CHEWING_API int chewing_userphrase_lookup(ChewingContext *ctx, const char *phrase_buf, const char *bopomofo_buf)
2054 {
2055 ChewingData *pgdata;
2056 ssize_t phone_len;
2057 uint16_t *phone_buf = 0;
2058 int ret;
2059 UserPhraseData *user_phrase_data;
2060
2061 if (!ctx || !phrase_buf || !bopomofo_buf) {
2062 return 0;
2063 }
2064 pgdata = ctx->data;
2065
2066 LOG_API("");
2067
2068 phone_len = UintArrayFromBopomofo(NULL, 0, bopomofo_buf);
2069 phone_buf = ALC(uint16_t, phone_len + 1);
2070 if (!phone_buf)
2071 return 0;
2072 ret = UintArrayFromBopomofo(phone_buf, phone_len + 1, bopomofo_buf);
2073 if (ret == -1) {
2074 free(phone_buf);
2075 return 0;
2076 }
2077
2078 user_phrase_data = UserGetPhraseFirst(pgdata, phone_buf);
2079 while (user_phrase_data) {
2080 if (strcmp(phrase_buf, user_phrase_data->wordSeq) == 0)
2081 break;
2082 user_phrase_data = UserGetPhraseNext(pgdata, phone_buf);
2083 }
2084 UserGetPhraseEnd(pgdata, phone_buf);
2085 free(phone_buf);
2086 return user_phrase_data == NULL ? 0 : 1;
2087 }
2088
chewing_cand_string_by_index_static(ChewingContext * ctx,int index)2089 CHEWING_API const char *chewing_cand_string_by_index_static(ChewingContext *ctx, int index)
2090 {
2091 ChewingData *pgdata;
2092 const char *s;
2093
2094 if (!ctx) {
2095 return NULL;
2096 }
2097 pgdata = ctx->data;
2098
2099 LOG_API("index = %d", index);
2100
2101 if (0 <= index && index < ctx->output->pci->nTotalChoice) {
2102 s = ctx->output->pci->totalChoiceStr[index];
2103 } else {
2104 s = "";
2105 }
2106 return s;
2107 }
2108
chewing_cand_choose_by_index(ChewingContext * ctx,int index)2109 CHEWING_API int chewing_cand_choose_by_index(ChewingContext *ctx, int index)
2110 {
2111 ChewingData *pgdata;
2112 ChewingOutput *pgo;
2113
2114 int ret;
2115
2116 if (!ctx) {
2117 return -1;
2118 }
2119 pgdata = ctx->data;
2120 pgo = ctx->output;
2121
2122 LOG_API("index = %d", index);
2123
2124 if (pgdata->choiceInfo.nTotalChoice == 0)
2125 return -1;
2126
2127 ret = SelectCandidate(pgdata, index);
2128 if (ret == 0) {
2129 CallPhrasing(pgdata, 0);
2130 MakeOutputWithRtn(pgo, pgdata, KEYSTROKE_ABSORB);
2131 }
2132 return ret;
2133 }
2134
chewing_cand_open(ChewingContext * ctx)2135 CHEWING_API int chewing_cand_open(ChewingContext *ctx)
2136 {
2137 ChewingData *pgdata;
2138 int pos;
2139
2140 if (!ctx) {
2141 return -1;
2142 }
2143 pgdata = ctx->data;
2144
2145 LOG_API("");
2146
2147 if (pgdata->bSelect)
2148 return 0;
2149 if (pgdata->chiSymbolBufLen == 0)
2150 return -1;
2151
2152 pos = pgdata->chiSymbolCursor;
2153 if (pgdata->chiSymbolCursor == pgdata->chiSymbolBufLen)
2154 --pos;
2155
2156 chooseCandidate(ctx, ChewingIsChiAt(pos, pgdata), pos);
2157
2158 return 0;
2159 }
2160
chewing_cand_close(ChewingContext * ctx)2161 CHEWING_API int chewing_cand_close(ChewingContext *ctx)
2162 {
2163 ChewingData *pgdata;
2164
2165 if (!ctx) {
2166 return -1;
2167 }
2168 pgdata = ctx->data;
2169
2170 LOG_API("");
2171
2172 if (ctx->data->bSelect) {
2173 ChoiceEndChoice(ctx->data);
2174 }
2175
2176 return 0;
2177
2178 }
2179
chewing_cand_list_first(ChewingContext * ctx)2180 CHEWING_API int chewing_cand_list_first(ChewingContext *ctx)
2181 {
2182 ChewingData *pgdata;
2183
2184 if (!ctx) {
2185 return -1;
2186 }
2187 pgdata = ctx->data;
2188
2189 LOG_API("");
2190
2191 if (!pgdata->bSelect)
2192 return -1;
2193
2194 return ChoiceFirstAvail(pgdata);
2195 }
2196
chewing_cand_list_last(ChewingContext * ctx)2197 CHEWING_API int chewing_cand_list_last(ChewingContext *ctx)
2198 {
2199 ChewingData *pgdata;
2200
2201 if (!ctx) {
2202 return -1;
2203 }
2204 pgdata = ctx->data;
2205
2206 LOG_API("");
2207
2208 if (!pgdata->bSelect)
2209 return -1;
2210
2211 return ChoiceLastAvail(pgdata);
2212 }
2213
chewing_cand_list_has_next(ChewingContext * ctx)2214 CHEWING_API int chewing_cand_list_has_next(ChewingContext *ctx)
2215 {
2216 ChewingData *pgdata;
2217
2218 if (!ctx) {
2219 return 0;
2220 }
2221 pgdata = ctx->data;
2222
2223 LOG_API("");
2224
2225 if (!pgdata->bSelect)
2226 return 0;
2227
2228 return ChoiceHasNextAvail(pgdata);
2229 }
2230
chewing_cand_list_has_prev(ChewingContext * ctx)2231 CHEWING_API int chewing_cand_list_has_prev(ChewingContext *ctx)
2232 {
2233 ChewingData *pgdata;
2234
2235 if (!ctx) {
2236 return 0;
2237 }
2238 pgdata = ctx->data;
2239
2240 LOG_API("");
2241
2242 if (!pgdata->bSelect)
2243 return 0;
2244
2245 return ChoiceHasPrevAvail(pgdata);
2246 }
2247
chewing_cand_list_next(ChewingContext * ctx)2248 CHEWING_API int chewing_cand_list_next(ChewingContext *ctx)
2249 {
2250 ChewingData *pgdata;
2251
2252 if (!ctx) {
2253 return -1;
2254 }
2255 pgdata = ctx->data;
2256
2257 LOG_API("");
2258
2259 if (!pgdata->bSelect)
2260 return -1;
2261
2262 return ChoiceNextAvail(pgdata);
2263 }
2264
chewing_cand_list_prev(ChewingContext * ctx)2265 CHEWING_API int chewing_cand_list_prev(ChewingContext *ctx)
2266 {
2267 ChewingData *pgdata;
2268
2269 if (!ctx) {
2270 return -1;
2271 }
2272 pgdata = ctx->data;
2273
2274 LOG_API("");
2275
2276 if (!pgdata->bSelect)
2277 return -1;
2278
2279 return ChoicePrevAvail(pgdata);
2280 }
2281
chewing_commit_preedit_buf(ChewingContext * ctx)2282 CHEWING_API int chewing_commit_preedit_buf(ChewingContext *ctx)
2283 {
2284 ChewingData *pgdata;
2285 ChewingOutput *pgo;
2286 int len;
2287
2288 if (!ctx) {
2289 return -1;
2290 }
2291 pgdata = ctx->data;
2292 pgo = ctx->output;
2293
2294 LOG_API("");
2295
2296 if (pgdata->bSelect)
2297 return -1;
2298
2299 len = pgdata->chiSymbolBufLen;
2300
2301 if (!len)
2302 return -1;
2303
2304 WriteChiSymbolToCommitBuf(pgdata, pgo, len);
2305 AutoLearnPhrase(pgdata);
2306 CleanAllBuf(pgdata);
2307
2308 MakeOutputWithRtn(pgo, pgdata, KEYSTROKE_COMMIT);
2309
2310 return 0;
2311 }
2312
chewing_clean_preedit_buf(ChewingContext * ctx)2313 CHEWING_API int chewing_clean_preedit_buf(ChewingContext *ctx)
2314 {
2315 ChewingData *pgdata;
2316 ChewingOutput *pgo;
2317
2318 if (!ctx) {
2319 return -1;
2320 }
2321 pgdata = ctx->data;
2322 pgo = ctx->output;
2323
2324 LOG_API("");
2325
2326 if (pgdata->bSelect)
2327 return -1;
2328
2329 CleanAllBuf(pgdata);
2330
2331 MakeOutput(pgo, pgdata);
2332 return 0;
2333 }
2334
chewing_clean_bopomofo_buf(ChewingContext * ctx)2335 CHEWING_API int chewing_clean_bopomofo_buf(ChewingContext *ctx)
2336 {
2337 ChewingData *pgdata;
2338 ChewingOutput *pgo;
2339
2340 if (!ctx) {
2341 return -1;
2342 }
2343 pgdata = ctx->data;
2344 pgo = ctx->output;
2345
2346 LOG_API("");
2347
2348 if (BopomofoIsEntering(&pgdata->bopomofoData)) {
2349 BopomofoRemoveAll(&pgdata->bopomofoData);
2350 }
2351
2352 MakeOutput(pgo, pgdata);
2353 return 0;
2354 }
2355