1 /***************************************************************************
2  *   Copyright (C) 2002~2005 by Yuking                                     *
3  *   yuking_net@sohu.com                                                   *
4  *                                                                         *
5  *   This program is free software; you can redistribute it and/or modify  *
6  *   it under the terms of the GNU General Public License as published by  *
7  *   the Free Software Foundation; either version 2 of the License, or     *
8  *   (at your option) any later version.                                   *
9  *                                                                         *
10  *   This program is distributed in the hope that it will be useful,       *
11  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
12  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
13  *   GNU General Public License for more details.                          *
14  *                                                                         *
15  *   You should have received a copy of the GNU General Public License     *
16  *   along with this program; if not, write to the                         *
17  *   Free Software Foundation, Inc.,                                       *
18  *   51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.              *
19  ***************************************************************************/
20 #include "config.h"
21 
22 #include <libintl.h>
23 #include <stdio.h>
24 #include <limits.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <unistd.h>
28 #include <sys/stat.h>
29 #include <assert.h>
30 #include <ctype.h>
31 
32 #include "fcitx/fcitx.h"
33 #include "fcitx-utils/utils.h"
34 #include "fcitx/ime.h"
35 #include "fcitx/keys.h"
36 #include "fcitx/ui.h"
37 #include "fcitx/frontend.h"
38 #include "fcitx/module.h"
39 #include "fcitx/context.h"
40 #include "fcitx/profile.h"
41 #include "fcitx/configfile.h"
42 #include "fcitx-config/xdg.h"
43 #include "fcitx-utils/utf8.h"
44 #include "fcitx-utils/log.h"
45 #include "py.h"
46 #include "PYFA.h"
47 #include "pyParser.h"
48 #include "sp.h"
49 #include "pyconfig.h"
50 #include "spdata.h"
51 #include "module/quickphrase/fcitx-quickphrase.h"
52 
53 #define PY_INDEX_MAGIC_NUMBER 0xf7462e34
54 #define PINYIN_TEMP_FILE "pinyin_XXXXXX"
55 
56 typedef struct {
57     PY_CAND_WORD_TYPE type;
58     ADJUSTORDER order;
59     FcitxPinyinState* pystate;
60 } PYCandWordSortContext;
61 
62 static void LoadPYPhraseDict(FcitxPinyinState* pystate, FILE* fp,
63                              boolean isSystem, boolean stripDup);
64 static void ReloadConfigPY(void* arg);
65 static void PinyinMigration();
66 static int PYCandWordCmp(const void* b, const void* a, void* arg);
67 static boolean PYGetPYMapByHZ(FcitxPinyinState*pystate, char *strHZ,
68                               char* mapHint, char *strMap);
69 
70 FCITX_DEFINE_PLUGIN(fcitx_pinyin, ime2, FcitxIMClass2) = {
71     PYCreate,
72     PYDestroy,
73     ReloadConfigPY,
74     NULL,
75     NULL,
76     NULL,
77     NULL,
78     NULL
79 };
80 
DECLARE_ADDFUNCTIONS(Pinyin)81 DECLARE_ADDFUNCTIONS(Pinyin)
82 
83 void *PYCreate(FcitxInstance* instance)
84 {
85     FcitxPinyinState *pystate = fcitx_utils_new(FcitxPinyinState);
86     InitMHPY(&pystate->pyconfig.MHPY_C, MHPY_C_TEMPLATE);
87     InitMHPY(&pystate->pyconfig.MHPY_S, MHPY_S_TEMPLATE);
88     InitPYTable(&pystate->pyconfig);
89     InitPYSplitData(&pystate->pyconfig);
90     if (!LoadPYConfig(&pystate->pyconfig)) {
91         free(pystate->pyconfig.MHPY_C);
92         free(pystate->pyconfig.MHPY_S);
93         free(pystate->pyconfig.PYTable);
94         FreePYSplitData(&pystate->pyconfig);
95         free(pystate);
96         return NULL;
97     }
98 
99     PinyinMigration();
100 
101     pystate->pool = fcitx_memory_pool_create();
102 
103     FcitxInstanceRegisterIM(instance,
104                             pystate,
105                             "pinyin",
106                             _("Pinyin"),
107                             "pinyin",
108                             PYInit,
109                             ResetPYStatus,
110                             DoPYInput,
111                             PYGetCandWords,
112                             NULL,
113                             SavePY,
114                             NULL,
115                             NULL,
116                             5,
117                             "zh_CN");
118     FcitxInstanceRegisterIM(instance,
119                             pystate,
120                             "shuangpin",
121                             _("Shuangpin"),
122                             "shuangpin",
123                             SPInit,
124                             ResetPYStatus,
125                             DoPYInput,
126                             PYGetCandWords,
127                             NULL,
128                             SavePY,
129                             NULL,
130                             NULL,
131                             5,
132                             "zh_CN");
133     pystate->owner = instance;
134 
135     FcitxPinyinAddFunctions(instance);
136     return pystate;
137 }
138 
PYDestroy(void * arg)139 void PYDestroy(void* arg)
140 {
141     FcitxPinyinState *pystate = (FcitxPinyinState*)arg;
142     free(pystate->pyconfig.MHPY_C);
143     free(pystate->pyconfig.MHPY_S);
144     free(pystate->pyconfig.PYTable);
145     FreePYSplitData(&pystate->pyconfig);
146     FcitxConfigFree(&pystate->pyconfig.gconfig);
147     fcitx_memory_pool_destroy(pystate->pool);
148 
149     int i, j, k;
150     PYFA *PYFAList = pystate->PYFAList;
151     for (i = 0; i < pystate->iPYFACount; i++) {
152         for (j = 0; j < PYFAList[i].iBase; j++) {
153             PyPhrase* phrase = USER_PHRASE_NEXT(PYFAList[i].pyBase[j].userPhrase);
154             for (k = 0; k < PYFAList[i].pyBase[j].iUserPhrase; k++) {
155                 PyPhrase* cur = phrase;
156                 fcitx_utils_free(cur->strPhrase);
157                 fcitx_utils_free(cur->strMap);
158                 phrase = USER_PHRASE_NEXT(phrase);
159                 free(cur);
160             }
161 
162             free(PYFAList[i].pyBase[j].userPhrase);
163             fcitx_utils_free(PYFAList[i].pyBase[j].phrase);
164         }
165         free(PYFAList[i].pyBase);
166     }
167     free(PYFAList);
168 
169     while(pystate->pyFreq) {
170         PyFreq* pCurFreq = pystate->pyFreq;
171         pystate->pyFreq = pCurFreq->next;
172         while (pCurFreq->HZList) {
173             HZ* pHZ = pCurFreq->HZList;
174             pCurFreq->HZList = pHZ->next;
175             free(pHZ);
176         }
177         free(pCurFreq);
178     }
179 
180     free(pystate);
181 }
182 
PYInit(void * arg)183 boolean PYInit(void *arg)
184 {
185     FcitxPinyinState *pystate = (FcitxPinyinState*)arg;
186     boolean flag = true;
187     FcitxInstanceSetContext(pystate->owner, CONTEXT_IM_KEYBOARD_LAYOUT, "us");
188     FcitxInstanceSetContext(pystate->owner, CONTEXT_SHOW_REMIND_STATUS, &flag);
189     pystate->bSP = false;
190     return true;
191 }
192 
SPInit(void * arg)193 boolean SPInit(void *arg)
194 {
195     FcitxPinyinState *pystate = (FcitxPinyinState*)arg;
196     boolean flag = true;
197     FcitxInstanceSetContext(pystate->owner, CONTEXT_IM_KEYBOARD_LAYOUT, "us");
198     FcitxInstanceSetContext(pystate->owner, CONTEXT_SHOW_REMIND_STATUS, &flag);
199     pystate->bSP = true;
200     FcitxPinyinConfig* pyconfig = &pystate->pyconfig;
201     pyconfig->cNonS = 'o';
202     memcpy(pyconfig->SPMap_S, SPMap_S_Ziranma, sizeof(SPMap_S_Ziranma));
203     memcpy(pyconfig->SPMap_C, SPMap_C_Ziranma, sizeof(SPMap_C_Ziranma));
204 
205     LoadSPData(pystate);
206     return true;
207 }
208 
LoadPYBaseDict(FcitxPinyinState * pystate)209 boolean LoadPYBaseDict(FcitxPinyinState *pystate)
210 {
211     FILE *fp;
212     int i, j;
213     int32_t iLen;
214 
215     fp = FcitxXDGGetFileWithPrefix("pinyin", PY_BASE_FILE, "r", NULL);
216     if (!fp)
217         return false;
218 
219     fcitx_utils_read_int32(fp, &pystate->iPYFACount);
220     pystate->PYFAList = (PYFA*)fcitx_utils_malloc0(sizeof(PYFA) * pystate->iPYFACount);
221     PYFA *PYFAList = pystate->PYFAList;
222     for (i = 0; i < pystate->iPYFACount; i++) {
223         fread(PYFAList[i].strMap, sizeof(char) * 2, 1, fp);
224         PYFAList[i].strMap[2] = '\0';
225 
226         fcitx_utils_read_int32(fp, &PYFAList[i].iBase);
227         PYFAList[i].pyBase = (PyBase*)fcitx_utils_malloc0(sizeof(PyBase) * PYFAList[i].iBase);
228         for (j = 0; j < PYFAList[i].iBase; j++) {
229             int8_t len;
230             fread(&len, sizeof(char), 1, fp);
231             fread(PYFAList[i].pyBase[j].strHZ, sizeof(char) * len, 1, fp);
232             PYFAList[i].pyBase[j].strHZ[len] = '\0';
233             fcitx_utils_read_int32(fp, &iLen);
234             PYFAList[i].pyBase[j].iIndex = iLen;
235             PYFAList[i].pyBase[j].iHit = 0;
236             if (iLen > pystate->iCounter)
237                 pystate->iCounter = iLen;
238             PYFAList[i].pyBase[j].iPhrase = 0;
239             PYFAList[i].pyBase[j].iUserPhrase = 0;
240             PYFAList[i].pyBase[j].userPhrase = fcitx_utils_new(PyUsrPhrase);
241             PYFAList[i].pyBase[j].userPhrase->next = PYFAList[i].pyBase[j].userPhrase;
242         }
243     }
244 
245     fclose(fp);
246     pystate->bPYBaseDictLoaded = true;
247 
248     pystate->iOrigCounter = pystate->iCounter;
249 
250     pystate->pyFreq = fcitx_utils_new(PyFreq);
251     // pystate->pyFreq->next = NULL;
252 
253     return true;
254 }
255 
LoadPYPhraseDict(FcitxPinyinState * pystate,FILE * fp,boolean isSystem,boolean stripDup)256 void LoadPYPhraseDict(FcitxPinyinState* pystate, FILE *fp, boolean isSystem, boolean stripDup)
257 {
258     int j, k;
259     int32_t i, count, iLen;
260     char strBase[UTF8_MAX_LENGTH + 1];
261     PyPhrase *phrase = NULL, *temp;
262     PYFA* PYFAList = pystate->PYFAList;
263     while (!feof(fp)) {
264         int8_t clen;
265         if (!fcitx_utils_read_int32(fp, &i))
266             break;
267         if (!fread(&clen, sizeof(int8_t), 1, fp))
268             break;
269         if (clen <= 0 || clen > UTF8_MAX_LENGTH)
270             break;
271         if (!fread(strBase, sizeof(char) * clen, 1, fp))
272             break;
273         strBase[clen] = '\0';
274         if (!fcitx_utils_read_int32(fp, &count))
275             break;
276 
277         j = GetBaseIndex(pystate, i, strBase);
278         if (j == -1)
279             break;
280 
281         if (isSystem) {
282             phrase = (PyPhrase*)fcitx_utils_malloc0(sizeof(PyPhrase) * count);
283             temp = phrase;
284         } else {
285             PYFAList[i].pyBase[j].iUserPhrase = count;
286             temp = &PYFAList[i].pyBase[j].userPhrase->phrase;
287         }
288 
289         for (k = 0; k < count; k++) {
290             if (!isSystem)
291                 phrase = (PyPhrase*)fcitx_utils_malloc0(sizeof(PyUsrPhrase));
292 
293             fcitx_utils_read_int32(fp, &iLen);
294 
295             if (isSystem) {
296                 phrase->strMap = (char*)fcitx_memory_pool_alloc(pystate->pool, sizeof(char) * (iLen + 1));
297             } else {
298                 phrase->strMap = (char*)fcitx_utils_malloc0(sizeof(char) * (iLen + 1));
299             }
300             fread(phrase->strMap, sizeof(char) * iLen, 1, fp);
301             phrase->strMap[iLen] = '\0';
302 
303             fcitx_utils_read_int32(fp, &iLen);
304 
305             if (isSystem) {
306                 phrase->strPhrase = (char*)fcitx_memory_pool_alloc(pystate->pool, sizeof(char) * (iLen + 1));
307             } else {
308                 phrase->strPhrase = (char*)fcitx_utils_malloc0(sizeof(char) * (iLen + 1));
309             }
310             fread(phrase->strPhrase, sizeof(char) * iLen, 1, fp);
311             phrase->strPhrase[iLen] = '\0';
312 
313             fcitx_utils_read_int32(fp, &iLen);
314             phrase->iIndex = iLen;
315             if (iLen > pystate->iCounter)
316                 pystate->iCounter = iLen;
317             if (isSystem) {
318                 phrase->iHit = 0;
319                 phrase ++;
320             } else {
321                 fcitx_utils_read_int32(fp, &iLen);
322                 phrase->iHit = iLen;
323 
324                 ((PyUsrPhrase*)phrase)->next = ((PyUsrPhrase*) temp)->next;
325                 ((PyUsrPhrase*)temp)->next = (PyUsrPhrase*) phrase;
326 
327                 temp = phrase;
328             }
329         }
330 
331         if (isSystem) {
332             if (PYFAList[i].pyBase[j].iPhrase == 0) {
333                 PYFAList[i].pyBase[j].iPhrase = count;
334                 PYFAList[i].pyBase[j].phrase = temp;
335             } else {
336                 int m, n;
337                 boolean *flag = fcitx_utils_malloc0(sizeof(boolean) * count);
338                 int left = count;
339                 phrase = temp;
340                 if (stripDup) {
341                     for (m = 0; m < count; m++) {
342                         for (n = 0; n < PYFAList[i].pyBase[j].iPhrase; n++) {
343                             int result = strcmp(PYFAList[i].pyBase[j].phrase[n].strMap, phrase[m].strMap);
344                             if (result == 0) {
345                                 if (strcmp(PYFAList[i].pyBase[j].phrase[n].strPhrase, phrase[m].strPhrase) == 0)
346                                     break;
347                             }
348                         }
349                         if (n != PYFAList[i].pyBase[j].iPhrase) {
350                             flag[m] = 1;
351                             left -- ;
352                         }
353                     }
354                 }
355                 int orig = PYFAList[i].pyBase[j].iPhrase;
356                 if (left >= 0) {
357                     PYFAList[i].pyBase[j].iPhrase += left;
358                     PYFAList[i].pyBase[j].phrase = realloc(PYFAList[i].pyBase[j].phrase, sizeof(PyPhrase) * PYFAList[i].pyBase[j].iPhrase);
359                     //memcpy(phrase, oldph, sizeof(PyPhrase) * old);
360                 }
361                 for (m = 0; m < count; m ++) {
362                     if (!flag[m]) {
363                         memcpy(&PYFAList[i].pyBase[j].phrase[orig], &phrase[m], sizeof(PyPhrase));
364                         orig ++ ;
365                     }
366                     else {
367                         // free(phrase[m].strMap);
368                         // free(phrase[m].strPhrase);
369                     }
370                 }
371                 assert(orig == PYFAList[i].pyBase[j].iPhrase);
372                 free(flag);
373                 free(phrase);
374             }
375         }
376     }
377 }
378 
LoadPYOtherDict(FcitxPinyinState * pystate)379 boolean LoadPYOtherDict(FcitxPinyinState* pystate)
380 {
381     //下面开始读系统词组
382     FILE *fp;
383     int32_t i, j, k, iLen;
384     uint32_t iIndex;
385     PyFreq *pyFreqTemp, *pPyFreq;
386     HZ *HZTemp, *pHZ;
387     PYFA* PYFAList = pystate->PYFAList;
388 
389     pystate->bPYOtherDictLoaded = true;
390 
391     fp = FcitxXDGGetFileWithPrefix("pinyin", PY_PHRASE_FILE, "r", NULL);
392     if (!fp)
393         FcitxLog(ERROR, _("Cannot find System Database of Pinyin!"));
394     else {
395         LoadPYPhraseDict(pystate, fp, true, false);
396         fclose(fp);
397         FcitxStringHashSet *sset = FcitxXDGGetFiles("pinyin", NULL, ".mb");
398         FcitxStringHashSet *curStr = sset;
399         while (curStr) {
400             if (strcmp(curStr->name, PY_PHRASE_FILE) != 0
401                 && strcmp(curStr->name, PY_USERPHRASE_FILE) != 0
402                 && strcmp(curStr->name, PY_SYMBOL_FILE) != 0
403                 && strcmp(curStr->name, PY_BASE_FILE) != 0
404                 && strcmp(curStr->name, PY_FREQ_FILE) != 0) {
405 
406                 fp = FcitxXDGGetFileWithPrefix("pinyin", curStr->name, "r", NULL);
407                 if (fp) {
408                     LoadPYPhraseDict(pystate, fp, true, true);
409                     fclose(fp);
410                 }
411             }
412             curStr = curStr->hh.next;
413         }
414 
415         fcitx_utils_free_string_hash_set(sset);
416 
417         pystate->iOrigCounter = pystate->iCounter;
418     }
419 
420     //下面开始读取用户词库
421     fp = FcitxXDGGetFileUserWithPrefix("pinyin", PY_USERPHRASE_FILE, "r", NULL);
422     if (fp) {
423         LoadPYPhraseDict(pystate, fp, false, false);
424         fclose(fp);
425     }
426     //下面读取索引文件
427     fp = FcitxXDGGetFileUserWithPrefix("pinyin", PY_INDEX_FILE, "r", NULL);
428     if (fp) {
429         uint32_t magic = 0;
430         fcitx_utils_read_uint32(fp, &magic);
431         if (magic == PY_INDEX_MAGIC_NUMBER) {
432             fcitx_utils_read_int32(fp, &iLen);
433             if (iLen > pystate->iCounter)
434                 pystate->iCounter = iLen;
435             while (!feof(fp)) {
436                 fcitx_utils_read_int32(fp, &i);
437                 fcitx_utils_read_int32(fp, &j);
438                 fcitx_utils_read_int32(fp, &k);
439                 fcitx_utils_read_uint32(fp, &iIndex);
440                 fcitx_utils_read_int32(fp, &iLen);
441 
442                 if (i < pystate->iPYFACount) {
443                     if (j < PYFAList[i].iBase) {
444                         if (k < PYFAList[i].pyBase[j].iPhrase) {
445                             if (k >= 0) {
446                                 PYFAList[i].pyBase[j].phrase[k].iIndex = iIndex;
447                                 PYFAList[i].pyBase[j].phrase[k].iHit = iLen;
448                             } else {
449                                 PYFAList[i].pyBase[j].iIndex = iIndex;
450                                 PYFAList[i].pyBase[j].iHit = iLen;
451                             }
452                         }
453                     }
454                 }
455             }
456         } else {
457             FcitxLog(WARNING, _("Pinyin Index Magic Number Doesn't match"));
458         }
459 
460         fclose(fp);
461     }
462     //下面读取常用词表
463     fp = FcitxXDGGetFileUserWithPrefix("pinyin", PY_FREQ_FILE, "r", NULL);
464     if (fp) {
465         pPyFreq = pystate->pyFreq;
466 
467         fcitx_utils_read_uint32(fp, &pystate->iPYFreqCount);
468 
469         for (i = 0; i < pystate->iPYFreqCount; i++) {
470             pyFreqTemp = fcitx_utils_new(PyFreq);
471             // pyFreqTemp->next = NULL;
472 
473             fread(pyFreqTemp->strPY, sizeof(char) * 11, 1, fp);
474             fcitx_utils_read_uint32(fp, &pyFreqTemp->iCount);
475 
476             pyFreqTemp->HZList = fcitx_utils_new(HZ);
477             // pyFreqTemp->HZList->next = NULL;
478             pHZ = pyFreqTemp->HZList;
479 
480             for (k = 0; k < pyFreqTemp->iCount; k++) {
481                 int8_t slen;
482                 HZTemp = fcitx_utils_new(HZ);
483                 fread(&slen, sizeof(int8_t), 1, fp);
484                 fread(HZTemp->strHZ, sizeof(char) * slen, 1, fp);
485                 HZTemp->strHZ[slen] = '\0';
486                 fcitx_utils_read_int32(fp, &HZTemp->iPYFA);
487                 fcitx_utils_read_uint32(fp, &HZTemp->iHit);
488                 fcitx_utils_read_uint32(fp, &HZTemp->iIndex);
489                 pHZ->next = HZTemp;
490                 pHZ = HZTemp;
491             }
492 
493             pPyFreq->next = pyFreqTemp;
494             pPyFreq = pyFreqTemp;
495         }
496 
497         fclose(fp);
498     }
499     return true;
500 }
501 
ResetPYStatus(void * arg)502 void ResetPYStatus(void* arg)
503 {
504     FcitxPinyinState *pystate = (FcitxPinyinState*)arg;
505     pystate->iPYInsertPoint = 0;
506     pystate->iPYSelected = 0;
507     pystate->strFindString[0] = '\0';
508     pystate->strPYAuto[0] = '\0';
509 
510     pystate->bIsPYAddFreq = false;
511     pystate->bIsPYDelFreq = false;
512     pystate->bIsPYDelUserPhr = false;
513 
514     pystate->findMap.iMode = PARSE_SINGLEHZ;     //只要不是PARSE_ERROR就可以
515 }
516 
GetBaseIndex(FcitxPinyinState * pystate,int32_t iPYFA,char * strBase)517 int GetBaseIndex(FcitxPinyinState* pystate, int32_t iPYFA, char *strBase)
518 {
519     int i;
520 
521     if (iPYFA < pystate->iPYFACount) {
522         for (i = 0; i < pystate->PYFAList[iPYFA].iBase; i++) {
523             if (!strcmp(strBase, pystate->PYFAList[iPYFA].pyBase[i].strHZ))
524                 return i;
525         }
526     }
527 
528     return -1;
529 }
530 
DoPYInput(void * arg,FcitxKeySym sym,unsigned int state)531 INPUT_RETURN_VALUE DoPYInput(void* arg, FcitxKeySym sym, unsigned int state)
532 {
533     FcitxPinyinState *pystate = (FcitxPinyinState*) arg;
534     FcitxInputState *input = FcitxInstanceGetInputState(pystate->owner);
535     int i = 0;
536     int val;
537     INPUT_RETURN_VALUE retVal;
538     FcitxCandidateWordList *candList = FcitxInputStateGetCandidateList(input);
539 
540     if (sym == 0 && state == 0)
541         sym = FcitxKey_VoidSymbol;
542 
543     if (!pystate->bPYBaseDictLoaded)
544         LoadPYBaseDict(pystate);
545     if (!pystate->bPYOtherDictLoaded)
546         LoadPYOtherDict(pystate);
547 
548     retVal = IRV_TO_PROCESS;
549 
550     /* is not in py special state */
551     if (!pystate->bIsPYAddFreq && !pystate->bIsPYDelFreq && !pystate->bIsPYDelUserPhr) {
552         if ((FcitxHotkeyIsHotKeyLAZ(sym, state)
553              || FcitxHotkeyIsHotKey(sym, state, FCITX_SEPARATOR)
554              || (pystate->bSP && FcitxInputStateGetRawInputBufferSize(input) > 0 && pystate->bSP_UseSemicolon && FcitxHotkeyIsHotKey(sym, state, FCITX_SEMICOLON)))) {
555             FcitxInputStateSetIsInRemind(input, false);
556             FcitxInputStateSetShowCursor(input, true);
557 
558             /* we cannot insert seperator in the first, nor there is a existing separator */
559             if (FcitxHotkeyIsHotKey(sym, state, FCITX_SEPARATOR)) {
560                 if (!pystate->iPYInsertPoint)
561                     return IRV_TO_PROCESS;
562                 if (pystate->strFindString[pystate->iPYInsertPoint - 1] == PY_SEPARATOR)
563                     return IRV_DO_NOTHING;
564             }
565 
566             val = i = strlen(pystate->strFindString);
567 
568             if (!pystate->bSP &&
569                 pystate->pyconfig.bUseVForQuickPhrase && i == 0 &&
570                 FcitxHotkeyIsKey(sym, state, FcitxKey_v, FcitxKeyState_None)) {
571                 int key = sym;
572                 boolean useDup = false;
573                 boolean append = true;
574                 if (FcitxQuickPhraseLaunch(pystate->owner, &key,
575                                            &useDup, &append))
576                     return IRV_DISPLAY_MESSAGE;
577             }
578 
579             /* do the insert */
580             for (; i > pystate->iPYInsertPoint; i--)
581                 pystate->strFindString[i] = pystate->strFindString[i - 1];
582 
583             pystate->strFindString[pystate->iPYInsertPoint++] = sym;
584             pystate->strFindString[val + 1] = '\0';
585             ParsePY(&pystate->pyconfig, pystate->strFindString, &pystate->findMap, PY_PARSE_INPUT_USER, pystate->bSP);
586 
587             val = 0;
588             for (i = 0; i < pystate->iPYSelected; i++)
589                 val += fcitx_utf8_strlen(pystate->pySelected[i].strHZ);
590 
591             retVal = IRV_DISPLAY_CANDWORDS;
592             if (pystate->findMap.iHZCount > (MAX_WORDS_USER_INPUT - val)) {
593                 UpdateFindString(pystate, val);
594                 ParsePY(&pystate->pyconfig, pystate->strFindString, &pystate->findMap, PY_PARSE_INPUT_USER, pystate->bSP);
595                 retVal = IRV_DO_NOTHING;
596             }
597 
598         } else if (FcitxHotkeyIsHotKey(sym, state, FCITX_BACKSPACE)) {
599             if (pystate->iPYSelected) {
600                 char strTemp[MAX_USER_INPUT + 1];
601 
602                 val = strlen(pystate->strFindString);
603                 strcpy(strTemp, pystate->pySelected[pystate->iPYSelected - 1].strPY);
604                 strcat(strTemp, pystate->strFindString);
605                 strcpy(pystate->strFindString, strTemp);
606                 pystate->iPYInsertPoint += strlen(pystate->strFindString) - val;
607                 pystate->iPYSelected--;
608                 ParsePY(&pystate->pyconfig, pystate->strFindString, &pystate->findMap, PY_PARSE_INPUT_USER, pystate->bSP);
609 
610                 retVal = IRV_DISPLAY_CANDWORDS;
611             } else if (pystate->iPYInsertPoint) {
612                 char *move_src = (pystate->strFindString
613                                   + pystate->iPYInsertPoint);
614                 /* we cannot delete it if cursor is at the first */
615                 val = ((pystate->iPYInsertPoint > 1)
616                        && (move_src[-2] == PY_SEPARATOR)) ? 2 : 1;
617                 memmove(move_src - val, move_src, strlen(move_src) + 1);
618                 ParsePY(&pystate->pyconfig, pystate->strFindString,
619                         &pystate->findMap, PY_PARSE_INPUT_USER, pystate->bSP);
620                 pystate->iPYInsertPoint -= val;
621 
622                 if (!strlen(pystate->strFindString)) {
623                     retVal = IRV_CLEAN;
624                 } else {
625                     retVal = IRV_DISPLAY_CANDWORDS;
626                 }
627             } else {
628                 if (!strlen(pystate->strFindString)) {
629                     retVal = IRV_TO_PROCESS;
630                 } else {
631                     retVal = IRV_DO_NOTHING;
632                 }
633             }
634         } else if (FcitxInputStateGetRawInputBufferSize(input) && FcitxHotkeyIsHotKey(sym, state, FCITX_DELETE)) {
635             if (pystate->iPYInsertPoint == strlen(pystate->strFindString))
636                 retVal = IRV_DO_NOTHING;
637             char *move_dst = (pystate->strFindString
638                                 + pystate->iPYInsertPoint);
639             val = (move_dst[1] == PY_SEPARATOR) ? 2 : 1;
640             memmove(move_dst, move_dst + val, strlen(move_dst + val) + 1);
641 
642             ParsePY(&pystate->pyconfig, pystate->strFindString,
643                     &pystate->findMap, PY_PARSE_INPUT_USER, pystate->bSP);
644             if (!strlen(pystate->strFindString)) {
645                 retVal = IRV_CLEAN;
646             } else {
647                 retVal = IRV_DISPLAY_CANDWORDS;
648             }
649         } else if (FcitxInputStateGetRawInputBufferSize(input) && FcitxHotkeyIsHotKey(sym, state, FCITX_HOME)) {
650             pystate->iPYInsertPoint = 0;
651             retVal = IRV_DISPLAY_CANDWORDS;
652         } else if (FcitxInputStateGetRawInputBufferSize(input) && FcitxHotkeyIsHotKey(sym, state, FCITX_END)) {
653             pystate->iPYInsertPoint = strlen(pystate->strFindString);
654             retVal = IRV_DISPLAY_CANDWORDS;
655         } else if (FcitxInputStateGetRawInputBufferSize(input) && FcitxHotkeyIsHotKey(sym, state, FCITX_RIGHT)) {
656             if (pystate->iPYInsertPoint == strlen(pystate->strFindString)) {
657                 retVal = IRV_DO_NOTHING;
658             } else {
659                 pystate->iPYInsertPoint++;
660                 retVal = IRV_DISPLAY_CANDWORDS;
661             }
662         } else if (FcitxInputStateGetRawInputBufferSize(input) && FcitxHotkeyIsHotKey(sym, state, FCITX_LEFT)) {
663             if (pystate->iPYInsertPoint <= 0) {
664                 if (pystate->iPYSelected) {
665                     char strTemp[MAX_USER_INPUT + 1];
666 
667                     val = strlen(pystate->strFindString);
668                     strcpy(strTemp, pystate->pySelected[pystate->iPYSelected - 1].strPY);
669                     strcat(strTemp, pystate->strFindString);
670                     strcpy(pystate->strFindString, strTemp);
671                     pystate->iPYInsertPoint = strlen(pystate->strFindString) - val;
672                     pystate->iPYSelected--;
673                     ParsePY(&pystate->pyconfig, pystate->strFindString, &pystate->findMap, PY_PARSE_INPUT_USER, pystate->bSP);
674 
675                     retVal = IRV_DISPLAY_CANDWORDS;
676                 } else {
677                     retVal = IRV_DO_NOTHING;
678                 }
679             } else {
680                 pystate->iPYInsertPoint--;
681                 retVal = IRV_DISPLAY_CANDWORDS;
682             }
683         } else if (FcitxHotkeyIsHotKey(sym, state, FCITX_SPACE)) {
684             if (FcitxCandidateWordPageCount(candList) == 0) {
685                 if (FcitxInputStateGetRawInputBufferSize(input) == 0)
686                     retVal = IRV_TO_PROCESS;
687                 else
688                     retVal = IRV_DO_NOTHING;
689             } else {
690                 retVal = FcitxCandidateWordChooseByIndex(candList, 0);
691             }
692         } else if (FcitxInputStateGetRawInputBufferSize(input) && FcitxHotkeyIsHotKey(sym, state, FCITX_ENTER)) {
693             char* outputString = FcitxInputStateGetOutputString(input);
694             strcpy(outputString, "");
695             if (pystate->iPYSelected) {
696                 for (i = 0; i < pystate->iPYSelected; i++) {
697                     strcat(outputString, pystate->pySelected[i].strHZ);
698                 }
699             }
700 
701             for (i = 0; i < pystate->findMap.iHZCount; i++) {
702                 strcat(outputString, pystate->findMap.strPYParsed[i]);
703             }
704             retVal = IRV_COMMIT_STRING;
705         } else if (FcitxHotkeyIsHotKey(sym, state, pystate->pyconfig.hkPYDelUserPhr)) {
706             if (!pystate->bIsPYDelUserPhr) {
707                 int i;
708                 FcitxCandidateWord* candWord = NULL;
709                 for (i = 0;
710                      (candWord = FcitxCandidateWordGetByIndex(candList, i));
711                      i++) {
712                     if (candWord->owner == pystate) {
713                         PYCandWord* pycandWord = candWord->priv;
714                         if (pycandWord->iWhich == PY_CAND_USERPHRASE)
715                             break;
716                     }
717                 }
718 
719                 if (!candWord) {
720                     retVal = IRV_TO_PROCESS;
721                 } else {
722 
723                     pystate->bIsPYDelUserPhr = true;
724                     FcitxInputStateSetIsDoInputOnly(input, true);
725 
726                     FcitxInstanceCleanInputWindowUp(pystate->owner);
727                     FcitxMessagesAddMessageStringsAtLast(
728                         FcitxInputStateGetAuxUp(input), MSG_TIPS,
729                         _("Press index to delete user phrase (ESC for cancel)"));
730                     FcitxInputStateSetShowCursor(input, false);
731 
732                     return IRV_DISPLAY_MESSAGE;
733                 }
734             }
735         } else if (FcitxHotkeyIsHotKey(sym, state, pystate->pyconfig.hkPYAddFreq)) {
736             if (!pystate->bIsPYAddFreq && pystate->findMap.iHZCount == 1 && FcitxInputStateGetRawInputBufferSize(input)) {
737                 pystate->bIsPYAddFreq = true;
738                 FcitxInputStateSetIsDoInputOnly(input, true);
739 
740                 FcitxInstanceCleanInputWindowUp(pystate->owner);
741                 FcitxMessagesAddMessageStringsAtLast(
742                     FcitxInputStateGetAuxUp(input), MSG_TIPS,
743                     _("Press number to make word in frequent list"));
744                 FcitxInputStateSetShowCursor(input, false);
745 
746                 return IRV_DISPLAY_MESSAGE;
747             }
748         } else if (FcitxHotkeyIsHotKey(sym, state, pystate->pyconfig.hkPYDelFreq)) {
749             if (!pystate->bIsPYDelFreq) {
750                 val = 0;
751                 int i;
752                 FcitxCandidateWord* candWord = NULL;
753                 for (i = 0;
754                      (candWord = FcitxCandidateWordGetByIndex(candList, i));
755                      i++) {
756                     if (candWord->owner == pystate) {
757                         PYCandWord* pycandWord = candWord->priv;
758                         if (pycandWord->iWhich == PY_CAND_FREQ) {
759                             val = i + 1;
760                         }
761                     }
762                 }
763 
764                 if (val == 0)
765                     return IRV_DO_NOTHING;
766 
767                 FcitxInstanceCleanInputWindowUp(pystate->owner);
768                 if (val == 1) {
769                     FcitxMessagesAddMessageAtLast(FcitxInputStateGetAuxUp(input), MSG_TIPS, _("Press 1 to delete %s in frequent list (ESC for cancel)"), pystate->strFindString);
770                 } else {
771                     FcitxMessagesAddMessageAtLast(FcitxInputStateGetAuxUp(input), MSG_TIPS, _("Press 1-%d to delete %s in frequent list (ESC for cancel)"), val, pystate->strFindString);
772                 }
773 
774                 pystate->bIsPYDelFreq = true;
775                 FcitxInputStateSetIsDoInputOnly(input, true);
776 
777                 FcitxInputStateSetShowCursor(input, false);
778 
779                 return IRV_DISPLAY_MESSAGE;
780             }
781         }
782     }
783 
784     int iKey;
785     iKey = FcitxCandidateWordCheckChooseKey(candList, sym, state);
786     if (retVal == IRV_TO_PROCESS) {
787         if (iKey >= 0) {
788             FcitxCandidateWord *candWord;
789             candWord = FcitxCandidateWordGetByIndex(candList, iKey);
790             if (!FcitxInputStateGetIsInRemind(input)) {
791                 if (!FcitxInputStateGetRawInputBufferSize(input))
792                     return IRV_TO_PROCESS;
793                 else if (candWord == NULL)
794                     return IRV_DO_NOTHING;
795                 else {
796                     if (candWord->owner == pystate && (pystate->bIsPYAddFreq || pystate->bIsPYDelFreq || pystate->bIsPYDelUserPhr)) {
797                         PYCandWord* pycandWord = candWord->priv;
798                         if (pystate->bIsPYAddFreq) {
799                             PYAddFreq(pystate, pycandWord);
800                             pystate->bIsPYAddFreq = false;
801                         } else if (pystate->bIsPYDelFreq) {
802                             PYDelFreq(pystate, pycandWord);
803                             pystate->bIsPYDelFreq = false;
804                         } else {
805                             if (pycandWord->iWhich == PY_CAND_USERPHRASE)
806                                 PYDelUserPhrase(pystate, pycandWord->cand.phrase.iPYFA,
807                                                 pycandWord->cand.phrase.iBase, (PyUsrPhrase*) pycandWord->cand.phrase.phrase);
808                             pystate->bIsPYDelUserPhr = false;
809                         }
810                         FcitxInputStateSetIsDoInputOnly(input, false);
811                         FcitxInputStateSetShowCursor(input, true);
812 
813                         retVal = IRV_DISPLAY_CANDWORDS;
814                     }
815                 }
816             }
817         } else if (sym == FcitxKey_VoidSymbol) {
818             ParsePY(&pystate->pyconfig, pystate->strFindString, &pystate->findMap, PY_PARSE_INPUT_USER, pystate->bSP);
819             pystate->iPYInsertPoint = 0;
820             retVal = IRV_DISPLAY_CANDWORDS;
821         } else if (FcitxHotkeyIsHotKey(sym, state, FCITX_ESCAPE)) {
822             return IRV_TO_PROCESS;
823         } else if (pystate->bIsPYAddFreq || pystate->bIsPYDelFreq ||
824                    pystate->bIsPYDelUserPhr) {
825             return IRV_DO_NOTHING;
826         }
827     }
828 
829     if (!FcitxInputStateGetIsInRemind(input)) {
830         UpdateCodeInputPY(pystate);
831         CalculateCursorPosition(pystate);
832     } else {
833         if (retVal == IRV_TO_PROCESS && iKey < 0) {
834             ResetPYStatus(pystate);
835             FcitxInstanceCleanInputWindow(pystate->owner);
836             FcitxUIUpdateInputWindow(pystate->owner);
837         }
838     }
839 
840     return retVal;
841 }
842 
843 /*
844  * 本函数根据当前插入点计算光标的实际位置
845  */
CalculateCursorPosition(FcitxPinyinState * pystate)846 void CalculateCursorPosition(FcitxPinyinState* pystate)
847 {
848     int i;
849     int iTemp;
850     FcitxInputState* input = FcitxInstanceGetInputState(pystate->owner);
851 
852     int iCursorPos = 0;
853     int hzLen = 0;
854 
855     for (i = 0; i < pystate->iPYSelected; i++)
856         iCursorPos += strlen(pystate->pySelected[i].strHZ);
857 
858     hzLen = iCursorPos;
859 
860     if (pystate->iPYInsertPoint > strlen(pystate->strFindString))
861         pystate->iPYInsertPoint = strlen(pystate->strFindString);
862     iTemp = pystate->iPYInsertPoint;
863 
864     for (i = 0; i < pystate->findMap.iHZCount; i++) {
865         if (strlen(pystate->findMap.strPYParsed[i]) >= iTemp) {
866             iCursorPos += iTemp;
867             break;
868         }
869         iCursorPos += strlen(pystate->findMap.strPYParsed[i]);
870 
871         iCursorPos++;
872         iTemp -= strlen(pystate->findMap.strPYParsed[i]);
873     }
874     FcitxInputStateSetCursorPos(input, iCursorPos);
875 
876     if (pystate->pyconfig.bFixCursorAtHead)
877         FcitxInputStateSetClientCursorPos(input, 0);
878     else
879         FcitxInputStateSetClientCursorPos(input, hzLen);
880 }
881 
882 /*
883  * 由于拼音的编辑功能修改了strFindString,必须保证FcitxInputStateGetRawInputBuffer(input)与用户的输入一致
884  */
UpdateCodeInputPY(FcitxPinyinState * pystate)885 void UpdateCodeInputPY(FcitxPinyinState* pystate)
886 {
887     int i;
888     FcitxInputState* input = FcitxInstanceGetInputState(pystate->owner);
889     char* strCodeInput = FcitxInputStateGetRawInputBuffer(input);
890 
891     strCodeInput[0] = '\0';
892     for (i = 0; i < pystate->iPYSelected; i++)
893         strcat(strCodeInput, pystate->pySelected[i].strPY);
894     strcat(strCodeInput, pystate->strFindString);
895     FcitxInputStateSetRawInputBufferSize(input, strlen(strCodeInput));
896 }
897 
UpdateFindString(FcitxPinyinState * pystate,int val)898 void UpdateFindString(FcitxPinyinState* pystate, int val)
899 {
900     int i;
901 
902     pystate->strFindString[0] = '\0';
903     for (i = 0; i < pystate->findMap.iHZCount; i++) {
904         if (i >= MAX_WORDS_USER_INPUT - val)
905             break;
906         strcat(pystate->strFindString, pystate->findMap.strPYParsed[i]);
907     }
908     if (pystate->iPYInsertPoint > strlen(pystate->strFindString))
909         pystate->iPYInsertPoint = strlen(pystate->strFindString);
910 }
911 
PYGetCandWords(void * arg)912 INPUT_RETURN_VALUE PYGetCandWords(void* arg)
913 {
914     int iVal;
915     FcitxPinyinState *pystate = (FcitxPinyinState*) arg;
916     FcitxInputState *input = FcitxInstanceGetInputState(pystate->owner);
917     FcitxGlobalConfig* config = FcitxInstanceGetGlobalConfig(pystate->owner);
918     FcitxMessages* msgPreedit = FcitxInputStateGetPreedit(input);
919     FcitxMessages* msgClientPreedit = FcitxInputStateGetClientPreedit(input);
920     FcitxCandidateWordList* candList = FcitxInputStateGetCandidateList(input);
921 
922     FcitxCandidateWordSetPageSize(candList, config->iMaxCandWord);
923     FcitxCandidateWordSetChoose(candList, DIGIT_STR_CHOOSE);
924 
925     /* update preedit string */
926     int i;
927     FcitxMessagesSetMessageCount(msgPreedit, 0);
928     FcitxMessagesSetMessageCount(msgClientPreedit, 0);
929     if (pystate->iPYSelected) {
930         FcitxMessagesAddMessageStringsAtLast(msgPreedit, MSG_OTHER, "");
931         FcitxMessagesAddMessageStringsAtLast(msgClientPreedit, MSG_OTHER, "");
932         for (i = 0; i < pystate->iPYSelected; i++) {
933             FcitxMessagesMessageConcat(msgPreedit, FcitxMessagesGetMessageCount(msgPreedit) - 1, pystate->pySelected[i].strHZ);
934             FcitxMessagesMessageConcat(msgClientPreedit, FcitxMessagesGetMessageCount(msgClientPreedit) - 1, pystate->pySelected[i].strHZ);
935         }
936     }
937 
938     for (i = 0; i < pystate->findMap.iHZCount; i++) {
939         FcitxMessagesAddMessageStringsAtLast(msgPreedit, MSG_CODE,
940                                              pystate->findMap.strPYParsed[i]);
941         if (i < pystate->findMap.iHZCount - 1)
942             FcitxMessagesMessageConcat(msgPreedit, FcitxMessagesGetMessageCount(msgPreedit) - 1, " ");
943     }
944 
945     if (pystate->findMap.iMode == PARSE_ERROR) {
946         for (i = 0; i < pystate->findMap.iHZCount; i++) {
947             FcitxMessagesAddMessageStringsAtLast(
948                 msgClientPreedit, MSG_CODE, pystate->findMap.strPYParsed[i]);
949         }
950         char* errorAuto = FcitxUIMessagesToCString(msgClientPreedit);
951         FcitxInstanceCleanInputWindowDown(pystate->owner);
952 
953         FcitxCandidateWord candWord;
954         candWord.owner = pystate;
955         candWord.callback = PYGetCandWord;
956         candWord.priv = NULL;
957         candWord.strWord = strdup(errorAuto);
958         candWord.strExtra = NULL;
959         candWord.wordType = MSG_OTHER;
960         FcitxCandidateWordAppend(candList, &candWord);
961         return IRV_DISPLAY_CANDWORDS;
962     }
963 
964     if (FcitxInputStateGetIsInRemind(input))
965         return PYGetRemindCandWords(pystate);
966 
967     //判断是不是要输入常用字或符号
968     PyFreq* pCurFreq = pystate->pyFreq->next;
969     for (iVal = 0; iVal < pystate->iPYFreqCount; iVal++) {
970         if (!strcmp(pystate->strFindString, pCurFreq->strPY))
971             break;
972         pCurFreq = pCurFreq->next;
973     }
974 
975     if (pystate->pyconfig.bPYCreateAuto)
976         PYCreateAuto(pystate);
977 
978     if (pystate->strPYAuto[0]) {
979         FcitxCandidateWord candWord;
980         PYCandWord* pycandword = fcitx_utils_new(PYCandWord);
981         pycandword->iWhich = PY_CAND_AUTO;
982         candWord.owner = pystate;
983         candWord.callback = PYGetCandWord;
984         candWord.priv = pycandword;
985         candWord.strWord = strdup(pystate->strPYAuto);
986         candWord.strExtra = NULL;
987         candWord.wordType = MSG_OTHER;
988         FcitxCandidateWordAppend(candList, &candWord);
989     }
990 
991     PYGetPhraseCandWords(pystate);
992     if (pCurFreq)
993         PYGetFreqCandWords(pystate, pCurFreq);
994     PYGetBaseCandWords(pystate, pCurFreq);
995 
996     if (FcitxCandidateWordPageCount(candList) != 0) {
997         FcitxCandidateWord *candWord = FcitxCandidateWordGetCurrentWindow(candList);
998         FcitxMessagesAddMessageStringsAtLast(msgClientPreedit, MSG_INPUT,
999                                              candWord->strWord);
1000     }
1001 
1002     return IRV_DISPLAY_CANDWORDS;
1003 }
1004 
1005 /*
1006  * 根据用户的录入自动生成一个汉字组合
1007  * 此处采用的策略是按照使用频率最高的字/词
1008  */
PYCreateAuto(FcitxPinyinState * pystate)1009 void PYCreateAuto(FcitxPinyinState* pystate)
1010 {
1011     PYCandIndex candPos;
1012     int val;
1013     int iMatchedLength;
1014     char str[3];
1015     PyPhrase *phrase;
1016     PyPhrase *phraseSelected = NULL;
1017     PyBase *baseSelected = NULL;
1018     PYFA *pPYFA = NULL;
1019     char strMap[MAX_WORDS_USER_INPUT * 2 + 1];
1020     int iCount;
1021     PYFA* PYFAList = pystate->PYFAList;
1022     FcitxPinyinConfig* pyconfig = &pystate->pyconfig;
1023 
1024     pystate->strPYAuto[0] = '\0';
1025     pystate->strPYAutoMap[0] = '\0';
1026     str[2] = '\0';
1027 
1028     if (pystate->findMap.iHZCount == 1)
1029         return;
1030 
1031     while (fcitx_utf8_strlen(pystate->strPYAuto) < pystate->findMap.iHZCount) {
1032         phraseSelected = NULL;
1033         baseSelected = NULL;
1034 
1035         iCount = fcitx_utf8_strlen(pystate->strPYAuto);
1036         str[0] = pystate->findMap.strMap[iCount][0];
1037         str[1] = pystate->findMap.strMap[iCount][1];
1038 
1039         strMap[0] = '\0';
1040 
1041         for (val = iCount + 1; val < pystate->findMap.iHZCount; val++)
1042             strcat(strMap, pystate->findMap.strMap[val]);
1043 
1044         candPos.iPYFA = 0;
1045         candPos.iBase = 0;
1046         candPos.iPhrase = 0;
1047         if ((pystate->findMap.iHZCount - iCount) > 1) {
1048             for (candPos.iPYFA = 0; candPos.iPYFA < pystate->iPYFACount; candPos.iPYFA++) {
1049                 if (!Cmp2Map(pyconfig, PYFAList[candPos.iPYFA].strMap, str, pystate->bSP)) {
1050                     for (candPos.iBase = 0; candPos.iBase < PYFAList[candPos.iPYFA].iBase; candPos.iBase++) {
1051                         phrase = USER_PHRASE_NEXT(PYFAList[candPos.iPYFA].pyBase[candPos.iBase].userPhrase);
1052                         for (candPos.iPhrase = 0;
1053                                 candPos.iPhrase < PYFAList[candPos.iPYFA].pyBase[candPos.iBase].iUserPhrase; candPos.iPhrase++) {
1054                             val = CmpMap(pyconfig, phrase->strMap, strMap, &iMatchedLength, pystate->bSP);
1055                             if (!val && iMatchedLength == (pystate->findMap.iHZCount - 1) * 2)
1056                                 return;
1057                             if (!val || (val && (strlen(phrase->strMap) == iMatchedLength))) {
1058                                 if (!phraseSelected) {
1059                                     baseSelected = &(PYFAList[candPos.iPYFA].pyBase[candPos.iBase]);
1060                                     pPYFA = &PYFAList[candPos.iPYFA];
1061                                     phraseSelected = phrase;
1062                                 } else if (strlen(phrase->strMap) <= (pystate->findMap.iHZCount - 1) * 2) {
1063                                     if (strlen(phrase->strMap) == strlen(phraseSelected->strMap)) {
1064                                         //先看词频,如果词频一样,再最近优先
1065                                         if ((phrase->iHit > phraseSelected->iHit)
1066                                                 || ((phrase->iHit == phraseSelected->iHit)
1067                                                     && (phrase->iIndex > phraseSelected->iIndex))) {
1068                                             baseSelected = &(PYFAList[candPos.iPYFA].pyBase[candPos.iBase]);
1069                                             pPYFA = &PYFAList[candPos.iPYFA];
1070                                             phraseSelected = phrase;
1071                                         }
1072                                     } else if (strlen(phrase->strMap) > strlen(phraseSelected->strMap)) {
1073                                         baseSelected = &(PYFAList[candPos.iPYFA].pyBase[candPos.iBase]);
1074                                         pPYFA = &PYFAList[candPos.iPYFA];
1075                                         phraseSelected = phrase;
1076                                     }
1077                                 }
1078                             }
1079                             phrase = USER_PHRASE_NEXT(phrase);
1080                         }
1081                     }
1082                 }
1083             }
1084 
1085             for (candPos.iPYFA = 0; candPos.iPYFA < pystate->iPYFACount; candPos.iPYFA++) {
1086                 if (!Cmp2Map(pyconfig, PYFAList[candPos.iPYFA].strMap, str, pystate->bSP)) {
1087                     for (candPos.iBase = 0; candPos.iBase < PYFAList[candPos.iPYFA].iBase; candPos.iBase++) {
1088                         for (candPos.iPhrase = 0;
1089                                 candPos.iPhrase < PYFAList[candPos.iPYFA].pyBase[candPos.iBase].iPhrase; candPos.iPhrase++) {
1090                             val =
1091                                 CmpMap(pyconfig, PYFAList[candPos.iPYFA].pyBase[candPos.iBase].phrase[candPos.iPhrase].strMap,
1092                                        strMap, &iMatchedLength, pystate->bSP);
1093                             if (!val && iMatchedLength == (pystate->findMap.iHZCount - 1) * 2)
1094                                 return;
1095                             if (!val || (val && (strlen(PYFAList[candPos.iPYFA].pyBase[candPos.iBase].phrase[candPos.iPhrase].strMap)
1096                                                  == iMatchedLength))) {
1097                                 if (!phraseSelected) {
1098                                     baseSelected = &(PYFAList[candPos.iPYFA].pyBase[candPos.iBase]);
1099                                     pPYFA = &PYFAList[candPos.iPYFA];
1100                                     phraseSelected = &(PYFAList[candPos.iPYFA].pyBase[candPos.iBase].phrase[candPos.iPhrase]);
1101                                 } else if (strlen(PYFAList[candPos.iPYFA].pyBase[candPos.iBase].phrase[candPos.iPhrase].strMap)
1102                                            <= (pystate->findMap.iHZCount - 1) * 2) {
1103                                     if (strlen(PYFAList[candPos.iPYFA].pyBase[candPos.iBase].phrase[candPos.iPhrase].strMap)
1104                                             == strlen(phraseSelected->strMap)) {
1105                                         //先看词频,如果词频一样,再最近优先
1106                                         if ((PYFAList[candPos.iPYFA].pyBase[candPos.iBase].phrase[candPos.iPhrase].iHit >
1107                                                 phraseSelected->iHit)
1108                                                 ||
1109                                                 ((PYFAList[candPos.iPYFA].pyBase[candPos.iBase].phrase[candPos.iPhrase].iHit ==
1110                                                   phraseSelected->iHit)
1111                                                  &&
1112                                                  (PYFAList[candPos.iPYFA].pyBase[candPos.iBase].phrase[candPos.iPhrase].iIndex >
1113                                                   phraseSelected->iIndex))) {
1114                                             baseSelected = &(PYFAList[candPos.iPYFA].pyBase[candPos.iBase]);
1115                                             pPYFA = &PYFAList[candPos.iPYFA];
1116                                             phraseSelected = &(PYFAList[candPos.iPYFA].pyBase[candPos.iBase].phrase[candPos.iPhrase]);
1117                                         }
1118                                     } else if (strlen(PYFAList[candPos.iPYFA].pyBase[candPos.iBase].phrase[candPos.iPhrase].strMap)
1119                                                > strlen(phraseSelected->strMap)) {
1120                                         baseSelected = &(PYFAList[candPos.iPYFA].pyBase[candPos.iBase]);
1121                                         pPYFA = &PYFAList[candPos.iPYFA];
1122                                         phraseSelected = &(PYFAList[candPos.iPYFA].pyBase[candPos.iBase].phrase[candPos.iPhrase]);
1123                                     }
1124                                 }
1125                             }
1126                         }
1127                     }
1128                 }
1129             }
1130             if (baseSelected) {
1131                 strcat(pystate->strPYAuto, baseSelected->strHZ);
1132                 strcat(pystate->strPYAutoMap, pPYFA->strMap);
1133                 strcat(pystate->strPYAuto, phraseSelected->strPhrase);
1134                 strcat(pystate->strPYAutoMap, phraseSelected->strMap);
1135             }
1136         }
1137 
1138         if (!baseSelected) {
1139             val = -1;
1140             baseSelected = NULL;
1141             for (candPos.iPYFA = 0; candPos.iPYFA < pystate->iPYFACount; candPos.iPYFA++) {
1142                 if (!Cmp2Map(pyconfig, PYFAList[candPos.iPYFA].strMap, str, pystate->bSP)) {
1143                     for (candPos.iBase = 0; candPos.iBase < PYFAList[candPos.iPYFA].iBase; candPos.iBase++) {
1144                         if ((int)
1145                                 (PYFAList[candPos.iPYFA].pyBase[candPos.iBase].iHit) > val) {
1146                             val = PYFAList[candPos.iPYFA].pyBase[candPos.iBase].iHit;
1147                             baseSelected = &(PYFAList[candPos.iPYFA].pyBase[candPos.iBase]);
1148                             pPYFA = &PYFAList[candPos.iPYFA];
1149                         }
1150                     }
1151                 }
1152             }
1153 
1154             if (baseSelected) {
1155                 strcat(pystate->strPYAuto, baseSelected->strHZ);
1156                 strcat(pystate->strPYAutoMap, pPYFA->strMap);
1157             } else {            //出错了
1158                 pystate->strPYAuto[0] = '\0';
1159                 return;
1160             }
1161         }
1162     }
1163 }
1164 
PYGetCandWord(void * arg,FcitxCandidateWord * candWord)1165 INPUT_RETURN_VALUE PYGetCandWord(void* arg, FcitxCandidateWord* candWord)
1166 {
1167     FcitxPinyinState *pystate = (FcitxPinyinState*)arg;
1168     FcitxInputState* input = FcitxInstanceGetInputState(pystate->owner);
1169 
1170     if (candWord->priv == NULL) {
1171         strcpy(FcitxInputStateGetOutputString(input), candWord->strWord);
1172         return IRV_COMMIT_STRING;
1173     }
1174 
1175     char *pBase = NULL, *pPhrase = NULL;
1176     char *pBaseMap = NULL, *pPhraseMap = NULL;
1177     unsigned int *pIndex = NULL;
1178     boolean bAddNewPhrase = true;
1179     int i;
1180     char strHZString[MAX_WORDS_USER_INPUT * UTF8_MAX_LENGTH + 1];
1181     int iLen;
1182     PYFA* PYFAList = pystate->PYFAList;
1183     FcitxInstance* instance = pystate->owner;
1184     PYCandWord* pycandWord = candWord->priv;
1185     FcitxProfile* profile = FcitxInstanceGetProfile(pystate->owner);
1186 
1187     switch (pycandWord->iWhich) {
1188     case PY_CAND_AUTO:
1189         pBase = pystate->strPYAuto;
1190         pBaseMap = pystate->strPYAutoMap;
1191         bAddNewPhrase = (pystate->iPYSelected > 0) || pystate->pyconfig.bPYSaveAutoAsPhrase;
1192         break;
1193     case PY_CAND_BASE:         //是系统单字
1194         pBase = PYFAList[pycandWord->cand.base.iPYFA].pyBase[pycandWord->cand.base.iBase].strHZ;
1195         pBaseMap = PYFAList[pycandWord->cand.base.iPYFA].strMap;
1196         pIndex = &(PYFAList[pycandWord->cand.base.iPYFA].pyBase[pycandWord->cand.base.iBase].iIndex);
1197         PYFAList[pycandWord->cand.base.iPYFA].pyBase[pycandWord->cand.base.iBase].iHit++;
1198         pystate->iOrderCount++;
1199         break;
1200     case PY_CAND_USERPHRASE:   //是用户词组
1201         pystate->iNewPYPhraseCount++; // fall through
1202     case PY_CAND_SYSPHRASE:    //是系统词组
1203         pBase = PYFAList[pycandWord->cand.phrase.iPYFA].pyBase[pycandWord->cand.phrase.iBase].strHZ;
1204         pBaseMap = PYFAList[pycandWord->cand.phrase.iPYFA].strMap;
1205         pPhrase = pycandWord->cand.phrase.phrase->strPhrase;
1206         pPhraseMap = pycandWord->cand.phrase.phrase->strMap;
1207         pIndex = &(pycandWord->cand.phrase.phrase->iIndex);
1208         pycandWord->cand.phrase.phrase->iHit++;
1209         pystate->iOrderCount++;
1210         break;
1211     case PY_CAND_FREQ:         //是常用字
1212         pBase = pycandWord->cand.freq.hz->strHZ;
1213         pBaseMap = PYFAList[pycandWord->cand.freq.hz->iPYFA].strMap;
1214         pycandWord->cand.freq.hz->iHit++;
1215         pIndex = &(pycandWord->cand.freq.hz->iIndex);
1216         pystate->iNewFreqCount++;
1217         break;
1218     case PY_CAND_REMIND: {
1219         strcpy(pystate->strPYRemindSource, pycandWord->cand.remind.phrase->strPhrase + pycandWord->cand.remind.iLength);
1220         strcpy(pystate->strPYRemindMap, pycandWord->cand.remind.phrase->strMap + pycandWord->cand.remind.iLength);
1221         pBase = pystate->strPYRemindSource;
1222         strcpy(FcitxInputStateGetOutputString(input), pBase);
1223         FcitxCandidateWordReset(FcitxInputStateGetCandidateList(input));
1224         INPUT_RETURN_VALUE retVal = PYGetRemindCandWords(pystate);
1225         if (retVal == IRV_DISPLAY_CANDWORDS)
1226             return IRV_COMMIT_STRING_REMIND;
1227         else
1228             return IRV_COMMIT_STRING;
1229     }
1230     }
1231 
1232     if (pIndex && (*pIndex != pystate->iCounter))
1233         *pIndex = ++pystate->iCounter;
1234     if (pystate->iOrderCount >= AUTOSAVE_ORDER_COUNT) {
1235         SavePYIndex(pystate);
1236     }
1237     if (pystate->iNewFreqCount >= AUTOSAVE_FREQ_COUNT) {
1238         SavePYFreq(pystate);
1239     }
1240 
1241     strcpy(strHZString, pBase);
1242     if (pPhrase)
1243         strcat(strHZString, pPhrase);
1244     iLen = fcitx_utf8_strlen(strHZString);
1245     if (iLen == pystate->findMap.iHZCount) {
1246         pystate->strPYAuto[0] = '\0';
1247         for (iLen = 0; iLen < pystate->iPYSelected; iLen++)
1248             strcat(pystate->strPYAuto, pystate->pySelected[iLen].strHZ);
1249         strcat(pystate->strPYAuto, strHZString);
1250         ParsePY(&pystate->pyconfig, FcitxInputStateGetRawInputBuffer(input), &pystate->findMap, PY_PARSE_INPUT_USER, pystate->bSP);
1251         strHZString[0] = '\0';
1252         for (i = 0; i < pystate->iPYSelected; i++)
1253             strcat(strHZString, pystate->pySelected[i].strMap);
1254         if (pBaseMap)
1255             strcat(strHZString, pBaseMap);
1256         if (pPhraseMap)
1257             strcat(strHZString, pPhraseMap);
1258         if (bAddNewPhrase && (fcitx_utf8_strlen(pystate->strPYAuto) <= (MAX_PY_PHRASE_LENGTH)))
1259             PYAddUserPhrase(pystate, pystate->strPYAuto, strHZString, false);
1260         FcitxInstanceCleanInputWindow(instance);
1261         strcpy(FcitxInputStateGetOutputString(input), pystate->strPYAuto);
1262         if (profile->bUseRemind) {
1263             FcitxInputStateGetRawInputBuffer(input)[0] = '\0';
1264             FcitxInputStateSetRawInputBufferSize(input, 0);
1265             strcpy(pystate->strPYRemindSource, pystate->strPYAuto);
1266             strcpy(pystate->strPYRemindMap, strHZString);
1267             INPUT_RETURN_VALUE retVal = PYGetRemindCandWords(pystate);
1268 
1269             if (retVal != IRV_TO_PROCESS) {
1270                 pystate->iPYInsertPoint = 0;
1271                 pystate->strFindString[0] = '\0';
1272 
1273                 return IRV_COMMIT_STRING_REMIND;
1274             }
1275         }
1276 
1277         return IRV_COMMIT_STRING;
1278     }
1279     //此时进入自造词状态
1280     pystate->pySelected[pystate->iPYSelected].strPY[0] = '\0';
1281     pystate->pySelected[pystate->iPYSelected].strMap[0] = '\0';
1282     for (i = 0; i < iLen; i++)
1283         strcat(pystate->pySelected[pystate->iPYSelected].strPY, pystate->findMap.strPYParsed[i]);
1284     if (pBaseMap)
1285         strcat(pystate->pySelected[pystate->iPYSelected].strMap, pBaseMap);
1286     if (pPhraseMap)
1287         strcat(pystate->pySelected[pystate->iPYSelected].strMap, pPhraseMap);
1288     strcpy(pystate->pySelected[pystate->iPYSelected].strHZ, strHZString);
1289     pystate->iPYSelected++;
1290     pystate->strFindString[0] = '\0';
1291     for (; i < pystate->findMap.iHZCount; i++)
1292         strcat(pystate->strFindString, pystate->findMap.strPYParsed[i]);
1293     DoPYInput(pystate, 0, 0);
1294     pystate->iPYInsertPoint = strlen(pystate->strFindString);
1295 
1296     CalculateCursorPosition(pystate);
1297     return IRV_DISPLAY_CANDWORDS;
1298 }
1299 
PYGetPhraseCandWords(FcitxPinyinState * pystate)1300 void PYGetPhraseCandWords(FcitxPinyinState* pystate)
1301 {
1302     PYCandIndex candPos;
1303     char str[3];
1304     int val, iMatchedLength;
1305     char strMap[MAX_WORDS_USER_INPUT * 2 + 1];
1306     PyPhrase *phrase;
1307     PYFA* PYFAList = pystate->PYFAList;
1308     FcitxPinyinConfig* pyconfig = &pystate->pyconfig;
1309     FcitxInputState* input = FcitxInstanceGetInputState(pystate->owner);
1310 
1311     if (pystate->findMap.iHZCount == 1)
1312         return;
1313 
1314     UT_array candtemp;
1315     utarray_init(&candtemp, fcitx_ptr_icd);
1316 
1317     str[0] = pystate->findMap.strMap[0][0];
1318     str[1] = pystate->findMap.strMap[0][1];
1319     str[2] = '\0';
1320     strMap[0] = '\0';
1321     for (val = 1; val < pystate->findMap.iHZCount; val++)
1322         strcat(strMap, pystate->findMap.strMap[val]);
1323     for (candPos.iPYFA = 0; candPos.iPYFA < pystate->iPYFACount; candPos.iPYFA++) {
1324         if (!Cmp2Map(pyconfig, PYFAList[candPos.iPYFA].strMap, str, pystate->bSP)) {
1325             for (candPos.iBase = 0; candPos.iBase < PYFAList[candPos.iPYFA].iBase; candPos.iBase++) {
1326                 phrase = USER_PHRASE_NEXT(PYFAList[candPos.iPYFA].pyBase[candPos.iBase].userPhrase);
1327                 for (candPos.iPhrase = 0;
1328                         candPos.iPhrase < PYFAList[candPos.iPYFA].pyBase[candPos.iBase].iUserPhrase; candPos.iPhrase++) {
1329                     val = CmpMap(pyconfig, phrase->strMap, strMap, &iMatchedLength, pystate->bSP);
1330                     if (!val || (val && (strlen(phrase->strMap) == iMatchedLength))) {
1331                         PYCandWord *pycandWord = fcitx_utils_new(PYCandWord);
1332                         PYAddPhraseCandWord(pystate, candPos, phrase, false, pycandWord);
1333                         utarray_push_back(&candtemp, &pycandWord);
1334                     }
1335 
1336                     phrase = USER_PHRASE_NEXT(phrase);
1337                 }
1338             }
1339         }
1340     }
1341 
1342     for (candPos.iPYFA = 0; candPos.iPYFA < pystate->iPYFACount; candPos.iPYFA++) {
1343         if (!Cmp2Map(pyconfig, PYFAList[candPos.iPYFA].strMap, str, pystate->bSP)) {
1344             for (candPos.iBase = 0; candPos.iBase < PYFAList[candPos.iPYFA].iBase; candPos.iBase++) {
1345                 for (candPos.iPhrase = 0; candPos.iPhrase < PYFAList[candPos.iPYFA].pyBase[candPos.iBase].iPhrase; candPos.iPhrase++) {
1346                     val = CmpMap(
1347                               pyconfig,
1348                               PYFAList[candPos.iPYFA].pyBase[candPos.iBase].phrase[candPos.iPhrase].strMap,
1349                               strMap,
1350                               &iMatchedLength,
1351                               pystate->bSP);
1352                     if (!val ||
1353                             (val && (strlen(PYFAList[candPos.iPYFA].pyBase[candPos.iBase].phrase[candPos.iPhrase].strMap) == iMatchedLength))) {
1354                         PYCandWord* pycandWord = fcitx_utils_new(PYCandWord);
1355                         PYAddPhraseCandWord(pystate, candPos, &(PYFAList[candPos.iPYFA].pyBase[candPos.iBase].phrase[candPos.iPhrase]), true, pycandWord);
1356                         utarray_push_back(&candtemp, &pycandWord);
1357                     }
1358                 }
1359             }
1360         }
1361     }
1362 
1363     PYCandWordSortContext context;
1364     context.order = pystate->pyconfig.phraseOrder;
1365     context.type = PY_CAND_SYSPHRASE;
1366     context.pystate = pystate;
1367     if (context.order != AD_NO)
1368         utarray_msort_r(&candtemp, PYCandWordCmp, &context);
1369 
1370     PYCandWord** pcand = NULL;
1371     for (pcand = (PYCandWord**) utarray_front(&candtemp);
1372             pcand != NULL;
1373             pcand = (PYCandWord**) utarray_next(&candtemp, pcand)) {
1374         FcitxCandidateWord candWord;
1375         candWord.callback = PYGetCandWord;
1376         candWord.owner = pystate;
1377         candWord.priv = *pcand;
1378         candWord.strExtra = NULL;
1379         candWord.strWord = NULL;
1380         if ((*pcand)->iWhich == PY_CAND_USERPHRASE)
1381             candWord.wordType = MSG_USERPHR;
1382         else
1383             candWord.wordType = MSG_OTHER;
1384         const char* pBase = PYFAList[(*pcand)->cand.phrase.iPYFA].pyBase[(*pcand)->cand.phrase.iBase].strHZ;
1385         const char* pPhrase = (*pcand)->cand.phrase.phrase->strPhrase;
1386         fcitx_utils_alloc_cat_str(candWord.strWord, pBase, pPhrase);
1387         FcitxCandidateWordAppend(FcitxInputStateGetCandidateList(input),
1388                                  &candWord);
1389     }
1390 
1391     utarray_done(&candtemp);
1392 }
1393 
1394 /*
1395  * 将一个词加入到候选列表的合适位置中
1396  * b = true 表示是系统词组,false表示是用户词组
1397  */
PYAddPhraseCandWord(FcitxPinyinState * pystate,PYCandIndex pos,PyPhrase * phrase,boolean b,PYCandWord * pycandword)1398 boolean PYAddPhraseCandWord(FcitxPinyinState* pystate, PYCandIndex pos, PyPhrase * phrase, boolean b, PYCandWord* pycandword)
1399 {
1400     PYFA* PYFAList = pystate->PYFAList;
1401     char str[MAX_WORDS_USER_INPUT * UTF8_MAX_LENGTH + 1];
1402 
1403     strcpy(str, PYFAList[pos.iPYFA].pyBase[pos.iBase].strHZ);
1404     strcat(str, phrase->strPhrase);
1405     if (pystate->strPYAuto[0]) {
1406         if (strcmp(pystate->strPYAuto, str) == 0) {
1407             return false;
1408         }
1409     }
1410 
1411     pycandword->iWhich = (b) ? PY_CAND_SYSPHRASE : PY_CAND_USERPHRASE;
1412     pycandword->cand.phrase.phrase = phrase;
1413     pycandword->cand.phrase.iPYFA = pos.iPYFA;
1414     pycandword->cand.phrase.iBase = pos.iBase;
1415     return true;
1416 }
1417 
1418 //*****************************************************
1419 
PYGetBaseCandWords(FcitxPinyinState * pystate,PyFreq * pCurFreq)1420 void PYGetBaseCandWords(FcitxPinyinState* pystate, PyFreq* pCurFreq)
1421 {
1422     PYCandIndex candPos = {
1423         0, 0, 0
1424     };
1425     char str[3];
1426     PYFA* PYFAList = pystate->PYFAList;
1427     FcitxPinyinConfig* pyconfig = &pystate->pyconfig;
1428     FcitxInputState* input = FcitxInstanceGetInputState(pystate->owner);
1429     UT_array candtemp;
1430     utarray_init(&candtemp, fcitx_ptr_icd);
1431 
1432     str[0] = pystate->findMap.strMap[0][0];
1433     str[1] = pystate->findMap.strMap[0][1];
1434     str[2] = '\0';
1435     for (candPos.iPYFA = 0; candPos.iPYFA < pystate->iPYFACount; candPos.iPYFA++) {
1436         if (!Cmp2Map(pyconfig, PYFAList[candPos.iPYFA].strMap, str, pystate->bSP)) {
1437             for (candPos.iBase = 0; candPos.iBase < PYFAList[candPos.iPYFA].iBase; candPos.iBase++) {
1438                 if (!PYIsInFreq(pCurFreq, PYFAList[candPos.iPYFA].pyBase[candPos.iBase].strHZ)) {
1439                     PYCandWord *pycandWord = fcitx_utils_new(PYCandWord);
1440                     PYAddBaseCandWord(candPos, pycandWord);
1441                     utarray_push_back(&candtemp, &pycandWord);
1442                 }
1443             }
1444         }
1445     }
1446 
1447     PYCandWordSortContext context;
1448     context.order = pystate->pyconfig.baseOrder;
1449     context.type = PY_CAND_BASE;
1450     context.pystate = pystate;
1451     if (context.order != AD_NO)
1452         utarray_msort_r(&candtemp, PYCandWordCmp, &context);
1453 
1454     PYCandWord** pcand = NULL;
1455     for (pcand = (PYCandWord**) utarray_front(&candtemp);
1456             pcand != NULL;
1457             pcand = (PYCandWord**) utarray_next(&candtemp, pcand)) {
1458         FcitxCandidateWord candWord;
1459         candWord.callback = PYGetCandWord;
1460         candWord.owner = pystate;
1461         candWord.priv = *pcand;
1462         candWord.strExtra = NULL;
1463         candWord.strWord = strdup(PYFAList[(*pcand)->cand.base.iPYFA].pyBase[(*pcand)->cand.base.iBase].strHZ);
1464         candWord.wordType = MSG_OTHER;
1465 
1466         FcitxCandidateWordAppend(FcitxInputStateGetCandidateList(input), &candWord);
1467     }
1468 
1469     utarray_done(&candtemp);
1470 }
1471 
1472 /*
1473  * 将一个字加入到候选列表的合适位置中
1474  */
PYAddBaseCandWord(PYCandIndex pos,PYCandWord * pycandWord)1475 void PYAddBaseCandWord(PYCandIndex pos, PYCandWord* pycandWord)
1476 {
1477     pycandWord->iWhich = PY_CAND_BASE;
1478     pycandWord->cand.base.iPYFA = pos.iPYFA;
1479     pycandWord->cand.base.iBase = pos.iBase;
1480 }
1481 
PYGetFreqCandWords(FcitxPinyinState * pystate,PyFreq * pCurFreq)1482 void PYGetFreqCandWords(FcitxPinyinState* pystate, PyFreq* pCurFreq)
1483 {
1484     int i;
1485     HZ *hz;
1486     UT_array candtemp;
1487     FcitxInputState* input = FcitxInstanceGetInputState(pystate->owner);
1488     utarray_init(&candtemp, fcitx_ptr_icd);
1489 
1490     if (pCurFreq) {
1491         hz = pCurFreq->HZList->next;
1492         for (i = 0; i < pCurFreq->iCount; i++) {
1493             PYCandWord *pycandWord = fcitx_utils_new(PYCandWord);
1494             PYAddFreqCandWord(pCurFreq, hz, pCurFreq->strPY, pycandWord);
1495             utarray_push_back(&candtemp, &pycandWord);
1496             hz = hz->next;
1497         }
1498     }
1499 
1500     PYCandWordSortContext context;
1501     context.order = pystate->pyconfig.freqOrder;
1502     context.type = PY_CAND_FREQ;
1503     context.pystate = pystate;
1504     if (context.order != AD_NO)
1505         utarray_msort_r(&candtemp, PYCandWordCmp, &context);
1506 
1507     PYCandWord** pcand = NULL;
1508     for (pcand = (PYCandWord**) utarray_front(&candtemp);
1509             pcand != NULL;
1510             pcand = (PYCandWord**) utarray_next(&candtemp, pcand)) {
1511         FcitxCandidateWord candWord;
1512         candWord.callback = PYGetCandWord;
1513         candWord.owner = pystate;
1514         candWord.priv = *pcand;
1515         candWord.strExtra = NULL;
1516         candWord.strWord = strdup((*pcand)->cand.freq.hz->strHZ);
1517         candWord.wordType = MSG_USERPHR;
1518 
1519         FcitxCandidateWordAppend(FcitxInputStateGetCandidateList(input), &candWord);
1520     }
1521 
1522     utarray_done(&candtemp);
1523 }
1524 
1525 /*
1526  * 将一个常用字加入到候选列表的合适位置中
1527  */
PYAddFreqCandWord(PyFreq * pyFreq,HZ * hz,char * strPY,PYCandWord * pycandWord)1528 void PYAddFreqCandWord(PyFreq* pyFreq, HZ * hz, char *strPY, PYCandWord* pycandWord)
1529 {
1530     pycandWord->iWhich = PY_CAND_FREQ;
1531     pycandWord->cand.freq.hz = hz;
1532     pycandWord->cand.freq.strPY = strPY;
1533     pycandWord->cand.freq.pyFreq = pyFreq;
1534 }
1535 
1536 /*
1537  * 将一个词组保存到用户词组库中
1538  * 返回true表示是新词组
1539  */
PYAddUserPhrase(FcitxPinyinState * pystate,const char * phrase,const char * map,boolean incHit)1540 boolean PYAddUserPhrase(FcitxPinyinState* pystate, const char *phrase, const char *map, boolean incHit)
1541 {
1542     PyUsrPhrase *userPhrase, *newPhrase, *temp;
1543     char str[UTF8_MAX_LENGTH + 1];
1544     int i, j, k, iTemp;
1545     int clen;
1546     PYFA* PYFAList = pystate->PYFAList;
1547     FcitxPinyinConfig* pyconfig = &pystate->pyconfig;
1548 
1549     //如果短于两个汉字,则不能组成词组
1550     if (fcitx_utf8_strlen(phrase) < 2)
1551         return false;
1552     str[0] = map[0];
1553     str[1] = map[1];
1554     str[2] = '\0';
1555     i = GetBaseMapIndex(pystate, str);
1556 
1557     clen = fcitx_utf8_char_len(phrase);
1558     strncpy(str, phrase, clen);
1559     str[clen] = '\0';
1560     j = GetBaseIndex(pystate, i, str);;
1561     //判断该词组是否已经在库中
1562     //首先,看它是不是在用户词组库中
1563     userPhrase = PYFAList[i].pyBase[j].userPhrase->next;
1564     for (k = 0; k < PYFAList[i].pyBase[j].iUserPhrase; k++) {
1565         if (!strcmp(map + 2, userPhrase->phrase.strMap)
1566                 && !strcmp(phrase + clen, userPhrase->phrase.strPhrase)) {
1567             if (incHit) {
1568                 userPhrase->phrase.iHit ++;
1569                 userPhrase->phrase.iIndex = ++pystate->iCounter;
1570             }
1571             return false;
1572         }
1573         userPhrase = userPhrase->next;
1574     }
1575 
1576     //然后,看它是不是在系统词组库中
1577     for (k = 0; k < PYFAList[i].pyBase[j].iPhrase; k++)
1578         if (!strcmp(map + 2, PYFAList[i].pyBase[j].phrase[k].strMap)
1579                 && !strcmp(phrase + clen, PYFAList[i].pyBase[j].phrase[k].strPhrase)) {
1580             if (incHit) {
1581                 PYFAList[i].pyBase[j].phrase[k].iHit ++;
1582                 PYFAList[i].pyBase[j].phrase[k].iIndex = ++pystate->iCounter;
1583             }
1584             return false;
1585         }
1586     //下面将词组添加到列表中
1587     newPhrase = fcitx_utils_new(PyUsrPhrase);
1588     newPhrase->phrase.strMap = (char*)fcitx_utils_malloc0(sizeof(char) * strlen(map + 2) + 1);
1589     newPhrase->phrase.strPhrase = (char*)fcitx_utils_malloc0(sizeof(char) * strlen(phrase + clen) + 1);
1590     strcpy(newPhrase->phrase.strMap, map + 2);
1591     strcpy(newPhrase->phrase.strPhrase, phrase + clen);
1592     newPhrase->phrase.iIndex = ++pystate->iCounter;
1593     newPhrase->phrase.iHit = 1;
1594     temp = PYFAList[i].pyBase[j].userPhrase;
1595     userPhrase = PYFAList[i].pyBase[j].userPhrase->next;
1596     for (k = 0; k < PYFAList[i].pyBase[j].iUserPhrase; k++) {
1597         if (CmpMap(pyconfig, map + 2, userPhrase->phrase.strMap, &iTemp, pystate->bSP) > 0)
1598             break;
1599         temp = userPhrase;
1600         userPhrase = userPhrase->next;
1601     }
1602 
1603     newPhrase->next = temp->next;
1604     temp->next = newPhrase;
1605     PYFAList[i].pyBase[j].iUserPhrase++;
1606     pystate->iNewPYPhraseCount++;
1607     if (pystate->iNewPYPhraseCount >= AUTOSAVE_PHRASE_COUNT) {
1608         SavePYUserPhrase(pystate);
1609     }
1610 
1611     return true;
1612 }
1613 
PYDelUserPhrase(FcitxPinyinState * pystate,int32_t iPYFA,int iBase,PyUsrPhrase * phrase)1614 void PYDelUserPhrase(FcitxPinyinState* pystate, int32_t iPYFA, int iBase, PyUsrPhrase * phrase)
1615 {
1616     PyUsrPhrase *temp;
1617     PYFA* PYFAList = pystate->PYFAList;
1618 
1619     //首先定位该词组
1620     temp = PYFAList[iPYFA].pyBase[iBase].userPhrase;
1621     while (temp) {
1622         if (temp->next == phrase)
1623             break;
1624         temp = temp->next;
1625     }
1626     if (!temp)
1627         return;
1628     temp->next = phrase->next;
1629     free(phrase->phrase.strPhrase);
1630     free(phrase->phrase.strMap);
1631     free(phrase);
1632     PYFAList[iPYFA].pyBase[iBase].iUserPhrase--;
1633     pystate->iNewPYPhraseCount++;
1634     if (pystate->iNewPYPhraseCount >= AUTOSAVE_PHRASE_COUNT) {
1635         SavePYUserPhrase(pystate);
1636     }
1637 }
1638 
GetBaseMapIndex(FcitxPinyinState * pystate,char * strMap)1639 int GetBaseMapIndex(FcitxPinyinState* pystate, char *strMap)
1640 {
1641     int i;
1642 
1643     for (i = 0; i < pystate->iPYFACount; i++) {
1644         if (!strcmp(strMap, pystate->PYFAList[i].strMap))
1645             return i;
1646     }
1647     return -1;
1648 }
1649 
1650 /*
1651  * 保存用户词库
1652  */
SavePYUserPhrase(FcitxPinyinState * pystate)1653 void SavePYUserPhrase(FcitxPinyinState* pystate)
1654 {
1655     int j, k;
1656     int32_t i, iTemp;
1657     char *tempfile, *pstr;
1658     FILE *fp;
1659     PyPhrase *phrase;
1660     PYFA* PYFAList = pystate->PYFAList;
1661     int fd;
1662 
1663     FcitxXDGGetFileUserWithPrefix("pinyin", "", "w", NULL);
1664     FcitxXDGGetFileUserWithPrefix("pinyin", PINYIN_TEMP_FILE, NULL, &tempfile);
1665     fd = mkstemp(tempfile);
1666     fp = NULL;
1667 
1668     if (fd > 0)
1669         fp = fdopen(fd, "w");
1670 
1671     if (!fp) {
1672         FcitxLog(ERROR, _("Cannot Save User Pinyin Database: %s"), tempfile);
1673         free(tempfile);
1674         return;
1675     }
1676 
1677     for (i = 0; i < pystate->iPYFACount; i++) {
1678         for (j = 0; j < PYFAList[i].iBase; j++) {
1679             iTemp = PYFAList[i].pyBase[j].iUserPhrase;
1680             if (iTemp) {
1681                 char clen;
1682                 fcitx_utils_write_int32(fp, i);
1683                 clen = strlen(PYFAList[i].pyBase[j].strHZ);
1684                 fwrite(&clen, sizeof(char), 1, fp);
1685                 fwrite(PYFAList[i].pyBase[j].strHZ, sizeof(char) * clen, 1, fp);
1686                 fcitx_utils_write_int32(fp, iTemp);
1687                 phrase = USER_PHRASE_NEXT(PYFAList[i].pyBase[j].userPhrase);
1688                 for (k = 0; k < PYFAList[i].pyBase[j].iUserPhrase; k++) {
1689                     iTemp = strlen(phrase->strMap);
1690                     fcitx_utils_write_int32(fp, iTemp);
1691                     fwrite(phrase->strMap, sizeof(char) * iTemp, 1, fp);
1692 
1693                     iTemp = strlen(phrase->strPhrase);
1694                     fcitx_utils_write_int32(fp, iTemp);
1695                     fwrite(phrase->strPhrase, sizeof(char) * iTemp, 1, fp);
1696 
1697                     fcitx_utils_write_uint32(fp, phrase->iIndex);
1698                     fcitx_utils_write_uint32(fp, phrase->iHit);
1699                     phrase = USER_PHRASE_NEXT(phrase);
1700                 }
1701             }
1702         }
1703     }
1704 
1705     fclose(fp);
1706     FcitxXDGGetFileUserWithPrefix("pinyin", PY_USERPHRASE_FILE, NULL, &pstr);
1707     if (access(pstr, 0))
1708         unlink(pstr);
1709     rename(tempfile, pstr);
1710     free(pstr);
1711     free(tempfile);
1712     pystate->iNewPYPhraseCount = 0;
1713 }
1714 
SavePYFreq(FcitxPinyinState * pystate)1715 void SavePYFreq(FcitxPinyinState *pystate)
1716 {
1717     int32_t i;
1718     int k;
1719     char *pstr;
1720     char *tempfile;
1721     FILE *fp;
1722     PyFreq *pPyFreq;
1723     HZ *hz;
1724     int fd;
1725 
1726     FcitxXDGGetFileUserWithPrefix("pinyin", "", "w", NULL);
1727     FcitxXDGGetFileUserWithPrefix("pinyin", PINYIN_TEMP_FILE, NULL, &tempfile);
1728     fd = mkstemp(tempfile);
1729     fp = NULL;
1730 
1731     if (fd > 0)
1732         fp = fdopen(fd, "w");
1733 
1734     if (!fp) {
1735         FcitxLog(ERROR, _("Cannot Save Frequent word: %s"), tempfile);
1736         free(tempfile);
1737         return;
1738     }
1739 
1740     i = 0;
1741     pPyFreq = pystate->pyFreq->next;
1742     while (pPyFreq) {
1743         i++;
1744         pPyFreq = pPyFreq->next;
1745     }
1746     fcitx_utils_write_int32(fp, i);
1747     pPyFreq = pystate->pyFreq->next;
1748     while (pPyFreq) {
1749         fwrite(pPyFreq->strPY, sizeof(char) * 11, 1, fp);
1750         fcitx_utils_write_int32(fp, pPyFreq->iCount);
1751         hz = pPyFreq->HZList->next;
1752         for (k = 0; k < pPyFreq->iCount; k++) {
1753             char slen = strlen(hz->strHZ);
1754             fwrite(&slen, sizeof(char), 1, fp);
1755             fwrite(hz->strHZ, sizeof(char) * slen, 1, fp);
1756             fcitx_utils_write_int32(fp, hz->iPYFA);
1757             fcitx_utils_write_uint32(fp, hz->iHit);
1758             fcitx_utils_write_int32(fp, hz->iIndex);
1759 
1760             hz = hz->next;
1761         }
1762         pPyFreq = pPyFreq->next;
1763     }
1764 
1765     fclose(fp);
1766 
1767     FcitxXDGGetFileUserWithPrefix("pinyin", PY_FREQ_FILE, NULL, &pstr);
1768     if (access(pstr, 0))
1769         unlink(pstr);
1770     rename(tempfile, pstr);
1771 
1772     free(pstr);
1773     free(tempfile);
1774     pystate->iNewFreqCount = 0;
1775 }
1776 
1777 /*
1778  * 保存索引文件
1779  */
SavePYIndex(FcitxPinyinState * pystate)1780 void SavePYIndex(FcitxPinyinState *pystate)
1781 {
1782     int32_t i, j, k;
1783     char *pstr;
1784     char *tempfile;
1785     FILE *fp;
1786     PYFA* PYFAList = pystate->PYFAList;
1787     int fd;
1788 
1789     FcitxXDGGetFileUserWithPrefix("pinyin", "", "w", NULL);
1790     FcitxXDGGetFileUserWithPrefix("pinyin", PINYIN_TEMP_FILE, NULL, &tempfile);
1791     fd = mkstemp(tempfile);
1792     fp = NULL;
1793 
1794     if (fd > 0)
1795         fp = fdopen(fd, "w");
1796 
1797     if (!fp) {
1798         FcitxLog(ERROR, _("Cannot Save Pinyin Index: %s"), tempfile);
1799         free(tempfile);
1800         return;
1801     }
1802 
1803     fcitx_utils_write_uint32(fp, PY_INDEX_MAGIC_NUMBER);
1804 
1805     //Save Counter
1806     fcitx_utils_write_uint32(fp, pystate->iCounter);
1807     //先保存索引不为0的单字
1808     k = -1;
1809     for (i = 0; i < pystate->iPYFACount; i++) {
1810         for (j = 0; j < PYFAList[i].iBase; j++) {
1811             if (PYFAList[i].pyBase[j].iIndex > pystate->iOrigCounter) {
1812                 fcitx_utils_write_int32(fp, i);
1813                 fcitx_utils_write_int32(fp, j);
1814                 fcitx_utils_write_int32(fp, k);
1815                 fcitx_utils_write_uint32(fp, PYFAList[i].pyBase[j].iIndex);
1816                 fcitx_utils_write_uint32(fp, PYFAList[i].pyBase[j].iHit);
1817             }
1818         }
1819     }
1820 
1821     //再保存索引不为0的系统词组
1822     for (i = 0; i < pystate->iPYFACount; i++) {
1823         for (j = 0; j < PYFAList[i].iBase; j++) {
1824             for (k = 0; k < PYFAList[i].pyBase[j].iPhrase; k++) {
1825                 if (PYFAList[i].pyBase[j].phrase[k].iIndex
1826                     > pystate->iOrigCounter) {
1827                     fcitx_utils_write_int32(fp, i);
1828                     fcitx_utils_write_int32(fp, j);
1829                     fcitx_utils_write_int32(fp, k);
1830                     fcitx_utils_write_uint32(
1831                         fp, PYFAList[i].pyBase[j].phrase[k].iIndex);
1832                     fcitx_utils_write_uint32(
1833                         fp, PYFAList[i].pyBase[j].phrase[k].iHit);
1834                 }
1835             }
1836         }
1837     }
1838 
1839     fclose(fp);
1840 
1841     FcitxXDGGetFileUserWithPrefix("pinyin", PY_INDEX_FILE, NULL, &pstr);
1842     if (access(pstr, 0))
1843         unlink(pstr);
1844     rename(tempfile, pstr);
1845 
1846     free(pstr);
1847     free(tempfile);
1848     pystate->iOrderCount = 0;
1849 }
1850 
1851 /*
1852  * 设置拼音的常用字表
1853  * 只有以下情形才能设置
1854  *  当用户输入单字时
1855  * 至于常用词的问题暂时不考虑
1856  */
PYAddFreq(FcitxPinyinState * pystate,PYCandWord * pycandWord)1857 void PYAddFreq(FcitxPinyinState* pystate, PYCandWord* pycandWord)
1858 {
1859     int i;
1860     HZ *HZTemp;
1861     PyFreq *freq;
1862     HZ *hz;
1863     PYFA* PYFAList = pystate->PYFAList;
1864     PyFreq* pCurFreq = pystate->pyFreq->next;
1865     for (i = 0; i < pystate->iPYFreqCount; i++) {
1866         if (!strcmp(pystate->strFindString, pCurFreq->strPY))
1867             break;
1868         pCurFreq = pCurFreq->next;
1869     }
1870 
1871     //能到这儿来,就说明候选列表中都是单字
1872     //首先,看这个字是不是已经在常用字表中
1873     i = 1;
1874     if (pCurFreq) {
1875         i = -1;
1876         if (pycandWord->iWhich != PY_CAND_FREQ) {
1877             //说明该字是系统单字
1878             HZTemp = pCurFreq->HZList->next;
1879             for (i = 0; i < pCurFreq->iCount; i++) {
1880                 if (!strcmp(PYFAList[pycandWord->cand.base.iPYFA].pyBase[pycandWord->cand.base.iBase].strHZ, HZTemp->strHZ)) {
1881                     i = -1;
1882                     break;
1883                 }
1884                 HZTemp = HZTemp->next;
1885             }
1886         }
1887     }
1888     //借用i来指示是否需要添加新的常用字
1889     if (i < 0)
1890         return;
1891     //需要添加该字,此时该字必然是系统单字
1892     if (!pCurFreq) {
1893         freq = fcitx_utils_new(PyFreq);
1894         freq->HZList = fcitx_utils_new(HZ);
1895         freq->HZList->next = NULL;
1896         strcpy(freq->strPY, pystate->strFindString);
1897         freq->next = NULL;
1898         freq->iCount = 0;
1899         pCurFreq = pystate->pyFreq;
1900         for (i = 0; i < pystate->iPYFreqCount; i++)
1901             pCurFreq = pCurFreq->next;
1902         pCurFreq->next = freq;
1903         pystate->iPYFreqCount++;
1904         pCurFreq = freq;
1905     }
1906 
1907     HZTemp = fcitx_utils_new(HZ);
1908     strcpy(HZTemp->strHZ, PYFAList[pycandWord->cand.base.iPYFA].pyBase[pycandWord->cand.base.iBase].strHZ);
1909     HZTemp->iPYFA = pycandWord->cand.base.iPYFA;
1910     HZTemp->iHit = 0;
1911     HZTemp->iIndex = 0;
1912     HZTemp->next = NULL;
1913     //将HZTemp加到链表尾部
1914     hz = pCurFreq->HZList;
1915     for (i = 0; i < pCurFreq->iCount; i++)
1916         hz = hz->next;
1917     hz->next = HZTemp;
1918     pCurFreq->iCount++;
1919     pystate->iNewFreqCount++;
1920     if (pystate->iNewFreqCount >= AUTOSAVE_FREQ_COUNT) {
1921         SavePYFreq(pystate);
1922     }
1923 }
1924 
1925 /*
1926  * 删除拼音常用字表中的某个字
1927  */
PYDelFreq(FcitxPinyinState * pystate,PYCandWord * pycandWord)1928 void PYDelFreq(FcitxPinyinState *pystate, PYCandWord* pycandWord)
1929 {
1930     HZ *hz;
1931 
1932     //能到这儿来,就说明候选列表中都是单字
1933     //首先,看这个字是不是已经在常用字表中
1934     if (pycandWord->iWhich != PY_CAND_FREQ)
1935         return;
1936     //先找到需要删除单字的位置
1937     hz = pycandWord->cand.freq.pyFreq->HZList;
1938     while (hz->next != pycandWord->cand.freq.hz)
1939         hz = hz->next;
1940     hz->next = pycandWord->cand.freq.hz->next;
1941     free(pycandWord->cand.freq.hz);
1942     pycandWord->cand.freq.pyFreq->iCount--;
1943     pystate->iNewFreqCount++;
1944     if (pystate->iNewFreqCount >= AUTOSAVE_FREQ_COUNT) {
1945         SavePYFreq(pystate);
1946     }
1947 }
1948 
1949 /*
1950  * 判断一个字是否已经是常用字
1951  */
PYIsInFreq(PyFreq * pCurFreq,char * strHZ)1952 boolean PYIsInFreq(PyFreq* pCurFreq, char *strHZ)
1953 {
1954     HZ *hz;
1955     int i;
1956 
1957     if (!pCurFreq)
1958         return false;
1959     hz = pCurFreq->HZList->next;
1960     for (i = 0; i < pCurFreq->iCount; i++) {
1961         if (!strcmp(strHZ, hz->strHZ))
1962             return true;
1963         hz = hz->next;
1964     }
1965 
1966     return false;
1967 }
1968 
1969 /*
1970  * 取得拼音的联想字串
1971  *  按照频率来定排列顺序
1972  */
PYGetRemindCandWords(void * arg)1973 INPUT_RETURN_VALUE PYGetRemindCandWords(void *arg)
1974 {
1975     int i, j;
1976     PyPhrase *phrase;
1977     FcitxPinyinState* pystate = (FcitxPinyinState*) arg;
1978     FcitxGlobalConfig* config = FcitxInstanceGetGlobalConfig(pystate->owner);
1979     boolean bDisablePagingInRemind = config->bDisablePagingInRemind;
1980     FcitxInputState *input = FcitxInstanceGetInputState(pystate->owner);
1981     PYFA* PYFAList = pystate->PYFAList;
1982 
1983     if (!pystate->strPYRemindSource[0])
1984         return IRV_TO_PROCESS;
1985 
1986     PyBase* pyBaseForRemind = NULL;
1987     for (i = 0; i < pystate->iPYFACount; i++) {
1988         if (!strncmp(pystate->strPYRemindMap, PYFAList[i].strMap, 2)) {
1989             for (j = 0; j < PYFAList[i].iBase; j++) {
1990                 if (!fcitx_utf8_strncmp(pystate->strPYRemindSource, PYFAList[i].pyBase[j].strHZ, 1)) {
1991                     pyBaseForRemind = &(PYFAList[i].pyBase[j]);
1992                     goto _HIT;
1993                 }
1994             }
1995         }
1996     }
1997 
1998 _HIT:
1999     if (!pyBaseForRemind)
2000         return IRV_TO_PROCESS;
2001 
2002     UT_array candtemp;
2003     utarray_init(&candtemp, fcitx_ptr_icd);
2004 
2005     for (i = 0; i < pyBaseForRemind->iPhrase; i++) {
2006 
2007         if (bDisablePagingInRemind && utarray_len(&candtemp) >= FcitxCandidateWordGetPageSize(FcitxInputStateGetCandidateList(input)))
2008             break;
2009 
2010         if (fcitx_utf8_strlen(pystate->strPYRemindSource) == 1) {
2011             if (fcitx_utf8_strlen(pyBaseForRemind->phrase[i].strPhrase) == 1) {
2012                 PYCandWord *pycandWord = fcitx_utils_new(PYCandWord);
2013                 PYAddRemindCandWord(pystate, &pyBaseForRemind->phrase[i], pycandWord);
2014                 utarray_push_back(&candtemp, &pycandWord);
2015             }
2016         } else if (strlen(pyBaseForRemind->phrase[i].strPhrase) == strlen(pystate->strPYRemindSource)) {
2017             if (!strncmp
2018                     (pystate->strPYRemindSource + fcitx_utf8_char_len(pystate->strPYRemindSource),
2019                      pyBaseForRemind->phrase[i].strPhrase, strlen(pystate->strPYRemindSource + fcitx_utf8_char_len(pystate->strPYRemindSource)))
2020                ) {
2021                 PYCandWord *pycandWord = fcitx_utils_new(PYCandWord);
2022                 PYAddRemindCandWord(pystate, &pyBaseForRemind->phrase[i], pycandWord);
2023                 utarray_push_back(&candtemp, &pycandWord);
2024             }
2025         }
2026     }
2027 
2028     phrase = &pyBaseForRemind->userPhrase->next->phrase;
2029     for (i = 0; i < pyBaseForRemind->iUserPhrase; i++) {
2030         if (bDisablePagingInRemind && utarray_len(&candtemp) >= FcitxCandidateWordGetPageSize(FcitxInputStateGetCandidateList(input)))
2031             break;
2032 
2033         if (fcitx_utf8_strlen(pystate->strPYRemindSource) == 1) {
2034             if (fcitx_utf8_strlen(phrase->strPhrase) == 1) {
2035                 PYCandWord *pycandWord = fcitx_utils_new(PYCandWord);
2036                 PYAddRemindCandWord(pystate, phrase, pycandWord);
2037                 utarray_push_back(&candtemp, &pycandWord);
2038             }
2039         } else if (strlen(phrase->strPhrase) == strlen(pystate->strPYRemindSource)) {
2040             if (!strncmp
2041                     (pystate->strPYRemindSource + fcitx_utf8_char_len(pystate->strPYRemindSource),
2042                      phrase->strPhrase, strlen(pystate->strPYRemindSource + fcitx_utf8_char_len(pystate->strPYRemindSource)))) {
2043                 PYCandWord *pycandWord = fcitx_utils_new(PYCandWord);
2044                 PYAddRemindCandWord(pystate, phrase, pycandWord);
2045                 utarray_push_back(&candtemp, &pycandWord);
2046             }
2047         }
2048 
2049         phrase = USER_PHRASE_NEXT(phrase);
2050     }
2051 
2052     if (utarray_len(&candtemp) == 0) {
2053         utarray_done(&candtemp);
2054         return IRV_TO_PROCESS;
2055     }
2056 
2057     FcitxMessages *msg_up = FcitxInputStateGetAuxUp(input);
2058     FcitxMessagesSetMessageCount(msg_up, 0);
2059     FcitxMessagesAddMessageStringsAtLast(msg_up, MSG_TIPS, _("Remind: "));
2060     FcitxMessagesAddMessageStringsAtLast(msg_up, MSG_INPUT,
2061                                          pystate->strPYRemindSource);
2062 
2063     utarray_foreach(pcand, &candtemp, PYCandWord*) {
2064         FcitxCandidateWord candWord;
2065         candWord.callback = PYGetCandWord;
2066         candWord.owner = pystate;
2067         candWord.priv = *pcand;
2068         candWord.strExtra = NULL;
2069         candWord.strWord = strdup((*pcand)->cand.remind.phrase->strPhrase + (*pcand)->cand.remind.iLength);
2070         candWord.wordType = MSG_OTHER;
2071 
2072         FcitxCandidateWordAppend(FcitxInputStateGetCandidateList(input), &candWord);
2073     }
2074 
2075     utarray_done(&candtemp);
2076 
2077     FcitxInputStateSetIsInRemind(input, (FcitxCandidateWordPageCount(FcitxInputStateGetCandidateList(input)) != 0));
2078     return IRV_DISPLAY_CANDWORDS;
2079 }
2080 
PYAddRemindCandWord(FcitxPinyinState * pystate,PyPhrase * phrase,PYCandWord * pycandWord)2081 void PYAddRemindCandWord(FcitxPinyinState* pystate, PyPhrase * phrase, PYCandWord* pycandWord)
2082 {
2083     PYRemindCandWord* pyRemindCandWords = &pycandWord->cand.remind;
2084 
2085     pycandWord->iWhich = PY_CAND_REMIND;
2086     pyRemindCandWords->phrase = phrase;
2087     pyRemindCandWords->iLength = strlen(pystate->strPYRemindSource) - fcitx_utf8_char_len(pystate->strPYRemindSource);
2088 }
2089 
PYGetPYByHZ(FcitxPinyinState * pystate,const char * strHZ,char * strPY)2090 void PYGetPYByHZ(FcitxPinyinState*pystate, const char *strHZ, char *strPY)
2091 {
2092     int i, j;
2093     char str_PY[MAX_PY_LENGTH + 1];
2094     PYFA* PYFAList = pystate->PYFAList;
2095 
2096     strPY[0] = '\0';
2097     for (i = pystate->iPYFACount - 1; i >= 0; i--) {
2098         if (MapToPY(PYFAList[i].strMap, str_PY)) {
2099             for (j = 0; j < PYFAList[i].iBase; j++) {
2100                 if (!strcmp(PYFAList[i].pyBase[j].strHZ, strHZ)) {
2101                     if (strPY[0])
2102                         strcat(strPY, " ");
2103                     strcat(strPY, str_PY);
2104                 }
2105             }
2106         }
2107     }
2108 }
2109 
SavePY(void * arg)2110 void SavePY(void *arg)
2111 {
2112     FcitxPinyinState *pystate = (FcitxPinyinState*)arg;
2113     if (pystate->iNewPYPhraseCount)
2114         SavePYUserPhrase(pystate);
2115     if (pystate->iOrderCount)
2116         SavePYIndex(pystate);
2117     if (pystate->iNewFreqCount)
2118         SavePYFreq(pystate);
2119 }
2120 
ReloadConfigPY(void * arg)2121 void ReloadConfigPY(void* arg)
2122 {
2123     FcitxPinyinState *pystate = (FcitxPinyinState*)arg;
2124 
2125     LoadPYConfig(&pystate->pyconfig);
2126 }
2127 
PinyinMigration()2128 void PinyinMigration()
2129 {
2130     char* olduserphrase, *oldpyindex, *newuserphrase, *newpyindex;
2131     FcitxXDGGetFileUserWithPrefix("", PY_USERPHRASE_FILE, NULL, &olduserphrase);
2132     FcitxXDGGetFileUserWithPrefix("", PY_INDEX_FILE, NULL, &oldpyindex);
2133     FcitxXDGGetFileUserWithPrefix("pinyin", PY_USERPHRASE_FILE, NULL, &newuserphrase);
2134     FcitxXDGGetFileUserWithPrefix("pinyin", PY_INDEX_FILE, NULL, &newpyindex);
2135 
2136     struct stat olduserphrasestat, oldpyindexstat, newuserphrasestat, newpyindexstat;
2137 
2138     /* check old file are all not exist */
2139     if (stat(newpyindex, &newpyindexstat) == -1 && stat(newuserphrase, &newuserphrasestat) == -1) {
2140         if (stat(oldpyindex, &oldpyindexstat) == 0 || stat(olduserphrase, &olduserphrasestat) == 0) {
2141             FcitxLog(INFO, _("Migrate the old file path to the new one"));
2142             /* there might be a very very rare case, that ~/.config/fcitx/pinyin
2143              * and ~/.config/fcitx in different filesystem, who the fucking guy
2144              * do this meaningless go die */
2145             link(oldpyindex, newpyindex);
2146             link(olduserphrase, newuserphrase);
2147         }
2148     }
2149 
2150 
2151     free(oldpyindex);
2152     free(olduserphrase);
2153     free(newpyindex);
2154     free(newuserphrase);
2155 }
2156 
2157 /* decend sort */
PYCandWordCmp(const void * b,const void * a,void * arg)2158 int PYCandWordCmp(const void* b, const void *a, void* arg)
2159 {
2160     const PYCandWord* canda = *(PYCandWord**)a;
2161     const PYCandWord* candb = *(PYCandWord**)b;
2162     PYCandWordSortContext *context = arg;
2163 
2164     switch (context->type) {
2165     case PY_CAND_BASE: {
2166         switch (context->order) {
2167         case AD_NO:
2168             return 0;
2169         case AD_FAST: {
2170             int delta = context->pystate->PYFAList[canda->cand.base.iPYFA].pyBase[canda->cand.base.iBase].iIndex
2171                         - context->pystate->PYFAList[candb->cand.base.iPYFA].pyBase[candb->cand.base.iBase].iIndex;
2172             if (delta != 0)
2173                 return delta;
2174 
2175             delta = context->pystate->PYFAList[canda->cand.base.iPYFA].pyBase[canda->cand.base.iBase].iHit
2176                     - context->pystate->PYFAList[candb->cand.base.iPYFA].pyBase[candb->cand.base.iBase].iHit;
2177             return delta;
2178         }
2179         break;
2180         case AD_FREQ: {
2181             int delta = context->pystate->PYFAList[canda->cand.base.iPYFA].pyBase[canda->cand.base.iBase].iHit
2182                         - context->pystate->PYFAList[candb->cand.base.iPYFA].pyBase[candb->cand.base.iBase].iHit;
2183             if (delta != 0)
2184                 return delta;
2185 
2186             delta = context->pystate->PYFAList[canda->cand.base.iPYFA].pyBase[canda->cand.base.iBase].iIndex
2187                     - context->pystate->PYFAList[candb->cand.base.iPYFA].pyBase[candb->cand.base.iBase].iIndex;
2188             return delta;
2189         }
2190         break;
2191         }
2192     }
2193     break;
2194     case PY_CAND_SYSPHRASE:
2195     case PY_CAND_USERPHRASE: {
2196         switch (context->order) {
2197         case AD_NO:
2198             return strlen(canda->cand.phrase.phrase->strPhrase) - strlen(candb->cand.phrase.phrase->strPhrase);
2199             break;
2200         case AD_FAST: {
2201             int size = strlen(canda->cand.phrase.phrase->strPhrase) - strlen(candb->cand.phrase.phrase->strPhrase);
2202             if (size != 0)
2203                 return size;
2204 
2205             if (canda->cand.phrase.phrase->iIndex - candb->cand.phrase.phrase->iIndex != 0)
2206                 return canda->cand.phrase.phrase->iIndex - candb->cand.phrase.phrase->iIndex;
2207 
2208             return canda->cand.phrase.phrase->iHit - candb->cand.phrase.phrase->iHit;
2209         }
2210         break;
2211         case AD_FREQ: {
2212             int size = strlen(canda->cand.phrase.phrase->strPhrase) - strlen(candb->cand.phrase.phrase->strPhrase);
2213             if (size != 0)
2214                 return size;
2215 
2216             if (canda->cand.phrase.phrase->iHit - candb->cand.phrase.phrase->iHit != 0)
2217                 return canda->cand.phrase.phrase->iHit - candb->cand.phrase.phrase->iHit;
2218 
2219             return canda->cand.phrase.phrase->iIndex - candb->cand.phrase.phrase->iIndex;
2220         }
2221         break;
2222         }
2223     }
2224     break;
2225     case PY_CAND_FREQ: {
2226         switch (context->order) {
2227         case AD_NO:
2228             return 0;
2229         case AD_FAST:
2230             return canda->cand.freq.hz->iIndex - candb->cand.freq.hz->iIndex;
2231         case AD_FREQ:
2232             return canda->cand.freq.hz->iHit - candb->cand.freq.hz->iHit;
2233         }
2234     }
2235     break;
2236     case PY_CAND_REMIND:
2237         return canda->cand.remind.phrase->iHit - canda->cand.remind.phrase->iHit;
2238     default:
2239         return 0;
2240     }
2241 
2242     return 0;
2243 }
2244 
2245 static char*
PYSP2QP(FcitxPinyinState * pystate,const char * strSP)2246 PYSP2QP(FcitxPinyinState *pystate, const char* strSP)
2247 {
2248     char strQP[MAX_PY_LENGTH + 1] = "";
2249     SP2QP(&pystate->pyconfig, strSP, strQP);
2250     return strdup(strQP);
2251 }
2252 
2253 static boolean
PYGetPYMapByHZ(FcitxPinyinState * pystate,char * strHZ,char * mapHint,char * strMap)2254 PYGetPYMapByHZ(FcitxPinyinState* pystate, char* strHZ,
2255                char* mapHint, char* strMap)
2256 {
2257     int i, j;
2258     PYFA* PYFAList = pystate->PYFAList;
2259 
2260     strMap[0] = '\0';
2261     for (i = pystate->iPYFACount - 1; i >= 0; i--) {
2262         if (!Cmp2Map(&pystate->pyconfig, PYFAList[i].strMap, mapHint, false)) {
2263             for (j = 0; j < PYFAList[i].iBase; j++) {
2264                 if (!strcmp(PYFAList[i].pyBase[j].strHZ, strHZ)) {
2265                     strcpy(strMap, PYFAList[i].strMap);
2266                     return true;
2267                 }
2268             }
2269         }
2270     }
2271     return false;
2272 }
2273 
2274 static void
PYAddUserPhraseFromCString(FcitxPinyinState * pystate,const char * strHZ)2275 PYAddUserPhraseFromCString(FcitxPinyinState *pystate, const char *strHZ)
2276 {
2277     const char *pivot;
2278     char *sp;
2279     char singleHZ[UTF8_MAX_LENGTH + 1];
2280     char strMap[3];
2281     if (!fcitx_utf8_check_string(strHZ))
2282         return;
2283 
2284     pivot = strHZ;
2285     size_t hzCount = fcitx_utf8_strlen(strHZ);
2286     size_t hzCountLocal = 0;
2287 
2288     if (pystate->iPYSelected) {
2289         int i;
2290         for (i = 0 ; i < pystate->iPYSelected; i ++) {
2291             hzCountLocal += strlen(pystate->pySelected[i].strMap) / 2;
2292         }
2293     }
2294     hzCountLocal += pystate->findMap.iHZCount;
2295 
2296     /* in order not to get a wrong one, use strict check */
2297     if (hzCountLocal != hzCount || hzCount > MAX_PY_PHRASE_LENGTH)
2298         return;
2299     char *totalMap = fcitx_utils_malloc0(1 + 2 * hzCount);
2300 
2301     if (pystate->iPYSelected) {
2302         int i;
2303         for (i = 0 ; i < pystate->iPYSelected; i ++)
2304             strcat(totalMap, pystate->pySelected[i].strMap);
2305         strHZ = fcitx_utf8_get_nth_char(strHZ, strlen(totalMap) / 2);
2306     }
2307 
2308     int i = 0;
2309     while (*strHZ) {
2310         uint32_t chr;
2311 
2312         sp = fcitx_utf8_get_char(strHZ, &chr);
2313         size_t len = sp - strHZ;
2314         strncpy(singleHZ, strHZ, len);
2315         singleHZ[len] = '\0';
2316 
2317         if (!PYGetPYMapByHZ(pystate, singleHZ, pystate->findMap.strMap[i],
2318                             strMap)) {
2319             free(totalMap);
2320             return;
2321         }
2322 
2323         strncat(totalMap, strMap, 2);
2324         strHZ = sp;
2325         i ++;
2326     }
2327 
2328     PYAddUserPhrase(pystate, pivot, totalMap, true);
2329     free(totalMap);
2330 }
2331 
2332 #include "fcitx-pinyin-addfunctions.h"
2333