1 /*
2  * e-html-editor-spell-dialog.c
3  *
4  * Copyright (C) 2012 Dan Vrátil <dvratil@redhat.com>
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of version 2 of the GNU Lesser General Public
8  * License as published by the Free Software Foundation.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this program; if not, write to the
17  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18  * Boston, MA 02111-1307, USA.
19  */
20 
21 #include "evolution-config.h"
22 
23 #include "e-html-editor-spell-check-dialog.h"
24 
25 #include <glib/gi18n-lib.h>
26 #include <enchant.h>
27 
28 #include "e-spell-checker.h"
29 #include "e-spell-dictionary.h"
30 
31 #include "e-dialog-widgets.h"
32 
33 #define E_HTML_EDITOR_SPELL_CHECK_DIALOG_GET_PRIVATE(obj) \
34 	(G_TYPE_INSTANCE_GET_PRIVATE \
35 	((obj), E_TYPE_HTML_EDITOR_SPELL_CHECK_DIALOG, EHTMLEditorSpellCheckDialogPrivate))
36 
37 struct _EHTMLEditorSpellCheckDialogPrivate {
38 	GtkWidget *add_word_button;
39 	GtkWidget *back_button;
40 	GtkWidget *dictionary_combo;
41 	GtkWidget *ignore_button;
42 	GtkWidget *replace_button;
43 	GtkWidget *replace_all_button;
44 	GtkWidget *skip_button;
45 	GtkWidget *suggestion_label;
46 	GtkWidget *tree_view;
47 
48 	gchar *word;
49 	ESpellDictionary *current_dict;
50 };
51 
52 enum {
53 	COLUMN_NAME,
54 	COLUMN_DICTIONARY,
55 	NUM_COLUMNS
56 };
57 
G_DEFINE_TYPE(EHTMLEditorSpellCheckDialog,e_html_editor_spell_check_dialog,E_TYPE_HTML_EDITOR_DIALOG)58 G_DEFINE_TYPE (
59 	EHTMLEditorSpellCheckDialog,
60 	e_html_editor_spell_check_dialog,
61 	E_TYPE_HTML_EDITOR_DIALOG)
62 
63 static void
64 html_editor_spell_check_dialog_set_word (EHTMLEditorSpellCheckDialog *dialog,
65                                          const gchar *word)
66 {
67 	EHTMLEditor *editor;
68 	EContentEditor *cnt_editor;
69 	GtkTreeView *tree_view;
70 	GtkListStore *store;
71 	gchar *markup;
72 	GList *list, *link;
73 	gboolean empty;
74 
75 	if (word == NULL)
76 		return;
77 
78 	if (dialog->priv->word != word) {
79 		g_free (dialog->priv->word);
80 		dialog->priv->word = g_strdup (word);
81 	}
82 
83 	markup = g_strdup_printf (_("<b>Suggestions for “%s”</b>"), word);
84 	gtk_label_set_markup (
85 		GTK_LABEL (dialog->priv->suggestion_label), markup);
86 	g_free (markup);
87 
88 	tree_view = GTK_TREE_VIEW (dialog->priv->tree_view);
89 	store = GTK_LIST_STORE (gtk_tree_view_get_model (tree_view));
90 	gtk_list_store_clear (store);
91 
92 	list = e_spell_dictionary_get_suggestions (
93 		dialog->priv->current_dict, word, -1);
94 
95 	empty = list == NULL;
96 
97 	for (link = list; link != NULL; link = g_list_next (link)) {
98 		GtkTreeIter iter;
99 		gchar *suggestion = link->data;
100 
101 		gtk_list_store_append (store, &iter);
102 		gtk_list_store_set (store, &iter, 0, suggestion, -1);
103 	}
104 
105 	gtk_widget_set_sensitive (dialog->priv->replace_button, !empty);
106 	gtk_widget_set_sensitive (dialog->priv->replace_all_button, !empty);
107 
108 	if (!empty) {
109 		GtkTreeSelection *tree_selection;
110 
111 		/* Select the first suggestion */
112 		tree_selection = gtk_tree_view_get_selection (
113 			GTK_TREE_VIEW (dialog->priv->tree_view));
114 		gtk_tree_selection_select_path (tree_selection, gtk_tree_path_new_first ());
115 	}
116 
117 	g_list_free_full (list, (GDestroyNotify) g_free);
118 
119 	/* We give focus to WebKit so that the currently selected word
120 	 * is highlited. Without focus selection is not visible (at
121 	 * least with my default color scheme). The focus in fact is not
122 	 * given to WebKit, because this dialog is modal, but it satisfies
123 	 * it in a way that it paints the selection :) */
124 	editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog));
125 	cnt_editor = e_html_editor_get_content_editor (editor);
126 	gtk_widget_grab_focus (GTK_WIDGET (cnt_editor));
127 }
128 
129 static gboolean
html_editor_spell_check_dialog_next(EHTMLEditorSpellCheckDialog * dialog)130 html_editor_spell_check_dialog_next (EHTMLEditorSpellCheckDialog *dialog)
131 {
132 	EHTMLEditor *editor;
133 	EContentEditor *cnt_editor;
134 	gchar *next_word;
135 
136 	editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog));
137 	cnt_editor = e_html_editor_get_content_editor (editor);
138 
139 	next_word = e_content_editor_spell_check_next_word (cnt_editor, dialog->priv->word);
140 	if (next_word && *next_word) {
141 		html_editor_spell_check_dialog_set_word (dialog, next_word);
142 		g_free (next_word);
143 		return TRUE;
144 	}
145 	g_free (next_word);
146 
147 	/* Close the dialog */
148 	gtk_widget_hide (GTK_WIDGET (dialog));
149 	return FALSE;
150 }
151 
152 static gboolean
html_editor_spell_check_dialog_prev(EHTMLEditorSpellCheckDialog * dialog)153 html_editor_spell_check_dialog_prev (EHTMLEditorSpellCheckDialog *dialog)
154 {
155 	EHTMLEditor *editor;
156 	EContentEditor *cnt_editor;
157 	gchar *prev_word;
158 
159 	editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog));
160 	cnt_editor = e_html_editor_get_content_editor (editor);
161 
162 	prev_word = e_content_editor_spell_check_prev_word (cnt_editor, dialog->priv->word);
163 	if (prev_word && *prev_word) {
164 		html_editor_spell_check_dialog_set_word (dialog, prev_word);
165 		g_free (prev_word);
166 		return TRUE;
167 	}
168 	g_free (prev_word);
169 
170 	/* Close the dialog */
171 	gtk_widget_hide (GTK_WIDGET (dialog));
172 	return FALSE;
173 }
174 
175 static gboolean
html_editor_spell_check_dialog_next_idle_cb(gpointer user_data)176 html_editor_spell_check_dialog_next_idle_cb (gpointer user_data)
177 {
178 	EHTMLEditorSpellCheckDialog *dialog = user_data;
179 
180 	g_return_val_if_fail (E_IS_HTML_EDITOR_SPELL_CHECK_DIALOG (dialog), FALSE);
181 
182 	html_editor_spell_check_dialog_next (dialog);
183 	g_object_unref (dialog);
184 
185 	return FALSE;
186 }
187 
188 static void
html_editor_spell_check_dialog_replace(EHTMLEditorSpellCheckDialog * dialog)189 html_editor_spell_check_dialog_replace (EHTMLEditorSpellCheckDialog *dialog)
190 {
191 	EHTMLEditor *editor;
192 	EContentEditor *cnt_editor;
193 	GtkTreeModel *model;
194 	GtkTreeSelection *selection;
195 	GtkTreeIter iter;
196 	gchar *replacement;
197 
198 	editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog));
199 	cnt_editor = e_html_editor_get_content_editor (editor);
200 
201 	selection = gtk_tree_view_get_selection (
202 		GTK_TREE_VIEW (dialog->priv->tree_view));
203 	if (!gtk_tree_selection_get_selected (selection, &model, &iter))
204 		return;
205 	gtk_tree_model_get (model, &iter, 0, &replacement, -1);
206 
207 	e_content_editor_replace (cnt_editor, replacement);
208 
209 	g_free (replacement);
210 
211 	g_idle_add (html_editor_spell_check_dialog_next_idle_cb, g_object_ref (dialog));
212 }
213 
214 static void
html_editor_spell_check_dialog_replace_all(EHTMLEditorSpellCheckDialog * dialog)215 html_editor_spell_check_dialog_replace_all (EHTMLEditorSpellCheckDialog *dialog)
216 {
217 	EHTMLEditor *editor;
218 	EContentEditor *cnt_editor;
219 	GtkTreeModel *model;
220 	GtkTreeSelection *selection;
221 	GtkTreeIter iter;
222 	gchar *replacement;
223 
224 	selection = gtk_tree_view_get_selection (
225 		GTK_TREE_VIEW (dialog->priv->tree_view));
226 	if (!gtk_tree_selection_get_selected (selection, &model, &iter))
227 		return;
228 	gtk_tree_model_get (model, &iter, 0, &replacement, -1);
229 
230 	editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog));
231 	cnt_editor = e_html_editor_get_content_editor (editor);
232 
233 	e_content_editor_replace_all (
234 		cnt_editor,
235 		E_CONTENT_EDITOR_FIND_CASE_INSENSITIVE |
236 		E_CONTENT_EDITOR_FIND_WRAP_AROUND,
237 		dialog->priv->word,
238 		replacement);
239 
240 	g_free (replacement);
241 
242 	g_idle_add (html_editor_spell_check_dialog_next_idle_cb, g_object_ref (dialog));
243 }
244 
245 static void
html_editor_spell_check_dialog_ignore(EHTMLEditorSpellCheckDialog * dialog)246 html_editor_spell_check_dialog_ignore (EHTMLEditorSpellCheckDialog *dialog)
247 {
248 	if (dialog->priv->word == NULL)
249 		return;
250 	e_spell_dictionary_ignore_word (
251 		dialog->priv->current_dict, dialog->priv->word, -1);
252 
253 	html_editor_spell_check_dialog_next (dialog);
254 }
255 
256 static void
html_editor_spell_check_dialog_learn(EHTMLEditorSpellCheckDialog * dialog)257 html_editor_spell_check_dialog_learn (EHTMLEditorSpellCheckDialog *dialog)
258 {
259 	if (dialog->priv->word == NULL)
260 		return;
261 
262 	e_spell_dictionary_learn_word (dialog->priv->current_dict, dialog->priv->word, -1);
263 
264 	html_editor_spell_check_dialog_next (dialog);
265 }
266 
267 static void
html_editor_spell_check_dialog_set_dictionary(EHTMLEditorSpellCheckDialog * dialog)268 html_editor_spell_check_dialog_set_dictionary (EHTMLEditorSpellCheckDialog *dialog)
269 {
270 	GtkComboBox *combo_box;
271 	GtkTreeModel *model;
272 	GtkTreeIter iter;
273 	ESpellDictionary *dictionary;
274 
275 	combo_box = GTK_COMBO_BOX (dialog->priv->dictionary_combo);
276 	if (gtk_combo_box_get_active_iter (combo_box, &iter)) {
277 		model = gtk_combo_box_get_model (combo_box);
278 
279 		gtk_tree_model_get (model, &iter, 1, &dictionary, -1);
280 
281 		dialog->priv->current_dict = dictionary;
282 
283 		/* Update suggestions */
284 		html_editor_spell_check_dialog_set_word (dialog, dialog->priv->word);
285 	}
286 }
287 
288 static void
html_editor_spell_check_dialog_show(GtkWidget * widget)289 html_editor_spell_check_dialog_show (GtkWidget *widget)
290 {
291 	EHTMLEditor *editor;
292 	EContentEditor *cnt_editor;
293 	EHTMLEditorSpellCheckDialog *dialog;
294 
295 	dialog = E_HTML_EDITOR_SPELL_CHECK_DIALOG (widget);
296 	editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog));
297 	cnt_editor = e_html_editor_get_content_editor (editor);
298 
299 	g_free (dialog->priv->word);
300 	dialog->priv->word = NULL;
301 
302 	e_content_editor_on_dialog_open (cnt_editor, E_CONTENT_EDITOR_DIALOG_SPELLCHECK);
303 
304 	/* Select the first word or quit */
305 	if (html_editor_spell_check_dialog_next (dialog)) {
306 		GTK_WIDGET_CLASS (e_html_editor_spell_check_dialog_parent_class)->show (widget);
307 	} else {
308 		e_content_editor_on_dialog_close (cnt_editor, E_CONTENT_EDITOR_DIALOG_SPELLCHECK);
309 	}
310 }
311 
312 static void
html_editor_spell_check_dialog_hide(GtkWidget * widget)313 html_editor_spell_check_dialog_hide (GtkWidget *widget)
314 {
315 	EContentEditor *cnt_editor;
316 	EHTMLEditor *editor;
317 	EHTMLEditorSpellCheckDialog *dialog = E_HTML_EDITOR_SPELL_CHECK_DIALOG (widget);
318 
319 	editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog));
320 	cnt_editor = e_html_editor_get_content_editor (editor);
321 
322 	e_content_editor_on_dialog_close (cnt_editor, E_CONTENT_EDITOR_DIALOG_SPELLCHECK);
323 
324 	/* Chain up to parent implementation */
325 	GTK_WIDGET_CLASS (e_html_editor_spell_check_dialog_parent_class)->hide (widget);
326 }
327 
328 static void
html_editor_spell_check_dialog_finalize(GObject * object)329 html_editor_spell_check_dialog_finalize (GObject *object)
330 {
331 	EHTMLEditorSpellCheckDialogPrivate *priv;
332 
333 	priv = E_HTML_EDITOR_SPELL_CHECK_DIALOG_GET_PRIVATE (object);
334 
335 	g_free (priv->word);
336 
337 	/* Chain up to parent's finalize() method. */
338 	G_OBJECT_CLASS (e_html_editor_spell_check_dialog_parent_class)->finalize (object);
339 }
340 
341 static void
html_editor_spell_check_dialog_constructed(GObject * object)342 html_editor_spell_check_dialog_constructed (GObject *object)
343 {
344 	EHTMLEditorSpellCheckDialog *dialog;
345 
346 	/* Chain up to parent's constructed() method. */
347 	G_OBJECT_CLASS (e_html_editor_spell_check_dialog_parent_class)->constructed (object);
348 
349 	dialog = E_HTML_EDITOR_SPELL_CHECK_DIALOG (object);
350 
351 	e_html_editor_spell_check_dialog_update_dictionaries (dialog);
352 }
353 
354 static void
e_html_editor_spell_check_dialog_class_init(EHTMLEditorSpellCheckDialogClass * class)355 e_html_editor_spell_check_dialog_class_init (EHTMLEditorSpellCheckDialogClass *class)
356 {
357 	GtkWidgetClass *widget_class;
358 	GObjectClass *object_class;
359 
360 	g_type_class_add_private (
361 		class, sizeof (EHTMLEditorSpellCheckDialogPrivate));
362 
363 	object_class = G_OBJECT_CLASS (class);
364 	object_class->finalize = html_editor_spell_check_dialog_finalize;
365 	object_class->constructed = html_editor_spell_check_dialog_constructed;
366 
367 	widget_class = GTK_WIDGET_CLASS (class);
368 	widget_class->show = html_editor_spell_check_dialog_show;
369 	widget_class->hide = html_editor_spell_check_dialog_hide;
370 }
371 
372 static void
e_html_editor_spell_check_dialog_init(EHTMLEditorSpellCheckDialog * dialog)373 e_html_editor_spell_check_dialog_init (EHTMLEditorSpellCheckDialog *dialog)
374 {
375 	GtkWidget *widget;
376 	GtkGrid *main_layout;
377 	GtkListStore *store;
378 	GtkTreeViewColumn *column;
379 	GtkCellRenderer *renderer;
380 
381 	dialog->priv = E_HTML_EDITOR_SPELL_CHECK_DIALOG_GET_PRIVATE (dialog);
382 
383 	main_layout = e_html_editor_dialog_get_container (E_HTML_EDITOR_DIALOG (dialog));
384 
385 	/* == Suggestions == */
386 	widget = gtk_label_new ("");
387 	gtk_label_set_markup (GTK_LABEL (widget), _("<b>Suggestions</b>"));
388 	gtk_misc_set_alignment (GTK_MISC (widget), 0, 0.5);
389 	gtk_grid_attach (main_layout, widget, 0, 0, 2, 1);
390 	dialog->priv->suggestion_label = widget;
391 
392 	/* Tree view */
393 	widget = gtk_tree_view_new ();
394 	gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (widget), FALSE);
395 	gtk_widget_set_vexpand (widget, TRUE);
396 	gtk_widget_set_hexpand (widget, TRUE);
397 	dialog->priv->tree_view = widget;
398 
399 	/* Column */
400 	column = gtk_tree_view_column_new_with_attributes (
401 		"", gtk_cell_renderer_text_new (), "text", 0, NULL);
402 	gtk_tree_view_append_column (GTK_TREE_VIEW (widget), column);
403 
404 	/* Store */
405 	store = gtk_list_store_new (1, G_TYPE_STRING);
406 	gtk_tree_view_set_model (
407 		GTK_TREE_VIEW (widget), GTK_TREE_MODEL (store));
408 
409 	/* Scrolled Window */
410 	widget = gtk_scrolled_window_new (NULL, NULL);
411 	gtk_widget_set_size_request (widget, 150, -1);
412 	gtk_scrolled_window_set_policy (
413 		GTK_SCROLLED_WINDOW (widget),
414 		GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
415 	gtk_scrolled_window_set_shadow_type (
416 		GTK_SCROLLED_WINDOW (widget),
417 		GTK_SHADOW_ETCHED_IN);
418 	gtk_container_add (GTK_CONTAINER (widget), dialog->priv->tree_view);
419 	gtk_grid_attach (main_layout, widget, 0, 1, 1, 5);
420 
421 	/* Replace */
422 	widget = e_dialog_button_new_with_icon ("edit-find-replace", _("Replace"));
423 	gtk_grid_attach (main_layout, widget, 1, 1, 1, 1);
424 	dialog->priv->replace_button = widget;
425 
426 	g_signal_connect_swapped (
427 		widget, "clicked",
428 		G_CALLBACK (html_editor_spell_check_dialog_replace), dialog);
429 
430 	/* Replace All */
431 	widget = gtk_button_new_with_mnemonic (_("Replace All"));
432 	gtk_grid_attach (main_layout, widget, 1, 2, 1, 1);
433 	dialog->priv->replace_all_button = widget;
434 
435 	g_signal_connect_swapped (
436 		widget, "clicked",
437 		G_CALLBACK (html_editor_spell_check_dialog_replace_all), dialog);
438 
439 	/* Ignore */
440 	widget = e_dialog_button_new_with_icon ("edit-clear", _("Ignore"));
441 	gtk_grid_attach (main_layout, widget, 1, 3, 1, 1);
442 	dialog->priv->ignore_button = widget;
443 
444 	g_signal_connect_swapped (
445 		widget, "clicked",
446 		G_CALLBACK (html_editor_spell_check_dialog_ignore), dialog);
447 
448 	/* Skip */
449 	widget = e_dialog_button_new_with_icon ("go-next", _("Skip"));
450 	gtk_grid_attach (main_layout, widget, 1, 4, 1, 1);
451 	dialog->priv->skip_button = widget;
452 
453 	g_signal_connect_swapped (
454 		widget, "clicked",
455 		G_CALLBACK (html_editor_spell_check_dialog_next), dialog);
456 
457 	/* Back */
458 	widget = e_dialog_button_new_with_icon ("go-previous", _("Back"));
459 	gtk_grid_attach (main_layout, widget, 1, 5, 1, 1);
460 
461 	g_signal_connect_swapped (
462 		widget, "clicked",
463 		G_CALLBACK (html_editor_spell_check_dialog_prev), dialog);
464 
465 	/* Dictionary label */
466 	widget = gtk_label_new ("");
467 	gtk_label_set_markup (GTK_LABEL (widget), _("<b>Dictionary</b>"));
468 	gtk_misc_set_alignment (GTK_MISC (widget), 0, 0);
469 	gtk_grid_attach (main_layout, widget, 0, 6, 2, 1);
470 
471 	/* Dictionaries combo */
472 	widget = gtk_combo_box_new ();
473 	gtk_grid_attach (main_layout, widget, 0, 7, 1, 1);
474 	dialog->priv->dictionary_combo = widget;
475 
476 	renderer = gtk_cell_renderer_text_new ();
477 	gtk_cell_layout_pack_start (
478 		GTK_CELL_LAYOUT (widget), renderer, TRUE);
479 	gtk_cell_layout_add_attribute (
480 		GTK_CELL_LAYOUT (widget), renderer, "text", 0);
481 
482 	g_signal_connect_swapped (
483 		widget, "changed",
484 		G_CALLBACK (html_editor_spell_check_dialog_set_dictionary), dialog);
485 
486 	/* Add Word button */
487 	widget = e_dialog_button_new_with_icon ("list-add", _("Add word"));
488 	gtk_grid_attach (main_layout, widget, 1, 7, 1, 1);
489 	dialog->priv->add_word_button = widget;
490 
491 	g_signal_connect_swapped (
492 		widget, "clicked",
493 		G_CALLBACK (html_editor_spell_check_dialog_learn), dialog);
494 
495 	gtk_widget_show_all (GTK_WIDGET (main_layout));
496 }
497 
498 GtkWidget *
e_html_editor_spell_check_dialog_new(EHTMLEditor * editor)499 e_html_editor_spell_check_dialog_new (EHTMLEditor *editor)
500 {
501 	return g_object_new (
502 		E_TYPE_HTML_EDITOR_SPELL_CHECK_DIALOG,
503 		"editor", editor,
504 		"title", _("Spell Checking"),
505 		NULL);
506 }
507 
508 void
e_html_editor_spell_check_dialog_update_dictionaries(EHTMLEditorSpellCheckDialog * dialog)509 e_html_editor_spell_check_dialog_update_dictionaries (EHTMLEditorSpellCheckDialog *dialog)
510 {
511 	EHTMLEditor *editor;
512 	EContentEditor *cnt_editor;
513 	ESpellChecker *spell_checker;
514 	GtkComboBox *combo_box;
515 	GtkListStore *store = NULL;
516 	GQueue queue = G_QUEUE_INIT;
517 	gchar **languages;
518 	guint n_languages = 0;
519 	guint ii;
520 
521 	g_return_if_fail (E_IS_HTML_EDITOR_SPELL_CHECK_DIALOG (dialog));
522 
523 	editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog));
524 	cnt_editor = e_html_editor_get_content_editor (editor);
525 	spell_checker = e_content_editor_ref_spell_checker (cnt_editor);
526 
527 	languages = e_spell_checker_list_active_languages (
528 		spell_checker, &n_languages);
529 	for (ii = 0; ii < n_languages; ii++) {
530 		ESpellDictionary *dictionary;
531 
532 		dictionary = e_spell_checker_ref_dictionary (
533 			spell_checker, languages[ii]);
534 		if (dictionary != NULL)
535 			g_queue_push_tail (&queue, dictionary);
536 		else
537 			g_warning (
538 				"%s: No '%s' dictionary found",
539 				G_STRFUNC, languages[ii]);
540 	}
541 	g_strfreev (languages);
542 
543 	/* Populate a list store for the combo box. */
544 	store = gtk_list_store_new (
545 		NUM_COLUMNS,
546 		G_TYPE_STRING,			/* COLUMN_NAME */
547 		E_TYPE_SPELL_DICTIONARY);	/* COLUMN_DICTIONARY */
548 
549 	while (!g_queue_is_empty (&queue)) {
550 		ESpellDictionary *dictionary;
551 		GtkTreeIter iter;
552 		const gchar *name = NULL;
553 
554 		dictionary = g_queue_pop_head (&queue);
555 		name = e_spell_dictionary_get_name (dictionary);
556 
557 		gtk_list_store_append (store, &iter);
558 		gtk_list_store_set (
559 			store, &iter,
560 			COLUMN_NAME, name,
561 			COLUMN_DICTIONARY, dictionary,
562 			-1);
563 
564 		g_object_unref (dictionary);
565 	}
566 
567 	/* FIXME Try to restore selection. */
568 	combo_box = GTK_COMBO_BOX (dialog->priv->dictionary_combo);
569 	gtk_combo_box_set_model (combo_box, GTK_TREE_MODEL (store));
570 	gtk_combo_box_set_active (combo_box, 0);
571 
572 	g_object_unref (store);
573 	g_clear_object (&spell_checker);
574 }
575 
576