1 /* EasyTAG - tag editor for audio files
2  * Copyright (C) 2013-2015  David King <amigadave@amigadave.com>
3  *
4  * This program is free software; you can redistribute it and/or modify it
5  * under the terms of the GNU General Public License as published by the Free
6  * Software Foundation; either version 2 of the License, or (at your option)
7  * any later version.
8  *
9  * This program is distributed in the hope that it will be useful, but WITHOUT
10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
12  * more details.
13  *
14  * You should have received a copy of the GNU General Public License along with
15  * this program; if not, write to the Free Software Foundation, Inc., 51
16  * Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
17  */
18 
19 #include "config.h"
20 
21 #include "playlist_dialog.h"
22 
23 #include <glib/gi18n.h>
24 
25 #include "application_window.h"
26 #include "browser.h"
27 #include "charset.h"
28 #include "easytag.h"
29 #include "misc.h"
30 #include "picture.h"
31 #include "scan.h"
32 #include "scan_dialog.h"
33 #include "setting.h"
34 
35 typedef struct
36 {
37     GtkWidget *name_mask_radio;
38     GtkWidget *name_mask_entry;
39     GtkWidget *selected_files_check;
40     GtkWidget *path_relative_radio;
41     GtkWidget *playlist_parent_check;
42     GtkWidget *playlist_dos_check;
43     GtkWidget *content_filenames_radio;
44     GtkWidget *content_extended_radio;
45     GtkWidget *content_extended_mask_radio;
46     GtkWidget *content_mask_entry;
47 } EtPlaylistDialogPrivate;
48 
G_DEFINE_TYPE_WITH_PRIVATE(EtPlaylistDialog,et_playlist_dialog,GTK_TYPE_DIALOG)49 G_DEFINE_TYPE_WITH_PRIVATE (EtPlaylistDialog, et_playlist_dialog, GTK_TYPE_DIALOG)
50 
51 /*
52  * Function to replace UNIX ForwardSlash with a DOS BackSlash
53  */
54 static void
55 convert_forwardslash_to_backslash (const gchar *string)
56 {
57     gchar *tmp;
58 
59     while ((tmp = strchr (string,'/')) != NULL)
60     {
61         *tmp = '\\';
62     }
63 }
64 
65 /*
66  * Write a playlist
67  *  - 'playlist_name' in file system encoding (not UTF-8)
68  */
69 static gboolean
write_playlist(EtPlaylistDialog * self,GFile * file,GError ** error)70 write_playlist (EtPlaylistDialog *self, GFile *file, GError **error)
71 {
72     EtPlaylistDialogPrivate *priv;
73     GFile *parent;
74     GFileOutputStream *ostream;
75     GString *to_write;
76     GList *l;
77     GList *etfilelist = NULL;
78     gchar *basedir;
79     gchar *temp;
80     EtPlaylistContent playlist_content;
81 
82     g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
83 
84     priv = et_playlist_dialog_get_instance_private (self);
85 
86     ostream = g_file_replace (file, NULL, FALSE, G_FILE_CREATE_NONE, NULL, error);
87 
88     if (!ostream)
89     {
90         g_assert (error == NULL || *error != NULL);
91         return FALSE;
92     }
93 
94     /* 'base directory' where is located the playlist. Used also to write file with a
95      * relative path for file located in this directory and sub-directories
96      */
97     parent = g_file_get_parent (file);
98     basedir = g_file_get_path (parent);
99     g_object_unref (parent);
100 
101     playlist_content = g_settings_get_enum (MainSettings, "playlist-content");
102 
103     /* 1) First line of the file (if playlist content is not set to "write only
104      * list of files") */
105     if (playlist_content != ET_PLAYLIST_CONTENT_FILENAMES)
106     {
107         gsize bytes_written;
108 
109         to_write = g_string_new ("#EXTM3U\r\n");
110 
111         if (!g_output_stream_write_all (G_OUTPUT_STREAM (ostream),
112                                         to_write->str, to_write->len,
113                                         &bytes_written, NULL, error))
114         {
115             g_debug ("Only %" G_GSIZE_FORMAT " bytes out of %" G_GSIZE_FORMAT
116                      "bytes of data were written", bytes_written,
117                      to_write->len);
118             g_assert (error == NULL || *error != NULL);
119             g_string_free (to_write, TRUE);
120             g_object_unref (ostream);
121             return FALSE;
122         }
123         g_string_free (to_write, TRUE);
124     }
125 
126     if (g_settings_get_boolean (MainSettings, "playlist-selected-only"))
127     {
128         GList *selfilelist = NULL;
129         GtkTreeSelection *selection = et_application_window_browser_get_selection (ET_APPLICATION_WINDOW (MainWindow));
130 
131         selfilelist = gtk_tree_selection_get_selected_rows(selection, NULL);
132 
133         for (l = selfilelist; l != NULL; l = g_list_next (l))
134         {
135             ET_File *etfile;
136 
137             etfile = et_application_window_browser_get_et_file_from_path (ET_APPLICATION_WINDOW (MainWindow),
138                                                                           l->data);
139             etfilelist = g_list_prepend (etfilelist, etfile);
140         }
141 
142         etfilelist = g_list_reverse (etfilelist);
143 
144         g_list_free_full (selfilelist, (GDestroyNotify)gtk_tree_path_free);
145     }else
146     {
147         etfilelist = ETCore->ETFileList;
148     }
149 
150     for (l = etfilelist; l != NULL; l = g_list_next (l))
151     {
152         const ET_File *etfile;
153         const gchar *filename;
154         gint duration;
155 
156         etfile = (ET_File *)l->data;
157         filename = ((File_Name *)etfile->FileNameCur->data)->value;
158         duration = ((ET_File_Info *)etfile->ETFileInfo)->duration;
159 
160         if (g_settings_get_boolean (MainSettings, "playlist-relative"))
161         {
162             // Keep only files in this directory and sub-dirs
163             if ( strncmp(filename,basedir,strlen(basedir))==0 )
164             {
165                 gsize bytes_written;
166 
167                 /* 2) Write the header. */
168                 switch (playlist_content)
169                 {
170                     case ET_PLAYLIST_CONTENT_FILENAMES:
171                         /* No header written. */
172                         break;
173                     case ET_PLAYLIST_CONTENT_EXTENDED:
174                         /* Header has extended information. */
175                         temp = g_path_get_basename (filename);
176                         to_write = g_string_new ("#EXTINF:");
177                         /* Must be written in system encoding (not UTF-8). */
178                         g_string_append_printf (to_write, "%d,%s\r\n", duration,
179                                                 temp);
180 
181                         if (!g_output_stream_write_all (G_OUTPUT_STREAM (ostream),
182                                                         to_write->str,
183                                                         to_write->len,
184                                                         &bytes_written, NULL,
185                                                         error))
186                         {
187                             g_debug ("Only %" G_GSIZE_FORMAT " bytes out of %"
188                                      G_GSIZE_FORMAT "bytes of data were written",
189                                      bytes_written, to_write->len);
190                             g_assert (error == NULL || *error != NULL);
191                             g_string_free (to_write, TRUE);
192                             g_object_unref (ostream);
193                             return FALSE;
194                         }
195                         g_string_free (to_write, TRUE);
196                         g_free (temp);
197                         break;
198                     case ET_PLAYLIST_CONTENT_EXTENDED_MASK:
199                     {
200                         /* Header uses information generated from a mask. */
201                         gchar *mask = filename_from_display (gtk_entry_get_text (GTK_ENTRY (priv->content_mask_entry)));
202                         /* Special case: do not replace illegal characters and
203                          * do not check if there is a directory separator in
204                          * the mask. */
205                         gchar *filename_generated_utf8 = et_scan_generate_new_filename_from_mask (etfile, mask, TRUE);
206                         gchar *filename_generated = filename_from_display (filename_generated_utf8);
207 
208                         to_write = g_string_new ("#EXTINF:");
209                         /* Must be written in system encoding (not UTF-8). */
210                         g_string_append_printf (to_write, "%d,%s\r\n", duration,
211                                                 filename_generated);
212 
213                         if (!g_output_stream_write_all (G_OUTPUT_STREAM (ostream),
214                                                         to_write->str,
215                                                         to_write->len,
216                                                         &bytes_written, NULL,
217                                                         error))
218                         {
219                             g_debug ("Only %" G_GSIZE_FORMAT " bytes out of %"
220                                      G_GSIZE_FORMAT "bytes of data were written",
221                                      bytes_written, to_write->len);
222                             g_assert (error == NULL || *error != NULL);
223                             g_string_free (to_write, TRUE);
224                             g_object_unref (ostream);
225                             return FALSE;
226                         }
227                         g_string_free (to_write, TRUE);
228                         g_free (mask);
229                         g_free (filename_generated_utf8);
230 
231                         break;
232                     }
233                     default:
234                         g_assert_not_reached ();
235                         break;
236                 }
237 
238                 /* 3) Write the file path. */
239                 if (g_settings_get_boolean (MainSettings,
240                                             "playlist-dos-separator"))
241                 {
242                     gchar *filename_conv = g_strdup(filename+strlen(basedir)+1);
243                     convert_forwardslash_to_backslash (filename_conv);
244 
245                     to_write = g_string_new (filename_conv);
246                     /* Must be written in system encoding (not UTF-8)*/
247                     to_write = g_string_append (to_write, "\r\n");
248 
249                     if (!g_output_stream_write_all (G_OUTPUT_STREAM (ostream),
250                                                     to_write->str,
251                                                     to_write->len,
252                                                     &bytes_written, NULL,
253                                                     error))
254                     {
255                         g_debug ("Only %" G_GSIZE_FORMAT " bytes out of %"
256                                  G_GSIZE_FORMAT "bytes of data were written",
257                                  bytes_written, to_write->len);
258                         g_assert (error == NULL || *error != NULL);
259                         g_string_free (to_write, TRUE);
260                         g_object_unref (ostream);
261                         return FALSE;
262                     }
263                     g_string_free (to_write, TRUE);
264                     g_free(filename_conv);
265                 }else
266                 {
267                     to_write = g_string_new (filename+strlen(basedir)+1);
268                     /* Must be written in system encoding (not UTF-8)*/
269                     to_write = g_string_append (to_write, "\r\n");
270 
271                     if (!g_output_stream_write_all (G_OUTPUT_STREAM (ostream),
272                                                     to_write->str,
273                                                     to_write->len,
274                                                     &bytes_written, NULL,
275                                                     error))
276                     {
277                         g_debug ("Only %" G_GSIZE_FORMAT " bytes out of %"
278                                  G_GSIZE_FORMAT "bytes of data were written",
279                                  bytes_written, to_write->len);
280                         g_assert (error == NULL || *error != NULL);
281                         g_string_free (to_write, TRUE);
282                         g_object_unref (ostream);
283                         return FALSE;
284                     }
285                     g_string_free (to_write, TRUE);
286                 }
287             }
288         }
289         else /* !ETSettings:playlist-relative */
290         {
291             gsize bytes_written;
292 
293             /* 2) Write the header. */
294             switch (playlist_content)
295             {
296                 case ET_PLAYLIST_CONTENT_FILENAMES:
297                     /* No header written. */
298                     break;
299                 case ET_PLAYLIST_CONTENT_EXTENDED:
300                     /* Header has extended information. */
301                     temp = g_path_get_basename (filename);
302                     to_write = g_string_new ("#EXTINF:");
303                     /* Must be written in system encoding (not UTF-8). */
304                     g_string_append_printf (to_write, "%d,%s\r\n", duration,
305                                             temp);
306                     g_free (temp);
307 
308                     if (!g_output_stream_write_all (G_OUTPUT_STREAM (ostream),
309                                                     to_write->str, to_write->len,
310                                                     &bytes_written, NULL, error))
311                     {
312                         g_debug ("Only %" G_GSIZE_FORMAT " bytes out of %"
313                                  G_GSIZE_FORMAT" bytes of data were written",
314                                  bytes_written, to_write->len);
315                         g_assert (error == NULL || *error != NULL);
316                         g_string_free (to_write, TRUE);
317                         g_object_unref (ostream);
318                         return FALSE;
319                     }
320                     g_string_free (to_write, TRUE);
321                     break;
322                 case ET_PLAYLIST_CONTENT_EXTENDED_MASK:
323                 {
324                     /* Header uses information generated from a mask. */
325                     gchar *mask = filename_from_display (gtk_entry_get_text (GTK_ENTRY (priv->content_mask_entry)));
326                     /* Special case: do not replace illegal characters and
327                      * do not check if there is a directory separator in
328                      * the mask. */
329                     gchar *filename_generated_utf8 = et_scan_generate_new_filename_from_mask (etfile, mask, TRUE);
330                     gchar *filename_generated = filename_from_display (filename_generated_utf8);
331 
332                     to_write = g_string_new ("#EXTINF:");
333                     /* Must be written in system encoding (not UTF-8). */
334                     g_string_append_printf (to_write, "%d,%s\r\n", duration,
335                                             filename_generated);
336                     g_free (filename_generated);
337 
338                     if (!g_output_stream_write_all (G_OUTPUT_STREAM (ostream),
339                                                     to_write->str, to_write->len,
340                                                     &bytes_written, NULL, error))
341                     {
342                         g_debug ("Only %" G_GSIZE_FORMAT " bytes out of %"
343                                  G_GSIZE_FORMAT" bytes of data were written",
344                                  bytes_written, to_write->len);
345                         g_assert (error == NULL || *error != NULL);
346                         g_string_free (to_write, TRUE);
347                         g_object_unref (ostream);
348                         return FALSE;
349                     }
350                     g_string_free (to_write, TRUE);
351                     g_free (mask);
352                     g_free (filename_generated_utf8);
353                 }
354                 break;
355                 default:
356                     g_assert_not_reached ();
357                     break;
358             }
359 
360             /* 3) Write the file path. */
361             if (g_settings_get_boolean (MainSettings,
362                                         "playlist-dos-separator"))
363             {
364                 gchar *filename_conv = g_strdup(filename);
365                 convert_forwardslash_to_backslash(filename_conv);
366 
367                 to_write = g_string_new (filename_conv);
368                 /* Must be written in system encoding (not UTF-8)*/
369                 to_write = g_string_append (to_write, "\r\n");
370 
371                 if (!g_output_stream_write_all (G_OUTPUT_STREAM (ostream),
372                                                 to_write->str, to_write->len,
373                                                 &bytes_written, NULL, error))
374                 {
375                     g_debug ("Only %" G_GSIZE_FORMAT " bytes out of %"
376                              G_GSIZE_FORMAT" bytes of data were written",
377                              bytes_written, to_write->len);
378                     g_assert (error == NULL || *error != NULL);
379                     g_string_free (to_write, TRUE);
380                     g_object_unref (ostream);
381                     return FALSE;
382                 }
383                 g_string_free (to_write, TRUE);
384                 g_free(filename_conv);
385             }else
386             {
387                 to_write = g_string_new (filename);
388                 /* Must be written in system encoding (not UTF-8)*/
389                 to_write = g_string_append (to_write, "\r\n");
390 
391                 if (!g_output_stream_write_all (G_OUTPUT_STREAM (ostream),
392                                                 to_write->str, to_write->len,
393                                                 &bytes_written, NULL, error))
394                 {
395                     g_debug ("Only %" G_GSIZE_FORMAT " bytes out of %"
396                              G_GSIZE_FORMAT" bytes of data were written",
397                              bytes_written, to_write->len);
398                     g_assert (error == NULL || *error != NULL);
399                     g_string_free (to_write, TRUE);
400                     g_object_unref (ostream);
401                     return FALSE;
402                 }
403                 g_string_free (to_write, TRUE);
404             }
405         }
406     }
407 
408     if (g_settings_get_boolean (MainSettings, "playlist-selected-only"))
409     {
410         g_list_free (etfilelist);
411     }
412 
413     g_assert (error == NULL || *error == NULL);
414     g_object_unref (ostream);
415     g_free(basedir);
416     return TRUE;
417 }
418 
419 static void
write_button_clicked(EtPlaylistDialog * self)420 write_button_clicked (EtPlaylistDialog *self)
421 {
422     EtPlaylistDialogPrivate *priv;
423     gchar *playlist_name = NULL;
424     gchar *playlist_path_utf8;      // Path
425     gchar *playlist_basename_utf8;  // Filename
426     gchar *playlist_name_utf8;      // Path + filename
427     gchar *temp;
428     GtkWidget *msgdialog;
429 
430     priv = et_playlist_dialog_get_instance_private (self);
431 
432     /* Check if playlist name was filled. */
433     if (g_settings_get_boolean (MainSettings, "playlist-use-mask")
434         && *(gtk_entry_get_text (GTK_ENTRY (priv->name_mask_entry))) == '\0')
435     {
436         /* TODO: Can this happen? */
437         g_settings_set_boolean (MainSettings, "playlist-use-mask", FALSE);
438     }
439 
440     // Path of the playlist file (may be truncated later if PLAYLIST_CREATE_IN_PARENT_DIR is TRUE)
441     temp = g_file_get_path (et_application_window_get_current_path (ET_APPLICATION_WINDOW (MainWindow)));
442     playlist_path_utf8 = g_filename_display_name (temp);
443     g_free (temp);
444 
445     /* Build the playlist filename. */
446     if (g_settings_get_boolean (MainSettings, "playlist-use-mask"))
447     {
448         EtConvertSpaces convert_mode;
449 
450         if (!ETCore->ETFileList)
451             return;
452 
453         playlist_name = g_settings_get_string (MainSettings,
454                                                "playlist-filename-mask");
455 
456         /* Generate filename from tag of the current selected file (FIXME). */
457         temp = filename_from_display (playlist_name);
458         g_free (playlist_name);
459         playlist_basename_utf8 = et_scan_generate_new_filename_from_mask (ETCore->ETFileDisplayed,
460                                                                           temp,
461                                                                           FALSE);
462         g_free (temp);
463 
464         /* Replace Characters (with scanner). */
465         convert_mode = g_settings_get_enum (MainSettings,
466                                             "rename-convert-spaces");
467 
468         switch (convert_mode)
469         {
470             case ET_CONVERT_SPACES_SPACES:
471                 Scan_Convert_Underscore_Into_Space (playlist_basename_utf8);
472                 Scan_Convert_P20_Into_Space (playlist_basename_utf8);
473                 break;
474             case ET_CONVERT_SPACES_UNDERSCORES:
475                 Scan_Convert_Space_Into_Underscore (playlist_basename_utf8);
476                 break;
477             case ET_CONVERT_SPACES_REMOVE:
478                 Scan_Remove_Spaces (playlist_basename_utf8);
479                 break;
480             /* FIXME: Check that this is intended. */
481             case ET_CONVERT_SPACES_NO_CHANGE:
482             default:
483                 g_assert_not_reached ();
484                 break;
485         }
486     }else // PLAYLIST_USE_DIR_NAME
487     {
488 
489         if ( strcmp(playlist_path_utf8,G_DIR_SEPARATOR_S)==0 )
490         {
491             playlist_basename_utf8 = g_strdup("playlist");
492         }else
493         {
494             gchar *tmp_string = g_strdup(playlist_path_utf8);
495             // Remove last '/'
496             if (tmp_string[strlen(tmp_string)-1]==G_DIR_SEPARATOR)
497                 tmp_string[strlen(tmp_string)-1] = '\0';
498             // Get directory name
499             temp = g_path_get_basename(tmp_string);
500             playlist_basename_utf8 = g_strdup(temp);
501             g_free(tmp_string);
502             g_free(temp);
503         }
504 
505     }
506 
507     /* Must be placed after "Build the playlist filename", as we can truncate
508      * the path! */
509     if (g_settings_get_boolean (MainSettings, "playlist-parent-directory"))
510     {
511         if ( (strcmp(playlist_path_utf8,G_DIR_SEPARATOR_S) != 0) )
512         {
513             gchar *tmp;
514             // Remove last '/'
515             if (playlist_path_utf8[strlen(playlist_path_utf8)-1]==G_DIR_SEPARATOR)
516                 playlist_path_utf8[strlen(playlist_path_utf8)-1] = '\0';
517             // Get parent directory
518             if ( (tmp=strrchr(playlist_path_utf8,G_DIR_SEPARATOR)) != NULL )
519                 *(tmp + 1) = '\0';
520         }
521     }
522 
523     // Generate path + filename of playlist
524     if (playlist_path_utf8[strlen(playlist_path_utf8)-1]==G_DIR_SEPARATOR)
525         playlist_name_utf8 = g_strconcat(playlist_path_utf8,playlist_basename_utf8,".m3u",NULL);
526     else
527         playlist_name_utf8 = g_strconcat(playlist_path_utf8,G_DIR_SEPARATOR_S,playlist_basename_utf8,".m3u",NULL);
528 
529     g_free(playlist_path_utf8);
530     g_free(playlist_basename_utf8);
531 
532     playlist_name = filename_from_display(playlist_name_utf8);
533 
534     {
535         GFile *file = g_file_new_for_path (playlist_name);
536         GError *error = NULL;
537 
538         if (!write_playlist (self, file, &error))
539         {
540             // Writing fails...
541             msgdialog = gtk_message_dialog_new (GTK_WINDOW (self),
542                                                GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
543                                                GTK_MESSAGE_ERROR,
544                                                GTK_BUTTONS_CLOSE,
545                                                _("Cannot write playlist file ‘%s’"),
546                                                playlist_name_utf8);
547             gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (msgdialog),
548                                                       "%s", error->message);
549             gtk_window_set_title(GTK_WINDOW(msgdialog),_("Playlist File Error"));
550 
551             gtk_dialog_run(GTK_DIALOG(msgdialog));
552             gtk_widget_destroy(msgdialog);
553             g_error_free (error);
554         }else
555         {
556             gchar *msg;
557             msg = g_strdup_printf (_("Wrote playlist file ‘%s’"),
558                                    playlist_name_utf8);
559             et_application_window_status_bar_message (ET_APPLICATION_WINDOW (MainWindow),
560                                                       msg, TRUE);
561             g_free (msg);
562         }
563         g_object_unref (file);
564     }
565     g_free(playlist_name_utf8);
566     g_free(playlist_name);
567 }
568 
569 /*
570  * on_response:
571  * @dialog: the dialog which emitted the response signal
572  * @response_id: the response ID
573  * @user_data: user data set when the signal was connected
574  *
575  * Signal handler for the write playlist dialog.
576  */
577 static void
on_response(GtkDialog * dialog,gint response_id,gpointer user_data)578 on_response (GtkDialog *dialog, gint response_id, gpointer user_data)
579 {
580     switch (response_id)
581     {
582         case GTK_RESPONSE_OK:
583             write_button_clicked (ET_PLAYLIST_DIALOG (dialog));
584             break;
585         case GTK_RESPONSE_CANCEL:
586             gtk_widget_hide (GTK_WIDGET (dialog));
587             break;
588         case GTK_RESPONSE_DELETE_EVENT:
589             break;
590         default:
591             g_assert_not_reached ();
592     }
593 }
594 
595 /*
596  * entry_check_content_mask:
597  * @entry: the entry for which to check the mask
598  * @user_data: user data set when the signal was connected
599  *
600  * Display an icon in the entry if the current text contains an invalid mask.
601  */
602 static void
entry_check_content_mask(GtkEntry * entry,gpointer user_data)603 entry_check_content_mask (GtkEntry *entry, gpointer user_data)
604 {
605     gchar *tmp  = NULL;
606     gchar *mask = NULL;
607 
608     g_return_if_fail (entry != NULL);
609 
610     mask = g_strdup (gtk_entry_get_text (entry));
611 
612     if (et_str_empty (mask))
613         goto Bad_Mask;
614 
615     while (mask)
616     {
617         if ( (tmp=strrchr(mask,'%'))==NULL )
618         {
619             /* There is no more code. */
620             /* No code in mask is accepted. */
621             goto Good_Mask;
622         }
623         if (strlen(tmp)>1 && (tmp[1]=='t' || tmp[1]=='a' || tmp[1]=='b' || tmp[1]=='y' ||
624                               tmp[1]=='g' || tmp[1]=='n' || tmp[1]=='l' || tmp[1]=='c' || tmp[1]=='i'))
625         {
626             /* The code is valid. */
627             /* No separator is accepted. */
628             *(mask+strlen(mask)-strlen(tmp)) = '\0';
629         }else
630         {
631             goto Bad_Mask;
632         }
633     }
634 
635     Bad_Mask:
636         g_free(mask);
637         gtk_entry_set_icon_from_icon_name (entry, GTK_ENTRY_ICON_SECONDARY,
638                                            "emblem-unreadable");
639         gtk_entry_set_icon_tooltip_text (entry, GTK_ENTRY_ICON_SECONDARY,
640                                          _("Invalid scanner mask"));
641         return;
642 
643     Good_Mask:
644         g_free(mask);
645         gtk_entry_set_icon_from_icon_name (entry, GTK_ENTRY_ICON_SECONDARY,
646                                            NULL);
647         return;
648 }
649 
650 static void
create_playlist_dialog(EtPlaylistDialog * self)651 create_playlist_dialog (EtPlaylistDialog *self)
652 {
653     EtPlaylistDialogPrivate *priv;
654     GtkDialog *dialog;
655 
656     priv = et_playlist_dialog_get_instance_private (self);
657     dialog = GTK_DIALOG (self);
658 
659     gtk_dialog_add_buttons (dialog, _("_Cancel"), GTK_RESPONSE_CANCEL,
660                             _("_Save"), GTK_RESPONSE_OK, NULL);
661     gtk_dialog_set_default_response (dialog, GTK_RESPONSE_OK);
662 
663     /* Playlist name */
664     g_settings_bind (MainSettings, "playlist-filename-mask",
665                      priv->name_mask_entry, "text", G_SETTINGS_BIND_DEFAULT);
666     g_settings_bind (MainSettings, "playlist-use-mask", priv->name_mask_radio,
667                      "active", G_SETTINGS_BIND_DEFAULT);
668 
669     /* Playlist options */
670     g_settings_bind (MainSettings, "playlist-selected-only",
671                      priv->selected_files_check, "active",
672                      G_SETTINGS_BIND_DEFAULT);
673 
674     g_settings_bind (MainSettings, "playlist-relative",
675                      priv->path_relative_radio, "active",
676                      G_SETTINGS_BIND_DEFAULT);
677 
678     /* Create playlist in parent directory. */
679     g_settings_bind (MainSettings, "playlist-parent-directory",
680                      priv->playlist_parent_check, "active",
681                      G_SETTINGS_BIND_DEFAULT);
682 
683     /* DOS Separator. */
684     g_settings_bind (MainSettings, "playlist-dos-separator",
685                      priv->playlist_dos_check, "active",
686                      G_SETTINGS_BIND_DEFAULT);
687 
688     /* Playlist content */
689     g_settings_bind (MainSettings, "playlist-default-mask",
690                      priv->content_mask_entry, "text",
691                      G_SETTINGS_BIND_DEFAULT);
692 
693     /* Mask status icon. Signal connection to check if mask is correct in the
694      * mask entry. */
695     g_signal_connect (priv->content_mask_entry, "changed",
696                       G_CALLBACK (entry_check_content_mask), NULL);
697 
698     g_settings_bind_with_mapping (MainSettings, "playlist-content",
699                                   priv->content_filenames_radio, "active",
700                                   G_SETTINGS_BIND_DEFAULT,
701                                   et_settings_enum_radio_get,
702                                   et_settings_enum_radio_set,
703                                   priv->content_filenames_radio, NULL);
704     g_settings_bind_with_mapping (MainSettings, "playlist-content",
705                                   priv->content_extended_radio, "active",
706                                   G_SETTINGS_BIND_DEFAULT,
707                                   et_settings_enum_radio_get,
708                                   et_settings_enum_radio_set,
709                                   priv->content_extended_radio, NULL);
710     g_settings_bind_with_mapping (MainSettings, "playlist-content",
711                                   priv->content_extended_mask_radio, "active",
712                                   G_SETTINGS_BIND_DEFAULT,
713                                   et_settings_enum_radio_get,
714                                   et_settings_enum_radio_set,
715                                   priv->content_extended_mask_radio, NULL);
716 
717     /* To initialize the mask status icon and visibility. */
718     g_signal_emit_by_name (priv->name_mask_entry, "changed");
719     g_signal_emit_by_name (priv->content_mask_entry, "changed");
720 }
721 
722 static void
et_playlist_dialog_init(EtPlaylistDialog * self)723 et_playlist_dialog_init (EtPlaylistDialog *self)
724 {
725     gtk_widget_init_template (GTK_WIDGET (self));
726     create_playlist_dialog (self);
727 }
728 
729 static void
et_playlist_dialog_class_init(EtPlaylistDialogClass * klass)730 et_playlist_dialog_class_init (EtPlaylistDialogClass *klass)
731 {
732     GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
733 
734     gtk_widget_class_set_template_from_resource (widget_class,
735                                                  "/org/gnome/EasyTAG/playlist_dialog.ui");
736     gtk_widget_class_bind_template_child_private (widget_class,
737                                                   EtPlaylistDialog,
738                                                   name_mask_radio);
739     gtk_widget_class_bind_template_child_private (widget_class,
740                                                   EtPlaylistDialog,
741                                                   name_mask_entry);
742     gtk_widget_class_bind_template_child_private (widget_class,
743                                                   EtPlaylistDialog,
744                                                   selected_files_check);
745     gtk_widget_class_bind_template_child_private (widget_class,
746                                                   EtPlaylistDialog,
747                                                   path_relative_radio);
748     gtk_widget_class_bind_template_child_private (widget_class,
749                                                   EtPlaylistDialog,
750                                                   playlist_parent_check);
751     gtk_widget_class_bind_template_child_private (widget_class,
752                                                   EtPlaylistDialog,
753                                                   playlist_dos_check);
754     gtk_widget_class_bind_template_child_private (widget_class,
755                                                   EtPlaylistDialog,
756                                                   content_filenames_radio);
757     gtk_widget_class_bind_template_child_private (widget_class,
758                                                   EtPlaylistDialog,
759                                                   content_extended_radio);
760     gtk_widget_class_bind_template_child_private (widget_class,
761                                                   EtPlaylistDialog,
762                                                   content_extended_mask_radio);
763     gtk_widget_class_bind_template_child_private (widget_class,
764                                                   EtPlaylistDialog,
765                                                   content_mask_entry);
766     gtk_widget_class_bind_template_callback (widget_class,
767                                              entry_check_content_mask);
768     gtk_widget_class_bind_template_callback (widget_class, on_response);
769 }
770 
771 /*
772  * et_playlist_dialog_new:
773  *
774  * Create a new EtPlaylistDialog instance.
775  *
776  * Returns: a new #EtPlaylistDialog
777  */
778 EtPlaylistDialog *
et_playlist_dialog_new(GtkWindow * parent)779 et_playlist_dialog_new (GtkWindow *parent)
780 {
781     g_return_val_if_fail (GTK_WINDOW (parent), NULL);
782 
783     return g_object_new (ET_TYPE_PLAYLIST_DIALOG, "transient-for", parent,
784                          NULL);
785 }
786