[evolution/webkit-composer: 139/147] ESpellEntry: Add a "spell-checker" property.



commit fa4643e520d9943714df33fab5de563946cf3742
Author: Matthew Barnes <mbarnes redhat com>
Date:   Sun Jan 20 14:31:05 2013 -0500

    ESpellEntry: Add a "spell-checker" property.
    
    By default, ESpellEntry creates its own ESpellChecker instance.  The
    property is writable so a different ESpellChecker can be shared across
    multiple spell checking widgets.
    
    Listen for "notify::active-languages" signals from ESpellChecker to
    trigger spelling rechecks, rather than monitoring GSettings directly.
    
    Also, because ESpellChecker is configured automatically from GSettings
    (via ESettingsSpellChecker), there's no need to do it ourselves.
    
    New functions:
    
        e_spell_entry_get_spell_checker()
        e_spell_entry_set_spell_checker()

 doc/reference/libeutil/libeutil-sections.txt |    2 +
 e-util/e-spell-entry.c                       |  307 +++++++++++++++++---------
 e-util/e-spell-entry.h                       |    5 +
 3 files changed, 204 insertions(+), 110 deletions(-)
---
diff --git a/doc/reference/libeutil/libeutil-sections.txt b/doc/reference/libeutil/libeutil-sections.txt
index f46bc5d..951c6d7 100644
--- a/doc/reference/libeutil/libeutil-sections.txt
+++ b/doc/reference/libeutil/libeutil-sections.txt
@@ -3716,6 +3716,8 @@ e_spell_entry_new
 e_spell_entry_set_languages
 e_spell_entry_get_checking_enabled
 e_spell_entry_set_checking_enabled
+e_spell_entry_get_spell_checker
+e_spell_entry_set_spell_checker
 <SUBSECTION Standard>
 E_SPELL_ENTRY
 E_IS_SPELL_ENTRY
