1 /* -*- c-basic-offset:2; tab-width:2; indent-tabs-mode:nil -*- */
2
3 #include <stdio.h>
4
5 #include <pobl/bl_mem.h> /* malloc/alloca/free */
6 #include <pobl/bl_str.h> /* strdup/bl_str_sep/bl_snprintf */
7 #include <pobl/bl_util.h> /* DIGIT_STR_LEN */
8 #include <pobl/bl_conf_io.h>
9
10 #include <ui_im.h>
11
12 #include "../im_info.h"
13 #include "ef_str_parser.h"
14 #include "dict.h"
15
16 #if 0
17 #define IM_SKK_DEBUG 1
18 #endif
19
20 #define MAX_CAPTION_LEN 64
21
22 typedef u_int16_t wchar;
23
24 typedef enum input_mode {
25 HIRAGANA,
26 KATAKANA,
27 ALPHABET_FULL,
28 ALPHABET,
29 MAX_INPUT_MODE
30
31 } input_mode_t;
32
33 typedef struct im_skk {
34 /* input method common object */
35 ui_im_t im;
36
37 int is_enabled;
38 /*
39 * 1: direct input 2: convert 3: convert with okurigana
40 * 4: convert with sokuon okurigana
41 */
42 int is_preediting;
43
44 vt_char_encoding_t term_encoding;
45
46 char *encoding_name; /* encoding of conversion engine */
47
48 /* conv is NULL if term_encoding == skk encoding */
49 ef_parser_t *parser_term; /* for term encoding */
50 ef_conv_t *conv; /* for term encoding */
51
52 ef_char_t preedit[MAX_CAPTION_LEN];
53 u_int preedit_len;
54
55 void *candidate;
56
57 char *status[MAX_INPUT_MODE];
58
59 int dan;
60 int prev_dan;
61
62 input_mode_t mode;
63
64 int8_t sticky_shift;
65 int8_t start_candidate;
66
67 int8_t is_editing_new_word;
68 ef_char_t new_word[MAX_CAPTION_LEN];
69 u_int new_word_len;
70
71 ef_char_t preedit_orig[MAX_CAPTION_LEN];
72 u_int preedit_orig_len;
73 int is_preediting_orig;
74 int prev_dan_orig;
75 input_mode_t mode_orig;
76 ef_char_t visual_chars[2];
77
78 void *completion;
79
80 } im_skk_t;
81
82 /* --- static variables --- */
83
84 ui_im_export_syms_t *syms = NULL; /* mlterm internal symbols */
85 static int ref_count = 0;
86 static KeySym sticky_shift_ksym;
87 static int sticky_shift_ch;
88
89 /* --- static functions --- */
90
preedit_add(im_skk_t * skk,wchar wch)91 static void preedit_add(im_skk_t *skk, wchar wch) {
92 ef_char_t ch;
93
94 if (skk->preedit_len >= sizeof(skk->preedit) / sizeof(skk->preedit[0])) {
95 return;
96 }
97
98 if (wch > 0xff) {
99 if (skk->mode == KATAKANA && 0xa4a1 <= wch && wch <= 0xa4f3) {
100 wch += 0x100;
101 }
102
103 ch.ch[0] = (wch >> 8) & 0x7f;
104 ch.ch[1] = wch & 0x7f;
105 ch.size = 2;
106 ch.cs = JISX0208_1983;
107 } else {
108 ch.ch[0] = wch;
109 ch.size = 1;
110 ch.cs = US_ASCII;
111 }
112 ch.property = 0;
113
114 skk->preedit[skk->preedit_len++] = ch;
115 }
116
preedit_destroy(im_skk_t * skk)117 static void preedit_destroy(im_skk_t *skk) { --skk->preedit_len; }
118
119 static void candidate_clear(im_skk_t *skk);
120
preedit_clear(im_skk_t * skk)121 static void preedit_clear(im_skk_t *skk) {
122 if (skk->is_preediting && skk->mode == ALPHABET) {
123 skk->mode = HIRAGANA;
124 }
125
126 skk->preedit_len = 0;
127 skk->is_preediting = 0;
128 skk->dan = 0;
129 skk->prev_dan = 0;
130
131 candidate_clear(skk);
132 }
133
preedit_to_visual(im_skk_t * skk)134 static void preedit_to_visual(im_skk_t *skk) {
135 if (skk->prev_dan) {
136 if (skk->is_preediting == 4) {
137 skk->preedit[skk->preedit_len] = skk->visual_chars[1];
138 skk->preedit[skk->preedit_len - 1] = skk->visual_chars[0];
139 skk->preedit_len++;
140 } else {
141 skk->preedit[skk->preedit_len - 1] = skk->visual_chars[0];
142 }
143 }
144 }
145
preedit_backup_visual_chars(im_skk_t * skk)146 static void preedit_backup_visual_chars(im_skk_t *skk) {
147 if (skk->prev_dan) {
148 if (skk->is_preediting == 4) {
149 skk->visual_chars[1] = skk->preedit[skk->preedit_len - 1];
150 skk->visual_chars[0] = skk->preedit[skk->preedit_len - 2];
151 } else {
152 skk->visual_chars[0] = skk->preedit[skk->preedit_len - 1];
153 }
154 }
155 }
156
preedit(im_skk_t * skk,ef_char_t * preedit,u_int preedit_len,int rev_len,char * candidateword,size_t candidateword_len,char * pos)157 static void preedit(im_skk_t *skk, ef_char_t *preedit, u_int preedit_len, int rev_len,
158 char *candidateword /* already converted to term encoding */,
159 /* 0 means the length of candidateword is 0 or necessity of strlen() */
160 size_t candidateword_len,
161 char *pos) {
162 int x;
163 int y;
164 int rev_pos = 0;
165
166 if (skk->preedit_orig_len > 0) {
167 ef_char_t *p;
168
169 if ((p = alloca(sizeof(*p) * (skk->preedit_orig_len + 1 + preedit_len + skk->new_word_len)))) {
170 memcpy(p, skk->preedit_orig, sizeof(*p) * skk->preedit_orig_len);
171 p[skk->preedit_orig_len].ch[0] = ':';
172 p[skk->preedit_orig_len].size = 1;
173 p[skk->preedit_orig_len].property = 0;
174 p[skk->preedit_orig_len].cs = US_ASCII;
175
176 if (skk->new_word_len > 0) {
177 memcpy(p + skk->preedit_orig_len + 1, skk->new_word, sizeof(*p) * skk->new_word_len);
178 }
179
180 if (preedit_len > 0) {
181 memcpy(p + skk->preedit_orig_len + 1 + skk->new_word_len, preedit,
182 preedit_len * sizeof(*p));
183 }
184
185 preedit = p;
186 rev_pos = skk->preedit_orig_len + 1 + skk->new_word_len;
187 preedit_len += rev_pos;
188 }
189 }
190
191 if (preedit == NULL) {
192 goto candidate;
193 } else if (preedit_len == 0) {
194 if (skk->im.preedit.filled_len > 0) {
195 /* Stop preediting. */
196 skk->im.preedit.filled_len = 0;
197 }
198 } else {
199 ef_char_t ch;
200 vt_char_t *p;
201 size_t pos_len;
202 u_int count;
203
204 pos_len = strlen(pos);
205
206 if ((p = realloc(skk->im.preedit.chars, sizeof(vt_char_t) * (preedit_len + pos_len))) == NULL) {
207 return;
208 }
209
210 (*syms->vt_str_init)(skk->im.preedit.chars = p,
211 skk->im.preedit.num_chars = (preedit_len + pos_len));
212
213 skk->im.preedit.filled_len = 0;
214 for (count = 0; count < preedit_len; count++) {
215 int is_fullwidth;
216 int is_comb;
217
218 ch = preedit[count];
219
220 if ((*syms->vt_convert_to_internal_ch)(skk->im.vtparser, &ch) <= 0) {
221 continue;
222 }
223
224 if (ch.property & EF_FULLWIDTH) {
225 is_fullwidth = 1;
226 } else if (ch.property & EF_AWIDTH) {
227 /* TODO: check col_size_of_width_a */
228 is_fullwidth = 1;
229 } else {
230 is_fullwidth = IS_FULLWIDTH_CS(ch.cs);
231 }
232
233 if (ch.property & EF_COMBINING) {
234 is_comb = 1;
235
236 if ((*syms->vt_char_combine)(p - 1, ef_char_to_int(&ch), ch.cs, is_fullwidth,
237 (ch.property & EF_AWIDTH) ? 1 : 0, is_comb,
238 VT_FG_COLOR, VT_BG_COLOR, 0, 0, LS_UNDERLINE_SINGLE, 0, 0)) {
239 continue;
240 }
241
242 /*
243 * if combining failed , char is normally appended.
244 */
245 } else {
246 is_comb = 0;
247 }
248
249 if (rev_pos <= skk->im.preedit.filled_len && skk->im.preedit.filled_len < rev_pos + rev_len) {
250 (*syms->vt_char_set)(p, ef_char_to_int(&ch), ch.cs, is_fullwidth,
251 (ch.property & EF_AWIDTH) ? 1 : 0, is_comb, VT_BG_COLOR,
252 VT_FG_COLOR, 0, 0, LS_UNDERLINE_SINGLE, 0, 0);
253 } else {
254 (*syms->vt_char_set)(p, ef_char_to_int(&ch), ch.cs, is_fullwidth,
255 (ch.property & EF_AWIDTH) ? 1 : 0, is_comb, VT_FG_COLOR,
256 VT_BG_COLOR, 0, 0, LS_UNDERLINE_SINGLE, 0, 0);
257 }
258
259 p++;
260 skk->im.preedit.filled_len++;
261 }
262
263 for (; pos_len > 0; pos_len--) {
264 (*syms->vt_char_set)(p++, *(pos++), US_ASCII, 0, 0, 0, VT_FG_COLOR, VT_BG_COLOR, 0, 0,
265 LS_UNDERLINE_SINGLE, 0, 0);
266 skk->im.preedit.filled_len++;
267 }
268 }
269
270 (*skk->im.listener->draw_preedit_str)(skk->im.listener->self, skk->im.preedit.chars,
271 skk->im.preedit.filled_len, skk->im.preedit.cursor_offset);
272
273 candidate:
274 if (candidateword == NULL) {
275 return;
276 } else if (candidateword_len == 0 && strlen(candidateword) == 0) {
277 if (skk->im.stat_screen) {
278 (*skk->im.stat_screen->destroy)(skk->im.stat_screen);
279 skk->im.stat_screen = NULL;
280 }
281 } else {
282 u_char *tmp = NULL;
283
284 (*skk->im.listener->get_spot)(skk->im.listener->self, skk->im.preedit.chars,
285 skk->im.preedit.segment_offset, &x, &y);
286
287 if (skk->im.stat_screen == NULL) {
288 if (!(skk->im.stat_screen = (*syms->ui_im_status_screen_new)(
289 skk->im.disp, skk->im.font_man, skk->im.color_man, skk->im.vtparser,
290 (*skk->im.listener->is_vertical)(skk->im.listener->self),
291 (*skk->im.listener->get_line_height)(skk->im.listener->self), x, y))) {
292 #ifdef DEBUG
293 bl_warn_printf(BL_DEBUG_TAG " ui_im_status_screen_new() failed.\n");
294 #endif
295
296 return;
297 }
298 } else {
299 (*skk->im.stat_screen->show)(skk->im.stat_screen);
300 (*skk->im.stat_screen->set_spot)(skk->im.stat_screen, x, y);
301 }
302
303 (*skk->im.stat_screen->set)(skk->im.stat_screen, skk->parser_term, candidateword);
304
305 if (tmp) {
306 free(tmp);
307 }
308 }
309 }
310
commit(im_skk_t * skk)311 static void commit(im_skk_t *skk) {
312 (*skk->im.listener->write_to_term)(skk->im.listener->self,
313 (u_char*)skk->preedit, skk->preedit_len * sizeof(ef_char_t),
314 ef_str_parser_get());
315 }
316
insert_char(im_skk_t * skk,u_char key_char)317 static int insert_char(im_skk_t *skk, u_char key_char) {
318 static struct {
319 wchar a;
320 wchar i;
321 wchar u;
322 wchar e;
323 wchar o;
324
325 } kana_table[] = {
326 /* a */ /* i */ /* u */ /* e */ /* o */
327 {0xa4a2, 0xa4a4, 0xa4a6, 0xa4a8, 0xa4aa},
328 {0xa4d0, 0xa4d3, 0xa4d6, 0xa4d9, 0xa4dc}, /* b */
329 {0xa4ab, 0xa4ad, 0xa4af, 0xa4b1, 0xa4b3}, /* c */
330 {0xa4c0, 0xa4c2, 0xa4c5, 0xa4c7, 0xa4c9}, /* d */
331 {0xa4e3, 0xa4a3, 0xa4e5, 0xa4a7, 0xa4e7}, /* xy/dh */
332 {0, 0, 0xa4d5, 0, 0,}, /* f */
333 {0xa4ac, 0xa4ae, 0xa4b0, 0xa4b2, 0xa4b4}, /* g */
334 {0xa4cf, 0xa4d2, 0xa4d5, 0xa4d8, 0xa4db}, /* h */
335 {0xa4e3, 0, 0xa4e5, 0xa4a7, 0xa4e7}, /* ch/sh */
336 {0, 0xa4b8, 0, 0, 0,}, /* j */
337 {0xa4ab, 0xa4ad, 0xa4af, 0xa4b1, 0xa4b3}, /* k */
338 {0xa4a1, 0xa4a3, 0xa4a5, 0xa4a7, 0xa4a9}, /* l */
339 {0xa4de, 0xa4df, 0xa4e0, 0xa4e1, 0xa4e2}, /* m */
340 {0xa4ca, 0xa4cb, 0xa4cc, 0xa4cd, 0xa4ce}, /* n */
341 {0, 0, 0, 0, 0,},
342 {0xa4d1, 0xa4d4, 0xa4d7, 0xa4da, 0xa4dd}, /* p */
343 {0, 0, 0, 0, 0,},
344 {0xa4e9, 0xa4ea, 0xa4eb, 0xa4ec, 0xa4ed}, /* r */
345 {0xa4b5, 0xa4b7, 0xa4b9, 0xa4bb, 0xa4bd}, /* s */
346 {0xa4bf, 0xa4c1, 0xa4c4, 0xa4c6, 0xa4c8}, /* t */
347 {0, 0, 0, 0, 0,},
348 {0, 0, 0, 0, 0,},
349 {0xa4ef, 0xa4f0, 0, 0xa4f1, 0xa4f2}, /* w */
350 {0xa4a1, 0xa4a3, 0xa4a5, 0xa4a7, 0xa4a9}, /* x */
351 {0xa4e4, 0, 0xa4e6, 0, 0xa4e8}, /* y */
352 {0xa4b6, 0xa4b8, 0xa4ba, 0xa4bc, 0xa4be}, /* z */
353 };
354 static wchar sign_table1[] = {
355 0xa1aa, 0xa1c9, 0xa1f4, 0xa1f0, 0xa1f3, 0xa1f5, 0xa1c7, 0xa1ca, 0xa1cb, 0xa1f6, 0xa1dc,
356 0xa1a4, 0xa1bc, 0xa1a3, 0xa1bf, 0xa3b0, 0xa3b1, 0xa3b2, 0xa3b3, 0xa3b4, 0xa3b5, 0xa3b6,
357 0xa3b7, 0xa3b8, 0xa3b9, 0xa1a7, 0xa1a8, 0xa1e3, 0xa1e1, 0xa1e4, 0xa1a9, 0xa1f7,
358 };
359 static wchar sign_table2[] = {
360 0xa1ce, 0xa1ef, 0xa1cf, 0xa1b0, 0xa1b2,
361 };
362 static wchar sign_table3[] = {
363 0xa1d0, 0xa1c3, 0xa1d1, 0xa1c1,
364 };
365 wchar wch;
366
367 if (skk->dan) {
368 preedit_destroy(skk);
369 }
370
371 if (key_char == 'a' || key_char == 'i' || key_char == 'u' || key_char == 'e' || key_char == 'o') {
372 if (skk->dan == 'f' - 'a') {
373 if (key_char != 'u') {
374 preedit_add(skk, 0xa4d5); /* hu */
375 skk->dan = 'x' - 'a';
376 }
377 } else if (skk->dan == 'j' - 'a') {
378 if (key_char != 'i') {
379 preedit_add(skk, 0xa4b8); /* zi */
380 skk->dan = 'e' - 'a';
381 }
382 }
383
384 if (key_char == 'a') {
385 wch = kana_table[skk->dan].a;
386 skk->dan = 0;
387 } else if (key_char == 'i') {
388 if (skk->dan == 'i' - 'a') {
389 skk->dan = 0;
390
391 return 0; /* shi/chi */
392 }
393
394 wch = kana_table[skk->dan].i;
395 skk->dan = 0;
396 } else if (key_char == 'u') {
397 if (skk->dan == 'j' - 'a') {
398 preedit_add(skk, 0xa4b8); /* zi */
399 skk->dan = 'e' - 'a';
400 }
401 wch = kana_table[skk->dan].u;
402 skk->dan = 0;
403 } else if (key_char == 'e') {
404 if (skk->dan == 'f' - 'a') {
405 preedit_add(skk, 0xa4d5); /* hu */
406 skk->dan = 'x' - 'a';
407 } else if (skk->dan == 'j' - 'a') {
408 preedit_add(skk, 0xa4b8); /* zi */
409 skk->dan = 'x' - 'a';
410 }
411 wch = kana_table[skk->dan].e;
412 skk->dan = 0;
413 } else /* if( key_char == 'o') */
414 {
415 if (skk->dan == 'f' - 'a') {
416 preedit_add(skk, 0xa4d5); /* hu */
417 skk->dan = 'x' - 'a';
418 } else if (skk->dan == 'j' - 'a') {
419 preedit_add(skk, 0xa4b8); /* zi */
420 skk->dan = 'e' - 'a';
421 }
422 wch = kana_table[skk->dan].o;
423 skk->dan = 0;
424 }
425 } else if (('!' <= key_char && key_char <= '@') || ('[' <= key_char && key_char <= '_') ||
426 ('{' <= key_char && key_char <= '~')) {
427 if (skk->dan) {
428 preedit_add(skk, skk->dan + 'a');
429 skk->dan = 0;
430 }
431
432 if (key_char <= '@') {
433 wch = sign_table1[key_char - '!'];
434 } else if (key_char <= '_') {
435 wch = sign_table2[key_char - '['];
436 } else {
437 wch = sign_table3[key_char - '{'];
438 }
439 } else {
440 if (skk->dan == 'n' - 'a' && key_char != 'a' && key_char != 'i' && key_char != 'u' &&
441 key_char != 'e' && key_char != 'o' && key_char != 'y') {
442 if (key_char == 'n') {
443 wch = 0xa4f3; /* n */
444 skk->dan = 0;
445 } else {
446 preedit_add(skk, 0xa4f3);
447 wch = key_char;
448 skk->dan = key_char - 'a';
449 }
450 } else if (key_char == skk->dan + 'a') {
451 preedit_add(skk, 0xa4c3);
452 wch = key_char;
453 } else if (key_char == 'y') {
454 if (skk->dan == 'k' - 'a') {
455 preedit_add(skk, 0xa4ad); /* ki */
456 skk->dan = 'x' - 'a';
457 } else if (skk->dan == 'g' - 'a') {
458 preedit_add(skk, 0xa4ae); /* gi */
459 skk->dan = 'x' - 'a';
460 } else if (skk->dan == 's' - 'a') {
461 preedit_add(skk, 0xa4b7); /* si */
462 skk->dan = 'x' - 'a';
463 } else if (skk->dan == 'z' - 'a') {
464 preedit_add(skk, 0xa4b8); /* zi */
465 skk->dan = 'x' - 'a';
466 } else if (skk->dan == 't' - 'a' || skk->dan == 'c' - 'a') {
467 preedit_add(skk, 0xa4c1); /* ti */
468 skk->dan = 'x' - 'a';
469 } else if (skk->dan == 'd' - 'a') {
470 preedit_add(skk, 0xa4c2); /* di */
471 skk->dan = 'x' - 'a';
472 } else if (skk->dan == 'n' - 'a') {
473 preedit_add(skk, 0xa4cb); /* ni */
474 skk->dan = 'x' - 'a';
475 } else if (skk->dan == 'h' - 'a') {
476 preedit_add(skk, 0xa4d2); /* hi */
477 skk->dan = 'x' - 'a';
478 } else if (skk->dan == 'b' - 'a') {
479 preedit_add(skk, 0xa4d3); /* bi */
480 skk->dan = 'x' - 'a';
481 } else if (skk->dan == 'p' - 'a') {
482 preedit_add(skk, 0xa4d4); /* pi */
483 skk->dan = 'x' - 'a';
484 } else if (skk->dan == 'm' - 'a') {
485 preedit_add(skk, 0xa4df); /* mi */
486 skk->dan = 'x' - 'a';
487 } else if (skk->dan == 'r' - 'a') {
488 preedit_add(skk, 0xa4ea); /* ri */
489 skk->dan = 'x' - 'a';
490 }
491
492 if (skk->dan == 'x' - 'a') {
493 skk->dan = 'e' - 'a';
494 wch = 'y';
495 } else if (skk->dan == 'v' - 'a') {
496 if (key_char == 'u') {
497 wch = 0xa5f4;
498 skk->dan = 0;
499 } else {
500 preedit_add(skk, 0xa5f4); /* v */
501 skk->dan = 'x' - 'a';
502
503 return insert_char(skk, key_char);
504 }
505 } else {
506 goto normal;
507 }
508 } else if (key_char == 'h') {
509 if (skk->dan == 'c' - 'a') {
510 preedit_add(skk, 0xa4c1); /* ti */
511 skk->dan = 'i' - 'a';
512 } else if (skk->dan == 's' - 'a') {
513 preedit_add(skk, 0xa4b7); /* si */
514 skk->dan = 'i' - 'a';
515 } else if (skk->dan == 'd' - 'a') {
516 preedit_add(skk, 0xa4c7); /* di */
517 skk->dan = 'e' - 'a';
518 } else {
519 goto normal;
520 }
521
522 wch = 'h';
523 } else {
524 normal:
525 if (skk->dan) {
526 preedit_add(skk, skk->dan + 'a');
527 }
528
529 wch = key_char;
530
531 if ('a' <= key_char && key_char <= 'z') {
532 skk->dan = key_char - 'a';
533 } else {
534 skk->dan = 0;
535 }
536 }
537 }
538
539 if (wch == 0) {
540 return 1;
541 }
542
543 preedit_add(skk, wch);
544
545 return 0;
546 }
547
insert_alphabet_full(im_skk_t * skk,u_char key_char)548 static void insert_alphabet_full(im_skk_t *skk, u_char key_char) {
549 if (('a' <= key_char && key_char <= 'z') || ('A' <= key_char && key_char <= 'Z') ||
550 ('0' <= key_char && key_char <= '9')) {
551 preedit_add(skk, 0x2300 + key_char + 0x8080);
552 } else if (0x20 <= key_char && key_char <= 0x7e) {
553 insert_char(skk, key_char);
554 }
555 }
556
557 /*
558 * methods of ui_im_t
559 */
560
destroy(ui_im_t * im)561 static void destroy(ui_im_t *im) {
562 im_skk_t *skk;
563
564 skk = (im_skk_t*)im;
565
566 (*skk->parser_term->destroy)(skk->parser_term);
567
568 if (skk->conv) {
569 (*skk->conv->destroy)(skk->conv);
570 }
571
572 free(skk->status[HIRAGANA]);
573 free(skk->status[KATAKANA]);
574 free(skk->status[ALPHABET_FULL]);
575
576 if (skk->completion) {
577 dict_completion_finish(&skk->completion);
578 }
579
580 if (skk->candidate) {
581 dict_candidate_finish(&skk->candidate);
582 }
583
584 free(skk);
585
586 ref_count--;
587
588 if (ref_count == 0) {
589 dict_final();
590 }
591
592 #ifdef IM_SKK_DEBUG
593 bl_debug_printf(BL_DEBUG_TAG " An object was destroyed. ref_count: %d\n", ref_count);
594 #endif
595 }
596
switch_mode(ui_im_t * im)597 static int switch_mode(ui_im_t *im) {
598 im_skk_t *skk;
599
600 skk = (im_skk_t*)im;
601
602 if ((skk->is_enabled = (!skk->is_enabled))) {
603 skk->mode = HIRAGANA;
604 preedit(skk, "", 0, 0, skk->status[skk->mode], 0, "");
605 } else {
606 preedit_clear(skk);
607 preedit(skk, "", 0, 0, "", 0, "");
608 }
609
610 return 1;
611 }
612
start_to_register_new_word(im_skk_t * skk)613 static void start_to_register_new_word(im_skk_t *skk) {
614 memcpy(skk->preedit_orig, skk->preedit, sizeof(skk->preedit[0]) * skk->preedit_len);
615 if (skk->prev_dan) {
616 if (skk->is_preediting == 4) {
617 skk->preedit_len--;
618 }
619
620 skk->preedit_orig[skk->preedit_len - 1].ch[0] = skk->prev_dan + 'a';
621 skk->preedit_orig[skk->preedit_len - 1].size = 1;
622 skk->preedit_orig[skk->preedit_len - 1].cs = US_ASCII;
623 skk->preedit_orig[skk->preedit_len - 1].property = 0;
624 }
625
626 skk->preedit_orig_len = skk->preedit_len;
627 skk->is_preediting_orig = skk->is_preediting;
628 skk->dan = 0;
629 skk->prev_dan_orig = skk->prev_dan;
630 skk->mode_orig = skk->mode;
631 candidate_clear(skk);
632
633 skk->new_word_len = 0;
634 skk->is_editing_new_word = 1;
635 preedit_clear(skk);
636 skk->is_preediting = 0;
637 }
638
stop_to_register_new_word(im_skk_t * skk)639 static void stop_to_register_new_word(im_skk_t *skk) {
640 memcpy(skk->preedit, skk->preedit_orig, sizeof(skk->preedit_orig[0]) * skk->preedit_orig_len);
641 skk->preedit_len = skk->preedit_orig_len;
642 skk->preedit_orig_len = 0;
643 skk->dan = 0;
644 skk->prev_dan = skk->prev_dan_orig;
645 skk->mode = skk->mode_orig;
646
647 skk->new_word_len = 0;
648 skk->is_editing_new_word = 0;
649 skk->is_preediting = skk->is_preediting_orig;
650
651 preedit_to_visual(skk);
652 }
653
candidate_set(im_skk_t * skk,int step)654 static void candidate_set(im_skk_t *skk, int step) {
655 if (skk->preedit_len == 0) {
656 return;
657 }
658
659 if (skk->prev_dan) {
660 if (skk->is_preediting == 4) {
661 skk->visual_chars[1] = skk->preedit[--skk->preedit_len];
662 }
663
664 skk->visual_chars[0] = skk->preedit[skk->preedit_len - 1];
665 skk->preedit[skk->preedit_len - 1].ch[0] = skk->prev_dan + 'a';
666 skk->preedit[skk->preedit_len - 1].size = 1;
667 skk->preedit[skk->preedit_len - 1].cs = US_ASCII;
668 skk->preedit[skk->preedit_len - 1].property = 0;
669 }
670
671 skk->preedit_len = dict_candidate(skk->preedit, skk->preedit_len, &skk->candidate, step);
672
673 if (!skk->candidate) {
674 if (skk->is_editing_new_word) {
675 return;
676 }
677
678 #if 0
679 if (skk->prev_dan) {
680 skk->preedit[skk->preedit_len - 1] = skk->visual_chars[0];
681
682 if (skk->is_preediting == 4) {
683 skk->preedit_len++;
684 }
685 }
686 #endif
687
688 start_to_register_new_word(skk);
689
690 return;
691 }
692
693 if (skk->prev_dan) {
694 skk->preedit[skk->preedit_len++] = skk->visual_chars[0];
695
696 if (skk->is_preediting == 4) {
697 skk->preedit[skk->preedit_len++] = skk->visual_chars[1];
698 }
699 }
700
701 if (skk->dan) {
702 ef_char_t *ch;
703
704 ch = skk->preedit + (skk->preedit_len++);
705 ch->ch[0] = skk->dan + 'a';
706 ch->cs = US_ASCII;
707 ch->size = 0;
708 ch->property = 0;
709 }
710 }
711
candidate_unset(im_skk_t * skk)712 static void candidate_unset(im_skk_t *skk) {
713 if (skk->candidate) {
714 skk->preedit_len = dict_candidate_reset_and_finish(skk->preedit, &skk->candidate);
715 }
716
717 preedit_to_visual(skk);
718 }
719
candidate_clear(im_skk_t * skk)720 static void candidate_clear(im_skk_t *skk) {
721 if (skk->candidate) {
722 dict_candidate_finish(&skk->candidate);
723 }
724 }
725
fix(im_skk_t * skk)726 static int fix(im_skk_t *skk) {
727 if (skk->preedit_len > 0) {
728 if (skk->candidate) {
729 dict_candidate_add_to_local(skk->candidate);
730 }
731
732 if (skk->is_editing_new_word) {
733 memcpy(skk->new_word + skk->new_word_len, skk->preedit,
734 skk->preedit_len * sizeof(skk->preedit[0]));
735 skk->new_word_len += skk->preedit_len;
736 preedit(skk, "", 0, 0, skk->status[skk->mode], 0, "");
737 } else {
738 preedit(skk, "", 0, 0, skk->status[skk->mode], 0, "");
739 commit(skk);
740 }
741 preedit_clear(skk);
742 candidate_clear(skk);
743 } else if (skk->is_editing_new_word) {
744 if (skk->new_word_len > 0) {
745 dict_add_new_word_to_local(skk->preedit_orig, skk->preedit_orig_len, skk->new_word,
746 skk->new_word_len);
747
748 candidate_clear(skk);
749 stop_to_register_new_word(skk);
750 candidate_set(skk, 0);
751 commit(skk);
752 preedit_clear(skk);
753 candidate_clear(skk);
754 } else {
755 stop_to_register_new_word(skk);
756 candidate_clear(skk);
757 }
758 } else {
759 return 1;
760 }
761
762 return 0;
763 }
764
key_event(ui_im_t * im,u_char key_char,KeySym ksym,XKeyEvent * event)765 static int key_event(ui_im_t *im, u_char key_char, KeySym ksym, XKeyEvent *event) {
766 im_skk_t *skk;
767 int ret = 0;
768 char *pos = "";
769 char *cand = NULL;
770 u_int cand_len = 0;
771 int preedited_alphabet;
772
773 skk = (im_skk_t*)im;
774
775 if (skk->preedit_len > 0 && !skk->candidate && (ksym == XK_Tab || ksym == XK_ISO_Left_Tab)) {
776 skk->preedit_len =
777 dict_completion(skk->preedit, skk->preedit_len, &skk->completion,
778 ksym == XK_ISO_Left_Tab || (event->state & ShiftMask) ? -1 : 1);
779 goto end;
780 } else if (skk->completion) {
781 if ((event->state & ControlMask) && key_char == '\x07') {
782 skk->preedit_len = dict_completion_reset_and_finish(skk->preedit, &skk->completion);
783
784 goto end;
785 } else if (ksym == XK_Shift_L || ksym == XK_Shift_R || ksym == XK_Control_L ||
786 ksym == XK_Control_R) {
787 /* Starting Shift+Tab or Control+g */
788 return 0;
789 }
790
791 dict_completion_finish(&skk->completion);
792 }
793
794 if (key_char == ' ' && (event->state & ShiftMask)) {
795 fix(skk);
796 switch_mode(im);
797
798 if (!skk->is_enabled) {
799 return 0;
800 }
801
802 goto end;
803 } else if (!skk->is_enabled) {
804 return 1;
805 }
806
807 /* skk is enabled */
808
809 if (skk->mode != ALPHABET && skk->mode != ALPHABET_FULL) {
810 if (sticky_shift_ch ? (key_char == sticky_shift_ch) :
811 (sticky_shift_ksym ? (ksym == sticky_shift_ksym) : 0)) {
812 if (skk->sticky_shift) {
813 skk->sticky_shift = 0;
814 } else {
815 skk->sticky_shift = 1;
816
817 return 0;
818 }
819 } else if (skk->sticky_shift) {
820 if ('a' <= key_char && key_char <= 'z') {
821 key_char -= 0x20;
822 }
823 event->state |= ShiftMask;
824 skk->sticky_shift = 0;
825 }
826 }
827
828 if ((skk->mode != ALPHABET && key_char == 'l') ||
829 (skk->mode != ALPHABET_FULL && key_char == 'L')) {
830 if (!skk->is_editing_new_word) {
831 fix(skk);
832 }
833 skk->mode = (key_char == 'l') ? ALPHABET : ALPHABET_FULL;
834 cand = skk->status[skk->mode];
835 } else if (key_char == '\r' || key_char == '\n') {
836 if ((event->state & ControlMask) && key_char == '\n') {
837 if (!skk->is_editing_new_word || skk->preedit_len > 0) {
838 fix(skk);
839 }
840
841 if (skk->mode == ALPHABET || skk->mode == ALPHABET_FULL) {
842 /* Ctrl+j */
843 skk->mode = HIRAGANA;
844 cand = skk->status[skk->mode];
845 }
846 } else {
847 preedited_alphabet = (skk->mode == ALPHABET && skk->is_preediting);
848
849 ret = fix(skk);
850
851 if (preedited_alphabet) {
852 cand = skk->status[skk->mode];
853 }
854 }
855 } else if (ksym == XK_BackSpace || ksym == XK_Delete || key_char == 0x08 /* Ctrl+h */) {
856 if (skk->preedit_len > 0) {
857 if (skk->candidate) {
858 candidate_unset(skk);
859 cand = skk->status[skk->mode];
860 } else {
861 skk->dan = skk->prev_dan = 0;
862 skk->is_preediting = 1;
863
864 if (skk->preedit_len == 1) {
865 preedit_clear(skk);
866 } else {
867 preedit_destroy(skk);
868 }
869 }
870 } else if (skk->is_editing_new_word) {
871 if (skk->new_word_len > 0) {
872 skk->new_word_len--;
873 }
874 } else {
875 ret = 1;
876 }
877 } else if ((event->state & ControlMask) && key_char == '\x07') {
878 /* Ctrl+g */
879 if (skk->candidate) {
880 candidate_unset(skk);
881 candidate_clear(skk);
882 skk->dan = skk->prev_dan = 0;
883 skk->is_preediting = 1;
884 } else if (skk->is_editing_new_word && skk->preedit_len == 0) {
885 stop_to_register_new_word(skk);
886 } else {
887 preedit_clear(skk);
888 }
889 cand = skk->status[skk->mode];
890 } else if (key_char != ' ' && key_char != '\0') {
891 if (key_char < ' ') {
892 ret = 1;
893 } else if (skk->mode != ALPHABET && key_char == 'q') {
894 if (skk->mode == HIRAGANA) {
895 skk->mode = KATAKANA;
896 } else {
897 skk->mode = HIRAGANA;
898 }
899 cand = skk->status[skk->mode];
900 } else if (skk->mode != ALPHABET && !skk->is_preediting && key_char == '/') {
901 skk->is_preediting = 1;
902 skk->mode = ALPHABET;
903 cand = skk->status[skk->mode];
904 } else {
905 if (skk->candidate && !skk->dan) {
906 preedited_alphabet = (skk->mode == ALPHABET && skk->is_preediting);
907
908 fix(skk);
909
910 if (preedited_alphabet) {
911 cand = skk->status[skk->mode];
912 }
913 }
914
915 if (skk->mode != ALPHABET && skk->mode != ALPHABET_FULL) {
916 while (event->state & ShiftMask) {
917 if ('A' <= key_char && key_char <= 'Z') {
918 key_char += 0x20;
919 } else if (key_char < 'a' || 'z' < key_char) {
920 break;
921 }
922
923 if (skk->preedit_len == 0) {
924 skk->is_preediting = 1;
925 } else if (skk->is_preediting && !skk->dan) {
926 skk->is_preediting = 2;
927 }
928
929 break;
930 }
931 }
932
933 if (skk->mode == ALPHABET) {
934 preedit_add(skk, key_char);
935
936 if (!skk->is_preediting) {
937 fix(skk);
938 }
939 } else if (skk->mode == ALPHABET_FULL) {
940 insert_alphabet_full(skk, key_char);
941
942 fix(skk);
943 } else if (insert_char(skk, key_char) != 0) {
944 ret = 1;
945 } else if (skk->is_preediting >= 2) {
946 if (skk->dan) {
947 if (skk->dan == skk->prev_dan) {
948 /* sokuon */
949 skk->is_preediting++;
950 } else {
951 skk->prev_dan = skk->dan;
952 }
953 } else {
954 if (!skk->prev_dan &&
955 (key_char == 'i' || key_char == 'u' || key_char == 'e' || key_char == 'o')) {
956 skk->prev_dan = key_char - 'a';
957 }
958
959 skk->is_preediting++;
960 candidate_set(skk, 0);
961 }
962 } else if (!skk->dan && !skk->is_preediting) {
963 fix(skk);
964 }
965 }
966 } else if (skk->is_preediting && (key_char == ' ' || ksym == XK_Up || ksym == XK_Down ||
967 ksym == XK_Right || ksym == XK_Left)) {
968 int update = 0;
969 int step;
970 int cur_index;
971 u_int num;
972
973 if (!skk->candidate) {
974 if (key_char != ' ') {
975 return 1;
976 }
977
978 step = 0;
979 skk->start_candidate = 1;
980 } else {
981 dict_candidate_get_state(skk->candidate, &cur_index, &num);
982
983 if (ksym == XK_Left) {
984 step = -1;
985
986 if (cur_index % CAND_UNIT == 0) {
987 update = 1;
988 }
989 } else if (ksym == XK_Up) {
990 step = -CAND_UNIT;
991 update = 1;
992 } else if (ksym == XK_Down) {
993 step = CAND_UNIT;
994 update = 1;
995 } else {
996 step = 1;
997
998 if (cur_index == num - 1) {
999 if (!skk->is_editing_new_word) {
1000 preedit_backup_visual_chars(skk);
1001 candidate_unset(skk);
1002 candidate_clear(skk);
1003 start_to_register_new_word(skk);
1004 cand = skk->status[skk->mode];
1005
1006 goto end;
1007 } else {
1008 update = 1;
1009 }
1010 } else if ((cur_index % CAND_UNIT == CAND_UNIT - 1) || skk->start_candidate) {
1011 update = 1;
1012 }
1013 }
1014
1015 skk->start_candidate = 0;
1016 }
1017
1018 preedited_alphabet = (skk->mode == ALPHABET && skk->is_preediting);
1019
1020 candidate_set(skk, step);
1021
1022 if (preedited_alphabet && !skk->is_preediting) {
1023 cand = skk->status[skk->mode];
1024 }
1025
1026 if (skk->candidate) {
1027 dict_candidate_get_state(skk->candidate, &cur_index, &num);
1028
1029 if (update) {
1030 if ((cand = alloca(1024))) {
1031 dict_candidate_get_list(skk->candidate, cand, 1024, skk->conv);
1032 }
1033 }
1034
1035 if ((pos = alloca(4 + DIGIT_STR_LEN(int)*2 + 1))) {
1036 sprintf(pos, " [%d/%d]", cur_index + 1, num);
1037 }
1038 }
1039 } else if (skk->is_editing_new_word) {
1040 if (key_char == ' ') {
1041 preedit_add(skk, key_char);
1042 fix(skk);
1043 }
1044
1045 ret = 0;
1046 } else {
1047 ret = 1;
1048 }
1049
1050 end:
1051 preedit(skk, skk->preedit, skk->preedit_len, skk->is_preediting ? skk->preedit_len : 0, cand,
1052 cand_len, pos);
1053
1054 return ret;
1055 }
1056
is_active(ui_im_t * im)1057 static int is_active(ui_im_t *im) { return ((im_skk_t*)im)->is_enabled; }
1058
focused(ui_im_t * im)1059 static void focused(ui_im_t *im) {
1060 im_skk_t *skk;
1061
1062 skk = (im_skk_t*)im;
1063
1064 if (skk->im.stat_screen) {
1065 (*skk->im.stat_screen->show)(skk->im.stat_screen);
1066 }
1067 }
1068
unfocused(ui_im_t * im)1069 static void unfocused(ui_im_t *im) {
1070 im_skk_t *skk;
1071
1072 skk = (im_skk_t*)im;
1073
1074 if (skk->im.stat_screen) {
1075 (*skk->im.stat_screen->hide)(skk->im.stat_screen);
1076 }
1077 }
1078
set_sticky_shift_key(char * key)1079 static void set_sticky_shift_key(char *key) {
1080 int digit;
1081 if (strlen(key) == 1) {
1082 sticky_shift_ch = *key;
1083 } else if (sscanf(key, "\\x%x", &digit) == 1) {
1084 sticky_shift_ch = digit;
1085 } else {
1086 sticky_shift_ksym = (*syms->XStringToKeysym)(key);
1087 sticky_shift_ch = 0;
1088
1089 return;
1090 }
1091
1092 sticky_shift_ksym = 0;
1093 }
1094
1095 /* --- global functions --- */
1096
im_skk_new(u_int64_t magic,vt_char_encoding_t term_encoding,ui_im_export_syms_t * export_syms,char * options,u_int mod_ignore_mask)1097 ui_im_t *im_skk_new(u_int64_t magic, vt_char_encoding_t term_encoding,
1098 ui_im_export_syms_t *export_syms, char *options, u_int mod_ignore_mask) {
1099 im_skk_t *skk;
1100 ef_parser_t *parser;
1101 char *env;
1102
1103 if (magic != (u_int64_t)IM_API_COMPAT_CHECK_MAGIC) {
1104 bl_error_printf("Incompatible input method API.\n");
1105
1106 return NULL;
1107 }
1108
1109 if (ref_count == 0) {
1110 syms = export_syms;
1111 }
1112
1113 if (!(skk = calloc(1, sizeof(im_skk_t)))) {
1114 #ifdef DEBUG
1115 bl_warn_printf(BL_DEBUG_TAG " malloc failed.\n");
1116 #endif
1117
1118 goto error;
1119 }
1120
1121 if ((env = getenv("SKK_DICTIONARY"))) {
1122 dict_set_global(env);
1123 }
1124
1125 if ((env = getenv("SKK_STICKY_SHIFT_KEY"))) {
1126 set_sticky_shift_key(env);
1127 }
1128
1129 /* Same processing as skk_widget_new() in mc_im.c */
1130 if (options) {
1131 #if 1
1132 /* XXX Compat with 3.8.3 or before. */
1133 if (!strchr(options, '=')) {
1134 dict_set_global(options);
1135 } else
1136 #endif
1137 {
1138 char *next;
1139
1140 do {
1141 if ((next = strchr(options, ','))) {
1142 *(next++) = '\0';
1143 }
1144
1145 if (strncmp(options, "sskey=", 6) == 0) {
1146 set_sticky_shift_key(options + 6);
1147 } else if (strncmp(options, "dict=", 5) == 0) {
1148 dict_set_global(options + 5);
1149 }
1150 } while ((options = next));
1151 }
1152 }
1153
1154 skk->term_encoding = term_encoding;
1155 skk->encoding_name = (*syms->vt_get_char_encoding_name)(term_encoding);
1156
1157 if (!(skk->conv = (*syms->vt_char_encoding_conv_new)(term_encoding))) {
1158 goto error;
1159 }
1160
1161 if (!(skk->parser_term = (*syms->vt_char_encoding_parser_new)(term_encoding))) {
1162 goto error;
1163 }
1164
1165 skk->status[HIRAGANA] = "\xa4\xab\xa4\xca";
1166 skk->status[KATAKANA] = "\xa5\xab\xa5\xca";
1167 skk->status[ALPHABET_FULL] = "\xc1\xb4\xb1\xd1";
1168 skk->status[ALPHABET] = "SKK";
1169
1170 if (term_encoding == VT_EUCJP || !(parser = (*syms->vt_char_encoding_parser_new)(VT_EUCJP))) {
1171 input_mode_t mode;
1172
1173 for (mode = 0; mode <= ALPHABET_FULL; mode++) {
1174 skk->status[mode] = strdup(skk->status[mode]);
1175 }
1176 } else {
1177 input_mode_t mode;
1178 u_char buf[64]; /* enough for EUCJP(5bytes) -> Other CS */
1179
1180 for (mode = 0; mode <= ALPHABET_FULL; mode++) {
1181 (*parser->init)(parser);
1182 (*parser->set_str)(parser, skk->status[mode], 5);
1183 (*skk->conv->init)(skk->conv);
1184 (*skk->conv->convert)(skk->conv, buf, sizeof(buf) - 1, parser);
1185 skk->status[mode] = strdup(buf);
1186 }
1187
1188 (*parser->destroy)(parser);
1189 }
1190
1191 /*
1192 * set methods of ui_im_t
1193 */
1194 skk->im.destroy = destroy;
1195 skk->im.key_event = key_event;
1196 skk->im.switch_mode = switch_mode;
1197 skk->im.is_active = is_active;
1198 skk->im.focused = focused;
1199 skk->im.unfocused = unfocused;
1200
1201 ref_count++;
1202
1203 #ifdef IM_SKK_DEBUG
1204 bl_debug_printf("New object was created. ref_count is %d.\n", ref_count);
1205 #endif
1206
1207 return (ui_im_t*)skk;
1208
1209 error:
1210 if (skk) {
1211 if (skk->parser_term) {
1212 (*skk->parser_term->destroy)(skk->parser_term);
1213 }
1214
1215 if (skk->conv) {
1216 (*skk->conv->destroy)(skk->conv);
1217 }
1218
1219 free(skk);
1220 }
1221
1222 return NULL;
1223 }
1224
1225 /* --- API for external tools --- */
1226
im_skk_get_info(char * locale,char * encoding)1227 im_info_t *im_skk_get_info(char *locale, char *encoding) {
1228 im_info_t *result;
1229
1230 if ((result = malloc(sizeof(im_info_t)))) {
1231 result->id = strdup("skk");
1232 result->name = strdup("Skk");
1233 result->num_args = 0;
1234 result->args = NULL;
1235 result->readable_args = NULL;
1236 }
1237
1238 return result;
1239 }
1240