gtkhtml r8761 - branches/mbarnes-composer/components/editor



Author: mbarnes
Date: Sat Mar  1 17:41:34 2008
New Revision: 8761
URL: http://svn.gnome.org/viewvc/gtkhtml?rev=8761&view=rev

Log:
Add spell checking to the context menu by way of some hardcode UI
merging, unmerging, and visibility control.

Add "added" and "ignored" signals to the spell check dialog so we
can update words flagged as misspelled when a dictionary changes.


Modified:
   branches/mbarnes-composer/components/editor/gtkhtml-editor-actions.c
   branches/mbarnes-composer/components/editor/gtkhtml-editor-actions.h
   branches/mbarnes-composer/components/editor/gtkhtml-editor-private.c
   branches/mbarnes-composer/components/editor/gtkhtml-editor-private.h
   branches/mbarnes-composer/components/editor/gtkhtml-editor.ui
   branches/mbarnes-composer/components/editor/gtkhtml-spell-checker.c
   branches/mbarnes-composer/components/editor/gtkhtml-spell-dialog.c
   branches/mbarnes-composer/components/editor/gtkhtml-spell-language.c

Modified: branches/mbarnes-composer/components/editor/gtkhtml-editor-actions.c
==============================================================================
--- branches/mbarnes-composer/components/editor/gtkhtml-editor-actions.c	(original)
+++ branches/mbarnes-composer/components/editor/gtkhtml-editor-actions.c	Sat Mar  1 17:41:34 2008
@@ -111,6 +111,8 @@
  * "context-delete-column"
  * "context-delete-row"
  * "context-delete-table"
+ * "context-spell-add"
+ * "context-spell-ignore"
  *
  * Context Menu Actions (HTML only)
  * --------------------------------
@@ -419,6 +421,88 @@
 }
 
 static void
