[gedit/wip/spell-checking: 2/2] spell: factor out a SpellNavigator



commit 269acb4f77d775c15c442803ac9cc55583b5f190
Author: Sébastien Wilmet <swilmet gnome org>
Date:   Sat Aug 1 20:00:33 2015 +0200

    spell: factor out a SpellNavigator
    
    The idea is to make more code reusable. gedit-spell-plugin is specific
    to gedit.
    
    GeditSpellNavigator is an interface.
    GeditSpellNavigatorGtv implements the interface for a GtkTextView.
    
    A later commit will remove the SpellCheckerDialog signals, and use
    directly a SpellNavigator. That way, to support other types of buffers
    (e.g. for WebKitGTK+), one just need to implement a new SpellNavigator.
    Implementing a new spell checker widget is also easy, for example
    instead of a dialog window, one can implement a new widget that could be
    packed below an headerbar.
    
    The UI and the logic are separated.

 plugins/spell/Makefile.am                 |    4 +
 plugins/spell/gedit-spell-navigator-gtv.c |  386 +++++++++++++++++++++++
 plugins/spell/gedit-spell-navigator-gtv.h |   47 +++
 plugins/spell/gedit-spell-navigator.c     |  100 ++++++
 plugins/spell/gedit-spell-navigator.h     |   62 ++++
 plugins/spell/gedit-spell-plugin.c        |  487 ++---------------------------
 6 files changed, 622 insertions(+), 464 deletions(-)
---
diff --git a/plugins/spell/Makefile.am b/plugins/spell/Makefile.am
index c80ef77..5d6d667 100644
--- a/plugins/spell/Makefile.am
+++ b/plugins/spell/Makefile.am
@@ -34,6 +34,10 @@ plugins_spell_libspell_la_SOURCES =                  \
        plugins/spell/gedit-spell-checker-language.h    \
        plugins/spell/gedit-spell-language-dialog.c     \
        plugins/spell/gedit-spell-language-dialog.h     \
+       plugins/spell/gedit-spell-navigator.c           \
+       plugins/spell/gedit-spell-navigator.h           \
+       plugins/spell/gedit-spell-navigator-gtv.c       \
+       plugins/spell/gedit-spell-navigator-gtv.h       \
        plugins/spell/gedit-automatic-spell-checker.c   \
        plugins/spell/gedit-automatic-spell-checker.h   \
        plugins/spell/gedit-spell-utils.c               \
