Mercurial > pidgin.yaz
annotate src/gtkspell.c @ 1466:1a24237f5865
[gaim-migrate @ 1476]
jabber got chat and handles "not acceptable" JIDs better (e.g. you can sign on as "warmenhoven" or "warmenhoven@jabber.com" or "warmenhoven@jabber.com/GAIM", and all three act the exact same). also started to rewrite get file for TOC, but only got far enough that AIM aborts the transfer and gaim handles the stopped transfer gracefully (AIM doesn't segfault; it just doesn't do what I expect it to). other doc updates.
committer: Tailor Script <tailor@pidgin.im>
author | Eric Warmenhoven <eric@warmenhoven.org> |
---|---|
date | Sat, 03 Feb 2001 13:30:57 +0000 |
parents | 3dfd2a83fb5e |
children | 7f7857c5036e |
rev | line source |
---|---|
1117 | 1 /* gtkspell - a spell-checking addon for GtkText |
2 * Copyright (c) 2000 Evan Martin. | |
3 * vim: ts=4 sw=4 | |
4 * This library is free software; you can redistribute it and/or | |
5 * modify it under the terms of the GNU Lesser 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 * Lesser General Public License for more details. | |
13 * | |
14 * You should have received a copy of the GNU Lesser General Public | |
15 * License along with this library; if not, write to the Free Software | |
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
17 */ | |
18 | |
19 #include <gtk/gtk.h> | |
20 | |
21 #include <sys/types.h> | |
22 #include <sys/wait.h> | |
23 #include <sys/time.h> | |
24 #include <unistd.h> | |
25 #include <stdio.h> | |
26 #include <signal.h> | |
27 #include <ctype.h> | |
28 #include <string.h> | |
29 #include <stdlib.h> | |
30 #include <errno.h> | |
31 | |
32 /* TODO: | |
33 * handle dictionary changes | |
34 * asynchronous lookups | |
35 */ | |
36 | |
37 /* size of the text buffer used in various word-processing routines. */ | |
38 #define BUFSIZE 1024 | |
39 /* number of suggestions to display on each menu. */ | |
40 #define MENUCOUNT 10 | |
41 #define BUGEMAIL "gtkspell-devel@lists.sourceforge.net" | |
42 | |
43 /* because we keep only one copy of the spell program running, | |
44 * all ispell-related variables can be static. | |
45 */ | |
46 static pid_t spell_pid = -1; | |
47 static int fd_write[2], fd_read[2]; | |
48 static int signal_set_up = 0; | |
49 | |
50 /* FIXME? */ | |
51 static GdkColor highlight = { 0, 255*256, 0, 0 }; | |
52 | |
53 static void entry_insert_cb(GtkText *gtktext, | |
54 gchar *newtext, guint len, guint *ppos, gpointer d); | |
55 static void set_up_signal(); | |
56 | |
57 int gtkspell_running() { | |
58 return (spell_pid > 0); | |
59 } | |
60 | |
61 static void error_print(const char *fmt, ...) { | |
62 va_list ap; | |
63 va_start(ap, fmt); | |
64 fprintf(stderr, "gtkspell: "); | |
65 vfprintf(stderr, fmt, ap); | |
66 va_end(ap); | |
67 } | |
68 | |
69 /* functions to interface with pipe */ | |
70 static void writetext(char *text) { | |
71 write(fd_write[1], text, strlen(text)); | |
72 } | |
73 static int readpipe(char *buf, int bufsize) { | |
74 int len; | |
75 len = read(fd_read[0], buf, bufsize-1); | |
76 if (len < 0) { | |
77 error_print("read: %s\n", strerror(errno)); | |
78 return -1; | |
79 } else if (len == 0) { | |
80 error_print("pipe closed.\n"); | |
81 return -1; | |
82 } else if (len == bufsize-1) { | |
83 error_print("buffer overflowed?\n"); | |
84 } | |
85 | |
86 buf[len] = 0; | |
87 return len; | |
88 } | |
89 static int readline(char *buf) { | |
90 return readpipe(buf, BUFSIZE); | |
91 } | |
92 | |
93 static int readresponse(char *buf) { | |
94 int len; | |
95 len = readpipe(buf, BUFSIZE); | |
96 | |
97 /* all ispell responses of any reasonable length should end in \n\n. | |
98 * depending on the speed of the spell checker, this may require more | |
99 * reading. */ | |
100 if (len >= 2 && (buf[len-1] != '\n' || buf[len-2] != '\n')) { | |
101 len += readpipe(buf+len, BUFSIZE-len); | |
102 } | |
103 | |
104 /* now we can remove all of the the trailing newlines. */ | |
105 while (len > 0 && buf[len-1] == '\n') | |
106 buf[--len] = 0; | |
107 | |
108 return len; | |
109 } | |
110 | |
111 | |
112 void gtkspell_stop() { | |
113 if (gtkspell_running()) { | |
1415
3dfd2a83fb5e
[gaim-migrate @ 1425]
Eric Warmenhoven <eric@warmenhoven.org>
parents:
1127
diff
changeset
|
114 kill(spell_pid, SIGHUP); |
1117 | 115 } |
116 } | |
117 | |
118 int gtkspell_start(char *path, char * args[]) { | |
119 int fd_error[2]; | |
120 char buf[BUFSIZE]; | |
121 | |
122 if (gtkspell_running()) { | |
123 error_print("gtkspell_start called while already running.\n"); | |
124 gtkspell_stop(); | |
125 } | |
126 | |
127 if (!signal_set_up) { | |
128 set_up_signal(); | |
129 signal_set_up = 1; | |
130 } | |
131 | |
132 pipe(fd_write); | |
133 pipe(fd_read); | |
134 pipe(fd_error); | |
135 | |
136 spell_pid = fork(); | |
137 if (spell_pid < 0) { | |
138 error_print("fork: %s\n", strerror(errno)); | |
139 return -1; | |
140 } else if (spell_pid == 0) { | |
141 dup2(fd_write[0], 0); | |
142 dup2(fd_read[1], 1); | |
143 dup2(fd_error[1], 2); | |
144 close(fd_read[0]); | |
145 close(fd_error[0]); | |
146 close(fd_write[1]); | |
147 | |
148 if (path == NULL) { | |
149 if (execvp(args[0], args) < 0) | |
150 error_print("execvp('%s'): %s\n", args[0], strerror(errno)); | |
151 } else { | |
152 if (execv(path, args) < 0) | |
153 error_print("execv('%s'): %s\n", path, strerror(errno)); | |
154 } | |
155 /* if we get here, we failed. | |
156 * send some text on the pipe to indicate status. | |
157 */ | |
158 write(fd_read[1], "!", 1); | |
159 | |
160 _exit(0); | |
161 } else { | |
162 /* there are at least two ways to fail: | |
163 * - the exec() can fail | |
164 * - the exec() can succeed, but the program can dump the help screen | |
165 * we must check for both. | |
166 */ | |
167 fd_set rfds; | |
168 struct timeval tv; | |
169 | |
170 FD_ZERO(&rfds); | |
171 FD_SET(fd_error[0], &rfds); | |
172 FD_SET(fd_read[0], &rfds); | |
173 tv.tv_sec = 2; | |
174 tv.tv_usec = 0; | |
175 if (select(MAX(fd_error[0], fd_read[0])+1, | |
176 &rfds, NULL, NULL, &tv) < 0) { | |
177 /* FIXME: is this needed? */ | |
178 error_print("Timed out waiting for spell command.\n"); | |
179 gtkspell_stop(); | |
180 return -1; | |
181 } | |
182 | |
183 if (FD_ISSET(fd_error[0], &rfds)) { /* stderr readable? */ | |
184 error_print("Spell command printed on stderr -- probably failed.\n"); | |
185 gtkspell_stop(); | |
186 return -1; | |
187 } | |
188 | |
189 /* otherwise, fd_read[0] is set. */ | |
190 readline(buf); | |
191 | |
192 /* ispell should print something like this: | |
193 * @(#) International Ispell Version 3.1.20 10/10/95 | |
194 * if it doesn't, it's an error. */ | |
195 if (buf[0] != '@') { | |
196 gtkspell_stop(); | |
197 return -1; | |
198 } | |
199 } | |
200 | |
201 /* put ispell into terse mode. | |
202 * this makes it not respond on correctly spelled words. */ | |
203 sprintf(buf, "!\n"); | |
204 writetext(buf); | |
205 return 0; | |
206 } | |
207 | |
208 static GList* misspelled_suggest(char *word) { | |
209 char buf[BUFSIZE]; | |
210 char *newword; | |
211 GList *l = NULL; | |
212 int count; | |
213 | |
214 sprintf(buf, "^%s\n", word); /* guard against ispell control chars */ | |
215 writetext(buf); | |
216 readresponse(buf); | |
217 | |
218 switch (buf[0]) { /* first char is ispell command. */ | |
219 case 0: /* no response: word is ok. */ | |
220 return NULL; | |
221 case '&': /* misspelled, with suggestions */ | |
222 /* & <orig> <count> <ofs>: <miss>, <miss>, <guess>, ... */ | |
223 strtok(buf, " "); /* & */ | |
224 newword = strtok(NULL, " "); /* orig */ | |
225 l = g_list_append(l, g_strdup(newword)); | |
226 newword = strtok(NULL, " "); /* count */ | |
227 count = atoi(newword); | |
228 strtok(NULL, " "); /* ofs: */ | |
229 | |
230 while ((newword = strtok(NULL, ",")) != NULL) { | |
231 int len = strlen(newword); | |
232 if (newword[len-1] == ' ' || newword[len-1] == '\n') | |
233 newword[len-1] = 0; | |
234 if (count == 0) { | |
235 g_list_append(l, NULL); /* signal the "suggestions" */ | |
236 } | |
237 /* add it to the list, skipping the initial space. */ | |
238 l = g_list_append(l, | |
239 g_strdup(newword[0] == ' ' ? newword+1 : newword)); | |
240 | |
241 count--; | |
242 } | |
243 return l; | |
244 | |
245 case '#': /* misspelled, no suggestions */ | |
246 /* # <orig> <ofs> */ | |
247 strtok(buf, " "); /* & */ | |
248 newword = strtok(NULL, " "); /* orig */ | |
249 l = g_list_append(l, g_strdup(newword)); | |
250 return l; | |
251 default: | |
252 error_print("Unsupported spell command '%c'.\n" | |
253 "This is a bug; mail " BUGEMAIL " about it.\n", buf[0]); | |
254 } | |
255 return NULL; | |
256 } | |
257 | |
258 static int misspelled_test(char *word) { | |
259 char buf[BUFSIZE]; | |
260 sprintf(buf, "^%s\n", word); /* guard against ispell control chars */ | |
261 writetext(buf); | |
262 readresponse(buf); | |
263 | |
264 if (buf[0] == 0) { | |
265 return 0; | |
266 } else if (buf[0] == '&' || buf[0] == '#') { | |
267 return 1; | |
268 } | |
269 | |
270 error_print("Unsupported spell command '%c'.\n" | |
271 "This is a bug; mail " BUGEMAIL " about it.\n", buf[0]); | |
272 return -1; | |
273 } | |
274 | |
275 static gboolean iswordsep(char c) { | |
276 return !isalpha(c) && c != '\''; | |
277 } | |
278 | |
279 static gboolean get_word_from_pos(GtkText* gtktext, int pos, char* buf, | |
280 int *pstart, int *pend) { | |
281 gint start, end; | |
282 | |
283 if (iswordsep(GTK_TEXT_INDEX(gtktext, pos))) return FALSE; | |
284 | |
285 for (start = pos; start >= 0; --start) { | |
286 if (iswordsep(GTK_TEXT_INDEX(gtktext, start))) break; | |
287 } | |
288 start++; | |
289 | |
290 for (end = pos; end <= gtk_text_get_length(gtktext); end++) { | |
291 if (iswordsep(GTK_TEXT_INDEX(gtktext, end))) break; | |
292 } | |
293 | |
294 if (buf) { | |
295 for (pos = start; pos < end; pos++) | |
296 buf[pos-start] = GTK_TEXT_INDEX(gtktext, pos); | |
297 buf[pos-start] = 0; | |
298 } | |
299 | |
300 if (pstart) *pstart = start; | |
301 if (pend) *pend = end; | |
302 | |
303 return TRUE; | |
304 } | |
305 | |
306 static gboolean get_curword(GtkText* gtktext, char* buf, | |
307 int *pstart, int *pend) { | |
308 int pos = gtk_editable_get_position(GTK_EDITABLE(gtktext)); | |
309 return get_word_from_pos(gtktext, pos, buf, pstart, pend); | |
310 } | |
311 | |
312 static void change_color(GtkText *gtktext, | |
313 int start, int end, GdkColor *color) { | |
314 char *newtext = gtk_editable_get_chars(GTK_EDITABLE(gtktext), start, end); | |
315 gtk_text_freeze(gtktext); | |
316 gtk_signal_handler_block_by_func(GTK_OBJECT(gtktext), | |
317 GTK_SIGNAL_FUNC(entry_insert_cb), NULL); | |
318 | |
319 gtk_text_set_point(gtktext, start); | |
320 gtk_text_forward_delete(gtktext, end-start); | |
321 | |
322 if (newtext && end-start > 0) | |
323 gtk_text_insert(gtktext, NULL, color, NULL, newtext, end-start); | |
324 | |
325 gtk_signal_handler_unblock_by_func(GTK_OBJECT(gtktext), | |
326 GTK_SIGNAL_FUNC(entry_insert_cb), NULL); | |
327 gtk_text_thaw(gtktext); | |
1415
3dfd2a83fb5e
[gaim-migrate @ 1425]
Eric Warmenhoven <eric@warmenhoven.org>
parents:
1127
diff
changeset
|
328 g_free(newtext); |
1117 | 329 } |
330 | |
331 static gboolean check_at(GtkText *gtktext, int from_pos) { | |
332 int start, end; | |
333 char buf[BUFSIZE]; | |
334 | |
335 if (!get_word_from_pos(gtktext, from_pos, buf, &start, &end)) { | |
336 return FALSE; | |
337 } | |
338 | |
339 if (misspelled_test(buf)) { | |
340 if (highlight.pixel == 0) { | |
341 /* add an entry for the highlight in the color map. */ | |
342 GdkColormap *gc = gtk_widget_get_colormap(GTK_WIDGET(gtktext)); | |
343 gdk_colormap_alloc_color(gc, &highlight, FALSE, TRUE);; | |
344 } | |
345 change_color(gtktext, start, end, &highlight); | |
346 return TRUE; | |
347 } else { | |
348 change_color(gtktext, start, end, | |
349 &(GTK_WIDGET(gtktext)->style->fg[0])); | |
350 return FALSE; | |
351 } | |
352 } | |
353 | |
354 void gtkspell_check_all(GtkText *gtktext) { | |
355 guint origpos; | |
356 guint pos = 0; | |
357 guint len; | |
358 float adj_value; | |
359 | |
360 if (!gtkspell_running()) return; | |
361 | |
362 len = gtk_text_get_length(gtktext); | |
363 | |
364 adj_value = gtktext->vadj->value; | |
365 gtk_text_freeze(gtktext); | |
366 origpos = gtk_editable_get_position(GTK_EDITABLE(gtktext)); | |
367 while (pos < len) { | |
368 while (pos < len && iswordsep(GTK_TEXT_INDEX(gtktext, pos))) | |
369 pos++; | |
370 while (pos < len && !iswordsep(GTK_TEXT_INDEX(gtktext, pos))) | |
371 pos++; | |
372 if (pos > 0) | |
373 check_at(gtktext, pos-1); | |
374 } | |
375 gtk_text_thaw(gtktext); | |
376 gtk_editable_set_position(GTK_EDITABLE(gtktext), origpos); | |
377 } | |
378 | |
379 static void entry_insert_cb(GtkText *gtktext, | |
380 gchar *newtext, guint len, guint *ppos, gpointer d) { | |
381 int origpos; | |
382 | |
383 if (!gtkspell_running()) return; | |
384 | |
385 gtk_signal_handler_block_by_func(GTK_OBJECT(gtktext), | |
386 GTK_SIGNAL_FUNC(entry_insert_cb), | |
387 NULL); | |
388 gtk_text_insert(GTK_TEXT(gtktext), NULL, | |
389 &(GTK_WIDGET(gtktext)->style->fg[0]), NULL, newtext, len); | |
390 gtk_signal_handler_unblock_by_func(GTK_OBJECT(gtktext), | |
391 GTK_SIGNAL_FUNC(entry_insert_cb), | |
392 NULL); | |
393 gtk_signal_emit_stop_by_name(GTK_OBJECT(gtktext), "insert-text"); | |
394 *ppos += len; | |
395 | |
396 origpos = gtk_editable_get_position(GTK_EDITABLE(gtktext)); | |
397 | |
398 if (iswordsep(newtext[0])) { | |
399 /* did we just end a word? */ | |
400 if (*ppos >= 2) check_at(gtktext, *ppos-2); | |
401 | |
402 /* did we just split a word? */ | |
403 if (*ppos < gtk_text_get_length(gtktext)) | |
404 check_at(gtktext, *ppos+1); | |
405 } else { | |
406 /* check as they type, *except* if they're typing at the end (the most | |
407 * common case. | |
408 */ | |
409 if (*ppos < gtk_text_get_length(gtktext) && | |
410 !iswordsep(GTK_TEXT_INDEX(gtktext, *ppos))) | |
411 check_at(gtktext, *ppos-1); | |
412 } | |
413 | |
414 gtk_editable_set_position(GTK_EDITABLE(gtktext), origpos); | |
415 } | |
416 | |
417 static void entry_delete_cb(GtkText *gtktext, | |
418 gint start, gint end, gpointer d) { | |
419 int origpos; | |
420 | |
421 if (!gtkspell_running()) return; | |
422 | |
423 origpos = gtk_editable_get_position(GTK_EDITABLE(gtktext)); | |
424 check_at(gtktext, start-1); | |
425 gtk_editable_set_position(GTK_EDITABLE(gtktext), origpos); | |
426 gtk_editable_select_region(GTK_EDITABLE(gtktext), origpos, origpos); | |
427 /* this is to *UNDO* the selection, in case they were holding shift | |
428 * while hitting backspace. */ | |
429 } | |
430 | |
431 static void replace_word(GtkWidget *w, gpointer d) { | |
432 int start, end; | |
433 char *newword; | |
434 char buf[BUFSIZE]; | |
435 | |
436 /* we don't save their position, | |
437 * because the cursor is moved by the click. */ | |
438 | |
439 gtk_text_freeze(GTK_TEXT(d)); | |
440 | |
441 gtk_label_get(GTK_LABEL(GTK_BIN(w)->child), &newword); | |
442 get_curword(GTK_TEXT(d), buf, &start, &end); | |
443 | |
444 gtk_text_set_point(GTK_TEXT(d), end); | |
445 gtk_text_backward_delete(GTK_TEXT(d), end-start); | |
446 gtk_text_insert(GTK_TEXT(d), NULL, NULL, NULL, newword, strlen(newword)); | |
447 | |
448 gtk_text_thaw(GTK_TEXT(d)); | |
449 } | |
450 | |
451 static GtkMenu *make_menu(GList *l, GtkText *gtktext) { | |
452 GtkWidget *menu, *item; | |
453 char *caption; | |
454 menu = gtk_menu_new(); { | |
455 caption = g_strdup_printf("Not in dictionary: %s", (char*)l->data); | |
456 item = gtk_menu_item_new_with_label(caption); | |
457 /* I'd like to make it so this item is never selectable, like | |
458 * the menu titles in the GNOME panel... unfortunately, the GNOME | |
459 * panel creates their own custom widget to do this! */ | |
460 gtk_widget_show(item); | |
461 gtk_menu_append(GTK_MENU(menu), item); | |
462 | |
463 item = gtk_menu_item_new(); | |
464 gtk_widget_show(item); | |
465 gtk_menu_append(GTK_MENU(menu), item); | |
466 | |
467 l = l->next; | |
468 if (l == NULL) { | |
469 item = gtk_menu_item_new_with_label("(no suggestions)"); | |
470 gtk_widget_show(item); | |
471 gtk_menu_append(GTK_MENU(menu), item); | |
472 } else { | |
473 GtkWidget *curmenu = menu; | |
474 int count = 0; | |
475 do { | |
476 if (l->data == NULL && l->next != NULL) { | |
477 count = 0; | |
478 curmenu = gtk_menu_new(); | |
479 item = gtk_menu_item_new_with_label("Other Possibilities..."); | |
480 gtk_widget_show(item); | |
481 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), curmenu); | |
482 gtk_menu_append(GTK_MENU(curmenu), item); | |
483 l = l->next; | |
484 } else if (count > MENUCOUNT) { | |
485 count -= MENUCOUNT; | |
486 item = gtk_menu_item_new_with_label("More..."); | |
487 gtk_widget_show(item); | |
488 gtk_menu_append(GTK_MENU(curmenu), item); | |
489 curmenu = gtk_menu_new(); | |
490 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), curmenu); | |
491 } | |
492 item = gtk_menu_item_new_with_label((char*)l->data); | |
493 gtk_signal_connect(GTK_OBJECT(item), "activate", | |
494 GTK_SIGNAL_FUNC(replace_word), gtktext); | |
495 gtk_widget_show(item); | |
496 gtk_menu_append(GTK_MENU(curmenu), item); | |
497 count++; | |
498 } while ((l = l->next) != NULL); | |
499 } | |
500 } | |
501 return GTK_MENU(menu); | |
502 } | |
503 | |
504 static void popup_menu(GtkText *gtktext, GdkEventButton *eb) { | |
505 char buf[BUFSIZE]; | |
506 GList *list, *l; | |
507 | |
508 get_curword(gtktext, buf, NULL, NULL); | |
509 | |
510 list = misspelled_suggest(buf); | |
511 if (list != NULL) { | |
512 gtk_menu_popup(make_menu(list, gtktext), NULL, NULL, NULL, NULL, | |
513 eb->button, eb->time); | |
514 for (l = list; l != NULL; l = l->next) | |
515 g_free(l->data); | |
516 g_list_free(list); | |
517 } | |
518 } | |
519 | |
520 /* ok, this is pretty wacky: | |
521 * we need to let the right-mouse-click go through, so it moves the cursor, | |
522 * but we *can't* let it go through, because GtkText interprets rightclicks as | |
523 * weird selection modifiers. | |
524 * | |
525 * so what do we do? forge rightclicks as leftclicks, then popup the menu. | |
526 * HACK HACK HACK. | |
527 */ | |
528 static gint button_press_intercept_cb(GtkText *gtktext, GdkEvent *e, gpointer d) { | |
529 GdkEventButton *eb; | |
530 gboolean retval; | |
531 | |
532 if (!gtkspell_running()) return FALSE; | |
533 | |
534 if (e->type != GDK_BUTTON_PRESS) return FALSE; | |
535 eb = (GdkEventButton*) e; | |
536 | |
537 if (eb->button != 3) return FALSE; | |
538 | |
539 /* forge the leftclick */ | |
540 eb->button = 1; | |
541 | |
542 gtk_signal_handler_block_by_func(GTK_OBJECT(gtktext), | |
543 GTK_SIGNAL_FUNC(button_press_intercept_cb), d); | |
544 gtk_signal_emit_by_name(GTK_OBJECT(gtktext), "button-press-event", | |
545 e, &retval); | |
546 gtk_signal_handler_unblock_by_func(GTK_OBJECT(gtktext), | |
547 GTK_SIGNAL_FUNC(button_press_intercept_cb), d); | |
548 gtk_signal_emit_stop_by_name(GTK_OBJECT(gtktext), "button-press-event"); | |
549 | |
550 /* now do the menu wackiness */ | |
551 popup_menu(gtktext, eb); | |
552 return TRUE; | |
553 } | |
554 | |
555 void gtkspell_uncheck_all(GtkText *gtktext) { | |
556 int origpos; | |
557 char *text; | |
558 float adj_value; | |
559 | |
560 adj_value = gtktext->vadj->value; | |
561 gtk_text_freeze(gtktext); | |
562 origpos = gtk_editable_get_position(GTK_EDITABLE(gtktext)); | |
563 text = gtk_editable_get_chars(GTK_EDITABLE(gtktext), 0, -1); | |
564 gtk_text_set_point(gtktext, 0); | |
565 gtk_text_forward_delete(gtktext, gtk_text_get_length(gtktext)); | |
566 gtk_text_insert(gtktext, NULL, NULL, NULL, text, strlen(text)); | |
567 gtk_text_thaw(gtktext); | |
568 | |
569 gtk_editable_set_position(GTK_EDITABLE(gtktext), origpos); | |
570 gtk_adjustment_set_value(gtktext->vadj, adj_value); | |
571 } | |
572 | |
573 void gtkspell_attach(GtkText *gtktext) { | |
574 gtk_signal_connect(GTK_OBJECT(gtktext), "insert-text", | |
575 GTK_SIGNAL_FUNC(entry_insert_cb), NULL); | |
576 gtk_signal_connect_after(GTK_OBJECT(gtktext), "delete-text", | |
577 GTK_SIGNAL_FUNC(entry_delete_cb), NULL); | |
578 gtk_signal_connect(GTK_OBJECT(gtktext), "button-press-event", | |
579 GTK_SIGNAL_FUNC(button_press_intercept_cb), NULL); | |
580 } | |
581 | |
582 void gtkspell_detach(GtkText *gtktext) { | |
583 gtk_signal_disconnect_by_func(GTK_OBJECT(gtktext), | |
584 GTK_SIGNAL_FUNC(entry_insert_cb), NULL); | |
585 gtk_signal_disconnect_by_func(GTK_OBJECT(gtktext), | |
586 GTK_SIGNAL_FUNC(entry_delete_cb), NULL); | |
587 gtk_signal_disconnect_by_func(GTK_OBJECT(gtktext), | |
588 GTK_SIGNAL_FUNC(button_press_intercept_cb), NULL); | |
589 | |
590 gtkspell_uncheck_all(gtktext); | |
591 } | |
592 | |
593 static void sigchld(int param) { | |
594 if (gtkspell_running() && | |
595 (waitpid(spell_pid, NULL, WNOHANG) == spell_pid)) { | |
596 spell_pid = 0; | |
597 } else { | |
598 /* a default SIGCHLD handler. | |
599 * what else to do here? */ | |
600 waitpid(-1, NULL, WNOHANG); | |
601 } | |
602 } | |
603 | |
604 static void set_up_signal() { | |
605 struct sigaction sigact; | |
606 memset(&sigact, 0, sizeof(struct sigaction)); | |
607 | |
608 sigact.sa_handler = sigchld; | |
609 sigaction(SIGCHLD, &sigact, NULL); | |
610 } |