1 /*
2  * gedit-spell-plugin.c
3  *
4  * Copyright (C) 2002-2005 Paolo Maggi
5  * Copyright (C) 2015-2016 Sébastien Wilmet
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2, or (at your option)
10  * any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, see <http://www.gnu.org/licenses/>.
19  */
20 
21 #include "gedit-spell-plugin.h"
22 
23 #include <glib/gi18n.h>
24 #include <gedit/gedit-debug.h>
25 #include <gedit/gedit-app.h>
26 #include <gedit/gedit-window.h>
27 #include <gedit/gedit-window-activatable.h>
28 #include <gspell/gspell.h>
29 #include <libpeas-gtk/peas-gtk-configurable.h>
30 
31 #include "gedit-spell-app-activatable.h"
32 
33 #define GEDIT_METADATA_ATTRIBUTE_SPELL_LANGUAGE "gedit-spell-language"
34 #define GEDIT_METADATA_ATTRIBUTE_SPELL_ENABLED  "gedit-spell-enabled"
35 
36 #define SPELL_ENABLED_STR "1"
37 #define SPELL_BASE_SETTINGS	"org.gnome.gedit.plugins.spell"
38 #define SETTINGS_KEY_HIGHLIGHT_MISSPELLED "highlight-misspelled"
39 
40 static void gedit_window_activatable_iface_init (GeditWindowActivatableInterface *iface);
41 static void peas_gtk_configurable_iface_init (PeasGtkConfigurableInterface *iface);
42 
43 struct _GeditSpellPluginPrivate
44 {
45 	GeditWindow *window;
46 	GSettings   *settings;
47 };
48 
49 enum
50 {
51 	PROP_0,
52 	PROP_WINDOW
53 };
54 
55 typedef struct _SpellConfigureWidget SpellConfigureWidget;
56 
57 struct _SpellConfigureWidget
58 {
59 	GtkWidget *content;
60 	GtkWidget *highlight_button;
61 
62 	GSettings *settings;
63 };
64 
65 G_DEFINE_DYNAMIC_TYPE_EXTENDED (GeditSpellPlugin,
66 				gedit_spell_plugin,
67 				PEAS_TYPE_EXTENSION_BASE,
68 				0,
69 				G_IMPLEMENT_INTERFACE_DYNAMIC (GEDIT_TYPE_WINDOW_ACTIVATABLE,
70 							       gedit_window_activatable_iface_init)
71 				G_IMPLEMENT_INTERFACE_DYNAMIC (PEAS_GTK_TYPE_CONFIGURABLE,
72 							       peas_gtk_configurable_iface_init)
73 				G_ADD_PRIVATE_DYNAMIC (GeditSpellPlugin))
74 
75 static void
gedit_spell_plugin_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)76 gedit_spell_plugin_set_property (GObject      *object,
77 				 guint         prop_id,
78 				 const GValue *value,
79 				 GParamSpec   *pspec)
80 {
81 	GeditSpellPlugin *plugin = GEDIT_SPELL_PLUGIN (object);
82 
83 	switch (prop_id)
84 	{
85 		case PROP_WINDOW:
86 			plugin->priv->window = GEDIT_WINDOW (g_value_dup_object (value));
87 			break;
88 
89 		default:
90 			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
91 			break;
92 	}
93 }
94 
95 static void
gedit_spell_plugin_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)96 gedit_spell_plugin_get_property (GObject    *object,
97 				 guint       prop_id,
98 				 GValue     *value,
99 				 GParamSpec *pspec)
100 {
101 	GeditSpellPlugin *plugin = GEDIT_SPELL_PLUGIN (object);
102 
103 	switch (prop_id)
104 	{
105 		case PROP_WINDOW:
106 			g_value_set_object (value, plugin->priv->window);
107 			break;
108 
109 		default:
110 			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
111 			break;
112 	}
113 }
114 
115 static void
gedit_spell_plugin_dispose(GObject * object)116 gedit_spell_plugin_dispose (GObject *object)
117 {
118 	GeditSpellPlugin *plugin = GEDIT_SPELL_PLUGIN (object);
119 
120 	gedit_debug_message (DEBUG_PLUGINS, "GeditSpellPlugin disposing");
121 
122 	g_clear_object (&plugin->priv->window);
123 	g_clear_object (&plugin->priv->settings);
124 
125 	G_OBJECT_CLASS (gedit_spell_plugin_parent_class)->dispose (object);
126 }
127 
128 static void
gedit_spell_plugin_class_init(GeditSpellPluginClass * klass)129 gedit_spell_plugin_class_init (GeditSpellPluginClass *klass)
130 {
131 	GObjectClass *object_class = G_OBJECT_CLASS (klass);
132 
133 	object_class->set_property = gedit_spell_plugin_set_property;
134 	object_class->get_property = gedit_spell_plugin_get_property;
135 	object_class->dispose = gedit_spell_plugin_dispose;
136 
137 	g_object_class_override_property (object_class, PROP_WINDOW, "window");
138 }
139 
140 static void
gedit_spell_plugin_class_finalize(GeditSpellPluginClass * klass)141 gedit_spell_plugin_class_finalize (GeditSpellPluginClass *klass)
142 {
143 }
144 
145 static void
gedit_spell_plugin_init(GeditSpellPlugin * plugin)146 gedit_spell_plugin_init (GeditSpellPlugin *plugin)
147 {
148 	gedit_debug_message (DEBUG_PLUGINS, "GeditSpellPlugin initializing");
149 
150 	plugin->priv = gedit_spell_plugin_get_instance_private (plugin);
151 	plugin->priv->settings = g_settings_new (SPELL_BASE_SETTINGS);
152 }
153 
154 static GspellChecker *
get_spell_checker(GeditDocument * doc)155 get_spell_checker (GeditDocument *doc)
156 {
157 	GspellTextBuffer *gspell_buffer;
158 
159 	gspell_buffer = gspell_text_buffer_get_from_gtk_text_buffer (GTK_TEXT_BUFFER (doc));
160 	return gspell_text_buffer_get_spell_checker (gspell_buffer);
161 }
162 
163 static const GspellLanguage *
get_language_from_metadata(GeditDocument * doc)164 get_language_from_metadata (GeditDocument *doc)
165 {
166 	const GspellLanguage *lang = NULL;
167 	gchar *language_code = NULL;
168 
169 	language_code = gedit_document_get_metadata (doc, GEDIT_METADATA_ATTRIBUTE_SPELL_LANGUAGE);
170 
171 	if (language_code != NULL)
172 	{
173 		lang = gspell_language_lookup (language_code);
174 		g_free (language_code);
175 	}
176 
177 	return lang;
178 }
179 
180 static void
check_spell_cb(GSimpleAction * action,GVariant * parameter,gpointer data)181 check_spell_cb (GSimpleAction *action,
182 		GVariant      *parameter,
183 		gpointer       data)
184 {
185 	GeditSpellPlugin *plugin = GEDIT_SPELL_PLUGIN (data);
186 	GeditSpellPluginPrivate *priv;
187 	GeditView *view;
188 	GspellNavigator *navigator;
189 	GtkWidget *dialog;
190 
191 	gedit_debug (DEBUG_PLUGINS);
192 
193 	priv = plugin->priv;
194 
195 	view = gedit_window_get_active_view (priv->window);
196 	g_return_if_fail (view != NULL);
197 
198 	navigator = gspell_navigator_text_view_new (GTK_TEXT_VIEW (view));
199 	dialog = gspell_checker_dialog_new (GTK_WINDOW (priv->window), navigator);
200 
201 	gtk_widget_show (dialog);
202 }
203 
204 static void
language_dialog_response_cb(GtkDialog * dialog,gint response_id,gpointer user_data)205 language_dialog_response_cb (GtkDialog *dialog,
206 			     gint       response_id,
207 			     gpointer   user_data)
208 {
209 	if (response_id == GTK_RESPONSE_HELP)
210 	{
211 		gedit_app_show_help (GEDIT_APP (g_application_get_default ()),
212 				     GTK_WINDOW (dialog),
213 				     NULL,
214 				     "gedit-spellcheck");
215 		return;
216 	}
217 
218 	gtk_widget_destroy (GTK_WIDGET (dialog));
219 }
220 
221 static void
set_language_cb(GSimpleAction * action,GVariant * parameter,gpointer data)222 set_language_cb (GSimpleAction *action,
223 		 GVariant      *parameter,
224 		 gpointer       data)
225 {
226 	GeditSpellPlugin *plugin = GEDIT_SPELL_PLUGIN (data);
227 	GeditSpellPluginPrivate *priv;
228 	GeditDocument *doc;
229 	GspellChecker *checker;
230 	const GspellLanguage *lang;
231 	GtkWidget *dialog;
232 	GtkWindowGroup *window_group;
233 
234 	gedit_debug (DEBUG_PLUGINS);
235 
236 	priv = plugin->priv;
237 
238 	doc = gedit_window_get_active_document (priv->window);
239 	g_return_if_fail (doc != NULL);
240 
241 	checker = get_spell_checker (doc);
242 	g_return_if_fail (checker != NULL);
243 
244 	lang = gspell_checker_get_language (checker);
245 
246 	dialog = gspell_language_chooser_dialog_new (GTK_WINDOW (priv->window),
247 						     lang,
248 						     GTK_DIALOG_MODAL |
249 						     GTK_DIALOG_DESTROY_WITH_PARENT);
250 
251 	g_object_bind_property (dialog, "language",
252 				checker, "language",
253 				G_BINDING_DEFAULT);
254 
255 	window_group = gedit_window_get_group (priv->window);
256 
257 	gtk_window_group_add_window (window_group, GTK_WINDOW (dialog));
258 
259 	gtk_dialog_add_button (GTK_DIALOG (dialog),
260 			       _("_Help"),
261 			       GTK_RESPONSE_HELP);
262 
263 	g_signal_connect (dialog,
264 			  "response",
265 			  G_CALLBACK (language_dialog_response_cb),
266 			  NULL);
267 
268 	gtk_widget_show (dialog);
269 }
270 
271 static void
inline_checker_activate_cb(GSimpleAction * action,GVariant * parameter,gpointer data)272 inline_checker_activate_cb (GSimpleAction *action,
273 			    GVariant      *parameter,
274 			    gpointer       data)
275 {
276 	GeditSpellPlugin *plugin = GEDIT_SPELL_PLUGIN (data);
277 	GeditSpellPluginPrivate *priv = plugin->priv;
278 	GVariant *state;
279 	gboolean active;
280 	GeditView *view;
281 
282 	gedit_debug (DEBUG_PLUGINS);
283 
284 	state = g_action_get_state (G_ACTION (action));
285 	g_return_if_fail (state != NULL);
286 
287 	active = g_variant_get_boolean (state);
288 	g_variant_unref (state);
289 
290 	/* We must toggle ourself the value. */
291 	active = !active;
292 	g_action_change_state (G_ACTION (action), g_variant_new_boolean (active));
293 
294 	view = gedit_window_get_active_view (priv->window);
295 	if (view != NULL)
296 	{
297 		GeditDocument *doc;
298 
299 		doc = GEDIT_DOCUMENT (gtk_text_view_get_buffer (GTK_TEXT_VIEW (view)));
300 
301 		/* Set metadata in the "activate" handler, not in "change-state"
302 		 * because "change-state" is called every time the state
303 		 * changes, not specifically when the user has changed the state
304 		 * herself. For example "change-state" is called to initialize
305 		 * the sate to the default value specified in the GActionEntry.
306 		 */
307 		gedit_document_set_metadata (doc,
308 					     GEDIT_METADATA_ATTRIBUTE_SPELL_ENABLED,
309 					     active ? SPELL_ENABLED_STR : NULL,
310 					     NULL);
311 	}
312 }
313 
314 static void
inline_checker_change_state_cb(GSimpleAction * action,GVariant * state,gpointer data)315 inline_checker_change_state_cb (GSimpleAction *action,
316 				GVariant      *state,
317 				gpointer       data)
318 {
319 	GeditSpellPlugin *plugin = GEDIT_SPELL_PLUGIN (data);
320 	GeditSpellPluginPrivate *priv = plugin->priv;
321 	GeditView *view;
322 	gboolean active;
323 
324 	gedit_debug (DEBUG_PLUGINS);
325 
326 	active = g_variant_get_boolean (state);
327 
328 	gedit_debug_message (DEBUG_PLUGINS, active ? "Inline Checker activated" : "Inline Checker deactivated");
329 
330 	view = gedit_window_get_active_view (priv->window);
331 	if (view != NULL)
332 	{
333 		GspellTextView *gspell_view;
334 
335 		gspell_view = gspell_text_view_get_from_gtk_text_view (GTK_TEXT_VIEW (view));
336 		gspell_text_view_set_inline_spell_checking (gspell_view, active);
337 
338 		g_simple_action_set_state (action, g_variant_new_boolean (active));
339 	}
340 }
341 
342 static void
update_ui(GeditSpellPlugin * plugin)343 update_ui (GeditSpellPlugin *plugin)
344 {
345 	GeditSpellPluginPrivate *priv;
346 	GeditTab *tab;
347 	GeditView *view = NULL;
348 	gboolean editable_view;
349 	GAction *check_spell_action;
350 	GAction *config_spell_action;
351 	GAction *inline_checker_action;
352 
353 	gedit_debug (DEBUG_PLUGINS);
354 
355 	priv = plugin->priv;
356 
357 	tab = gedit_window_get_active_tab (priv->window);
358 	if (tab != NULL)
359 	{
360 		view = gedit_tab_get_view (tab);
361 	}
362 
363 	editable_view = (view != NULL) && gtk_text_view_get_editable (GTK_TEXT_VIEW (view));
364 
365 	check_spell_action = g_action_map_lookup_action (G_ACTION_MAP (priv->window),
366 	                                                 "check-spell");
367 	g_simple_action_set_enabled (G_SIMPLE_ACTION (check_spell_action),
368 				     editable_view);
369 
370 	config_spell_action = g_action_map_lookup_action (G_ACTION_MAP (priv->window),
371 	                                                  "config-spell");
372 	g_simple_action_set_enabled (G_SIMPLE_ACTION (config_spell_action),
373 				     editable_view);
374 
375 	inline_checker_action = g_action_map_lookup_action (G_ACTION_MAP (priv->window),
376 							    "inline-spell-checker");
377 	g_simple_action_set_enabled (G_SIMPLE_ACTION (inline_checker_action),
378 				     editable_view);
379 
380 	/* Update only on normal state to avoid garbage changes during e.g. file
381 	 * loading.
382 	 */
383 	if (tab != NULL &&
384 	    gedit_tab_get_state (tab) == GEDIT_TAB_STATE_NORMAL)
385 	{
386 		GspellTextView *gspell_view;
387 		gboolean inline_checking_enabled;
388 
389 		gspell_view = gspell_text_view_get_from_gtk_text_view (GTK_TEXT_VIEW (view));
390 		inline_checking_enabled = gspell_text_view_get_inline_spell_checking (gspell_view);
391 
392 		g_action_change_state (inline_checker_action,
393 				       g_variant_new_boolean (inline_checking_enabled));
394 	}
395 }
396 
397 static void
setup_inline_checker_from_metadata(GeditSpellPlugin * plugin,GeditView * view)398 setup_inline_checker_from_metadata (GeditSpellPlugin *plugin,
399 				    GeditView        *view)
400 {
401 	GeditDocument *doc;
402 	gboolean enabled;
403 	gchar *enabled_str;
404 	GspellTextView *gspell_view;
405 	GeditView *active_view;
406 
407 	doc = GEDIT_DOCUMENT (gtk_text_view_get_buffer (GTK_TEXT_VIEW (view)));
408 
409 	enabled = g_settings_get_boolean(plugin->priv->settings, SETTINGS_KEY_HIGHLIGHT_MISSPELLED);
410 	enabled_str = gedit_document_get_metadata (doc, GEDIT_METADATA_ATTRIBUTE_SPELL_ENABLED);
411 	if (enabled_str != NULL)
412 	{
413 		enabled = g_str_equal (enabled_str, SPELL_ENABLED_STR);
414 		g_free (enabled_str);
415 	}
416 
417 	gspell_view = gspell_text_view_get_from_gtk_text_view (GTK_TEXT_VIEW (view));
418 	gspell_text_view_set_inline_spell_checking (gspell_view, enabled);
419 
420 	/* In case that the view is the active one we mark the spell action */
421 	active_view = gedit_window_get_active_view (plugin->priv->window);
422 
423 	if (active_view == view)
424 	{
425 		GAction *action;
426 
427 		action = g_action_map_lookup_action (G_ACTION_MAP (plugin->priv->window),
428 		                                     "inline-spell-checker");
429 		g_action_change_state (action, g_variant_new_boolean (enabled));
430 	}
431 }
432 
433 static void
language_notify_cb(GspellChecker * checker,GParamSpec * pspec,GeditDocument * doc)434 language_notify_cb (GspellChecker *checker,
435 		    GParamSpec    *pspec,
436 		    GeditDocument *doc)
437 {
438 	const GspellLanguage *lang;
439 	const gchar *language_code;
440 
441 	g_return_if_fail (GEDIT_IS_DOCUMENT (doc));
442 
443 	lang = gspell_checker_get_language (checker);
444 	g_return_if_fail (lang != NULL);
445 
446 	language_code = gspell_language_get_code (lang);
447 	g_return_if_fail (language_code != NULL);
448 
449 	gedit_document_set_metadata (doc,
450 				     GEDIT_METADATA_ATTRIBUTE_SPELL_LANGUAGE, language_code,
451 				     NULL);
452 }
453 
454 static void
on_document_loaded(GeditDocument * doc,GeditSpellPlugin * plugin)455 on_document_loaded (GeditDocument    *doc,
456 		    GeditSpellPlugin *plugin)
457 {
458 	GspellChecker *checker;
459 	GeditTab *tab;
460 	GeditView *view;
461 
462 	checker = get_spell_checker (doc);
463 
464 	if (checker != NULL)
465 	{
466 		const GspellLanguage *lang;
467 
468 		lang = get_language_from_metadata (doc);
469 
470 		if (lang != NULL)
471 		{
472 			g_signal_handlers_block_by_func (checker, language_notify_cb, doc);
473 			gspell_checker_set_language (checker, lang);
474 			g_signal_handlers_unblock_by_func (checker, language_notify_cb, doc);
475 		}
476 	}
477 
478 	tab = gedit_tab_get_from_document (doc);
479 	view = gedit_tab_get_view (tab);
480 	setup_inline_checker_from_metadata (plugin, view);
481 }
482 
483 static void
on_document_saved(GeditDocument * doc,gpointer user_data)484 on_document_saved (GeditDocument *doc,
485 		   gpointer       user_data)
486 {
487 	GeditTab *tab;
488 	GeditView *view;
489 	GspellChecker *checker;
490 	const gchar *language_code = NULL;
491 	GspellTextView *gspell_view;
492 	gboolean inline_checking_enabled;
493 
494 	/* Make sure to save the metadata here too */
495 
496 	checker = get_spell_checker (doc);
497 
498 	if (checker != NULL)
499 	{
500 		const GspellLanguage *lang;
501 
502 		lang = gspell_checker_get_language (checker);
503 		if (lang != NULL)
504 		{
505 			language_code = gspell_language_get_code (lang);
506 		}
507 	}
508 
509 	tab = gedit_tab_get_from_document (doc);
510 	view = gedit_tab_get_view (tab);
511 
512 	gspell_view = gspell_text_view_get_from_gtk_text_view (GTK_TEXT_VIEW (view));
513 	inline_checking_enabled = gspell_text_view_get_inline_spell_checking (gspell_view);
514 
515 	gedit_document_set_metadata (doc,
516 	                             GEDIT_METADATA_ATTRIBUTE_SPELL_ENABLED,
517 				     inline_checking_enabled ? SPELL_ENABLED_STR : NULL,
518 	                             GEDIT_METADATA_ATTRIBUTE_SPELL_LANGUAGE,
519 	                             language_code,
520 	                             NULL);
521 }
522 
523 static void
activate_spell_checking_in_view(GeditSpellPlugin * plugin,GeditView * view)524 activate_spell_checking_in_view (GeditSpellPlugin *plugin,
525 				 GeditView        *view)
526 {
527 	GeditDocument *doc;
528 
529 	doc = GEDIT_DOCUMENT (gtk_text_view_get_buffer (GTK_TEXT_VIEW (view)));
530 
531 	/* It is possible that a GspellChecker has already been set, for example
532 	 * if a GeditTab has moved to another window.
533 	 */
534 	if (get_spell_checker (doc) == NULL)
535 	{
536 		const GspellLanguage *lang;
537 		GspellChecker *checker;
538 		GspellTextBuffer *gspell_buffer;
539 
540 		lang = get_language_from_metadata (doc);
541 		checker = gspell_checker_new (lang);
542 
543 		g_signal_connect_object (checker,
544 					 "notify::language",
545 					 G_CALLBACK (language_notify_cb),
546 					 doc,
547 					 0);
548 
549 		gspell_buffer = gspell_text_buffer_get_from_gtk_text_buffer (GTK_TEXT_BUFFER (doc));
550 		gspell_text_buffer_set_spell_checker (gspell_buffer, checker);
551 		g_object_unref (checker);
552 
553 		setup_inline_checker_from_metadata (plugin, view);
554 	}
555 
556 	g_signal_connect_object (doc,
557 				 "loaded",
558 				 G_CALLBACK (on_document_loaded),
559 				 plugin,
560 				 0);
561 
562 	g_signal_connect_object (doc,
563 				 "saved",
564 				 G_CALLBACK (on_document_saved),
565 				 plugin,
566 				 0);
567 }
568 
569 static void
disconnect_view(GeditSpellPlugin * plugin,GeditView * view)570 disconnect_view (GeditSpellPlugin *plugin,
571 		 GeditView        *view)
572 {
573 	GtkTextBuffer *buffer;
574 
575 	buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (view));
576 
577 	/* It should still be the same buffer as the one where the signal
578 	 * handlers were connected. If not, we assume that the old buffer is
579 	 * finalized. And it is anyway safe to call
580 	 * g_signal_handlers_disconnect_by_func() if no signal handlers are
581 	 * found.
582 	 */
583 	g_signal_handlers_disconnect_by_func (buffer, on_document_loaded, plugin);
584 	g_signal_handlers_disconnect_by_func (buffer, on_document_saved, plugin);
585 }
586 
587 static void
deactivate_spell_checking_in_view(GeditSpellPlugin * plugin,GeditView * view)588 deactivate_spell_checking_in_view (GeditSpellPlugin *plugin,
589 				   GeditView        *view)
590 {
591 	GtkTextBuffer *gtk_buffer;
592 	GspellTextBuffer *gspell_buffer;
593 	GspellTextView *gspell_view;
594 
595 	disconnect_view (plugin, view);
596 
597 	gtk_buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (view));
598 	gspell_buffer = gspell_text_buffer_get_from_gtk_text_buffer (gtk_buffer);
599 	gspell_text_buffer_set_spell_checker (gspell_buffer, NULL);
600 
601 	gspell_view = gspell_text_view_get_from_gtk_text_view (GTK_TEXT_VIEW (view));
602 	gspell_text_view_set_inline_spell_checking (gspell_view, FALSE);
603 }
604 
605 static void
tab_added_cb(GeditWindow * window,GeditTab * tab,GeditSpellPlugin * plugin)606 tab_added_cb (GeditWindow      *window,
607 	      GeditTab         *tab,
608 	      GeditSpellPlugin *plugin)
609 {
610 	activate_spell_checking_in_view (plugin, gedit_tab_get_view (tab));
611 }
612 
613 static void
tab_removed_cb(GeditWindow * window,GeditTab * tab,GeditSpellPlugin * plugin)614 tab_removed_cb (GeditWindow      *window,
615 		GeditTab         *tab,
616 		GeditSpellPlugin *plugin)
617 {
618 	/* Don't deactivate completely the spell checking in @tab, since the tab
619 	 * can be moved to another window and we don't want to loose the spell
620 	 * checking settings (they are not saved in metadata for unsaved
621 	 * documents).
622 	 */
623 	disconnect_view (plugin, gedit_tab_get_view (tab));
624 }
625 
626 static void
gedit_spell_plugin_activate(GeditWindowActivatable * activatable)627 gedit_spell_plugin_activate (GeditWindowActivatable *activatable)
628 {
629 	GeditSpellPlugin *plugin;
630 	GeditSpellPluginPrivate *priv;
631 	GList *views;
632 	GList *l;
633 
634 	const GActionEntry action_entries[] =
635 	{
636 		{ "check-spell", check_spell_cb },
637 		{ "config-spell", set_language_cb },
638 		{ "inline-spell-checker",
639 		  inline_checker_activate_cb,
640 		  NULL,
641 		  "false",
642 		  inline_checker_change_state_cb }
643 	};
644 
645 	gedit_debug (DEBUG_PLUGINS);
646 
647 	plugin = GEDIT_SPELL_PLUGIN (activatable);
648 	priv = plugin->priv;
649 
650 	g_action_map_add_action_entries (G_ACTION_MAP (priv->window),
651 	                                 action_entries,
652 	                                 G_N_ELEMENTS (action_entries),
653 	                                 activatable);
654 
655 	update_ui (plugin);
656 
657 	views = gedit_window_get_views (priv->window);
658 	for (l = views; l != NULL; l = l->next)
659 	{
660 		activate_spell_checking_in_view (plugin, GEDIT_VIEW (l->data));
661 	}
662 
663 	g_signal_connect (priv->window,
664 			  "tab-added",
665 			  G_CALLBACK (tab_added_cb),
666 			  activatable);
667 
668 	g_signal_connect (priv->window,
669 			  "tab-removed",
670 			  G_CALLBACK (tab_removed_cb),
671 			  activatable);
672 }
673 
674 static void
gedit_spell_plugin_deactivate(GeditWindowActivatable * activatable)675 gedit_spell_plugin_deactivate (GeditWindowActivatable *activatable)
676 {
677 	GeditSpellPlugin *plugin;
678 	GeditSpellPluginPrivate *priv;
679 	GList *views;
680 	GList *l;
681 
682 	gedit_debug (DEBUG_PLUGINS);
683 
684 	plugin = GEDIT_SPELL_PLUGIN (activatable);
685 	priv = plugin->priv;
686 
687 	g_action_map_remove_action (G_ACTION_MAP (priv->window), "check-spell");
688 	g_action_map_remove_action (G_ACTION_MAP (priv->window), "config-spell");
689 	g_action_map_remove_action (G_ACTION_MAP (priv->window), "inline-spell-checker");
690 
691 	g_signal_handlers_disconnect_by_func (priv->window, tab_added_cb, activatable);
692 	g_signal_handlers_disconnect_by_func (priv->window, tab_removed_cb, activatable);
693 
694 	views = gedit_window_get_views (priv->window);
695 	for (l = views; l != NULL; l = l->next)
696 	{
697 		deactivate_spell_checking_in_view (plugin, GEDIT_VIEW (l->data));
698 	}
699 }
700 
701 static void
gedit_spell_plugin_update_state(GeditWindowActivatable * activatable)702 gedit_spell_plugin_update_state (GeditWindowActivatable *activatable)
703 {
704 	gedit_debug (DEBUG_PLUGINS);
705 
706 	update_ui (GEDIT_SPELL_PLUGIN (activatable));
707 }
708 
709 static void
gedit_window_activatable_iface_init(GeditWindowActivatableInterface * iface)710 gedit_window_activatable_iface_init (GeditWindowActivatableInterface *iface)
711 {
712 	iface->activate = gedit_spell_plugin_activate;
713 	iface->deactivate = gedit_spell_plugin_deactivate;
714 	iface->update_state = gedit_spell_plugin_update_state;
715 }
716 
717 G_MODULE_EXPORT void
peas_register_types(PeasObjectModule * module)718 peas_register_types (PeasObjectModule *module)
719 {
720 	gedit_spell_plugin_register_type (G_TYPE_MODULE (module));
721 	gedit_spell_app_activatable_register (G_TYPE_MODULE (module));
722 
723 	peas_object_module_register_extension_type (module,
724 						    GEDIT_TYPE_WINDOW_ACTIVATABLE,
725 						    GEDIT_TYPE_SPELL_PLUGIN);
726 	peas_object_module_register_extension_type (module,
727 						    PEAS_GTK_TYPE_CONFIGURABLE,
728 						    GEDIT_TYPE_SPELL_PLUGIN);
729 }
730 
731 static void
highlight_button_toggled(GtkToggleButton * button,SpellConfigureWidget * widget)732 highlight_button_toggled (GtkToggleButton     *button,
733 				 SpellConfigureWidget *widget)
734 {
735 	gboolean status = gtk_toggle_button_get_active (button);
736 	g_settings_set_boolean(widget->settings, SETTINGS_KEY_HIGHLIGHT_MISSPELLED, status);
737 }
738 
739 static void
configure_widget_destroyed(GtkWidget * widget,gpointer data)740 configure_widget_destroyed (GtkWidget *widget,
741 			    gpointer   data)
742 {
743 	SpellConfigureWidget *conf_widget = (SpellConfigureWidget *)data;
744 
745 	gedit_debug (DEBUG_PLUGINS);
746 
747 	g_object_unref (conf_widget->settings);
748 	g_slice_free (SpellConfigureWidget, data);
749 
750 	gedit_debug_message (DEBUG_PLUGINS, "END");
751 }
752 
753 static SpellConfigureWidget *
get_configure_widget(GeditSpellPlugin * plugin)754 get_configure_widget (GeditSpellPlugin *plugin)
755 {
756 	SpellConfigureWidget *widget;
757 	GtkBuilder *builder;
758 	gchar *root_objects[] = {
759 		"spell_dialog_content",
760 		NULL
761 	};
762 
763 	gedit_debug (DEBUG_PLUGINS);
764 
765 	widget = g_slice_new (SpellConfigureWidget);
766 	widget->settings = g_object_ref (plugin->priv->settings);
767 
768 	builder = gtk_builder_new ();
769 	gtk_builder_add_objects_from_resource (builder, "/org/gnome/gedit/plugins/spell/ui/gedit-spell-setup-dialog.ui",
770 	                                       root_objects, NULL);
771 	widget->content = GTK_WIDGET (gtk_builder_get_object (builder, "spell_dialog_content"));
772 	g_object_ref (widget->content);
773 
774 	widget->highlight_button = GTK_WIDGET (gtk_builder_get_object (builder, "highlight_button"));
775 	g_object_unref (builder);
776 
777 	gboolean status = g_settings_get_boolean(widget->settings, SETTINGS_KEY_HIGHLIGHT_MISSPELLED);
778 	gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (widget->highlight_button), status);
779 
780 	g_signal_connect (widget->highlight_button,
781 		          "toggled",
782 		          G_CALLBACK (highlight_button_toggled),
783 		          widget);
784 
785 	g_signal_connect (widget->content,
786 			  "destroy",
787 			  G_CALLBACK (configure_widget_destroyed),
788 			  widget);
789 
790 	return widget;
791 }
792 
793 
794 static GtkWidget *
gedit_spell_plugin_create_configure_widget(PeasGtkConfigurable * configurable)795 gedit_spell_plugin_create_configure_widget (PeasGtkConfigurable *configurable)
796 {
797 	SpellConfigureWidget *widget;
798 
799 	widget = get_configure_widget (GEDIT_SPELL_PLUGIN (configurable));
800 
801 	return widget->content;
802 }
803 
804 static void
peas_gtk_configurable_iface_init(PeasGtkConfigurableInterface * iface)805 peas_gtk_configurable_iface_init (PeasGtkConfigurableInterface *iface)
806 {
807 	iface->create_configure_widget = gedit_spell_plugin_create_configure_widget;
808 }
809 
810 /* ex:set ts=8 noet: */
811