1 /* EasyTAG - tag editor for audio files
2  * Copyright (C) 2014,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 "file.h"
22 
23 #include <glib/gi18n.h>
24 #include <glib/gstdio.h>
25 #include <string.h>
26 #include <stdlib.h>
27 #include <unistd.h>
28 #include <utime.h>
29 
30 #include "application_window.h"
31 #include "easytag.h"
32 #include "file_tag.h"
33 #include "file_list.h"
34 #ifdef ENABLE_MP3
35 #   include "id3_tag.h"
36 #endif
37 #include "picture.h"
38 #include "ape_tag.h"
39 #ifdef ENABLE_OGG
40 #include "ogg_tag.h"
41 #endif
42 #ifdef ENABLE_FLAC
43 #include "flac_tag.h"
44 #endif
45 #ifdef ENABLE_MP4
46 #include "mp4_tag.h"
47 #endif
48 #ifdef ENABLE_WAVPACK
49 #include "wavpack_tag.h"
50 #endif
51 #ifdef ENABLE_OPUS
52 #include "opus_tag.h"
53 #endif
54 #include "browser.h"
55 #include "log.h"
56 #include "misc.h"
57 #include "setting.h"
58 #include "charset.h"
59 
60 #include "win32/win32dep.h"
61 
62 static gboolean ET_Free_File_Name_List            (GList *FileNameList);
63 static gboolean ET_Free_File_Tag_List (GList *FileTagList);
64 
65 static void ET_Mark_File_Tag_As_Saved (ET_File *ETFile);
66 
67 static gboolean ET_Add_File_Name_To_List (ET_File *ETFile,
68                                           File_Name *FileName);
69 static gboolean ET_Add_File_Tag_To_List (ET_File *ETFile, File_Tag  *FileTag);
70 
71 /*
72  * Create a new ET_File structure
73  */
74 ET_File *
ET_File_Item_New(void)75 ET_File_Item_New (void)
76 {
77     ET_File *ETFile;
78 
79     ETFile = g_slice_new0 (ET_File);
80 
81     return ETFile;
82 }
83 
84 /*
85  * Comparison function for sorting by ascending filename.
86  */
87 gint
ET_Comp_Func_Sort_File_By_Ascending_Filename(const ET_File * ETFile1,const ET_File * ETFile2)88 ET_Comp_Func_Sort_File_By_Ascending_Filename (const ET_File *ETFile1,
89                                               const ET_File *ETFile2)
90 {
91     const gchar *file1_ck = ((File_Name *)((GList *)ETFile1->FileNameCur)->data)->value_ck;
92     const gchar *file2_ck = ((File_Name *)((GList *)ETFile2->FileNameCur)->data)->value_ck;
93     // !!!! : Must be the same rules as "Cddb_Track_List_Sort_Func" to be
94     // able to sort in the same order files in cddb and in the file list.
95     return g_settings_get_boolean (MainSettings,
96                                    "sort-case-sensitive") ? strcmp (file1_ck, file2_ck)
97                                                           : strcasecmp (file1_ck, file2_ck);
98 }
99 
100 /*
101  * Comparison function for sorting by descending filename.
102  */
103 gint
ET_Comp_Func_Sort_File_By_Descending_Filename(const ET_File * ETFile1,const ET_File * ETFile2)104 ET_Comp_Func_Sort_File_By_Descending_Filename (const ET_File *ETFile1,
105                                                const ET_File *ETFile2)
106 {
107     return ET_Comp_Func_Sort_File_By_Ascending_Filename(ETFile2,ETFile1);
108 }
109 
110 
111 /*
112  * Comparison function for sorting by ascending disc number.
113  */
114 /* FIXME: Handle non-numeric disc number. */
115 gint
et_comp_func_sort_file_by_ascending_disc_number(const ET_File * ETFile1,const ET_File * ETFile2)116 et_comp_func_sort_file_by_ascending_disc_number (const ET_File *ETFile1,
117                                                  const ET_File *ETFile2)
118 {
119     gint track1, track2;
120 
121     if (!ETFile1->FileTag->data
122         || !((File_Tag *)ETFile1->FileTag->data)->disc_number)
123     {
124         track1 = 0;
125     }
126     else
127     {
128         track1 = atoi (((File_Tag *)ETFile1->FileTag->data)->disc_number);
129     }
130 
131     if (!ETFile2->FileTag->data
132         || !((File_Tag *)ETFile2->FileTag->data)->disc_number)
133     {
134         track2 = 0;
135     }
136     else
137     {
138         track2 = atoi (((File_Tag *)ETFile2->FileTag->data)->disc_number);
139     }
140 
141     /* Second criterion. */
142     if (track1 == track2)
143     {
144         return ET_Comp_Func_Sort_File_By_Ascending_Filename (ETFile1, ETFile2);
145     }
146 
147     /* First criterion. */
148     return (track1 - track2);
149 }
150 
151 /*
152  * Comparison function for sorting by descending disc number.
153  */
154 gint
et_comp_func_sort_file_by_descending_disc_number(const ET_File * ETFile1,const ET_File * ETFile2)155 et_comp_func_sort_file_by_descending_disc_number (const ET_File *ETFile1,
156                                                   const ET_File *ETFile2)
157 {
158     return et_comp_func_sort_file_by_ascending_disc_number (ETFile2, ETFile1);
159 }
160 
161 
162 /*
163  * Comparison function for sorting by ascending track number.
164  */
165 /* FIXME: Handle non-numeric track number. */
166 gint
ET_Comp_Func_Sort_File_By_Ascending_Track_Number(const ET_File * ETFile1,const ET_File * ETFile2)167 ET_Comp_Func_Sort_File_By_Ascending_Track_Number (const ET_File *ETFile1,
168                                                   const ET_File *ETFile2)
169 {
170     gint track1, track2;
171 
172     if ( !ETFile1->FileTag->data || !((File_Tag *)ETFile1->FileTag->data)->track )
173         track1 = 0;
174     else
175         track1 = atoi( ((File_Tag *)ETFile1->FileTag->data)->track );
176 
177     if ( !ETFile2->FileTag->data || !((File_Tag *)ETFile2->FileTag->data)->track )
178         track2 = 0;
179     else
180         track2 = atoi( ((File_Tag *)ETFile2->FileTag->data)->track );
181 
182     // Second criterion
183     if (track1 == track2)
184         return ET_Comp_Func_Sort_File_By_Ascending_Filename(ETFile1,ETFile2);
185 
186     // First criterion
187     return (track1 - track2);
188 }
189 
190 /*
191  * Comparison function for sorting by descending track number.
192  */
193 gint
ET_Comp_Func_Sort_File_By_Descending_Track_Number(const ET_File * ETFile1,const ET_File * ETFile2)194 ET_Comp_Func_Sort_File_By_Descending_Track_Number (const ET_File *ETFile1,
195                                                    const ET_File *ETFile2)
196 {
197     return ET_Comp_Func_Sort_File_By_Ascending_Track_Number(ETFile2,ETFile1);
198 }
199 
200 
201 /*
202  * Comparison function for sorting by ascending creation date.
203  */
204 gint
ET_Comp_Func_Sort_File_By_Ascending_Creation_Date(const ET_File * ETFile1,const ET_File * ETFile2)205 ET_Comp_Func_Sort_File_By_Ascending_Creation_Date (const ET_File *ETFile1,
206                                                    const ET_File *ETFile2)
207 {
208     GFile *file;
209     GFileInfo *info;
210     guint64 time1 = 0;
211     guint64 time2 = 0;
212 
213     /* TODO: Report errors? */
214     file = g_file_new_for_path (((File_Name *)ETFile1->FileNameCur->data)->value);
215     info = g_file_query_info (file, G_FILE_ATTRIBUTE_TIME_CHANGED,
216                               G_FILE_QUERY_INFO_NONE, NULL, NULL);
217 
218     g_object_unref (file);
219 
220     if (info)
221     {
222         time1 = g_file_info_get_attribute_uint64 (info,
223                                                   G_FILE_ATTRIBUTE_TIME_CHANGED);
224         g_object_unref (info);
225     }
226 
227     file = g_file_new_for_path (((File_Name *)ETFile2->FileNameCur->data)->value);
228     info = g_file_query_info (file, G_FILE_ATTRIBUTE_TIME_CHANGED,
229                               G_FILE_QUERY_INFO_NONE, NULL, NULL);
230 
231     g_object_unref (file);
232 
233     if (info)
234     {
235         time2 = g_file_info_get_attribute_uint64 (info,
236                                                   G_FILE_ATTRIBUTE_TIME_CHANGED);
237         g_object_unref (info);
238     }
239 
240     /* Second criterion. */
241     if (time1 == time2)
242     {
243         return ET_Comp_Func_Sort_File_By_Ascending_Filename (ETFile1, ETFile2);
244     }
245 
246     /* First criterion. */
247     return (gint64)(time1 - time2);
248 }
249 
250 /*
251  * Comparison function for sorting by descending creation date.
252  */
253 gint
ET_Comp_Func_Sort_File_By_Descending_Creation_Date(const ET_File * ETFile1,const ET_File * ETFile2)254 ET_Comp_Func_Sort_File_By_Descending_Creation_Date (const ET_File *ETFile1,
255                                                     const ET_File *ETFile2)
256 {
257     return ET_Comp_Func_Sort_File_By_Ascending_Creation_Date(ETFile2,ETFile1);
258 }
259 
260 /*
261  * et_file_list_sort_string:
262  * @str1: a UTF-8 string, or %NULL
263  * @str2: a UTF-8 string to compare against, or %NULL
264  * @file1: an #ET_File corresponding to @str1
265  * @file2: an #ET_File corresponding to @str2
266  * @case_sensitive: whether the sorting should obey case
267  *
268  * Compare two UTF-8 strings, normalizing them before doing so, falling back to
269  * the filenames if the strings are otherwise identical, and obeying the
270  * requested case-sensitivity.
271  *
272  * Returns: an integer less than, equal to, or greater than zero, if str1 is
273  * less than, equal to or greater than str2
274  */
275 static gint
et_file_list_sort_string(const gchar * str1,const gchar * str2,const ET_File * file1,const ET_File * file2,gboolean case_sensitive)276 et_file_list_sort_string (const gchar *str1,
277                           const gchar *str2,
278                           const ET_File *file1,
279                           const ET_File *file2,
280                           gboolean case_sensitive)
281 {
282     gint result;
283 
284     if (case_sensitive)
285     {
286         result = et_normalized_strcmp0 (str1, str1);
287     }
288     else
289     {
290         result = et_normalized_strcasecmp0 (str1, str2);
291     }
292 
293     if (result == 0)
294     {
295         /* Secondary criterion. */
296         return ET_Comp_Func_Sort_File_By_Ascending_Filename (file1, file2);
297     }
298     else
299     {
300         /* Primary criterion. */
301         return result;
302     }
303 }
304 
305 /*
306  * Comparison function for sorting by ascending title.
307  */
308 gint
ET_Comp_Func_Sort_File_By_Ascending_Title(const ET_File * ETFile1,const ET_File * ETFile2)309 ET_Comp_Func_Sort_File_By_Ascending_Title (const ET_File *ETFile1,
310                                            const ET_File *ETFile2)
311 {
312    // Compare pointers just in case they are the same (e.g. both are NULL)
313    if ((ETFile1->FileTag->data == ETFile2->FileTag->data)
314    ||  (((File_Tag *)ETFile1->FileTag->data)->title == ((File_Tag *)ETFile2->FileTag->data)->title))
315         return 0;
316 
317     if (!ETFile1->FileTag->data)
318     {
319         return -1;
320     }
321 
322     if (!ETFile2->FileTag->data)
323     {
324         return 1;
325     }
326 
327     return et_file_list_sort_string (((File_Tag *)ETFile1->FileTag->data)->title,
328                                      ((File_Tag *)ETFile2->FileTag->data)->title,
329                                      ETFile1, ETFile2,
330                                      g_settings_get_boolean (MainSettings,
331                                                              "sort-case-sensitive"));
332 }
333 
334 /*
335  * Comparison function for sorting by descending title.
336  */
337 gint
ET_Comp_Func_Sort_File_By_Descending_Title(const ET_File * ETFile1,const ET_File * ETFile2)338 ET_Comp_Func_Sort_File_By_Descending_Title (const ET_File *ETFile1,
339                                             const ET_File *ETFile2)
340 {
341     return ET_Comp_Func_Sort_File_By_Ascending_Title(ETFile2,ETFile1);
342 }
343 
344 
345 /*
346  * Comparison function for sorting by ascending artist.
347  */
348 gint
ET_Comp_Func_Sort_File_By_Ascending_Artist(const ET_File * ETFile1,const ET_File * ETFile2)349 ET_Comp_Func_Sort_File_By_Ascending_Artist (const ET_File *ETFile1,
350                                             const ET_File *ETFile2)
351 {
352    // Compare pointers just in case they are the same (e.g. both are NULL)
353    if ((ETFile1->FileTag->data == ETFile2->FileTag->data)
354    ||  (((File_Tag *)ETFile1->FileTag->data)->artist == ((File_Tag *)ETFile2->FileTag->data)->artist))
355         return 0;
356 
357     if (!ETFile1->FileTag->data)
358     {
359         return -1;
360     }
361 
362     if (!ETFile2->FileTag->data)
363     {
364         return 1;
365     }
366 
367     return et_file_list_sort_string (((File_Tag *)ETFile1->FileTag->data)->artist,
368                                      ((File_Tag *)ETFile2->FileTag->data)->artist,
369                                      ETFile1, ETFile2,
370                                      g_settings_get_boolean (MainSettings,
371                                                              "sort-case-sensitive"));
372 }
373 
374 /*
375  * Comparison function for sorting by descending artist.
376  */
377 gint
ET_Comp_Func_Sort_File_By_Descending_Artist(const ET_File * ETFile1,const ET_File * ETFile2)378 ET_Comp_Func_Sort_File_By_Descending_Artist (const ET_File *ETFile1,
379                                              const ET_File *ETFile2)
380 {
381     return ET_Comp_Func_Sort_File_By_Ascending_Artist(ETFile2,ETFile1);
382 }
383 
384 /*
385  * Comparison function for sorting by ascending album artist.
386  */
387 gint
ET_Comp_Func_Sort_File_By_Ascending_Album_Artist(const ET_File * ETFile1,const ET_File * ETFile2)388 ET_Comp_Func_Sort_File_By_Ascending_Album_Artist (const ET_File *ETFile1,
389                                                   const ET_File *ETFile2)
390 {
391    // Compare pointers just in case they are the same (e.g. both are NULL)
392    if ((ETFile1->FileTag->data == ETFile2->FileTag->data)
393    ||  (((File_Tag *)ETFile1->FileTag->data)->album_artist == ((File_Tag *)ETFile2->FileTag->data)->album_artist))
394         return 0;
395 
396     if (!ETFile1->FileTag->data)
397     {
398         return -1;
399     }
400 
401     if (!ETFile2->FileTag->data)
402     {
403         return 1;
404     }
405 
406     return et_file_list_sort_string (((File_Tag *)ETFile1->FileTag->data)->album_artist,
407                                      ((File_Tag *)ETFile2->FileTag->data)->album_artist,
408                                      ETFile1, ETFile2,
409                                      g_settings_get_boolean (MainSettings,
410                                                              "sort-case-sensitive"));
411 }
412 
413 /*
414  * Comparison function for sorting by descending album artist.
415  */
416 gint
ET_Comp_Func_Sort_File_By_Descending_Album_Artist(const ET_File * ETFile1,const ET_File * ETFile2)417 ET_Comp_Func_Sort_File_By_Descending_Album_Artist (const ET_File *ETFile1,
418                                                    const ET_File *ETFile2)
419 {
420     return ET_Comp_Func_Sort_File_By_Ascending_Album_Artist(ETFile2,ETFile1);
421 }
422 
423 /*
424  * Comparison function for sorting by ascending album.
425  */
426 gint
ET_Comp_Func_Sort_File_By_Ascending_Album(const ET_File * ETFile1,const ET_File * ETFile2)427 ET_Comp_Func_Sort_File_By_Ascending_Album (const ET_File *ETFile1,
428                                            const ET_File *ETFile2)
429 {
430    // Compare pointers just in case they are the same (e.g. both are NULL)
431    if ((ETFile1->FileTag->data == ETFile2->FileTag->data)
432    ||  (((File_Tag *)ETFile1->FileTag->data)->album == ((File_Tag *)ETFile2->FileTag->data)->album))
433         return 0;
434 
435     if (!ETFile1->FileTag->data)
436     {
437         return -1;
438     }
439 
440     if (!ETFile2->FileTag->data)
441     {
442         return 1;
443     }
444 
445     return et_file_list_sort_string (((File_Tag *)ETFile1->FileTag->data)->album,
446                                      ((File_Tag *)ETFile2->FileTag->data)->album,
447                                      ETFile1, ETFile2,
448                                      g_settings_get_boolean (MainSettings,
449                                                              "sort-case-sensitive"));
450 }
451 
452 /*
453  * Comparison function for sorting by descending album.
454  */
455 gint
ET_Comp_Func_Sort_File_By_Descending_Album(const ET_File * ETFile1,const ET_File * ETFile2)456 ET_Comp_Func_Sort_File_By_Descending_Album (const ET_File *ETFile1,
457                                             const ET_File *ETFile2)
458 {
459     return ET_Comp_Func_Sort_File_By_Ascending_Album(ETFile2,ETFile1);
460 }
461 
462 
463 /*
464  * Comparison function for sorting by ascending year.
465  */
466 gint
ET_Comp_Func_Sort_File_By_Ascending_Year(const ET_File * ETFile1,const ET_File * ETFile2)467 ET_Comp_Func_Sort_File_By_Ascending_Year (const ET_File *ETFile1,
468                                           const ET_File *ETFile2)
469 {
470     gint year1, year2;
471 
472     if ( !ETFile1->FileTag->data || !((File_Tag *)ETFile1->FileTag->data)->year )
473         year1 = 0;
474     else
475         year1 = atoi( ((File_Tag *)ETFile1->FileTag->data)->year );
476 
477     if ( !ETFile2->FileTag->data || !((File_Tag *)ETFile2->FileTag->data)->year )
478         year2 = 0;
479     else
480         year2 = atoi( ((File_Tag *)ETFile2->FileTag->data)->year );
481 
482     // Second criterion
483     if (year1 == year2)
484         return ET_Comp_Func_Sort_File_By_Ascending_Filename(ETFile1,ETFile2);
485 
486     // First criterion
487     return (year1 - year2);
488 }
489 
490 /*
491  * Comparison function for sorting by descending year.
492  */
493 gint
ET_Comp_Func_Sort_File_By_Descending_Year(const ET_File * ETFile1,const ET_File * ETFile2)494 ET_Comp_Func_Sort_File_By_Descending_Year (const ET_File *ETFile1,
495                                            const ET_File *ETFile2)
496 {
497     return ET_Comp_Func_Sort_File_By_Ascending_Year(ETFile2,ETFile1);
498 }
499 
500 
501 /*
502  * Comparison function for sorting by ascending genre.
503  */
504 gint
ET_Comp_Func_Sort_File_By_Ascending_Genre(const ET_File * ETFile1,const ET_File * ETFile2)505 ET_Comp_Func_Sort_File_By_Ascending_Genre (const ET_File *ETFile1,
506                                            const ET_File *ETFile2)
507 {
508    // Compare pointers just in case they are the same (e.g. both are NULL)
509    if ((ETFile1->FileTag->data == ETFile2->FileTag->data)
510    ||  (((File_Tag *)ETFile1->FileTag->data)->genre == ((File_Tag *)ETFile2->FileTag->data)->genre))
511         return 0;
512 
513     if (!ETFile1->FileTag->data)
514     {
515         return -1;
516     }
517 
518     if (!ETFile2->FileTag->data)
519     {
520         return 1;
521     }
522 
523     return et_file_list_sort_string (((File_Tag *)ETFile1->FileTag->data)->genre,
524                                      ((File_Tag *)ETFile2->FileTag->data)->genre,
525                                      ETFile1, ETFile2,
526                                      g_settings_get_boolean (MainSettings,
527                                                              "sort-case-sensitive"));
528 }
529 
530 /*
531  * Comparison function for sorting by descending genre.
532  */
533 gint
ET_Comp_Func_Sort_File_By_Descending_Genre(const ET_File * ETFile1,const ET_File * ETFile2)534 ET_Comp_Func_Sort_File_By_Descending_Genre (const ET_File *ETFile1,
535                                             const ET_File *ETFile2)
536 {
537     return ET_Comp_Func_Sort_File_By_Ascending_Genre(ETFile2,ETFile1);
538 }
539 
540 
541 /*
542  * Comparison function for sorting by ascending comment.
543  */
544 gint
ET_Comp_Func_Sort_File_By_Ascending_Comment(const ET_File * ETFile1,const ET_File * ETFile2)545 ET_Comp_Func_Sort_File_By_Ascending_Comment (const ET_File *ETFile1,
546                                              const ET_File *ETFile2)
547 {
548    // Compare pointers just in case they are the same (e.g. both are NULL)
549    if ((ETFile1->FileTag->data == ETFile2->FileTag->data)
550    ||  (((File_Tag *)ETFile1->FileTag->data)->comment == ((File_Tag *)ETFile2->FileTag->data)->comment))
551         return 0;
552 
553     if (!ETFile1->FileTag->data)
554     {
555         return -1;
556     }
557 
558     if (!ETFile2->FileTag->data)
559     {
560         return 1;
561     }
562 
563     return et_file_list_sort_string (((File_Tag *)ETFile1->FileTag->data)->comment,
564                                      ((File_Tag *)ETFile2->FileTag->data)->comment,
565                                      ETFile1, ETFile2,
566                                      g_settings_get_boolean (MainSettings,
567                                                              "sort-case-sensitive"));
568 }
569 
570 /*
571  * Comparison function for sorting by descending comment.
572  */
573 gint
ET_Comp_Func_Sort_File_By_Descending_Comment(const ET_File * ETFile1,const ET_File * ETFile2)574 ET_Comp_Func_Sort_File_By_Descending_Comment (const ET_File *ETFile1,
575                                               const ET_File *ETFile2)
576 {
577     return ET_Comp_Func_Sort_File_By_Ascending_Comment(ETFile2,ETFile1);
578 }
579 
580 
581 /*
582  * Comparison function for sorting by ascending composer.
583  */
584 gint
ET_Comp_Func_Sort_File_By_Ascending_Composer(const ET_File * ETFile1,const ET_File * ETFile2)585 ET_Comp_Func_Sort_File_By_Ascending_Composer (const ET_File *ETFile1,
586                                               const ET_File *ETFile2)
587 {
588    // Compare pointers just in case they are the same (e.g. both are NULL)
589    if ((ETFile1->FileTag->data == ETFile2->FileTag->data)
590    ||  (((File_Tag *)ETFile1->FileTag->data)->composer == ((File_Tag *)ETFile2->FileTag->data)->composer))
591         return 0;
592 
593     if (!ETFile1->FileTag->data)
594     {
595         return -1;
596     }
597 
598     if (!ETFile2->FileTag->data)
599     {
600         return 1;
601     }
602 
603     return et_file_list_sort_string (((File_Tag *)ETFile1->FileTag->data)->composer,
604                                      ((File_Tag *)ETFile2->FileTag->data)->composer,
605                                      ETFile1, ETFile2,
606                                      g_settings_get_boolean (MainSettings,
607                                                              "sort-case-sensitive"));
608 }
609 
610 /*
611  * Comparison function for sorting by descending composer.
612  */
613 gint
ET_Comp_Func_Sort_File_By_Descending_Composer(const ET_File * ETFile1,const ET_File * ETFile2)614 ET_Comp_Func_Sort_File_By_Descending_Composer (const ET_File *ETFile1,
615                                                const ET_File *ETFile2)
616 {
617     return ET_Comp_Func_Sort_File_By_Ascending_Composer(ETFile2,ETFile1);
618 }
619 
620 
621 /*
622  * Comparison function for sorting by ascending original artist.
623  */
624 gint
ET_Comp_Func_Sort_File_By_Ascending_Orig_Artist(const ET_File * ETFile1,const ET_File * ETFile2)625 ET_Comp_Func_Sort_File_By_Ascending_Orig_Artist (const ET_File *ETFile1,
626                                                  const ET_File *ETFile2)
627 {
628    // Compare pointers just in case they are the same (e.g. both are NULL)
629    if ((ETFile1->FileTag->data == ETFile2->FileTag->data)
630    ||  (((File_Tag *)ETFile1->FileTag->data)->orig_artist == ((File_Tag *)ETFile2->FileTag->data)->orig_artist))
631         return 0;
632 
633     if (!ETFile1->FileTag->data)
634     {
635         return -1;
636     }
637 
638     if (!ETFile2->FileTag->data)
639     {
640         return 1;
641     }
642 
643     return et_file_list_sort_string (((File_Tag *)ETFile1->FileTag->data)->orig_artist,
644                                      ((File_Tag *)ETFile2->FileTag->data)->orig_artist,
645                                      ETFile1, ETFile2,
646                                      g_settings_get_boolean (MainSettings,
647                                                              "sort-case-sensitive"));
648 }
649 
650 /*
651  * Comparison function for sorting by descending original artist.
652  */
653 gint
ET_Comp_Func_Sort_File_By_Descending_Orig_Artist(const ET_File * ETFile1,const ET_File * ETFile2)654 ET_Comp_Func_Sort_File_By_Descending_Orig_Artist (const ET_File *ETFile1,
655                                                   const ET_File *ETFile2)
656 {
657     return ET_Comp_Func_Sort_File_By_Ascending_Orig_Artist(ETFile2,ETFile1);
658 }
659 
660 
661 /*
662  * Comparison function for sorting by ascending copyright.
663  */
664 gint
ET_Comp_Func_Sort_File_By_Ascending_Copyright(const ET_File * ETFile1,const ET_File * ETFile2)665 ET_Comp_Func_Sort_File_By_Ascending_Copyright (const ET_File *ETFile1,
666                                                const ET_File *ETFile2)
667 {
668    // Compare pointers just in case they are the same (e.g. both are NULL)
669    if ((ETFile1->FileTag->data == ETFile2->FileTag->data)
670    ||  (((File_Tag *)ETFile1->FileTag->data)->copyright == ((File_Tag *)ETFile2->FileTag->data)->copyright))
671         return 0;
672 
673     if (!ETFile1->FileTag->data)
674     {
675         return -1;
676     }
677 
678     if (!ETFile2->FileTag->data)
679     {
680         return 1;
681     }
682 
683     return et_file_list_sort_string (((File_Tag *)ETFile1->FileTag->data)->copyright,
684                                      ((File_Tag *)ETFile2->FileTag->data)->copyright,
685                                      ETFile1, ETFile2,
686                                      g_settings_get_boolean (MainSettings,
687                                                              "sort-case-sensitive"));
688 }
689 
690 /*
691  * Comparison function for sorting by descending copyright.
692  */
693 gint
ET_Comp_Func_Sort_File_By_Descending_Copyright(const ET_File * ETFile1,const ET_File * ETFile2)694 ET_Comp_Func_Sort_File_By_Descending_Copyright (const ET_File *ETFile1,
695                                                 const ET_File *ETFile2)
696 {
697     return ET_Comp_Func_Sort_File_By_Ascending_Copyright(ETFile2,ETFile1);
698 }
699 
700 
701 /*
702  * Comparison function for sorting by ascending URL.
703  */
704 gint
ET_Comp_Func_Sort_File_By_Ascending_Url(const ET_File * ETFile1,const ET_File * ETFile2)705 ET_Comp_Func_Sort_File_By_Ascending_Url (const ET_File *ETFile1,
706                                          const ET_File *ETFile2)
707 {
708     /* Compare pointers just in case they are the same (e.g. both are NULL). */
709     if ((ETFile1->FileTag->data == ETFile2->FileTag->data)
710         || (((File_Tag *)ETFile1->FileTag->data)->url
711             == ((File_Tag *)ETFile2->FileTag->data)->url))
712     {
713         return 0;
714     }
715 
716     if (!ETFile1->FileTag->data)
717     {
718         return -1;
719     }
720 
721     if (!ETFile2->FileTag->data)
722     {
723         return 1;
724     }
725 
726     return et_file_list_sort_string (((File_Tag *)ETFile1->FileTag->data)->url,
727                                      ((File_Tag *)ETFile2->FileTag->data)->url,
728                                      ETFile1, ETFile2,
729                                      g_settings_get_boolean (MainSettings,
730                                                              "sort-case-sensitive"));
731 }
732 
733 /*
734  * Comparison function for sorting by descending URL.
735  */
736 gint
ET_Comp_Func_Sort_File_By_Descending_Url(const ET_File * ETFile1,const ET_File * ETFile2)737 ET_Comp_Func_Sort_File_By_Descending_Url (const ET_File *ETFile1,
738                                           const ET_File *ETFile2)
739 {
740     return ET_Comp_Func_Sort_File_By_Ascending_Url(ETFile2,ETFile1);
741 }
742 
743 
744 /*
745  * Comparison function for sorting by ascending encoded by.
746  */
747 gint
ET_Comp_Func_Sort_File_By_Ascending_Encoded_By(const ET_File * ETFile1,const ET_File * ETFile2)748 ET_Comp_Func_Sort_File_By_Ascending_Encoded_By (const ET_File *ETFile1,
749                                                 const ET_File *ETFile2)
750 {
751     /* Compare pointers just in case they are the same (e.g. both are NULL). */
752     if ((ETFile1->FileTag->data == ETFile2->FileTag->data)
753         || (((File_Tag *)ETFile1->FileTag->data)->encoded_by
754             == ((File_Tag *)ETFile2->FileTag->data)->encoded_by))
755     {
756         return 0;
757     }
758 
759     if (!ETFile1->FileTag->data)
760     {
761         return -1;
762     }
763 
764     if (!ETFile2->FileTag->data)
765     {
766         return 1;
767     }
768 
769     return et_file_list_sort_string (((File_Tag *)ETFile1->FileTag->data)->encoded_by,
770                                      ((File_Tag *)ETFile2->FileTag->data)->encoded_by,
771                                      ETFile1, ETFile2,
772                                      g_settings_get_boolean (MainSettings,
773                                                              "sort-case-sensitive"));
774 }
775 
776 /*
777  * Comparison function for sorting by descendingencoded by.
778  */
779 gint
ET_Comp_Func_Sort_File_By_Descending_Encoded_By(const ET_File * ETFile1,const ET_File * ETFile2)780 ET_Comp_Func_Sort_File_By_Descending_Encoded_By (const ET_File *ETFile1,
781                                                  const ET_File *ETFile2)
782 {
783     return ET_Comp_Func_Sort_File_By_Ascending_Encoded_By(ETFile2,ETFile1);
784 }
785 
786 
787 /*
788  * Comparison function for sorting by ascending file type (mp3, ogg, ...).
789  */
790 gint
ET_Comp_Func_Sort_File_By_Ascending_File_Type(const ET_File * ETFile1,const ET_File * ETFile2)791 ET_Comp_Func_Sort_File_By_Ascending_File_Type (const ET_File *ETFile1,
792                                                const ET_File *ETFile2)
793 {
794     if ( !ETFile1->ETFileDescription ) return -1;
795     if ( !ETFile2->ETFileDescription ) return 1;
796 
797     // Second criterion
798     if (ETFile1->ETFileDescription->FileType == ETFile2->ETFileDescription->FileType)
799         return ET_Comp_Func_Sort_File_By_Ascending_Filename(ETFile1,ETFile2);
800 
801     // First criterion
802     return (ETFile1->ETFileDescription->FileType - ETFile2->ETFileDescription->FileType);
803 }
804 
805 /*
806  * Comparison function for sorting by descending file type (mp3, ogg, ...).
807  */
808 gint
ET_Comp_Func_Sort_File_By_Descending_File_Type(const ET_File * ETFile1,const ET_File * ETFile2)809 ET_Comp_Func_Sort_File_By_Descending_File_Type (const ET_File *ETFile1,
810                                                 const ET_File *ETFile2)
811 {
812     return ET_Comp_Func_Sort_File_By_Ascending_File_Type(ETFile2,ETFile1);
813 }
814 
815 
816 /*
817  * Comparison function for sorting by ascending file size.
818  */
819 gint
ET_Comp_Func_Sort_File_By_Ascending_File_Size(const ET_File * ETFile1,const ET_File * ETFile2)820 ET_Comp_Func_Sort_File_By_Ascending_File_Size (const ET_File *ETFile1,
821                                                const ET_File *ETFile2)
822 {
823     if ( !ETFile1->ETFileInfo ) return -1;
824     if ( !ETFile2->ETFileInfo ) return 1;
825 
826     // Second criterion
827     if (ETFile1->ETFileInfo->size == ETFile2->ETFileInfo->size)
828         return ET_Comp_Func_Sort_File_By_Ascending_Filename(ETFile1,ETFile2);
829 
830     // First criterion
831     return (ETFile1->ETFileInfo->size - ETFile2->ETFileInfo->size);
832 }
833 
834 /*
835  * Comparison function for sorting by descending file size.
836  */
837 gint
ET_Comp_Func_Sort_File_By_Descending_File_Size(const ET_File * ETFile1,const ET_File * ETFile2)838 ET_Comp_Func_Sort_File_By_Descending_File_Size (const ET_File *ETFile1,
839                                                 const ET_File *ETFile2)
840 {
841     return ET_Comp_Func_Sort_File_By_Ascending_File_Size(ETFile2,ETFile1);
842 }
843 
844 
845 /*
846  * Comparison function for sorting by ascending file duration.
847  */
848 gint
ET_Comp_Func_Sort_File_By_Ascending_File_Duration(const ET_File * ETFile1,const ET_File * ETFile2)849 ET_Comp_Func_Sort_File_By_Ascending_File_Duration (const ET_File *ETFile1,
850                                                    const ET_File *ETFile2)
851 {
852     if ( !ETFile1->ETFileInfo ) return -1;
853     if ( !ETFile2->ETFileInfo ) return 1;
854 
855     // Second criterion
856     if (ETFile1->ETFileInfo->duration == ETFile2->ETFileInfo->duration)
857         return ET_Comp_Func_Sort_File_By_Ascending_Filename(ETFile1,ETFile2);
858 
859     // First criterion
860     return (ETFile1->ETFileInfo->duration - ETFile2->ETFileInfo->duration);
861 }
862 
863 /*
864  * Comparison function for sorting by descending file duration.
865  */
866 gint
ET_Comp_Func_Sort_File_By_Descending_File_Duration(const ET_File * ETFile1,const ET_File * ETFile2)867 ET_Comp_Func_Sort_File_By_Descending_File_Duration (const ET_File *ETFile1,
868                                                     const ET_File *ETFile2)
869 {
870     return ET_Comp_Func_Sort_File_By_Ascending_File_Duration(ETFile2,ETFile1);
871 }
872 
873 
874 /*
875  * Comparison function for sorting by ascending file bitrate.
876  */
877 gint
ET_Comp_Func_Sort_File_By_Ascending_File_Bitrate(const ET_File * ETFile1,const ET_File * ETFile2)878 ET_Comp_Func_Sort_File_By_Ascending_File_Bitrate (const ET_File *ETFile1,
879                                                   const ET_File *ETFile2)
880 {
881     if ( !ETFile1->ETFileInfo ) return -1;
882     if ( !ETFile2->ETFileInfo ) return 1;
883 
884     // Second criterion
885     if (ETFile1->ETFileInfo->bitrate == ETFile2->ETFileInfo->bitrate)
886         return ET_Comp_Func_Sort_File_By_Ascending_Filename(ETFile1,ETFile2);
887 
888     // First criterion
889     return (ETFile1->ETFileInfo->bitrate - ETFile2->ETFileInfo->bitrate);
890 }
891 
892 /*
893  * Comparison function for sorting by descending file bitrate.
894  */
895 gint
ET_Comp_Func_Sort_File_By_Descending_File_Bitrate(const ET_File * ETFile1,const ET_File * ETFile2)896 ET_Comp_Func_Sort_File_By_Descending_File_Bitrate (const ET_File *ETFile1,
897                                                    const ET_File *ETFile2)
898 {
899     return ET_Comp_Func_Sort_File_By_Ascending_File_Bitrate(ETFile2,ETFile1);
900 }
901 
902 
903 /*
904  * Comparison function for sorting by ascending file samplerate.
905  */
906 gint
ET_Comp_Func_Sort_File_By_Ascending_File_Samplerate(const ET_File * ETFile1,const ET_File * ETFile2)907 ET_Comp_Func_Sort_File_By_Ascending_File_Samplerate (const ET_File *ETFile1,
908                                                      const ET_File *ETFile2)
909 {
910     if ( !ETFile1->ETFileInfo ) return -1;
911     if ( !ETFile2->ETFileInfo ) return 1;
912 
913     // Second criterion
914     if (ETFile1->ETFileInfo->samplerate == ETFile2->ETFileInfo->samplerate)
915         return ET_Comp_Func_Sort_File_By_Ascending_Filename(ETFile1,ETFile2);
916 
917     // First criterion
918     return (ETFile1->ETFileInfo->samplerate - ETFile2->ETFileInfo->samplerate);
919 }
920 
921 /*
922  * Comparison function for sorting by descending file samplerate.
923  */
924 gint
ET_Comp_Func_Sort_File_By_Descending_File_Samplerate(const ET_File * ETFile1,const ET_File * ETFile2)925 ET_Comp_Func_Sort_File_By_Descending_File_Samplerate (const ET_File *ETFile1,
926                                                       const ET_File *ETFile2)
927 {
928     return ET_Comp_Func_Sort_File_By_Ascending_File_Samplerate(ETFile2,ETFile1);
929 }
930 
931 /*********************
932  * Freeing functions *
933  *********************/
934 
935 /*
936  * Frees one item of the full main list of files.
937  */
938 void
ET_Free_File_List_Item(ET_File * ETFile)939 ET_Free_File_List_Item (ET_File *ETFile)
940 {
941     if (ETFile)
942     {
943         /* Frees the lists */
944         if (ETFile->FileNameList)
945         {
946             ET_Free_File_Name_List(ETFile->FileNameList);
947         }
948         if (ETFile->FileNameListBak)
949         {
950             ET_Free_File_Name_List(ETFile->FileNameListBak);
951         }
952         if (ETFile->FileTagList)
953         {
954             ET_Free_File_Tag_List (ETFile->FileTagList);
955         }
956         if (ETFile->FileTagListBak)
957         {
958             ET_Free_File_Tag_List (ETFile->FileTagListBak);
959         }
960         /* Frees infos of ETFileInfo */
961         if (ETFile->ETFileInfo)
962         {
963             et_file_info_free (ETFile->ETFileInfo);
964         }
965 
966         g_free(ETFile->ETFileExtension);
967         g_slice_free (ET_File, ETFile);
968     }
969 }
970 
971 
972 /*
973  * Frees the full list: GList *FileNameList.
974  */
975 static gboolean
ET_Free_File_Name_List(GList * FileNameList)976 ET_Free_File_Name_List (GList *FileNameList)
977 {
978     g_return_val_if_fail (FileNameList != NULL, FALSE);
979 
980     FileNameList = g_list_first (FileNameList);
981 
982     g_list_free_full (FileNameList, (GDestroyNotify)et_file_name_free);
983 
984     return TRUE;
985 }
986 
987 /*
988  * Frees the full list: GList *TagList.
989  */
990 static gboolean
ET_Free_File_Tag_List(GList * FileTagList)991 ET_Free_File_Tag_List (GList *FileTagList)
992 {
993     GList *l;
994 
995     g_return_val_if_fail (FileTagList != NULL, FALSE);
996 
997     FileTagList = g_list_first (FileTagList);
998 
999     for (l = FileTagList; l != NULL; l = g_list_next (l))
1000     {
1001         if ((File_Tag *)l->data)
1002         {
1003             et_file_tag_free ((File_Tag *)l->data);
1004         }
1005     }
1006 
1007     g_list_free (FileTagList);
1008 
1009     return TRUE;
1010 }
1011 
1012 
1013 /*********************
1014  * Copying functions *
1015  *********************/
1016 
1017 /********************
1018  * Saving functions *
1019  ********************/
1020 
1021 /*
1022  * Do the same thing of ET_Save_File_Name_From_UI, but without getting the
1023  * data from the UI.
1024  */
1025 gboolean
ET_Save_File_Name_Internal(const ET_File * ETFile,File_Name * FileName)1026 ET_Save_File_Name_Internal (const ET_File *ETFile,
1027                             File_Name *FileName)
1028 {
1029     gchar *filename_new = NULL;
1030     gchar *dirname = NULL;
1031     gchar *filename;
1032     gchar *extension;
1033     gchar *pos;
1034     gboolean success;
1035 
1036     g_return_val_if_fail (ETFile != NULL && FileName != NULL, FALSE);
1037 
1038     // Get the current path to the file
1039     dirname = g_path_get_dirname( ((File_Name *)ETFile->FileNameNew->data)->value );
1040 
1041     // Get the name of file (and rebuild it with extension with a 'correct' case)
1042     filename = g_path_get_basename( ((File_Name *)ETFile->FileNameNew->data)->value );
1043 
1044     // Remove the extension
1045     if ((pos=strrchr(filename, '.'))!=NULL)
1046         *pos = 0;
1047 
1048     /* Convert filename extension (lower/upper). */
1049     extension = ET_File_Format_File_Extension (ETFile);
1050 
1051     // Check length of filename
1052     //ET_File_Name_Check_Length(ETFile,filename);
1053 
1054     // Regenerate the new filename (without path)
1055     filename_new = g_strconcat(filename,extension,NULL);
1056     g_free(extension);
1057     g_free(filename);
1058 
1059     success = et_file_name_set_from_components (FileName, filename_new,
1060                                                 dirname,
1061                                                 g_settings_get_boolean (MainSettings,
1062                                                                         "rename-replace-illegal-chars"));
1063 
1064     g_free (filename_new);
1065     g_free (dirname);
1066     return success;
1067 }
1068 
1069 /*
1070  * Do the same thing of et_tag_area_create_file_tag without getting the data from the UI.
1071  */
1072 gboolean
ET_Save_File_Tag_Internal(ET_File * ETFile,File_Tag * FileTag)1073 ET_Save_File_Tag_Internal (ET_File *ETFile, File_Tag *FileTag)
1074 {
1075     File_Tag *FileTagCur;
1076 
1077 
1078     g_return_val_if_fail (ETFile != NULL && ETFile->FileTag != NULL
1079                           && FileTag != NULL, FALSE);
1080 
1081     FileTagCur = (File_Tag *)ETFile->FileTag->data;
1082 
1083     /* Title */
1084     if (!et_str_empty (FileTagCur->title))
1085     {
1086         FileTag->title = g_strdup(FileTagCur->title);
1087         g_strstrip (FileTag->title);
1088     } else
1089     {
1090         FileTag->title = NULL;
1091     }
1092 
1093     /* Artist */
1094     if (!et_str_empty (FileTagCur->artist))
1095     {
1096         FileTag->artist = g_strdup(FileTagCur->artist);
1097         g_strstrip (FileTag->artist);
1098     } else
1099     {
1100         FileTag->artist = NULL;
1101     }
1102 
1103     /* Album Artist */
1104     if (!et_str_empty (FileTagCur->album_artist))
1105     {
1106         FileTag->album_artist = g_strdup(FileTagCur->album_artist);
1107         g_strstrip (FileTag->album_artist);
1108     } else
1109     {
1110         FileTag->album_artist = NULL;
1111     }
1112 
1113 
1114     /* Album */
1115     if (!et_str_empty (FileTagCur->album))
1116     {
1117         FileTag->album = g_strdup(FileTagCur->album);
1118         g_strstrip (FileTag->album);
1119     } else
1120     {
1121         FileTag->album = NULL;
1122     }
1123 
1124 
1125     /* Disc Number */
1126     if (!et_str_empty (FileTagCur->disc_number))
1127     {
1128         FileTag->disc_number = g_strdup(FileTagCur->disc_number);
1129         g_strstrip (FileTag->disc_number);
1130     } else
1131     {
1132         FileTag->disc_number = NULL;
1133     }
1134 
1135 
1136     /* Discs Total */
1137     if (!et_str_empty (FileTagCur->disc_total))
1138     {
1139         FileTag->disc_total = et_disc_number_to_string (atoi (FileTagCur->disc_total));
1140         g_strstrip (FileTag->disc_total);
1141     }
1142     else
1143     {
1144         FileTag->disc_total = NULL;
1145     }
1146 
1147 
1148     /* Year */
1149     if (!et_str_empty (FileTagCur->year))
1150     {
1151         FileTag->year = g_strdup(FileTagCur->year);
1152         g_strstrip (FileTag->year);
1153     } else
1154     {
1155         FileTag->year = NULL;
1156     }
1157 
1158 
1159     /* Track */
1160     if (!et_str_empty (FileTagCur->track))
1161     {
1162         gchar *tmp_str;
1163 
1164         FileTag->track = et_track_number_to_string (atoi (FileTagCur->track));
1165 
1166         /* This field must contain only digits. */
1167         tmp_str = FileTag->track;
1168         while (g_ascii_isdigit (*tmp_str)) tmp_str++;
1169             *tmp_str = 0;
1170         g_strstrip (FileTag->track);
1171     } else
1172     {
1173         FileTag->track = NULL;
1174     }
1175 
1176 
1177     /* Track Total */
1178     if (!et_str_empty (FileTagCur->track_total))
1179     {
1180         FileTag->track_total = et_track_number_to_string (atoi (FileTagCur->track_total));
1181         g_strstrip (FileTag->track_total);
1182     } else
1183     {
1184         FileTag->track_total = NULL;
1185     }
1186 
1187 
1188     /* Genre */
1189     if (!et_str_empty (FileTagCur->genre))
1190     {
1191         FileTag->genre = g_strdup(FileTagCur->genre);
1192         g_strstrip (FileTag->genre);
1193     } else
1194     {
1195         FileTag->genre = NULL;
1196     }
1197 
1198 
1199     /* Comment */
1200     if (!et_str_empty (FileTagCur->comment))
1201     {
1202         FileTag->comment = g_strdup(FileTagCur->comment);
1203         g_strstrip (FileTag->comment);
1204     } else
1205     {
1206         FileTag->comment = NULL;
1207     }
1208 
1209 
1210     /* Composer */
1211     if (!et_str_empty (FileTagCur->composer))
1212     {
1213         FileTag->composer = g_strdup(FileTagCur->composer);
1214         g_strstrip (FileTag->composer);
1215     } else
1216     {
1217         FileTag->composer = NULL;
1218     }
1219 
1220 
1221     /* Original Artist */
1222     if (!et_str_empty (FileTagCur->orig_artist))
1223     {
1224         FileTag->orig_artist = g_strdup(FileTagCur->orig_artist);
1225         g_strstrip (FileTag->orig_artist);
1226     } else
1227     {
1228         FileTag->orig_artist = NULL;
1229     }
1230 
1231 
1232     /* Copyright */
1233     if (!et_str_empty (FileTagCur->copyright))
1234     {
1235         FileTag->copyright = g_strdup(FileTagCur->copyright);
1236         g_strstrip (FileTag->copyright);
1237     } else
1238     {
1239         FileTag->copyright = NULL;
1240     }
1241 
1242 
1243     /* URL */
1244     if (!et_str_empty (FileTagCur->url))
1245     {
1246         FileTag->url = g_strdup(FileTagCur->url);
1247         g_strstrip (FileTag->url);
1248     } else
1249     {
1250         FileTag->url = NULL;
1251     }
1252 
1253 
1254     /* Encoded by */
1255     if (!et_str_empty (FileTagCur->encoded_by))
1256     {
1257         FileTag->encoded_by = g_strdup(FileTagCur->encoded_by);
1258         g_strstrip (FileTag->encoded_by);
1259     } else
1260     {
1261         FileTag->encoded_by = NULL;
1262     }
1263 
1264 
1265     /* Picture */
1266     et_file_tag_set_picture (FileTag, FileTagCur->picture);
1267 
1268     return TRUE;
1269 }
1270 
1271 
1272 /*
1273  * Save data contained into File_Tag structure to the file on hard disk.
1274  */
1275 gboolean
ET_Save_File_Tag_To_HD(ET_File * ETFile,GError ** error)1276 ET_Save_File_Tag_To_HD (ET_File *ETFile, GError **error)
1277 {
1278     const ET_File_Description *description;
1279     const gchar *cur_filename;
1280     const gchar *cur_filename_utf8;
1281     gboolean state;
1282     GFile *file;
1283     GFileInfo *fileinfo;
1284 
1285     g_return_val_if_fail (ETFile != NULL, FALSE);
1286     g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
1287 
1288     cur_filename = ((File_Name *)(ETFile->FileNameCur)->data)->value;
1289     cur_filename_utf8 = ((File_Name *)(ETFile->FileNameCur)->data)->value_utf8;
1290 
1291     description = ETFile->ETFileDescription;
1292 
1293     /* Store the file timestamps (in case they are to be preserved) */
1294     file = g_file_new_for_path (cur_filename);
1295     fileinfo = g_file_query_info (file, "time::*", G_FILE_QUERY_INFO_NONE,
1296                                   NULL, NULL);
1297 
1298     switch (description->TagType)
1299     {
1300 #ifdef ENABLE_MP3
1301         case ID3_TAG:
1302             state = id3tag_write_file_tag (ETFile, error);
1303             break;
1304 #endif
1305 #ifdef ENABLE_OGG
1306         case OGG_TAG:
1307             state = ogg_tag_write_file_tag (ETFile, error);
1308             break;
1309 #endif
1310 #ifdef ENABLE_FLAC
1311         case FLAC_TAG:
1312             state = flac_tag_write_file_tag (ETFile, error);
1313             break;
1314 #endif
1315         case APE_TAG:
1316             state = ape_tag_write_file_tag (ETFile, error);
1317             break;
1318 #ifdef ENABLE_MP4
1319         case MP4_TAG:
1320             state = mp4tag_write_file_tag (ETFile, error);
1321             break;
1322 #endif
1323 #ifdef ENABLE_WAVPACK
1324         case WAVPACK_TAG:
1325             state = wavpack_tag_write_file_tag (ETFile, error);
1326             break;
1327 #endif
1328 #ifdef ENABLE_OPUS
1329         case OPUS_TAG:
1330             state = ogg_tag_write_file_tag (ETFile, error);
1331             break;
1332 #endif
1333 #ifndef ENABLE_MP3
1334         case ID3_TAG:
1335 #endif
1336 #ifndef ENABLE_OGG
1337         case OGG_TAG:
1338 #endif
1339 #ifndef ENABLE_FLAC
1340         case FLAC_TAG:
1341 #endif
1342 #ifndef ENABLE_MP4
1343         case MP4_TAG:
1344 #endif
1345 #ifndef ENABLE_WAVPACK
1346         case WAVPACK_TAG:
1347 #endif
1348 #ifndef ENABLE_OPUS
1349         case OPUS_TAG:
1350 #endif
1351         case UNKNOWN_TAG:
1352         default:
1353             Log_Print (LOG_ERROR,
1354                        "Saving to HD: Undefined function for tag type '%d' (file %s).",
1355                        (gint)description->TagType, cur_filename_utf8);
1356             state = FALSE;
1357             break;
1358     }
1359 
1360     /* Update properties for the file. */
1361     if (fileinfo)
1362     {
1363         if (g_settings_get_boolean (MainSettings,
1364                                     "file-preserve-modification-time"))
1365         {
1366             g_file_set_attributes_from_info (file, fileinfo,
1367                                              G_FILE_QUERY_INFO_NONE,
1368                                              NULL, NULL);
1369         }
1370 
1371         g_object_unref (fileinfo);
1372     }
1373 
1374     /* Update the stored file modification time to prevent EasyTAG from warning
1375      * that an external program has changed the file. */
1376     fileinfo = g_file_query_info (file,
1377                                   G_FILE_ATTRIBUTE_TIME_MODIFIED,
1378                                   G_FILE_QUERY_INFO_NONE, NULL, NULL);
1379 
1380     if (fileinfo)
1381     {
1382         ETFile->FileModificationTime = g_file_info_get_attribute_uint64 (fileinfo,
1383                                                                          G_FILE_ATTRIBUTE_TIME_MODIFIED);
1384         g_object_unref (fileinfo);
1385     }
1386 
1387     g_object_unref (file);
1388 
1389     if (state==TRUE)
1390     {
1391 
1392         /* Update date and time of the parent directory of the file after
1393          * changing the tag value (ex: needed for Amarok for refreshing). Note
1394          * that when renaming a file the parent directory is automatically
1395          * updated.
1396          */
1397         if (g_settings_get_boolean (MainSettings,
1398                                     "file-update-parent-modification-time"))
1399         {
1400             gchar *path = g_path_get_dirname (cur_filename);
1401             g_utime (path, NULL);
1402             g_free (path);
1403         }
1404 
1405         ET_Mark_File_Tag_As_Saved(ETFile);
1406         return TRUE;
1407     }
1408     else
1409     {
1410         g_assert (error == NULL || *error != NULL);
1411 
1412         return FALSE;
1413     }
1414 }
1415 
1416 /*
1417  * Check if 'FileName' and 'FileTag' differ with those of 'ETFile'.
1418  * Manage undo feature for the ETFile and the main undo list.
1419  */
1420 gboolean
ET_Manage_Changes_Of_File_Data(ET_File * ETFile,File_Name * FileName,File_Tag * FileTag)1421 ET_Manage_Changes_Of_File_Data (ET_File *ETFile,
1422                                 File_Name *FileName,
1423                                 File_Tag *FileTag)
1424 {
1425     gboolean undo_added = FALSE;
1426 
1427     g_return_val_if_fail (ETFile != NULL, FALSE);
1428 
1429     /*
1430      * Detect changes of filename and generate the filename undo list
1431      */
1432     if (FileName)
1433     {
1434         if (ETFile->FileNameNew
1435             && et_file_name_detect_difference ((File_Name *)(ETFile->FileNameNew)->data,
1436                                                FileName) == TRUE)
1437         {
1438             ET_Add_File_Name_To_List(ETFile,FileName);
1439             undo_added |= TRUE;
1440         }else
1441         {
1442             et_file_name_free (FileName);
1443         }
1444     }
1445 
1446     /*
1447      * Detect changes in tag data and generate the tag undo list
1448      */
1449     if (FileTag)
1450     {
1451         if (ETFile->FileTag
1452             && et_file_tag_detect_difference ((File_Tag *)(ETFile->FileTag)->data,
1453                                               FileTag) == TRUE)
1454         {
1455             ET_Add_File_Tag_To_List(ETFile,FileTag);
1456             undo_added |= TRUE;
1457         }
1458         else
1459         {
1460             et_file_tag_free (FileTag);
1461         }
1462     }
1463 
1464     /*
1465      * Generate main undo (file history of modifications)
1466      */
1467     if (undo_added)
1468     {
1469         ETCore->ETHistoryFileList = et_history_list_add (ETCore->ETHistoryFileList,
1470                                                          ETFile);
1471     }
1472 
1473     //return TRUE;
1474     return undo_added;
1475 }
1476 
1477 /*
1478  * Add a FileName item to the history list of ETFile
1479  */
1480 static gboolean
ET_Add_File_Name_To_List(ET_File * ETFile,File_Name * FileName)1481 ET_Add_File_Name_To_List (ET_File *ETFile, File_Name *FileName)
1482 {
1483     GList *cut_list = NULL;
1484 
1485     g_return_val_if_fail (ETFile != NULL && FileName != NULL, FALSE);
1486 
1487     /* How it works : Cut the FileNameList list after the current item,
1488      * and appends it to the FileNameListBak list for saving the data.
1489      * Then appends the new item to the FileNameList list */
1490     if (ETFile->FileNameList)
1491     {
1492         cut_list = ETFile->FileNameNew->next; // Cut after the current item...
1493         ETFile->FileNameNew->next = NULL;
1494     }
1495     if (cut_list)
1496         cut_list->prev = NULL;
1497 
1498     /* Add the new item to the list */
1499     ETFile->FileNameList = g_list_append(ETFile->FileNameList,FileName);
1500     /* Set the current item to use */
1501     ETFile->FileNameNew  = g_list_last(ETFile->FileNameList);
1502     /* Backup list */
1503     /* FIX ME! Keep only the saved item */
1504     ETFile->FileNameListBak = g_list_concat(ETFile->FileNameListBak,cut_list);
1505 
1506     return TRUE;
1507 }
1508 
1509 /*
1510  * Add a FileTag item to the history list of ETFile
1511  */
1512 static gboolean
ET_Add_File_Tag_To_List(ET_File * ETFile,File_Tag * FileTag)1513 ET_Add_File_Tag_To_List (ET_File *ETFile, File_Tag *FileTag)
1514 {
1515     GList *cut_list = NULL;
1516 
1517     g_return_val_if_fail (ETFile != NULL && FileTag != NULL, FALSE);
1518 
1519     if (ETFile->FileTag)
1520     {
1521         cut_list = ETFile->FileTag->next; // Cut after the current item...
1522         ETFile->FileTag->next = NULL;
1523     }
1524     if (cut_list)
1525         cut_list->prev = NULL;
1526 
1527     /* Add the new item to the list */
1528     ETFile->FileTagList = g_list_append(ETFile->FileTagList,FileTag);
1529     /* Set the current item to use */
1530     ETFile->FileTag     = g_list_last(ETFile->FileTagList);
1531     /* Backup list */
1532     ETFile->FileTagListBak = g_list_concat(ETFile->FileTagListBak,cut_list);
1533 
1534     return TRUE;
1535 }
1536 
1537 /*
1538  * Applies one undo to the ETFile data (to reload the previous data).
1539  * Returns TRUE if an undo had been applied.
1540  */
ET_Undo_File_Data(ET_File * ETFile)1541 gboolean ET_Undo_File_Data (ET_File *ETFile)
1542 {
1543     gboolean has_filename_undo_data = FALSE;
1544     gboolean has_filetag_undo_data  = FALSE;
1545     guint    filename_key, filetag_key, undo_key;
1546 
1547     g_return_val_if_fail (ETFile != NULL, FALSE);
1548 
1549     /* Find the valid key */
1550     if (ETFile->FileNameNew->prev && ETFile->FileNameNew->data)
1551         filename_key = ((File_Name *)ETFile->FileNameNew->data)->key;
1552     else
1553         filename_key = 0;
1554     if (ETFile->FileTag->prev && ETFile->FileTag->data)
1555         filetag_key = ((File_Tag *)ETFile->FileTag->data)->key;
1556     else
1557         filetag_key = 0;
1558     // The key to use
1559     undo_key = MAX(filename_key,filetag_key);
1560 
1561     /* Undo filename */
1562     if (ETFile->FileNameNew->prev && ETFile->FileNameNew->data
1563     && (undo_key==((File_Name *)ETFile->FileNameNew->data)->key))
1564     {
1565         ETFile->FileNameNew = ETFile->FileNameNew->prev;
1566         has_filename_undo_data = TRUE; // To indicate that an undo has been applied
1567     }
1568 
1569     /* Undo tag data */
1570     if (ETFile->FileTag->prev && ETFile->FileTag->data
1571     && (undo_key==((File_Tag *)ETFile->FileTag->data)->key))
1572     {
1573         ETFile->FileTag = ETFile->FileTag->prev;
1574         has_filetag_undo_data  = TRUE;
1575     }
1576 
1577     return has_filename_undo_data | has_filetag_undo_data;
1578 }
1579 
1580 
1581 /*
1582  * Returns TRUE if file contains undo data (filename or tag)
1583  */
1584 gboolean
ET_File_Data_Has_Undo_Data(const ET_File * ETFile)1585 ET_File_Data_Has_Undo_Data (const ET_File *ETFile)
1586 {
1587     gboolean has_filename_undo_data = FALSE;
1588     gboolean has_filetag_undo_data  = FALSE;
1589 
1590     g_return_val_if_fail (ETFile != NULL, FALSE);
1591 
1592     if (ETFile->FileNameNew && ETFile->FileNameNew->prev) has_filename_undo_data = TRUE;
1593     if (ETFile->FileTag && ETFile->FileTag->prev)         has_filetag_undo_data  = TRUE;
1594 
1595     return has_filename_undo_data | has_filetag_undo_data;
1596 }
1597 
1598 
1599 /*
1600  * Applies one redo to the ETFile data. Returns TRUE if a redo had been applied.
1601  */
ET_Redo_File_Data(ET_File * ETFile)1602 gboolean ET_Redo_File_Data (ET_File *ETFile)
1603 {
1604     gboolean has_filename_redo_data = FALSE;
1605     gboolean has_filetag_redo_data  = FALSE;
1606     guint    filename_key, filetag_key, undo_key;
1607 
1608     g_return_val_if_fail (ETFile != NULL, FALSE);
1609 
1610     /* Find the valid key */
1611     if (ETFile->FileNameNew->next && ETFile->FileNameNew->next->data)
1612         filename_key = ((File_Name *)ETFile->FileNameNew->next->data)->key;
1613     else
1614         filename_key = (guint)~0; // To have the max value for guint
1615     if (ETFile->FileTag->next && ETFile->FileTag->next->data)
1616         filetag_key = ((File_Tag *)ETFile->FileTag->next->data)->key;
1617     else
1618         filetag_key = (guint)~0; // To have the max value for guint
1619     // The key to use
1620     undo_key = MIN(filename_key,filetag_key);
1621 
1622     /* Redo filename */
1623     if (ETFile->FileNameNew->next && ETFile->FileNameNew->next->data
1624     && (undo_key==((File_Name *)ETFile->FileNameNew->next->data)->key))
1625     {
1626         ETFile->FileNameNew = ETFile->FileNameNew->next;
1627         has_filename_redo_data = TRUE; // To indicate that a redo has been applied
1628     }
1629 
1630     /* Redo tag data */
1631     if (ETFile->FileTag->next && ETFile->FileTag->next->data
1632     && (undo_key==((File_Tag *)ETFile->FileTag->next->data)->key))
1633     {
1634         ETFile->FileTag = ETFile->FileTag->next;
1635         has_filetag_redo_data  = TRUE;
1636     }
1637 
1638     return has_filename_redo_data | has_filetag_redo_data;
1639 }
1640 
1641 
1642 /*
1643  * Returns TRUE if file contains redo data (filename or tag)
1644  */
1645 gboolean
ET_File_Data_Has_Redo_Data(const ET_File * ETFile)1646 ET_File_Data_Has_Redo_Data (const ET_File *ETFile)
1647 {
1648     gboolean has_filename_redo_data = FALSE;
1649     gboolean has_filetag_redo_data  = FALSE;
1650 
1651     g_return_val_if_fail (ETFile != NULL, FALSE);
1652 
1653     if (ETFile->FileNameNew && ETFile->FileNameNew->next) has_filename_redo_data = TRUE;
1654     if (ETFile->FileTag && ETFile->FileTag->next)         has_filetag_redo_data  = TRUE;
1655 
1656     return has_filename_redo_data | has_filetag_redo_data;
1657 }
1658 
1659 /*
1660  * Checks if the current files had been changed but not saved.
1661  * Returns TRUE if the file has been saved.
1662  * Returns FALSE if some changes haven't been saved.
1663  */
1664 gboolean
et_file_check_saved(const ET_File * ETFile)1665 et_file_check_saved (const ET_File *ETFile)
1666 {
1667     const File_Tag *FileTag = NULL;
1668     const File_Name *FileNameNew = NULL;
1669 
1670     g_return_val_if_fail (ETFile != NULL, TRUE);
1671 
1672     if (ETFile->FileTag)
1673     {
1674         FileTag = ETFile->FileTag->data;
1675     }
1676 
1677     if (ETFile->FileNameNew)
1678     {
1679         FileNameNew = ETFile->FileNameNew->data;
1680     }
1681 
1682     /* Check if the tag has been changed. */
1683     if (FileTag && !FileTag->saved)
1684     {
1685         return FALSE;
1686     }
1687 
1688     /* Check if name of file has been changed. */
1689     if (FileNameNew && !FileNameNew->saved)
1690     {
1691         return FALSE;
1692     }
1693 
1694     /* No changes. */
1695     return TRUE;
1696 }
1697 
1698 /*******************
1699  * Extra functions *
1700  *******************/
1701 
1702 /*
1703  * Set to TRUE the value of 'FileTag->saved' for the File_Tag item passed in parameter.
1704  * And set ALL other values of the list to FALSE.
1705  */
1706 static void
Set_Saved_Value_Of_File_Tag(File_Tag * FileTag,gboolean saved)1707 Set_Saved_Value_Of_File_Tag (File_Tag *FileTag, gboolean saved)
1708 {
1709     if (FileTag) FileTag->saved = saved;
1710 }
1711 
1712 static void
ET_Mark_File_Tag_As_Saved(ET_File * ETFile)1713 ET_Mark_File_Tag_As_Saved (ET_File *ETFile)
1714 {
1715     File_Tag *FileTag;
1716     GList *FileTagList;
1717 
1718     FileTag     = (File_Tag *)ETFile->FileTag->data;    // The current FileTag, to set to TRUE
1719     FileTagList = ETFile->FileTagList;
1720     g_list_foreach(FileTagList,(GFunc)Set_Saved_Value_Of_File_Tag,FALSE); // All other FileTag set to FALSE
1721     FileTag->saved = TRUE; // The current FileTag set to TRUE
1722 }
1723 
1724 
ET_Mark_File_Name_As_Saved(ET_File * ETFile)1725 void ET_Mark_File_Name_As_Saved (ET_File *ETFile)
1726 {
1727     File_Name *FileNameNew;
1728     GList *FileNameList;
1729 
1730     FileNameNew  = (File_Name *)ETFile->FileNameNew->data;    // The current FileName, to set to TRUE
1731     FileNameList = ETFile->FileNameList;
1732     g_list_foreach(FileNameList,(GFunc)Set_Saved_Value_Of_File_Tag,FALSE);
1733     FileNameNew->saved = TRUE;
1734 }
1735 
1736 /*
1737  * et_file_generate_name:
1738  * @ETFile: the file from which to read the existing name
1739  * @new_file_name_utf8: UTF-8 encoded new filename
1740  *
1741  * This function generates a new filename using path of the old file and the
1742  * new name.
1743  *
1744  * Returns: a newly-allocated filename, in UTF-8
1745  */
1746 #if 1
1747 gchar *
et_file_generate_name(const ET_File * ETFile,const gchar * new_file_name_utf8)1748 et_file_generate_name (const ET_File *ETFile,
1749                        const gchar *new_file_name_utf8)
1750 {
1751     gchar *dirname_utf8;
1752 
1753     g_return_val_if_fail (ETFile && ETFile->FileNameNew->data, NULL);
1754     g_return_val_if_fail (new_file_name_utf8, NULL);
1755 
1756     if ((dirname_utf8 = g_path_get_dirname (((File_Name *)ETFile->FileNameNew->data)->value_utf8)))
1757     {
1758         gchar *extension;
1759         gchar *new_file_name_path_utf8;
1760 
1761         /* Convert filename extension (lower/upper). */
1762         extension = ET_File_Format_File_Extension (ETFile);
1763 
1764         // Check length of filename (limit ~255 characters)
1765         //ET_File_Name_Check_Length(ETFile,new_file_name_utf8);
1766 
1767         if (g_path_is_absolute (new_file_name_utf8))
1768         {
1769             /* Just add the extension. */
1770             new_file_name_path_utf8 = g_strconcat (new_file_name_utf8,
1771                                                    extension, NULL);
1772         }
1773         else
1774         {
1775             /* New path (with filename). */
1776             if (strcmp (dirname_utf8, G_DIR_SEPARATOR_S) == 0)
1777             {
1778                 /* Root directory. */
1779                 new_file_name_path_utf8 = g_strconcat (dirname_utf8,
1780                                                        new_file_name_utf8,
1781                                                        extension, NULL);
1782             }
1783             else
1784             {
1785                 new_file_name_path_utf8 = g_strconcat (dirname_utf8,
1786                                                        G_DIR_SEPARATOR_S,
1787                                                        new_file_name_utf8,
1788                                                        extension, NULL);
1789             }
1790         }
1791 
1792         g_free (dirname_utf8);
1793         g_free (extension);
1794 
1795         return new_file_name_path_utf8;
1796     }
1797 
1798     return NULL;
1799 }
1800 #else
1801 /* FOR TESTING */
1802 /* Returns filename in file system encoding */
et_file_generate_name(ET_File * ETFile,gchar * new_file_name_utf8)1803 gchar *et_file_generate_name (ET_File *ETFile, gchar *new_file_name_utf8)
1804 {
1805     gchar *dirname;
1806 
1807     if (ETFile && ETFile->FileNameNew->data && new_file_name_utf8
1808     && (dirname=g_path_get_dirname(((File_Name *)ETFile->FileNameNew->data)->value)) )
1809     {
1810         gchar *extension;
1811         gchar *new_file_name_path;
1812         gchar *new_file_name;
1813 
1814         new_file_name = filename_from_display(new_file_name_utf8);
1815 
1816         /* Convert filename extension (lower/upper). */
1817         extension = ET_File_Format_File_Extension (ETFile);
1818 
1819         // Check length of filename (limit ~255 characters)
1820         //ET_File_Name_Check_Length(ETFile,new_file_name_utf8);
1821 
1822         // If filemame starts with /, it's a full filename with path but without extension
1823         if (g_path_is_absolute(new_file_name))
1824         {
1825             // We just add the extension
1826             new_file_name_path = g_strconcat(new_file_name,extension,NULL);
1827         }else
1828         {
1829             // New path (with filename)
1830             if ( strcmp(dirname,G_DIR_SEPARATOR_S)==0 ) // Root directory?
1831                 new_file_name_path = g_strconcat(dirname,new_file_name,extension,NULL);
1832             else
1833                 new_file_name_path = g_strconcat(dirname,G_DIR_SEPARATOR_S,new_file_name,extension,NULL);
1834         }
1835 
1836         g_free(dirname);
1837         g_free(new_file_name);
1838         g_free(extension);
1839         return new_file_name_path; // in file system encoding
1840     }else
1841     {
1842         return NULL;
1843     }
1844 }
1845 #endif
1846 
1847 
1848 /* Convert filename extension (lower/upper/no change). */
1849 gchar *
ET_File_Format_File_Extension(const ET_File * ETFile)1850 ET_File_Format_File_Extension (const ET_File *ETFile)
1851 {
1852     EtFilenameExtensionMode mode;
1853 
1854     mode = g_settings_get_enum (MainSettings, "rename-extension-mode");
1855 
1856     switch (mode)
1857     {
1858         case ET_FILENAME_EXTENSION_LOWER_CASE:
1859             return g_ascii_strdown (ETFile->ETFileDescription->Extension, -1);
1860         case ET_FILENAME_EXTENSION_UPPER_CASE:
1861             return g_ascii_strup (ETFile->ETFileDescription->Extension, -1);
1862         case ET_FILENAME_EXTENSION_NO_CHANGE:
1863         default:
1864             return g_strdup (ETFile->ETFileExtension);
1865     };
1866 }
1867