diff --git a/plugins/spell/gedit-spell-navigator-gtv.c b/plugins/spell/gedit-spell-navigator-gtv.c
new file mode 100644
index 0000000..3ae97c9
--- /dev/null
+++ b/plugins/spell/gedit-spell-navigator-gtv.c
@@ -0,0 +1,386 @@
+/*
+ * gedit-spell-navigator-gtv.c
+ * This file is part of gedit
+ *
+ * Copyright (C) 2015 - Sébastien Wilmet <swilmet gnome org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "gedit-spell-navigator-gtv.h"
+#include "gedit-spell-utils.h"
+
+typedef struct _GeditSpellNavigatorGtvPrivate GeditSpellNavigatorGtvPrivate;
+
+struct _GeditSpellNavigatorGtvPrivate
+{
+       GtkTextView *view;
+       GtkTextBuffer *buffer;
+       GeditSpellChecker *spell_checker;
+
+       /* Delimit the region to spell check. */
+       GtkTextMark *start_boundary;
+       GtkTextMark *end_boundary;
+
+       /* Current misspelled word. */
+       GtkTextMark *word_start;
+       GtkTextMark *word_end;
+};
+
+enum
+{
+       PROP_0,
+       PROP_VIEW,
+       PROP_SPELL_CHECKER,
+};
+
+static void gedit_spell_navigator_iface_init (gpointer g_iface, gpointer iface_data);
+
+G_DEFINE_TYPE_WITH_CODE (GeditSpellNavigatorGtv,
+                        gedit_spell_navigator_gtv,
+                        G_TYPE_OBJECT,
+                        G_ADD_PRIVATE (GeditSpellNavigatorGtv)
+                        G_IMPLEMENT_INTERFACE (GEDIT_TYPE_SPELL_NAVIGATOR,
+                                               gedit_spell_navigator_iface_init))
+
+static void
+init_boundaries (GeditSpellNavigatorGtv *navigator)
+{
+       GeditSpellNavigatorGtvPrivate *priv;
+       GtkTextIter start;
+       GtkTextIter end;
+
+       priv = gedit_spell_navigator_gtv_get_instance_private (navigator);
+
+       g_return_if_fail (priv->start_boundary == NULL);
+       g_return_if_fail (priv->end_boundary == NULL);
+
+       if (!gtk_text_buffer_get_selection_bounds (priv->buffer, &start, &end))
+       {
+               /* No selection, take the whole buffer. */
+               gtk_text_buffer_get_bounds (priv->buffer, &start, &end);
+       }
+
+       if (gtk_text_iter_inside_word (&start) &&
+           !gtk_text_iter_starts_word (&start))
+       {
+               gtk_text_iter_backward_word_start (&start);
+       }
+
+       if (gtk_text_iter_inside_word (&end))
+       {
+               gtk_text_iter_forward_word_end (&end);
+       }
+
+       priv->start_boundary = gtk_text_buffer_create_mark (priv->buffer, NULL, &start, TRUE);
+       priv->end_boundary = gtk_text_buffer_create_mark (priv->buffer, NULL, &end, FALSE);
+}
+
+static void
+set_view (GeditSpellNavigatorGtv *navigator,
+         GtkTextView            *view)
+{
+       GeditSpellNavigatorGtvPrivate *priv;
+
+       priv = gedit_spell_navigator_gtv_get_instance_private (navigator);
+
+       g_return_if_fail (priv->view == NULL);
+       g_return_if_fail (priv->buffer == NULL);
+
+       priv->view = g_object_ref (view);
+       priv->buffer = g_object_ref (gtk_text_view_get_buffer (view));
+
+       init_boundaries (navigator);
+
+       g_object_notify (G_OBJECT (navigator), "view");
+}
+
+static void
+set_spell_checker (GeditSpellNavigatorGtv *navigator,
+                  GeditSpellChecker      *spell_checker)
+{
+       GeditSpellNavigatorGtvPrivate *priv;
+
+       priv = gedit_spell_navigator_gtv_get_instance_private (navigator);
+
+       if (g_set_object (&priv->spell_checker, spell_checker))
+       {
+               g_object_notify (G_OBJECT (navigator), "spell-checker");
+       }
+}
+
+static void
+gedit_spell_navigator_gtv_get_property (GObject    *object,
+                                       guint       prop_id,
+                                       GValue     *value,
+                                       GParamSpec *pspec)
+{
+       GeditSpellNavigatorGtvPrivate *priv;
+
+       priv = gedit_spell_navigator_gtv_get_instance_private (GEDIT_SPELL_NAVIGATOR_GTV (object));
+
+       switch (prop_id)
+       {
+               case PROP_VIEW:
+                       g_value_set_object (value, priv->view);
+                       break;
+
+               case PROP_SPELL_CHECKER:
+                       g_value_set_object (value, priv->spell_checker);
+                       break;
+
+               default:
+                       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+                       break;
+       }
+}
+
+static void
+gedit_spell_navigator_gtv_set_property (GObject      *object,
+                                       guint         prop_id,
+                                       const GValue *value,
+                                       GParamSpec   *pspec)
+{
+       GeditSpellNavigatorGtv *navigator = GEDIT_SPELL_NAVIGATOR_GTV (object);
+
+       switch (prop_id)
+       {
+               case PROP_VIEW:
+                       set_view (navigator, g_value_get_object (value));
+                       break;
+
+               case PROP_SPELL_CHECKER:
+                       set_spell_checker (navigator, g_value_get_object (value));
+                       break;
+
+               default:
+                       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+                       break;
+       }
+}
+
+static void
+gedit_spell_navigator_gtv_dispose (GObject *object)
+{
+       GeditSpellNavigatorGtvPrivate *priv;
+
+       priv = gedit_spell_navigator_gtv_get_instance_private (GEDIT_SPELL_NAVIGATOR_GTV (object));
+
+       g_clear_object (&priv->view);
+       g_clear_object (&priv->spell_checker);
+
+       if (priv->buffer != NULL)
+       {
+               if (priv->start_boundary != NULL)
+               {
+                       gtk_text_buffer_delete_mark (priv->buffer, priv->start_boundary);
+                       priv->start_boundary = NULL;
+               }
+
+               if (priv->end_boundary != NULL)
+               {
+                       gtk_text_buffer_delete_mark (priv->buffer, priv->end_boundary);
+                       priv->end_boundary = NULL;
+               }
+
+               if (priv->word_start != NULL)
+               {
+                       gtk_text_buffer_delete_mark (priv->buffer, priv->word_start);
+                       priv->word_start = NULL;
+               }
+
+               if (priv->word_end != NULL)
+               {
+                       gtk_text_buffer_delete_mark (priv->buffer, priv->word_end);
+                       priv->word_end = NULL;
+               }
+
+               g_object_unref (priv->buffer);
+               priv->buffer = NULL;
+       }
+
+       G_OBJECT_CLASS (gedit_spell_navigator_gtv_parent_class)->dispose (object);
+}
+
+static void
+gedit_spell_navigator_gtv_class_init (GeditSpellNavigatorGtvClass *klass)
+{
+       GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+       object_class->get_property = gedit_spell_navigator_gtv_get_property;
+       object_class->set_property = gedit_spell_navigator_gtv_set_property;
+       object_class->dispose = gedit_spell_navigator_gtv_dispose;
+
+       g_object_class_install_property (object_class,
+                                        PROP_VIEW,
+                                        g_param_spec_object ("view",
+                                                             "View",
+                                                             "",
+                                                             GTK_TYPE_TEXT_VIEW,
+                                                             G_PARAM_READWRITE |
+                                                             G_PARAM_CONSTRUCT_ONLY |
+                                                             G_PARAM_STATIC_STRINGS));
+
+       g_object_class_override_property (object_class, PROP_SPELL_CHECKER, "spell-checker");
+}
+
+static void
+gedit_spell_navigator_gtv_init (GeditSpellNavigatorGtv *self)
+{
+}
+
+static gchar *
+gedit_spell_navigator_gtv_goto_next (GeditSpellNavigator *navigator)
+{
+       GeditSpellNavigatorGtvPrivate *priv;
+       GtkTextIter word_start;
+       GtkTextIter end;
+
+       priv = gedit_spell_navigator_gtv_get_instance_private (GEDIT_SPELL_NAVIGATOR_GTV (navigator));
+
+       g_assert ((priv->word_start == NULL && priv->word_end == NULL) ||
+                 (priv->word_start != NULL && priv->word_end != NULL));
+
+       gtk_text_buffer_get_iter_at_mark (priv->buffer, &end, priv->end_boundary);
+
+       if (priv->word_start == NULL)
+       {
+               GtkTextIter start;
+
+               gtk_text_buffer_get_iter_at_mark (priv->buffer, &start, priv->start_boundary);
+
+               priv->word_start = gtk_text_buffer_create_mark (priv->buffer, NULL, &start, TRUE);
+               priv->word_end = gtk_text_buffer_create_mark (priv->buffer, NULL, &start, FALSE);
+
+               word_start = start;
+       }
+       else
+       {
+               GtkTextIter word_end;
+
+               gtk_text_buffer_get_iter_at_mark (priv->buffer, &word_end, priv->word_end);
+
+               if (gtk_text_iter_compare (&end, &word_end) <= 0)
+               {
+                       return NULL;
+               }
+
+               word_start = word_end;
+       }
+
+       while (TRUE)
+       {
+               GtkTextIter word_end;
+               gchar *word;
+               gboolean correctly_spelled;
+               GError *error = NULL;
+
+               if (!gtk_text_iter_starts_word (&word_start))
+               {
+                       GtkTextIter iter;
+
+                       iter = word_start;
+                       gtk_text_iter_forward_word_end (&word_start);
+
+                       if (gtk_text_iter_equal (&iter, &word_start))
+                       {
+                               /* Didn't move, we are at the end. */
+                               return NULL;
+                       }
+
+                       gtk_text_iter_backward_word_start (&word_start);
+               }
+
+               if (!gedit_spell_utils_skip_no_spell_check (&word_start, &end))
+               {
+                       return NULL;
+               }
+
+               g_return_val_if_fail (gtk_text_iter_starts_word (&word_start), NULL);
+
+               word_end = word_start;
+               gtk_text_iter_forward_word_end (&word_end);
+
+               if (gtk_text_iter_compare (&end, &word_end) < 0)
+               {
+                       return NULL;
+               }
+
+               word = gtk_text_buffer_get_text (priv->buffer, &word_start, &word_end, FALSE);
+
+               correctly_spelled = gedit_spell_checker_check_word (priv->spell_checker, word, &error);
+
+               if (error != NULL)
+               {
+                       g_warning ("Spell checking: %s", error->message);
+                       g_error_free (error);
+                       g_free (word);
+                       return NULL;
+               }
+
+               if (!correctly_spelled)
+               {
+                       /* Found! */
+                       gtk_text_buffer_move_mark (priv->buffer, priv->word_start, &word_start);
+                       gtk_text_buffer_move_mark (priv->buffer, priv->word_end, &word_end);
+
+                       return word;
+               }
+
+               word_start = word_end;
+               g_free (word);
+       }
+
+       return NULL;
+}
+
+static void
+gedit_spell_navigator_gtv_change (GeditSpellNavigator *navigator,
+                                 const gchar         *word,
+                                 const gchar         *change_to)
+{
+}
+
+static void
+gedit_spell_navigator_gtv_change_all (GeditSpellNavigator *navigator,
+                                     const gchar         *word,
+                                     const gchar         *change_to)
+{
+}
+
+static void
+gedit_spell_navigator_iface_init (gpointer g_iface,
+                                 gpointer iface_data)
+{
+       GeditSpellNavigatorInterface *iface = g_iface;
+
+       iface->goto_next = gedit_spell_navigator_gtv_goto_next;
+       iface->change = gedit_spell_navigator_gtv_change;
+       iface->change_all = gedit_spell_navigator_gtv_change_all;
+}
+
+GeditSpellNavigator *
+gedit_spell_navigator_gtv_new (GtkTextView       *view,
+                              GeditSpellChecker *spell_checker)
+{
+       g_return_val_if_fail (GTK_IS_TEXT_VIEW (view), NULL);
+       g_return_val_if_fail (GEDIT_IS_SPELL_CHECKER (spell_checker), NULL);
+
+       return g_object_new (GEDIT_TYPE_SPELL_NAVIGATOR_GTV,
+                            "view", view,
+                            "spell-checker", spell_checker,
+                            NULL);
+}
+
+/* ex:set ts=8 noet: */
diff --git a/plugins/spell/gedit-spell-navigator-gtv.h b/plugins/spell/gedit-spell-navigator-gtv.h
new file mode 100644
index 0000000..487e3fd
--- /dev/null
+++ b/plugins/spell/gedit-spell-navigator-gtv.h
@@ -0,0 +1,47 @@
+/*
+ * gedit-spell-navigator-gtv.h
+ * This file is part of gedit
+ *
+ * Copyright (C) 2015 - Sébastien Wilmet <swilmet gnome org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __GEDIT_SPELL_NAVIGATOR_GTV_H__
+#define __GEDIT_SPELL_NAVIGATOR_GTV_H__
+
+#include <gtk/gtk.h>
+#include "gedit-spell-navigator.h"
+#include "gedit-spell-checker.h"
+
+G_BEGIN_DECLS
+
+#define GEDIT_TYPE_SPELL_NAVIGATOR_GTV (gedit_spell_navigator_gtv_get_type ())
+G_DECLARE_DERIVABLE_TYPE (GeditSpellNavigatorGtv, gedit_spell_navigator_gtv,
+                         GEDIT, SPELL_NAVIGATOR_GTV,
+                         GObject)
+
+struct _GeditSpellNavigatorGtvClass
+{
+       GObjectClass parent_class;
+};
+
+GeditSpellNavigator *  gedit_spell_navigator_gtv_new           (GtkTextView       *view,
+                                                                GeditSpellChecker *spell_checker);
+
+G_END_DECLS
+
+#endif /* __GEDIT_SPELL_NAVIGATOR_GTV_H__ */
+
+/* ex:set ts=8 noet: */
diff --git a/plugins/spell/gedit-spell-navigator.c b/plugins/spell/gedit-spell-navigator.c
new file mode 100644
index 0000000..837e7df
--- /dev/null
+++ b/plugins/spell/gedit-spell-navigator.c
@@ -0,0 +1,100 @@
+/*
+ * gedit-spell-navigator.c
+ * This file is part of gedit
+ *
+ * Copyright (C) 2015 - Sébastien Wilmet <swilmet gnome org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "gedit-spell-navigator.h"
+#include "gedit-spell-checker.h"
+
+G_DEFINE_INTERFACE (GeditSpellNavigator, gedit_spell_navigator, G_TYPE_OBJECT)
+
+static gchar *
+gedit_spell_navigator_goto_next_default (GeditSpellNavigator *navigator)
+{
+       return NULL;
+}
+
+static void
+gedit_spell_navigator_change_default (GeditSpellNavigator *navigator,
+                                     const gchar         *word,
+                                     const gchar         *change_to)
+{
+}
+
+static void
+gedit_spell_navigator_change_all_default (GeditSpellNavigator *navigator,
+                                         const gchar         *word,
+                                         const gchar         *change_to)
+{
+}
+
+static void
+gedit_spell_navigator_default_init (GeditSpellNavigatorInterface *iface)
+{
+       iface->goto_next = gedit_spell_navigator_goto_next_default;
+       iface->change = gedit_spell_navigator_change_default;
+       iface->change_all = gedit_spell_navigator_change_all_default;
+
+       g_object_interface_install_property (iface,
+                                            g_param_spec_object ("spell-checker",
+                                                                 "Spell Checker",
+                                                                 "",
+                                                                 GEDIT_TYPE_SPELL_CHECKER,
+                                                                 G_PARAM_READWRITE |
+                                                                 G_PARAM_CONSTRUCT |
+                                                                 G_PARAM_STATIC_STRINGS));
+}
+
+/**
+ * gedit_spell_navigator_goto_next:
+ * @navigator: a #GeditSpellNavigator.
+ *
+ * Goes to the next misspelled word. When called the first time, goes to the
+ * first misspelled word.
+ *
+ * Returns: the next misspelled word, or %NULL if finished.
+ */
+gchar *
+gedit_spell_navigator_goto_next (GeditSpellNavigator *navigator)
+{
+       g_return_val_if_fail (GEDIT_IS_SPELL_NAVIGATOR (navigator), NULL);
+
+       return GEDIT_SPELL_NAVIGATOR_GET_IFACE (navigator)->goto_next (navigator);
+}
+
+void
+gedit_spell_navigator_change (GeditSpellNavigator *navigator,
+                             const gchar         *word,
+                             const gchar         *change_to)
+{
+       g_return_if_fail (GEDIT_IS_SPELL_NAVIGATOR (navigator));
+
+       GEDIT_SPELL_NAVIGATOR_GET_IFACE (navigator)->change (navigator, word, change_to);
+}
+
+void
+gedit_spell_navigator_change_all (GeditSpellNavigator *navigator,
+                                 const gchar         *word,
+                                 const gchar         *change_to)
+{
+       g_return_if_fail (GEDIT_IS_SPELL_NAVIGATOR (navigator));
+
+       GEDIT_SPELL_NAVIGATOR_GET_IFACE (navigator)->change_all (navigator, word, change_to);
+}
+
+/* ex:set ts=8 noet: */
diff --git a/plugins/spell/gedit-spell-navigator.h b/plugins/spell/gedit-spell-navigator.h
new file mode 100644
index 0000000..92e6197
--- /dev/null
+++ b/plugins/spell/gedit-spell-navigator.h
@@ -0,0 +1,62 @@
+/*
+ * gedit-spell-navigator.h
+ * This file is part of gedit
+ *
+ * Copyright (C) 2015 - Sébastien Wilmet <swilmet gnome org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __GEDIT_SPELL_NAVIGATOR_H__
+#define __GEDIT_SPELL_NAVIGATOR_H__
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define GEDIT_TYPE_SPELL_NAVIGATOR (gedit_spell_navigator_get_type ())
+G_DECLARE_INTERFACE (GeditSpellNavigator, gedit_spell_navigator,
+                    GEDIT, SPELL_NAVIGATOR,
+                    GObject)
+
+struct _GeditSpellNavigatorInterface
+{
+       GTypeInterface parent_interface;
+
+       gchar *         (* goto_next)           (GeditSpellNavigator *navigator);
+
+       void            (* change)              (GeditSpellNavigator *navigator,
+                                                const gchar         *word,
+                                                const gchar         *change_to);
+
+       void            (* change_all)          (GeditSpellNavigator *navigator,
+                                                const gchar         *word,
+                                                const gchar         *change_to);
+};
+
+gchar *                gedit_spell_navigator_goto_next         (GeditSpellNavigator *navigator);
+
+void           gedit_spell_navigator_change            (GeditSpellNavigator *navigator,
+                                                        const gchar         *word,
+                                                        const gchar         *change_to);
+
+void           gedit_spell_navigator_change_all        (GeditSpellNavigator *navigator,
+                                                        const gchar         *word,
+                                                        const gchar         *change_to);
+
+G_END_DECLS
+
+#endif /* __GEDIT_SPELL_NAVIGATOR_H__ */
+
+/* ex:set ts=8 noet: */
diff --git a/plugins/spell/gedit-spell-plugin.c b/plugins/spell/gedit-spell-plugin.c
index e174ae7..0b2bf2b 100644
--- a/plugins/spell/gedit-spell-plugin.c
+++ b/plugins/spell/gedit-spell-plugin.c
@@ -33,6 +33,7 @@
 #include "gedit-spell-checker.h"
 #include "gedit-spell-checker-dialog.h"
 #include "gedit-spell-language-dialog.h"
