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