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