[evolution/webkit-composer: 120/147] ESpellDictionary: Keep a weak ref on the ESpellChecker.



commit b96825baadd3e7461f36163eab27fc804deee435
Author: Matthew Barnes <mbarnes redhat com>
Date:   Wed Jan 16 17:43:30 2013 -0500

    ESpellDictionary: Keep a weak ref on the ESpellChecker.
    
    Avoids a reference cycle, since ESpellChecker keeps a strong reference
    on all ESpellDictionary instances.
    
    This however changes ownership semantics on EnchantDict, since an
    EnchantBroker is needed to free the EnchantDict (for some reason) and
    ESpellChecker owns the EnchantBroker.  It must therefore also own all
    the EnchantDicts.
    
    Add e_spell_checker_get_enchant_dict(), which returns an EnchantDict
    for the given language code.  ESpellDictionary will have to use this
    to fetch an EnchantDict from the ESpellChecker as needed.  This also
    eliminates e_spell_checker_free_dict().
    
    In addition, rename e_spell_dictionary_get_parent_checker() to
    e_spell_dictionary_ref_spell_checker() and have it return a new
    reference by way of g_weak_ref_get().

 doc/reference/libeutil/libeutil-sections.txt |    4 +-
 e-util/e-spell-checker.c                     |   89 +++++++++++++++---
 e-util/e-spell-checker.h                     |    5 +-
 e-util/e-spell-dictionary.c                  |  134 ++++++++++++++++++--------
 e-util/e-spell-dictionary.h                  |    6 +-
 5 files changed, 179 insertions(+), 59 deletions(-)
---
diff --git a/doc/reference/libeutil/libeutil-sections.txt b/doc/reference/libeutil/libeutil-sections.txt
index d7d4522..8571db9 100644
--- a/doc/reference/libeutil/libeutil-sections.txt
+++ b/doc/reference/libeutil/libeutil-sections.txt
@@ -3659,9 +3659,9 @@ ESpellChecker
 e_spell_checker_instance
 e_spell_checker_list_available_dicts
 e_spell_checker_ref_dictionary
+e_spell_checker_get_enchant_dict
 e_spell_checker_set_active_dictionaries
 e_spell_checker_get_active_dictionaries
-e_spell_checker_free_dict
 e_spell_checker_learn_word
 e_spell_checker_ignore_word
 <SUBSECTION Standard>
@@ -3687,12 +3687,12 @@ e_spell_dictionary_equal
 e_spell_dictionary_compare
 e_spell_dictionary_get_name
 e_spell_dictionary_get_code
+e_spell_dictionary_ref_spell_checker
 e_spell_dictionary_check
 e_spell_dictionary_learn_word
 e_spell_dictionary_ignore_word
 e_spell_dictionary_get_suggestions
 e_spell_dictionary_store_correction
-e_spell_dictionary_get_parent_checker
 <SUBSECTION Standard>
 E_SPELL_DICTIONARY
 E_IS_SPELL_DICTIONARY
