1 /*
2  * pluma-encodings-dialog.c
3  * This file is part of pluma
4  *
5  * Copyright (C) 2002-2005 Paolo Maggi
6  * Copyright (C) 2012-2021 MATE Developers
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, write to the Free Software
20  * Foundation, Inc., 51 Franklin St, Fifth Floor,
21  * Boston, MA 02110-1301, USA.
22  */
23 
24 /*
25  * Modified by the pluma Team, 2002-2005. See the AUTHORS file for a
26  * list of people on the pluma Team.
27  * See the ChangeLog files for a list of changes.
28  *
29  * $Id$
30  */
31 
32 #ifdef HAVE_CONFIG_H
33 #include <config.h>
34 #endif
35 
36 #include <string.h>
37 
38 #include <glib.h>
39 #include <glib/gi18n.h>
40 #include <gtk/gtk.h>
41 
42 #include "pluma-encodings-dialog.h"
43 #include "pluma-encodings.h"
44 #include "pluma-utils.h"
45 #include "pluma-debug.h"
46 #include "pluma-help.h"
47 #include "pluma-dirs.h"
48 #include "pluma-settings.h"
49 
50 struct _PlumaEncodingsDialogPrivate
51 {
52 	GSettings	*enc_settings;
53 
54 	GtkListStore	*available_liststore;
55 	GtkListStore	*displayed_liststore;
56 	GtkWidget	*available_treeview;
57 	GtkWidget	*displayed_treeview;
58 	GtkWidget	*add_button;
59 	GtkWidget	*remove_button;
60 
61 	GSList		*show_in_menu_list;
62 };
63 
G_DEFINE_TYPE_WITH_PRIVATE(PlumaEncodingsDialog,pluma_encodings_dialog,GTK_TYPE_DIALOG)64 G_DEFINE_TYPE_WITH_PRIVATE (PlumaEncodingsDialog, pluma_encodings_dialog, GTK_TYPE_DIALOG)
65 
66 static void
67 pluma_encodings_dialog_finalize (GObject *object)
68 {
69 	PlumaEncodingsDialogPrivate *priv = pluma_encodings_dialog_get_instance_private (PLUMA_ENCODINGS_DIALOG(object));
70 
71 	g_slist_free (priv->show_in_menu_list);
72 
73 	G_OBJECT_CLASS (pluma_encodings_dialog_parent_class)->finalize (object);
74 }
75 
76 static void
pluma_encodings_dialog_dispose(GObject * object)77 pluma_encodings_dialog_dispose (GObject *object)
78 {
79 	PlumaEncodingsDialogPrivate *priv = pluma_encodings_dialog_get_instance_private (PLUMA_ENCODINGS_DIALOG(object));
80 
81 	g_clear_object (&priv->enc_settings);
82 
83 	G_OBJECT_CLASS (pluma_encodings_dialog_parent_class)->dispose (object);
84 }
85 
86 static void
pluma_encodings_dialog_class_init(PlumaEncodingsDialogClass * klass)87 pluma_encodings_dialog_class_init (PlumaEncodingsDialogClass *klass)
88 {
89 	GObjectClass *object_class = G_OBJECT_CLASS (klass);
90 
91 	object_class->finalize = pluma_encodings_dialog_finalize;
92 	object_class->dispose = pluma_encodings_dialog_dispose;
93 }
94 
95 enum {
96 	COLUMN_NAME,
97 	COLUMN_CHARSET,
98 	N_COLUMNS
99 };
100 
101 static void
count_selected_items_func(GtkTreeModel * model,GtkTreePath * path,GtkTreeIter * iter,gpointer data)102 count_selected_items_func (GtkTreeModel *model,
103 			   GtkTreePath  *path,
104 			   GtkTreeIter  *iter,
105 			   gpointer      data)
106 {
107 	int *count = data;
108 
109 	*count += 1;
110 }
111 
112 static void
available_selection_changed_callback(GtkTreeSelection * selection,PlumaEncodingsDialog * dialogs)113 available_selection_changed_callback (GtkTreeSelection     *selection,
114 				      PlumaEncodingsDialog *dialogs)
115 {
116 	int count;
117 
118 	count = 0;
119 	gtk_tree_selection_selected_foreach (selection,
120 					     count_selected_items_func,
121 					     &count);
122 
123 	gtk_widget_set_sensitive (dialogs->priv->add_button, count > 0);
124 }
125 
126 static void
displayed_selection_changed_callback(GtkTreeSelection * selection,PlumaEncodingsDialog * dialogs)127 displayed_selection_changed_callback (GtkTreeSelection     *selection,
128 				      PlumaEncodingsDialog *dialogs)
129 {
130 	int count;
131 
132 	count = 0;
133 	gtk_tree_selection_selected_foreach (selection,
134 					     count_selected_items_func,
135 					     &count);
136 
137 	gtk_widget_set_sensitive (dialogs->priv->remove_button, count > 0);
138 }
139 
140 static void
get_selected_encodings_func(GtkTreeModel * model,GtkTreePath * path,GtkTreeIter * iter,gpointer data)141 get_selected_encodings_func (GtkTreeModel *model,
142 			     GtkTreePath  *path,
143 			     GtkTreeIter  *iter,
144 			     gpointer      data)
145 {
146 	GSList **list = data;
147 	gchar *charset;
148 	const PlumaEncoding *enc;
149 
150 	charset = NULL;
151 	gtk_tree_model_get (model, iter, COLUMN_CHARSET, &charset, -1);
152 
153 	enc = pluma_encoding_get_from_charset (charset);
154 	g_free (charset);
155 
156 	*list = g_slist_prepend (*list, (gpointer)enc);
157 }
158 
159 static void
update_shown_in_menu_tree_model(GtkListStore * store,GSList * list)160 update_shown_in_menu_tree_model (GtkListStore *store,
161 				 GSList       *list)
162 {
163 	GtkTreeIter iter;
164 
165 	gtk_list_store_clear (store);
166 
167 	while (list != NULL)
168 	{
169 		const PlumaEncoding *enc;
170 
171 		enc = (const PlumaEncoding*) list->data;
172 
173 		gtk_list_store_append (store, &iter);
174 		gtk_list_store_set (store, &iter,
175 				    COLUMN_CHARSET,
176 				    pluma_encoding_get_charset (enc),
177 				    COLUMN_NAME,
178 				    pluma_encoding_get_name (enc), -1);
179 
180 		list = g_slist_next (list);
181 	}
182 }
183 
184 static void
add_button_clicked_callback(GtkWidget * button,PlumaEncodingsDialog * dialog)185 add_button_clicked_callback (GtkWidget            *button,
186 			     PlumaEncodingsDialog *dialog)
187 {
188 	GtkTreeSelection *selection;
189 	GSList *encodings;
190 	GSList *tmp;
191 
192 	selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (dialog->priv->available_treeview));
193 
194 	encodings = NULL;
195 	gtk_tree_selection_selected_foreach (selection,
196 					     get_selected_encodings_func,
197 					     &encodings);
198 
199 	tmp = encodings;
200 	while (tmp != NULL)
201 	{
202 		if (g_slist_find (dialog->priv->show_in_menu_list, tmp->data) == NULL)
203 			dialog->priv->show_in_menu_list = g_slist_prepend (dialog->priv->show_in_menu_list,
204 									   tmp->data);
205 
206 		tmp = g_slist_next (tmp);
207 	}
208 
209 	g_slist_free (encodings);
210 
211 	update_shown_in_menu_tree_model (GTK_LIST_STORE (dialog->priv->displayed_liststore),
212 					 dialog->priv->show_in_menu_list);
213 }
214 
215 static void
remove_button_clicked_callback(GtkWidget * button,PlumaEncodingsDialog * dialog)216 remove_button_clicked_callback (GtkWidget            *button,
217 				PlumaEncodingsDialog *dialog)
218 {
219 	GtkTreeSelection *selection;
220 	GSList *encodings;
221 	GSList *tmp;
222 
223 	selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (dialog->priv->displayed_treeview));
224 
225 	encodings = NULL;
226 	gtk_tree_selection_selected_foreach (selection,
227 					     get_selected_encodings_func,
228 					     &encodings);
229 
230 	tmp = encodings;
231 	while (tmp != NULL)
232 	{
233 		dialog->priv->show_in_menu_list = g_slist_remove (dialog->priv->show_in_menu_list,
234 								  tmp->data);
235 
236 		tmp = g_slist_next (tmp);
237 	}
238 
239 	g_slist_free (encodings);
240 
241 	update_shown_in_menu_tree_model (GTK_LIST_STORE (dialog->priv->displayed_liststore),
242 					 dialog->priv->show_in_menu_list);
243 }
244 
245 static void
init_shown_in_menu_tree_model(PlumaEncodingsDialog * dialog)246 init_shown_in_menu_tree_model (PlumaEncodingsDialog *dialog)
247 {
248 	GtkTreeIter iter;
249 	gchar **enc_strv;
250 	GSList *list, *tmp;
251 
252 	/* add data to the list store */
253 	enc_strv = g_settings_get_strv (dialog->priv->enc_settings,
254 					PLUMA_SETTINGS_ENCODING_SHOWN_IN_MENU);
255 
256 	list = _pluma_encoding_strv_to_list ((const gchar * const *)enc_strv);
257 
258 	for (tmp = list; tmp != NULL; tmp = g_slist_next (tmp))
259 	{
260 		const PlumaEncoding *enc;
261 
262 		enc = (const PlumaEncoding *) tmp->data;
263 
264 		dialog->priv->show_in_menu_list = g_slist_prepend (dialog->priv->show_in_menu_list,
265 								   tmp->data);
266 
267 		gtk_list_store_append (dialog->priv->displayed_liststore,
268 				       &iter);
269 		gtk_list_store_set (dialog->priv->displayed_liststore,
270 				    &iter,
271 				    COLUMN_CHARSET,
272 				    pluma_encoding_get_charset (enc),
273 				    COLUMN_NAME,
274 				    pluma_encoding_get_name (enc), -1);
275 	}
276 
277 	g_strfreev (enc_strv);
278 	g_slist_free (list);
279 }
280 
281 static void
response_handler(GtkDialog * dialog,gint response_id,PlumaEncodingsDialog * dlg)282 response_handler (GtkDialog            *dialog,
283 		  gint                  response_id,
284                   PlumaEncodingsDialog *dlg)
285 {
286 	if (response_id == GTK_RESPONSE_HELP)
287 	{
288 		pluma_help_display (GTK_WINDOW (dialog), "pluma", NULL);
289 		g_signal_stop_emission_by_name (dialog, "response");
290 		return;
291 	}
292 
293 	if (response_id == GTK_RESPONSE_OK)
294 	{
295 		gchar **encs;
296 
297 		encs = _pluma_encoding_list_to_strv (dlg->priv->show_in_menu_list);
298 
299 		g_settings_set_strv (dlg->priv->enc_settings,
300 				     PLUMA_SETTINGS_ENCODING_SHOWN_IN_MENU,
301 				     (const gchar * const *)encs);
302 
303 		g_strfreev (encs);
304 	}
305 }
306 
307 static void
pluma_encodings_dialog_init(PlumaEncodingsDialog * dlg)308 pluma_encodings_dialog_init (PlumaEncodingsDialog *dlg)
309 {
310 	GtkWidget *content;
311 	GtkCellRenderer *cell_renderer;
312 	GtkTreeModel *sort_model;
313 	GtkTreeViewColumn *column;
314 	GtkTreeIter parent_iter;
315 	GtkTreeSelection *selection;
316 	const PlumaEncoding *enc;
317 	GtkWidget *error_widget;
318 	int i;
319 	gboolean ret;
320 	gchar *root_objects[] = {
321 		"encodings-dialog-contents",
322 		NULL
323 	};
324 
325 	dlg->priv = pluma_encodings_dialog_get_instance_private (dlg);
326 
327 	dlg->priv->enc_settings = g_settings_new (PLUMA_SCHEMA_ID);
328 
329 	pluma_dialog_add_button (GTK_DIALOG (dlg), _("_Cancel"), "process-stop", GTK_RESPONSE_CANCEL);
330 	pluma_dialog_add_button (GTK_DIALOG (dlg), _("_OK"), "gtk-ok", GTK_RESPONSE_OK);
331 	pluma_dialog_add_button (GTK_DIALOG (dlg), _("_Help"), "help-browser", GTK_RESPONSE_HELP);
332 
333 	gtk_window_set_title (GTK_WINDOW (dlg), _("Character Encodings"));
334 	gtk_window_set_default_size (GTK_WINDOW (dlg), 650, 400);
335 
336 	/* HIG defaults */
337 	gtk_container_set_border_width (GTK_CONTAINER (dlg), 5);
338 	gtk_box_set_spacing (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dlg))),
339 			     2); /* 2 * 5 + 2 = 12 */
340 
341 	gtk_dialog_set_default_response (GTK_DIALOG (dlg),
342 					 GTK_RESPONSE_OK);
343 
344 	g_signal_connect (dlg,
345 			  "response",
346 			  G_CALLBACK (response_handler),
347 			  dlg);
348 
349 	ret = pluma_utils_get_ui_objects (PLUMA_DATADIR "/ui/pluma-encodings-dialog.ui",
350 					  root_objects,
351 					  &error_widget,
352 					  "encodings-dialog-contents", &content,
353 					  "add-button", &dlg->priv->add_button,
354 					  "remove-button", &dlg->priv->remove_button,
355 					  "available-treeview", &dlg->priv->available_treeview,
356 					  "displayed-treeview", &dlg->priv->displayed_treeview,
357 					  NULL);
358 
359 	if (!ret)
360 	{
361 		gtk_widget_show (error_widget);
362 
363 		gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dlg))),
364 		                    error_widget,
365 		                    TRUE, TRUE, 0);
366 		gtk_container_set_border_width (GTK_CONTAINER (error_widget), 5);
367 
368 		return;
369 	}
370 
371 	gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dlg))),
372 			    content, TRUE, TRUE, 0);
373 	g_object_unref (content);
374 	gtk_container_set_border_width (GTK_CONTAINER (content), 5);
375 
376 	gtk_button_set_image (GTK_BUTTON (dlg->priv->add_button), gtk_image_new_from_icon_name ("list-add", GTK_ICON_SIZE_BUTTON));
377 	gtk_button_set_image (GTK_BUTTON (dlg->priv->remove_button), gtk_image_new_from_icon_name ("list-remove", GTK_ICON_SIZE_BUTTON));
378 
379 	g_signal_connect (dlg->priv->add_button,
380 			  "clicked",
381 			  G_CALLBACK (add_button_clicked_callback),
382 			  dlg);
383 	g_signal_connect (dlg->priv->remove_button,
384 			  "clicked",
385 			  G_CALLBACK (remove_button_clicked_callback),
386 			  dlg);
387 
388 	/* Tree view of available encodings */
389 	dlg->priv->available_liststore = gtk_list_store_new (N_COLUMNS,
390 							     G_TYPE_STRING,
391 							     G_TYPE_STRING);
392 
393 	cell_renderer = gtk_cell_renderer_text_new ();
394 	column = gtk_tree_view_column_new_with_attributes (_("_Description"),
395 							   cell_renderer,
396 							   "text", COLUMN_NAME,
397 							   NULL);
398 	gtk_tree_view_append_column (GTK_TREE_VIEW (dlg->priv->available_treeview),
399 				     column);
400 	gtk_tree_view_column_set_sort_column_id (column, COLUMN_NAME);
401 
402 	cell_renderer = gtk_cell_renderer_text_new ();
403 	column = gtk_tree_view_column_new_with_attributes (_("_Encoding"),
404 							   cell_renderer,
405 							   "text",
406 							   COLUMN_CHARSET,
407 							   NULL);
408 	gtk_tree_view_append_column (GTK_TREE_VIEW (dlg->priv->available_treeview),
409 				     column);
410 	gtk_tree_view_column_set_sort_column_id (column, COLUMN_CHARSET);
411 
412 	/* Add the data */
413 	i = 0;
414 	while ((enc = pluma_encoding_get_from_index (i)) != NULL)
415 	{
416 		gtk_list_store_append (dlg->priv->available_liststore,
417 				       &parent_iter);
418 		gtk_list_store_set (dlg->priv->available_liststore,
419 				    &parent_iter,
420 				    COLUMN_CHARSET,
421 				    pluma_encoding_get_charset (enc),
422 				    COLUMN_NAME,
423 				    pluma_encoding_get_name (enc), -1);
424 
425 		++i;
426 	}
427 
428 	/* Sort model */
429 	sort_model = gtk_tree_model_sort_new_with_model (GTK_TREE_MODEL (dlg->priv->available_liststore));
430 	gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (sort_model),
431 					      COLUMN_NAME,
432 					      GTK_SORT_ASCENDING);
433 
434 	gtk_tree_view_set_model (GTK_TREE_VIEW (dlg->priv->available_treeview),
435 				 sort_model);
436 	g_object_unref (G_OBJECT (dlg->priv->available_liststore));
437 	g_object_unref (G_OBJECT (sort_model));
438 
439 	selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (dlg->priv->available_treeview));
440 	gtk_tree_selection_set_mode (GTK_TREE_SELECTION (selection),
441 				     GTK_SELECTION_MULTIPLE);
442 
443 	available_selection_changed_callback (selection, dlg);
444 	g_signal_connect (selection,
445 			  "changed",
446 			  G_CALLBACK (available_selection_changed_callback),
447 			  dlg);
448 
449 	/* Tree view of selected encodings */
450 	dlg->priv->displayed_liststore = gtk_list_store_new (N_COLUMNS,
451 							     G_TYPE_STRING,
452 							     G_TYPE_STRING);
453 
454 	cell_renderer = gtk_cell_renderer_text_new ();
455 	column = gtk_tree_view_column_new_with_attributes (_("_Description"),
456 							   cell_renderer,
457 							   "text", COLUMN_NAME,
458 							   NULL);
459 	gtk_tree_view_append_column (GTK_TREE_VIEW (dlg->priv->displayed_treeview),
460 				     column);
461 	gtk_tree_view_column_set_sort_column_id (column, COLUMN_NAME);
462 
463 	cell_renderer = gtk_cell_renderer_text_new ();
464 	column = gtk_tree_view_column_new_with_attributes (_("_Encoding"),
465 							   cell_renderer,
466 							   "text",
467 							   COLUMN_CHARSET,
468 							   NULL);
469 	gtk_tree_view_append_column (GTK_TREE_VIEW (dlg->priv->displayed_treeview),
470 				     column);
471 	gtk_tree_view_column_set_sort_column_id (column, COLUMN_CHARSET);
472 
473 	/* Add the data */
474 	init_shown_in_menu_tree_model (dlg);
475 
476 	/* Sort model */
477 	sort_model = gtk_tree_model_sort_new_with_model (GTK_TREE_MODEL (dlg->priv->displayed_liststore));
478 
479 	gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE
480 					      (sort_model), COLUMN_NAME,
481 					      GTK_SORT_ASCENDING);
482 
483 	gtk_tree_view_set_model (GTK_TREE_VIEW (dlg->priv->displayed_treeview),
484 				 sort_model);
485 	g_object_unref (G_OBJECT (sort_model));
486 	g_object_unref (G_OBJECT (dlg->priv->displayed_liststore));
487 
488 	selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (dlg->priv->displayed_treeview));
489 	gtk_tree_selection_set_mode (GTK_TREE_SELECTION (selection),
490 				     GTK_SELECTION_MULTIPLE);
491 
492 	displayed_selection_changed_callback (selection, dlg);
493 	g_signal_connect (selection,
494 			  "changed",
495 			  G_CALLBACK (displayed_selection_changed_callback),
496 			  dlg);
497 }
498 
499 GtkWidget *
pluma_encodings_dialog_new(void)500 pluma_encodings_dialog_new (void)
501 {
502 	GtkWidget *dlg;
503 
504 	dlg = GTK_WIDGET (g_object_new (PLUMA_TYPE_ENCODINGS_DIALOG, NULL));
505 
506 	return dlg;
507 }
508 
509