+action_context_spell_add_cb (GtkAction *action,
+                             GtkhtmlEditor *editor)
+{
+	GtkHTML *html;
+	GtkhtmlSpellChecker *checker;
+	GList *active_spell_checkers;
+	const gchar *action_name;
+	gchar *word;
+
+	/* XXX This is messy.  We have to extract the language code from
+	 *     the action's name to know which dictionary to add the word
+	 *     to.  The action's name is either "context-spell-add-CODE"
+	 *     or "context-spell-add" if only one language is selected. */
+
+	html = gtkhtml_editor_get_html (editor);
+	action_name = gtk_action_get_name (action);
+	active_spell_checkers = editor->priv->active_spell_checkers;
+
+	g_return_if_fail (g_str_has_prefix (action_name, "context-spell-add"));
+	g_return_if_fail (active_spell_checkers != NULL);
+
+	/* Note: len("context-spell-add-") == 18 */
+	if (g_str_equal (action_name, "context-spell-add-")) {
+		const gchar *language_code = action_name + 18;
+
+		checker = g_hash_table_lookup (
+			editor->priv->available_spell_checkers,
+			gtkhtml_spell_language_lookup (language_code));
+	} else
+		checker = active_spell_checkers->data;
+	g_return_if_fail (checker != NULL);
+
+	word = html_engine_get_spell_word (html->engine);
+	g_return_if_fail (word != NULL);
+
+	gtkhtml_spell_checker_add_word (checker, word, -1);
+	html_engine_spell_check (html->engine);
+
+	g_free (word);
+}
+
+static void
+action_context_spell_ignore_cb (GtkAction *action,
+                                GtkhtmlEditor *editor)
+{
+	GtkhtmlSpellChecker *checker;
+	GList *active_spell_checkers;
+	GtkHTML *html;
+	gchar *word;
+
+	/* XXX This action gets fired by choosing "Ignore Misspelled Word"
+	 *     through the pop-up context menu.  Problem is, if multiple
+	 *     languages are enabled we don't know which dictionary to
+	 *     add the word to (for this session).  A reasonable heuristic
+	 *     is to first see if the dictionary for the current locale is
+	 *     enabled.  If not, then pick the first enabled dictionary. */
+
+	active_spell_checkers = editor->priv->active_spell_checkers;
+	g_return_if_fail (active_spell_checkers != NULL);
+
+	/* Find a spell checker to add the word to. */
+	if (g_list_length (active_spell_checkers) == 1)
+		checker = active_spell_checkers->data;
+	else {
+		checker = g_hash_table_lookup (
+			editor->priv->available_spell_checkers,
+			gtkhtml_spell_language_lookup (NULL));
+		if (g_list_find (active_spell_checkers, checker) == NULL)
+			checker = active_spell_checkers->data;
+	}
+
+	html = gtkhtml_editor_get_html (editor);
+	word = html_engine_get_spell_word (html->engine);
+	g_return_if_fail (word != NULL);
+
+	gtkhtml_spell_checker_add_word_to_session (checker, word, -1);
+	html_engine_spell_check (html->engine);
+
+	g_free (word);
+}
+
+static void
 action_copy_cb (GtkAction *action,
                 GtkhtmlEditor *editor)
 {
@@ -750,39 +834,51 @@
 action_language_cb (GtkToggleAction *action,
                     GtkhtmlEditor *editor)
 {
+	const GtkhtmlSpellLanguage *language;
 	GtkhtmlSpellChecker *checker;
 	const gchar *language_code;
+	GtkAction *add_action;
+	GList *list;
+	guint length;
+	gchar *action_name;
 	gboolean active;
-	gboolean sensitive;
 
-	language_code = gtk_action_get_name (GTK_ACTION (action));
 	active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
+	language_code = gtk_action_get_name (GTK_ACTION (action));
+	language = gtkhtml_spell_language_lookup (language_code);
 
 	checker = g_hash_table_lookup (
-		editor->priv->available_spell_checkers,
-		gtkhtml_spell_language_lookup (language_code));
+		editor->priv->available_spell_checkers, language);
 	g_return_if_fail (checker != NULL);
 
+	/* Update the list of active spell checkers. */
+	list = editor->priv->active_spell_checkers;
 	if (active)
-		editor->priv->active_spell_checkers =
-			g_list_insert_sorted (
-			editor->priv->active_spell_checkers,
-			g_object_ref (checker), (GCompareFunc)
-			gtkhtml_spell_checker_compare);
+		list = g_list_insert_sorted (
+			list, g_object_ref (checker),
+			(GCompareFunc) gtkhtml_spell_checker_compare);
 	else {
 		GList *link;
 
-		link = g_list_find (
-			editor->priv->active_spell_checkers, checker);
+		link = g_list_find (list, checker);
 		g_return_if_fail (link != NULL);
-		editor->priv->active_spell_checkers =
-			g_list_delete_link (
-			editor->priv->active_spell_checkers, link);
+		list = g_list_delete_link (list, link);
 		g_object_unref (checker);
 	}
+	editor->priv->active_spell_checkers = list;
+	length = g_list_length (list);
+
+	/* Update "Add Word To" context menu item visibility. */
+	action_name = g_strdup_printf ("context-spell-add-%s", language_code);
+	add_action = gtkhtml_editor_get_action (editor, action_name);
+	gtk_action_set_visible (add_action, active);
+	g_free (action_name);
+
+	gtk_action_set_visible (ACTION (CONTEXT_SPELL_ADD), length == 1);
+	gtk_action_set_visible (ACTION (CONTEXT_SPELL_ADD_MENU), length > 1);
+	gtk_action_set_visible (ACTION (CONTEXT_SPELL_IGNORE), length > 0);
 
-	sensitive = (editor->priv->active_spell_checkers != NULL);
-	gtk_action_set_sensitive (ACTION (SPELL_CHECK), sensitive);
+	gtk_action_set_sensitive (ACTION (SPELL_CHECK), length > 0);
 }
 
 static void
@@ -1779,7 +1875,7 @@
 	  N_("Properties"),
 	  NULL,
 	  NULL,
