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/gossip-debug.h>
36
#include <libempathy/gossip-conf.h>
38
#include "gossip-spell.h"
39
#include "gossip-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 gossip_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 (GossipConf *conf,
175
gossip_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 (!gossip_conf_notify_inited) {
199
gossip_conf_notify_add (gossip_conf_get (),
200
GOSSIP_PREFS_CHAT_SPELL_CHECKER_LANGUAGES,
201
spell_notify_languages_cb, NULL);
203
gossip_conf_notify_inited = TRUE;
207
gossip_debug (DEBUG_DOMAIN, "No languages to setup");
211
if (gossip_conf_get_string (gossip_conf_get (),
212
GOSSIP_PREFS_CHAT_SPELL_CHECKER_LANGUAGES,
217
strv = g_strsplit (str, ",", -1);
220
while (strv && strv[i]) {
223
gossip_debug (DEBUG_DOMAIN, "Setting up language:'%s'", strv[i]);
225
lang = g_slice_new0 (SpellLanguage);
227
lang->spell_config = new_aspell_config();
229
aspell_config_replace (lang->spell_config, "encoding", "utf-8");
230
aspell_config_replace (lang->spell_config, "lang", strv[i++]);
232
lang->spell_possible_err = new_aspell_speller (lang->spell_config);
234
if (aspell_error_number (lang->spell_possible_err) == 0) {
235
lang->spell_checker = to_aspell_speller (lang->spell_possible_err);
236
languages = g_list_append (languages, lang);
238
delete_aspell_config (lang->spell_config);
239
g_slice_free (SpellLanguage, lang);
252
gossip_spell_get_language_name (const char *code)
256
g_return_val_if_fail (code != NULL, NULL);
258
if (!iso_code_names) {
259
spell_iso_code_names_init ();
262
name = g_hash_table_lookup (iso_code_names, code);
267
return dgettext ("iso_639", name);
271
gossip_spell_get_language_codes (void)
273
AspellConfig *config;
274
AspellDictInfoList *dlist;
275
AspellDictInfoEnumeration *dels;
276
const AspellDictInfo *entry;
279
config = new_aspell_config ();
280
dlist = get_aspell_dict_info_list (config);
281
dels = aspell_dict_info_list_elements (dlist);
283
while ((entry = aspell_dict_info_enumeration_next (dels)) != 0) {
284
if (g_list_find_custom (codes, entry->code, (GCompareFunc) strcmp)) {
288
codes = g_list_append (codes, g_strdup (entry->code));
291
delete_aspell_dict_info_enumeration (dels);
292
delete_aspell_config (config);
298
gossip_spell_free_language_codes (GList *codes)
300
g_list_foreach (codes, (GFunc) g_free, NULL);
305
gossip_spell_check (const gchar *word)
309
gboolean correct = FALSE;
315
g_return_val_if_fail (word != NULL, FALSE);
317
spell_setup_languages ();
320
gossip_debug (DEBUG_DOMAIN, "No languages to check against");
324
/* Ignore certain cases like numbers, etc. */
325
for (p = word, digit = TRUE; *p && digit; p = g_utf8_next_char (p)) {
326
c = g_utf8_get_char (p);
327
digit = g_unichar_isdigit (c);
331
/* We don't spell check digits. */
332
gossip_debug (DEBUG_DOMAIN, "Not spell checking word:'%s', it is all digits", word);
337
n_langs = g_list_length (languages);
338
for (l = languages; l; l = l->next) {
343
correct = aspell_speller_check (lang->spell_checker, word, len);
344
if (n_langs > 1 && correct) {
353
gossip_spell_get_suggestions (const gchar *word)
357
const AspellWordList *suggestions;
358
AspellStringEnumeration *elements;
362
g_return_val_if_fail (word != NULL, NULL);
364
spell_setup_languages ();
368
for (l1 = languages; l1; l1 = l1->next) {
373
suggestions = aspell_speller_suggest (lang->spell_checker,
376
elements = aspell_word_list_elements (suggestions);
378
while ((next = aspell_string_enumeration_next (elements))) {
379
l2 = g_list_append (l2, g_strdup (next));
382
delete_aspell_string_enumeration (elements);
389
gossip_spell_supported (void)
391
if (g_getenv ("GOSSIP_SPELL_DISABLED")) {
392
gossip_debug (DEBUG_DOMAIN, "GOSSIP_SPELL_DISABLE env variable defined");
396
gossip_debug (DEBUG_DOMAIN, "Support enabled");
401
#else /* not HAVE_ASPELL */
404
gossip_spell_supported (void)
406
gossip_debug (DEBUG_DOMAIN, "Support disabled");
412
gossip_spell_get_suggestions (const gchar *word)
414
gossip_debug (DEBUG_DOMAIN, "Support disabled, could not get suggestions");
420
gossip_spell_check (const gchar *word)
422
gossip_debug (DEBUG_DOMAIN, "Support disabled, could not check spelling");
428
gossip_spell_get_language_name (const char *lang)
430
gossip_debug (DEBUG_DOMAIN, "Support disabled, could not get language name");
436
gossip_spell_get_language_codes (void)
438
gossip_debug (DEBUG_DOMAIN, "Support disabled, could not get language codes");
444
gossip_spell_free_language_codes (GList *codes)
448
#endif /* HAVE_ASPELL */
452
gossip_spell_free_suggestions (GList *suggestions)
454
g_list_foreach (suggestions, (GFunc) g_free, NULL);
455
g_list_free (suggestions);