[gnome-builder/wip/chergert/layout] spellcheck: get the spellcheck plugin working again



commit b5b2451f97cb0c3352a2855e2f880132f1369076
Author: Christian Hergert <chergert redhat com>
Date:   Sun Jul 16 21:56:37 2017 -0700

    spellcheck: get the spellcheck plugin working again
    
    This mostly finishes the port to get the spellcheck plugin in working
    order again.
    
    It still needs fit and polish, but that can mostly happen after we
    things merged to master.

 data/gtk/menus.ui                                |    2 +
 libide/sourceview/ide-source-style-scheme.c      |    6 +-
 plugins/spellcheck/gbp-spell-buffer-addin.c      |    8 +
 plugins/spellcheck/gbp-spell-dict.c              |   19 +-
 plugins/spellcheck/gbp-spell-editor-addin.c      |   77 +++-
 plugins/spellcheck/gbp-spell-editor-view-addin.c |  444 +++-----------
 plugins/spellcheck/gbp-spell-editor-view-addin.h |   10 +-
 plugins/spellcheck/gbp-spell-language-popover.c  |    4 +-
 plugins/spellcheck/gbp-spell-navigator.c         |   89 ++-
 plugins/spellcheck/gbp-spell-private.h           |   97 +++
 plugins/spellcheck/gbp-spell-widget-actions.c    |  163 +++++
 plugins/spellcheck/gbp-spell-widget.c            |  731 +++++++++-------------
 plugins/spellcheck/gbp-spell-widget.h            |   16 +-
 plugins/spellcheck/gbp-spell-widget.ui           |  103 +--
 plugins/spellcheck/gtk/menus.ui                  |    6 +
 plugins/spellcheck/meson.build                   |    1 +
 16 files changed, 854 insertions(+), 922 deletions(-)
---
diff --git a/data/gtk/menus.ui b/data/gtk/menus.ui
index a655429..a813bcf 100644
--- a/data/gtk/menus.ui
+++ b/data/gtk/menus.ui
@@ -135,6 +135,8 @@
         <attribute name="action">sourceview.delete-selection</attribute>
       </item>
     </section>
+    <section id="ide-source-view-popup-menu-spellcheck-section">
+    </section>
     <section id="ide-source-view-popup-menu-highlighting-section">
       <submenu id="ide-source-view-popup-menu-highlighting-submenu">
         <attribute name="label" translatable="yes">Highlighting</attribute>
diff --git a/libide/sourceview/ide-source-style-scheme.c b/libide/sourceview/ide-source-style-scheme.c
index dcfef6e..19982eb 100644
--- a/libide/sourceview/ide-source-style-scheme.c
+++ b/libide/sourceview/ide-source-style-scheme.c
@@ -65,11 +65,11 @@ ide_source_style_scheme_apply_style (GtkSourceStyleScheme *style_scheme,
       g_snprintf (defname, sizeof defname, "def%s", colon);
 
       style = gtk_source_style_scheme_get_style (style_scheme, defname);
-
-      if (style == NULL)
-        return FALSE;
     }
 