-	  NULL }
+	  NULL },
 };
 
 /*****************************************************************************
@@ -1898,24 +1994,60 @@
 	  G_CALLBACK (action_context_remove_link_cb) }
 };
 
+/*****************************************************************************
+ * Context Menu Actions (spell checking only)
+ *
+ * These actions are only visible when the word underneath the cursor is
+ * misspelled.
+ *****************************************************************************/
+
+static GtkActionEntry spell_context_entries[] = {
+
+	{ "context-spell-add",
+	  NULL,
+	  N_("Add Word to Dictionary"),
+	  NULL,
+	  NULL,
+          G_CALLBACK (action_context_spell_add_cb) },
+
+	{ "context-spell-ignore",
+	  NULL,
+	  N_("Ignore Misspelled Word"),
+	  NULL,
+	  NULL,
+	  G_CALLBACK (action_context_spell_ignore_cb) },
+
+	{ "context-spell-add-menu",
+	  NULL,
+	  N_("Add Word To"),
+	  NULL,
+	  NULL,
+	  NULL }
+};
+
 static void
 editor_actions_setup_languages_menu (GtkhtmlEditor *editor)
 {
 	GtkUIManager *manager;
 	GtkActionGroup *action_group;
 	const GList *available_languages;
+	guint merge_id;
 
 	manager = editor->priv->manager;
 	action_group = editor->priv->language_actions;
 	available_languages = gtkhtml_spell_language_get_available ();
+	merge_id = gtk_ui_manager_new_merge_id (manager);
 
 	while (available_languages != NULL) {
 		GtkhtmlSpellLanguage *language = available_languages->data;
+		GtkhtmlSpellChecker *checker;
 		GtkToggleAction *action;
 
+		checker = gtkhtml_spell_checker_new (language);
+
 		g_hash_table_insert (
 			editor->priv->available_spell_checkers,
-			language, gtkhtml_spell_checker_new (language));
+			language, checker);
 
 		action = gtk_toggle_action_new (
 			gtkhtml_spell_language_get_code (language),
@@ -1929,8 +2061,10 @@
 		gtk_action_group_add_action (
 			action_group, GTK_ACTION (action));
 
+		g_object_unref (action);
+
 		gtk_ui_manager_add_ui (
-			manager, gtk_ui_manager_new_merge_id (manager),
+			manager, merge_id,
 			"/main-menu/edit-menu/language-menu",
 			gtkhtml_spell_language_get_code (language),
 			gtkhtml_spell_language_get_code (language),
@@ -1940,6 +2074,80 @@
 	}
 }
 
+static void
+editor_actions_setup_spell_check_menu (GtkhtmlEditor *editor)
+{
+	GtkUIManager *manager;
+	GtkActionGroup *action_group;
+	const GList *available_languages;
+	guint merge_id;
+
+	manager = editor->priv->manager;
+	action_group = editor->priv->spell_check_actions;;
+	available_languages = gtkhtml_spell_language_get_available ();
+	merge_id = gtk_ui_manager_new_merge_id (manager);
+
+	while (available_languages != NULL) {
+		GtkhtmlSpellLanguage *language = available_languages->data;
+		GtkAction *action;
+		const gchar *code;
+		const gchar *name;
+		gchar *action_label;
+		gchar *action_name;
+
+		code = gtkhtml_spell_language_get_code (language);
+		name = gtkhtml_spell_language_get_name (language);
+
+		/* Add a suggestion menu. */
+
+		action_name = g_strdup_printf (
+			"context-spell-suggest-%s-menu", code);
+
+		action = gtk_action_new (action_name, name, NULL, NULL);
+		gtk_action_group_add_action (action_group, action);
+		g_object_unref (action);
+
+		gtk_ui_manager_add_ui (
+			manager, merge_id,
+			"/context-menu/context-spell-suggest",
+			action_name, action_name,
+			GTK_UI_MANAGER_MENU, FALSE);
+
+		g_free (action_name);
+
+		/* Add an item to the "Add Word To" menu. */
+
+		action_label = g_strdup_printf ("%s Dictionary", name);
+		action_name = g_strdup_printf ("context-spell-add-%s", code);
+
+		action = gtk_action_new (
+			action_name, action_label, NULL, NULL);
+
+		g_signal_connect (
+			action, "activate",
+			G_CALLBACK (action_context_spell_add_cb), editor);
+
+		/* Visibility is dependent on whether the
+		 * corresponding language action is active. */
+		gtk_action_set_visible (action, FALSE);
+
+		gtk_action_group_add_action (action_group, action);
+
+		g_object_unref (action);
+
+		gtk_ui_manager_add_ui (
+			manager, merge_id,
+			"/context-menu/context-spell-add-menu",
+			action_name, action_name,
+			GTK_UI_MANAGER_AUTO, FALSE);
+
+		g_free (action_label);
+		g_free (action_name);
+
+		available_languages = g_list_next (available_languages);
+	}
+}
+
 void
 gtkhtml_editor_actions_init (GtkhtmlEditor *editor)
 {
@@ -2007,9 +2215,23 @@
 		G_N_ELEMENTS (html_context_entries), editor);
 	gtk_ui_manager_insert_action_group (manager, action_group, 0);
 
+	/* Context Menu Actions (spell check only) */
+	action_group = editor->priv->spell_check_actions;
+	gtk_action_group_set_translation_domain (
+		action_group, GETTEXT_PACKAGE);
+	gtk_action_group_add_actions (
+		action_group, spell_context_entries,
+		G_N_ELEMENTS (spell_context_entries), editor);
+	gtk_ui_manager_insert_action_group (manager, action_group, 0);
+
 	/* Language actions are generated dynamically. */
-	action_group = editor->priv->language_actions;
 	editor_actions_setup_languages_menu (editor);
+	action_group = editor->priv->language_actions;
+	gtk_ui_manager_insert_action_group (manager, action_group, 0);
+
+	/* Some spell check actions are generated dynamically. */
+	action_group = editor->priv->suggestion_actions;
+	editor_actions_setup_spell_check_menu (editor);
 	gtk_ui_manager_insert_action_group (manager, action_group, 0);
 
 	/* Fine Tuning */

Modified: branches/mbarnes-composer/components/editor/gtkhtml-editor-actions.h
==============================================================================
--- branches/mbarnes-composer/components/editor/gtkhtml-editor-actions.h	(original)
+++ branches/mbarnes-composer/components/editor/gtkhtml-editor-actions.h	Sat Mar  1 17:41:34 2008
@@ -68,6 +68,12 @@
 	GTKHTML_EDITOR_ACTION ((editor), "context-properties-text")
 #define GTKHTML_EDITOR_ACTION_CONTEXT_REMOVE_LINK(editor) \
 	GTKHTML_EDITOR_ACTION ((editor), "context-remove-link")
+#define GTKHTML_EDITOR_ACTION_CONTEXT_SPELL_ADD(editor) \
+	GTKHTML_EDITOR_ACTION ((editor), "context-spell-add")
+#define GTKHTML_EDITOR_ACTION_CONTEXT_SPELL_ADD_MENU(editor) \
+	GTKHTML_EDITOR_ACTION ((editor), "context-spell-add-menu")
+#define GTKHTML_EDITOR_ACTION_CONTEXT_SPELL_IGNORE(editor) \
+	GTKHTML_EDITOR_ACTION ((editor), "context-spell-ignore")
 #define GTKHTML_EDITOR_ACTION_COPY(editor) \
 	GTKHTML_EDITOR_ACTION ((editor), "copy")
 #define GTKHTML_EDITOR_ACTION_CUT(editor) \

Modified: branches/mbarnes-composer/components/editor/gtkhtml-editor-private.c
==============================================================================
--- branches/mbarnes-composer/components/editor/gtkhtml-editor-private.c	(original)
+++ branches/mbarnes-composer/components/editor/gtkhtml-editor-private.c	Sat Mar  1 17:41:34 2008
@@ -67,6 +67,16 @@
 }
 
 static void
