Lines 1-5
Link Here
|
1 |
/* gtkspell - a spell-checking addon for GTK's TextView widget |
1 |
/* gtkspell |
2 |
* Copyright (c) 2002 Evan Martin. |
2 |
* Copyright (c) 2002 Evan Martin. |
|
|
3 |
* Copyright (c) 2004 Nathan Fredrickson. |
4 |
* |
5 |
* This library is free software; you can redistribute it and/or |
6 |
* modify it under the terms of the GNU Lesser General Public |
7 |
* License as published by the Free Software Foundation; either |
8 |
* version 2.1 of the License, or (at your option) any later version. |
9 |
* |
10 |
* This library is distributed in the hope that it will be useful, |
11 |
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
12 |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
13 |
* Lesser General Public License for more details. |
14 |
* |
15 |
* You should have received a copy of the GNU Lesser General Public |
16 |
* License along with this library; if not, write to the |
17 |
* Free Software Foundation, Inc., 59 Temple Place - Suite 330, |
18 |
* Boston, MA 02111-1307, USA. |
19 |
* |
3 |
*/ |
20 |
*/ |
4 |
|
21 |
|
5 |
/* vim: set ts=4 sw=4 wm=5 : */ |
22 |
/* vim: set ts=4 sw=4 wm=5 : */ |
Lines 7-48
Link Here
|
7 |
#include <gtk/gtk.h> |
24 |
#include <gtk/gtk.h> |
8 |
#include <libintl.h> |
25 |
#include <libintl.h> |
9 |
#include <locale.h> |
26 |
#include <locale.h> |
|
|
27 |
#include <enchant.h> |
10 |
#include "../config.h" |
28 |
#include "../config.h" |
11 |
#include "gtkspell.h" |
29 |
#include "gtkspell.h" |
12 |
|
30 |
|
13 |
#define _(String) dgettext (PACKAGE, String) |
31 |
#define _(String) dgettext(PACKAGE, String) |
14 |
|
32 |
#define N_(String) (String) |
15 |
#ifdef HAVE_ASPELL_H |
|
|
16 |
#define USING_ASPELL |
17 |
#include <aspell.h> |
18 |
#elif defined HAVE_PSPELL_H |
19 |
#define USING_PSPELL |
20 |
#include <pspell/pspell.h> |
21 |
#define AspellSpeller PspellManager |
22 |
#define speller manager |
23 |
#define aspell_speller_check pspell_manager_check |
24 |
#define aspell_speller_add_to_session pspell_manager_add_to_session |
25 |
#define aspell_speller_add_to_personal pspell_manager_add_to_personal |
26 |
#define aspell_speller_save_all_word_lists pspell_manager_save_all_word_lists |
27 |
#define aspell_speller_store_replacement pspell_manager_store_replacement |
28 |
#define AspellWordList PspellWordList |
29 |
#define AspellStringEnumeration PspellStringEmulation |
30 |
#define aspell_speller_suggest pspell_manager_suggest |
31 |
#define aspell_word_list_elements pspell_word_list_elements |
32 |
#define aspell_string_enumeration_next pspell_string_emulation_next |
33 |
#define delete_aspell_string_enumeration delete_pspell_string_emulation |
34 |
#define AspellConfig PspellConfig |
35 |
#define AspellCanHaveError PspellCanHaveError |
36 |
#define new_aspell_config new_pspell_config |
37 |
#define aspell_config_replace pspell_config_replace |
38 |
#define new_aspell_speller new_pspell_manager |
39 |
#define delete_aspell_config delete_pspell_config |
40 |
#define aspell_error_message pspell_error_message |
41 |
#define delete_aspell_speller delete_pspell_manager |
42 |
#define to_aspell_speller to_pspell_manager |
43 |
#define aspell_error_number pspell_error_number |
44 |
#define aspell pspell |
45 |
#endif |
46 |
|
33 |
|
47 |
const int debug = 0; |
34 |
const int debug = 0; |
48 |
const int quiet = 0; |
35 |
const int quiet = 0; |
Lines 53-60
struct _GtkSpell {
Link Here
|
53 |
GtkTextMark *mark_insert_start; |
40 |
GtkTextMark *mark_insert_start; |
54 |
GtkTextMark *mark_insert_end; |
41 |
GtkTextMark *mark_insert_end; |
55 |
gboolean deferred_check; |
42 |
gboolean deferred_check; |
56 |
AspellSpeller *speller; |
43 |
EnchantBroker *broker; |
|
|
44 |
EnchantDict *speller; |
57 |
GtkTextMark *mark_click; |
45 |
GtkTextMark *mark_click; |
|
|
46 |
gchar *lang; |
58 |
}; |
47 |
}; |
59 |
|
48 |
|
60 |
static void gtkspell_free(GtkSpell *spell); |
49 |
static void gtkspell_free(GtkSpell *spell); |
Lines 122-131
static void
Link Here
|
122 |
check_word(GtkSpell *spell, GtkTextBuffer *buffer, |
111 |
check_word(GtkSpell *spell, GtkTextBuffer *buffer, |
123 |
GtkTextIter *start, GtkTextIter *end) { |
112 |
GtkTextIter *start, GtkTextIter *end) { |
124 |
char *text; |
113 |
char *text; |
|
|
114 |
|
115 |
if (!spell->speller) |
116 |
return; |
117 |
|
125 |
text = gtk_text_buffer_get_text(buffer, start, end, FALSE); |
118 |
text = gtk_text_buffer_get_text(buffer, start, end, FALSE); |
126 |
if (debug) g_print("checking: %s\n", text); |
119 |
if (debug) g_print("checking: %s\n", text); |
127 |
if (g_unichar_isdigit(*text) == FALSE) /* don't check numbers */ |
120 |
if (g_unichar_isdigit(*text) == FALSE) /* don't check numbers */ |
128 |
if (aspell_speller_check(spell->speller, text, -1) == FALSE) |
121 |
if (enchant_dict_check(spell->speller, text, strlen(text)) != 0) |
129 |
gtk_text_buffer_apply_tag(buffer, spell->tag_highlight, start, end); |
122 |
gtk_text_buffer_apply_tag(buffer, spell->tag_highlight, start, end); |
130 |
g_free(text); |
123 |
g_free(text); |
131 |
} |
124 |
} |
Lines 295-307
add_to_dictionary(GtkWidget *menuitem, G
Link Here
|
295 |
GtkTextIter start, end; |
288 |
GtkTextIter start, end; |
296 |
GtkTextBuffer *buffer; |
289 |
GtkTextBuffer *buffer; |
297 |
|
290 |
|
|
|
291 |
if (!spell->speller) |
292 |
return; |
293 |
|
298 |
buffer = gtk_text_view_get_buffer(spell->view); |
294 |
buffer = gtk_text_view_get_buffer(spell->view); |
299 |
|
295 |
|
300 |
get_word_extents_from_mark(buffer, &start, &end, spell->mark_click); |
296 |
get_word_extents_from_mark(buffer, &start, &end, spell->mark_click); |
301 |
word = gtk_text_buffer_get_text(buffer, &start, &end, FALSE); |
297 |
word = gtk_text_buffer_get_text(buffer, &start, &end, FALSE); |
302 |
|
298 |
|
303 |
aspell_speller_add_to_personal(spell->speller, word, strlen(word)); |
299 |
enchant_dict_add_to_personal(spell->speller, word, strlen(word)); |
304 |
aspell_speller_save_all_word_lists(spell->speller); |
|
|
305 |
|
300 |
|
306 |
gtkspell_recheck_all(spell); |
301 |
gtkspell_recheck_all(spell); |
307 |
|
302 |
|
Lines 319-325
ignore_all(GtkWidget *menuitem, GtkSpell
Link Here
|
319 |
get_word_extents_from_mark(buffer, &start, &end, spell->mark_click); |
314 |
get_word_extents_from_mark(buffer, &start, &end, spell->mark_click); |
320 |
word = gtk_text_buffer_get_text(buffer, &start, &end, FALSE); |
315 |
word = gtk_text_buffer_get_text(buffer, &start, &end, FALSE); |
321 |
|
316 |
|
322 |
aspell_speller_add_to_session(spell->speller, word, strlen(word)); |
317 |
enchant_dict_add_to_session(spell->speller, word, strlen(word)); |
323 |
|
318 |
|
324 |
gtkspell_recheck_all(spell); |
319 |
gtkspell_recheck_all(spell); |
325 |
|
320 |
|
Lines 332-337
replace_word(GtkWidget *menuitem, GtkSpe
Link Here
|
332 |
const char *newword; |
327 |
const char *newword; |
333 |
GtkTextIter start, end; |
328 |
GtkTextIter start, end; |
334 |
GtkTextBuffer *buffer; |
329 |
GtkTextBuffer *buffer; |
|
|
330 |
|
331 |
if (!spell->speller) |
332 |
return; |
335 |
|
333 |
|
336 |
buffer = gtk_text_view_get_buffer(spell->view); |
334 |
buffer = gtk_text_view_get_buffer(spell->view); |
337 |
|
335 |
|
Lines 348-380
replace_word(GtkWidget *menuitem, GtkSpe
Link Here
|
348 |
gtk_text_buffer_delete(buffer, &start, &end); |
346 |
gtk_text_buffer_delete(buffer, &start, &end); |
349 |
gtk_text_buffer_insert(buffer, &start, newword, -1); |
347 |
gtk_text_buffer_insert(buffer, &start, newword, -1); |
350 |
|
348 |
|
351 |
aspell_speller_store_replacement(spell->speller, |
349 |
enchant_dict_store_replacement(spell->speller, |
352 |
oldword, strlen(oldword), |
350 |
oldword, strlen(oldword), |
353 |
newword, strlen(newword)); |
351 |
newword, strlen(newword)); |
354 |
|
352 |
|
355 |
g_free(oldword); |
353 |
g_free(oldword); |
356 |
} |
354 |
} |
357 |
|
355 |
|
358 |
GtkWidget* |
356 |
#define KNOWN_LANGUAGES 39 |
|
|
357 |
static gchar *known_languages [KNOWN_LANGUAGES * 2 + 1] = { |
358 |
"br", N_("Breton"), |
359 |
"ca", N_("Catalan"), |
360 |
"cs", N_("Czech"), |
361 |
"da", N_("Danish"), |
362 |
"de-DE", N_("German (Germany)"), |
363 |
"de-AT", N_("German (Austria)"), |
364 |
"de-CH", N_("German (Swiss)"), |
365 |
"el", N_("Greek"), |
366 |
"en", N_("English"), |
367 |
"en-US", N_("English (American)"), |
368 |
"en-GB", N_("English (British)"), |
369 |
"en-CA", N_("English (Canadian)"), |
370 |
"eo", N_("Esperanto"), |
371 |
"es", N_("Spanish"), |
372 |
"fi", N_("Finnish"), |
373 |
"fo", N_("Faroese"), |
374 |
"fr-FR", N_("French (France)"), |
375 |
"fr-CH", N_("French (Swiss)"), |
376 |
"hu", N_("Hungarian"), |
377 |
"ga", N_("Irish"), |
378 |
"gl", N_("Galician"), |
379 |
"he", N_("Hebrew"), |
380 |
"it", N_("Italian"), |
381 |
"la", N_("Latin"), |
382 |
"lt", N_("Lithuanian"), |
383 |
"nb-NO", N_("Norwegian (Bokmal)"), |
384 |
"nl", N_("Dutch"), |
385 |
"nn-NO", N_("Norwegian (Nyorsk)"), |
386 |
"no", N_("Norwegian"), |
387 |
"pl", N_("Polish"), |
388 |
"pt-PT", N_("Portuguese (Portugal)"), |
389 |
"pt-BR", N_("Portuguese (Brazilian)"), |
390 |
"ru", N_("Russian"), |
391 |
"sc", N_("Sardinian"), |
392 |
"sk", N_("Slovak"), |
393 |
"sl", N_("Slovenian"), |
394 |
"sv", N_("Swedish"), |
395 |
"uk", N_("Ukrainian"), |
396 |
"yi", N_("Yiddish"), |
397 |
NULL |
398 |
}; |
399 |
|
400 |
static void |
401 |
dict_describe (const char * const lang_tag, |
402 |
const char * const provider_name, |
403 |
const char * const provider_desc, |
404 |
const char * const provider_file, |
405 |
void * user_data) { |
406 |
printf("%s | %s | %s | %s\n", lang_tag, provider_name, provider_desc, provider_file); |
407 |
} |
408 |
|
409 |
/* replace any 1 hyphen with an underscore. converts en-US to en_US */ |
410 |
static gchar * |
411 |
normalize_language (const gchar *language) { |
412 |
gchar *dup, *hyphen; |
413 |
|
414 |
dup = g_strdup(language); |
415 |
hyphen = strchr(dup, '-'); |
416 |
if (hyphen) |
417 |
*hyphen = '_'; |
418 |
return dup; |
419 |
} |
420 |
|
421 |
static GSList * |
422 |
get_languages (GtkSpell *spell) { |
423 |
EnchantBroker *broker = spell->broker; |
424 |
gchar * lang; |
425 |
|
426 |
GSList *langs; |
427 |
gint i; |
428 |
|
429 |
langs = NULL; |
430 |
|
431 |
for (i=0; known_languages[i]; i++) { |
432 |
lang = normalize_language(known_languages [i]); |
433 |
i++; |
434 |
|
435 |
if (enchant_broker_dict_exists(spell->broker, lang)) { |
436 |
/* |
437 |
enchant_dict_describe(spell->speller, dict_describe, NULL); |
438 |
printf("Language: %s (%s)\n", known_languages[i], lang); |
439 |
*/ |
440 |
langs = g_slist_append(langs, GINT_TO_POINTER(i - 1)); |
441 |
} |
442 |
|
443 |
g_free(lang); |
444 |
} |
445 |
|
446 |
return langs; |
447 |
} |
448 |
|
449 |
static GtkWidget* |
359 |
build_suggestion_menu(GtkSpell *spell, GtkTextBuffer *buffer, |
450 |
build_suggestion_menu(GtkSpell *spell, GtkTextBuffer *buffer, |
360 |
const char *word) { |
451 |
const char *word) { |
361 |
const char *suggestion; |
452 |
const char *suggestion; |
362 |
GtkWidget *topmenu, *menu; |
453 |
GtkWidget *topmenu, *menu; |
363 |
GtkWidget *mi; |
454 |
GtkWidget *mi; |
364 |
GtkWidget *hbox; |
455 |
GtkWidget *hbox; |
365 |
int count = 0; |
|
|
366 |
void *spelldata; |
456 |
void *spelldata; |
367 |
const AspellWordList *suggestions; |
457 |
char ** suggestions; |
368 |
AspellStringEnumeration *elements; |
458 |
size_t n_suggs, i; |
369 |
char *label; |
459 |
char *label; |
370 |
|
460 |
|
371 |
topmenu = menu = gtk_menu_new(); |
461 |
topmenu = menu = gtk_menu_new(); |
372 |
|
462 |
|
373 |
suggestions = aspell_speller_suggest(spell->speller, word, -1); |
463 |
if (!spell->speller) |
374 |
elements = aspell_word_list_elements(suggestions); |
464 |
return topmenu; |
375 |
|
465 |
|
376 |
suggestion = aspell_string_enumeration_next(elements); |
466 |
suggestions = enchant_dict_suggest(spell->speller, word, strlen(word), &n_suggs); |
377 |
if (suggestion == NULL) { |
467 |
|
|
|
468 |
if (suggestions == NULL || !n_suggs) { |
378 |
/* no suggestions. put something in the menu anyway... */ |
469 |
/* no suggestions. put something in the menu anyway... */ |
379 |
GtkWidget *label; |
470 |
GtkWidget *label; |
380 |
label = gtk_label_new(""); |
471 |
label = gtk_label_new(""); |
Lines 386-393
build_suggestion_menu(GtkSpell *spell, G
Link Here
|
386 |
gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), mi); |
477 |
gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), mi); |
387 |
} else { |
478 |
} else { |
388 |
/* build a set of menus with suggestions. */ |
479 |
/* build a set of menus with suggestions. */ |
389 |
while (suggestion != NULL) { |
480 |
for (i = 0; i < n_suggs; i++) { |
390 |
if (count == 10) { |
481 |
if (i != 0 && i % 10 == 0) { |
391 |
mi = gtk_menu_item_new(); |
482 |
mi = gtk_menu_item_new(); |
392 |
gtk_widget_show(mi); |
483 |
gtk_widget_show(mi); |
393 |
gtk_menu_shell_append(GTK_MENU_SHELL(menu), mi); |
484 |
gtk_menu_shell_append(GTK_MENU_SHELL(menu), mi); |
Lines 398-417
build_suggestion_menu(GtkSpell *spell, G
Link Here
|
398 |
|
489 |
|
399 |
menu = gtk_menu_new(); |
490 |
menu = gtk_menu_new(); |
400 |
gtk_menu_item_set_submenu(GTK_MENU_ITEM(mi), menu); |
491 |
gtk_menu_item_set_submenu(GTK_MENU_ITEM(mi), menu); |
401 |
count = 0; |
|
|
402 |
} |
492 |
} |
403 |
mi = gtk_menu_item_new_with_label(suggestion); |
493 |
|
|
|
494 |
mi = gtk_menu_item_new_with_label(suggestions[i]); |
404 |
g_signal_connect(G_OBJECT(mi), "activate", |
495 |
g_signal_connect(G_OBJECT(mi), "activate", |
405 |
G_CALLBACK(replace_word), spell); |
496 |
G_CALLBACK(replace_word), spell); |
406 |
gtk_widget_show(mi); |
497 |
gtk_widget_show(mi); |
407 |
gtk_menu_shell_append(GTK_MENU_SHELL(menu), mi); |
498 |
gtk_menu_shell_append(GTK_MENU_SHELL(menu), mi); |
408 |
count++; |
|
|
409 |
suggestion = aspell_string_enumeration_next(elements); |
410 |
} |
499 |
} |
411 |
} |
500 |
} |
412 |
|
501 |
|
413 |
delete_aspell_string_enumeration(elements); |
|
|
414 |
|
415 |
/* Separator */ |
502 |
/* Separator */ |
416 |
mi = gtk_menu_item_new(); |
503 |
mi = gtk_menu_item_new(); |
417 |
gtk_widget_show(mi); |
504 |
gtk_widget_show(mi); |
Lines 437-442
build_suggestion_menu(GtkSpell *spell, G
Link Here
|
437 |
gtk_widget_show_all(mi); |
524 |
gtk_widget_show_all(mi); |
438 |
gtk_menu_shell_append(GTK_MENU_SHELL(topmenu), mi); |
525 |
gtk_menu_shell_append(GTK_MENU_SHELL(topmenu), mi); |
439 |
|
526 |
|
|
|
527 |
enchant_dict_free_suggestions(spell->speller, suggestions); |
528 |
|
440 |
return topmenu; |
529 |
return topmenu; |
441 |
} |
530 |
} |
442 |
|
531 |
|
Lines 515-523
popup_menu_event(GtkTextView *view, GtkS
Link Here
|
515 |
|
604 |
|
516 |
static gboolean |
605 |
static gboolean |
517 |
gtkspell_set_language_internal(GtkSpell *spell, const gchar *lang, GError **error) { |
606 |
gtkspell_set_language_internal(GtkSpell *spell, const gchar *lang, GError **error) { |
518 |
AspellConfig *config; |
|
|
519 |
AspellCanHaveError *err; |
520 |
AspellSpeller *speller; |
521 |
|
607 |
|
522 |
if (lang == NULL) { |
608 |
if (lang == NULL) { |
523 |
lang = g_getenv("LANG"); |
609 |
lang = g_getenv("LANG"); |
Lines 529-554
gtkspell_set_language_internal(GtkSpell
Link Here
|
529 |
} |
615 |
} |
530 |
} |
616 |
} |
531 |
|
617 |
|
532 |
config = new_aspell_config(); |
618 |
if (!spell->broker) |
533 |
if (lang) |
619 |
spell->broker = enchant_broker_init(); |
534 |
aspell_config_replace(config, "language-tag", lang); |
|
|
535 |
aspell_config_replace(config, "encoding", "utf-8"); |
536 |
err = new_aspell_speller(config); |
537 |
delete_aspell_config(config); |
538 |
|
620 |
|
539 |
if (aspell_error_number(err) != 0) { |
621 |
if (spell->speller) { |
540 |
#ifdef USING_ASPELL |
622 |
enchant_broker_free_dict(spell->broker, spell->speller); |
541 |
g_set_error(error, GTKSPELL_ERROR, GTKSPELL_ERROR_BACKEND, |
623 |
spell->speller = NULL; |
542 |
"aspell: %s", aspell_error_message(err)); |
624 |
} |
543 |
#elif defined USING_PSPELL |
625 |
|
|
|
626 |
if (!lang) |
627 |
lang = "en"; |
628 |
|
629 |
spell->speller = enchant_broker_request_dict (spell->broker, lang); |
630 |
if (!spell->speller) { |
544 |
g_set_error(error, GTKSPELL_ERROR, GTKSPELL_ERROR_BACKEND, |
631 |
g_set_error(error, GTKSPELL_ERROR, GTKSPELL_ERROR_BACKEND, |
545 |
"pspell: %s", aspell_error_message(err)); |
632 |
_("enchant error for language: %s"), lang); |
546 |
#endif |
|
|
547 |
return FALSE; |
633 |
return FALSE; |
548 |
} |
634 |
} |
549 |
if (spell->speller) |
635 |
|
550 |
delete_aspell_speller(spell->speller); |
636 |
if (spell->lang) |
551 |
spell->speller = to_aspell_speller(err); |
637 |
g_free(spell->lang); |
|
|
638 |
|
639 |
spell->lang = g_strdup(lang); |
552 |
|
640 |
|
553 |
return TRUE; |
641 |
return TRUE; |
554 |
} |
642 |
} |
Lines 705-712
gtkspell_free(GtkSpell *spell) {
Link Here
|
705 |
gtk_text_buffer_delete_mark(buffer, spell->mark_insert_end); |
793 |
gtk_text_buffer_delete_mark(buffer, spell->mark_insert_end); |
706 |
gtk_text_buffer_delete_mark(buffer, spell->mark_click); |
794 |
gtk_text_buffer_delete_mark(buffer, spell->mark_click); |
707 |
|
795 |
|
708 |
delete_aspell_speller(spell->speller); |
796 |
if (spell->broker) { |
|
|
797 |
if (spell->speller) |
798 |
enchant_broker_free_dict (spell->broker, spell->speller); |
799 |
|
800 |
enchant_broker_free (spell->broker); |
801 |
} |
709 |
|
802 |
|
|
|
803 |
if (spell->lang) { |
804 |
g_free(spell->lang); |
805 |
} |
806 |
|
710 |
g_signal_handlers_disconnect_matched(spell->view, |
807 |
g_signal_handlers_disconnect_matched(spell->view, |
711 |
G_SIGNAL_MATCH_DATA, |
808 |
G_SIGNAL_MATCH_DATA, |
712 |
0, 0, NULL, NULL, |
809 |
0, 0, NULL, NULL, |