+  if (style == NULL)
+    return FALSE;
+
   g_object_get (style,
                 "background", &background,
                 "background-set", &background_set,
diff --git a/plugins/spellcheck/gbp-spell-buffer-addin.c b/plugins/spellcheck/gbp-spell-buffer-addin.c
index c4d8e47..e93c69f 100644
--- a/plugins/spellcheck/gbp-spell-buffer-addin.c
+++ b/plugins/spellcheck/gbp-spell-buffer-addin.c
@@ -73,8 +73,16 @@ gbp_spell_buffer_addin_apply (GbpSpellBufferAddin *self)
 
   if (!gbp_spell_buffer_addin_get_enabled (self))
     {
+      GtkTextIter begin;
+      GtkTextIter end;
+
       gspell_text_buffer_set_spell_checker (spell_buffer, NULL);
       g_clear_object (&self->spellchecker);
+
+      gtk_text_buffer_get_bounds (GTK_TEXT_BUFFER (self->buffer), &begin, &end);
+      gtk_text_buffer_remove_tag (GTK_TEXT_BUFFER (self->buffer),
+                                  self->misspelled_tag, &begin, &end);
+
       return;
     }
 
diff --git a/plugins/spellcheck/gbp-spell-dict.c b/plugins/spellcheck/gbp-spell-dict.c
index 3d1f2d8..eaa003d 100644
--- a/plugins/spellcheck/gbp-spell-dict.c
+++ b/plugins/spellcheck/gbp-spell-dict.c
@@ -175,21 +175,10 @@ gbp_spell_dict_personal_contains (GbpSpellDict *self,
 {
   g_assert (GBP_IS_SPELL_DICT (self));
 
-  if (ide_str_empty0 (word))
-    return FALSE;
+  if (self->words != NULL && !ide_str_empty0 (word))
+    return g_hash_table_contains (self->words, word);
 
-  if (self->dict != NULL)
-    {
-      if (self->words == NULL)
-        return FALSE;
-
-      return (NULL != g_hash_table_lookup (self->words, word));
-    }
-  else
-    {
-      g_warning ("No dictionaries loaded");
-      return FALSE;
-    }
+  return FALSE;
 }
 
 gboolean
@@ -210,7 +199,7 @@ gbp_spell_dict_add_word_to_personal (GbpSpellDict *self,
     }
   else
     {
-      g_warning ("No dictionaries loaded");
+      g_warning ("No dictionaries loaded, cannot add word");
       return FALSE;
     }
 }
diff --git a/plugins/spellcheck/gbp-spell-editor-addin.c b/plugins/spellcheck/gbp-spell-editor-addin.c
index b84e322..2cb5bf9 100644
--- a/plugins/spellcheck/gbp-spell-editor-addin.c
+++ b/plugins/spellcheck/gbp-spell-editor-addin.c
@@ -21,12 +21,16 @@
 #include <glib/gi18n.h>
 
 #include "gbp-spell-editor-addin.h"
+#include "gbp-spell-private.h"
 
 struct _GbpSpellEditorAddin
 {
-  GObject        parent_instance;
+  GObject               parent_instance;
 
-  DzlDockWidget *dock;
+  IdeEditorPerspective *editor;
+
+  DzlDockWidget        *dock;
+  GbpSpellWidget       *widget;
 };
 
 static void
@@ -39,6 +43,8 @@ gbp_spell_editor_addin_load (IdeEditorAddin       *addin,
   g_assert (GBP_IS_SPELL_EDITOR_ADDIN (self));
   g_assert (IDE_IS_EDITOR_PERSPECTIVE (editor));
 
+  self->editor = editor;
+
   sidebar = ide_editor_perspective_get_transient_sidebar (editor);
 
   self->dock = g_object_new (DZL_TYPE_DOCK_WIDGET,
@@ -51,6 +57,15 @@ gbp_spell_editor_addin_load (IdeEditorAddin       *addin,
                     G_CALLBACK (gtk_widget_destroyed),
                     &self->dock);
   gtk_container_add (GTK_CONTAINER (sidebar), GTK_WIDGET (self->dock));
+
+  self->widget = g_object_new (GBP_TYPE_SPELL_WIDGET,
+                               "visible", TRUE,
+                               NULL);
+  g_signal_connect (self->widget,
+                    "destroy",
+                    G_CALLBACK (gtk_widget_destroyed),
+                    &self->widget);
+  gtk_container_add (GTK_CONTAINER (self->dock), GTK_WIDGET (self->widget));
 }
 
 static void
@@ -64,6 +79,11 @@ gbp_spell_editor_addin_unload (IdeEditorAddin       *addin,
 
   if (self->dock != NULL)
     gtk_widget_destroy (GTK_WIDGET (self->dock));
+
+  if (self->widget != NULL)
+    gtk_widget_destroy (GTK_WIDGET (self->widget));
+
+  self->editor = NULL;
 }
 
 static void
@@ -71,14 +91,31 @@ gbp_spell_editor_addin_view_set (IdeEditorAddin *addin,
                                  IdeLayoutView  *view)
 {
   GbpSpellEditorAddin *self = (GbpSpellEditorAddin *)addin;
+  IdeEditorView *current;
 
   g_assert (GBP_IS_SPELL_EDITOR_ADDIN (self));
   g_assert (!view || IDE_IS_LAYOUT_VIEW (view));
 
-  if (!IDE_IS_EDITOR_VIEW (view))
-    view = NULL;
+  /* If there is currently a view attached, and this is
+   * a new view, then we want to unset it so that the
+   * panel can be dismissed.
+   */
+
+  current = gbp_spell_widget_get_editor (self->widget);
+
+  if (current != NULL)
+    {
+      if (view == IDE_LAYOUT_VIEW (current))
+        return;
 
+      gbp_spell_widget_set_editor (self->widget, NULL);
 
+      /* TODO: We need transient sidebar API for this to dismiss our display.
+       *       Normally it's done automatically, but if the last view is closed
+       *       it can get confused.
+       */
+      g_object_set (self->editor, "right-visible", FALSE, NULL);
+    }
 }
 
 static void
@@ -101,3 +138,35 @@ static void
 gbp_spell_editor_addin_init (GbpSpellEditorAddin *self)
 {
 }
+
+void
+_gbp_spell_editor_addin_begin (GbpSpellEditorAddin *self,
+                               IdeEditorView       *view)
+{
+  IdeLayoutTransientSidebar *sidebar;
+
+  g_return_if_fail (GBP_IS_SPELL_EDITOR_ADDIN (self));
+  g_return_if_fail (IDE_IS_EDITOR_VIEW (view));
+
+  gbp_spell_widget_set_editor (self->widget, view);
+
+  sidebar = ide_editor_perspective_get_transient_sidebar (self->editor);
+  ide_layout_transient_sidebar_set_view (sidebar, IDE_LAYOUT_VIEW (view));
+  ide_layout_transient_sidebar_set_panel (sidebar, GTK_WIDGET (self->dock));
+
+  /* TODO: This needs API via transient sidebar panel */
+  g_object_set (self->editor, "right-visible", TRUE, NULL);
+}
+
+void
+_gbp_spell_editor_addin_cancel (GbpSpellEditorAddin *self,
+                                IdeEditorView       *view)
+{
+  g_return_if_fail (GBP_IS_SPELL_EDITOR_ADDIN (self));
+  g_return_if_fail (IDE_IS_EDITOR_VIEW (view));
+
+  gbp_spell_widget_set_editor (self->widget, NULL);
+
+  /* TODO: This needs API via transient sidebar panel */
+  g_object_set (self->editor, "right-visible", FALSE, NULL);
+}
diff --git a/plugins/spellcheck/gbp-spell-editor-view-addin.c 
b/plugins/spellcheck/gbp-spell-editor-view-addin.c
index bcd68a8..221f5f8 100644
--- a/plugins/spellcheck/gbp-spell-editor-view-addin.c
+++ b/plugins/spellcheck/gbp-spell-editor-view-addin.c
@@ -23,7 +23,10 @@
 #include <glib/gi18n.h>
 
 #include "gbp-spell-buffer-addin.h"
+#include "gbp-spell-editor-addin.h"
 #include "gbp-spell-editor-view-addin.h"
+#include "gbp-spell-navigator.h"
+#include "gbp-spell-private.h"
 #include "gbp-spell-utils.h"
 
 #define SPELLCHECKER_SUBREGION_LENGTH 500
@@ -41,42 +44,57 @@ struct _GbpSpellEditorViewAddin
 
   /* Owned references */
   DzlBindingGroup *buffer_addin_bindings;
+  GspellNavigator *navigator;
 
   gint             checking_count;
 };
 
-enum {
-  COMPLETED,
-  FAILED,
-  WORD_CHANGED,
-  N_SIGNALS
-};
+static void
+gbp_spell_editor_view_addin_begin (GSimpleAction *action,
+                                   GVariant      *variant,
+                                   gpointer       user_data)
+{
+  GbpSpellEditorViewAddin *self = user_data;
+  IdeEditorAddin *addin;
+  GtkWidget *editor;
 
-static guint signals [N_SIGNALS];
+  g_assert (G_IS_SIMPLE_ACTION (action));
+  g_assert (GBP_IS_SPELL_EDITOR_VIEW_ADDIN (self));
 
-static GtkTextTag *
-get_misspelled_tag (GbpSpellEditorViewAddin *self)
-{
-  g_assert (GBP_IS_SPELL_BUFFER_ADDIN (self));
+  editor = gtk_widget_get_ancestor (GTK_WIDGET (self->view), IDE_TYPE_EDITOR_PERSPECTIVE);
+  addin = ide_editor_addin_find_by_module_name (IDE_EDITOR_PERSPECTIVE (editor), "spellcheck-plugin");
+  _gbp_spell_editor_addin_begin (GBP_SPELL_EDITOR_ADDIN (addin), self->view);
+}
 
-  if (self->buffer_addin_bindings != NULL)
-    {
-      GObject *addin;
+static void
+gbp_spell_editor_view_addin_cancel (GSimpleAction *action,
+                                    GVariant      *variant,
+                                    gpointer       user_data)
+{
+  GbpSpellEditorViewAddin *self = user_data;
+  IdeEditorAddin *addin;
+  GtkWidget *editor;
 
-      addin = dzl_binding_group_get_source (self->buffer_addin_bindings);
-      if (addin != NULL)
-        return gbp_spell_buffer_addin_get_misspelled_tag (GBP_SPELL_BUFFER_ADDIN (addin));
-    }
+  g_assert (G_IS_SIMPLE_ACTION (action));
+  g_assert (GBP_IS_SPELL_EDITOR_VIEW_ADDIN (self));
 
-  return NULL;
+  editor = gtk_widget_get_ancestor (GTK_WIDGET (self->view), IDE_TYPE_EDITOR_PERSPECTIVE);
+  addin = ide_editor_addin_find_by_module_name (IDE_EDITOR_PERSPECTIVE (editor), "spellcheck-plugin");
+  _gbp_spell_editor_addin_cancel (GBP_SPELL_EDITOR_ADDIN (addin), self->view);
 }
 
+static const GActionEntry actions[] = {
+  { "spellcheck", gbp_spell_editor_view_addin_begin },
+  { "cancel-spellcheck", gbp_spell_editor_view_addin_cancel },
+};
+
 static void
 gbp_spell_editor_view_addin_load (IdeEditorViewAddin *addin,
                                   IdeEditorView      *view)
 {
   GbpSpellEditorViewAddin *self = (GbpSpellEditorViewAddin *)addin;
-  g_autoptr(DzlPropertiesGroup) group = NULL;
+  g_autoptr(GSimpleActionGroup) group = NULL;
+  g_autoptr(GPropertyAction) enabled_action = NULL;
   IdeBufferAddin *buffer_addin;
   GspellTextView *wrapper;
   IdeSourceView *source_view;
@@ -112,8 +130,10 @@ gbp_spell_editor_view_addin_load (IdeEditorViewAddin *addin,
                           G_BINDING_SYNC_CREATE);
   dzl_binding_group_set_source (self->buffer_addin_bindings, buffer_addin);
 
-  group = dzl_properties_group_new (G_OBJECT (buffer_addin));
-  dzl_properties_group_add_all_properties (group);
+  group = g_simple_action_group_new ();
+  enabled_action = g_property_action_new ("enabled", buffer_addin, "enabled");
+  g_action_map_add_action (G_ACTION_MAP (group), G_ACTION (enabled_action));
+  g_action_map_add_action_entries (G_ACTION_MAP (group), actions, G_N_ELEMENTS (actions), self);
   gtk_widget_insert_action_group (GTK_WIDGET (view), "spellcheck", G_ACTION_GROUP (group));
 }
 
@@ -131,6 +151,8 @@ gbp_spell_editor_view_addin_unload (IdeEditorViewAddin *addin,
   dzl_binding_group_set_source (self->buffer_addin_bindings, NULL);
   g_clear_object (&self->buffer_addin_bindings);
 
+  g_clear_object (&self->navigator);
+
   self->view = NULL;
 }
 
@@ -141,333 +163,12 @@ editor_view_addin_iface_init (IdeEditorViewAddinInterface *iface)
   iface->unload = gbp_spell_editor_view_addin_unload;
 }
 
-static void
-gbp_spell_editor_view_addin_select_misspelled_word (GbpSpellEditorViewAddin *self)
-{
-  IdeSourceView *view;
-  GtkTextBuffer *buffer;
-  GtkTextTag *tag;
-  GtkTextIter begin;
-  GtkTextIter end;
-
-  g_assert (GBP_IS_SPELL_EDITOR_VIEW_ADDIN (self));
-
-  if (self->view == NULL)
-    return;
-
-  view = ide_editor_view_get_view (self->view);
-  buffer = GTK_TEXT_BUFFER (ide_editor_view_get_buffer (self->view));
-  tag = get_misspelled_tag (self);
-
-  if (buffer != NULL && tag != NULL)
-    {
-      gtk_text_buffer_get_iter_at_mark (buffer, &begin, self->start_boundary);
-      gtk_text_buffer_get_iter_at_mark (buffer, &end, self->end_boundary);
-      gtk_text_buffer_remove_tag (buffer, tag, &begin, &end);
-
-      gtk_text_buffer_get_iter_at_mark (buffer, &begin, self->word_begin);
-      gtk_text_buffer_get_iter_at_mark (buffer, &end, self->word_end);
-      gtk_text_buffer_apply_tag (buffer, tag, &begin, &end);
-
-      gtk_widget_queue_draw (GTK_WIDGET (view));
-
-      ide_source_view_scroll_to_mark (view, self->word_begin, 0.25, TRUE, 1.0, 0.0, TRUE);
-    }
-}
-
-static gboolean
-gbp_spell_editor_view_addin_goto_next (GspellNavigator  *navigator,
-                                       gchar           **word,
-                                       GspellChecker   **checker,
-                                       GError          **error)
-{
-  GbpSpellEditorViewAddin *self = (GbpSpellEditorViewAddin *)navigator;
-  g_autofree gchar *real_word = NULL;
-  GspellChecker *spell_checker;
-  GtkTextBuffer *buffer;
-  GtkTextIter word_begin;
-  GtkTextIter word_end;
-  GtkTextIter end;
-  GtkTextTag *no_spell_check_tag;
-  gboolean ret = FALSE;
-
-  g_assert (GBP_IS_SPELL_EDITOR_VIEW_ADDIN (self));
-
-  if (self->view == NULL)
-    {
-      g_set_error (error,
-                   G_IO_ERROR,
-                   G_IO_ERROR_INVAL,
-                   "Cannot run spellchecker without view");
-      goto complete;
-    }
-
-  if (self->checking_count < 1)
-    {
-      g_set_error (error,
-                   G_IO_ERROR,
-                   G_IO_ERROR_INVAL,
-                   "Cannot run spellchecker before gbp_spell_editor_view_addin_begin_checking()");
-      goto complete;
-    }
-
-  g_assert (self->word_begin != NULL);
-  g_assert (self->word_end != NULL);
-  g_assert (self->start_boundary != NULL);
-  g_assert (self->end_boundary != NULL);
-
-  buffer = GTK_TEXT_BUFFER (ide_editor_view_get_buffer (self->view));
-  spell_checker = gbp_spell_editor_view_addin_get_checker (self);
-
-  if (spell_checker == NULL)
-    {
-      g_set_error (error,
-                   G_IO_ERROR,
-                   G_IO_ERROR_INVAL,
-                   "Cannot run spellchecker without buffer");
-      goto complete;
-    }
-
-  if (gspell_checker_get_language (spell_checker) == NULL)
-    {
-      g_set_error (error,
-                   GSPELL_CHECKER_ERROR,
-                   GSPELL_CHECKER_ERROR_NO_LANGUAGE_SET,
-                   "%s",
-                   _("No language set. Check your dictionary installation."));
-      goto complete;
-    }
-
-  gtk_text_buffer_get_iter_at_mark (buffer, &end, self->end_boundary);
-  gtk_text_buffer_get_iter_at_mark (buffer, &word_end, self->word_end);
-
-  if (gtk_text_iter_compare (&end, &word_end) <= 0)
-    goto complete;
-
-  word_begin = word_end;
-  no_spell_check_tag = gbp_spell_utils_get_no_spell_check_tag (buffer);
-
-  while (TRUE)
-    {
-      g_autofree gchar *local_word = NULL;
-      g_autoptr(GError) local_error = NULL;
-      gboolean correctly_spelled;
-
-      if (!gbp_spell_utils_text_iter_starts_word (&word_begin))
-        {
-          GtkTextIter iter;
-
-          iter = word_begin;
-          gbp_spell_utils_text_iter_forward_word_end (&word_begin);
-
-          if (gtk_text_iter_equal (&iter, &word_begin))
-            goto complete;
-
-          gbp_spell_utils_text_iter_backward_word_start (&word_begin);
-        }
-
-      if (!gbp_spell_utils_skip_no_spell_check (no_spell_check_tag, &word_begin, &end))
-        goto complete;
-
-      g_return_val_if_fail (gbp_spell_utils_text_iter_starts_word (&word_begin), FALSE);
-
-      word_end = word_begin;
-      gbp_spell_utils_text_iter_forward_word_end (&word_end);
-
-      if (gtk_text_iter_compare (&end, &word_end) < 0)
-        goto complete;
-
-      local_word = gtk_text_buffer_get_text (buffer, &word_begin, &word_end, FALSE);
-      correctly_spelled = gspell_checker_check_word (spell_checker, local_word, -1, &local_error);
-
-      if (local_error != NULL)
-        {
-          g_propagate_error (error, g_steal_pointer (&local_error));
-          goto complete;
-        }
-
-      if (!correctly_spelled)
-        {
-          /* Found! */
-          gtk_text_buffer_move_mark (buffer, self->word_begin, &word_begin);
-          gtk_text_buffer_move_mark (buffer, self->word_end, &word_end);
-
-          gbp_spell_editor_view_addin_select_misspelled_word (self);
-
-          real_word = g_steal_pointer (&local_word);
-          ret = TRUE;
-          goto complete;
-       }
-
-      word_begin = word_end;
-    }
-
-complete:
-
-  if (ret)
-    {
-      if (word != NULL)
-        *word = g_steal_pointer (&real_word);
-
-      if (checker != NULL)
-        *checker = g_object_ref (spell_checker);
-    }
-
-  return ret;
-}
-
-static void
-gbp_spell_editor_view_addin_change (GspellNavigator *navigator,
-                                    const gchar     *word,
-                                    const gchar     *change_to)
-{
-  GbpSpellEditorViewAddin *self = (GbpSpellEditorViewAddin *)navigator;
-  g_autofree gchar *word_in_buffer = NULL;
-  GtkTextBuffer *buffer;
-  GtkTextIter word_begin;
-  GtkTextIter word_end;
-
-  g_assert (GBP_IS_SPELL_EDITOR_VIEW_ADDIN (navigator));
-  g_assert (word != NULL);
-  g_assert (change_to != NULL);
-  g_assert (GTK_IS_TEXT_MARK (self->word_begin));
-  g_assert (GTK_IS_TEXT_MARK (self->word_end));
-
-  if (self->view == NULL)
-    return;
-
-  buffer = GTK_TEXT_BUFFER (ide_editor_view_get_buffer (self->view));
-
-  gtk_text_buffer_get_iter_at_mark (buffer, &word_begin, self->word_begin);
-  gtk_text_buffer_get_iter_at_mark (buffer, &word_end, self->word_end);
-
-  word_in_buffer = gtk_text_buffer_get_slice (buffer, &word_begin, &word_end, TRUE);
-  g_return_if_fail (word_in_buffer != NULL);
-  g_return_if_fail (g_str_equal (word_in_buffer, word));
-
-  gtk_text_buffer_begin_user_action (buffer);
-
-  gtk_text_buffer_delete (buffer, &word_begin, &word_end);
-  gtk_text_buffer_insert (buffer, &word_begin, change_to, -1);
-
-  gtk_text_buffer_end_user_action (buffer);
-}
-
-static void
-gbp_spell_editor_view_addin_change_all (GspellNavigator *navigator,
-                                        const gchar     *word,
-                                        const gchar     *change_to)
-{
-  GbpSpellEditorViewAddin *self = (GbpSpellEditorViewAddin *)navigator;
-  GtkTextBuffer *buffer;
-  GtkTextIter iter;
-
-  g_assert (GSPELL_IS_NAVIGATOR (navigator));
-  g_assert (GBP_IS_SPELL_EDITOR_VIEW_ADDIN (self));
-  g_assert (word != NULL);
-  g_assert (change_to != NULL);
-  g_assert (GTK_IS_TEXT_MARK (self->start_boundary));
-  g_assert (GTK_IS_TEXT_MARK (self->end_boundary));
-
-  if (self->view == NULL)
-    return;
-
-  buffer = GTK_TEXT_BUFFER (ide_editor_view_get_buffer (self->view));
-
-  gtk_text_buffer_get_iter_at_mark (buffer, &iter, self->start_boundary);
-  gtk_text_buffer_begin_user_action (buffer);
-
-  while (TRUE)
-    {
-      gboolean found;
-      GtkTextIter match_begin;
-      GtkTextIter match_end;
-      GtkTextIter limit;
-
-      gtk_text_buffer_get_iter_at_mark (buffer, &limit, self->end_boundary);
-      found = gtk_text_iter_forward_search (&iter,
-                                            word,
-                                            (GTK_TEXT_SEARCH_VISIBLE_ONLY |
-                                             GTK_TEXT_SEARCH_TEXT_ONLY),
-                                            &match_begin,
-                                            &match_end,
-                                            &limit);
-
-      if (!found)
-        break;
-
-      if (gbp_spell_utils_text_iter_starts_word (&match_begin) &&
-          gbp_spell_utils_text_iter_ends_word (&match_end))
-        {
-          gtk_text_buffer_delete (buffer, &match_begin, &match_end);
-          gtk_text_buffer_insert (buffer, &match_end, change_to, -1);
-        }
-
-      iter = match_end;
-    }
-
-  gtk_text_buffer_end_user_action (buffer);
-}
-
-static void
-navigator_iface_init (GspellNavigatorInterface *iface)
-{
-  iface->goto_next = gbp_spell_editor_view_addin_goto_next;
-  iface->change = gbp_spell_editor_view_addin_change;
-  iface->change_all = gbp_spell_editor_view_addin_change_all;
-}
-
 G_DEFINE_TYPE_WITH_CODE (GbpSpellEditorViewAddin, gbp_spell_editor_view_addin, G_TYPE_OBJECT,
-                         G_IMPLEMENT_INTERFACE (GSPELL_TYPE_NAVIGATOR, navigator_iface_init)
-                         G_IMPLEMENT_INTERFACE (IDE_TYPE_EDITOR_VIEW_ADDIN,
-                                                editor_view_addin_iface_init))
+                         G_IMPLEMENT_INTERFACE (IDE_TYPE_EDITOR_VIEW_ADDIN, editor_view_addin_iface_init))
 
 static void
 gbp_spell_editor_view_addin_class_init (GbpSpellEditorViewAddinClass *klass)
 {
-  /**
-   * GbpSpellEditorViewAddin::completed:
-   *
-   * The "completed" signal is emitted after moving past the last word.
-   */
-  signals [COMPLETED] =
-    g_signal_new ("completed",
-                  G_TYPE_FROM_CLASS (klass),
-                  G_SIGNAL_RUN_LAST,
-                  0, NULL, NULL,
-                  g_cclosure_marshal_VOID__VOID,
-                  G_TYPE_NONE, 0);
-
-  /**
-   * GbpSpellEditorViewAddin::failed:
-   * @self: A #GbpSpellEditorViewAddin
-   * @error: (in) (transfer none): A #GError containing the reason
-   *
-   * The "failed" signal is emitted when a failure to move to the next
-   * item has occurred. @error contains the reason.
-   */
-  signals [FAILED] =
-    g_signal_new ("completed",
-                  G_TYPE_FROM_CLASS (klass),
-                  G_SIGNAL_RUN_LAST,
-                  0, NULL, NULL,
-                  g_cclosure_marshal_VOID__BOXED,
-                  G_TYPE_NONE, 1, G_TYPE_ERROR | G_SIGNAL_TYPE_STATIC_SCOPE);
-
-  /**
-   * GbpSpellEditorViewAddin::word-changed:
-   *
-   * The "word-changed" signal is emitted when the navigator has advanced
-   * to the next word. Connect to this to update your UI in reaction to
-   * a change in the underlying navigator.
-   */
-  signals [WORD_CHANGED] =
-    g_signal_new ("word-changed",
-                  G_TYPE_FROM_CLASS (klass),
-                  G_SIGNAL_RUN_LAST,
-                  0, NULL, NULL,
-                  g_cclosure_marshal_VOID__STRING,
-                  G_TYPE_NONE, 1, G_TYPE_STRING | G_SIGNAL_TYPE_STATIC_SCOPE);
 }
 
 static void