+editor_recheck_cb (GtkhtmlEditor *editor,
+                   GtkhtmlSpellDialog *dialog)
+{
+	GtkHTML *html;
+
+	html = gtkhtml_editor_get_html (editor);
+	html_engine_spell_check (html->engine);
+}
+
+static void
 editor_replace_cb (GtkhtmlEditor *editor,
                    const gchar *correction,
 		   GtkhtmlSpellDialog *dialog)
@@ -130,12 +140,19 @@
 	priv->context_actions = gtk_action_group_new ("core-context");
 	priv->html_context_actions = gtk_action_group_new ("html-context");
 	priv->language_actions = gtk_action_group_new ("language");
+	priv->spell_check_actions = gtk_action_group_new ("spell-check");
+	priv->suggestion_actions = gtk_action_group_new ("suggestion");
 
+	/* GtkhtmlSpellLanguage -> GtkhtmlSpellChecker */
 	priv->available_spell_checkers = g_hash_table_new_full (
 		g_direct_hash, g_direct_equal,
 		(GDestroyNotify) NULL,
 		(GDestroyNotify) g_object_unref);
 
+	/* GtkhtmlSpellLanguage -> UI Merge ID */
+	priv->spell_suggestion_menus =
+		g_hash_table_new (g_direct_hash, g_direct_equal);
+
 	filename = gtkhtml_editor_find_data_file ("gtkhtml-editor.ui");
 	gtk_ui_manager_add_ui_from_file (priv->manager, filename, &error);
 	g_free (filename);
@@ -320,6 +337,8 @@
 	DISPOSE (priv->context_actions);
 	DISPOSE (priv->html_context_actions);
 	DISPOSE (priv->language_actions);
+	DISPOSE (priv->spell_check_actions);
+	DISPOSE (priv->suggestion_actions);
 	DISPOSE (priv->glade_xml);
 
 	DISPOSE (priv->html_painter);
