Mercurial > pidgin
comparison plugins/spellchk.c @ 12711:145f76e74a9f
[gaim-migrate @ 15055]
Fix SF Bug #1384698 - "Last word in text auto replace not changed"
This uses astro96's idea:
'when the user types something like "Hi how r u" they will see this
(where the "^" is the cursor):
"Hi how are u^"
When they hit enter to send the message, instead of sending
the message, the last word would be replaced:
[Enter]
"Hi how are you^"
and then if they hit enter again it would send the message.'
I code a preference for this, but have #if 0'ed it before committing. I don't think we need a preference to disable this. If it turns out I'm wrong, the code is right there to use.
committer: Tailor Script <tailor@pidgin.im>
author | Richard Laager <rlaager@wiktel.com> |
---|---|
date | Wed, 04 Jan 2006 03:01:32 +0000 |
parents | 9c2a4e327718 |
children | 5f15d53b610a |
comparison
equal
deleted
inserted
replaced
12710:2d326453b33d | 12711:145f76e74a9f |
---|---|
38 #include "signals.h" | 38 #include "signals.h" |
39 #include "util.h" | 39 #include "util.h" |
40 #include "version.h" | 40 #include "version.h" |
41 | 41 |
42 #include "gtkplugin.h" | 42 #include "gtkplugin.h" |
43 #include "gtkprefs.h" | |
43 #include "gtkutils.h" | 44 #include "gtkutils.h" |
44 | 45 |
45 #include <stdio.h> | 46 #include <stdio.h> |
46 #include <string.h> | 47 #include <string.h> |
47 #ifndef _WIN32 | 48 #ifndef _WIN32 |
67 GtkTextMark *mark_insert_end; | 68 GtkTextMark *mark_insert_end; |
68 | 69 |
69 const gchar *word; | 70 const gchar *word; |
70 gboolean inserting; | 71 gboolean inserting; |
71 gboolean ignore_correction; | 72 gboolean ignore_correction; |
73 gboolean ignore_correction_on_send; | |
72 gint pos; | 74 gint pos; |
73 }; | 75 }; |
74 | 76 |
75 typedef struct _spellchk spellchk; | 77 typedef struct _spellchk spellchk; |
76 | 78 |
368 } | 370 } |
369 | 371 |
370 return TRUE; | 372 return TRUE; |
371 } | 373 } |
372 | 374 |
373 static void | 375 static gboolean |
374 check_range(spellchk *spell, GtkTextBuffer *buffer, | 376 check_range(spellchk *spell, GtkTextBuffer *buffer, |
375 GtkTextIter start, GtkTextIter end) { | 377 GtkTextIter start, GtkTextIter end, gboolean sending) |
376 | 378 { |
379 gboolean replaced; | |
377 gboolean result; | 380 gboolean result; |
378 gchar *tmp; | 381 gchar *tmp; |
379 int period_count = 0; | 382 int period_count = 0; |
380 gchar *word; | 383 gchar *word; |
381 GtkTextMark *mark; | 384 GtkTextMark *mark; |
382 GtkTextIter pos; | 385 GtkTextIter pos; |
383 | 386 |
384 if (substitute_simple_buffer(buffer)) | 387 if ((replaced = substitute_simple_buffer(buffer))) |
385 { | 388 { |
386 mark = gtk_text_buffer_get_insert(buffer); | 389 mark = gtk_text_buffer_get_insert(buffer); |
387 gtk_text_buffer_get_iter_at_mark(buffer, &pos, mark); | 390 gtk_text_buffer_get_iter_at_mark(buffer, &pos, mark); |
388 spell->pos = gtk_text_iter_get_offset(&pos); | 391 spell->pos = gtk_text_iter_get_offset(&pos); |
389 | 392 |
390 gtk_text_buffer_get_iter_at_mark(buffer, &start, mark); | 393 gtk_text_buffer_get_iter_at_mark(buffer, &start, mark); |
391 gtk_text_buffer_get_iter_at_mark(buffer, &end, mark); | 394 gtk_text_buffer_get_iter_at_mark(buffer, &end, mark); |
392 } | 395 } |
393 | 396 |
394 /* We need to go backwords to find out if we are inside a word or not. */ | 397 if (!sending) |
395 gtk_text_iter_backward_char(&end); | 398 { |
396 | 399 /* We need to go backwords to find out if we are inside a word or not. */ |
397 if (spellchk_inside_word(&end)) { | 400 gtk_text_iter_backward_char(&end); |
398 gtk_text_iter_forward_char(&end); | 401 |
399 return; /* We only pay attention to whole words. */ | 402 if (spellchk_inside_word(&end)) |
403 { | |
404 gtk_text_iter_forward_char(&end); | |
405 return replaced; /* We only pay attention to whole words. */ | |
406 } | |
400 } | 407 } |
401 | 408 |
402 /* We could be in the middle of a whitespace block. Check for that. */ | 409 /* We could be in the middle of a whitespace block. Check for that. */ |
403 result = gtk_text_iter_backward_char(&end); | 410 result = gtk_text_iter_backward_char(&end); |
404 | 411 |
405 if (!spellchk_inside_word(&end)) { | 412 if (!spellchk_inside_word(&end)) |
413 { | |
406 if (result) | 414 if (result) |
407 gtk_text_iter_forward_char(&end); | 415 gtk_text_iter_forward_char(&end); |
408 return; | 416 return replaced; |
409 } | 417 } |
410 | 418 |
411 if (result) | 419 if (result) |
412 gtk_text_iter_forward_char(&end); | 420 gtk_text_iter_forward_char(&end); |
413 | 421 |
452 gtk_text_buffer_get_iter_at_mark(buffer, &pos, mark); | 460 gtk_text_buffer_get_iter_at_mark(buffer, &pos, mark); |
453 spell->pos = gtk_text_iter_get_offset(&pos); | 461 spell->pos = gtk_text_iter_get_offset(&pos); |
454 | 462 |
455 g_free(word); | 463 g_free(word); |
456 g_free(tmp); | 464 g_free(tmp); |
457 return; | 465 return TRUE; |
458 } | 466 } |
459 g_free(tmp); | 467 g_free(tmp); |
460 | 468 |
461 spell->word = NULL; | 469 spell->word = NULL; |
462 | 470 |
471 return replaced; | |
463 } | 472 } |
464 | 473 |
465 /* insertion works like this: | 474 /* insertion works like this: |
466 * - before the text is inserted, we mark the position in the buffer. | 475 * - before the text is inserted, we mark the position in the buffer. |
467 * - after the text is inserted, we see where our mark is and use that and | 476 * - after the text is inserted, we see where our mark is and use that and |
487 gchar *text, gint len, spellchk *spell) | 496 gchar *text, gint len, spellchk *spell) |
488 { | 497 { |
489 GtkTextIter start, end; | 498 GtkTextIter start, end; |
490 GtkTextMark *mark; | 499 GtkTextMark *mark; |
491 | 500 |
501 spell->ignore_correction_on_send = FALSE; | |
502 | |
492 if (spell->ignore_correction) { | 503 if (spell->ignore_correction) { |
493 spell->ignore_correction = FALSE; | 504 spell->ignore_correction = FALSE; |
494 return; | 505 return; |
495 } | 506 } |
496 | 507 |
497 /* we need to check a range of text. */ | 508 /* we need to check a range of text. */ |
498 gtk_text_buffer_get_iter_at_mark(buffer, &start, spell->mark_insert_start); | 509 gtk_text_buffer_get_iter_at_mark(buffer, &start, spell->mark_insert_start); |
499 | 510 |
500 if (len == 1) | 511 if (len == 1) |
501 check_range(spell, buffer, start, *iter); | 512 check_range(spell, buffer, start, *iter, FALSE); |
502 | 513 |
503 /* if check_range modified the buffer, iter has been invalidated */ | 514 /* if check_range modified the buffer, iter has been invalidated */ |
504 mark = gtk_text_buffer_get_insert(buffer); | 515 mark = gtk_text_buffer_get_insert(buffer); |
505 gtk_text_buffer_get_iter_at_mark(buffer, &end, mark); | 516 gtk_text_buffer_get_iter_at_mark(buffer, &end, mark); |
506 gtk_text_buffer_move_mark(buffer, spell->mark_insert_end, &end); | 517 gtk_text_buffer_move_mark(buffer, spell->mark_insert_end, &end); |
507 | 518 |
508 spell->inserting = FALSE; | 519 spell->inserting = FALSE; |
509 | |
510 } | 520 } |
511 | 521 |
512 static void | 522 static void |
513 delete_range_after(GtkTextBuffer *buffer, | 523 delete_range_after(GtkTextBuffer *buffer, |
514 GtkTextIter *start, GtkTextIter *end, spellchk *spell) | 524 GtkTextIter *start, GtkTextIter *end, spellchk *spell) |
516 GtkTextIter start2, end2; | 526 GtkTextIter start2, end2; |
517 GtkTextMark *mark; | 527 GtkTextMark *mark; |
518 GtkTextIter pos; | 528 GtkTextIter pos; |
519 gint place; | 529 gint place; |
520 | 530 |
531 spell->ignore_correction_on_send = FALSE; | |
532 | |
521 if (!spell->word) | 533 if (!spell->word) |
522 return; | 534 return; |
523 | 535 |
524 if (spell->inserting == TRUE) | 536 if (spell->inserting == TRUE) |
525 return; | 537 return; |
526 | |
527 | 538 |
528 spell->inserting = TRUE; | 539 spell->inserting = TRUE; |
529 | 540 |
530 mark = gtk_text_buffer_get_insert(buffer); | 541 mark = gtk_text_buffer_get_insert(buffer); |
531 gtk_text_buffer_get_iter_at_mark(buffer, &pos, mark); | 542 gtk_text_buffer_get_iter_at_mark(buffer, &pos, mark); |
540 gtk_text_buffer_get_iter_at_mark(buffer, &end2, spell->mark_insert_end); | 551 gtk_text_buffer_get_iter_at_mark(buffer, &end2, spell->mark_insert_end); |
541 | 552 |
542 gtk_text_buffer_delete(buffer, &start2, &end2); | 553 gtk_text_buffer_delete(buffer, &start2, &end2); |
543 gtk_text_buffer_insert(buffer, &start2, spell->word, -1); | 554 gtk_text_buffer_insert(buffer, &start2, spell->word, -1); |
544 spell->ignore_correction = TRUE; | 555 spell->ignore_correction = TRUE; |
556 spell->ignore_correction_on_send = TRUE; | |
545 | 557 |
546 spell->inserting = FALSE; | 558 spell->inserting = FALSE; |
547 spell->word = NULL; | 559 spell->word = NULL; |
560 } | |
561 | |
562 static void | |
563 message_send_cb(GtkWidget *widget, spellchk *spell) | |
564 { | |
565 GtkTextBuffer *buffer; | |
566 GtkTextIter start, end; | |
567 GtkTextMark *mark; | |
568 gboolean replaced; | |
569 | |
570 if (spell->ignore_correction_on_send) | |
571 { | |
572 spell->ignore_correction_on_send = FALSE; | |
573 return; | |
574 } | |
575 | |
576 #if 0 | |
577 if (!gaim_prefs_get_bool("/plugins/gtk/spellchk/last_word_replace")) | |
578 return; | |
579 #endif | |
580 | |
581 buffer = gtk_text_view_get_buffer(spell->view); | |
582 | |
583 gtk_text_buffer_get_end_iter(buffer, &start); | |
584 gtk_text_buffer_get_end_iter(buffer, &end); | |
585 spell->inserting = TRUE; | |
586 replaced = check_range(spell, buffer, start, end, TRUE); | |
587 spell->inserting = FALSE; | |
588 | |
589 /* if check_range modified the buffer, iter has been invalidated */ | |
590 mark = gtk_text_buffer_get_insert(buffer); | |
591 gtk_text_buffer_get_iter_at_mark(buffer, &end, mark); | |
592 gtk_text_buffer_move_mark(buffer, spell->mark_insert_end, &end); | |
593 | |
594 if (replaced) | |
595 { | |
596 g_signal_stop_emission_by_name(widget, "message_send"); | |
597 spell->ignore_correction_on_send = TRUE; | |
598 } | |
548 } | 599 } |
549 | 600 |
550 static void | 601 static void |
551 spellchk_new_attach(GaimConversation *conv) | 602 spellchk_new_attach(GaimConversation *conv) |
552 { | 603 { |
591 G_CALLBACK(insert_text_before), spell); | 642 G_CALLBACK(insert_text_before), spell); |
592 g_signal_connect_after(G_OBJECT(buffer), | 643 g_signal_connect_after(G_OBJECT(buffer), |
593 "insert-text", | 644 "insert-text", |
594 G_CALLBACK(insert_text_after), spell); | 645 G_CALLBACK(insert_text_after), spell); |
595 | 646 |
647 g_signal_connect(G_OBJECT(gtkconv->entry), "message_send", | |
648 G_CALLBACK(message_send_cb), spell); | |
596 return; | 649 return; |
597 } | 650 } |
598 | 651 |
599 static int buf_get_line(char *ibuf, char **buf, int *position, int len) | 652 static int buf_get_line(char *ibuf, char **buf, int *position, int len) |
600 { | 653 { |
2041 GtkWidget *button; | 2094 GtkWidget *button; |
2042 GtkSizeGroup *sg; | 2095 GtkSizeGroup *sg; |
2043 GtkSizeGroup *sg2; | 2096 GtkSizeGroup *sg2; |
2044 GtkCellRenderer *renderer; | 2097 GtkCellRenderer *renderer; |
2045 GtkTreeViewColumn *column; | 2098 GtkTreeViewColumn *column; |
2046 | 2099 GtkWidget *vbox2; |
2047 ret = gtk_vbox_new(FALSE, 18); | 2100 GtkWidget *hbox2; |
2048 gtk_container_set_border_width (GTK_CONTAINER(ret), 12); | 2101 GtkWidget *vbox3; |
2102 | |
2103 ret = gtk_vbox_new(FALSE, GAIM_HIG_CAT_SPACE); | |
2104 gtk_container_set_border_width (GTK_CONTAINER(ret), GAIM_HIG_BORDER); | |
2049 | 2105 |
2050 vbox = gaim_gtk_make_frame(ret, _("Text Replacements")); | 2106 vbox = gaim_gtk_make_frame(ret, _("Text Replacements")); |
2051 gtk_container_set_border_width(GTK_CONTAINER(vbox), 4); | 2107 gtk_container_set_border_width(GTK_CONTAINER(vbox), 4); |
2052 gtk_widget_show(vbox); | 2108 gtk_widget_show(vbox); |
2053 | 2109 |
2136 | 2192 |
2137 gtk_widget_show(button); | 2193 gtk_widget_show(button); |
2138 | 2194 |
2139 vbox = gaim_gtk_make_frame(ret, _("Add a new text replacement")); | 2195 vbox = gaim_gtk_make_frame(ret, _("Add a new text replacement")); |
2140 | 2196 |
2197 hbox = gtk_hbox_new(FALSE, GAIM_HIG_BOX_SPACE); | |
2198 gtk_box_pack_start(GTK_BOX(vbox), hbox, TRUE, TRUE, 0); | |
2199 gtk_widget_show(hbox); | |
2200 vbox2 = gtk_vbox_new(FALSE, GAIM_HIG_BOX_SPACE); | |
2201 gtk_box_pack_start(GTK_BOX(hbox), vbox2, TRUE, TRUE, 0); | |
2202 gtk_widget_show(vbox2); | |
2203 | |
2141 sg = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL); | 2204 sg = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL); |
2142 sg2 = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL); | 2205 sg2 = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL); |
2143 | 2206 |
2144 hbox = gtk_hbox_new(FALSE, 2); | 2207 hbox2 = gtk_hbox_new(FALSE, 2); |
2145 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0); | 2208 gtk_box_pack_start(GTK_BOX(vbox2), hbox2, FALSE, FALSE, 0); |
2146 gtk_widget_show(hbox); | 2209 gtk_widget_show(hbox2); |
2147 | 2210 |
2148 label = gtk_label_new_with_mnemonic(_("You _type:")); | 2211 label = gtk_label_new_with_mnemonic(_("You _type:")); |
2149 gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0); | 2212 gtk_box_pack_start(GTK_BOX(hbox2), label, FALSE, FALSE, 0); |
2150 gtk_size_group_add_widget(sg, label); | 2213 gtk_size_group_add_widget(sg, label); |
2151 gtk_misc_set_alignment(GTK_MISC(label), 0, 0); | 2214 gtk_misc_set_alignment(GTK_MISC(label), 0, 0); |
2152 | 2215 |
2153 bad_entry = gtk_entry_new(); | 2216 bad_entry = gtk_entry_new(); |
2154 /* Set a minimum size. Since they're in a size group, the other entry will match up. */ | 2217 /* Set a minimum size. Since they're in a size group, the other entry will match up. */ |
2155 gtk_widget_set_size_request(bad_entry, 350, -1); | 2218 gtk_widget_set_size_request(bad_entry, 350, -1); |
2156 gtk_box_pack_start(GTK_BOX(hbox), bad_entry, TRUE, TRUE, 0); | 2219 gtk_box_pack_start(GTK_BOX(hbox2), bad_entry, TRUE, TRUE, 0); |
2157 gtk_size_group_add_widget(sg2, bad_entry); | 2220 gtk_size_group_add_widget(sg2, bad_entry); |
2158 gtk_label_set_mnemonic_widget(GTK_LABEL(label), bad_entry); | 2221 gtk_label_set_mnemonic_widget(GTK_LABEL(label), bad_entry); |
2159 gtk_widget_show(bad_entry); | 2222 gtk_widget_show(bad_entry); |
2160 | 2223 |
2161 hbox = gtk_hbox_new(FALSE, 2); | 2224 hbox2 = gtk_hbox_new(FALSE, 2); |
2162 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0); | 2225 gtk_box_pack_start(GTK_BOX(vbox2), hbox2, FALSE, FALSE, 0); |
2163 gtk_widget_show(hbox); | 2226 gtk_widget_show(hbox2); |
2164 | 2227 |
2165 label = gtk_label_new_with_mnemonic(_("You _send:")); | 2228 label = gtk_label_new_with_mnemonic(_("You _send:")); |
2166 gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0); | 2229 gtk_box_pack_start(GTK_BOX(hbox2), label, FALSE, FALSE, 0); |
2167 gtk_size_group_add_widget(sg, label); | 2230 gtk_size_group_add_widget(sg, label); |
2168 gtk_misc_set_alignment(GTK_MISC(label), 0, 0); | 2231 gtk_misc_set_alignment(GTK_MISC(label), 0, 0); |
2169 | 2232 |
2170 good_entry = gtk_entry_new(); | 2233 good_entry = gtk_entry_new(); |
2171 gtk_box_pack_start(GTK_BOX(hbox), good_entry, TRUE, TRUE, 0); | 2234 gtk_box_pack_start(GTK_BOX(hbox2), good_entry, TRUE, TRUE, 0); |
2172 gtk_size_group_add_widget(sg2, good_entry); | 2235 gtk_size_group_add_widget(sg2, good_entry); |
2173 gtk_label_set_mnemonic_widget(GTK_LABEL(label), good_entry); | 2236 gtk_label_set_mnemonic_widget(GTK_LABEL(label), good_entry); |
2174 gtk_widget_show(good_entry); | 2237 gtk_widget_show(good_entry); |
2175 | 2238 |
2176 /* Created here so it can be passed to whole_words_button_toggled. */ | 2239 /* Created here so it can be passed to whole_words_button_toggled. */ |
2179 complete_toggle = gtk_check_button_new_with_mnemonic(_("Only replace _whole words")); | 2242 complete_toggle = gtk_check_button_new_with_mnemonic(_("Only replace _whole words")); |
2180 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(complete_toggle), TRUE); | 2243 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(complete_toggle), TRUE); |
2181 g_signal_connect(G_OBJECT(complete_toggle), "clicked", | 2244 g_signal_connect(G_OBJECT(complete_toggle), "clicked", |
2182 G_CALLBACK(whole_words_button_toggled), case_toggle); | 2245 G_CALLBACK(whole_words_button_toggled), case_toggle); |
2183 gtk_widget_show(complete_toggle); | 2246 gtk_widget_show(complete_toggle); |
2184 gtk_box_pack_start(GTK_BOX(vbox), complete_toggle, FALSE, FALSE, 0); | 2247 gtk_box_pack_start(GTK_BOX(vbox2), complete_toggle, FALSE, FALSE, 0); |
2185 | 2248 |
2186 /* The button is created above so it can be passed to whole_words_button_toggled. */ | 2249 /* The button is created above so it can be passed to whole_words_button_toggled. */ |
2187 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(case_toggle), FALSE); | 2250 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(case_toggle), FALSE); |
2188 gtk_widget_show(case_toggle); | 2251 gtk_widget_show(case_toggle); |
2189 gtk_box_pack_start(GTK_BOX(vbox), case_toggle, FALSE, FALSE, 0); | 2252 gtk_box_pack_start(GTK_BOX(vbox2), case_toggle, FALSE, FALSE, 0); |
2190 | 2253 |
2191 hbox = gtk_hbutton_box_new(); | |
2192 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0); | |
2193 button = gtk_button_new_from_stock(GTK_STOCK_ADD); | 2254 button = gtk_button_new_from_stock(GTK_STOCK_ADD); |
2194 g_signal_connect(G_OBJECT(button), "clicked", | 2255 g_signal_connect(G_OBJECT(button), "clicked", |
2195 G_CALLBACK(list_add_new), NULL); | 2256 G_CALLBACK(list_add_new), NULL); |
2196 gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0); | 2257 vbox3 = gtk_vbox_new(FALSE, 0); |
2258 gtk_box_pack_start(GTK_BOX(hbox), vbox3, TRUE, FALSE, 0); | |
2259 gtk_widget_show(vbox3); | |
2260 gtk_box_pack_end(GTK_BOX(vbox3), button, FALSE, FALSE, 0); | |
2197 g_signal_connect(G_OBJECT(bad_entry), "changed", G_CALLBACK(on_entry_changed), button); | 2261 g_signal_connect(G_OBJECT(bad_entry), "changed", G_CALLBACK(on_entry_changed), button); |
2198 g_signal_connect(G_OBJECT(good_entry), "changed", G_CALLBACK(on_entry_changed), button); | 2262 g_signal_connect(G_OBJECT(good_entry), "changed", G_CALLBACK(on_entry_changed), button); |
2199 gtk_widget_set_sensitive(button, FALSE); | 2263 gtk_widget_set_sensitive(button, FALSE); |
2200 gtk_widget_show(button); | 2264 gtk_widget_show(button); |
2201 | 2265 |
2266 #if 0 | |
2267 vbox = gaim_gtk_make_frame(ret, _("General Text Replacement Options")); | |
2268 gaim_gtk_prefs_checkbox(_("Enable replacement of last word on send"), | |
2269 "/plugins/gtk/spellchk/last_word_replace", vbox); | |
2270 #endif | |
2202 | 2271 |
2203 gtk_widget_show_all(ret); | 2272 gtk_widget_show_all(ret); |
2204 return ret; | 2273 return ret; |
2205 } | 2274 } |
2206 | 2275 |
2237 }; | 2306 }; |
2238 | 2307 |
2239 static void | 2308 static void |
2240 init_plugin(GaimPlugin *plugin) | 2309 init_plugin(GaimPlugin *plugin) |
2241 { | 2310 { |
2311 #if 0 | |
2312 gaim_prefs_add_none("/plugins"); | |
2313 gaim_prefs_add_none("/plugins/gtk"); | |
2314 gaim_prefs_add_none("/plugins/gtk/spellchk"); | |
2315 gaim_prefs_add_bool("/plugins/gtk/spellchk/last_word_replace", TRUE); | |
2316 #endif | |
2242 } | 2317 } |
2243 | 2318 |
2244 GAIM_INIT_PLUGIN(spellcheck, init_plugin, info) | 2319 GAIM_INIT_PLUGIN(spellcheck, init_plugin, info) |