diff options
Diffstat (limited to 'plugins/spell')
-rw-r--r-- | plugins/spell/gedit-spell-app-activatable.c | 184 | ||||
-rw-r--r-- | plugins/spell/gedit-spell-app-activatable.h | 38 | ||||
-rw-r--r-- | plugins/spell/gedit-spell-plugin.c | 810 | ||||
-rw-r--r-- | plugins/spell/gedit-spell-plugin.h | 62 | ||||
-rw-r--r-- | plugins/spell/meson.build | 54 | ||||
-rw-r--r-- | plugins/spell/org.gnome.gedit.plugins.spell.gschema.xml | 9 | ||||
-rw-r--r-- | plugins/spell/resources/gedit-spell.gresource.xml | 6 | ||||
-rw-r--r-- | plugins/spell/resources/meson.build | 8 | ||||
-rw-r--r-- | plugins/spell/resources/ui/gedit-spell-setup-dialog.ui | 97 | ||||
-rw-r--r-- | plugins/spell/spell.plugin.desktop.in | 11 |
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 |