@@ -353,6 +372,7 @@
 	GtkhtmlEditorPrivate *priv = editor->priv;
 
 	g_hash_table_destroy (priv->available_spell_checkers);
+	g_hash_table_destroy (priv->spell_suggestion_menus);
 
 	g_free (priv->filename);
 	g_free (priv->current_folder);
@@ -527,6 +547,14 @@
 		editor_set_word (editor, GTKHTML_SPELL_DIALOG (dialog));
 
 		g_signal_connect_swapped (
+			dialog, "added",
+			G_CALLBACK (editor_recheck_cb), editor);
+
+		g_signal_connect_swapped (
+			dialog, "ignored",
+			G_CALLBACK (editor_recheck_cb), editor);
+
+		g_signal_connect_swapped (
 			dialog, "next-word",
 			G_CALLBACK (editor_next_word_cb), editor);
 
@@ -556,6 +584,8 @@
 	GtkHTML *html;
 	gboolean found = FALSE;
 
+	g_return_val_if_fail (GTKHTML_IS_EDITOR (editor), FALSE);
+
 	html = gtkhtml_editor_get_html (editor);
 
 	html_engine_disable_selection (html->engine);
@@ -572,7 +602,11 @@
 	GtkHTML *html;
 	gboolean found = FALSE;
 
+	g_return_val_if_fail (GTKHTML_IS_EDITOR (editor), FALSE);
+
 	html = gtkhtml_editor_get_html (editor);
+
+	html_engine_disable_selection (html->engine);
 	html_engine_backward_word (html->engine);
 
 	while (!found && html_engine_backward_word (html->engine))
@@ -581,22 +615,109 @@
 	return found;
 }
 
