1 /*
2  * xed-spell-plugin.c
3  *
4  * Copyright (C) 2002-2005 Paolo Maggi
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2, or (at your option)
9  * any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
19  */
20 
21 #include <config.h>
22 #include <string.h> /* For strlen */
23 #include <glib/gi18n-lib.h>
24 #include <libpeas-gtk/peas-gtk-configurable.h>
25 #include <xed/xed-app.h>
26 #include <xed/xed-window.h>
27 #include <xed/xed-window-activatable.h>
28 #include <xed/xed-debug.h>
29 #include <xed/xed-utils.h>
30 #include <gspell/gspell.h>
31 
32 #include "xed-spell-plugin.h"
33 
34 #define XED_METADATA_ATTRIBUTE_SPELL_LANGUAGE "metadata::xed-spell-language"
35 #define XED_METADATA_ATTRIBUTE_SPELL_ENABLED  "metadata::xed-spell-enabled"
36 
37 #define SPELL_ENABLED_STR "1"
38 
39 #define MENU_PATH "/MenuBar/ToolsMenu/ToolsOps_1"
40 
41 /* GSettings keys */
42 #define SPELL_SCHEMA        "org.x.editor.plugins.spell"
43 #define AUTOCHECK_TYPE_KEY  "autocheck-type"
44 
45 static void xed_window_activatable_iface_init (XedWindowActivatableInterface *iface);
46 static void peas_gtk_configurable_iface_init (PeasGtkConfigurableInterface *iface);
47 
48 struct _XedSpellPluginPrivate
49 {
50     XedWindow *window;
51 
52     GtkActionGroup *action_group;
53     guint           ui_id;
54 
55     GSettings *settings;
56 };
57 
58 enum
59 {
60    PROP_0,
61    PROP_WINDOW
62 };
63 
64 G_DEFINE_DYNAMIC_TYPE_EXTENDED (XedSpellPlugin,
65                                 xed_spell_plugin,
66                                 PEAS_TYPE_EXTENSION_BASE,
67                                 0,
68                                 G_IMPLEMENT_INTERFACE_DYNAMIC (XED_TYPE_WINDOW_ACTIVATABLE,
69                                                                xed_window_activatable_iface_init)
70                                 G_IMPLEMENT_INTERFACE_DYNAMIC (PEAS_GTK_TYPE_CONFIGURABLE,
71                                                                peas_gtk_configurable_iface_init)
72                                 G_ADD_PRIVATE_DYNAMIC (XedSpellPlugin))
73 
74 static void check_spell_cb (GtkAction      *action,
75                             XedSpellPlugin *plugin);
76 static void set_language_cb (GtkAction      *action,
77                              XedSpellPlugin *plugin);
78 static void inline_checker_cb (GtkAction      *action,
79                                XedSpellPlugin *plugin);
80 
81 /* UI actions. */
82 static const GtkActionEntry action_entries[] =
83 {
84     { "CheckSpell",
85       "tools-check-spelling-symbolic",
86       N_("_Check Spelling..."),
87       "<shift>F7",
88       N_("Check the current document for incorrect spelling"),
89       G_CALLBACK (check_spell_cb)
90     },
91 
92     { "ConfigSpell",
93       NULL,
94       N_("Set _Language..."),
95       NULL,
96       N_("Set the language of the current document"),
97       G_CALLBACK (set_language_cb)
98     }
99 };
100 
101 static const GtkToggleActionEntry toggle_action_entries[] =
102 {
103     { "InlineSpellChecker",
104       NULL,
105       N_("_Autocheck Spelling"),
106       NULL,
107       N_("Automatically spell-check the current document"),
108       G_CALLBACK (inline_checker_cb),
109       FALSE
110     }
111 };
112 
113 typedef struct _SpellConfigureWidget SpellConfigureWidget;
114 
115 struct _SpellConfigureWidget
116 {
117     GtkWidget *content;
118 
119     GtkWidget *never;
120     GtkWidget *always;
121     GtkWidget *document;
122 
123     GSettings *settings;
124 };
125 
126 typedef enum
127 {
128     AUTOCHECK_NEVER = 0,
129     AUTOCHECK_DOCUMENT,
130     AUTOCHECK_ALWAYS
131 } XedSpellPluginAutocheckType;
132 
133 static void
xed_spell_plugin_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)134 xed_spell_plugin_set_property (GObject      *object,
135                                guint         prop_id,
136                                const GValue *value,
137                                GParamSpec   *pspec)
138 {
139     XedSpellPlugin *plugin = XED_SPELL_PLUGIN (object);
140 
141     switch (prop_id)
142     {
143         case PROP_WINDOW:
144             plugin->priv->window = XED_WINDOW (g_value_dup_object (value));
145             break;
146         default:
147             G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
148             break;
149     }
150 }
151 
152 static void
xed_spell_plugin_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)153 xed_spell_plugin_get_property (GObject    *object,
154                                guint       prop_id,
155                                GValue     *value,
156                                GParamSpec *pspec)
157 {
158     XedSpellPlugin *plugin = XED_SPELL_PLUGIN (object);
159 
160     switch (prop_id)
161     {
162         case PROP_WINDOW:
163             g_value_set_object (value, plugin->priv->window);
164             break;
165         default:
166             G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
167             break;
168     }
169 }
170 
171 static void
xed_spell_plugin_dispose(GObject * object)172 xed_spell_plugin_dispose (GObject *object)
173 {
174     XedSpellPlugin *plugin = XED_SPELL_PLUGIN (object);
175 
176     xed_debug_message (DEBUG_PLUGINS, "XedSpellPlugin disposing");
177 
178     g_clear_object (&plugin->priv->settings);
179     g_clear_object (&plugin->priv->window);
180     g_clear_object (&plugin->priv->action_group);
181     g_clear_object (&plugin->priv->settings);
182 
183     G_OBJECT_CLASS (xed_spell_plugin_parent_class)->dispose (object);
184 }
185 
186 static void
xed_spell_plugin_class_init(XedSpellPluginClass * klass)187 xed_spell_plugin_class_init (XedSpellPluginClass *klass)
188 {
189     GObjectClass *object_class = G_OBJECT_CLASS (klass);
190 
191     object_class->dispose = xed_spell_plugin_dispose;
192     object_class->set_property = xed_spell_plugin_set_property;
193     object_class->get_property = xed_spell_plugin_get_property;
194 
195     g_object_class_override_property (object_class, PROP_WINDOW, "window");
196 }
197 
198 static void
xed_spell_plugin_class_finalize(XedSpellPluginClass * klass)199 xed_spell_plugin_class_finalize (XedSpellPluginClass *klass)
200 {
201 }
202 
203 static void
xed_spell_plugin_init(XedSpellPlugin * plugin)204 xed_spell_plugin_init (XedSpellPlugin *plugin)
205 {
206     xed_debug_message (DEBUG_PLUGINS, "XedSpellPlugin initializing");
207 
208     plugin->priv = xed_spell_plugin_get_instance_private (plugin);
209 
210     plugin->priv->settings = g_settings_new (SPELL_SCHEMA);
211 }
212 
213 static GspellChecker *
get_spell_checker(XedDocument * doc)214 get_spell_checker (XedDocument *doc)
215 {
216     GspellTextBuffer *gspell_buffer;
217 
218     gspell_buffer = gspell_text_buffer_get_from_gtk_text_buffer (GTK_TEXT_BUFFER (doc));
219     return gspell_text_buffer_get_spell_checker (gspell_buffer);
220 }
221 
222 static const GspellLanguage *
get_language_from_metadata(XedDocument * doc)223 get_language_from_metadata (XedDocument *doc)
224 {
225     const GspellLanguage *lang = NULL;
226     gchar *language_code = NULL;
227 
228     language_code = xed_document_get_metadata (doc, XED_METADATA_ATTRIBUTE_SPELL_LANGUAGE);
229 
230     if (language_code != NULL)
231     {
232         lang = gspell_language_lookup (language_code);
233         g_free (language_code);
234     }
235 
236     return lang;
237 }
238 
239 static void
check_spell_cb(GtkAction * action,XedSpellPlugin * plugin)240 check_spell_cb (GtkAction      *action,
241                 XedSpellPlugin *plugin)
242 {
243     XedSpellPluginPrivate *priv;
244     XedView *view;
245     GspellNavigator *navigator;
246     GtkWidget *dialog;
247 
248     xed_debug (DEBUG_PLUGINS);
249 
250     priv = plugin->priv;
251 
252     view = xed_window_get_active_view (priv->window);
253     g_return_if_fail (view != NULL);
254 
255     navigator = gspell_navigator_text_view_new (GTK_TEXT_VIEW (view));
256     dialog = gspell_checker_dialog_new (GTK_WINDOW (priv->window), navigator);
257 
258     gtk_widget_show (dialog);
259 }
260 
261 static XedSpellPluginAutocheckType
get_autocheck_type(XedSpellPlugin * plugin)262 get_autocheck_type (XedSpellPlugin *plugin)
263 {
264     XedSpellPluginAutocheckType autocheck_type;
265 
266     autocheck_type = g_settings_get_enum (plugin->priv->settings, AUTOCHECK_TYPE_KEY);
267 
268     return autocheck_type;
269 }
270 
271 static void
set_autocheck_type(GSettings * settings,XedSpellPluginAutocheckType autocheck_type)272 set_autocheck_type (GSettings                  *settings,
273                     XedSpellPluginAutocheckType autocheck_type)
274 {
275     if (!g_settings_is_writable (settings, AUTOCHECK_TYPE_KEY))
276     {
277         return;
278     }
279 
280     g_settings_set_enum (settings, AUTOCHECK_TYPE_KEY, autocheck_type);
281 }
282 
283 static void
language_dialog_response_cb(GtkDialog * dialog,gint response_id,gpointer user_data)284 language_dialog_response_cb (GtkDialog *dialog,
285                              gint       response_id,
286                              gpointer   user_data)
287 {
288     if (response_id == GTK_RESPONSE_HELP)
289     {
290         xed_app_show_help (XED_APP (g_application_get_default ()),
291                            GTK_WINDOW (dialog),
292                            NULL,
293                            "xed-spell-checker-plugin");
294         return;
295     }
296 
297     gtk_widget_destroy (GTK_WIDGET (dialog));
298 }
299 
300 static void
configure_widget_button_toggled(GtkToggleButton * button,SpellConfigureWidget * conf_widget)301 configure_widget_button_toggled (GtkToggleButton      *button,
302                                  SpellConfigureWidget *conf_widget)
303 {
304     xed_debug (DEBUG_PLUGINS);
305 
306     if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (conf_widget->always)))
307     {
308         set_autocheck_type (conf_widget->settings, AUTOCHECK_ALWAYS);
309     }
310     else if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (conf_widget->document)))
311     {
312         set_autocheck_type (conf_widget->settings, AUTOCHECK_DOCUMENT);
313     }
314     else
315     {
316         set_autocheck_type (conf_widget->settings, AUTOCHECK_NEVER);
317     }
318 }
319 
320 static void
configure_widget_destroyed(GtkWidget * widget,gpointer data)321 configure_widget_destroyed (GtkWidget *widget,
322                             gpointer   data)
323 {
324     SpellConfigureWidget *conf_widget = (SpellConfigureWidget *)data;
325 
326     xed_debug (DEBUG_PLUGINS);
327 
328     g_object_unref (conf_widget->settings);
329     g_slice_free (SpellConfigureWidget, data);
330 
331     xed_debug_message (DEBUG_PLUGINS, "END");
332 }
333 
334 static SpellConfigureWidget *
get_configure_widget(XedSpellPlugin * plugin)335 get_configure_widget (XedSpellPlugin *plugin)
336 {
337     SpellConfigureWidget *widget;
338     gchar *data_dir;
339     gchar *ui_file;
340     XedSpellPluginAutocheckType autocheck_type;
341     GtkWidget *error_widget;
342     gboolean ret;
343     gchar *root_objects[] = {
344         "spell_dialog_content",
345         NULL
346     };
347 
348     xed_debug (DEBUG_PLUGINS);
349 
350     widget = g_slice_new (SpellConfigureWidget);
351     widget->settings = g_object_ref (plugin->priv->settings);
352 
353     data_dir = peas_extension_base_get_data_dir (PEAS_EXTENSION_BASE (plugin));
354     ui_file = g_build_filename (data_dir, "xed-spell-setup-dialog.ui", NULL);
355     ret = xed_utils_get_ui_objects (ui_file,
356                                     root_objects,
357                                     &error_widget,
358                                     "spell_dialog_content", &widget->content,
359                                     "autocheck_never", &widget->never,
360                                     "autocheck_document", &widget->document,
361                                     "autocheck_always", &widget->always,
362                                     NULL);
363 
364     g_free (data_dir);
365     g_free (ui_file);
366 
367     if (!ret)
368     {
369         return NULL;
370     }
371 
372     autocheck_type = get_autocheck_type (plugin);
373 
374     if (autocheck_type == AUTOCHECK_ALWAYS)
375     {
376         gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (widget->always), TRUE);
377     }
378     else if (autocheck_type == AUTOCHECK_DOCUMENT)
379     {
380         gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (widget->document), TRUE);
381     }
382     else
383     {
384         gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (widget->never), TRUE);
385     }
386 
387     g_signal_connect (widget->always, "toggled",
388                       G_CALLBACK (configure_widget_button_toggled), widget);
389     g_signal_connect (widget->document, "toggled",
390                       G_CALLBACK (configure_widget_button_toggled), widget);
391     g_signal_connect (widget->never, "toggled",
392                       G_CALLBACK (configure_widget_button_toggled), widget);
393     g_signal_connect (widget->content, "destroy",
394                       G_CALLBACK (configure_widget_destroyed), widget);
395 
396     return widget;
397 }
398 
399 static void
set_language_cb(GtkAction * action,XedSpellPlugin * plugin)400 set_language_cb (GtkAction      *action,
401                  XedSpellPlugin *plugin)
402 {
403     XedSpellPluginPrivate *priv;
404     XedDocument *doc;
405     GspellChecker *checker;
406     const GspellLanguage *lang;
407     GtkWidget *dialog;
408     GtkWindowGroup *window_group;
409 
410     xed_debug (DEBUG_PLUGINS);
411 
412     priv = plugin->priv;
413 
414     doc = xed_window_get_active_document (priv->window);
415     g_return_if_fail (doc != NULL);
416 
417     checker = get_spell_checker (doc);
418     g_return_if_fail (checker != NULL);
419 
420     lang = gspell_checker_get_language (checker);
421 
422     dialog = gspell_language_chooser_dialog_new (GTK_WINDOW (priv->window),
423                                                  lang,
424                                                  GTK_DIALOG_MODAL |
425                                                  GTK_DIALOG_DESTROY_WITH_PARENT);
426 
427     g_object_bind_property (dialog, "language",
428                             checker, "language",
429                             G_BINDING_DEFAULT);
430 
431     window_group = xed_window_get_group (priv->window);
432 
433     gtk_window_group_add_window (window_group, GTK_WINDOW (dialog));
434 
435     gtk_dialog_add_button (GTK_DIALOG (dialog), _("_Help"), GTK_RESPONSE_HELP);
436 
437     g_signal_connect (dialog, "response",
438                       G_CALLBACK (language_dialog_response_cb), NULL);
439 
440     gtk_widget_show (dialog);
441 }
442 
443 static void
inline_checker_cb(GtkAction * action,XedSpellPlugin * plugin)444 inline_checker_cb (GtkAction      *action,
445                    XedSpellPlugin *plugin)
446 {
447     XedSpellPluginPrivate *priv;
448     XedView *view;
449     gboolean active;
450 
451     xed_debug (DEBUG_PLUGINS);
452 
453     priv = plugin->priv;
454     active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
455 
456     xed_debug_message (DEBUG_PLUGINS, active ? "Inline Checker activated" : "Inline Checker deactivated");
457 
458     view = xed_window_get_active_view (priv->window);
459     if (view != NULL)
460     {
461         XedDocument *doc;
462         GspellTextView *gspell_view;
463 
464         doc = XED_DOCUMENT (gtk_text_view_get_buffer (GTK_TEXT_VIEW (view)));
465 
466         if (get_autocheck_type (plugin) == AUTOCHECK_DOCUMENT)
467         {
468             xed_document_set_metadata (doc,
469                                        XED_METADATA_ATTRIBUTE_SPELL_ENABLED,
470                                        active ? SPELL_ENABLED_STR : NULL,
471                                        NULL);
472         }
473 
474         gspell_view = gspell_text_view_get_from_gtk_text_view (GTK_TEXT_VIEW (view));
475         gspell_text_view_set_inline_spell_checking (gspell_view, active);
476     }
477 }
478 
479 static void
update_ui(XedSpellPlugin * plugin)480 update_ui (XedSpellPlugin *plugin)
481 {
482     XedSpellPluginPrivate *priv;
483     XedView *view;
484     GtkAction *action;
485 
486     xed_debug (DEBUG_PLUGINS);
487 
488     priv = plugin->priv;
489 
490     view = xed_window_get_active_view (priv->window);
491 
492     if (view != NULL)
493     {
494         XedTab *tab;
495 
496         tab = xed_window_get_active_tab (priv->window);
497         g_return_if_fail (xed_tab_get_view (tab) == view);
498 
499         /* If the document is loading we can't get the metadata so we
500            endup with an useless speller */
501         if (xed_tab_get_state (tab) == XED_TAB_STATE_NORMAL)
502         {
503             GspellTextView *gspell_view;
504             gboolean inline_checking_enabled;
505 
506             gspell_view = gspell_text_view_get_from_gtk_text_view (GTK_TEXT_VIEW (view));
507             inline_checking_enabled = gspell_text_view_get_inline_spell_checking (gspell_view);
508 
509             action = gtk_action_group_get_action (priv->action_group, "InlineSpellChecker");
510 
511             g_signal_handlers_block_by_func (action, inline_checker_cb, plugin);
512             gspell_text_view_set_inline_spell_checking (gspell_view, inline_checking_enabled);
513             gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), inline_checking_enabled);
514             g_signal_handlers_unblock_by_func (action, inline_checker_cb, plugin);
515         }
516     }
517 
518     gtk_action_group_set_sensitive (priv->action_group,
519                                     (view != NULL) &&
520                                     gtk_text_view_get_editable (GTK_TEXT_VIEW (view)));
521 }
522 
523 static void
setup_inline_checker_from_metadata(XedSpellPlugin * plugin,XedView * view)524 setup_inline_checker_from_metadata (XedSpellPlugin *plugin,
525                                     XedView        *view)
526 {
527     XedSpellPluginPrivate *priv;
528     XedDocument *doc;
529     gboolean enabled = FALSE;
530     gchar *enabled_str = NULL;
531     GspellTextView *gspell_view;
532     XedView *active_view;
533     XedSpellPluginAutocheckType autocheck_type;
534 
535     priv = plugin->priv;
536 
537     autocheck_type = get_autocheck_type (plugin);
538 
539     doc = XED_DOCUMENT (gtk_text_view_get_buffer (GTK_TEXT_VIEW (view)));
540 
541     switch (autocheck_type)
542     {
543         case AUTOCHECK_ALWAYS:
544         {
545             enabled = TRUE;
546             break;
547         }
548         case AUTOCHECK_DOCUMENT:
549         {
550             enabled_str = xed_document_get_metadata (doc, XED_METADATA_ATTRIBUTE_SPELL_ENABLED);
551             break;
552         }
553         case AUTOCHECK_NEVER:
554         default:
555             enabled = FALSE;
556             break;
557     }
558 
559     if (enabled_str != NULL)
560     {
561         enabled = g_str_equal (enabled_str, SPELL_ENABLED_STR);
562         g_free (enabled_str);
563     }
564 
565     gspell_view = gspell_text_view_get_from_gtk_text_view (GTK_TEXT_VIEW (view));
566     gspell_text_view_set_inline_spell_checking (gspell_view, enabled);
567 
568     /* In case that the view is the active one we mark the spell action */
569     active_view = xed_window_get_active_view (plugin->priv->window);
570 
571     if (active_view == view && priv->action_group != NULL)
572     {
573         GtkAction *action;
574 
575         action = gtk_action_group_get_action (priv->action_group, "InlineSpellChecker");
576 
577         g_signal_handlers_block_by_func (action, inline_checker_cb, plugin);
578         gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), enabled);
579         g_signal_handlers_unblock_by_func (action, inline_checker_cb, plugin);
580     }
581 }
582 
583 static void
language_notify_cb(GspellChecker * checker,GParamSpec * pspec,XedDocument * doc)584 language_notify_cb (GspellChecker *checker,
585                     GParamSpec    *pspec,
586                     XedDocument   *doc)
587 {
588     const GspellLanguage *lang;
589     const gchar *language_code;
590 
591     g_return_if_fail (XED_IS_DOCUMENT (doc));
592 
593     lang = gspell_checker_get_language (checker);
594     g_return_if_fail (lang != NULL);
595 
596     language_code = gspell_language_get_code (lang);
597     g_return_if_fail (language_code != NULL);
598 
599     xed_document_set_metadata (doc, XED_METADATA_ATTRIBUTE_SPELL_LANGUAGE, language_code, NULL);
600 }
601 
602 static void
on_document_loaded(XedDocument * doc,XedSpellPlugin * plugin)603 on_document_loaded (XedDocument    *doc,
604                     XedSpellPlugin *plugin)
605 {
606 
607     GspellChecker *checker;
608     XedTab *tab;
609     XedView *view;
610 
611     checker = get_spell_checker (doc);
612 
613     if (checker != NULL)
614     {
615         const GspellLanguage *lang;
616 
617         lang = get_language_from_metadata (doc);
618 
619         if (lang != NULL)
620         {
621             g_signal_handlers_block_by_func (checker, language_notify_cb, doc);
622             gspell_checker_set_language (checker, lang);
623             g_signal_handlers_unblock_by_func (checker, language_notify_cb, doc);
624         }
625     }
626 
627     tab = xed_tab_get_from_document (doc);
628     view = xed_tab_get_view (tab);
629     setup_inline_checker_from_metadata (plugin, view);
630 }
631 
632 static void
on_document_saved(XedDocument * doc,XedSpellPlugin * plugin)633 on_document_saved (XedDocument    *doc,
634                    XedSpellPlugin *plugin)
635 {
636     XedTab *tab;
637     XedView *view;
638     GspellChecker *checker;
639     const gchar *language_code = NULL;
640     GspellTextView *gspell_view;
641     gboolean inline_checking_enabled;
642 
643     /* Make sure to save the metadata here too */
644     checker = get_spell_checker (doc);
645 
646     if (checker != NULL)
647     {
648         const GspellLanguage *lang;
649 
650         lang = gspell_checker_get_language (checker);
651         if (lang != NULL)
652         {
653             language_code = gspell_language_get_code (lang);
654         }
655     }
656 
657     tab = xed_tab_get_from_document (doc);
658     view = xed_tab_get_view (tab);
659 
660     gspell_view = gspell_text_view_get_from_gtk_text_view (GTK_TEXT_VIEW (view));
661     inline_checking_enabled = gspell_text_view_get_inline_spell_checking (gspell_view);
662 
663     if (get_autocheck_type (plugin) == AUTOCHECK_DOCUMENT)
664     {
665 
666         xed_document_set_metadata (doc,
667                                    XED_METADATA_ATTRIBUTE_SPELL_ENABLED,
668                                    inline_checking_enabled ? SPELL_ENABLED_STR : NULL,
669                                    XED_METADATA_ATTRIBUTE_SPELL_LANGUAGE,
670                                    language_code,
671                                    NULL);
672     }
673     else
674     {
675         xed_document_set_metadata (doc, XED_METADATA_ATTRIBUTE_SPELL_LANGUAGE, language_code, NULL);
676     }
677 }
678 
679 static void
activate_spell_checking_in_view(XedSpellPlugin * plugin,XedView * view)680 activate_spell_checking_in_view (XedSpellPlugin *plugin,
681                                  XedView        *view)
682 {
683     XedDocument *doc;
684 
685     doc = XED_DOCUMENT (gtk_text_view_get_buffer (GTK_TEXT_VIEW (view)));
686 
687     /* It is possible that a GspellChecker has already been set, for example
688      * if a XedTab has moved to another window.
689      */
690     if (get_spell_checker (doc) == NULL)
691     {
692         const GspellLanguage *lang;
693         GspellChecker *checker;
694         GspellTextBuffer *gspell_buffer;
695 
696         lang = get_language_from_metadata (doc);
697         checker = gspell_checker_new (lang);
698 
699         g_signal_connect_object (checker, "notify::language",
700                                  G_CALLBACK (language_notify_cb), doc, 0);
701 
702         gspell_buffer = gspell_text_buffer_get_from_gtk_text_buffer (GTK_TEXT_BUFFER (doc));
703         gspell_text_buffer_set_spell_checker (gspell_buffer, checker);
704         g_object_unref (checker);
705 
706         setup_inline_checker_from_metadata (plugin, view);
707     }
708 
709     g_signal_connect_object (doc, "loaded",
710                              G_CALLBACK (on_document_loaded), plugin, 0);
711     g_signal_connect_object (doc, "saved",
712                              G_CALLBACK (on_document_saved), plugin, 0);
713 }
714 
715 static void
disconnect_view(XedSpellPlugin * plugin,XedView * view)716 disconnect_view (XedSpellPlugin *plugin,
717                  XedView        *view)
718 {
719     GtkTextBuffer *buffer;
720 
721     buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (view));
722 
723     /* It should still be the same buffer as the one where the signal
724      * handlers were connected. If not, we assume that the old buffer is
725      * finalized. And it is anyway safe to call
726      * g_signal_handlers_disconnect_by_func() if no signal handlers are
727      * found.
728      */
729     g_signal_handlers_disconnect_by_func (buffer, on_document_loaded, plugin);
730     g_signal_handlers_disconnect_by_func (buffer, on_document_saved, plugin);
731 }
732 
733 static void
deactivate_spell_checking_in_view(XedSpellPlugin * plugin,XedView * view)734 deactivate_spell_checking_in_view (XedSpellPlugin *plugin,
735                                    XedView        *view)
736 {
737     GtkTextBuffer *gtk_buffer;
738     GspellTextBuffer *gspell_buffer;
739     GspellTextView *gspell_view;
740 
741     disconnect_view (plugin, view);
742 
743     gtk_buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (view));
744     gspell_buffer = gspell_text_buffer_get_from_gtk_text_buffer (gtk_buffer);
745     gspell_text_buffer_set_spell_checker (gspell_buffer, NULL);
746 
747     gspell_view = gspell_text_view_get_from_gtk_text_view (GTK_TEXT_VIEW (view));
748     gspell_text_view_set_inline_spell_checking (gspell_view, FALSE);
749 }
750 
751 static void
tab_added_cb(XedWindow * window,XedTab * tab,XedSpellPlugin * plugin)752 tab_added_cb (XedWindow      *window,
753               XedTab         *tab,
754               XedSpellPlugin *plugin)
755 {
756     activate_spell_checking_in_view (plugin, xed_tab_get_view (tab));
757 }
758 
759 static void
tab_removed_cb(XedWindow * window,XedTab * tab,XedSpellPlugin * plugin)760 tab_removed_cb (XedWindow      *window,
761                 XedTab         *tab,
762                 XedSpellPlugin *plugin)
763 {
764     /* Don't deactivate completely the spell checking in @tab, since the tab
765      * can be moved to another window and we don't want to loose the spell
766      * checking settings (they are not saved in metadata for unsaved
767      * documents).
768      */
769     disconnect_view (plugin, xed_tab_get_view (tab));
770 }
771 
772 static void
xed_spell_plugin_activate(XedWindowActivatable * activatable)773 xed_spell_plugin_activate (XedWindowActivatable *activatable)
774 {
775     XedSpellPlugin *plugin;
776     XedSpellPluginPrivate *priv;
777     GtkUIManager *manager;
778     GList *views;
779     GList *l;
780 
781     xed_debug (DEBUG_PLUGINS);
782 
783     plugin = XED_SPELL_PLUGIN (activatable);
784     priv = plugin->priv;
785 
786     manager = xed_window_get_ui_manager (priv->window);
787 
788     priv->action_group = gtk_action_group_new ("XedSpellPluginActions");
789     gtk_action_group_set_translation_domain (priv->action_group, GETTEXT_PACKAGE);
790     gtk_action_group_add_actions (priv->action_group,
791                                   action_entries,
792                                   G_N_ELEMENTS (action_entries),
793                                   activatable);
794     gtk_action_group_add_toggle_actions (priv->action_group,
795                                          toggle_action_entries,
796                                          G_N_ELEMENTS (toggle_action_entries),
797                                          activatable);
798 
799     gtk_ui_manager_insert_action_group (manager, priv->action_group, -1);
800 
801     priv->ui_id = gtk_ui_manager_new_merge_id (manager);
802 
803     gtk_ui_manager_add_ui (manager,
804                            priv->ui_id,
805                            MENU_PATH,
806                            "CheckSpell",
807                            "CheckSpell",
808                            GTK_UI_MANAGER_MENUITEM,
809                            FALSE);
810 
811     gtk_ui_manager_add_ui (manager,
812                            priv->ui_id,
813                            MENU_PATH,
814                            "InlineSpellChecker",
815                            "InlineSpellChecker",
816                            GTK_UI_MANAGER_MENUITEM,
817                            FALSE);
818 
819     gtk_ui_manager_add_ui (manager,
820                            priv->ui_id,
821                            MENU_PATH,
822                            "ConfigSpell",
823                            "ConfigSpell",
824                            GTK_UI_MANAGER_MENUITEM,
825                            FALSE);
826 
827     update_ui (XED_SPELL_PLUGIN (activatable));
828 
829     views = xed_window_get_views (priv->window);
830     for (l = views; l != NULL; l = l->next)
831     {
832         activate_spell_checking_in_view (plugin, XED_VIEW (l->data));
833     }
834 
835     g_signal_connect (priv->window, "tab-added",
836                       G_CALLBACK (tab_added_cb), activatable);
837     g_signal_connect (priv->window, "tab-removed",
838                       G_CALLBACK (tab_removed_cb), activatable);
839 }
840 
841 static void
xed_spell_plugin_deactivate(XedWindowActivatable * activatable)842 xed_spell_plugin_deactivate (XedWindowActivatable *activatable)
843 {
844     XedSpellPlugin *plugin;
845     XedSpellPluginPrivate *priv;
846     GtkUIManager *manager;
847     GList *views;
848     GList *l;
849 
850     xed_debug (DEBUG_PLUGINS);
851 
852     plugin = XED_SPELL_PLUGIN (activatable);
853     priv = plugin->priv;
854     manager = xed_window_get_ui_manager (priv->window);
855 
856     gtk_ui_manager_remove_ui (manager, priv->ui_id);
857     gtk_ui_manager_remove_action_group (manager, priv->action_group);
858 
859     g_signal_handlers_disconnect_by_func (priv->window, tab_added_cb, activatable);
860     g_signal_handlers_disconnect_by_func (priv->window, tab_removed_cb, activatable);
861 
862     views = xed_window_get_views (priv->window);
863     for (l = views; l != NULL; l = l->next)
864     {
865         deactivate_spell_checking_in_view (plugin, XED_VIEW (l->data));
866     }
867 }
868 
869 static void
xed_spell_plugin_update_state(XedWindowActivatable * activatable)870 xed_spell_plugin_update_state (XedWindowActivatable *activatable)
871 {
872     xed_debug (DEBUG_PLUGINS);
873 
874     update_ui (XED_SPELL_PLUGIN (activatable));
875 }
876 
877 static GtkWidget *
xed_spell_plugin_create_configure_widget(PeasGtkConfigurable * configurable)878 xed_spell_plugin_create_configure_widget (PeasGtkConfigurable *configurable)
879 {
880     SpellConfigureWidget *widget;
881 
882     widget = get_configure_widget (XED_SPELL_PLUGIN (configurable));
883 
884     return widget->content;
885 }
886 
887 static void
xed_window_activatable_iface_init(XedWindowActivatableInterface * iface)888 xed_window_activatable_iface_init (XedWindowActivatableInterface *iface)
889 {
890     iface->activate = xed_spell_plugin_activate;
891     iface->deactivate = xed_spell_plugin_deactivate;
892     iface->update_state = xed_spell_plugin_update_state;
893 }
894 
895 static void
peas_gtk_configurable_iface_init(PeasGtkConfigurableInterface * iface)896 peas_gtk_configurable_iface_init (PeasGtkConfigurableInterface *iface)
897 {
898     iface->create_configure_widget = xed_spell_plugin_create_configure_widget;
899 }
900 
901 G_MODULE_EXPORT void
peas_register_types(PeasObjectModule * module)902 peas_register_types (PeasObjectModule *module)
903 {
904     xed_spell_plugin_register_type (G_TYPE_MODULE (module));
905 
906     peas_object_module_register_extension_type (module,
907                                                 XED_TYPE_WINDOW_ACTIVATABLE,
908                                                 XED_TYPE_SPELL_PLUGIN);
909 
910     peas_object_module_register_extension_type (module,
911                                                 PEAS_GTK_TYPE_CONFIGURABLE,
912                                                 XED_TYPE_SPELL_PLUGIN);
913 }
914