~bcurtiswx/ubuntu/precise/empathy/3.4.2.1-0ubuntu1

« back to all changes in this revision

Viewing changes to libempathy-gtk/gossip-spell.c

  • Committer: Bazaar Package Importer
  • Author(s): Sjoerd Simons
  • Date: 2007-05-20 15:31:42 UTC
  • Revision ID: james.westby@ubuntu.com-20070520153142-r3auwguxdgxhktqb
Tags: upstream-0.4
ImportĀ upstreamĀ versionĀ 0.4

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
 
2
/*
 
3
 * Copyright (C) 2004-2007 Imendio AB
 
4
 *
 
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.
 
9
 *
 
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.
 
14
 *
 
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.
 
19
 *
 
20
 * Authors: Martyn Russell <martyn@imendio.com>
 
21
 *          Richard Hult <richard@imendio.com>
 
22
 */
 
23
 
 
24
#include "config.h"
 
25
 
 
26
#include <string.h>
 
27
#include <stdlib.h>
 
28
 
 
29
#include <glib/gi18n.h>
 
30
 
 
31
#ifdef HAVE_ASPELL
 
32
#include <aspell.h>
 
33
#endif
 
34
 
 
35
#include <libempathy/gossip-debug.h>
 
36
#include <libempathy/gossip-conf.h>
 
37
 
 
38
#include "gossip-spell.h"
 
39
#include "gossip-preferences.h"
 
40
 
 
41
#define DEBUG_DOMAIN "Spell"
 
42
 
 
43
#ifdef HAVE_ASPELL
 
44
 
 
45
/* Note: We could use aspell_reset_cache (NULL); periodically if we wanted
 
46
 * to...
 
47
 */
 
48
 
 
49
typedef struct {
 
50
        AspellConfig       *spell_config;
 
51
        AspellCanHaveError *spell_possible_err;
 
52
        AspellSpeller      *spell_checker;
 
53
} SpellLanguage;
 
54
 
 
55
#define ISO_CODES_DATADIR    ISO_CODES_PREFIX "/share/xml/iso-codes"
 
56
#define ISO_CODES_LOCALESDIR ISO_CODES_PREFIX "/share/locale"
 
57
 
 
58
static GHashTable  *iso_code_names = NULL;
 
59
static GList       *languages = NULL;
 
60
static gboolean     gossip_conf_notify_inited = FALSE;
 
61
 
 
62
static void
 
63
spell_iso_codes_parse_start_tag (GMarkupParseContext  *ctx,
 
64
                                 const gchar          *element_name,
 
65
                                 const gchar         **attr_names,
 
66
                                 const gchar         **attr_values,
 
67
                                 gpointer              data,
 
68
                                 GError              **error)
 
69
{
 
70
        const gchar *ccode_longB, *ccode_longT, *ccode;
 
71
        const gchar *lang_name;
 
72
 
 
73
        if (!g_str_equal (element_name, "iso_639_entry") ||
 
74
            attr_names == NULL || attr_values == NULL) {
 
75
                return;
 
76
        }
 
77
 
 
78
        ccode = NULL;
 
79
        ccode_longB = NULL;
 
80
        ccode_longT = NULL;
 
81
        lang_name = NULL;
 
82
 
 
83
        while (*attr_names && *attr_values) {
 
84
                if (g_str_equal (*attr_names, "iso_639_1_code")) {
 
85
                        if (**attr_values) {
 
86
                                ccode = *attr_values;
 
87
                        }
 
88
                }
 
89
                else if (g_str_equal (*attr_names, "iso_639_2B_code")) {
 
90
                        if (**attr_values) {
 
91
                                ccode_longB = *attr_values;
 
92
                        }
 
93
                }
 
94
                else if (g_str_equal (*attr_names, "iso_639_2T_code")) {
 
95
                        if (**attr_values) {
 
96
                                ccode_longT = *attr_values;
 
97
                        }
 
98
                }
 
99
                else if (g_str_equal (*attr_names, "name")) {
 
100
                        lang_name = *attr_values;
 
101
                }
 
102
 
 
103
                attr_names++;
 
104
                attr_values++;
 
105
        }
 
106
 
 
107
        if (!lang_name) {
 
108
                return;
 
109
        }
 
110
 
 
111
        if (ccode) {
 
112
                g_hash_table_insert (iso_code_names,
 
113
                                     g_strdup (ccode),
 
114
                                     g_strdup (lang_name));
 
115
        }
 
116
 
 
117
        if (ccode_longB) {
 
118
                g_hash_table_insert (iso_code_names,
 
119
                                     g_strdup (ccode_longB),
 
120
                                     g_strdup (lang_name));
 
121
        }
 
122
 
 
123
        if (ccode_longT) {
 
124
                g_hash_table_insert (iso_code_names,
 
125
                                     g_strdup (ccode_longT),
 
126
                                     g_strdup (lang_name));
 
127
        }
 
128
}
 
