1 /* -*- Mode: C; tab-width: 2; indent-tabs-mode: t; c-basic-offset: 2 -*- */
2 /* IM-JA Japanese Input Method Module for GTK-2.0
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  *
19  * Authors: Botond Botyanszki <boti@rocketmail.com>
20  *
21  */
22 
23 #include <stdio.h>
24 #include <string.h>
25 #include <gtk/gtk.h>
26 #include <gdk/gdkkeysyms.h>
27 
28 
29 #include "im-ja.h"
30 #include "im-ja-impl.h"
31 #include "error.h"
32 #include "common.h"
33 #include "romakana.h"
34 #include "hiragana-convtable.h"
35 #include "zenkaku-convtable.h"
36 #include "halfkata-convtable.h"
37 
38 extern IMJAConfig cfg;
39 
40 /* * * * * * * * * * * * * * * * */
41 
im_ja_kana_convert_tailing_n(IMJAContext * cn)42 gboolean im_ja_kana_convert_tailing_n(IMJAContext *cn) {
43 	if (strlen(cn->preedit_buf) == 0) return FALSE;
44 	if (cn->preedit_buf[strlen(cn->preedit_buf) - 1] == 'n') {
45 		cn->preedit_buf[strlen(cn->preedit_buf) - 1] = 0;
46 		switch (cn->input_method) {
47 		case IM_JA_HIRAGANA_INPUT:
48 			g_strlcat(cn->preedit_buf, "\xE3\x82\x93", BUFFERSIZE);
49 			break;
50 		case IM_JA_KATAKANA_INPUT:
51 			g_strlcat(cn->preedit_buf, "\xE3\x83\xB3", BUFFERSIZE);
52 			break;
53 		case IM_JA_HALFKATA_INPUT:
54 			g_strlcat(cn->preedit_buf, "\xEF\xBE\x9D", BUFFERSIZE);
55 			break;
56 		}
57 		return TRUE;
58 	}
59 	return FALSE;
60 }
61 
62 
im_ja_kana_commit_converted(IMJAContext * cn,gchar * kanastr)63 void im_ja_kana_commit_converted(IMJAContext *cn, gchar *kanastr) {
64 	gchar *lastkana;
65 
66 	/* Find any converted char */
67 	if (strlen(kanastr) == 0) return;
68 	lastkana = kanastr + strlen(kanastr);
69 
70 	while (isJPChar(g_utf8_get_char(g_utf8_prev_char(lastkana))) == FALSE) {
71 		lastkana = g_utf8_prev_char(lastkana);
72 		if (lastkana == kanastr) break;
73 	}
74 
75 	/* IM_JA_DEBUG("lastkana: %s, kanastr: %s\n", lastkana, kanastr); */
76 
77 	if (lastkana == kanastr + strlen(kanastr)) {
78 		g_strlcpy(cn->preedit_buf, kanastr, BUFFERSIZE);
79 		im_ja_commit(cn);
80 		im_ja_context_reset(cn);
81 	}
82 	else {
83 		if (lastkana != kanastr) {
84 			g_strlcpy(cn->preedit_buf, kanastr, BUFFERSIZE);
85 			cn->preedit_buf[lastkana - kanastr] = 0;
86 			im_ja_commit(cn);
87 		}
88 		/* copy new string back to preedit buffer */
89 		g_strlcpy(cn->preedit_buf, lastkana, BUFFERSIZE);
90 		cn->cursor_char_pos = g_utf8_strlen(cn->preedit_buf, -1);
91 		*lastkana = 0;
92 	}
93 }
94 
95 
im_ja_kana_filter_keypress(IMJAContext * cn,GdkEventKey * key)96 gboolean im_ja_kana_filter_keypress(IMJAContext *cn, GdkEventKey *key) {
97   gchar *tmpstr;
98 
99   if (key->type == GDK_KEY_RELEASE) {
100     return FALSE;
101   }
102   if (key->keyval == GDK_space) {
103     /* g_strlcat(cn->preedit_buf, " ", BUFFERSIZE); */
104 		gtk_im_context_reset(GTK_IM_CONTEXT(cn));
105 		g_strlcat(cn->preedit_buf, " ", BUFFERSIZE);
106 		im_ja_commit(cn); /* Insert a space */
107     /* FIXME: maybe this should be zenkaku space //ascii & unicode zenkaku diff: 65248 */
108     return TRUE;
109   }
110 
111   if (key->keyval == GDK_BackSpace) {
112     if (strlen(cn->preedit_buf) > 0) {
113       buffer_delchar(cn->preedit_buf);
114 			im_ja_preedit_changed(cn);
115       return TRUE;
116     }
117     return FALSE;
118   }
119 
120   if ((key->keyval == GDK_Return) || (ishotkey(key, COMMIT_PREEDIT, &cfg) == TRUE)) {
121     if (strlen(cn->preedit_buf) == 0) return FALSE;
122 		im_ja_kana_convert_tailing_n(cn);
123     if (g_utf8_validate(cn->preedit_buf, -1, NULL) != TRUE) {
124       printf("Cannot commit. utf8_validate failed: %s\n", cn->preedit_buf);
125     }
126     else im_ja_commit(cn);
127 		im_ja_on_reset(cn);
128     return TRUE;
129   }
130 
131   if (im_ja_is_printable_key(key) == TRUE) {
132     gchar utf8strg[7];
133 		gchar *tmpeuc;
134 		int utf8strg_end;
135 
136 		utf8strg_end = g_unichar_to_utf8(gdk_keyval_to_unicode(key->keyval), utf8strg);
137 		utf8strg[utf8strg_end] = 0;
138 		tmpeuc = utf82euc(utf8strg);
139 		if ((unsigned int)tmpeuc[0] > 128) {
140 			g_free(tmpeuc);
141 			IM_JA_DEBUG("*ERROR* non-ascii input\n");
142 			im_ja_input_utf8(cn, utf8strg);
143 			return TRUE;
144 		}
145 		g_free(tmpeuc);
146 
147 		g_strlcat(cn->preedit_buf, utf8strg, BUFFERSIZE);
148 
149 		tmpstr = roma2kana(cn->preedit_buf, cn->input_method);
150 		IM_JA_DEBUG("converted \"%s\" to \"%s\"\n", cn->preedit_buf, tmpstr);
151 		im_ja_kana_commit_converted(cn, tmpstr);
152 		g_free(tmpstr);
153 
154 		if (g_utf8_validate(cn->preedit_buf, -1, NULL) != TRUE) {
155 			printf("utf8_validate failed: %s\n", cn->preedit_buf);
156 		}
157 		im_ja_preedit_changed(cn);
158 		return TRUE;
159   }
160 
161   return FALSE;
162 }
163 
g_strrncmp(gchar * s1,size_t s1len,gchar * s2,size_t s2len)164 int g_strrncmp(gchar* s1, size_t s1len, gchar* s2, size_t s2len) {
165 
166   if( s1len > s2len )	s1 += s1len-s2len;
167   return strncmp(s1, s2, s2len);
168 }
169 
170 
171 /* Convert romaji to kana. Returns the kana string */
roma2kana(gchar * romastr,gint input_method)172 gchar *roma2kana(gchar *romastr, gint input_method) {
173 	gchar *result = NULL;
174 	gchar *tmpstr = NULL;
175 	romapair *romatable = NULL;
176 	gint i = 0, s1len, s2len;
177 
178   if (romastr == NULL) return NULL;
179   if (*romastr == '\0') return NULL;
180 
181   s1len = strlen(romastr);
182 
183 	switch (input_method) {
184 		case IM_JA_HIRAGANA_INPUT:
185 		case IM_JA_KATAKANA_INPUT:
186 			romatable = hiraganatable;
187 			break;
188 		case IM_JA_HALFKATA_INPUT:
189 			romatable = halfkatatable;
190 			break;
191 		case IM_JA_ZENKAKU_INPUT:
192 			romatable = zenkakutable;
193 			break;
194 	}
195 
196 	for (i = 0; ; i++) {
197     romapair rp = romatable[i];
198     if (rp.kana[0] == '0' && rp.roma[0] == '0') break;
199 		s2len = strlen(rp.roma);
200     /* Compare end of input and string in kana table */
201     if (g_strrncmp(romastr, s1len, rp.roma, s2len) == 0) {
202       result = g_new0(gchar, BUFFERSIZE);
203       strncat(result, romastr, s1len - s2len);
204       g_strlcat(result, rp.kana, BUFFERSIZE);
205 
206 			if (input_method == IM_JA_KATAKANA_INPUT) {
207 				tmpstr = result;
208 				result = hira2kata(tmpstr);
209 				g_free(tmpstr);
210 			}
211 			return result;
212     }
213   }
214 	return g_strdup(romastr); /* no change */
215 }
216 
roma2kana_i(gchar * romastr,gint * index,gint input_method)217 gchar *roma2kana_i(gchar *romastr, gint *index, gint input_method)
218 {
219 	gchar *result = NULL;
220 	gchar *tmpstr = NULL;
221 	gint i = 0,s1len,s2len;
222 	romapair *romatable = NULL;
223 
224 	if (romastr == NULL)
225 		return NULL;
226 
227 	result = g_new0(gchar, BUFFERSIZE);
228 
229 	if (*romastr == '\0')
230 		return result;
231 
232 	s1len = *index;
233 
234 	switch (input_method) {
235 		case IM_JA_HIRAGANA_INPUT:
236 		case IM_JA_KATAKANA_INPUT:
237 			romatable = hiraganatable;
238 			break;
239 		case IM_JA_HALFKATA_INPUT:
240 			romatable = halfkatatable;
241 			break;
242 		case IM_JA_ZENKAKU_INPUT:
243 			romatable = zenkakutable;
244 			break;
245 	}
246 
247 	for (i = 0; ; i++)
248 	{
249 		romapair rp = romatable[i];
250 		if (rp.kana[0] == '0' && rp.roma[0] == '0')
251 			break;
252 
253 		s2len = strlen(rp.roma);
254 
255 		/* Compare offset of input and string in kana table */
256 		if(g_strrncmp(romastr, s1len, rp.roma,s2len) == 0 )
257 		{
258 			*index += strlen(rp.kana) - s2len;
259 			strncat(result, romastr, s1len - s2len );
260 			g_strlcat(result, rp.kana, BUFFERSIZE);
261 			g_strlcat(result, romastr+s1len, BUFFERSIZE);
262 			if (input_method == IM_JA_KATAKANA_INPUT) {
263 				tmpstr = result;
264 				result = hira2kata(tmpstr);
265 				g_free(tmpstr);
266 			}
267 			return result;
268 		}
269 	}
270 
271 	/* no change */
272 	g_strlcat(result, romastr, BUFFERSIZE);
273 	return result;
274 }
275 
276 
roma2kana_i_lastpass(gchar * romastr,gint * index,gint input_method)277 gchar *roma2kana_i_lastpass(gchar *romastr, gint *index, gint input_method)
278 /* IMHO the cursor position change should be done in canna_rk.c
279 	 depending on the return value of this function. See e.g
280 	 im_ja_wnn_convert_tailing_n() or im_ja_kana_convert_tailing_n()
281 */
282 {
283 	gchar *result = NULL;
284 	gchar *subst_str = NULL;
285 	gchar *pattern="n";
286 	gint s1len;
287 
288 	if (romastr == NULL)
289 		return NULL;
290 
291 	result = g_new0(gchar, BUFFERSIZE);
292 
293 	if (*romastr == '\0')
294 		return result;
295 
296 	s1len = *index;
297 
298 	switch (input_method) {
299 	case IM_JA_HIRAGANA_INPUT:
300 		subst_str = "\xE3\x82\x93";
301 		break;
302 	case IM_JA_KATAKANA_INPUT:
303 		subst_str = "\xE3\x83\xB3";
304 		break;
305 	case IM_JA_HALFKATA_INPUT:
306 		subst_str = "\xEF\xBE\x9D";
307 		break;
308 	}
309 
310 	/* Compare offset of input and string in kana table */
311 	if(g_strrncmp(romastr, s1len, pattern, 1) == 0 )
312 	{
313 		*index += 3 - 1;
314 		strncat(result, romastr, s1len - 1 );
315 		g_strlcat(result, subst_str, BUFFERSIZE);
316 		g_strlcat(result, romastr+s1len, BUFFERSIZE);
317 		return result;
318 	}
319 
320 	/* no change */
321 	g_strlcat(result, romastr, BUFFERSIZE);
322 	return result;
323 }
324 
325