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