diff --git a/e-util/e-spell-entry.c b/e-util/e-spell-entry.c
index 831e578..3f0d5e4 100644
--- a/e-util/e-spell-entry.c
+++ b/e-util/e-spell-entry.c
@@ -34,7 +34,6 @@ struct _ESpellEntryPrivate {
 	PangoAttrList *attr_list;
 	gint mark_character;
 	gint entry_scroll_offset;
-	GSettings *settings;
 	gboolean custom_checkers;
 	gboolean checking_enabled;
 	GList *dictionaries;
@@ -43,11 +42,13 @@ struct _ESpellEntryPrivate {
 	gint *word_ends;
 
 	ESpellChecker *spell_checker;
+	guint active_languages_handler_id;
 };
 
 enum {
 	PROP_0,
-	PROP_CHECKING_ENABLED
+	PROP_CHECKING_ENABLED,
+	PROP_SPELL_CHECKER
 };
 
 G_DEFINE_TYPE (ESpellEntry, e_spell_entry, GTK_TYPE_ENTRY);
@@ -70,17 +71,14 @@ word_misspelled (ESpellEntry *entry,
 	g_strlcpy (word, text + start, end - start + 1);
 
 	if (g_unichar_isalpha (*word)) {
-		GList *li;
+		ESpellChecker *spell_checker;
 		gssize wlen = strlen (word);
 
-		for (li = entry->priv->dictionaries; li; li = g_list_next (li)) {
-			ESpellDictionary *dict = li->data;
-			if (e_spell_dictionary_check (dict, word, wlen)) {
-				result = FALSE;
-				break;
-			}
-		}
+		spell_checker = e_spell_entry_get_spell_checker (entry);
+		if (e_spell_checker_check_word (spell_checker, word, wlen))
+			result = FALSE;
 	}
+
 	g_free (word);
 
 	return result;
@@ -143,6 +141,7 @@ spell_entry_recheck_all (ESpellEntry *entry)
 	GtkWidget *widget = GTK_WIDGET (entry);
 	PangoLayout *layout;
 	gint length, i;
+	gboolean check_words = FALSE;
 
 	if (entry->priv->words == NULL)
 		return;
@@ -152,7 +151,15 @@ spell_entry_recheck_all (ESpellEntry *entry)
 	pango_attr_list_unref (entry->priv->attr_list);
 	entry->priv->attr_list = pango_attr_list_new ();
 
-	if (entry->priv->dictionaries && entry->priv->checking_enabled) {
+	if (e_spell_entry_get_checking_enabled (entry)) {
+		ESpellChecker *spell_checker;
+
+		spell_checker = e_spell_entry_get_spell_checker (entry);
+		if (e_spell_checker_count_active_languages (spell_checker) > 0)
+			check_words = TRUE;
+	}
+
+	if (check_words) {
 		/* Loop through words */
 		for (i = 0; entry->priv->words[i]; i++) {
 			length = strlen (entry->priv->words[i]);
@@ -290,18 +297,16 @@ static void
 ignore_all (GtkWidget *menuitem,
             ESpellEntry *entry)
 {
+	ESpellChecker *spell_checker;
 	gchar *word;
 	gint start, end;
-	GList *li;
 
 	get_word_extents_from_position (
 		entry, &start, &end, entry->priv->mark_character);
 	word = gtk_editable_get_chars (GTK_EDITABLE (entry), start, end);
 
-	for (li = entry->priv->dictionaries; li; li = g_list_next (li)) {
-		ESpellDictionary *dict = li->data;
-		e_spell_dictionary_ignore_word (dict, word, -1);
-	}
+	spell_checker = e_spell_entry_get_spell_checker (entry);
+	e_spell_checker_ignore_word (spell_checker, word);
 
 	g_free (word);
 
@@ -418,32 +423,53 @@ static GtkWidget *
 build_spelling_menu (ESpellEntry *entry,
                      const gchar *word)
 {
+	ESpellChecker *spell_checker;
 	ESpellDictionary *dict;
 	GtkWidget *topmenu, *mi;
+	GQueue queue = G_QUEUE_INIT;
+	gchar **active_languages;
+	guint ii, n_active_languages;
 	gchar *label;
 
 	topmenu = gtk_menu_new ();
 
-	if (entry->priv->dictionaries == NULL)
-		return topmenu;
+	spell_checker = e_spell_entry_get_spell_checker (entry);
+
+	active_languages = e_spell_checker_list_active_languages (
+		spell_checker, &n_active_languages);
+	for (ii = 0; ii < n_active_languages; ii++) {
+		dict = e_spell_checker_ref_dictionary (
+			spell_checker, active_languages[ii]);
+		if (dict != NULL)
+			g_queue_push_tail (&queue, dict);
+	}
+	g_strfreev (active_languages);
+
+	if (g_queue_is_empty (&queue))
+		goto exit;
 
 	/* Suggestions */
-	if (entry->priv->dictionaries->next == NULL) {
-		dict = entry->priv->dictionaries->data;
+	if (n_active_languages == 1) {
+		dict = g_queue_peek_head (&queue);
 		build_suggestion_menu (entry, topmenu, dict, word);
 	} else {
-		GList *li;
 		GtkWidget *menu;
-		const gchar *lang_name;
+		GList *list, *link;
+
+		list = g_queue_peek_head_link (&queue);
 
-		for (li = entry->priv->dictionaries; li; li = g_list_next (li)) {
-			dict = li->data;
+		for (link = list; link != NULL; link = g_list_next (link)) {
+			const gchar *lang_name;
+
+			dict = E_SPELL_DICTIONARY (link->data);
 
 			lang_name = e_spell_dictionary_get_name (dict);
 			if (lang_name == NULL)
 				lang_name = e_spell_dictionary_get_code (dict);
+			if (lang_name == NULL)
+				lang_name = "???";
 
-			mi = gtk_menu_item_new_with_label (lang_name ? lang_name : "???");
+			mi = gtk_menu_item_new_with_label (lang_name);
 
 			gtk_widget_show (mi);
 			gtk_menu_shell_append (GTK_MENU_SHELL (topmenu), mi);
@@ -467,28 +493,33 @@ build_spelling_menu (ESpellEntry *entry,
 		GTK_IMAGE_MENU_ITEM (mi),
 		gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU));
 
-	if (entry->priv->dictionaries->next == NULL) {
-		dict = entry->priv->dictionaries->data;
+	if (n_active_languages == 1) {
+		dict = g_queue_peek_head (&queue);
 		g_object_set_data (G_OBJECT (mi), "spell-entry-checker", dict);
 		g_signal_connect (
 			G_OBJECT (mi), "activate",
 			G_CALLBACK (add_to_dictionary), entry);
 	} else {
-		GList *li;
 		GtkWidget *menu, *submi;
-		const gchar *lang_name;
+		GList *list, *link;
 
 		menu = gtk_menu_new ();
 		gtk_menu_item_set_submenu (GTK_MENU_ITEM (mi), menu);
 
-		for (li = entry->priv->dictionaries; li; li = g_list_next (li)) {
-			dict = li->data;
+		list = g_queue_peek_head_link (&queue);
+
+		for (link = list; link != NULL; link = g_list_next (link)) {
+			const gchar *lang_name;
+
+			dict = E_SPELL_DICTIONARY (link->data);
 
 			lang_name = e_spell_dictionary_get_name (dict);
 			if (lang_name == NULL)
 				lang_name = e_spell_dictionary_get_code (dict);
+			if (lang_name == NULL)
+				lang_name = "???";
 
-			submi = gtk_menu_item_new_with_label (lang_name ? lang_name : "???");
+			submi = gtk_menu_item_new_with_label (lang_name);
 			g_object_set_data (G_OBJECT (submi), "spell-entry-checker", dict);
 			g_signal_connect (
 				submi, "activate",
@@ -511,6 +542,10 @@ build_spelling_menu (ESpellEntry *entry,
 	gtk_widget_show_all (mi);
 	gtk_menu_shell_append (GTK_MENU_SHELL (topmenu), mi);
 
+exit:
+	while (!g_queue_is_empty (&queue))
+		g_object_unref (g_queue_pop_head (&queue));
+
 	return topmenu;
 }
 
@@ -556,10 +591,12 @@ spell_entry_populate_popup (ESpellEntry *entry,
                             GtkMenu *menu,
                             gpointer data)
 {
+	ESpellChecker *spell_checker;
 	gint start, end;
 	gchar *word;
 
-	if (entry->priv->dictionaries == NULL)
+	spell_checker = e_spell_entry_get_spell_checker (entry);
+	if (e_spell_checker_count_active_languages (spell_checker) == 0)
 		return;
 
 	get_word_extents_from_position (
@@ -582,8 +619,10 @@ static void
 spell_entry_changed (GtkEditable *editable)
 {
 	ESpellEntry *entry = E_SPELL_ENTRY (editable);
+	ESpellChecker *spell_checker;
 
-	if (entry->priv->dictionaries == NULL)
+	spell_checker = e_spell_entry_get_spell_checker (entry);
+	if (e_spell_checker_count_active_languages (spell_checker) == 0)
 		return;
 
 	if (entry->priv->words != NULL) {
@@ -611,71 +650,6 @@ spell_entry_notify_scroll_offset (ESpellEntry *spell_entry)
 		&spell_entry->priv->entry_scroll_offset, NULL);
 }
 
-static GList *
-spell_entry_load_spell_languages (ESpellEntry *entry)
-{
-	ESpellChecker *spell_checker;
-	GSettings *settings;
-	GList *list = NULL;
-	gchar **strv;
-	gint ii;
-
-	/* Ask GSettings for a list of spell check language codes. */
-	settings = g_settings_new ("org.gnome.evolution.mail");
-	strv = g_settings_get_strv (settings, "composer-spell-languages");
-	g_object_unref (settings);
-
-	spell_checker = entry->priv->spell_checker;
-
-	/* Convert the codes to spell language structs. */
-	for (ii = 0; strv[ii] != NULL; ii++) {
-		ESpellDictionary *dictionary;
-		gchar *language_code = strv[ii];
-
-		dictionary = e_spell_checker_ref_dictionary (
-			spell_checker, language_code);
-		if (dictionary != NULL)
-			list = g_list_prepend (list, dictionary);
-	}
-
-	g_strfreev (strv);
-
-	list = g_list_reverse (list);
-
-	/* Pick a default spell language if it came back empty. */
-	if (list == NULL) {
-		ESpellDictionary *dictionary;
-
-		dictionary = e_spell_checker_ref_dictionary (
-			spell_checker, NULL);
-		if (dictionary != NULL)
-			list = g_list_prepend (list, dictionary);
-	}
-
-	return list;
-}
-
-static void
-spell_entry_settings_changed (ESpellEntry *spell_entry,
-                              const gchar *key)
-{
-	GList *languages;
-
-	g_return_if_fail (spell_entry != NULL);
-
-	if (spell_entry->priv->custom_checkers)
-		return;
-
-	if (key && !g_str_equal (key, "composer-spell-languages"))
-		return;
-
-	languages = spell_entry_load_spell_languages (spell_entry);
-	e_spell_entry_set_languages (spell_entry, languages);
-	g_list_free_full (languages, (GDestroyNotify) g_object_unref);
-
-	spell_entry->priv->custom_checkers = FALSE;
-}
-
 static gint
 spell_entry_find_position (ESpellEntry *spell_entry,
                            gint x)
@@ -701,6 +675,15 @@ spell_entry_find_position (ESpellEntry *spell_entry,
 }
 
 static void
+spell_entry_active_languages_cb (ESpellChecker *spell_checker,
+                                 GParamSpec *pspec,
+                                 ESpellEntry *spell_entry)
+{
+	if (gtk_widget_get_realized (GTK_WIDGET (spell_entry)))
+		spell_entry_recheck_all (spell_entry);
+}
+
+static void
 spell_entry_set_property (GObject *object,
                           guint property_id,
                           const GValue *value,
@@ -712,6 +695,12 @@ spell_entry_set_property (GObject *object,
 				E_SPELL_ENTRY (object),
 				g_value_get_boolean (value));
 			return;
+
+		case PROP_SPELL_CHECKER:
+			e_spell_entry_set_spell_checker (
+				E_SPELL_ENTRY (object),
+				g_value_get_object (value));
+			return;
 	}
 
 	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
@@ -730,6 +719,13 @@ spell_entry_get_property (GObject *object,
 				e_spell_entry_get_checking_enabled (
 				E_SPELL_ENTRY (object)));
 			return;
+
+		case PROP_SPELL_CHECKER:
+			g_value_set_object (
+				value,
+				e_spell_entry_get_spell_checker (
+				E_SPELL_ENTRY (object)));
+			return;
 	}
 
 	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
@@ -742,7 +738,13 @@ spell_entry_dispose (GObject *object)
 
 	priv = E_SPELL_ENTRY_GET_PRIVATE (object);
 
-	g_clear_object (&priv->settings);
+	if (priv->active_languages_handler_id > 0) {
+		g_signal_handler_disconnect (
+			priv->spell_checker,
+			priv->active_languages_handler_id);
+		priv->active_languages_handler_id = 0;
+	}
+
 	g_clear_object (&priv->spell_checker);
 
 	g_list_free_full (priv->dictionaries, g_object_unref);
@@ -772,6 +774,26 @@ spell_entry_finalize (GObject *object)
 	G_OBJECT_CLASS (e_spell_entry_parent_class)->finalize (object);
 }
 
+static void
+spell_entry_constructed (GObject *object)
+{
+	ESpellEntry *spell_entry;
+	ESpellChecker *spell_checker;
+
+	spell_entry = E_SPELL_ENTRY (object);
+
+	/* Chain up to parent's constructed() method. */
+	G_OBJECT_CLASS (e_spell_entry_parent_class)->constructed (object);
+
+	/* Install a default spell checker if there is not one already. */
+	spell_checker = e_spell_entry_get_spell_checker (spell_entry);
+	if (spell_checker == NULL) {
+		spell_checker = e_spell_checker_new ();
+		e_spell_entry_set_spell_checker (spell_entry, spell_checker);
+		g_object_unref (spell_checker);
+	}
+}
+
 static gboolean
 spell_entry_draw (GtkWidget *widget,
                   cairo_t *cr)
@@ -815,6 +837,7 @@ e_spell_entry_class_init (ESpellEntryClass *class)
 	object_class->get_property = spell_entry_get_property;
 	object_class->dispose = spell_entry_dispose;
 	object_class->finalize = spell_entry_finalize;
+	object_class->constructed = spell_entry_constructed;
 
 	widget_class = GTK_WIDGET_CLASS (class);
 	widget_class->draw = spell_entry_draw;
@@ -830,6 +853,17 @@ e_spell_entry_class_init (ESpellEntryClass *class)
 			TRUE,
 			G_PARAM_READWRITE |
 			G_PARAM_STATIC_STRINGS));
+
+	g_object_class_install_property (
+		object_class,
+		PROP_SPELL_CHECKER,
+		g_param_spec_object (
+			"spell-checker",
+			"Spell Checker",
+			"The spell checker object",
+			E_TYPE_SPELL_CHECKER,
+			G_PARAM_READWRITE |
+			G_PARAM_STATIC_STRINGS));
 }
 
 static void
@@ -839,7 +873,6 @@ e_spell_entry_init (ESpellEntry *spell_entry)
 	spell_entry->priv->attr_list = pango_attr_list_new ();
 	spell_entry->priv->dictionaries = NULL;
 	spell_entry->priv->checking_enabled = TRUE;
-	spell_entry->priv->spell_checker = e_spell_checker_new ();
 
 	g_signal_connect (
 		spell_entry, "popup-menu",
@@ -853,15 +886,6 @@ e_spell_entry_init (ESpellEntry *spell_entry)
 	g_signal_connect (
 		spell_entry, "notify::scroll-offset",
 		G_CALLBACK (spell_entry_notify_scroll_offset), NULL);
-
-	/* listen for languages changes */
-	spell_entry->priv->settings = g_settings_new ("org.gnome.evolution.mail");
-	g_signal_connect_swapped (
-		spell_entry->priv->settings, "changed",
-		G_CALLBACK (spell_entry_settings_changed), spell_entry);
-
-	/* load current settings */
-	spell_entry_settings_changed (spell_entry, NULL);
 }
 
 GtkWidget *
@@ -911,3 +935,66 @@ e_spell_entry_set_checking_enabled (ESpellEntry *spell_entry,
 
 	g_object_notify (G_OBJECT (spell_entry), "checking-enabled");
 }
+
+/**
+ * e_spell_entry_get_spell_checker:
+ * @spell_entry: an #ESpellEntry
+ *
+ * Returns the #ESpellChecker being used for spell checking.  By default,
+ * #ESpellEntry creates its own #ESpellChecker, but this can be overridden
+ * through e_spell_entry_set_spell_checker().
+ *
+ * Returns: an #ESpellChecker
+ **/
+ESpellChecker *
+e_spell_entry_get_spell_checker (ESpellEntry *spell_entry)
+{
+	g_return_val_if_fail (E_IS_SPELL_ENTRY (spell_entry), NULL);
+
+	return spell_entry->priv->spell_checker;
+}
+
+/**
+ * e_spell_entry_set_spell_checker:
+ * @spell_entry: an #ESpellEntry
+ * @spell_checker: an #ESpellChecker
+ *
+ * Sets the #ESpellChecker to use for spell checking.  By default,
+ * #ESpellEntry creates its own #ESpellChecker.  This function can be
+ * useful for sharing an #ESpellChecker across multiple spell-checking
+ * widgets, so the active spell checking languages stay synchronized.
+ **/
+void
+e_spell_entry_set_spell_checker (ESpellEntry *spell_entry,
+                                 ESpellChecker *spell_checker)
+{
+	gulong handler_id;
+
+	g_return_if_fail (E_IS_SPELL_ENTRY (spell_entry));
+	g_return_if_fail (E_IS_SPELL_CHECKER (spell_checker));
+
+	if (spell_checker == spell_entry->priv->spell_checker)
+		return;
+
+	if (spell_entry->priv->spell_checker != NULL) {
+		g_signal_handler_disconnect (
+			spell_entry->priv->spell_checker,
+			spell_entry->priv->active_languages_handler_id);
+		g_object_unref (spell_entry->priv->spell_checker);
+	}
+
+	spell_entry->priv->spell_checker = g_object_ref (spell_checker);
+
+	handler_id = g_signal_connect (
+		spell_checker, "notify::active-languages",
+		G_CALLBACK (spell_entry_active_languages_cb),
+		spell_entry);
+
+	spell_entry->priv->active_languages_handler_id = handler_id;
+
+	g_object_notify (G_OBJECT (spell_entry), "spell-checker");
+
+	if (gtk_widget_get_realized (GTK_WIDGET (spell_entry)))
+		spell_entry_recheck_all (spell_entry);
+}
+
diff --git a/e-util/e-spell-entry.h b/e-util/e-spell-entry.h
index 5b1ca11..acfc3cc 100644
--- a/e-util/e-spell-entry.h
+++ b/e-util/e-spell-entry.h
@@ -25,6 +25,8 @@
 
 #include <gtk/gtk.h>
 
+#include <e-util/e-util.h>
+
 /* Standard GObject macros */
 #define E_TYPE_SPELL_ENTRY \
 	(e_spell_entry_get_type ())
@@ -68,6 +70,9 @@ gboolean	e_spell_entry_get_checking_enabled
 void		e_spell_entry_set_checking_enabled
 						(ESpellEntry *spell_entry,
 						 gboolean enable_checking);
+ESpellChecker *	e_spell_entry_get_spell_checker	(ESpellEntry *spell_entry);
+void		e_spell_entry_set_spell_checker	(ESpellEntry *spell_entry,
+						 ESpellChecker *spell_checker);
 
 G_END_DECLS
 



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