1 /* EasyTAG - tag editor for audio files
2  * Copyright (C) 2014-2016  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_list.h"
22 
23 #include <gtk/gtk.h>
24 #include <glib/gi18n.h>
25 #include <locale.h>
26 #include <string.h>
27 #include <stdlib.h>
28 #include <sys/stat.h>
29 #include <unistd.h>
30 
31 #include "application_window.h"
32 #include "charset.h"
33 #include "easytag.h"
34 #include "log.h"
35 #include "misc.h"
36 #include "mpeg_header.h"
37 #include "monkeyaudio_header.h"
38 #include "musepack_header.h"
39 #include "picture.h"
40 #include "ape_tag.h"
41 #ifdef ENABLE_MP3
42 #include "id3_tag.h"
43 #endif
44 #ifdef ENABLE_OGG
45 #include "ogg_header.h"
46 #include "ogg_tag.h"
47 #endif
48 #ifdef ENABLE_FLAC
49 #include "flac_header.h"
50 #include "flac_tag.h"
51 #endif
52 #ifdef ENABLE_MP4
53 #include "mp4_header.h"
54 #include "mp4_tag.h"
55 #endif
56 #ifdef ENABLE_WAVPACK
57 #include "wavpack_header.h"
58 #include "wavpack_tag.h"
59 #endif
60 #ifdef ENABLE_OPUS
61 #include "opus_header.h"
62 #include "opus_tag.h"
63 #endif
64 
65 /*
66  * et_file_list_free:
67  * @file_list: (element-type ET_File) (allow-none): a list of files
68  *
69  * Frees the full list of files.
70  */
71 void
et_file_list_free(GList * file_list)72 et_file_list_free (GList *file_list)
73 {
74     g_return_if_fail (file_list != NULL);
75 
76     g_list_free_full (file_list, (GDestroyNotify)ET_Free_File_List_Item);
77 }
78 
79 static void
et_history_file_free(ET_History_File * file)80 et_history_file_free (ET_History_File *file)
81 {
82     g_slice_free (ET_History_File, file);
83 }
84 
85 /*
86  * History list contains only pointers, so no data to free except the history structure.
87  */
88 void
et_history_file_list_free(GList * file_list)89 et_history_file_list_free (GList *file_list)
90 {
91     g_return_if_fail (file_list != NULL);
92 
93     /* et_history_list_add() sets the list to the final element, so explicitly
94      * go back to the start. */
95     g_list_free_full (g_list_first (file_list),
96                       (GDestroyNotify)et_history_file_free);
97 }
98 
99 /*
100  * "Display" list contains only pointers, so NOTHING to free
101  */
102 void
et_displayed_file_list_free(GList * file_list)103 et_displayed_file_list_free (GList *file_list)
104 {
105 }
106 
107 /*
108  * ArtistAlbum list contains 3 levels of lists
109  */
110 void
et_artist_album_file_list_free(GList * file_list)111 et_artist_album_file_list_free (GList *file_list)
112 {
113     GList *l;
114 
115     g_return_if_fail (file_list != NULL);
116 
117     /* Pointers are stored inside the artist/album list-stores, so free them
118      * first. */
119     et_application_window_browser_clear_artist_model (ET_APPLICATION_WINDOW (MainWindow));
120     et_application_window_browser_clear_album_model (ET_APPLICATION_WINDOW (MainWindow));
121 
122     for (l = file_list; l != NULL; l = g_list_next (l))
123     {
124         GList *m;
125 
126         for (m = (GList *)l->data; m != NULL; m = g_list_next (m))
127         {
128             GList *n = (GList *)m->data;
129             if (n)
130                 g_list_free (n);
131         }
132 
133         if (l->data) /* Free AlbumList list. */
134             g_list_free ((GList *)l->data);
135     }
136 
137     g_list_free (file_list);
138 }
139 
140 /* Key for each item of ETFileList */
141 static guint
ET_File_Key_New(void)142 ET_File_Key_New (void)
143 {
144     static guint ETFileKey = 0;
145     return ++ETFileKey;
146 }
147 
148 /*
149  * et_core_read_file_info:
150  * @file: a file from which to read information
151  * @ETFileInfo: (out caller-allocates): a file information structure
152  * @error: a #GError to provide information on erros, or %NULL to ignore
153  *
154  * Fille @ETFileInfo with information about the file. Currently, this only
155  * fills the file size.
156  *
157  * Returns: %TRUE on success, %FALSE otherwise
158  */
159 static gboolean
et_core_read_file_info(GFile * file,ET_File_Info * ETFileInfo,GError ** error)160 et_core_read_file_info (GFile *file,
161                         ET_File_Info *ETFileInfo,
162                         GError **error)
163 {
164     GFileInfo *info;
165 
166     g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
167     g_return_val_if_fail (file != NULL && ETFileInfo != NULL, FALSE);
168 
169     info = g_file_query_info (file, G_FILE_ATTRIBUTE_STANDARD_SIZE,
170                               G_FILE_QUERY_INFO_NONE, NULL, error);
171 
172     if (!info)
173     {
174         g_assert (error == NULL || *error != NULL);
175         return FALSE;
176     }
177 
178     ETFileInfo->version    = 0;
179     ETFileInfo->bitrate    = 0;
180     ETFileInfo->samplerate = 0;
181     ETFileInfo->mode       = 0;
182     ETFileInfo->size = g_file_info_get_size (info);
183     ETFileInfo->duration   = 0;
184 
185     g_assert (error == NULL || *error == NULL);
186     g_object_unref (info);
187 
188     return TRUE;
189 }
190 
191 /*
192  * et_file_list_add:
193  * Add a file to the "main" list. And get all information of the file.
194  * The filename passed in should be in raw format, only convert it to UTF8 when
195  * displaying it.
196  */
197 GList *
et_file_list_add(GList * file_list,GFile * file)198 et_file_list_add (GList *file_list,
199                   GFile *file)
200 {
201     GList *result;
202     const ET_File_Description *description;
203     ET_File      *ETFile;
204     File_Name    *FileName;
205     File_Tag     *FileTag;
206     ET_File_Info *ETFileInfo;
207     gchar        *ETFileExtension;
208     guint         ETFileKey;
209     guint         undo_key;
210     GFileInfo *fileinfo;
211     gchar *filename;
212     gchar *display_path;
213     GError *error = NULL;
214     gboolean success;
215 
216     g_return_val_if_fail (file != NULL, file_list);
217 
218     /* Primary Key for this file */
219     ETFileKey = ET_File_Key_New();
220 
221     /* Get description of the file */
222     filename = g_file_get_path (file);
223     display_path = g_filename_display_name (filename);
224     description = ET_Get_File_Description (filename);
225 
226     /* Get real extension of the file (keeping the case) */
227     ETFileExtension = g_strdup(ET_Get_File_Extension(filename));
228 
229     /* Fill the File_Name structure for FileNameList */
230     FileName = et_file_name_new ();
231     FileName->saved      = TRUE;    /* The file hasn't been changed, so it's saved */
232     ET_Set_Filename_File_Name_Item (FileName, display_path, filename);
233 
234     /* Fill the File_Tag structure for FileTagList */
235     FileTag = et_file_tag_new ();
236     FileTag->saved = TRUE;    /* The file hasn't been changed, so it's saved */
237 
238     switch (description->TagType)
239     {
240 #ifdef ENABLE_MP3
241         case ID3_TAG:
242             if (!id3tag_read_file_tag (file, FileTag, &error))
243             {
244                 Log_Print (LOG_ERROR,
245                            _("Error reading ID3 tag from file ‘%s’: %s"),
246                            display_path, error->message);
247                 g_clear_error (&error);
248             }
249             break;
250 #endif
251 #ifdef ENABLE_OGG
252         case OGG_TAG:
253             if (!ogg_tag_read_file_tag (file, FileTag, &error))
254             {
255                 Log_Print (LOG_ERROR,
256                            _("Error reading tag from Ogg file ‘%s’: %s"),
257                            display_path, error->message);
258                 g_clear_error (&error);
259             }
260             break;
261 #endif
262 #ifdef ENABLE_FLAC
263         case FLAC_TAG:
264             if (!flac_tag_read_file_tag (file, FileTag, &error))
265             {
266                 Log_Print (LOG_ERROR,
267                            _("Error reading tag from FLAC file ‘%s’: %s"),
268                            display_path, error->message);
269                 g_clear_error (&error);
270             }
271             break;
272 #endif
273         case APE_TAG:
274             if (!ape_tag_read_file_tag (file, FileTag, &error))
275             {
276                 Log_Print (LOG_ERROR,
277                            _("Error reading APE tag from file ‘%s’: %s"),
278                            display_path, error->message);
279                 g_clear_error (&error);
280             }
281             break;
282 #ifdef ENABLE_MP4
283         case MP4_TAG:
284             if (!mp4tag_read_file_tag (file, FileTag, &error))
285             {
286                 Log_Print (LOG_ERROR,
287                            _("Error reading tag from MP4 file ‘%s’: %s"),
288                            display_path, error->message);
289                 g_clear_error (&error);
290             }
291             break;
292 #endif
293 #ifdef ENABLE_WAVPACK
294         case WAVPACK_TAG:
295             if (!wavpack_tag_read_file_tag (file, FileTag, &error))
296             {
297                 Log_Print (LOG_ERROR,
298                            _("Error reading tag from WavPack file ‘%s’: %s"),
299                            display_path, error->message);
300                 g_clear_error (&error);
301             }
302         break;
303 #endif
304 #ifdef ENABLE_OPUS
305         case OPUS_TAG:
306             if (!et_opus_tag_read_file_tag (file, FileTag, &error))
307             {
308                 Log_Print (LOG_ERROR,
309                            _("Error reading tag from Opus file ‘%s’: %s"),
310                            display_path, error->message);
311                 g_clear_error (&error);
312             }
313             break;
314 #endif
315 #ifndef ENABLE_MP3
316         case ID3_TAG:
317 #endif
318 #ifndef ENABLE_OGG
319         case OGG_TAG:
320 #endif
321 #ifndef ENABLE_FLAC
322         case FLAC_TAG:
323 #endif
324 #ifndef ENABLE_MP4
325         case MP4_TAG:
326 #endif
327 #ifndef ENABLE_WAVPACK
328         case WAVPACK_TAG:
329 #endif
330 #ifndef ENABLE_OPUS
331         case OPUS_TAG:
332 #endif
333         case UNKNOWN_TAG:
334         default:
335             /* FIXME: Translatable string. */
336             Log_Print (LOG_ERROR,
337                        "FileTag: Undefined tag type (%d) for file %s",
338                        (gint)description->TagType, display_path);
339             break;
340     }
341 
342     if (FileTag->year && g_utf8_strlen (FileTag->year, -1) > 4)
343     {
344         Log_Print (LOG_WARNING,
345                    _("The year value ‘%s’ seems to be invalid in file ‘%s’. The information will be lost when saving"),
346                    FileTag->year, display_path);
347     }
348 
349     /* Fill the ET_File_Info structure */
350     ETFileInfo = et_file_info_new ();
351 
352     switch (description->FileType)
353     {
354 #if defined ENABLE_MP3 && defined ENABLE_ID3LIB
355         case MP3_FILE:
356         case MP2_FILE:
357             success = et_mpeg_header_read_file_info (file, ETFileInfo, &error);
358             break;
359 #endif
360 #ifdef ENABLE_OGG
361         case OGG_FILE:
362             success = et_ogg_header_read_file_info (file, ETFileInfo, &error);
363             break;
364 #endif
365 #ifdef ENABLE_SPEEX
366         case SPEEX_FILE:
367             success = et_speex_header_read_file_info (file, ETFileInfo,
368                                                       &error);
369             break;
370 #endif
371 #ifdef ENABLE_FLAC
372         case FLAC_FILE:
373             success = et_flac_header_read_file_info (file, ETFileInfo, &error);
374             break;
375 #endif
376         case MPC_FILE:
377             success = et_mpc_header_read_file_info (file, ETFileInfo, &error);
378             break;
379         case MAC_FILE:
380             success = et_mac_header_read_file_info (file, ETFileInfo, &error);
381             break;
382 #ifdef ENABLE_WAVPACK
383         case WAVPACK_FILE:
384             success = et_wavpack_header_read_file_info (file, ETFileInfo,
385                                                         &error);
386             break;
387 #endif
388 #ifdef ENABLE_MP4
389         case MP4_FILE:
390             success = et_mp4_header_read_file_info (file, ETFileInfo, &error);
391             break;
392 #endif
393 #ifdef ENABLE_OPUS
394         case OPUS_FILE:
395             success = et_opus_read_file_info (file, ETFileInfo, &error);
396             break;
397 #endif
398         case OFR_FILE:
399 #if !defined ENABLE_MP3 && !defined ENABLE_ID3LIB
400         case MP3_FILE:
401         case MP2_FILE:
402 #endif
403 #ifndef ENABLE_OGG
404         case OGG_FILE:
405 #endif
406 #ifndef ENABLE_SPEEX
407         case SPEEX_FILE:
408 #endif
409 #ifndef ENABLE_FLAC
410         case FLAC_FILE:
411 #endif
412 #ifndef ENABLE_MP4
413         case MP4_FILE:
414 #endif
415 #ifndef ENABLE_WAVPACK
416         case WAVPACK_FILE:
417 #endif
418 #ifndef ENABLE_OPUS
419         case OPUS_FILE:
420 #endif
421         case UNKNOWN_FILE:
422         default:
423             /* FIXME: Translatable string. */
424             Log_Print (LOG_ERROR,
425                        "ETFileInfo: Undefined file type (%d) for file %s",
426                        (gint)description->FileType, display_path);
427             /* To get at least the file size. */
428             success = et_core_read_file_info (file, ETFileInfo, &error);
429             break;
430     }
431 
432     if (!success)
433     {
434         Log_Print (LOG_ERROR,
435                    _("Error while querying information for file ‘%s’: %s"),
436                    display_path, error->message);
437         g_error_free (error);
438     }
439 
440     /* Store the modification time of the file to check if the file was changed
441      * before saving */
442     fileinfo = g_file_query_info (file, G_FILE_ATTRIBUTE_TIME_MODIFIED,
443                                   G_FILE_QUERY_INFO_NONE, NULL, NULL);
444 
445     /* Attach all data defined above to this ETFile item */
446     ETFile = ET_File_Item_New();
447 
448     if (fileinfo)
449     {
450         ETFile->FileModificationTime = g_file_info_get_attribute_uint64 (fileinfo,
451                                                                          G_FILE_ATTRIBUTE_TIME_MODIFIED);
452         g_object_unref (fileinfo);
453     }
454     else
455     {
456         ETFile->FileModificationTime = 0;
457     }
458 
459     ETFile->IndexKey             = 0; // Will be renumered after...
460     ETFile->ETFileKey            = ETFileKey;
461     ETFile->ETFileDescription    = description;
462     ETFile->ETFileExtension      = ETFileExtension;
463     ETFile->FileNameList         = g_list_append(NULL,FileName);
464     ETFile->FileNameCur          = ETFile->FileNameList;
465     ETFile->FileNameNew          = ETFile->FileNameList;
466     ETFile->FileTagList          = g_list_append(NULL,FileTag);
467     ETFile->FileTag              = ETFile->FileTagList;
468     ETFile->ETFileInfo           = ETFileInfo;
469 
470     /* Add the item to the "main list" */
471     result = g_list_append (file_list, ETFile);
472 
473 
474     /*
475      * Process the filename and tag to generate undo if needed...
476      * The undo key must be the same for FileName and FileTag => changed in the same time
477      */
478     undo_key = et_undo_key_new ();
479 
480     FileName = et_file_name_new ();
481     FileName->key = undo_key;
482     ET_Save_File_Name_Internal(ETFile,FileName);
483 
484     FileTag = et_file_tag_new ();
485     FileTag->key = undo_key;
486     ET_Save_File_Tag_Internal(ETFile,FileTag);
487 
488     /*
489      * Generate undo for the file and the main undo list.
490      * If no changes detected, FileName and FileTag item are deleted.
491      */
492     ET_Manage_Changes_Of_File_Data(ETFile,FileName,FileTag);
493 
494     /*
495      * Display a message if the file was changed at start
496      */
497     FileTag  = (File_Tag *)ETFile->FileTag->data;
498     FileName = (File_Name *)ETFile->FileNameNew->data;
499     if ( (FileName && FileName->saved==FALSE) || (FileTag && FileTag->saved==FALSE) )
500     {
501         Log_Print (LOG_INFO, _("Automatic corrections applied for file ‘%s’"),
502                    display_path);
503     }
504 
505     /* Add the item to the ArtistAlbum list (placed here to take advantage of previous changes) */
506     //ET_Add_File_To_Artist_Album_File_List(ETFile);
507 
508     //ET_Debug_Print_File_List(ETCore->ETFileList,__FILE__,__LINE__,__FUNCTION__);
509 
510     g_free (filename);
511     g_free (display_path);
512 
513     return result;
514 }
515 
516 /*
517  * Comparison function for sorting by ascending artist in the ArtistAlbumList.
518  */
519 static gint
ET_Comp_Func_Sort_Artist_Item_By_Ascending_Artist(const GList * AlbumList1,const GList * AlbumList2)520 ET_Comp_Func_Sort_Artist_Item_By_Ascending_Artist (const GList *AlbumList1,
521                                                    const GList *AlbumList2)
522 {
523     const GList *etfilelist1 = NULL;
524     const GList *etfilelist2 = NULL;
525     const ET_File *etfile1 = NULL;
526     const ET_File *etfile2 = NULL;
527     const gchar *etfile1_artist;
528     const gchar *etfile2_artist;
529 
530     if (!AlbumList1 || !(etfilelist1 = (GList *)AlbumList1->data)
531         || !(etfile1 = (ET_File *)etfilelist1->data))
532     {
533         return -1;
534     }
535 
536     if (!AlbumList2 || !(etfilelist2 = (GList *)AlbumList2->data)
537         || !(etfile2 = (ET_File *)etfilelist2->data))
538     {
539         return 1;
540     }
541 
542     etfile1_artist = ((File_Tag *)etfile1->FileTag->data)->artist;
543     etfile2_artist = ((File_Tag *)etfile2->FileTag->data)->artist;
544 
545     if (g_settings_get_boolean (MainSettings, "sort-case-sensitive"))
546     {
547         return et_normalized_strcmp0 (etfile1_artist, etfile2_artist);
548     }
549     else
550     {
551         return et_normalized_strcasecmp0 (etfile1_artist, etfile2_artist);
552     }
553 }
554 
555 /*
556  * Comparison function for sorting by ascending album in the ArtistAlbumList.
557  */
558 static gint
ET_Comp_Func_Sort_Album_Item_By_Ascending_Album(const GList * etfilelist1,const GList * etfilelist2)559 ET_Comp_Func_Sort_Album_Item_By_Ascending_Album (const GList *etfilelist1,
560                                                  const GList *etfilelist2)
561 {
562     const ET_File *etfile1;
563     const ET_File *etfile2;
564     const gchar *etfile1_album;
565     const gchar *etfile2_album;
566 
567     if (!etfilelist1 || !(etfile1 = (ET_File *)etfilelist1->data))
568     {
569         return -1;
570     }
571 
572     if (!etfilelist2 || !(etfile2 = (ET_File *)etfilelist2->data))
573     {
574         return 1;
575     }
576 
577     etfile1_album  = ((File_Tag *)etfile1->FileTag->data)->album;
578     etfile2_album  = ((File_Tag *)etfile2->FileTag->data)->album;
579 
580     if (g_settings_get_boolean (MainSettings, "sort-case-sensitive"))
581     {
582         return et_normalized_strcmp0 (etfile1_album, etfile2_album);
583     }
584     else
585     {
586         return et_normalized_strcasecmp0 (etfile1_album, etfile2_album);
587     }
588 }
589 
590 /*
591  * Comparison function for sorting etfile in the ArtistAlbumList.
592  * FIX ME : should use the default sorting!
593  */
594 static gint
ET_Comp_Func_Sort_Etfile_Item_By_Ascending_Filename(const ET_File * ETFile1,const ET_File * ETFile2)595 ET_Comp_Func_Sort_Etfile_Item_By_Ascending_Filename (const ET_File *ETFile1,
596                                                      const ET_File *ETFile2)
597 {
598 
599     if (!ETFile1) return -1;
600     if (!ETFile2) return  1;
601 
602     return ET_Comp_Func_Sort_File_By_Ascending_Filename(ETFile1,ETFile2);
603 }
604 
605 /*
606  * The ETArtistAlbumFileList contains 3 levels of lists to sort the ETFile by artist then by album :
607  *  - "ETArtistAlbumFileList" list is a list of "ArtistList" items,
608  *  - "ArtistList" list is a list of "AlbumList" items,
609  *  - "AlbumList" list is a list of ETFile items.
610  * Note : use the function ET_Debug_Print_Artist_Album_List(...) to understand how it works, it needed...
611  */
612 static GList *
et_artist_album_list_add_file(GList * file_list,ET_File * ETFile)613 et_artist_album_list_add_file (GList *file_list,
614                                ET_File *ETFile)
615 {
616     const gchar *ETFile_Artist;
617     const gchar *ETFile_Album;
618     const gchar *etfile_artist;
619     const gchar *etfile_album;
620     GList *ArtistList;
621     GList *AlbumList = NULL;
622     GList *etfilelist = NULL;
623     ET_File *etfile = NULL;
624 
625     g_return_val_if_fail (ETFile != NULL, NULL);
626 
627     /* Album value of the ETFile passed in parameter. */
628     ETFile_Album = ((File_Tag *)ETFile->FileTag->data)->album;
629     /* Artist value of the ETFile passed in parameter. */
630     ETFile_Artist = ((File_Tag *)ETFile->FileTag->data)->artist;
631 
632     for (ArtistList = file_list; ArtistList != NULL;
633          ArtistList = g_list_next (ArtistList))
634     {
635         AlbumList = (GList *)ArtistList->data;  /* Take the first item */
636         /* Take the first item, and the first etfile item. */
637         if (AlbumList && (etfilelist = (GList *)AlbumList->data)
638             && (etfile = (ET_File *)etfilelist->data)
639             && ((File_Tag *)etfile->FileTag->data) != NULL)
640         {
641             etfile_artist = ((File_Tag *)etfile->FileTag->data)->artist;
642         }
643         else
644         {
645             etfile_artist = NULL;
646         }
647 
648         if ((etfile_artist &&  ETFile_Artist && strcmp (etfile_artist,
649                                                         ETFile_Artist) == 0)
650             || (!etfile_artist && !ETFile_Artist)) /* The "artist" values correspond? */
651         {
652             /* The "ArtistList" item was found! */
653             while (AlbumList)
654             {
655                 if ((etfilelist = (GList *)AlbumList->data)
656                     && (etfile = (ET_File *)etfilelist->data)
657                     && ((File_Tag *)etfile->FileTag->data) != NULL)
658                 {
659                     etfile_album = ((File_Tag *)etfile->FileTag->data)->album;
660                 }
661                 else
662                 {
663                     etfile_album = NULL;
664                 }
665 
666                 if ((etfile_album && ETFile_Album && strcmp (etfile_album,
667                                                              ETFile_Album) == 0)
668                     || (!etfile_album && !ETFile_Album))
669                     /* The "album" values correspond? */
670                 {
671                     /* The "AlbumList" item was found!
672                      * Add the ETFile to this AlbumList item */
673                     AlbumList->data = g_list_insert_sorted ((GList *)AlbumList->data,
674                                                             ETFile,
675                                                             (GCompareFunc)ET_Comp_Func_Sort_Etfile_Item_By_Ascending_Filename);
676                     return file_list;
677                 }
678 
679                 AlbumList = g_list_next (AlbumList);
680             }
681 
682             /* The "AlbumList" item was NOT found! => Add a new "AlbumList"
683              * item (+...) item to the "ArtistList" list. */
684             etfilelist = g_list_append (NULL, ETFile);
685             ArtistList->data = g_list_insert_sorted ((GList *)ArtistList->data,
686                                                      etfilelist,
687                                                      (GCompareFunc)ET_Comp_Func_Sort_Album_Item_By_Ascending_Album);
688             return file_list;
689         }
690     }
691 
692     /* The "ArtistList" item was NOT found! => Add a new "ArtistList" to the
693      * main list (=ETArtistAlbumFileList). */
694     etfilelist = g_list_append (NULL, ETFile);
695     AlbumList  = g_list_append (NULL, etfilelist);
696 
697     /* Sort the list by ascending Artist. */
698     return g_list_insert_sorted (file_list, AlbumList,
699                                  (GCompareFunc)ET_Comp_Func_Sort_Artist_Item_By_Ascending_Artist);
700 }
701 
702 GList *
et_artist_album_list_new_from_file_list(GList * file_list)703 et_artist_album_list_new_from_file_list (GList *file_list)
704 {
705     GList *result = NULL;
706     GList *l;
707 
708     for (l = g_list_first (file_list); l != NULL; l = g_list_next (l))
709     {
710         ET_File *ETFile = (ET_File *)l->data;
711         result = et_artist_album_list_add_file (result, ETFile);
712     }
713 
714     return result;
715 }
716 
717 /*
718  * Delete the corresponding file (allocated data was previously freed!). Return TRUE if deleted.
719  */
720 static gboolean
ET_Remove_File_From_Artist_Album_List(ET_File * ETFile)721 ET_Remove_File_From_Artist_Album_List (ET_File *ETFile)
722 {
723     GList *ArtistList;
724     GList *AlbumList;
725     GList *etfilelist;
726 
727     g_return_val_if_fail (ETFile != NULL, FALSE);
728 
729     /* Search for the ETFile in the list. */
730     for (ArtistList = ETCore->ETArtistAlbumFileList; ArtistList != NULL;
731          ArtistList = g_list_next (ArtistList))
732     {
733         for (AlbumList = g_list_first ((GList *)ArtistList->data);
734              AlbumList != NULL; AlbumList = g_list_next (AlbumList))
735         {
736             for (etfilelist = g_list_first ((GList *)AlbumList->data);
737                  etfilelist != NULL; etfilelist = g_list_next (etfilelist))
738             {
739                 ET_File *etfile = (ET_File *)etfilelist->data;
740 
741                 if (ETFile == etfile) /* The ETFile to delete was found! */
742                 {
743                     etfilelist = g_list_remove (etfilelist, ETFile);
744 
745                     if (etfilelist) /* Delete from AlbumList. */
746                     {
747                         AlbumList->data = (gpointer) g_list_first (etfilelist);
748                     }
749                     else
750                     {
751                         AlbumList = g_list_remove (AlbumList, AlbumList->data);
752 
753                         if (AlbumList) /* Delete from ArtistList. */
754                         {
755                             ArtistList->data = AlbumList;
756                         }
757                         else
758                         {
759                             /* Delete from the main list. */
760                             ETCore->ETArtistAlbumFileList = g_list_remove (ArtistList,ArtistList->data);
761 
762                             return TRUE;
763                         }
764 
765                         return TRUE;
766                     }
767 
768                     return TRUE;
769                 }
770             }
771         }
772     }
773 
774     return FALSE; /* ETFile is NUL, or not found in the list. */
775 }
776 
777 /*
778  * Returns the length of the list of displayed files
779  */
780 static guint
et_displayed_file_list_length(GList * displayed_list)781 et_displayed_file_list_length (GList *displayed_list)
782 {
783     GList *list;
784 
785     list = g_list_first (displayed_list);
786     return g_list_length (list);
787 }
788 
789 /*
790  * Renumber the list of displayed files (IndexKey) from 1 to n
791  */
792 static void
et_displayed_file_list_renumber(GList * displayed_list)793 et_displayed_file_list_renumber (GList *displayed_list)
794 {
795     GList *l = NULL;
796     guint i = 1;
797 
798     for (l = g_list_first (displayed_list); l != NULL; l = g_list_next (l))
799     {
800         ((ET_File *)l->data)->IndexKey = i++;
801     }
802 }
803 
804 /*
805  * Delete the corresponding file and free the allocated data. Return TRUE if deleted.
806  */
807 void
ET_Remove_File_From_File_List(ET_File * ETFile)808 ET_Remove_File_From_File_List (ET_File *ETFile)
809 {
810     GList *ETFileList = NULL;          // Item containing the ETFile to delete... (in ETCore->ETFileList)
811     GList *ETFileDisplayedList = NULL; // Item containing the ETFile to delete... (in ETCore->ETFileDisplayedList)
812 
813     // Remove infos of the file
814     ETCore->ETFileDisplayedList_TotalSize     -= ((ET_File_Info *)ETFile->ETFileInfo)->size;
815     ETCore->ETFileDisplayedList_TotalDuration -= ((ET_File_Info *)ETFile->ETFileInfo)->duration;
816 
817     // Find the ETFileList containing the ETFile item
818     ETFileDisplayedList = g_list_find(g_list_first(ETCore->ETFileDisplayedList),ETFile);
819     ETFileList = g_list_find (ETCore->ETFileList, ETFile);
820 
821     // Note : this ETFileList must be used only for ETCore->ETFileDisplayedList, and not ETCore->ETFileDisplayed
822     if (ETCore->ETFileDisplayedList == ETFileDisplayedList)
823     {
824         if (ETFileList->next)
825             ETCore->ETFileDisplayedList = ETFileDisplayedList->next;
826         else if (ETFileList->prev)
827             ETCore->ETFileDisplayedList = ETFileDisplayedList->prev;
828         else
829             ETCore->ETFileDisplayedList = NULL;
830     }
831     // If the current displayed file is just removing, it will be unable to display it again!
832     if (ETCore->ETFileDisplayed == ETFile)
833     {
834         if (ETCore->ETFileDisplayedList)
835             ETCore->ETFileDisplayed = (ET_File *)ETCore->ETFileDisplayedList->data;
836         else
837             ETCore->ETFileDisplayed = (ET_File *)NULL;
838     }
839 
840     /* Remove the file from the ETFileList list. */
841     ETCore->ETFileList = g_list_remove (ETCore->ETFileList, ETFile);
842 
843     // Remove the file from the ETArtistAlbumList list
844     ET_Remove_File_From_Artist_Album_List(ETFile);
845 
846     /* Remove the file from the ETFileDisplayedList list (if not already). */
847     ETCore->ETFileDisplayedList = g_list_remove (g_list_first (ETCore->ETFileDisplayedList),
848                                                  ETFile);
849 
850     // Free data of the file
851     ET_Free_File_List_Item(ETFile);
852 
853     /* Recalculate length of ETFileDisplayedList list. */
854     ETCore->ETFileDisplayedList_Length = et_displayed_file_list_length (ETCore->ETFileDisplayedList);
855 
856     /* To number the ETFile in the list. */
857     et_displayed_file_list_renumber (ETCore->ETFileDisplayedList);
858 
859     // Displaying...
860     if (ETCore->ETFileDisplayedList)
861     {
862         if (ETCore->ETFileDisplayed)
863         {
864             ET_Displayed_File_List_By_Etfile(ETCore->ETFileDisplayed);
865         }else if (ETCore->ETFileDisplayedList->data)
866         {
867             // Select the new file (synchronize index,...)
868             ET_Displayed_File_List_By_Etfile((ET_File *)ETCore->ETFileDisplayedList->data);
869         }
870     }else
871     {
872         // Reinit the tag and file area
873         et_application_window_file_area_clear (ET_APPLICATION_WINDOW (MainWindow));
874         et_application_window_tag_area_clear (ET_APPLICATION_WINDOW (MainWindow));
875         et_application_window_update_actions (ET_APPLICATION_WINDOW (MainWindow));
876     }
877 }
878 
879 /**************************
880  * File sorting functions *
881  **************************/
882 
883 /*
884  * Set appropriate sort order for the given column_id
885  */
886 static void
set_sort_order_for_column_id(gint column_id,GtkTreeViewColumn * column,EtSortMode sort_type)887 set_sort_order_for_column_id (gint column_id, GtkTreeViewColumn *column,
888                               EtSortMode sort_type)
889 {
890     EtApplicationWindow *window;
891     EtSortMode current_mode;
892 
893     window = ET_APPLICATION_WINDOW (MainWindow);
894 
895     /* Removing the sort indicator for the currently selected treeview
896      * column. */
897     current_mode = g_settings_get_enum (MainSettings, "sort-mode");
898 
899     if (current_mode < ET_SORT_MODE_ASCENDING_CREATION_DATE)
900     {
901         gtk_tree_view_column_set_sort_indicator (et_application_window_browser_get_column_for_column_id (window, current_mode / 2),
902                                                  FALSE);
903     }
904 
905     if (sort_type < ET_SORT_MODE_ASCENDING_CREATION_DATE)
906     {
907         gtk_tree_view_column_clicked (et_application_window_browser_get_column_for_column_id (window, column_id));
908 
909         if (sort_type % 2 == 0)
910         {
911             /* GTK_SORT_ASCENDING */
912             if (et_application_window_browser_get_sort_order_for_column_id (window, column_id)
913                 == GTK_SORT_DESCENDING)
914             {
915                 gtk_tree_view_column_set_sort_order (column,
916                                                      GTK_SORT_ASCENDING);
917             }
918         }
919         else
920         {
921             /* GTK_SORT_DESCENDING */
922             if (et_application_window_browser_get_sort_order_for_column_id (window, column_id)
923                 == GTK_SORT_ASCENDING)
924             {
925                 gtk_tree_view_column_set_sort_order (column,
926                                                      GTK_SORT_DESCENDING);
927             }
928         }
929     }
930 }
931 
932 /*
933  * Sort an 'ETFileList'
934  */
935 GList *
ET_Sort_File_List(GList * ETFileList,EtSortMode Sorting_Type)936 ET_Sort_File_List (GList *ETFileList,
937                    EtSortMode Sorting_Type)
938 {
939     EtApplicationWindow *window;
940     GtkTreeViewColumn *column;
941     GList *etfilelist;
942     gint column_id = Sorting_Type / 2;
943 
944     window = ET_APPLICATION_WINDOW (MainWindow);
945     column = et_application_window_browser_get_column_for_column_id (window,
946                                                                      column_id);
947 
948     /* Important to rewind before. */
949     etfilelist = g_list_first (ETFileList);
950 
951     set_sort_order_for_column_id (column_id, column, Sorting_Type);
952 
953     /* Sort... */
954     switch (Sorting_Type)
955     {
956         case ET_SORT_MODE_ASCENDING_FILENAME:
957             etfilelist = g_list_sort(etfilelist,(GCompareFunc)ET_Comp_Func_Sort_File_By_Ascending_Filename);
958             break;
959         case ET_SORT_MODE_DESCENDING_FILENAME:
960             etfilelist = g_list_sort(etfilelist,(GCompareFunc)ET_Comp_Func_Sort_File_By_Descending_Filename);
961             break;
962         case ET_SORT_MODE_ASCENDING_TITLE:
963             etfilelist = g_list_sort(etfilelist,(GCompareFunc)ET_Comp_Func_Sort_File_By_Ascending_Title);
964             break;
965         case ET_SORT_MODE_DESCENDING_TITLE:
966             etfilelist = g_list_sort(etfilelist,(GCompareFunc)ET_Comp_Func_Sort_File_By_Descending_Title);
967             break;
968         case ET_SORT_MODE_ASCENDING_ARTIST:
969             etfilelist = g_list_sort(etfilelist,(GCompareFunc)ET_Comp_Func_Sort_File_By_Ascending_Artist);
970             break;
971         case ET_SORT_MODE_DESCENDING_ARTIST:
972             etfilelist = g_list_sort(etfilelist,(GCompareFunc)ET_Comp_Func_Sort_File_By_Descending_Artist);
973             break;
974         case ET_SORT_MODE_ASCENDING_ALBUM_ARTIST:
975             etfilelist = g_list_sort(etfilelist,(GCompareFunc)ET_Comp_Func_Sort_File_By_Ascending_Album_Artist);
976             break;
977         case ET_SORT_MODE_DESCENDING_ALBUM_ARTIST:
978             etfilelist = g_list_sort(etfilelist,(GCompareFunc)ET_Comp_Func_Sort_File_By_Descending_Album_Artist);
979             break;
980 		case ET_SORT_MODE_ASCENDING_ALBUM:
981             etfilelist = g_list_sort(etfilelist,(GCompareFunc)ET_Comp_Func_Sort_File_By_Ascending_Album);
982             break;
983         case ET_SORT_MODE_DESCENDING_ALBUM:
984             etfilelist = g_list_sort(etfilelist,(GCompareFunc)ET_Comp_Func_Sort_File_By_Descending_Album);
985             break;
986         case ET_SORT_MODE_ASCENDING_YEAR:
987             etfilelist = g_list_sort(etfilelist,(GCompareFunc)ET_Comp_Func_Sort_File_By_Ascending_Year);
988             break;
989         case ET_SORT_MODE_DESCENDING_YEAR:
990             etfilelist = g_list_sort(etfilelist,(GCompareFunc)ET_Comp_Func_Sort_File_By_Descending_Year);
991             break;
992         case ET_SORT_MODE_ASCENDING_DISC_NUMBER:
993             etfilelist = g_list_sort (etfilelist,
994                                       (GCompareFunc)et_comp_func_sort_file_by_ascending_disc_number);
995             break;
996         case ET_SORT_MODE_DESCENDING_DISC_NUMBER:
997             etfilelist = g_list_sort (etfilelist,
998                                       (GCompareFunc)et_comp_func_sort_file_by_descending_disc_number);
999             break;
1000         case ET_SORT_MODE_ASCENDING_TRACK_NUMBER:
1001             etfilelist = g_list_sort (etfilelist,
1002                                       (GCompareFunc)ET_Comp_Func_Sort_File_By_Ascending_Track_Number);
1003             break;
1004         case ET_SORT_MODE_DESCENDING_TRACK_NUMBER:
1005             etfilelist = g_list_sort (etfilelist,
1006                                       (GCompareFunc)ET_Comp_Func_Sort_File_By_Descending_Track_Number);
1007             break;
1008         case ET_SORT_MODE_ASCENDING_GENRE:
1009             etfilelist = g_list_sort(etfilelist,(GCompareFunc)ET_Comp_Func_Sort_File_By_Ascending_Genre);
1010             break;
1011         case ET_SORT_MODE_DESCENDING_GENRE:
1012             etfilelist = g_list_sort(etfilelist,(GCompareFunc)ET_Comp_Func_Sort_File_By_Descending_Genre);
1013             break;
1014         case ET_SORT_MODE_ASCENDING_COMMENT:
1015             etfilelist = g_list_sort(etfilelist,(GCompareFunc)ET_Comp_Func_Sort_File_By_Ascending_Comment);
1016             break;
1017         case ET_SORT_MODE_DESCENDING_COMMENT:
1018             etfilelist = g_list_sort(etfilelist,(GCompareFunc)ET_Comp_Func_Sort_File_By_Descending_Comment);
1019             break;
1020         case ET_SORT_MODE_ASCENDING_COMPOSER:
1021             etfilelist = g_list_sort(etfilelist,(GCompareFunc)ET_Comp_Func_Sort_File_By_Ascending_Composer);
1022             break;
1023         case ET_SORT_MODE_DESCENDING_COMPOSER:
1024             etfilelist = g_list_sort(etfilelist,(GCompareFunc)ET_Comp_Func_Sort_File_By_Descending_Composer);
1025             break;
1026         case ET_SORT_MODE_ASCENDING_ORIG_ARTIST:
1027             etfilelist = g_list_sort(etfilelist,(GCompareFunc)ET_Comp_Func_Sort_File_By_Ascending_Orig_Artist);
1028             break;
1029         case ET_SORT_MODE_DESCENDING_ORIG_ARTIST:
1030             etfilelist = g_list_sort(etfilelist,(GCompareFunc)ET_Comp_Func_Sort_File_By_Descending_Orig_Artist);
1031             break;
1032         case ET_SORT_MODE_ASCENDING_COPYRIGHT:
1033             etfilelist = g_list_sort(etfilelist,(GCompareFunc)ET_Comp_Func_Sort_File_By_Ascending_Copyright);
1034             break;
1035         case ET_SORT_MODE_DESCENDING_COPYRIGHT:
1036             etfilelist = g_list_sort(etfilelist,(GCompareFunc)ET_Comp_Func_Sort_File_By_Descending_Copyright);
1037             break;
1038         case ET_SORT_MODE_ASCENDING_URL:
1039             etfilelist = g_list_sort(etfilelist,(GCompareFunc)ET_Comp_Func_Sort_File_By_Ascending_Url);
1040             break;
1041         case ET_SORT_MODE_DESCENDING_URL:
1042             etfilelist = g_list_sort(etfilelist,(GCompareFunc)ET_Comp_Func_Sort_File_By_Descending_Url);
1043             break;
1044         case ET_SORT_MODE_ASCENDING_ENCODED_BY:
1045             etfilelist = g_list_sort(etfilelist,(GCompareFunc)ET_Comp_Func_Sort_File_By_Ascending_Encoded_By);
1046             break;
1047         case ET_SORT_MODE_DESCENDING_ENCODED_BY:
1048             etfilelist = g_list_sort(etfilelist,(GCompareFunc)ET_Comp_Func_Sort_File_By_Descending_Encoded_By);
1049             break;
1050         case ET_SORT_MODE_ASCENDING_CREATION_DATE:
1051             etfilelist = g_list_sort (etfilelist,
1052                                       (GCompareFunc)ET_Comp_Func_Sort_File_By_Ascending_Creation_Date);
1053             break;
1054         case ET_SORT_MODE_DESCENDING_CREATION_DATE:
1055             etfilelist = g_list_sort (etfilelist,
1056                                       (GCompareFunc)ET_Comp_Func_Sort_File_By_Descending_Creation_Date);
1057             break;
1058         case ET_SORT_MODE_ASCENDING_FILE_TYPE:
1059             etfilelist = g_list_sort(etfilelist,(GCompareFunc)ET_Comp_Func_Sort_File_By_Ascending_File_Type);
1060             break;
1061         case ET_SORT_MODE_DESCENDING_FILE_TYPE:
1062             etfilelist = g_list_sort(etfilelist,(GCompareFunc)ET_Comp_Func_Sort_File_By_Descending_File_Type);
1063             break;
1064         case ET_SORT_MODE_ASCENDING_FILE_SIZE:
1065             etfilelist = g_list_sort(etfilelist,(GCompareFunc)ET_Comp_Func_Sort_File_By_Ascending_File_Size);
1066             break;
1067         case ET_SORT_MODE_DESCENDING_FILE_SIZE:
1068             etfilelist = g_list_sort(etfilelist,(GCompareFunc)ET_Comp_Func_Sort_File_By_Descending_File_Size);
1069             break;
1070         case ET_SORT_MODE_ASCENDING_FILE_DURATION:
1071             etfilelist = g_list_sort(etfilelist,(GCompareFunc)ET_Comp_Func_Sort_File_By_Ascending_File_Duration);
1072             break;
1073         case ET_SORT_MODE_DESCENDING_FILE_DURATION:
1074             etfilelist = g_list_sort(etfilelist,(GCompareFunc)ET_Comp_Func_Sort_File_By_Descending_File_Duration);
1075             break;
1076         case ET_SORT_MODE_ASCENDING_FILE_BITRATE:
1077             etfilelist = g_list_sort(etfilelist,(GCompareFunc)ET_Comp_Func_Sort_File_By_Ascending_File_Bitrate);
1078             break;
1079         case ET_SORT_MODE_DESCENDING_FILE_BITRATE:
1080             etfilelist = g_list_sort(etfilelist,(GCompareFunc)ET_Comp_Func_Sort_File_By_Descending_File_Bitrate);
1081             break;
1082         case ET_SORT_MODE_ASCENDING_FILE_SAMPLERATE:
1083             etfilelist = g_list_sort(etfilelist,(GCompareFunc)ET_Comp_Func_Sort_File_By_Ascending_File_Samplerate);
1084             break;
1085         case ET_SORT_MODE_DESCENDING_FILE_SAMPLERATE:
1086             etfilelist = g_list_sort(etfilelist,(GCompareFunc)ET_Comp_Func_Sort_File_By_Descending_File_Samplerate);
1087             break;
1088         default:
1089             g_assert_not_reached ();
1090             break;
1091     }
1092     /* Save sorting mode (note: needed when called from UI). */
1093     g_settings_set_enum (MainSettings, "sort-mode", Sorting_Type);
1094 
1095     //ETFileList = g_list_first(etfilelist);
1096     return etfilelist;
1097 }
1098 
1099 /*
1100  * Returns the first item of the "displayed list"
1101  */
1102 GList *
ET_Displayed_File_List_First(void)1103 ET_Displayed_File_List_First (void)
1104 {
1105     ETCore->ETFileDisplayedList = g_list_first(ETCore->ETFileDisplayedList);
1106     return ETCore->ETFileDisplayedList;
1107 }
1108 
1109 /*
1110  * Returns the previous item of the "displayed list". When no more item, it returns NULL.
1111  */
1112 GList *
ET_Displayed_File_List_Previous(void)1113 ET_Displayed_File_List_Previous (void)
1114 {
1115     if (ETCore->ETFileDisplayedList && ETCore->ETFileDisplayedList->prev)
1116         return ETCore->ETFileDisplayedList = ETCore->ETFileDisplayedList->prev;
1117     else
1118         return NULL;
1119 }
1120 
1121 /*
1122  * Returns the next item of the "displayed list".
1123  * When no more item, it returns NULL to don't "overwrite" the list.
1124  */
1125 GList *
ET_Displayed_File_List_Next(void)1126 ET_Displayed_File_List_Next (void)
1127 {
1128     if (ETCore->ETFileDisplayedList && ETCore->ETFileDisplayedList->next)
1129         return ETCore->ETFileDisplayedList = ETCore->ETFileDisplayedList->next;
1130     else
1131         return NULL;
1132 }
1133 
1134 /*
1135  * Returns the last item of the "displayed list"
1136  */
1137 GList *
ET_Displayed_File_List_Last(void)1138 ET_Displayed_File_List_Last (void)
1139 {
1140     ETCore->ETFileDisplayedList = g_list_last(ETCore->ETFileDisplayedList);
1141     return ETCore->ETFileDisplayedList;
1142 }
1143 
1144 /*
1145  * Returns the item of the "displayed list" which correspond to the given 'ETFile' (used into browser list).
1146  */
1147 GList *
ET_Displayed_File_List_By_Etfile(const ET_File * ETFile)1148 ET_Displayed_File_List_By_Etfile (const ET_File *ETFile)
1149 {
1150     GList *etfilelist;
1151 
1152     etfilelist = g_list_find (g_list_first (ETCore->ETFileDisplayedList),
1153                               ETFile);
1154 
1155     if (etfilelist)
1156     {
1157         /* To "save" the position like in ET_File_List_Next... (not very good -
1158          * FIXME) */
1159         ETCore->ETFileDisplayedList = etfilelist;
1160     }
1161 
1162     return etfilelist;
1163 }
1164 
1165 /*
1166  * Load the list of displayed files (calculate length, size, ...)
1167  * It contains part (filtrated : view by artists and albums) or full ETCore->ETFileList list
1168  */
1169 void
et_displayed_file_list_set(GList * ETFileList)1170 et_displayed_file_list_set (GList *ETFileList)
1171 {
1172     GList *l = NULL;
1173 
1174     ETCore->ETFileDisplayedList = g_list_first(ETFileList);
1175 
1176     ETCore->ETFileDisplayedList_Length = et_displayed_file_list_length (ETCore->ETFileDisplayedList);
1177     ETCore->ETFileDisplayedList_TotalSize     = 0;
1178     ETCore->ETFileDisplayedList_TotalDuration = 0;
1179 
1180     // Get size and duration of files in the list
1181     for (l = ETCore->ETFileDisplayedList; l != NULL; l = g_list_next (l))
1182     {
1183         ETCore->ETFileDisplayedList_TotalSize += ((ET_File_Info *)((ET_File *)l->data)->ETFileInfo)->size;
1184         ETCore->ETFileDisplayedList_TotalDuration += ((ET_File_Info *)((ET_File *)l->data)->ETFileInfo)->duration;
1185     }
1186 
1187     /* Sort the file list. */
1188     ET_Sort_File_List (ETCore->ETFileDisplayedList,
1189                        g_settings_get_enum (MainSettings,
1190                                             "sort-mode"));
1191 
1192     /* Synchronize, so that the core file list pointer always points to the
1193      * head of the list. */
1194     ETCore->ETFileList = g_list_first (ETCore->ETFileList);
1195 
1196     /* Should renums ETCore->ETFileDisplayedList only! */
1197     et_displayed_file_list_renumber (ETCore->ETFileDisplayedList);
1198 }
1199 
1200 /*
1201  * Function used to update path of filenames into list after renaming a parent directory
1202  * (for ex: "/mp3/old_path/file.mp3" to "/mp3/new_path/file.mp3"
1203  */
1204 void
et_file_list_update_directory_name(GList * file_list,const gchar * old_path,const gchar * new_path)1205 et_file_list_update_directory_name (GList *file_list,
1206                                     const gchar *old_path,
1207                                     const gchar *new_path)
1208 {
1209     GList *filelist;
1210     ET_File *file;
1211     GList *filenamelist;
1212     gchar *filename;
1213     gchar *old_path_tmp;
1214 
1215     g_return_if_fail (file_list != NULL);
1216     g_return_if_fail (!et_str_empty (old_path));
1217     g_return_if_fail (!et_str_empty (new_path));
1218 
1219     /* Add '/' to end of path to avoid ambiguity between a directory and a
1220      * filename... */
1221     if (old_path[strlen (old_path) - 1] == G_DIR_SEPARATOR)
1222     {
1223         old_path_tmp = g_strdup (old_path);
1224     }
1225     else
1226     {
1227         old_path_tmp = g_strconcat (old_path, G_DIR_SEPARATOR_S, NULL);
1228     }
1229 
1230     for (filelist = g_list_first (file_list); filelist != NULL;
1231          filelist = g_list_next (filelist))
1232     {
1233         if ((file = filelist->data))
1234         {
1235             for (filenamelist = file->FileNameList; filenamelist != NULL;
1236                  filenamelist = g_list_next (filenamelist))
1237             {
1238                 File_Name *FileName = (File_Name *)filenamelist->data;
1239 
1240                 if ( FileName && (filename=FileName->value) )
1241                 {
1242                     /* Replace path of filename. */
1243                     if (strncmp (filename, old_path_tmp, strlen (old_path_tmp))
1244                         == 0)
1245                     {
1246                         gchar *filename_tmp;
1247 
1248                         // Create the new filename
1249                         filename_tmp = g_strconcat (new_path,
1250                                                     (new_path[strlen (new_path) - 1] == G_DIR_SEPARATOR) ? "" : G_DIR_SEPARATOR_S,
1251                                                     &filename[strlen (old_path_tmp)],NULL);
1252 
1253                         ET_Set_Filename_File_Name_Item (FileName, NULL,
1254                                                         filename_tmp);
1255                         g_free (filename_tmp);
1256                     }
1257                 }
1258              }
1259         }
1260     }
1261 
1262     g_free (old_path_tmp);
1263 }
1264 
1265 /*
1266  * Execute one 'undo' in the main undo list (it selects the last ETFile changed,
1267  * before to apply an undo action)
1268  */
1269 ET_File *
ET_Undo_History_File_Data(void)1270 ET_Undo_History_File_Data (void)
1271 {
1272     ET_File *ETFile;
1273     const ET_History_File *ETHistoryFile;
1274 
1275     g_return_val_if_fail (ETCore->ETHistoryFileList != NULL, NULL);
1276     g_return_val_if_fail (et_history_list_has_undo (ETCore->ETHistoryFileList), NULL);
1277 
1278     ETHistoryFile = (ET_History_File *)ETCore->ETHistoryFileList->data;
1279     ETFile        = (ET_File *)ETHistoryFile->ETFile;
1280     ET_Displayed_File_List_By_Etfile(ETFile);
1281     ET_Undo_File_Data(ETFile);
1282 
1283     if (ETCore->ETHistoryFileList->prev)
1284         ETCore->ETHistoryFileList = ETCore->ETHistoryFileList->prev;
1285     return ETFile;
1286 }
1287 
1288 /*
1289  * et_history_list_has_undo:
1290  * @history_list: the end of a history list
1291  *
1292  * Returns: %TRUE if undo file list contains undo data, %FALSE otherwise
1293  */
1294 gboolean
et_history_list_has_undo(GList * history_list)1295 et_history_list_has_undo (GList *history_list)
1296 {
1297     return history_list && history_list->prev;
1298 }
1299 
1300 
1301 /*
1302  * Execute one 'redo' in the main undo list
1303  */
1304 ET_File *
ET_Redo_History_File_Data(void)1305 ET_Redo_History_File_Data (void)
1306 {
1307     ET_File *ETFile;
1308     ET_History_File *ETHistoryFile;
1309 
1310     if (!ETCore->ETHistoryFileList
1311         || !et_history_list_has_redo (ETCore->ETHistoryFileList))
1312     {
1313         return NULL;
1314     }
1315 
1316     ETHistoryFile = (ET_History_File *)ETCore->ETHistoryFileList->next->data;
1317     ETFile        = (ET_File *)ETHistoryFile->ETFile;
1318     ET_Displayed_File_List_By_Etfile(ETFile);
1319     ET_Redo_File_Data(ETFile);
1320 
1321     if (ETCore->ETHistoryFileList->next)
1322         ETCore->ETHistoryFileList = ETCore->ETHistoryFileList->next;
1323     return ETFile;
1324 }
1325 
1326 /*
1327  * et_history_list_has_redo:
1328  * @history_list: the end of a history list
1329  *
1330  * Returns: %TRUE if undo file list contains redo data, %FALSE otherwise
1331  */
1332 gboolean
et_history_list_has_redo(GList * history_list)1333 et_history_list_has_redo (GList *history_list)
1334 {
1335     return history_list && history_list->next;
1336 }
1337 
1338 /*
1339  * Add a ETFile item to the main undo list of files
1340  */
1341 GList *
et_history_list_add(GList * history_list,ET_File * ETFile)1342 et_history_list_add (GList *history_list,
1343                      ET_File *ETFile)
1344 {
1345     ET_History_File *ETHistoryFile;
1346     GList *result;
1347 
1348     g_return_val_if_fail (ETFile != NULL, FALSE);
1349 
1350     ETHistoryFile = g_slice_new0 (ET_History_File);
1351     ETHistoryFile->ETFile = ETFile;
1352 
1353     /* The undo list must contains one item before the 'first undo' data */
1354     if (!history_list)
1355     {
1356         result = g_list_append (NULL,
1357                                 g_slice_new0 (ET_History_File));
1358     }
1359     else
1360     {
1361         result = history_list;
1362     }
1363 
1364     /* Add the item to the list (cut end of list from the current element) */
1365     result = g_list_append (result, ETHistoryFile);
1366     /* TODO: Investigate whether this is sensible. */
1367     result = g_list_last (result);
1368 
1369     return result;
1370 }
1371 
1372 /*
1373  * et_file_list_check_all_saved:
1374  * @etfilelist: (element-type ET_File) (allow-none): a list of files
1375  *
1376  * Checks if some files, in the list, have been changed but not saved.
1377  *
1378  * Returns: %TRUE if all files have been saved, %FALSE otherwise
1379  */
1380 gboolean
et_file_list_check_all_saved(GList * etfilelist)1381 et_file_list_check_all_saved (GList *etfilelist)
1382 {
1383     if (!etfilelist)
1384     {
1385         return TRUE;
1386     }
1387     else
1388     {
1389         GList *l;
1390 
1391         for (l = g_list_first (etfilelist); l != NULL; l = g_list_next (l))
1392         {
1393             if (!et_file_check_saved ((ET_File *)l->data))
1394             {
1395                 return FALSE;
1396             }
1397         }
1398 
1399         return TRUE;
1400     }
1401 }
1402 
1403 /*
1404  * Returns the number of file in the directory of the selected file.
1405  * Parameter "path" should be in UTF-8
1406  */
1407 guint
et_file_list_get_n_files_in_path(GList * file_list,const gchar * path_utf8)1408 et_file_list_get_n_files_in_path (GList *file_list,
1409                                   const gchar *path_utf8)
1410 {
1411     gchar *path_key;
1412     GList *l;
1413     guint  count = 0;
1414 
1415     g_return_val_if_fail (path_utf8 != NULL, count);
1416 
1417     path_key = g_utf8_collate_key (path_utf8, -1);
1418 
1419     for (l = g_list_first (file_list); l != NULL; l = g_list_next (l))
1420     {
1421         ET_File *ETFile = (ET_File *)l->data;
1422         const gchar *cur_filename_utf8 = ((File_Name *)((GList *)ETFile->FileNameCur)->data)->value_utf8;
1423         gchar *dirname_utf8      = g_path_get_dirname(cur_filename_utf8);
1424         gchar *dirname_key = g_utf8_collate_key (dirname_utf8, -1);
1425 
1426         if (strcmp (dirname_utf8, path_utf8) == 0)
1427         {
1428             count++;
1429         }
1430 
1431         g_free (dirname_utf8);
1432         g_free (dirname_key);
1433     }
1434 
1435     g_free (path_key);
1436 
1437     return count;
1438 }
1439