+#include "gedit-spell-navigator-gtv.h"
 #include "gedit-spell-utils.h"
 
 #ifdef G_OS_WIN32
@@ -46,6 +47,7 @@
 #define SPELL_ENABLED_STR "1"
 
 #define VIEW_DATA_KEY "GeditSpellPlugin-ViewData"
+#define NAVIGATOR_KEY "GeditSpellPlugin-Navigator"
 
 static void gedit_window_activatable_iface_init (GeditWindowActivatableInterface *iface);
 
@@ -58,19 +60,6 @@ struct _GeditSpellPluginPrivate
        gulong          tab_removed_id;
 };
 
-typedef struct _CheckRange CheckRange;
-
-struct _CheckRange
-{
-       GtkTextMark *start_mark;
-       GtkTextMark *end_mark;
-
-       gint mw_start; /* misspelled word start */
-       gint mw_end;   /* end */
-
-       GtkTextMark *current_mark;
-};
-
 typedef struct _ViewData ViewData;
 
 struct _ViewData
@@ -116,7 +105,6 @@ static GActionEntry action_entries[] =
 };
 
 static GQuark spell_checker_id = 0;
-static GQuark check_range_id = 0;
 
 static ViewData *
 view_data_new (GeditSpellPlugin *plugin,
@@ -312,456 +300,41 @@ get_spell_checker_from_document (GeditDocument *doc)
        return checker;
 }
 
