Generate closures for src/
[empathy-mirror.git] / libempathy-gtk / empathy-spell.c
blobd097f1eeb93fb8f155bcc9e992ea6ddb9633963c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
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., 51 Franklin St, Fifth Floor,
18 * Boston, MA 02110-1301 USA
20 * Authors: Martyn Russell <martyn@imendio.com>
21 * Richard Hult <richard@imendio.com>
24 #include "config.h"
26 #include <string.h>
27 #include <stdlib.h>
29 #include <glib/gi18n-lib.h>
31 #ifdef HAVE_ENCHANT
32 #include <enchant.h>
33 #endif
35 #include "empathy-spell.h"
37 #define DEBUG_FLAG EMPATHY_DEBUG_OTHER
38 #include <libempathy/empathy-debug.h>
39 #include <libempathy/empathy-gsettings.h>
41 #ifdef HAVE_ENCHANT
43 typedef struct {
44 EnchantBroker *config;
45 EnchantDict *speller;
46 } SpellLanguage;
48 #define ISO_CODES_DATADIR ISO_CODES_PREFIX "/share/xml/iso-codes"
49 #define ISO_CODES_LOCALESDIR ISO_CODES_PREFIX "/share/locale"
51 /* Language code (gchar *) -> language name (gchar *) */
52 static GHashTable *iso_code_names = NULL;
53 /* Contains only _enabled_ languages
54 * Language code (gchar *) -> language (SpellLanguage *) */
55 static GHashTable *languages = NULL;
57 static void
58 spell_iso_codes_parse_start_tag (GMarkupParseContext *ctx,
59 const gchar *element_name,
60 const gchar **attr_names,
61 const gchar **attr_values,
62 gpointer data,
63 GError **error)
65 const gchar *ccode_longB, *ccode_longT, *ccode;
66 const gchar *lang_name;
68 if (!g_str_equal (element_name, "iso_639_entry") ||
69 attr_names == NULL || attr_values == NULL) {
70 return;
73 ccode = NULL;
74 ccode_longB = NULL;
75 ccode_longT = NULL;
76 lang_name = NULL;
78 while (*attr_names && *attr_values) {
79 if (g_str_equal (*attr_names, "iso_639_1_code")) {
80 if (**attr_values) {
81 ccode = *attr_values;
84 else if (g_str_equal (*attr_names, "iso_639_2B_code")) {
85 if (**attr_values) {
86 ccode_longB = *attr_values;
89 else if (g_str_equal (*attr_names, "iso_639_2T_code")) {
90 if (**attr_values) {
91 ccode_longT = *attr_values;
94 else if (g_str_equal (*attr_names, "name")) {
95 lang_name = *attr_values;
98 attr_names++;
99 attr_values++;
102 if (!lang_name) {
103 return;
106 if (ccode) {
107 g_hash_table_insert (iso_code_names,
108 g_strdup (ccode),
109 g_strdup (lang_name));
112 if (ccode_longB) {
113 g_hash_table_insert (iso_code_names,
114 g_strdup (ccode_longB),
115 g_strdup (lang_name));
118 if (ccode_longT) {
119 g_hash_table_insert (iso_code_names,
120 g_strdup (ccode_longT),
121 g_strdup (lang_name));
125 static void
126 spell_iso_code_names_init (void)
128 GError *err = NULL;
129 gchar *buf;
130 gsize buf_len;
132 iso_code_names = g_hash_table_new_full (g_str_hash, g_str_equal,
133 g_free, g_free);
135 bindtextdomain ("iso_639", ISO_CODES_LOCALESDIR);
136 bind_textdomain_codeset ("iso_639", "UTF-8");
138 /* FIXME: We should read this in chunks and pass to the parser. */
139 if (g_file_get_contents (ISO_CODES_DATADIR "/iso_639.xml", &buf, &buf_len, &err)) {
140 GMarkupParseContext *ctx;
141 GMarkupParser parser = {
142 spell_iso_codes_parse_start_tag,
143 NULL, NULL, NULL, NULL
146 ctx = g_markup_parse_context_new (&parser, 0, NULL, NULL);
147 if (!g_markup_parse_context_parse (ctx, buf, buf_len, &err)) {
148 g_warning ("Failed to parse '%s': %s",
149 ISO_CODES_DATADIR"/iso_639.xml",
150 err->message);
151 g_error_free (err);
154 g_markup_parse_context_free (ctx);
155 g_free (buf);
156 } else {
157 g_warning ("Failed to load '%s': %s",
158 ISO_CODES_DATADIR"/iso_639.xml", err->message);
159 g_error_free (err);
163 static void
164 spell_notify_languages_cb (GSettings *gsettings,
165 const gchar *key,
166 gpointer user_data)
168 DEBUG ("Resetting languages due to config change");
170 /* We just reset the languages list. */
171 if (languages != NULL) {
172 g_hash_table_destroy (languages);
173 languages = NULL;
177 static void
178 empathy_spell_free_language (SpellLanguage *lang)
180 enchant_broker_free_dict (lang->config, lang->speller);
181 enchant_broker_free (lang->config);
183 g_slice_free (SpellLanguage, lang);
186 static void
187 spell_setup_languages (void)
189 static GSettings *gsettings = NULL;
190 gchar *str;
192 if (gsettings == NULL) {
193 /* FIXME: this is never uninitialised */
194 gsettings = g_settings_new (EMPATHY_PREFS_CHAT_SCHEMA);
196 g_signal_connect (gsettings,
197 "changed::" EMPATHY_PREFS_CHAT_SPELL_CHECKER_LANGUAGES,
198 G_CALLBACK (spell_notify_languages_cb), NULL);
201 if (languages) {
202 return;
205 languages = g_hash_table_new_full (g_str_hash, g_str_equal,
206 g_free, (GDestroyNotify) empathy_spell_free_language);
208 str = g_settings_get_string (gsettings,
209 EMPATHY_PREFS_CHAT_SPELL_CHECKER_LANGUAGES);
211 if (str != NULL) {
212 gchar **strv;
213 gint i;
215 strv = g_strsplit (str, ",", -1);
217 i = 0;
218 while (strv && strv[i]) {
219 SpellLanguage *lang;
221 DEBUG ("Setting up language:'%s'", strv[i]);
223 lang = g_slice_new0 (SpellLanguage);
225 lang->config = enchant_broker_init ();
226 lang->speller = enchant_broker_request_dict (lang->config, strv[i]);
228 if (lang->speller == NULL) {
229 DEBUG ("language '%s' has no valid dict", strv[i]);
230 } else {
231 g_hash_table_insert (languages,
232 g_strdup (strv[i]),
233 lang);
236 i++;
239 if (strv) {
240 g_strfreev (strv);
243 g_free (str);
247 const gchar *
248 empathy_spell_get_language_name (const gchar *code)
250 const gchar *name;
252 g_return_val_if_fail (code != NULL, NULL);
254 if (!iso_code_names) {
255 spell_iso_code_names_init ();
258 name = g_hash_table_lookup (iso_code_names, code);
259 if (!name) {
260 return NULL;
263 return dgettext ("iso_639", name);
266 static void
267 enumerate_dicts (const gchar * const lang_tag,
268 const gchar * const provider_name,
269 const gchar * const provider_desc,
270 const gchar * const provider_file,
271 gpointer user_data)
273 GList **list = user_data;
274 gchar *lang = g_strdup (lang_tag);
276 if (strchr (lang, '_')) {
277 /* cut country part out of language */
278 strchr (lang, '_')[0] = '\0';
281 if (g_list_find_custom (*list, lang, (GCompareFunc) strcmp)) {
282 /* this language is already part of the list */
283 g_free (lang);
284 return;
287 *list = g_list_append (*list, g_strdup (lang));
290 GList *
291 empathy_spell_get_language_codes (void)
293 EnchantBroker *broker;
294 GList *list_langs = NULL;
296 broker = enchant_broker_init ();
297 enchant_broker_list_dicts (broker, enumerate_dicts, &list_langs);
298 enchant_broker_free (broker);
300 return list_langs;
303 GList *
304 empathy_spell_get_enabled_language_codes (void)
306 spell_setup_languages ();
307 return g_hash_table_get_keys (languages);
310 void
311 empathy_spell_free_language_codes (GList *codes)
313 g_list_foreach (codes, (GFunc) g_free, NULL);
314 g_list_free (codes);
317 gboolean
318 empathy_spell_check (const gchar *word)
320 gint enchant_result = 1;
321 const gchar *p;
322 gboolean digit;
323 gunichar c;
324 gint len;
325 GHashTableIter iter;
326 SpellLanguage *lang;
328 g_return_val_if_fail (word != NULL, FALSE);
330 spell_setup_languages ();
332 if (!languages) {
333 return TRUE;
336 /* Ignore certain cases like numbers, etc. */
337 for (p = word, digit = TRUE; *p && digit; p = g_utf8_next_char (p)) {
338 c = g_utf8_get_char (p);
339 digit = g_unichar_isdigit (c);
342 if (digit) {
343 /* We don't spell check digits. */
344 DEBUG ("Not spell checking word:'%s', it is all digits", word);
345 return TRUE;
348 len = strlen (word);
349 g_hash_table_iter_init (&iter, languages);
350 while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &lang)) {
351 enchant_result = enchant_dict_check (lang->speller, word, len);
353 if (enchant_result == 0) {
354 break;
358 return (enchant_result == 0);
361 GList *
362 empathy_spell_get_suggestions (const gchar *code,
363 const gchar *word)
365 gint len;
366 GList *suggestion_list = NULL;
367 SpellLanguage *lang;
368 gchar **suggestions;
369 gsize i, number_of_suggestions;
371 g_return_val_if_fail (code != NULL, NULL);
372 g_return_val_if_fail (word != NULL, NULL);
374 spell_setup_languages ();
376 if (!languages) {
377 return NULL;
380 len = strlen (word);
382 lang = g_hash_table_lookup (languages, code);
383 if (!lang) {
384 return NULL;
387 suggestions = enchant_dict_suggest (lang->speller, word, len,
388 &number_of_suggestions);
390 for (i = 0; i < number_of_suggestions; i++) {
391 suggestion_list = g_list_append (suggestion_list,
392 g_strdup (suggestions[i]));
395 if (suggestions) {
396 enchant_dict_free_string_list (lang->speller, suggestions);
399 return suggestion_list;
402 gboolean
403 empathy_spell_supported (void)
405 if (g_getenv ("EMPATHY_SPELL_DISABLED")) {
406 DEBUG ("EMPATHY_SPELL_DISABLE env variable defined");
407 return FALSE;
410 return TRUE;
413 void
414 empathy_spell_add_to_dictionary (const gchar *code,
415 const gchar *word)
417 SpellLanguage *lang;
419 g_return_if_fail (code != NULL);
420 g_return_if_fail (word != NULL);
422 spell_setup_languages ();
423 if (languages == NULL)
424 return;
426 lang = g_hash_table_lookup (languages, code);
427 if (lang == NULL)
428 return;
430 enchant_dict_add_to_pwl (lang->speller, word, strlen (word));
433 #else /* not HAVE_ENCHANT */
435 gboolean
436 empathy_spell_supported (void)
438 return FALSE;
441 GList *
442 empathy_spell_get_suggestions (const gchar *code,
443 const gchar *word)
445 DEBUG ("Support disabled, could not get suggestions");
447 return NULL;
450 gboolean
451 empathy_spell_check (const gchar *word)
453 DEBUG ("Support disabled, could not check spelling");
455 return TRUE;
458 const gchar *
459 empathy_spell_get_language_name (const gchar *lang)
461 DEBUG ("Support disabled, could not get language name");
463 return NULL;
466 GList *
467 empathy_spell_get_language_codes (void)
469 DEBUG ("Support disabled, could not get language codes");
471 return NULL;
474 void
475 empathy_spell_free_language_codes (GList *codes)
479 void
480 empathy_spell_add_to_dictionary (const gchar *code,
481 const gchar *word)
483 DEBUG ("Support disabled, could not expand the dictionary");
486 GList *
487 empathy_spell_get_enabled_language_codes (void)
489 DEBUG ("Support disabled, could not get enabled language codes");
491 return NULL;
494 #endif /* HAVE_ENCHANT */
497 void
498 empathy_spell_free_suggestions (GList *suggestions)
500 g_list_foreach (suggestions, (GFunc) g_free, NULL);
501 g_list_free (suggestions);