1 /***************************************************************************
2  *   Copyright (C) 2010~2010 by CSSlayer                                   *
3  *   wengxt@gmail.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 
21 #include <dlfcn.h>
22 #include <libintl.h>
23 #include <stdarg.h>
24 #include <sys/stat.h>
25 
26 #include "ui.h"
27 #include "addon.h"
28 #include "fcitx-utils/utarray.h"
29 #include "fcitx-config/xdg.h"
30 #include "fcitx-utils/log.h"
31 #include "fcitx-utils/utils.h"
32 #include "instance.h"
33 #include "hook-internal.h"
34 #include "ime-internal.h"
35 #include "candidate.h"
36 #include "frontend.h"
37 #include "instance-internal.h"
38 #include "addon-internal.h"
39 #include "ui-internal.h"
40 #include "candidate-internal.h"
41 
42 /**
43  * @file ui.c
44  *
45  * user interface related function.
46  */
47 
48 /**
49  * a single string message
50  **/
51 
52 struct _FcitxMessage {
53     /**
54      * The string of the message
55      **/
56     char            strMsg[MESSAGE_MAX_LENGTH + 1];
57     /**
58      * the type of the message
59      **/
60     FcitxMessageType        type;
61 } ;
62 
63 /**
64  * FcitxMessages to display on the input bar, this cannot be accessed directly
65  **/
66 
67 struct _FcitxMessages {
68     /**
69      * array of message strings
70      **/
71     struct _FcitxMessage msg[MAX_MESSAGE_COUNT];
72     /**
73      * number of message strings
74      **/
75     unsigned int msgCount;
76     /**
77      * the messages is updated or not
78      **/
79     boolean changed;
80 };
81 
82 #define UI_FUNC_IS_VALID(funcname) (!(FcitxInstanceGetCurrentCapacity(instance) & CAPACITY_CLIENT_SIDE_UI) && instance->ui && instance->ui->ui->funcname)
83 #define UI_FUNC_IS_VALID_FALLBACK(funcname) (!(FcitxInstanceGetCurrentCapacity(instance) & CAPACITY_CLIENT_SIDE_UI) && instance->uifallback && instance->uifallback->ui->funcname)
84 
85 static void FcitxUIShowInputWindow(FcitxInstance* instance);
86 static boolean FcitxUILoadInternal(FcitxInstance* instance, FcitxAddon* addon);
87 static void FcitxMenuItemFree(void* arg);
88 
89 static const UT_icd menuICD = {
90     sizeof(FcitxMenuItem), NULL, NULL, FcitxMenuItemFree
91 };
92 
93 FCITX_EXPORT_API
FcitxMessagesNew()94 FcitxMessages* FcitxMessagesNew()
95 {
96     return fcitx_utils_malloc0(sizeof(FcitxMessages));
97 }
98 
99 FCITX_EXPORT_API
FcitxMessagesSetMessageCount(FcitxMessages * m,int s)100 void FcitxMessagesSetMessageCount(FcitxMessages* m, int s)
101 {
102     if ((s) <= MAX_MESSAGE_COUNT && s >= 0)
103         ((m)->msgCount = (s));
104 
105     (m)->changed = true;
106 }
107 
108 FCITX_EXPORT_API
FcitxMessagesGetMessageCount(FcitxMessages * m)109 int FcitxMessagesGetMessageCount(FcitxMessages* m)
110 {
111     return m->msgCount;
112 }
113 
114 FCITX_EXPORT_API
FcitxMessagesIsMessageChanged(FcitxMessages * m)115 boolean FcitxMessagesIsMessageChanged(FcitxMessages* m)
116 {
117     return m->changed;
118 }
119 
120 FCITX_EXPORT_API
FcitxMessagesGetMessageString(FcitxMessages * m,int index)121 char* FcitxMessagesGetMessageString(FcitxMessages* m, int index)
122 {
123     return m->msg[index].strMsg;
124 }
125 
126 FCITX_EXPORT_API
FcitxMessagesGetMessageType(FcitxMessages * m,int index)127 FcitxMessageType FcitxMessagesGetMessageType(FcitxMessages* m, int index)
128 {
129     return m->msg[index].type & MSG_REGULAR_MASK;
130 }
131 
132 FCITX_EXPORT_API
FcitxMessagesGetClientMessageType(FcitxMessages * m,int index)133 FcitxMessageType FcitxMessagesGetClientMessageType(FcitxMessages* m, int index)
134 {
135     return m->msg[index].type;
136 }
137 
138 FCITX_EXPORT_API
FcitxMessagesSetMessageChanged(FcitxMessages * m,boolean changed)139 void FcitxMessagesSetMessageChanged(FcitxMessages* m, boolean changed)
140 {
141     m->changed = changed;
142 }
143 
144 FCITX_EXPORT_API
FcitxUILoad(FcitxInstance * instance)145 void FcitxUILoad(FcitxInstance* instance)
146 {
147     UT_array* addons = &instance->addons;
148     FcitxAddon *addon;
149 
150     for (addon = (FcitxAddon *) utarray_front(addons);
151             addon != NULL;
152             addon = (FcitxAddon *) utarray_next(addons, addon)) {
153         if (addon->bEnabled && addon->category == AC_UI) {
154             if (FcitxUILoadInternal(instance, addon))
155                 instance->uinormal = addon;
156 
157             if (instance->uinormal != NULL)
158                 break;
159         }
160     }
161 
162     instance->ui = instance->uinormal;
163 
164     if (instance->ui == NULL) {
165         FcitxLog(ERROR, "no usable user interface.");
166         return;
167     }
168 
169     if (addon->uifallback)
170         instance->fallbackuiName = strdup(addon->uifallback);
171 }
172 
FcitxUILoadInternal(FcitxInstance * instance,FcitxAddon * addon)173 boolean FcitxUILoadInternal(FcitxInstance* instance, FcitxAddon* addon)
174 {
175     boolean success = false;
176     char *modulePath;
177 
178     switch (addon->type) {
179 
180     case AT_SHAREDLIBRARY: {
181         FILE *fp = FcitxXDGGetLibFile(addon->library, "r", &modulePath);
182         void *handle;
183 
184         if (!fp)
185             break;
186 
187         fclose(fp);
188 
189         handle = dlopen(modulePath, RTLD_NOW | RTLD_NODELETE | (addon->loadLocal ? RTLD_LOCAL : RTLD_GLOBAL));
190 
191         if (!handle) {
192             FcitxLog(ERROR, _("UI: open %s fail %s") , modulePath , dlerror());
193             break;
194         }
195 
196         if (!FcitxCheckABIVersion(handle, addon->name)) {
197             FcitxLog(ERROR, "%s ABI Version Error", addon->name);
198             dlclose(handle);
199             break;
200         }
201 
202         addon->ui = FcitxGetSymbol(handle, addon->name, "ui");
203 
204         if (!addon->ui || !addon->ui->Create) {
205             FcitxLog(ERROR, _("UI: bad ui"));
206             dlclose(handle);
207             break;
208         }
209 
210         if ((addon->addonInstance = addon->ui->Create(instance)) == NULL) {
211             dlclose(handle);
212             break;
213         }
214 
215         /* some may register before ui load, so load it here */
216         if (addon->ui->RegisterStatus) {
217             UT_array* uistats = &instance->uistats;
218             FcitxUIStatus *status;
219 
220             for (status = (FcitxUIStatus *) utarray_front(uistats);
221                     status != NULL;
222                     status = (FcitxUIStatus *) utarray_next(uistats, status))
223                 addon->ui->RegisterStatus(addon->addonInstance, status);
224         }
225 
226         /* some may register before ui load, so load it here */
227         if (addon->ui->RegisterComplexStatus) {
228             UT_array* uicompstats = &instance->uicompstats;
229             FcitxUIComplexStatus *status;
230 
231             for (status = (FcitxUIComplexStatus *) utarray_front(uicompstats);
232                  status != NULL;
233                  status = (FcitxUIComplexStatus *) utarray_next(uicompstats, status))
234                 addon->ui->RegisterComplexStatus(addon->addonInstance, status);
235         }
236 
237         if (addon->ui->RegisterMenu) {
238             UT_array* uimenus = &instance->uimenus;
239             FcitxUIMenu **menupp;
240 
241             for (menupp = (FcitxUIMenu **) utarray_front(uimenus);
242                     menupp != NULL;
243                     menupp = (FcitxUIMenu **) utarray_next(uimenus, menupp))
244                 addon->ui->RegisterMenu(addon->addonInstance, *menupp);
245         }
246 
247         success = true;
248     }
249 
250     break;
251 
252     default:
253         break;
254     }
255 
256     free(modulePath);
257     return success;
258 }
259 
260 FCITX_EXPORT_API
FcitxMessagesAddMessageAtLast(FcitxMessages * message,FcitxMessageType type,const char * fmt,...)261 void FcitxMessagesAddMessageAtLast(FcitxMessages* message, FcitxMessageType type, const char *fmt, ...)
262 {
263 
264     if (message->msgCount < MAX_MESSAGE_COUNT) {
265         va_list ap;
266         va_start(ap, fmt);
267         FcitxMessagesSetMessageV(message, message->msgCount, type, fmt, ap);
268         va_end(ap);
269         message->msgCount ++;
270         message->changed = true;
271     }
272 }
273 
274 FCITX_EXPORT_API void
FcitxMessagesAddMessageVStringAtLast(FcitxMessages * message,FcitxMessageType type,size_t n,const char ** strs)275 FcitxMessagesAddMessageVStringAtLast(FcitxMessages *message,
276                                          FcitxMessageType type, size_t n,
277                                          const char **strs)
278 {
279     if (message->msgCount < MAX_MESSAGE_COUNT) {
280         FcitxMessagesSetMessageStringsReal(message, message->msgCount,
281                                            type, n, strs);
282         message->msgCount ++;
283         message->changed = true;
284     }
285 }
286 
287 FCITX_EXPORT_API
FcitxMessagesSetMessage(FcitxMessages * message,int position,int type,const char * fmt,...)288 void FcitxMessagesSetMessage(FcitxMessages* message, int position, int type, const char* fmt, ...)
289 {
290     va_list ap;
291     va_start(ap, fmt);
292     FcitxMessagesSetMessageV(message, position, type, fmt, ap);
293     va_end(ap);
294 }
295 
296 FCITX_EXPORT_API
FcitxMessagesSetMessageText(FcitxMessages * message,int position,const char * fmt,...)297 void FcitxMessagesSetMessageText(FcitxMessages* message, int position, const char* fmt, ...)
298 {
299     va_list ap;
300     va_start(ap, fmt);
301     FcitxMessagesSetMessageV(message, position, message->msg[position].type, fmt, ap);
302     va_end(ap);
303 }
304 
305 FCITX_EXPORT_API void
FcitxMessagesSetMessageTextVString(FcitxMessages * message,int position,size_t n,const char ** strs)306 FcitxMessagesSetMessageTextVString(FcitxMessages *message, int position,
307                                        size_t n, const char **strs)
308 {
309     FcitxMessagesSetMessageStringsReal(message, position,
310                                        message->msg[position].type, n, strs);
311 }
312 
313 FCITX_EXPORT_API
FcitxMessagesSetMessageV(FcitxMessages * message,int position,int type,const char * fmt,va_list ap)314 void FcitxMessagesSetMessageV(FcitxMessages* message, int position, int type, const char* fmt, va_list ap)
315 {
316     if (position < MAX_MESSAGE_COUNT) {
317         vsnprintf(message->msg[position].strMsg, MESSAGE_MAX_LENGTH, fmt, ap);
318         message->msg[position].type = type;
319         message->changed = true;
320     }
321 }
322 
323 FCITX_EXPORT_API
FcitxMessagesSetMessageStringsReal(FcitxMessages * message,int position,int type,size_t n,const char ** strs)324 void FcitxMessagesSetMessageStringsReal(FcitxMessages *message, int position,
325                                         int type, size_t n, const char **strs)
326 {
327     if (position < MAX_MESSAGE_COUNT) {
328         fcitx_utils_cat_str_simple_with_len(message->msg[position].strMsg,
329                                             MESSAGE_MAX_LENGTH + 1, n, strs);
330         message->msg[position].type = type;
331         message->changed = true;
332     }
333 }
334 
335 FCITX_EXPORT_API
FcitxMessagesMessageConcatLast(FcitxMessages * message,const char * text)336 void FcitxMessagesMessageConcatLast(FcitxMessages* message, const char* text)
337 {
338     strncat(message->msg[message->msgCount - 1].strMsg,
339             text, MESSAGE_MAX_LENGTH);
340     message->changed = true;
341 }
342 
343 FCITX_EXPORT_API
FcitxMessagesMessageConcat(FcitxMessages * message,int position,const char * text)344 void FcitxMessagesMessageConcat(FcitxMessages* message, int position, const char* text)
345 {
346     strncat(message->msg[position].strMsg, text, MESSAGE_MAX_LENGTH);
347     message->changed = true;
348 }
349 
350 FCITX_EXPORT_API
FcitxUICloseInputWindow(FcitxInstance * instance)351 void FcitxUICloseInputWindow(FcitxInstance* instance)
352 {
353     FcitxInstanceCleanInputWindow(instance);
354     instance->eventflag |= FEF_UI_UPDATE;
355 }
356 
357 FCITX_EXPORT_API
FcitxUIUpdateInputWindow(FcitxInstance * instance)358 void FcitxUIUpdateInputWindow(FcitxInstance *instance)
359 {
360     instance->eventflag |= FEF_UI_UPDATE;
361 }
362 
FcitxUIShowInputWindow(FcitxInstance * instance)363 void FcitxUIShowInputWindow(FcitxInstance* instance)
364 {
365     if (UI_FUNC_IS_VALID(ShowInputWindow))
366         instance->ui->ui->ShowInputWindow(instance->ui->addonInstance);
367 }
368 
369 FCITX_EXPORT_API
FcitxUIMoveInputWindow(FcitxInstance * instance)370 void FcitxUIMoveInputWindow(FcitxInstance* instance)
371 {
372     instance->eventflag |= FEF_UI_MOVE;
373 }
374 
375 FCITX_EXPORT_API
FcitxUIGetStatusByName(FcitxInstance * instance,const char * name)376 FcitxUIStatus *FcitxUIGetStatusByName(FcitxInstance* instance, const char* name)
377 {
378     UT_array* uistats = &instance->uistats;
379     FcitxUIStatus *status;
380 
381     for (status = (FcitxUIStatus *) utarray_front(uistats);
382          status != NULL;
383          status = (FcitxUIStatus *) utarray_next(uistats, status))
384         if (strcmp(status->name, name) == 0)
385             break;
386 
387     return status;
388 }
389 
390 FCITX_EXPORT_API
FcitxUIGetComplexStatusByName(FcitxInstance * instance,const char * name)391 FcitxUIComplexStatus *FcitxUIGetComplexStatusByName(FcitxInstance* instance, const char* name)
392 {
393     UT_array* uicompstats = &instance->uicompstats;
394     FcitxUIComplexStatus *compstatus;
395 
396     for (compstatus = (FcitxUIComplexStatus *) utarray_front(uicompstats);
397          compstatus != NULL;
398          compstatus = (FcitxUIComplexStatus *) utarray_next(uicompstats, compstatus))
399         if (strcmp(compstatus->name, name) == 0)
400             break;
401 
402     return compstatus;
403 }
404 
FcitxUICallUpdateStatus(FcitxInstance * instance,FcitxUIStatus * status)405 static inline void FcitxUICallUpdateStatus(FcitxInstance* instance, FcitxUIStatus* status)
406 {
407     if (UI_FUNC_IS_VALID(UpdateStatus))
408         instance->ui->ui->UpdateStatus(instance->ui->addonInstance, status);
409     FcitxInstanceProcessUIStatusChangedHook(instance, status->name);
410 }
411 
FcitxUICallUpdateComplexStatus(FcitxInstance * instance,FcitxUIComplexStatus * status)412 static inline void FcitxUICallUpdateComplexStatus(FcitxInstance* instance, FcitxUIComplexStatus* status)
413 {
414     if (UI_FUNC_IS_VALID(UpdateComplexStatus))
415         instance->ui->ui->UpdateComplexStatus(instance->ui->addonInstance, status);
416     FcitxInstanceProcessUIStatusChangedHook(instance, status->name);
417 }
418 
419 FCITX_EXPORT_API
FcitxUIRefreshStatus(FcitxInstance * instance,const char * name)420 void FcitxUIRefreshStatus(FcitxInstance* instance, const char* name)
421 {
422     FcitxUIStatus *status = FcitxUIGetStatusByName(instance, name);
423 
424     if (status != NULL) {
425         FcitxUICallUpdateStatus(instance, status);
426     }
427     else {
428         FcitxUIComplexStatus *compstatus = FcitxUIGetComplexStatusByName(instance, name);
429         if (!compstatus)
430             return;
431         FcitxUICallUpdateComplexStatus(instance, compstatus);
432     }
433 }
434 
435 FCITX_EXPORT_API
FcitxUIUpdateStatus(FcitxInstance * instance,const char * name)436 void FcitxUIUpdateStatus(FcitxInstance* instance, const char* name)
437 {
438     FcitxLog(DEBUG, "Update Status for %s", name);
439 
440     FcitxUIStatus *status = FcitxUIGetStatusByName(instance, name);
441 
442     if (status != NULL) {
443         if (status->toggleStatus)
444             status->toggleStatus(status->arg);
445 
446         FcitxUICallUpdateStatus(instance, status);
447     }
448     else {
449         FcitxUIComplexStatus *compstatus = FcitxUIGetComplexStatusByName(instance, name);
450         if (!compstatus)
451             return;
452 
453         if (compstatus->toggleStatus)
454             compstatus->toggleStatus(compstatus->arg);
455 
456         FcitxUICallUpdateComplexStatus(instance, compstatus);
457     }
458 }
459 
460 FCITX_EXPORT_API
FcitxUISetStatusString(FcitxInstance * instance,const char * name,const char * shortDesc,const char * longDesc)461 void FcitxUISetStatusString(FcitxInstance* instance, const char* name, const char* shortDesc, const char* longDesc)
462 {
463     char** pShort = NULL, **pLong = NULL;
464     FcitxUIStatus *status = FcitxUIGetStatusByName(instance, name);
465     FcitxUIComplexStatus *compstatus = NULL;
466     if (!status) {
467         compstatus = FcitxUIGetComplexStatusByName(instance, name);
468         if (!compstatus)
469             return;
470 
471         pShort = &compstatus->shortDescription;
472         pLong = &compstatus->longDescription;
473     }
474     else {
475         pShort = &status->shortDescription;
476         pLong = &status->longDescription;
477     }
478 
479     if (*pShort)
480         free(*pShort);
481 
482     if (*pLong)
483         free(*pLong);
484 
485     *pShort = strdup(shortDesc);
486     *pLong = strdup(longDesc);
487 
488     if (status){
489         FcitxUICallUpdateStatus(instance, status);
490     }
491     else if (compstatus) {
492         FcitxUICallUpdateComplexStatus(instance, compstatus);
493     }
494 }
495 
496 FCITX_EXPORT_API
FcitxUISetStatusVisable(FcitxInstance * instance,const char * name,boolean visible)497 void FcitxUISetStatusVisable(FcitxInstance* instance, const char* name, boolean visible)
498 {
499     FcitxUIStatus *status = FcitxUIGetStatusByName(instance, name);
500     if (!status) {
501         FcitxUIComplexStatus *compstatus = FcitxUIGetComplexStatusByName(instance, name);
502         if (!compstatus)
503             return;
504 
505         if (compstatus->visible != visible) {
506             compstatus->visible = visible;
507 
508             FcitxUICallUpdateComplexStatus(instance, compstatus);
509         }
510         return;
511     }
512 
513     if (status->visible != visible) {
514         status->visible = visible;
515 
516         FcitxUICallUpdateStatus(instance, status);
517     }
518 }
519 
520 FCITX_EXPORT_API
FcitxUIRegisterStatus(struct _FcitxInstance * instance,void * arg,const char * name,const char * shortDesc,const char * longDesc,void (* toggleStatus)(void * arg),boolean (* getStatus)(void * arg))521 void FcitxUIRegisterStatus(
522     struct _FcitxInstance* instance,
523     void* arg,
524     const char* name,
525     const char* shortDesc,
526     const char* longDesc,
527     void (*toggleStatus)(void *arg),
528     boolean(*getStatus)(void *arg)
529 )
530 {
531     FcitxUIStatus status;
532 
533     memset(&status, 0 , sizeof(FcitxUIStatus));
534     status.name = strdup(name);
535     status.shortDescription = strdup(shortDesc);
536     status.longDescription = strdup(longDesc);
537     status.getCurrentStatus = getStatus;
538     status.toggleStatus = toggleStatus;
539     status.arg = arg;
540     status.visible = true;
541 
542     UT_array* uistats = &instance->uistats;
543 
544     utarray_push_back(uistats, &status);
545     if (UI_FUNC_IS_VALID(RegisterStatus))
546         instance->ui->ui->RegisterStatus(instance->ui->addonInstance, (FcitxUIStatus*) utarray_back(uistats));
547     if (UI_FUNC_IS_VALID_FALLBACK(RegisterStatus))
548         instance->uifallback->ui->RegisterStatus(instance->uifallback->addonInstance, (FcitxUIStatus*) utarray_back(uistats));
549 }
550 
551 FCITX_EXPORT_API
FcitxUIRegisterComplexStatus(struct _FcitxInstance * instance,void * arg,const char * name,const char * shortDesc,const char * longDesc,void (* toggleStatus)(void * arg),const char * (* getIconName)(void * arg))552 void FcitxUIRegisterComplexStatus(
553     struct _FcitxInstance* instance,
554     void* arg,
555     const char* name,
556     const char* shortDesc,
557     const char* longDesc,
558     void (*toggleStatus)(void *arg),
559     const char*(*getIconName)(void *arg)
560 )
561 {
562     FcitxUIComplexStatus compstatus;
563 
564     memset(&compstatus, 0 , sizeof(FcitxUIComplexStatus));
565     compstatus.name = strdup(name);
566     compstatus.shortDescription = strdup(shortDesc);
567     compstatus.longDescription = strdup(longDesc);
568     compstatus.getIconName = getIconName;
569     compstatus.toggleStatus = toggleStatus;
570     compstatus.arg = arg;
571     compstatus.visible = true;
572 
573     UT_array* uicompstats = &instance->uicompstats;
574 
575     utarray_push_back(uicompstats, &compstatus);
576     if (UI_FUNC_IS_VALID(RegisterComplexStatus))
577         instance->ui->ui->RegisterComplexStatus(instance->ui->addonInstance, (FcitxUIComplexStatus*) utarray_back(uicompstats));
578     if (UI_FUNC_IS_VALID_FALLBACK(RegisterComplexStatus))
579         instance->uifallback->ui->RegisterComplexStatus(instance->uifallback->addonInstance, (FcitxUIComplexStatus*) utarray_back(uicompstats));
580 }
581 
582 FCITX_EXPORT_API
FcitxUIRegisterMenu(FcitxInstance * instance,FcitxUIMenu * menu)583 void FcitxUIRegisterMenu(FcitxInstance* instance, FcitxUIMenu* menu)
584 {
585     UT_array* uimenus = &instance->uimenus;
586 
587     if (!menu)
588         return ;
589 
590     menu->mark = -1;
591     menu->visible = true;
592 
593     utarray_push_back(uimenus, &menu);
594     if (UI_FUNC_IS_VALID(RegisterMenu))
595         instance->ui->ui->RegisterMenu(instance->ui->addonInstance, menu);
596     if (UI_FUNC_IS_VALID_FALLBACK(RegisterMenu))
597         instance->uifallback->ui->RegisterMenu(instance->uifallback->addonInstance, menu);
598 }
599 
600 FCITX_EXPORT_API
FcitxUIUnRegisterMenu(FcitxInstance * instance,FcitxUIMenu * menu)601 void FcitxUIUnRegisterMenu(FcitxInstance* instance, FcitxUIMenu* menu)
602 {
603     UT_array* uimenus = &instance->uimenus;
604 
605     if (!menu)
606         return;
607 
608     utarray_foreach(menup, uimenus, FcitxUIMenu*) {
609         if (*menup == menu) {
610             utarray_remove_quick(uimenus, utarray_eltidx(uimenus, menup));
611 
612             if (UI_FUNC_IS_VALID(UnRegisterMenu))
613                 instance->ui->ui->UnRegisterMenu(instance->ui->addonInstance, menu);
614             if (UI_FUNC_IS_VALID_FALLBACK(UnRegisterMenu))
615                 instance->uifallback->ui->UnRegisterMenu(instance->uifallback->addonInstance, menu);
616             break;
617         }
618     }
619 
620 }
621 
622 FCITX_EXPORT_API
FcitxMenuFinalize(FcitxUIMenu * menu)623 void FcitxMenuFinalize(FcitxUIMenu* menu)
624 {
625     utarray_done(&menu->shell);
626 }
627 
628 FCITX_EXPORT_API
FcitxMenuAddMenuItemWithData(FcitxUIMenu * menu,const char * string,FcitxMenuItemType type,FcitxUIMenu * subMenu,void * arg)629 void FcitxMenuAddMenuItemWithData(FcitxUIMenu* menu, const char* string, FcitxMenuItemType type, FcitxUIMenu* subMenu, void* arg)
630 {
631     FcitxMenuItem shell;
632     memset(&shell, 0, sizeof(FcitxMenuItem));
633 
634     if (string == NULL && type != MENUTYPE_DIVLINE) {
635         return;
636     }
637     if (string)
638         shell.tipstr = strdup(string);
639     else
640         shell.tipstr = NULL;
641 
642     shell.type = type;
643     shell.data = arg;
644 
645     shell.isselect = false;
646 
647     if (type == MENUTYPE_SUBMENU)
648         shell.subMenu = subMenu;
649 
650     utarray_push_back(&menu->shell, &shell);
651 }
652 
653 
654 FCITX_EXPORT_API
FcitxMenuAddMenuItem(FcitxUIMenu * menu,const char * string,FcitxMenuItemType type,FcitxUIMenu * subMenu)655 void FcitxMenuAddMenuItem(FcitxUIMenu* menu, const char* string, FcitxMenuItemType type, FcitxUIMenu* subMenu)
656 {
657     FcitxMenuAddMenuItemWithData(menu, string, type, subMenu, NULL);
658 }
659 
660 FCITX_EXPORT_API
FcitxMenuClear(FcitxUIMenu * menu)661 void FcitxMenuClear(FcitxUIMenu* menu)
662 {
663     utarray_clear(&menu->shell);
664 }
665 
666 FCITX_EXPORT_API
FcitxUIOnInputFocus(FcitxInstance * instance)667 void FcitxUIOnInputFocus(FcitxInstance* instance)
668 {
669     if (UI_FUNC_IS_VALID(OnInputFocus))
670         instance->ui->ui->OnInputFocus(instance->ui->addonInstance);
671 
672     FcitxInstanceProcessInputFocusHook(instance);
673 
674     FcitxInstanceResetInput(instance);
675 
676     boolean changed;
677 
678     if (instance->lastIC == instance->CurrentIC && instance->delayedIM) {
679         FcitxInstanceSwitchIMByName(instance, instance->delayedIM);
680         changed = true;
681     } else {
682         changed = FcitxInstanceUpdateCurrentIM(instance, false, false);
683     }
684 
685     if (instance->config->bShowInputWindowWhenFocusIn && changed)
686        FcitxInstanceShowInputSpeed(instance, false);
687     else
688        FcitxUICloseInputWindow(instance);
689 }
690 
691 FCITX_EXPORT_API
FcitxUIOnInputUnFocus(struct _FcitxInstance * instance)692 void FcitxUIOnInputUnFocus(struct _FcitxInstance* instance)
693 {
694     if (UI_FUNC_IS_VALID(OnInputUnFocus))
695         instance->ui->ui->OnInputUnFocus(instance->ui->addonInstance);
696 
697     FcitxInstanceProcessInputUnFocusHook(instance);
698 }
699 
700 FCITX_EXPORT_API
FcitxUICommitPreedit(FcitxInstance * instance)701 void FcitxUICommitPreedit(FcitxInstance* instance)
702 {
703     if (!instance->CurrentIC)
704         return;
705 
706     boolean callOnClose = false;
707     boolean doServerSideCommit = false;
708     if (!instance->config->bDontCommitPreeditWhenUnfocus
709         && !(instance->CurrentIC->contextCaps & CAPACITY_CLIENT_UNFOCUS_COMMIT)) {
710         callOnClose = true;
711         doServerSideCommit = true;
712     }
713 
714     if (instance->CurrentIC->contextCaps & CAPACITY_CLIENT_UNFOCUS_COMMIT) {
715         callOnClose = true;
716     }
717 
718     if (callOnClose) {
719         FcitxIM* im = FcitxInstanceGetCurrentIM(instance);
720         if (im && im->OnClose) {
721             im->OnClose(im->klass, CET_LostFocus);
722         }
723     }
724 
725     if (doServerSideCommit) {
726         FcitxInputState* input = FcitxInstanceGetInputState(instance);
727         FcitxMessages* clientPreedit = FcitxInputStateGetClientPreedit(input);
728 
729         if (FcitxMessagesGetMessageCount(clientPreedit) > 0) {
730             char* str = FcitxUIMessagesToCStringForCommit(clientPreedit);
731             if (str[0]) {
732                 FcitxInstanceCommitString(instance, instance->CurrentIC, str);
733             }
734             free(str);
735         }
736         FcitxMessagesSetMessageCount(clientPreedit, 0);
737     }
738 
739 }
740 
741 FCITX_EXPORT_API
FcitxUIOnTriggerOn(FcitxInstance * instance)742 void FcitxUIOnTriggerOn(FcitxInstance* instance)
743 {
744     if (UI_FUNC_IS_VALID(OnTriggerOn))
745         instance->ui->ui->OnTriggerOn(instance->ui->addonInstance);
746 
747     FcitxInstanceProcessTriggerOnHook(instance);
748 
749     instance->timeStart = time(NULL);
750 
751     FcitxInstanceShowInputSpeed(instance, false);
752 }
753 
754 FCITX_EXPORT_API
FcitxUIDisplayMessage(FcitxInstance * instance,char * title,char ** msg,int length)755 void FcitxUIDisplayMessage(FcitxInstance *instance, char *title, char **msg, int length)
756 {
757     if (UI_FUNC_IS_VALID(DisplayMessage))
758         instance->ui->ui->DisplayMessage(instance->ui->addonInstance, title, msg, length);
759 }
760 
761 FCITX_EXPORT_API
FcitxUIOnTriggerOff(FcitxInstance * instance)762 void FcitxUIOnTriggerOff(FcitxInstance* instance)
763 {
764     if (UI_FUNC_IS_VALID(OnTriggerOff))
765         instance->ui->ui->OnTriggerOff(instance->ui->addonInstance);
766 
767     FcitxInstanceProcessTriggerOffHook(instance);
768 
769     instance->totaltime += difftime(time(NULL), instance->timeStart);
770 }
771 
772 FCITX_EXPORT_API
FcitxMenuUpdate(FcitxUIMenu * menu)773 void FcitxMenuUpdate(FcitxUIMenu* menu)
774 {
775     if (menu && menu->UpdateMenu) {
776         menu->UpdateMenu(menu);
777     }
778 }
779 
780 /*
781  * 判断鼠标点击处是否处于指定的区域内
782  */
783 FCITX_EXPORT_API
784 boolean
FcitxUIIsInBox(int x0,int y0,int x1,int y1,int w,int h)785 FcitxUIIsInBox(int x0, int y0, int x1, int y1, int w, int h)
786 {
787     if (x0 >= x1 && x0 <= x1 + w && y0 >= y1 && y0 <= y1 + h)
788         return true;
789 
790     return false;
791 }
792 
793 FCITX_EXPORT_API
FcitxUISupportMainWindow(FcitxInstance * instance)794 boolean FcitxUISupportMainWindow(FcitxInstance* instance)
795 {
796     if (UI_FUNC_IS_VALID(MainWindowSizeHint))
797         return true;
798     else
799         return false;
800 }
801 
802 FCITX_EXPORT_API
FcitxUIGetMainWindowSize(FcitxInstance * instance,int * x,int * y,int * w,int * h)803 void FcitxUIGetMainWindowSize(FcitxInstance* instance, int* x, int* y, int* w, int* h)
804 {
805     if (UI_FUNC_IS_VALID(MainWindowSizeHint))
806         instance->ui->ui->MainWindowSizeHint(instance->ui->addonInstance, x, y, w, h);
807 }
808 
FcitxUIUseDefaultHighlight(FcitxInstance * instance,FcitxCandidateWordList * candList)809 static inline boolean FcitxUIUseDefaultHighlight(FcitxInstance* instance, FcitxCandidateWordList* candList)
810 {
811     if (candList->overrideHighlight) {
812         return candList->overrideHighlightValue;
813     } else {
814         return !FcitxInstanceGetContextBoolean(instance, CONTEXT_DISABLE_AUTO_FIRST_CANDIDATE_HIGHTLIGHT);
815     }
816 }
817 
818 FCITX_EXPORT_API
FcitxUINewMessageToOldStyleMessage(FcitxInstance * instance,FcitxMessages * msgUp,FcitxMessages * msgDown)819 int FcitxUINewMessageToOldStyleMessage(FcitxInstance* instance, FcitxMessages* msgUp, FcitxMessages* msgDown)
820 {
821     int i = 0;
822     FcitxInputState* input = instance->input;
823     int extraLength = input->iCursorPos;
824     FcitxMessagesSetMessageCount(msgUp, 0);
825     FcitxMessagesSetMessageCount(msgDown, 0);
826 
827     for (i = 0; i < FcitxMessagesGetMessageCount(input->msgAuxUp) ; i ++) {
828         FcitxMessagesAddMessageStringsAtLast(msgUp, FcitxMessagesGetMessageType(input->msgAuxUp, i), FcitxMessagesGetMessageString(input->msgAuxUp, i));
829         extraLength += strlen(FcitxMessagesGetMessageString(input->msgAuxUp, i));
830     }
831 
832     for (i = 0; i < FcitxMessagesGetMessageCount(input->msgPreedit) ; i ++)
833         FcitxMessagesAddMessageStringsAtLast(msgUp, FcitxMessagesGetMessageType(input->msgPreedit, i), FcitxMessagesGetMessageString(input->msgPreedit, i));
834 
835     for (i = 0; i < FcitxMessagesGetMessageCount(input->msgAuxDown) ; i ++)
836         FcitxMessagesAddMessageStringsAtLast(msgDown, FcitxMessagesGetMessageType(input->msgAuxDown, i), FcitxMessagesGetMessageString(input->msgAuxDown, i));
837 
838     FcitxCandidateWord* candWord = NULL;
839 
840     for (candWord = FcitxCandidateWordGetCurrentWindow(input->candList), i = 0;
841             candWord != NULL;
842             candWord = FcitxCandidateWordGetCurrentWindowNext(input->candList, candWord), i ++) {
843         char strTemp[3] = { '\0', '\0', '\0' };
844         strTemp[0] = FcitxCandidateWordGetChoose(input->candList)[i];
845 
846         if (instance->config->bPointAfterNumber)
847             strTemp[1] = '.';
848 
849         if (candWord->strWord == NULL)
850             continue;
851 
852         unsigned int mod = FcitxCandidateWordGetModifier(input->candList);
853 
854         FcitxMessagesAddMessageStringsAtLast(
855             msgDown, MSG_INDEX, (mod & FcitxKeyState_Super) ? "M-" : "",
856             (mod & FcitxKeyState_Ctrl) ? "C-" : "",
857             (mod & FcitxKeyState_Alt) ? "A-" : "",
858             (mod & FcitxKeyState_Shift) ? "S-" : "", strTemp);
859 
860         FcitxMessageType type = candWord->wordType;
861 
862         if (i == 0
863             && FcitxCandidateWordGetCurrentPage(input->candList) == 0
864             && type == MSG_OTHER
865             && FcitxUIUseDefaultHighlight(instance, input->candList)
866         ) {
867             type = MSG_FIRSTCAND;
868         }
869 
870         FcitxMessagesAddMessageStringsAtLast(msgDown, type, candWord->strWord);
871 
872         if (candWord->strExtra && strlen(candWord->strExtra))
873             FcitxMessagesAddMessageStringsAtLast(msgDown, candWord->extraType,
874                                                  candWord->strExtra);
875 
876         FcitxMessagesAddMessageStringsAtLast(msgDown, MSG_OTHER, " ");
877     }
878 
879     return extraLength;
880 }
881 
882 FCITX_EXPORT_API
FcitxUIMessagesToCString(FcitxMessages * messages)883 char* FcitxUIMessagesToCString(FcitxMessages* messages)
884 {
885     int length = 0;
886     int i = 0;
887     int count = FcitxMessagesGetMessageCount(messages);
888     char *message_strs[count];
889     int msg_count = 0;
890 
891     for (i = 0;i < count;i++) {
892         char *msg_str = FcitxMessagesGetMessageString(messages, i);
893         message_strs[msg_count++] = msg_str;
894         length += strlen(msg_str);
895     }
896 
897     char *str = fcitx_utils_malloc0(sizeof(char) * (length + 1));
898 
899     for (i = 0;i < msg_count;i++) {
900         strcat(str, message_strs[i]);
901     }
902 
903     return str;
904 }
905 
FcitxUIMessagesToCStringForCommit(FcitxMessages * messages)906 char* FcitxUIMessagesToCStringForCommit(FcitxMessages* messages)
907 {
908     int length = 0;
909     int i = 0;
910     int count = FcitxMessagesGetMessageCount(messages);
911     char *message_strs[count];
912     int msg_count = 0;
913 
914     for (i = 0;i < count;i++) {
915         if ((FcitxMessagesGetClientMessageType(messages, i) &
916              MSG_DONOT_COMMIT_WHEN_UNFOCUS) == 0) {
917             char *msg_str = FcitxMessagesGetMessageString(messages, i);
918             message_strs[msg_count++] = msg_str;
919             length += strlen(msg_str);
920         }
921     }
922 
923     char* str = fcitx_utils_malloc0(sizeof(char) * (length + 1));
924 
925     for (i = 0;i < msg_count;i++) {
926         strcat(str, message_strs[i]);
927     }
928 
929     return str;
930 }
931 
932 
933 FCITX_EXPORT_API
FcitxUICandidateWordToCString(FcitxInstance * instance)934 char* FcitxUICandidateWordToCString(FcitxInstance* instance)
935 {
936     size_t len = 0;
937     int i;
938     FcitxInputState* input = instance->input;
939     FcitxCandidateWord* candWord;
940     for (candWord = FcitxCandidateWordGetCurrentWindow(input->candList), i = 0;
941             candWord != NULL;
942             candWord = FcitxCandidateWordGetCurrentWindowNext(input->candList, candWord), i ++) {
943         char strTemp[3] = { '\0', '\0', '\0' };
944         strTemp[0] = FcitxCandidateWordGetChoose(input->candList)[i];
945 
946         if (instance->config->bPointAfterNumber)
947             strTemp[1] = '.';
948 
949         len += strlen(strTemp);
950         len += strlen(candWord->strWord);
951 
952         if (candWord->strExtra && strlen(candWord->strExtra))
953             len += strlen(candWord->strExtra);
954 
955         len++;
956     }
957 
958     char *result = fcitx_utils_malloc0(sizeof(char) * (len + 1));
959 
960     for (candWord = FcitxCandidateWordGetCurrentWindow(input->candList), i = 0;
961             candWord != NULL;
962             candWord = FcitxCandidateWordGetCurrentWindowNext(input->candList, candWord), i ++) {
963         char strTemp[3] = { '\0', '\0', '\0' };
964         strTemp[0] = FcitxCandidateWordGetChoose(input->candList)[i];
965 
966         if (instance->config->bPointAfterNumber)
967             strTemp[1] = '.';
968 
969         strcat(result, strTemp);
970         strcat(result, candWord->strWord);
971 
972         if (candWord->strExtra && strlen(candWord->strExtra))
973             strcat(result, candWord->strExtra);
974 
975         strcat(result, " ");
976     }
977 
978     return result;
979 }
980 
FcitxUIUpdateInputWindowReal(FcitxInstance * instance)981 void FcitxUIUpdateInputWindowReal(FcitxInstance *instance)
982 {
983     FcitxInputState* input = instance->input;
984     FcitxInputContext* ic = FcitxInstanceGetCurrentIC(instance);
985     FcitxCapacityFlags flags = CAPACITY_NONE;
986     if (ic != NULL)
987         flags = ic->contextCaps;
988 
989     if (flags & CAPACITY_CLIENT_SIDE_UI) {
990         FcitxInstanceUpdateClientSideUI(instance, ic);
991         return;
992     }
993 
994     FcitxInstanceUpdatePreedit(instance, ic);
995 
996     boolean toshow = false;
997 
998     if (FcitxMessagesGetMessageCount(input->msgAuxUp) != 0
999         || FcitxMessagesGetMessageCount(input->msgAuxDown) != 0)
1000         toshow = true;
1001 
1002     if (FcitxCandidateWordGetListSize(input->candList) > 1)
1003         toshow = true;
1004 
1005     if (FcitxCandidateWordGetListSize(input->candList) == 1
1006             && (!instance->config->bHideInputWindowWhenOnlyPreeditString
1007                 || !instance->config->bHideInputWindowWhenOnlyOneCandidate))
1008         toshow = true;
1009 
1010     if (FcitxMessagesGetMessageCount(input->msgPreedit) != 0
1011             && !((flags & CAPACITY_PREEDIT) && instance->config->bHideInputWindowWhenOnlyPreeditString && instance->profile->bUsePreedit))
1012         toshow = true;
1013 
1014     if (!toshow) {
1015         if (UI_FUNC_IS_VALID(CloseInputWindow))
1016             instance->ui->ui->CloseInputWindow(instance->ui->addonInstance);
1017     } else
1018         FcitxUIShowInputWindow(instance);
1019 
1020     FcitxMessagesSetMessageChanged(input->msgAuxUp, false);
1021     FcitxMessagesSetMessageChanged(input->msgAuxDown, false);
1022     FcitxMessagesSetMessageChanged(input->msgPreedit, false);
1023     FcitxMessagesSetMessageChanged(input->msgClientPreedit, false);
1024 }
1025 
FcitxUIMoveInputWindowReal(FcitxInstance * instance)1026 void FcitxUIMoveInputWindowReal(FcitxInstance *instance)
1027 {
1028     if (UI_FUNC_IS_VALID(MoveInputWindow))
1029         instance->ui->ui->MoveInputWindow(instance->ui->addonInstance);
1030 }
1031 
1032 FCITX_EXPORT_API
FcitxUISwitchToFallback(struct _FcitxInstance * instance)1033 void FcitxUISwitchToFallback(struct _FcitxInstance* instance)
1034 {
1035     if (!instance->fallbackuiName || instance->ui != instance->uinormal)
1036         return;
1037 
1038     if (!instance->uifallback) {
1039         // load fallback ui
1040         FcitxAddon* fallbackAddon = FcitxAddonsGetAddonByName(&instance->addons, instance->fallbackuiName);
1041         if (!fallbackAddon || !fallbackAddon->bEnabled || !FcitxUILoadInternal(instance, fallbackAddon)) {
1042             // reset fallbackuiName, never load it again and again
1043             free(instance->fallbackuiName);
1044             instance->fallbackuiName = NULL;
1045             return;
1046         }
1047         instance->uifallback = fallbackAddon;
1048         if (instance->uifallback->ui->Suspend)
1049             instance->uifallback->ui->Suspend(instance->uifallback->addonInstance);
1050     }
1051 
1052     if (instance->uinormal->ui->Suspend)
1053         instance->uinormal->ui->Suspend(instance->uinormal->addonInstance);
1054 
1055     if (instance->uifallback->ui->Resume)
1056         instance->uifallback->ui->Resume(instance->uifallback->addonInstance);
1057 
1058     instance->ui = instance->uifallback;
1059 }
1060 
1061 FCITX_EXPORT_API
FcitxUIResumeFromFallback(struct _FcitxInstance * instance)1062 void FcitxUIResumeFromFallback(struct _FcitxInstance* instance)
1063 {
1064     if (!instance->uifallback || instance->ui != instance->uifallback)
1065         return;
1066     if (instance->uifallback->ui->Suspend)
1067         instance->uifallback->ui->Suspend(instance->uifallback->addonInstance);
1068 
1069     if (instance->uinormal->ui->Resume)
1070         instance->uinormal->ui->Resume(instance->uinormal->addonInstance);
1071 
1072     instance->ui = instance->uinormal;
1073 
1074 }
1075 
1076 FCITX_EXPORT_API
FcitxUIIsFallback(struct _FcitxInstance * instance,struct _FcitxAddon * addon)1077 boolean FcitxUIIsFallback(struct _FcitxInstance* instance, struct _FcitxAddon* addon)
1078 {
1079     return instance->fallbackuiName != NULL && strcmp(instance->fallbackuiName, addon->name) == 0;
1080 }
1081 
1082 FCITX_EXPORT_API
FcitxMenuInit(FcitxUIMenu * menu)1083 void FcitxMenuInit(FcitxUIMenu* menu)
1084 {
1085     memset(menu, 0, sizeof(FcitxUIMenu));
1086     utarray_init(&menu->shell, &menuICD);
1087 }
1088 
FcitxMenuItemFree(void * arg)1089 void FcitxMenuItemFree(void* arg)
1090 {
1091     FcitxMenuItem* item = arg;
1092     if (item->tipstr)
1093         free(item->tipstr);
1094     if (item->data) {
1095         free(item->data);
1096     }
1097 }
1098 
1099 FCITX_EXPORT_API
FcitxUIGetMenuByStatusName(FcitxInstance * instance,const char * name)1100 FcitxUIMenu* FcitxUIGetMenuByStatusName(FcitxInstance* instance, const char* name)
1101 {
1102     FcitxUIStatus* status = FcitxUIGetStatusByName(instance, name);
1103     if (!status) {
1104         FcitxUIComplexStatus* compstatus = FcitxUIGetComplexStatusByName(instance, name);
1105         if (!compstatus)
1106             return NULL;
1107     }
1108 
1109     UT_array* uimenus = &instance->uimenus;
1110     FcitxUIMenu** menupp, *menup = NULL;
1111     for (menupp = (FcitxUIMenu **) utarray_front(uimenus);
1112          menupp != NULL;
1113          menupp = (FcitxUIMenu **) utarray_next(uimenus, menupp)
1114         ) {
1115         if ((*menupp)->candStatusBind && strcmp((*menupp)->candStatusBind, name) == 0) {
1116             menup = *menupp;
1117             break;
1118         }
1119     }
1120 
1121     return menup;
1122 }
1123 
1124 // kate: indent-mode cstyle; space-indent on; indent-width 4;
1125