129
 
 
130
static void
 
131
spell_iso_code_names_init (void)
 
132
{
 
133
        GError *err = NULL;
 
134
        gchar  *buf;
 
135
        gsize   buf_len;
 
136
 
 
137
        iso_code_names = g_hash_table_new_full (g_str_hash, g_str_equal,
 
138
                                                g_free, g_free);
 
139
 
 
140
        bindtextdomain ("iso_639", ISO_CODES_LOCALESDIR);
 
141
        bind_textdomain_codeset ("iso_639", "UTF-8");
 
142
 
 
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
 
149
                };
 
150
 
 
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",
 
155
                                   err->message);
 
156
                        g_error_free (err);
 
157
                }
 
158
 
 
159
                g_markup_parse_context_free (ctx);
 
160
                g_free (buf);
 
161
        } else {
 
162
                g_warning ("Failed to load '%s': %s",
 
163
                                ISO_CODES_DATADIR"/iso_639.xml", err->message);
 
164
                g_error_free (err);
 
165
        }
 
166
}
 
167
 
 
168
static void
 
169
spell_notify_languages_cb (GossipConf  *conf,
 
170
                           const gchar *key,
 
171
                           gpointer     user_data)
 
172
{
 
173
        GList *l;
 
174
 
 
175
        gossip_debug (DEBUG_DOMAIN, "Resetting languages due to config change");
 
176
 
 
177
        /* We just reset the languages list. */
 
178
        for (l = languages; l; l = l->next) {
 
179
                SpellLanguage *lang;
 
180
 
 
181
                lang = l->data;
 
182
 
 
183
                delete_aspell_config (lang->spell_config);
 
184
                delete_aspell_speller (lang->spell_checker);
 
185
 
 
186
                g_slice_free (SpellLanguage, lang);
 
187
        }
 
188
 
 
189
        g_list_free (languages);
 
190
        languages = NULL;
 
191
}
 
192
 
 
193
static void
 
194
spell_setup_languages (void)
 
195
{
 
196
        gchar  *str;
 
197
 
 
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);
 
202
 
 
203
                gossip_conf_notify_inited = TRUE;
 
204
        }
 
205
 
 
206
        if (languages) {
 
207
                gossip_debug (DEBUG_DOMAIN, "No languages to setup");
 
208
                return;
 
209
        }
 
