1 /*
2  * pluma-spell-plugin.c
3  *
4  * Copyright (C) 2002-2005 Paolo Maggi
5  * Copyright (C) 2012-2021 MATE Developers
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, write to the Free Software
19  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
20  *
21  * $Id$
22  */
23 
24 #ifdef HAVE_CONFIG_H
25 #include <config.h>
26 #endif
27 
28 #include "pluma-spell-plugin.h"
29 #include "pluma-spell-utils.h"
30 
31 #include <string.h> /* For strlen */
32 
33 #include <glib/gi18n.h>
34 #include <gmodule.h>
35 #include <libpeas-gtk/peas-gtk-configurable.h>
36 
37 #include <pluma/pluma-window-activatable.h>
38 #include <pluma/pluma-window.h>
39 #include <pluma/pluma-debug.h>
40 #include <pluma/pluma-statusbar.h>
41 #include <pluma/pluma-utils.h>
42 
43 #include "pluma-spell-checker.h"
44 #include "pluma-spell-checker-dialog.h"
45 #include "pluma-spell-language-dialog.h"
46 #include "pluma-automatic-spell-checker.h"
47 
48 #define PLUMA_METADATA_ATTRIBUTE_SPELL_LANGUAGE "metadata::pluma-spell-language"
49 #define PLUMA_METADATA_ATTRIBUTE_SPELL_ENABLED  "metadata::pluma-spell-enabled"
50 
51 #define MENU_PATH "/MenuBar/ToolsMenu/ToolsOps_1"
52 
53 /* GSettings keys */
54 #define SPELL_SCHEMA		"org.mate.pluma.plugins.spell"
55 #define AUTOCHECK_TYPE_KEY	"autocheck-type"
56 
57 static void pluma_window_activatable_iface_init (PlumaWindowActivatableInterface *iface);
58 static void peas_gtk_configurable_iface_init (PeasGtkConfigurableInterface *iface);
59 
60 enum {
61 	PROP_0,
62 	PROP_WINDOW
63 };
64 
65 struct _PlumaSpellPluginPrivate
66 {
67 	PlumaWindow *window;
68 
69 	GtkActionGroup *action_group;
70 	guint           ui_id;
71 	guint           message_cid;
72 	gulong          tab_added_id;
73 	gulong          tab_removed_id;
74 
75 	GSettings *settings;
76 };
77 
78 G_DEFINE_DYNAMIC_TYPE_EXTENDED (PlumaSpellPlugin,
79                                 pluma_spell_plugin,
80                                 PEAS_TYPE_EXTENSION_BASE,
81                                 0,
82                                 G_ADD_PRIVATE_DYNAMIC (PlumaSpellPlugin)
83                                 G_IMPLEMENT_INTERFACE_DYNAMIC (PLUMA_TYPE_WINDOW_ACTIVATABLE,
84                                                                pluma_window_activatable_iface_init)
85                                 G_IMPLEMENT_INTERFACE_DYNAMIC (PEAS_GTK_TYPE_CONFIGURABLE,
86                                                                peas_gtk_configurable_iface_init))
87 
88 static void	spell_cb	(GtkAction *action, PlumaSpellPlugin *plugin);
89 static void	set_language_cb	(GtkAction *action, PlumaSpellPlugin *plugin);
90 static void	auto_spell_cb	(GtkAction *action, PlumaSpellPlugin *plugin);
91 
92 /* UI actions. */
93 static const GtkActionEntry action_entries[] =
94 {
95 	{ "CheckSpell",
96 	  "tools-check-spelling",
97 	  N_("_Check Spelling..."),
98 	  "<shift>F7",
99 	  N_("Check the current document for incorrect spelling"),
100 	  G_CALLBACK (spell_cb)
101 	},
102 
103 	{ "ConfigSpell",
104 	  NULL,
105 	  N_("Set _Language..."),
106 	  NULL,
107 	  N_("Set the language of the current document"),
108 	  G_CALLBACK (set_language_cb)
109 	}
110 };
111 
112 static const GtkToggleActionEntry toggle_action_entries[] =
113 {
114 	{ "AutoSpell",
115 	  NULL,
116 	  N_("_Autocheck Spelling"),
117 	  "<control>F7",
118 	  N_("Automatically spell-check the current document"),
119 	  G_CALLBACK (auto_spell_cb),
120 	  FALSE
121 	}
122 };
123 
124 typedef struct _SpellConfigureDialog SpellConfigureDialog;
125 
126 struct _SpellConfigureDialog
127 {
128 	GtkWidget *content;
129 
130 	GtkWidget *never;
131 	GtkWidget *always;
132 	GtkWidget *document;
133 
134 	GSettings *settings;
135 };
136 
137 typedef enum
138 {
139 	AUTOCHECK_NEVER = 0,
140 	AUTOCHECK_DOCUMENT,
141 	AUTOCHECK_ALWAYS
142 } PlumaSpellPluginAutocheckType;
143 
144 typedef struct _CheckRange CheckRange;
145 
146 struct _CheckRange
147 {
148 	GtkTextMark *start_mark;
149 	GtkTextMark *end_mark;
150 
151 	gint mw_start; /* misspelled word start */
152 	gint mw_end;   /* end */
153 
154 	GtkTextMark *current_mark;
155 };
156 
157 static GQuark spell_checker_id = 0;
158 static GQuark check_range_id = 0;
159 
160 static void
pluma_spell_plugin_init(PlumaSpellPlugin * plugin)161 pluma_spell_plugin_init (PlumaSpellPlugin *plugin)
162 {
163 	pluma_debug_message (DEBUG_PLUGINS, "PlumaSpellPlugin initializing");
164 
165 	plugin->priv = pluma_spell_plugin_get_instance_private (plugin);
166 
167 	plugin->priv->settings = g_settings_new (SPELL_SCHEMA);
168 }
169 
170 static void
pluma_spell_plugin_dispose(GObject * object)171 pluma_spell_plugin_dispose (GObject *object)
172 {
173 	PlumaSpellPlugin *plugin = PLUMA_SPELL_PLUGIN (object);
174 
175 	pluma_debug_message (DEBUG_PLUGINS, "PlumaSpellPlugin disposing");
176 
177 	if (plugin->priv->window != NULL)
178 	{
179 		g_object_unref (plugin->priv->window);
180 		plugin->priv->window = NULL;
181 	}
182 
183 	if (plugin->priv->action_group)
184 	{
185 		g_object_unref (plugin->priv->action_group);
186 		plugin->priv->action_group = NULL;
187 	}
188 
189 	g_object_unref (G_OBJECT (plugin->priv->settings));
190 
191 	G_OBJECT_CLASS (pluma_spell_plugin_parent_class)->dispose (object);
192 }
193 
194 static void
set_spell_language_cb(PlumaSpellChecker * spell,const PlumaSpellCheckerLanguage * lang,PlumaDocument * doc)195 set_spell_language_cb (PlumaSpellChecker   *spell,
196 		       const PlumaSpellCheckerLanguage *lang,
197 		       PlumaDocument 	   *doc)
198 {
199 	const gchar *key;
200 
201 	g_return_if_fail (PLUMA_IS_DOCUMENT (doc));
202 	g_return_if_fail (lang != NULL);
203 
204 	key = pluma_spell_checker_language_to_key (lang);
205 	g_return_if_fail (key != NULL);
206 
207 	pluma_document_set_metadata (doc, PLUMA_METADATA_ATTRIBUTE_SPELL_LANGUAGE,
208 				     key, NULL);
209 }
210 
211 static void
set_language_from_metadata(PlumaSpellChecker * spell,PlumaDocument * doc)212 set_language_from_metadata (PlumaSpellChecker *spell,
213 			    PlumaDocument     *doc)
214 {
215 	const PlumaSpellCheckerLanguage *lang = NULL;
216 	gchar *value = NULL;
217 
218 	value = pluma_document_get_metadata (doc, PLUMA_METADATA_ATTRIBUTE_SPELL_LANGUAGE);
219 
220 	if (value != NULL)
221 	{
222 		lang = pluma_spell_checker_language_from_key (value);
223 		g_free (value);
224 	}
225 
226 	if (lang != NULL)
227 	{
228 		g_signal_handlers_block_by_func (spell, set_spell_language_cb, doc);
229 		pluma_spell_checker_set_language (spell, lang);
230 		g_signal_handlers_unblock_by_func (spell, set_spell_language_cb, doc);
231 	}
232 }
233 
234 static PlumaSpellPluginAutocheckType
get_autocheck_type(PlumaSpellPlugin * plugin)235 get_autocheck_type (PlumaSpellPlugin *plugin)
236 {
237 	PlumaSpellPluginAutocheckType autocheck_type;
238 
239 	autocheck_type = g_settings_get_enum (plugin->priv->settings,
240 					      AUTOCHECK_TYPE_KEY);
241 
242 	return autocheck_type;
243 }
244 
245 static void
set_autocheck_type(GSettings * settings,PlumaSpellPluginAutocheckType autocheck_type)246 set_autocheck_type (GSettings *settings,
247 		    PlumaSpellPluginAutocheckType autocheck_type)
248 {
249 	if (!g_settings_is_writable (settings,
250 				     AUTOCHECK_TYPE_KEY))
251 	{
252 		return;
253 	}
254 
255 	g_settings_set_enum (settings,
256 			     AUTOCHECK_TYPE_KEY,
257 			     autocheck_type);
258 }
259 
260 static PlumaSpellChecker *
get_spell_checker_from_document(PlumaDocument * doc)261 get_spell_checker_from_document (PlumaDocument *doc)
262 {
263 	PlumaSpellChecker *spell;
264 	gpointer data;
265 
266 	pluma_debug (DEBUG_PLUGINS);
267 
268 	g_return_val_if_fail (doc != NULL, NULL);
269 
270 	data = g_object_get_qdata (G_OBJECT (doc), spell_checker_id);
271 
272 	if (data == NULL)
273 	{
274 		spell = pluma_spell_checker_new ();
275 
276 		set_language_from_metadata (spell, doc);
277 
278 		g_object_set_qdata_full (G_OBJECT (doc),
279 					 spell_checker_id,
280 					 spell,
281 					 (GDestroyNotify) g_object_unref);
282 
283 		g_signal_connect (spell,
284 				  "set_language",
285 				  G_CALLBACK (set_spell_language_cb),
286 				  doc);
287 	}
288 	else
289 	{
290 		g_return_val_if_fail (PLUMA_IS_SPELL_CHECKER (data), NULL);
291 		spell = PLUMA_SPELL_CHECKER (data);
292 	}
293 
294 	return spell;
295 }
296 
297 static CheckRange *
get_check_range(PlumaDocument * doc)298 get_check_range (PlumaDocument *doc)
299 {
300 	CheckRange *range;
301 
302 	pluma_debug (DEBUG_PLUGINS);
303 
304 	g_return_val_if_fail (doc != NULL, NULL);
305 
306 	range = (CheckRange *) g_object_get_qdata (G_OBJECT (doc), check_range_id);
307 
308 	return range;
309 }
310 
311 static void
update_current(PlumaDocument * doc,gint current)312 update_current (PlumaDocument *doc,
313 		gint           current)
314 {
315 	CheckRange *range;
316 	GtkTextIter iter;
317 	GtkTextIter end_iter;
318 
319 	pluma_debug (DEBUG_PLUGINS);
320 
321 	g_return_if_fail (doc != NULL);
322 	g_return_if_fail (current >= 0);
323 
324 	range = get_check_range (doc);
325 	g_return_if_fail (range != NULL);
326 
327 	gtk_text_buffer_get_iter_at_offset (GTK_TEXT_BUFFER (doc),
328 					    &iter, current);
329 
330 	if (!gtk_text_iter_inside_word (&iter))
331 	{
332 		/* if we're not inside a word,
333 		 * we must be in some spaces.
334 		 * skip forward to the beginning of the next word. */
335 		if (!gtk_text_iter_is_end (&iter))
336 		{
337 			gtk_text_iter_forward_word_end (&iter);
338 			gtk_text_iter_backward_word_start (&iter);
339 		}
340 	}
341 	else
342 	{
343 		if (!gtk_text_iter_starts_word (&iter))
344 			gtk_text_iter_backward_word_start (&iter);
345 	}
346 
347 	gtk_text_buffer_get_iter_at_mark (GTK_TEXT_BUFFER (doc),
348 					  &end_iter,
349 					  range->end_mark);
350 
351 	if (gtk_text_iter_compare (&end_iter, &iter) < 0)
352 	{
353 		gtk_text_buffer_move_mark (GTK_TEXT_BUFFER (doc),
354 					   range->current_mark,
355 					   &end_iter);
356 	}
357 	else
358 	{
359 		gtk_text_buffer_move_mark (GTK_TEXT_BUFFER (doc),
360 					   range->current_mark,
361 					   &iter);
362 	}
363 }
364 
365 static void
set_check_range(PlumaDocument * doc,GtkTextIter * start,GtkTextIter * end)366 set_check_range (PlumaDocument *doc,
367 		 GtkTextIter   *start,
368 		 GtkTextIter   *end)
369 {
370 	CheckRange *range;
371 	GtkTextIter iter;
372 
373 	pluma_debug (DEBUG_PLUGINS);
374 
375 	range = get_check_range (doc);
376 
377 	if (range == NULL)
378 	{
379 		pluma_debug_message (DEBUG_PLUGINS, "There was not a previous check range");
380 
381 		gtk_text_buffer_get_end_iter (GTK_TEXT_BUFFER (doc), &iter);
382 
383 		range = g_new0 (CheckRange, 1);
384 
385 		range->start_mark = gtk_text_buffer_create_mark (GTK_TEXT_BUFFER (doc),
386 				"check_range_start_mark", &iter, TRUE);
387 
388 		range->end_mark = gtk_text_buffer_create_mark (GTK_TEXT_BUFFER (doc),
389 				"check_range_end_mark", &iter, FALSE);
390 
391 		range->current_mark = gtk_text_buffer_create_mark (GTK_TEXT_BUFFER (doc),
392 				"check_range_current_mark", &iter, TRUE);
393 
394 		g_object_set_qdata_full (G_OBJECT (doc),
395 				 check_range_id,
396 				 range,
397 				 (GDestroyNotify)g_free);
398 	}
399 
400 	if (pluma_spell_utils_skip_no_spell_check (start, end))
401 	 {
402 		if (!gtk_text_iter_inside_word (end))
403 		{
404 			/* if we're neither inside a word,
405 			 * we must be in some spaces.
406 			 * skip backward to the end of the previous word. */
407 			if (!gtk_text_iter_is_end (end))
408 			{
409 				gtk_text_iter_backward_word_start (end);
410 				gtk_text_iter_forward_word_end (end);
411 			}
412 		}
413 		else
414 		{
415 			if (!gtk_text_iter_ends_word (end))
416 				gtk_text_iter_forward_word_end (end);
417 		}
418 	}
419 	else
420 	{
421 		/* no spell checking in the specified range */
422 		start = end;
423 	}
424 
425 	gtk_text_buffer_move_mark (GTK_TEXT_BUFFER (doc),
426 				   range->start_mark,
427 				   start);
428 	gtk_text_buffer_move_mark (GTK_TEXT_BUFFER (doc),
429 				   range->end_mark,
430 				   end);
431 
432 	range->mw_start = -1;
433 	range->mw_end = -1;
434 
435 	update_current (doc, gtk_text_iter_get_offset (start));
436 }
437 
438 static gchar *
get_current_word(PlumaDocument * doc,gint * start,gint * end)439 get_current_word (PlumaDocument *doc, gint *start, gint *end)
440 {
441 	const CheckRange *range;
442 	GtkTextIter end_iter;
443 	GtkTextIter current_iter;
444 	gint range_end;
445 
446 	pluma_debug (DEBUG_PLUGINS);
447 
448 	g_return_val_if_fail (doc != NULL, NULL);
449 	g_return_val_if_fail (start != NULL, NULL);
450 	g_return_val_if_fail (end != NULL, NULL);
451 
452 	range = get_check_range (doc);
453 	g_return_val_if_fail (range != NULL, NULL);
454 
455 	gtk_text_buffer_get_iter_at_mark (GTK_TEXT_BUFFER (doc),
456 			&end_iter, range->end_mark);
457 
458 	range_end = gtk_text_iter_get_offset (&end_iter);
459 
460 	gtk_text_buffer_get_iter_at_mark (GTK_TEXT_BUFFER (doc),
461 			&current_iter, range->current_mark);
462 
463 	end_iter = current_iter;
464 
465 	if (!gtk_text_iter_is_end (&end_iter))
466 	{
467 		pluma_debug_message (DEBUG_PLUGINS, "Current is not end");
468 
469 		gtk_text_iter_forward_word_end (&end_iter);
470 	}
471 
472 	*start = gtk_text_iter_get_offset (&current_iter);
473 	*end = MIN (gtk_text_iter_get_offset (&end_iter), range_end);
474 
475 	pluma_debug_message (DEBUG_PLUGINS, "Current word extends [%d, %d]", *start, *end);
476 
477 	if (!(*start < *end))
478 		return NULL;
479 
480 	return gtk_text_buffer_get_slice (GTK_TEXT_BUFFER (doc),
481 					  &current_iter,
482 					  &end_iter,
483 					  TRUE);
484 }
485 
486 static gboolean
goto_next_word(PlumaDocument * doc)487 goto_next_word (PlumaDocument *doc)
488 {
489 	CheckRange *range;
490 	GtkTextIter current_iter;
491 	GtkTextIter old_current_iter;
492 	GtkTextIter end_iter;
493 
494 	pluma_debug (DEBUG_PLUGINS);
495 
496 	g_return_val_if_fail (doc != NULL, FALSE);
497 
498 	range = get_check_range (doc);
499 	g_return_val_if_fail (range != NULL, FALSE);
500 
501 	gtk_text_buffer_get_iter_at_mark (GTK_TEXT_BUFFER (doc),
502 					  &current_iter,
503 					  range->current_mark);
504 	gtk_text_buffer_get_end_iter (GTK_TEXT_BUFFER (doc), &end_iter);
505 
506 	old_current_iter = current_iter;
507 
508 	gtk_text_iter_forward_word_ends (&current_iter, 2);
509 	gtk_text_iter_backward_word_start (&current_iter);
510 
511 	if (pluma_spell_utils_skip_no_spell_check (&current_iter, &end_iter) &&
512 	    (gtk_text_iter_compare (&old_current_iter, &current_iter) < 0) &&
513 	    (gtk_text_iter_compare (&current_iter, &end_iter) < 0))
514 	{
515 		update_current (doc, gtk_text_iter_get_offset (&current_iter));
516 		return TRUE;
517 	}
518 
519 	return FALSE;
520 }
521 
522 static gchar *
get_next_misspelled_word(PlumaView * view)523 get_next_misspelled_word (PlumaView *view)
524 {
525 	PlumaDocument *doc;
526 	CheckRange *range;
527 	gint start, end;
528 	gchar *word;
529 	PlumaSpellChecker *spell;
530 
531 	g_return_val_if_fail (view != NULL, NULL);
532 
533 	doc = PLUMA_DOCUMENT (gtk_text_view_get_buffer (GTK_TEXT_VIEW (view)));
534 	g_return_val_if_fail (doc != NULL, NULL);
535 
536 	range = get_check_range (doc);
537 	g_return_val_if_fail (range != NULL, NULL);
538 
539 	spell = get_spell_checker_from_document (doc);
540 	g_return_val_if_fail (spell != NULL, NULL);
541 
542 	word = get_current_word (doc, &start, &end);
543 	if (word == NULL)
544 		return NULL;
545 
546 	pluma_debug_message (DEBUG_PLUGINS, "Word to check: %s", word);
547 
548 	while (pluma_spell_checker_check_word (spell, word, -1))
549 	{
550 		g_free (word);
551 
552 		if (!goto_next_word (doc))
553 			return NULL;
554 
555 		/* may return null if we reached the end of the selection */
556 		word = get_current_word (doc, &start, &end);
557 		if (word == NULL)
558 			return NULL;
559 
560 		pluma_debug_message (DEBUG_PLUGINS, "Word to check: %s", word);
561 	}
562 
563 	if (!goto_next_word (doc))
564 		update_current (doc, gtk_text_buffer_get_char_count (GTK_TEXT_BUFFER (doc)));
565 
566 	if (word != NULL)
567 	{
568 		GtkTextIter s, e;
569 
570 		range->mw_start = start;
571 		range->mw_end = end;
572 
573 		pluma_debug_message (DEBUG_PLUGINS, "Select [%d, %d]", start, end);
574 
575 		gtk_text_buffer_get_iter_at_offset (GTK_TEXT_BUFFER (doc), &s, start);
576 		gtk_text_buffer_get_iter_at_offset (GTK_TEXT_BUFFER (doc), &e, end);
577 
578 		gtk_text_buffer_select_range (GTK_TEXT_BUFFER (doc), &s, &e);
579 
580 		pluma_view_scroll_to_cursor (view);
581 	}
582 	else
583 	{
584 		range->mw_start = -1;
585 		range->mw_end = -1;
586 	}
587 
588 	return word;
589 }
590 
591 static void
ignore_cb(PlumaSpellCheckerDialog * dlg,const gchar * w,PlumaView * view)592 ignore_cb (PlumaSpellCheckerDialog *dlg,
593 	   const gchar             *w,
594 	   PlumaView               *view)
595 {
596 	gchar *word = NULL;
597 
598 	pluma_debug (DEBUG_PLUGINS);
599 
600 	g_return_if_fail (w != NULL);
601 	g_return_if_fail (view != NULL);
602 
603 	word = get_next_misspelled_word (view);
604 	if (word == NULL)
605 	{
606 		pluma_spell_checker_dialog_set_completed (dlg);
607 
608 		return;
609 	}
610 
611 	pluma_spell_checker_dialog_set_misspelled_word (PLUMA_SPELL_CHECKER_DIALOG (dlg),
612 							word,
613 							-1);
614 
615 	g_free (word);
616 }
617 
618 static void
change_cb(PlumaSpellCheckerDialog * dlg,const gchar * word,const gchar * change,PlumaView * view)619 change_cb (PlumaSpellCheckerDialog *dlg,
620 	   const gchar             *word,
621 	   const gchar             *change,
622 	   PlumaView               *view)
623 {
624 	PlumaDocument *doc;
625 	CheckRange *range;
626 	gchar *w = NULL;
627 	GtkTextIter start, end;
628 
629 	pluma_debug (DEBUG_PLUGINS);
630 
631 	g_return_if_fail (view != NULL);
632 	g_return_if_fail (word != NULL);
633 	g_return_if_fail (change != NULL);
634 
635 	doc = PLUMA_DOCUMENT (gtk_text_view_get_buffer (GTK_TEXT_VIEW (view)));
636 	g_return_if_fail (doc != NULL);
637 
638 	range = get_check_range (doc);
639 	g_return_if_fail (range != NULL);
640 
641 	gtk_text_buffer_get_iter_at_offset (GTK_TEXT_BUFFER (doc), &start, range->mw_start);
642 	if (range->mw_end < 0)
643 		gtk_text_buffer_get_end_iter (GTK_TEXT_BUFFER (doc), &end);
644 	else
645 		gtk_text_buffer_get_iter_at_offset (GTK_TEXT_BUFFER (doc), &end, range->mw_end);
646 
647 	w = gtk_text_buffer_get_slice (GTK_TEXT_BUFFER (doc), &start, &end, TRUE);
648 	g_return_if_fail (w != NULL);
649 
650 	if (strcmp (w, word) != 0)
651 	{
652 		g_free (w);
653 		return;
654 	}
655 
656 	g_free (w);
657 
658 	gtk_text_buffer_begin_user_action (GTK_TEXT_BUFFER(doc));
659 
660 	gtk_text_buffer_delete (GTK_TEXT_BUFFER (doc), &start, &end);
661 	gtk_text_buffer_insert (GTK_TEXT_BUFFER (doc), &start, change, -1);
662 
663 	gtk_text_buffer_end_user_action (GTK_TEXT_BUFFER(doc));
664 
665 	update_current (doc, range->mw_start + g_utf8_strlen (change, -1));
666 
667 	/* go to next misspelled word */
668 	ignore_cb (dlg, word, view);
669 }
670 
671 static void
change_all_cb(PlumaSpellCheckerDialog * dlg,const gchar * word,const gchar * change,PlumaView * view)672 change_all_cb (PlumaSpellCheckerDialog *dlg,
673 	       const gchar             *word,
674 	       const gchar             *change,
675 	       PlumaView               *view)
676 {
677 	PlumaDocument *doc;
678 	CheckRange *range;
679 	gchar *w = NULL;
680 	GtkTextIter start, end;
681 	gint flags = 0;
682 
683 	pluma_debug (DEBUG_PLUGINS);
684 
685 	g_return_if_fail (view != NULL);
686 	g_return_if_fail (word != NULL);
687 	g_return_if_fail (change != NULL);
688 
689 	doc = PLUMA_DOCUMENT (gtk_text_view_get_buffer (GTK_TEXT_VIEW (view)));
690 	g_return_if_fail (doc != NULL);
691 
692 	range = get_check_range (doc);
693 	g_return_if_fail (range != NULL);
694 
695 	gtk_text_buffer_get_iter_at_offset (GTK_TEXT_BUFFER (doc), &start, range->mw_start);
696 	if (range->mw_end < 0)
697 		gtk_text_buffer_get_end_iter (GTK_TEXT_BUFFER (doc), &end);
698 	else
699 		gtk_text_buffer_get_iter_at_offset (GTK_TEXT_BUFFER (doc), &end, range->mw_end);
700 
701 	w = gtk_text_buffer_get_slice (GTK_TEXT_BUFFER (doc), &start, &end, TRUE);
702 	g_return_if_fail (w != NULL);
703 
704 	if (strcmp (w, word) != 0)
705 	{
706 		g_free (w);
707 		return;
708 	}
709 
710 	g_free (w);
711 
712 	PLUMA_SEARCH_SET_CASE_SENSITIVE (flags, TRUE);
713 	PLUMA_SEARCH_SET_ENTIRE_WORD (flags, TRUE);
714 
715 	/* CHECK: currently this function does escaping etc */
716 	pluma_document_replace_all (doc, word, change, flags);
717 
718 	update_current (doc, range->mw_start + g_utf8_strlen (change, -1));
719 
720 	/* go to next misspelled word */
721 	ignore_cb (dlg, word, view);
722 }
723 
724 static void
add_word_cb(PlumaSpellCheckerDialog * dlg,const gchar * word,PlumaView * view)725 add_word_cb (PlumaSpellCheckerDialog *dlg,
726 	     const gchar             *word,
727 	     PlumaView               *view)
728 {
729 	g_return_if_fail (view != NULL);
730 	g_return_if_fail (word != NULL);
731 
732 	/* go to next misspelled word */
733 	ignore_cb (dlg, word, view);
734 }
735 
736 static void
language_dialog_response(GtkDialog * dlg,gint res_id,PlumaSpellChecker * spell)737 language_dialog_response (GtkDialog         *dlg,
738 			  gint               res_id,
739 			  PlumaSpellChecker *spell)
740 {
741 	if (res_id == GTK_RESPONSE_OK)
742 	{
743 		const PlumaSpellCheckerLanguage *lang;
744 
745 		lang = pluma_spell_language_get_selected_language (PLUMA_SPELL_LANGUAGE_DIALOG (dlg));
746 		if (lang != NULL)
747 			pluma_spell_checker_set_language (spell, lang);
748 	}
749 
750 	gtk_widget_destroy (GTK_WIDGET (dlg));
751 }
752 
753 static SpellConfigureDialog *
get_configure_dialog(PlumaSpellPlugin * plugin)754 get_configure_dialog (PlumaSpellPlugin *plugin)
755 {
756 	SpellConfigureDialog *dialog = NULL;
757 	gchar *data_dir;
758 	gchar *ui_file;
759 	PlumaSpellPluginAutocheckType autocheck_type;
760 	GtkWidget *error_widget;
761 	gboolean ret;
762 	gchar *root_objects[] = {
763 		"spell_dialog_content",
764 		NULL
765 	};
766 
767 	pluma_debug (DEBUG_PLUGINS);
768 
769 	dialog = g_slice_new (SpellConfigureDialog);
770 	dialog->settings = g_object_ref (plugin->priv->settings);
771 
772 	data_dir = peas_extension_base_get_data_dir (PEAS_EXTENSION_BASE (plugin));
773 	ui_file = g_build_filename (data_dir, "pluma-spell-setup-dialog.ui", NULL);
774 	ret = pluma_utils_get_ui_objects (ui_file,
775 					  root_objects,
776 					  &error_widget,
777 					  "spell_dialog_content", &dialog->content,
778 					  "autocheck_never", &dialog->never,
779 					  "autocheck_document", &dialog->document,
780 					  "autocheck_always", &dialog->always,
781 					  NULL);
782 
783 	g_free (data_dir);
784 	g_free (ui_file);
785 
786 	if (!ret)
787 	{
788 		return NULL;
789 	}
790 
791 	autocheck_type = get_autocheck_type (plugin);
792 
793 	if (autocheck_type == AUTOCHECK_ALWAYS)
794 	{
795 		gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (dialog->always), TRUE);
796 	}
797 	else if (autocheck_type == AUTOCHECK_DOCUMENT)
798 	{
799 		gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (dialog->document), TRUE);
800 	}
801 	else
802 	{
803 		gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (dialog->never), TRUE);
804 	}
805 
806 	return dialog;
807 }
808 
809 static void
configure_dialog_button_toggled(GtkToggleButton * button,SpellConfigureDialog * dialog)810 configure_dialog_button_toggled (GtkToggleButton      *button,
811                                  SpellConfigureDialog *dialog)
812 {
813 	pluma_debug (DEBUG_PLUGINS);
814 
815 	if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (dialog->always)))
816 	{
817 		set_autocheck_type (dialog->settings, AUTOCHECK_ALWAYS);
818 	}
819 	else if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (dialog->document)))
820 	{
821 		set_autocheck_type (dialog->settings, AUTOCHECK_DOCUMENT);
822 	}
823 	else
824 	{
825 		set_autocheck_type (dialog->settings, AUTOCHECK_NEVER);
826 	}
827 }
828 
829 static void
configure_dialog_destroyed(GtkWidget * widget,gpointer data)830 configure_dialog_destroyed (GtkWidget *widget,
831                             gpointer   data)
832 {
833 	SpellConfigureDialog *dialog = (SpellConfigureDialog *) data;
834 
835 	pluma_debug (DEBUG_PLUGINS);
836 
837 	g_object_unref (dialog->settings);
838 	g_slice_free (SpellConfigureDialog, data);
839 }
840 
841 static void
set_language_cb(GtkAction * action,PlumaSpellPlugin * plugin)842 set_language_cb (GtkAction   *action,
843 		 PlumaSpellPlugin *plugin)
844 {
845 	PlumaWindow *window;
846 	PlumaDocument *doc;
847 	PlumaSpellChecker *spell;
848 	const PlumaSpellCheckerLanguage *lang;
849 	GtkWidget *dlg;
850 	GtkWindowGroup *wg;
851 	gchar *data_dir;
852 
853 	pluma_debug (DEBUG_PLUGINS);
854 
855 	window = PLUMA_WINDOW (plugin->priv->window);
856 	doc = pluma_window_get_active_document (window);
857 	g_return_if_fail (doc != NULL);
858 
859 	spell = get_spell_checker_from_document (doc);
860 	g_return_if_fail (spell != NULL);
861 
862 	lang = pluma_spell_checker_get_language (spell);
863 
864 	data_dir = peas_extension_base_get_data_dir (PEAS_EXTENSION_BASE (plugin));
865 	dlg = pluma_spell_language_dialog_new (GTK_WINDOW (window),
866 					       lang,
867 					       data_dir);
868 	g_free (data_dir);
869 
870 	wg = pluma_window_get_group (window);
871 
872 	gtk_window_group_add_window (wg, GTK_WINDOW (dlg));
873 
874 	gtk_window_set_modal (GTK_WINDOW (dlg), TRUE);
875 
876 	g_signal_connect (dlg,
877 			  "response",
878 			  G_CALLBACK (language_dialog_response),
879 			  spell);
880 
881 	gtk_widget_show (dlg);
882 }
883 
884 static void
spell_cb(GtkAction * action,PlumaSpellPlugin * plugin)885 spell_cb (GtkAction   *action,
886 	  PlumaSpellPlugin *plugin)
887 {
888 	PlumaSpellPluginPrivate *data;
889 	PlumaWindow *window;
890 	PlumaView *view;
891 	PlumaDocument *doc;
892 	PlumaSpellChecker *spell;
893 	GtkWidget *dlg;
894 	GtkTextIter start, end;
895 	gchar *word;
896 	gchar *data_dir;
897 
898 	pluma_debug (DEBUG_PLUGINS);
899 
900 	data = plugin->priv;
901 	window = PLUMA_WINDOW (data->window);
902 	view = pluma_window_get_active_view (window);
903 	g_return_if_fail (view != NULL);
904 
905 	doc = PLUMA_DOCUMENT (gtk_text_view_get_buffer (GTK_TEXT_VIEW (view)));
906 	g_return_if_fail (doc != NULL);
907 
908 	spell = get_spell_checker_from_document (doc);
909 	g_return_if_fail (spell != NULL);
910 
911 	if (gtk_text_buffer_get_char_count (GTK_TEXT_BUFFER (doc)) <= 0)
912 	{
913 		GtkWidget *statusbar;
914 
915 		statusbar = pluma_window_get_statusbar (window);
916 		pluma_statusbar_flash_message (PLUMA_STATUSBAR (statusbar),
917 					       data->message_cid,
918 					       _("The document is empty."));
919 
920 		return;
921 	}
922 
923 	if (!gtk_text_buffer_get_selection_bounds (GTK_TEXT_BUFFER (doc),
924 						   &start,
925 						   &end))
926 	{
927 		/* no selection, get the whole doc */
928 		gtk_text_buffer_get_bounds (GTK_TEXT_BUFFER (doc),
929 					    &start,
930 					    &end);
931 	}
932 
933 	set_check_range (doc, &start, &end);
934 
935 	word = get_next_misspelled_word (view);
936 	if (word == NULL)
937 	{
938 		GtkWidget *statusbar;
939 
940 		statusbar = pluma_window_get_statusbar (window);
941 		pluma_statusbar_flash_message (PLUMA_STATUSBAR (statusbar),
942 					       data->message_cid,
943 					       _("No misspelled words"));
944 
945 		return;
946 	}
947 
948 	data_dir = peas_extension_base_get_data_dir (PEAS_EXTENSION_BASE (plugin));
949 	dlg = pluma_spell_checker_dialog_new_from_spell_checker (spell, data_dir);
950 	g_free (data_dir);
951 
952 	gtk_window_set_modal (GTK_WINDOW (dlg), TRUE);
953 	gtk_window_set_transient_for (GTK_WINDOW (dlg),
954 				      GTK_WINDOW (window));
955 
956 	g_signal_connect (dlg, "ignore", G_CALLBACK (ignore_cb), view);
957 	g_signal_connect (dlg, "ignore_all", G_CALLBACK (ignore_cb), view);
958 
959 	g_signal_connect (dlg, "change", G_CALLBACK (change_cb), view);
960 	g_signal_connect (dlg, "change_all", G_CALLBACK (change_all_cb), view);
961 
962 	g_signal_connect (dlg, "add_word_to_personal", G_CALLBACK (add_word_cb), view);
963 
964 	pluma_spell_checker_dialog_set_misspelled_word (PLUMA_SPELL_CHECKER_DIALOG (dlg),
965 							word,
966 							-1);
967 
968 	g_free (word);
969 
970 	gtk_widget_show (dlg);
971 }
972 
973 static void
set_auto_spell(PlumaWindow * window,PlumaDocument * doc,gboolean active)974 set_auto_spell (PlumaWindow   *window,
975 		PlumaDocument *doc,
976 		gboolean       active)
977 {
978 	PlumaAutomaticSpellChecker *autospell;
979 	PlumaSpellChecker *spell;
980 
981 	spell = get_spell_checker_from_document (doc);
982 	g_return_if_fail (spell != NULL);
983 
984 	autospell = pluma_automatic_spell_checker_get_from_document (doc);
985 
986 	if (active)
987 	{
988 		if (autospell == NULL)
989 		{
990 			PlumaView *active_view;
991 
992 			active_view = pluma_window_get_active_view (window);
993 			g_return_if_fail (active_view != NULL);
994 
995 			autospell = pluma_automatic_spell_checker_new (doc, spell);
996 
997 			if (doc == pluma_window_get_active_document (window))
998 			{
999 				pluma_automatic_spell_checker_attach_view (autospell, active_view);
1000 			}
1001 
1002 			pluma_automatic_spell_checker_recheck_all (autospell);
1003 		}
1004 	}
1005 	else
1006 	{
1007 		if (autospell != NULL)
1008 			pluma_automatic_spell_checker_free (autospell);
1009 	}
1010 }
1011 
1012 static void
auto_spell_cb(GtkAction * action,PlumaSpellPlugin * plugin)1013 auto_spell_cb (GtkAction   *action,
1014 	       PlumaSpellPlugin *plugin)
1015 {
1016 	PlumaWindow *window;
1017 	PlumaDocument *doc;
1018 	gboolean active;
1019 
1020 	pluma_debug (DEBUG_PLUGINS);
1021 
1022 	window = PLUMA_WINDOW (plugin->priv->window);
1023 
1024 	active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
1025 
1026 	pluma_debug_message (DEBUG_PLUGINS, active ? "Auto Spell activated" : "Auto Spell deactivated");
1027 
1028 	doc = pluma_window_get_active_document (window);
1029 	if (doc == NULL)
1030 		return;
1031 
1032 	if (get_autocheck_type (plugin) == AUTOCHECK_DOCUMENT)
1033 	{
1034 		pluma_document_set_metadata (doc,
1035 				     PLUMA_METADATA_ATTRIBUTE_SPELL_ENABLED,
1036 				     active ? "1" : NULL, NULL);
1037 	}
1038 
1039 	set_auto_spell (window, doc, active);
1040 }
1041 
1042 static void
update_ui(PlumaSpellPlugin * plugin)1043 update_ui (PlumaSpellPlugin *plugin)
1044 {
1045 	PlumaSpellPluginPrivate *data;
1046 	PlumaWindow *window;
1047 	PlumaDocument *doc;
1048 	PlumaView *view;
1049 	gboolean autospell;
1050 	GtkAction *action;
1051 
1052 	pluma_debug (DEBUG_PLUGINS);
1053 
1054 	data = plugin->priv;
1055 	window = PLUMA_WINDOW (data->window);
1056 	doc = pluma_window_get_active_document (window);
1057 	view = pluma_window_get_active_view (window);
1058 
1059 	autospell = (doc != NULL &&
1060 	             pluma_automatic_spell_checker_get_from_document (doc) != NULL);
1061 
1062 	if (doc != NULL)
1063 	{
1064 		PlumaTab *tab;
1065 		PlumaTabState state;
1066 
1067 		tab = pluma_window_get_active_tab (window);
1068 		state = pluma_tab_get_state (tab);
1069 
1070 		/* If the document is loading we can't get the metadata so we
1071 		   endup with an useless speller */
1072 		if (state == PLUMA_TAB_STATE_NORMAL)
1073 		{
1074 			action = gtk_action_group_get_action (data->action_group,
1075 							      "AutoSpell");
1076 
1077 			g_signal_handlers_block_by_func (action, auto_spell_cb,
1078 							 plugin);
1079 			set_auto_spell (window, doc, autospell);
1080 			gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action),
1081 						      autospell);
1082 			g_signal_handlers_unblock_by_func (action, auto_spell_cb,
1083 							   plugin);
1084 		}
1085 	}
1086 
1087 	gtk_action_group_set_sensitive (data->action_group,
1088 					(view != NULL) &&
1089 					gtk_text_view_get_editable (GTK_TEXT_VIEW (view)));
1090 }
1091 
1092 static void
set_auto_spell_from_metadata(PlumaSpellPlugin * plugin,PlumaDocument * doc,GtkActionGroup * action_group)1093 set_auto_spell_from_metadata (PlumaSpellPlugin *plugin,
1094 			      PlumaDocument  *doc,
1095 			      GtkActionGroup *action_group)
1096 {
1097 	gboolean active = FALSE;
1098 	gchar *active_str = NULL;
1099 	PlumaWindow *window;
1100 	PlumaDocument *active_doc;
1101 	PlumaSpellPluginAutocheckType autocheck_type;
1102 
1103 	autocheck_type = get_autocheck_type (plugin);
1104 
1105 	switch (autocheck_type)
1106 	{
1107 		case AUTOCHECK_ALWAYS:
1108 		{
1109 			active = TRUE;
1110 			break;
1111 		}
1112 		case AUTOCHECK_DOCUMENT:
1113 		{
1114 			active_str = pluma_document_get_metadata (doc,
1115 						  PLUMA_METADATA_ATTRIBUTE_SPELL_ENABLED);
1116 			break;
1117 		}
1118 		case AUTOCHECK_NEVER:
1119 		default:
1120 			active = FALSE;
1121 			break;
1122 	}
1123 
1124 	if (active_str)
1125 	{
1126 		active = *active_str == '1';
1127 
1128 		g_free (active_str);
1129 	}
1130 
1131 	window = PLUMA_WINDOW (plugin->priv->window);
1132 
1133 	set_auto_spell (window, doc, active);
1134 
1135 	/* In case that the doc is the active one we mark the spell action */
1136 	active_doc = pluma_window_get_active_document (window);
1137 
1138 	if (active_doc == doc && action_group != NULL)
1139 	{
1140 		GtkAction *action;
1141 
1142 		action = gtk_action_group_get_action (action_group,
1143 						      "AutoSpell");
1144 
1145 		g_signal_handlers_block_by_func (action, auto_spell_cb,
1146 						 plugin);
1147 		gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action),
1148 					      active);
1149 		g_signal_handlers_unblock_by_func (action, auto_spell_cb,
1150 						   plugin);
1151 	}
1152 }
1153 
1154 static void
on_document_loaded(PlumaDocument * doc,const GError * error,PlumaSpellPlugin * plugin)1155 on_document_loaded (PlumaDocument *doc,
1156 		    const GError  *error,
1157 		    PlumaSpellPlugin *plugin)
1158 {
1159 	if (error == NULL)
1160 	{
1161 		PlumaSpellChecker *spell;
1162 
1163 		spell = PLUMA_SPELL_CHECKER (g_object_get_qdata (G_OBJECT (doc),
1164 								 spell_checker_id));
1165 		if (spell != NULL)
1166 		{
1167 			set_language_from_metadata (spell, doc);
1168 		}
1169 
1170 		set_auto_spell_from_metadata (plugin, doc, plugin->priv->action_group);
1171 	}
1172 }
1173 
1174 static void
on_document_saved(PlumaDocument * doc,const GError * error,PlumaSpellPlugin * plugin)1175 on_document_saved (PlumaDocument *doc,
1176 		   const GError  *error,
1177 		   PlumaSpellPlugin *plugin)
1178 {
1179 	PlumaAutomaticSpellChecker *autospell;
1180 	PlumaSpellChecker *spell;
1181 	const gchar *key;
1182 
1183 	if (error != NULL)
1184 	{
1185 		return;
1186 	}
1187 
1188 	/* Make sure to save the metadata here too */
1189 	autospell = pluma_automatic_spell_checker_get_from_document (doc);
1190 	spell = PLUMA_SPELL_CHECKER (g_object_get_qdata (G_OBJECT (doc), spell_checker_id));
1191 
1192 	if (spell != NULL)
1193 	{
1194 		key = pluma_spell_checker_language_to_key (pluma_spell_checker_get_language (spell));
1195 	}
1196 	else
1197 	{
1198 		key = NULL;
1199 	}
1200 
1201 	if (get_autocheck_type (plugin) == AUTOCHECK_DOCUMENT)
1202 	{
1203 
1204 		pluma_document_set_metadata (doc,
1205 				PLUMA_METADATA_ATTRIBUTE_SPELL_ENABLED,
1206 				autospell != NULL ? "1" : NULL,
1207 				PLUMA_METADATA_ATTRIBUTE_SPELL_LANGUAGE,
1208 				key,
1209 				NULL);
1210 	}
1211 	else
1212 	{
1213 		pluma_document_set_metadata (doc,
1214 				PLUMA_METADATA_ATTRIBUTE_SPELL_LANGUAGE,
1215 				key,
1216 				NULL);
1217 	}
1218 }
1219 
1220 static void
tab_added_cb(PlumaWindow * window,PlumaTab * tab,PlumaSpellPlugin * plugin)1221 tab_added_cb (PlumaWindow *window,
1222 	      PlumaTab    *tab,
1223 	      PlumaSpellPlugin *plugin)
1224 {
1225 	PlumaDocument *doc;
1226 	gchar *uri;
1227 
1228 	doc = pluma_tab_get_document (tab);
1229 
1230 	g_object_get(G_OBJECT(doc), "uri", &uri, NULL);
1231 
1232 	if (!uri)
1233 	{
1234 		set_auto_spell_from_metadata (plugin, doc, plugin->priv->action_group);
1235 
1236 		g_free(uri);
1237 	}
1238 
1239 	g_signal_connect (doc, "loaded",
1240 			  G_CALLBACK (on_document_loaded),
1241 			  plugin);
1242 
1243 	g_signal_connect (doc, "saved",
1244 			  G_CALLBACK (on_document_saved),
1245 			  plugin);
1246 }
1247 
1248 static void
tab_removed_cb(PlumaWindow * window,PlumaTab * tab,PlumaSpellPlugin * plugin)1249 tab_removed_cb (PlumaWindow *window,
1250 		PlumaTab    *tab,
1251 		PlumaSpellPlugin *plugin)
1252 {
1253 	PlumaDocument *doc;
1254 
1255 	doc = pluma_tab_get_document (tab);
1256 
1257 	g_signal_handlers_disconnect_by_func (doc, on_document_loaded, plugin);
1258 	g_signal_handlers_disconnect_by_func (doc, on_document_saved, plugin);
1259 }
1260 
1261 static void
pluma_spell_plugin_activate(PlumaWindowActivatable * activatable)1262 pluma_spell_plugin_activate (PlumaWindowActivatable *activatable)
1263 {
1264 	PlumaSpellPlugin *plugin;
1265 	PlumaSpellPluginPrivate *data;
1266 	PlumaWindow *window;
1267 	GtkUIManager *manager;
1268 	GList *docs, *l;
1269 
1270 	pluma_debug (DEBUG_PLUGINS);
1271 
1272 	plugin = PLUMA_SPELL_PLUGIN (activatable);
1273 	data = plugin->priv;
1274 	window = PLUMA_WINDOW (data->window);
1275 
1276 	manager = pluma_window_get_ui_manager (window);
1277 
1278 	data->action_group = gtk_action_group_new ("PlumaSpellPluginActions");
1279 	gtk_action_group_set_translation_domain (data->action_group,
1280 						 GETTEXT_PACKAGE);
1281 	gtk_action_group_add_actions (data->action_group,
1282 					   action_entries,
1283 					   G_N_ELEMENTS (action_entries),
1284 					   plugin);
1285 	gtk_action_group_add_toggle_actions (data->action_group,
1286 					     toggle_action_entries,
1287 					     G_N_ELEMENTS (toggle_action_entries),
1288 					     plugin);
1289 
1290 	gtk_ui_manager_insert_action_group (manager, data->action_group, -1);
1291 
1292 	data->ui_id = gtk_ui_manager_new_merge_id (manager);
1293 
1294 	data->message_cid = gtk_statusbar_get_context_id
1295 			(GTK_STATUSBAR (pluma_window_get_statusbar (window)),
1296 			 "spell_plugin_message");
1297 
1298 	gtk_ui_manager_add_ui (manager,
1299 			       data->ui_id,
1300 			       MENU_PATH,
1301 			       "CheckSpell",
1302 			       "CheckSpell",
1303 			       GTK_UI_MANAGER_MENUITEM,
1304 			       FALSE);
1305 
1306 	gtk_ui_manager_add_ui (manager,
1307 			       data->ui_id,
1308 			       MENU_PATH,
1309 			       "AutoSpell",
1310 			       "AutoSpell",
1311 			       GTK_UI_MANAGER_MENUITEM,
1312 			       FALSE);
1313 
1314 	gtk_ui_manager_add_ui (manager,
1315 			       data->ui_id,
1316 			       MENU_PATH,
1317 			       "ConfigSpell",
1318 			       "ConfigSpell",
1319 			       GTK_UI_MANAGER_MENUITEM,
1320 			       FALSE);
1321 
1322 	update_ui (plugin);
1323 
1324 	docs = pluma_window_get_documents (window);
1325 	for (l = docs; l != NULL; l = g_list_next (l))
1326 	{
1327 		PlumaDocument *doc = PLUMA_DOCUMENT (l->data);
1328 
1329 		set_auto_spell_from_metadata (plugin, doc,
1330 					      data->action_group);
1331 
1332 		g_signal_handlers_disconnect_by_func (doc,
1333 		                                      on_document_loaded,
1334 		                                      plugin);
1335 
1336 		g_signal_handlers_disconnect_by_func (doc,
1337 		                                      on_document_saved,
1338 		                                      plugin);
1339 	}
1340 
1341 	data->tab_added_id =
1342 		g_signal_connect (window, "tab-added",
1343 				  G_CALLBACK (tab_added_cb), plugin);
1344 	data->tab_removed_id =
1345 		g_signal_connect (window, "tab-removed",
1346 				  G_CALLBACK (tab_removed_cb), plugin);
1347 }
1348 
1349 static void
pluma_spell_plugin_deactivate(PlumaWindowActivatable * activatable)1350 pluma_spell_plugin_deactivate (PlumaWindowActivatable *activatable)
1351 {
1352 	PlumaSpellPluginPrivate *data;
1353 	PlumaWindow *window;
1354 	GtkUIManager *manager;
1355 
1356 	pluma_debug (DEBUG_PLUGINS);
1357 
1358 	data = PLUMA_SPELL_PLUGIN (activatable)->priv;
1359 	window = PLUMA_WINDOW (data->window);
1360 
1361 	manager = pluma_window_get_ui_manager (window);
1362 
1363 	gtk_ui_manager_remove_ui (manager, data->ui_id);
1364 	gtk_ui_manager_remove_action_group (manager, data->action_group);
1365 
1366 	g_signal_handler_disconnect (window, data->tab_added_id);
1367 	g_signal_handler_disconnect (window, data->tab_removed_id);
1368 }
1369 
1370 static void
pluma_spell_plugin_update_state(PlumaWindowActivatable * activatable)1371 pluma_spell_plugin_update_state (PlumaWindowActivatable *activatable)
1372 {
1373 	pluma_debug (DEBUG_PLUGINS);
1374 
1375 	update_ui (PLUMA_SPELL_PLUGIN (activatable));
1376 }
1377 
1378 static void
pluma_spell_plugin_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)1379 pluma_spell_plugin_set_property (GObject      *object,
1380                                  guint         prop_id,
1381                                  const GValue *value,
1382                                  GParamSpec   *pspec)
1383 {
1384 	PlumaSpellPlugin *plugin = PLUMA_SPELL_PLUGIN (object);
1385 
1386 	switch (prop_id)
1387 	{
1388 		case PROP_WINDOW:
1389 			plugin->priv->window = PLUMA_WINDOW (g_value_dup_object (value));
1390 			break;
1391 
1392 		default:
1393 			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1394 			break;
1395 	}
1396 }
1397 
1398 static void
pluma_spell_plugin_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)1399 pluma_spell_plugin_get_property (GObject    *object,
1400                                  guint       prop_id,
1401                                  GValue     *value,
1402                                  GParamSpec *pspec)
1403 {
1404 	PlumaSpellPlugin *plugin = PLUMA_SPELL_PLUGIN (object);
1405 
1406 	switch (prop_id)
1407 	{
1408 		case PROP_WINDOW:
1409 			g_value_set_object (value, plugin->priv->window);
1410 			break;
1411 
1412 		default:
1413 			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1414 			break;
1415 	}
1416 }
1417 
1418 static GtkWidget *
pluma_spell_plugin_create_configure_widget(PeasGtkConfigurable * configurable)1419 pluma_spell_plugin_create_configure_widget (PeasGtkConfigurable *configurable)
1420 {
1421 	SpellConfigureDialog *dialog;
1422 
1423 	dialog = get_configure_dialog (PLUMA_SPELL_PLUGIN (configurable));
1424 
1425 	g_signal_connect (dialog->always,
1426 	                  "toggled",
1427 	                  G_CALLBACK (configure_dialog_button_toggled),
1428 	                  dialog);
1429 	g_signal_connect (dialog->document,
1430 	                  "toggled",
1431 	                  G_CALLBACK (configure_dialog_button_toggled),
1432 	                  dialog);
1433 	g_signal_connect (dialog->never,
1434 	                  "toggled",
1435 	                  G_CALLBACK (configure_dialog_button_toggled),
1436 	                  dialog);
1437 
1438 	g_signal_connect (dialog->content,
1439 	                  "destroy",
1440 	                  G_CALLBACK (configure_dialog_destroyed),
1441 	                  dialog);
1442 
1443 	return dialog->content;
1444 }
1445 
1446 static void
pluma_spell_plugin_class_init(PlumaSpellPluginClass * klass)1447 pluma_spell_plugin_class_init (PlumaSpellPluginClass *klass)
1448 {
1449 	GObjectClass *object_class = G_OBJECT_CLASS (klass);
1450 
1451 	object_class->dispose = pluma_spell_plugin_dispose;
1452 	object_class->set_property = pluma_spell_plugin_set_property;
1453 	object_class->get_property = pluma_spell_plugin_get_property;
1454 
1455 	g_object_class_override_property (object_class, PROP_WINDOW, "window");
1456 
1457 	if (spell_checker_id == 0)
1458 		spell_checker_id = g_quark_from_string ("PlumaSpellCheckerID");
1459 
1460 	if (check_range_id == 0)
1461 		check_range_id = g_quark_from_string ("CheckRangeID");
1462 }
1463 
1464 static void
pluma_spell_plugin_class_finalize(PlumaSpellPluginClass * klass)1465 pluma_spell_plugin_class_finalize (PlumaSpellPluginClass *klass)
1466 {
1467 	/* dummy function - used by G_DEFINE_DYNAMIC_TYPE_EXTENDED */
1468 }
1469 
1470 static void
pluma_window_activatable_iface_init(PlumaWindowActivatableInterface * iface)1471 pluma_window_activatable_iface_init (PlumaWindowActivatableInterface *iface)
1472 {
1473 	iface->activate = pluma_spell_plugin_activate;
1474 	iface->deactivate = pluma_spell_plugin_deactivate;
1475 	iface->update_state = pluma_spell_plugin_update_state;
1476 }
1477 
1478 static void
peas_gtk_configurable_iface_init(PeasGtkConfigurableInterface * iface)1479 peas_gtk_configurable_iface_init (PeasGtkConfigurableInterface *iface)
1480 {
1481 	iface->create_configure_widget = pluma_spell_plugin_create_configure_widget;
1482 }
1483 
1484 G_MODULE_EXPORT void
peas_register_types(PeasObjectModule * module)1485 peas_register_types (PeasObjectModule *module)
1486 {
1487 	pluma_spell_plugin_register_type (G_TYPE_MODULE (module));
1488 
1489 	peas_object_module_register_extension_type (module,
1490 	                                            PLUMA_TYPE_WINDOW_ACTIVATABLE,
1491 	                                            PLUMA_TYPE_SPELL_PLUGIN);
1492 
1493 	peas_object_module_register_extension_type (module,
1494 	                                            PEAS_GTK_TYPE_CONFIGURABLE,
1495 	                                            PLUMA_TYPE_SPELL_PLUGIN);
1496 }
1497