+/* Action callback for context menu spelling suggestions.
+ * XXX This should really be in gtkhtml-editor-actions.c */
+static void
+action_context_spell_suggest_cb (GtkAction *action,
+                                 GtkhtmlEditor *editor)
+{
+	GtkHTML *html;
+	gchar *label;
+
+	html = gtkhtml_editor_get_html (editor);
+
+	/* The action's label is the replacement word. */
+	g_object_get (action, "label", &label, NULL);
+	g_return_if_fail (label != NULL);
+
+	html_engine_replace_spell_word_with (html->engine, label);
+
+	g_free (label);
+}
+
+/* Helper for gtkhtml_editor_update_context() */
+static void
+editor_spell_checkers_foreach (GtkhtmlSpellChecker *checker,
+                               GtkhtmlEditor *editor)
+{
+	const GtkhtmlSpellLanguage *language;
+	const gchar *language_code;
+	GtkActionGroup *action_group;
+	GtkUIManager *manager;
+	GtkHTML *html;
+	GList *list;
+	gchar *path;
+	gchar *word;
+	gint count = 0;
+	guint merge_id;
+
+	language = gtkhtml_spell_checker_get_language (checker);
+	language_code = gtkhtml_spell_language_get_code (language);
+
+	html = gtkhtml_editor_get_html (editor);
+	word = html_engine_get_spell_word (html->engine);
+	list = gtkhtml_spell_checker_get_suggestions (checker, word, -1);
+
+	manager = gtkhtml_editor_get_ui_manager (editor);
+	action_group = editor->priv->suggestion_actions;
+	merge_id = editor->priv->spell_suggestions_merge_id;
+
+	path = g_strdup_printf (
+		"/context-menu/context-spell-suggest/"
+		"context-spell-suggest-%s-menu", language_code);
+
+	while (list != NULL) {
+		gchar *action_label = list->data;
+		gchar *action_name;
+		GtkAction *action;
+
+		/* Action name just needs to be unique. */
+		action_name = g_strdup_printf (
+			"suggest-%s-%d", language_code, count++);
+
+		action = gtk_action_new (
+			action_name, action_label, NULL, NULL);
+
+		g_signal_connect (
+			action, "activate", G_CALLBACK (
+			action_context_spell_suggest_cb), editor);
+
+		gtk_action_group_add_action (action_group, action);
+
+		gtk_ui_manager_add_ui (
+			manager, merge_id, path,
+			action_name, action_name,
+			GTK_UI_MANAGER_AUTO, FALSE);
+
+		g_free (action_label);
+		g_free (action_name);
+
+		list = g_list_delete_link (list, list);
+	}
+
+	g_free (path);
+	g_free (word);
+}
+
 void
 gtkhtml_editor_update_context (GtkhtmlEditor *editor)
 {
 	GtkHTML *html;
 	HTMLType type;
 	HTMLObject *object;
-	gboolean selection;
+	GtkUIManager *manager;
+	GtkActionGroup *action_group;
+	GList *list;
 	gboolean visible;
+	guint merge_id;
 
 	html = gtkhtml_editor_get_html (editor);
+	manager = gtkhtml_editor_get_ui_manager (editor);
 	gtk_html_update_styles (html);
 
 	/* Update context menu item visibility. */
 
 	object = html->engine->cursor->object;
-	selection = html_engine_is_selection_active (html->engine);
 
 	if (object != NULL)
 		type = HTML_OBJECT_TYPE (object);
@@ -627,7 +748,7 @@
 	 *   - Cursor is on an image that has a URL or target.
 	 */
 	visible =
-		(selection &&
+		(html_engine_is_selection_active (html->engine) &&
 		 html_engine_selection_contains_link (html->engine)) ||
 		(type == HTML_TYPE_LINKTEXT) ||
 		(type == HTML_TYPE_IMAGE &&
@@ -669,4 +790,44 @@
 	/* Note the |= (cursor must be in a table cell). */
 	visible |= (type == HTML_TYPE_TABLE);
 	gtk_action_set_visible (ACTION (CONTEXT_PROPERTIES_TABLE), visible);
+
+	/********************** Spell Check Suggestions **********************/
+
+	object = html->engine->cursor->object;
+	action_group = editor->priv->suggestion_actions;
+
+	/* Remove the old content from the context menu. */
+	merge_id = editor->priv->spell_suggestions_merge_id;
+	if (merge_id > 0) {
+		gtk_ui_manager_remove_ui (manager, merge_id);
+		editor->priv->spell_suggestions_merge_id = 0;
+	}
+
+	/* Clear the action group for spelling suggestions. */
+	list = gtk_action_group_list_actions (action_group);
+	while (list != NULL) {
+		GtkAction *action = list->data;
+
+		gtk_action_group_remove_action (action_group, action);
+		list = g_list_delete_link (list, list);
+	}
+
+	/* Decide if we should show spell checking items. */
+	visible =
+		!html_engine_is_selection_active (html->engine) &&
+		object != NULL && html_object_is_text (object) &&
+		!html_engine_spell_word_is_valid (html->engine);
+	action_group = editor->priv->spell_check_actions;
+	gtk_action_group_set_visible (action_group, visible);
+
+	/* Exit early if spell checking items are invisible. */
+	if (!visible)
+		return;
+
+	list = editor->priv->active_spell_checkers;
+	merge_id = gtk_ui_manager_new_merge_id (manager);
+	editor->priv->spell_suggestions_merge_id = merge_id;
+
+	/* Add actions and context menu content for active languages. */
+	g_list_foreach (list, (GFunc) editor_spell_checkers_foreach, editor);
 }

Modified: branches/mbarnes-composer/components/editor/gtkhtml-editor-private.h
==============================================================================
--- branches/mbarnes-composer/components/editor/gtkhtml-editor-private.h	(original)
+++ branches/mbarnes-composer/components/editor/gtkhtml-editor-private.h	Sat Mar  1 17:41:34 2008
@@ -100,6 +100,8 @@
 	GtkActionGroup *context_actions;
 	GtkActionGroup *html_context_actions;
 	GtkActionGroup *language_actions;
+	GtkActionGroup *spell_check_actions;
+	GtkActionGroup *suggestion_actions;
 	GladeXML *glade_xml;
 
 	/*** Rendering ***/
@@ -110,7 +112,9 @@
 	/*** Spell Checking ***/
 
 	GHashTable *available_spell_checkers;
+	GHashTable *spell_suggestion_menus;
 	GList *active_spell_checkers;
+	guint spell_suggestions_merge_id;
 
 	/*** Main Window Widgets ***/
 

Modified: branches/mbarnes-composer/components/editor/gtkhtml-editor.ui
==============================================================================
--- branches/mbarnes-composer/components/editor/gtkhtml-editor.ui	(original)
+++ branches/mbarnes-composer/components/editor/gtkhtml-editor.ui	Sat Mar  1 17:41:34 2008
@@ -170,7 +170,11 @@
       <menuitem action='context-delete-cell'/>
     </menu>
     <separator/>
-    <placeholder name='context-spell-suggestions'/>
+    <placeholder name='context-spell-suggest'/>
+    <separator/>
+    <menuitem action='context-spell-ignore'/>
+    <menu action='context-spell-add-menu'/>
+    <menuitem action='context-spell-add'/>
     <separator/>
     <menu action='context-input-methods-menu'/>
   </popup>

Modified: branches/mbarnes-composer/components/editor/gtkhtml-spell-checker.c
==============================================================================
--- branches/mbarnes-composer/components/editor/gtkhtml-spell-checker.c	(original)
+++ branches/mbarnes-composer/components/editor/gtkhtml-spell-checker.c	Sat Mar  1 17:41:34 2008
@@ -80,29 +80,6 @@
 	return TRUE;
 }
 
-static const GtkhtmlSpellLanguage *
-spell_checker_pick_default_language (void)
-{
-	const GtkhtmlSpellLanguage *language = NULL;
-	const gchar * const *language_names;
-	const GList *available_languages;
-	gint ii;
-
-	language_names = g_get_language_names ();
-	available_languages = gtkhtml_spell_language_get_available ();
-
-	for (ii = 0; language == NULL && language_names[ii] != NULL; ii++)
-		language = gtkhtml_spell_language_lookup (language_names[ii]);
-
-	if (language == NULL)
-		language = gtkhtml_spell_language_lookup ("en_US");
-
-	if (language == NULL && available_languages != NULL)
-		language = available_languages->data;
-
-	return language;
-}
-
 static GObject *
 spell_checker_constructor (GType type,
                            guint n_construct_properties,
@@ -399,7 +376,7 @@
 	}
 
 	if (priv->language == NULL)
-		priv->language = spell_checker_pick_default_language ();
+		priv->language = gtkhtml_spell_language_lookup (NULL);
 
 	if (priv->language != NULL) {
 		const gchar *code;

Modified: branches/mbarnes-composer/components/editor/gtkhtml-spell-dialog.c
==============================================================================
--- branches/mbarnes-composer/components/editor/gtkhtml-spell-dialog.c	(original)
+++ branches/mbarnes-composer/components/editor/gtkhtml-spell-dialog.c	Sat Mar  1 17:41:34 2008
@@ -38,6 +38,8 @@
 };
 
 enum {
+	ADDED,
+	IGNORED,
 	NEXT_WORD,
 	PREV_WORD,
 	REPLACE,
@@ -66,6 +68,25 @@
 static guint signals[LAST_SIGNAL];
 
 static void
+spell_dialog_render_checker (GtkComboBox *combo_box,
+                             GtkCellRenderer *renderer,
+                             GtkTreeModel *model,
+                             GtkTreeIter *iter)
+{
+	const GtkhtmlSpellLanguage *language;
+	GtkhtmlSpellChecker *checker;
+	const gchar *name;
+
+	gtk_tree_model_get (model, iter, 0, &checker, -1);
+	language = gtkhtml_spell_checker_get_language (checker);
+	name = gtkhtml_spell_language_get_name (language);
+
+	g_object_set (renderer, "text", name, NULL);
+
+	g_object_unref (checker);
+}
+
+static void
 spell_dialog_update_buttons (GtkhtmlSpellDialog *dialog)
 {
 	gboolean sensitive;
@@ -155,6 +176,8 @@
 	word = gtkhtml_spell_dialog_get_word (dialog);
 
 	gtkhtml_spell_checker_add_word (checker, word, -1);
+	g_signal_emit (dialog, signals[ADDED], 0);
+
 	gtkhtml_spell_dialog_next_word (dialog);
 }
 
@@ -168,6 +191,8 @@
 	word = gtkhtml_spell_dialog_get_word (dialog);
 
 	gtkhtml_spell_checker_add_word_to_session (checker, word, -1);
+	g_signal_emit (dialog, signals[IGNORED], 0);
+
 	gtkhtml_spell_dialog_next_word (dialog);
 }
 
@@ -335,6 +360,22 @@
 			NULL,
 			G_PARAM_READWRITE));
 