210
 
 
211
        if (gossip_conf_get_string (gossip_conf_get (),
 
212
                                     GOSSIP_PREFS_CHAT_SPELL_CHECKER_LANGUAGES,
 
213
                                     &str) && str) {
 
214
                gchar **strv;
 
215
                gint    i;
 
216
 
 
217
                strv = g_strsplit (str, ",", -1);
 
218
 
 
219
                i = 0;
 
220
                while (strv && strv[i]) {
 
221
                        SpellLanguage *lang;
 
222
 
 
223
                        gossip_debug (DEBUG_DOMAIN, "Setting up language:'%s'", strv[i]);
 
224
 
 
225
                        lang = g_slice_new0 (SpellLanguage);
 
226
 
 
227
                        lang->spell_config = new_aspell_config();
 
228
 
 
229
                        aspell_config_replace (lang->spell_config, "encoding", "utf-8");
 
230
                        aspell_config_replace (lang->spell_config, "lang", strv[i++]);
 
231
 
 
232
                        lang->spell_possible_err = new_aspell_speller (lang->spell_config);
 
233
 
 
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);
 
237
                        } else {
 
238
                                delete_aspell_config (lang->spell_config);
 
239
                                g_slice_free (SpellLanguage, lang);
 
240
                        }
 
241
                }
 
242
 
 
243
                if (strv) {
 
244
                        g_strfreev (strv);
 
245
                }
 
246
 
 
247
                g_free (str);
 
248
        }
 
249
}
 
250
 
 
251
const char *
 
252
gossip_spell_get_language_name (const char *code)
 
253
{
 
254
        const gchar *name;
 
255
 
 
256
        g_return_val_if_fail (code != NULL, NULL);
 
257
 
 
258
        if (!iso_code_names) {
 
259
                spell_iso_code_names_init ();
 
260
        }
 
261
 
 
262
        name = g_hash_table_lookup (iso_code_names, code);
 
263
        if (!name) {
 
264
                return NULL;
 
265
        }
 
266
 
 
267
        return dgettext ("iso_639", name);
 
268
}
 
269
 
 
270
GList *
 
271
gossip_spell_get_language_codes (void)
 
272
{
 
273
        AspellConfig              *config;
 
274
        AspellDictInfoList        *dlist;
 
275
        AspellDictInfoEnumeration *dels;
 
276
        const AspellDictInfo      *entry;
 
277
        GList                     *codes = NULL;
 
278
 
 
279
        config = new_aspell_config ();
 
280
        dlist = get_aspell_dict_info_list (config);
 
281
        dels = aspell_dict_info_list_elements (dlist);
 
282
 
 
283
        while ((entry = aspell_dict_info_enumeration_next (dels)) != 0) {
 
284
                if (g_list_find_custom (codes, entry->code, (GCompareFunc) strcmp)) {
 
285
                        continue;
 
286
                }
 
287
 
 
288
                codes = g_list_append (codes, g_strdup (entry->code));
 
289
        }
 
290
 
 
291
        delete_aspell_dict_info_enumeration (dels);
 
292
        delete_aspell_config (config);
 
293
 
 
294
        return codes;
 
295
}
 
296
 
 
297
void
 
298
gossip_spell_free_language_codes (GList *codes)
 
299
{
 
300
        g_list_foreach (codes, (GFunc) g_free, NULL);
 
301
        g_list_free (codes);
 
302
}
 
303
 
 
304
gboolean
 
305
gossip_spell_check (const gchar *word)
 
306
{
 
307
        GList       *l;
 
308
        gint         n_langs;
 
309
        gboolean     correct = FALSE;
 
310
        gint         len;
 
311
        const gchar *p;
 
312
        gunichar     c;
 
313
        gboolean     digit;
 
314
 
 
315
        g_return_val_if_fail (word != NULL, FALSE);
 
316
 
 
317
        spell_setup_languages ();
 
318
 
 
319
        if (!languages) {
 
320
                gossip_debug (DEBUG_DOMAIN, "No languages to check against");
 
321
                return TRUE;
 
322
        }
 
323
 
 
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);
 
328
        }
 
329
 
 
330
        if (digit) {
 
331
                /* We don't spell check digits. */
 
332
                gossip_debug (DEBUG_DOMAIN, "Not spell checking word:'%s', it is all digits", word);
 
333
                return TRUE;
 
334
        }
 
335
 
 
336
        len = strlen (word);
 
337
        n_langs = g_list_length (languages);
 
