4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of version 2 of the GNU Lesser General Public
6 * License as published by the Free Software Foundation.
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 * General Public License for more details.
13 * You should have received a copy of the GNU Lesser General Public
14 * License along with this program; if not, write to the
15 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
16 * Boston, MA 02111-1307, USA.
23 #include "e-util-private.h"
24 #include "e-spell-dictionary.h"
25 #include "e-spell-checker.h"
27 #include <glib/gi18n-lib.h>
30 #define E_SPELL_DICTIONARY_GET_PRIVATE(obj) \
31 (G_TYPE_INSTANCE_GET_PRIVATE \
32 ((obj), E_TYPE_SPELL_DICTIONARY, ESpellDictionaryPrivate))
37 * The #ESpellDictionary is a wrapper around #EnchantDict.
45 struct _ESpellDictionaryPrivate
{
46 GWeakRef spell_checker
;
53 #define ISO_639_DOMAIN "iso_639"
54 #define ISO_3166_DOMAIN "iso_3166"
56 static GHashTable
*iso_639_table
= NULL
;
57 static GHashTable
*iso_3166_table
= NULL
;
66 #define ISOCODESLOCALEDIR ISO_CODES_PREFIX "/share/locale"
75 _get_iso_codes_prefix (void)
77 static gchar retval
[1000];
78 static gint beenhere
= 0;
84 if (!(temp_dir
= g_win32_get_package_installation_directory_of_module (_e_get_dll_hmodule ()))) {
85 strcpy (retval
, ISO_CODES_PREFIX
);
89 strcpy (retval
, temp_dir
);
96 _get_isocodeslocaledir (void)
98 static gchar retval
[1000];
99 static gint beenhere
= 0;
104 strcpy (retval
, _get_iso_codes_prefix ());
105 strcat (retval
, "\\share\\locale" );
110 #undef ISO_CODES_PREFIX
111 #define ISO_CODES_PREFIX _get_iso_codes_prefix ()
113 #undef ISOCODESLOCALEDIR
114 #define ISOCODESLOCALEDIR _get_isocodeslocaledir ()
119 iso_639_start_element (GMarkupParseContext
*context
,
120 const gchar
*element_name
,
121 const gchar
**attribute_names
,
122 const gchar
**attribute_values
,
126 GHashTable
*hash_table
= data
;
127 const gchar
*iso_639_1_code
= NULL
;
128 const gchar
*iso_639_2_code
= NULL
;
129 const gchar
*name
= NULL
;
130 const gchar
*code
= NULL
;
133 if (g_strcmp0 (element_name
, "iso_639_entry") != 0) {
137 for (ii
= 0; attribute_names
[ii
] != NULL
; ii
++) {
138 if (strcmp (attribute_names
[ii
], "name") == 0)
139 name
= attribute_values
[ii
];
140 else if (strcmp (attribute_names
[ii
], "iso_639_1_code") == 0)
141 iso_639_1_code
= attribute_values
[ii
];
142 else if (strcmp (attribute_names
[ii
], "iso_639_2T_code") == 0)
143 iso_639_2_code
= attribute_values
[ii
];
146 code
= (iso_639_1_code
!= NULL
) ? iso_639_1_code
: iso_639_2_code
;
148 if (code
!= NULL
&& *code
!= '\0' && name
!= NULL
&& *name
!= '\0')
149 g_hash_table_insert (
150 hash_table
, g_strdup (code
),
151 g_strdup (dgettext (ISO_639_DOMAIN
, name
)));
155 iso_3166_start_element (GMarkupParseContext
*context
,
156 const gchar
*element_name
,
157 const gchar
**attribute_names
,
158 const gchar
**attribute_values
,
162 GHashTable
*hash_table
= data
;
163 const gchar
*name
= NULL
;
164 const gchar
*code
= NULL
;
167 if (strcmp (element_name
, "iso_3166_entry") != 0)
170 for (ii
= 0; attribute_names
[ii
] != NULL
; ii
++) {
171 if (strcmp (attribute_names
[ii
], "name") == 0)
172 name
= attribute_values
[ii
];
173 else if (strcmp (attribute_names
[ii
], "alpha_2_code") == 0)
174 code
= attribute_values
[ii
];
177 if (code
!= NULL
&& *code
!= '\0' && name
!= NULL
&& *name
!= '\0')
178 g_hash_table_insert (
179 hash_table
, g_ascii_strdown (code
, -1),
180 g_strdup (dgettext (ISO_3166_DOMAIN
, name
)));
183 static GMarkupParser iso_639_parser
= {
184 iso_639_start_element
,
185 NULL
, NULL
, NULL
, NULL
188 static GMarkupParser iso_3166_parser
= {
189 iso_3166_start_element
,
190 NULL
, NULL
, NULL
, NULL
194 iso_codes_parse (const GMarkupParser
*parser
,
195 const gchar
*basename
,
196 GHashTable
*hash_table
)
198 GMappedFile
*mapped_file
;
200 GError
*error
= NULL
;
202 filename
= g_build_filename (
203 ISO_CODES_PREFIX
, "share", "xml",
204 "iso-codes", basename
, NULL
);
205 mapped_file
= g_mapped_file_new (filename
, FALSE
, &error
);
208 if (mapped_file
!= NULL
) {
209 GMarkupParseContext
*context
;
210 const gchar
*contents
;
213 context
= g_markup_parse_context_new (
214 parser
, 0, hash_table
, NULL
);
215 contents
= g_mapped_file_get_contents (mapped_file
);
216 length
= g_mapped_file_get_length (mapped_file
);
217 g_markup_parse_context_parse (
218 context
, contents
, length
, &error
);
219 g_markup_parse_context_free (context
);
220 #if GLIB_CHECK_VERSION(2,21,3)
221 g_mapped_file_unref (mapped_file
);
223 g_mapped_file_free (mapped_file
);
228 g_warning ("%s: %s", basename
, error
->message
);
229 g_error_free (error
);
233 #endif /* HAVE_ISO_CODES */
235 struct _enchant_dict_description_data
{
241 describe_dictionary (const gchar
*language_tag
,
242 const gchar
*provider_name
,
243 const gchar
*provider_desc
,
244 const gchar
*provider_file
,
247 struct _enchant_dict_description_data
*data
= user_data
;
248 const gchar
*iso_639_name
;
249 const gchar
*iso_3166_name
;
250 gchar
*language_name
;
254 /* Split language code into lowercase tokens. */
255 lowercase
= g_ascii_strdown (language_tag
, -1);
256 tokens
= g_strsplit (lowercase
, "_", -1);
259 g_return_if_fail (tokens
!= NULL
);
261 iso_639_name
= g_hash_table_lookup (iso_639_table
, tokens
[0]);
263 if (iso_639_name
== NULL
) {
264 language_name
= g_strdup_printf (
265 /* Translators: %s is the language ISO code. */
266 C_("language", "Unknown (%s)"), language_tag
);
270 if (g_strv_length (tokens
) < 2) {
271 language_name
= g_strdup (iso_639_name
);
275 iso_3166_name
= g_hash_table_lookup (iso_3166_table
, tokens
[1]);
277 if (iso_3166_name
!= NULL
)
278 language_name
= g_strdup_printf (
279 /* Translators: The first %s is the language name, and the
280 * second is the country name. Example: "French (France)" */
281 C_("language", "%s (%s)"), iso_639_name
, iso_3166_name
);
283 language_name
= g_strdup_printf (
284 /* Translators: The first %s is the language name, and the
285 * second is the country name. Example: "French (France)" */
286 C_("language", "%s (%s)"), iso_639_name
, tokens
[1]);
291 data
->language_tag
= g_strdup (language_tag
);
292 data
->dict_name
= language_name
;
296 spell_dictionary_set_enchant_dict (ESpellDictionary
*dictionary
,
297 EnchantDict
*enchant_dict
)
299 struct _enchant_dict_description_data data
;
301 enchant_dict_describe (enchant_dict
, describe_dictionary
, &data
);
303 dictionary
->priv
->code
= data
.language_tag
;
304 dictionary
->priv
->name
= data
.dict_name
;
305 dictionary
->priv
->collate_key
= g_utf8_collate_key (data
.dict_name
, -1);
309 spell_dictionary_set_spell_checker (ESpellDictionary
*dictionary
,
310 ESpellChecker
*spell_checker
)
312 g_return_if_fail (E_IS_SPELL_CHECKER (spell_checker
));
314 g_weak_ref_set (&dictionary
->priv
->spell_checker
, spell_checker
);
318 spell_dictionary_set_property (GObject
*object
,
323 switch (property_id
) {
324 case PROP_SPELL_CHECKER
:
325 spell_dictionary_set_spell_checker (
326 E_SPELL_DICTIONARY (object
),
327 g_value_get_object (value
));
331 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, property_id
, pspec
);
335 spell_dictionary_get_property (GObject
*object
,
340 switch (property_id
) {
341 case PROP_SPELL_CHECKER
:
342 g_value_take_object (
344 e_spell_dictionary_ref_spell_checker (
345 E_SPELL_DICTIONARY (object
)));
349 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, property_id
, pspec
);
353 spell_dictionary_dispose (GObject
*object
)
355 ESpellDictionaryPrivate
*priv
;
357 priv
= E_SPELL_DICTIONARY_GET_PRIVATE (object
);
359 g_weak_ref_set (&priv
->spell_checker
, NULL
);
361 /* Chain up to parent's dispose() method. */
362 G_OBJECT_CLASS (e_spell_dictionary_parent_class
)->dispose (object
);
366 spell_dictionary_finalize (GObject
*object
)
368 ESpellDictionaryPrivate
*priv
;
370 priv
= E_SPELL_DICTIONARY_GET_PRIVATE (object
);
374 g_free (priv
->collate_key
);
376 /* Chain up to parent's finalize() method. */
377 G_OBJECT_CLASS (e_spell_dictionary_parent_class
)->finalize (object
);
381 e_spell_dictionary_class_init (ESpellDictionaryClass
*class)
383 GObjectClass
*object_class
;
385 g_type_class_add_private (class, sizeof (ESpellDictionaryPrivate
));
387 object_class
= G_OBJECT_CLASS (class);
388 object_class
->set_property
= spell_dictionary_set_property
;
389 object_class
->get_property
= spell_dictionary_get_property
;
390 object_class
->dispose
= spell_dictionary_dispose
;
391 object_class
->finalize
= spell_dictionary_finalize
;
393 g_object_class_install_property (
396 g_param_spec_object (
399 "Parent spell checker",
400 E_TYPE_SPELL_CHECKER
,
402 G_PARAM_CONSTRUCT_ONLY
));
406 e_spell_dictionary_init (ESpellDictionary
*dictionary
)
408 dictionary
->priv
= E_SPELL_DICTIONARY_GET_PRIVATE (dictionary
);
410 if (!iso_639_table
&& !iso_3166_table
) {
411 #if defined (ENABLE_NLS) && defined (HAVE_ISO_CODES)
412 bindtextdomain (ISO_639_DOMAIN
, ISOCODESLOCALEDIR
);
413 bind_textdomain_codeset (ISO_639_DOMAIN
, "UTF-8");
415 bindtextdomain (ISO_3166_DOMAIN
, ISOCODESLOCALEDIR
);
416 bind_textdomain_codeset (ISO_3166_DOMAIN
, "UTF-8");
417 #endif /* ENABLE_NLS && HAVE_ISO_CODES */
419 iso_639_table
= g_hash_table_new_full (
420 (GHashFunc
) g_str_hash
,
421 (GEqualFunc
) g_str_equal
,
422 (GDestroyNotify
) g_free
,
423 (GDestroyNotify
) g_free
);
425 iso_3166_table
= g_hash_table_new_full (
426 (GHashFunc
) g_str_hash
,
427 (GEqualFunc
) g_str_equal
,
428 (GDestroyNotify
) g_free
,
429 (GDestroyNotify
) g_free
);
431 #ifdef HAVE_ISO_CODES
433 &iso_639_parser
, "iso_639.xml", iso_639_table
);
435 &iso_3166_parser
, "iso_3166.xml", iso_3166_table
);
436 #endif /* HAVE_ISO_CODES */
441 e_spell_dictionary_new (ESpellChecker
*spell_checker
,
442 EnchantDict
*enchant_dict
)
444 ESpellDictionary
*dictionary
;
446 g_return_val_if_fail (E_IS_SPELL_CHECKER (spell_checker
), NULL
);
447 g_return_val_if_fail (enchant_dict
!= NULL
, NULL
);
449 dictionary
= g_object_new (
450 E_TYPE_SPELL_DICTIONARY
,
451 "spell-checker", spell_checker
, NULL
);
453 /* Since EnchantDict is not reference counted, ESpellChecker
454 * is loaning us the EnchantDict pointer. We do not own it. */
455 spell_dictionary_set_enchant_dict (dictionary
, enchant_dict
);
461 e_spell_dictionary_new_bare (ESpellChecker
*spell_checker
,
462 const gchar
*language_tag
)
464 ESpellDictionary
*dictionary
;
465 struct _enchant_dict_description_data descr_data
;
467 g_return_val_if_fail (E_IS_SPELL_CHECKER (spell_checker
), NULL
);
468 g_return_val_if_fail (language_tag
!= NULL
, NULL
);
470 dictionary
= g_object_new (
471 E_TYPE_SPELL_DICTIONARY
,
472 "spell-checker", spell_checker
, NULL
);
474 descr_data
.language_tag
= NULL
;
475 descr_data
.dict_name
= NULL
;
477 describe_dictionary (language_tag
, NULL
, NULL
, NULL
, &descr_data
);
479 dictionary
->priv
->code
= descr_data
.language_tag
;
480 dictionary
->priv
->name
= descr_data
.dict_name
;
481 dictionary
->priv
->collate_key
= g_utf8_collate_key (descr_data
.dict_name
, -1);
487 * e_spell_dictionary_hash:
488 * @dictionary: an #ESpellDictionary
490 * Generates a hash value for @dictionary based on its ISO code.
491 * This function is intended for easily hashing an #ESpellDictionary
492 * to add to a #GHashTable or similar data structure.
494 * Returns: a hash value for @dictionary
497 e_spell_dictionary_hash (ESpellDictionary
*dictionary
)
501 g_return_val_if_fail (E_IS_SPELL_DICTIONARY (dictionary
), 0);
503 code
= e_spell_dictionary_get_code (dictionary
);
505 return g_str_hash (code
);
509 * e_spell_dictionary_equal:
510 * @dictionary1: an #ESpellDictionary
511 * @dictionary2: another #ESpellDictionary
513 * Checks two #ESpellDictionary instances for equality based on their
516 * Returns: %TRUE if @dictionary1 and @dictionary2 are equal
519 e_spell_dictionary_equal (ESpellDictionary
*dictionary1
,
520 ESpellDictionary
*dictionary2
)
522 const gchar
*code1
, *code2
;
524 g_return_val_if_fail (E_IS_SPELL_DICTIONARY (dictionary1
), FALSE
);
525 g_return_val_if_fail (E_IS_SPELL_DICTIONARY (dictionary2
), FALSE
);
527 if (dictionary1
== dictionary2
)
530 code1
= e_spell_dictionary_get_code (dictionary1
);
531 code2
= e_spell_dictionary_get_code (dictionary2
);
533 return g_str_equal (code1
, code2
);
537 * e_spell_dictionary_compare:
538 * @dictionary1: an #ESpellDictionary
539 * @dictionary2: another #ESpellDictionary
541 * Compares @dictionary1 and @dictionary2 by their display names for
542 * the purpose of lexicographical sorting. Use this function where a
543 * #GCompareFunc callback is required, such as g_list_sort().
545 * Returns: 0 if the names match,
546 * a negative value if @dictionary1 < @dictionary2,
547 * or a positive value of @dictionary1 > @dictionary2
550 e_spell_dictionary_compare (ESpellDictionary
*dictionary1
,
551 ESpellDictionary
*dictionary2
)
553 g_return_val_if_fail (E_IS_SPELL_DICTIONARY (dictionary1
), 0);
554 g_return_val_if_fail (E_IS_SPELL_DICTIONARY (dictionary2
), 0);
557 dictionary1
->priv
->collate_key
,
558 dictionary2
->priv
->collate_key
);
562 * e_spell_dictionary_get_name:
563 * @dictionary: an #ESpellDictionary
565 * Returns the display name of the dictionary (for example
566 * "English (British)")
568 * Returns: the display name of the @dictionary
571 e_spell_dictionary_get_name (ESpellDictionary
*dictionary
)
573 g_return_val_if_fail (E_IS_SPELL_DICTIONARY (dictionary
), NULL
);
575 return dictionary
->priv
->name
;
579 * e_spell_dictionary_get_code:
580 * @dictionary: an #ESpellDictionary
582 * Returns the ISO code of the spell-checking language for
583 * @dictionary (for example "en_US").
585 * Returns: the language code of the @dictionary
588 e_spell_dictionary_get_code (ESpellDictionary
*dictionary
)
590 g_return_val_if_fail (E_IS_SPELL_DICTIONARY (dictionary
), NULL
);
592 return dictionary
->priv
->code
;
596 * e_spell_dictionary_ref_spell_checker:
597 * @dictionary: an #ESpellDictionary
599 * Returns a new reference to the #ESpellChecker which owns the dictionary.
600 * Unreference the #ESpellChecker with g_object_unref() when finished with it.
602 * Returns: an #ESpellChecker
605 e_spell_dictionary_ref_spell_checker (ESpellDictionary
*dictionary
)
607 g_return_val_if_fail (E_IS_SPELL_DICTIONARY (dictionary
), NULL
);
609 return g_weak_ref_get (&dictionary
->priv
->spell_checker
);
613 * e_spell_dictionary_check_word:
614 * @dictionary: an #ESpellDictionary
615 * @word: a word to spell-check
616 * @length: length of @word in bytes or -1 when %NULL-terminated
618 * Tries to lookup the @word in the @dictionary to check whether
619 * it's spelled correctly or not.
621 * Returns: %TRUE if @word is recognized, %FALSE otherwise
624 e_spell_dictionary_check_word (ESpellDictionary
*dictionary
,
628 ESpellChecker
*spell_checker
;
629 EnchantDict
*enchant_dict
;
632 g_return_val_if_fail (E_IS_SPELL_DICTIONARY (dictionary
), TRUE
);
633 g_return_val_if_fail (word
!= NULL
&& *word
!= '\0', TRUE
);
635 spell_checker
= e_spell_dictionary_ref_spell_checker (dictionary
);
636 g_return_val_if_fail (spell_checker
!= NULL
, TRUE
);
638 enchant_dict
= e_spell_checker_get_enchant_dict (
639 spell_checker
, e_spell_dictionary_get_code (dictionary
));
640 g_return_val_if_fail (enchant_dict
!= NULL
, TRUE
);
642 recognized
= (enchant_dict_check (enchant_dict
, word
, length
) == 0);
644 g_object_unref (spell_checker
);
650 * e_spell_dictionary_learn_word:
651 * @dictionary: an #ESpellDictionary
652 * @word: a word to add to @dictionary
653 * @length: length of @word in bytes or -1 when %NULL-terminated
655 * Permanently adds @word to @dictionary so that next time calling
656 * e_spell_dictionary_check() on the @word will return %TRUE.
659 e_spell_dictionary_learn_word (ESpellDictionary
*dictionary
,
663 ESpellChecker
*spell_checker
;
664 EnchantDict
*enchant_dict
;
666 g_return_if_fail (E_IS_SPELL_DICTIONARY (dictionary
));
667 g_return_if_fail (word
!= NULL
&& *word
!= '\0');
669 spell_checker
= e_spell_dictionary_ref_spell_checker (dictionary
);
670 g_return_if_fail (spell_checker
!= NULL
);
672 enchant_dict
= e_spell_checker_get_enchant_dict (
673 spell_checker
, e_spell_dictionary_get_code (dictionary
));
674 g_return_if_fail (enchant_dict
!= NULL
);
676 enchant_dict_add_to_personal (enchant_dict
, word
, length
);
678 g_object_unref (spell_checker
);
682 * e_spell_dictionary_ignore_word:
683 * @dictionary: an #ESpellDictionary
684 * @word: a word to add to ignore list
685 * @length: length of @word in bytes or -1 when %NULL-terminated
687 * Adds @word to temporary ignore list of the @dictionary, so that
688 * e_spell_dictionary_check() on the @word will return %TRUE. The
689 * list is cleared when the dictionary is freed.
692 e_spell_dictionary_ignore_word (ESpellDictionary
*dictionary
,
696 ESpellChecker
*spell_checker
;
697 EnchantDict
*enchant_dict
;
699 g_return_if_fail (E_IS_SPELL_DICTIONARY (dictionary
));
700 g_return_if_fail (word
!= NULL
&& *word
!= '\0');
702 spell_checker
= e_spell_dictionary_ref_spell_checker (dictionary
);
703 g_return_if_fail (spell_checker
!= NULL
);
705 enchant_dict
= e_spell_checker_get_enchant_dict (
706 spell_checker
, e_spell_dictionary_get_code (dictionary
));
707 g_return_if_fail (enchant_dict
!= NULL
);
709 enchant_dict_add_to_session (enchant_dict
, word
, length
);
711 g_object_unref (spell_checker
);
715 * e_spell_dictionary_get_suggestions:
716 * @dictionary: an #ESpellDictionary
717 * @word: a word to which to find suggestions
718 * @length: length of @word in bytes or -1 when %NULL-terminated
720 * Provides list of alternative spellings of @word.
722 * Free the returned spelling suggestions with g_free(), and the list
723 * itself with g_list_free(). An easy way to free the list properly in
724 * one step is as follows:
727 * g_list_free_full (list, (GDestroyNotify) g_free);
730 * Returns: a list of spelling suggestions for @word
733 e_spell_dictionary_get_suggestions (ESpellDictionary
*dictionary
,
737 ESpellChecker
*spell_checker
;
738 EnchantDict
*enchant_dict
;
743 g_return_val_if_fail (E_IS_SPELL_DICTIONARY (dictionary
), NULL
);
744 g_return_val_if_fail (word
!= NULL
&& *word
!= '\0', NULL
);
746 spell_checker
= e_spell_dictionary_ref_spell_checker (dictionary
);
747 g_return_val_if_fail (spell_checker
!= NULL
, NULL
);
749 enchant_dict
= e_spell_checker_get_enchant_dict (
750 spell_checker
, e_spell_dictionary_get_code (dictionary
));
751 g_return_val_if_fail (enchant_dict
!= NULL
, NULL
);
753 suggestions
= enchant_dict_suggest (enchant_dict
, word
, length
, &count
);
754 for (ii
= 0; ii
< count
; ii
++)
755 list
= g_list_prepend (list
, g_strdup (suggestions
[ii
]));
756 enchant_dict_free_suggestions (enchant_dict
, suggestions
);
758 g_object_unref (spell_checker
);
760 return g_list_reverse (list
);
764 * e_spell_dictionary_add_correction
765 * @dictionary: an #ESpellDictionary
766 * @misspelled: a misspelled word
767 * @misspelled_length: length of @misspelled in bytes or -1 when
769 * @correction: the corrected word
770 * @correction_length: length of @correction in bytes or -1 when
773 * Learns a new @correction of @misspelled word.
776 e_spell_dictionary_store_correction (ESpellDictionary
*dictionary
,
777 const gchar
*misspelled
,
778 gsize misspelled_length
,
779 const gchar
*correction
,
780 gsize correction_length
)
782 ESpellChecker
*spell_checker
;
783 EnchantDict
*enchant_dict
;
785 g_return_if_fail (E_IS_SPELL_DICTIONARY (dictionary
));
786 g_return_if_fail (misspelled
!= NULL
&& *misspelled
!= '\0');
787 g_return_if_fail (correction
!= NULL
&& *correction
!= '\0');
789 spell_checker
= e_spell_dictionary_ref_spell_checker (dictionary
);
790 g_return_if_fail (spell_checker
!= NULL
);
792 enchant_dict
= e_spell_checker_get_enchant_dict (
793 spell_checker
, e_spell_dictionary_get_code (dictionary
));
794 g_return_if_fail (enchant_dict
!= NULL
);
796 enchant_dict_store_replacement (
798 misspelled
, misspelled_length
,
799 correction
, correction_length
);
801 g_object_unref (spell_checker
);