-static CheckRange *
-get_check_range (GeditDocument *doc)
-{
-       gedit_debug (DEBUG_PLUGINS);
-
-       g_return_val_if_fail (GEDIT_IS_DOCUMENT (doc), NULL);
-
-       return g_object_get_qdata (G_OBJECT (doc), check_range_id);
-}
-
-static void
-update_current (GeditDocument *doc,
-               gint           current_offset)
-{
-       CheckRange *range;
-       GtkTextIter iter;
-       GtkTextIter end_iter;
-
-       gedit_debug (DEBUG_PLUGINS);
-
-       g_return_if_fail (GEDIT_IS_DOCUMENT (doc));
-       g_return_if_fail (current_offset >= 0);
-
-       range = get_check_range (doc);
-       g_return_if_fail (range != NULL);
-
-       gtk_text_buffer_get_iter_at_offset (GTK_TEXT_BUFFER (doc),
-                                           &iter, current_offset);
-
-       if (!gtk_text_iter_inside_word (&iter))
-       {
-               /* if we're not inside a word,
-                * we must be in some spaces.
-                * skip forward to the beginning of the next word. */
-               if (!gtk_text_iter_is_end (&iter))
-               {
-                       gtk_text_iter_forward_word_end (&iter);
-                       gtk_text_iter_backward_word_start (&iter);
-               }
-       }
-       else if (!gtk_text_iter_starts_word (&iter))
-       {
-               gtk_text_iter_backward_word_start (&iter);
-       }
-
-       gtk_text_buffer_get_iter_at_mark (GTK_TEXT_BUFFER (doc),
-                                         &end_iter,
-                                         range->end_mark);
-
-       if (gtk_text_iter_compare (&end_iter, &iter) < 0)
-       {
-               gtk_text_buffer_move_mark (GTK_TEXT_BUFFER (doc),
-                                          range->current_mark,
-                                          &end_iter);
-       }
-       else
-       {
-               gtk_text_buffer_move_mark (GTK_TEXT_BUFFER (doc),
-                                          range->current_mark,
-                                          &iter);
-       }
-}
-
 static void
