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 
23 #include "table.h"
24 
25 #include <stdio.h>
26 #include <string.h>
27 #include <stdlib.h>
28 #include <libintl.h>
29 #include <errno.h>
30 
31 #include "fcitx/fcitx.h"
32 #include "fcitx/keys.h"
33 #include "fcitx/ui.h"
34 #include "fcitx/profile.h"
35 #include "fcitx-utils/log.h"
36 #include "fcitx/module.h"
37 #include "fcitx/frontend.h"
38 #include "fcitx/candidate.h"
39 #include "fcitx/context.h"
40 #include "fcitx-config/xdg.h"
41 #include "fcitx-utils/utarray.h"
42 #include "fcitx-utils/utils.h"
43 #include "im/pinyin/fcitx-pinyin.h"
44 #include "module/punc/fcitx-punc.h"
45 #include "module/pinyin-enhance/fcitx-pinyin-enhance.h"
46 
47 #define MAX_TABLE_INPUT 50
48 
49 static void TableMetaDataFree(TableMetaData *table);
50 typedef struct {
51     ADJUSTORDER order;
52     int simpleLevel;
53 } TableCandWordSortContext;
54 
55 static void *TableCreate(FcitxInstance* instance);
56 static int TableCandCmp(const void* a, const void* b, void* arg);
57 static INPUT_RETURN_VALUE TableKeyBlocker(void* arg, FcitxKeySym sym, unsigned int state);
58 static INPUT_RETURN_VALUE Table_PYGetCandWord(void *arg,
59                                               FcitxCandidateWord *candidateWord);
60 
61 FCITX_DEFINE_PLUGIN(fcitx_table, ime2, FcitxIMClass2) =  {
62     TableCreate,
63     NULL,
64     ReloadTableConfig,
65     NULL,
66     NULL,
67     NULL,
68     NULL,
69     NULL
70 };
71 
72 static boolean LoadTableConfig(TableConfig* config);
73 static void SaveTableConfig(TableConfig* config);
74 
GetTableMod(TableMetaData * table)75 static inline unsigned int GetTableMod(TableMetaData* table)
76 {
77     static unsigned int cmodtable[] = {
78         FcitxKeyState_None, FcitxKeyState_Alt,
79         FcitxKeyState_Ctrl, FcitxKeyState_Shift
80     };
81     if (fcitx_unlikely(table->chooseModifier >= _CM_COUNT))
82         table->chooseModifier = _CM_COUNT - 1;
83 
84     return cmodtable[table->chooseModifier];
85 }
86 
LoadTableConfig(TableConfig * config)87 boolean LoadTableConfig(TableConfig* config)
88 {
89     FcitxConfigFileDesc* configDesc = GetTableGlobalConfigDesc();
90     if (configDesc == NULL)
91         return false;
92 
93     FILE *fp;
94     fp = FcitxXDGGetFileUserWithPrefix("conf", "fcitx-table.config", "r", NULL);
95     if (!fp) {
96         if (errno == ENOENT)
97             SaveTableConfig(config);
98     }
99 
100     FcitxConfigFile *cfile = FcitxConfigParseConfigFileFp(fp, configDesc);
101 
102     TableConfigConfigBind(config, cfile, configDesc);
103     FcitxConfigBindSync((FcitxGenericConfig*)config);
104 
105     if (fp)
106         fclose(fp);
107 
108     return true;
109 }
110 
SaveTableConfig(TableConfig * config)111 void SaveTableConfig(TableConfig *config)
112 {
113     FCITX_UNUSED(config);
114 }
115 
TableCreate(FcitxInstance * instance)116 void *TableCreate(FcitxInstance* instance)
117 {
118     FcitxTableState *tbl = fcitx_utils_malloc0(sizeof(FcitxTableState));
119     tbl->owner = instance;
120     if (!LoadTableConfig(&tbl->config)) {
121         free(tbl);
122         return NULL;
123     }
124     LoadTableInfo(tbl);
125 
126 
127     return tbl;
128 }
129 
SaveTableIM(void * arg)130 void SaveTableIM(void *arg)
131 {
132     TableMetaData* table = (TableMetaData*) arg;
133 
134     if (!table->tableDict)
135         return;
136     if (table->tableDict && table->tableDict->iTableChanged)
137         SaveTableDict(table);
138 }
139 
TableMetaDataGetName(TableMetaData * table)140 static inline char* TableMetaDataGetName(TableMetaData* table)
141 {
142     return (strlen(table->uniqueName) == 0) ?
143         table->strIconName : table->uniqueName;
144 }
145 
TableMetaDataInsert(TableMetaData ** tableSet,TableMetaData * table)146 static inline void TableMetaDataInsert(TableMetaData** tableSet, TableMetaData* table)
147 {
148     char* name = TableMetaDataGetName(table);
149     HASH_ADD_KEYPTR(hh, *tableSet, name, strlen(name), table);
150 }
151 
TableMetaDataFind(TableMetaData * table,const char * name)152 static inline TableMetaData* TableMetaDataFind(TableMetaData* table, const char* name)
153 {
154     TableMetaData* result = NULL;
155     HASH_FIND_STR(table, name, result);
156     return result;
157 }
158 
TableMetaDataUnlink(TableMetaData ** tableSet,TableMetaData * table)159 static inline void TableMetaDataUnlink(TableMetaData** tableSet, TableMetaData* table)
160 {
161     HASH_DEL(*tableSet, table);
162 }
163 
TableMetaDataRemove(TableMetaData ** tableSet,TableMetaData * table)164 static inline void TableMetaDataRemove(TableMetaData** tableSet, TableMetaData* table)
165 {
166     HASH_DEL(*tableSet, table);
167     TableMetaDataFree(table);
168 }
169 
TableMetaDataRegister(FcitxTableState * tbl,TableMetaData * table)170 static inline void TableMetaDataRegister(FcitxTableState* tbl, TableMetaData* table)
171 {
172     table->status = TABLE_REGISTERED;
173     FcitxInstanceRegisterIM(
174         tbl->owner,
175         table,
176         TableMetaDataGetName(table),
177         table->strName,
178         table->strIconName,
179         TableInit,
180         TableResetStatus,
181         DoTableInput,
182         TableGetCandWords,
183         TablePhraseTips,
184         SaveTableIM,
185         NULL,
186         TableKeyBlocker,
187         table->iPriority,
188         table->langCode
189     );
190 }
191 
TableConfigStealTableName(FcitxConfigFile * cfile)192 static inline char* TableConfigStealTableName(FcitxConfigFile* cfile)
193 {
194     FcitxConfigOption* option = FcitxConfigFileGetOption(cfile, "CodeTable",
195                                                          "UniqueName");
196     if (option && strlen(option->rawValue))
197         return option->rawValue;
198 
199     option = FcitxConfigFileGetOption(cfile, "CodeTable", "IconName");
200     if (option)
201         return option->rawValue;
202     return NULL;
203 }
204 
205 /*
206  * Read table configuration
207  * and returns whether table im is really changed.
208  */
LoadTableInfo(FcitxTableState * tbl)209 boolean LoadTableInfo(FcitxTableState *tbl)
210 {
211     FcitxStringHashSet* sset = NULL;
212     tbl->bTablePhraseTips = false;
213     if (tbl->curLoadedTable) {
214         FreeTableDict(tbl->curLoadedTable);
215         tbl->curLoadedTable = NULL;
216     }
217 
218     sset = FcitxXDGGetFiles("table", NULL, ".conf");
219 
220     {
221         TableMetaData* titer = tbl->tables;
222         while(titer)
223         {
224             titer->status = TABLE_PENDING;
225             titer = titer->hh.next;
226         }
227     }
228 
229     boolean imchanged = false;
230     HASH_FOREACH(string, sset, FcitxStringHashSet) {
231         // FcitxLog(INFO, _("Load Table Config File:%s"), string->name);
232         FILE* fp = FcitxXDGGetFileWithPrefix("table", string->name, "r", NULL);
233         if (!fp) {
234             continue;
235         }
236         FcitxConfigFile *cfile = FcitxConfigParseConfigFileFp(fp, GetTableConfigDesc());
237         fclose(fp);
238         if (!cfile) {
239             continue;
240         }
241         char *tableName = TableConfigStealTableName(cfile);
242         boolean needunregister = true;
243         if (!tableName) {
244             continue;
245         }
246         TableMetaData* t = TableMetaDataFind(tbl->tables, tableName);
247         if (!t) {
248             t = fcitx_utils_new(TableMetaData);
249             needunregister = false;
250         } else {
251             TableMetaDataUnlink(&tbl->tables, t);
252         }
253         TableMetaDataConfigBind(t, cfile, GetTableConfigDesc());
254         FcitxConfigBindSync((FcitxGenericConfig*)t);
255         if (t->bEnabled) {
256             t->confName = strdup(string->name);
257             t->owner = tbl;
258             TableMetaDataInsert(&tbl->tables, t);
259             if (needunregister)
260                 t->status = TABLE_REGISTERED;
261             else
262                 t->status = TABLE_NEW;
263         } else {
264             if (needunregister) {
265                 FcitxInstanceUnregisterIM(tbl->owner,
266                                             TableMetaDataGetName(t));
267                 imchanged = true;
268             }
269             TableMetaDataFree(t);
270         }
271     }
272     fcitx_utils_free_string_hash_set(sset);
273 
274     TableMetaData* titer = tbl->tables;
275     while (titer) {
276         /*
277          * if it's this case, the configuration file may already gone
278          * thus let's remove it
279          */
280         if (titer->status == TABLE_PENDING) {
281             TableMetaData* cur = titer;
282             FcitxInstanceUnregisterIM(tbl->owner, TableMetaDataGetName(cur));
283             TableMetaDataRemove(&tbl->tables, cur);
284             imchanged = true;
285         } else {
286             if (titer->status != TABLE_REGISTERED) {
287                 // FcitxLog(INFO, "register %s", TableMetaDataGetName(titer));
288                 TableMetaDataRegister(tbl, titer);
289                 imchanged = true;
290             }
291             titer = titer->hh.next;
292         }
293     }
294 
295     tbl->iTableCount = HASH_COUNT(tbl->tables);
296     return imchanged;
297 }
298 
299 CONFIG_DESC_DEFINE(GetTableConfigDesc, "table.desc")
300 CONFIG_DESC_DEFINE(GetTableGlobalConfigDesc, "fcitx-table.desc")
301 
TableInit(void * arg)302 boolean TableInit(void *arg)
303 {
304     TableMetaData* table = (TableMetaData*) arg;
305     FcitxTableState *tbl = table->owner;
306     boolean flag = true;
307     FcitxInstanceSetContext(tbl->owner, CONTEXT_IM_KEYBOARD_LAYOUT, table->kbdlayout);
308     FcitxInstanceSetContext(tbl->owner, CONTEXT_SHOW_REMIND_STATUS, &flag);
309     if (table->bUseAlternativePageKey) {
310         FcitxInstanceSetContext(tbl->owner, CONTEXT_ALTERNATIVE_PREVPAGE_KEY,
311                                 table->hkAlternativePrevPage);
312         FcitxInstanceSetContext(tbl->owner, CONTEXT_ALTERNATIVE_NEXTPAGE_KEY,
313                                 table->hkAlternativeNextPage);
314     }
315     FcitxAddon* pyaddon = FcitxPinyinGetAddon(tbl->owner);
316     tbl->pyaddon = pyaddon;
317     tbl->PYBaseOrder = AD_FREQ;
318 
319     FcitxPinyinReset(tbl->owner);
320     return true;
321 }
322 
TableResetStatus(void * arg)323 void TableResetStatus(void* arg)
324 {
325     TableMetaData* table = (TableMetaData*) arg;
326     FcitxTableState *tbl = table->owner;
327     FcitxInputState *input = FcitxInstanceGetInputState(tbl->owner);
328     tbl->bIsTableAddPhrase = false;
329     tbl->bIsTableDelPhrase = false;
330     tbl->bIsTableClearFreq = false;
331     tbl->bIsTableAdjustOrder = false;
332     FcitxInputStateSetIsDoInputOnly(input, false);
333     //bSingleHZMode = false;
334 }
335 
TableCheckNoMatch(TableMetaData * table,const char * code)336 boolean TableCheckNoMatch(TableMetaData* table, const char* code)
337 {
338     FcitxInstance *instance = table->owner->owner;
339     FcitxInputState *input = FcitxInstanceGetInputState(instance);
340     FcitxCandidateWordList* candList = FcitxInputStateGetCandidateList(input);
341     if (!table->bTableExactMatch) {
342         return FcitxCandidateWordGetListSize(candList) == 0;
343     } else {
344         return (FcitxCandidateWordGetListSize(candList) == 0) &&
345             TableFindFirstMatchCode(table, code, false, false) == -1;
346     }
347 }
348 
DoTableInput(void * arg,FcitxKeySym sym,unsigned int state)349 INPUT_RETURN_VALUE DoTableInput(void* arg, FcitxKeySym sym, unsigned int state)
350 {
351     TableMetaData* table = (TableMetaData*) arg;
352     FcitxTableState *tbl = table->owner;
353     INPUT_RETURN_VALUE retVal;
354     FcitxInstance *instance = tbl->owner;
355     FcitxInputState *input = FcitxInstanceGetInputState(instance);
356     FcitxGlobalConfig* config = FcitxInstanceGetGlobalConfig(instance);
357     char* strCodeInput = FcitxInputStateGetRawInputBuffer(input);
358     FcitxCandidateWordList* candList = FcitxInputStateGetCandidateList(input);
359     char *output_str = FcitxInputStateGetOutputString(input);
360 
361     FcitxCandidateWordSetChooseAndModifier(candList, table->strChoose,
362                                            GetTableMod(table));
363     if (table->bUseAlternativeCandidateWordNumber) {
364         FcitxCandidateWordSetPageSize(candList, table->iCandidateWordNumber);
365     } else {
366         FcitxCandidateWordSetPageSize(candList, config->iMaxCandWord);
367     }
368 
369     if (table != tbl->curLoadedTable && tbl->curLoadedTable) {
370         FreeTableDict(tbl->curLoadedTable);
371         tbl->curLoadedTable = NULL;
372     }
373 
374     if (tbl->curLoadedTable == NULL) {
375         if (!LoadTableDict(table)) {
376             FcitxInstanceUnregisterIM(instance, TableMetaDataGetName(table));
377             FcitxInstanceUpdateIMList(instance);
378             return IRV_DONOT_PROCESS;
379         }
380         tbl->curLoadedTable = table;
381     }
382 
383     if (FcitxHotkeyIsHotKeyModifierCombine(sym, state))
384         return IRV_TO_PROCESS;
385 
386     if (tbl->bTablePhraseTips) {
387         if (FcitxHotkeyIsHotKey(sym, state, FCITX_CTRL_DELETE)) {
388             tbl->bTablePhraseTips = false;
389             TableDelPhraseByHZ(table->tableDict, FcitxMessagesGetMessageString(FcitxInputStateGetAuxUp(input), 1));
390             return IRV_CLEAN;
391         } else if (state == FcitxKeyState_None && (sym != FcitxKey_Control_L && sym != FcitxKey_Control_R && sym != FcitxKey_Shift_L && sym != FcitxKey_Shift_R)) {
392             FcitxInstanceCleanInputWindow(instance);
393             tbl->bTablePhraseTips = false;
394             FcitxUICloseInputWindow(instance);
395         }
396     }
397 
398     retVal = IRV_DO_NOTHING;
399     if (state == FcitxKeyState_None &&
400         (IsInputKey(table->tableDict, sym)
401          || IsEndKey(table, sym)
402          || (table->bUseMatchingKey && sym == table->cMatchingKey)
403          || (table->bUsePY && sym == table->cPinyin)
404          || (strCodeInput[0] == table->cPinyin && table->bUsePY && sym == FcitxKey_apostrophe)
405         )
406        ) {
407         if (FcitxInputStateGetIsInRemind(input))
408             FcitxCandidateWordReset(candList);
409         FcitxInputStateSetIsInRemind(input, false);
410 
411         /* it's not in special state */
412         if (!tbl->bIsTableAddPhrase && !tbl->bIsTableDelPhrase &&
413             !tbl->bIsTableAdjustOrder && !tbl->bIsTableClearFreq) {
414             size_t raw_size = FcitxInputStateGetRawInputBufferSize(input);
415             if (FcitxHotkeyCheckChooseKeyAndModifier(sym, state,
416                                                      table->strChoose,
417                                                      GetTableMod(table)) >= 0) {
418                 size_t len1 = strlen(strCodeInput);
419                 size_t len = len1 > raw_size ? raw_size : len1;
420                 char buf[len + 2];
421                 memcpy(buf, strCodeInput, len);
422                 buf[len] = (char)sym;
423                 buf[len + 1] = '\0';
424                 if (TableFindFirstMatchCode(table, buf, false, false) == -1)
425                     return IRV_TO_PROCESS;
426             }
427 
428             /* check we use Pinyin or Not */
429             if (strCodeInput[0] == table->cPinyin && table->bUsePY) {
430                 if (raw_size != (MAX_PY_LENGTH * 5 + 1)) {
431                     strCodeInput[raw_size] = (char) sym;
432                     raw_size++;
433                     strCodeInput[raw_size] = '\0';
434                     FcitxInputStateSetRawInputBufferSize(input, raw_size);
435                     retVal = IRV_DISPLAY_CANDWORDS;
436                 } else {
437                     retVal = IRV_DO_NOTHING;
438                 }
439             } else {
440                 /* length is not too large */
441                 if (((raw_size < table->tableDict->iCodeLength) ||
442                      (table->tableDict->bHasPinyin &&
443                       raw_size < table->tableDict->iPYCodeLength) ||
444                      (((TableCheckNoMatch(table, FcitxInputStateGetRawInputBuffer(input)) &&
445                         table->bNoMatchDontCommit) || !table->bUseAutoSend) &&
446                       raw_size >= table->tableDict->iCodeLength)) &&
447                     raw_size <= MAX_TABLE_INPUT) {
448                     strCodeInput[raw_size] = (char)sym;
449                     raw_size++;
450                     strCodeInput[raw_size] = '\0';
451                     FcitxInputStateSetRawInputBufferSize(input, raw_size);
452 
453                     if (raw_size == 1 && strCodeInput[0] == table->cPinyin &&
454                         table->bUsePY) {
455                         retVal = IRV_DISPLAY_LAST;
456                     } else {
457                         char        *strTemp = NULL;
458                         char        *strLastFirstCand;
459                         CANDTYPE     lastFirstCandType;
460 
461                         strLastFirstCand = (char *)NULL;
462                         lastFirstCandType = CT_AUTOPHRASE;
463                         if (FcitxCandidateWordPageCount(candList) != 0) {
464                             // to realize auto-sending HZ to client
465                             FcitxCandidateWord *candWord = NULL;
466                             candWord = FcitxCandidateWordGetCurrentWindow(candList);
467                             if (candWord->owner == table) {
468                                 TABLECANDWORD* tableCandWord = candWord->priv;
469                                 lastFirstCandType = tableCandWord->flag;
470                                 INPUT_RETURN_VALUE ret = _TableGetCandWord(table, tableCandWord, false);
471                                 if (ret & IRV_FLAG_PENDING_COMMIT_STRING)
472                                     strLastFirstCand = output_str;
473                             }
474                         }
475 
476                         retVal = TableGetCandWords(table);
477                         int key = FcitxInputStateGetRawInputBuffer(input)[0];
478                         if (!table->bIgnorePunc
479                             || (table->bIgnorePunc
480                                 && table->ignorePuncList
481                                 && (!table->ignorePuncList[0] || strchr(table->ignorePuncList, key)))) {
482                             FcitxPuncGetPunc2(instance, &key, &strTemp, NULL);
483                         }
484                         if (IsEndKey(table, sym)) {
485                             if (raw_size == 1)
486                                 return IRV_TO_PROCESS;
487 
488                             if (FcitxCandidateWordPageCount(candList) == 0) {
489                                 FcitxInputStateSetRawInputBufferSize(input, 0);
490                                 return IRV_CLEAN;
491                             }
492 
493                             if (FcitxCandidateWordGetCurrentWindowSize(candList) == 1) {
494                                 retVal = FcitxCandidateWordChooseByIndex(candList, 0);
495                                 return retVal;
496                             }
497 
498                             return IRV_DISPLAY_CANDWORDS;
499                         } else if (table->bUseAutoSend
500                                    && table->iTableAutoSendToClientWhenNone
501                                    && (!(retVal & IRV_FLAG_PENDING_COMMIT_STRING))
502                                    && (raw_size >= (table->iTableAutoSendToClientWhenNone + 1))
503                                    && TableCheckNoMatch(table, FcitxInputStateGetRawInputBuffer(input))) {
504                             if (strLastFirstCand && (lastFirstCandType != CT_AUTOPHRASE)) {
505                                 FcitxInstanceCommitString(instance, FcitxInstanceGetCurrentIC(instance), strLastFirstCand);
506                             } else if (table->bSendRawPreedit) {
507                                 strCodeInput[raw_size - 1] = '\0';
508                                 FcitxInstanceCommitString(instance, FcitxInstanceGetCurrentIC(instance), strCodeInput);
509                             }
510                             retVal = IRV_DISPLAY_CANDWORDS;
511                             FcitxInputStateSetRawInputBufferSize(input, 1);
512                             strCodeInput[0] = sym;
513                             strCodeInput[1] = '\0';
514                         } else if ((raw_size == 1) && strTemp &&
515                                    FcitxCandidateWordPageCount(candList) == 0) {
516                             /**
517                              * 如果第一个字母是标点,并且没有候选字/词
518                              * 则当做标点处理──适用于二笔这样的输入
519                              **/
520                             FcitxInstanceCleanInputWindow(instance);
521                             FcitxInputStateGetRawInputBuffer(input)[0] = '\0';
522                             FcitxInputStateSetRawInputBufferSize(input, 0);
523                             TableResetStatus(table);
524                             return IRV_TO_PROCESS;
525                         }
526                     }
527                 } else {
528                     if (table->bUseAutoSend && table->iTableAutoSendToClient) {
529                         retVal = IRV_DISPLAY_CANDWORDS;
530                         if (FcitxCandidateWordPageCount(candList)) {
531                             FcitxCandidateWord* candWord = FcitxCandidateWordGetCurrentWindow(candList);
532                             if (candWord->owner == table) {
533                                 TABLECANDWORD* tableCandWord = candWord->priv;
534                                 if (tableCandWord->flag != CT_AUTOPHRASE) {
535                                     INPUT_RETURN_VALUE ret = TableGetCandWord(table, candWord);
536                                     if (ret & IRV_FLAG_PENDING_COMMIT_STRING) {
537                                         FcitxInstanceCommitString(instance, FcitxInstanceGetCurrentIC(instance), output_str);
538                                     }
539                                 }
540                             }
541                         } else if (table->bSendRawPreedit) {
542                             FcitxInstanceCommitString(instance, FcitxInstanceGetCurrentIC(instance), strCodeInput);
543                         }
544 
545                         FcitxInputStateSetRawInputBufferSize(input, 1);
546                         strCodeInput[0] = sym;
547                         strCodeInput[1] = '\0';
548                         FcitxInputStateSetIsInRemind(input, false);
549 
550                     } else
551                         retVal = IRV_DO_NOTHING;
552                 }
553             }
554         }
555     } else {
556         FcitxMessages *msg_up = FcitxInputStateGetAuxUp(input);
557         FcitxMessages *msg_down = FcitxInputStateGetAuxDown(input);
558         if (tbl->bIsTableAddPhrase) {
559             if (FcitxHotkeyIsHotKey(sym, state, FCITX_LEFT)) {
560                 if (tbl->iTableNewPhraseHZCount < table->tableDict->iHZLastInputCount && tbl->iTableNewPhraseHZCount < PHRASE_MAX_LENGTH) {
561                     tbl->iTableNewPhraseHZCount++;
562                     TableCreateNewPhrase(table);
563                 }
564             } else if (FcitxHotkeyIsHotKey(sym, state, FCITX_RIGHT)) {
565                 if (tbl->iTableNewPhraseHZCount > 2) {
566                     tbl->iTableNewPhraseHZCount--;
567                     TableCreateNewPhrase(table);
568                 }
569             } else if (FcitxHotkeyIsHotKey(sym, state, FCITX_ENTER)) {
570                 if (strcmp("????", FcitxMessagesGetMessageString(msg_down, 0)))
571                     TableInsertPhrase(table->tableDict, FcitxMessagesGetMessageString(msg_down, 1), FcitxMessagesGetMessageString(msg_down, 0));
572                 tbl->bIsTableAddPhrase = false;
573                 FcitxInputStateSetIsDoInputOnly(input, false);
574                 return IRV_CLEAN;
575             } else if (FcitxHotkeyIsHotKey(sym, state, FCITX_ESCAPE)) {
576                 tbl->bIsTableAddPhrase = false;
577                 FcitxInputStateSetIsDoInputOnly(input, false);
578                 return IRV_CLEAN;
579             } else {
580                 return IRV_DO_NOTHING;
581             }
582             return IRV_DISPLAY_MESSAGE;
583         }
584         if (FcitxHotkeyIsHotKey(sym, state, tbl->config.hkTableAddPhrase)) {
585             if (!tbl->bIsTableAddPhrase) {
586                 if (table->tableDict->iHZLastInputCount < 2 || !table->tableDict->bRule) //词组最少为两个汉字
587                     return IRV_DO_NOTHING;
588 
589                 tbl->bTablePhraseTips = false;
590                 tbl->iTableNewPhraseHZCount = 2;
591                 tbl->bIsTableAddPhrase = true;
592                 FcitxInputStateSetIsDoInputOnly(input, true);
593                 FcitxInputStateSetShowCursor(input, false);
594 
595                 FcitxInstanceCleanInputWindow(instance);
596                 FcitxMessagesAddMessageStringsAtLast(msg_up, MSG_TIPS, _("Left/Right to choose selected character, Press Enter to confirm, Press Escape to Cancel"));
597 
598                 FcitxMessagesAddMessageStringsAtLast(msg_down,
599                                                      MSG_FIRSTCAND, "");
600                 FcitxMessagesAddMessageStringsAtLast(msg_down, MSG_CODE, "");
601                 TableCreateNewPhrase(table);
602                 retVal = IRV_DISPLAY_MESSAGE;
603             } else
604                 retVal = IRV_TO_PROCESS;
605 
606             return retVal;
607         } else if (FcitxHotkeyIsHotKey(sym, state, tbl->config.hkLookupPinyin) && tbl->pyaddon) {
608             char strPY[128];
609 
610             //如果刚刚输入的是个词组,刚不查拼音
611             if (fcitx_utf8_strlen(output_str) != 1)
612                 return IRV_DO_NOTHING;
613 
614             FcitxInputStateSetRawInputBufferSize(input, 0);
615 
616             FcitxInstanceCleanInputWindow(instance);
617             FcitxMessagesAddMessageStringsAtLast(FcitxInputStateGetAuxUp(input),
618                                                  MSG_INPUT, output_str);
619 
620             FcitxMessagesAddMessageStringsAtLast(
621                 FcitxInputStateGetAuxDown(input), MSG_CODE, _("Pinyin: "));
622             const int8_t *py_list;
623             if ((py_list = FcitxPinyinEnhanceFindPy(tbl->owner, output_str))
624                 && *py_list) {
625                 int8_t i;
626                 int offset = 0;
627                 for (i = 0;i < *py_list && offset + 16 < sizeof(strPY);i++) {
628                     int len;
629                     const int8_t *py;
630                     if (i > 0) {
631                         memcpy(strPY + offset, ", ", 2);
632                         offset += 2;
633                     }
634                     py = pinyin_enhance_pylist_get(py_list, i);
635                     FcitxPinyinEnhancePyToString(tbl->owner, strPY + offset,
636                                                  py, &len);
637                     offset += len;
638                 }
639             } else {
640                 FcitxPinyinLoadBaseDict(tbl->owner);
641                 FcitxPinyinGetPyByHZ(tbl->owner, output_str, strPY);
642             }
643             FcitxMessagesAddMessageStringsAtLast(
644                 FcitxInputStateGetAuxDown(input), MSG_TIPS,
645                 (strPY[0]) ? strPY : _("Cannot found Pinyin"));
646             FcitxInputStateSetShowCursor(input, false);
647 
648             return IRV_DISPLAY_MESSAGE;
649         }
650 
651         if (!FcitxInputStateGetRawInputBufferSize(input) && !FcitxInputStateGetIsInRemind(input))
652             return IRV_TO_PROCESS;
653 
654         if (FcitxHotkeyIsHotKey(sym, state, FCITX_ESCAPE)) {
655             if (tbl->bIsTableDelPhrase || tbl->bIsTableAdjustOrder || tbl->bIsTableClearFreq) {
656                 TableResetStatus(table);
657                 FcitxInstanceCleanInputWindowUp(instance);
658                 FcitxMessagesAddMessageStringsAtLast(
659                     FcitxInputStateGetPreedit(input), MSG_INPUT,
660                     FcitxInputStateGetRawInputBuffer(input));
661                 FcitxMessagesAddMessageStringsAtLast(
662                     FcitxInputStateGetClientPreedit(input),
663                     MSG_INPUT | MSG_DONOT_COMMIT_WHEN_UNFOCUS,
664                     FcitxInputStateGetRawInputBuffer(input));
665                 retVal = IRV_DISPLAY_CANDWORDS;
666             } else
667                 return IRV_CLEAN;
668         } else if (FcitxHotkeyCheckChooseKey(sym, state, table->strChoose) >= 0) {
669             int iKey;
670             iKey = FcitxHotkeyCheckChooseKey(sym, state, table->strChoose);
671 
672             if (FcitxCandidateWordPageCount(candList) == 0)
673                 return IRV_TO_PROCESS;
674 
675             if (FcitxCandidateWordGetByIndex(candList, iKey) == NULL)
676                 return IRV_DO_NOTHING;
677             else {
678                 FcitxCandidateWord* candWord = FcitxCandidateWordGetByIndex(candList, iKey);
679                 if (candWord->owner == table && tbl->bIsTableDelPhrase) {
680                     TableDelPhraseByIndex(table, candWord->priv);
681                     tbl->bIsTableDelPhrase = false;
682                     FcitxInputStateSetIsDoInputOnly(input, false);
683                     retVal = IRV_DISPLAY_CANDWORDS;
684                 } else if (candWord->owner == table && tbl->bIsTableAdjustOrder) {
685                     TableAdjustOrderByIndex(table, candWord->priv);
686                     tbl->bIsTableAdjustOrder = false;
687                     FcitxInputStateSetIsDoInputOnly(input, false);
688                     retVal = IRV_DISPLAY_CANDWORDS;
689                 } else if (candWord->owner == table && tbl->bIsTableClearFreq) {
690                     TableClearFreqByIndex(table, candWord->priv);
691                     tbl->bIsTableClearFreq = false;
692                     FcitxInputStateSetIsDoInputOnly(input, false);
693                     retVal = IRV_DISPLAY_CANDWORDS;
694                 }
695                 else
696                     return IRV_TO_PROCESS;
697             }
698         } else if (!tbl->bIsTableDelPhrase && !tbl->bIsTableAdjustOrder && !tbl->bIsTableClearFreq) {
699             if (FcitxHotkeyIsHotKey(sym, state, tbl->config.hkTableAdjustOrder)) {
700                 if (FcitxCandidateWordGetListSize(candList) < 2 || FcitxInputStateGetIsInRemind(input))
701                     return IRV_DO_NOTHING;
702 
703                 tbl->bIsTableAdjustOrder = true;
704                 FcitxInstanceCleanInputWindowUp(instance);
705                 FcitxMessagesAddMessageStringsAtLast(FcitxInputStateGetAuxUp(input), MSG_TIPS, _("Choose the phrase to be put in the front, Press Escape to Cancel"));
706                 retVal = IRV_DISPLAY_MESSAGE;
707             } else if (FcitxHotkeyIsHotKey(sym, state, tbl->config.hkTableDelPhrase)) {
708                 if (!FcitxCandidateWordPageCount(candList) || FcitxInputStateGetIsInRemind(input))
709                     return IRV_DO_NOTHING;
710 
711                 tbl->bIsTableDelPhrase = true;
712                 FcitxInstanceCleanInputWindowUp(instance);
713                 FcitxMessagesAddMessageStringsAtLast(FcitxInputStateGetAuxUp(input), MSG_TIPS, _("Choose the phrase to be deleted, Press Escape to Cancel"));
714                 retVal = IRV_DISPLAY_MESSAGE;
715             } else if (FcitxHotkeyIsHotKey(sym, state, tbl->config.hkTableClearFreq)) {
716                 if (!FcitxCandidateWordPageCount(candList) || FcitxInputStateGetIsInRemind(input))
717                     return IRV_DO_NOTHING;
718 
719                 tbl->bIsTableClearFreq = true;
720                 FcitxInstanceCleanInputWindowUp(instance);
721                 FcitxMessagesAddMessageStringsAtLast(FcitxInputStateGetAuxUp(input), MSG_TIPS, _("Choose the phrase to clear typing history, Press Escape to Cancel"));
722                 retVal = IRV_DISPLAY_MESSAGE;
723             } else if (FcitxHotkeyIsHotKey(sym, state, FCITX_BACKSPACE)) {
724                 if (!FcitxInputStateGetRawInputBufferSize(input)) {
725                     FcitxInputStateSetIsInRemind(input, false);
726                     return IRV_DONOT_PROCESS_CLEAN;
727                 }
728 
729                 FcitxInputStateSetRawInputBufferSize(input, FcitxInputStateGetRawInputBufferSize(input) - 1);
730                 strCodeInput[FcitxInputStateGetRawInputBufferSize(input)] = '\0';
731 
732                 if (FcitxInputStateGetRawInputBufferSize(input) == 1 && strCodeInput[0] == table->cPinyin && table->bUsePY) {
733                     FcitxCandidateWordReset(candList);
734                     retVal = IRV_DISPLAY_LAST;
735                 } else if (FcitxInputStateGetRawInputBufferSize(input))
736                     retVal = IRV_DISPLAY_CANDWORDS;
737                 else
738                     retVal = IRV_CLEAN;
739             } else if (FcitxHotkeyIsHotKey(sym, state, table->hkCommitKey)) {
740                 if (FcitxInputStateGetRawInputBufferSize(input) == 1 && strCodeInput[0] == table->cPinyin && table->bUsePY)
741                     retVal = IRV_COMMIT_STRING;
742                 else {
743                     if (FcitxCandidateWordPageCount(candList) == 0 && table->bCommitKeyCommitWhenNoMatch) {
744                         FcitxInstanceCommitString(instance, FcitxInstanceGetCurrentIC(instance), FcitxInputStateGetRawInputBuffer(input));
745                         FcitxInputStateSetRawInputBufferSize(input, 0);
746                         FcitxInputStateGetRawInputBuffer(input)[0] = '\0';
747                         FcitxInputStateSetIsInRemind(input, false);
748                         FcitxInstanceCleanInputWindow(instance);
749                         FcitxUIUpdateInputWindow(instance);
750                         retVal = IRV_DO_NOTHING;
751                     }
752                     else {
753                         retVal = FcitxCandidateWordChooseByIndex(candList, 0);
754                         if (retVal == IRV_TO_PROCESS)
755                             retVal = IRV_DO_NOTHING;
756                     }
757                 }
758             } else {
759                 return IRV_TO_PROCESS;
760             }
761         }
762     }
763 
764     if (!FcitxInputStateGetIsInRemind(input)) {
765         if (tbl->bIsTableDelPhrase || tbl->bIsTableAdjustOrder || tbl->bIsTableClearFreq)
766             FcitxInputStateSetIsDoInputOnly(input, true);
767     }
768 
769     if (tbl->bIsTableDelPhrase || tbl->bIsTableAdjustOrder || tbl->bIsTableClearFreq || FcitxInputStateGetIsInRemind(input))
770         FcitxInputStateSetShowCursor(input, false);
771     else {
772         FcitxInputStateSetShowCursor(input, true);
773         FcitxInputStateSetCursorPos(input, strlen(FcitxInputStateGetRawInputBuffer(input)));
774         FcitxInputStateSetClientCursorPos(input, 0);
775     }
776 
777     return retVal;
778 }
779 
TableGetCandWord(void * arg,FcitxCandidateWord * candWord)780 INPUT_RETURN_VALUE TableGetCandWord(void* arg, FcitxCandidateWord* candWord)
781 {
782     TableMetaData* table = (TableMetaData*) arg;
783     FcitxTableState *tbl = table->owner;
784     FcitxInputState* input = FcitxInstanceGetInputState(tbl->owner);
785     TABLECANDWORD* tableCandWord = candWord->priv;
786 
787     INPUT_RETURN_VALUE retVal = _TableGetCandWord(table, tableCandWord, true);
788     if (retVal & IRV_FLAG_PENDING_COMMIT_STRING) {
789         if (table->bAutoPhrase && (fcitx_utf8_strlen(FcitxInputStateGetOutputString(input)) == 1 || (fcitx_utf8_strlen(FcitxInputStateGetOutputString(input)) > 1 && table->bAutoPhrasePhrase)))
790             UpdateHZLastInput(table, FcitxInputStateGetOutputString(input));
791 
792         if (tbl->pCurCandRecord)
793             TableUpdateHitFrequency(table, tbl->pCurCandRecord);
794     }
795 
796     return retVal;
797 }
798 
799 /*
800  * 第二个参数表示是否进入联想模式,实现自动上屏功能时,不能使用联想模式
801  */
_TableGetCandWord(TableMetaData * table,TABLECANDWORD * tableCandWord,boolean _bRemind)802 INPUT_RETURN_VALUE _TableGetCandWord(TableMetaData* table, TABLECANDWORD* tableCandWord, boolean _bRemind)
803 {
804     char           *pCandWord = NULL;
805     FcitxTableState* tbl = table->owner;
806     FcitxInstance *instance = tbl->owner;
807     FcitxInputState *input = FcitxInstanceGetInputState(instance);
808     FcitxProfile *profile = FcitxInstanceGetProfile(instance);
809 
810     if (tableCandWord->flag == CT_FH)
811         return TableGetFHCandWord(table, tableCandWord);
812 
813     FcitxInputStateSetIsInRemind(input, false);
814 
815     if (tableCandWord->flag == CT_NORMAL)
816         tbl->pCurCandRecord = tableCandWord->candWord.record;
817     else
818         tbl->pCurCandRecord = (RECORD *)NULL;
819 
820     if (table->tableDict->iTableChanged >= TABLE_AUTO_SAVE_AFTER)
821         SaveTableDict(table);
822 
823     switch (tableCandWord->flag) {
824     case CT_NORMAL:
825         pCandWord = tableCandWord->candWord.record->strHZ;
826         break;
827     case CT_AUTOPHRASE:
828         if (table->iSaveAutoPhraseAfter) {
829             /* 当_bRemind为false时,不应该计算自动组词的频度,因此此时实际并没有选择这个词 */
830             if (table->iSaveAutoPhraseAfter >= tableCandWord->candWord.autoPhrase->iSelected && _bRemind)
831                 tableCandWord->candWord.autoPhrase->iSelected++;
832             if (table->iSaveAutoPhraseAfter == tableCandWord->candWord.autoPhrase->iSelected)    //保存自动词组
833                 TableInsertPhrase(table->tableDict, tableCandWord->candWord.autoPhrase->strCode, tableCandWord->candWord.autoPhrase->strHZ);
834         }
835         pCandWord = tableCandWord->candWord.autoPhrase->strHZ;
836         break;
837     case CT_FH:
838         pCandWord = table->tableDict->fh[tableCandWord->candWord.iFHIndex].strFH;
839         break;
840     case CT_REMIND: {
841         strcpy(tbl->strTableRemindSource, tableCandWord->candWord.record->strHZ + strlen(tbl->strTableRemindSource));
842         strcpy(FcitxInputStateGetOutputString(input), tbl->strTableRemindSource);
843         INPUT_RETURN_VALUE retVal = TableGetRemindCandWords(table);
844         if (retVal == IRV_DISPLAY_CANDWORDS)
845             return IRV_COMMIT_STRING_REMIND;
846         else
847             return IRV_COMMIT_STRING;
848     }
849     }
850 
851     if (profile->bUseRemind && _bRemind) {
852         strcpy(tbl->strTableRemindSource, pCandWord);
853         strcpy(FcitxInputStateGetOutputString(input), pCandWord);
854         INPUT_RETURN_VALUE retVal = TableGetRemindCandWords(table);
855         if (retVal == IRV_DISPLAY_CANDWORDS)
856             return IRV_COMMIT_STRING_REMIND;
857     } else {
858         if (table->bPromptTableCode) {
859             RECORD         *temp;
860 
861             FcitxInstanceCleanInputWindow(instance);
862             FcitxMessagesAddMessageStringsAtLast(FcitxInputStateGetAuxUp(input), MSG_INPUT, FcitxInputStateGetRawInputBuffer(input));
863 
864             FcitxMessagesAddMessageStringsAtLast(FcitxInputStateGetAuxDown(input), MSG_TIPS, pCandWord);
865             temp = table->tableDict->tableSingleHZ[CalHZIndex(pCandWord)];
866             if (temp) {
867                 FcitxMessagesAddMessageStringsAtLast(FcitxInputStateGetAuxDown(input), MSG_CODE, temp->strCode);
868             }
869         } else {
870             FcitxInstanceCleanInputWindow(instance);
871         }
872     }
873 
874     if (fcitx_utf8_strlen(pCandWord) == 1)
875         FcitxInputStateSetLastIsSingleChar(input, 1);
876     else
877         FcitxInputStateSetLastIsSingleChar(input, 0);
878 
879     strcpy(FcitxInputStateGetOutputString(input), pCandWord);
880     return IRV_COMMIT_STRING;
881 }
882 
TableGetPinyinCandWords(TableMetaData * table)883 INPUT_RETURN_VALUE TableGetPinyinCandWords(TableMetaData* table)
884 {
885     FcitxTableState* tbl = table->owner;
886     FcitxInstance *instance = tbl->owner;
887 
888     if (!tbl->pyaddon)
889         return IRV_DISPLAY_CANDWORDS;
890 
891     FcitxInputState *input = FcitxInstanceGetInputState(instance);
892 
893     strcpy(FcitxPinyinGetFindString(tbl->owner),
894            FcitxInputStateGetRawInputBuffer(input) + 1);
895 
896     FcitxKeySym dummy1 = FcitxKey_None;
897     unsigned int dummy2 = 0;
898     FcitxPinyinDoInput(tbl->owner, &dummy1, &dummy2);
899     FcitxPinyinGetCandwords(tbl->owner);
900 
901     FcitxInputStateGetRawInputBuffer(input)[0] = table->cPinyin;
902     FcitxInputStateGetRawInputBuffer(input)[1] = '\0';
903 
904     strcat(FcitxInputStateGetRawInputBuffer(input),
905            FcitxPinyinGetFindString(tbl->owner));
906     FcitxInputStateSetRawInputBufferSize(input, strlen(FcitxInputStateGetRawInputBuffer(input)));
907 
908     FcitxInstanceCleanInputWindowUp(instance);
909     FcitxMessagesAddMessageStringsAtLast(
910         FcitxInputStateGetPreedit(input), MSG_INPUT,
911         FcitxInputStateGetRawInputBuffer(input));
912     FcitxMessagesAddMessageStringsAtLast(
913         FcitxInputStateGetClientPreedit(input),
914         MSG_INPUT | MSG_DONOT_COMMIT_WHEN_UNFOCUS,
915         FcitxInputStateGetRawInputBuffer(input));
916     FcitxInputStateSetCursorPos(input, FcitxInputStateGetRawInputBufferSize(input));
917     FcitxInputStateSetClientCursorPos(input, 0);
918 
919     //下面将拼音的候选字表转换为码表输入法的样式
920     FcitxCandidateWord* candWord;
921     for (candWord = FcitxCandidateWordGetFirst(FcitxInputStateGetCandidateList(input));
922          candWord != NULL;
923          candWord = FcitxCandidateWordGetNext(FcitxInputStateGetCandidateList(input), candWord)) {
924         char* pstr;
925         if (fcitx_utf8_strlen(candWord->strWord) == 1) {
926             RECORD* recTemp = table->tableDict->tableSingleHZ[CalHZIndex(candWord->strWord)];
927             if (!recTemp)
928                 pstr = (char *) NULL;
929             else
930                 pstr = recTemp->strCode;
931 
932         } else
933             pstr = (char *) NULL;
934 
935         if (pstr) {
936             candWord->strExtra = strdup(pstr);
937             candWord->extraType = MSG_CODE;
938         }
939         tbl->pygetcandword = candWord->callback;
940         candWord->callback = Table_PYGetCandWord;
941         candWord->owner = table;
942     }
943 
944     return IRV_DISPLAY_CANDWORDS;
945 }
946 
TableGetCandWords(void * arg)947 INPUT_RETURN_VALUE TableGetCandWords(void* arg)
948 {
949     TableMetaData* table = (TableMetaData*) arg;
950     FcitxTableState *tbl = table->owner;
951     int             i;
952     RECORD         *recTemp;
953     FcitxInstance *instance = tbl->owner;
954     FcitxInputState *input = FcitxInstanceGetInputState(instance);
955     FcitxCandidateWordList* candList = FcitxInputStateGetCandidateList(input);
956     FcitxCandidateWordSetLayoutHint(candList, table->layoutHint);
957 
958     if (FcitxInputStateGetRawInputBuffer(input)[0] == '\0')
959         return IRV_TO_PROCESS;
960 
961     if (FcitxInputStateGetIsInRemind(input))
962         return TableGetRemindCandWords(table);
963 
964     if (!strcmp(FcitxInputStateGetRawInputBuffer(input), table->strSymbol))
965         return TableGetFHCandWords(table);
966 
967     if (FcitxInputStateGetRawInputBuffer(input)[0] == table->cPinyin && table->bUsePY && tbl->pyaddon)
968         return TableGetPinyinCandWords(table);
969 
970     if (TableFindFirstMatchCode(table, FcitxInputStateGetRawInputBuffer(input), table->bTableExactMatch, true) == -1 && !table->tableDict->iAutoPhrase) {
971         if (FcitxInputStateGetRawInputBufferSize(input)) {
972             FcitxMessagesSetMessageCount(FcitxInputStateGetPreedit(input), 0);
973             FcitxMessagesSetMessageCount(FcitxInputStateGetClientPreedit(input), 0);
974             FcitxMessagesAddMessageStringsAtLast(FcitxInputStateGetPreedit(input), MSG_INPUT, FcitxInputStateGetRawInputBuffer(input));
975             FcitxMessageType type = MSG_INPUT;
976             if (!table->bSendRawPreedit)
977                 type |= MSG_DONOT_COMMIT_WHEN_UNFOCUS;
978             FcitxMessagesAddMessageStringsAtLast(FcitxInputStateGetClientPreedit(input), type, FcitxInputStateGetRawInputBuffer(input));
979             FcitxInputStateSetCursorPos(input, strlen(FcitxInputStateGetRawInputBuffer(input)));
980             FcitxInputStateSetClientCursorPos(input, 0);
981         }
982         //Not Found
983         return IRV_DISPLAY_CANDWORDS;
984     }
985 
986     TableCandWordSortContext context;
987     context.order = table->tableOrder;
988     context.simpleLevel = table->iSimpleLevel;
989     UT_array candTemp;
990     utarray_init(&candTemp, fcitx_ptr_icd);
991 
992     while (table->tableDict->currentRecord && table->tableDict->currentRecord != table->tableDict->recordHead) {
993         if (table->tableDict->currentRecord->type != RECORDTYPE_CONSTRUCT &&
994             table->tableDict->currentRecord->type != RECORDTYPE_PROMPT &&
995             !TableCompareCode(table, FcitxInputStateGetRawInputBuffer(input), table->tableDict->currentRecord->strCode, table->bTableExactMatch)) {
996             TABLECANDWORD* tableCandWord = fcitx_utils_malloc0(sizeof(TABLECANDWORD));
997             TableAddCandWord(table->tableDict->currentRecord, tableCandWord);
998             utarray_push_back(&candTemp, &tableCandWord);
999         }
1000         table->tableDict->currentRecord = table->tableDict->currentRecord->next;
1001     }
1002 
1003     /* seems AD_NO will go back to n^2, really effect performance */
1004     if (table->tableOrder != AD_NO)
1005         utarray_msort_r(&candTemp, TableCandCmp, &context);
1006 
1007     TABLECANDWORD** pcand = NULL;
1008     for (pcand = (TABLECANDWORD**) utarray_front(&candTemp);
1009             pcand != NULL;
1010             pcand = (TABLECANDWORD**) utarray_next(&candTemp, pcand)) {
1011         TABLECANDWORD* tableCandWord = *pcand;
1012         FcitxCandidateWord candWord;
1013         candWord.callback = TableGetCandWord;
1014         candWord.owner = table;
1015         candWord.priv = tableCandWord;
1016         candWord.strWord = strdup(tableCandWord->candWord.record->strHZ);
1017         candWord.strExtra = NULL;
1018         candWord.wordType = MSG_OTHER;
1019 
1020         const char* pstr = NULL;
1021         if ((tableCandWord->flag == CT_NORMAL) && (tableCandWord->candWord.record->type == RECORDTYPE_PINYIN)) {
1022             if (fcitx_utf8_strlen(tableCandWord->candWord.record->strHZ) == 1) {
1023                 recTemp = table->tableDict->tableSingleHZ[CalHZIndex(tableCandWord->candWord.record->strHZ)];
1024                 if (!recTemp)
1025                     pstr = (char *) NULL;
1026                 else
1027                     pstr = recTemp->strCode;
1028             } else
1029                 pstr = (char *) NULL;
1030         } else if (HasMatchingKey(table, FcitxInputStateGetRawInputBuffer(input)))
1031             pstr = (tableCandWord->flag == CT_NORMAL) ? tableCandWord->candWord.record->strCode : tableCandWord->candWord.autoPhrase->strCode;
1032         else
1033             pstr = ((tableCandWord->flag == CT_NORMAL) ? tableCandWord->candWord.record->strCode : tableCandWord->candWord.autoPhrase->strCode) + FcitxInputStateGetRawInputBufferSize(input);
1034 
1035         if (pstr) {
1036             if (table->customPrompt) {
1037                 size_t codelen = strlen(pstr);
1038                 int i = 0;
1039                 int totallen = 0;
1040                 for (i = 0; i < codelen; i ++) {
1041                     RECORD* rec = table->tableDict->promptCode[(uint8_t) pstr[i]];
1042                     if (rec) {
1043                         totallen += strlen(rec->strHZ);
1044                     } else
1045                         totallen += 1;
1046                 }
1047 
1048                 candWord.strExtra = fcitx_utils_malloc0(sizeof(char) * (totallen + 1 + 3));
1049                 if (codelen)
1050                     strcpy(candWord.strExtra, "\xef\xbd\x9e");
1051                 for (i = 0; i < codelen; i ++) {
1052                     RECORD* rec = table->tableDict->promptCode[(uint8_t) pstr[i]];
1053                     if (rec) {
1054                         strcat(candWord.strExtra, rec->strHZ);
1055                     } else {
1056                         char temp[2] = {pstr[i], '\0'};
1057                         strcat(candWord.strExtra, temp);
1058                     }
1059                 }
1060             }
1061             else {
1062                 candWord.strExtra = strdup(pstr);
1063             }
1064             candWord.extraType = MSG_CODE;
1065         }
1066 
1067         FcitxCandidateWordAppend(candList, &candWord);
1068     }
1069     utarray_clear(&candTemp);
1070 
1071     if (table->tableDict->bRule && table->bAutoPhrase && FcitxInputStateGetRawInputBufferSize(input) == table->tableDict->iCodeLength) {
1072         for (i = table->tableDict->iAutoPhrase - 1; i >= 0; i--) {
1073             if (!TableCompareCode(table, FcitxInputStateGetRawInputBuffer(input), table->tableDict->autoPhrase[i].strCode, table->bTableExactMatch)) {
1074                 if (TableHasPhrase(table->tableDict, table->tableDict->autoPhrase[i].strCode, table->tableDict->autoPhrase[i].strHZ)) {
1075                     TABLECANDWORD* tableCandWord = fcitx_utils_malloc0(sizeof(TABLECANDWORD));
1076                     TableAddAutoCandWord(table, i, tableCandWord);
1077                     utarray_push_back(&candTemp, &tableCandWord);
1078                 }
1079             }
1080         }
1081     }
1082 
1083     for (pcand = (TABLECANDWORD**) utarray_front(&candTemp);
1084             pcand != NULL;
1085             pcand = (TABLECANDWORD**) utarray_next(&candTemp, pcand)) {
1086         TABLECANDWORD* tableCandWord = *pcand;
1087         FcitxCandidateWord candWord;
1088         candWord.callback = TableGetCandWord;
1089         candWord.owner = table;
1090         candWord.priv = tableCandWord;
1091         candWord.strWord = strdup(tableCandWord->candWord.autoPhrase->strHZ);
1092         candWord.strExtra = NULL;
1093         candWord.wordType = MSG_USERPHR;
1094 
1095         FcitxCandidateWordAppend(candList, &candWord);
1096     }
1097 
1098     utarray_done(&candTemp);
1099 
1100     INPUT_RETURN_VALUE retVal = IRV_DISPLAY_CANDWORDS;
1101 
1102     if (table->bUseAutoSend && table->iTableAutoSendToClient && (FcitxInputStateGetRawInputBufferSize(input) >= table->iTableAutoSendToClient)) {
1103         if (FcitxCandidateWordGetCurrentWindowSize(candList) == 1) {  //如果只有一个候选词,则送到客户程序中
1104             FcitxCandidateWord* candWord = FcitxCandidateWordGetCurrentWindow(candList);
1105             if (candWord->owner == table) {
1106                 TABLECANDWORD* tableCandWord = candWord->priv;
1107                 if (tableCandWord->flag != CT_AUTOPHRASE || (tableCandWord->flag == CT_AUTOPHRASE && !table->iSaveAutoPhraseAfter))
1108                     if (!(tableCandWord->flag == CT_NORMAL && tableCandWord->candWord.record->type == RECORDTYPE_PINYIN))
1109                         retVal = FcitxCandidateWordChooseByIndex(candList, 0);
1110             }
1111         }
1112     }
1113 
1114     if (FcitxInputStateGetRawInputBufferSize(input)) {
1115         FcitxMessagesSetMessageCount(FcitxInputStateGetPreedit(input), 0);
1116         FcitxMessagesAddMessageStringsAtLast(FcitxInputStateGetPreedit(input), MSG_INPUT, FcitxInputStateGetRawInputBuffer(input));
1117         FcitxInputStateSetCursorPos(input, strlen(FcitxInputStateGetRawInputBuffer(input)));
1118         FcitxCandidateWord* candWord = NULL;
1119         if (table->bFirstCandidateAsPreedit && (candWord = FcitxCandidateWordGetFirst(candList))) {
1120             FcitxMessagesSetMessageCount(FcitxInputStateGetClientPreedit(input), 0);
1121             FcitxMessagesAddMessageStringsAtLast(FcitxInputStateGetClientPreedit(input), MSG_INPUT, candWord->strWord);
1122             FcitxInputStateSetClientCursorPos(input, 0);
1123         }
1124         else {
1125             FcitxMessagesSetMessageCount(FcitxInputStateGetClientPreedit(input), 0);
1126             FcitxMessageType type = MSG_INPUT;
1127             if (!table->bSendRawPreedit)
1128                 type |= MSG_DONOT_COMMIT_WHEN_UNFOCUS;
1129             FcitxMessagesAddMessageStringsAtLast(FcitxInputStateGetClientPreedit(input), type, FcitxInputStateGetRawInputBuffer(input));
1130             FcitxInputStateSetClientCursorPos(input, 0);
1131         }
1132     }
1133 
1134     return retVal;
1135 }
1136 
TableAddAutoCandWord(TableMetaData * table,short which,TABLECANDWORD * tableCandWord)1137 void TableAddAutoCandWord(TableMetaData* table, short which, TABLECANDWORD* tableCandWord)
1138 {
1139     tableCandWord->flag = CT_AUTOPHRASE;
1140     tableCandWord->candWord.autoPhrase = &table->tableDict->autoPhrase[which];
1141 }
1142 
TableAddCandWord(RECORD * record,TABLECANDWORD * tableCandWord)1143 void TableAddCandWord(RECORD * record, TABLECANDWORD* tableCandWord)
1144 {
1145     tableCandWord->flag = CT_NORMAL;
1146     tableCandWord->candWord.record = record;
1147 }
1148 
TableClearFreqByIndex(TableMetaData * table,TABLECANDWORD * tableCandWord)1149 void TableClearFreqByIndex(TableMetaData* table, TABLECANDWORD* tableCandWord)
1150 {
1151     RECORD         *recTemp;
1152 
1153     recTemp = tableCandWord->candWord.record;
1154     recTemp->iHit = 0;
1155 
1156     table->tableDict->iTableChanged++;
1157 }
1158 
1159 /*
1160  * 根据序号调整词组顺序,序号从1开始
1161  * 将指定的字/词调整到同样编码的最前面
1162  */
TableAdjustOrderByIndex(TableMetaData * table,TABLECANDWORD * tableCandWord)1163 void TableAdjustOrderByIndex(TableMetaData* table, TABLECANDWORD* tableCandWord)
1164 {
1165     RECORD         *recTemp;
1166     int             iTemp;
1167 
1168     recTemp = tableCandWord->candWord.record;
1169     while (!strcmp(recTemp->strCode, recTemp->prev->strCode))
1170         recTemp = recTemp->prev;
1171     if (recTemp == tableCandWord->candWord.record)   //说明已经是第一个
1172         return;
1173 
1174     //将指定的字/词放到recTemp前
1175     tableCandWord->candWord.record->prev->next = tableCandWord->candWord.record->next;
1176     tableCandWord->candWord.record->next->prev = tableCandWord->candWord.record->prev;
1177     recTemp->prev->next = tableCandWord->candWord.record;
1178     tableCandWord->candWord.record->prev = recTemp->prev;
1179     recTemp->prev = tableCandWord->candWord.record;
1180     tableCandWord->candWord.record->next = recTemp;
1181 
1182     table->tableDict->iTableChanged++;
1183 
1184     //需要的话,更新索引
1185     if (tableCandWord->candWord.record->strCode[1] == '\0') {
1186         size_t tmp_len = strlen(table->tableDict->strInputCode);
1187         for (iTemp = 0; iTemp < tmp_len; iTemp++) {
1188             if (table->tableDict->recordIndex[iTemp].cCode ==
1189                 tableCandWord->candWord.record->strCode[0]) {
1190                 table->tableDict->recordIndex[iTemp].record = tableCandWord->candWord.record;
1191                 break;
1192             }
1193         }
1194     }
1195 }
1196 
1197 /*
1198  * 根据序号删除词组,序号从1开始
1199  */
TableDelPhraseByIndex(TableMetaData * table,TABLECANDWORD * tableCandWord)1200 void TableDelPhraseByIndex(TableMetaData* table, TABLECANDWORD* tableCandWord)
1201 {
1202     if (tableCandWord->flag != CT_NORMAL)
1203         return;
1204 
1205     if (fcitx_utf8_strlen(tableCandWord->candWord.record->strHZ) <= 1)
1206         return;
1207 
1208     TableDelPhrase(table->tableDict, tableCandWord->candWord.record);
1209 }
1210 
TableCreateNewPhrase(TableMetaData * table)1211 void TableCreateNewPhrase(TableMetaData* table)
1212 {
1213     int             i;
1214     FcitxTableState* tbl = table->owner;
1215     FcitxInstance *instance = tbl->owner;
1216     FcitxInputState* input = FcitxInstanceGetInputState(instance);
1217     FcitxMessages *msg_down = FcitxInputStateGetAuxDown(input);
1218 
1219     FcitxMessagesSetMessageTextStrings(msg_down, 0, "");
1220     for (i = tbl->iTableNewPhraseHZCount; i > 0; i--)
1221         FcitxMessagesMessageConcat(msg_down, 0, table->tableDict->hzLastInput[table->tableDict->iHZLastInputCount - i].strHZ);
1222 
1223     boolean bCanntFindCode = TableCreatePhraseCode(table->tableDict, FcitxMessagesGetMessageString(msg_down, 0));
1224 
1225     if (!bCanntFindCode) {
1226         FcitxMessagesSetMessageCount(msg_down, 2);
1227         FcitxMessagesSetMessageTextStrings(msg_down, 1,
1228                                            table->tableDict->strNewPhraseCode);
1229     } else {
1230         FcitxMessagesSetMessageCount(msg_down, 1);
1231         FcitxMessagesSetMessageTextStrings(msg_down, 0, "????");
1232     }
1233 
1234 }
1235 
1236 /*
1237  * 获取联想候选字列表
1238  */
TableGetRemindCandWords(TableMetaData * table)1239 INPUT_RETURN_VALUE TableGetRemindCandWords(TableMetaData* table)
1240 {
1241     FcitxTableState* tbl = table->owner;
1242     int             iLength;
1243     RECORD         *tableRemind = NULL;
1244     FcitxGlobalConfig *config = FcitxInstanceGetGlobalConfig(tbl->owner);
1245     FcitxInstance *instance = tbl->owner;
1246     FcitxInputState *input = FcitxInstanceGetInputState(instance);
1247     boolean bDisablePagingInRemind = config->bDisablePagingInRemind;
1248     FcitxCandidateWordList *cand_list = FcitxInputStateGetCandidateList(input);
1249 
1250     if (!tbl->strTableRemindSource[0])
1251         return IRV_TO_PROCESS;
1252 
1253     FcitxInputStateGetRawInputBuffer(input)[0] = '\0';
1254     FcitxInputStateSetRawInputBufferSize(input, 0);
1255     FcitxCandidateWordReset(cand_list);
1256 
1257     iLength = fcitx_utf8_strlen(tbl->strTableRemindSource);
1258     tableRemind = table->tableDict->recordHead->next;
1259 
1260     while (tableRemind != table->tableDict->recordHead) {
1261         if (bDisablePagingInRemind &&
1262             FcitxCandidateWordGetListSize(cand_list) >=
1263             FcitxCandidateWordGetPageSize(cand_list))
1264             break;
1265 
1266         if (((iLength + 1) == fcitx_utf8_strlen(tableRemind->strHZ))) {
1267             if (!fcitx_utf8_strncmp(tableRemind->strHZ,
1268                                     tbl->strTableRemindSource, iLength) &&
1269                 fcitx_utf8_get_nth_char(tableRemind->strHZ, iLength)) {
1270                 TABLECANDWORD *tableCandWord = fcitx_utils_new(TABLECANDWORD);
1271                 TableAddRemindCandWord(tableRemind, tableCandWord);
1272                 FcitxCandidateWord candWord;
1273                 candWord.callback = TableGetCandWord;
1274                 candWord.owner = table;
1275                 candWord.priv = tableCandWord;
1276                 candWord.strExtra = NULL;
1277                 candWord.strWord = strdup(tableCandWord->candWord.record->strHZ + strlen(tbl->strTableRemindSource));
1278                 candWord.wordType = MSG_OTHER;
1279                 FcitxCandidateWordAppend(cand_list, &candWord);
1280             }
1281         }
1282         tableRemind = tableRemind->next;
1283     }
1284 
1285     FcitxInstanceCleanInputWindowUp(instance);
1286     FcitxMessagesAddMessageStringsAtLast(FcitxInputStateGetAuxUp(input),
1287                                          MSG_TIPS, _("Remind:"));
1288     FcitxMessagesAddMessageStringsAtLast(FcitxInputStateGetAuxUp(input),
1289                                          MSG_INPUT, tbl->strTableRemindSource);
1290     int count = FcitxCandidateWordPageCount(cand_list);
1291     FcitxInputStateSetIsInRemind(input, count);
1292     if (count) {
1293         return IRV_DISPLAY_CANDWORDS;
1294     } else {
1295         return IRV_CLEAN;
1296     }
1297 }
1298 
TableAddRemindCandWord(RECORD * record,TABLECANDWORD * tableCandWord)1299 void TableAddRemindCandWord(RECORD * record, TABLECANDWORD* tableCandWord)
1300 {
1301     tableCandWord->flag = CT_REMIND;
1302     tableCandWord->candWord.record = record;
1303 }
1304 
TableGetRemindCandWord(void * arg,TABLECANDWORD * tableCandWord)1305 INPUT_RETURN_VALUE TableGetRemindCandWord(void* arg, TABLECANDWORD* tableCandWord)
1306 {
1307     TableMetaData* table = (TableMetaData*) arg;
1308     FcitxTableState* tbl = table->owner;
1309     FcitxInstance *instance = tbl->owner;
1310     FcitxInputState *input = FcitxInstanceGetInputState(instance);
1311 
1312     tableCandWord->candWord.record->iHit++;
1313     strcpy(tbl->strTableRemindSource, tableCandWord->candWord.record->strHZ + strlen(tbl->strTableRemindSource));
1314     TableGetRemindCandWords(table);
1315 
1316     strcpy(FcitxInputStateGetOutputString(input), tbl->strTableRemindSource);
1317     return IRV_COMMIT_STRING_REMIND;
1318 }
1319 
TableGetFHCandWords(TableMetaData * table)1320 INPUT_RETURN_VALUE TableGetFHCandWords(TableMetaData* table)
1321 {
1322     int             i;
1323     FcitxTableState* tbl = table->owner;
1324     FcitxInstance *instance = tbl->owner;
1325     FcitxInputState *input = FcitxInstanceGetInputState(instance);
1326 
1327     FcitxInstanceCleanInputWindowUp(instance);
1328     FcitxMessagesAddMessageStringsAtLast(FcitxInputStateGetPreedit(input), MSG_INPUT, FcitxInputStateGetRawInputBuffer(input));
1329     FcitxMessagesAddMessageStringsAtLast(FcitxInputStateGetClientPreedit(input), MSG_INPUT | MSG_DONOT_COMMIT_WHEN_UNFOCUS, FcitxInputStateGetRawInputBuffer(input));
1330     FcitxInputStateSetCursorPos(input, FcitxInputStateGetRawInputBufferSize(input));
1331     FcitxInputStateSetClientCursorPos(input, 0);
1332 
1333     if (!table->tableDict->iFH)
1334         return IRV_DISPLAY_MESSAGE;
1335 
1336     for (i = 0; i < table->tableDict->iFH; i++) {
1337         TABLECANDWORD* tableCandWord = fcitx_utils_malloc0(sizeof(TABLECANDWORD));
1338         tableCandWord->flag = CT_FH;
1339         tableCandWord->candWord.iFHIndex = i;
1340         FcitxCandidateWord candWord;
1341         candWord.callback = TableGetCandWord;
1342         candWord.owner = table;
1343         candWord.priv = tableCandWord;
1344         candWord.strExtra = NULL;
1345         candWord.strWord = strdup(table->tableDict->fh[i].strFH);
1346         candWord.wordType = MSG_OTHER;
1347         FcitxCandidateWordAppend(FcitxInputStateGetCandidateList(input), &candWord);
1348     }
1349     return IRV_DISPLAY_CANDWORDS;
1350 }
1351 
TableGetFHCandWord(TableMetaData * table,TABLECANDWORD * tableCandWord)1352 INPUT_RETURN_VALUE TableGetFHCandWord(TableMetaData* table, TABLECANDWORD* tableCandWord)
1353 {
1354     FcitxTableState* tbl = table->owner;
1355     FcitxInstance *instance = tbl->owner;
1356     FcitxInputState *input = FcitxInstanceGetInputState(instance);
1357 
1358     strcpy(FcitxInputStateGetOutputString(input), table->tableDict->fh[tableCandWord->candWord.iFHIndex].strFH);
1359     return IRV_COMMIT_STRING;
1360 }
1361 
TablePhraseTips(void * arg)1362 boolean TablePhraseTips(void *arg)
1363 {
1364     TableMetaData* table = (TableMetaData*) arg;
1365     FcitxTableState *tbl = table->owner;
1366     RECORD *recTemp = NULL;
1367     char strTemp[PHRASE_MAX_LENGTH * UTF8_MAX_LENGTH + 1] = "", *ps;
1368     short i, j;
1369     FcitxInstance *instance = tbl->owner;
1370     FcitxInputState *input = FcitxInstanceGetInputState(instance);
1371 
1372     if (!table->tableDict->recordHead)
1373         return false;
1374 
1375     //如果最近输入了一个词组,这个工作就不需要了
1376     if (FcitxInputStateGetLastIsSingleChar(input) != 1)
1377         return false;
1378 
1379     j = (table->tableDict->iHZLastInputCount > PHRASE_MAX_LENGTH) ? table->tableDict->iHZLastInputCount - PHRASE_MAX_LENGTH : 0;
1380     for (i = j; i < table->tableDict->iHZLastInputCount; i++)
1381         strcat(strTemp, table->tableDict->hzLastInput[i].strHZ);
1382     //如果只有一个汉字,这个工作也不需要了
1383     if (fcitx_utf8_strlen(strTemp) < 2)
1384         return false;
1385 
1386     //首先要判断是不是已经在词库中
1387     ps = strTemp;
1388     FcitxMessages *msg_up = FcitxInputStateGetAuxUp(input);
1389     FcitxMessages *msg_down = FcitxInputStateGetAuxDown(input);
1390     for (i = 0; i < (table->tableDict->iHZLastInputCount - j - 1); i++) {
1391         recTemp = TableFindPhrase(table->tableDict, ps);
1392         if (recTemp) {
1393             FcitxInstanceCleanInputWindow(instance);
1394             FcitxMessagesAddMessageStringsAtLast(
1395                 msg_up, MSG_TIPS, _("Phrase is already in Dict "));
1396             FcitxMessagesAddMessageStringsAtLast(msg_up, MSG_INPUT, ps);
1397 
1398             FcitxMessagesAddMessageStringsAtLast(msg_down, MSG_FIRSTCAND,
1399                                                  _("Code is "));
1400             FcitxMessagesAddMessageStringsAtLast(msg_down, MSG_CODE,
1401                                                  recTemp->strCode);
1402             FcitxMessagesAddMessageStringsAtLast(msg_down, MSG_TIPS,
1403                                                  _(" Ctrl+Delete To Delete"));
1404             tbl->bTablePhraseTips = true;
1405             FcitxInputStateSetShowCursor(input, false);
1406 
1407             return true;
1408         }
1409         ps = ps + fcitx_utf8_char_len(ps);
1410     }
1411 
1412     return false;
1413 }
1414 
UpdateHZLastInput(TableMetaData * table,const char * str)1415 void UpdateHZLastInput(TableMetaData* table, const char *str)
1416 {
1417     unsigned int i, j;
1418     unsigned int str_len = fcitx_utf8_strlen(str);
1419     TableDict *const tableDict = table->tableDict;
1420     SINGLE_HZ *const hzLastInput = tableDict->hzLastInput;
1421 
1422     for (i = 0;i < str_len;i++) {
1423         if (tableDict->iHZLastInputCount < PHRASE_MAX_LENGTH) {
1424             tableDict->iHZLastInputCount++;
1425         } else {
1426             for (j = 0;j < (tableDict->iHZLastInputCount - 1);j++) {
1427                 strncpy(hzLastInput[j].strHZ, hzLastInput[j + 1].strHZ,
1428                         fcitx_utf8_char_len(hzLastInput[j + 1].strHZ));
1429             }
1430         }
1431         unsigned int char_len = fcitx_utf8_char_len(str);
1432         strncpy(hzLastInput[tableDict->iHZLastInputCount - 1].strHZ,
1433                 str, char_len);
1434         hzLastInput[tableDict->iHZLastInputCount - 1].strHZ[char_len] = '\0';
1435         str += char_len;
1436     }
1437 
1438     if (tableDict->bRule && table->bAutoPhrase) {
1439         TableCreateAutoPhrase(table, (char)(str_len));
1440     }
1441 }
1442 
TableMetaDataFree(TableMetaData * table)1443 void TableMetaDataFree(TableMetaData *table)
1444 {
1445     if (!table)
1446         return;
1447     FcitxConfigFree(&table->config);
1448     fcitx_utils_free(table->confName);
1449     free(table);
1450 }
1451 
TableCandCmp(const void * a,const void * b,void * arg)1452 int TableCandCmp(const void* a, const void* b, void *arg)
1453 {
1454     TABLECANDWORD* canda = *(TABLECANDWORD**)a;
1455     TABLECANDWORD* candb = *(TABLECANDWORD**)b;
1456     TableCandWordSortContext* context = arg;
1457 
1458     if (context->simpleLevel > 0) {
1459         size_t lengthA = strlen(canda->candWord.record->strCode);
1460         size_t lengthB = strlen(candb->candWord.record->strCode);
1461 
1462         if (lengthA <= context->simpleLevel && lengthB <= context->simpleLevel) {
1463             /* we use msort which is stable, so it doesn't matter */
1464             return 0;
1465         }
1466         if (lengthA > context->simpleLevel && lengthB <= context->simpleLevel) {
1467             return 1;
1468         }
1469         if (lengthA <= context->simpleLevel && lengthB > context->simpleLevel) {
1470             return -1;
1471         }
1472     }
1473 
1474     switch (context->order) {
1475     case AD_NO:
1476         /* actually this is dead code, since AD_NO doesn't sort at all */
1477         return 0;
1478     case AD_FAST: {
1479         int result = strcmp(canda->candWord.record->strCode,
1480                             candb->candWord.record->strCode);
1481         if (result != 0)
1482             return result;
1483         return candb->candWord.record->iIndex - canda->candWord.record->iIndex;
1484     }
1485     case AD_FREQ: {
1486         int result = strcmp(canda->candWord.record->strCode,
1487                             candb->candWord.record->strCode);
1488         if (result != 0)
1489             return result;
1490         return candb->candWord.record->iHit - canda->candWord.record->iHit;
1491     }
1492     }
1493     return 0;
1494 }
1495 
TableKeyBlocker(void * arg,FcitxKeySym sym,unsigned int state)1496 INPUT_RETURN_VALUE TableKeyBlocker(void* arg, FcitxKeySym sym, unsigned int state)
1497 {
1498     TableMetaData *table = arg;
1499     FcitxInstance *instance = table->owner->owner;
1500     FcitxInputState *input = FcitxInstanceGetInputState(instance);
1501 
1502     do {
1503         if (!table->bCommitAndPassByInvalidKey)
1504             break;
1505         if (!FcitxHotkeyIsHotKeySimple(sym, state))
1506             break;
1507         FcitxCandidateWordList *cand_list;
1508         cand_list = FcitxInputStateGetCandidateList(input);
1509         if (FcitxCandidateWordPageCount(cand_list)) {
1510             FcitxCandidateWord *candWord;
1511             candWord = FcitxCandidateWordGetCurrentWindow(cand_list);
1512             if (candWord->owner != table)
1513                 break;
1514             TABLECANDWORD* tableCandWord = candWord->priv;
1515             if (tableCandWord->flag == CT_AUTOPHRASE)
1516                 break;
1517             INPUT_RETURN_VALUE ret = TableGetCandWord(table, candWord);
1518             if (!(ret & IRV_FLAG_PENDING_COMMIT_STRING))
1519                 break;
1520             FcitxInstanceCommitString(
1521                 instance, FcitxInstanceGetCurrentIC(instance),
1522                 FcitxInputStateGetOutputString(input));
1523         } else if (table->bSendRawPreedit) {
1524             FcitxInstanceCommitString(
1525                 instance, FcitxInstanceGetCurrentIC(instance),
1526                 FcitxInputStateGetRawInputBuffer(input));
1527         }
1528         FcitxInputStateSetRawInputBufferSize(input, 0);
1529         FcitxInputStateGetRawInputBuffer(input)[0] = '\0';
1530         FcitxInputStateSetIsInRemind(input, false);
1531         FcitxInstanceCleanInputWindow(instance);
1532         FcitxUIUpdateInputWindow(instance);
1533         return IRV_FLAG_FORWARD_KEY;
1534     } while (0);
1535     return FcitxStandardKeyBlocker(input, sym, state);
1536 }
1537 
ReloadTableConfig(void * arg)1538 void ReloadTableConfig(void* arg)
1539 {
1540     FcitxTableState* tbl = arg;
1541     LoadTableConfig(&tbl->config);
1542     if (LoadTableInfo(tbl))
1543         FcitxInstanceUpdateIMList(tbl->owner);
1544 }
1545 
1546 INPUT_RETURN_VALUE
Table_PYGetCandWord(void * arg,FcitxCandidateWord * candidateWord)1547 Table_PYGetCandWord(void* arg, FcitxCandidateWord* candidateWord)
1548 {
1549     TableMetaData* table = arg;
1550     FcitxTableState* tbl = table->owner;
1551     INPUT_RETURN_VALUE retVal = tbl->pygetcandword(tbl->pyaddon->addonInstance,
1552                                                    candidateWord);
1553     FcitxPinyinReset(tbl->owner);
1554     FcitxInputState *state = FcitxInstanceGetInputState(tbl->owner);
1555     if (!(retVal & IRV_FLAG_PENDING_COMMIT_STRING)) {
1556         strcpy(FcitxInputStateGetOutputString(state), candidateWord->strWord);
1557     }
1558 
1559     return IRV_COMMIT_STRING | IRV_FLAG_RESET_INPUT;
1560 }
1561 
1562 // kate: indent-mode cstyle; space-indent on; indent-width 0;
1563