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