@@ -569,22 +270,30 @@ gbp_spell_editor_view_addin_end_checking (GbpSpellEditorViewAddin *self)
       if (GBP_IS_SPELL_BUFFER_ADDIN (buffer_addin))
         gbp_spell_buffer_addin_end_checking (GBP_SPELL_BUFFER_ADDIN (buffer_addin));
 
-
       if (self->view != NULL)
         {
-          IdeSourceView *view = ide_editor_view_get_view (self->view);
-          GtkTextBuffer *buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (view));
-
-          gtk_text_buffer_delete_mark (buffer, self->word_begin);
-          gtk_text_buffer_delete_mark (buffer, self->word_end);
-          gtk_text_buffer_delete_mark (buffer, self->start_boundary);
-          gtk_text_buffer_delete_mark (buffer, self->end_boundary);
+          IdeBuffer *buffer = ide_editor_view_get_buffer (self->view);
+
+          /*
+           * We could be in disposal here, so its possible the buffer has
+           * already been cleared and released.
+           */
+
+          if (buffer != NULL)
+            {
+              gtk_text_buffer_delete_mark (GTK_TEXT_BUFFER (buffer), self->word_begin);
+              gtk_text_buffer_delete_mark (GTK_TEXT_BUFFER (buffer), self->word_end);
+              gtk_text_buffer_delete_mark (GTK_TEXT_BUFFER (buffer), self->start_boundary);
+              gtk_text_buffer_delete_mark (GTK_TEXT_BUFFER (buffer), self->end_boundary);
+            }
         }
 
       self->word_begin = NULL;
       self->word_end = NULL;
       self->start_boundary = NULL;
       self->end_boundary = NULL;
+
+      g_clear_object (&self->navigator);
     }
 }
 
@@ -612,3 +321,34 @@ gbp_spell_editor_view_addin_get_checker (GbpSpellEditorViewAddin *self)
 
   return NULL;
 }
+
+/**
+ * gbp_spell_editor_view_addin_get_navigator:
+ * @self: a #GbpSpellEditorViewAddin
+ *
+ * This function may return %NULL before
+ * gbp_spell_editor_view_addin_begin_checking() has been called.
+ *
+ * Returns: (nullable) (transfer none): A #GspellNavigator or %NULL
+ *
+ * Since: 3.26
+ */
+GspellNavigator *
+gbp_spell_editor_view_addin_get_navigator (GbpSpellEditorViewAddin *self)
+{
+  g_return_val_if_fail (GBP_IS_SPELL_EDITOR_VIEW_ADDIN (self), NULL);
+
+  if (self->navigator == NULL)
+    {
+      if (self->view != NULL)
+        {
+          IdeSourceView *view = ide_editor_view_get_view (self->view);
+
+          self->navigator = gbp_spell_navigator_new (GTK_TEXT_VIEW (view));
+          if (self->navigator)
+            g_object_ref_sink (self->navigator);
+        }
+    }
+
+  return self->navigator;
+}
diff --git a/plugins/spellcheck/gbp-spell-editor-view-addin.h 
b/plugins/spellcheck/gbp-spell-editor-view-addin.h
index e4c59ec..8f67e15 100644
--- a/plugins/spellcheck/gbp-spell-editor-view-addin.h
+++ b/plugins/spellcheck/gbp-spell-editor-view-addin.h
@@ -27,8 +27,12 @@ G_BEGIN_DECLS
 
 G_DECLARE_FINAL_TYPE (GbpSpellEditorViewAddin, gbp_spell_editor_view_addin, GBP, SPELL_EDITOR_VIEW_ADDIN, 
GObject)
 
-void           gbp_spell_editor_view_addin_begin_checking (GbpSpellEditorViewAddin *self);
-void           gbp_spell_editor_view_addin_end_checking   (GbpSpellEditorViewAddin *self);
-GspellChecker *gbp_spell_editor_view_addin_get_checker    (GbpSpellEditorViewAddin *self);
+void             gbp_spell_editor_view_addin_begin_checking     (GbpSpellEditorViewAddin *self);
+void             gbp_spell_editor_view_addin_end_checking       (GbpSpellEditorViewAddin *self);
+GspellChecker   *gbp_spell_editor_view_addin_get_checker        (GbpSpellEditorViewAddin *self);
+GspellNavigator *gbp_spell_editor_view_addin_get_navigator      (GbpSpellEditorViewAddin *self);
+guint            gbp_spell_editor_view_addin_get_count          (GbpSpellEditorViewAddin *self,
+                                                                 const gchar             *word);
+gboolean         gbp_spell_editor_view_addin_move_to_word_start (GbpSpellEditorViewAddin *self);
 
 G_END_DECLS
diff --git a/plugins/spellcheck/gbp-spell-language-popover.c b/plugins/spellcheck/gbp-spell-language-popover.c
index 0138c13..0a18980 100644
--- a/plugins/spellcheck/gbp-spell-language-popover.c
+++ b/plugins/spellcheck/gbp-spell-language-popover.c
@@ -34,7 +34,7 @@ struct _GbpSpellLanguagePopover
   GtkPopover           *popover;
   GtkTreeView          *treeview;
   GtkTreeSelection     *selection;
-  GtkTreeStore         *store;
+  GtkTreeModel         *store;
   GtkScrolledWindow    *scrolled_window;
   const GspellLanguage *language;
 
@@ -142,7 +142,7 @@ create_popover (GbpSpellLanguagePopover *self)
   self->selection = gtk_tree_view_get_selection (self->treeview);
   gtk_tree_selection_set_mode (self->selection, GTK_SELECTION_BROWSE);
 
-  self->store = GTK_TREE_STORE (gtk_list_store_new (N_COLUMNS, G_TYPE_STRING, GSPELL_TYPE_LANGUAGE));
+  self->store = GTK_TREE_MODEL (gtk_list_store_new (N_COLUMNS, G_TYPE_STRING, GSPELL_TYPE_LANGUAGE));
   gtk_tree_view_set_model (self->treeview, GTK_TREE_MODEL (self->store));
   gtk_tree_view_insert_column_with_attributes (self->treeview, -1, NULL,
                                                gtk_cell_renderer_text_new (),
diff --git a/plugins/spellcheck/gbp-spell-navigator.c b/plugins/spellcheck/gbp-spell-navigator.c
index 66daaf7..1c7f45b 100644
--- a/plugins/spellcheck/gbp-spell-navigator.c
+++ b/plugins/spellcheck/gbp-spell-navigator.c
@@ -22,6 +22,7 @@
 #include <glib/gi18n.h>
 #include <ide.h>
 
+#include "gbp-spell-buffer-addin.h"
 #include "gbp-spell-navigator.h"
 #include "gbp-spell-utils.h"
 
@@ -74,6 +75,51 @@ words_count_state_free (gpointer user_data)
   g_slice_free (WordsCountState, state);
 }
 
+static GtkTextTag *
+get_misspelled_tag (GbpSpellNavigator *self)
+{
+  IdeBufferAddin *buffer_addin;
+
+  g_assert (GBP_IS_SPELL_NAVIGATOR (self));
+  g_assert (self->buffer != NULL);
+  g_assert (IDE_IS_BUFFER (self->buffer));
+
+  buffer_addin = ide_buffer_addin_find_by_module_name (IDE_BUFFER (self->buffer), "spellcheck-plugin");
+  if (buffer_addin != NULL)
+    return gbp_spell_buffer_addin_get_misspelled_tag (GBP_SPELL_BUFFER_ADDIN (buffer_addin));
+
+  return NULL;
+}
+
+static void
+gbp_spell_navigator_select_misspelled_word (GbpSpellNavigator *self)
+{
+  GtkTextTag *tag;
+  GtkTextIter begin;
+  GtkTextIter end;
+
+  g_assert (GBP_IS_SPELL_NAVIGATOR (self));
+
+  if (self->view == NULL)
+    return;
+
+  if (self->buffer != NULL && NULL != (tag = get_misspelled_tag (self)))
+    {
+      gtk_text_buffer_get_iter_at_mark (self->buffer, &begin, self->start_boundary);
+      gtk_text_buffer_get_iter_at_mark (self->buffer, &end, self->end_boundary);
+      gtk_text_buffer_remove_tag (self->buffer, tag, &begin, &end);
+
+      gtk_text_buffer_get_iter_at_mark (self->buffer, &begin, self->word_start);
+      gtk_text_buffer_get_iter_at_mark (self->buffer, &end, self->word_end);
+      gtk_text_buffer_apply_tag (self->buffer, tag, &begin, &end);
+
+      gtk_widget_queue_draw (GTK_WIDGET (self->view));
+
+      ide_source_view_scroll_to_mark (IDE_SOURCE_VIEW (self->view), self->word_start,
+                                      0.25, TRUE, 1.0, 0.0, TRUE);
+    }
+}
+
 static gboolean
 gbp_spell_navigator_words_count_cb (WordsCountState *state)
 {
@@ -238,9 +284,6 @@ gbp_spell_navigator_dispose (GObject *object)
 {
   GbpSpellNavigator *self = (GbpSpellNavigator *)object;
 
-  ide_source_view_set_misspelled_word (IDE_SOURCE_VIEW (self->view), NULL, NULL);
-  gtk_widget_queue_resize (GTK_WIDGET (self->view));
-
   g_clear_object (&self->view);
   g_clear_pointer (&self->words_count, g_hash_table_unref);
 
@@ -302,7 +345,7 @@ set_view (GbpSpellNavigator *self,
       gtk_text_buffer_get_iter_at_mark (self->buffer, &end, self->end_boundary);
       self->words_count = gbp_spell_navigator_count_words (self, &start, &end);
 
-      g_object_notify (G_OBJECT (self), "view");
+      g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_VIEW]);
     }
 }
 
