Help: Use stable 'if' namespace instead of experimental
[empathy-mirror.git] / libempathy-gtk / empathy-spell.c
blob27f27f902f034f0b05a4a27db803a7b8db63f2c9
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"
25 #include "empathy-spell.h"
27 #include <glib/gi18n-lib.h>
29 #ifdef HAVE_ENCHANT
30 #include <enchant.h>
31 #endif
33 #include "empathy-gsettings.h"
35 #define DEBUG_FLAG EMPATHY_DEBUG_OTHER
36 #include "empathy-debug.h"
38 #ifdef HAVE_ENCHANT
40 typedef struct {
41 EnchantBroker *config;
42 EnchantDict *speller;
43 } SpellLanguage;
45 #define ISO_CODES_DATADIR ISO_CODES_PREFIX "/share/xml/iso-codes"
46 #define ISO_CODES_LOCALESDIR ISO_CODES_PREFIX "/share/locale"
48 /* Language code (gchar *) -> language name (gchar *) */
49 static GHashTable *iso_code_names = NULL;
50 /* Contains only _enabled_ languages
51 * Language code (gchar *) -> language (SpellLanguage *) */
52 static GHashTable *languages = NULL;
54 static void
55 spell_iso_codes_parse_start_tag (GMarkupParseContext *ctx,
56 const gchar *element_name,
57 const gchar **attr_names,
58 const gchar **attr_values,
59 gpointer data,
60 GError **error)
62 const gchar *ccode_longB, *ccode_longT, *ccode;
63 const gchar *lang_name;
65 if (!g_str_equal (element_name, "iso_639_entry") ||
66 attr_names == NULL || attr_values == NULL) {
67 return;
70 ccode = NULL;
71 ccode_longB = NULL;
72 ccode_longT = NULL;
73 lang_name = NULL;
75 while (*attr_names && *attr_values) {
76 if (g_str_equal (*attr_names, "iso_639_1_code")) {
77 if (**attr_values) {
78 ccode = *attr_values;
81 else if (g_str_equal (*attr_names, "iso_639_2B_code")) {
82 if (**attr_values) {
83 ccode_longB = *attr_values;
86 else if (g_str_equal (*attr_names, "iso_639_2T_code")) {
87 if (**attr_values) {
88 ccode_longT = *attr_values;
91 else if (g_str_equal (*attr_names, "name")) {
92 lang_name = *attr_values;
95 attr_names++;
96 attr_values++;
99 if (!lang_name) {
100 return;
103 if (ccode) {
104 g_hash_table_insert (iso_code_names,
105 g_strdup (ccode),
106 g_strdup (lang_name));
109 if (ccode_longB) {
110 g_hash_table_insert (iso_code_names,
111 g_strdup (ccode_longB),
112 g_strdup (lang_name));
115 if (ccode_longT) {
116 g_hash_table_insert (iso_code_names,
117 g_strdup (ccode_longT),
118 g_strdup (lang_name));
122 static void
123 spell_iso_code_names_init (void)
125 GError *err = NULL;
126 gchar *buf;
127 gsize buf_len;
129 iso_code_names = g_hash_table_new_full (g_str_hash, g_str_equal,
130 g_free, g_free);
132 bindtextdomain ("iso_639", ISO_CODES_LOCALESDIR);
133 bind_textdomain_codeset ("iso_639", "UTF-8");
135 /* FIXME: We should read this in chunks and pass to the parser. */
136 if (g_file_get_contents (ISO_CODES_DATADIR "/iso_639.xml", &buf, &buf_len, &err)) {
137 GMarkupParseContext *ctx;
138 GMarkupParser parser = {
139 spell_iso_codes_parse_start_tag,
140 NULL, NULL, NULL, NULL
143 ctx = g_markup_parse_context_new (&parser, 0, NULL, NULL);
144 if (!g_markup_parse_context_parse (ctx, buf, buf_len, &err)) {
145 g_warning ("Failed to parse '%s': %s",
146 ISO_CODES_DATADIR"/iso_639.xml",
147 err->message);
148 g_error_free (err);
151 g_markup_parse_context_free (ctx);
152 g_free (buf);
153 } else {
154 g_warning ("Failed to load '%s': %s",
155 ISO_CODES_DATADIR"/iso_639.xml", err->message);
156 g_error_free (err);
160 static void
161 spell_notify_languages_cb (GSettings *gsettings,
162 const gchar *key,
163 gpointer user_data)
165 DEBUG ("Resetting languages due to config change");
167 /* We just reset the languages list. */
168 if (languages != NULL) {
169 g_hash_table_unref (languages);
170 languages = NULL;
174 static void
175 empathy_spell_free_language (SpellLanguage *lang)
177 enchant_broker_free_dict (lang->config, lang->speller);
178 enchant_broker_free (lang->config);
180 g_slice_free (SpellLanguage, lang);
183 static void
184 spell_setup_languages (void)
186 static GSettings *gsettings = NULL;
187 gchar *str;
189 if (gsettings == NULL) {
190 /* FIXME: this is never uninitialised */
191 gsettings = g_settings_new (EMPATHY_PREFS_CHAT_SCHEMA);
193 g_signal_connect (gsettings,
194 "changed::" EMPATHY_PREFS_CHAT_SPELL_CHECKER_LANGUAGES,
195 G_CALLBACK (spell_notify_languages_cb), NULL);
198 if (languages) {
199 return;
202 languages = g_hash_table_new_full (g_str_hash, g_str_equal,
203 g_free, (GDestroyNotify) empathy_spell_free_language);
205 str = g_settings_get_string (gsettings,
206 EMPATHY_PREFS_CHAT_SPELL_CHECKER_LANGUAGES);
208 if (str != NULL) {
209 gchar **strv;
210 gint i;
212 strv = g_strsplit (str, ",", -1);
214 i = 0;
215 while (strv && strv[i]) {
216 SpellLanguage *lang;
218 DEBUG ("Setting up language:'%s'", strv[i]);
220 lang = g_slice_new0 (SpellLanguage);
222 lang->config = enchant_broker_init ();
223 lang->speller = enchant_broker_request_dict (lang->config, strv[i]);
225 if (lang->speller == NULL) {
226 DEBUG ("language '%s' has no valid dict", strv[i]);
227 } else {
228 g_hash_table_insert (languages,
229 g_strdup (strv[i]),
230 lang);
233 i++;
236 if (strv) {
237 g_strfreev (strv);
240 g_free (str);
244 const gchar *
245 empathy_spell_get_language_name (const gchar *code)
247 const gchar *name;
249 g_return_val_if_fail (code != NULL, NULL);
251 if (!iso_code_names) {
252 spell_iso_code_names_init ();
255 name = g_hash_table_lookup (iso_code_names, code);
256 if (!name) {
257 return NULL;
260 return dgettext ("iso_639", name);
263 static void
264 enumerate_dicts (const gchar * const lang_tag,
265 const gchar * const provider_name,
266 const gchar * const provider_desc,
267 const gchar * const provider_file,
268 gpointer user_data)
270 GList **list = user_data;
271 gchar *lang = g_strdup (lang_tag);
273 if (strchr (lang, '_')) {
274 /* cut country part out of language */
275 strchr (lang, '_')[0] = '\0';
278 if (g_list_find_custom (*list, lang, (GCompareFunc) strcmp)) {
279 /* this language is already part of the list */
280 g_free (lang);
281 return;
284 *list = g_list_append (*list, lang);
287 GList *
288 empathy_spell_get_language_codes (void)
290 EnchantBroker *broker;
291 GList *list_langs = NULL;
293 broker = enchant_broker_init ();
294 enchant_broker_list_dicts (broker, enumerate_dicts, &list_langs);
295 enchant_broker_free (broker);
297 return list_langs;
300 GList *
301 empathy_spell_get_enabled_language_codes (void)
303 spell_setup_languages ();
304 return g_hash_table_get_keys (languages);
307 void
308 empathy_spell_free_language_codes (GList *codes)
310 g_list_foreach (codes, (GFunc) g_free, NULL);
311 g_list_free (codes);
314 gboolean
315 empathy_spell_check (const gchar *word)
317 gint enchant_result = 1;
318 const gchar *p;
319 gboolean digit;
320 gunichar c;
321 gint len;
322 GHashTableIter iter;
323 SpellLanguage *lang;
325 g_return_val_if_fail (word != NULL, FALSE);
327 spell_setup_languages ();
329 if (!languages) {
330 return TRUE;
333 /* Ignore certain cases like numbers, etc. */
334 for (p = word, digit = TRUE; *p && digit; p = g_utf8_next_char (p)) {
335 c = g_utf8_get_char (p);
336 digit = g_unichar_isdigit (c);
339 if (digit) {
340 /* We don't spell check digits. */
341 DEBUG ("Not spell checking word:'%s', it is all digits", word);
342 return TRUE;
345 len = strlen (word);
346 g_hash_table_iter_init (&iter, languages);
347 while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &lang)) {
348 enchant_result = enchant_dict_check (lang->speller, word, len);
350 if (enchant_result == 0) {
351 break;
355 return (enchant_result == 0);
358 GList *
359 empathy_spell_get_suggestions (const gchar *code,
360 const gchar *word)
362 gint len;
363 GList *suggestion_list = NULL;
364 SpellLanguage *lang;
365 gchar **suggestions;
366 gsize i, number_of_suggestions;
368 g_return_val_if_fail (code != NULL, NULL);
369 g_return_val_if_fail (word != NULL, NULL);
371 spell_setup_languages ();
373 if (!languages) {
374 return NULL;
377 len = strlen (word);
379 lang = g_hash_table_lookup (languages, code);
380 if (!lang) {
381 return NULL;
384 suggestions = enchant_dict_suggest (lang->speller, word, len,
385 &number_of_suggestions);
387 for (i = 0; i < number_of_suggestions; i++) {
388 suggestion_list = g_list_append (suggestion_list,
389 g_strdup (suggestions[i]));
392 if (suggestions) {
393 enchant_dict_free_string_list (lang->speller, suggestions);
396 return suggestion_list;
399 gboolean
400 empathy_spell_supported (void)
402 if (g_getenv ("EMPATHY_SPELL_DISABLED")) {
403 DEBUG ("EMPATHY_SPELL_DISABLE env variable defined");
404 return FALSE;
407 return TRUE;
410 void
411 empathy_spell_add_to_dictionary (const gchar *code,
412 const gchar *word)
414 SpellLanguage *lang;
416 g_return_if_fail (code != NULL);
417 g_return_if_fail (word != NULL);
419 spell_setup_languages ();
420 if (languages == NULL)
421 return;
423 lang = g_hash_table_lookup (languages, code);
424 if (lang == NULL)
425 return;
427 enchant_dict_add_to_pwl (lang->speller, word, strlen (word));
430 #else /* not HAVE_ENCHANT */
432 gboolean
433 empathy_spell_supported (void)
435 return FALSE;
438 GList *
439 empathy_spell_get_suggestions (const gchar *code,
440 const gchar *word)
442 DEBUG ("Support disabled, could not get suggestions");
444 return NULL;
447 gboolean
448 empathy_spell_check (const gchar *word)
450 DEBUG ("Support disabled, could not check spelling");
452 return TRUE;
455 const gchar *
456 empathy_spell_get_language_name (const gchar *lang)
458 DEBUG ("Support disabled, could not get language name");
460 return NULL;
463 GList *
464 empathy_spell_get_language_codes (void)
466 DEBUG ("Support disabled, could not get language codes");
468 return NULL;
471 void
472 empathy_spell_free_language_codes (GList *codes)
476 void
477 empathy_spell_add_to_dictionary (const gchar *code,
478 const gchar *word)
480 DEBUG ("Support disabled, could not expand the dictionary");
483 GList *
484 empathy_spell_get_enabled_language_codes (void)
486 DEBUG ("Support disabled, could not get enabled language codes");
488 return NULL;
491 #endif /* HAVE_ENCHANT */
494 void
495 empathy_spell_free_suggestions (GList *suggestions)
497 g_list_foreach (suggestions, (GFunc) g_free, NULL);
498 g_list_free (suggestions);