|
|
/* gtkspell - a spell-checking addon for GTK's TextView widget |
/* gtkspell |
* Copyright (c) 2002 Evan Martin. | * Copyright (c) 2002 Evan Martin. |
|
* Copyright (c) 2004 Nathan Fredrickson. |
|
* |
|
* This library is free software; you can redistribute it and/or |
|
* modify it under the terms of the GNU Lesser General Public |
|
* License as published by the Free Software Foundation; either |
|
* version 2.1 of the License, or (at your option) any later version. |
|
* |
|
* This library is distributed in the hope that it will be useful, |
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
|
* Lesser General Public License for more details. |
|
* |
|
* You should have received a copy of the GNU Lesser General Public |
|
* License along with this library; if not, write to the |
|
* Free Software Foundation, Inc., 59 Temple Place - Suite 330, |
|
* Boston, MA 02111-1307, USA. |
|
* |
*/ | */ |
| |
/* vim: set ts=4 sw=4 wm=5 : */ | /* vim: set ts=4 sw=4 wm=5 : */ |
|
|
#include <gtk/gtk.h> | #include <gtk/gtk.h> |
#include <libintl.h> | #include <libintl.h> |
#include <locale.h> | #include <locale.h> |
|
#include <enchant.h> |
#include "../config.h" | #include "../config.h" |
#include "gtkspell.h" | #include "gtkspell.h" |
| |
#define _(String) dgettext (PACKAGE, String) |
#define _(String) dgettext(PACKAGE, String) |
|
#define N_(String) (String) |
#ifdef HAVE_ASPELL_H |
|
#define USING_ASPELL |
|
#include <aspell.h> |
|
#elif defined HAVE_PSPELL_H |
|
#define USING_PSPELL |
|
#include <pspell/pspell.h> |
|
#define AspellSpeller PspellManager |
|
#define speller manager |
|
#define aspell_speller_check pspell_manager_check |
|
#define aspell_speller_add_to_session pspell_manager_add_to_session |
|
#define aspell_speller_add_to_personal pspell_manager_add_to_personal |
|
#define aspell_speller_save_all_word_lists pspell_manager_save_all_word_lists |
|
#define aspell_speller_store_replacement pspell_manager_store_replacement |
|
#define AspellWordList PspellWordList |
|
#define AspellStringEnumeration PspellStringEmulation |
|
#define aspell_speller_suggest pspell_manager_suggest |
|
#define aspell_word_list_elements pspell_word_list_elements |
|
#define aspell_string_enumeration_next pspell_string_emulation_next |
|
#define delete_aspell_string_enumeration delete_pspell_string_emulation |
|
#define AspellConfig PspellConfig |
|
#define AspellCanHaveError PspellCanHaveError |
|
#define new_aspell_config new_pspell_config |
|
#define aspell_config_replace pspell_config_replace |
|
#define new_aspell_speller new_pspell_manager |
|
#define delete_aspell_config delete_pspell_config |
|
#define aspell_error_message pspell_error_message |
|
#define delete_aspell_speller delete_pspell_manager |
|
#define to_aspell_speller to_pspell_manager |
|
#define aspell_error_number pspell_error_number |
|
#define aspell pspell |
|
#endif |
|
| |
const int debug = 0; | const int debug = 0; |
const int quiet = 0; | const int quiet = 0; |
|
|
GtkTextMark *mark_insert_start; | GtkTextMark *mark_insert_start; |
GtkTextMark *mark_insert_end; | GtkTextMark *mark_insert_end; |
gboolean deferred_check; | gboolean deferred_check; |
AspellSpeller *speller; |
EnchantBroker *broker; |
|
EnchantDict *speller; |
GtkTextMark *mark_click; | GtkTextMark *mark_click; |
|
gchar *lang; |
}; | }; |
| |
static void gtkspell_free(GtkSpell *spell); | static void gtkspell_free(GtkSpell *spell); |
|
|
check_word(GtkSpell *spell, GtkTextBuffer *buffer, | check_word(GtkSpell *spell, GtkTextBuffer *buffer, |
GtkTextIter *start, GtkTextIter *end) { | GtkTextIter *start, GtkTextIter *end) { |
char *text; | char *text; |
|
|
|
if (!spell->speller) |
|
return; |
|
|
text = gtk_text_buffer_get_text(buffer, start, end, FALSE); | text = gtk_text_buffer_get_text(buffer, start, end, FALSE); |
if (debug) g_print("checking: %s\n", text); | if (debug) g_print("checking: %s\n", text); |
if (g_unichar_isdigit(*text) == FALSE) /* don't check numbers */ | if (g_unichar_isdigit(*text) == FALSE) /* don't check numbers */ |
if (aspell_speller_check(spell->speller, text, -1) == FALSE) |
if (enchant_dict_check(spell->speller, text, strlen(text)) != 0) |
gtk_text_buffer_apply_tag(buffer, spell->tag_highlight, start, end); | gtk_text_buffer_apply_tag(buffer, spell->tag_highlight, start, end); |
g_free(text); | g_free(text); |
} | } |
|
Lines 295-307
add_to_dictionary(GtkWidget *menuitem, G
|
Link Here
|
|---|
|
GtkTextIter start, end; | GtkTextIter start, end; |
GtkTextBuffer *buffer; | GtkTextBuffer *buffer; |
| |
|
if (!spell->speller) |
|
return; |
|
|
buffer = gtk_text_view_get_buffer(spell->view); | buffer = gtk_text_view_get_buffer(spell->view); |
| |
get_word_extents_from_mark(buffer, &start, &end, spell->mark_click); | get_word_extents_from_mark(buffer, &start, &end, spell->mark_click); |
word = gtk_text_buffer_get_text(buffer, &start, &end, FALSE); | word = gtk_text_buffer_get_text(buffer, &start, &end, FALSE); |
|
|
aspell_speller_add_to_personal(spell->speller, word, strlen(word)); |
enchant_dict_add_to_personal(spell->speller, word, strlen(word)); |
aspell_speller_save_all_word_lists(spell->speller); |
|
| |
gtkspell_recheck_all(spell); | gtkspell_recheck_all(spell); |
| |
|
Lines 319-325
ignore_all(GtkWidget *menuitem, GtkSpell
|
Link Here
|
|---|
|
get_word_extents_from_mark(buffer, &start, &end, spell->mark_click); | get_word_extents_from_mark(buffer, &start, &end, spell->mark_click); |
word = gtk_text_buffer_get_text(buffer, &start, &end, FALSE); | word = gtk_text_buffer_get_text(buffer, &start, &end, FALSE); |
| |
aspell_speller_add_to_session(spell->speller, word, strlen(word)); |
enchant_dict_add_to_session(spell->speller, word, strlen(word)); |
| |
gtkspell_recheck_all(spell); | gtkspell_recheck_all(spell); |
| |
|
Lines 332-337
replace_word(GtkWidget *menuitem, GtkSpe
|
Link Here
|
|---|
|
const char *newword; | const char *newword; |
GtkTextIter start, end; | GtkTextIter start, end; |
GtkTextBuffer *buffer; | GtkTextBuffer *buffer; |
|
|
|
if (!spell->speller) |
|
return; |
| |
buffer = gtk_text_view_get_buffer(spell->view); | buffer = gtk_text_view_get_buffer(spell->view); |
| |
|
Lines 348-380
replace_word(GtkWidget *menuitem, GtkSpe
|
Link Here
|
|---|
|
gtk_text_buffer_delete(buffer, &start, &end); | gtk_text_buffer_delete(buffer, &start, &end); |
gtk_text_buffer_insert(buffer, &start, newword, -1); | gtk_text_buffer_insert(buffer, &start, newword, -1); |
| |
aspell_speller_store_replacement(spell->speller, |
enchant_dict_store_replacement(spell->speller, |
oldword, strlen(oldword), |
oldword, strlen(oldword), |
newword, strlen(newword)); |
newword, strlen(newword)); |
| |
g_free(oldword); | g_free(oldword); |
} | } |
| |
GtkWidget* |
#define KNOWN_LANGUAGES 39 |
|
static gchar *known_languages [KNOWN_LANGUAGES * 2 + 1] = { |
|
"br", N_("Breton"), |
|
"ca", N_("Catalan"), |
|
"cs", N_("Czech"), |
|
"da", N_("Danish"), |
|
"de-DE", N_("German (Germany)"), |
|
"de-AT", N_("German (Austria)"), |
|
"de-CH", N_("German (Swiss)"), |
|
"el", N_("Greek"), |
|
"en", N_("English"), |
|
"en-US", N_("English (American)"), |
|
"en-GB", N_("English (British)"), |
|
"en-CA", N_("English (Canadian)"), |
|
"eo", N_("Esperanto"), |
|
"es", N_("Spanish"), |
|
"fi", N_("Finnish"), |
|
"fo", N_("Faroese"), |
|
"fr-FR", N_("French (France)"), |
|
"fr-CH", N_("French (Swiss)"), |
|
"hu", N_("Hungarian"), |
|
"ga", N_("Irish"), |
|
"gl", N_("Galician"), |
|
"he", N_("Hebrew"), |
|
"it", N_("Italian"), |
|
"la", N_("Latin"), |
|
"lt", N_("Lithuanian"), |
|
"nb-NO", N_("Norwegian (Bokmal)"), |
|
"nl", N_("Dutch"), |
|
"nn-NO", N_("Norwegian (Nyorsk)"), |
|
"no", N_("Norwegian"), |
|
"pl", N_("Polish"), |
|
"pt-PT", N_("Portuguese (Portugal)"), |
|
"pt-BR", N_("Portuguese (Brazilian)"), |
|
"ru", N_("Russian"), |
|
"sc", N_("Sardinian"), |
|
"sk", N_("Slovak"), |
|
"sl", N_("Slovenian"), |
|
"sv", N_("Swedish"), |
|
"uk", N_("Ukrainian"), |
|
"yi", N_("Yiddish"), |
|
NULL |
|
}; |
|
|
|
static void |
|
dict_describe (const char * const lang_tag, |
|
const char * const provider_name, |
|
const char * const provider_desc, |
|
const char * const provider_file, |
|
void * user_data) { |
|
printf("%s | %s | %s | %s\n", lang_tag, provider_name, provider_desc, provider_file); |
|
} |
|
|
|
/* replace any 1 hyphen with an underscore. converts en-US to en_US */ |
|
static gchar * |
|
normalize_language (const gchar *language) { |
|
gchar *dup, *hyphen; |
|
|
|
dup = g_strdup(language); |
|
hyphen = strchr(dup, '-'); |
|
if (hyphen) |
|
*hyphen = '_'; |
|
return dup; |
|
} |
|
|
|
static GSList * |
|
get_languages (GtkSpell *spell) { |
|
EnchantBroker *broker = spell->broker; |
|
gchar * lang; |
|
|
|
GSList *langs; |
|
gint i; |
|
|
|
langs = NULL; |
|
|
|
for (i=0; known_languages[i]; i++) { |
|
lang = normalize_language(known_languages [i]); |
|
i++; |
|
|
|
if (enchant_broker_dict_exists(spell->broker, lang)) { |
|
/* |
|
enchant_dict_describe(spell->speller, dict_describe, NULL); |
|
printf("Language: %s (%s)\n", known_languages[i], lang); |
|
*/ |
|
langs = g_slist_append(langs, GINT_TO_POINTER(i - 1)); |
|
} |
|
|
|
g_free(lang); |
|
} |
|
|
|
return langs; |
|
} |
|
|
|
static GtkWidget* |
build_suggestion_menu(GtkSpell *spell, GtkTextBuffer *buffer, | build_suggestion_menu(GtkSpell *spell, GtkTextBuffer *buffer, |
const char *word) { | const char *word) { |
const char *suggestion; | const char *suggestion; |
GtkWidget *topmenu, *menu; | GtkWidget *topmenu, *menu; |
GtkWidget *mi; | GtkWidget *mi; |
GtkWidget *hbox; | GtkWidget *hbox; |
int count = 0; |
|
void *spelldata; | void *spelldata; |
const AspellWordList *suggestions; |
char ** suggestions; |
AspellStringEnumeration *elements; |
size_t n_suggs, i; |
char *label; | char *label; |
| |
topmenu = menu = gtk_menu_new(); | topmenu = menu = gtk_menu_new(); |
| |
suggestions = aspell_speller_suggest(spell->speller, word, -1); |
if (!spell->speller) |
elements = aspell_word_list_elements(suggestions); |
return topmenu; |
| |
suggestion = aspell_string_enumeration_next(elements); |
suggestions = enchant_dict_suggest(spell->speller, word, strlen(word), &n_suggs); |
if (suggestion == NULL) { |
|
|
if (suggestions == NULL || !n_suggs) { |
/* no suggestions. put something in the menu anyway... */ | /* no suggestions. put something in the menu anyway... */ |
GtkWidget *label; | GtkWidget *label; |
label = gtk_label_new(""); | label = gtk_label_new(""); |
|
Lines 386-393
build_suggestion_menu(GtkSpell *spell, G
|
Link Here
|
|---|
|
gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), mi); | gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), mi); |
} else { | } else { |
/* build a set of menus with suggestions. */ | /* build a set of menus with suggestions. */ |
while (suggestion != NULL) { |
for (i = 0; i < n_suggs; i++) { |
if (count == 10) { |
if (i != 0 && i % 10 == 0) { |
mi = gtk_menu_item_new(); | mi = gtk_menu_item_new(); |
gtk_widget_show(mi); | gtk_widget_show(mi); |
gtk_menu_shell_append(GTK_MENU_SHELL(menu), mi); | gtk_menu_shell_append(GTK_MENU_SHELL(menu), mi); |
|
Lines 398-417
build_suggestion_menu(GtkSpell *spell, G
|
Link Here
|
|---|
|
| |
menu = gtk_menu_new(); | menu = gtk_menu_new(); |
gtk_menu_item_set_submenu(GTK_MENU_ITEM(mi), menu); | gtk_menu_item_set_submenu(GTK_MENU_ITEM(mi), menu); |
count = 0; |
|
} | } |
mi = gtk_menu_item_new_with_label(suggestion); |
|
|
mi = gtk_menu_item_new_with_label(suggestions[i]); |
g_signal_connect(G_OBJECT(mi), "activate", | g_signal_connect(G_OBJECT(mi), "activate", |
G_CALLBACK(replace_word), spell); |
G_CALLBACK(replace_word), spell); |
gtk_widget_show(mi); | gtk_widget_show(mi); |
gtk_menu_shell_append(GTK_MENU_SHELL(menu), mi); | gtk_menu_shell_append(GTK_MENU_SHELL(menu), mi); |
count++; |
|
suggestion = aspell_string_enumeration_next(elements); |
|
} | } |
} | } |
| |
delete_aspell_string_enumeration(elements); |
|
|
|
/* Separator */ | /* Separator */ |
mi = gtk_menu_item_new(); | mi = gtk_menu_item_new(); |
gtk_widget_show(mi); | gtk_widget_show(mi); |
|
Lines 437-442
build_suggestion_menu(GtkSpell *spell, G
|
Link Here
|
|---|
|
gtk_widget_show_all(mi); | gtk_widget_show_all(mi); |
gtk_menu_shell_append(GTK_MENU_SHELL(topmenu), mi); | gtk_menu_shell_append(GTK_MENU_SHELL(topmenu), mi); |
| |
|
enchant_dict_free_suggestions(spell->speller, suggestions); |
|
|
return topmenu; | return topmenu; |
} | } |
| |
|
Lines 515-523
popup_menu_event(GtkTextView *view, GtkS
|
Link Here
|
|---|
|
| |
static gboolean | static gboolean |
gtkspell_set_language_internal(GtkSpell *spell, const gchar *lang, GError **error) { | gtkspell_set_language_internal(GtkSpell *spell, const gchar *lang, GError **error) { |
AspellConfig *config; |
|
AspellCanHaveError *err; |
|
AspellSpeller *speller; |
|
| |
if (lang == NULL) { | if (lang == NULL) { |
lang = g_getenv("LANG"); | lang = g_getenv("LANG"); |
|
Lines 529-554
gtkspell_set_language_internal(GtkSpell
|
Link Here
|
|---|
|
} | } |
} | } |
| |
config = new_aspell_config(); |
if (!spell->broker) |
if (lang) |
spell->broker = enchant_broker_init(); |
aspell_config_replace(config, "language-tag", lang); |
|
aspell_config_replace(config, "encoding", "utf-8"); |
|
err = new_aspell_speller(config); |
|
delete_aspell_config(config); |
|
| |
if (aspell_error_number(err) != 0) { |
if (spell->speller) { |
#ifdef USING_ASPELL |
enchant_broker_free_dict(spell->broker, spell->speller); |
g_set_error(error, GTKSPELL_ERROR, GTKSPELL_ERROR_BACKEND, |
spell->speller = NULL; |
"aspell: %s", aspell_error_message(err)); |
} |
#elif defined USING_PSPELL |
|
|
if (!lang) |
|
lang = "en"; |
|
|
|
spell->speller = enchant_broker_request_dict (spell->broker, lang); |
|
if (!spell->speller) { |
g_set_error(error, GTKSPELL_ERROR, GTKSPELL_ERROR_BACKEND, | g_set_error(error, GTKSPELL_ERROR, GTKSPELL_ERROR_BACKEND, |
"pspell: %s", aspell_error_message(err)); |
_("enchant error for language: %s"), lang); |
#endif |
|
return FALSE; | return FALSE; |
} |
} |
if (spell->speller) |
|
delete_aspell_speller(spell->speller); |
if (spell->lang) |
spell->speller = to_aspell_speller(err); |
g_free(spell->lang); |
|
|
|
spell->lang = g_strdup(lang); |
| |
return TRUE; | return TRUE; |
} | } |
|
Lines 705-712
gtkspell_free(GtkSpell *spell) {
|
Link Here
|
|---|
|
gtk_text_buffer_delete_mark(buffer, spell->mark_insert_end); | gtk_text_buffer_delete_mark(buffer, spell->mark_insert_end); |
gtk_text_buffer_delete_mark(buffer, spell->mark_click); | gtk_text_buffer_delete_mark(buffer, spell->mark_click); |
| |
delete_aspell_speller(spell->speller); |
if (spell->broker) { |
|
if (spell->speller) |
|
enchant_broker_free_dict (spell->broker, spell->speller); |
|
|
|
enchant_broker_free (spell->broker); |
|
} |
| |
|
if (spell->lang) { |
|
g_free(spell->lang); |
|
} |
|
|
g_signal_handlers_disconnect_matched(spell->view, | g_signal_handlers_disconnect_matched(spell->view, |
G_SIGNAL_MATCH_DATA, | G_SIGNAL_MATCH_DATA, |
0, 0, NULL, NULL, | 0, 0, NULL, NULL, |