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 
9 #include <ui_im.h>
10 
11 #include "../im_common.h"
12 #include "../im_info.h"
13 #include "wnnlib.h"
14 
15 #if 0
16 #define IM_WNN_DEBUG 1
17 #endif
18 
19 /* Don't forget to modify digit_to_wstr() if MAX_DIGIT_NUM is changed. */
20 #define MAX_DIGIT_NUM 3 /* 999 */
21 #define CAND_WINDOW_ROWS 5
22 
23 typedef struct im_wnn {
24   /* input method common object */
25   ui_im_t im;
26 
27   char buf[1024];
28   int is_enabled;
29   int is_cand;
30 
31   vt_char_encoding_t term_encoding;
32 
33   char *encoding_name; /* encoding of conversion engine */
34 
35   /* conv is NULL if term_encoding == wnn encoding */
36   ef_parser_t *parser_term; /* for term encoding */
37   ef_conv_t *conv;          /* for term encoding */
38 
39   jcConvBuf *convbuf;
40   int dan;
41 
42 } im_wnn_t;
43 
44 /* --- static variables --- */
45 
46 static int ref_count = 0;
47 static ui_im_export_syms_t *syms = NULL; /* mlterm internal symbols */
48 static ef_parser_t *parser_wchar = NULL;
49 
50 /* --- static functions --- */
51 
digit_to_wstr(wchar * dst,int digit)52 static wchar *digit_to_wstr(wchar *dst, int digit) {
53   int num;
54 
55   if (digit >= 1000) {
56     /* Ignore it because MAX_DIGIT_NUM is 3 (=999). */
57     digit -= (digit / 1000) * 1000;
58   }
59 
60   if (digit >= 100) {
61     num = digit / 100;
62     *(dst++) = num + 0x30;
63     digit -= num * 100;
64   } else if (((digit - 1) / CAND_WINDOW_ROWS + 1) * CAND_WINDOW_ROWS >= 100) {
65     *(dst++) = ' ';
66   }
67 
68   if (digit >= 10) {
69     num = digit / 10;
70     *(dst++) = num + 0x30;
71     digit -= num * 10;
72   } else if (((digit - 1) / CAND_WINDOW_ROWS + 1) * CAND_WINDOW_ROWS >= 10) {
73     *(dst++) = ' ';
74   }
75 
76   *(dst++) = digit + 0x30;
77 
78   return dst;
79 }
80 
wchar_parser_set_str(ef_parser_t * parser,u_char * str,size_t size)81 static void wchar_parser_set_str(ef_parser_t *parser, u_char *str, size_t size) {
82   parser->str = str;
83   parser->left = size;
84   parser->marked_left = 0;
85   parser->is_eos = 0;
86 }
87 
wchar_parser_destroy(ef_parser_t * parser)88 static void wchar_parser_destroy(ef_parser_t *parser) { free(parser); }
89 
wchar_parser_next_char(ef_parser_t * parser,ef_char_t * ch)90 static int wchar_parser_next_char(ef_parser_t *parser, ef_char_t *ch) {
91   wchar wch;
92 
93   if (parser->is_eos) {
94     return 0;
95   }
96 
97   ef_parser_mark(parser);
98 
99   wch = ((wchar*)parser->str)[0];
100 
101   if (wch < 0x100) {
102     ch->size = 1;
103 
104     if (wch < 0x80) {
105       ch->ch[0] = wch;
106       ch->cs = US_ASCII;
107     } else {
108       ch->ch[0] = (wch & 0x7f);
109       ch->cs = JISX0201_KATA;
110     }
111   } else if ((wch & 0x8080) == 0x8080) {
112     ef_int_to_bytes(ch->ch, 2, wch & ~0x8080);
113     ch->size = 2;
114     ch->cs = JISX0208_1983;
115   } else {
116     ef_parser_reset(parser);
117 
118     return 0;
119   }
120 
121   ef_parser_n_increment(parser, 2);
122   ch->property = 0;
123 
124   return 1;
125 }
126 
wchar_parser_new(void)127 static ef_parser_t *wchar_parser_new(void) {
128   ef_parser_t *parser;
129 
130   if ((parser = malloc(sizeof(ef_parser_t))) == NULL) {
131     return NULL;
132   }
133 
134   ef_parser_init(parser);
135 
136   parser->init = ef_parser_init;
137   parser->set_str = wchar_parser_set_str;
138   parser->destroy = wchar_parser_destroy;
139   parser->next_char = wchar_parser_next_char;
140 
141   return parser;
142 }
143 
preedit(im_wnn_t * wnn,char * preedit,size_t preedit_len,int rev_pos,int rev_len,char * candidateword,size_t candidateword_len,char * pos)144 static void preedit(im_wnn_t *wnn, char *preedit,                                      /* wchar */
145                     size_t preedit_len, int rev_pos, int rev_len, char *candidateword, /* wchar */
146                     size_t candidateword_len, char *pos) {
147   int x;
148   int y;
149 
150   if (preedit == NULL) {
151     goto candidate;
152   } else if (preedit_len == 0) {
153     if (wnn->im.preedit.filled_len > 0) {
154       /* Stop preediting. */
155       wnn->im.preedit.filled_len = 0;
156     }
157   } else {
158     ef_char_t ch;
159     vt_char_t *p;
160     u_int num_chars;
161     u_int len;
162     u_char *tmp = NULL;
163     size_t pos_len;
164 
165     wnn->im.preedit.cursor_offset = rev_pos;
166 
167     num_chars = 0;
168     (*parser_wchar->init)(parser_wchar);
169     (*parser_wchar->set_str)(parser_wchar, preedit, preedit_len);
170     while ((*parser_wchar->next_char)(parser_wchar, &ch)) {
171       num_chars++;
172     }
173 
174     pos_len = strlen(pos);
175 
176     if ((p = realloc(wnn->im.preedit.chars, sizeof(vt_char_t) * (num_chars + pos_len))) ==
177         NULL) {
178       return;
179     }
180 
181     if ((len = im_convert_encoding(parser_wchar, wnn->conv, preedit, &tmp, preedit_len))) {
182       preedit = tmp;
183       preedit_len = len;
184     }
185 
186     (*syms->vt_str_init)(wnn->im.preedit.chars = p,
187                          wnn->im.preedit.num_chars = (num_chars + pos_len));
188     wnn->im.preedit.filled_len = 0;
189 
190     (*wnn->parser_term->init)(wnn->parser_term);
191     (*wnn->parser_term->set_str)(wnn->parser_term, preedit, preedit_len);
192     while ((*wnn->parser_term->next_char)(wnn->parser_term, &ch)) {
193       int is_fullwidth;
194       int is_comb;
195 
196       if ((*syms->vt_convert_to_internal_ch)(wnn->im.vtparser, &ch) <= 0) {
197         continue;
198       }
199 
200       if (ch.property & EF_FULLWIDTH) {
201         is_fullwidth = 1;
202       } else if (ch.property & EF_AWIDTH) {
203         /* TODO: check col_size_of_width_a */
204         is_fullwidth = 1;
205       } else {
206         is_fullwidth = IS_FULLWIDTH_CS(ch.cs);
207       }
208 
209       if (ch.property & EF_COMBINING) {
210         is_comb = 1;
211 
212         if ((*syms->vt_char_combine)(p - 1, ef_char_to_int(&ch), ch.cs, is_fullwidth,
213                                      (ch.property & EF_AWIDTH) ? 1 : 0, is_comb,
214                                      VT_FG_COLOR, VT_BG_COLOR, 0, 0, LS_UNDERLINE_SINGLE, 0, 0)) {
215           continue;
216         }
217 
218         /*
219          * if combining failed , char is normally appended.
220          */
221       } else {
222         is_comb = 0;
223       }
224 
225       if (wnn->im.preedit.cursor_offset <= wnn->im.preedit.filled_len &&
226           wnn->im.preedit.filled_len < wnn->im.preedit.cursor_offset + rev_len) {
227         (*syms->vt_char_set)(p, ef_char_to_int(&ch), ch.cs, is_fullwidth,
228                              (ch.property & EF_AWIDTH) ? 1 : 0, is_comb, VT_BG_COLOR,
229                              VT_FG_COLOR, 0, 0, LS_UNDERLINE_SINGLE, 0, 0);
230       } else {
231         (*syms->vt_char_set)(p, ef_char_to_int(&ch), ch.cs, is_fullwidth,
232                              (ch.property & EF_AWIDTH) ? 1 : 0, is_comb, VT_FG_COLOR,
233                              VT_BG_COLOR, 0, 0, LS_UNDERLINE_SINGLE, 0, 0);
234       }
235 
236       p++;
237       wnn->im.preedit.filled_len++;
238     }
239 
240     for (; pos_len > 0; pos_len--) {
241       (*syms->vt_char_set)(p++, *(pos++), US_ASCII, 0, 0, 0, VT_FG_COLOR, VT_BG_COLOR, 0, 0,
242                            LS_UNDERLINE_SINGLE, 0, 0);
243       wnn->im.preedit.filled_len++;
244     }
245 
246     if (tmp) {
247       free(tmp);
248     }
249   }
250 
251   (*wnn->im.listener->draw_preedit_str)(wnn->im.listener->self, wnn->im.preedit.chars,
252                                         wnn->im.preedit.filled_len, wnn->im.preedit.cursor_offset);
253 
254 candidate:
255   if (candidateword == NULL) {
256     return;
257   } else if (candidateword_len == 0) {
258     if (wnn->im.stat_screen) {
259       (*wnn->im.stat_screen->destroy)(wnn->im.stat_screen);
260       wnn->im.stat_screen = NULL;
261     }
262   } else {
263     u_char *tmp = NULL;
264 
265     (*wnn->im.listener->get_spot)(wnn->im.listener->self, wnn->im.preedit.chars,
266                                   wnn->im.preedit.segment_offset, &x, &y);
267 
268     if (wnn->im.stat_screen == NULL) {
269       if (!(wnn->im.stat_screen = (*syms->ui_im_status_screen_new)(
270                 wnn->im.disp, wnn->im.font_man, wnn->im.color_man, wnn->im.vtparser,
271                 (*wnn->im.listener->is_vertical)(wnn->im.listener->self),
272                 (*wnn->im.listener->get_line_height)(wnn->im.listener->self), x, y))) {
273 #ifdef DEBUG
274         bl_warn_printf(BL_DEBUG_TAG " ui_im_status_screen_new() failed.\n");
275 #endif
276 
277         return;
278       }
279     } else {
280       (*wnn->im.stat_screen->show)(wnn->im.stat_screen);
281       (*wnn->im.stat_screen->set_spot)(wnn->im.stat_screen, x, y);
282     }
283 
284     (*parser_wchar->init)(parser_wchar);
285     if (im_convert_encoding(parser_wchar, wnn->conv, candidateword, &tmp, candidateword_len)) {
286       candidateword = tmp;
287     }
288 
289     (*wnn->im.stat_screen->set)(wnn->im.stat_screen, wnn->parser_term, candidateword);
290 
291     if (tmp) {
292       free(tmp);
293     }
294   }
295 }
296 
commit(im_wnn_t * wnn,const char * str,size_t len)297 static void commit(im_wnn_t *wnn, const char *str, size_t len) {
298   (*wnn->im.listener->write_to_term)(wnn->im.listener->self, str, len, parser_wchar);
299 }
300 
insert_char(im_wnn_t * wnn,u_char key_char)301 static int insert_char(im_wnn_t *wnn, u_char key_char) {
302   static struct {
303     wchar a;
304     wchar i;
305     wchar u;
306     wchar e;
307     wchar o;
308 
309   } kana_table[] = {
310       /* a */ /* i */ /* u */ /* e */ /* o */
311       {0xa4a2, 0xa4a4, 0xa4a6, 0xa4a8, 0xa4aa},
312       {0xa4d0, 0xa4d3, 0xa4d6, 0xa4d9, 0xa4dc}, /* b */
313       {0xa4ab, 0xa4ad, 0xa4af, 0xa4b1, 0xa4b3}, /* c */
314       {0xa4c0, 0xa4c2, 0xa4c5, 0xa4c7, 0xa4c9}, /* d */
315       {0xa4e3, 0xa4a3, 0xa4e5, 0xa4a7, 0xa4e7}, /* xy */
316       {0, 0, 0xa4d5, 0, 0,},                    /* f */
317       {0xa4ac, 0xa4ae, 0xa4b0, 0xa4b2, 0xa4b4}, /* g */
318       {0xa4cf, 0xa4d2, 0xa4d5, 0xa4d8, 0xa4db}, /* h */
319       {0xa4e3, 0, 0xa4e5, 0xa4a7, 0xa4e7},      /* ch/sh */
320       {0, 0xa4b8, 0, 0, 0,},                    /* j */
321       {0xa4ab, 0xa4ad, 0xa4af, 0xa4b1, 0xa4b3}, /* k */
322       {0xa4a1, 0xa4a3, 0xa4a5, 0xa4a7, 0xa4a9}, /* l */
323       {0xa4de, 0xa4df, 0xa4e0, 0xa4e1, 0xa4e2}, /* m */
324       {0xa4ca, 0xa4cb, 0xa4cc, 0xa4cd, 0xa4ce}, /* n */
325       {0, 0, 0, 0, 0,},
326       {0xa4d1, 0xa4d4, 0xa4d7, 0xa4da, 0xa4dd}, /* p */
327       {0, 0, 0, 0, 0,},
328       {0xa4e9, 0xa4ea, 0xa4eb, 0xa4ec, 0xa4ed}, /* r */
329       {0xa4b5, 0xa4b7, 0xa4b9, 0xa4bb, 0xa4bd}, /* s */
330       {0xa4bf, 0xa4c1, 0xa4c4, 0xa4c6, 0xa4c8}, /* t */
331       {0, 0, 0, 0, 0,},
332       {0, 0, 0, 0, 0,},
333       {0xa4ef, 0xa4f0, 0, 0xa4f1, 0xa4f2},      /* w */
334       {0xa4a1, 0xa4a3, 0xa4a5, 0xa4a7, 0xa4a9}, /* x */
335       {0xa4e4, 0, 0xa4e6, 0, 0xa4e8},           /* y */
336       {0xa4b6, 0xa4b8, 0xa4ba, 0xa4bc, 0xa4be}, /* z */
337   };
338   static wchar sign_table1[] = {
339       0xa1aa, 0xa1c9, 0xa1f4, 0xa1f0, 0xa1f3, 0xa1f5, 0xa1c7, 0xa1ca, 0xa1cb, 0xa1f6, 0xa1dc,
340       0xa1a4, 0xa1bc, 0xa1a3, 0xa1bf, 0xa3b0, 0xa3b1, 0xa3b2, 0xa3b3, 0xa3b4, 0xa3b5, 0xa3b6,
341       0xa3b7, 0xa3b8, 0xa3b9, 0xa1a7, 0xa1a8, 0xa1e3, 0xa1e1, 0xa1e4, 0xa1a9, 0xa1f7,
342   };
343   static wchar sign_table2[] = {
344       0xa1ce, 0xa1ef, 0xa1cf, 0xa1b0, 0xa1b2,
345   };
346   static wchar sign_table3[] = {
347       0xa1d0, 0xa1c3, 0xa1d1, 0xa1c1,
348   };
349   wchar wch;
350 
351   if (wnn->dan) {
352     jcDeleteChar(wnn->convbuf, 1);
353   }
354 
355   if (key_char == 'a' || key_char == 'i' || key_char == 'u' || key_char == 'e' || key_char == 'o') {
356     if (wnn->dan == 'f' - 'a') {
357       if (key_char != 'u') {
358         jcInsertChar(wnn->convbuf, 0xa4d5); /* hu */
359         wnn->dan = 'x' - 'a';
360       }
361     } else if (wnn->dan == 'j' - 'a') {
362       if (key_char != 'i') {
363         jcInsertChar(wnn->convbuf, 0xa4b8); /* zi */
364         wnn->dan = 'e' - 'a';
365       }
366     }
367 
368     if (key_char == 'a') {
369       wch = kana_table[wnn->dan].a;
370       wnn->dan = 0;
371     } else if (key_char == 'i') {
372       if (wnn->dan == 'i' - 'a') {
373         wnn->dan = 0;
374 
375         return 0; /* shi/chi */
376       }
377 
378       wch = kana_table[wnn->dan].i;
379       wnn->dan = 0;
380     } else if (key_char == 'u') {
381       if (wnn->dan == 'j' - 'a') {
382         jcInsertChar(wnn->convbuf, 0xa4b8); /* zi */
383         wnn->dan = 'e' - 'a';
384       }
385       wch = kana_table[wnn->dan].u;
386       wnn->dan = 0;
387     } else if (key_char == 'e') {
388       if (wnn->dan == 'f' - 'a') {
389         jcInsertChar(wnn->convbuf, 0xa4d5); /* hu */
390         wnn->dan = 'x' - 'a';
391       } else if (wnn->dan == 'j' - 'a') {
392         jcInsertChar(wnn->convbuf, 0xa4b8); /* zi */
393         wnn->dan = 'x' - 'a';
394       }
395       wch = kana_table[wnn->dan].e;
396       wnn->dan = 0;
397     } else /* if( key_char == 'o') */
398     {
399       if (wnn->dan == 'f' - 'a') {
400         jcInsertChar(wnn->convbuf, 0xa4d5); /* hu */
401         wnn->dan = 'x' - 'a';
402       } else if (wnn->dan == 'j' - 'a') {
403         jcInsertChar(wnn->convbuf, 0xa4b8); /* zi */
404         wnn->dan = 'e' - 'a';
405       }
406       wch = kana_table[wnn->dan].o;
407       wnn->dan = 0;
408     }
409   } else if (('!' <= key_char && key_char <= '@') || ('[' <= key_char && key_char <= '_') ||
410              ('{' <= key_char && key_char <= '~')) {
411     if (wnn->dan) {
412       jcInsertChar(wnn->convbuf, wnn->dan + 'a');
413       wnn->dan = 0;
414     }
415 
416     if (key_char <= '@') {
417       wch = sign_table1[key_char - '!'];
418     } else if (key_char <= '_') {
419       wch = sign_table2[key_char - '['];
420     } else {
421       wch = sign_table3[key_char - '{'];
422     }
423   } else {
424     if (wnn->dan == 'n' - 'a' && key_char != 'a' && key_char != 'i' && key_char != 'u' &&
425         key_char != 'e' && key_char != 'o' && key_char != 'y') {
426       if (key_char == 'n') {
427         wch = 0xa4f3; /* n */
428         wnn->dan = 0;
429       } else {
430         jcInsertChar(wnn->convbuf, 0xa4f3);
431         wch = key_char;
432         wnn->dan = key_char - 'a';
433       }
434     } else if (key_char == wnn->dan + 'a') {
435       jcInsertChar(wnn->convbuf, 0xa4c3);
436       wch = key_char;
437     } else if (key_char == 'y') {
438       if (wnn->dan == 'k' - 'a') {
439         jcInsertChar(wnn->convbuf, 0xa4ad); /* ki */
440         wnn->dan = 'x' - 'a';
441       } else if (wnn->dan == 'g' - 'a') {
442         jcInsertChar(wnn->convbuf, 0xa4ae); /* gi */
443         wnn->dan = 'x' - 'a';
444       } else if (wnn->dan == 's' - 'a') {
445         jcInsertChar(wnn->convbuf, 0xa4b7); /* si */
446         wnn->dan = 'x' - 'a';
447       } else if (wnn->dan == 'z' - 'a') {
448         jcInsertChar(wnn->convbuf, 0xa4b8); /* zi */
449         wnn->dan = 'x' - 'a';
450       } else if (wnn->dan == 't' - 'a' || wnn->dan == 'c' - 'a') {
451         jcInsertChar(wnn->convbuf, 0xa4c1); /* ti */
452         wnn->dan = 'x' - 'a';
453       } else if (wnn->dan == 'd' - 'a') {
454         jcInsertChar(wnn->convbuf, 0xa4c2); /* di */
455         wnn->dan = 'x' - 'a';
456       } else if (wnn->dan == 'n' - 'a') {
457         jcInsertChar(wnn->convbuf, 0xa4cb); /* ni */
458         wnn->dan = 'x' - 'a';
459       } else if (wnn->dan == 'h' - 'a') {
460         jcInsertChar(wnn->convbuf, 0xa4d2); /* hi */
461         wnn->dan = 'x' - 'a';
462       } else if (wnn->dan == 'b' - 'a') {
463         jcInsertChar(wnn->convbuf, 0xa4d3); /* bi */
464         wnn->dan = 'x' - 'a';
465       } else if (wnn->dan == 'p' - 'a') {
466         jcInsertChar(wnn->convbuf, 0xa4d4); /* pi */
467         wnn->dan = 'x' - 'a';
468       } else if (wnn->dan == 'm' - 'a') {
469         jcInsertChar(wnn->convbuf, 0xa4df); /* mi */
470         wnn->dan = 'x' - 'a';
471       } else if (wnn->dan == 'r' - 'a') {
472         jcInsertChar(wnn->convbuf, 0xa4ea); /* ri */
473         wnn->dan = 'x' - 'a';
474       }
475 
476       if (wnn->dan == 'x' - 'a') {
477         wnn->dan = 'e' - 'a';
478         wch = 'y';
479       } else if (wnn->dan == 'v' - 'a') {
480         if (key_char == 'u') {
481           wch = 0xa5f4;
482           wnn->dan = 0;
483         } else {
484           jcInsertChar(wnn->convbuf, 0xa5f4); /* v */
485           wnn->dan = 'x' - 'a';
486 
487           return insert_char(wnn, key_char);
488         }
489       } else {
490         goto normal;
491       }
492     } else if (key_char == 'h') {
493       if (wnn->dan == 'c' - 'a') {
494         jcInsertChar(wnn->convbuf, 0xa4c1); /* ti */
495         wnn->dan = 'i' - 'a';
496       } else if (wnn->dan == 's' - 'a') {
497         jcInsertChar(wnn->convbuf, 0xa4b7); /* si */
498         wnn->dan = 'i' - 'a';
499       } else if (wnn->dan == 'd' - 'a') {
500         jcInsertChar(wnn->convbuf, 0xa4c7); /* di */
501         wnn->dan = 'e' - 'a';
502       } else {
503         goto normal;
504       }
505 
506       wch = 'h';
507     } else {
508     normal:
509       if (wnn->dan) {
510         jcInsertChar(wnn->convbuf, wnn->dan + 'a');
511       }
512 
513       wch = key_char;
514 
515       if ('a' <= key_char && key_char <= 'z') {
516         wnn->dan = key_char - 'a';
517       } else {
518         wnn->dan = 0;
519       }
520     }
521   }
522 
523   if (wch == 0) {
524     return 1;
525   }
526 
527   if (jcInsertChar(wnn->convbuf, wch) != 0) {
528 #ifdef DEBUG
529     bl_debug_printf(BL_DEBUG_TAG " InsertChar failed.\n");
530 #endif
531   }
532 
533   return 0;
534 }
535 
fix(im_wnn_t * wnn)536 static int fix(im_wnn_t *wnn) {
537   if (wnn->convbuf->displayEnd > wnn->convbuf->displayBuf) {
538     wnn->dan = 0;
539     wnn->is_cand = 0;
540     preedit(wnn, "", 0, 0, 0, "", 0, "");
541     commit(wnn, wnn->convbuf->displayBuf,
542            (wnn->convbuf->displayEnd - wnn->convbuf->displayBuf) * 2);
543     jcFix(wnn->convbuf);
544     jcClear(wnn->convbuf);
545 
546     return 0;
547   } else {
548     return 1;
549   }
550 }
551 
552 /*
553  * methods of ui_im_t
554  */
555 
destroy(ui_im_t * im)556 static void destroy(ui_im_t *im) {
557   im_wnn_t *wnn;
558   struct wnn_buf *buf;
559 
560   wnn = (im_wnn_t*)im;
561 
562   (*wnn->parser_term->destroy)(wnn->parser_term);
563 
564   if (wnn->conv) {
565     (*wnn->conv->destroy)(wnn->conv);
566   }
567 
568   buf = wnn->convbuf->wnn;
569   jcDestroyBuffer(wnn->convbuf, 1);
570   jcClose(buf);
571 
572   free(wnn);
573 
574   ref_count--;
575 
576 #ifdef IM_WNN_DEBUG
577   bl_debug_printf(BL_DEBUG_TAG " An object was destroyed. ref_count: %d\n", ref_count);
578 #endif
579 
580   if (ref_count == 0) {
581     (*parser_wchar->destroy)(parser_wchar);
582     parser_wchar = NULL;
583   }
584 }
585 
switch_mode(ui_im_t * im)586 static int switch_mode(ui_im_t *im) {
587   im_wnn_t *wnn;
588 
589   wnn = (im_wnn_t*)im;
590 
591   if ((wnn->is_enabled = (!wnn->is_enabled))) {
592     preedit(wnn, NULL, 0, 0, 0, NULL, 0, "");
593   } else {
594     jcClear(wnn->convbuf);
595     preedit(wnn, "", 0, 0, 0, "", 0, "");
596   }
597 
598   return 1;
599 }
600 
key_event(ui_im_t * im,u_char key_char,KeySym ksym,XKeyEvent * event)601 static int key_event(ui_im_t *im, u_char key_char, KeySym ksym, XKeyEvent *event) {
602   wchar kana[2] = {0xa4ab, 0xa4ca};
603   im_wnn_t *wnn;
604   wchar *cand = NULL;
605   size_t cand_len = 0;
606   int ret = 0;
607   char *pos = "";
608 
609   wnn = (im_wnn_t*)im;
610 
611   if (key_char == ' ' && (event->state & ShiftMask)) {
612     switch_mode(im);
613 
614     if (!wnn->is_enabled) {
615       return 0;
616     }
617   } else if (!wnn->is_enabled) {
618     return 1;
619   } else if (key_char == '\r' || key_char == '\n') {
620     ret = fix(wnn);
621   } else if (ksym == XK_BackSpace || ksym == XK_Delete || key_char == 0x08 /* Ctrl+h */) {
622     if (wnn->im.preedit.filled_len > 0) {
623       wnn->dan = 0;
624       wnn->is_cand = 0;
625 
626       if (jcIsConverted(wnn->convbuf, 0)) {
627         jcCancel(wnn->convbuf);
628         jcBottom(wnn->convbuf);
629       } else {
630         jcDeleteChar(wnn->convbuf, 1);
631       }
632     } else {
633       ret = 1;
634     }
635   } else if (key_char != ' ' && key_char != '\0') {
636     if (wnn->im.preedit.filled_len > 0 && jcIsConverted(wnn->convbuf, 0)) {
637       if (key_char < ' ') {
638         if (key_char == 0x03 || key_char == 0x07) /* Ctrl+c, Ctrl+g */
639         {
640           jcCancel(wnn->convbuf);
641           jcBottom(wnn->convbuf);
642         }
643 
644         ret = 1;
645       } else {
646         fix(wnn);
647       }
648     } else if (key_char < ' ') {
649       ret = 1;
650     } else {
651       wnn->is_cand = 0;
652     }
653 
654     if (ret == 0 && insert_char(wnn, key_char) != 0) {
655       ret = 1;
656     }
657   } else {
658     if (key_char == ' ' && wnn->im.preedit.filled_len == 0) {
659       ret = 1;
660     }
661 
662     if (key_char == ' ' || ksym == XK_Up || ksym == XK_Down) {
663       if (!jcIsConverted(wnn->convbuf, 0)) {
664         wchar kanji[2] = {0xb4c1, 0xbbfa};
665 
666         if (key_char != ' ') {
667           ret = 1;
668         }
669 
670         if (jcConvert(wnn->convbuf, 0, 0, 0) != 0) {
671 #ifdef DEBUG
672           bl_debug_printf(BL_DEBUG_TAG " jcConvert failed.\n");
673 #endif
674         }
675 
676         cand = kanji;
677         cand_len = 2;
678 
679         wnn->dan = 0;
680         wnn->is_cand = 0;
681       } else {
682         int ncand;
683         int curcand;
684 
685         if (jcNext(wnn->convbuf, 0, ksym == XK_Up ? JC_PREV : JC_NEXT) != 0) {
686 #ifdef DEBUG
687           bl_debug_printf(BL_DEBUG_TAG " jcNext failed.\n");
688 #endif
689         }
690 
691         if (jcCandidateInfo(wnn->convbuf, 0, &ncand, &curcand) == 0 &&
692             (!wnn->is_cand ||
693              (ksym == XK_Up
694                   ? (curcand % CAND_WINDOW_ROWS == CAND_WINDOW_ROWS - 1 || curcand == ncand - 1)
695                   : (curcand % CAND_WINDOW_ROWS == 0)))) {
696           wchar tmp[1024];
697           wchar *src;
698           wchar *dst;
699           int count;
700           int beg = curcand - curcand % CAND_WINDOW_ROWS;
701 
702           wnn->is_cand = 1;
703 
704           for (count = 0; count < CAND_WINDOW_ROWS; count++) {
705             if (jcGetCandidate(wnn->convbuf, beg + count, tmp, sizeof(tmp)) == 0) {
706               cand_len += (MAX_DIGIT_NUM + 1);
707               for (src = tmp; *src; src++) {
708                 cand_len++;
709               }
710 
711               if (count < CAND_WINDOW_ROWS - 1 && beg + count < ncand - 1) {
712                 cand_len++; /* '\n' */
713               }
714             }
715           }
716 
717           if ((cand = alloca(cand_len * sizeof(wchar)))) {
718             dst = cand;
719 
720             for (count = 0; count < CAND_WINDOW_ROWS; count++) {
721               if (jcGetCandidate(wnn->convbuf, beg + count, tmp, sizeof(tmp)) == 0) {
722                 dst = digit_to_wstr(dst, beg + count + 1);
723                 *(dst++) = ' ';
724 
725                 for (src = tmp; *src; src++) {
726                   *(dst++) = *src;
727                 }
728 
729                 if (count < CAND_WINDOW_ROWS - 1 && beg + count < ncand - 1) {
730                   *(dst++) = '\n';
731                 }
732               }
733             }
734           }
735 
736           cand_len = dst - cand;
737         }
738 
739         if ((pos = alloca(4 + DIGIT_STR_LEN(int)*2 + 1))) {
740           sprintf(pos, " [%d/%d]", curcand + 1, ncand);
741         }
742       }
743     } else if (ksym == XK_Right) {
744       if (event->state & ShiftMask) {
745         jcExpand(wnn->convbuf, 0, jcIsConverted(wnn->convbuf, 0));
746       } else {
747         jcMove(wnn->convbuf, 0, JC_FORWARD);
748       }
749 
750       wnn->dan = 0;
751       wnn->is_cand = 0;
752     } else if (ksym == XK_Left) {
753       if (event->state & ShiftMask) {
754         jcShrink(wnn->convbuf, 0, jcIsConverted(wnn->convbuf, 0));
755       } else {
756         jcMove(wnn->convbuf, 0, JC_BACKWARD);
757       }
758 
759       wnn->dan = 0;
760       wnn->is_cand = 0;
761     } else {
762       ret = 1;
763     }
764   }
765 
766   if (jcIsConverted(wnn->convbuf, 0)) {
767     preedit(wnn, wnn->convbuf->displayBuf,
768             (wnn->convbuf->displayEnd - wnn->convbuf->displayBuf) * 2,
769             wnn->convbuf->clauseInfo[wnn->convbuf->curLCStart].dispp - wnn->convbuf->displayBuf,
770             wnn->convbuf->clauseInfo[wnn->convbuf->curLCEnd].dispp -
771                 wnn->convbuf->clauseInfo[wnn->convbuf->curLCStart].dispp,
772             cand, cand_len * sizeof(wchar), pos);
773   } else {
774     preedit(wnn, wnn->convbuf->displayBuf,
775             (wnn->convbuf->displayEnd - wnn->convbuf->displayBuf) * 2, jcDotOffset(wnn->convbuf), 0,
776             (char*)kana, sizeof(kana), pos);
777   }
778 
779   return ret;
780 }
781 
is_active(ui_im_t * im)782 static int is_active(ui_im_t *im) { return ((im_wnn_t*)im)->is_enabled; }
783 
focused(ui_im_t * im)784 static void focused(ui_im_t *im) {
785   im_wnn_t *wnn;
786 
787   wnn = (im_wnn_t*)im;
788 
789   if (wnn->im.cand_screen) {
790     (*wnn->im.cand_screen->show)(wnn->im.cand_screen);
791   }
792 }
793 
unfocused(ui_im_t * im)794 static void unfocused(ui_im_t *im) {
795   im_wnn_t *wnn;
796 
797   wnn = (im_wnn_t*)im;
798 
799   if (wnn->im.cand_screen) {
800     (*wnn->im.cand_screen->hide)(wnn->im.cand_screen);
801   }
802 }
803 
804 /* --- global functions --- */
805 
im_wnn_new(u_int64_t magic,vt_char_encoding_t term_encoding,ui_im_export_syms_t * export_syms,char * server,u_int mod_ignore_mask)806 ui_im_t *im_wnn_new(u_int64_t magic, vt_char_encoding_t term_encoding,
807                     ui_im_export_syms_t *export_syms, char *server, u_int mod_ignore_mask) {
808   im_wnn_t *wnn;
809   struct wnn_buf *buf;
810 
811   if (magic != (u_int64_t)IM_API_COMPAT_CHECK_MAGIC) {
812     bl_error_printf("Incompatible input method API.\n");
813 
814     return NULL;
815   }
816 
817   if (ref_count == 0) {
818     syms = export_syms;
819     parser_wchar = wchar_parser_new();
820   }
821 
822   if (!(wnn = calloc(1, sizeof(im_wnn_t)))) {
823 #ifdef DEBUG
824     bl_warn_printf(BL_DEBUG_TAG " malloc failed.\n");
825 #endif
826 
827     goto error;
828   }
829 
830   wnn->term_encoding = term_encoding;
831   wnn->encoding_name = (*syms->vt_get_char_encoding_name)(term_encoding);
832 
833   if (!(wnn->conv = (*syms->vt_char_encoding_conv_new)(term_encoding))) {
834     goto error;
835   }
836 
837   if (!(wnn->parser_term = (*syms->vt_char_encoding_parser_new)(term_encoding))) {
838     goto error;
839   }
840 
841   if (!(buf = jcOpen(server, "", 0, "", bl_msg_printf, bl_msg_printf, 0))) {
842 #ifdef DEBUG
843     bl_debug_printf(BL_DEBUG_TAG " jcOpen failed.\n");
844 #endif
845 
846     goto error;
847   }
848 
849   wnn->convbuf = jcCreateBuffer(buf, 0, 0);
850 
851   /*
852    * set methods of ui_im_t
853    */
854   wnn->im.destroy = destroy;
855   wnn->im.key_event = key_event;
856   wnn->im.switch_mode = switch_mode;
857   wnn->im.is_active = is_active;
858   wnn->im.focused = focused;
859   wnn->im.unfocused = unfocused;
860 
861   ref_count++;
862 
863 #ifdef IM_WNN_DEBUG
864   bl_debug_printf("New object was created. ref_count is %d.\n", ref_count);
865 #endif
866 
867   return (ui_im_t*)wnn;
868 
869 error:
870   if (ref_count == 0) {
871     if (parser_wchar) {
872       (*parser_wchar->destroy)(parser_wchar);
873       parser_wchar = NULL;
874     }
875   }
876 
877   if (wnn) {
878     if (wnn->parser_term) {
879       (*wnn->parser_term->destroy)(wnn->parser_term);
880     }
881 
882     if (wnn->conv) {
883       (*wnn->conv->destroy)(wnn->conv);
884     }
885 
886     buf = wnn->convbuf->wnn;
887     jcDestroyBuffer(wnn->convbuf, 1);
888     jcClose(buf);
889 
890     free(wnn);
891   }
892 
893   return NULL;
894 }
895 
896 /* --- API for external tools --- */
897 
im_wnn_get_info(char * locale,char * encoding)898 im_info_t *im_wnn_get_info(char *locale, char *encoding) {
899   im_info_t *result;
900 
901   if ((result = malloc(sizeof(im_info_t)))) {
902     result->id = strdup("wnn");
903     result->name = strdup("Wnn");
904     result->num_args = 0;
905     result->args = NULL;
906     result->readable_args = NULL;
907   }
908 
909   return result;
910 }
911