-set_check_range (GeditDocument *doc,
-                GtkTextIter   *start,
-                GtkTextIter   *end)
-{
-       CheckRange *range;
-       GtkTextIter iter;
-
-       gedit_debug (DEBUG_PLUGINS);
-
-       range = get_check_range (doc);
-
-       if (range == NULL)
-       {
-               gedit_debug_message (DEBUG_PLUGINS, "There was not a previous check range");
-
-               gtk_text_buffer_get_end_iter (GTK_TEXT_BUFFER (doc), &iter);
-
-               range = g_new0 (CheckRange, 1);
-
-               range->start_mark = gtk_text_buffer_create_mark (GTK_TEXT_BUFFER (doc),
-                                                                NULL,
-                                                                &iter,
-                                                                TRUE);
-
-               range->end_mark = gtk_text_buffer_create_mark (GTK_TEXT_BUFFER (doc),
-                                                              NULL,
-                                                              &iter,
-                                                              FALSE);
-
-               range->current_mark = gtk_text_buffer_create_mark (GTK_TEXT_BUFFER (doc),
-                                                                  NULL,
-                                                                  &iter,
-                                                                  TRUE);
-
-               g_object_set_qdata_full (G_OBJECT (doc),
-                                        check_range_id,
-                                        range,
-                                        g_free);
-       }
-
-       if (gedit_spell_utils_skip_no_spell_check (start, end))
-       {
-               if (!gtk_text_iter_inside_word (end))
-               {
-                       /* if we're neither inside a word,
-                        * we must be in some spaces.
-                        * skip backward to the end of the previous word. */
-                       if (!gtk_text_iter_is_end (end))
-                       {
-                               gtk_text_iter_backward_word_start (end);
-                               gtk_text_iter_forward_word_end (end);
-                       }
-               }
-               else
-               {
-                       if (!gtk_text_iter_ends_word (end))
-                       {
-                               gtk_text_iter_forward_word_end (end);
-                       }
-               }
-       }
-       else
-       {
-               /* no spell checking in the specified range */
-               start = end;
-       }
-
-       gtk_text_buffer_move_mark (GTK_TEXT_BUFFER (doc),
-                                  range->start_mark,
-                                  start);
-
-       gtk_text_buffer_move_mark (GTK_TEXT_BUFFER (doc),
-                                  range->end_mark,
-                                  end);
-
-       range->mw_start = -1;
-       range->mw_end = -1;
-
-       update_current (doc, gtk_text_iter_get_offset (start));
-}
-
-static gchar *
-get_current_word (GeditDocument *doc,
-                 gint          *start,
-                 gint          *end)
-{
-       const CheckRange *range;
-       GtkTextIter end_iter;
-       GtkTextIter current_iter;
-       gint range_end;
-
-       gedit_debug (DEBUG_PLUGINS);
-
-       g_return_val_if_fail (GEDIT_IS_DOCUMENT (doc), NULL);
-       g_return_val_if_fail (start != NULL, NULL);
-       g_return_val_if_fail (end != NULL, NULL);
-
-       range = get_check_range (doc);
-       g_return_val_if_fail (range != NULL, NULL);
-
-       gtk_text_buffer_get_iter_at_mark (GTK_TEXT_BUFFER (doc),
-                                         &end_iter,
-                                         range->end_mark);
-
-       range_end = gtk_text_iter_get_offset (&end_iter);
-
-       gtk_text_buffer_get_iter_at_mark (GTK_TEXT_BUFFER (doc),
-                                         &current_iter,
-                                         range->current_mark);
-
-       end_iter = current_iter;
-
-       if (!gtk_text_iter_is_end (&end_iter))
-       {
-               gedit_debug_message (DEBUG_PLUGINS, "Current is not end");
-
-               gtk_text_iter_forward_word_end (&end_iter);
-       }
-
-       *start = gtk_text_iter_get_offset (&current_iter);
-       *end = MIN (gtk_text_iter_get_offset (&end_iter), range_end);
-
-       gedit_debug_message (DEBUG_PLUGINS, "Current word extends [%d, %d]", *start, *end);
-
-       if (*start >= *end)
-       {
-               return NULL;
-       }
-
-       return gtk_text_buffer_get_slice (GTK_TEXT_BUFFER (doc),
-                                         &current_iter,
-                                         &end_iter,
-                                         TRUE);
-}
-
-static gboolean
-goto_next_word (GeditDocument *doc)
-{
-       CheckRange *range;
-       GtkTextIter current_iter;
-       GtkTextIter old_current_iter;
-       GtkTextIter end_iter;
-
-       gedit_debug (DEBUG_PLUGINS);
-
-       g_return_val_if_fail (doc != NULL, FALSE);
-
-       range = get_check_range (doc);
-       g_return_val_if_fail (range != NULL, FALSE);
-
-       gtk_text_buffer_get_iter_at_mark (GTK_TEXT_BUFFER (doc),
-                                         &current_iter,
-                                         range->current_mark);
-       gtk_text_buffer_get_end_iter (GTK_TEXT_BUFFER (doc), &end_iter);
-
-       old_current_iter = current_iter;
-
-       gtk_text_iter_forward_word_ends (&current_iter, 2);
-       gtk_text_iter_backward_word_start (&current_iter);
-
-       if (gedit_spell_utils_skip_no_spell_check (&current_iter, &end_iter) &&
-           (gtk_text_iter_compare (&old_current_iter, &current_iter) < 0) &&
-           (gtk_text_iter_compare (&current_iter, &end_iter) < 0))
-       {
-               update_current (doc, gtk_text_iter_get_offset (&current_iter));
-               return TRUE;
-       }
-
-       return FALSE;
-}
-
-static gchar *
-get_next_misspelled_word (GeditView *view,
-                         gint      *word_start_offset,
-                         gint      *word_end_offset)
+goto_next_cb (GeditSpellCheckerDialog *dialog,
+             GeditSpellNavigator     *navigator)
 {
-       GeditDocument *doc;
-       CheckRange *range;
-       gint start, end;
        gchar *word;
-       GeditSpellChecker *checker;
-       GError *error = NULL;
-
-       doc = GEDIT_DOCUMENT (gtk_text_view_get_buffer (GTK_TEXT_VIEW (view)));
-
-       range = get_check_range (doc);
-       g_return_val_if_fail (range != NULL, NULL);
-
-       checker = get_spell_checker_from_document (doc);
-       g_return_val_if_fail (checker != NULL, NULL);
-
-       word = get_current_word (doc, &start, &end);
-       if (word == NULL)
-       {
-               return NULL;
-       }
-
-       gedit_debug_message (DEBUG_PLUGINS, "Word to check: %s", word);
-
-       while (gedit_spell_checker_check_word (checker, word, &error))
-       {
-               g_free (word);
-
-               if (!goto_next_word (doc))
-               {
-                       return NULL;
-               }
-
-               /* may return null if we reached the end of the selection */
-               word = get_current_word (doc, &start, &end);
-               if (word == NULL)
-               {
-                       return NULL;
-               }
-
-               gedit_debug_message (DEBUG_PLUGINS, "Word to check: %s", word);
-       }
 
-       if (error != NULL)
-       {
-               g_warning ("Spell checking plugin: %s", error->message);
-               g_error_free (error);
-               return NULL;
-       }
-
-       if (!goto_next_word (doc))
-       {
-               update_current (doc, gtk_text_buffer_get_char_count (GTK_TEXT_BUFFER (doc)));
-       }
+       word = gedit_spell_navigator_goto_next (navigator);
 
        if (word != NULL)
        {
-               range->mw_start = start;
-               range->mw_end = end;
-               *word_start_offset = start;
-               *word_end_offset = end;
+               gedit_spell_checker_dialog_set_misspelled_word (dialog, word);
+               g_free (word);
        }
        else
        {
-               range->mw_start = -1;
-               range->mw_end = -1;
-               *word_start_offset = -1;
-               *word_end_offset = -1;
-       }
-
-       return word;
-}
-
-static void
-select_misspelled_word (GeditView *view,
-                       gint       word_start_offset,
-                       gint       word_end_offset)
-{
-       GtkTextBuffer *buffer;
-       GtkTextIter word_start_iter;
-       GtkTextIter word_end_iter;
-
-       buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (view));
-
-       gtk_text_buffer_get_iter_at_offset (buffer, &word_start_iter, word_start_offset);
-       gtk_text_buffer_get_iter_at_offset (buffer, &word_end_iter, word_end_offset);
-
-       gedit_debug_message (DEBUG_PLUGINS, "Select [%d, %d]", word_start_offset, word_end_offset);
-       gtk_text_buffer_select_range (buffer, &word_start_iter, &word_end_iter);
-
-       gedit_view_scroll_to_cursor (view);
-}
-
-static void
-goto_next_cb (GeditSpellCheckerDialog *dialog,
-             GeditView               *view)
-{
-       gchar *word = NULL;
-       gint word_start_offset;
-       gint word_end_offset;
-
-       gedit_debug (DEBUG_PLUGINS);
-
-       word = get_next_misspelled_word (view, &word_start_offset, &word_end_offset);
-       if (word == NULL)
-       {
                gedit_spell_checker_dialog_set_completed (dialog);
-               return;
        }
-
-       select_misspelled_word (view, word_start_offset, word_end_offset);
-
-       gedit_spell_checker_dialog_set_misspelled_word (dialog, word);
-
-       g_free (word);
 }
 
 static void
 change_cb (GeditSpellCheckerDialog *dialog,
           const gchar             *word,
           const gchar             *change_to,
-          GeditView               *view)
+          GeditSpellNavigator     *navigator)
 {
-       GeditDocument *doc;
-       CheckRange *range;
-       GtkTextIter start;
-       GtkTextIter end;
-       gchar *word_in_buffer = NULL;
-
-       gedit_debug (DEBUG_PLUGINS);
-
-       doc = GEDIT_DOCUMENT (gtk_text_view_get_buffer (GTK_TEXT_VIEW (view)));
-       range = get_check_range (doc);
-       g_return_if_fail (range != NULL);
-
-       gtk_text_buffer_get_iter_at_offset (GTK_TEXT_BUFFER (doc), &start, range->mw_start);
-       if (range->mw_end < 0)
-       {
-               gtk_text_buffer_get_end_iter (GTK_TEXT_BUFFER (doc), &end);
-       }
-       else
-       {
-               gtk_text_buffer_get_iter_at_offset (GTK_TEXT_BUFFER (doc), &end, range->mw_end);
-       }
-
-       word_in_buffer = gtk_text_buffer_get_slice (GTK_TEXT_BUFFER (doc), &start, &end, TRUE);
-       g_return_if_fail (word_in_buffer != NULL);
-       g_return_if_fail (g_strcmp0 (word_in_buffer, word) == 0);
-       g_free (word_in_buffer);
-
-       gtk_text_buffer_begin_user_action (GTK_TEXT_BUFFER (doc));
-
-       gtk_text_buffer_delete (GTK_TEXT_BUFFER (doc), &start, &end);
-       gtk_text_buffer_insert (GTK_TEXT_BUFFER (doc), &start, change_to, -1);
-
-       gtk_text_buffer_end_user_action (GTK_TEXT_BUFFER (doc));
-
-       update_current (doc, range->mw_start + g_utf8_strlen (change_to, -1));
+       gedit_spell_navigator_change (navigator, word, change_to);
 }
 
 static void
 change_all_cb (GeditSpellCheckerDialog *dialog,
               const gchar             *word,
               const gchar             *change_to,
-              GeditView               *view)
+              GeditSpellNavigator     *navigator)
 {
-       GeditDocument *doc;
-       CheckRange *range;
-       GtkTextIter start;
-       GtkTextIter end;
-       gchar *word_in_buffer = NULL;
-       GtkSourceSearchSettings *search_settings;
-       GtkSourceSearchContext *search_context;
-
-       gedit_debug (DEBUG_PLUGINS);
-
-       doc = GEDIT_DOCUMENT (gtk_text_view_get_buffer (GTK_TEXT_VIEW (view)));
-       range = get_check_range (doc);
-       g_return_if_fail (range != NULL);
-
-       gtk_text_buffer_get_iter_at_offset (GTK_TEXT_BUFFER (doc), &start, range->mw_start);
-       if (range->mw_end < 0)
-       {
-               gtk_text_buffer_get_end_iter (GTK_TEXT_BUFFER (doc), &end);
-       }
-       else
-       {
-               gtk_text_buffer_get_iter_at_offset (GTK_TEXT_BUFFER (doc), &end, range->mw_end);
-       }
-
-       word_in_buffer = gtk_text_buffer_get_slice (GTK_TEXT_BUFFER (doc), &start, &end, TRUE);
-       g_return_if_fail (word_in_buffer != NULL);
-       g_return_if_fail (g_strcmp0 (word_in_buffer, word) == 0);
-       g_free (word_in_buffer);
-
-       search_settings = gtk_source_search_settings_new ();
-       gtk_source_search_settings_set_case_sensitive (search_settings, TRUE);
-       gtk_source_search_settings_set_at_word_boundaries (search_settings, TRUE);
-       gtk_source_search_settings_set_search_text (search_settings, word);
-
-       search_context = gtk_source_search_context_new (GTK_SOURCE_BUFFER (doc),
-                                                       search_settings);
-
-       gtk_source_search_context_set_highlight (search_context, FALSE);
-
-       gtk_source_search_context_replace_all (search_context, change_to, -1, NULL);
-
-       update_current (doc, range->mw_start + g_utf8_strlen (change_to, -1));
-
-       g_object_unref (search_settings);
-       g_object_unref (search_context);
+       gedit_spell_navigator_change_all (navigator, word, change_to);
 }
 
 static void