338
        for (l = languages; l; l = l->next) {
 
339
                SpellLanguage *lang;
 
340
 
 
341
                lang = l->data;
 
342
 
 
343
                correct = aspell_speller_check (lang->spell_checker, word, len);
 
344
                if (n_langs > 1 && correct) {
 
345
                        break;
 
346
                }
 
347
        }
 
348
 
 
349
        return correct;
 
350
}
 
351
 
 
352
GList *
 
353
gossip_spell_get_suggestions (const gchar *word)
 
354
{
 
355
        GList                   *l1;
 
356
        GList                   *l2 = NULL;
 
357
        const AspellWordList    *suggestions;
 
358
        AspellStringEnumeration *elements;
 
359
        const char              *next;
 
360
        gint                     len;
 
361
 
 
362
        g_return_val_if_fail (word != NULL, NULL);
 
363
 
 
364
        spell_setup_languages ();
 
365
 
 
366
        len = strlen (word);
 
367
 
 
368
        for (l1 = languages; l1; l1 = l1->next) {
 
369
                SpellLanguage *lang;
 
370
 
 
371
                lang = l1->data;
 
372
 
 
373
                suggestions = aspell_speller_suggest (lang->spell_checker,
 
374
                                                      word, len);
 
375
 
 
376
                elements = aspell_word_list_elements (suggestions);
 
377
 
 
378
                while ((next = aspell_string_enumeration_next (elements))) {
 
379
                        l2 = g_list_append (l2, g_strdup (next));
 
380
                }
 
381
 
 
382
                delete_aspell_string_enumeration (elements);
 
383
        }
 
384
 
 
385
        return l2;
 
386
}
 
387
 
 
388
gboolean
 
389
gossip_spell_supported (void)
 
390
{
 
391
        if (g_getenv ("GOSSIP_SPELL_DISABLED")) {
 
392
                gossip_debug (DEBUG_DOMAIN, "GOSSIP_SPELL_DISABLE env variable defined");
 
393
                return FALSE;
 
394
        }
 
395
 
 
396
        gossip_debug (DEBUG_DOMAIN, "Support enabled");
 
397
 
 
398
        return TRUE;
 
399
}
 
400
 
 
401
#else /* not HAVE_ASPELL */
 
402
 
 
403
gboolean
 
404
gossip_spell_supported (void)
 
405
{
 
406
        gossip_debug (DEBUG_DOMAIN, "Support disabled");
 
407
 
 
408
        return FALSE;
 
409
}
 
410
 
 
411
GList *
 
412
gossip_spell_get_suggestions (const gchar *word)
 
413
{
 
414
        gossip_debug (DEBUG_DOMAIN, "Support disabled, could not get suggestions");
 
415
 
 
416
        return NULL;
 
417
}
 
418
 
 
419
gboolean
 
420
gossip_spell_check (const gchar *word)
 
421
{
 
422
        gossip_debug (DEBUG_DOMAIN, "Support disabled, could not check spelling");
 
423
 
 
424
        return TRUE;
 
425
}
 
426
 
 
427
const char *
 
428
gossip_spell_get_language_name (const char *lang)
 
429
{
 
430
        gossip_debug (DEBUG_DOMAIN, "Support disabled, could not get language name");
 
431
 
 
432
        return NULL;
 
433
}
 
434
 
 
435
GList *
 
436
gossip_spell_get_language_codes (void)
 
437
{
 
438
        gossip_debug (DEBUG_DOMAIN, "Support disabled, could not get language codes");
 
439
 
 
440
        return NULL;
 
441
}
 
442
 
 
443
void
 
444
gossip_spell_free_language_codes (GList *codes)
 
445
{
 
446
}
 
447
 
 
448
#endif /* HAVE_ASPELL */
 
449
 
 
450
 
 
451
void
 
452
gossip_spell_free_suggestions (GList *suggestions)
 
453
{
 
454
        g_list_foreach (suggestions, (GFunc) g_free, NULL);
 
455
        g_list_free (suggestions);
 
456
}
 
457