Mercurial > pidgin
comparison src/gtkspell.c @ 2454:64e20158271e
[gaim-migrate @ 2467]
I hacked gtkspell.
committer: Tailor Script <tailor@pidgin.im>
author | Eric Warmenhoven <eric@warmenhoven.org> |
---|---|
date | Tue, 09 Oct 2001 01:26:17 +0000 |
parents | f15d449b3167 |
children | 308f010ed664 |
comparison
equal
deleted
inserted
replaced
2453:b72143059ad4 | 2454:64e20158271e |
---|---|
33 * handle dictionary changes | 33 * handle dictionary changes |
34 * asynchronous lookups | 34 * asynchronous lookups |
35 */ | 35 */ |
36 | 36 |
37 /* size of the text buffer used in various word-processing routines. */ | 37 /* size of the text buffer used in various word-processing routines. */ |
38 #define BUFSIZE 1024 | 38 /* #define BUFSIZE 1024 */ |
39 /* number of suggestions to display on each menu. */ | 39 /* number of suggestions to display on each menu. */ |
40 #define MENUCOUNT 10 | 40 #define MENUCOUNT 10 |
41 #define BUGEMAIL "gtkspell-devel@lists.sourceforge.net" | 41 #define BUGEMAIL "gtkspell-devel@lists.sourceforge.net" |
42 | 42 |
43 /* because we keep only one copy of the spell program running, | 43 /* because we keep only one copy of the spell program running, |
72 | 72 |
73 /* functions to interface with pipe */ | 73 /* functions to interface with pipe */ |
74 static void writetext(char *text) { | 74 static void writetext(char *text) { |
75 write(fd_write[1], text, strlen(text)); | 75 write(fd_write[1], text, strlen(text)); |
76 } | 76 } |
77 static int readpipe(char *buf, int bufsize) { | 77 |
78 int len; | 78 static char *readline() { |
79 len = read(fd_read[0], buf, bufsize-1); | 79 int len = 1024; |
80 if (len < 0) { | 80 gchar *buf = g_malloc(len); |
81 error_print("read: %s\n", strerror(errno)); | 81 int pos = 0; |
82 return -1; | 82 do { |
83 } else if (len == 0) { | 83 int val = read(fd_read[0], buf + pos, 1); |
84 error_print("pipe closed.\n"); | 84 if (val <= 0) { |
85 return -1; | 85 error_print("read: %s\n", strerror(errno)); |
86 } else if (len == bufsize-1) { | 86 g_free(buf); |
87 error_print("buffer overflowed?\n"); | 87 return NULL; |
88 } | 88 } |
89 | 89 pos += val; |
90 buf[len] = 0; | 90 if (pos == len) { |
91 return len; | 91 len *= 2; |
92 } | 92 buf = g_realloc(buf, len); |
93 static int readline(char *buf) { | 93 } |
94 return readpipe(buf, BUFSIZE); | 94 } while (buf[pos - 1] != '\n'); |
95 } | 95 |
96 | 96 buf = g_realloc(buf, pos + 1); |
97 static int readresponse(char *buf) { | 97 buf[pos] = 0; |
98 int len; | 98 return buf; |
99 len = readpipe(buf, BUFSIZE); | 99 } |
100 | 100 |
101 /* all ispell responses of any reasonable length should end in \n\n. | 101 static char *readresponse() { |
102 * depending on the speed of the spell checker, this may require more | 102 char *r1, *r2, *result; |
103 * reading. */ | 103 |
104 if (len >= 2 && (buf[len-1] != '\n' || buf[len-2] != '\n')) { | 104 r1 = readline(); |
105 len += readpipe(buf+len, BUFSIZE-len); | 105 if (!r1) |
106 } | 106 return NULL; |
107 | 107 if (*r1 == '\n') { |
108 /* now we can remove all of the the trailing newlines. */ | 108 g_strchomp(r1); |
109 while (len > 0 && buf[len-1] == '\n') | 109 return r1; |
110 buf[--len] = 0; | 110 } |
111 | 111 r2 = readline(); |
112 return len; | 112 if (!r2) { |
113 g_free(r1); | |
114 return NULL; | |
115 } | |
116 | |
117 while (r2 && *r2 != '\n') { | |
118 char *tmp = r1; | |
119 r1 = g_strconcat(tmp, r2, NULL); | |
120 g_free(tmp); | |
121 g_free(r2); | |
122 r2 = readline(); | |
123 } | |
124 | |
125 if (!r2) { | |
126 g_free(r1); | |
127 return NULL; | |
128 } | |
129 | |
130 result = g_strconcat(r1, r2, NULL); | |
131 g_free(r1); | |
132 g_free(r2); | |
133 g_strchomp(result); | |
134 return result; | |
113 } | 135 } |
114 | 136 |
115 | 137 |
116 void gtkspell_stop() { | 138 void gtkspell_stop() { |
117 if (gtkspell_running()) { | 139 if (gtkspell_running()) { |
122 } | 144 } |
123 } | 145 } |
124 | 146 |
125 int gtkspell_start(char *path, char * args[]) { | 147 int gtkspell_start(char *path, char * args[]) { |
126 int fd_error[2]; | 148 int fd_error[2]; |
127 char buf[BUFSIZE]; | |
128 | 149 |
129 if (gtkspell_running()) { | 150 if (gtkspell_running()) { |
130 error_print("gtkspell_start called while already running.\n"); | 151 error_print("gtkspell_start called while already running.\n"); |
131 gtkspell_stop(); | 152 gtkspell_stop(); |
132 } | 153 } |
176 * - the exec() can succeed, but the program can dump the help screen | 197 * - the exec() can succeed, but the program can dump the help screen |
177 * we must check for both. | 198 * we must check for both. |
178 */ | 199 */ |
179 fd_set rfds; | 200 fd_set rfds; |
180 struct timeval tv; | 201 struct timeval tv; |
202 char *buf; | |
181 | 203 |
182 close(fd_write[0]); | 204 close(fd_write[0]); |
183 close(fd_read[1]); | 205 close(fd_read[1]); |
184 | 206 |
185 FD_ZERO(&rfds); | 207 FD_ZERO(&rfds); |
205 /* we're done with stderr, now. */ | 227 /* we're done with stderr, now. */ |
206 close(fd_error[0]); | 228 close(fd_error[0]); |
207 close(fd_error[1]); | 229 close(fd_error[1]); |
208 | 230 |
209 /* otherwise, fd_read[0] is set. */ | 231 /* otherwise, fd_read[0] is set. */ |
210 readline(buf); | 232 buf = readline(); |
211 | 233 |
212 /* ispell should print something like this: | 234 /* ispell should print something like this: |
213 * @(#) International Ispell Version 3.1.20 10/10/95 | 235 * @(#) International Ispell Version 3.1.20 10/10/95 |
214 * if it doesn't, it's an error. */ | 236 * if it doesn't, it's an error. */ |
215 if (buf[0] != '@') { | 237 if (!buf || buf[0] != '@') { |
238 if (buf) | |
239 g_free(buf); | |
216 gtkspell_stop(); | 240 gtkspell_stop(); |
217 return -1; | 241 return -1; |
218 } | 242 } |
243 g_free(buf); | |
219 } | 244 } |
220 | 245 |
221 /* put ispell into terse mode. | 246 /* put ispell into terse mode. |
222 * this makes it not respond on correctly spelled words. */ | 247 * this makes it not respond on correctly spelled words. */ |
223 sprintf(buf, "!\n"); | 248 writetext("!\n"); |
224 writetext(buf); | |
225 return 0; | 249 return 0; |
226 } | 250 } |
227 | 251 |
228 static GList* misspelled_suggest(char *word) { | 252 static GList* misspelled_suggest(char *word) { |
229 char buf[BUFSIZE]; | 253 char *buf; |
230 char *newword; | 254 char *newword; |
231 GList *l = NULL; | 255 GList *l = NULL; |
232 int count; | 256 int count; |
233 | 257 |
234 sprintf(buf, "^%s\n", word); /* guard against ispell control chars */ | 258 buf = g_strdup_printf("^%s\n", word); /* guard against ispell control chars */ |
235 writetext(buf); | 259 writetext(buf); |
236 readresponse(buf); | 260 g_free(buf); |
261 buf = readresponse(); | |
262 | |
263 if (!buf) | |
264 return NULL; | |
237 | 265 |
238 switch (buf[0]) { /* first char is ispell command. */ | 266 switch (buf[0]) { /* first char is ispell command. */ |
239 case 0: /* no response: word is ok. */ | 267 case 0: /* no response: word is ok. */ |
268 g_free(buf); | |
240 return NULL; | 269 return NULL; |
241 case '&': /* misspelled, with suggestions */ | 270 case '&': /* misspelled, with suggestions */ |
242 /* & <orig> <count> <ofs>: <miss>, <miss>, <guess>, ... */ | 271 /* & <orig> <count> <ofs>: <miss>, <miss>, <guess>, ... */ |
243 strtok(buf, " "); /* & */ | 272 strtok(buf, " "); /* & */ |
244 newword = strtok(NULL, " "); /* orig */ | 273 newword = strtok(NULL, " "); /* orig */ |
258 l = g_list_append(l, | 287 l = g_list_append(l, |
259 g_strdup(newword[0] == ' ' ? newword+1 : newword)); | 288 g_strdup(newword[0] == ' ' ? newword+1 : newword)); |
260 | 289 |
261 count--; | 290 count--; |
262 } | 291 } |
292 g_free(buf); | |
263 return l; | 293 return l; |
264 | 294 |
265 case '#': /* misspelled, no suggestions */ | 295 case '#': /* misspelled, no suggestions */ |
266 case '?': /* ispell is guessing. */ | 296 case '?': /* ispell is guessing. */ |
267 /* # <orig> <ofs> */ | 297 /* # <orig> <ofs> */ |
268 strtok(buf, " "); /* & */ | 298 strtok(buf, " "); /* & */ |
269 newword = strtok(NULL, " "); /* orig */ | 299 newword = strtok(NULL, " "); /* orig */ |
270 l = g_list_append(l, g_strdup(newword)); | 300 l = g_list_append(l, g_strdup(newword)); |
301 g_free(buf); | |
271 return l; | 302 return l; |
272 default: | 303 default: |
273 error_print("Unsupported spell command '%c'.\n" | 304 error_print("Unsupported spell command '%c'.\n" |
274 "This is a bug; mail " BUGEMAIL " about it.\n", buf[0]); | 305 "This is a bug; mail " BUGEMAIL " about it.\n", buf[0]); |
275 error_print("Input [%s]\nOutput [%s]\n", word, buf); | 306 error_print("Input [%s]\nOutput [%s]\n", word, buf); |
276 | 307 |
277 } | 308 } |
309 g_free(buf); | |
278 return NULL; | 310 return NULL; |
279 } | 311 } |
280 | 312 |
281 static int misspelled_test(char *word) { | 313 static int misspelled_test(char *word) { |
282 char buf[BUFSIZE]; | 314 char *buf; |
283 sprintf(buf, "^%s\n", word); /* guard against ispell control chars */ | 315 buf = g_strdup_printf("^%s\n", word); /* guard against ispell control chars */ |
284 writetext(buf); | 316 writetext(buf); |
285 readresponse(buf); | 317 g_free(buf); |
318 buf = readresponse(); | |
319 | |
320 if (!buf) | |
321 return 0; | |
286 | 322 |
287 if (buf[0] == 0) { | 323 if (buf[0] == 0) { |
324 g_free(buf); | |
288 return 0; | 325 return 0; |
289 } else if (buf[0] == '&' || buf[0] == '#' || buf[0] == '?') { | 326 } else if (buf[0] == '&' || buf[0] == '#' || buf[0] == '?') { |
327 g_free(buf); | |
290 return 1; | 328 return 1; |
291 } | 329 } |
292 | 330 |
293 error_print("Unsupported spell command '%c'.\n" | 331 error_print("Unsupported spell command '%c'.\n" |
294 "This is a bug; mail " BUGEMAIL " about it.\n", buf[0]); | 332 "This is a bug; mail " BUGEMAIL " about it.\n", buf[0]); |
295 error_print("Input [%s]\nOutput [%s]\n", word, buf); | 333 error_print("Input [%s]\nOutput [%s]\n", word, buf); |
334 g_free(buf); | |
296 return -1; | 335 return -1; |
297 } | 336 } |
298 | 337 |
299 static gboolean iswordsep(char c) { | 338 static gboolean iswordsep(char c) { |
300 return !isalpha(c) && c != '\''; | 339 return !isalpha(c) && c != '\''; |
301 } | 340 } |
302 | 341 |
303 static gboolean get_word_from_pos(GtkText* gtktext, int pos, char* buf, | 342 static gboolean get_word_from_pos(GtkText* gtktext, int pos, char** buf, |
304 int *pstart, int *pend) { | 343 int *pstart, int *pend) { |
305 gint start, end; | 344 gint start, end; |
306 | 345 |
307 if (iswordsep(GTK_TEXT_INDEX(gtktext, pos))) return FALSE; | 346 if (iswordsep(GTK_TEXT_INDEX(gtktext, pos))) return FALSE; |
308 | 347 |
314 for (end = pos; end <= gtk_text_get_length(gtktext); end++) { | 353 for (end = pos; end <= gtk_text_get_length(gtktext); end++) { |
315 if (iswordsep(GTK_TEXT_INDEX(gtktext, end))) break; | 354 if (iswordsep(GTK_TEXT_INDEX(gtktext, end))) break; |
316 } | 355 } |
317 | 356 |
318 if (buf) { | 357 if (buf) { |
358 *buf = g_malloc(end - start + 1); | |
319 for (pos = start; pos < end; pos++) | 359 for (pos = start; pos < end; pos++) |
320 buf[pos-start] = GTK_TEXT_INDEX(gtktext, pos); | 360 (*buf)[pos-start] = GTK_TEXT_INDEX(gtktext, pos); |
321 buf[pos-start] = 0; | 361 (*buf)[pos-start] = 0; |
322 } | 362 } |
323 | 363 |
324 if (pstart) *pstart = start; | 364 if (pstart) *pstart = start; |
325 if (pend) *pend = end; | 365 if (pend) *pend = end; |
326 | 366 |
327 return TRUE; | 367 return TRUE; |
328 } | 368 } |
329 | 369 |
330 static gboolean get_curword(GtkText* gtktext, char* buf, | 370 static gboolean get_curword(GtkText* gtktext, char** buf, |
331 int *pstart, int *pend) { | 371 int *pstart, int *pend) { |
332 int pos = gtk_editable_get_position(GTK_EDITABLE(gtktext)); | 372 int pos = gtk_editable_get_position(GTK_EDITABLE(gtktext)); |
333 return get_word_from_pos(gtktext, pos, buf, pstart, pend); | 373 return get_word_from_pos(gtktext, pos, buf, pstart, pend); |
334 } | 374 } |
335 | 375 |
352 g_free(newtext); | 392 g_free(newtext); |
353 } | 393 } |
354 | 394 |
355 static gboolean check_at(GtkText *gtktext, int from_pos) { | 395 static gboolean check_at(GtkText *gtktext, int from_pos) { |
356 int start, end; | 396 int start, end; |
357 char buf[BUFSIZE]; | 397 char *buf = NULL; |
358 | 398 |
359 if (!get_word_from_pos(gtktext, from_pos, buf, &start, &end)) { | 399 if (!get_word_from_pos(gtktext, from_pos, &buf, &start, &end)) { |
400 if (buf) | |
401 g_free(buf); | |
360 return FALSE; | 402 return FALSE; |
361 } | 403 } |
362 | 404 |
363 if (misspelled_test(buf)) { | 405 if (misspelled_test(buf)) { |
364 if (highlight.pixel == 0) { | 406 if (highlight.pixel == 0) { |
365 /* add an entry for the highlight in the color map. */ | 407 /* add an entry for the highlight in the color map. */ |
366 GdkColormap *gc = gtk_widget_get_colormap(GTK_WIDGET(gtktext)); | 408 GdkColormap *gc = gtk_widget_get_colormap(GTK_WIDGET(gtktext)); |
367 gdk_colormap_alloc_color(gc, &highlight, FALSE, TRUE);; | 409 gdk_colormap_alloc_color(gc, &highlight, FALSE, TRUE);; |
368 } | 410 } |
369 change_color(gtktext, start, end, &highlight); | 411 change_color(gtktext, start, end, &highlight); |
412 if (buf) | |
413 g_free(buf); | |
370 return TRUE; | 414 return TRUE; |
371 } else { | 415 } else { |
372 change_color(gtktext, start, end, | 416 change_color(gtktext, start, end, |
373 &(GTK_WIDGET(gtktext)->style->fg[0])); | 417 &(GTK_WIDGET(gtktext)->style->fg[0])); |
418 if (buf) | |
419 g_free(buf); | |
374 return FALSE; | 420 return FALSE; |
375 } | 421 } |
376 } | 422 } |
377 | 423 |
378 void gtkspell_check_all(GtkText *gtktext) { | 424 void gtkspell_check_all(GtkText *gtktext) { |
454 } | 500 } |
455 | 501 |
456 static void replace_word(GtkWidget *w, gpointer d) { | 502 static void replace_word(GtkWidget *w, gpointer d) { |
457 int start, end; | 503 int start, end; |
458 char *newword; | 504 char *newword; |
459 char buf[BUFSIZE]; | |
460 | 505 |
461 /* we don't save their position, | 506 /* we don't save their position, |
462 * because the cursor is moved by the click. */ | 507 * because the cursor is moved by the click. */ |
463 | 508 |
464 gtk_text_freeze(GTK_TEXT(d)); | 509 gtk_text_freeze(GTK_TEXT(d)); |
465 | 510 |
466 gtk_label_get(GTK_LABEL(GTK_BIN(w)->child), &newword); | 511 gtk_label_get(GTK_LABEL(GTK_BIN(w)->child), &newword); |
467 get_curword(GTK_TEXT(d), buf, &start, &end); | 512 get_curword(GTK_TEXT(d), NULL, &start, &end); |
468 | 513 |
469 gtk_text_set_point(GTK_TEXT(d), end); | 514 gtk_text_set_point(GTK_TEXT(d), end); |
470 gtk_text_backward_delete(GTK_TEXT(d), end-start); | 515 gtk_text_backward_delete(GTK_TEXT(d), end-start); |
471 gtk_text_insert(GTK_TEXT(d), NULL, NULL, NULL, newword, strlen(newword)); | 516 gtk_text_insert(GTK_TEXT(d), NULL, NULL, NULL, newword, strlen(newword)); |
472 | 517 |
477 GtkWidget *menu, *item; | 522 GtkWidget *menu, *item; |
478 char *caption; | 523 char *caption; |
479 menu = gtk_menu_new(); { | 524 menu = gtk_menu_new(); { |
480 caption = g_strdup_printf("Not in dictionary: %s", (char*)l->data); | 525 caption = g_strdup_printf("Not in dictionary: %s", (char*)l->data); |
481 item = gtk_menu_item_new_with_label(caption); | 526 item = gtk_menu_item_new_with_label(caption); |
527 g_free(caption); | |
482 /* I'd like to make it so this item is never selectable, like | 528 /* I'd like to make it so this item is never selectable, like |
483 * the menu titles in the GNOME panel... unfortunately, the GNOME | 529 * the menu titles in the GNOME panel... unfortunately, the GNOME |
484 * panel creates their own custom widget to do this! */ | 530 * panel creates their own custom widget to do this! */ |
485 gtk_widget_show(item); | 531 gtk_widget_show(item); |
486 gtk_menu_append(GTK_MENU(menu), item); | 532 gtk_menu_append(GTK_MENU(menu), item); |
525 } | 571 } |
526 return GTK_MENU(menu); | 572 return GTK_MENU(menu); |
527 } | 573 } |
528 | 574 |
529 static void popup_menu(GtkText *gtktext, GdkEventButton *eb) { | 575 static void popup_menu(GtkText *gtktext, GdkEventButton *eb) { |
530 char buf[BUFSIZE]; | 576 char *buf = NULL; |
531 GList *list, *l; | 577 GList *list, *l; |
532 | 578 |
533 get_curword(gtktext, buf, NULL, NULL); | 579 get_curword(gtktext, &buf, NULL, NULL); |
534 | 580 |
535 list = misspelled_suggest(buf); | 581 list = misspelled_suggest(buf); |
582 if (buf) | |
583 g_free(buf); | |
536 if (list != NULL) { | 584 if (list != NULL) { |
537 gtk_menu_popup(make_menu(list, gtktext), NULL, NULL, NULL, NULL, | 585 gtk_menu_popup(make_menu(list, gtktext), NULL, NULL, NULL, NULL, |
538 eb->button, eb->time); | 586 eb->button, eb->time); |
539 for (l = list; l != NULL; l = l->next) | 587 for (l = list; l != NULL; l = l->next) |
540 g_free(l->data); | 588 g_free(l->data); |