1 /* EasyTAG - Tag editor for audio files
2  * Copyright (C) 2014  David King <amigadave@amigadave.com>
3  * Copyright (C) 2000-2003  Jerome Couderc <easytag@gmail.com>
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
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
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18  */
19 
20 #include "config.h"
21 
22 #include <glib/gi18n.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <sys/types.h>
26 #include <unistd.h>
27 #include <sys/stat.h>
28 #include <errno.h>
29 
30 #include "setting.h"
31 #include "application_window.h"
32 #include "cddb_dialog.h"
33 #include "load_files_dialog.h"
34 #include "playlist_dialog.h"
35 #include "preferences_dialog.h"
36 #include "search_dialog.h"
37 #include "easytag.h"
38 #include "charset.h"
39 #include "scan_dialog.h"
40 #include "log.h"
41 #include "misc.h"
42 #include "browser.h"
43 #include "et_core.h"
44 
45 #include "win32/win32dep.h"
46 
47 /* Referenced in the header. */
48 GSettings *MainSettings;
49 
50 /***************
51  * Declaration *
52  ***************/
53 
54 /*
55  * Nota :
56  *  - no trailing slashes on directory name to avoid problem with
57  *    NetBSD's mkdir(2).
58  */
59 
60 // File of masks for tag scanner
61 static const gchar SCAN_TAG_MASKS_FILE[] = "scan_tag.mask";
62 // File of masks for rename file scanner
63 static const gchar RENAME_FILE_MASKS_FILE[] = "rename_file.mask";
64 // File for history of BrowserEntry combobox
65 static const gchar PATH_ENTRY_HISTORY_FILE[] = "browser_path.history";
66 // File for history of run program combobox for directories
67 static const gchar RUN_PROGRAM_WITH_DIRECTORY_HISTORY_FILE[] = "run_program_with_directory.history";
68 // File for history of run program combobox for files
69 static const gchar RUN_PROGRAM_WITH_FILE_HISTORY_FILE[] = "run_program_with_file.history";
70 // File for history of search string combobox
71 static const gchar SEARCH_FILE_HISTORY_FILE[] = "search_file.history";
72 
73 
74 
75 /**************
76  * Prototypes *
77  **************/
78 
79 static gboolean Create_Easytag_Directory (void);
80 
81 /*************
82  * Functions *
83  *************/
84 
85 static void
check_default_path(void)86 check_default_path (void)
87 {
88     GVariant *default_path;
89     const gchar *path;
90 
91     default_path = g_settings_get_value (MainSettings, "default-path");
92     path = g_variant_get_bytestring (default_path);
93 
94     if (!*path)
95     {
96         path = g_get_user_special_dir (G_USER_DIRECTORY_MUSIC);
97         g_settings_set_value (MainSettings, "default-path",
98                               g_variant_new_bytestring (path ? path
99                                                              : g_get_home_dir ()));
100     }
101 
102     g_variant_unref (default_path);
103 }
104 
105 /*
106  * Define and Load default values into config variables
107  */
Init_Config_Variables(void)108 void Init_Config_Variables (void)
109 {
110     MainSettings = g_settings_new ("org.gnome.EasyTAG");
111 
112     /*
113      * Common
114      */
115     check_default_path ();
116 }
117 
118 /*
119  * check_or_create_file:
120  * @filename: (type filename): the filename to create
121  *
122  * Check that the provided @filename exists, and if not, create it.
123  */
124 static void
check_or_create_file(const gchar * filename)125 check_or_create_file (const gchar *filename)
126 {
127     gchar *file_path;
128     GFile *file;
129     GFileOutputStream *ostream;
130     GError *error = NULL;
131 
132     g_return_if_fail (filename != NULL);
133 
134     file_path = g_build_filename (g_get_user_config_dir (), PACKAGE_TARNAME,
135                                   filename, NULL);
136 
137     file = g_file_new_for_path (file_path);
138 
139     if (!(ostream = g_file_append_to (file, G_FILE_CREATE_NONE, NULL, &error)))
140     {
141         g_debug ("Cannot create or open file ‘%s’: %s", file_path,
142                  error->message);
143         g_error_free (error);
144     }
145     else
146     {
147         g_object_unref (ostream);
148     }
149 
150     g_free (file_path);
151     g_object_unref (file);
152 }
153 
154 /*
155  * Create the main directory with empty history files
156  */
Setting_Create_Files(void)157 gboolean Setting_Create_Files (void)
158 {
159     /* The file to write */
160     if (!Create_Easytag_Directory ())
161     {
162         return FALSE;
163     }
164 
165     check_or_create_file (SCAN_TAG_MASKS_FILE);
166     check_or_create_file (RENAME_FILE_MASKS_FILE);
167     check_or_create_file (PATH_ENTRY_HISTORY_FILE);
168     check_or_create_file (RUN_PROGRAM_WITH_DIRECTORY_HISTORY_FILE);
169     check_or_create_file (RUN_PROGRAM_WITH_FILE_HISTORY_FILE);
170     check_or_create_file (SEARCH_FILE_HISTORY_FILE);
171 
172     return TRUE;
173 }
174 
175 
176 
177 /*
178  * Save the contents of a list store to a file
179  */
180 static void
Save_List_Store_To_File(const gchar * filename,GtkListStore * liststore,gint colnum)181 Save_List_Store_To_File (const gchar *filename,
182                          GtkListStore *liststore,
183                          gint colnum)
184 {
185     gchar *file_path;
186     gchar *display_path;
187     GFile *file;
188     GFileOutputStream *ostream;
189     GtkTreeIter iter;
190     GError *error = NULL;
191 
192     if (!gtk_tree_model_get_iter_first (GTK_TREE_MODEL (liststore), &iter)
193         || !Create_Easytag_Directory ())
194     {
195         return;
196     }
197 
198     /* The file to write */
199     file_path = g_build_filename (g_get_user_config_dir (), PACKAGE_TARNAME,
200                                   filename, NULL);
201     file = g_file_new_for_path (file_path);
202     g_free (file_path);
203 
204     if (!(ostream = g_file_replace (file, NULL, FALSE, G_FILE_CREATE_NONE, NULL,
205                                     &error)))
206     {
207         goto err;
208     }
209     else
210     {
211         GString *data;
212         gsize bytes_written;
213 
214         data = g_string_new ("");
215 
216         do
217         {
218             gchar *text;
219 
220             gtk_tree_model_get (GTK_TREE_MODEL (liststore), &iter, colnum,
221                                 &text, -1);
222             g_string_append (data, text);
223             g_free (text);
224             g_string_append_c (data, '\n');
225         } while (gtk_tree_model_iter_next (GTK_TREE_MODEL (liststore), &iter));
226 
227         if (!g_output_stream_write_all (G_OUTPUT_STREAM (ostream), data->str,
228                                         data->len, &bytes_written, NULL,
229                                         &error))
230         {
231             g_string_free (data, TRUE);
232             g_object_unref (ostream);
233             goto err;
234         }
235 
236         g_string_free (data, TRUE);
237         g_object_unref (ostream);
238     }
239 
240     g_object_unref (file);
241     return;
242 
243 err:
244     file_path = g_file_get_path (file);
245     display_path = g_filename_display_name (file_path);
246     Log_Print (LOG_ERROR, _("Cannot write list to file ‘%s’: %s"),
247                display_path, error->message);
248 
249     g_error_free (error);
250     g_free (display_path);
251     g_free (file_path);
252     g_object_unref (file);
253     return;
254 }
255 
256 /*
257  * Populate a list store with data from a file passed in as first parameter
258  */
259 static gboolean
Populate_List_Store_From_File(const gchar * filename,GtkListStore * liststore,gint text_column)260 Populate_List_Store_From_File (const gchar *filename,
261                                GtkListStore *liststore,
262                                gint text_column)
263 {
264     gchar *file_path;
265     GFile *file;
266     GFileInputStream *istream;
267     gboolean entries_set = FALSE;
268     GError *error = NULL;
269 
270     /* The file to write */
271     g_return_val_if_fail (filename != NULL, FALSE);
272 
273     file_path = g_build_filename (g_get_user_config_dir (), PACKAGE_TARNAME,
274                                   filename, NULL);
275     file = g_file_new_for_path (file_path);
276     g_free (file_path);
277 
278     istream = g_file_read (file, NULL, &error);
279 
280     if (!istream)
281     {
282         goto err;
283     }
284     else
285     {
286         GDataInputStream *data;
287         gsize bytes_read;
288         gchar *line;
289 
290         data = g_data_input_stream_new (G_INPUT_STREAM (istream));
291         /* TODO: Find a safer alternative to _ANY. */
292         g_data_input_stream_set_newline_type (data,
293                                               G_DATA_STREAM_NEWLINE_TYPE_ANY);
294 
295         while ((line = g_data_input_stream_read_line (data, &bytes_read, NULL,
296                                                       &error)))
297         {
298             gchar *utf8_line;
299 
300             utf8_line = Try_To_Validate_Utf8_String (line);
301             g_free (line);
302 
303             if (!et_str_empty (utf8_line))
304             {
305                 gtk_list_store_insert_with_values (liststore, NULL, G_MAXINT,
306                                                    text_column, utf8_line, -1);
307                 entries_set = TRUE;
308             }
309 
310             g_free (utf8_line);
311         }
312 
313         g_object_unref (data);
314 
315         if (error)
316         {
317             g_object_unref (istream);
318             goto err;
319         }
320     }
321 
322     g_object_unref (istream);
323     g_object_unref (file);
324 
325     return entries_set;
326 
327 err:
328     file_path = g_file_get_path (file);
329     Log_Print (LOG_ERROR, _("Cannot open file ‘%s’: %s"), file_path,
330                error->message);
331 
332     g_free (file_path);
333     g_error_free (error);
334     g_object_unref (file);
335     return entries_set;
336 }
337 
338 
339 /*
340  * Functions for writing and reading list of 'Fill Tag' masks
341  */
342 void
Load_Scan_Tag_Masks_List(GtkListStore * liststore,gint colnum,const gchar * const * fallback)343 Load_Scan_Tag_Masks_List (GtkListStore *liststore, gint colnum,
344                           const gchar * const *fallback)
345 {
346     gsize i = 0;
347     GtkTreeIter iter;
348 
349     if (!Populate_List_Store_From_File(SCAN_TAG_MASKS_FILE, liststore, colnum))
350     {
351         /* Fall back to defaults. */
352         Log_Print (LOG_OK, _("Loading default ‘Fill Tag’ masks…"));
353 
354         while(fallback[i])
355         {
356             gtk_list_store_insert_with_values (liststore, &iter, G_MAXINT,
357                                                colnum, fallback[i], -1);
358             i++;
359         }
360     }
361 }
362 
Save_Scan_Tag_Masks_List(GtkListStore * liststore,gint colnum)363 void Save_Scan_Tag_Masks_List (GtkListStore *liststore, gint colnum)
364 {
365     Save_List_Store_To_File(SCAN_TAG_MASKS_FILE, liststore, colnum);
366 }
367 
368 
369 /*
370  * Functions for writing and reading list of 'Rename File' masks
371  */
372 void
Load_Rename_File_Masks_List(GtkListStore * liststore,gint colnum,const gchar * const * fallback)373 Load_Rename_File_Masks_List (GtkListStore *liststore, gint colnum,
374                              const gchar * const *fallback)
375 {
376     gsize i = 0;
377     GtkTreeIter iter;
378 
379     if (!Populate_List_Store_From_File(RENAME_FILE_MASKS_FILE, liststore, colnum))
380     {
381         /* Fall back to defaults. */
382         Log_Print (LOG_OK, _("Loading default ‘Rename File’ masks…"));
383 
384         while(fallback[i])
385         {
386             gtk_list_store_insert_with_values (liststore, &iter, G_MAXINT,
387                                                colnum, fallback[i], -1);
388             i++;
389         }
390     }
391 }
392 
Save_Rename_File_Masks_List(GtkListStore * liststore,gint colnum)393 void Save_Rename_File_Masks_List (GtkListStore *liststore, gint colnum)
394 {
395     Save_List_Store_To_File(RENAME_FILE_MASKS_FILE, liststore, colnum);
396 }
397 
398 /*
399  * Functions for writing and reading list of 'BrowserEntry' combobox
400  */
Load_Path_Entry_List(GtkListStore * liststore,gint colnum)401 void Load_Path_Entry_List (GtkListStore *liststore, gint colnum)
402 {
403     Populate_List_Store_From_File(PATH_ENTRY_HISTORY_FILE, liststore, colnum);
404 }
Save_Path_Entry_List(GtkListStore * liststore,gint colnum)405 void Save_Path_Entry_List (GtkListStore *liststore, gint colnum)
406 {
407     Save_List_Store_To_File(PATH_ENTRY_HISTORY_FILE, liststore, colnum);
408 }
409 
410 /*
411  * Functions for writing and reading list of combobox to run program (tree browser)
412  */
Load_Run_Program_With_Directory_List(GtkListStore * liststore,gint colnum)413 void Load_Run_Program_With_Directory_List (GtkListStore *liststore, gint colnum)
414 {
415     Populate_List_Store_From_File(RUN_PROGRAM_WITH_DIRECTORY_HISTORY_FILE, liststore, colnum);
416 }
Save_Run_Program_With_Directory_List(GtkListStore * liststore,gint colnum)417 void Save_Run_Program_With_Directory_List (GtkListStore *liststore, gint colnum)
418 {
419     Save_List_Store_To_File(RUN_PROGRAM_WITH_DIRECTORY_HISTORY_FILE, liststore, colnum);
420 }
421 
422 /*
423  * Functions for writing and reading list of combobox to run program (file browser)
424  */
Load_Run_Program_With_File_List(GtkListStore * liststore,gint colnum)425 void Load_Run_Program_With_File_List (GtkListStore *liststore, gint colnum)
426 {
427     Populate_List_Store_From_File(RUN_PROGRAM_WITH_FILE_HISTORY_FILE, liststore, colnum);
428 }
Save_Run_Program_With_File_List(GtkListStore * liststore,gint colnum)429 void Save_Run_Program_With_File_List (GtkListStore *liststore, gint colnum)
430 {
431     Save_List_Store_To_File(RUN_PROGRAM_WITH_FILE_HISTORY_FILE, liststore, colnum);
432 }
433 
434 /*
435  * Functions for writing and reading list of combobox to search a string into file (tag or filename)
436  */
Load_Search_File_List(GtkListStore * liststore,gint colnum)437 void Load_Search_File_List (GtkListStore *liststore, gint colnum)
438 {
439     Populate_List_Store_From_File(SEARCH_FILE_HISTORY_FILE, liststore, colnum);
440 }
Save_Search_File_List(GtkListStore * liststore,gint colnum)441 void Save_Search_File_List (GtkListStore *liststore, gint colnum)
442 {
443     Save_List_Store_To_File(SEARCH_FILE_HISTORY_FILE, liststore, colnum);
444 }
445 
446 /*
447  * migrate_config_to_xdg_dir:
448  * @old_path: (type filename): the path to migrate from
449  * @new_path: (type filename): the path to migrate to
450  *
451  * Migrate the EasyTAG configuration files contained in the old path to the new
452  * one.
453  */
454 static void
migrate_config_file_dir(const gchar * old_path,const gchar * new_path)455 migrate_config_file_dir (const gchar *old_path, const gchar *new_path)
456 {
457     gsize i;
458     static const gchar *filenames[] = { SCAN_TAG_MASKS_FILE,
459                                         RENAME_FILE_MASKS_FILE,
460                                         PATH_ENTRY_HISTORY_FILE,
461                                         RUN_PROGRAM_WITH_DIRECTORY_HISTORY_FILE,
462                                         RUN_PROGRAM_WITH_FILE_HISTORY_FILE,
463                                         SEARCH_FILE_HISTORY_FILE,
464                                         NULL
465     };
466 
467     g_debug ("Migrating configuration from directory ‘%s’ to ‘%s’", old_path,
468              new_path);
469 
470     for (i = 0; filenames[i]; i++)
471     {
472         gchar *old_filename, *new_filename;
473         GFile *old_file, *new_file;
474 
475         old_filename = g_build_filename (old_path, filenames[i], NULL);
476 
477         if (!g_file_test (old_filename, G_FILE_TEST_EXISTS))
478         {
479             g_free (old_filename);
480             continue;
481         }
482 
483         new_filename = g_build_filename (new_path, filenames[i], NULL);
484         old_file = g_file_new_for_path (old_filename);
485         new_file = g_file_new_for_path (new_filename);
486 
487         if (!g_file_move (old_file, new_file, G_FILE_COPY_NONE, NULL, NULL,
488                           NULL, NULL))
489         {
490             g_debug ("Failed to migrate configuration file ‘%s’",
491                      filenames[i]);
492         }
493 
494         g_free (old_filename);
495         g_free (new_filename);
496         g_object_unref (old_file);
497         g_object_unref (new_file);
498     }
499 }
500 
501 /**
502  * Create the directory used by EasyTAG to store user configuration files.
503  *
504  * Returns: %TRUE if the directory was created, or already exists. %FALSE if
505  * the directory could not be created.
506  */
507 static gboolean
Create_Easytag_Directory(void)508 Create_Easytag_Directory (void)
509 {
510     gchar *easytag_path = NULL;
511     gint result;
512 
513     /* Directory to create (if it does not exist) with absolute path. */
514     easytag_path = g_build_filename (g_get_user_config_dir (), PACKAGE_TARNAME,
515                                      NULL);
516 
517     if (g_file_test (easytag_path, G_FILE_TEST_IS_DIR))
518     {
519         g_free (easytag_path);
520         return TRUE;
521     }
522 
523     result = g_mkdir_with_parents (easytag_path, S_IRWXU);
524 
525     if (result == -1)
526     {
527         g_debug ("Cannot create directory ‘%s’: %s", easytag_path,
528                  g_strerror (errno));
529         g_free (easytag_path);
530         return FALSE;
531     }
532     else
533     {
534         gchar *old_path = g_build_filename (g_get_home_dir (),
535                                             "." PACKAGE_TARNAME, NULL);
536 
537         if (g_file_test (old_path, G_FILE_TEST_IS_DIR))
538         {
539             migrate_config_file_dir (old_path, easytag_path);
540         }
541 
542         g_free (old_path);
543         g_free (easytag_path);
544 
545         return TRUE;
546     }
547 }
548 
549 /*
550  * et_settings_enum_get:
551  * @value: the property value to be set (active item on combo box)
552  * @variant: the variant to set the @value from
553  * @user_data: the #GType of the #GSettings enum
554  *
555  * Wrapper function to convert an enum-type GSettings key into an integer
556  * value.
557  *
558  * Returns: %TRUE if the mapping was successful, %FALSE otherwise
559  */
560 gboolean
et_settings_enum_get(GValue * value,GVariant * variant,gpointer user_data)561 et_settings_enum_get (GValue *value, GVariant *variant, gpointer user_data)
562 {
563     GType enum_type;
564     GEnumClass *enum_class;
565     GEnumValue *enum_value;
566     const gchar *nick;
567 
568     g_return_val_if_fail (user_data != NULL, FALSE);
569 
570     enum_type = (GType)GPOINTER_TO_SIZE (user_data);
571     enum_class = g_type_class_ref (enum_type);
572     nick = g_variant_get_string (variant, NULL);
573     enum_value = g_enum_get_value_by_nick (enum_class, nick);
574     g_type_class_unref (enum_class);
575 
576     if (!enum_value)
577     {
578         g_warning ("Unable to lookup %s enum nick '%s' from GType",
579                    g_type_name (enum_type),
580                    nick);
581         return FALSE;
582     }
583 
584     g_value_set_int (value, enum_value->value);
585     return TRUE;
586 }
587 
588 /*
589  * et_settings_enum_set:
590  * @value: the property value to set the @variant from
591  * @expected_type: the expected type of the returned variant
592  * @user_data: the #GType of the #GSettings enum
593  *
594  * Wrapper function to convert an integer value into a string suitable for
595  * storing into an enum-type GSettings key.
596  *
597  * Returns: a new GVariant containing the mapped value, or %NULL upon failure
598  */
599 GVariant *
et_settings_enum_set(const GValue * value,const GVariantType * expected_type,gpointer user_data)600 et_settings_enum_set (const GValue *value, const GVariantType *expected_type,
601                       gpointer user_data)
602 {
603     GType enum_type;
604     GEnumClass *enum_class;
605     GEnumValue *enum_value;
606 
607     g_return_val_if_fail (user_data != NULL, NULL);
608 
609     enum_type = (GType)GPOINTER_TO_SIZE (user_data);
610     enum_class = g_type_class_ref (enum_type);
611     enum_value = g_enum_get_value (enum_class, g_value_get_int (value));
612     g_type_class_unref (enum_class);
613 
614     if (!enum_value)
615     {
616         g_warning ("Unable to lookup %s enum value '%d' from GType",
617                    g_type_name (enum_type), g_value_get_int (value));
618         return NULL;
619     }
620 
621     return g_variant_new (g_variant_type_peek_string (expected_type),
622                           enum_value->value_nick);
623 }
624 
625 /*
626  * et_settings_enum_radio_get:
627  * @value: the property value to be set
628  * @variant: the variant to set the @value from
629  * @user_data: the widget on which the setting should be applied
630  *
631  * Wrapper function to convert an enum-type GSettings key state to the active
632  * radio button.
633  *
634  * Returns: %TRUE
635  */
636 gboolean
et_settings_enum_radio_get(GValue * value,GVariant * variant,gpointer user_data)637 et_settings_enum_radio_get (GValue *value, GVariant *variant,
638                             gpointer user_data)
639 {
640     const gchar *name;
641     const gchar *setting;
642 
643     name = gtk_widget_get_name (GTK_WIDGET (user_data));
644     setting = g_variant_get_string (variant, NULL);
645 
646     /* Only set the radio button which matches the setting to active. */
647     if (g_strcmp0 (name, setting) == 0)
648     {
649         g_value_set_boolean (value, TRUE);
650     }
651 
652     return TRUE;
653 }
654 
655 /*
656  * et_settings_enum_radio_set:
657  * @value: the property value to set the @variant from
658  * @expected_type: the expected type of the returned variant
659  * @user_data: the widget which the setting should be taken from
660  *
661  * Wrapper function to convert the active radiobutton to the value of an
662  * enum-type GSettings key.
663  *
664  * Returns: a new GVariant containing the mapped value, or %NULL upon failure
665  */
666 GVariant *
et_settings_enum_radio_set(const GValue * value,const GVariantType * expected_type,gpointer user_data)667 et_settings_enum_radio_set (const GValue *value,
668                             const GVariantType *expected_type,
669                             gpointer user_data)
670 {
671     GVariant *variant = NULL;
672     const gchar *name;
673 
674     /* Ignore buttons that are not active. */
675     if (!g_value_get_boolean (value))
676     {
677         return variant;
678     }
679 
680     name = gtk_widget_get_name (GTK_WIDGET (user_data));
681     variant = g_variant_new_string (name);
682 
683     return variant;
684 }
685 
686 /*
687  * et_settings_flags_toggle_get:
688  * @value: the property value to be set (active item on combo box)
689  * @variant: the variant to set the @value from
690  * @user_data: the #GType of the #GSettings flags
691  *
692  * Wrapper function to convert a flags-type GSettings key state into the active
693  * toggle button.
694  *
695  * Returns: %TRUE if the mapping was successful, %FALSE otherwise
696  */
697 gboolean
et_settings_flags_toggle_get(GValue * value,GVariant * variant,gpointer user_data)698 et_settings_flags_toggle_get (GValue *value, GVariant *variant, gpointer user_data)
699 {
700     const gchar *name;
701     GType flags_type;
702     GFlagsClass *flags_class;
703     GVariantIter iter;
704     GFlagsValue *flags_value;
705     const gchar *nick;
706     guint flags = 0;
707 
708     g_return_val_if_fail (user_data != NULL, FALSE);
709 
710     name = gtk_widget_get_name (GTK_WIDGET (user_data));
711     flags_type = (GType)GPOINTER_TO_SIZE (g_object_get_data (G_OBJECT (user_data),
712                                                                        "flags-type"));
713     flags_class = g_type_class_ref (flags_type);
714 
715     g_variant_iter_init (&iter, variant);
716 
717     while (g_variant_iter_next (&iter, "&s", &nick))
718     {
719         flags_value = g_flags_get_value_by_nick (flags_class, nick);
720 
721         if (flags_value)
722         {
723             flags |= flags_value->value;
724         }
725         else
726         {
727             g_warning ("Unable to lookup %s flags nick '%s' from GType",
728                        g_type_name (flags_type), nick);
729             g_type_class_unref (flags_class);
730             return FALSE;
731         }
732     }
733 
734     flags_value = g_flags_get_value_by_nick (flags_class, name);
735     g_type_class_unref (flags_class);
736 
737     /* TRUE if settings flag is set for this widget, which will make the widget
738      * active. */
739     g_value_set_boolean (value, flags & flags_value->value);
740     return TRUE;
741 }
742 
743 /*
744  * et_settings_flags_toggle_set:
745  * @value: the property value to set the @variant from
746  * @expected_type: the expected type of the returned variant
747  * @user_data: the widget associated with the changed setting
748  *
749  * Wrapper function to convert a boolean value into a string suitable for
750  * storing into a flags-type GSettings key.
751  *
752  * Returns: a new GVariant containing the mapped value, or %NULL upon failure
753  */
754 GVariant *
et_settings_flags_toggle_set(const GValue * value,const GVariantType * expected_type,gpointer user_data)755 et_settings_flags_toggle_set (const GValue *value,
756                               const GVariantType *expected_type,
757                               gpointer user_data)
758 {
759     const gchar *name;
760     GType flags_type;
761     GFlagsClass *flags_class;
762     GFlagsValue *flags_value;
763     guint mask;
764     GVariantBuilder builder;
765     const gchar *flags_key;
766     guint flags;
767 
768     g_return_val_if_fail (user_data != NULL, NULL);
769 
770     name = gtk_widget_get_name (GTK_WIDGET (user_data));
771     flags_type = (GType)GPOINTER_TO_SIZE (g_object_get_data (G_OBJECT (user_data),
772                                                                        "flags-type"));
773     flags_class = g_type_class_ref (flags_type);
774     flags_value = g_flags_get_value_by_nick (flags_class, name);
775     mask = flags_class->mask;
776 
777     if (!flags_value)
778     {
779         g_warning ("Unable to lookup %s flags value '%d' from GType",
780                    g_type_name (flags_type), g_value_get_boolean (value));
781         g_type_class_unref (flags_class);
782         return NULL;
783     }
784 
785     flags_key = g_object_get_data (G_OBJECT (user_data), "flags-key");
786     flags = g_settings_get_flags (MainSettings, flags_key);
787 
788     if (g_value_get_boolean (value))
789     {
790         flags |= flags_value->value;
791     }
792     else
793     {
794         flags &= (flags_value->value ^ mask);
795     }
796 
797     g_variant_builder_init (&builder, expected_type);
798 
799     while (flags)
800     {
801         flags_value = g_flags_get_first_value (flags_class, flags);
802 
803         if (flags_value == NULL)
804         {
805             g_variant_builder_clear (&builder);
806             g_type_class_unref (flags_class);
807             return NULL;
808         }
809 
810         g_variant_builder_add (&builder, "s", flags_value->value_nick);
811         flags &= ~flags_value->value;
812     }
813 
814     g_type_class_unref (flags_class);
815 
816     return g_variant_builder_end (&builder);
817 }
818