1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
3 * Copyright (C) 2004-2007 Imendio AB
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License as
7 * published by the Free Software Foundation; either version 2 of the
8 * License, or (at your option) any later version.
10 * This program 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 * General Public License for more details.
15 * You should have received a copy of the GNU General Public
16 * License along with this program; if not, write to the
17 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 * Boston, MA 02111-1307, USA.
20 * Authors: Martyn Russell <martyn@imendio.com>
21 * Richard Hult <richard@imendio.com>
29 #include <glib/gi18n.h>
35 #include <libempathy/empathy-debug.h>
36 #include <libempathy/empathy-conf.h>
38 #include "empathy-spell.h"
39 #include "empathy-preferences.h"
41 #define DEBUG_DOMAIN "Spell"
45 /* Note: We could use aspell_reset_cache (NULL); periodically if we wanted
50 AspellConfig *spell_config;
51 AspellCanHaveError *spell_possible_err;
52 AspellSpeller *spell_checker;
55 #define ISO_CODES_DATADIR ISO_CODES_PREFIX "/share/xml/iso-codes"
56 #define ISO_CODES_LOCALESDIR ISO_CODES_PREFIX "/share/locale"
58 static GHashTable *iso_code_names = NULL;
59 static GList *languages = NULL;
60 static gboolean empathy_conf_notify_inited = FALSE;
63 spell_iso_codes_parse_start_tag (GMarkupParseContext *ctx,
64 const gchar *element_name,
65 const gchar **attr_names,
66 const gchar **attr_values,
70 const gchar *ccode_longB, *ccode_longT, *ccode;
71 const gchar *lang_name;
73 if (!g_str_equal (element_name, "iso_639_entry") ||
74 attr_names == NULL || attr_values == NULL) {
83 while (*attr_names && *attr_values) {
84 if (g_str_equal (*attr_names, "iso_639_1_code")) {
89 else if (g_str_equal (*attr_names, "iso_639_2B_code")) {
91 ccode_longB = *attr_values;
94 else if (g_str_equal (*attr_names, "iso_639_2T_code")) {
96 ccode_longT = *attr_values;
99 else if (g_str_equal (*attr_names, "name")) {
100 lang_name = *attr_values;
112 g_hash_table_insert (iso_code_names,
114 g_strdup (lang_name));
118 g_hash_table_insert (iso_code_names,
119 g_strdup (ccode_longB),
120 g_strdup (lang_name));
124 g_hash_table_insert (iso_code_names,
125 g_strdup (ccode_longT),
126 g_strdup (lang_name));
131 spell_iso_code_names_init (void)
137 iso_code_names = g_hash_table_new_full (g_str_hash, g_str_equal,
140 bindtextdomain ("iso_639", ISO_CODES_LOCALESDIR);
141 bind_textdomain_codeset ("iso_639", "UTF-8");
143 /* FIXME: We should read this in chunks and pass to the parser. */
144 if (g_file_get_contents (ISO_CODES_DATADIR "/iso_639.xml", &buf, &buf_len, &err)) {
145 GMarkupParseContext *ctx;
146 GMarkupParser parser = {
147 spell_iso_codes_parse_start_tag,
148 NULL, NULL, NULL, NULL
151 ctx = g_markup_parse_context_new (&parser, 0, NULL, NULL);
152 if (!g_markup_parse_context_parse (ctx, buf, buf_len, &err)) {
153 g_warning ("Failed to parse '%s': %s",
154 ISO_CODES_DATADIR"/iso_639.xml",
159 g_markup_parse_context_free (ctx);
162 g_warning ("Failed to load '%s': %s",
163 ISO_CODES_DATADIR"/iso_639.xml", err->message);
169 spell_notify_languages_cb (EmpathyConf *conf,
175 empathy_debug (DEBUG_DOMAIN, "Resetting languages due to config change");
177 /* We just reset the languages list. */
178 for (l = languages; l; l = l->next) {
183 delete_aspell_config (lang->spell_config);
184 delete_aspell_speller (lang->spell_checker);
186 g_slice_free (SpellLanguage, lang);
189 g_list_free (languages);
194 spell_setup_languages (void)
198 if (!empathy_conf_notify_inited) {
199 empathy_conf_notify_add (empathy_conf_get (),
200 EMPATHY_PREFS_CHAT_SPELL_CHECKER_LANGUAGES,
201 spell_notify_languages_cb, NULL);
203 empathy_conf_notify_inited = TRUE;
210 if (empathy_conf_get_string (empathy_conf_get (),
211 EMPATHY_PREFS_CHAT_SPELL_CHECKER_LANGUAGES,
216 strv = g_strsplit (str, ",", -1);
219 while (strv && strv[i]) {
222 empathy_debug (DEBUG_DOMAIN, "Setting up language:'%s'", strv[i]);
224 lang = g_slice_new0 (SpellLanguage);
226 lang->spell_config = new_aspell_config();
228 aspell_config_replace (lang->spell_config, "encoding", "utf-8");
229 aspell_config_replace (lang->spell_config, "lang", strv[i++]);
231 lang->spell_possible_err = new_aspell_speller (lang->spell_config);
233 if (aspell_error_number (lang->spell_possible_err) == 0) {
234 lang->spell_checker = to_aspell_speller (lang->spell_possible_err);
235 languages = g_list_append (languages, lang);
237 delete_aspell_config (lang->spell_config);
238 g_slice_free (SpellLanguage, lang);
251 empathy_spell_get_language_name (const char *code)
255 g_return_val_if_fail (code != NULL, NULL);
257 if (!iso_code_names) {
258 spell_iso_code_names_init ();
261 name = g_hash_table_lookup (iso_code_names, code);
266 return dgettext ("iso_639", name);
270 empathy_spell_get_language_codes (void)
272 AspellConfig *config;
273 AspellDictInfoList *dlist;
274 AspellDictInfoEnumeration *dels;
275 const AspellDictInfo *entry;
278 config = new_aspell_config ();
279 dlist = get_aspell_dict_info_list (config);
280 dels = aspell_dict_info_list_elements (dlist);
282 while ((entry = aspell_dict_info_enumeration_next (dels)) != 0) {
283 if (g_list_find_custom (codes, entry->code, (GCompareFunc) strcmp)) {
287 codes = g_list_append (codes, g_strdup (entry->code));
290 delete_aspell_dict_info_enumeration (dels);
291 delete_aspell_config (config);
297 empathy_spell_free_language_codes (GList *codes)
299 g_list_foreach (codes, (GFunc) g_free, NULL);
304 empathy_spell_check (const gchar *word)
308 gboolean correct = FALSE;
314 g_return_val_if_fail (word != NULL, FALSE);
316 spell_setup_languages ();
319 empathy_debug (DEBUG_DOMAIN, "No languages to check against");
323 /* Ignore certain cases like numbers, etc. */
324 for (p = word, digit = TRUE; *p && digit; p = g_utf8_next_char (p)) {
325 c = g_utf8_get_char (p);
326 digit = g_unichar_isdigit (c);
330 /* We don't spell check digits. */
331 empathy_debug (DEBUG_DOMAIN, "Not spell checking word:'%s', it is all digits", word);
336 n_langs = g_list_length (languages);
337 for (l = languages; l; l = l->next) {
342 correct = aspell_speller_check (lang->spell_checker, word, len);
343 if (n_langs > 1 && correct) {
352 empathy_spell_get_suggestions (const gchar *word)
356 const AspellWordList *suggestions;
357 AspellStringEnumeration *elements;
361 g_return_val_if_fail (word != NULL, NULL);
363 spell_setup_languages ();
367 for (l1 = languages; l1; l1 = l1->next) {
372 suggestions = aspell_speller_suggest (lang->spell_checker,
375 elements = aspell_word_list_elements (suggestions);
377 while ((next = aspell_string_enumeration_next (elements))) {
378 l2 = g_list_append (l2, g_strdup (next));
381 delete_aspell_string_enumeration (elements);
388 empathy_spell_supported (void)
390 if (g_getenv ("EMPATHY_SPELL_DISABLED")) {
391 empathy_debug (DEBUG_DOMAIN, "EMPATHY_SPELL_DISABLE env variable defined");
398 #else /* not HAVE_ASPELL */
401 empathy_spell_supported (void)
407 empathy_spell_get_suggestions (const gchar *word)
409 empathy_debug (DEBUG_DOMAIN, "Support disabled, could not get suggestions");
415 empathy_spell_check (const gchar *word)
417 empathy_debug (DEBUG_DOMAIN, "Support disabled, could not check spelling");
423 empathy_spell_get_language_name (const char *lang)
425 empathy_debug (DEBUG_DOMAIN, "Support disabled, could not get language name");
431 empathy_spell_get_language_codes (void)
433 empathy_debug (DEBUG_DOMAIN, "Support disabled, could not get language codes");
439 empathy_spell_free_language_codes (GList *codes)
443 #endif /* HAVE_ASPELL */
447 empathy_spell_free_suggestions (GList *suggestions)
449 g_list_foreach (suggestions, (GFunc) g_free, NULL);
450 g_list_free (suggestions);