1 /*
2  * gedit-encodings-dialog.c
3  * This file is part of gedit
4  *
5  * Copyright (C) 2002-2005 Paolo Maggi
6  * Copyright (C) 2015 Sébastien Wilmet
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, see <http://www.gnu.org/licenses/>.
20  */
21 
22 #include "config.h"
23 
24 #include "gedit-encodings-dialog.h"
25 
26 #include <string.h>
27 #include <glib/gi18n.h>
28 #include <gtksourceview/gtksource.h>
29 
30 #include "gedit-settings.h"
31 
32 typedef enum _State
33 {
34 	STATE_UNMODIFIED,
35 	STATE_MODIFIED,
36 	STATE_RESET
37 } State;
38 
39 struct _GeditEncodingsDialog
40 {
41 	GtkDialog parent_instance;
42 
43 	GSettings *enc_settings;
44 
45 	/* Available encodings */
46 	GtkListStore *liststore_available;
47 	GtkTreeModelSort *sort_available;
48 	GtkTreeView *treeview_available;
49 	GtkWidget *add_button;
50 
51 	/* Chosen encodings */
52 	GtkListStore *liststore_chosen;
53 	GtkTreeView *treeview_chosen;
54 	GtkWidget *remove_button;
55 	GtkWidget *up_button;
56 	GtkWidget *down_button;
57 	GtkWidget *reset_button;
58 
59 	State state;
60 };
61 
62 enum
63 {
64 	COLUMN_NAME,
65 	COLUMN_CHARSET,
66 	COLUMN_ENCODING,
67 	N_COLUMNS
68 };
69 
G_DEFINE_TYPE(GeditEncodingsDialog,gedit_encodings_dialog,GTK_TYPE_DIALOG)70 G_DEFINE_TYPE (GeditEncodingsDialog, gedit_encodings_dialog, GTK_TYPE_DIALOG)
71 
72 static void
73 set_modified (GeditEncodingsDialog *dialog)
74 {
75 	dialog->state = STATE_MODIFIED;
76 	gtk_widget_set_sensitive (dialog->reset_button, TRUE);
77 }
78 
79 static void
append_encoding(GtkListStore * liststore,const GtkSourceEncoding * encoding)80 append_encoding (GtkListStore            *liststore,
81 		 const GtkSourceEncoding *encoding)
82 {
83 	GtkTreeIter iter;
84 
85 	gtk_list_store_append (liststore, &iter);
86 	gtk_list_store_set (liststore, &iter,
87 			    COLUMN_NAME, gtk_source_encoding_get_name (encoding),
88 			    COLUMN_ENCODING, encoding,
89 			    -1);
90 
91 	if (encoding == gtk_source_encoding_get_current ())
92 	{
93 		gchar *charset = g_strdup_printf (_("%s (Current Locale)"),
94 						  gtk_source_encoding_get_charset (encoding));
95 
96 		gtk_list_store_set (liststore, &iter,
97 				    COLUMN_CHARSET, charset,
98 				    -1);
99 
100 		g_free (charset);
101 	}
102 	else
103 	{
104 		gtk_list_store_set (liststore, &iter,
105 				    COLUMN_CHARSET, gtk_source_encoding_get_charset (encoding),
106 				    -1);
107 	}
108 }
109 
110 static void
init_liststores(GeditEncodingsDialog * dialog,gboolean reset)111 init_liststores (GeditEncodingsDialog *dialog,
112 		 gboolean              reset)
113 {
114 	gboolean default_candidates;
115 	GSList *chosen_encodings;
116 	GSList *all_encodings;
117 	GSList *l;
118 
119 	/* Chosen encodings */
120 
121 	if (reset)
122 	{
123 		chosen_encodings = gtk_source_encoding_get_default_candidates ();
124 		default_candidates = TRUE;
125 	}
126 	else
127 	{
128 		chosen_encodings = gedit_settings_get_candidate_encodings (&default_candidates);
129 	}
130 
131 	gtk_widget_set_sensitive (dialog->reset_button, !default_candidates);
132 
133 	for (l = chosen_encodings; l != NULL; l = l->next)
134 	{
135 		const GtkSourceEncoding *cur_encoding = l->data;
136 		append_encoding (dialog->liststore_chosen, cur_encoding);
137 	}
138 
139 	/* Available encodings */
140 
141 	all_encodings = gtk_source_encoding_get_all ();
142 
143 	for (l = chosen_encodings; l != NULL; l = l->next)
144 	{
145 		const GtkSourceEncoding *chosen_encoding = l->data;
146 		all_encodings = g_slist_remove (all_encodings, chosen_encoding);
147 	}
148 
149 	for (l = all_encodings; l != NULL; l = l->next)
150 	{
151 		const GtkSourceEncoding *cur_encoding = l->data;
152 		append_encoding (dialog->liststore_available, cur_encoding);
153 	}
154 
155 	g_slist_free (chosen_encodings);
156 	g_slist_free (all_encodings);
157 }
158 
159 static void
reset_dialog_response_cb(GtkDialog * msg_dialog,gint response,GeditEncodingsDialog * dialog)160 reset_dialog_response_cb (GtkDialog            *msg_dialog,
161 			  gint                  response,
162 			  GeditEncodingsDialog *dialog)
163 {
164 	if (response == GTK_RESPONSE_ACCEPT)
165 	{
166 		gtk_list_store_clear (dialog->liststore_available);
167 		gtk_list_store_clear (dialog->liststore_chosen);
168 
169 		init_liststores (dialog, TRUE);
170 		dialog->state = STATE_RESET;
171 	}
172 
173 	gtk_widget_destroy (GTK_WIDGET (msg_dialog));
174 }
175 
176 static void
reset_button_clicked_cb(GtkWidget * button,GeditEncodingsDialog * dialog)177 reset_button_clicked_cb (GtkWidget            *button,
178 			 GeditEncodingsDialog *dialog)
179 {
180 	GtkDialog *msg_dialog;
181 
182 	msg_dialog = GTK_DIALOG (gtk_message_dialog_new (GTK_WINDOW (dialog),
183 							 GTK_DIALOG_DESTROY_WITH_PARENT |
184 							 GTK_DIALOG_MODAL,
185 							 GTK_MESSAGE_QUESTION,
186 							 GTK_BUTTONS_NONE,
187 							 "%s",
188 							 _("Do you really want to reset the "
189 							   "character encodings’ preferences?")));
190 
191 	gtk_dialog_add_buttons (msg_dialog,
192 				_("_Cancel"), GTK_RESPONSE_CANCEL,
193 				_("_Reset"), GTK_RESPONSE_ACCEPT,
194 				NULL);
195 
196 	g_signal_connect (msg_dialog,
197 			  "response",
198 			  G_CALLBACK (reset_dialog_response_cb),
199 			  dialog);
200 
201 	gtk_widget_show_all (GTK_WIDGET (msg_dialog));
202 }
203 
204 static GSList *
get_chosen_encodings_list(GeditEncodingsDialog * dialog)205 get_chosen_encodings_list (GeditEncodingsDialog *dialog)
206 {
207 	GtkTreeModel *model = GTK_TREE_MODEL (dialog->liststore_chosen);
208 	GtkTreeIter iter;
209 	gboolean iter_set;
210 	GSList *ret = NULL;
211 
212 	iter_set = gtk_tree_model_get_iter_first (model, &iter);
213 
214 	while (iter_set)
215 	{
216 		const GtkSourceEncoding *encoding = NULL;
217 
218 		gtk_tree_model_get (model, &iter,
219 				    COLUMN_ENCODING, &encoding,
220 				    -1);
221 
222 		ret = g_slist_prepend (ret, (gpointer)encoding);
223 
224 		iter_set = gtk_tree_model_iter_next (model, &iter);
225 	}
226 
227 	return g_slist_reverse (ret);
228 }
229 
230 static gchar **
encoding_list_to_strv(const GSList * enc_list)231 encoding_list_to_strv (const GSList *enc_list)
232 {
233 	GSList *l;
234 	GPtrArray *array;
235 
236 	array = g_ptr_array_sized_new (g_slist_length ((GSList *)enc_list) + 1);
237 
238 	for (l = (GSList *)enc_list; l != NULL; l = l->next)
239 	{
240 		const GtkSourceEncoding *enc = l->data;
241 		const gchar *charset = gtk_source_encoding_get_charset (enc);
242 
243 		g_return_val_if_fail (charset != NULL, NULL);
244 
245 		g_ptr_array_add (array, g_strdup (charset));
246 	}
247 
248 	g_ptr_array_add (array, NULL);
249 
250 	return (gchar **)g_ptr_array_free (array, FALSE);
251 }
252 
253 static void
apply_settings(GeditEncodingsDialog * dialog)254 apply_settings (GeditEncodingsDialog *dialog)
255 {
256 	switch (dialog->state)
257 	{
258 		case STATE_MODIFIED:
259 		{
260 			GSList *enc_list;
261 			gchar **enc_strv;
262 
263 			enc_list = get_chosen_encodings_list (dialog);
264 			enc_strv = encoding_list_to_strv (enc_list);
265 
266 			g_settings_set_strv (dialog->enc_settings,
267 					     GEDIT_SETTINGS_CANDIDATE_ENCODINGS,
268 					     (const gchar * const *)enc_strv);
269 
270 			g_slist_free (enc_list);
271 			g_strfreev (enc_strv);
272 			break;
273 		}
274 
275 		case STATE_RESET:
276 			g_settings_reset (dialog->enc_settings,
277 					  GEDIT_SETTINGS_CANDIDATE_ENCODINGS);
278 			break;
279 
280 		case STATE_UNMODIFIED:
281 			/* Do nothing. */
282 			break;
283 
284 		default:
285 			g_assert_not_reached ();
286 
287 	}
288 }
289 
290 static void
gedit_encodings_dialog_response(GtkDialog * gtk_dialog,gint response_id)291 gedit_encodings_dialog_response (GtkDialog *gtk_dialog,
292                                  gint       response_id)
293 {
294 	GeditEncodingsDialog *dialog = GEDIT_ENCODINGS_DIALOG (gtk_dialog);
295 
296 	switch (response_id)
297 	{
298 		case GTK_RESPONSE_APPLY:
299 			apply_settings (dialog);
300 			break;
301 
302 		case GTK_RESPONSE_CANCEL:
303 		default:
304 			/* Do nothing */
305 			break;
306 	}
307 }
308 
309 static void
gedit_encodings_dialog_dispose(GObject * object)310 gedit_encodings_dialog_dispose (GObject *object)
311 {
312 	GeditEncodingsDialog *dialog = GEDIT_ENCODINGS_DIALOG (object);
313 
314 	g_clear_object (&dialog->enc_settings);
315 	g_clear_object (&dialog->add_button);
316 	g_clear_object (&dialog->remove_button);
317 	g_clear_object (&dialog->up_button);
318 	g_clear_object (&dialog->down_button);
319 	g_clear_object (&dialog->reset_button);
320 
321 	G_OBJECT_CLASS (gedit_encodings_dialog_parent_class)->dispose (object);
322 }
323 
324 static void
gedit_encodings_dialog_class_init(GeditEncodingsDialogClass * klass)325 gedit_encodings_dialog_class_init (GeditEncodingsDialogClass *klass)
326 {
327 	GObjectClass *object_class = G_OBJECT_CLASS (klass);
328 	GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
329 	GtkDialogClass *dialog_class = GTK_DIALOG_CLASS (klass);
330 
331 	object_class->dispose = gedit_encodings_dialog_dispose;
332 
333 	dialog_class->response = gedit_encodings_dialog_response;
334 
335 	/* Bind class to template */
336 	gtk_widget_class_set_template_from_resource (widget_class,
337 	                                             "/org/gnome/gedit/ui/gedit-encodings-dialog.ui");
338 	gtk_widget_class_bind_template_child (widget_class, GeditEncodingsDialog, liststore_available);
339 	gtk_widget_class_bind_template_child (widget_class, GeditEncodingsDialog, liststore_chosen);
340 	gtk_widget_class_bind_template_child (widget_class, GeditEncodingsDialog, sort_available);
341 	gtk_widget_class_bind_template_child (widget_class, GeditEncodingsDialog, treeview_available);
342 	gtk_widget_class_bind_template_child (widget_class, GeditEncodingsDialog, treeview_chosen);
343 	gtk_widget_class_bind_template_child_full (widget_class, "scrolledwindow_available", FALSE, 0);
344 	gtk_widget_class_bind_template_child_full (widget_class, "scrolledwindow_chosen", FALSE, 0);
345 	gtk_widget_class_bind_template_child_full (widget_class, "toolbar_available", FALSE, 0);
346 	gtk_widget_class_bind_template_child_full (widget_class, "toolbar_chosen", FALSE, 0);
347 }
348 
349 static void
update_add_button_sensitivity(GeditEncodingsDialog * dialog)350 update_add_button_sensitivity (GeditEncodingsDialog *dialog)
351 {
352 	GtkTreeSelection *selection;
353 	gint count;
354 
355 	selection = gtk_tree_view_get_selection (dialog->treeview_available);
356 	count = gtk_tree_selection_count_selected_rows (selection);
357 	gtk_widget_set_sensitive (dialog->add_button, count > 0);
358 }
359 
360 static void
update_remove_button_sensitivity(GeditEncodingsDialog * dialog)361 update_remove_button_sensitivity (GeditEncodingsDialog *dialog)
362 {
363 	const GtkSourceEncoding *utf8_encoding;
364 	const GtkSourceEncoding *current_encoding;
365 	GtkTreeSelection *selection;
366 	GtkTreeModel *model;
367 	GList *selected_rows;
368 	GList *l;
369 	gboolean sensitive;
370 
371 	utf8_encoding = gtk_source_encoding_get_utf8 ();
372 	current_encoding = gtk_source_encoding_get_current ();
373 
374 	selection = gtk_tree_view_get_selection (dialog->treeview_chosen);
375 
376 	selected_rows = gtk_tree_selection_get_selected_rows (selection, &model);
377 	g_return_if_fail (model == GTK_TREE_MODEL (dialog->liststore_chosen));
378 
379 	sensitive = FALSE;
380 	for (l = selected_rows; l != NULL; l = l->next)
381 	{
382 		GtkTreePath *path = l->data;
383 		GtkTreeIter iter;
384 		const GtkSourceEncoding *encoding = NULL;
385 
386 		if (!gtk_tree_model_get_iter (model, &iter, path))
387 		{
388 			g_warning ("Remove button: invalid path");
389 			continue;
390 		}
391 
392 		gtk_tree_model_get (model, &iter,
393 				    COLUMN_ENCODING, &encoding,
394 				    -1);
395 
396 		/* If at least one encoding other than UTF-8 or current is
397 		 * selected, set the Remove button as sensitive. But if UTF-8 or
398 		 * current is selected, it won't be removed. So Ctrl+A works
399 		 * fine to remove (almost) all encodings in one go.
400 		 */
401 		if (encoding != utf8_encoding &&
402 		    encoding != current_encoding)
403 		{
404 			sensitive = TRUE;
405 			break;
406 		}
407 	}
408 
409 	gtk_widget_set_sensitive (dialog->remove_button, sensitive);
410 
411 	g_list_free_full (selected_rows, (GDestroyNotify) gtk_tree_path_free);
412 }
413 
414 static void
update_up_down_buttons_sensitivity(GeditEncodingsDialog * dialog)415 update_up_down_buttons_sensitivity (GeditEncodingsDialog *dialog)
416 {
417 	GtkTreeSelection *selection;
418 	gint count;
419 	GList *selected_rows;
420 	GtkTreeModel *model;
421 	GtkTreePath *path;
422 	gint *indices;
423 	gint depth;
424 	gint items_count;
425 	gboolean first_item_selected;
426 	gboolean last_item_selected;
427 
428 	selection = gtk_tree_view_get_selection (dialog->treeview_chosen);
429 	count = gtk_tree_selection_count_selected_rows (selection);
430 
431 	if (count != 1)
432 	{
433 		gtk_widget_set_sensitive (dialog->up_button, FALSE);
434 		gtk_widget_set_sensitive (dialog->down_button, FALSE);
435 		return;
436 	}
437 
438 	selected_rows = gtk_tree_selection_get_selected_rows (selection, &model);
439 	g_assert (g_list_length (selected_rows) == 1);
440 
441 	path = selected_rows->data;
442 	indices = gtk_tree_path_get_indices_with_depth (path, &depth);
443 	g_assert (depth == 1);
444 
445 	items_count = gtk_tree_model_iter_n_children (model, NULL);
446 
447 	first_item_selected = indices[0] == 0;
448 	last_item_selected = indices[0] == (items_count - 1);
449 
450 	gtk_widget_set_sensitive (dialog->up_button, !first_item_selected);
451 	gtk_widget_set_sensitive (dialog->down_button, !last_item_selected);
452 
453 	g_list_free_full (selected_rows, (GDestroyNotify) gtk_tree_path_free);
454 }
455 
456 static void
update_chosen_buttons_sensitivity(GeditEncodingsDialog * dialog)457 update_chosen_buttons_sensitivity (GeditEncodingsDialog *dialog)
458 {
459 	update_remove_button_sensitivity (dialog);
460 	update_up_down_buttons_sensitivity (dialog);
461 }
462 
463 /* Removes all @paths from @orig, and append them at the end of @dest in the
464  * same order.
465  */
466 static void
transfer_encodings(GList * paths,GtkListStore * orig,GtkListStore * dest)467 transfer_encodings (GList        *paths,
468 		    GtkListStore *orig,
469 		    GtkListStore *dest)
470 {
471 	GtkTreeModel *model_orig = GTK_TREE_MODEL (orig);
472 	GList *refs = NULL;
473 	GList *l;
474 
475 	for (l = paths; l != NULL; l = l->next)
476 	{
477 		GtkTreePath *path = l->data;
478 		refs = g_list_prepend (refs, gtk_tree_row_reference_new (model_orig, path));
479 	}
480 
481 	refs = g_list_reverse (refs);
482 
483 	for (l = refs; l != NULL; l = l->next)
484 	{
485 		GtkTreeRowReference *ref = l->data;
486 		GtkTreePath *path;
487 		GtkTreeIter iter;
488 		const GtkSourceEncoding *encoding = NULL;
489 
490 		path = gtk_tree_row_reference_get_path (ref);
491 
492 		if (!gtk_tree_model_get_iter (model_orig, &iter, path))
493 		{
494 			gtk_tree_path_free (path);
495 			g_warning ("Remove encoding: invalid path");
496 			continue;
497 		}
498 
499 		/* Transfer encoding */
500 		gtk_tree_model_get (model_orig, &iter,
501 				    COLUMN_ENCODING, &encoding,
502 				    -1);
503 
504 		append_encoding (dest, encoding);
505 
506 		gtk_list_store_remove (orig, &iter);
507 
508 		gtk_tree_path_free (path);
509 	}
510 
511 	g_list_free_full (refs, (GDestroyNotify) gtk_tree_row_reference_free);
512 }
513 
514 static void
add_button_clicked_cb(GtkWidget * button,GeditEncodingsDialog * dialog)515 add_button_clicked_cb (GtkWidget            *button,
516 		       GeditEncodingsDialog *dialog)
517 {
518 	GtkTreeSelection *selection;
519 	GtkTreeModel *model;
520 	GList *filter_paths;
521 	GList *children_paths = NULL;
522 	GList *l;
523 
524 	selection = gtk_tree_view_get_selection (dialog->treeview_available);
525 	filter_paths = gtk_tree_selection_get_selected_rows (selection, &model);
526 
527 	g_return_if_fail (model == GTK_TREE_MODEL (dialog->sort_available));
528 
529 	for (l = filter_paths; l != NULL; l = l->next)
530 	{
531 		GtkTreePath *filter_path = l->data;
532 		GtkTreePath *child_path;
533 
534 		child_path = gtk_tree_model_sort_convert_path_to_child_path (dialog->sort_available,
535 		                                                             filter_path);
536 
537 		children_paths = g_list_prepend (children_paths, child_path);
538 	}
539 
540 	children_paths = g_list_reverse (children_paths);
541 
542 	transfer_encodings (children_paths,
543 	                    dialog->liststore_available,
544 	                    dialog->liststore_chosen);
545 
546 	set_modified (dialog);
547 
548 	/* For the treeview_available, it's more natural to unselect the added
549 	 * encodings.
550 	 * Note that when removing encodings from treeview_chosen, it is
551 	 * desirable to keep a selection, so we can remove several elements in a
552 	 * row (if the user doesn't know that several elements can be selected
553 	 * at once with Ctrl or Shift).
554 	 */
555 	gtk_tree_selection_unselect_all (selection);
556 
557 	g_list_free_full (filter_paths, (GDestroyNotify) gtk_tree_path_free);
558 	g_list_free_full (children_paths, (GDestroyNotify) gtk_tree_path_free);
559 }
560 
561 static void
remove_button_clicked_cb(GtkWidget * button,GeditEncodingsDialog * dialog)562 remove_button_clicked_cb (GtkWidget            *button,
563 			  GeditEncodingsDialog *dialog)
564 {
565 	const GtkSourceEncoding *utf8_encoding;
566 	const GtkSourceEncoding *current_encoding;
567 	GtkTreeSelection *selection;
568 	GtkTreeModel *model;
569 	GList *selected_rows;
570 	GList *to_remove = NULL;
571 	GList *l;
572 
573 	utf8_encoding = gtk_source_encoding_get_utf8 ();
574 	current_encoding = gtk_source_encoding_get_current ();
575 
576 	selection = gtk_tree_view_get_selection (dialog->treeview_chosen);
577 	selected_rows = gtk_tree_selection_get_selected_rows (selection, &model);
578 
579 	g_return_if_fail (model == GTK_TREE_MODEL (dialog->liststore_chosen));
580 
581 	/* Ensure that UTF-8 and the current locale encodings cannot be removed. */
582 	for (l = selected_rows; l != NULL; l = l->next)
583 	{
584 		GtkTreePath *path = l->data;
585 		GtkTreeIter iter;
586 		const GtkSourceEncoding *encoding = NULL;
587 
588 		if (!gtk_tree_model_get_iter (model, &iter, path))
589 		{
590 			gtk_tree_path_free (path);
591 			g_warning ("Remove button: invalid path");
592 			continue;
593 		}
594 
595 		gtk_tree_model_get (model, &iter,
596 				    COLUMN_ENCODING, &encoding,
597 				    -1);
598 
599 		if (encoding == utf8_encoding ||
600 		    encoding == current_encoding)
601 		{
602 			gtk_tree_path_free (path);
603 		}
604 		else
605 		{
606 			to_remove = g_list_prepend (to_remove, path);
607 		}
608 	}
609 
610 	to_remove = g_list_reverse (to_remove);
611 
612 	transfer_encodings (to_remove,
613 	                    dialog->liststore_chosen,
614 	                    dialog->liststore_available);
615 
616 	set_modified (dialog);
617 
618 	g_list_free (selected_rows);
619 	g_list_free_full (to_remove, (GDestroyNotify) gtk_tree_path_free);
620 }
621 
622 static void
up_button_clicked_cb(GtkWidget * button,GeditEncodingsDialog * dialog)623 up_button_clicked_cb (GtkWidget            *button,
624 		      GeditEncodingsDialog *dialog)
625 {
626 	GtkTreeSelection *selection;
627 	GtkTreeModel *model;
628 	GList *selected_rows;
629 	GtkTreePath *path;
630 	GtkTreeIter iter;
631 	GtkTreeIter prev_iter;
632 
633 	selection = gtk_tree_view_get_selection (dialog->treeview_chosen);
634 	selected_rows = gtk_tree_selection_get_selected_rows (selection, &model);
635 
636 	g_return_if_fail (model == GTK_TREE_MODEL (dialog->liststore_chosen));
637 	g_return_if_fail (g_list_length (selected_rows) == 1);
638 
639 	path = selected_rows->data;
640 	if (!gtk_tree_model_get_iter (model, &iter, path))
641 	{
642 		g_return_if_reached ();
643 	}
644 
645 	prev_iter = iter;
646 	if (!gtk_tree_model_iter_previous (model, &prev_iter))
647 	{
648 		g_return_if_reached ();
649 	}
650 
651 	gtk_list_store_move_before (dialog->liststore_chosen,
652 	                            &iter,
653 	                            &prev_iter);
654 
655 	set_modified (dialog);
656 
657 	update_chosen_buttons_sensitivity (dialog);
658 
659 	g_list_free_full (selected_rows, (GDestroyNotify) gtk_tree_path_free);
660 }
661 
662 static void
down_button_clicked_cb(GtkWidget * button,GeditEncodingsDialog * dialog)663 down_button_clicked_cb (GtkWidget            *button,
664 			GeditEncodingsDialog *dialog)
665 {
666 	GtkTreeSelection *selection;
667 	GtkTreeModel *model;
668 	GList *selected_rows;
669 	GtkTreePath *path;
670 	GtkTreeIter iter;
671 	GtkTreeIter next_iter;
672 
673 	selection = gtk_tree_view_get_selection (dialog->treeview_chosen);
674 	selected_rows = gtk_tree_selection_get_selected_rows (selection, &model);
675 
676 	g_return_if_fail (model == GTK_TREE_MODEL (dialog->liststore_chosen));
677 	g_return_if_fail (g_list_length (selected_rows) == 1);
678 
679 	path = selected_rows->data;
680 	if (!gtk_tree_model_get_iter (model, &iter, path))
681 	{
682 		g_return_if_reached ();
683 	}
684 
685 	next_iter = iter;
686 	if (!gtk_tree_model_iter_next (model, &next_iter))
687 	{
688 		g_return_if_reached ();
689 	}
690 
691 	gtk_list_store_move_after (dialog->liststore_chosen,
692 	                           &iter,
693 	                           &next_iter);
694 
695 	set_modified (dialog);
696 
697 	update_chosen_buttons_sensitivity (dialog);
698 
699 	g_list_free_full (selected_rows, (GDestroyNotify) gtk_tree_path_free);
700 }
701 
702 static void
init_toolbar_available(GeditEncodingsDialog * dialog)703 init_toolbar_available (GeditEncodingsDialog *dialog)
704 {
705 	GtkWidget *scrolled_window;
706 	GtkToolbar *toolbar;
707 	GtkStyleContext *context;
708 
709 	scrolled_window = GTK_WIDGET (gtk_widget_get_template_child (GTK_WIDGET (dialog),
710 								     GEDIT_TYPE_ENCODINGS_DIALOG,
711 								     "scrolledwindow_available"));
712 
713 	toolbar = GTK_TOOLBAR (gtk_widget_get_template_child (GTK_WIDGET (dialog),
714 							      GEDIT_TYPE_ENCODINGS_DIALOG,
715 							      "toolbar_available"));
716 
717 	context = gtk_widget_get_style_context (scrolled_window);
718 	gtk_style_context_set_junction_sides (context, GTK_JUNCTION_BOTTOM);
719 
720 	context = gtk_widget_get_style_context (GTK_WIDGET (toolbar));
721 	gtk_style_context_set_junction_sides (context, GTK_JUNCTION_TOP);
722 	gtk_style_context_add_class (context, GTK_STYLE_CLASS_INLINE_TOOLBAR);
723 
724 	/* Add button */
725 	dialog->add_button = GTK_WIDGET (gtk_tool_button_new (NULL, NULL));
726 	g_object_ref_sink (dialog->add_button);
727 
728 	gtk_tool_button_set_icon_name (GTK_TOOL_BUTTON (dialog->add_button), "list-add-symbolic");
729 	gtk_tool_item_set_tooltip_text (GTK_TOOL_ITEM (dialog->add_button), _("Add"));
730 
731 	gtk_toolbar_insert (toolbar, GTK_TOOL_ITEM (dialog->add_button), -1);
732 
733 	g_signal_connect_object (dialog->add_button,
734 				 "clicked",
735 				 G_CALLBACK (add_button_clicked_cb),
736 				 dialog,
737 				 0);
738 
739 	gtk_widget_show_all (GTK_WIDGET (toolbar));
740 }
741 
742 static void
init_toolbar_chosen(GeditEncodingsDialog * dialog)743 init_toolbar_chosen (GeditEncodingsDialog *dialog)
744 {
745 	GtkWidget *scrolled_window;
746 	GtkToolbar *toolbar;
747 	GtkStyleContext *context;
748 	GtkWidget *left_box;
749 	GtkWidget *right_box;
750 	GtkToolItem *left_group;
751 	GtkToolItem *right_group;
752 	GtkToolItem *separator;
753 
754 	scrolled_window = GTK_WIDGET (gtk_widget_get_template_child (GTK_WIDGET (dialog),
755 								     GEDIT_TYPE_ENCODINGS_DIALOG,
756 								     "scrolledwindow_chosen"));
757 
758 	toolbar = GTK_TOOLBAR (gtk_widget_get_template_child (GTK_WIDGET (dialog),
759 							      GEDIT_TYPE_ENCODINGS_DIALOG,
760 							      "toolbar_chosen"));
761 
762 	context = gtk_widget_get_style_context (scrolled_window);
763 	gtk_style_context_set_junction_sides (context, GTK_JUNCTION_BOTTOM);
764 
765 	context = gtk_widget_get_style_context (GTK_WIDGET (toolbar));
766 	gtk_style_context_set_junction_sides (context, GTK_JUNCTION_TOP);
767 	gtk_style_context_add_class (context, GTK_STYLE_CLASS_INLINE_TOOLBAR);
768 
769 	/* Remove button */
770 	dialog->remove_button = gtk_button_new_from_icon_name ("list-remove-symbolic", GTK_ICON_SIZE_SMALL_TOOLBAR);
771 	g_object_ref_sink (dialog->remove_button);
772 	gtk_widget_set_tooltip_text (dialog->remove_button, _("Remove"));
773 
774 	g_signal_connect_object (dialog->remove_button,
775 				 "clicked",
776 				 G_CALLBACK (remove_button_clicked_cb),
777 				 dialog,
778 				 0);
779 
780 	/* Up button */
781 	dialog->up_button = gtk_button_new_from_icon_name ("go-up-symbolic", GTK_ICON_SIZE_SMALL_TOOLBAR);
782 	g_object_ref_sink (dialog->up_button);
783 	gtk_widget_set_tooltip_text (dialog->up_button, _("Move to a higher priority"));
784 
785 	g_signal_connect_object (dialog->up_button,
786 				 "clicked",
787 				 G_CALLBACK (up_button_clicked_cb),
788 				 dialog,
789 				 0);
790 
791 	/* Down button */
792 	dialog->down_button = gtk_button_new_from_icon_name ("go-down-symbolic", GTK_ICON_SIZE_SMALL_TOOLBAR);
793 	g_object_ref_sink (dialog->down_button);
794 	gtk_widget_set_tooltip_text (dialog->down_button, _("Move to a lower priority"));
795 
796 	g_signal_connect_object (dialog->down_button,
797 				 "clicked",
798 				 G_CALLBACK (down_button_clicked_cb),
799 				 dialog,
800 				 0);
801 
802 	/* Left group (with a trick for rounded borders) */
803 	left_box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
804 	left_group = gtk_tool_item_new ();
805 	gtk_box_pack_start (GTK_BOX (left_box), dialog->remove_button, FALSE, FALSE, 0);
806 	gtk_box_pack_start (GTK_BOX (left_box), dialog->up_button, FALSE, FALSE, 0);
807 	gtk_box_pack_start (GTK_BOX (left_box), dialog->down_button, FALSE, FALSE, 0);
808 	gtk_container_add (GTK_CONTAINER (left_group), left_box);
809 	gtk_toolbar_insert (toolbar, left_group, -1);
810 
811 	/* Separator */
812 	separator = gtk_separator_tool_item_new ();
813 	gtk_separator_tool_item_set_draw (GTK_SEPARATOR_TOOL_ITEM (separator), FALSE);
814 	gtk_tool_item_set_expand (separator, TRUE);
815 	gtk_toolbar_insert (toolbar, separator, -1);
816 
817 	/* Reset button */
818 	dialog->reset_button = gtk_button_new_with_mnemonic (_("_Reset"));
819 	g_object_ref_sink (dialog->reset_button);
820 
821 	g_signal_connect_object (dialog->reset_button,
822 				 "clicked",
823 				 G_CALLBACK (reset_button_clicked_cb),
824 				 dialog,
825 				 0);
826 
827 	/* Right group */
828 	right_box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
829 	right_group = gtk_tool_item_new ();
830 	gtk_box_pack_start (GTK_BOX (right_box), dialog->reset_button, FALSE, FALSE, 0);
831 	gtk_container_add (GTK_CONTAINER (right_group), right_box);
832 	gtk_toolbar_insert (toolbar, right_group, -1);
833 
834 	gtk_widget_show_all (GTK_WIDGET (toolbar));
835 }
836 
837 static void
gedit_encodings_dialog_init(GeditEncodingsDialog * dialog)838 gedit_encodings_dialog_init (GeditEncodingsDialog *dialog)
839 {
840 	GtkTreeSelection *selection;
841 
842 	dialog->enc_settings = g_settings_new ("org.gnome.gedit.preferences.encodings");
843 
844 	gtk_widget_init_template (GTK_WIDGET (dialog));
845 
846 	init_toolbar_available (dialog);
847 	init_toolbar_chosen (dialog);
848 	init_liststores (dialog, FALSE);
849 	dialog->state = STATE_UNMODIFIED;
850 
851 	/* Available encodings */
852 	gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (dialog->sort_available),
853 					      COLUMN_NAME,
854 					      GTK_SORT_ASCENDING);
855 
856 	selection = gtk_tree_view_get_selection (dialog->treeview_available);
857 
858 	g_signal_connect_swapped (selection,
859 				  "changed",
860 				  G_CALLBACK (update_add_button_sensitivity),
861 				  dialog);
862 
863 	update_add_button_sensitivity (dialog);
864 
865 	/* Chosen encodings */
866 	selection = gtk_tree_view_get_selection (dialog->treeview_chosen);
867 
868 	g_signal_connect_swapped (selection,
869 				  "changed",
870 				  G_CALLBACK (update_chosen_buttons_sensitivity),
871 				  dialog);
872 
873 	update_chosen_buttons_sensitivity (dialog);
874 }
875 
876 GtkWidget *
gedit_encodings_dialog_new(void)877 gedit_encodings_dialog_new (void)
878 {
879 	return g_object_new (GEDIT_TYPE_ENCODINGS_DIALOG,
880 			     "use-header-bar", TRUE,
881 			     NULL);
882 }
883 
884 /* ex:set ts=8 noet: */
885