diff --git a/e-util/e-spell-checker.c b/e-util/e-spell-checker.c
index 306b0eb..7b66e4b 100644
--- a/e-util/e-spell-checker.c
+++ b/e-util/e-spell-checker.c
@@ -36,6 +36,11 @@ struct _ESpellCheckerPrivate {
 	EnchantBroker *broker;
 	GHashTable *dictionaries_cache;
 	gboolean dictionaries_loaded;
+
+	/* We retain ownership of the EnchantDict's since they
+	 * have to be freed through enchant_broker_free_dict()
+	 * and we also own the EnchantBroker. */
+	GHashTable *enchant_dicts;
 };
 
 enum {
@@ -66,6 +71,19 @@ G_DEFINE_TYPE_EXTENDED (
  * so it can be set as a default spell-checker to WebKit editors
  */
 
+static gboolean
+spell_checker_enchant_dicts_foreach_cb (gpointer key,
+                                        gpointer value,
+                                        gpointer user_data)
+{
+	EnchantDict *enchant_dict = value;
+	EnchantBroker *enchant_broker = user_data;
+
+	enchant_broker_free_dict (enchant_broker, enchant_dict);
+
+	return TRUE;
+}
+
 static void
 wksc_check_spelling (WebKitSpellChecker *webkit_checker,
                      const gchar *word,
@@ -310,14 +328,35 @@ spell_checker_dispose (GObject *object)
 	g_list_free_full (priv->active, g_object_unref);
 	priv->active = NULL;
 
-	enchant_broker_free (priv->broker);
-	priv->broker = NULL;
+	g_hash_table_remove_all (priv->dictionaries_cache);
 
 	/* Chain up to parent's dispose() method. */
 	G_OBJECT_CLASS (e_spell_checker_parent_class)->dispose (object);
 }
 
 static void
+spell_checker_finalize (GObject *object)
+{
+	ESpellCheckerPrivate *priv;
+
+	priv = E_SPELL_CHECKER_GET_PRIVATE (object);
+
+	g_hash_table_destroy (priv->dictionaries_cache);
+
+	/* Freeing EnchantDicts requires help from EnchantBroker. */
+	g_hash_table_foreach_remove (
+		priv->enchant_dicts,
+		spell_checker_enchant_dicts_foreach_cb,
+		priv->broker);
+	g_hash_table_destroy (priv->enchant_dicts);
+
+	enchant_broker_free (priv->broker);
+
+	/* Chain up to parent's finalize() method. */
+	G_OBJECT_CLASS (e_spell_checker_parent_class)->finalize (object);
+}
+
+static void
 e_spell_checker_class_init (ESpellCheckerClass *class)
 {
 	GObjectClass *object_class;
@@ -328,6 +367,7 @@ e_spell_checker_class_init (ESpellCheckerClass *class)
 	object_class->set_property = spell_checker_set_property;
 	object_class->get_property = spell_checker_get_property;
 	object_class->dispose = spell_checker_dispose;
+	object_class->finalize = spell_checker_finalize;
 
 	g_object_class_install_property (
 		object_class,
@@ -355,6 +395,7 @@ static void
 e_spell_checker_init (ESpellChecker *checker)
 {
 	GHashTable *dictionaries_cache;
+	GHashTable *enchant_dicts;
 
 	dictionaries_cache = g_hash_table_new_full (
 		(GHashFunc) g_str_hash,
@@ -362,10 +403,17 @@ e_spell_checker_init (ESpellChecker *checker)
 		(GDestroyNotify) NULL,
 		(GDestroyNotify) g_object_unref);
 
+	enchant_dicts = g_hash_table_new_full (
+		(GHashFunc) g_str_hash,
+		(GEqualFunc) g_str_equal,
+		(GDestroyNotify) g_free,
+		(GDestroyNotify) NULL);
+
 	checker->priv = E_SPELL_CHECKER_GET_PRIVATE (checker);
 
 	checker->priv->broker = enchant_broker_init ();
 	checker->priv->dictionaries_cache = dictionaries_cache;
+	checker->priv->enchant_dicts = enchant_dicts;
 }
 
 ESpellChecker *
@@ -394,12 +442,19 @@ list_enchant_dicts (const gchar * const lang_tag,
 		ESpellDictionary *dictionary;
 		const gchar *code;
 
+		/* Note that we retain ownership of the EnchantDict.
+		 * Since EnchantDict is not reference counted, we're
+		 * merely loaning the pointer to ESpellDictionary. */
 		dictionary = e_spell_dictionary_new (checker, enchant_dict);
 		code = e_spell_dictionary_get_code (dictionary);
 
 		g_hash_table_insert (
 			checker->priv->dictionaries_cache,
 			(gpointer) code, dictionary);
+
+		g_hash_table_insert (
+			checker->priv->enchant_dicts,
+			g_strdup (code), enchant_dict);
 	}
 }
 
@@ -474,6 +529,26 @@ e_spell_checker_ref_dictionary (ESpellChecker *checker,
 }
 
 /**
+ * e_spell_checker_get_enchant_dict:
+ * @checker: an #ESpellChecker
+ * @language_code: language code of a dictionary, or %NULL
+ *
+ * Returns the #EnchantDict for @language_code, or %NULL if there is none.
+ *
+ * Returns: the #EnchantDict for @language_code, or %NULL
+ **/
+EnchantDict *
+e_spell_checker_get_enchant_dict (ESpellChecker *checker,
+                                  const gchar *language_code)
+{
+	g_return_val_if_fail (E_IS_SPELL_CHECKER (checker), NULL);
+	g_return_val_if_fail (language_code != NULL, NULL);
+
+	return g_hash_table_lookup (
+		checker->priv->enchant_dicts, language_code);
+}
+
+/**
  * e_spell_checker_get_active_dictionaries:
  * @checker: an #ESpellChecker
  *
@@ -509,16 +584,6 @@ e_spell_checker_set_active_dictionaries (ESpellChecker *checker,
 	g_list_foreach (checker->priv->active, (GFunc) g_object_ref, NULL);
 }
 
-void
-e_spell_checker_free_dict (ESpellChecker *checker,
-                           EnchantDict *enchant_dict)
-{
-	g_return_if_fail (E_IS_SPELL_CHECKER (checker));
-	g_return_if_fail (enchant_dict != NULL);
-
-	enchant_broker_free_dict (checker->priv->broker, enchant_dict);
-}
-
 /**
  * e_spell_checker_ignore_word:
  * @checker: an #ESpellChecker
diff --git a/e-util/e-spell-checker.h b/e-util/e-spell-checker.h
index 66d1f1a..6b59452 100644
--- a/e-util/e-spell-checker.h
+++ b/e-util/e-spell-checker.h
@@ -67,13 +67,14 @@ GList *		e_spell_checker_list_available_dicts
 ESpellDictionary *
 		e_spell_checker_ref_dictionary	(ESpellChecker *checker,
 						 const gchar *language_code);
+EnchantDict *	e_spell_checker_get_enchant_dict
+						(ESpellChecker *checker,
+						 const gchar *language_code);
 void		e_spell_checker_set_active_dictionaries
 						(ESpellChecker *checker,
 						 GList *active_dicts);
 GList *		e_spell_checker_get_active_dictionaries
 						(ESpellChecker *checker);
-void		e_spell_checker_free_dict	(ESpellChecker *checker,
-						 EnchantDict *enchant_dict);
 void		e_spell_checker_learn_word	(ESpellChecker *checker,
 						 const gchar *word);
 void		e_spell_checker_ignore_word	(ESpellChecker *checker,
diff --git a/e-util/e-spell-dictionary.c b/e-util/e-spell-dictionary.c
index 5857250..a3f3be7 100644
--- a/e-util/e-spell-dictionary.c
+++ b/e-util/e-spell-dictionary.c
@@ -42,8 +42,7 @@ enum {
 };
 
 struct _ESpellDictionaryPrivate {
-	ESpellChecker *spell_checker;
-	EnchantDict *enchant_dict;
+	GWeakRef spell_checker;
 
 	gchar *name;
 	gchar *code;
@@ -321,7 +320,6 @@ spell_dictionary_set_enchant_dict (ESpellDictionary *dictionary,
 
 	enchant_dict_describe (enchant_dict, describe_dictionary, &data);
 
-	dictionary->priv->enchant_dict = enchant_dict;
 	dictionary->priv->code = data.language_tag;
 	dictionary->priv->name = data.dict_name;
 	dictionary->priv->collate_key = g_utf8_collate_key (data.dict_name, -1);
@@ -332,9 +330,8 @@ spell_dictionary_set_spell_checker (ESpellDictionary *dictionary,
                                     ESpellChecker *spell_checker)
 {
 	g_return_if_fail (E_IS_SPELL_CHECKER (spell_checker));
-	g_return_if_fail (dictionary->priv->spell_checker == NULL);
 
-	dictionary->priv->spell_checker = g_object_ref (spell_checker);
+	g_weak_ref_set (&dictionary->priv->spell_checker, spell_checker);
 }
 
 static void
@@ -362,9 +359,9 @@ spell_dictionary_get_property (GObject *object,
 {
 	switch (property_id) {
 		case PROP_SPELL_CHECKER:
-			g_value_set_object (
+			g_value_take_object (
 				value,
-				e_spell_dictionary_get_parent_checker (
+				e_spell_dictionary_ref_spell_checker (
 				E_SPELL_DICTIONARY (object)));
 			return;
 	}
@@ -379,7 +376,7 @@ spell_dictionary_dispose (GObject *object)
 
 	priv = E_SPELL_DICTIONARY_GET_PRIVATE (object);
 
-	g_clear_object (&priv->spell_checker);
+	g_weak_ref_set (&priv->spell_checker, NULL);
 
 	/* Chain up to parent's dispose() method. */
 	G_OBJECT_CLASS (e_spell_dictionary_parent_class)->dispose (object);
@@ -392,8 +389,6 @@ spell_dictionary_finalize (GObject *object)
 
 	priv = E_SPELL_DICTIONARY_GET_PRIVATE (object);
 
-	e_spell_checker_free_dict (priv->spell_checker, priv->enchant_dict);
-
 	g_free (priv->name);
 	g_free (priv->code);
 	g_free (priv->collate_key);
@@ -463,18 +458,20 @@ e_spell_dictionary_init (ESpellDictionary *dictionary)
 }
 
 ESpellDictionary *
-e_spell_dictionary_new (ESpellChecker *parent_checker,
+e_spell_dictionary_new (ESpellChecker *spell_checker,
                         EnchantDict *enchant_dict)
 {
 	ESpellDictionary *dictionary;
 
-	g_return_val_if_fail (E_IS_SPELL_CHECKER (parent_checker), NULL);
+	g_return_val_if_fail (E_IS_SPELL_CHECKER (spell_checker), NULL);
 	g_return_val_if_fail (enchant_dict != NULL, NULL);
 
 	dictionary = g_object_new (
 		E_TYPE_SPELL_DICTIONARY,
-		"spell-checker", parent_checker, NULL);
+		"spell-checker", spell_checker, NULL);
 
+	/* Since EnchantDict is not reference counted, ESpellChecker
+	 * is loaning us the EnchantDict pointer.  We do not own it. */
 	spell_dictionary_set_enchant_dict (dictionary, enchant_dict);
 
 	return dictionary;
@@ -590,6 +587,23 @@ e_spell_dictionary_get_code (ESpellDictionary *dictionary)
 }
 
 /**
+ * e_spell_dictionary_ref_spell_checker:
+ * @dictionary: an #ESpellDictionary
+ *
+ * Returns a new reference to the #ESpellChecker which owns the dictionary.
+ * Unreference the #ESpellChecker with g_object_unref() when finished with it.
+ *
+ * Returns: an #ESpellChecker
+ **/
+ESpellChecker *
+e_spell_dictionary_ref_spell_checker (ESpellDictionary *dictionary)
+{
+	g_return_val_if_fail (E_IS_SPELL_DICTIONARY (dictionary), NULL);
+
+	return g_weak_ref_get (&dictionary->priv->spell_checker);
+}
+
+/**
  * e_spell_dictionary_check:
  * @dictionary: an #ESpellDictionary
  * @word: a word to spell-check
@@ -605,11 +619,25 @@ e_spell_dictionary_check (ESpellDictionary *dictionary,
                           const gchar *word,
                           gsize length)
 {
+	ESpellChecker *spell_checker;
+	EnchantDict *enchant_dict;
+	gboolean correctly_spelled;
+
 	g_return_val_if_fail (E_IS_SPELL_DICTIONARY (dictionary), TRUE);
 	g_return_val_if_fail (word != NULL && *word != '\0', TRUE);
 
-	return enchant_dict_check (
-		dictionary->priv->enchant_dict, word, length);
+	spell_checker = e_spell_dictionary_ref_spell_checker (dictionary);
+	g_return_val_if_fail (spell_checker != NULL, TRUE);
+
+	enchant_dict = e_spell_checker_get_enchant_dict (
+		spell_checker, e_spell_dictionary_get_code (dictionary));
+	g_return_val_if_fail (enchant_dict != NULL, TRUE);
+
+	correctly_spelled = enchant_dict_check (enchant_dict, word, length);
+
+	g_object_unref (spell_checker);
+
+	return correctly_spelled;
 }
 
 /**
@@ -626,11 +654,22 @@ e_spell_dictionary_learn_word (ESpellDictionary *dictionary,
                                const gchar *word,
                                gsize length)
 {
+	ESpellChecker *spell_checker;
+	EnchantDict *enchant_dict;
+
 	g_return_if_fail (E_IS_SPELL_DICTIONARY (dictionary));
 	g_return_if_fail (word != NULL && *word != '\0');
 
-	enchant_dict_add_to_personal (
-		dictionary->priv->enchant_dict, word, length);
+	spell_checker = e_spell_dictionary_ref_spell_checker (dictionary);
+	g_return_if_fail (spell_checker != NULL);
+
+	enchant_dict = e_spell_checker_get_enchant_dict (
+		spell_checker, e_spell_dictionary_get_code (dictionary));
+	g_return_if_fail (enchant_dict != NULL);
+
+	enchant_dict_add_to_personal (enchant_dict, word, length);
+
+	g_object_unref (spell_checker);
 }
 
 /**
@@ -648,11 +687,22 @@ e_spell_dictionary_ignore_word (ESpellDictionary *dictionary,
                                 const gchar *word,
                                 gsize length)
 {
+	ESpellChecker *spell_checker;
+	EnchantDict *enchant_dict;
+
 	g_return_if_fail (E_IS_SPELL_DICTIONARY (dictionary));
 	g_return_if_fail (word != NULL && *word != '\0');
 
-	enchant_dict_add_to_session (
-		dictionary->priv->enchant_dict, word, length);
+	spell_checker = e_spell_dictionary_ref_spell_checker (dictionary);
+	g_return_if_fail (spell_checker != NULL);
+
+	enchant_dict = e_spell_checker_get_enchant_dict (
+		spell_checker, e_spell_dictionary_get_code (dictionary));
+	g_return_if_fail (enchant_dict != NULL);
+
+	enchant_dict_add_to_session (enchant_dict, word, length);
+
+	g_object_unref (spell_checker);
 }
 
 /**
@@ -678,6 +728,8 @@ e_spell_dictionary_get_suggestions (ESpellDictionary *dictionary,
                                     const gchar *word,
                                     gsize length)
 {
+	ESpellChecker *spell_checker;
+	EnchantDict *enchant_dict;
 	GList *list = NULL;
 	gchar **suggestions;
 	gsize ii, count;
@@ -685,12 +737,19 @@ e_spell_dictionary_get_suggestions (ESpellDictionary *dictionary,
 	g_return_val_if_fail (E_IS_SPELL_DICTIONARY (dictionary), NULL);
 	g_return_val_if_fail (word != NULL && *word != '\0', NULL);
 
-	suggestions = enchant_dict_suggest (
-		dictionary->priv->enchant_dict, word, length, &count);
+	spell_checker = e_spell_dictionary_ref_spell_checker (dictionary);
+	g_return_val_if_fail (spell_checker != NULL, NULL);
+
+	enchant_dict = e_spell_checker_get_enchant_dict (
+		spell_checker, e_spell_dictionary_get_code (dictionary));
+	g_return_val_if_fail (enchant_dict != NULL, NULL);
+
+	suggestions = enchant_dict_suggest (enchant_dict, word, length, &count);
 	for (ii = 0; ii < count; ii++)
 		list = g_list_prepend (list, g_strdup (suggestions[ii]));
-	enchant_dict_free_suggestions (
-		dictionary->priv->enchant_dict, suggestions);
+	enchant_dict_free_suggestions (enchant_dict, suggestions);
+
+	g_object_unref (spell_checker);
 
 	return g_list_reverse (list);
 }
@@ -714,30 +773,25 @@ e_spell_dictionary_store_correction (ESpellDictionary *dictionary,
                                      const gchar *correction,
                                      gsize correction_length)
 {
+	ESpellChecker *spell_checker;
+	EnchantDict *enchant_dict;
+
 	g_return_if_fail (E_IS_SPELL_DICTIONARY (dictionary));
 	g_return_if_fail (misspelled != NULL && *misspelled != '\0');
 	g_return_if_fail (correction != NULL && *correction != '\0');
 
+	spell_checker = e_spell_dictionary_ref_spell_checker (dictionary);
+	g_return_if_fail (spell_checker != NULL);
+
+	enchant_dict = e_spell_checker_get_enchant_dict (
+		spell_checker, e_spell_dictionary_get_code (dictionary));
+	g_return_if_fail (enchant_dict != NULL);
+
 	enchant_dict_store_replacement (
-		dictionary->priv->enchant_dict,
+		enchant_dict,
 		misspelled, misspelled_length,
 		correction, correction_length);
-}
-
-/**
- * e_spell_dictionary_get_parent_checker:
- * @dictionary: an #ESpellDictionary
- *
- * Returns an #ESpellChecker which is parent (and the original owner) of
- * the @dictionary.
- *
- * Returns: an #ESpellChecker
- */
-ESpellChecker *
-e_spell_dictionary_get_parent_checker (ESpellDictionary *dictionary)
-{
-	g_return_val_if_fail (E_IS_SPELL_DICTIONARY (dictionary), NULL);
 
-	return dictionary->priv->spell_checker;
+	g_object_unref (spell_checker);
 }
 
diff --git a/e-util/e-spell-dictionary.h b/e-util/e-spell-dictionary.h
index b25896c..77956fa 100644
--- a/e-util/e-spell-dictionary.h
+++ b/e-util/e-spell-dictionary.h
@@ -63,7 +63,7 @@ struct _ESpellDictionaryClass {
 
 GType		e_spell_dictionary_get_type	(void) G_GNUC_CONST;
 ESpellDictionary *
-		e_spell_dictionary_new		(ESpellChecker *parent_checker,
+		e_spell_dictionary_new		(ESpellChecker *spell_checker,
 						 EnchantDict *enchant_dict);
 guint		e_spell_dictionary_hash		(ESpellDictionary *dictionary);
 gboolean	e_spell_dictionary_equal	(ESpellDictionary *dictionary1,
@@ -72,6 +72,8 @@ gint		e_spell_dictionary_compare	(ESpellDictionary *dictionary1,
 						 ESpellDictionary *dictionary2);
 const gchar *	e_spell_dictionary_get_name	(ESpellDictionary *dictionary);
 const gchar *	e_spell_dictionary_get_code	(ESpellDictionary *dictionary);
+ESpellChecker *	e_spell_dictionary_ref_spell_checker
+						(ESpellDictionary *dictionary);
 gboolean	e_spell_dictionary_check	(ESpellDictionary *dictionary,
 						 const gchar *word,
 						 gsize length);
@@ -91,8 +93,6 @@ void		e_spell_dictionary_store_correction
 						 gsize misspelled_length,
 						 const gchar *correction,
 						 gsize correction_length);
-ESpellChecker *	e_spell_dictionary_get_parent_checker
-						(ESpellDictionary *dictionary);
 
 G_END_DECLS
 



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]