1 #include <stdlib.h>
2 #include <ctype.h>
3 #include "IBusChewingUtil.h"
4 #include "IBusChewingProperties.h"
5 #include "IBusChewingPreEdit.h"
6 #include "IBusChewingPreEdit-private.h"
7 
8 /**************************************
9  * Methods
10  */
11 
ibus_chewing_pre_edit_new(MkdgBackend * backend)12 IBusChewingPreEdit *ibus_chewing_pre_edit_new(MkdgBackend * backend)
13 {
14     IBusChewingPreEdit *self = g_new0(IBusChewingPreEdit, 1);
15     self->iProperties = ibus_chewing_properties_new(backend, self, NULL);
16     self->preEdit = g_string_sized_new(IBUS_CHEWING_MAX_BYTES);
17     self->outgoing = g_string_sized_new(IBUS_CHEWING_MAX_BYTES);
18     self->keyLast = 0;
19     self->bpmfLen = 0;
20     self->wordLen = 0;
21     self->engine = NULL;
22 
23     /* Create chewing context */
24     gchar buf[100];
25     g_snprintf(buf, 100, "%s/.chewing", getenv("HOME"));
26 #if !CHEWING_CHECK_VERSION(0,4,0)
27     gint ret = chewing_Init(QUOTE_ME(CHEWING_DATADIR_REAL), buf);
28     if (ret) {
29 	IBUS_CHEWING_LOG(ERROR,
30 			 "ibus_chewing_pre_edit_new() chewing_Init(%s,%s)",
31 			 QUOTE_ME(CHEWING_DATADIR_REAL), buf);
32     }
33 #endif
34     self->context = chewing_new();
35     chewing_set_ChiEngMode(self->context, CHINESE_MODE);
36 
37     self->iTable =
38 	g_object_ref_sink(ibus_chewing_lookup_table_new
39 			  (self->iProperties, self->context));
40     return self;
41 }
42 
ibus_chewing_pre_edit_free(IBusChewingPreEdit * self)43 void ibus_chewing_pre_edit_free(IBusChewingPreEdit * self)
44 {
45     /* properties need not be freed here */
46     chewing_delete(self->context);
47     g_string_free(self->preEdit, TRUE);
48     g_string_free(self->outgoing, TRUE);
49     ibus_lookup_table_clear(self->iTable);
50     g_object_unref(self->iTable);
51     g_free(self);
52 }
53 
ibus_chewing_pre_edit_get_bopomofo_string(IBusChewingPreEdit * self)54 gchar *ibus_chewing_pre_edit_get_bopomofo_string(IBusChewingPreEdit * self)
55 {
56 #if CHEWING_CHECK_VERSION(0,4,0)
57     const gchar *buf = chewing_bopomofo_String_static(self->context);
58     self->bpmfLen = (gint) g_utf8_strlen(buf, 0);
59     return g_strdup(buf);
60 #else
61     return chewing_zuin_String(self->context, &(self->bpmfLen));
62 #endif
63 }
64 
ibus_chewing_pre_edit_use_all_configure(IBusChewingPreEdit * self)65 void ibus_chewing_pre_edit_use_all_configure(IBusChewingPreEdit * self)
66 {
67     mkdg_properties_use_all(self->iProperties->properties, NULL);
68 }
69 
ibus_chewing_pre_edit_update_outgoing(IBusChewingPreEdit * self)70 void ibus_chewing_pre_edit_update_outgoing(IBusChewingPreEdit * self)
71 {
72     if (ibus_chewing_pre_edit_has_flag(self, FLAG_UPDATED_OUTGOING)) {
73 	/* Commit already sent to outgoing, no need to update again */
74 	return;
75     }
76     if (chewing_commit_Check(self->context)) {
77 	/* commit_Check=1 means new commit available */
78 	gchar *commitStr = chewing_commit_String(self->context);
79 	IBUS_CHEWING_LOG(INFO, "commitStr=|%s|\n", commitStr);
80 	g_string_append(self->outgoing, commitStr);
81 	g_free(commitStr);
82 	ibus_chewing_pre_edit_set_flag(self, FLAG_UPDATED_OUTGOING);
83     }
84     IBUS_CHEWING_LOG(INFO, "outgoing=|%s|\n", self->outgoing->str);
85     IBUS_CHEWING_LOG(DEBUG,
86 		     "ibus_chewing_pre_edit_update_outgoing(-): return: outgoing=|%s|",
87 		     self->outgoing->str);
88 }
89 
ibus_chewing_pre_edit_update(IBusChewingPreEdit * self)90 void ibus_chewing_pre_edit_update(IBusChewingPreEdit * self)
91 {
92     IBUS_CHEWING_LOG(DEBUG, "* ibus_chewing_pre_edit_update(-)");
93 
94     /* Make preEdit */
95     gchar *bufferStr = chewing_buffer_String(self->context);
96     gchar *bpmfStr = ibus_chewing_pre_edit_get_bopomofo_string(self);
97     g_string_assign(self->preEdit, "");
98     gint i;
99     gchar *cP = bufferStr;
100     gunichar uniCh;
101     IBUS_CHEWING_LOG(INFO,
102 		     "* ibus_chewing_pre_edit_update(-)  bufferStr=|%s|, bpmfStr=|%s| bpmfLen=%d cursor=%d",
103 		     bufferStr, bpmfStr, self->bpmfLen, cursor_current);
104     for (i = 0; i < chewing_buffer_Len(self->context) && cP != NULL; i++) {
105 	if (i == cursor_current) {
106 	    /* Insert bopomofo string */
107 	    g_string_append(self->preEdit, bpmfStr);
108 	}
109 	uniCh = g_utf8_get_char(cP);
110 	g_string_append_unichar(self->preEdit, uniCh);
111 	cP = g_utf8_next_char(cP);
112     }
113     if (chewing_buffer_Len(self->context) <= cursor_current) {
114 	g_string_append(self->preEdit, bpmfStr);
115     }
116     self->wordLen = i + self->bpmfLen;
117     g_free(bufferStr);
118     g_free(bpmfStr);
119 
120     ibus_chewing_pre_edit_update_outgoing(self);
121 
122 }
123 
ibus_chewing_pre_edit_length(IBusChewingPreEdit * self)124 guint ibus_chewing_pre_edit_length(IBusChewingPreEdit * self)
125 {
126     return self->preEdit->len;
127 }
128 
ibus_chewing_pre_edit_word_length(IBusChewingPreEdit * self)129 guint ibus_chewing_pre_edit_word_length(IBusChewingPreEdit * self)
130 {
131     return self->wordLen;
132 }
133 
ibus_chewing_pre_edit_word_limit(IBusChewingPreEdit * self)134 guint ibus_chewing_pre_edit_word_limit(IBusChewingPreEdit * self)
135 {
136     return chewing_get_maxChiSymbolLen(self->context);
137 }
138 
ibus_chewing_pre_edit_get_pre_edit(IBusChewingPreEdit * self)139 gchar *ibus_chewing_pre_edit_get_pre_edit(IBusChewingPreEdit * self)
140 {
141     return self->preEdit->str;
142 }
143 
ibus_chewing_pre_edit_get_outgoing(IBusChewingPreEdit * self)144 gchar *ibus_chewing_pre_edit_get_outgoing(IBusChewingPreEdit * self)
145 {
146     return self->outgoing->str;
147 }
148 
149 /* force_commit is run when receiving IGNORE event */
ibus_chewing_pre_edit_force_commit(IBusChewingPreEdit * self)150 void ibus_chewing_pre_edit_force_commit(IBusChewingPreEdit * self)
151 {
152     IBUS_CHEWING_LOG(INFO,
153 		     "ibus_chewing_pre_edit_force_commit(-) bpmf_check=%d buffer_check=%d",
154 		     bpmf_check, chewing_buffer_Check(self->context));
155 
156     /* Ignore the context and
157      * Commit whatever in preedit buffer
158      */
159     g_string_append(self->outgoing, self->preEdit->str);
160 
161     ibus_chewing_pre_edit_clear_pre_edit(self);
162     ibus_chewing_pre_edit_update(self);
163 
164 }
165 
ibus_chewing_pre_edit_clear(IBusChewingPreEdit * self)166 void ibus_chewing_pre_edit_clear(IBusChewingPreEdit * self)
167 {
168     IBUS_CHEWING_LOG(INFO, "ibus_chewing_pre_edit_clear(-)");
169 
170     ibus_chewing_pre_edit_clear_pre_edit(self);
171     ibus_chewing_pre_edit_clear_outgoing(self);
172     ibus_chewing_pre_edit_update(self);
173 }
174 
ibus_chewing_pre_edit_clear_bopomofo(IBusChewingPreEdit * self)175 void ibus_chewing_pre_edit_clear_bopomofo(IBusChewingPreEdit * self)
176 {
177     IBUS_CHEWING_LOG(DEBUG, "ibus_chewing_pre_edit_clear_bopomofo(-)");
178 
179     /* Esc key can close candidate list, clear bopomofo, and clear
180      * the whole pre-edit buffer. Make sure it acts as we expected.
181      */
182     if (table_is_showing) {
183         chewing_handle_Esc(self->context);
184     }
185 
186     if (bpmf_check) {
187         chewing_handle_Esc(self->context);
188     }
189 
190     ibus_chewing_pre_edit_update(self);
191 }
192 
ibus_chewing_pre_edit_clear_pre_edit(IBusChewingPreEdit * self)193 void ibus_chewing_pre_edit_clear_pre_edit(IBusChewingPreEdit * self)
194 {
195     IBUS_CHEWING_LOG(DEBUG, "ibus_chewing_pre_edit_clear_pre_edit(-)");
196 
197     ibus_chewing_pre_edit_clear_bopomofo(self);
198 
199     /* Save the orig Esc clean buffer state */
200     gint origState = chewing_get_escCleanAllBuf(self->context);
201     chewing_set_escCleanAllBuf(self->context, TRUE);
202 
203     chewing_handle_Esc(self->context);
204 
205     chewing_set_escCleanAllBuf(self->context, origState);
206     ibus_chewing_pre_edit_update(self);
207 }
208 
ibus_chewing_pre_edit_clear_outgoing(IBusChewingPreEdit * self)209 void ibus_chewing_pre_edit_clear_outgoing(IBusChewingPreEdit * self)
210 {
211     IBUS_CHEWING_LOG(DEBUG, "ibus_chewing_pre_edit_clear_outgoing(-)");
212     g_string_assign(self->outgoing, "");
213 
214 }
215 
216 #define is_chinese ibus_chewing_pre_edit_get_chi_eng_mode(self)
217 #define is_full_shape ibus_chewing_pre_edit_get_full_half_mode(self)
ibus_chewing_pre_edit_get_chi_eng_mode(IBusChewingPreEdit * self)218 gboolean ibus_chewing_pre_edit_get_chi_eng_mode(IBusChewingPreEdit * self)
219 {
220     return chewing_get_ChiEngMode(self->context) != 0;
221 }
222 
ibus_chewing_pre_edit_get_full_half_mode(IBusChewingPreEdit * self)223 gboolean ibus_chewing_pre_edit_get_full_half_mode(IBusChewingPreEdit *
224 						  self)
225 {
226     return chewing_get_ShapeMode(self->context) != 0;
227 }
228 
ibus_chewing_pre_edit_set_chi_eng_mode(IBusChewingPreEdit * self,gboolean chineseMode)229 void ibus_chewing_pre_edit_set_chi_eng_mode(IBusChewingPreEdit * self,
230 					    gboolean chineseMode)
231 {
232     /* Clear bopomofo when toggling Chi-Eng Mode */
233     if (!chineseMode && is_chinese && bpmf_check) {
234         ibus_chewing_pre_edit_clear_bopomofo(self);
235     }
236     chewing_set_ChiEngMode(self->context, (chineseMode) ? 1 : 0);
237 }
238 
ibus_chewing_pre_edit_set_full_half_mode(IBusChewingPreEdit * self,gboolean fullShapeMode)239 void ibus_chewing_pre_edit_set_full_half_mode(IBusChewingPreEdit * self,
240 					      gboolean fullShapeMode)
241 {
242     if (is_chinese && bpmf_check) {
243     /* Clear bopomofo when toggling Full-Half Mode */
244         ibus_chewing_pre_edit_clear_bopomofo(self);
245     }
246     chewing_set_ShapeMode(self->context, (fullShapeMode) ? 1 : 0);
247 }
248 
249 /**************************************
250  * ibus_chewing_pre_edit key processing
251  */
self_key_sym_fix(IBusChewingPreEdit * self,KSym kSym,KeyModifiers unmaskedMod)252 KSym self_key_sym_fix(IBusChewingPreEdit * self, KSym kSym,
253 		      KeyModifiers unmaskedMod)
254 {
255     gchar caseConversionMode = default_english_case_short;
256     if (!ibus_chewing_pre_edit_get_property_boolean(self,
257 						    "capslock-toggle-chinese"))
258     {
259 	caseConversionMode = 'n';
260     }
261     if (is_chinese) {
262 	/*
263 	 * Ignore the status of CapsLock, thus
264 	 */
265 	if (is_shift) {
266 	    return toupper(kSym);
267 	}
268 	return tolower(kSym);
269     } else {
270 	/* May need to change case if Caps Lock toggle chinese */
271 	switch (caseConversionMode) {
272 	case 'l':
273 	    if (is_shift) {
274 		/* Uppercase */
275 		return toupper(kSym);
276 	    }
277 	    /* Lowercase */
278 	    return tolower(kSym);
279 	case 'u':
280 	    if (is_shift) {
281 		/* Lowercase */
282 		return tolower(kSym);
283 	    }
284 	    /* Uppercase */
285 	    return toupper(kSym);
286 	default:
287 	    break;
288 	}
289     }
290     return kSym;
291 }
292 
self_handle_key_sym_default(IBusChewingPreEdit * self,KSym kSym,KeyModifiers unmaskedMod)293 EventResponse self_handle_key_sym_default(IBusChewingPreEdit * self,
294 					  KSym kSym,
295 					  KeyModifiers unmaskedMod)
296 {
297     filter_modifiers(IBUS_SHIFT_MASK);
298     handle_log("key_sym_default");
299 
300     /* Seem like we need to disable easy symbol temporarily
301      * otherwise the key won't process
302      */
303     gint easySymbolInput = chewing_get_easySymbolInput(self->context);
304     if (maskedMod != IBUS_SHIFT_MASK) {
305 	chewing_set_easySymbolInput(self->context, 0);
306     }
307     EventResponse response = EVENT_RESPONSE_UNDECIDED;
308     KSym fixedKSym = self_key_sym_fix(self, kSym, unmaskedMod);
309     IBUS_CHEWING_LOG(DEBUG,
310 		     "* self_handle_key_sym_default(): new kSym %x(%s), %x(%s)",
311 		     fixedKSym, key_sym_get_name(fixedKSym), unmaskedMod,
312 		     modifiers_to_string(unmaskedMod));
313     gint ret = chewing_handle_Default(self->context, fixedKSym);
314     /* Handle quick commit */
315     ibus_chewing_pre_edit_clear_flag(self, FLAG_UPDATED_OUTGOING);
316     ibus_chewing_pre_edit_update_outgoing(self);
317 
318     switch (ret) {
319     case 0:
320     case KEYSTROKE_COMMIT:
321 	response = EVENT_RESPONSE_PROCESS;
322 	break;
323     case KEYSTROKE_ABSORB:
324 	response = EVENT_RESPONSE_ABSORB;
325 	break;
326     case KEYSTROKE_IGNORE:
327 	response = EVENT_RESPONSE_IGNORE;
328 	break;
329     default:
330 	break;
331     }
332 
333     IBUS_CHEWING_LOG(DEBUG,
334 		     "self_handle_key_sym_default() ret=%d response=%d",
335 		     ret, response);
336     /* Restore easySymbolInput */
337     chewing_set_easySymbolInput(self->context, easySymbolInput);
338     return response;
339 }
340 
341 /* Return FALSE if the key should not be processed with input method */
self_handle_num(IBusChewingPreEdit * self,KSym kSym,KeyModifiers unmaskedMod)342 EventResponse self_handle_num(IBusChewingPreEdit * self, KSym kSym,
343 			      KeyModifiers unmaskedMod)
344 {
345     filter_modifiers(IBUS_SHIFT_MASK | IBUS_CONTROL_MASK);
346     absorb_when_release;
347     handle_log("num");
348 
349     if (is_ctrl_only) {
350 	return
351 	    event_process_or_ignore(!chewing_handle_CtrlNum
352 				    (self->context, kSym));
353     }
354     /* maskedMod= 0 */
355     return self_handle_key_sym_default(self, kSym, unmaskedMod);
356 }
357 
self_handle_num_keypad(IBusChewingPreEdit * self,KSym kSym,KeyModifiers unmaskedMod)358 EventResponse self_handle_num_keypad(IBusChewingPreEdit * self,
359 				     KSym kSym, KeyModifiers unmaskedMod)
360 {
361     filter_modifiers(IBUS_SHIFT_MASK | IBUS_CONTROL_MASK);
362     absorb_when_release;
363     handle_log("num_keypad");
364 
365     KSym kSymEquiv = key_sym_KP_to_normal(kSym);
366 
367     if ((maskedMod != 0) && (!is_shift_only) && (!is_ctrl_only)) {
368 	return EVENT_RESPONSE_IGNORE;
369     }
370 
371     if (is_ctrl_only) {
372 	return
373 	    event_process_or_ignore(!chewing_handle_CtrlNum
374 				    (self->context, kSymEquiv));
375     }
376 
377     if (bpmf_check) {
378         return EVENT_RESPONSE_IGNORE;
379     }
380 
381     if (!buffer_is_empty && table_is_showing) {
382         return EVENT_RESPONSE_IGNORE;
383     }
384 
385     /* maskedMod= 0 */
386     gint origChiEngMode = chewing_get_ChiEngMode(self->context);
387     ibus_chewing_pre_edit_set_chi_eng_mode(self, FALSE);
388 
389     return self_handle_key_sym_default(self, kSymEquiv, unmaskedMod);
390     ibus_chewing_pre_edit_set_chi_eng_mode(self, origChiEngMode);
391 }
392 
self_handle_caps_lock(IBusChewingPreEdit * self,KSym kSym,KeyModifiers unmaskedMod)393 EventResponse self_handle_caps_lock(IBusChewingPreEdit * self, KSym kSym,
394 				    KeyModifiers unmaskedMod)
395 {
396     filter_modifiers(0);
397 
398     if (!ibus_chewing_pre_edit_get_property_boolean(self,
399 						    "capslock-toggle-chinese")) {
400         /* Ignore the Caps Lock event when it does not toggle Chinese */
401         return EVENT_RESPONSE_IGNORE;
402     }
403 
404     absorb_when_release;
405     handle_log("caps_lock");
406 
407     /* Clear bopomofo when toggling Chi-Eng Mode */
408     if (is_chinese && bpmf_check) {
409         ibus_chewing_pre_edit_clear_bopomofo(self);
410     }
411 
412     return
413 	event_process_or_ignore(!chewing_handle_Capslock(self->context));
414 }
415 
self_handle_shift(IBusChewingPreEdit * self,KSym kSym,KeyModifiers unmaskedMod)416 EventResponse self_handle_shift(IBusChewingPreEdit * self, KSym kSym,
417 				KeyModifiers unmaskedMod)
418 {
419     filter_modifiers(IBUS_SHIFT_MASK);
420     handle_log("shift");
421 
422     gboolean shiftIsToggleChinese =
423 	ibus_chewing_pre_edit_get_property_boolean(self,
424 						   "shift-toggle-chinese");
425 
426     if (!shiftIsToggleChinese) {
427 	return EVENT_RESPONSE_IGNORE;
428     }
429 
430     if (!event_is_released(unmaskedMod)) {
431 	return EVENT_RESPONSE_ABSORB;
432     }
433 
434     /* keyLast != Shift means Shift is just part of combination,
435      * thus should not be recognized as single Shift key
436      */
437     if (self->keyLast != IBUS_KEY_Shift_L
438 	&& self->keyLast != IBUS_KEY_Shift_R) {
439 	return EVENT_RESPONSE_ABSORB;
440     }
441 
442     ibus_chewing_pre_edit_toggle_chi_eng_mode(self);
443     return EVENT_RESPONSE_ABSORB;
444 }
445 
self_handle_space(IBusChewingPreEdit * self,KSym kSym,KeyModifiers unmaskedMod)446 EventResponse self_handle_space(IBusChewingPreEdit * self, KSym kSym,
447 				KeyModifiers unmaskedMod)
448 {
449     filter_modifiers(IBUS_SHIFT_MASK | IBUS_CONTROL_MASK);
450     absorb_when_release;
451     handle_log("space");
452 
453     if (is_shift_only) {
454 	    ibus_chewing_pre_edit_toggle_full_half_mode(self);
455 	    return EVENT_RESPONSE_PROCESS;
456     }
457 
458     /* Bug of libchewing:
459      * when "space to select" is enabled, chewing_handle_Space() will
460      * ignore the first space. Therefore, use chewing_handle_Space()
461      * only if the buffer is not empty and we want to select.
462      */
463     gboolean spaceAsSelection =
464 	ibus_chewing_pre_edit_get_property_boolean(self,
465 						   "space-as-selection");
466 
467     if (is_chinese && !buffer_is_empty && !bpmf_check && spaceAsSelection) {
468         return event_process_or_ignore(!chewing_handle_Space(self->context));
469     } else {
470         return self_handle_key_sym_default(self, kSym, unmaskedMod);
471     }
472 }
473 
self_handle_return(IBusChewingPreEdit * self,KSym kSym,KeyModifiers unmaskedMod)474 EventResponse self_handle_return(IBusChewingPreEdit * self, KSym kSym,
475 				 KeyModifiers unmaskedMod)
476 {
477     filter_modifiers(0);
478     absorb_when_release;
479     ignore_when_buffer_is_empty;
480     handle_log("return");
481 
482     EventResponse response =
483 	event_process_or_ignore(!chewing_handle_Enter(self->context));
484 
485     /* Handle quick commit */
486     ibus_chewing_pre_edit_clear_flag(self, FLAG_UPDATED_OUTGOING);
487     ibus_chewing_pre_edit_update_outgoing(self);
488 
489     return response;
490 }
491 
self_handle_backspace(IBusChewingPreEdit * self,KSym kSym,KeyModifiers unmaskedMod)492 EventResponse self_handle_backspace(IBusChewingPreEdit * self, KSym kSym,
493 				    KeyModifiers unmaskedMod)
494 {
495     filter_modifiers(0);
496     absorb_when_release;
497 
498     if (buffer_is_empty && !table_is_showing) {
499         return EVENT_RESPONSE_IGNORE;
500     }
501 
502     handle_log("backspace");
503 
504 #if !CHEWING_CHECK_VERSION(0,4,0)
505     if (table_is_showing) {
506         return event_process_or_ignore(!chewing_handle_Esc(self->context));
507     }
508 #endif
509 
510     return
511 	event_process_or_ignore(!chewing_handle_Backspace(self->context));
512 }
513 
self_handle_delete(IBusChewingPreEdit * self,KSym kSym,KeyModifiers unmaskedMod)514 EventResponse self_handle_delete(IBusChewingPreEdit * self, KSym kSym,
515 				 KeyModifiers unmaskedMod)
516 {
517     filter_modifiers(0);
518     absorb_when_release;
519     ignore_when_buffer_is_empty;
520     handle_log("delete");
521 
522     return event_process_or_ignore(!chewing_handle_Del(self->context));
523 }
524 
self_handle_escape(IBusChewingPreEdit * self,KSym kSym,KeyModifiers unmaskedMod)525 EventResponse self_handle_escape(IBusChewingPreEdit * self, KSym kSym,
526 				 KeyModifiers unmaskedMod)
527 {
528     filter_modifiers(0);
529     absorb_when_release;
530 
531     if (buffer_is_empty && !table_is_showing) {
532         return EVENT_RESPONSE_IGNORE;
533     }
534 
535     handle_log("escape");
536 
537     return event_process_or_ignore(!chewing_handle_Esc(self->context));
538 }
539 
self_handle_left(IBusChewingPreEdit * self,KSym kSym,KeyModifiers unmaskedMod)540 EventResponse self_handle_left(IBusChewingPreEdit * self, KSym kSym,
541 			       KeyModifiers unmaskedMod)
542 {
543     filter_modifiers(IBUS_SHIFT_MASK);
544     absorb_when_release;
545 
546     if (buffer_is_empty && !table_is_showing) {
547         return EVENT_RESPONSE_IGNORE;
548     }
549 
550     handle_log("left");
551 
552     if (is_shift_only) {
553 	return
554 	    event_process_or_ignore(!chewing_handle_ShiftLeft
555 				    (self->context));
556     }
557 
558     return event_process_or_ignore(!chewing_handle_Left(self->context));
559 }
560 
self_handle_up(IBusChewingPreEdit * self,KSym kSym,KeyModifiers unmaskedMod)561 EventResponse self_handle_up(IBusChewingPreEdit * self, KSym kSym,
562 			     KeyModifiers unmaskedMod)
563 {
564     filter_modifiers(0);
565     absorb_when_release;
566 
567     if (buffer_is_empty && !table_is_showing) {
568         return EVENT_RESPONSE_IGNORE;
569     }
570 
571     handle_log("up");
572 
573     return event_process_or_ignore(!chewing_handle_Up(self->context));
574 }
575 
self_handle_right(IBusChewingPreEdit * self,KSym kSym,KeyModifiers unmaskedMod)576 EventResponse self_handle_right(IBusChewingPreEdit * self, KSym kSym,
577 				KeyModifiers unmaskedMod)
578 {
579     filter_modifiers(IBUS_SHIFT_MASK);
580     absorb_when_release;
581 
582     if (buffer_is_empty && !table_is_showing) {
583         return EVENT_RESPONSE_IGNORE;
584     }
585 
586     handle_log("right");
587 
588     if (is_shift_only) {
589 	return
590 	    event_process_or_ignore(!chewing_handle_ShiftRight
591 				    (self->context));
592     }
593 
594     return event_process_or_ignore(!chewing_handle_Right(self->context));
595 }
596 
self_handle_down(IBusChewingPreEdit * self,KSym kSym,KeyModifiers unmaskedMod)597 EventResponse self_handle_down(IBusChewingPreEdit * self, KSym kSym,
598 			       KeyModifiers unmaskedMod)
599 {
600     filter_modifiers(0);
601     absorb_when_release;
602 
603     if (buffer_is_empty && !table_is_showing) {
604         return EVENT_RESPONSE_IGNORE;
605     }
606 
607     handle_log("down");
608 
609     return event_process_or_ignore(!chewing_handle_Down(self->context));
610 }
611 
self_handle_page_up(IBusChewingPreEdit * self,KSym kSym,KeyModifiers unmaskedMod)612 EventResponse self_handle_page_up(IBusChewingPreEdit * self, KSym kSym,
613 				  KeyModifiers unmaskedMod)
614 {
615     filter_modifiers(0);
616     absorb_when_release;
617 
618     if (buffer_is_empty && !table_is_showing) {
619         return EVENT_RESPONSE_IGNORE;
620     }
621 
622     handle_log("page_up");
623 
624 #if !CHEWING_CHECK_VERSION(0,4,0)
625     if (table_is_showing) {
626         return event_process_or_ignore(!chewing_handle_Left(self->context));
627     }
628 #endif
629 
630     return event_process_or_ignore(!chewing_handle_PageUp(self->context));
631 }
632 
self_handle_page_down(IBusChewingPreEdit * self,KSym kSym,KeyModifiers unmaskedMod)633 EventResponse self_handle_page_down(IBusChewingPreEdit * self, KSym kSym,
634 				    KeyModifiers unmaskedMod)
635 {
636     filter_modifiers(0);
637     absorb_when_release;
638 
639     if (buffer_is_empty && !table_is_showing) {
640         return EVENT_RESPONSE_IGNORE;
641     }
642 
643     handle_log("page_down");
644 
645 #if !CHEWING_CHECK_VERSION(0,4,0)
646     if (table_is_showing) {
647         return event_process_or_ignore(!chewing_handle_Right(self->context));
648     }
649 #endif
650 
651     return
652 	event_process_or_ignore(!chewing_handle_PageDown(self->context));
653 }
654 
self_handle_tab(IBusChewingPreEdit * self,KSym kSym,KeyModifiers unmaskedMod)655 EventResponse self_handle_tab(IBusChewingPreEdit * self, KSym kSym,
656 			      KeyModifiers unmaskedMod)
657 {
658     filter_modifiers(0);
659     absorb_when_release;
660     ignore_when_buffer_is_empty;
661     handle_log("tab");
662 
663     return event_process_or_ignore(!chewing_handle_Tab(self->context));
664 }
665 
self_handle_home(IBusChewingPreEdit * self,KSym kSym,KeyModifiers unmaskedMod)666 EventResponse self_handle_home(IBusChewingPreEdit * self, KSym kSym,
667 			       KeyModifiers unmaskedMod)
668 {
669     filter_modifiers(0);
670     absorb_when_release;
671     ignore_when_buffer_is_empty;
672     handle_log("home");
673 
674     return event_process_or_ignore(!chewing_handle_Home(self->context));
675 }
676 
self_handle_end(IBusChewingPreEdit * self,KSym kSym,KeyModifiers unmaskedMod)677 EventResponse self_handle_end(IBusChewingPreEdit * self, KSym kSym,
678 			      KeyModifiers unmaskedMod)
679 {
680     filter_modifiers(0);
681     absorb_when_release;
682     ignore_when_buffer_is_empty;
683     handle_log("end");
684 
685     return event_process_or_ignore(!chewing_handle_End(self->context));
686 }
687 
self_handle_special(IBusChewingPreEdit * self,KSym kSym,KeyModifiers unmaskedMod)688 EventResponse self_handle_special(IBusChewingPreEdit * self, KSym kSym,
689 				  KeyModifiers unmaskedMod)
690 {
691     /* KSym >=128 is special key, which IM ignore. */
692     return EVENT_RESPONSE_IGNORE;
693 }
694 
self_handle_default(IBusChewingPreEdit * self,KSym kSym,KeyModifiers unmaskedMod)695 EventResponse self_handle_default(IBusChewingPreEdit * self, KSym kSym,
696 				  KeyModifiers unmaskedMod)
697 {
698     filter_modifiers(IBUS_SHIFT_MASK);
699     absorb_when_release;
700     handle_log("default");
701 
702     return self_handle_key_sym_default(self, kSym, unmaskedMod);
703 }
704 
705 KeyHandlingRule keyHandlingRules[] = {
706     {
707      IBUS_KEY_0, IBUS_KEY_9, self_handle_num}
708     , {
709        IBUS_KEY_KP_0, IBUS_KEY_KP_9, self_handle_num_keypad}
710     , {
711        IBUS_KEY_Caps_Lock, IBUS_KEY_Caps_Lock, self_handle_caps_lock}
712     , {
713        IBUS_KEY_Shift_L, IBUS_KEY_Shift_R, self_handle_shift}
714     , {
715        IBUS_KEY_space, IBUS_KEY_space, self_handle_space}
716     , {
717        IBUS_KEY_Return, IBUS_KEY_Return, self_handle_return}
718     , {
719        IBUS_KEY_KP_Enter, IBUS_KEY_KP_Enter, self_handle_return}
720     , {
721        IBUS_KEY_BackSpace, IBUS_KEY_BackSpace, self_handle_backspace}
722     , {
723        IBUS_KEY_Delete, IBUS_KEY_Delete, self_handle_delete}
724     , {
725        IBUS_KEY_KP_Delete, IBUS_KEY_KP_Delete, self_handle_delete}
726     , {
727        IBUS_KEY_Escape, IBUS_KEY_Escape, self_handle_escape}
728     , {
729        IBUS_KEY_Left, IBUS_KEY_Left, self_handle_left}
730     , {
731        IBUS_KEY_KP_Left, IBUS_KEY_KP_Left, self_handle_left}
732     , {
733        IBUS_KEY_Up, IBUS_KEY_Up, self_handle_up}
734     , {
735        IBUS_KEY_KP_Up, IBUS_KEY_KP_Up, self_handle_up}
736     , {
737        IBUS_KEY_Right, IBUS_KEY_Right, self_handle_right}
738     , {
739        IBUS_KEY_KP_Right, IBUS_KEY_KP_Right, self_handle_right}
740     , {
741        IBUS_KEY_Down, IBUS_KEY_Down, self_handle_down}
742     , {
743        IBUS_KEY_KP_Down, IBUS_KEY_KP_Down, self_handle_down}
744     , {
745        IBUS_KEY_Page_Up, IBUS_KEY_Page_Up, self_handle_page_up}
746     , {
747        IBUS_KEY_KP_Page_Up, IBUS_KEY_KP_Page_Up, self_handle_page_up}
748     , {
749        IBUS_KEY_Page_Down, IBUS_KEY_Page_Down, self_handle_page_down}
750     , {
751        IBUS_KEY_KP_Page_Down, IBUS_KEY_KP_Page_Down, self_handle_page_down}
752     , {
753        IBUS_KEY_Tab, IBUS_KEY_Tab, self_handle_tab}
754     , {
755        IBUS_KEY_Home, IBUS_KEY_Home, self_handle_home}
756     , {
757        IBUS_KEY_KP_Home, IBUS_KEY_KP_Home, self_handle_home}
758     , {
759        IBUS_KEY_End, IBUS_KEY_End, self_handle_end}
760     , {
761        IBUS_KEY_KP_End, IBUS_KEY_KP_End, self_handle_end}
762     , {
763        128, G_MAXUINT, self_handle_special}
764     , {
765        0, 0, self_handle_default}
766     ,
767 };
768 
self_key_sym_find_key_handling_rule(KSym kSym)769 static KeyHandlingRule *self_key_sym_find_key_handling_rule(KSym kSym)
770 {
771     gint i;
772     for (i = 0; keyHandlingRules[i].kSymLower != 0; i++) {
773 	if ((keyHandlingRules[i].kSymLower <= kSym) &&
774 	    (kSym <= keyHandlingRules[i].kSymUpper)) {
775 	    return &(keyHandlingRules[i]);
776 	}
777     }
778     return &(keyHandlingRules[i]);
779 }
780 
781 #define handle_key(kSym, unmaskedMod) (self_key_sym_find_key_handling_rule(kSym))->keyFunc(self, kSym, unmaskedMod)
782 
783 #define process_key_debug(prompt) IBUS_CHEWING_LOG(DEBUG, "ibus_chewing_pre_edit_process_key(): %s flags=%x buff_check=%d bpmf_check=%d cursor=%d total_choice=%d is_chinese=%d is_full_shape=%d is_plain_zhuyin=%d" ,\
784 	prompt,	self->flags, chewing_buffer_Check(self->context),\
785 	bpmf_check, cursor_current, total_choice, is_chinese, is_full_shape, is_plain_zhuyin)
786 
787 
788 /* keyCode should be converted to kSym already */
ibus_chewing_pre_edit_process_key(IBusChewingPreEdit * self,KSym kSym,KeyModifiers unmaskedMod)789 gboolean ibus_chewing_pre_edit_process_key
790     (IBusChewingPreEdit * self, KSym kSym, KeyModifiers unmaskedMod) {
791     IBUS_CHEWING_LOG(INFO,
792 		     "***** ibus_chewing_pre_edit_process_key(-,%x(%s),%x(%s))",
793 		     kSym, key_sym_get_name(kSym),
794 		     unmaskedMod, modifiers_to_string(unmaskedMod));
795     process_key_debug("Before response");
796 
797     /* Find corresponding rule */
798     EventResponse response;
799     response = handle_key(kSym, unmaskedMod);
800 
801     IBUS_CHEWING_LOG(DEBUG,
802 		     "ibus_chewing_pre_edit_process_key() response=%x",
803 		     response);
804     process_key_debug("After response");
805     self->keyLast = kSym;
806     switch (response) {
807     case EVENT_RESPONSE_ABSORB:
808 	return TRUE;
809     case EVENT_RESPONSE_IGNORE:
810 	return FALSE;
811     default:
812 	break;
813     }
814 
815     /**
816      *Plain zhuyin mode
817      */
818     if (is_plain_zhuyin && !bpmf_check) {
819 	/* libchewing functions are used here to skip the check
820 	 * that handle_key functions perform.
821 	 */
822 	if (kSym == IBUS_KEY_Escape) {
823 	    ibus_chewing_pre_edit_clear_pre_edit(self);
824 	} else if (kSym == IBUS_KEY_Return && table_is_showing) {
825 	    /* Use Enter to select the last chosen */
826 	    chewing_handle_Up(self->context);
827 	    chewing_handle_Enter(self->context);
828 	} else if (is_chinese && !table_is_showing) {
829 	    /* Character completed, and lookup table is not show */
830 	    /* Then open lookup table */
831 
832 	    if (is_shift) {
833 		/* For Chinese symbols */
834 		chewing_handle_Left(self->context);
835 	    }
836 	    chewing_handle_Down(self->context);
837 	} else if (total_choice == 0) {
838 	    /* lookup table is shown */
839 	    /* but selection is done */
840 	    chewing_handle_Enter(self->context);
841 	}
842     }
843     process_key_debug("After plain-zhuyin handling");
844 
845     ibus_chewing_pre_edit_update(self);
846 
847     guint candidateCount =
848 	ibus_chewing_lookup_table_update(self->iTable, self->iProperties,
849 					 self->context);
850     IBUS_CHEWING_LOG(INFO,
851 		     "ibus_chewing_pre_edit_process_key() candidateCount=%d",
852 		     candidateCount);
853 
854     if (candidateCount) {
855 	ibus_chewing_pre_edit_set_flag(self, FLAG_TABLE_SHOW);
856     } else {
857 	ibus_chewing_pre_edit_clear_flag(self, FLAG_TABLE_SHOW);
858     }
859     return TRUE;
860 }
861 
ibus_chewing_pre_edit_key_code_to_key_sym(IBusChewingPreEdit * self,KSym keySym,guint keyCode,KeyModifiers unmaskedMod)862 KSym ibus_chewing_pre_edit_key_code_to_key_sym
863     (IBusChewingPreEdit * self, KSym keySym,
864      guint keyCode, KeyModifiers unmaskedMod) {
865     KSym kSym = keySym;
866     if (!is_chinese) {
867 	/* English mode, pass as-is */
868 	return kSym;
869     }
870 
871     if (!ibus_chewing_pre_edit_is_system_keyboard_layout(self)) {
872 	/* Use en_US keyboard layout */
873 	/* ibus_keymap_lookup_key_sym treats keycode >= 256 */
874 	/* as IBUS_VoidSymbol */
875 	kSym =
876 	    ibus_keymap_lookup_keysym(ibus_keymap_get
877 				      ("us"), keyCode, unmaskedMod);
878 	if (kSym == IBUS_VoidSymbol) {
879 	    /* Restore key_sym */
880 	    kSym = keySym;
881 	}
882     }
883 
884     return kSym;
885 }
886