+	signals[ADDED] = g_signal_new (
+		"added",
+		G_OBJECT_CLASS_TYPE (object_class),
+		G_SIGNAL_RUN_LAST,
+		0, NULL, NULL,
+		g_cclosure_marshal_VOID__VOID,
+		G_TYPE_NONE, 0);
+
+	signals[IGNORED] = g_signal_new (
+		"ignored",
+		G_OBJECT_CLASS_TYPE (object_class),
+		G_SIGNAL_RUN_LAST,
+		0, NULL, NULL,
+		g_cclosure_marshal_VOID__VOID,
+		G_TYPE_NONE, 0);
+
 	signals[NEXT_WORD] = g_signal_new (
 		"next-word",
 		G_OBJECT_CLASS_TYPE (object_class),
@@ -558,9 +599,10 @@
 	renderer = gtk_cell_renderer_text_new ();
 	gtk_cell_layout_pack_start (
 		GTK_CELL_LAYOUT (widget), renderer, TRUE);
-	gtk_cell_layout_add_attribute (
+	gtk_cell_layout_set_cell_data_func (
 		GTK_CELL_LAYOUT (widget), renderer,
-		"text", COMBO_COLUMN_TEXT);
+		(GtkCellLayoutDataFunc) spell_dialog_render_checker,
+		NULL, NULL);
 	g_signal_connect_swapped (
 		widget, "changed",
 		G_CALLBACK (spell_dialog_update_tree_view), dialog);
@@ -689,31 +731,23 @@
 	g_list_foreach (list, (GFunc) g_object_unref, NULL);
 	g_list_free (list);
 
-	/* Copy the new list of spell checkers. */
-	list = g_list_copy (spell_checkers);
+	/* Copy and sort the new list of spell checkers. */
+	list = g_list_sort (
+		g_list_copy (spell_checkers),
+		(GCompareFunc) gtkhtml_spell_checker_compare);
 	g_list_foreach (list, (GFunc) g_object_ref, NULL);
 	dialog->priv->spell_checkers = list;
 
 	/* Populate a list store for the combo box. */
 
-	store = gtk_list_store_new (
-		2, GTKHTML_TYPE_SPELL_CHECKER, G_TYPE_STRING);
+	store = gtk_list_store_new (1, GTKHTML_TYPE_SPELL_CHECKER);
 
 	while (list != NULL) {
 		GtkhtmlSpellChecker *checker = list->data;
-		const GtkhtmlSpellLanguage *language;
-		const gchar *language_name;
 		GtkTreeIter iter;
 
-		language = gtkhtml_spell_checker_get_language (checker);
-		language_name = gtkhtml_spell_language_get_name (language);
-
 		gtk_list_store_append (store, &iter);
-
-		gtk_list_store_set (
-			store, &iter,
-			COMBO_COLUMN_CHECKER, checker,
-			COMBO_COLUMN_TEXT, language_name, -1);
+		gtk_list_store_set (store, &iter, 0, checker, -1);
 
 		list = g_list_next (list);
 	}

Modified: branches/mbarnes-composer/components/editor/gtkhtml-spell-language.c
==============================================================================
--- branches/mbarnes-composer/components/editor/gtkhtml-spell-language.c	(original)
+++ branches/mbarnes-composer/components/editor/gtkhtml-spell-language.c	Sat Mar  1 17:41:34 2008
@@ -322,17 +322,38 @@
 	return available_languages;
 }
 
