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);