summaryrefslogtreecommitdiffstats
path: root/plugins/spell
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/spell')
-rw-r--r--plugins/spell/gedit-spell-app-activatable.c184
-rw-r--r--plugins/spell/gedit-spell-app-activatable.h38
-rw-r--r--plugins/spell/gedit-spell-plugin.c810
-rw-r--r--plugins/spell/gedit-spell-plugin.h62
-rw-r--r--plugins/spell/meson.build54
-rw-r--r--plugins/spell/org.gnome.gedit.plugins.spell.gschema.xml9
-rw-r--r--plugins/spell/resources/gedit-spell.gresource.xml6
-rw-r--r--plugins/spell/resources/meson.build8
-rw-r--r--plugins/spell/resources/ui/gedit-spell-setup-dialog.ui97
-rw-r--r--plugins/spell/spell.plugin.desktop.in11
10 files changed, 1279 insertions, 0 deletions
diff --git a/plugins/spell/gedit-spell-app-activatable.c b/plugins/spell/gedit-spell-app-activatable.c
new file mode 100644
index 0000000..c08b0cd
--- /dev/null
+++ b/plugins/spell/gedit-spell-app-activatable.c
@@ -0,0 +1,184 @@
+/*
+ * gedit-spell-app-activatable.c
+ * This file is part of gedit
+ *
+ * Copyright (C) 2014 - Ignacio Casal Quinteiro
+ *
+ * gedit is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * gedit 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 gedit. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "gedit-spell-app-activatable.h"
+#include <glib/gi18n.h>
+#include <libpeas/peas-object-module.h>
+#include <gedit/gedit-app-activatable.h>
+#include <gedit/gedit-app.h>
+
+typedef struct _GeditSpellAppActivatablePrivate GeditSpellAppActivatablePrivate;
+
+struct _GeditSpellAppActivatable
+{
+ GObject parent;
+};
+
+struct _GeditSpellAppActivatablePrivate
+{
+ GeditApp *app;
+ GeditMenuExtension *menu_ext;
+};
+
+enum
+{
+ PROP_0,
+ PROP_APP
+};
+
+static void gedit_app_activatable_iface_init (GeditAppActivatableInterface *iface);
+
+G_DEFINE_DYNAMIC_TYPE_EXTENDED (GeditSpellAppActivatable,
+ gedit_spell_app_activatable,
+ G_TYPE_OBJECT,
+ 0,
+ G_ADD_PRIVATE_DYNAMIC (GeditSpellAppActivatable)
+ G_IMPLEMENT_INTERFACE_DYNAMIC (GEDIT_TYPE_APP_ACTIVATABLE,
+ gedit_app_activatable_iface_init))
+
+static void
+gedit_spell_app_activatable_dispose (GObject *object)
+{
+ GeditSpellAppActivatable *activatable = GEDIT_SPELL_APP_ACTIVATABLE (object);
+ GeditSpellAppActivatablePrivate *priv = gedit_spell_app_activatable_get_instance_private (activatable);
+
+ g_clear_object (&priv->app);
+ g_clear_object (&priv->menu_ext);
+
+ G_OBJECT_CLASS (gedit_spell_app_activatable_parent_class)->dispose (object);
+}
+
+static void
+gedit_spell_app_activatable_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GeditSpellAppActivatable *activatable = GEDIT_SPELL_APP_ACTIVATABLE (object);
+ GeditSpellAppActivatablePrivate *priv = gedit_spell_app_activatable_get_instance_private (activatable);
+
+ switch (prop_id)
+ {
+ case PROP_APP:
+ priv->app = GEDIT_APP (g_value_dup_object (value));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gedit_spell_app_activatable_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GeditSpellAppActivatable *activatable = GEDIT_SPELL_APP_ACTIVATABLE (object);
+ GeditSpellAppActivatablePrivate *priv = gedit_spell_app_activatable_get_instance_private (activatable);
+
+ switch (prop_id)
+ {
+ case PROP_APP:
+ g_value_set_object (value, priv->app);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gedit_spell_app_activatable_class_init (GeditSpellAppActivatableClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->dispose = gedit_spell_app_activatable_dispose;
+ object_class->set_property = gedit_spell_app_activatable_set_property;
+ object_class->get_property = gedit_spell_app_activatable_get_property;
+
+ g_object_class_override_property (object_class, PROP_APP, "app");
+}
+
+static void
+gedit_spell_app_activatable_class_finalize (GeditSpellAppActivatableClass *klass)
+{
+}
+
+static void
+gedit_spell_app_activatable_init (GeditSpellAppActivatable *self)
+{
+}
+
+static void
+gedit_spell_app_activatable_activate (GeditAppActivatable *activatable)
+{
+ GeditSpellAppActivatable *app_activatable = GEDIT_SPELL_APP_ACTIVATABLE (activatable);
+ GeditSpellAppActivatablePrivate *priv = gedit_spell_app_activatable_get_instance_private (app_activatable);
+ GMenuItem *item;
+
+ const gchar *accels[] = { "<Shift>F7", NULL };
+ gtk_application_set_accels_for_action (GTK_APPLICATION (priv->app),"win.check-spell", accels);
+ priv->menu_ext = gedit_app_activatable_extend_menu (activatable, "spell-section");
+
+ item = g_menu_item_new (_("_Check Spelling…"), "win.check-spell");
+ gedit_menu_extension_append_menu_item (priv->menu_ext, item);
+ g_object_unref (item);
+
+ item = g_menu_item_new (_("Set _Language…"), "win.config-spell");
+ gedit_menu_extension_append_menu_item (priv->menu_ext, item);
+ g_object_unref (item);
+
+ item = g_menu_item_new (_("_Highlight Misspelled Words"), "win.inline-spell-checker");
+ gedit_menu_extension_append_menu_item (priv->menu_ext, item);
+ g_object_unref (item);
+}
+
+static void
+gedit_spell_app_activatable_deactivate (GeditAppActivatable *activatable)
+{
+ GeditSpellAppActivatable *app_activatable = GEDIT_SPELL_APP_ACTIVATABLE (activatable);
+ GeditSpellAppActivatablePrivate *priv = gedit_spell_app_activatable_get_instance_private (app_activatable);
+
+ const gchar *accels[] = { NULL };
+ gtk_application_set_accels_for_action (GTK_APPLICATION (priv->app),"win.check-spell", accels);
+ g_clear_object (&priv->menu_ext);
+}
+
+static void
+gedit_app_activatable_iface_init (GeditAppActivatableInterface *iface)
+{
+ iface->activate = gedit_spell_app_activatable_activate;
+ iface->deactivate = gedit_spell_app_activatable_deactivate;
+}
+
+void
+gedit_spell_app_activatable_register (GTypeModule *module)
+{
+ gedit_spell_app_activatable_register_type (module);
+
+ peas_object_module_register_extension_type (PEAS_OBJECT_MODULE (module),
+ GEDIT_TYPE_APP_ACTIVATABLE,
+ GEDIT_TYPE_SPELL_APP_ACTIVATABLE);
+}
+
+/* ex:set ts=8 noet: */
diff --git a/plugins/spell/gedit-spell-app-activatable.h b/plugins/spell/gedit-spell-app-activatable.h
new file mode 100644
index 0000000..aebb5ca
--- /dev/null
+++ b/plugins/spell/gedit-spell-app-activatable.h
@@ -0,0 +1,38 @@
+/*
+ * gedit-spell-app-activatable.h
+ * This file is part of gedit
+ *
+ * Copyright (C) 2014 - Ignacio Casal Quinteiro
+ *
+ * gedit is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * gedit 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 gedit. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#ifndef GEDIT_SPELL_APP_ACTIVATABLE_H
+#define GEDIT_SPELL_APP_ACTIVATABLE_H
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define GEDIT_TYPE_SPELL_APP_ACTIVATABLE (gedit_spell_app_activatable_get_type ())
+G_DECLARE_FINAL_TYPE (GeditSpellAppActivatable, gedit_spell_app_activatable,
+ GEDIT, SPELL_APP_ACTIVATABLE,
+ GObject)
+
+void gedit_spell_app_activatable_register (GTypeModule *module);
+
+G_END_DECLS
+
+#endif /* GEDIT_SPELL_APP_ACTIVATABLE_H */
diff --git a/plugins/spell/gedit-spell-plugin.c b/plugins/spell/gedit-spell-plugin.c
new file mode 100644
index 0000000..6116778
--- /dev/null
+++ b/plugins/spell/gedit-spell-plugin.c
@@ -0,0 +1,810 @@
+/*
+ * gedit-spell-plugin.c
+ *
+ * Copyright (C) 2002-2005 Paolo Maggi
+ * Copyright (C) 2015-2016 Sébastien Wilmet
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "gedit-spell-plugin.h"
+
+#include <glib/gi18n.h>
+#include <gedit/gedit-debug.h>
+#include <gedit/gedit-app.h>
+#include <gedit/gedit-window.h>
+#include <gedit/gedit-window-activatable.h>
+#include <gspell/gspell.h>
+#include <libpeas-gtk/peas-gtk-configurable.h>
+
+#include "gedit-spell-app-activatable.h"
+
+#define GEDIT_METADATA_ATTRIBUTE_SPELL_LANGUAGE "gedit-spell-language"
+#define GEDIT_METADATA_ATTRIBUTE_SPELL_ENABLED "gedit-spell-enabled"
+
+#define SPELL_ENABLED_STR "1"
+#define SPELL_BASE_SETTINGS "org.gnome.gedit.plugins.spell"
+#define SETTINGS_KEY_HIGHLIGHT_MISSPELLED "highlight-misspelled"
+
+static void gedit_window_activatable_iface_init (GeditWindowActivatableInterface *iface);
+static void peas_gtk_configurable_iface_init (PeasGtkConfigurableInterface *iface);
+
+struct _GeditSpellPluginPrivate
+{
+ GeditWindow *window;
+ GSettings *settings;
+};
+
+enum
+{
+ PROP_0,
+ PROP_WINDOW
+};
+
+typedef struct _SpellConfigureWidget SpellConfigureWidget;
+
+struct _SpellConfigureWidget
+{
+ GtkWidget *content;
+ GtkWidget *highlight_button;
+
+ GSettings *settings;
+};
+
+G_DEFINE_DYNAMIC_TYPE_EXTENDED (GeditSpellPlugin,
+ gedit_spell_plugin,
+ PEAS_TYPE_EXTENSION_BASE,
+ 0,
+ G_IMPLEMENT_INTERFACE_DYNAMIC (GEDIT_TYPE_WINDOW_ACTIVATABLE,
+ gedit_window_activatable_iface_init)
+ G_IMPLEMENT_INTERFACE_DYNAMIC (PEAS_GTK_TYPE_CONFIGURABLE,
+ peas_gtk_configurable_iface_init)
+ G_ADD_PRIVATE_DYNAMIC (GeditSpellPlugin))
+
+static void
+gedit_spell_plugin_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GeditSpellPlugin *plugin = GEDIT_SPELL_PLUGIN (object);
+
+ switch (prop_id)
+ {
+ case PROP_WINDOW:
+ plugin->priv->window = GEDIT_WINDOW (g_value_dup_object (value));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gedit_spell_plugin_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GeditSpellPlugin *plugin = GEDIT_SPELL_PLUGIN (object);
+
+ switch (prop_id)
+ {
+ case PROP_WINDOW:
+ g_value_set_object (value, plugin->priv->window);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gedit_spell_plugin_dispose (GObject *object)
+{
+ GeditSpellPlugin *plugin = GEDIT_SPELL_PLUGIN (object);
+
+ gedit_debug_message (DEBUG_PLUGINS, "GeditSpellPlugin disposing");
+
+ g_clear_object (&plugin->priv->window);
+ g_clear_object (&plugin->priv->settings);
+
+ G_OBJECT_CLASS (gedit_spell_plugin_parent_class)->dispose (object);
+}
+
+static void
+gedit_spell_plugin_class_init (GeditSpellPluginClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->set_property = gedit_spell_plugin_set_property;
+ object_class->get_property = gedit_spell_plugin_get_property;
+ object_class->dispose = gedit_spell_plugin_dispose;
+
+ g_object_class_override_property (object_class, PROP_WINDOW, "window");
+}
+
+static void
+gedit_spell_plugin_class_finalize (GeditSpellPluginClass *klass)
+{
+}
+
+static void
+gedit_spell_plugin_init (GeditSpellPlugin *plugin)
+{
+ gedit_debug_message (DEBUG_PLUGINS, "GeditSpellPlugin initializing");
+
+ plugin->priv = gedit_spell_plugin_get_instance_private (plugin);
+ plugin->priv->settings = g_settings_new (SPELL_BASE_SETTINGS);
+}
+
+static GspellChecker *
+get_spell_checker (GeditDocument *doc)
+{
+ GspellTextBuffer *gspell_buffer;
+
+ gspell_buffer = gspell_text_buffer_get_from_gtk_text_buffer (GTK_TEXT_BUFFER (doc));
+ return gspell_text_buffer_get_spell_checker (gspell_buffer);
+}
+
+static const GspellLanguage *
+get_language_from_metadata (GeditDocument *doc)
+{
+ const GspellLanguage *lang = NULL;
+ gchar *language_code = NULL;
+
+ language_code = gedit_document_get_metadata (doc, GEDIT_METADATA_ATTRIBUTE_SPELL_LANGUAGE);
+
+ if (language_code != NULL)
+ {
+ lang = gspell_language_lookup (language_code);
+ g_free (language_code);
+ }
+
+ return lang;
+}
+
+static void
+check_spell_cb (GSimpleAction *action,
+ GVariant *parameter,
+ gpointer data)
+{
+ GeditSpellPlugin *plugin = GEDIT_SPELL_PLUGIN (data);
+ GeditSpellPluginPrivate *priv;
+ GeditView *view;
+ GspellNavigator *navigator;
+ GtkWidget *dialog;
+
+ gedit_debug (DEBUG_PLUGINS);
+
+ priv = plugin->priv;
+
+ view = gedit_window_get_active_view (priv->window);
+ g_return_if_fail (view != NULL);
+
+ navigator = gspell_navigator_text_view_new (GTK_TEXT_VIEW (view));
+ dialog = gspell_checker_dialog_new (GTK_WINDOW (priv->window), navigator);
+
+ gtk_widget_show (dialog);
+}
+
+static void
+language_dialog_response_cb (GtkDialog *dialog,
+ gint response_id,
+ gpointer user_data)
+{
+ if (response_id == GTK_RESPONSE_HELP)
+ {
+ gedit_app_show_help (GEDIT_APP (g_application_get_default ()),
+ GTK_WINDOW (dialog),
+ NULL,
+ "gedit-spellcheck");
+ return;
+ }
+
+ gtk_widget_destroy (GTK_WIDGET (dialog));
+}
+
+static void
+set_language_cb (GSimpleAction *action,
+ GVariant *parameter,
+ gpointer data)
+{
+ GeditSpellPlugin *plugin = GEDIT_SPELL_PLUGIN (data);
+ GeditSpellPluginPrivate *priv;
+ GeditDocument *doc;
+ GspellChecker *checker;
+ const GspellLanguage *lang;
+ GtkWidget *dialog;
+ GtkWindowGroup *window_group;
+
+ gedit_debug (DEBUG_PLUGINS);
+
+ priv = plugin->priv;
+
+ doc = gedit_window_get_active_document (priv->window);
+ g_return_if_fail (doc != NULL);
+
+ checker = get_spell_checker (doc);
+ g_return_if_fail (checker != NULL);
+
+ lang = gspell_checker_get_language (checker);
+
+ dialog = gspell_language_chooser_dialog_new (GTK_WINDOW (priv->window),
+ lang,
+ GTK_DIALOG_MODAL |
+ GTK_DIALOG_DESTROY_WITH_PARENT);
+
+ g_object_bind_property (dialog, "language",
+ checker, "language",
+ G_BINDING_DEFAULT);
+
+ window_group = gedit_window_get_group (priv->window);
+
+ gtk_window_group_add_window (window_group, GTK_WINDOW (dialog));
+
+ gtk_dialog_add_button (GTK_DIALOG (dialog),
+ _("_Help"),
+ GTK_RESPONSE_HELP);
+
+ g_signal_connect (dialog,
+ "response",
+ G_CALLBACK (language_dialog_response_cb),
+ NULL);
+
+ gtk_widget_show (dialog);
+}
+
+static void
+inline_checker_activate_cb (GSimpleAction *action,
+ GVariant *parameter,
+ gpointer data)
+{
+ GeditSpellPlugin *plugin = GEDIT_SPELL_PLUGIN (data);
+ GeditSpellPluginPrivate *priv = plugin->priv;
+ GVariant *state;
+ gboolean active;
+ GeditView *view;
+
+ gedit_debug (DEBUG_PLUGINS);
+
+ state = g_action_get_state (G_ACTION (action));
+ g_return_if_fail (state != NULL);
+
+ active = g_variant_get_boolean (state);
+ g_variant_unref (state);
+
+ /* We must toggle ourself the value. */
+ active = !active;
+ g_action_change_state (G_ACTION (action), g_variant_new_boolean (active));
+
+ view = gedit_window_get_active_view (priv->window);
+ if (view != NULL)
+ {
+ GeditDocument *doc;
+
+ doc = GEDIT_DOCUMENT (gtk_text_view_get_buffer (GTK_TEXT_VIEW (view)));
+
+ /* Set metadata in the "activate" handler, not in "change-state"
+ * because "change-state" is called every time the state
+ * changes, not specifically when the user has changed the state
+ * herself. For example "change-state" is called to initialize
+ * the sate to the default value specified in the GActionEntry.
+ */
+ gedit_document_set_metadata (doc,
+ GEDIT_METADATA_ATTRIBUTE_SPELL_ENABLED,
+ active ? SPELL_ENABLED_STR : NULL,
+ NULL);
+ }
+}
+
+static void
+inline_checker_change_state_cb (GSimpleAction *action,
+ GVariant *state,
+ gpointer data)
+{
+ GeditSpellPlugin *plugin = GEDIT_SPELL_PLUGIN (data);
+ GeditSpellPluginPrivate *priv = plugin->priv;
+ GeditView *view;
+ gboolean active;
+
+ gedit_debug (DEBUG_PLUGINS);
+
+ active = g_variant_get_boolean (state);
+
+ gedit_debug_message (DEBUG_PLUGINS, active ? "Inline Checker activated" : "Inline Checker deactivated");
+
+ view = gedit_window_get_active_view (priv->window);
+ if (view != NULL)
+ {
+ GspellTextView *gspell_view;
+
+ gspell_view = gspell_text_view_get_from_gtk_text_view (GTK_TEXT_VIEW (view));
+ gspell_text_view_set_inline_spell_checking (gspell_view, active);
+
+ g_simple_action_set_state (action, g_variant_new_boolean (active));
+ }
+}
+
+static void
+update_ui (GeditSpellPlugin *plugin)
+{
+ GeditSpellPluginPrivate *priv;
+ GeditTab *tab;
+ GeditView *view = NULL;
+ gboolean editable_view;
+ GAction *check_spell_action;
+ GAction *config_spell_action;
+ GAction *inline_checker_action;
+
+ gedit_debug (DEBUG_PLUGINS);
+
+ priv = plugin->priv;
+
+ tab = gedit_window_get_active_tab (priv->window);
+ if (tab != NULL)
+ {
+ view = gedit_tab_get_view (tab);
+ }
+
+ editable_view = (view != NULL) && gtk_text_view_get_editable (GTK_TEXT_VIEW (view));
+
+ check_spell_action = g_action_map_lookup_action (G_ACTION_MAP (priv->window),
+ "check-spell");
+ g_simple_action_set_enabled (G_SIMPLE_ACTION (check_spell_action),
+ editable_view);
+
+ config_spell_action = g_action_map_lookup_action (G_ACTION_MAP (priv->window),
+ "config-spell");
+ g_simple_action_set_enabled (G_SIMPLE_ACTION (config_spell_action),
+ editable_view);
+
+ inline_checker_action = g_action_map_lookup_action (G_ACTION_MAP (priv->window),
+ "inline-spell-checker");
+ g_simple_action_set_enabled (G_SIMPLE_ACTION (inline_checker_action),
+ editable_view);
+
+ /* Update only on normal state to avoid garbage changes during e.g. file
+ * loading.
+ */
+ if (tab != NULL &&
+ gedit_tab_get_state (tab) == GEDIT_TAB_STATE_NORMAL)
+ {
+ GspellTextView *gspell_view;
+ gboolean inline_checking_enabled;
+
+ gspell_view = gspell_text_view_get_from_gtk_text_view (GTK_TEXT_VIEW (view));
+ inline_checking_enabled = gspell_text_view_get_inline_spell_checking (gspell_view);
+
+ g_action_change_state (inline_checker_action,
+ g_variant_new_boolean (inline_checking_enabled));
+ }
+}
+
+static void
+setup_inline_checker_from_metadata (GeditSpellPlugin *plugin,
+ GeditView *view)
+{
+ GeditDocument *doc;
+ gboolean enabled;
+ gchar *enabled_str;
+ GspellTextView *gspell_view;
+ GeditView *active_view;
+
+ doc = GEDIT_DOCUMENT (gtk_text_view_get_buffer (GTK_TEXT_VIEW (view)));
+
+ enabled = g_settings_get_boolean(plugin->priv->settings, SETTINGS_KEY_HIGHLIGHT_MISSPELLED);
+ enabled_str = gedit_document_get_metadata (doc, GEDIT_METADATA_ATTRIBUTE_SPELL_ENABLED);
+ if (enabled_str != NULL)
+ {
+ enabled = g_str_equal (enabled_str, SPELL_ENABLED_STR);
+ g_free (enabled_str);
+ }
+
+ gspell_view = gspell_text_view_get_from_gtk_text_view (GTK_TEXT_VIEW (view));
+ gspell_text_view_set_inline_spell_checking (gspell_view, enabled);
+
+ /* In case that the view is the active one we mark the spell action */
+ active_view = gedit_window_get_active_view (plugin->priv->window);
+
+ if (active_view == view)
+ {
+ GAction *action;
+
+ action = g_action_map_lookup_action (G_ACTION_MAP (plugin->priv->window),
+ "inline-spell-checker");
+ g_action_change_state (action, g_variant_new_boolean (enabled));
+ }
+}
+
+static void
+language_notify_cb (GspellChecker *checker,
+ GParamSpec *pspec,
+ GeditDocument *doc)
+{
+ const GspellLanguage *lang;
+ const gchar *language_code;
+
+ g_return_if_fail (GEDIT_IS_DOCUMENT (doc));
+
+ lang = gspell_checker_get_language (checker);
+ g_return_if_fail (lang != NULL);
+
+ language_code = gspell_language_get_code (lang);
+ g_return_if_fail (language_code != NULL);
+
+ gedit_document_set_metadata (doc,
+ GEDIT_METADATA_ATTRIBUTE_SPELL_LANGUAGE, language_code,
+ NULL);
+}
+
+static void
+on_document_loaded (GeditDocument *doc,
+ GeditSpellPlugin *plugin)
+{
+ GspellChecker *checker;
+ GeditTab *tab;
+ GeditView *view;
+
+ checker = get_spell_checker (doc);
+
+ if (checker != NULL)
+ {
+ const GspellLanguage *lang;
+
+ lang = get_language_from_metadata (doc);
+
+ if (lang != NULL)
+ {
+ g_signal_handlers_block_by_func (checker, language_notify_cb, doc);
+ gspell_checker_set_language (checker, lang);
+ g_signal_handlers_unblock_by_func (checker, language_notify_cb, doc);
+ }
+ }
+
+ tab = gedit_tab_get_from_document (doc);
+ view = gedit_tab_get_view (tab);
+ setup_inline_checker_from_metadata (plugin, view);
+}
+
+static void
+on_document_saved (GeditDocument *doc,
+ gpointer user_data)
+{
+ GeditTab *tab;
+ GeditView *view;
+ GspellChecker *checker;
+ const gchar *language_code = NULL;
+ GspellTextView *gspell_view;
+ gboolean inline_checking_enabled;
+
+ /* Make sure to save the metadata here too */
+
+ checker = get_spell_checker (doc);
+
+ if (checker != NULL)
+ {
+ const GspellLanguage *lang;
+
+ lang = gspell_checker_get_language (checker);
+ if (lang != NULL)
+ {
+ language_code = gspell_language_get_code (lang);
+ }
+ }
+
+ tab = gedit_tab_get_from_document (doc);
+ view = gedit_tab_get_view (tab);
+
+ gspell_view = gspell_text_view_get_from_gtk_text_view (GTK_TEXT_VIEW (view));
+ inline_checking_enabled = gspell_text_view_get_inline_spell_checking (gspell_view);
+
+ gedit_document_set_metadata (doc,
+ GEDIT_METADATA_ATTRIBUTE_SPELL_ENABLED,
+ inline_checking_enabled ? SPELL_ENABLED_STR : NULL,
+ GEDIT_METADATA_ATTRIBUTE_SPELL_LANGUAGE,
+ language_code,
+ NULL);
+}
+
+static void
+activate_spell_checking_in_view (GeditSpellPlugin *plugin,
+ GeditView *view)
+{
+ GeditDocument *doc;
+
+ doc = GEDIT_DOCUMENT (gtk_text_view_get_buffer (GTK_TEXT_VIEW (view)));
+
+ /* It is possible that a GspellChecker has already been set, for example
+ * if a GeditTab has moved to another window.
+ */
+ if (get_spell_checker (doc) == NULL)
+ {
+ const GspellLanguage *lang;
+ GspellChecker *checker;
+ GspellTextBuffer *gspell_buffer;
+
+ lang = get_language_from_metadata (doc);
+ checker = gspell_checker_new (lang);
+
+ g_signal_connect_object (checker,
+ "notify::language",
+ G_CALLBACK (language_notify_cb),
+ doc,
+ 0);
+
+ gspell_buffer = gspell_text_buffer_get_from_gtk_text_buffer (GTK_TEXT_BUFFER (doc));
+ gspell_text_buffer_set_spell_checker (gspell_buffer, checker);
+ g_object_unref (checker);
+
+ setup_inline_checker_from_metadata (plugin, view);
+ }
+
+ g_signal_connect_object (doc,
+ "loaded",
+ G_CALLBACK (on_document_loaded),
+ plugin,
+ 0);
+
+ g_signal_connect_object (doc,
+ "saved",
+ G_CALLBACK (on_document_saved),
+ plugin,
+ 0);
+}
+
+static void
+disconnect_view (GeditSpellPlugin *plugin,
+ GeditView *view)
+{
+ GtkTextBuffer *buffer;
+
+ buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (view));
+
+ /* It should still be the same buffer as the one where the signal
+ * handlers were connected. If not, we assume that the old buffer is
+ * finalized. And it is anyway safe to call
+ * g_signal_handlers_disconnect_by_func() if no signal handlers are
+ * found.
+ */
+ g_signal_handlers_disconnect_by_func (buffer, on_document_loaded, plugin);
+ g_signal_handlers_disconnect_by_func (buffer, on_document_saved, plugin);
+}
+
+static void
+deactivate_spell_checking_in_view (GeditSpellPlugin *plugin,
+ GeditView *view)
+{
+ GtkTextBuffer *gtk_buffer;
+ GspellTextBuffer *gspell_buffer;
+ GspellTextView *gspell_view;
+
+ disconnect_view (plugin, view);
+
+ gtk_buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (view));
+ gspell_buffer = gspell_text_buffer_get_from_gtk_text_buffer (gtk_buffer);
+ gspell_text_buffer_set_spell_checker (gspell_buffer, NULL);
+
+ gspell_view = gspell_text_view_get_from_gtk_text_view (GTK_TEXT_VIEW (view));
+ gspell_text_view_set_inline_spell_checking (gspell_view, FALSE);
+}
+
+static void
+tab_added_cb (GeditWindow *window,
+ GeditTab *tab,
+ GeditSpellPlugin *plugin)
+{
+ activate_spell_checking_in_view (plugin, gedit_tab_get_view (tab));
+}
+
+static void
+tab_removed_cb (GeditWindow *window,
+ GeditTab *tab,
+ GeditSpellPlugin *plugin)
+{
+ /* Don't deactivate completely the spell checking in @tab, since the tab
+ * can be moved to another window and we don't want to loose the spell
+ * checking settings (they are not saved in metadata for unsaved
+ * documents).
+ */
+ disconnect_view (plugin, gedit_tab_get_view (tab));
+}
+
+static void
+gedit_spell_plugin_activate (GeditWindowActivatable *activatable)
+{
+ GeditSpellPlugin *plugin;
+ GeditSpellPluginPrivate *priv;
+ GList *views;
+ GList *l;
+
+ const GActionEntry action_entries[] =
+ {
+ { "check-spell", check_spell_cb },
+ { "config-spell", set_language_cb },
+ { "inline-spell-checker",
+ inline_checker_activate_cb,
+ NULL,
+ "false",
+ inline_checker_change_state_cb }
+ };
+
+ gedit_debug (DEBUG_PLUGINS);
+
+ plugin = GEDIT_SPELL_PLUGIN (activatable);
+ priv = plugin->priv;
+
+ g_action_map_add_action_entries (G_ACTION_MAP (priv->window),
+ action_entries,
+ G_N_ELEMENTS (action_entries),
+ activatable);
+
+ update_ui (plugin);
+
+ views = gedit_window_get_views (priv->window);
+ for (l = views; l != NULL; l = l->next)
+ {
+ activate_spell_checking_in_view (plugin, GEDIT_VIEW (l->data));
+ }
+
+ g_signal_connect (priv->window,
+ "tab-added",
+ G_CALLBACK (tab_added_cb),
+ activatable);
+
+ g_signal_connect (priv->window,
+ "tab-removed",
+ G_CALLBACK (tab_removed_cb),
+ activatable);
+}
+
+static void
+gedit_spell_plugin_deactivate (GeditWindowActivatable *activatable)
+{
+ GeditSpellPlugin *plugin;
+ GeditSpellPluginPrivate *priv;
+ GList *views;
+ GList *l;
+
+ gedit_debug (DEBUG_PLUGINS);
+
+ plugin = GEDIT_SPELL_PLUGIN (activatable);
+ priv = plugin->priv;
+
+ g_action_map_remove_action (G_ACTION_MAP (priv->window), "check-spell");
+ g_action_map_remove_action (G_ACTION_MAP (priv->window), "config-spell");
+ g_action_map_remove_action (G_ACTION_MAP (priv->window), "inline-spell-checker");
+
+ g_signal_handlers_disconnect_by_func (priv->window, tab_added_cb, activatable);
+ g_signal_handlers_disconnect_by_func (priv->window, tab_removed_cb, activatable);
+
+ views = gedit_window_get_views (priv->window);
+ for (l = views; l != NULL; l = l->next)
+ {
+ deactivate_spell_checking_in_view (plugin, GEDIT_VIEW (l->data));
+ }
+}
+
+static void
+gedit_spell_plugin_update_state (GeditWindowActivatable *activatable)
+{
+ gedit_debug (DEBUG_PLUGINS);
+
+ update_ui (GEDIT_SPELL_PLUGIN (activatable));
+}
+
+static void
+gedit_window_activatable_iface_init (GeditWindowActivatableInterface *iface)
+{
+ iface->activate = gedit_spell_plugin_activate;
+ iface->deactivate = gedit_spell_plugin_deactivate;
+ iface->update_state = gedit_spell_plugin_update_state;
+}
+
+G_MODULE_EXPORT void
+peas_register_types (PeasObjectModule *module)
+{
+ gedit_spell_plugin_register_type (G_TYPE_MODULE (module));
+ gedit_spell_app_activatable_register (G_TYPE_MODULE (module));
+
+ peas_object_module_register_extension_type (module,
+ GEDIT_TYPE_WINDOW_ACTIVATABLE,
+ GEDIT_TYPE_SPELL_PLUGIN);
+ peas_object_module_register_extension_type (module,
+ PEAS_GTK_TYPE_CONFIGURABLE,
+ GEDIT_TYPE_SPELL_PLUGIN);
+}
+
+static void
+highlight_button_toggled (GtkToggleButton *button,
+ SpellConfigureWidget *widget)
+{
+ gboolean status = gtk_toggle_button_get_active (button);
+ g_settings_set_boolean(widget->settings, SETTINGS_KEY_HIGHLIGHT_MISSPELLED, status);
+}
+
+static void
+configure_widget_destroyed (GtkWidget *widget,
+ gpointer data)
+{
+ SpellConfigureWidget *conf_widget = (SpellConfigureWidget *)data;
+
+ gedit_debug (DEBUG_PLUGINS);
+
+ g_object_unref (conf_widget->settings);
+ g_slice_free (SpellConfigureWidget, data);
+
+ gedit_debug_message (DEBUG_PLUGINS, "END");
+}
+
+static SpellConfigureWidget *
+get_configure_widget (GeditSpellPlugin *plugin)
+{
+ SpellConfigureWidget *widget;
+ GtkBuilder *builder;
+ gchar *root_objects[] = {
+ "spell_dialog_content",
+ NULL
+ };
+
+ gedit_debug (DEBUG_PLUGINS);
+
+ widget = g_slice_new (SpellConfigureWidget);
+ widget->settings = g_object_ref (plugin->priv->settings);
+
+ builder = gtk_builder_new ();
+ gtk_builder_add_objects_from_resource (builder, "/org/gnome/gedit/plugins/spell/ui/gedit-spell-setup-dialog.ui",
+ root_objects, NULL);
+ widget->content = GTK_WIDGET (gtk_builder_get_object (builder, "spell_dialog_content"));
+ g_object_ref (widget->content);
+
+ widget->highlight_button = GTK_WIDGET (gtk_builder_get_object (builder, "highlight_button"));
+ g_object_unref (builder);
+
+ gboolean status = g_settings_get_boolean(widget->settings, SETTINGS_KEY_HIGHLIGHT_MISSPELLED);
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (widget->highlight_button), status);
+
+ g_signal_connect (widget->highlight_button,
+ "toggled",
+ G_CALLBACK (highlight_button_toggled),
+ widget);
+
+ g_signal_connect (widget->content,
+ "destroy",
+ G_CALLBACK (configure_widget_destroyed),
+ widget);
+
+ return widget;
+}
+
+
+static GtkWidget *
+gedit_spell_plugin_create_configure_widget (PeasGtkConfigurable *configurable)
+{
+ SpellConfigureWidget *widget;
+
+ widget = get_configure_widget (GEDIT_SPELL_PLUGIN (configurable));
+
+ return widget->content;
+}
+
+static void
+peas_gtk_configurable_iface_init (PeasGtkConfigurableInterface *iface)
+{
+ iface->create_configure_widget = gedit_spell_plugin_create_configure_widget;
+}
+
+/* ex:set ts=8 noet: */
diff --git a/plugins/spell/gedit-spell-plugin.h b/plugins/spell/gedit-spell-plugin.h
new file mode 100644
index 0000000..13539b4
--- /dev/null
+++ b/plugins/spell/gedit-spell-plugin.h
@@ -0,0 +1,62 @@
+/*
+ * gedit-spell-plugin.h
+ *
+ * Copyright (C) 2002-2005 Paolo Maggi
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef GEDIT_SPELL_PLUGIN_H
+#define GEDIT_SPELL_PLUGIN_H
+
+#include <glib.h>
+#include <glib-object.h>
+#include <libpeas/peas-extension-base.h>
+#include <libpeas/peas-object-module.h>
+
+G_BEGIN_DECLS
+
+#define GEDIT_TYPE_SPELL_PLUGIN (gedit_spell_plugin_get_type ())
+#define GEDIT_SPELL_PLUGIN(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GEDIT_TYPE_SPELL_PLUGIN, GeditSpellPlugin))
+#define GEDIT_SPELL_PLUGIN_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GEDIT_TYPE_SPELL_PLUGIN, GeditSpellPluginClass))
+#define GEDIT_IS_SPELL_PLUGIN(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GEDIT_TYPE_SPELL_PLUGIN))
+#define GEDIT_IS_SPELL_PLUGIN_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GEDIT_TYPE_SPELL_PLUGIN))
+#define GEDIT_SPELL_PLUGIN_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GEDIT_TYPE_SPELL_PLUGIN, GeditSpellPluginClass))
+
+typedef struct _GeditSpellPlugin GeditSpellPlugin;
+typedef struct _GeditSpellPluginPrivate GeditSpellPluginPrivate;
+typedef struct _GeditSpellPluginClass GeditSpellPluginClass;
+
+struct _GeditSpellPlugin
+{
+ PeasExtensionBase parent_instance;
+
+ /*< private >*/
+ GeditSpellPluginPrivate *priv;
+};
+
+struct _GeditSpellPluginClass
+{
+ PeasExtensionBaseClass parent_class;
+};
+
+GType gedit_spell_plugin_get_type (void) G_GNUC_CONST;
+
+G_MODULE_EXPORT void peas_register_types (PeasObjectModule *module);
+
+G_END_DECLS
+
+#endif /* GEDIT_SPELL_PLUGIN_H */
+
+/* ex:set ts=8 noet: */
diff --git a/plugins/spell/meson.build b/plugins/spell/meson.build
new file mode 100644
index 0000000..8ce7634
--- /dev/null
+++ b/plugins/spell/meson.build
@@ -0,0 +1,54 @@
+libspell_sources = files(
+ 'gedit-spell-app-activatable.c',
+ 'gedit-spell-plugin.c',
+)
+
+libspell_deps = [
+ libgedit_dep,
+ gspell_dep,
+]
+
+subdir('resources')
+
+libspell_sha = shared_module(
+ 'spell',
+ sources: libspell_sources,
+ include_directories: root_include_dir,
+ dependencies: libspell_deps,
+ install: true,
+ install_dir: join_paths(
+ pkglibdir,
+ 'plugins',
+ ),
+ name_suffix: module_suffix,
+)
+
+spell_gschema_file = files('org.gnome.gedit.plugins.spell.gschema.xml')
+install_data(
+ spell_gschema_file,
+ install_dir: join_paths(get_option('prefix'), get_option('datadir'), 'glib-2.0/schemas')
+)
+
+if xmllint.found()
+ test(
+ 'validate-spell-gschema',
+ xmllint,
+ args: [
+ '--noout',
+ '--dtdvalid', gschema_dtd,
+ spell_gschema_file,
+ ]
+ )
+endif
+
+custom_target(
+ 'spell.plugin',
+ input: 'spell.plugin.desktop.in',
+ output: 'spell.plugin',
+ command: msgfmt_plugin_cmd,
+ install: true,
+ install_dir: join_paths(
+ pkglibdir,
+ 'plugins',
+ )
+)
diff --git a/plugins/spell/org.gnome.gedit.plugins.spell.gschema.xml b/plugins/spell/org.gnome.gedit.plugins.spell.gschema.xml
new file mode 100644
index 0000000..c1dd186
--- /dev/null
+++ b/plugins/spell/org.gnome.gedit.plugins.spell.gschema.xml
@@ -0,0 +1,9 @@
+<schemalist gettext-domain="gedit">
+ <schema id="org.gnome.gedit.plugins.spell" path="/org/gnome/gedit/plugins/spell/">
+ <key name="highlight-misspelled" type="b">
+ <default>false</default>
+ <summary>Highlight misspelled words</summary>
+ <description>Default setting for highlight misspelled words.</description>
+ </key>
+ </schema>
+</schemalist>
diff --git a/plugins/spell/resources/gedit-spell.gresource.xml b/plugins/spell/resources/gedit-spell.gresource.xml
new file mode 100644
index 0000000..861e061
--- /dev/null
+++ b/plugins/spell/resources/gedit-spell.gresource.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<gresources>
+ <gresource prefix="/org/gnome/gedit/plugins/spell">
+ <file preprocess="xml-stripblanks">ui/gedit-spell-setup-dialog.ui</file>
+ </gresource>
+</gresources>
diff --git a/plugins/spell/resources/meson.build b/plugins/spell/resources/meson.build
new file mode 100644
index 0000000..ad15d5e
--- /dev/null
+++ b/plugins/spell/resources/meson.build
@@ -0,0 +1,8 @@
+libspell_res = gnome.compile_resources(
+ 'gedit-spell-resources',
+ 'gedit-spell.gresource.xml',
+)
+
+libspell_sources += [
+ libspell_res.get(0),
+]
diff --git a/plugins/spell/resources/ui/gedit-spell-setup-dialog.ui b/plugins/spell/resources/ui/gedit-spell-setup-dialog.ui
new file mode 100644
index 0000000..5e922c2
--- /dev/null
+++ b/plugins/spell/resources/ui/gedit-spell-setup-dialog.ui
@@ -0,0 +1,97 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Generated with glade 3.22.1 -->
+<interface>
+ <requires lib="gtk+" version="3.0"/>
+ <object class="GtkDialog" id="spell_dialog">
+ <property name="can_focus">False</property>
+ <property name="title" translatable="yes">Configure spell plugin</property>
+ <property name="type_hint">normal</property>
+ <child>
+ <placeholder/>
+ </child>
+ <child internal-child="vbox">
+ <object class="GtkBox" id="dialog-vbox1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">8</property>
+ <child internal-child="action_area">
+ <object class="GtkButtonBox" id="dialog-action_area1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="layout_style">end</property>
+ <child>
+ <object class="GtkButton" id="button1">
+ <property name="label" translatable="yes">_Close</property>
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="can_default">True</property>
+ <property name="receives_default">True</property>
+ <property name="use_underline">True</property>
+ <property name="always_show_image">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="padding">10</property>
+ <property name="pack_type">end</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkBox" id="spell_dialog_content">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="vexpand">True</property>
+ <property name="border_width">10</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkLabel" id="label1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">Defaults for new documents</property>
+ <property name="xalign">0</property>
+ <attributes>
+ <attribute name="weight" value="bold"/>
+ </attributes>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="highlight_button">
+ <property name="label" translatable="yes">Highlight misspelled words</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="draw_indicator">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+</interface>
diff --git a/plugins/spell/spell.plugin.desktop.in b/plugins/spell/spell.plugin.desktop.in
new file mode 100644
index 0000000..1741079
--- /dev/null
+++ b/plugins/spell/spell.plugin.desktop.in
@@ -0,0 +1,11 @@
+[Plugin]
+Module=spell
+IAge=3
+Name=Spell Checker
+Description=Checks the spelling of the current document.
+# TRANSLATORS: Do NOT translate or transliterate this text!
+# This is an icon file name.
+Icon=tools-check-spelling
+Authors=Paolo Maggi <paolo@gnome.org>;Sébastien Wilmet <swilmet@gnome.org>
+Copyright=Copyright © 2002-2005 Paolo Maggi;Copyright © 2015 Sébastien Wilmet
+Website=http://www.gedit.org