@@ -359,12 +402,12 @@ gbp_spell_navigator_class_init (GbpSpellNavigatorClass *klass)
 
   properties [PROP_VIEW] =
     g_param_spec_object ("view",
-                        "View",
-                        "the view",
-                        GTK_TYPE_TEXT_VIEW,
-                        G_PARAM_READWRITE |
-                        G_PARAM_CONSTRUCT_ONLY |
-                        G_PARAM_STATIC_STRINGS);
+                         "View",
+                         "the view",
+                         GTK_TYPE_TEXT_VIEW,
+                         G_PARAM_READWRITE |
+                         G_PARAM_CONSTRUCT_ONLY |
+                         G_PARAM_STATIC_STRINGS);
 
   properties [PROP_WORDS_COUNTED] =
     g_param_spec_boolean ("words-counted",
@@ -381,30 +424,6 @@ gbp_spell_navigator_init (GbpSpellNavigator *self)
 {
 }
 
-static void
-select_misspelled_word (GbpSpellNavigator *self)
-{
-  GtkTextIter word_start;
-  GtkTextIter word_end;
-
-  g_assert (GBP_IS_SPELL_NAVIGATOR (self));
-
-  gtk_text_buffer_get_iter_at_mark (self->buffer, &word_start, self->word_start);
-  gtk_text_buffer_get_iter_at_mark (self->buffer, &word_end, self->word_end);
-
-  ide_source_view_set_misspelled_word (IDE_SOURCE_VIEW (self->view), &word_start, &word_end);
-  gtk_widget_queue_draw (GTK_WIDGET (self->view));
-
-  g_return_if_fail (gtk_text_view_get_buffer (self->view) == self->buffer);
-
-  gtk_text_view_scroll_to_mark (self->view,
-                                self->word_start,
-                                0.25,
-                                FALSE,
-                                0.0,
-                                0.0);
-}
-
 /* Go to the start of the current checked word so that
  * we can re-check it again, change of language for example
  */
@@ -535,7 +554,7 @@ gbp_spell_navigator_goto_next (GspellNavigator  *navigator,
             /* Found! */
             gtk_text_buffer_move_mark (self->buffer, self->word_start, &word_start);
             gtk_text_buffer_move_mark (self->buffer, self->word_end, &word_end);
-            select_misspelled_word (self);
+            gbp_spell_navigator_select_misspelled_word (self);
 
             if (spell_checker_p != NULL)
               *spell_checker_p = g_object_ref (spell_checker);
diff --git a/plugins/spellcheck/gbp-spell-private.h b/plugins/spellcheck/gbp-spell-private.h
new file mode 100644
index 0000000..595ba96
--- /dev/null
+++ b/plugins/spellcheck/gbp-spell-private.h
@@ -0,0 +1,97 @@
+/* gbp-spell-widget-private.h
+ *
+ * Copyright (C) 2016 Sebastien Lafargue <slafargue gnome org>
+ * Copyright (C) 2017 Christian Hergert <chergert redhat com>
+ *
+ * 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 3 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/>.
+ */
+
+#pragma once
+
+#include <gspell/gspell.h>
+#include <ide.h>
+
+#include "gbp-spell-dict.h"
+#include "gbp-spell-widget.h"
+#include "gbp-spell-editor-addin.h"
+#include "gbp-spell-editor-view-addin.h"
+
+G_BEGIN_DECLS
+
+typedef enum
+{
+  CHECK_WORD_NONE,
+  CHECK_WORD_CHECKING,
+  CHECK_WORD_IDLE
+} CheckWordState;
+
+struct _GbpSpellWidget
+{
+  GtkBin                 parent_instance;
+
+  /* Owned references */
+  IdeEditorView           *editor;
+  GbpSpellEditorViewAddin *editor_view_addin;
+  DzlSignalGroup          *editor_view_addin_signals;
+  GPtrArray               *words_array;
+  GbpSpellDict            *dict;
+
+  /* Borrowed references */
+  const GspellLanguage  *language;
+
+  /* Template references */
+  GtkLabel              *word_label;
+  GtkLabel              *count_label;
+  GtkEntry              *word_entry;
+  GtkListBox            *suggestions_box;
+  GtkBox                *count_box;
+  GtkWidget             *dict_word_entry;
+  GtkWidget             *dict_add_button;
+  GtkWidget             *dict_words_list;
+  GtkButton             *language_chooser_button;
+  GtkWidget             *placeholder;
+
+  /* GSource identifiers */
+  guint                  check_word_timeout_id;
+  guint                  dict_check_word_timeout_id;
+
+  guint                  current_word_count;
+  CheckWordState         check_word_state;
+  CheckWordState         dict_check_word_state;
+
+  guint                  is_checking_word : 1;
+  guint                  is_check_word_invalid : 1;
+  guint                  is_check_word_idle : 1;
+  guint                  is_word_entry_valid : 1;
+
+  guint                  is_dict_checking_word : 1;
+  guint                  is_dict_check_word_invalid : 1;
+  guint                  is_dict_check_word_idle : 1;
+
+  guint                  spellchecking_status : 1;
+};
+
+void       _gbp_spell_widget_init_actions   (GbpSpellWidget *self);
+void       _gbp_spell_widget_update_actions (GbpSpellWidget *self);
+gboolean   _gbp_spell_widget_move_next_word (GbpSpellWidget *self);
+GtkWidget *_gbp_spell_widget_get_entry      (GbpSpellWidget *self);
+void       _gbp_spell_widget_change         (GbpSpellWidget *self,
+                                             gboolean        change_all);
+
+void       _gbp_spell_editor_addin_begin    (GbpSpellEditorAddin *self,
+                                             IdeEditorView       *view);
+void       _gbp_spell_editor_addin_cancel   (GbpSpellEditorAddin *self,
+                                             IdeEditorView       *view);
+
+G_END_DECLS
diff --git a/plugins/spellcheck/gbp-spell-widget-actions.c b/plugins/spellcheck/gbp-spell-widget-actions.c
new file mode 100644
index 0000000..b51cb15
--- /dev/null
+++ b/plugins/spellcheck/gbp-spell-widget-actions.c
@@ -0,0 +1,163 @@
+/* gbp-spell-widget-actions.c
+ *
+ * Copyright (C) 2016 Sebastien Lafargue <slafargue gnome org>
+ * Copyright (C) 2017 Christian Hergert <chergert redhat com>
+ *
+ * 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 3 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/>.
+ */
+
+#define G_LOG_DOMAIN "gbp-spell-widget-actions"
+
+#include "gbp-spell-dict.h"
+#include "gbp-spell-private.h"
+
+static void
+gbp_spell_widget_actions_change (GSimpleAction *action,
+                                 GVariant      *param,
+                                 gpointer       user_data)
+{
+  GbpSpellWidget *self = user_data;
+
+  g_assert (G_IS_SIMPLE_ACTION (action));
+  g_assert (GBP_IS_SPELL_WIDGET (self));
+
+  _gbp_spell_widget_change (self, FALSE);
+}
+
+static void
+gbp_spell_widget_actions_change_all (GSimpleAction *action,
+                                     GVariant      *param,
+                                     gpointer       user_data)
+{
+  GbpSpellWidget *self = user_data;
+
+  g_assert (G_IS_SIMPLE_ACTION (action));
+  g_assert (GBP_IS_SPELL_WIDGET (self));
+
+  _gbp_spell_widget_change (self, TRUE);
+}
+
+static void
+gbp_spell_widget_actions_ignore (GSimpleAction *action,
+                                 GVariant      *param,
+                                 gpointer       user_data)
+{
+  GbpSpellWidget *self = user_data;
+
+  g_assert (G_IS_SIMPLE_ACTION (action));
+  g_assert (GBP_IS_SPELL_WIDGET (self));
+
+  _gbp_spell_widget_move_next_word (self);
+}
+
+static void
+gbp_spell_widget_actions_ignore_all (GSimpleAction *action,
+                                     GVariant      *param,
+                                     gpointer       user_data)
+{
+  GbpSpellWidget *self = user_data;
+
+  g_assert (G_IS_SIMPLE_ACTION (action));
+  g_assert (GBP_IS_SPELL_WIDGET (self));
+
+  if (self->editor_view_addin != NULL)
+    {
+      GspellChecker *checker;
+      const gchar *word;
+
+      checker = gbp_spell_editor_view_addin_get_checker (self->editor_view_addin);
+      word = gtk_label_get_text (self->word_label);
+
+      if (!ide_str_empty0 (word))
+        {
+          gspell_checker_add_word_to_session (checker, word, -1);
+          _gbp_spell_widget_move_next_word (self);
+        }
+    }
+}
+
+static void
+gbp_spell_widget_actions_move_next_word (GSimpleAction *action,
+                                         GVariant      *param,
+                                         gpointer       user_data)
+{
+  GbpSpellWidget *self = user_data;
+
+  g_assert (G_IS_SIMPLE_ACTION (action));
+  g_assert (GBP_IS_SPELL_WIDGET (self));
+
+  _gbp_spell_widget_move_next_word (self);
+}
+
+static const GActionEntry actions[] = {
+  { "change", gbp_spell_widget_actions_change },
+  { "change-all", gbp_spell_widget_actions_change_all },
+  { "ignore", gbp_spell_widget_actions_ignore },
+  { "ignore-all", gbp_spell_widget_actions_ignore_all },
+  { "move-next-word", gbp_spell_widget_actions_move_next_word },
+};
+
+void
+_gbp_spell_widget_init_actions (GbpSpellWidget *self)
+{
+  g_autoptr(GSimpleActionGroup) group = NULL;
+
+  g_return_if_fail (GBP_IS_SPELL_WIDGET (self));
+
+  group = g_simple_action_group_new ();
+  g_action_map_add_action_entries (G_ACTION_MAP (group), actions, G_N_ELEMENTS (actions), self);
+  gtk_widget_insert_action_group (GTK_WIDGET (self), "spell-widget", G_ACTION_GROUP (group));
+}
+
+void
+_gbp_spell_widget_update_actions (GbpSpellWidget *self)
+{
+  gboolean can_change = FALSE;
+  gboolean can_change_all = FALSE;
+  gboolean can_ignore = FALSE;
+  gboolean can_ignore_all = FALSE;
+  gboolean can_move_next_word = FALSE;
+
+  g_return_if_fail (GBP_IS_SPELL_WIDGET (self));
+
+  if (IDE_IS_EDITOR_VIEW (self->editor) &&
+      GBP_IS_SPELL_EDITOR_VIEW_ADDIN (self->editor_view_addin) &&
+      self->spellchecking_status == TRUE)
+    {
+      g_assert (IDE_IS_EDITOR_VIEW_ADDIN (self->editor_view_addin));
+
+      can_change = TRUE;
+      can_change_all = TRUE;
+      can_move_next_word = TRUE;
+
+      can_ignore = self->current_word_count > 0;
+      can_ignore_all = self->current_word_count > 1;
+    }
+
+  dzl_gtk_widget_action_set (GTK_WIDGET (self), "spell-widget", "change",
+                             "enabled", can_change,
+                             NULL);
+  dzl_gtk_widget_action_set (GTK_WIDGET (self), "spell-widget", "change-all",
+                             "enabled", can_change_all,
+                             NULL);
+  dzl_gtk_widget_action_set (GTK_WIDGET (self), "spell-widget", "ignore",
+                             "enabled", can_ignore,
+                             NULL);
+  dzl_gtk_widget_action_set (GTK_WIDGET (self), "spell-widget", "ignore-all",
+                             "enabled", can_ignore_all,
+                             NULL);
+  dzl_gtk_widget_action_set (GTK_WIDGET (self), "spell-widget", "move-next-word",
+                             "enabled", can_move_next_word,
+                             NULL);
+}
diff --git a/plugins/spellcheck/gbp-spell-widget.c b/plugins/spellcheck/gbp-spell-widget.c
index b33688b..6a1d3b8 100644
--- a/plugins/spellcheck/gbp-spell-widget.c
+++ b/plugins/spellcheck/gbp-spell-widget.c
@@ -26,67 +26,9 @@
 #include "gbp-spell-dict.h"
 #include "gbp-spell-language-popover.h"
 #include "gbp-spell-navigator.h"
+#include "gbp-spell-private.h"
 #include "gbp-spell-widget.h"
 
-typedef enum
-{
-  CHECK_WORD_NONE,
-  CHECK_WORD_CHECKING,
-  CHECK_WORD_IDLE
-} CheckWordState;
-
-struct _GbpSpellWidget
-{
-  GtkBin                 parent_instance;
-
-  GspellNavigator       *navigator;
-  IdeSourceView         *view;
-  IdeBuffer             *buffer;
-  GspellChecker         *checker;
-  GbpSpellDict          *dict;
-  GPtrArray             *words_array;
-  const GspellLanguage  *spellchecker_language;
-
-  GtkLabel              *word_label;
-  GtkLabel              *count_label;
-  GtkEntry              *word_entry;
-  GtkButton             *ignore_button;
-  GtkButton             *ignore_all_button;
-  GtkButton             *change_button;
-  GtkButton             *change_all_button;
-  GtkListBox            *suggestions_box;
-  GtkBox                *count_box;
-
-  GtkWidget             *dict_word_entry;
-  GtkWidget             *dict_add_button;
-  GtkWidget             *dict_words_list;
-
-  GtkButton             *highlight_switch;
-  GtkButton             *language_chooser_button;
-
-  GtkWidget             *placeholder;
-  GAction               *view_spellchecking_action;
-
-  guint                  current_word_count;
-  guint                  check_word_timeout_id;
-  guint                  dict_check_word_timeout_id;
-  CheckWordState         check_word_state;
-  CheckWordState         dict_check_word_state;
-
-  guint                  view_spellchecker_set : 1;
-
-  guint                  is_checking_word : 1;
-  guint                  is_check_word_invalid : 1;
-  guint                  is_check_word_idle : 1;
-  guint                  is_word_entry_valid : 1;
-
-  guint                  is_dict_checking_word : 1;
-  guint                  is_dict_check_word_invalid : 1;
-  guint                  is_dict_check_word_idle : 1;
-
-  guint                  spellchecking_status : 1;
-};
-
 G_DEFINE_TYPE (GbpSpellWidget, gbp_spell_widget, GTK_TYPE_BIN)
 
 #define CHECK_WORD_INTERVAL_MIN      100
@@ -95,7 +37,7 @@ G_DEFINE_TYPE (GbpSpellWidget, gbp_spell_widget, GTK_TYPE_BIN)
 
 enum {
   PROP_0,
-  PROP_VIEW,
+  PROP_EDITOR,
   N_PROPS
 };
 
@@ -104,14 +46,11 @@ static GParamSpec *properties [N_PROPS];
 static void
 clear_suggestions_box (GbpSpellWidget *self)
 {
-  GList *children;
-
   g_assert (GBP_IS_SPELL_WIDGET (self));
 
-  children = gtk_container_get_children (GTK_CONTAINER (self->suggestions_box));
-
-  for (GList *l = children; l != NULL; l = g_list_next (l))
-    gtk_widget_destroy (GTK_WIDGET (l->data));
+  gtk_container_foreach (GTK_CONTAINER (self->suggestions_box),
+                         (GtkCallback)gtk_widget_destroy,
+                         NULL);
 }
 
 static void
@@ -122,35 +61,11 @@ update_global_sensiblility (GbpSpellWidget *self,
 
   gtk_entry_set_text (self->word_entry, "");
   clear_suggestions_box (self);
-
-  gtk_widget_set_sensitive (GTK_WIDGET (self->word_entry), sensibility);
-  gtk_widget_set_sensitive (GTK_WIDGET (self->ignore_button), sensibility);
-  gtk_widget_set_sensitive (GTK_WIDGET (self->ignore_all_button), sensibility);
-  gtk_widget_set_sensitive (GTK_WIDGET (self->change_button), sensibility);
-  gtk_widget_set_sensitive (GTK_WIDGET (self->change_all_button), sensibility);
-  gtk_widget_set_sensitive (GTK_WIDGET (self->suggestions_box), sensibility);
-}
-
-static void
-update_change_ignore_sensibility (GbpSpellWidget *self)
-{
-  gboolean entry_sensitivity;
-
-  g_assert (GBP_IS_SPELL_WIDGET (self));
-
-  entry_sensitivity = (gtk_entry_get_text_length (self->word_entry) > 0);
-
-  gtk_widget_set_sensitive (GTK_WIDGET (self->change_button),
-                            entry_sensitivity);
-  gtk_widget_set_sensitive (GTK_WIDGET (self->change_all_button),
-                            entry_sensitivity && (self->current_word_count > 1));
-
-  gtk_widget_set_sensitive (GTK_WIDGET (self->ignore_all_button),
-                            self->current_word_count > 1);
+  _gbp_spell_widget_update_actions (self);
 }
 
 GtkWidget *
-gbp_spell_widget_get_entry (GbpSpellWidget *self)
+_gbp_spell_widget_get_entry (GbpSpellWidget *self)
 {
   g_return_val_if_fail (GBP_IS_SPELL_WIDGET (self), NULL);
 
@@ -161,18 +76,14 @@ static GtkWidget *
 create_suggestion_row (GbpSpellWidget *self,
                        const gchar    *word)
 {
-  GtkWidget *row;
-
   g_assert (GBP_IS_SPELL_WIDGET (self));
   g_assert (!ide_str_empty0 (word));
 
-  row = g_object_new (GTK_TYPE_LABEL,
-                      "label", word,
-                      "visible", TRUE,
-                      "halign", GTK_ALIGN_START,
-                      NULL);
-
-  return row;
+  return g_object_new (GTK_TYPE_LABEL,
+                       "label", word,
+                       "visible", TRUE,
+                       "xalign", 0.0f,
+                       NULL);
 }
 
 static void
@@ -180,6 +91,7 @@ fill_suggestions_box (GbpSpellWidget  *self,
                       const gchar     *word,
                       gchar          **first_result)
 {
+  GspellChecker *checker;
   GSList *suggestions = NULL;
   GtkWidget *item;
 
@@ -189,13 +101,20 @@ fill_suggestions_box (GbpSpellWidget  *self,
   *first_result = NULL;
 
   clear_suggestions_box (self);
+
   if (ide_str_empty0 (word))
     {
       gtk_widget_set_sensitive (GTK_WIDGET (self->suggestions_box), FALSE);
       return;
     }
 
-  if (NULL == (suggestions = gspell_checker_get_suggestions (self->checker, word, -1)))
+  if (self->editor_view_addin != NULL)
+    {
+      checker = gbp_spell_editor_view_addin_get_checker (self->editor_view_addin);
+      suggestions = gspell_checker_get_suggestions (checker, word, -1);
+    }
+
+  if (suggestions == NULL)
     {
       gtk_label_set_text (GTK_LABEL (self->placeholder), _("No suggestions"));
       gtk_widget_set_sensitive (GTK_WIDGET (self->suggestions_box), FALSE);
@@ -203,10 +122,13 @@ fill_suggestions_box (GbpSpellWidget  *self,
   else
     {
       *first_result = g_strdup (suggestions->data);
+
       gtk_widget_set_sensitive (GTK_WIDGET (self->suggestions_box), TRUE);
-      for (GSList *l = (GSList *)suggestions; l != NULL; l = l->next)
+
+      for (const GSList *iter = suggestions; iter; iter = iter->next)
         {
-          item = create_suggestion_row (self, l->data);
+          const gchar *iter_word = iter->data;
+          item = create_suggestion_row (self, iter_word);
           gtk_list_box_insert (self->suggestions_box, item, -1);
         }
 
@@ -217,13 +139,20 @@ fill_suggestions_box (GbpSpellWidget  *self,
 static void
 update_count_label (GbpSpellWidget *self)
 {
+  GspellNavigator *navigator;
   const gchar *word;
   guint count;
 
   g_assert (GBP_IS_SPELL_WIDGET (self));
 
+  if (self->editor_view_addin == NULL)
+    return;
+
+  navigator = gbp_spell_editor_view_addin_get_navigator (self->editor_view_addin);
   word = gtk_label_get_text (self->word_label);
-  if (0 != (count = gbp_spell_navigator_get_count (GBP_SPELL_NAVIGATOR (self->navigator), word)))
+  count = gbp_spell_navigator_get_count (GBP_SPELL_NAVIGATOR (navigator), word);
+
+  if (count > 0)
     {
       g_autofree gchar *count_text = NULL;
 
@@ -239,28 +168,34 @@ update_count_label (GbpSpellWidget *self)
     gtk_widget_set_visible (GTK_WIDGET (self->count_box), TRUE);
 
   self->current_word_count = count;
-  update_change_ignore_sensibility (self);
+
+  _gbp_spell_widget_update_actions (self);
 }
 
-static gboolean
-jump_to_next_misspelled_word (GbpSpellWidget *self)
+gboolean
+_gbp_spell_widget_move_next_word (GbpSpellWidget *self)
 {
-  GspellChecker *checker = NULL;
   g_autofree gchar *word = NULL;
   g_autofree gchar *first_result = NULL;
-  GtkListBoxRow *row;
   g_autoptr(GError) error = NULL;
+  GspellNavigator *navigator;
+  GtkListBoxRow *row;
   gboolean ret = FALSE;
 
   g_assert (GBP_IS_SPELL_WIDGET (self));
 
-  gtk_widget_grab_focus (GTK_WIDGET (self->word_entry));
-  if ((ret = gspell_navigator_goto_next (self->navigator, &word, &checker, &error)))
+  if (self->editor_view_addin == NULL)
+    return FALSE;
+
+  navigator = gbp_spell_editor_view_addin_get_navigator (self->editor_view_addin);
+
+  if ((ret = gspell_navigator_goto_next (navigator, &word, NULL, &error)))
     {
       gtk_label_set_text (self->word_label, word);
       update_count_label (self);
 
       fill_suggestions_box (self, word, &first_result);
+
       if (!ide_str_empty0 (first_result))
         {
           row = gtk_list_box_get_row_at_index (self->suggestions_box, 0);
@@ -275,71 +210,46 @@ jump_to_next_misspelled_word (GbpSpellWidget *self)
       self->spellchecking_status = FALSE;
 
       gtk_label_set_text (GTK_LABEL (self->placeholder), _("Completed spell checking"));
-      gtk_widget_grab_focus (self->dict_word_entry);
       update_global_sensiblility (self, FALSE);
     }
 
-  return ret;
-}
+  _gbp_spell_widget_update_actions (self);
 
-GtkWidget *
-gbp_spell_widget_new (IdeSourceView *source_view)
-{
-  return g_object_new (GBP_TYPE_SPELL_WIDGET,
-                       "view", source_view,
-                       NULL);
-}
-
-static IdeSourceView *
-gbp_spell_widget_get_view (GbpSpellWidget *self)
-{
-  g_return_val_if_fail (GBP_IS_SPELL_WIDGET (self), NULL);
-
-  return self->view;
-}
-
-static void
-gbp_spell_widget_set_view (GbpSpellWidget *self,
-                           IdeSourceView  *view)
-{
-  g_return_if_fail (GBP_IS_SPELL_WIDGET (self));
-  g_return_if_fail (IDE_IS_SOURCE_VIEW (view));
-
-  ide_set_weak_pointer (&self->view, view);
-  if (GSPELL_IS_NAVIGATOR (self->navigator))
-    g_clear_object (&self->navigator);
-
-  self->navigator = gbp_spell_navigator_new (GTK_TEXT_VIEW (view));
+  return ret;
 }
 
 static gboolean
 check_word_timeout_cb (GbpSpellWidget *self)
 {
-  const gchar *word;
   g_autoptr(GError) error = NULL;
-  gchar *icon_name;
+  GspellChecker *checker;
+  const gchar *icon_name = "";
+  const gchar *word;
   gboolean ret = TRUE;
 
   g_assert (GBP_IS_SPELL_WIDGET (self));
+  g_assert (self->editor_view_addin != NULL);
+
+  checker = gbp_spell_editor_view_addin_get_checker (self->editor_view_addin);
 
   self->check_word_state = CHECK_WORD_CHECKING;
 
   word = gtk_entry_get_text (self->word_entry);
+
   if (!ide_str_empty0 (word))
     {
       /* FIXME: suggestions can give a multiple-words suggestion
        * that failed to the checkword test, ex: auto tools
        */
-      ret = gspell_checker_check_word (self->checker, word, -1, &error);
+      ret = gspell_checker_check_word (checker, word, -1, &error);
       if (error != NULL)
         {
           g_message ("check error:%s\n", error->message);
         }
 
-      icon_name = ret ? "" : "dialog-warning-symbolic";
+      if (!ret)
+        icon_name = "dialog-warning-symbolic";
     }
-  else
-    icon_name = "";
 
   gtk_entry_set_icon_from_icon_name (self->word_entry,
                                      GTK_ENTRY_ICON_SECONDARY,
@@ -349,13 +259,14 @@ check_word_timeout_cb (GbpSpellWidget *self)
   self->is_word_entry_valid = ret;
 
   self->check_word_timeout_id = 0;
+
   if (self->is_check_word_invalid == TRUE)
     {
-      self->check_word_timeout_id = g_timeout_add_full (G_PRIORITY_DEFAULT,
+      self->check_word_timeout_id = g_timeout_add_full (G_PRIORITY_LOW,
                                                         CHECK_WORD_INTERVAL_MIN,
                                                         (GSourceFunc)check_word_timeout_cb,
-                                                        self,
-                                                        NULL);
+                                                        g_object_ref (self),
+                                                        g_object_unref);
       self->check_word_state = CHECK_WORD_IDLE;
       self->is_check_word_invalid = FALSE;
     }
@@ -372,7 +283,7 @@ gbp_spell_widget__word_entry_changed_cb (GbpSpellWidget *self,
   g_assert (GBP_IS_SPELL_WIDGET (self));
   g_assert (GTK_IS_ENTRY (entry));
 
-  update_change_ignore_sensibility (self);
+  _gbp_spell_widget_update_actions (self);
 
   word = gtk_entry_get_text (self->word_entry);
   if (ide_str_empty0 (word) && self->spellchecking_status == TRUE)
@@ -389,89 +300,17 @@ gbp_spell_widget__word_entry_changed_cb (GbpSpellWidget *self,
       return;
     }
 
-  if (self->check_word_state == CHECK_WORD_IDLE)
+  ide_clear_source (&self->check_word_timeout_id);
+
+  if (self->editor_view_addin != NULL)
     {
-      g_source_remove (self->check_word_timeout_id);
-      self->check_word_timeout_id = 0;
+      self->check_word_timeout_id = g_timeout_add_full (G_PRIORITY_LOW,
+                                                        CHECK_WORD_INTERVAL_MIN,
+                                                        (GSourceFunc)check_word_timeout_cb,
+                                                        g_object_ref (self),
+                                                        g_object_unref);
+      self->check_word_state = CHECK_WORD_IDLE;
     }
-
-  self->check_word_timeout_id = g_timeout_add_full (G_PRIORITY_DEFAULT,
-                                                    CHECK_WORD_INTERVAL_MIN,
-                                                    (GSourceFunc)check_word_timeout_cb,
-                                                    self,
-                                                    NULL);
-  self->check_word_state = CHECK_WORD_IDLE;
-}
-
-static void
-gbp_spell_widget__ignore_button_clicked_cb (GbpSpellWidget *self,
-                                            GtkButton      *button)
-{
-  g_assert (GBP_IS_SPELL_WIDGET (self));
-  g_assert (GTK_IS_BUTTON (button));
-
-  jump_to_next_misspelled_word (self);
-}
-
-static void
-gbp_spell_widget__ignore_all_button_clicked_cb (GbpSpellWidget *self,
-                                                GtkButton      *button)
-{
-  const gchar *word;
-
-  g_assert (GBP_IS_SPELL_WIDGET (self));
-  g_assert (GTK_IS_BUTTON (button));
-
-  word = gtk_label_get_text (self->word_label);
-  g_assert (!ide_str_empty0 (word));
-
-  gspell_checker_add_word_to_session (self->checker, word, -1);
-  jump_to_next_misspelled_word (self);
-}
-
-static void
-change_misspelled_word (GbpSpellWidget *self,
-                        gboolean        change_all)
-{
-  const gchar *word;
-  g_autofree gchar *change_to = NULL;
-
-  g_assert (GBP_IS_SPELL_WIDGET (self));
-
-  word = gtk_label_get_text (self->word_label);
-  g_assert (!ide_str_empty0 (word));
-
-  change_to = g_strdup (gtk_entry_get_text (self->word_entry));
-  g_assert (!ide_str_empty0 (change_to));
-
-  gspell_checker_set_correction (self->checker, word, -1, change_to, -1);
-
-  if (change_all)
-    gspell_navigator_change_all (self->navigator, word, change_to);
-  else
-    gspell_navigator_change (self->navigator, word, change_to);
-
-  jump_to_next_misspelled_word (self);
-}
-
-static void
-gbp_spell_widget__change_button_clicked_cb (GbpSpellWidget *self,
-                                            GtkButton      *button)
-{
-  g_assert (GBP_IS_SPELL_WIDGET (self));
-  g_assert (GTK_IS_BUTTON (button));
-
-  change_misspelled_word (self, FALSE);
-}
-
-static void
-gbp_spell_widget__change_all_button_clicked_cb (GbpSpellWidget *self,
-                                                GtkButton      *button)
-{
-  g_assert (GBP_IS_SPELL_WIDGET (self));
-  g_assert (GTK_IS_BUTTON (button));
-
-  change_misspelled_word (self, TRUE);
 }
 
 static void
@@ -495,7 +334,7 @@ gbp_spell_widget__row_selected_cb (GbpSpellWidget *self,
 
       gtk_entry_set_text (self->word_entry, word);
       gtk_editable_set_position (GTK_EDITABLE (self->word_entry), -1);
-      update_change_ignore_sensibility (self);
+      _gbp_spell_widget_update_actions (self);
 
       g_signal_handlers_unblock_by_func (self->word_entry, gbp_spell_widget__word_entry_changed_cb, self);
     }
@@ -511,71 +350,7 @@ gbp_spell_widget__row_activated_cb (GbpSpellWidget *self,
   g_assert (GTK_IS_LIST_BOX (listbox));
 
   if (row != NULL)
-    change_misspelled_word (self, FALSE);
-}
-
-static gboolean
-gbp_spell_widget__key_press_event_cb (GbpSpellWidget *self,
-                                      GdkEventKey    *event)
-{
-  g_assert (IDE_IS_SOURCE_VIEW (self->view));
-  g_assert (event != NULL);
-
-  switch (event->keyval)
-    {
-    case GDK_KEY_Escape:
-      dzl_gtk_widget_action (GTK_WIDGET (self->view),
-                         "frame", "show-spellcheck",
-                         g_variant_new_int32 (0));
-      return GDK_EVENT_STOP;
-
-    default:
-      break;
-    }
-
-  return GDK_EVENT_PROPAGATE;
-}
-
-static void
-gbp_spell__widget_mapped_cb (GbpSpellWidget *self)
-{
-  GActionGroup *group = NULL;
-  GtkWidget *widget = GTK_WIDGET (self->view);
-  g_autoptr (GVariant) value = NULL;
-
-  g_assert (GBP_IS_SPELL_WIDGET (self));
-
-  while ((group == NULL) && (widget != NULL))
-    {
-      group = gtk_widget_get_action_group (widget, "view");
-      widget = gtk_widget_get_parent (widget);
-    }
-
-  if (group != NULL &&
-      NULL != (self->view_spellchecking_action = g_action_map_lookup_action (G_ACTION_MAP (group),
-                                                                             "spellchecking")))
-    {
-      value = g_action_get_state (self->view_spellchecking_action);
-      self->view_spellchecker_set = g_variant_get_boolean (value);
-      gtk_switch_set_active (GTK_SWITCH (self->highlight_switch), self->view_spellchecker_set);
-    }
-
-  jump_to_next_misspelled_word (self);
-}
-
-static void
-gbp_spell_widget__highlight_switch_toggled_cb (GbpSpellWidget *self,
-                                               gboolean        state,
-                                               GtkSwitch      *switch_button)
-{
-  GspellTextView *spell_text_view;
-
-  g_assert (GBP_IS_SPELL_WIDGET (self));
-  g_assert (GTK_IS_SWITCH (switch_button));
-
-  gtk_switch_set_state (switch_button, state);
-  spell_text_view = gspell_text_view_get_from_gtk_text_view (GTK_TEXT_VIEW (self->view));
-  gspell_text_view_set_inline_spell_checking (spell_text_view, state);
+    _gbp_spell_widget_change (self, FALSE);
 }
 
 static void
@@ -619,13 +394,23 @@ get_next_row_to_focus (GtkListBox    *listbox,
 static gboolean
 dict_check_word_timeout_cb (GbpSpellWidget *self)
 {
-  const gchar *word;
   g_autofree gchar *tooltip = NULL;
-  gchar *icon_name;
+  GspellChecker *checker;
+  const gchar *icon_name = "";
+  const gchar *word;
   gboolean valid = FALSE;
 
   g_assert (GBP_IS_SPELL_WIDGET (self));
 
+  if (self->editor_view_addin == NULL)
+    {
+      /* lost our chance */
+      self->dict_check_word_timeout_id = 0;
+      return G_SOURCE_REMOVE;
+    }
+
+  checker = gbp_spell_editor_view_addin_get_checker (self->editor_view_addin);
+
   self->dict_check_word_state = CHECK_WORD_CHECKING;
 
   word = gtk_entry_get_text (GTK_ENTRY (self->dict_word_entry));
@@ -633,9 +418,9 @@ dict_check_word_timeout_cb (GbpSpellWidget *self)
     {
       if (gbp_spell_dict_personal_contains (self->dict, word))
         gtk_widget_set_tooltip_text (self->dict_word_entry, _("This word is already in the personal 
dictionary"));
-      else if (gspell_checker_check_word (self->checker, word, -1, NULL))
+      else if (gspell_checker_check_word (checker, word, -1, NULL))
         {
-          tooltip = g_strdup_printf (_("This word is already in the %s dictionary"), 
gspell_language_get_name (self->spellchecker_language));
+          tooltip = g_strdup_printf (_("This word is already in the %s dictionary"), 
gspell_language_get_name (self->language));
           gtk_widget_set_tooltip_text (self->dict_word_entry, tooltip);
         }
       else
@@ -644,10 +429,9 @@ dict_check_word_timeout_cb (GbpSpellWidget *self)
           gtk_widget_set_tooltip_text (self->dict_word_entry, NULL);
         }
 
-      icon_name = valid ? "" : "dialog-warning-symbolic";
+      if (!valid)
+        icon_name = "dialog-warning-symbolic";
     }
-  else
-    icon_name = "";
 
   gtk_widget_set_sensitive (GTK_WIDGET (self->dict_add_button), valid);
   gtk_entry_set_icon_from_icon_name (GTK_ENTRY (self->dict_word_entry),
@@ -755,8 +539,8 @@ dict_row_key_pressed_event_cb (GbpSpellWidget *self,
   g_assert (event != NULL);
   g_assert (GTK_IS_LIST_BOX (listbox));
 
-  if (NULL != (row = gtk_list_box_get_selected_row (listbox)) &&
-      event->keyval == GDK_KEY_Delete)
+  if (event->keyval == GDK_KEY_Delete &&
+      NULL != (row = gtk_list_box_get_selected_row (listbox)))
     {
       remove_dict_row (self, GTK_LIST_BOX (self->dict_words_list), GTK_LIST_BOX_ROW (row));
       return GDK_EVENT_STOP;
@@ -810,7 +594,7 @@ check_dict_available (GbpSpellWidget *self)
 {
   g_assert (GBP_IS_SPELL_WIDGET (self));
 
-  return (self->checker != NULL && self->spellchecker_language != NULL);
+  return (self->editor_view_addin != NULL && self->language != NULL);
 }
 
 static void
@@ -826,6 +610,7 @@ gbp_spell_widget__add_button_clicked_cb (GbpSpellWidget *self,
   g_assert (GTK_IS_BUTTON (button));
 
   word = gtk_entry_get_text (GTK_ENTRY (self->dict_word_entry));
+
   /* TODO: check if word already in dict */
   if (check_dict_available (self) && !ide_str_empty0 (word))
     {
@@ -845,7 +630,6 @@ gbp_spell_widget__add_button_clicked_cb (GbpSpellWidget *self,
         }
 
       gtk_entry_set_text (GTK_ENTRY (self->dict_word_entry), "");
-
     }
 }
 
@@ -892,16 +676,24 @@ gbp_spell_widget__language_notify_cb (GbpSpellWidget *self,
   const GspellLanguage *spell_language;
   g_autofree gchar *word = NULL;
   g_autofree gchar *first_result = NULL;
+  GspellNavigator *navigator;
+  GspellChecker *checker;
   GtkListBoxRow *row;
 
   g_assert (GBP_IS_SPELL_WIDGET (self));
   g_assert (GTK_IS_BUTTON (language_chooser_button));
 
-  current_language = gspell_checker_get_language (self->checker);
+  if (self->editor_view_addin == NULL)
+    return;
+
+  checker = gbp_spell_editor_view_addin_get_checker (self->editor_view_addin);
+  navigator = gbp_spell_editor_view_addin_get_navigator (self->editor_view_addin);
+
+  current_language = gspell_checker_get_language (checker);
   spell_language = gspell_language_chooser_get_language (GSPELL_LANGUAGE_CHOOSER (language_chooser_button));
   if (gspell_language_compare (current_language, spell_language) != 0)
     {
-      gspell_checker_set_language (self->checker, spell_language);
+      gspell_checker_set_language (checker, spell_language);
       fill_suggestions_box (self, word, &first_result);
       if (!ide_str_empty0 (first_result))
         {
@@ -910,6 +702,7 @@ gbp_spell_widget__language_notify_cb (GbpSpellWidget *self,
         }
 
       g_clear_pointer (&self->words_array, g_ptr_array_unref);
+
       if (current_language == NULL)
         {
           dict_clean_listbox (self);
@@ -922,8 +715,9 @@ gbp_spell_widget__language_notify_cb (GbpSpellWidget *self,
       gbp_spell_widget__dict_word_entry_changed_cb (self, GTK_ENTRY (self->dict_word_entry));
       gtk_widget_set_sensitive (GTK_WIDGET (self->dict_words_list), TRUE);
 
-      gbp_spell_navigator_goto_word_start (GBP_SPELL_NAVIGATOR (self->navigator));
-      jump_to_next_misspelled_word (self);
+      gbp_spell_navigator_goto_word_start (GBP_SPELL_NAVIGATOR (navigator));
+
+      _gbp_spell_widget_move_next_word (self);
     }
 }
 
@@ -942,7 +736,7 @@ gbp_spell_widget__word_entry_suggestion_activate (GbpSpellWidget *self,
 
   gtk_entry_set_text (self->word_entry, word);
   gtk_editable_set_position (GTK_EDITABLE (self->word_entry), -1);
-  update_change_ignore_sensibility (self);
+  _gbp_spell_widget_update_actions (self);
 
   g_signal_handlers_unblock_by_func (self->word_entry, gbp_spell_widget__word_entry_changed_cb, self);
 }
@@ -953,18 +747,25 @@ gbp_spell_widget__populate_popup_cb (GbpSpellWidget *self,
                                      GtkEntry       *entry)
 {
   GSList *suggestions = NULL;
+  GspellChecker *checker;
   const gchar *text;
   GtkWidget *item;
-  gint count = 0;
+  guint count = 0;
 
   g_assert (GBP_IS_SPELL_WIDGET (self));
   g_assert (GTK_IS_WIDGET (popup));
   g_assert (GTK_IS_ENTRY (entry));
 
+  if (self->editor_view_addin == NULL)
+    return;
+
+  checker = gbp_spell_editor_view_addin_get_checker (self->editor_view_addin);
   text = gtk_entry_get_text (entry);
-  if (self->is_word_entry_valid ||
-      ide_str_empty0 (text) ||
-      NULL == (suggestions = gspell_checker_get_suggestions (self->checker, text, -1)))
+
+  if (self->is_word_entry_valid || ide_str_empty0 (text))
+    suggestions = gspell_checker_get_suggestions (checker, text, -1);
+
+  if (suggestions == NULL)
     return;
 
   item = g_object_new (GTK_TYPE_SEPARATOR_MENU_ITEM,
@@ -973,13 +774,16 @@ gbp_spell_widget__populate_popup_cb (GbpSpellWidget *self,
   gtk_menu_shell_prepend (GTK_MENU_SHELL (popup), item);
 
   suggestions = g_slist_reverse (suggestions);
-  for (GSList *l = (GSList *)suggestions; l != NULL; l = l->next)
+
+  for (const GSList *iter = suggestions; iter; iter = iter->next)
     {
+      const gchar *word = iter->data;
+
       item = g_object_new (GTK_TYPE_MENU_ITEM,
-                           "label", l->data,
+                           "label", word,
                            "visible", TRUE,
                            NULL);
-      g_object_set_data (G_OBJECT (item), "word", g_strdup (l->data));
+      g_object_set_data (G_OBJECT (item), "word", g_strdup (word));
       gtk_menu_shell_prepend (GTK_MENU_SHELL (popup), item);
       g_signal_connect_object (item,
                                "activate",
@@ -1028,27 +832,8 @@ static void
 gbp_spell_widget_constructed (GObject *object)
 {
   GbpSpellWidget *self = (GbpSpellWidget *)object;
-  GspellTextBuffer *spell_buffer;
-
-  g_assert (IDE_IS_SOURCE_VIEW (self->view));
-
-  self->buffer = IDE_BUFFER (gtk_text_view_get_buffer (GTK_TEXT_VIEW (self->view)));
-  ide_buffer_set_spell_checking (self->buffer, TRUE);
-
-  self->spellchecking_status = TRUE;
-
-  spell_buffer = gspell_text_buffer_get_from_gtk_text_buffer (GTK_TEXT_BUFFER (self->buffer));
-  self->checker = gspell_text_buffer_get_spell_checker (spell_buffer);
-  gbp_spell_dict_set_checker (self->dict, self->checker);
 
-  self->spellchecker_language = gspell_checker_get_language (self->checker);
-  gspell_language_chooser_set_language (GSPELL_LANGUAGE_CHOOSER (self->language_chooser_button),
-                                        self->spellchecker_language);
-
-  g_signal_connect_swapped (self->navigator,
-                            "notify::words-counted",
-                            G_CALLBACK (gbp_spell_widget__words_counted_cb),
-                            self);
+  _gbp_spell_widget_init_actions (self);
 
   g_signal_connect_swapped (self->word_entry,
                             "changed",
@@ -1060,26 +845,6 @@ gbp_spell_widget_constructed (GObject *object)
                             G_CALLBACK (gbp_spell_widget__populate_popup_cb),
                             self);
 
-  g_signal_connect_swapped (self->ignore_button,
-                            "clicked",
-                            G_CALLBACK (gbp_spell_widget__ignore_button_clicked_cb),
-                            self);
-
-  g_signal_connect_swapped (self->ignore_all_button,
-                            "clicked",
-                            G_CALLBACK (gbp_spell_widget__ignore_all_button_clicked_cb),
-                            self);
-
-  g_signal_connect_swapped (self->change_button,
-                            "clicked",
-                            G_CALLBACK (gbp_spell_widget__change_button_clicked_cb),
-                            self);
-
-  g_signal_connect_swapped (self->change_all_button,
-                            "clicked",
-                            G_CALLBACK (gbp_spell_widget__change_all_button_clicked_cb),
-                            self);
-
   g_signal_connect_swapped (self->suggestions_box,
                             "row-selected",
                             G_CALLBACK (gbp_spell_widget__row_selected_cb),
@@ -1090,16 +855,6 @@ gbp_spell_widget_constructed (GObject *object)
                             G_CALLBACK (gbp_spell_widget__row_activated_cb),
                             self);
 
-  g_signal_connect_swapped (self,
-                            "key-press-event",
-                            G_CALLBACK (gbp_spell_widget__key_press_event_cb),
-                            self);
-
-  g_signal_connect_swapped (self->highlight_switch,
-                            "state-set",
-                            G_CALLBACK (gbp_spell_widget__highlight_switch_toggled_cb),
-                            self);
-
   g_signal_connect_object (self->language_chooser_button,
                            "notify::language",
                            G_CALLBACK (gbp_spell_widget__language_notify_cb),
@@ -1120,16 +875,6 @@ gbp_spell_widget_constructed (GObject *object)
   gtk_widget_set_visible (self->placeholder, TRUE);
   gtk_list_box_set_placeholder (self->suggestions_box, self->placeholder);
 
-  /* Due to the change of focus between the view and the spellchecker widget,
-   * we need to start checking only when the widget is mapped,
-   * so the view can keep the selection on the first word.
-   */
-  g_signal_connect_object (self,
-                           "map",
-                           G_CALLBACK (gbp_spell__widget_mapped_cb),
-                           NULL,
-                           G_CONNECT_AFTER);
-
   g_signal_connect_swapped (self->dict,
                             "loaded",
                             G_CALLBACK (gbp_spell_widget__dict__loaded_cb),
@@ -1143,42 +888,73 @@ gbp_spell_widget_constructed (GObject *object)
 }
 
 static void
-gbp_spell_widget_finalize (GObject *object)
+gbp_spell_widget_bind_addin (GbpSpellWidget          *self,
+                             GbpSpellEditorViewAddin *editor_view_addin,
+                             DzlSignalGroup          *editor_view_addin_signals)
 {
-  GbpSpellWidget *self = (GbpSpellWidget *)object;
-  GspellTextView *spell_text_view;
-  const GspellLanguage *spell_language;
-  GtkTextBuffer *buffer;
+  GspellChecker *checker;
 
-  ide_clear_source (&self->check_word_timeout_id);
+  g_assert (GBP_IS_SPELL_WIDGET (self));
+  g_assert (GBP_IS_SPELL_EDITOR_VIEW_ADDIN (editor_view_addin));
+  g_assert (DZL_IS_SIGNAL_GROUP (editor_view_addin_signals));
+  g_assert (self->editor_view_addin == NULL);
 
-  /* Set back the view spellchecking previous state */
-  if (self->view != NULL)
-    {
-      spell_text_view = gspell_text_view_get_from_gtk_text_view (GTK_TEXT_VIEW (self->view));
+  self->editor_view_addin = g_object_ref (editor_view_addin);
 
-      if (self->view_spellchecker_set)
-        {
-          gspell_text_view_set_inline_spell_checking (spell_text_view, TRUE);
-          spell_language = gspell_checker_get_language (self->checker);
-          if (gspell_language_compare (self->spellchecker_language, spell_language) != 0)
-            gspell_checker_set_language (self->checker, self->spellchecker_language);
-        }
-      else
-        {
-          gspell_text_view_set_inline_spell_checking (spell_text_view, FALSE);
-          gspell_text_view_set_enable_language_menu (spell_text_view, FALSE);
+  gbp_spell_editor_view_addin_begin_checking (editor_view_addin);
 
-          buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (self->view));
-          ide_buffer_set_spell_checking (IDE_BUFFER (buffer), FALSE);
-        }
+  checker = gbp_spell_editor_view_addin_get_checker (editor_view_addin);
+  gbp_spell_dict_set_checker (self->dict, checker);
 
-      ide_clear_weak_pointer (&self->view);
-    }
+  self->language = gspell_checker_get_language (checker);
+  gspell_language_chooser_set_language (GSPELL_LANGUAGE_CHOOSER (self->language_chooser_button), 
self->language);
+
+  self->spellchecking_status = TRUE;
+
+  _gbp_spell_widget_move_next_word (self);
+}
+
+static void
+gbp_spell_widget_unbind_addin (GbpSpellWidget *self,
+                               DzlSignalGroup *editor_view_addin_signals)
+{
+  g_assert (GBP_IS_SPELL_WIDGET (self));
+  g_assert (DZL_IS_SIGNAL_GROUP (editor_view_addin_signals));
+  g_assert (self->editor_view_addin != NULL);
+
+  gbp_spell_editor_view_addin_end_checking (self->editor_view_addin);
+  gbp_spell_dict_set_checker (self->dict, NULL);
+  self->language = NULL;
+  gspell_language_chooser_set_language (GSPELL_LANGUAGE_CHOOSER (self->language_chooser_button), NULL);
 
-  g_clear_object (&self->navigator);
+  g_clear_object (&self->editor_view_addin);
 
-  G_OBJECT_CLASS (gbp_spell_widget_parent_class)->finalize (object);
+  _gbp_spell_widget_update_actions (self);
+}
+
+static void
+gbp_spell_widget_destroy (GtkWidget *widget)
+{
+  GbpSpellWidget *self = (GbpSpellWidget *)widget;
+
+  g_assert (GBP_IS_SPELL_WIDGET (self));
+
+  ide_clear_source (&self->check_word_timeout_id);
+  ide_clear_source (&self->dict_check_word_timeout_id);
+
+  if (self->editor != NULL)
+    gbp_spell_widget_set_editor (self, NULL);
+
+  self->language = NULL;
+
+  /* Ensure reference holding things are released */
+  g_clear_object (&self->editor);
+  g_clear_object (&self->editor_view_addin);
+  g_clear_object (&self->editor_view_addin_signals);
+  g_clear_object (&self->dict);
+  g_clear_pointer (&self->words_array, g_ptr_array_unref);
+
+  GTK_WIDGET_CLASS (gbp_spell_widget_parent_class)->destroy (widget);
 }
 
 static void
@@ -1191,8 +967,8 @@ gbp_spell_widget_get_property (GObject    *object,
 
   switch (prop_id)
     {
-    case PROP_VIEW:
-      g_value_set_object (value, gbp_spell_widget_get_view (self));
+    case PROP_EDITOR:
+      g_value_set_object (value, gbp_spell_widget_get_editor (self));
       break;
 
     default:
@@ -1210,8 +986,8 @@ gbp_spell_widget_set_property (GObject      *object,
 
   switch (prop_id)
     {
-    case PROP_VIEW:
-      gbp_spell_widget_set_view (self, g_value_get_object (value));
+    case PROP_EDITOR:
+      gbp_spell_widget_set_editor (self, g_value_get_object (value));
       break;
 
     default:
@@ -1226,16 +1002,15 @@ gbp_spell_widget_class_init (GbpSpellWidgetClass *klass)
   GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
 
   object_class->constructed = gbp_spell_widget_constructed;
-  object_class->finalize = gbp_spell_widget_finalize;
   object_class->get_property = gbp_spell_widget_get_property;
   object_class->set_property = gbp_spell_widget_set_property;
 
-  properties [PROP_VIEW] =
-    g_param_spec_object ("view",
-                         "View",
-                         "The source view.",
-                         IDE_TYPE_SOURCE_VIEW,
-                         (G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
+  widget_class->destroy = gbp_spell_widget_destroy;
+
+  properties [PROP_EDITOR] =
+    g_param_spec_object ("editor", NULL, NULL,
+                         IDE_TYPE_EDITOR_VIEW,
+                         (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
 
   g_object_class_install_properties (object_class, N_PROPS, properties);
 
@@ -1244,26 +1019,23 @@ gbp_spell_widget_class_init (GbpSpellWidgetClass *klass)
   gtk_widget_class_bind_template_child (widget_class, GbpSpellWidget, word_label);
   gtk_widget_class_bind_template_child (widget_class, GbpSpellWidget, count_label);
   gtk_widget_class_bind_template_child (widget_class, GbpSpellWidget, word_entry);
-  gtk_widget_class_bind_template_child (widget_class, GbpSpellWidget, ignore_button);
-  gtk_widget_class_bind_template_child (widget_class, GbpSpellWidget, ignore_all_button);
-  gtk_widget_class_bind_template_child (widget_class, GbpSpellWidget, change_button);
-  gtk_widget_class_bind_template_child (widget_class, GbpSpellWidget, change_all_button);
-  gtk_widget_class_bind_template_child (widget_class, GbpSpellWidget, highlight_switch);
   gtk_widget_class_bind_template_child (widget_class, GbpSpellWidget, language_chooser_button);
   gtk_widget_class_bind_template_child (widget_class, GbpSpellWidget, suggestions_box);
   gtk_widget_class_bind_template_child (widget_class, GbpSpellWidget, dict_word_entry);
   gtk_widget_class_bind_template_child (widget_class, GbpSpellWidget, dict_add_button);
   gtk_widget_class_bind_template_child (widget_class, GbpSpellWidget, dict_words_list);
   gtk_widget_class_bind_template_child (widget_class, GbpSpellWidget, count_box);
+
+  g_type_ensure (GBP_TYPE_SPELL_LANGUAGE_POPOVER);
 }
 
 static void
 gbp_spell_widget_init (GbpSpellWidget *self)
 {
-  gtk_widget_init_template (GTK_WIDGET (self));
   self->dict = gbp_spell_dict_new (NULL);
 
-  self->view_spellchecker_set = FALSE;
+  gtk_widget_init_template (GTK_WIDGET (self));
+
   /* FIXME: do not work, Gtk+ bug */
   gtk_entry_set_icon_tooltip_text (self->word_entry,
                                    GTK_ENTRY_ICON_SECONDARY,
@@ -1273,4 +1045,105 @@ gbp_spell_widget_init (GbpSpellWidget *self)
                             "key-press-event",
                             G_CALLBACK (dict_row_key_pressed_event_cb),
                             self);
+
+  self->editor_view_addin_signals = dzl_signal_group_new (GBP_TYPE_SPELL_EDITOR_VIEW_ADDIN);
+
+  dzl_signal_group_connect_swapped (self->editor_view_addin_signals,
+                                    "notify::words-counted",
+                                    G_CALLBACK (gbp_spell_widget__words_counted_cb),
+                                    self);
+
+  g_signal_connect_swapped (self->editor_view_addin_signals,
+                            "bind",
+                            G_CALLBACK (gbp_spell_widget_bind_addin),
+                            self);
+
+  g_signal_connect_swapped (self->editor_view_addin_signals,
+                            "unbind",
+                            G_CALLBACK (gbp_spell_widget_unbind_addin),
+                            self);
+}
+
+/**
+ * gbp_spell_widget_get_editor:
+ * @self: a #GbpSpellWidget
+ *
+ * Gets the editor that is currently being spellchecked.
+ *
+ * Returns: (nullable) (transfer none): An #IdeEditorView or %NULL
+ *
+ * Since: 3.26
+ */
+IdeEditorView *
+gbp_spell_widget_get_editor (GbpSpellWidget *self)
+{
+  g_return_val_if_fail (GBP_IS_SPELL_WIDGET (self), NULL);
+
+  return self->editor;
+}
+
+void
+gbp_spell_widget_set_editor (GbpSpellWidget *self,
+                             IdeEditorView  *editor)
+{
+  g_return_if_fail (GBP_IS_SPELL_WIDGET (self));
+  g_return_if_fail (!editor || IDE_IS_EDITOR_VIEW (editor));
+
+  if (g_set_object (&self->editor, editor))
+    {
+      IdeEditorViewAddin *addin = NULL;
+
+      if (editor != NULL)
+        addin = ide_editor_view_addin_find_by_module_name (editor, "spellcheck-plugin");
+
+      dzl_signal_group_set_target (self->editor_view_addin_signals, addin);
+
+      g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_EDITOR]);
+    }
 }
+
+GtkWidget *
+gbp_spell_widget_new (IdeEditorView *editor)
+{
+  g_return_val_if_fail (!editor || IDE_IS_EDITOR_VIEW (editor), NULL);
+
+  return g_object_new (GBP_TYPE_SPELL_WIDGET,
+                       "editor", editor,
+                       NULL);
+}
+
+void
+_gbp_spell_widget_change (GbpSpellWidget *self,
+                          gboolean        change_all)
+{
+  g_autofree gchar *change_to = NULL;
+  GspellNavigator *navigator;
+  GspellChecker *checker;
+  const gchar *word;
+
+  g_assert (GBP_IS_SPELL_WIDGET (self));
+  g_assert (IDE_IS_EDITOR_VIEW (self->editor));
+  g_assert (GBP_IS_SPELL_EDITOR_VIEW_ADDIN (self->editor_view_addin));
+
+  checker = gbp_spell_editor_view_addin_get_checker (self->editor_view_addin);
+  g_assert (GSPELL_IS_CHECKER (checker));
+
+  word = gtk_label_get_text (self->word_label);
+  g_assert (!ide_str_empty0 (word));
+
+  change_to = g_strdup (gtk_entry_get_text (self->word_entry));
+  g_assert (!ide_str_empty0 (change_to));
+
+  navigator = gbp_spell_editor_view_addin_get_navigator (self->editor_view_addin);
+  g_assert (navigator != NULL);
+
+  gspell_checker_set_correction (checker, word, -1, change_to, -1);
+
+  if (change_all)
+    gspell_navigator_change_all (navigator, word, change_to);
+  else
+    gspell_navigator_change (navigator, word, change_to);
+
+  _gbp_spell_widget_move_next_word (self);
+}
+
diff --git a/plugins/spellcheck/gbp-spell-widget.h b/plugins/spellcheck/gbp-spell-widget.h
index 7249aff..b555c0b 100644
--- a/plugins/spellcheck/gbp-spell-widget.h
+++ b/plugins/spellcheck/gbp-spell-widget.h
@@ -16,13 +16,9 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
-#ifndef GBP_SPELL_WIDGET_H
-#define GBP_SPELL_WIDGET_H
+#pragma once
 
-#include <glib-object.h>
-#include <gtk/gtk.h>
-
-#include "sourceview/ide-source-view.h"
+#include <ide.h>
 
 G_BEGIN_DECLS
 
@@ -30,9 +26,9 @@ G_BEGIN_DECLS
 
 G_DECLARE_FINAL_TYPE (GbpSpellWidget, gbp_spell_widget, GBP, SPELL_WIDGET, GtkBin)
 
-GtkWidget       *gbp_spell_widget_new          (IdeSourceView           *source_view);
-GtkWidget       *gbp_spell_widget_get_entry    (GbpSpellWidget    *self);
+GtkWidget     *gbp_spell_widget_new        (IdeEditorView  *editor);
+IdeEditorView *gbp_spell_widget_get_editor (GbpSpellWidget *self);
+void           gbp_spell_widget_set_editor (GbpSpellWidget *self,
+                                            IdeEditorView  *editor);
 
 G_END_DECLS
-
-#endif /* GBP_SPELL_WIDGET_H */
diff --git a/plugins/spellcheck/gbp-spell-widget.ui b/plugins/spellcheck/gbp-spell-widget.ui
index 02ec8cf..f000721 100644
--- a/plugins/spellcheck/gbp-spell-widget.ui
+++ b/plugins/spellcheck/gbp-spell-widget.ui
@@ -8,6 +8,9 @@
         <property name="row_spacing">6</property>
         <property name="column_spacing">6</property>
         <property name="margin">6</property>
+        <style>
+          <class name="gb-spellchecker"/>
+        </style>
         <child>
           <object class="GtkLabel">
             <property name="visible">true</property>
@@ -61,19 +64,21 @@
             <property name="hexpand">true</property>
             <property name="homogeneous">true</property>
             <child>
-              <object class="GtkButton" id="ignore_button">
+              <object class="GtkButton">
+                <property name="action-name">spell-widget.ignore</property>
                 <property name="label" translatable="yes">_Ignore</property>
                 <property name="visible">true</property>
-                <property name="can_focus">true</property>
-                <property name="use_underline">True</property>
+                <property name="can-focus">true</property>
+                <property name="use-underline">True</property>
               </object>
             </child>
             <child>
-              <object class="GtkButton" id="ignore_all_button">
+              <object class="GtkButton">
+                <property name="action-name">spell-widget.ignore-all</property>
                 <property name="label" translatable="yes">Ignore _All</property>
                 <property name="visible">true</property>
-                <property name="can_focus">true</property>
-                <property name="use_underline">True</property>
+                <property name="can-focus">true</property>
+                <property name="use-underline">True</property>
               </object>
             </child>
             <style>
@@ -91,8 +96,8 @@
             <property name="visible">true</property>
             <property name="label" translatable="yes">Change _to</property>
             <property name="xalign">0</property>
-            <property name="use_underline">True</property>
-            <property name="mnemonic_widget">word_entry</property>
+            <property name="use-underline">True</property>
+            <property name="mnemonic-widget">word_entry</property>
           </object>
           <packing>
             <property name="left_attach">0</property>
@@ -103,7 +108,7 @@
           <object class="GtkEntry" id="word_entry">
             <property name="visible">true</property>
             <property name="can_default">true</property>
-            <property name="can_focus">true</property>
+            <property name="can-focus">true</property>
             <property name="width-chars">20</property>
             <property name="max-width-chars">30</property>
           </object>
@@ -120,19 +125,21 @@
             <property name="hexpand">true</property>
             <property name="homogeneous">true</property>
             <child>
-              <object class="GtkButton" id="change_button">
+              <object class="GtkButton">
+                <property name="action-name">spell-widget.change</property>
                 <property name="label" translatable="yes">Cha_nge</property>
                 <property name="visible">true</property>
-                <property name="can_focus">true</property>
-                <property name="use_underline">True</property>
+                <property name="can-focus">true</property>
+                <property name="use-underline">True</property>
               </object>
             </child>
             <child>
-              <object class="GtkButton" id="change_all_button">
+              <object class="GtkButton">
+                <property name="action-name">spell-widget.change-all</property>
                 <property name="label" translatable="yes">Change A_ll</property>
                 <property name="visible">true</property>
-                <property name="can_focus">true</property>
-                <property name="use_underline">True</property>
+                <property name="can-focus">true</property>
+                <property name="use-underline">True</property>
               </object>
             </child>
             <style>
@@ -152,8 +159,8 @@
             <property name="margin-top">6</property>
             <property name="xalign">0</property>
             <property name="valign">start</property>
-            <property name="use_underline">True</property>
-            <property name="mnemonic_widget">suggestions_box</property>
+            <property name="use-underline">True</property>
+            <property name="mnemonic-widget">suggestions_box</property>
           </object>
           <packing>
             <property name="left_attach">0</property>
@@ -171,7 +178,7 @@
             <child>
               <object class="GtkListBox" id="suggestions_box">
                 <property name="visible">true</property>
-                <property name="can_focus">true</property>
+                <property name="can-focus">true</property>
                 <property name="activate-on-single-click">false</property>
               </object>
             </child>
@@ -187,8 +194,8 @@
             <property name="visible">true</property>
             <property name="label" translatable="yes">Add Word</property>
             <property name="xalign">0</property>
-            <property name="use_underline">True</property>
-            <property name="mnemonic_widget">word_entry</property>
+            <property name="use-underline">True</property>
+            <property name="mnemonic-widget">word_entry</property>
           </object>
           <packing>
             <property name="left_attach">0</property>
@@ -203,7 +210,7 @@
               <object class="GtkEntry" id="dict_word_entry">
                 <property name="visible">true</property>
                 <property name="hexpand">true</property>
-                <property name="can_focus">true</property>
+                <property name="can-focus">true</property>
                 <property name="width-chars">20</property>
                 <property name="max-width-chars">20</property>
               </object>
@@ -212,8 +219,8 @@
               <object class="GtkButton" id="dict_add_button">
                 <property name="label" translatable="yes">A_dd</property>
                 <property name="visible">true</property>
-                <property name="can_focus">true</property>
-                <property name="use_underline">True</property>
+                <property name="can-focus">true</property>
+                <property name="use-underline">True</property>
               </object>
             </child>
           </object>
@@ -250,7 +257,7 @@
               <object class="GtkListBox" id="dict_words_list">
                 <property name="visible">true</property>
                 <property name="can_default">true</property>
-                <property name="can_focus">true</property>
+                <property name="can-focus">true</property>
                 <property name="activate-on-single-click">false</property>
               </object>
             </child>
@@ -262,19 +269,6 @@
           </packing>
         </child>
         <child>
-          <object class="GtkLabel">
-            <property name="visible">true</property>
-            <property name="label" translatable="yes">Options</property>
-            <property name="xalign">0</property>
-            <property name="expand">false</property>
-            <property name="use_underline">True</property>
-          </object>
-          <packing>
-            <property name="left_attach">0</property>
-            <property name="top_attach">7</property>
-          </packing>
-        </child>
-        <child>
           <object class="GtkSeparator">
             <property name="visible">true</property>
             <property name="hexpand">true</property>
@@ -292,8 +286,8 @@
             <property name="label" translatable="yes">_Language</property>
             <property name="xalign">0</property>
             <property name="expand">false</property>
-            <property name="use_underline">True</property>
-            <property name="mnemonic_widget">language_chooser_button</property>
+            <property name="use-underline">True</property>
+            <property name="mnemonic-widget">language_chooser_button</property>
           </object>
           <packing>
             <property name="left_attach">0</property>
@@ -303,7 +297,7 @@
         <child>
           <object class="GbpSpellLanguagePopover" id="language_chooser_button">
             <property name="visible">true</property>
-            <property name="can_focus">true</property>
+            <property name="can-focus">true</property>
             <property name="hexpand">true</property>
             <property name="valign">fill</property>
           </object>
@@ -313,35 +307,6 @@
             <property name="width">2</property>
           </packing>
         </child>
-        <child>
-          <object class="GtkLabel">
-            <property name="visible">true</property>
-            <property name="label" translatable="yes">_Highlight</property>
-            <property name="xalign">0</property>
-            <property name="expand">false</property>
-            <property name="use_underline">True</property>
-            <property name="mnemonic_widget">highlight_switch</property>
-          </object>
-          <packing>
-            <property name="left_attach">0</property>
-            <property name="top_attach">10</property>
-          </packing>
-        </child>
-        <child>
-          <object class="GtkSwitch" id="highlight_switch">
-            <property name="visible">true</property>
-            <property name="can_focus">true</property>
-            <property name="halign">start</property>
-            <property name="expand">false</property>
-          </object>
-          <packing>
-            <property name="left_attach">1</property>
-            <property name="top_attach">10</property>
-          </packing>
-        </child>
-        <style>
-          <class name="gb-spellchecker"/>
-        </style>
       </object>
     </child>
   </template>
diff --git a/plugins/spellcheck/gtk/menus.ui b/plugins/spellcheck/gtk/menus.ui
index a2e703f..18a0a0a 100644
--- a/plugins/spellcheck/gtk/menus.ui
+++ b/plugins/spellcheck/gtk/menus.ui
@@ -1,6 +1,12 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <interface>
   <menu id="ide-source-view-popup-menu">
+    <section id="ide-source-view-popup-menu-spellcheck-section">
+      <item>
+        <attribute name="label" translatable="yes">Check _Spelling</attribute>
+        <attribute name="action">spellcheck.spellcheck</attribute>
+      </item>
+    </section>
     <section id="ide-source-view-popup-menu-highlighting-section">
       <submenu id="ide-source-view-popup-menu-highlighting-submenu">
         <attribute name="label" translatable="yes">Highlighting</attribute>
diff --git a/plugins/spellcheck/meson.build b/plugins/spellcheck/meson.build
index de94684..a8dc003 100644
--- a/plugins/spellcheck/meson.build
+++ b/plugins/spellcheck/meson.build
@@ -24,6 +24,7 @@ spellcheck_sources = [
   'gbp-spell-utils.h',
   'gbp-spell-widget.c',
   'gbp-spell-widget.h',
+  'gbp-spell-widget-actions.c',
   spellcheck_resources[0],
 ]
 



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