1 /***************************************************************************
2  *   Copyright (C) 2012~2012 by CSSlayer                                   *
3  *                                                                         *
4  *   This program is free software; you can redistribute it and/or modify  *
5  *   it under the terms of the GNU General Public License as published by  *
6  *   the Free Software Foundation; either version 2 of the License, or     *
7  *   (at your option) any later version.                                   *
8  *                                                                         *
9  *   This program is distributed in the hope that it will be useful,       *
10  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
11  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
12  *   GNU General Public License for more details.                          *
13  *                                                                         *
14  *   You should have received a copy of the GNU General Public License     *
15  *   along with this program; if not, write to the                         *
16  *   Free Software Foundation, Inc.,                                       *
17  *   51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.              *
18  ***************************************************************************/
19 
20 #include <libintl.h>
21 
22 #include "fcitx/instance.h"
23 #include "fcitx/module.h"
24 #include "fcitx/hook.h"
25 #include "fcitx/context.h"
26 #include "fcitx/candidate.h"
27 #include <fcitx/keys.h>
28 #include "fcitx-config/xdg.h"
29 #include "charselectdata.h"
30 
31 typedef struct _UnicodeModule {
32     FcitxGenericConfig gconfig;
33     FcitxHotkey key[2];
34     boolean enable;
35     CharSelectData* charselectdata;
36     char buffer[MAX_USER_INPUT * UTF8_MAX_LENGTH + 1];
37     FcitxInstance* owner;
38     boolean loaded;
39 } UnicodeModule;
40 
41 static void* UnicodeCreate(FcitxInstance* instance);
42 boolean UnicodePreFilter(void* arg, FcitxKeySym sym, unsigned int state,
43                              INPUT_RETURN_VALUE *r);
44 void UnicodeReloadConfig(void* arg);
45 void UnicodeReset(void* arg);
46 INPUT_RETURN_VALUE UnicodeHotkey(void* arg);
47 INPUT_RETURN_VALUE UnicodeGetCandWords(UnicodeModule* uni);
48 
49 FCITX_DEFINE_PLUGIN(fcitx_unicode, module, FcitxModule) = {
50     UnicodeCreate,
51     NULL,
52     NULL,
53     NULL,
54     UnicodeReloadConfig
55 };
56 
57 CONFIG_BINDING_BEGIN(UnicodeModule)
58 CONFIG_BINDING_REGISTER("Unicode", "Key", key)
CONFIG_BINDING_END()59 CONFIG_BINDING_END()
60 
61 CONFIG_DEFINE_LOAD_AND_SAVE(Unicode, UnicodeModule, "fcitx-unicode")
62 
63 void* UnicodeCreate(FcitxInstance* instance)
64 {
65     UnicodeModule* uni = fcitx_utils_new(UnicodeModule);
66     uni->owner = instance;
67     if (!UnicodeLoadConfig(uni)) {
68         free(uni);
69         return NULL;
70     }
71 
72     FcitxIMEventHook imhk;
73     imhk.arg = uni;
74     imhk.func = UnicodeReset;
75     FcitxInstanceRegisterResetInputHook(instance, imhk);
76 
77     FcitxKeyFilterHook kfhk;
78     kfhk.arg = uni;
79     kfhk.func = UnicodePreFilter;
80     FcitxInstanceRegisterPreInputFilter(instance, kfhk);
81 
82     kfhk.arg = &uni->enable;
83     kfhk.func = FcitxDummyReleaseInputHook;
84     FcitxInstanceRegisterPreReleaseInputFilter(instance, kfhk);
85 
86     FcitxHotkeyHook hkhk;
87     hkhk.arg = uni;
88     hkhk.hotkey = uni->key;
89     hkhk.hotkeyhandle = UnicodeHotkey;
90     FcitxInstanceRegisterHotkeyFilter(instance, hkhk);
91 
92     return uni;
93 }
94 
UnicodePreFilter(void * arg,FcitxKeySym sym,unsigned int state,INPUT_RETURN_VALUE * r)95 boolean UnicodePreFilter(void* arg, FcitxKeySym sym, unsigned int state,
96                          INPUT_RETURN_VALUE *r)
97 {
98     INPUT_RETURN_VALUE retVal = IRV_TO_PROCESS;
99     do {
100         UnicodeModule *uni = arg;
101         if (!uni->enable) {
102             return false;
103         }
104         FcitxInstance *instance = uni->owner;
105         FcitxInputState *input = FcitxInstanceGetInputState(instance);
106         FcitxGlobalConfig *fc = FcitxInstanceGetGlobalConfig(instance);
107         FcitxCandidateWordList *candList;
108         FcitxCandidateWord *candWord;
109         candList = FcitxInputStateGetCandidateList(input);
110 
111         FcitxCandidateWordSetPageSize(candList, fc->iMaxCandWord);
112         FcitxCandidateWordSetChooseAndModifier(candList, DIGIT_STR_CHOOSE,
113                                                FcitxKeyState_Alt);
114         if (FcitxHotkeyIsHotKey(sym, state,
115                                 FcitxConfigPrevPageKey(instance, fc))) {
116             if (FcitxCandidateWordGoPrevPage(candList))
117                 retVal = IRV_DISPLAY_MESSAGE;
118             else
119                 retVal = IRV_DO_NOTHING;
120         } else if (FcitxHotkeyIsHotKey(sym, state,
121                                        FcitxConfigNextPageKey(instance, fc))) {
122             if (FcitxCandidateWordGoNextPage(candList))
123                 retVal = IRV_DISPLAY_MESSAGE;
124             else
125                 retVal = IRV_DO_NOTHING;
126         } else if (FcitxHotkeyIsHotKey(sym, state, FCITX_BACKSPACE)) {
127             size_t len = strlen(uni->buffer);
128             if (len > 0)
129                 uni->buffer[--len] = '\0';
130             if (len == 0) {
131                 retVal = IRV_CLEAN;
132             } else {
133                 retVal = UnicodeGetCandWords(uni);
134             }
135         } else if (FcitxHotkeyIsHotKey(sym, state, FCITX_ESCAPE)) {
136             retVal = IRV_CLEAN;
137         } else if (FcitxHotkeyIsHotKey(sym, state, fc->nextWord)) {
138             candWord = FcitxCandidateWordGetFocus(candList, true);
139             candWord = FcitxCandidateWordGetNext(candList, candWord);
140             if (!candWord) {
141                 FcitxCandidateWordSetPage(candList, 0);
142                 candWord = FcitxCandidateWordGetFirst(candList);
143             } else {
144                 FcitxCandidateWordSetFocus(
145                     candList, FcitxCandidateWordGetIndex(candList, candWord));
146             }
147             if (candWord) {
148                 FcitxCandidateWordSetType(candWord, MSG_CANDIATE_CURSOR);
149                 retVal = IRV_FLAG_UPDATE_INPUT_WINDOW;
150             }
151         } else if (FcitxHotkeyIsHotKey(sym, state, fc->prevWord)) {
152             candWord = FcitxCandidateWordGetFocus(candList, true);
153             candWord = FcitxCandidateWordGetPrev(candList, candWord);
154             if (!candWord) {
155                 FcitxCandidateWordSetPage(
156                     candList, FcitxCandidateWordPageCount(candList) - 1);
157                 candWord = FcitxCandidateWordGetLast(candList);
158             } else {
159                 FcitxCandidateWordSetFocus(
160                     candList, FcitxCandidateWordGetIndex(candList, candWord));
161             }
162             if (candWord) {
163                 FcitxCandidateWordSetType(candWord, MSG_CANDIATE_CURSOR);
164                 retVal = IRV_FLAG_UPDATE_INPUT_WINDOW;
165             }
166         } else if (FcitxHotkeyIsHotKey(sym, state, FCITX_ENTER)) {
167             candWord = FcitxCandidateWordGetFocus(candList, true);
168             if (candWord) {
169                 retVal = FcitxCandidateWordChooseByTotalIndex(
170                     candList, FcitxCandidateWordGetIndex(candList, candWord));
171             }
172         }
173 
174         if (retVal == IRV_TO_PROCESS) {
175             int index = FcitxCandidateWordCheckChooseKey(candList, sym, state);
176             if (index >= 0)
177                 retVal = FcitxCandidateWordChooseByIndex(candList, index);
178         }
179 
180         FcitxKeySym keymain = FcitxHotkeyPadToMain(sym);
181         if (retVal == IRV_TO_PROCESS && FcitxHotkeyIsHotKeySimple(keymain, state)) {
182             char buf[2];
183             buf[0] = keymain;
184             buf[1] = '\0';
185             if (strlen(uni->buffer) < MAX_USER_INPUT)
186                 strcat(uni->buffer, buf);
187             retVal = UnicodeGetCandWords(uni);
188         }
189     } while(0);
190 
191     if (retVal == IRV_TO_PROCESS) {
192         retVal = IRV_DO_NOTHING;
193     }
194     *r = retVal;
195     return true;
196 }
197 
UnicodeGetCandWord(void * arg,FcitxCandidateWord * candWord)198 INPUT_RETURN_VALUE UnicodeGetCandWord(void* arg, FcitxCandidateWord* candWord)
199 {
200     UnicodeModule* uni = arg;
201     FcitxInputState *input = FcitxInstanceGetInputState(uni->owner);
202     strcpy(FcitxInputStateGetOutputString(input), candWord->strWord);
203     return IRV_COMMIT_STRING;
204 }
205 
UnicodeGetCandWords(UnicodeModule * uni)206 INPUT_RETURN_VALUE UnicodeGetCandWords(UnicodeModule* uni)
207 {
208     FcitxInputState *input = FcitxInstanceGetInputState(uni->owner);
209     FcitxInstanceCleanInputWindow(uni->owner);
210     FcitxMessagesAddMessageStringsAtLast(FcitxInputStateGetPreedit(input),
211                                          MSG_INPUT, uni->buffer);
212     FcitxInputStateSetShowCursor(input, true);
213     FcitxInputStateSetCursorPos(input, strlen(uni->buffer));
214 
215     FcitxCandidateWordList* candList = FcitxInputStateGetCandidateList(input);
216     FcitxCandidateWordSetLayoutHint(candList, CLH_Vertical);
217 
218     UT_array* result = CharSelectDataFind(uni->charselectdata, uni->buffer);
219     utarray_foreach(c, result, uint32_t) {
220         char* s = fcitx_utils_malloc0(sizeof(char) * (UTF8_MAX_LENGTH + 1));
221         fcitx_ucs4_to_utf8(*c, s);
222         FcitxCandidateWord candWord;
223         candWord.callback = UnicodeGetCandWord;
224         candWord.owner = uni;
225         candWord.priv = NULL;
226         candWord.extraType = MSG_OTHER;
227         candWord.wordType = MSG_CODE;
228         candWord.strWord = s;
229         char* name = CharSelectDataName(uni->charselectdata, *c);
230         fcitx_utils_alloc_cat_str(candWord.strExtra, " ", name);
231         free(name);
232         FcitxCandidateWordAppend(candList, &candWord);
233     }
234     utarray_free(result);
235     if (FcitxCandidateWordGetListSize(candList)) {
236         FcitxCandidateWordSetType(FcitxCandidateWordGetFirst(candList),
237                                   MSG_CANDIATE_CURSOR);
238     }
239     return IRV_FLAG_UPDATE_INPUT_WINDOW;
240 }
241 
UnicodeReloadConfig(void * arg)242 void UnicodeReloadConfig(void* arg)
243 {
244     UnicodeModule* uni = arg;
245     UnicodeLoadConfig(uni);
246 }
247 
UnicodeReset(void * arg)248 void UnicodeReset(void* arg)
249 {
250     UnicodeModule* uni = arg;
251     uni->enable = false;
252     uni->buffer[0] = '\0';
253 }
254 
UnicodeHotkey(void * arg)255 INPUT_RETURN_VALUE UnicodeHotkey(void* arg)
256 {
257     UnicodeModule* uni = arg;
258 
259     if (!uni->loaded) {
260         uni->charselectdata = CharSelectDataCreate();
261         uni->loaded = true;
262     }
263 
264     if (!uni->charselectdata)
265         return IRV_TO_PROCESS;
266     uni->enable = true;
267 
268     FcitxInstanceCleanInputWindow(uni->owner);
269     FcitxInputState *input = FcitxInstanceGetInputState(uni->owner);
270     FcitxInputStateSetShowCursor(input, false);
271     FcitxMessagesAddMessageStringsAtLast(FcitxInputStateGetAuxUp(input),
272                                          MSG_TIPS, _("Search unicode"));
273     return IRV_DISPLAY_MESSAGE;
274 }
275