+static const GtkhtmlSpellLanguage *
+spell_language_pick_default (void)
+{
+	const GtkhtmlSpellLanguage *language = NULL;
+	const gchar * const *language_names;
+	const GList *available_languages;
+	gint ii;
+
+	language_names = g_get_language_names ();
+	available_languages = gtkhtml_spell_language_get_available ();
+
+	for (ii = 0; language == NULL && language_names[ii] != NULL; ii++)
+		language = gtkhtml_spell_language_lookup (language_names[ii]);
+
+	if (language == NULL)
+		language = gtkhtml_spell_language_lookup ("en_US");
+
+	if (language == NULL && available_languages != NULL)
+		language = available_languages->data;
+
+	return language;
+}
+
 const GtkhtmlSpellLanguage *
 gtkhtml_spell_language_lookup (const gchar *language_code)
 {
 	const GtkhtmlSpellLanguage *closest_match = NULL;
 	const GList *available_languages;
 
-	g_return_val_if_fail (language_code != NULL, NULL);
-
 	available_languages = gtkhtml_spell_language_get_available ();
 
-	while (available_languages != NULL) {
+	while (available_languages != NULL && language_code != NULL) {
 		GtkhtmlSpellLanguage *language = available_languages->data;
 		const gchar *code = language->code;
 		gsize length = strlen (code);
@@ -346,6 +367,9 @@
 		available_languages = g_list_next (available_languages);
 	}
 
+	if (closest_match == NULL)
+		closest_match = spell_language_pick_default ();
+
 	return closest_match;
 }
 



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