@@ -847,11 +420,8 @@ spell_cb (GSimpleAction *action,
        GeditView *view;
        GeditDocument *doc;
        GeditSpellChecker *checker;
+       GeditSpellNavigator *navigator;
        GtkWidget *dialog;
-       GtkTextIter start;
-       GtkTextIter end;
-       gint word_start_offset;
-       gint word_end_offset;
        gchar *word;
 
        gedit_debug (DEBUG_PLUGINS);
@@ -878,19 +448,9 @@ spell_cb (GSimpleAction *action,
                return;
        }
 
-       if (!gtk_text_buffer_get_selection_bounds (GTK_TEXT_BUFFER (doc),
-                                                  &start,
-                                                  &end))
-       {
-               /* no selection, get the whole doc */
-               gtk_text_buffer_get_bounds (GTK_TEXT_BUFFER (doc),
-                                           &start,
-                                           &end);
-       }
-
-       set_check_range (doc, &start, &end);
+       navigator = gedit_spell_navigator_gtv_new (GTK_TEXT_VIEW (view), checker);
 
-       word = get_next_misspelled_word (view, &word_start_offset, &word_end_offset);
+       word = gedit_spell_navigator_goto_next (navigator);
        if (word == NULL)
        {
                GtkWidget *statusbar;
@@ -900,33 +460,37 @@ spell_cb (GSimpleAction *action,
                                               priv->statusbar_context_id,
                                               _("No misspelled words"));
 
+               g_object_unref (navigator);
                return;
        }
 
        dialog = gedit_spell_checker_dialog_new (GTK_WINDOW (priv->window), checker);
        gtk_window_set_modal (GTK_WINDOW (dialog), TRUE);
 
+       g_object_set_data_full (G_OBJECT (dialog),
+                               NAVIGATOR_KEY,
+                               navigator,
+                               g_object_unref);
+
        g_signal_connect (dialog,
                          "change",
                          G_CALLBACK (change_cb),
-                         view);
+                         navigator);
 
        g_signal_connect (dialog,
                          "change-all",
                          G_CALLBACK (change_all_cb),
-                         view);
+                         navigator);
 
        g_signal_connect (dialog,
                          "goto-next",
                          G_CALLBACK (goto_next_cb),
-                         view);
+                         navigator);
 
        gedit_spell_checker_dialog_set_misspelled_word (GEDIT_SPELL_CHECKER_DIALOG (dialog), word);
-
        g_free (word);
 
        gtk_widget_show (dialog);
-       select_misspelled_word (view, word_start_offset, word_end_offset);
 }
 
 static void
@@ -1297,11 +861,6 @@ gedit_spell_plugin_class_init (GeditSpellPluginClass *klass)
                spell_checker_id = g_quark_from_string ("GeditSpellCheckerID");
        }
 
-       if (check_range_id == 0)
-       {
-               check_range_id = g_quark_from_string ("CheckRangeID");
-       }
-
        g_object_class_override_property (object_class, PROP_WINDOW, "window");
 }
 



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