aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/et_core.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/et_core.c')
-rwxr-xr-xsrc/et_core.c4835
1 files changed, 4835 insertions, 0 deletions
diff --git a/src/et_core.c b/src/et_core.c
new file mode 100755
index 0000000..3e608f3
--- /dev/null
+++ b/src/et_core.c
@@ -0,0 +1,4835 @@
+/* et_core.c - 2001/10/21 */
+/*
+ * EasyTAG - Tag editor for MP3 and Ogg Vorbis files
+ * Copyright (C) 2000-2003 Jerome Couderc <easytag@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <config.h>
+
+#include <gtk/gtk.h>
+#include <glib/gi18n-lib.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <utime.h>
+#include <ctype.h>
+#include "errno.h"
+
+#include "easytag.h"
+#include "et_core.h"
+#include "mpeg_header.h"
+#include "monkeyaudio_header.h"
+#include "musepack_header.h"
+#ifdef ENABLE_MP3
+# include "id3_tag.h"
+#endif
+#include "picture.h"
+#include "ape_tag.h"
+#ifdef ENABLE_OGG
+# include "ogg_header.h"
+# include "ogg_tag.h"
+#endif
+#ifdef ENABLE_FLAC
+# include "flac_header.h"
+# include "flac_tag.h"
+#endif
+#ifdef ENABLE_MP4
+# include "mp4_header.h"
+# include "mp4_tag.h"
+#endif
+#ifdef ENABLE_WAVPACK
+# include "wavpack_header.h"
+# include "wavpack_tag.h"
+#endif
+#include "bar.h"
+#include "browser.h"
+#include "log.h"
+#include "misc.h"
+#include "setting.h"
+#include "msgbox.h"
+#include "charset.h"
+
+#ifdef WIN32
+# include "win32/win32dep.h"
+#endif
+
+
+/***************
+ * Declaration *
+ ***************/
+
+ET_Core *ETCore = NULL;
+
+/*
+ * Colors Used
+ */
+GdkColor LIGHT_BLUE = {0, 0xddd1, 0xeeec, 0xffff};
+GdkColor RED = {0, 0xffff, 0x0000, 0x0000};
+GdkColor LIGHT_RED = {0, 0xffff, 0x8888, 0x8888};
+GdkColor GREY = {0, 0xaa00, 0xaa00, 0xaa00};
+GdkColor LIGHT_GREY = {0, 0xd2d2, 0xd2d2, 0xd2d2};
+GdkColor YELLOW = {0, 0xffff, 0xffff, 0xcccc};
+GdkColor BLACK = {0, 0x0000, 0x0000, 0x0000};
+
+
+/**************
+ * Prototypes *
+ **************/
+
+void ET_Core_Create (void);
+void ET_Core_Initialize (void);
+void ET_Core_Free (void);
+void ET_Core_Destroy (void);
+
+//gboolean ET_File_Is_Supported (gchar *filename);
+gchar *ET_Get_File_Extension (gchar *filename);
+ET_File_Description *ET_Get_File_Description (gchar *filename);
+ET_File_Description *ET_Get_File_Description_From_Extension (gchar *extension);
+
+gboolean ET_Free_File_List (void);
+gboolean ET_Free_File_List_Item (ET_File *ETFile);
+gboolean ET_Free_File_Name_List (GList *FileNameList);
+gboolean ET_Free_File_Tag_List (GList *FileTagList);
+gboolean ET_Free_File_Name_Item (File_Name *FileName);
+gboolean ET_Free_File_Tag_Item (File_Tag *FileTag);
+gboolean ET_Free_File_Tag_Item_Other_Field (File_Tag *FileTag);
+gboolean ET_Free_File_Info_Item (ET_File_Info *ETFileInfo);
+gboolean ET_Free_History_File_List (void);
+gboolean ET_Free_Displayed_File_List (void);
+gboolean ET_Free_Artist_Album_File_List (void);
+
+void ET_Initialize_File_Item (ET_File *ETFile);
+void ET_Initialize_File_Tag_Item (File_Tag *FileTag);
+void ET_Initialize_File_Name_Item (File_Name *FileName);
+void ET_Initialize_File_Info_Item (ET_File_Info *ETFileInfo);
+
+//gboolean ET_Copy_File_Tag_Item (ET_File *ETFile, File_Tag *FileTag);
+gboolean ET_Copy_File_Tag_Item_Other_Field (ET_File *ETFile, File_Tag *FileTag);
+//gboolean ET_Set_Field_File_Name_Item (gint *FileNameField, gchar *value);
+//gboolean ET_Set_Field_File_Name_Item (gchar **FileNameField, gchar *value);
+//gboolean ET_Set_Field_File_Tag_Item (gint *FileTagField, gchar *value);
+//gboolean ET_Set_Field_File_Tag_Picture (gchar **FileTagField, Picture *pic);
+
+guint ET_File_Key_New (void);
+guint ET_Undo_Key_New (void);
+
+GList *ET_File_List_Remove (GList *item_to_remove);
+
+gboolean ET_Remove_File_From_File_List (ET_File *ETFile);
+gboolean ET_Remove_File_From_Artist_Album_List (ET_File *ETFile);
+
+void ET_Display_File_And_List_Status_To_UI (ET_File *ETFile);
+void ET_Display_Filename_To_UI (ET_File *ETFile);
+gboolean ET_Display_File_Tag_To_UI (ET_File *ETFile);
+gboolean ET_Display_File_Info_To_UI (ET_File_Info *ETFileInfo);
+
+gboolean ET_Save_File_Name_From_UI (ET_File *ETFile, File_Name *FileName);
+gboolean ET_Save_File_Name_Internal (ET_File *ETFile, File_Name *FileName);
+gboolean ET_Save_File_Tag_From_UI (File_Tag *FileTag);
+gboolean ET_Save_File_Tag_Internal (ET_File *ETFile, File_Tag *FileTag);
+
+void ET_Mark_File_Tag_As_Saved (ET_File *ETFile);
+void ET_Mark_File_Name_As_Saved (ET_File *ETFile);
+
+gboolean ET_Manage_Changes_Of_File_Data (ET_File *ETFile, File_Name *FileName, File_Tag *FileTag);
+gboolean ET_Detect_Changes_Of_File_Name (File_Name *FileName1, File_Name *FileName2);
+gboolean ET_Detect_Changes_Of_File_Tag (File_Tag *FileTag1, File_Tag *FileTag2);
+gboolean ET_Add_File_Name_To_List (ET_File *ETFile, File_Name *FileName);
+gboolean ET_Add_File_Tag_To_List (ET_File *ETFile, File_Tag *FileTag);
+gboolean ET_Add_File_To_History_List (ET_File *ETFile);
+gboolean ET_Add_File_To_Artist_Album_File_List (ET_File *ETFile);
+
+GList *ET_Displayed_File_List_First (void);
+GList *ET_Displayed_File_List_Previous (void);
+GList *ET_Displayed_File_List_Next (void);
+GList *ET_Displayed_File_List_Last (void);
+GList *ET_Displayed_File_List_By_Etfile (ET_File *ETFile);
+GList *ET_Displayed_File_List_By_Position (gulong pos_in_list);
+guint ET_Displayed_File_List_Get_Length (void);
+void ET_Displayed_File_List_Number (void);
+
+gboolean ET_Set_Displayed_File_List (GList *ETFileList);
+
+gboolean ET_Read_File_Info (gchar *filename, ET_File_Info *ETFileInfo);
+//gboolean ET_File_Name_Convert_Character (gchar *filename_utf8);
+void ET_File_Name_Check_Length (ET_File *ETFile, gchar *filename_utf8);
+
+gint ET_Comp_Func_Sort_File_By_Ascending_Index_Key (ET_File *ETFile1, ET_File *ETFile2);
+
+gint ET_Comp_Func_Sort_Artist_Item_By_Ascending_Artist (GList *AlbumList1, GList *AlbumList2);
+gint ET_Comp_Func_Sort_Album_Item_By_Ascending_Album (GList *etfilelist1, GList *etfilelist2);
+gint ET_Comp_Func_Sort_Etfile_Item_By_Ascending_Filename (ET_File *ETFile1, ET_File *ETFile2);
+
+
+/*******************
+ * Basic functions *
+ *******************/
+
+/*
+ * Returns the extension of the file
+ */
+gchar *ET_Get_File_Extension (gchar *filename)
+{
+ if (filename)
+ return strrchr(filename, '.');
+ else
+ return NULL;
+}
+
+
+/*
+ * Determine description of file using his extension.
+ * If extension is NULL or not found into the tab, it returns the last entry for UNKNOWN_FILE.
+ */
+ET_File_Description *ET_Get_File_Description_From_Extension (gchar *extension)
+{
+ guint i;
+
+ if (!extension) // Unknown file
+ return (ET_File_Description*) &ETFileDescription[ET_FILE_DESCRIPTION_SIZE];
+
+ for (i=0; i<ET_FILE_DESCRIPTION_SIZE; i++) // Use of '<' instead of '<=' to avoid to test for Unknown file
+ if ( strcasecmp(extension,ETFileDescription[i].Extension)==0 )
+ return (ET_File_Description*) &ETFileDescription[i];
+
+ // If not found in the list
+ return (ET_File_Description*) &ETFileDescription[ET_FILE_DESCRIPTION_SIZE];
+}
+
+
+/*
+ * Determines description of file.
+ * Determines first the extension. If extension is NULL or not found into the tab,
+ * it returns the last entry for UNKNOWN_FILE.
+ */
+ET_File_Description *ET_Get_File_Description (gchar *filename)
+{
+ return ET_Get_File_Description_From_Extension(ET_Get_File_Extension(filename));
+}
+
+
+/*
+ * Returns TRUE if the file is supported, else returns FALSE
+ */
+gboolean ET_File_Is_Supported (gchar *filename)
+{
+ if (ET_Get_File_Description(filename)->FileType != UNKNOWN_FILE)
+ return TRUE;
+ else
+ return FALSE;
+}
+
+
+
+
+/*****************************************************************************
+ * Manipulation of ET_Core functions (main functions needed for the program) *
+ *****************************************************************************/
+void ET_Core_Create (void)
+{
+ // Allocate
+ if (ETCore == NULL)
+ ETCore = g_malloc0(sizeof(ET_Core));
+
+ // Initialize
+ ET_Core_Initialize();
+}
+
+void ET_Core_Initialize (void)
+{
+ ETCore->ETFileList = NULL;
+ ETCore->ETFileDisplayedList = NULL;
+ ETCore->ETFileDisplayedListPtr = NULL;
+ ETCore->ETFileDisplayedList_Length = 0;
+ ETCore->ETFileDisplayedList_TotalSize = 0;
+ ETCore->ETFileDisplayedList_TotalDuration = 0;
+ ETCore->ETFileDisplayed = NULL;
+ ETCore->ETHistoryFileList = NULL;
+ ETCore->ETArtistAlbumFileList = NULL;
+}
+
+void ET_Core_Free (void)
+{
+ // Frees first lists, then initialize
+ if (ETCore->ETFileList)
+ ET_Free_File_List();
+
+ if (ETCore->ETFileDisplayedList)
+ ET_Free_Displayed_File_List();
+
+ if (ETCore->ETHistoryFileList)
+ ET_Free_History_File_List();
+
+ if (ETCore->ETArtistAlbumFileList)
+ ET_Free_Artist_Album_File_List();
+
+ // Initialize by security
+ ET_Core_Initialize();
+}
+
+void ET_Core_Destroy (void)
+{
+ // Free attached data
+ ET_Core_Free();
+
+ // Unallocate
+ g_free(ETCore);
+ ETCore = NULL;
+}
+
+
+
+/**************************
+ * Initializing functions *
+ **************************/
+
+/*
+ * Create a new File_Name structure
+ */
+File_Name *ET_File_Name_Item_New (void)
+{
+ File_Name *FileName;
+
+ FileName = g_malloc0(sizeof(File_Name));
+ ET_Initialize_File_Name_Item(FileName);
+
+ return FileName;
+}
+
+
+/*
+ * Create a new File_Tag structure
+ */
+File_Tag *ET_File_Tag_Item_New (void)
+{
+ File_Tag *FileTag;
+
+ FileTag = g_malloc0(sizeof(File_Tag));
+ ET_Initialize_File_Tag_Item(FileTag);
+
+ return FileTag;
+}
+
+
+/*
+ * Create a new File_Info structure
+ */
+ET_File_Info *ET_File_Info_Item_New (void)
+{
+ ET_File_Info *ETFileInfo;
+
+ ETFileInfo = g_malloc0(sizeof(ET_File_Info));
+ ET_Initialize_File_Info_Item(ETFileInfo);
+
+ return ETFileInfo;
+}
+
+
+/*
+ * Create a new ET_File structure
+ */
+ET_File *ET_File_Item_New (void)
+{
+ ET_File *ETFile;
+
+ ETFile = g_malloc0(sizeof(ET_File));
+ ET_Initialize_File_Item(ETFile);
+
+ return ETFile;
+}
+
+
+void ET_Initialize_File_Name_Item (File_Name *FileName)
+{
+ if (FileName)
+ {
+ FileName->key = ET_Undo_Key_New();
+ FileName->saved = FALSE;
+ FileName->value = NULL;
+ FileName->value_utf8 = NULL;
+ FileName->value_ck = NULL;
+ }
+}
+
+
+void ET_Initialize_File_Tag_Item (File_Tag *FileTag)
+{
+ if (FileTag)
+ {
+ FileTag->key = ET_Undo_Key_New();
+ FileTag->saved = FALSE;
+ FileTag->title = NULL;
+ FileTag->artist = NULL;
+ FileTag->album = NULL;
+ FileTag->disc_number = NULL;
+ FileTag->track = NULL;
+ FileTag->track_total = NULL;
+ FileTag->year = NULL;
+ FileTag->genre = NULL;
+ FileTag->comment = NULL;
+ FileTag->composer = NULL;
+ FileTag->orig_artist = NULL;
+ FileTag->copyright = NULL;
+ FileTag->url = NULL;
+ FileTag->encoded_by = NULL;
+ FileTag->picture = NULL;
+ FileTag->other = NULL;
+ }
+}
+
+
+void ET_Initialize_File_Info_Item (ET_File_Info *ETFileInfo)
+{
+ if (ETFileInfo)
+ {
+ ETFileInfo->mpc_profile = NULL;
+ ETFileInfo->mpc_version = NULL;
+ }
+}
+
+
+void ET_Initialize_File_Item (ET_File *ETFile)
+{
+ if (ETFile)
+ {
+ ETFile->IndexKey = 0;
+ ETFile->ETFileKey = 0;
+ ETFile->ETFileDescription = NULL;
+ ETFile->ETFileInfo = NULL;
+ ETFile->FileNameCur = NULL;
+ ETFile->FileNameNew = NULL;
+ ETFile->FileNameList = NULL;
+ ETFile->FileNameListBak = NULL;
+ ETFile->FileTag = NULL;
+ ETFile->FileTagList = NULL;
+ ETFile->FileTagListBak = NULL;
+ }
+}
+
+
+/* Key for each item of ETFileList */
+guint ET_File_Key_New (void)
+{
+ static guint ETFileKey = 0;
+ return ++ETFileKey;
+}
+
+/* Key for Undo */
+guint ET_Undo_Key_New (void)
+{
+ static guint ETUndoKey = 0;
+ return ++ETUndoKey;
+}
+
+
+
+
+/**********************************
+ * File adding/removing functions *
+ **********************************/
+
+/*
+ * ET_Add_File_To_File_List: Add a file to the "main" list. And get all informations of the file.
+ * The filename passed in should be in raw format, only convert it to UTF8 when displaying it.
+ */
+GList *ET_Add_File_To_File_List (gchar *filename)
+{
+ ET_File_Description *ETFileDescription;
+ ET_File *ETFile;
+ File_Name *FileName;
+ File_Tag *FileTag;
+ ET_File_Info *ETFileInfo;
+ gchar *ETFileExtension;
+ guint ETFileKey;
+ guint undo_key;
+ gchar *filename_utf8 = filename_to_display(filename);
+
+ if (!filename)
+ return ETCore->ETFileList;
+
+ /* Primary Key for this file */
+ ETFileKey = ET_File_Key_New();
+
+ /* Get description of the file */
+ ETFileDescription = ET_Get_File_Description(filename);
+
+ /* Get real extension of the file (keeping the case) */
+ ETFileExtension = g_strdup(ET_Get_File_Extension(filename));
+
+ /* Fill the File_Name structure for FileNameList */
+ FileName = ET_File_Name_Item_New();
+ FileName->saved = TRUE; /* The file hasn't been changed, so it's saved */
+ FileName->value = filename;
+ FileName->value_utf8 = filename_utf8;
+ FileName->value_ck = g_utf8_collate_key_for_filename(filename_utf8, -1);
+
+ /* Fill the File_Tag structure for FileTagList */
+ FileTag = ET_File_Tag_Item_New();
+ FileTag->saved = TRUE; /* The file hasn't been changed, so it's saved */
+
+ switch (ETFileDescription->TagType)
+ {
+#ifdef ENABLE_MP3
+ case ID3_TAG:
+ Id3tag_Read_File_Tag(filename,FileTag);
+ break;
+#endif
+#ifdef ENABLE_OGG
+ case OGG_TAG:
+ Ogg_Tag_Read_File_Tag(filename,FileTag);
+ break;
+#endif
+#ifdef ENABLE_FLAC
+ case FLAC_TAG:
+ Flac_Tag_Read_File_Tag(filename,FileTag);
+ break;
+#endif
+ case APE_TAG:
+ Ape_Tag_Read_File_Tag(filename,FileTag);
+ break;
+#ifdef ENABLE_MP4
+ case MP4_TAG:
+ Mp4tag_Read_File_Tag(filename,FileTag);
+ break;
+#endif
+#ifdef ENABLE_WAVPACK
+ case WAVPACK_TAG:
+ Wavpack_Tag_Read_File_Tag(filename, FileTag);
+ break;
+#endif
+ case UNKNOWN_TAG:
+ default:
+ Log_Print("FileTag: Undefined tag type (%d) for file %s",ETFileDescription->TagType,filename_utf8);
+ break;
+ }
+
+ /* Fill the ET_File_Info structure */
+ ETFileInfo = ET_File_Info_Item_New ();
+
+ switch (ETFileDescription->FileType)
+ {
+#ifdef ENABLE_MP3
+ case MP3_FILE:
+ case MP2_FILE:
+ Mpeg_Header_Read_File_Info(filename,ETFileInfo);
+ break;
+#endif
+#ifdef ENABLE_OGG
+ case OGG_FILE:
+ Ogg_Header_Read_File_Info(filename,ETFileInfo);
+ break;
+#endif
+#ifdef ENABLE_SPEEX
+ case SPEEX_FILE:
+ Speex_Header_Read_File_Info(filename,ETFileInfo);
+ break;
+#endif
+#ifdef ENABLE_FLAC
+ case FLAC_FILE:
+ Flac_Header_Read_File_Info(filename,ETFileInfo);
+ break;
+#endif
+ case MPC_FILE:
+ Mpc_Header_Read_File_Info(filename,ETFileInfo);
+ break;
+ case MAC_FILE:
+ Mac_Header_Read_File_Info(filename,ETFileInfo);
+ break;
+#ifdef ENABLE_WAVPACK
+ case WAVPACK_FILE:
+ Wavpack_Header_Read_File_Info(filename, ETFileInfo);
+ break;
+#endif
+#ifdef ENABLE_MP4
+ case MP4_FILE:
+ Mp4_Header_Read_File_Info(filename,ETFileInfo);
+ break;
+#endif
+ case UNKNOWN_FILE:
+ default:
+ Log_Print("ETFileInfo: Undefined file type (%d) for file %s",ETFileDescription->FileType,filename_utf8);
+ ET_Read_File_Info(filename,ETFileInfo); // To get at least the file size
+ break;
+ }
+
+ /* Attach all data defined above to this ETFile item */
+ ETFile = ET_File_Item_New();
+ ETFile->IndexKey = 0; // Will be renumered after...
+ ETFile->ETFileKey = ETFileKey;
+ ETFile->ETFileDescription = ETFileDescription;
+ ETFile->ETFileExtension = ETFileExtension;
+ ETFile->FileNameList = g_list_append(NULL,FileName);
+ ETFile->FileNameCur = ETFile->FileNameList;
+ ETFile->FileNameNew = ETFile->FileNameList;
+ ETFile->FileTagList = g_list_append(NULL,FileTag);
+ ETFile->FileTag = ETFile->FileTagList;
+ ETFile->ETFileInfo = ETFileInfo;
+
+ /* Add the item to the "main list" */
+ ETCore->ETFileList = g_list_append(ETCore->ETFileList,ETFile);
+
+
+ /*
+ * Process the filename and tag to generate undo if needed...
+ * The undo key must be the same for FileName and FileTag => changed in the same time
+ */
+ undo_key = ET_Undo_Key_New();
+
+ FileName = ET_File_Name_Item_New();
+ FileName->key = undo_key;
+ ET_Save_File_Name_Internal(ETFile,FileName);
+
+ FileTag = ET_File_Tag_Item_New();
+ FileTag->key = undo_key;
+ ET_Save_File_Tag_Internal(ETFile,FileTag);
+
+ /*
+ * Generate undo for the file and the main undo list.
+ * If no changes detected, FileName and FileTag item are deleted.
+ */
+ ET_Manage_Changes_Of_File_Data(ETFile,FileName,FileTag);
+
+ /* Add the item to the ArtistAlbum list (placed here to take advantage of previous changes) */
+ //ET_Add_File_To_Artist_Album_File_List(ETFile);
+
+ //ET_Debug_Print_File_List(ETCore->ETFileList,__FILE__,__LINE__,__FUNCTION__);
+ return ETCore->ETFileList;
+}
+
+gboolean ET_Create_Artist_Album_File_List (void)
+{
+ GList *ETFileList;
+
+ if (ETCore->ETArtistAlbumFileList)
+ ET_Free_Artist_Album_File_List();
+
+ ETFileList = g_list_first(ETCore->ETFileList);
+ while (ETFileList)
+ {
+ ET_File *ETFile = (ET_File *)ETFileList->data;
+ ET_Add_File_To_Artist_Album_File_List(ETFile);
+ ETFileList = ETFileList->next;
+ }
+ //ET_Debug_Print_Artist_Album_List(__FILE__,__LINE__,__FUNCTION__);
+ return TRUE;
+}
+/*
+ * The ETArtistAlbumFileList contains 3 levels of lists to sort the ETFile by artist then by album :
+ * - "ETArtistAlbumFileList" list is a list of "ArtistList" items,
+ * - "ArtistList" list is a list of "AlbumList" items,
+ * - "AlbumList" list is a list of ETFile items.
+ * Note : use the function ET_Debug_Print_Artist_Album_List(...) to understand how it works, it needed...
+ */
+gboolean ET_Add_File_To_Artist_Album_File_List (ET_File *ETFile)
+{
+ if (ETFile)
+ {
+ gchar *ETFile_Artist = ((File_Tag *)ETFile->FileTag->data)->artist; // Artist value of the ETFile passed in parameter
+ gchar *ETFile_Album = ((File_Tag *)ETFile->FileTag->data)->album; // Album value of the ETFile passed in parameter
+ gchar *etfile_artist = NULL;
+ gchar *etfile_album = NULL;
+ GList *ArtistList = NULL;
+ GList *AlbumList = NULL;
+ GList *etfilelist = NULL;
+ ET_File *etfile = NULL;
+
+
+ ArtistList = ETCore->ETArtistAlbumFileList;
+ while (ArtistList)
+ {
+ AlbumList = (GList *)ArtistList->data; /* Take the first item */
+ if (AlbumList
+ && (etfilelist = (GList *)AlbumList->data) /* Take the first item */
+ && (etfile = (ET_File *)etfilelist->data) /* Take the first etfile item */
+ && ((File_Tag *)etfile->FileTag->data) != NULL )
+ {
+ etfile_artist = ((File_Tag *)etfile->FileTag->data)->artist;
+ }else
+ {
+ etfile_artist = NULL;
+ }
+
+ if ( (etfile_artist && ETFile_Artist && strcmp(etfile_artist,ETFile_Artist)==0)
+ || (!etfile_artist && !ETFile_Artist) ) // The "artist" values correspond?
+ {
+ // The "ArtistList" item was found!
+ while (AlbumList)
+ {
+ if ( (etfilelist = (GList *)AlbumList->data)
+ && (etfile = (ET_File *)etfilelist->data)
+ && ((File_Tag *)etfile->FileTag->data) != NULL )
+ {
+ etfile_album = ((File_Tag *)etfile->FileTag->data)->album;
+ }else
+ {
+ etfile_album = NULL;
+ }
+
+ if ( (etfile_album && ETFile_Album && strcmp(etfile_album,ETFile_Album)==0)
+ || (!etfile_album && !ETFile_Album) ) // The "album" values correspond?
+ {
+ // The "AlbumList" item was found!
+ // Add the ETFile to this AlbumList item
+ //g_print(">>> add to etfile list (%s)\n",g_path_get_basename(((File_Name *)ETFile->FileNameCur->data)->value));
+ AlbumList->data = (gpointer) g_list_append((GList *)AlbumList->data,ETFile);
+ AlbumList->data = (gpointer) g_list_sort((GList *)AlbumList->data,(GCompareFunc)ET_Comp_Func_Sort_Etfile_Item_By_Ascending_Filename);
+ return TRUE;
+ }
+ AlbumList = AlbumList->next;
+ }
+ // The "AlbumList" item was NOT found! => Add a new "AlbumList" item (+...) item to the "ArtistList" list
+ etfilelist = g_list_append(NULL,ETFile);
+ //g_print(">>> add new album (%s)\n",g_path_get_basename(((File_Name *)ETFile->FileNameCur->data)->value));
+ ArtistList->data = (gpointer) g_list_append((GList *)ArtistList->data,etfilelist);
+ ArtistList->data = (gpointer) g_list_sort((GList *)ArtistList->data,(GCompareFunc)ET_Comp_Func_Sort_Album_Item_By_Ascending_Album);
+ return TRUE;
+ }
+ ArtistList = ArtistList->next;
+ }
+ // The "ArtistList" item was NOT found! => Add a new "ArtistList" to the main list (=ETArtistAlbumFileList)
+ etfilelist = g_list_append(NULL,ETFile);
+ AlbumList = g_list_append(NULL,etfilelist);
+ //g_print(">>> add new artist (%s)(etfile:%x AlbumList:%x)\n",g_path_get_basename(((File_Name *)ETFile->FileNameCur->data)->value),etfilelist,AlbumList);
+ ETCore->ETArtistAlbumFileList = g_list_append(ETCore->ETArtistAlbumFileList,AlbumList);
+ // Sort the list by ascending Artist
+ ETCore->ETArtistAlbumFileList = g_list_sort(ETCore->ETArtistAlbumFileList,(GCompareFunc)ET_Comp_Func_Sort_Artist_Item_By_Ascending_Artist);
+
+ return TRUE;
+ }else
+ {
+ return FALSE;
+ }
+}
+
+
+
+/*
+ * Delete the corresponding file and free the allocated data. Return TRUE if deleted.
+ */
+gboolean ET_Remove_File_From_File_List (ET_File *ETFile)
+{
+ GList *ETFileList = NULL; // Item containing the ETFile to delete... (in ETCore->ETFileList)
+ GList *ETFileDisplayedList = NULL; // Item containing the ETFile to delete... (in ETCore->ETFileDisplayedList)
+
+ // Remove infos of the file
+ ETCore->ETFileDisplayedList_TotalSize -= ((ET_File_Info *)ETFile->ETFileInfo)->size;
+ ETCore->ETFileDisplayedList_TotalDuration -= ((ET_File_Info *)ETFile->ETFileInfo)->duration;
+
+ // Find the ETFileList containing the ETFile item
+ ETFileDisplayedList = g_list_find(g_list_first(ETCore->ETFileDisplayedList),ETFile);
+ ETFileList = g_list_find(g_list_first(ETCore->ETFileList),ETFile);
+
+ // Note : this ETFileList must be used only for ETCore->ETFileDisplayedList, and not ETCore->ETFileDisplayed
+ if (ETCore->ETFileDisplayedList == ETFileDisplayedList)
+ {
+ if (ETFileList->next)
+ ETCore->ETFileDisplayedList = ETFileDisplayedList->next;
+ else if (ETFileList->prev)
+ ETCore->ETFileDisplayedList = ETFileDisplayedList->prev;
+ else
+ ETCore->ETFileDisplayedList = NULL;
+ }
+ // If the current displayed file is just removing, it will be unable to display it again!
+ if (ETCore->ETFileDisplayed == ETFile)
+ {
+ if (ETCore->ETFileDisplayedList)
+ ETCore->ETFileDisplayed = (ET_File *)ETCore->ETFileDisplayedList->data;
+ else
+ ETCore->ETFileDisplayed = (ET_File *)NULL;
+ }
+
+ // Remove the file from the ETFileList list
+ ETCore->ETFileList = g_list_first(g_list_remove_link(g_list_first(ETCore->ETFileList),ETFileList));
+
+ // Remove the file from the ETArtistAlbumList list
+ ET_Remove_File_From_Artist_Album_List(ETFile);
+ //ET_Debug_Print_Artist_Album_List(__FILE__,__LINE__,__FUNCTION__);
+
+ // Remove the file from the ETFileDisplayedList list (if not already done)
+ if ( (ETFileDisplayedList = g_list_find(ETCore->ETFileDisplayedList,ETFile)) )
+ {
+ ETCore->ETFileDisplayedList = g_list_first(g_list_remove_link(g_list_first(ETCore->ETFileDisplayedList),ETFileDisplayedList));
+ }
+
+ // Free data of the file
+ ET_Free_File_List_Item(ETFile);
+ if (ETFileList)
+ g_list_free(ETFileList);
+ if (ETFileDisplayedList)
+ g_list_free(ETFileDisplayedList);
+
+ // Recalculate length of ETFileDisplayedList list
+ ET_Displayed_File_List_Get_Length();
+
+ // To number the ETFile in the list
+ ET_Displayed_File_List_Number();
+
+ // Displaying...
+ if (ETCore->ETFileDisplayedList)
+ {
+ if (ETCore->ETFileDisplayed)
+ {
+ ET_Displayed_File_List_By_Etfile(ETCore->ETFileDisplayed);
+ }else if (ETCore->ETFileDisplayedList->data)
+ {
+ // Select the new file (synchronize index,...)
+ ET_Displayed_File_List_By_Etfile((ET_File *)ETCore->ETFileDisplayedList->data);
+ }
+ }else
+ {
+ // Reinit the tag and file area
+ Clear_File_Entry_Field();
+ Clear_Header_Fields();
+ Clear_Tag_Entry_Fields();
+ gtk_label_set_text(GTK_LABEL(FileIndex),"0/0:");
+ Update_Command_Buttons_Sensivity();
+ }
+
+ return TRUE;
+}
+
+
+/*
+ * Delete the corresponding file (allocated data was previously freed!). Return TRUE if deleted.
+ */
+gboolean ET_Remove_File_From_Artist_Album_List (ET_File *ETFile)
+{
+ GList *ArtistList;
+ GList *AlbumList;
+ GList *etfilelist;
+ ET_File *etfile;
+
+
+ // Search the ETFile in the list...
+ ArtistList = ETCore->ETArtistAlbumFileList;
+ while (ArtistList && ETFile)
+ {
+ AlbumList = g_list_first((GList *)ArtistList->data);
+ while (AlbumList)
+ {
+ etfilelist = g_list_first((GList *)AlbumList->data);
+ while (etfilelist)
+ {
+ etfile = (ET_File *)etfilelist->data;
+ if (ETFile == etfile) // The ETFile to delete was found!
+ {
+ etfilelist = g_list_remove(etfilelist,ETFile);
+ if (etfilelist) // Delete from AlbumList
+ {
+ AlbumList->data = (gpointer) g_list_first(etfilelist);
+ }else
+ {
+ AlbumList = g_list_remove(AlbumList,AlbumList->data);
+ if (AlbumList) // Delete from ArtistList
+ {
+ ArtistList->data = (gpointer) g_list_first(AlbumList);
+ }else
+ {
+ ETCore->ETArtistAlbumFileList = g_list_remove(ArtistList,ArtistList->data); // Delete from the main list
+ if (ETCore->ETArtistAlbumFileList)
+ ETCore->ETArtistAlbumFileList = g_list_first(ETCore->ETArtistAlbumFileList);
+ return TRUE;
+ }
+ return TRUE;
+ }
+ return TRUE;
+ }
+ etfilelist = etfilelist->next;
+ }
+ AlbumList = AlbumList->next;
+ }
+ ArtistList = ArtistList->next;
+ }
+ //ET_Debug_Print_Artist_Album_List(__FILE__,__LINE__,__FUNCTION__);
+ return FALSE; // ETFile is NUL, or not found in the list
+}
+
+
+
+/**************************
+ * File sorting functions *
+ **************************/
+
+/*
+ * Sort the 'ETFileDisplayedList' following the 'Sorting_Type'
+ * Note : Add also new sorting in 'Browser_List_Sort_Func'
+ */
+void ET_Sort_Displayed_File_List (ET_Sorting_Type Sorting_Type)
+{
+ ETCore->ETFileDisplayedList = ET_Sort_File_List(ETCore->ETFileDisplayedList,Sorting_Type);
+}
+/*
+ * Sort an 'ETFileList'
+ */
+GList *ET_Sort_File_List (GList *ETFileList, ET_Sorting_Type Sorting_Type)
+{
+ // Important to rewind before
+ GList *etfilelist = g_list_first(ETFileList);
+
+ // Save sorting mode (note: needed when called from UI)
+ SORTING_FILE_MODE = Sorting_Type;
+
+ // Sort...
+ switch (Sorting_Type)
+ {
+ case SORTING_UNKNOWN:
+ case SORTING_BY_ASCENDING_FILENAME:
+ etfilelist = g_list_sort(etfilelist,(GCompareFunc)ET_Comp_Func_Sort_File_By_Ascending_Filename);
+ break;
+ case SORTING_BY_DESCENDING_FILENAME:
+ etfilelist = g_list_sort(etfilelist,(GCompareFunc)ET_Comp_Func_Sort_File_By_Descending_Filename);
+ break;
+ case SORTING_BY_ASCENDING_TRACK_NUMBER:
+ etfilelist = g_list_sort(etfilelist,(GCompareFunc)ET_Comp_Func_Sort_File_By_Ascending_Track_Number);
+ break;
+ case SORTING_BY_DESCENDING_TRACK_NUMBER:
+ etfilelist = g_list_sort(etfilelist,(GCompareFunc)ET_Comp_Func_Sort_File_By_Descending_Track_Number);
+ break;
+ case SORTING_BY_ASCENDING_CREATION_DATE:
+ etfilelist = g_list_sort(etfilelist,(GCompareFunc)ET_Comp_Func_Sort_File_By_Ascending_Creation_Date);
+ break;
+ case SORTING_BY_DESCENDING_CREATION_DATE:
+ etfilelist = g_list_sort(etfilelist,(GCompareFunc)ET_Comp_Func_Sort_File_By_Descending_Creation_Date);
+ break;
+ case SORTING_BY_ASCENDING_TITLE:
+ etfilelist = g_list_sort(etfilelist,(GCompareFunc)ET_Comp_Func_Sort_File_By_Ascending_Title);
+ break;
+ case SORTING_BY_DESCENDING_TITLE:
+ etfilelist = g_list_sort(etfilelist,(GCompareFunc)ET_Comp_Func_Sort_File_By_Descending_Title);
+ break;
+ case SORTING_BY_ASCENDING_ARTIST:
+ etfilelist = g_list_sort(etfilelist,(GCompareFunc)ET_Comp_Func_Sort_File_By_Ascending_Artist);
+ break;
+ case SORTING_BY_DESCENDING_ARTIST:
+ etfilelist = g_list_sort(etfilelist,(GCompareFunc)ET_Comp_Func_Sort_File_By_Descending_Artist);
+ break;
+ case SORTING_BY_ASCENDING_ALBUM:
+ etfilelist = g_list_sort(etfilelist,(GCompareFunc)ET_Comp_Func_Sort_File_By_Ascending_Album);
+ break;
+ case SORTING_BY_DESCENDING_ALBUM:
+ etfilelist = g_list_sort(etfilelist,(GCompareFunc)ET_Comp_Func_Sort_File_By_Descending_Album);
+ break;
+ case SORTING_BY_ASCENDING_YEAR:
+ etfilelist = g_list_sort(etfilelist,(GCompareFunc)ET_Comp_Func_Sort_File_By_Ascending_Year);
+ break;
+ case SORTING_BY_DESCENDING_YEAR:
+ etfilelist = g_list_sort(etfilelist,(GCompareFunc)ET_Comp_Func_Sort_File_By_Descending_Year);
+ break;
+ case SORTING_BY_ASCENDING_GENRE:
+ etfilelist = g_list_sort(etfilelist,(GCompareFunc)ET_Comp_Func_Sort_File_By_Ascending_Genre);
+ break;
+ case SORTING_BY_DESCENDING_GENRE:
+ etfilelist = g_list_sort(etfilelist,(GCompareFunc)ET_Comp_Func_Sort_File_By_Descending_Genre);
+ break;
+ case SORTING_BY_ASCENDING_COMMENT:
+ etfilelist = g_list_sort(etfilelist,(GCompareFunc)ET_Comp_Func_Sort_File_By_Ascending_Comment);
+ break;
+ case SORTING_BY_DESCENDING_COMMENT:
+ etfilelist = g_list_sort(etfilelist,(GCompareFunc)ET_Comp_Func_Sort_File_By_Descending_Comment);
+ break;
+ case SORTING_BY_ASCENDING_COMPOSER:
+ etfilelist = g_list_sort(etfilelist,(GCompareFunc)ET_Comp_Func_Sort_File_By_Ascending_Composer);
+ break;
+ case SORTING_BY_DESCENDING_COMPOSER:
+ etfilelist = g_list_sort(etfilelist,(GCompareFunc)ET_Comp_Func_Sort_File_By_Descending_Composer);
+ break;
+ case SORTING_BY_ASCENDING_ORIG_ARTIST:
+ etfilelist = g_list_sort(etfilelist,(GCompareFunc)ET_Comp_Func_Sort_File_By_Ascending_Orig_Artist);
+ break;
+ case SORTING_BY_DESCENDING_ORIG_ARTIST:
+ etfilelist = g_list_sort(etfilelist,(GCompareFunc)ET_Comp_Func_Sort_File_By_Descending_Orig_Artist);
+ break;
+ case SORTING_BY_ASCENDING_COPYRIGHT:
+ etfilelist = g_list_sort(etfilelist,(GCompareFunc)ET_Comp_Func_Sort_File_By_Ascending_Copyright);
+ break;
+ case SORTING_BY_DESCENDING_COPYRIGHT:
+ etfilelist = g_list_sort(etfilelist,(GCompareFunc)ET_Comp_Func_Sort_File_By_Descending_Copyright);
+ break;
+ case SORTING_BY_ASCENDING_URL:
+ etfilelist = g_list_sort(etfilelist,(GCompareFunc)ET_Comp_Func_Sort_File_By_Ascending_Url);
+ break;
+ case SORTING_BY_DESCENDING_URL:
+ etfilelist = g_list_sort(etfilelist,(GCompareFunc)ET_Comp_Func_Sort_File_By_Descending_Url);
+ break;
+ case SORTING_BY_ASCENDING_ENCODED_BY:
+ etfilelist = g_list_sort(etfilelist,(GCompareFunc)ET_Comp_Func_Sort_File_By_Ascending_Encoded_By);
+ break;
+ case SORTING_BY_DESCENDING_ENCODED_BY:
+ etfilelist = g_list_sort(etfilelist,(GCompareFunc)ET_Comp_Func_Sort_File_By_Descending_Encoded_By);
+ break;
+ case SORTING_BY_ASCENDING_FILE_TYPE:
+ etfilelist = g_list_sort(etfilelist,(GCompareFunc)ET_Comp_Func_Sort_File_By_Ascending_File_Type);
+ break;
+ case SORTING_BY_DESCENDING_FILE_TYPE:
+ etfilelist = g_list_sort(etfilelist,(GCompareFunc)ET_Comp_Func_Sort_File_By_Descending_File_Type);
+ break;
+ case SORTING_BY_ASCENDING_FILE_SIZE:
+ etfilelist = g_list_sort(etfilelist,(GCompareFunc)ET_Comp_Func_Sort_File_By_Ascending_File_Size);
+ break;
+ case SORTING_BY_DESCENDING_FILE_SIZE:
+ etfilelist = g_list_sort(etfilelist,(GCompareFunc)ET_Comp_Func_Sort_File_By_Descending_File_Size);
+ break;
+ case SORTING_BY_ASCENDING_FILE_DURATION:
+ etfilelist = g_list_sort(etfilelist,(GCompareFunc)ET_Comp_Func_Sort_File_By_Ascending_File_Duration);
+ break;
+ case SORTING_BY_DESCENDING_FILE_DURATION:
+ etfilelist = g_list_sort(etfilelist,(GCompareFunc)ET_Comp_Func_Sort_File_By_Descending_File_Duration);
+ break;
+ case SORTING_BY_ASCENDING_FILE_BITRATE:
+ etfilelist = g_list_sort(etfilelist,(GCompareFunc)ET_Comp_Func_Sort_File_By_Ascending_File_Bitrate);
+ break;
+ case SORTING_BY_DESCENDING_FILE_BITRATE:
+ etfilelist = g_list_sort(etfilelist,(GCompareFunc)ET_Comp_Func_Sort_File_By_Descending_File_Bitrate);
+ break;
+ case SORTING_BY_ASCENDING_FILE_SAMPLERATE:
+ etfilelist = g_list_sort(etfilelist,(GCompareFunc)ET_Comp_Func_Sort_File_By_Ascending_File_Samplerate);
+ break;
+ case SORTING_BY_DESCENDING_FILE_SAMPLERATE:
+ etfilelist = g_list_sort(etfilelist,(GCompareFunc)ET_Comp_Func_Sort_File_By_Descending_File_Samplerate);
+ break;
+ }
+ //ETFileList = g_list_first(etfilelist);
+ return g_list_first(etfilelist);
+}
+
+
+/*
+ * Sort the list of files following the 'Sorting_Type' value. The new sorting is displayed in the UI.
+ */
+void ET_Sort_Displayed_File_List_And_Update_UI (ET_Sorting_Type Sorting_Type)
+{
+ if (!ETCore->ETFileList) return;
+
+ ET_Save_File_Data_From_UI(ETCore->ETFileDisplayed);
+
+ /* Sort the list */
+ ET_Sort_Displayed_File_List(Sorting_Type);
+
+ /* To number the ETFile in the list */
+ ET_Displayed_File_List_Number();
+
+ /* Reload files in browser list */
+ ET_Displayed_File_List_By_Etfile(ETCore->ETFileDisplayed); // Just to update 'ETFileDisplayedList'
+ Browser_List_Select_File_By_Etfile(ETCore->ETFileDisplayed,TRUE);
+ ET_Display_File_Data_To_UI(ETCore->ETFileDisplayed);
+
+ Browser_List_Refresh_Sort();
+ Update_Command_Buttons_Sensivity();
+}
+
+
+/*
+ * Comparison function for sorting by ascending file name.
+ */
+gint ET_Comp_Func_Sort_File_By_Ascending_Filename (ET_File *ETFile1, ET_File *ETFile2)
+{
+ gchar *file1_ck = ((File_Name *)((GList *)ETFile1->FileNameCur)->data)->value_ck;
+ gchar *file2_ck = ((File_Name *)((GList *)ETFile2->FileNameCur)->data)->value_ck;
+ // !!!! : Must be the same rules as "Cddb_Track_List_Sort_Func" to be
+ // able to sort in the same order files in cddb and in the file list.
+ return SORTING_FILE_CASE_SENSITIVE ? strcmp(file1_ck,file2_ck) : strcasecmp(file1_ck,file2_ck);
+}
+
+/*
+ * Comparison function for sorting by descending file name.
+ */
+gint ET_Comp_Func_Sort_File_By_Descending_Filename (ET_File *ETFile1, ET_File *ETFile2)
+{
+ return ET_Comp_Func_Sort_File_By_Ascending_Filename(ETFile2,ETFile1);
+}
+
+
+/*
+ * Comparison function for sorting by ascending track number.
+ */
+gint ET_Comp_Func_Sort_File_By_Ascending_Track_Number (ET_File *ETFile1, ET_File *ETFile2)
+{
+ gint track1, track2;
+
+ if ( !ETFile1->FileTag->data || !((File_Tag *)ETFile1->FileTag->data)->track )
+ track1 = 0;
+ else
+ track1 = atoi( ((File_Tag *)ETFile1->FileTag->data)->track );
+
+ if ( !ETFile2->FileTag->data || !((File_Tag *)ETFile2->FileTag->data)->track )
+ track2 = 0;
+ else
+ track2 = atoi( ((File_Tag *)ETFile2->FileTag->data)->track );
+
+ // Second criterion
+ if (track1 == track2)
+ return ET_Comp_Func_Sort_File_By_Ascending_Filename(ETFile1,ETFile2);
+
+ // First criterion
+ return (track1 - track2);
+}
+
+/*
+ * Comparison function for sorting by descending track number.
+ */
+gint ET_Comp_Func_Sort_File_By_Descending_Track_Number (ET_File *ETFile1, ET_File *ETFile2)
+{
+ return ET_Comp_Func_Sort_File_By_Ascending_Track_Number(ETFile2,ETFile1);
+}
+
+
+/*
+ * Comparison function for sorting by ascending creation date.
+ */
+gint ET_Comp_Func_Sort_File_By_Ascending_Creation_Date (ET_File *ETFile1, ET_File *ETFile2)
+{
+ struct stat statbuf1;
+ struct stat statbuf2;
+ gchar *filename1 = ((File_Name *)ETFile1->FileNameCur->data)->value;
+ gchar *filename2 = ((File_Name *)ETFile2->FileNameCur->data)->value;
+
+ lstat(filename1, &statbuf1);
+ lstat(filename2, &statbuf2);
+
+ // Second criterion
+ if (statbuf1.st_ctime == statbuf2.st_ctime)
+ return ET_Comp_Func_Sort_File_By_Ascending_Filename(ETFile1,ETFile2);
+
+ // First criterion
+ return (statbuf1.st_ctime - statbuf2.st_ctime); // Creation date
+ //return (statbuf1.st_mtime - statbuf2.st_mtime); // Modification date
+}
+
+/*
+ * Comparison function for sorting by descending creation date.
+ */
+gint ET_Comp_Func_Sort_File_By_Descending_Creation_Date (ET_File *ETFile1, ET_File *ETFile2)
+{
+ return ET_Comp_Func_Sort_File_By_Ascending_Creation_Date(ETFile2,ETFile1);
+}
+
+
+/*
+ * Comparison function for sorting by ascending title.
+ */
+gint ET_Comp_Func_Sort_File_By_Ascending_Title (ET_File *ETFile1, ET_File *ETFile2)
+{
+ // Compare pointers just in case they are the same (e.g. both are NULL)
+ if ((ETFile1->FileTag->data == ETFile2->FileTag->data)
+ || (((File_Tag *)ETFile1->FileTag->data)->title == ((File_Tag *)ETFile2->FileTag->data)->title))
+ return 0;
+
+ if ( !ETFile1->FileTag->data || !((File_Tag *)ETFile1->FileTag->data)->title )
+ return -1;
+ if ( !ETFile2->FileTag->data || !((File_Tag *)ETFile2->FileTag->data)->title )
+ return 1;
+
+ if (SORTING_FILE_CASE_SENSITIVE)
+ {
+ if ( strcmp(((File_Tag *)ETFile1->FileTag->data)->title,((File_Tag *)ETFile2->FileTag->data)->title) == 0 )
+ // Second criterion
+ return ET_Comp_Func_Sort_File_By_Ascending_Filename(ETFile1,ETFile2);
+ else
+ // First criterion
+ return strcmp(((File_Tag *)ETFile1->FileTag->data)->title,((File_Tag *)ETFile2->FileTag->data)->title);
+ }else
+ {
+ if ( strcasecmp(((File_Tag *)ETFile1->FileTag->data)->title,((File_Tag *)ETFile2->FileTag->data)->title) == 0 )
+ // Second criterion
+ return ET_Comp_Func_Sort_File_By_Ascending_Filename(ETFile1,ETFile2);
+ else
+ // First criterion
+ return strcasecmp(((File_Tag *)ETFile1->FileTag->data)->title,((File_Tag *)ETFile2->FileTag->data)->title);
+ }
+}
+
+/*
+ * Comparison function for sorting by descending title.
+ */
+gint ET_Comp_Func_Sort_File_By_Descending_Title (ET_File *ETFile1, ET_File *ETFile2)
+{
+ return ET_Comp_Func_Sort_File_By_Ascending_Title(ETFile2,ETFile1);
+}
+
+
+/*
+ * Comparison function for sorting by ascending artist.
+ */
+gint ET_Comp_Func_Sort_File_By_Ascending_Artist (ET_File *ETFile1, ET_File *ETFile2)
+{
+ // Compare pointers just in case they are the same (e.g. both are NULL)
+ if ((ETFile1->FileTag->data == ETFile2->FileTag->data)
+ || (((File_Tag *)ETFile1->FileTag->data)->artist == ((File_Tag *)ETFile2->FileTag->data)->artist))
+ return 0;
+
+ if ( !ETFile1->FileTag->data || !((File_Tag *)ETFile1->FileTag->data)->artist )
+ return -1;
+ if ( !ETFile2->FileTag->data || !((File_Tag *)ETFile2->FileTag->data)->artist )
+ return 1;
+
+ if (SORTING_FILE_CASE_SENSITIVE)
+ {
+ if ( strcmp(((File_Tag *)ETFile1->FileTag->data)->artist,((File_Tag *)ETFile2->FileTag->data)->artist) == 0 )
+ // Second criterion
+ return ET_Comp_Func_Sort_File_By_Ascending_Filename(ETFile1,ETFile2);
+ else
+ // First criterion
+ return strcmp(((File_Tag *)ETFile1->FileTag->data)->artist,((File_Tag *)ETFile2->FileTag->data)->artist);
+ }else
+ {
+ if ( strcasecmp(((File_Tag *)ETFile1->FileTag->data)->artist,((File_Tag *)ETFile2->FileTag->data)->artist) == 0 )
+ // Second criterion
+ return ET_Comp_Func_Sort_File_By_Ascending_Filename(ETFile1,ETFile2);
+ else
+ // First criterion
+ return strcasecmp(((File_Tag *)ETFile1->FileTag->data)->artist,((File_Tag *)ETFile2->FileTag->data)->artist);
+ }
+}
+
+/*
+ * Comparison function for sorting by descending artist.
+ */
+gint ET_Comp_Func_Sort_File_By_Descending_Artist (ET_File *ETFile1, ET_File *ETFile2)
+{
+ return ET_Comp_Func_Sort_File_By_Ascending_Album(ETFile2,ETFile1);
+}
+
+
+/*
+ * Comparison function for sorting by ascending album.
+ */
+gint ET_Comp_Func_Sort_File_By_Ascending_Album (ET_File *ETFile1, ET_File *ETFile2)
+{
+ // Compare pointers just in case they are the same (e.g. both are NULL)
+ if ((ETFile1->FileTag->data == ETFile2->FileTag->data)
+ || (((File_Tag *)ETFile1->FileTag->data)->album == ((File_Tag *)ETFile2->FileTag->data)->album))
+ return 0;
+
+ if ( !ETFile1->FileTag->data || !((File_Tag *)ETFile1->FileTag->data)->album )
+ return -1;
+ if ( !ETFile2->FileTag->data || !((File_Tag *)ETFile2->FileTag->data)->album )
+ return 1;
+
+ if (SORTING_FILE_CASE_SENSITIVE)
+ {
+ if ( strcmp(((File_Tag *)ETFile1->FileTag->data)->album,((File_Tag *)ETFile2->FileTag->data)->album) == 0 )
+ // Second criterion
+ return ET_Comp_Func_Sort_File_By_Ascending_Filename(ETFile1,ETFile2);
+ else
+ // First criterion
+ return strcmp(((File_Tag *)ETFile1->FileTag->data)->album,((File_Tag *)ETFile2->FileTag->data)->album);
+ }else
+ {
+ if ( strcasecmp(((File_Tag *)ETFile1->FileTag->data)->album,((File_Tag *)ETFile2->FileTag->data)->album) == 0 )
+ // Second criterion
+ return ET_Comp_Func_Sort_File_By_Ascending_Filename(ETFile1,ETFile2);
+ else
+ // First criterion
+ return strcasecmp(((File_Tag *)ETFile1->FileTag->data)->album,((File_Tag *)ETFile2->FileTag->data)->album);
+ }
+}
+
+/*
+ * Comparison function for sorting by descending album.
+ */
+gint ET_Comp_Func_Sort_File_By_Descending_Album (ET_File *ETFile1, ET_File *ETFile2)
+{
+ return ET_Comp_Func_Sort_File_By_Ascending_Album(ETFile2,ETFile1);
+}
+
+
+/*
+ * Comparison function for sorting by ascending year.
+ */
+gint ET_Comp_Func_Sort_File_By_Ascending_Year (ET_File *ETFile1, ET_File *ETFile2)
+{
+ gint year1, year2;
+
+ if ( !ETFile1->FileTag->data || !((File_Tag *)ETFile1->FileTag->data)->year )
+ year1 = 0;
+ else
+ year1 = atoi( ((File_Tag *)ETFile1->FileTag->data)->year );
+
+ if ( !ETFile2->FileTag->data || !((File_Tag *)ETFile2->FileTag->data)->year )
+ year2 = 0;
+ else
+ year2 = atoi( ((File_Tag *)ETFile2->FileTag->data)->year );
+
+ // Second criterion
+ if (year1 == year2)
+ return ET_Comp_Func_Sort_File_By_Ascending_Filename(ETFile1,ETFile2);
+
+ // First criterion
+ return (year1 - year2);
+}
+
+/*
+ * Comparison function for sorting by descending year.
+ */
+gint ET_Comp_Func_Sort_File_By_Descending_Year (ET_File *ETFile1, ET_File *ETFile2)
+{
+ return ET_Comp_Func_Sort_File_By_Ascending_Year(ETFile2,ETFile1);
+}
+
+
+/*
+ * Comparison function for sorting by ascending genre.
+ */
+gint ET_Comp_Func_Sort_File_By_Ascending_Genre (ET_File *ETFile1, ET_File *ETFile2)
+{
+ // Compare pointers just in case they are the same (e.g. both are NULL)
+ if ((ETFile1->FileTag->data == ETFile2->FileTag->data)
+ || (((File_Tag *)ETFile1->FileTag->data)->genre == ((File_Tag *)ETFile2->FileTag->data)->genre))
+ return 0;
+
+ if ( !ETFile1->FileTag->data || !((File_Tag *)ETFile1->FileTag->data)->genre ) return -1;
+ if ( !ETFile2->FileTag->data || !((File_Tag *)ETFile2->FileTag->data)->genre ) return 1;
+
+ if (SORTING_FILE_CASE_SENSITIVE)
+ {
+ if ( strcmp(((File_Tag *)ETFile1->FileTag->data)->genre,((File_Tag *)ETFile2->FileTag->data)->genre) == 0 )
+ // Second criterion
+ return ET_Comp_Func_Sort_File_By_Ascending_Filename(ETFile1,ETFile2);
+ else
+ // First criterion
+ return strcmp(((File_Tag *)ETFile1->FileTag->data)->genre,((File_Tag *)ETFile2->FileTag->data)->genre);
+ }else
+ {
+ if ( strcasecmp(((File_Tag *)ETFile1->FileTag->data)->genre,((File_Tag *)ETFile2->FileTag->data)->genre) == 0 )
+ // Second criterion
+ return ET_Comp_Func_Sort_File_By_Ascending_Filename(ETFile1,ETFile2);
+ else
+ // First criterion
+ return strcasecmp(((File_Tag *)ETFile1->FileTag->data)->genre,((File_Tag *)ETFile2->FileTag->data)->genre);
+ }
+}
+
+/*
+ * Comparison function for sorting by descending genre.
+ */
+gint ET_Comp_Func_Sort_File_By_Descending_Genre (ET_File *ETFile1, ET_File *ETFile2)
+{
+ return ET_Comp_Func_Sort_File_By_Ascending_Genre(ETFile2,ETFile1);
+}
+
+
+/*
+ * Comparison function for sorting by ascending comment.
+ */
+gint ET_Comp_Func_Sort_File_By_Ascending_Comment (ET_File *ETFile1, ET_File *ETFile2)
+{
+ // Compare pointers just in case they are the same (e.g. both are NULL)
+ if ((ETFile1->FileTag->data == ETFile2->FileTag->data)
+ || (((File_Tag *)ETFile1->FileTag->data)->comment == ((File_Tag *)ETFile2->FileTag->data)->comment))
+ return 0;
+
+ if ( !ETFile1->FileTag->data || !((File_Tag *)ETFile1->FileTag->data)->comment )
+ return -1;
+ if ( !ETFile2->FileTag->data || !((File_Tag *)ETFile2->FileTag->data)->comment )
+ return 1;
+
+ if (SORTING_FILE_CASE_SENSITIVE)
+ {
+ if ( strcmp(((File_Tag *)ETFile1->FileTag->data)->comment,((File_Tag *)ETFile2->FileTag->data)->comment) == 0 )
+ // Second criterion
+ return ET_Comp_Func_Sort_File_By_Ascending_Filename(ETFile1,ETFile2);
+ else
+ // First criterion
+ return strcmp(((File_Tag *)ETFile1->FileTag->data)->comment,((File_Tag *)ETFile2->FileTag->data)->comment);
+ }else
+ {
+ if ( strcasecmp(((File_Tag *)ETFile1->FileTag->data)->comment,((File_Tag *)ETFile2->FileTag->data)->comment) == 0 )
+ // Second criterion
+ return ET_Comp_Func_Sort_File_By_Ascending_Filename(ETFile1,ETFile2);
+ else
+ // First criterion
+ return strcasecmp(((File_Tag *)ETFile1->FileTag->data)->comment,((File_Tag *)ETFile2->FileTag->data)->comment);
+ }
+}
+
+/*
+ * Comparison function for sorting by descending comment.
+ */
+gint ET_Comp_Func_Sort_File_By_Descending_Comment (ET_File *ETFile1, ET_File *ETFile2)
+{
+ return ET_Comp_Func_Sort_File_By_Ascending_Comment(ETFile2,ETFile1);
+}
+
+
+/*
+ * Comparison function for sorting by ascending composer.
+ */
+gint ET_Comp_Func_Sort_File_By_Ascending_Composer (ET_File *ETFile1, ET_File *ETFile2)
+{
+ // Compare pointers just in case they are the same (e.g. both are NULL)
+ if ((ETFile1->FileTag->data == ETFile2->FileTag->data)
+ || (((File_Tag *)ETFile1->FileTag->data)->composer == ((File_Tag *)ETFile2->FileTag->data)->composer))
+ return 0;
+
+ if ( !ETFile1->FileTag->data || !((File_Tag *)ETFile1->FileTag->data)->composer )
+ return -1;
+ if ( !ETFile2->FileTag->data || !((File_Tag *)ETFile2->FileTag->data)->composer )
+ return 1;
+
+ if (SORTING_FILE_CASE_SENSITIVE)
+ {
+ if ( strcmp(((File_Tag *)ETFile1->FileTag->data)->composer,((File_Tag *)ETFile2->FileTag->data)->composer) == 0 )
+ // Second criterion
+ return ET_Comp_Func_Sort_File_By_Ascending_Filename(ETFile1,ETFile2);
+ else
+ // First criterion
+ return strcmp(((File_Tag *)ETFile1->FileTag->data)->composer,((File_Tag *)ETFile2->FileTag->data)->composer);
+ }else
+ {
+ if ( strcasecmp(((File_Tag *)ETFile1->FileTag->data)->composer,((File_Tag *)ETFile2->FileTag->data)->composer) == 0 )
+ // Second criterion
+ return ET_Comp_Func_Sort_File_By_Ascending_Filename(ETFile1,ETFile2);
+ else
+ // First criterion
+ return strcasecmp(((File_Tag *)ETFile1->FileTag->data)->composer,((File_Tag *)ETFile2->FileTag->data)->composer);
+ }
+}
+
+/*
+ * Comparison function for sorting by descending composer.
+ */
+gint ET_Comp_Func_Sort_File_By_Descending_Composer (ET_File *ETFile1, ET_File *ETFile2)
+{
+ return ET_Comp_Func_Sort_File_By_Ascending_Composer(ETFile2,ETFile1);
+}
+
+
+/*
+ * Comparison function for sorting by ascending original artist.
+ */
+gint ET_Comp_Func_Sort_File_By_Ascending_Orig_Artist (ET_File *ETFile1, ET_File *ETFile2)
+{
+ // Compare pointers just in case they are the same (e.g. both are NULL)
+ if ((ETFile1->FileTag->data == ETFile2->FileTag->data)
+ || (((File_Tag *)ETFile1->FileTag->data)->orig_artist == ((File_Tag *)ETFile2->FileTag->data)->orig_artist))
+ return 0;
+
+ if ( !ETFile1->FileTag->data || !((File_Tag *)ETFile1->FileTag->data)->orig_artist )
+ return -1;
+ if ( !ETFile2->FileTag->data || !((File_Tag *)ETFile2->FileTag->data)->orig_artist )
+ return 1;
+
+ if (SORTING_FILE_CASE_SENSITIVE)
+ {
+ if ( strcmp(((File_Tag *)ETFile1->FileTag->data)->orig_artist,((File_Tag *)ETFile2->FileTag->data)->orig_artist) == 0 )
+ // Second criterion
+ return ET_Comp_Func_Sort_File_By_Ascending_Filename(ETFile1,ETFile2);
+ else
+ // First criterion
+ return strcmp(((File_Tag *)ETFile1->FileTag->data)->orig_artist,((File_Tag *)ETFile2->FileTag->data)->orig_artist);
+ }else
+ {
+ if ( strcasecmp(((File_Tag *)ETFile1->FileTag->data)->orig_artist,((File_Tag *)ETFile2->FileTag->data)->orig_artist) == 0 )
+ // Second criterion
+ return ET_Comp_Func_Sort_File_By_Ascending_Filename(ETFile1,ETFile2);
+ else
+ // First criterion
+ return strcasecmp(((File_Tag *)ETFile1->FileTag->data)->orig_artist,((File_Tag *)ETFile2->FileTag->data)->orig_artist);
+ }
+}
+
+/*
+ * Comparison function for sorting by descending original artist.
+ */
+gint ET_Comp_Func_Sort_File_By_Descending_Orig_Artist (ET_File *ETFile1, ET_File *ETFile2)
+{
+ return ET_Comp_Func_Sort_File_By_Ascending_Orig_Artist(ETFile2,ETFile1);
+}
+
+
+/*
+ * Comparison function for sorting by ascending copyright.
+ */
+gint ET_Comp_Func_Sort_File_By_Ascending_Copyright (ET_File *ETFile1, ET_File *ETFile2)
+{
+ // Compare pointers just in case they are the same (e.g. both are NULL)
+ if ((ETFile1->FileTag->data == ETFile2->FileTag->data)
+ || (((File_Tag *)ETFile1->FileTag->data)->copyright == ((File_Tag *)ETFile2->FileTag->data)->copyright))
+ return 0;
+
+ if ( !ETFile1->FileTag->data || !((File_Tag *)ETFile1->FileTag->data)->copyright )
+ return -1;
+ if ( !ETFile2->FileTag->data || !((File_Tag *)ETFile2->FileTag->data)->copyright )
+ return 1;
+
+ if (SORTING_FILE_CASE_SENSITIVE)
+ {
+ if ( strcmp(((File_Tag *)ETFile1->FileTag->data)->copyright,((File_Tag *)ETFile2->FileTag->data)->copyright) == 0 )
+ // Second criterion
+ return ET_Comp_Func_Sort_File_By_Ascending_Filename(ETFile1,ETFile2);
+ else
+ // First criterion
+ return strcmp(((File_Tag *)ETFile1->FileTag->data)->copyright,((File_Tag *)ETFile2->FileTag->data)->copyright);
+ }else
+ {
+ if ( strcasecmp(((File_Tag *)ETFile1->FileTag->data)->copyright,((File_Tag *)ETFile2->FileTag->data)->copyright) == 0 )
+ // Second criterion
+ return ET_Comp_Func_Sort_File_By_Ascending_Filename(ETFile1,ETFile2);
+ else
+ // First criterion
+ return strcasecmp(((File_Tag *)ETFile1->FileTag->data)->copyright,((File_Tag *)ETFile2->FileTag->data)->copyright);
+ }
+}
+
+/*
+ * Comparison function for sorting by descending copyright.
+ */
+gint ET_Comp_Func_Sort_File_By_Descending_Copyright (ET_File *ETFile1, ET_File *ETFile2)
+{
+ return ET_Comp_Func_Sort_File_By_Ascending_Copyright(ETFile2,ETFile1);
+}
+
+
+/*
+ * Comparison function for sorting by ascending URL.
+ */
+gint ET_Comp_Func_Sort_File_By_Ascending_Url (ET_File *ETFile1, ET_File *ETFile2)
+{
+ // Compare pointers just in case they are the same (e.g. both are NULL)
+ if ((ETFile1->FileTag->data == ETFile2->FileTag->data)
+ || (((File_Tag *)ETFile1->FileTag->data)->url == ((File_Tag *)ETFile2->FileTag->data)->url))
+ return 0;
+
+ if ( !ETFile1->FileTag->data || !((File_Tag *)ETFile1->FileTag->data)->url )
+ return -1;
+ if ( !ETFile2->FileTag->data || !((File_Tag *)ETFile2->FileTag->data)->url )
+ return 1;
+
+ if (SORTING_FILE_CASE_SENSITIVE)
+ {
+ if ( strcmp(((File_Tag *)ETFile1->FileTag->data)->url,((File_Tag *)ETFile2->FileTag->data)->url) == 0 )
+ // Second criterion
+ return ET_Comp_Func_Sort_File_By_Ascending_Filename(ETFile1,ETFile2);
+ else
+ // First criterion
+ return strcmp(((File_Tag *)ETFile1->FileTag->data)->url,((File_Tag *)ETFile2->FileTag->data)->url);
+ }else
+ {
+ if ( strcasecmp(((File_Tag *)ETFile1->FileTag->data)->url,((File_Tag *)ETFile2->FileTag->data)->url) == 0 )
+ // Second criterion
+ return ET_Comp_Func_Sort_File_By_Ascending_Filename(ETFile1,ETFile2);
+ else
+ // First criterion
+ return strcasecmp(((File_Tag *)ETFile1->FileTag->data)->url,((File_Tag *)ETFile2->FileTag->data)->url);
+ }
+}
+
+/*
+ * Comparison function for sorting by descending URL.
+ */
+gint ET_Comp_Func_Sort_File_By_Descending_Url (ET_File *ETFile1, ET_File *ETFile2)
+{
+ return ET_Comp_Func_Sort_File_By_Ascending_Url(ETFile2,ETFile1);
+}
+
+
+/*
+ * Comparison function for sorting by ascending encoded by.
+ */
+gint ET_Comp_Func_Sort_File_By_Ascending_Encoded_By (ET_File *ETFile1, ET_File *ETFile2)
+{
+ // Compare pointers just in case they are the same (e.g. both are NULL)
+ if ((ETFile1->FileTag->data == ETFile2->FileTag->data)
+ || (((File_Tag *)ETFile1->FileTag->data)->encoded_by == ((File_Tag *)ETFile2->FileTag->data)->encoded_by))
+ return 0;
+
+ if ( !ETFile1->FileTag->data || !((File_Tag *)ETFile1->FileTag->data)->encoded_by )
+ return -1;
+ if ( !ETFile2->FileTag->data || !((File_Tag *)ETFile2->FileTag->data)->encoded_by )
+ return 1;
+
+ if (SORTING_FILE_CASE_SENSITIVE)
+ {
+ if ( strcmp(((File_Tag *)ETFile1->FileTag->data)->encoded_by,((File_Tag *)ETFile2->FileTag->data)->encoded_by) == 0 )
+ // Second criterion
+ return ET_Comp_Func_Sort_File_By_Ascending_Filename(ETFile1,ETFile2);
+ else
+ // First criterion
+ return strcmp(((File_Tag *)ETFile1->FileTag->data)->encoded_by,((File_Tag *)ETFile2->FileTag->data)->encoded_by);
+ }else
+ {
+ if ( strcasecmp(((File_Tag *)ETFile1->FileTag->data)->encoded_by,((File_Tag *)ETFile2->FileTag->data)->encoded_by) == 0 )
+ // Second criterion
+ return ET_Comp_Func_Sort_File_By_Ascending_Filename(ETFile1,ETFile2);
+ else
+ // First criterion
+ return strcasecmp(((File_Tag *)ETFile1->FileTag->data)->encoded_by,((File_Tag *)ETFile2->FileTag->data)->encoded_by);
+ }
+}
+
+/*
+ * Comparison function for sorting by descendingencoded by.
+ */
+gint ET_Comp_Func_Sort_File_By_Descending_Encoded_By (ET_File *ETFile1, ET_File *ETFile2)
+{
+ return ET_Comp_Func_Sort_File_By_Ascending_Encoded_By(ETFile2,ETFile1);
+}
+
+
+/*
+ * Comparison function for sorting by ascending file type (mp3, ogg, ...).
+ */
+gint ET_Comp_Func_Sort_File_By_Ascending_File_Type (ET_File *ETFile1, ET_File *ETFile2)
+{
+ if ( !ETFile1->ETFileDescription ) return -1;
+ if ( !ETFile2->ETFileDescription ) return 1;
+
+ // Second criterion
+ if (ETFile1->ETFileDescription->FileType == ETFile2->ETFileDescription->FileType)
+ return ET_Comp_Func_Sort_File_By_Ascending_Filename(ETFile1,ETFile2);
+
+ // First criterion
+ return (ETFile1->ETFileDescription->FileType - ETFile2->ETFileDescription->FileType);
+}
+
+/*
+ * Comparison function for sorting by descending file type (mp3, ogg, ...).
+ */
+gint ET_Comp_Func_Sort_File_By_Descending_File_Type (ET_File *ETFile1, ET_File *ETFile2)
+{
+ return ET_Comp_Func_Sort_File_By_Ascending_File_Type(ETFile2,ETFile1);
+}
+
+
+/*
+ * Comparison function for sorting by ascending file size.
+ */
+gint ET_Comp_Func_Sort_File_By_Ascending_File_Size (ET_File *ETFile1, ET_File *ETFile2)
+{
+ if ( !ETFile1->ETFileInfo ) return -1;
+ if ( !ETFile2->ETFileInfo ) return 1;
+
+ // Second criterion
+ if (ETFile1->ETFileInfo->size == ETFile2->ETFileInfo->size)
+ return ET_Comp_Func_Sort_File_By_Ascending_Filename(ETFile1,ETFile2);
+
+ // First criterion
+ return (ETFile1->ETFileInfo->size - ETFile2->ETFileInfo->size);
+}
+
+/*
+ * Comparison function for sorting by descending file size.
+ */
+gint ET_Comp_Func_Sort_File_By_Descending_File_Size (ET_File *ETFile1, ET_File *ETFile2)
+{
+ return ET_Comp_Func_Sort_File_By_Ascending_File_Size(ETFile2,ETFile1);
+}
+
+
+/*
+ * Comparison function for sorting by ascending file duration.
+ */
+gint ET_Comp_Func_Sort_File_By_Ascending_File_Duration (ET_File *ETFile1, ET_File *ETFile2)
+{
+ if ( !ETFile1->ETFileInfo ) return -1;
+ if ( !ETFile2->ETFileInfo ) return 1;
+
+ // Second criterion
+ if (ETFile1->ETFileInfo->duration == ETFile2->ETFileInfo->duration)
+ return ET_Comp_Func_Sort_File_By_Ascending_Filename(ETFile1,ETFile2);
+
+ // First criterion
+ return (ETFile1->ETFileInfo->duration - ETFile2->ETFileInfo->duration);
+}
+
+/*
+ * Comparison function for sorting by descending file duration.
+ */
+gint ET_Comp_Func_Sort_File_By_Descending_File_Duration (ET_File *ETFile1, ET_File *ETFile2)
+{
+ return ET_Comp_Func_Sort_File_By_Ascending_File_Duration(ETFile2,ETFile1);
+}
+
+
+/*
+ * Comparison function for sorting by ascending file bitrate.
+ */
+gint ET_Comp_Func_Sort_File_By_Ascending_File_Bitrate (ET_File *ETFile1, ET_File *ETFile2)
+{
+ if ( !ETFile1->ETFileInfo ) return -1;
+ if ( !ETFile2->ETFileInfo ) return 1;
+
+ // Second criterion
+ if (ETFile1->ETFileInfo->bitrate == ETFile2->ETFileInfo->bitrate)
+ return ET_Comp_Func_Sort_File_By_Ascending_Filename(ETFile1,ETFile2);
+
+ // First criterion
+ return (ETFile1->ETFileInfo->bitrate - ETFile2->ETFileInfo->bitrate);
+}
+
+/*
+ * Comparison function for sorting by descending file bitrate.
+ */
+gint ET_Comp_Func_Sort_File_By_Descending_File_Bitrate (ET_File *ETFile1, ET_File *ETFile2)
+{
+ return ET_Comp_Func_Sort_File_By_Ascending_File_Bitrate(ETFile2,ETFile1);
+}
+
+
+/*
+ * Comparison function for sorting by ascending file samplerate.
+ */
+gint ET_Comp_Func_Sort_File_By_Ascending_File_Samplerate (ET_File *ETFile1, ET_File *ETFile2)
+{
+ if ( !ETFile1->ETFileInfo ) return -1;
+ if ( !ETFile2->ETFileInfo ) return 1;
+
+ // Second criterion
+ if (ETFile1->ETFileInfo->samplerate == ETFile2->ETFileInfo->samplerate)
+ return ET_Comp_Func_Sort_File_By_Ascending_Filename(ETFile1,ETFile2);
+
+ // First criterion
+ return (ETFile1->ETFileInfo->samplerate - ETFile2->ETFileInfo->samplerate);
+}
+
+/*
+ * Comparison function for sorting by descending file samplerate.
+ */
+gint ET_Comp_Func_Sort_File_By_Descending_File_Samplerate (ET_File *ETFile1, ET_File *ETFile2)
+{
+ return ET_Comp_Func_Sort_File_By_Ascending_File_Samplerate(ETFile2,ETFile1);
+}
+
+
+/*
+ * Comparison function for sorting by ascending IndexKey (used mainly to have the ETFileSelectionList already sorted.)
+ */
+gint ET_Comp_Func_Sort_File_By_Ascending_Index_Key (ET_File *ETFile1, ET_File *ETFile2)
+{
+ return (ETFile1->IndexKey - ETFile2->IndexKey);
+}
+
+
+/*
+ * Comparison function for sorting by ascending artist in the ArtistAlbumList.
+ */
+gint ET_Comp_Func_Sort_Artist_Item_By_Ascending_Artist (GList *AlbumList1, GList *AlbumList2)
+{
+ GList *etfilelist1 = NULL, *etfilelist2 = NULL;
+ ET_File *etfile1 = NULL, *etfile2 = NULL;
+ gchar *etfile1_artist, *etfile2_artist;
+
+ if (!AlbumList1
+ || !(etfilelist1 = (GList *)AlbumList1->data)
+ || !(etfile1 = (ET_File *)etfilelist1->data)
+ || !(etfile1_artist = ((File_Tag *)etfile1->FileTag->data)->artist) )
+ return -1;
+
+ if (!AlbumList2
+ || !(etfilelist2 = (GList *)AlbumList2->data)
+ || !(etfile2 = (ET_File *)etfilelist2->data)
+ || !(etfile2_artist = ((File_Tag *)etfile2->FileTag->data)->artist) )
+ return 1;
+
+ //if (SORTING_FILE_CASE_SENSITIVE)
+ // return strcmp(etfile1_artist,etfile2_artist);
+ //else
+ return strcasecmp(etfile1_artist,etfile2_artist);
+}
+
+/*
+ * Comparison function for sorting by ascending album in the ArtistAlbumList.
+ */
+gint ET_Comp_Func_Sort_Album_Item_By_Ascending_Album (GList *etfilelist1, GList *etfilelist2)
+{
+ ET_File *etfile1, *etfile2;
+ gchar *etfile1_album, *etfile2_album;
+
+ if (!etfilelist1
+ || !(etfile1 = (ET_File *)etfilelist1->data)
+ || !(etfile1_album = ((File_Tag *)etfile1->FileTag->data)->album) )
+ return -1;
+
+ if (!etfilelist2
+ || !(etfile2 = (ET_File *)etfilelist2->data)
+ || !(etfile2_album = ((File_Tag *)etfile2->FileTag->data)->album) )
+ return 1;
+
+ //if (SORTING_FILE_CASE_SENSITIVE)
+ // return strcmp(etfile1_album,etfile2_album);
+ //else
+ return strcasecmp(etfile1_album,etfile2_album);
+}
+
+/*
+ * Comparison function for sorting etfile in the ArtistAlbumList.
+ * FIX ME : should use the default sorting!
+ */
+gint ET_Comp_Func_Sort_Etfile_Item_By_Ascending_Filename (ET_File *ETFile1, ET_File *ETFile2)
+{
+
+ if (!ETFile1) return -1;
+ if (!ETFile2) return 1;
+
+ return ET_Comp_Func_Sort_File_By_Ascending_Filename(ETFile1,ETFile2);
+}
+
+
+
+
+/***************************
+ * List handling functions *
+ ***************************/
+
+/*
+ * Returns the first item of the "displayed list"
+ */
+GList *ET_Displayed_File_List_First (void)
+{
+ ETCore->ETFileDisplayedList = g_list_first(ETCore->ETFileDisplayedList);
+ return ETCore->ETFileDisplayedList;
+}
+
+/*
+ * Returns the previous item of the "displayed list". When no more item, it returns NULL.
+ */
+GList *ET_Displayed_File_List_Previous (void)
+{
+ if (ETCore->ETFileDisplayedList && ETCore->ETFileDisplayedList->prev)
+ return ETCore->ETFileDisplayedList = ETCore->ETFileDisplayedList->prev;
+ else
+ return NULL;
+}
+
+/*
+ * Returns the next item of the "displayed list".
+ * When no more item, it returns NULL to don't "overwrite" the list.
+ */
+GList *ET_Displayed_File_List_Next (void)
+{
+ if (ETCore->ETFileDisplayedList && ETCore->ETFileDisplayedList->next)
+ return ETCore->ETFileDisplayedList = ETCore->ETFileDisplayedList->next;
+ else
+ return NULL;
+}
+
+/*
+ * Returns the last item of the "displayed list"
+ */
+GList *ET_Displayed_File_List_Last (void)
+{
+ ETCore->ETFileDisplayedList = g_list_last(ETCore->ETFileDisplayedList);
+ return ETCore->ETFileDisplayedList;
+}
+
+/*
+ * Returns the item of the "displayed list" which correspond to the given 'ETFile' (used into browser list).
+ */
+GList *ET_Displayed_File_List_By_Etfile (ET_File *ETFile)
+{
+ GList *etfilelist;
+
+ etfilelist = ET_Displayed_File_List_First();
+ while (ETFile && etfilelist)
+ {
+ if ( ETFile == (ET_File *)etfilelist->data )
+ break;
+ etfilelist = ET_Displayed_File_List_Next();
+ }
+ ETCore->ETFileDisplayedList = etfilelist; // To "save" the position like in ET_File_List_Next... (not very good - FIX ME)
+ return etfilelist;
+}
+
+/*
+ * Returns the item of the "displayed list" which have the position 'pos_in_list' in the list (used into CDDB result list).
+ * Range for 'pos_in_list' is 1 to ETFileDisplayedList_Length.
+ */
+GList *ET_Displayed_File_List_By_Position (gulong pos_in_list)
+{
+ GList *etfilelist;
+ guint i = 1;
+
+ etfilelist = ET_Displayed_File_List_First();
+ while (etfilelist)
+ {
+ if (i == pos_in_list)
+ break;
+ etfilelist = ET_Displayed_File_List_Next();
+ i++;
+ }
+ ETCore->ETFileDisplayedList = etfilelist; // To "save" the position like in ET_Displayed_File_List_Next... (not very good - FIX ME)
+ return etfilelist;
+}
+
+/*
+ * Just returns the current item of the "main list"
+ */
+/*GList *ET_Displayed_File_List_Current (void)
+{
+ return ETCore->ETFileDisplayedList;
+ //return ETCore->ETFileDisplayedListPtr;
+}*/
+
+/*
+ * Renumber the list of displayed files (IndexKey) from 1 to n
+ */
+void ET_Displayed_File_List_Number (void)
+{
+ GList *list = NULL;
+ guint i = 1;
+
+ list = g_list_first(ETCore->ETFileDisplayedList);
+ while (list)
+ {
+ ((ET_File *)list->data)->IndexKey = i++;
+ list = list->next;
+ }
+}
+
+/*
+ * Returns the length of the list of displayed files
+ */
+guint ET_Displayed_File_List_Get_Length (void)
+{
+ GList *list = NULL;
+
+ list = g_list_first(ETCore->ETFileDisplayedList);
+ ETCore->ETFileDisplayedList_Length = g_list_length(list);
+ return ETCore->ETFileDisplayedList_Length;
+}
+
+/*
+ * Load the list of displayed files (calculate length, size, ...)
+ * It contains part (filtrated : view by artists and albums) or full ETCore->ETFileList list
+ */
+gboolean ET_Set_Displayed_File_List (GList *ETFileList)
+{
+ GList *list = NULL;
+
+ ETCore->ETFileDisplayedList = g_list_first(ETFileList);
+
+ //ETCore->ETFileDisplayedListPtr = ETCore->ETFileDisplayedList;
+ ETCore->ETFileDisplayedList_Length = ET_Displayed_File_List_Get_Length();
+ ETCore->ETFileDisplayedList_TotalSize = 0;
+ ETCore->ETFileDisplayedList_TotalDuration = 0;
+
+ // Get size and duration of files in the list
+ list = ETCore->ETFileDisplayedList;
+ while (list)
+ {
+ ETCore->ETFileDisplayedList_TotalSize += ((ET_File_Info *)((ET_File *)list->data)->ETFileInfo)->size;
+ ETCore->ETFileDisplayedList_TotalDuration += ((ET_File_Info *)((ET_File *)list->data)->ETFileInfo)->duration;
+ list = list->next;
+ }
+
+ // Sort the file list
+ ET_Sort_Displayed_File_List(SORTING_FILE_MODE);
+
+ // Should renums ETCore->ETFileDisplayedList only!
+ ET_Displayed_File_List_Number();
+
+ return TRUE;
+}
+
+
+
+/*********************
+ * Freeing functions *
+ *********************/
+
+/*
+ * Frees the full main list of files: GList *ETFileList and reinitialize it.
+ */
+gboolean ET_Free_File_List (void)
+{
+ GList *list = NULL;
+
+ if (!ETCore || !ETCore->ETFileList) return FALSE;
+
+ list = g_list_last(ETCore->ETFileList);
+ while (list)
+ {
+ ET_Free_File_List_Item((ET_File *)list->data);
+ if (!list->prev) break;
+ list = list->prev;
+ }
+
+ g_list_free(list);
+ ETCore->ETFileList = (GList *)NULL;
+ return TRUE;
+}
+
+
+/*
+ * Frees one item of the full main list of files.
+ */
+gboolean ET_Free_File_List_Item (ET_File *ETFile)
+{
+ if (ETFile)
+ {
+ /* Frees the lists */
+ ET_Free_File_Name_List(ETFile->FileNameList);
+ ET_Free_File_Name_List(ETFile->FileNameListBak);
+ ET_Free_File_Tag_List (ETFile->FileTagList);
+ ET_Free_File_Tag_List (ETFile->FileTagListBak);
+ /* Frees infos of ETFileInfo */
+ ET_Free_File_Info_Item (ETFile->ETFileInfo);
+ g_free(ETFile->ETFileExtension);
+ g_free(ETFile);
+ ETFile = NULL;
+ }
+ return TRUE;
+}
+
+
+/*
+ * Frees the full list: GList *FileNameList.
+ */
+gboolean ET_Free_File_Name_List (GList *FileNameList)
+{
+ GList *list;
+
+ if (!FileNameList) return FALSE;
+
+ list = g_list_last(FileNameList);
+ while (list)
+ {
+ if ( (File_Name *)list->data )
+ ET_Free_File_Name_Item( (File_Name *)list->data );
+
+ if (!list->prev) break;
+ list = list->prev;
+ }
+ g_list_free(list);
+ FileNameList = (GList *)NULL;
+ return TRUE;
+}
+
+
+/*
+ * Frees a File_Name item.
+ */
+gboolean ET_Free_File_Name_Item (File_Name *FileName)
+{
+ if (!FileName) return FALSE;
+
+ g_free(FileName->value);
+ g_free(FileName->value_utf8);
+ g_free(FileName->value_ck);
+ g_free(FileName);
+ FileName = (File_Name *)NULL;
+ return TRUE;
+}
+
+
+/*
+ * Frees the full list: GList *TagList.
+ */
+gboolean ET_Free_File_Tag_List (GList *FileTagList)
+{
+ GList *list;
+
+ if (!FileTagList) return FALSE;
+
+ list = g_list_last(FileTagList);
+ while (list)
+ {
+ if ( (File_Tag *)list->data )
+ ET_Free_File_Tag_Item( (File_Tag *)list->data );
+
+ if (!list->prev) break;
+ list = list->prev;
+ }
+ g_list_free(list);
+ FileTagList = (GList *)NULL;
+ return TRUE;
+}
+
+
+/*
+ * Frees the list of 'other' field in a File_Tag item (contains attached gchar data).
+ */
+gboolean ET_Free_File_Tag_Item_Other_Field (File_Tag *FileTag)
+{
+ GList *other = FileTag->other;
+
+ while (other)
+ {
+ g_free(other->data);
+
+ if (!other->next) break;
+ other = other->next;
+ }
+ g_list_free(FileTag->other);
+
+ return TRUE;
+}
+
+
+/*
+ * Frees a File_Tag item.
+ */
+gboolean ET_Free_File_Tag_Item (File_Tag *FileTag)
+{
+ if (!FileTag) return FALSE;
+
+ g_free(FileTag->title);
+ g_free(FileTag->artist);
+ g_free(FileTag->album);
+ g_free(FileTag->disc_number);
+ g_free(FileTag->year);
+ g_free(FileTag->track);
+ g_free(FileTag->track_total);
+ g_free(FileTag->genre);
+ g_free(FileTag->comment);
+ g_free(FileTag->composer);
+ g_free(FileTag->orig_artist);
+ g_free(FileTag->copyright);
+ g_free(FileTag->url);
+ g_free(FileTag->encoded_by);
+ Picture_Free(FileTag->picture);
+ // Free list of other fields
+ ET_Free_File_Tag_Item_Other_Field(FileTag);
+
+ g_free(FileTag);
+ FileTag = (File_Tag *)NULL;
+ return TRUE;
+}
+
+
+/*
+ * Frees a File_Info item.
+ */
+gboolean ET_Free_File_Info_Item (ET_File_Info *ETFileInfo)
+{
+ if (!ETFileInfo) return FALSE;
+
+ g_free(ETFileInfo->mpc_profile);
+ g_free(ETFileInfo->mpc_version);
+
+ g_free(ETFileInfo);
+ ETFileInfo = (ET_File_Info *)NULL;
+ return TRUE;
+}
+
+
+/*
+ * History list contains only pointers, so no data to free except the history structure.
+ */
+gboolean ET_Free_History_File_List (void)
+{
+ GList *list;
+
+ if (!ETCore || !ETCore->ETHistoryFileList) return FALSE;
+
+ ETCore->ETHistoryFileList = g_list_first(ETCore->ETHistoryFileList);
+ list = ETCore->ETHistoryFileList;
+ while (list)
+ {
+ g_free( (ET_History_File *)list->data );
+
+ if (!list->next) break;
+ list = list->next;
+ }
+ g_list_free(ETCore->ETHistoryFileList);
+ ETCore->ETHistoryFileList = (GList *)NULL;
+ return TRUE;
+}
+
+/*
+ * "Display" list contains only pointers, so NOTHING to free
+ */
+gboolean ET_Free_Displayed_File_List (void)
+{
+ if (!ETCore || !ETCore->ETFileDisplayedList) return FALSE;
+
+ ETCore->ETFileDisplayedList = g_list_first(ETCore->ETFileDisplayedList);
+ ETCore->ETFileDisplayedList = (GList *)NULL;
+
+ return TRUE;
+}
+
+
+/*
+ * ArtistAlbum list contains 3 levels of lists
+ */
+gboolean ET_Free_Artist_Album_File_List (void)
+{
+ GList *ArtistList;
+ GList *AlbumList;
+ GList *etfilelist;
+
+ if (!ETCore || !ETCore->ETArtistAlbumFileList) return FALSE;
+
+ ArtistList = ETCore->ETArtistAlbumFileList;
+ while (ArtistList)
+ {
+ AlbumList = (GList *)ArtistList->data;
+ while (AlbumList)
+ {
+ etfilelist = (GList *)AlbumList->data;
+ if (etfilelist)
+ g_list_free(etfilelist);
+ AlbumList = AlbumList->next;
+ }
+ if (ArtistList->data) // Free AlbumList list
+ g_list_free((GList *)ArtistList->data);
+
+ ArtistList = ArtistList->next;
+ }
+ if (ETCore->ETArtistAlbumFileList)
+ g_list_free(ETCore->ETArtistAlbumFileList);
+
+ ETCore->ETArtistAlbumFileList = (GList *)NULL;
+
+ return TRUE;
+}
+
+
+
+
+/*********************
+ * Copying functions *
+ *********************/
+
+/*
+ * Duplicate the 'other' list
+ */
+gboolean ET_Copy_File_Tag_Item_Other_Field (ET_File *ETFile, File_Tag *FileTag)
+{
+ File_Tag *FileTagCur;
+ GList *list = NULL;
+
+ FileTagCur = (File_Tag *)(ETFile->FileTag)->data;
+ list = FileTagCur->other;
+ while (list)
+ {
+ FileTag->other = g_list_append(FileTag->other,g_strdup((gchar *)list->data));
+ list = list->next;
+ }
+ return TRUE;
+}
+
+
+/*
+ * Copy data of the File_Tag structure (of ETFile) to the FileTag item.
+ * Reallocate data if not null.
+ */
+gboolean ET_Copy_File_Tag_Item (ET_File *ETFile, File_Tag *FileTag)
+{
+ File_Tag *FileTagCur;
+
+ if (!ETFile || !ETFile->FileTag || !(File_Tag *)(ETFile->FileTag)->data || !FileTag)
+ return FALSE;
+
+ /* The data to duplicate to FileTag */
+ FileTagCur = (File_Tag *)(ETFile->FileTag)->data;
+ // Key for the item, may be overwritten
+ FileTag->key = ET_Undo_Key_New();
+
+ if (FileTagCur->title)
+ {
+ FileTag->title = g_strdup(FileTagCur->title);
+ }else
+ {
+ g_free(FileTag->title);
+ FileTag->title = NULL;
+ }
+
+ if (FileTagCur->artist)
+ {
+ FileTag->artist = g_strdup(FileTagCur->artist);
+ }else
+ {
+ g_free(FileTag->artist);
+ FileTag->artist = NULL;
+ }
+
+ if (FileTagCur->album)
+ {
+ FileTag->album = g_strdup(FileTagCur->album);
+ }else
+ {
+ g_free(FileTag->album);
+ FileTag->album = NULL;
+ }
+
+ if (FileTagCur->disc_number)
+ {
+ FileTag->disc_number = g_strdup(FileTagCur->disc_number);
+ }else
+ {
+ g_free(FileTag->disc_number);
+ FileTag->disc_number = NULL;
+ }
+
+ if (FileTagCur->year)
+ {
+ FileTag->year = g_strdup(FileTagCur->year);
+ }else
+ {
+ g_free(FileTag->year);
+ FileTag->year = NULL;
+ }
+
+ if (FileTagCur->track)
+ {
+ FileTag->track = g_strdup(FileTagCur->track);
+ }else
+ {
+ g_free(FileTag->track);
+ FileTag->track = NULL;
+ }
+
+ if (FileTagCur->track_total)
+ {
+ FileTag->track_total = g_strdup(FileTagCur->track_total);
+ }else
+ {
+ g_free(FileTag->track_total);
+ FileTag->track_total = NULL;
+ }
+
+ if (FileTagCur->genre)
+ {
+ FileTag->genre = g_strdup(FileTagCur->genre);
+ }else
+ {
+ g_free(FileTag->genre);
+ FileTag->genre = NULL;
+ }
+
+ if (FileTagCur->comment)
+ {
+ FileTag->comment = g_strdup(FileTagCur->comment);
+ }else
+ {
+ g_free(FileTag->comment);
+ FileTag->comment = NULL;
+ }
+
+ if (FileTagCur->composer)
+ {
+ FileTag->composer = g_strdup(FileTagCur->composer);
+ }else
+ {
+ g_free(FileTag->composer);
+ FileTag->composer = NULL;
+ }
+
+ if (FileTagCur->orig_artist)
+ {
+ FileTag->orig_artist = g_strdup(FileTagCur->orig_artist);
+ }else
+ {
+ g_free(FileTag->orig_artist);
+ FileTag->orig_artist = NULL;
+ }
+
+ if (FileTagCur->copyright)
+ {
+ FileTag->copyright = g_strdup(FileTagCur->copyright);
+ }else
+ {
+ g_free(FileTag->copyright);
+ FileTag->copyright = NULL;
+ }
+
+ if (FileTagCur->url)
+ {
+ FileTag->url = g_strdup(FileTagCur->url);
+ }else
+ {
+ g_free(FileTag->url);
+ FileTag->url = NULL;
+ }
+
+ if (FileTagCur->encoded_by)
+ {
+ FileTag->encoded_by = g_strdup(FileTagCur->encoded_by);
+ }else
+ {
+ g_free(FileTag->encoded_by);
+ FileTag->encoded_by = NULL;
+ }
+
+ if (FileTagCur->picture)
+ {
+ if (FileTag->picture)
+ Picture_Free(FileTag->picture);
+ FileTag->picture = Picture_Copy(FileTagCur->picture);
+ }else if (FileTag->picture)
+ {
+ Picture_Free(FileTag->picture);
+ FileTag->picture = NULL;
+ }
+
+ if (FileTagCur->other)
+ {
+ ET_Copy_File_Tag_Item_Other_Field(ETFile,FileTag);
+ }else
+ {
+ ET_Free_File_Tag_Item_Other_Field (FileTag);
+ }
+
+ return TRUE;
+}
+
+
+/*
+ * Set the value of a field of a FileName item (for ex, value of FileName->value)
+ * Must be used only for the 'gchar *' components (Only used for the filename)
+ */
+gboolean ET_Set_Field_File_Name_Item (gchar **FileNameField, gchar *value)
+{
+ return ET_Set_Field_File_Tag_Item(FileNameField,value);
+}
+
+
+/*
+ * Fill content of a FileName item according to the filename passed in argument (UTF-8 filename or not)
+ * Calculate also the collate key.
+ */
+gboolean ET_Set_Filename_File_Name_Item (File_Name *FileName, gchar *filename_utf8, gchar *filename)
+{
+ if (!FileName)
+ return FALSE;
+
+ if (filename_utf8 && filename)
+ {
+ FileName->value_utf8 = g_strdup(filename_utf8);
+ FileName->value = g_strdup(filename);
+ FileName->value_ck = g_utf8_collate_key_for_filename(FileName->value_utf8, -1);
+ }else if (filename_utf8)
+ {
+ FileName->value_utf8 = g_strdup(filename_utf8);
+ FileName->value = filename_from_display(filename_utf8);
+ FileName->value_ck = g_utf8_collate_key_for_filename(FileName->value_utf8, -1);
+ }else if (filename)
+ {
+ FileName->value_utf8 = filename_to_display(filename);;
+ FileName->value = g_strdup(filename);
+ FileName->value_ck = g_utf8_collate_key_for_filename(FileName->value_utf8, -1);
+ }else
+ {
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+
+/*
+ * Set the value of a field of a FileTag item (for ex, value of FileTag->title)
+ * Must be used only for the 'gchar *' components
+ */
+gboolean ET_Set_Field_File_Tag_Item (gchar **FileTagField, gchar *value)
+{
+ if (!FileTagField) return FALSE;
+
+ if (*FileTagField != NULL)
+ {
+ g_free(*FileTagField);
+ *FileTagField = NULL;
+ }
+
+ if (value != NULL)
+ {
+ if (g_utf8_strlen(value, -1) > 0)
+ *FileTagField = g_strdup(value);
+ else
+ *FileTagField = NULL;
+ }
+
+ return TRUE;
+}
+
+
+/*
+ * Set the value of a field of a FileTag Picture item.
+ */
+gboolean ET_Set_Field_File_Tag_Picture (Picture **FileTagField, Picture *pic)
+{
+ if (!FileTagField) return FALSE;
+
+ if (*FileTagField != NULL)
+ {
+ Picture_Free((Picture *)*FileTagField);
+ *FileTagField = NULL;
+ }
+
+ if (pic)
+ *FileTagField = Picture_Copy(pic);
+
+ return TRUE;
+}
+
+
+/************************
+ * Displaying functions *
+ ************************/
+
+/*
+ * Display informations of the file (Position + Header + Tag) to the user interface.
+ * Before doing it, it saves data of the file currently displayed
+ */
+void ET_Display_File_Data_To_UI (ET_File *ETFile)
+{
+ ET_File_Description *ETFileDescription;
+ gchar *cur_filename;
+ gchar *cur_filename_utf8;
+ gchar *msg;
+
+ if (!ETFile) return;
+
+ cur_filename = ((File_Name *)((GList *)ETFile->FileNameCur)->data)->value;
+ cur_filename_utf8 = ((File_Name *)((GList *)ETFile->FileNameCur)->data)->value_utf8;
+ ETFileDescription = ETFile->ETFileDescription;
+
+ /* Save the current displayed file */
+ ETCore->ETFileDisplayed = ETFile;
+
+ /* Display position in list + show/hide icon if file writable/read_only (cur_filename) */
+ ET_Display_File_And_List_Status_To_UI(ETFile);
+
+ /* Display filename (and his path) (value in FileNameNew) */
+ ET_Display_Filename_To_UI(ETFile);
+
+ /* Display tag data */
+ switch (ETFileDescription->TagType)
+ {
+#ifdef ENABLE_MP3
+ case ID3_TAG:
+ gtk_frame_set_label(GTK_FRAME(TagFrame),_("ID3 Tag"));
+ ET_Display_File_Tag_To_UI(ETFile);
+ break;
+#endif
+#ifdef ENABLE_OGG
+ case OGG_TAG:
+ gtk_frame_set_label(GTK_FRAME(TagFrame),_("Ogg Vorbis Tag"));
+ ET_Display_File_Tag_To_UI(ETFile);
+ break;
+#endif
+#ifdef ENABLE_FLAC
+ case FLAC_TAG:
+ gtk_frame_set_label(GTK_FRAME(TagFrame),_("FLAC Vorbis Tag"));
+ ET_Display_File_Tag_To_UI(ETFile);
+ break;
+#endif
+ case APE_TAG:
+ gtk_frame_set_label(GTK_FRAME(TagFrame),_("APE Tag"));
+ ET_Display_File_Tag_To_UI(ETFile);
+ break;
+#ifdef ENABLE_MP4
+ case MP4_TAG:
+ gtk_frame_set_label(GTK_FRAME(TagFrame),_("MP4/M4A/AAC Tag"));
+ ET_Display_File_Tag_To_UI(ETFile);
+ break;
+#endif
+#ifdef ENABLE_WAVPACK
+ case WAVPACK_TAG:
+ gtk_frame_set_label(GTK_FRAME(TagFrame),_("Wavpack Tag"));
+ ET_Display_File_Tag_To_UI(ETFile);
+ break;
+#endif
+ case UNKNOWN_TAG:
+ default:
+ gtk_frame_set_label(GTK_FRAME(TagFrame),_("Tag"));
+ ET_Display_File_Tag_To_UI(ETFile); // To reinit screen
+ Log_Print("FileTag: Undefined tag type %d for file %s.",ETFileDescription->TagType,cur_filename_utf8);
+ break;
+ }
+
+ /* Display controls in tag area */
+ Tag_Area_Display_Controls(ETFile);
+
+ /* Display file data, header data and file type */
+ switch (ETFileDescription->FileType)
+ {
+#ifdef ENABLE_MP3
+ case MP3_FILE:
+ gtk_frame_set_label(GTK_FRAME(FileFrame),_("MP3 File"));
+ Mpeg_Header_Display_File_Info_To_UI(cur_filename,ETFile->ETFileInfo);
+ break;
+ case MP2_FILE:
+ gtk_frame_set_label(GTK_FRAME(FileFrame),_("MP2 File"));
+ Mpeg_Header_Display_File_Info_To_UI(cur_filename,ETFile->ETFileInfo);
+ break;
+#endif
+#ifdef ENABLE_OGG
+ case OGG_FILE:
+ gtk_frame_set_label(GTK_FRAME(FileFrame),_("Ogg Vorbis File"));
+ Ogg_Header_Display_File_Info_To_UI(cur_filename,ETFile->ETFileInfo);
+ break;
+#endif
+#ifdef ENABLE_SPEEX
+ case SPEEX_FILE:
+ gtk_frame_set_label(GTK_FRAME(FileFrame),_("Speex File"));
+ Ogg_Header_Display_File_Info_To_UI(cur_filename,ETFile->ETFileInfo);
+ break;
+#endif
+#ifdef ENABLE_FLAC
+ case FLAC_FILE:
+ gtk_frame_set_label(GTK_FRAME(FileFrame),_("FLAC File"));
+ Flac_Header_Display_File_Info_To_UI(cur_filename,ETFile->ETFileInfo);
+ break;
+#endif
+ case MPC_FILE:
+ gtk_frame_set_label(GTK_FRAME(FileFrame),_("MusePack File"));
+ Mpc_Header_Display_File_Info_To_UI(cur_filename,ETFile->ETFileInfo);
+ break;
+ case MAC_FILE:
+ gtk_frame_set_label(GTK_FRAME(FileFrame),_("Monkey's Audio File"));
+ Mac_Header_Display_File_Info_To_UI(cur_filename,ETFile->ETFileInfo);
+ break;
+#ifdef ENABLE_MP4
+ case MP4_FILE:
+ gtk_frame_set_label(GTK_FRAME(FileFrame),_("MP4/AAC File"));
+ Mp4_Header_Display_File_Info_To_UI(cur_filename,ETFile->ETFileInfo);
+ break;
+#endif
+#ifdef ENABLE_WAVPACK
+ case WAVPACK_FILE:
+ gtk_frame_set_label(GTK_FRAME(FileFrame),_("Wavpack File"));
+ Wavpack_Header_Display_File_Info_To_UI(cur_filename,ETFile->ETFileInfo);
+ break;
+#endif
+ case UNKNOWN_FILE:
+ default:
+ gtk_frame_set_label(GTK_FRAME(FileFrame),_("File"));
+ // Default displaying
+ ET_Display_File_Info_To_UI(ETFile->ETFileInfo);
+ Log_Print("ETFileInfo: Undefined file type %d for file %s.",ETFileDescription->FileType,cur_filename_utf8);
+ break;
+ }
+
+ msg = g_strdup_printf(_("File: '%s'"), cur_filename_utf8);
+ Statusbar_Message(msg,FALSE);
+ g_free(msg);
+}
+
+
+/*
+ * Toggle visibility of the small status icon if filename is read-only or not found.
+ * Show the position of the current file in the list, by using the index and list length.
+ */
+void ET_Display_File_And_List_Status_To_UI (ET_File *ETFile)
+{
+ FILE *file;
+ gchar *text;
+ gchar *cur_filename;
+
+ if (!ETFile) return;
+
+ cur_filename = ((File_Name *)((GList *)ETFile->FileNameCur)->data)->value;
+
+ /* Show/hide 'AccessStatusIcon' */
+ if ( (file=fopen(cur_filename,"r+b"))!=NULL )
+ {
+ gtk_widget_hide(ReadOnlyStatusIconBox);
+ gtk_widget_hide(BrokenStatusIconBox);
+ fclose(file);
+ }else
+ {
+ switch(errno)
+ {
+ case EACCES: /* Permission denied */
+ case EROFS: /* Read-only file system */
+ /* Read only file */
+ gtk_widget_show_all(ReadOnlyStatusIconBox);
+ gtk_widget_hide(BrokenStatusIconBox);
+ break;
+ case ENOENT: /* No such file or directory */
+ default:
+ /* File not found */
+ gtk_widget_show_all(BrokenStatusIconBox);
+ gtk_widget_hide(ReadOnlyStatusIconBox);
+ }
+ }
+
+ /* Show position of current file in list */
+ text = g_strdup_printf("%d/%d:",ETFile->IndexKey,ETCore->ETFileDisplayedList_Length);
+ gtk_label_set_text(GTK_LABEL(FileIndex),text);
+ g_free(text);
+}
+
+void ET_Display_Filename_To_UI (ET_File *ETFile)
+{
+ gchar *pos;
+ gchar *new_filename_utf8;
+ gchar *basename_utf8;
+ gchar *dirname_utf8;
+ gchar *text;
+
+ if (!ETFile) return;
+
+ new_filename_utf8 = ((File_Name *)((GList *)ETFile->FileNameNew)->data)->value_utf8;
+
+ /*
+ * Set filename into FileEntry
+ */
+ basename_utf8 = g_path_get_basename(new_filename_utf8);
+
+ // Remove the extension
+ if ((pos=g_utf8_strrchr(basename_utf8, -1, '.'))!=NULL)
+ *pos = 0;
+ gtk_entry_set_text(GTK_ENTRY(FileEntry),basename_utf8);
+ /*FIX ME : gchar *tmp = ET_Utf8_Validate_Full_String(basename_utf8);
+ g_free(tmp);*/
+ g_free(basename_utf8);
+ // Justify to the left text into FileEntry
+ gtk_editable_set_position(GTK_EDITABLE(FileEntry),0);
+
+ /*
+ * Set the path to the file into BrowserEntry (dirbrowser)
+ */
+ dirname_utf8 = g_path_get_dirname(new_filename_utf8);
+ Browser_Entry_Set_Text(dirname_utf8);
+
+ // And refresh the number of files in this directory
+ text = g_strdup_printf(_("%u file(s)"),ET_Get_Number_Of_Files_In_Directory(dirname_utf8));
+ Browser_Label_Set_Text(text);
+ g_free(dirname_utf8);
+ g_free(text);
+}
+
+
+/*
+ * Display all tag infos (tags fields) into entry boxes of the user interface.
+ * These data have the same structure for all files.
+ */
+gboolean ET_Display_File_Tag_To_UI (ET_File *ETFile)
+{
+ File_Tag *FileTag = NULL;
+ //GtkTextBuffer *textbuffer;
+
+ if (!ETFile || !ETFile->FileTag)
+ {
+ // Reset all tag entries
+ Clear_Tag_Entry_Fields();
+ //Tag_Area_Set_Sensitive(FALSE);
+ return FALSE;
+ }
+
+ //Tag_Area_Set_Sensitive(TRUE); // Causes displaying problem when saving files
+
+ FileTag = (File_Tag *)(ETFile->FileTag->data);
+
+ /* Show title */
+ if (FileTag && FileTag->title)
+ {
+ gchar *tmp = ET_Utf8_Validate_Full_String(FileTag->title);
+ gtk_entry_set_text(GTK_ENTRY(TitleEntry), tmp);
+ g_free(tmp);
+ }else
+ gtk_entry_set_text(GTK_ENTRY(TitleEntry),"");
+
+ /* Show artist */
+ if (FileTag && FileTag->artist)
+ {
+ gchar *tmp = ET_Utf8_Validate_Full_String(FileTag->artist);
+ gtk_entry_set_text(GTK_ENTRY(ArtistEntry), tmp);
+ g_free(tmp);
+ }else
+ gtk_entry_set_text(GTK_ENTRY(ArtistEntry),"");
+
+ /* Show album */
+ if (FileTag && FileTag->album)
+ {
+ gchar *tmp = ET_Utf8_Validate_Full_String(FileTag->album);
+ gtk_entry_set_text(GTK_ENTRY(AlbumEntry), tmp);
+ g_free(tmp);
+ }else
+ gtk_entry_set_text(GTK_ENTRY(AlbumEntry),"");
+
+ /* Show disc_number */
+ if (FileTag && FileTag->disc_number)
+ {
+ gchar *tmp = ET_Utf8_Validate_Full_String(FileTag->disc_number);
+ gtk_entry_set_text(GTK_ENTRY(DiscNumberEntry), tmp);
+ g_free(tmp);
+ }else
+ gtk_entry_set_text(GTK_ENTRY(DiscNumberEntry),"");
+
+ /* Show year */
+ if (FileTag && FileTag->year)
+ {
+ gchar *tmp = ET_Utf8_Validate_Full_String(FileTag->year);
+ gtk_entry_set_text(GTK_ENTRY(YearEntry),tmp);
+ g_free(tmp);
+ }else
+ gtk_entry_set_text(GTK_ENTRY(YearEntry),"");
+
+ /* Show track */
+ if (FileTag && FileTag->track)
+ {
+ gchar *tmp = ET_Utf8_Validate_Full_String(FileTag->track);
+ gtk_entry_set_text(GTK_ENTRY(GTK_BIN(TrackEntryCombo)->child),tmp);
+ g_free(tmp);
+ }else
+ gtk_entry_set_text(GTK_ENTRY(GTK_BIN(TrackEntryCombo)->child),"");
+
+ /* Show number of tracks on the album */
+ if (FileTag && FileTag->track_total)
+ {
+ gchar *tmp = ET_Utf8_Validate_Full_String(FileTag->track_total);
+ gtk_entry_set_text(GTK_ENTRY(TrackTotalEntry),tmp);
+ g_free(tmp);
+ }else
+ gtk_entry_set_text(GTK_ENTRY(TrackTotalEntry),"");
+
+ /* Show genre */
+ if (FileTag && FileTag->genre)
+ {
+ gchar *tmp = ET_Utf8_Validate_Full_String(FileTag->genre);
+ gtk_entry_set_text(GTK_ENTRY(GTK_BIN(GenreCombo)->child), tmp);
+ g_free(tmp);
+ }else
+ gtk_entry_set_text(GTK_ENTRY(GTK_BIN(GenreCombo)->child),"");
+
+ /* Show comment */
+ //textbuffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(CommentView));
+ if (FileTag && FileTag->comment)
+ {
+ gchar *tmp = ET_Utf8_Validate_Full_String(FileTag->comment);
+ //gtk_text_buffer_set_text(GTK_TEXT_BUFFER(textbuffer), FileTag->comment, -1);
+ gtk_entry_set_text(GTK_ENTRY(CommentEntry), tmp);
+ g_free(tmp);
+ }else
+ //gtk_text_buffer_set_text(GTK_TEXT_BUFFER(textbuffer), "", -1);
+ gtk_entry_set_text(GTK_ENTRY(CommentEntry),"");
+
+ /* Show composer */
+ if (FileTag && FileTag->composer)
+ {
+ gchar *tmp = ET_Utf8_Validate_Full_String(FileTag->composer);
+ gtk_entry_set_text(GTK_ENTRY(ComposerEntry), tmp);
+ g_free(tmp);
+ }else
+ gtk_entry_set_text(GTK_ENTRY(ComposerEntry),"");
+
+ /* Show original artist */
+ if (FileTag && FileTag->orig_artist)
+ {
+ gchar *tmp = ET_Utf8_Validate_Full_String(FileTag->orig_artist);
+ gtk_entry_set_text(GTK_ENTRY(OrigArtistEntry), tmp);
+ g_free(tmp);
+ }else
+ gtk_entry_set_text(GTK_ENTRY(OrigArtistEntry),"");
+
+ /* Show copyright */
+ if (FileTag && FileTag->copyright)
+ {
+ gchar *tmp = ET_Utf8_Validate_Full_String(FileTag->copyright);
+ gtk_entry_set_text(GTK_ENTRY(CopyrightEntry), tmp);
+ g_free(tmp);
+ }else
+ gtk_entry_set_text(GTK_ENTRY(CopyrightEntry),"");
+
+ /* Show URL */
+ if (FileTag && FileTag->url)
+ {
+ gchar *tmp = ET_Utf8_Validate_Full_String(FileTag->url);
+ gtk_entry_set_text(GTK_ENTRY(URLEntry), tmp);
+ g_free(tmp);
+ }else
+ gtk_entry_set_text(GTK_ENTRY(URLEntry),"");
+
+ /* Show Encoded by */
+ if (FileTag && FileTag->encoded_by)
+ {
+ gchar *tmp = ET_Utf8_Validate_Full_String(FileTag->encoded_by);
+ gtk_entry_set_text(GTK_ENTRY(EncodedByEntry), tmp);
+ g_free(tmp);
+ }else
+ gtk_entry_set_text(GTK_ENTRY(EncodedByEntry),"");
+
+ /* Show picture */
+ PictureEntry_Clear();
+ if (FileTag && FileTag->picture)
+ {
+ Picture *pic = FileTag->picture;
+ guint nbr_pic = 0;
+ GtkWidget *page;
+ gchar *string;
+
+ PictureEntry_Update(FileTag->picture, 0);
+
+ // Count the number of items
+ while (pic)
+ {
+ nbr_pic++;
+ pic = pic->next;
+ }
+
+ // Get page "Pictures" of the notebook
+ page = gtk_notebook_get_nth_page(GTK_NOTEBOOK(TagNoteBook),1);
+ string = g_strdup_printf(_("Pictures (%d)"),nbr_pic);
+ // Update the notebook tab
+ gtk_notebook_set_tab_label_text(GTK_NOTEBOOK(TagNoteBook),page,string);
+ // Update the notebook menu
+ gtk_notebook_set_menu_label_text(GTK_NOTEBOOK(TagNoteBook),page,string);
+ g_free(string);
+
+ }else
+ {
+ GtkWidget *page;
+ // Get page "Pictures" of the notebook
+ page = gtk_notebook_get_nth_page(GTK_NOTEBOOK(TagNoteBook),1);
+ // Update the notebook tab
+ gtk_notebook_set_tab_label_text(GTK_NOTEBOOK(TagNoteBook),page,"Pictures");
+ // Update the notebook menu
+ gtk_notebook_set_menu_label_text(GTK_NOTEBOOK(TagNoteBook),page,"Pictures");
+ }
+
+ return TRUE;
+}
+
+
+/*
+ * "Default" way to display File Info to the user interface.
+ */
+gboolean ET_Display_File_Info_To_UI(ET_File_Info *ETFileInfo)
+{
+ gchar *text;
+ gchar *time = NULL;
+ gchar *time1 = NULL;
+ gchar *size = NULL;
+ gchar *size1 = NULL;
+
+ /* MPEG, Layer versions */
+ text = g_strdup_printf("%d, Layer %d",ETFileInfo->version,ETFileInfo->layer);
+ gtk_label_set_text(GTK_LABEL(VersionValueLabel),text);
+ g_free(text);
+
+ /* Bitrate */
+ text = g_strdup_printf(_("%d kb/s"),ETFileInfo->bitrate);
+ gtk_label_set_text(GTK_LABEL(BitrateValueLabel),text);
+ g_free(text);
+
+ /* Samplerate */
+ text = g_strdup_printf(_("%d Hz"),ETFileInfo->samplerate);
+ gtk_label_set_text(GTK_LABEL(SampleRateValueLabel),text);
+ g_free(text);
+
+ /* Mode */
+ text = g_strdup_printf("%d",ETFileInfo->mode);
+ gtk_label_set_text(GTK_LABEL(ModeValueLabel),text);
+ g_free(text);
+
+ /* Size */
+ size = Convert_Size(ETFileInfo->size);
+ size1 = Convert_Size(ETCore->ETFileDisplayedList_TotalSize);
+ text = g_strdup_printf("%s (%s)",size,size1);
+ gtk_label_set_text(GTK_LABEL(SizeValueLabel),text);
+ g_free(size);
+ g_free(size1);
+ g_free(text);
+
+ /* Duration */
+ time = Convert_Duration(ETFileInfo->duration);
+ time1 = Convert_Duration(ETCore->ETFileDisplayedList_TotalDuration);
+ text = g_strdup_printf("%s (%s)",time,time1);
+ gtk_label_set_text(GTK_LABEL(DurationValueLabel),text);
+ g_free(time);
+ g_free(time1);
+ g_free(text);
+
+ return TRUE;
+}
+
+
+
+
+/********************
+ * Saving functions *
+ ********************/
+
+/*
+ * Save informations of the file, contained into the entries of the user interface, in the list.
+ * An undo key is generated to be used for filename and tag if there are changed is the same time.
+ * Filename and Tag.
+ */
+void ET_Save_File_Data_From_UI (ET_File *ETFile)
+{
+ ET_File_Description *ETFileDescription;
+ File_Name *FileName;
+ File_Tag *FileTag;
+ guint undo_key;
+ gchar *cur_filename_utf8;
+
+ if (!ETFile ||
+ !ETFile->FileNameCur ||
+ !ETFile->FileNameCur->data)
+ return;
+
+ cur_filename_utf8 = ((File_Name *)((GList *)ETFile->FileNameCur)->data)->value_utf8;
+ ETFileDescription = ETFile->ETFileDescription;
+ undo_key = ET_Undo_Key_New();
+
+
+ /*
+ * Save filename and generate undo for filename
+ */
+ FileName = g_malloc0(sizeof(File_Name));
+ ET_Initialize_File_Name_Item(FileName);
+ FileName->key = undo_key;
+ ET_Save_File_Name_From_UI(ETFile,FileName); // Used for all files!
+
+ /*
+ * Save tag data and generate undo for tag
+ */
+ FileTag = g_malloc0(sizeof(File_Tag));
+ ET_Initialize_File_Tag_Item(FileTag);
+ FileTag->key = undo_key;
+
+ switch (ETFileDescription->TagType)
+ {
+#ifdef ENABLE_MP3
+ case ID3_TAG:
+#endif
+#ifdef ENABLE_OGG
+ case OGG_TAG:
+#endif
+#ifdef ENABLE_FLAC
+ case FLAC_TAG:
+#endif
+#ifdef ENABLE_MP4
+ case MP4_TAG:
+#endif
+#ifdef ENABLE_WAVPACK
+ case WAVPACK_TAG:
+#endif
+ case APE_TAG:
+ ET_Save_File_Tag_From_UI(FileTag);
+ ET_Copy_File_Tag_Item_Other_Field(ETFile,FileTag);
+ break;
+ case UNKNOWN_TAG:
+ default:
+ Log_Print("FileTag: Undefined tag type %d for file %s.",ETFileDescription->TagType,cur_filename_utf8);
+ break;
+ }
+
+ /*
+ * Generate undo for the file and the main undo list.
+ * If no changes detected, FileName and FileTag item are deleted.
+ */
+ ET_Manage_Changes_Of_File_Data(ETFile,FileName,FileTag);
+
+ /* Refresh file into browser list */
+ Browser_List_Refresh_File_In_List(ETFile);
+}
+
+
+/*
+ * Save displayed filename into list if it had been changed. Generates also an history list for undo/redo.
+ * - ETFile : the current etfile that we want to save,
+ * - FileName : where is 'temporary' saved the new filename.
+ *
+ * Note : it builds new filename (with path) from strings encoded into file system
+ * encoding, not UTF-8 (it preserves file system encoding of parent directories).
+ */
+gboolean ET_Save_File_Name_From_UI (ET_File *ETFile, File_Name *FileName)
+{
+ gchar *filename_new = NULL;
+ gchar *dirname = NULL;
+ gchar *filename;
+ gchar *filename_utf8;
+ gchar *extension;
+
+ if (!ETFile || !FileName)
+ return FALSE;
+
+ filename_utf8 = (gchar *)gtk_entry_get_text(GTK_ENTRY(FileEntry));
+ filename = filename_from_display(filename_utf8);
+ if (!filename)
+ {
+ // If translation fails...
+ GtkWidget *msgbox;
+ gchar *msg;
+ gchar *filename_escaped_utf8 = g_strescape(filename_utf8, NULL);
+ msg = g_strdup_printf(_("Could not convert filename : '%s'\n"
+ "into system filename encoding\n"
+ "(Try setting the environment variable G_FILENAME_ENCODING)."), filename_escaped_utf8);
+ msgbox = msg_box_new(_("Filename translation"),msg,GTK_STOCK_DIALOG_ERROR,BUTTON_OK,0);
+ msg_box_hide_check_button(MSG_BOX(msgbox));
+ msg_box_run(MSG_BOX(msgbox));
+ gtk_widget_destroy(msgbox);
+ g_free(msg);
+ g_free(filename);
+ g_free(filename_escaped_utf8);
+ return FALSE;
+ }
+
+ // Get the current path to the file
+ dirname = g_path_get_dirname( ((File_Name *)ETFile->FileNameNew->data)->value );
+
+ // Convert filename extension (lower or upper)
+ extension = ET_File_Name_Format_Extension(ETFile);
+
+ // Check length of filename (limit ~255 characters)
+ //ET_File_Name_Check_Length(ETFile,filename);
+
+ // Filename (in file system encoding!)
+ if (filename && strlen(filename)>0)
+ {
+ // Regenerate the new filename (without path)
+ filename_new = g_strconcat(filename,extension,NULL);
+ }else
+ {
+ // Keep the 'last' filename (if a 'blank' filename was entered in the fileentry for ex)...
+ filename_new = g_path_get_basename( ((File_Name *)ETFile->FileNameNew->data)->value );
+ }
+ g_free(extension);
+ g_free(filename);
+
+ // Check if new filename seems to be correct
+ if ( !filename_new || strlen(filename_new) <= strlen(ETFile->ETFileDescription->Extension) )
+ {
+ FileName->value = NULL;
+ FileName->value_utf8 = NULL;
+ FileName->value_ck = NULL;
+
+ g_free(filename_new);
+ g_free(dirname);
+ return FALSE;
+ }
+
+ // Convert the illegal characters
+ ET_File_Name_Convert_Character(filename_new); // FIX ME : should be in UTF8?
+
+ // Set the new file name (in file system encoding)
+ FileName->value = g_strconcat(dirname,G_DIR_SEPARATOR_S,filename_new,NULL);
+ // Set the new file name (in UTF-8 encoding)
+ FileName->value_utf8 = filename_to_display(FileName->value);
+ // Calculates collate key
+ FileName->value_ck = g_utf8_collate_key_for_filename(FileName->value_utf8, -1);
+
+ g_free(filename_new);
+ g_free(dirname);
+ return TRUE;
+}
+
+
+/*
+ * Do the same thing of ET_Save_File_Name_From_UI, but without getting the
+ * data from the UI.
+ */
+gboolean ET_Save_File_Name_Internal (ET_File *ETFile, File_Name *FileName)
+{
+ gchar *filename_new = NULL;
+ gchar *dirname = NULL;
+ gchar *filename;
+ gchar *extension;
+ gchar *pos;
+
+
+ if (!ETFile || !FileName)
+ return FALSE;
+
+ // Get the current path to the file
+ dirname = g_path_get_dirname( ((File_Name *)ETFile->FileNameNew->data)->value );
+
+ // Get the name of file (and rebuild it with extension with a 'correct' case)
+ filename = g_path_get_basename( ((File_Name *)ETFile->FileNameNew->data)->value );
+
+ // Remove the extension
+ if ((pos=strrchr(filename, '.'))!=NULL)
+ *pos = 0;
+
+ // Convert filename extension (lower/upper)
+ extension = ET_File_Name_Format_Extension(ETFile);
+
+ // Check length of filename
+ //ET_File_Name_Check_Length(ETFile,filename);
+
+ // Regenerate the new filename (without path)
+ filename_new = g_strconcat(filename,extension,NULL);
+ g_free(extension);
+ g_free(filename);
+
+ // Check if new filename seems to be correct
+ if (filename_new)
+ {
+ // Convert the illegal characters
+ ET_File_Name_Convert_Character(filename_new);
+
+ // Set the new file name (in file system encoding)
+ FileName->value = g_strconcat(dirname,G_DIR_SEPARATOR_S,filename_new,NULL);
+ // Set the new file name (in UTF-8 encoding)
+ FileName->value_utf8 = filename_to_display(FileName->value);
+ // Calculate collate key
+ FileName->value_ck = g_utf8_collate_key_for_filename(FileName->value_utf8, -1);
+
+ g_free(filename_new);
+ g_free(dirname);
+ return TRUE;
+ }else
+ {
+ FileName->value = NULL;
+ FileName->value_utf8 = NULL;
+ FileName->value_ck = NULL;
+
+ g_free(filename_new);
+ g_free(dirname);
+ return FALSE;
+ }
+}
+
+
+/*
+ * Load values, entered into entries of the UI, into a File_Tag structure which had been newly created.
+ */
+gboolean ET_Save_File_Tag_From_UI (File_Tag *FileTag)
+{
+ gchar *buffer = NULL;
+ //GtkTextBuffer *textbuffer;
+ //GtkTextIter start_iter;
+ //GtkTextIter end_iter;
+
+ if (!FileTag)
+ return FALSE;
+
+ /* Title */
+ buffer = g_strdup(gtk_entry_get_text(GTK_ENTRY(TitleEntry)));
+ Strip_String(buffer);
+
+ if ( g_utf8_strlen(buffer, -1) > 0 )
+ FileTag->title = buffer;
+ else
+ {
+ FileTag->title = NULL;
+ g_free(buffer);
+ }
+
+ /* Artist */
+ buffer = g_strdup(gtk_entry_get_text(GTK_ENTRY(ArtistEntry)));
+ Strip_String(buffer);
+
+ if ( g_utf8_strlen(buffer, -1) > 0 )
+ FileTag->artist = buffer;
+ else
+ {
+ FileTag->artist = NULL;
+ g_free(buffer);
+ }
+
+ /* Album */
+ buffer = g_strdup(gtk_entry_get_text(GTK_ENTRY(AlbumEntry)));
+ Strip_String(buffer);
+
+ if ( g_utf8_strlen(buffer, -1) > 0 )
+ FileTag->album = buffer;
+ else
+ {
+ FileTag->album = NULL;
+ g_free(buffer);
+ }
+
+ /* Disc Number */
+ buffer = g_strdup(gtk_entry_get_text(GTK_ENTRY(DiscNumberEntry)));
+ Strip_String(buffer);
+
+ if ( g_utf8_strlen(buffer, -1) > 0 )
+ FileTag->disc_number = buffer;
+ else
+ {
+ FileTag->disc_number = NULL;
+ g_free(buffer);
+ }
+
+ /* Year */
+ buffer = g_strdup(gtk_entry_get_text(GTK_ENTRY(YearEntry)));
+ Strip_String(buffer);
+
+ if ( g_utf8_strlen(buffer, -1) > 0 )
+ FileTag->year = buffer;
+ else
+ {
+ FileTag->year = NULL;
+ g_free(buffer);
+ }
+
+ /* Track */
+ buffer = g_strdup(gtk_entry_get_text(GTK_ENTRY(GTK_BIN(TrackEntryCombo)->child)));
+ Strip_String(buffer);
+
+ if ( g_utf8_strlen(buffer, -1) > 0 )
+ {
+ if (NUMBER_TRACK_FORMATED) {
+ FileTag->track = g_strdup_printf("%.*d",NUMBER_TRACK_FORMATED_SPIN_BUTTON,atoi(buffer));
+ g_free(buffer);
+ } else
+ FileTag->track = buffer;
+ } else
+ {
+ FileTag->track = NULL;
+ g_free(buffer);
+ }
+
+ /* Track Total */
+ buffer = g_strdup(gtk_entry_get_text(GTK_ENTRY(TrackTotalEntry)));
+ Strip_String(buffer);
+
+ if ( g_utf8_strlen(buffer, -1) > 0 )
+ {
+ if (NUMBER_TRACK_FORMATED)
+ {
+ FileTag->track_total = g_strdup_printf("%.*d",NUMBER_TRACK_FORMATED_SPIN_BUTTON,atoi(buffer));
+ g_free(buffer);
+ } else
+ FileTag->track_total = buffer;
+ } else
+ {
+ FileTag->track_total = NULL;
+ g_free(buffer);
+ }
+
+ /* Genre */
+ buffer = g_strdup(gtk_entry_get_text(GTK_ENTRY(GTK_BIN(GenreCombo)->child)));
+ Strip_String(buffer);
+
+ if ( g_utf8_strlen(buffer, -1) > 0 )
+ FileTag->genre = buffer;
+ else
+ {
+ FileTag->genre = NULL;
+ g_free(buffer);
+ }
+
+ /* Comment */
+ //textbuffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(CommentView));
+ //gtk_text_buffer_get_bounds(GTK_TEXT_BUFFER(textbuffer),&start_iter,&end_iter);
+ //buffer = gtk_text_buffer_get_text(GTK_TEXT_BUFFER(textbuffer),&start_iter,&end_iter,TRUE);
+ buffer = g_strdup(gtk_entry_get_text(GTK_ENTRY(CommentEntry)));
+ Strip_String(buffer);
+
+ if ( g_utf8_strlen(buffer, -1) > 0 )
+ FileTag->comment = buffer;
+ else
+ {
+ FileTag->comment = NULL;
+ g_free(buffer);
+ }
+
+ /* Composer */
+ buffer = g_strdup(gtk_entry_get_text(GTK_ENTRY(ComposerEntry)));
+ Strip_String(buffer);
+
+ if ( g_utf8_strlen(buffer, -1) > 0 )
+ FileTag->composer = buffer;
+ else
+ {
+ FileTag->composer = NULL;
+ g_free(buffer);
+ }
+
+ /* Original Artist */
+ buffer = g_strdup(gtk_entry_get_text(GTK_ENTRY(OrigArtistEntry)));
+ Strip_String(buffer);
+
+ if ( g_utf8_strlen(buffer, -1) > 0 )
+ FileTag->orig_artist = buffer;
+ else
+ {
+ FileTag->orig_artist = NULL;
+ g_free(buffer);
+ }
+
+ /* Copyright */
+ buffer = g_strdup(gtk_entry_get_text(GTK_ENTRY(CopyrightEntry)));
+ Strip_String(buffer);
+
+ if ( g_utf8_strlen(buffer, -1) > 0 )
+ FileTag->copyright = buffer;
+ else
+ {
+ FileTag->copyright = NULL;
+ g_free(buffer);
+ }
+
+ /* URL */
+ buffer = g_strdup(gtk_entry_get_text(GTK_ENTRY(URLEntry)));
+ Strip_String(buffer);
+
+ if ( g_utf8_strlen(buffer, -1) > 0 )
+ FileTag->url = buffer;
+ else
+ {
+ FileTag->url = NULL;
+ g_free(buffer);
+ }
+
+ /* Encoded by */
+ buffer = g_strdup(gtk_entry_get_text(GTK_ENTRY(EncodedByEntry)));
+ Strip_String(buffer);
+
+ if ( g_utf8_strlen(buffer, -1) > 0 )
+ FileTag->encoded_by = buffer;
+ else
+ {
+ FileTag->encoded_by = NULL;
+ g_free(buffer);
+ }
+
+ /* Picture */
+ {
+ Picture *pic, *prev_pic = NULL;
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+
+ if (FileTag->picture)
+ {
+ Picture_Free(FileTag->picture);
+ FileTag->picture = NULL;
+ }
+
+ model = gtk_tree_view_get_model(GTK_TREE_VIEW(PictureEntryView));
+ if (gtk_tree_model_get_iter_first(model, &iter))
+ {
+ do
+ {
+ gtk_tree_model_get(model, &iter, PICTURE_COLUMN_DATA, &pic,-1);
+ pic = Picture_Copy_One(pic);
+ if (!FileTag->picture)
+ FileTag->picture = pic;
+ else
+ prev_pic->next = pic;
+ prev_pic = pic;
+ } while (gtk_tree_model_iter_next(model, &iter));
+ }
+ }
+
+ return TRUE;
+}
+
+
+/*
+ * Do the same thing of ET_Save_File_Tag_From_UI without getting the data from the UI.
+ */
+gboolean ET_Save_File_Tag_Internal (ET_File *ETFile, File_Tag *FileTag)
+{
+ File_Tag *FileTagCur;
+
+
+ if (!ETFile || !ETFile->FileTag || !FileTag)
+ return FALSE;
+
+ FileTagCur = (File_Tag *)ETFile->FileTag->data;
+
+ /* Title */
+ if ( FileTagCur->title && g_utf8_strlen(FileTagCur->title, -1)>0 )
+ {
+ FileTag->title = g_strdup(FileTagCur->title);
+ Strip_String(FileTag->title);
+ } else
+ {
+ FileTag->title = NULL;
+ }
+
+ /* Artist */
+ if ( FileTagCur->artist && g_utf8_strlen(FileTagCur->artist, -1)>0 )
+ {
+ FileTag->artist = g_strdup(FileTagCur->artist);
+ Strip_String(FileTag->artist);
+ } else
+ {
+ FileTag->artist = NULL;
+ }
+
+
+ /* Album */
+ if ( FileTagCur->album && g_utf8_strlen(FileTagCur->album, -1)>0 )
+ {
+ FileTag->album = g_strdup(FileTagCur->album);
+ Strip_String(FileTag->album);
+ } else
+ {
+ FileTag->album = NULL;
+ }
+
+
+ /* Disc Number */
+ if ( FileTagCur->disc_number && g_utf8_strlen(FileTagCur->disc_number, -1)>0 )
+ {
+ FileTag->disc_number = g_strdup(FileTagCur->disc_number);
+ Strip_String(FileTag->disc_number);
+ } else
+ {
+ FileTag->disc_number = NULL;
+ }
+
+
+ /* Year */
+ if ( FileTagCur->year && g_utf8_strlen(FileTagCur->year, -1)>0 )
+ {
+ FileTag->year = g_strdup(FileTagCur->year);
+ Strip_String(FileTag->year);
+ } else
+ {
+ FileTag->year = NULL;
+ }
+
+
+ /* Track */
+ if ( FileTagCur->track && g_utf8_strlen(FileTagCur->track, -1)>0 )
+ {
+ gchar *tmp_str;
+
+ if (NUMBER_TRACK_FORMATED)
+ FileTag->track = g_strdup_printf("%.*d",NUMBER_TRACK_FORMATED_SPIN_BUTTON,atoi(FileTagCur->track));
+ else
+ FileTag->track = g_strdup(FileTagCur->track);
+ // This field must contain only digits
+ tmp_str = FileTag->track;
+ while (isdigit((guchar)*tmp_str)) tmp_str++;
+ *tmp_str = 0;
+ Strip_String(FileTag->track);
+ } else
+ {
+ FileTag->track = NULL;
+ }
+
+
+ /* Track Total */
+ if ( FileTagCur->track_total && g_utf8_strlen(FileTagCur->track_total, -1)>0 )
+ {
+ if (NUMBER_TRACK_FORMATED)
+ FileTag->track_total = g_strdup_printf("%.*d",NUMBER_TRACK_FORMATED_SPIN_BUTTON,atoi(FileTagCur->track_total));
+ else
+ FileTag->track_total = g_strdup(FileTagCur->track_total);
+ Strip_String(FileTag->track_total);
+ } else
+ {
+ FileTag->track_total = NULL;
+ }
+
+
+ /* Genre */
+ if ( FileTagCur->genre && g_utf8_strlen(FileTagCur->genre, -1)>0 )
+ {
+ FileTag->genre = g_strdup(FileTagCur->genre);
+ Strip_String(FileTag->genre);
+ } else
+ {
+ FileTag->genre = NULL;
+ }
+
+
+ /* Comment */
+ if ( FileTagCur->comment && g_utf8_strlen(FileTagCur->comment, -1)>0 )
+ {
+ FileTag->comment = g_strdup(FileTagCur->comment);
+ Strip_String(FileTag->comment);
+ } else
+ {
+ FileTag->comment = NULL;
+ }
+
+
+ /* Composer */
+ if ( FileTagCur->composer && g_utf8_strlen(FileTagCur->composer, -1)>0 )
+ {
+ FileTag->composer = g_strdup(FileTagCur->composer);
+ Strip_String(FileTag->composer);
+ } else
+ {
+ FileTag->composer = NULL;
+ }
+
+
+ /* Original Artist */
+ if ( FileTagCur->orig_artist && g_utf8_strlen(FileTagCur->orig_artist, -1)>0 )
+ {
+ FileTag->orig_artist = g_strdup(FileTagCur->orig_artist);
+ Strip_String(FileTag->orig_artist);
+ } else
+ {
+ FileTag->orig_artist = NULL;
+ }
+
+
+ /* Copyright */
+ if ( FileTagCur->copyright && g_utf8_strlen(FileTagCur->copyright, -1)>0 )
+ {
+ FileTag->copyright = g_strdup(FileTagCur->copyright);
+ Strip_String(FileTag->copyright);
+ } else
+ {
+ FileTag->copyright = NULL;
+ }
+
+
+ /* URL */
+ if ( FileTagCur->url && g_utf8_strlen(FileTagCur->url, -1)>0 )
+ {
+ FileTag->url = g_strdup(FileTagCur->url);
+ Strip_String(FileTag->url);
+ } else
+ {
+ FileTag->url = NULL;
+ }
+
+
+ /* Encoded by */
+ if ( FileTagCur->encoded_by && g_utf8_strlen(FileTagCur->encoded_by, -1)>0 )
+ {
+ FileTag->encoded_by = g_strdup(FileTagCur->encoded_by);
+ Strip_String(FileTag->encoded_by);
+ } else
+ {
+ FileTag->encoded_by = NULL;
+ }
+
+
+ /* Picture */
+ if(FileTagCur->picture)
+ {
+ if (FileTag->picture)
+ Picture_Free(FileTag->picture);
+ FileTag->picture = Picture_Copy(FileTagCur->picture);
+ }else if (FileTag->picture)
+ {
+ Picture_Free(FileTag->picture);
+ FileTag->picture = NULL;
+ }
+
+ return TRUE;
+}
+
+
+/*
+ * Save data contained into File_Tag structure to the file on hard disk.
+ */
+gboolean ET_Save_File_Tag_To_HD (ET_File *ETFile)
+{
+ ET_File_Description *ETFileDescription;
+ gchar *cur_filename;
+ gchar *cur_filename_utf8;
+ gboolean state;
+ struct stat statbuf;
+ struct utimbuf utimbufbuf;
+ gboolean file_set_properties;
+
+ if (!ETFile) return FALSE;
+
+ cur_filename = ((File_Name *)(ETFile->FileNameCur)->data)->value;
+ cur_filename_utf8 = ((File_Name *)(ETFile->FileNameCur)->data)->value_utf8;
+
+ ETFileDescription = ETFile->ETFileDescription;
+
+ // Save permissions of the file (cause they may change with files on NFS)
+ if ( stat(cur_filename,&statbuf)!=-1 )
+ file_set_properties = TRUE;
+ else
+ file_set_properties = FALSE;
+
+ switch (ETFileDescription->TagType)
+ {
+#ifdef ENABLE_MP3
+ case ID3_TAG:
+ state = Id3tag_Write_File_Tag(ETFile);
+ break;
+#endif
+#ifdef ENABLE_OGG
+ case OGG_TAG:
+ state = Ogg_Tag_Write_File_Tag(ETFile);
+ break;
+#endif
+#ifdef ENABLE_FLAC
+ case FLAC_TAG:
+ state = Flac_Tag_Write_File_Tag(ETFile);
+ break;
+#endif
+ case APE_TAG:
+ state = Ape_Tag_Write_File_Tag(ETFile);
+ break;
+#ifdef ENABLE_MP4
+ case MP4_TAG:
+ state = Mp4tag_Write_File_Tag(ETFile);
+ break;
+#endif
+#ifdef ENABLE_WAVPACK
+ case WAVPACK_TAG:
+ state = Wavpack_Tag_Write_File_Tag(ETFile);
+ break;
+#endif
+ case UNKNOWN_TAG:
+ default:
+ Log_Print("Saving to HD: Undefined function for tag type '%d' (file %s).",
+ ETFileDescription->TagType,cur_filename_utf8);
+ state = FALSE;
+ break;
+ }
+
+ // Update properties for the file
+ if ( file_set_properties == TRUE )
+ {
+#ifndef WIN32
+ chmod(cur_filename,statbuf.st_mode & (S_IRWXU|S_IRWXG|S_IRWXO));
+ chown(cur_filename,statbuf.st_uid,statbuf.st_gid);
+#endif
+ if (PRESERVE_MODIFICATION_TIME)
+ {
+ utimbufbuf.actime = statbuf.st_atime; // Last access time
+ utimbufbuf.modtime = statbuf.st_mtime; // Last modification time
+ utime(cur_filename,&utimbufbuf);
+ }
+ }
+
+ if (state==TRUE)
+ {
+ ET_Mark_File_Tag_As_Saved(ETFile);
+ return TRUE;
+ }else
+ {
+ return FALSE;
+ }
+}
+
+/*
+ * Function used to update path of filenames into list after renaming a parent directory
+ * (for ex: "/mp3/old_path/file.mp3" to "/mp3/new_path/file.mp3"
+ */
+void ET_Update_Directory_Name_Into_File_List (gchar* last_path, gchar *new_path)
+{
+ GList *filelist;
+ ET_File *file;
+ GList *filenamelist;
+ gchar *filename;
+ gchar *last_path_tmp;
+
+ if (!ETCore->ETFileList || !last_path || !new_path ||
+ strlen(last_path) < 1 || strlen(new_path) < 1 )
+ return;
+
+ // Add '/' to end of path to avoid ambiguity between a directory and a filename...
+ if (last_path[strlen(last_path)-1]==G_DIR_SEPARATOR)
+ last_path_tmp = g_strdup(last_path);
+ else
+ last_path_tmp = g_strconcat(last_path,G_DIR_SEPARATOR_S,NULL);
+
+ filelist = g_list_first(ETCore->ETFileList);
+ while (filelist)
+ {
+ if ( (file=filelist->data) && (filenamelist=file->FileNameList) )
+ {
+ while (filenamelist)
+ {
+ File_Name *FileName = (File_Name *)filenamelist->data;
+
+ if ( FileName && (filename=FileName->value) )
+ {
+ // Replace path of filename
+ if ( strncmp(filename,last_path_tmp,strlen(last_path_tmp))==0 )
+ {
+ gchar *filename_tmp;
+
+ // Create the new filename
+ filename_tmp = g_strconcat(new_path,
+ (new_path[strlen(new_path)-1]==G_DIR_SEPARATOR) ? "" : G_DIR_SEPARATOR_S,
+ &filename[strlen(last_path_tmp)],NULL);
+
+ // Replace the file name (in file system encoding)
+ g_free(FileName->value);
+ FileName->value = filename_tmp;
+ // Replace the file name (in file system encoding)
+ g_free(FileName->value_utf8);
+ FileName->value_utf8 = filename_to_display(FileName->value);
+ // Recalculate the collate key
+ g_free(FileName->value_ck);
+ FileName->value_ck = g_utf8_collate_key_for_filename(FileName->value_utf8, -1);
+ }
+ }
+ filenamelist = g_list_next(filenamelist);
+ }
+ }
+ filelist = g_list_next(filelist);
+ }
+
+ g_free(last_path_tmp);
+}
+
+
+
+/***********************
+ * Undo/Redo functions *
+ ***********************/
+
+/*
+ * Check if 'FileName' and 'FileTag' differ with those of 'ETFile'.
+ * Manage undo feature for the ETFile and the main undo list.
+ */
+gboolean ET_Manage_Changes_Of_File_Data (ET_File *ETFile, File_Name *FileName, File_Tag *FileTag)
+{
+ gboolean undo_added = FALSE;
+
+ if (!ETFile) return FALSE;
+
+ /*
+ * Detect changes of filename and generate the filename undo list
+ */
+ if (FileName)
+ {
+ if ( ETFile->FileNameNew && ET_Detect_Changes_Of_File_Name( (File_Name *)(ETFile->FileNameNew)->data,FileName )==TRUE )
+ {
+ ET_Add_File_Name_To_List(ETFile,FileName);
+ undo_added |= TRUE;
+ }else
+ {
+ ET_Free_File_Name_Item(FileName);
+ }
+ }
+
+ /*
+ * Detect changes in tag data and generate the tag undo list
+ */
+ if (FileTag)
+ {
+ if ( ETFile->FileTag && ET_Detect_Changes_Of_File_Tag( (File_Tag *)(ETFile->FileTag)->data,FileTag )==TRUE )
+ {
+ ET_Add_File_Tag_To_List(ETFile,FileTag);
+ undo_added |= TRUE;
+ }else
+ {
+ ET_Free_File_Tag_Item(FileTag);
+ }
+ }
+
+ /*
+ * Generate main undo (file history of modifications)
+ */
+ if (undo_added)
+ ET_Add_File_To_History_List(ETFile);
+
+ return TRUE; //undo_added;
+}
+
+
+/*
+ * Compares two File_Name items :
+ * - returns TRUE if there aren't the same
+ * - else returns FALSE
+ */
+gboolean ET_Detect_Changes_Of_File_Name (File_Name *FileName1, File_Name *FileName2)
+{
+ gchar *filename1_ck;
+ gchar *filename2_ck;
+
+ if ( (!FileName1 && !FileName2)
+ || (!FileName1->value && !FileName2->value) )
+ return FALSE;
+
+ if ( ( FileName1 && !FileName2)
+ || (!FileName1 && FileName2)
+ || ( FileName1->value && !FileName2->value)
+ || (!FileName1->value && FileName2->value) )
+ return TRUE;
+
+ // Compare collate keys (with FileName->value converted to UTF-8 as it contains raw data)
+ filename1_ck = FileName1->value_ck;
+ filename2_ck = FileName2->value_ck;
+
+ // Filename changed ? (we check path + file)
+ if ( strcmp(filename1_ck,filename2_ck) != 0 )
+ {
+ return TRUE;
+ }else
+ {
+ // No changes
+ return FALSE;
+ }
+}
+
+/*
+ * Compares two File_Tag items and returns TRUE if there aren't the same.
+ * Notes:
+ * - if field is '' or NULL => will be removed
+ */
+gboolean ET_Detect_Changes_Of_File_Tag (File_Tag *FileTag1, File_Tag *FileTag2)
+{
+ Picture *pic1;
+ Picture *pic2;
+
+ if ( !FileTag1 && !FileTag2 )
+ return FALSE;
+
+ if ( ( FileTag1 && !FileTag2)
+ || (!FileTag1 && FileTag2) )
+ return TRUE;
+
+ /* Title */
+ if ( FileTag1->title && !FileTag2->title && g_utf8_strlen(FileTag1->title, -1)>0 ) return TRUE;
+ if (!FileTag1->title && FileTag2->title && g_utf8_strlen(FileTag2->title, -1)>0 ) return TRUE;
+ if ( FileTag1->title && FileTag2->title && g_utf8_collate(FileTag1->title,FileTag2->title)!=0 ) return TRUE;
+
+ /* Artist */
+ if ( FileTag1->artist && !FileTag2->artist && g_utf8_strlen(FileTag1->artist, -1)>0 ) return TRUE;
+ if (!FileTag1->artist && FileTag2->artist && g_utf8_strlen(FileTag2->artist, -1)>0 ) return TRUE;
+ if ( FileTag1->artist && FileTag2->artist && g_utf8_collate(FileTag1->artist,FileTag2->artist)!=0 ) return TRUE;
+
+ /* Album */
+ if ( FileTag1->album && !FileTag2->album && g_utf8_strlen(FileTag1->album, -1)>0 ) return TRUE;
+ if (!FileTag1->album && FileTag2->album && g_utf8_strlen(FileTag2->album, -1)>0 ) return TRUE;
+ if ( FileTag1->album && FileTag2->album && g_utf8_collate(FileTag1->album,FileTag2->album)!=0 ) return TRUE;
+
+ /* Disc Number */
+ if ( FileTag1->disc_number && !FileTag2->disc_number && g_utf8_strlen(FileTag1->disc_number, -1)>0 ) return TRUE;
+ if (!FileTag1->disc_number && FileTag2->disc_number && g_utf8_strlen(FileTag2->disc_number, -1)>0 ) return TRUE;
+ if ( FileTag1->disc_number && FileTag2->disc_number && g_utf8_collate(FileTag1->disc_number,FileTag2->disc_number)!=0 ) return TRUE;
+
+ /* Year */
+ if ( FileTag1->year && !FileTag2->year && g_utf8_strlen(FileTag1->year, -1)>0 ) return TRUE;
+ if (!FileTag1->year && FileTag2->year && g_utf8_strlen(FileTag2->year, -1)>0 ) return TRUE;
+ if ( FileTag1->year && FileTag2->year && g_utf8_collate(FileTag1->year,FileTag2->year)!=0 ) return TRUE;
+
+ /* Track */
+ if ( FileTag1->track && !FileTag2->track && g_utf8_strlen(FileTag1->track, -1)>0 ) return TRUE;
+ if (!FileTag1->track && FileTag2->track && g_utf8_strlen(FileTag2->track, -1)>0 ) return TRUE;
+ if ( FileTag1->track && FileTag2->track && g_utf8_collate(FileTag1->track,FileTag2->track)!=0 ) return TRUE;
+
+ /* Track Total */
+ if ( FileTag1->track_total && !FileTag2->track_total && g_utf8_strlen(FileTag1->track_total, -1)>0 ) return TRUE;
+ if (!FileTag1->track_total && FileTag2->track_total && g_utf8_strlen(FileTag2->track_total, -1)>0 ) return TRUE;
+ if ( FileTag1->track_total && FileTag2->track_total && g_utf8_collate(FileTag1->track_total,FileTag2->track_total)!=0 ) return TRUE;
+
+ /* Genre */
+ if ( FileTag1->genre && !FileTag2->genre && g_utf8_strlen(FileTag1->genre, -1)>0 ) return TRUE;
+ if (!FileTag1->genre && FileTag2->genre && g_utf8_strlen(FileTag2->genre, -1)>0 ) return TRUE;
+ if ( FileTag1->genre && FileTag2->genre && g_utf8_collate(FileTag1->genre,FileTag2->genre)!=0 ) return TRUE;
+
+ /* Comment */
+ if ( FileTag1->comment && !FileTag2->comment && g_utf8_strlen(FileTag1->comment, -1)>0 ) return TRUE;
+ if (!FileTag1->comment && FileTag2->comment && g_utf8_strlen(FileTag2->comment, -1)>0 ) return TRUE;
+ if ( FileTag1->comment && FileTag2->comment && g_utf8_collate(FileTag1->comment,FileTag2->comment)!=0 ) return TRUE;
+
+ /* Composer */
+ if ( FileTag1->composer && !FileTag2->composer && g_utf8_strlen(FileTag1->composer, -1)>0 ) return TRUE;
+ if (!FileTag1->composer && FileTag2->composer && g_utf8_strlen(FileTag2->composer, -1)>0 ) return TRUE;
+ if ( FileTag1->composer && FileTag2->composer && g_utf8_collate(FileTag1->composer,FileTag2->composer)!=0 ) return TRUE;
+
+ /* Original artist */
+ if ( FileTag1->orig_artist && !FileTag2->orig_artist && g_utf8_strlen(FileTag1->orig_artist, -1)>0 ) return TRUE;
+ if (!FileTag1->orig_artist && FileTag2->orig_artist && g_utf8_strlen(FileTag2->orig_artist, -1)>0 ) return TRUE;
+ if ( FileTag1->orig_artist && FileTag2->orig_artist && g_utf8_collate(FileTag1->orig_artist,FileTag2->orig_artist)!=0 ) return TRUE;
+
+ /* Copyright */
+ if ( FileTag1->copyright && !FileTag2->copyright && g_utf8_strlen(FileTag1->copyright, -1)>0 ) return TRUE;
+ if (!FileTag1->copyright && FileTag2->copyright && g_utf8_strlen(FileTag2->copyright, -1)>0 ) return TRUE;
+ if ( FileTag1->copyright && FileTag2->copyright && g_utf8_collate(FileTag1->copyright,FileTag2->copyright)!=0 ) return TRUE;
+
+ /* URL */
+ if ( FileTag1->url && !FileTag2->url && g_utf8_strlen(FileTag1->url, -1)>0 ) return TRUE;
+ if (!FileTag1->url && FileTag2->url && g_utf8_strlen(FileTag2->url, -1)>0 ) return TRUE;
+ if ( FileTag1->url && FileTag2->url && g_utf8_collate(FileTag1->url,FileTag2->url)!=0 ) return TRUE;
+
+ /* Encoded by */
+ if ( FileTag1->encoded_by && !FileTag2->encoded_by && g_utf8_strlen(FileTag1->encoded_by, -1)>0 ) return TRUE;
+ if (!FileTag1->encoded_by && FileTag2->encoded_by && g_utf8_strlen(FileTag2->encoded_by, -1)>0 ) return TRUE;
+ if ( FileTag1->encoded_by && FileTag2->encoded_by && g_utf8_collate(FileTag1->encoded_by,FileTag2->encoded_by)!=0 ) return TRUE;
+
+ /* Picture */
+ pic1 = FileTag1->picture;
+ pic2 = FileTag2->picture;
+
+ for(;;)
+ {
+ if( (pic1 && !pic2) || (!pic1 && pic2) )
+ return TRUE;
+ if (!pic1 || !pic2)
+ break; // => no changes
+ //if (!pic1->data || !pic2->data)
+ // break; // => no changes
+
+ if (pic1->data && !pic2->data)
+ return TRUE;
+ if (!pic1->data && pic2->data)
+ return TRUE;
+ if (pic1->size != pic2->size
+ || memcmp(pic1->data, pic2->data, pic1->size))
+ return TRUE;
+ if (pic1->type != pic2->type)
+ return TRUE;
+ if (pic1->description && !pic2->description
+ && g_utf8_strlen(pic1->description, -1)>0 )
+ return TRUE;
+ if (!pic1->description && pic2->description
+ && g_utf8_strlen(pic2->description, -1)>0 )
+ return TRUE;
+ if (pic1->description && pic2->description
+ && g_utf8_collate(pic1->description, pic2->description)!=0 )
+ return TRUE;
+
+ pic1 = pic1->next;
+ pic2 = pic2->next;
+ }
+
+ return FALSE; /* No changes */
+}
+
+
+/*
+ * Add a FileName item to the history list of ETFile
+ */
+gboolean ET_Add_File_Name_To_List (ET_File *ETFile, File_Name *FileName)
+{
+ GList *cut_list = NULL;
+
+ if (!ETFile || !FileName)
+ return FALSE;
+
+ /* How it works : Cut the FileNameList list after the current item,
+ * and appends it to the FileNameListBak list for saving the data.
+ * Then appends the new item to the FileNameList list */
+ if (ETFile->FileNameList)
+ {
+ cut_list = ETFile->FileNameNew->next; // Cut after the current item...
+ ETFile->FileNameNew->next = NULL;
+ }
+ if (cut_list)
+ cut_list->prev = NULL;
+
+ /* Add the new item to the list */
+ ETFile->FileNameList = g_list_append(ETFile->FileNameList,FileName);
+ /* Set the current item to use */
+ ETFile->FileNameNew = g_list_last(ETFile->FileNameList);
+ /* Backup list */
+ /* FIX ME! Keep only the saved item */
+ ETFile->FileNameListBak = g_list_concat(ETFile->FileNameListBak,cut_list);
+
+ return TRUE;
+}
+
+/*
+ * Add a FileTag item to the history list of ETFile
+ */
+gboolean ET_Add_File_Tag_To_List (ET_File *ETFile, File_Tag *FileTag)
+{
+ GList *cut_list = NULL;
+
+ if (!ETFile || !FileTag)
+ return FALSE;
+
+ if (ETFile->FileTag)
+ {
+ cut_list = ETFile->FileTag->next; // Cut after the current item...
+ ETFile->FileTag->next = NULL;
+ }
+ if (cut_list)
+ cut_list->prev = NULL;
+
+ /* Add the new item to the list */
+ ETFile->FileTagList = g_list_append(ETFile->FileTagList,FileTag);
+ /* Set the current item to use */
+ ETFile->FileTag = g_list_last(ETFile->FileTagList);
+ /* Backup list */
+ ETFile->FileTagListBak = g_list_concat(ETFile->FileTagListBak,cut_list);
+
+ return TRUE;
+}
+
+/*
+ * Add a ETFile item to the main undo list of files
+ */
+gboolean ET_Add_File_To_History_List (ET_File *ETFile)
+{
+ ET_History_File *ETHistoryFile;
+
+ if (!ETFile) return FALSE;
+
+ ETHistoryFile = g_malloc0(sizeof(ET_History_File));
+ ETHistoryFile->ETFile = ETFile;
+
+ /* The undo list must contains one item before the 'first undo' data */
+ if (!ETCore->ETHistoryFileList)
+ ETCore->ETHistoryFileList = g_list_append(ETCore->ETHistoryFileList,g_malloc0(sizeof(ET_History_File)));
+
+ /* Add the item to the list (cut end of list from the current element) */
+ ETCore->ETHistoryFileList = g_list_append(ETCore->ETHistoryFileList,ETHistoryFile);
+ ETCore->ETHistoryFileList = g_list_last(ETCore->ETHistoryFileList);
+
+ return TRUE;
+}
+
+
+/*
+ * Applies one undo to the ETFile data (to reload the previous data).
+ * Returns TRUE if an undo had been applied.
+ */
+gboolean ET_Undo_File_Data (ET_File *ETFile)
+{
+ gboolean has_filename_undo_data = FALSE;
+ gboolean has_filetag_undo_data = FALSE;
+ guint filename_key, filetag_key, undo_key;
+
+ if (!ETFile)
+ return FALSE;
+
+ /* Find the valid key */
+ if (ETFile->FileNameNew->prev && ETFile->FileNameNew->data)
+ filename_key = ((File_Name *)ETFile->FileNameNew->data)->key;
+ else
+ filename_key = 0;
+ if (ETFile->FileTag->prev && ETFile->FileTag->data)
+ filetag_key = ((File_Tag *)ETFile->FileTag->data)->key;
+ else
+ filetag_key = 0;
+ // The key to use
+ undo_key = MAX(filename_key,filetag_key);
+
+ /* Undo filename */
+ if (ETFile->FileNameNew->prev && ETFile->FileNameNew->data
+ && (undo_key==((File_Name *)ETFile->FileNameNew->data)->key))
+ {
+ ETFile->FileNameNew = ETFile->FileNameNew->prev;
+ has_filename_undo_data = TRUE; // To indicate that an undo has been applied
+ }
+
+ /* Undo tag data */
+ if (ETFile->FileTag->prev && ETFile->FileTag->data
+ && (undo_key==((File_Tag *)ETFile->FileTag->data)->key))
+ {
+ ETFile->FileTag = ETFile->FileTag->prev;
+ has_filetag_undo_data = TRUE;
+ }
+
+ return has_filename_undo_data | has_filetag_undo_data;
+}
+
+
+/*
+ * Returns TRUE if file contains undo data (filename or tag)
+ */
+gboolean ET_File_Data_Has_Undo_Data (ET_File *ETFile)
+{
+ gboolean has_filename_undo_data = FALSE;
+ gboolean has_filetag_undo_data = FALSE;
+
+ if (!ETFile) return FALSE;
+
+ if (ETFile->FileNameNew && ETFile->FileNameNew->prev) has_filename_undo_data = TRUE;
+ if (ETFile->FileTag && ETFile->FileTag->prev) has_filetag_undo_data = TRUE;
+
+ return has_filename_undo_data | has_filetag_undo_data;
+}
+
+
+/*
+ * Applies one redo to the ETFile data. Returns TRUE if a redo had been applied.
+ */
+gboolean ET_Redo_File_Data (ET_File *ETFile)
+{
+ gboolean has_filename_redo_data = FALSE;
+ gboolean has_filetag_redo_data = FALSE;
+ guint filename_key, filetag_key, undo_key;
+
+ if (!ETFile)
+ return FALSE;
+
+ /* Find the valid key */
+ if (ETFile->FileNameNew->next && ETFile->FileNameNew->next->data)
+ filename_key = ((File_Name *)ETFile->FileNameNew->next->data)->key;
+ else
+ filename_key = (guint)~0; // To have the max value for guint
+ if (ETFile->FileTag->next && ETFile->FileTag->next->data)
+ filetag_key = ((File_Tag *)ETFile->FileTag->next->data)->key;
+ else
+ filetag_key = (guint)~0; // To have the max value for guint
+ // The key to use
+ undo_key = MIN(filename_key,filetag_key);
+
+ /* Redo filename */
+ if (ETFile->FileNameNew->next && ETFile->FileNameNew->next->data
+ && (undo_key==((File_Name *)ETFile->FileNameNew->next->data)->key))
+ {
+ ETFile->FileNameNew = ETFile->FileNameNew->next;
+ has_filename_redo_data = TRUE; // To indicate that a redo has been applied
+ }
+
+ /* Redo tag data */
+ if (ETFile->FileTag->next && ETFile->FileTag->next->data
+ && (undo_key==((File_Tag *)ETFile->FileTag->next->data)->key))
+ {
+ ETFile->FileTag = ETFile->FileTag->next;
+ has_filetag_redo_data = TRUE;
+ }
+
+ return has_filename_redo_data | has_filetag_redo_data;
+}
+
+
+/*
+ * Returns TRUE if file contains redo data (filename or tag)
+ */
+gboolean ET_File_Data_Has_Redo_Data (ET_File *ETFile)
+{
+ gboolean has_filename_redo_data = FALSE;
+ gboolean has_filetag_redo_data = FALSE;
+
+ if (!ETFile) return FALSE;
+
+ if (ETFile->FileNameNew && ETFile->FileNameNew->next) has_filename_redo_data = TRUE;
+ if (ETFile->FileTag && ETFile->FileTag->next) has_filetag_redo_data = TRUE;
+
+ return has_filename_redo_data | has_filetag_redo_data;
+}
+
+
+/*
+ * Execute one 'undo' in the main undo list (it selects the last ETFile changed,
+ * before to apply an undo action)
+ */
+ET_File *ET_Undo_History_File_Data (void)
+{
+ ET_File *ETFile;
+ ET_History_File *ETHistoryFile;
+
+ if (!ETCore->ETHistoryFileList || !ET_History_File_List_Has_Undo_Data()) return NULL;
+
+ ETHistoryFile = (ET_History_File *)ETCore->ETHistoryFileList->data;
+ ETFile = (ET_File *)ETHistoryFile->ETFile;
+ ET_Displayed_File_List_By_Etfile(ETFile);
+ ET_Undo_File_Data(ETFile);
+
+ if (ETCore->ETHistoryFileList->prev)
+ ETCore->ETHistoryFileList = ETCore->ETHistoryFileList->prev;
+ return ETFile;
+}
+
+
+/*
+ * Returns TRUE if undo file list contains undo data
+ */
+gboolean ET_History_File_List_Has_Undo_Data (void)
+{
+ if (ETCore->ETHistoryFileList && ETCore->ETHistoryFileList->prev)
+ return TRUE;
+ else
+ return FALSE;
+}
+
+
+/*
+ * Execute one 'redo' in the main undo list
+ */
+ET_File *ET_Redo_History_File_Data (void)
+{
+ ET_File *ETFile;
+ ET_History_File *ETHistoryFile;
+
+ if (!ETCore->ETHistoryFileList || !ET_History_File_List_Has_Redo_Data()) return NULL;
+
+ ETHistoryFile = (ET_History_File *)ETCore->ETHistoryFileList->next->data;
+ ETFile = (ET_File *)ETHistoryFile->ETFile;
+ ET_Displayed_File_List_By_Etfile(ETFile);
+ ET_Redo_File_Data(ETFile);
+
+ if (ETCore->ETHistoryFileList->next)
+ ETCore->ETHistoryFileList = ETCore->ETHistoryFileList->next;
+ return ETFile;
+}
+
+
+/*
+ * Returns TRUE if undo file list contains redo data
+ */
+gboolean ET_History_File_List_Has_Redo_Data (void)
+{
+ if (ETCore->ETHistoryFileList && ETCore->ETHistoryFileList->next)
+ return TRUE;
+ else
+ return FALSE;
+}
+
+
+
+
+/**********************
+ * Checking functions *
+ **********************/
+
+
+/*
+ * Ckecks if the current files had been changed but not saved.
+ * Returns TRUE if the file has been saved.
+ * Returns FALSE if some changes haven't been saved.
+ */
+gboolean ET_Check_If_File_Is_Saved (ET_File *ETFile)
+{
+ File_Tag *FileTag = NULL;
+ File_Name *FileNameNew = NULL;
+
+ if (!ETFile) return TRUE;
+
+ if (ETFile->FileTag)
+ FileTag = ETFile->FileTag->data;
+ if (ETFile->FileNameNew)
+ FileNameNew = ETFile->FileNameNew->data;
+
+ // Check if the tag has been changed
+ if ( FileTag && FileTag->saved != TRUE )
+ return FALSE;
+
+ // Check if name of file has been changed
+ if ( FileNameNew && FileNameNew->saved != TRUE )
+ return FALSE;
+
+ // No changes
+ return TRUE;
+}
+
+
+/*
+ * Ckecks if some files, in the list, had been changed but not saved.
+ * Returns TRUE if all files have been saved.
+ * Returns FALSE if some changes haven't been saved.
+ */
+gboolean ET_Check_If_All_Files_Are_Saved (void)
+{
+ /* Check if some files haven't been saved, if didn't nochange=0 */
+ if (!ETCore->ETFileList)
+ {
+ return TRUE;
+ }else
+ {
+ GList *tmplist = g_list_first(ETCore->ETFileList);
+ while (tmplist)
+ {
+ if ( ET_Check_If_File_Is_Saved((ET_File *)tmplist->data) == FALSE )
+ return FALSE;
+
+ if (!tmplist->next) break;
+ tmplist = g_list_next(tmplist);
+ }
+ return TRUE;
+ }
+}
+
+
+
+
+/*******************
+ * Extra functions *
+ *******************/
+
+/*
+ * Set to TRUE the value of 'FileTag->saved' for the File_Tag item passed in parameter.
+ * And set ALL other values of the list to FALSE.
+ */
+void Set_Saved_Value_Of_File_Tag (File_Tag *FileTag, gboolean saved)
+{
+ if (FileTag) FileTag->saved = saved;
+}
+void ET_Mark_File_Tag_As_Saved (ET_File *ETFile)
+{
+ File_Tag *FileTag;
+ GList *FileTagList;
+
+ FileTag = (File_Tag *)ETFile->FileTag->data; // The current FileTag, to set to TRUE
+ FileTagList = ETFile->FileTagList;
+ g_list_foreach(FileTagList,(GFunc)Set_Saved_Value_Of_File_Tag,FALSE); // All other FileTag set to FALSE
+ FileTag->saved = TRUE; // The current FileTag set to TRUE
+}
+
+
+/*
+ * Set to TRUE the value of 'FileName->saved' for the File_Name item passed in parameter.
+ * And set ALL other values of the list to FALSE.
+ */
+void Set_Saved_Value_Of_File_Name (File_Name *FileName, gboolean saved)
+{
+ if (FileName) FileName->saved = saved;
+}
+void ET_Mark_File_Name_As_Saved (ET_File *ETFile)
+{
+ File_Name *FileNameNew;
+ GList *FileNameList;
+
+ FileNameNew = (File_Name *)ETFile->FileNameNew->data; // The current FileName, to set to TRUE
+ FileNameList = ETFile->FileNameList;
+ g_list_foreach(FileNameList,(GFunc)Set_Saved_Value_Of_File_Tag,FALSE);
+ FileNameNew->saved = TRUE;
+}
+
+
+/*
+ * Currently, it's a way by default to fill file size into ET_File_Info structure
+ */
+gboolean ET_Read_File_Info (gchar *filename, ET_File_Info *ETFileInfo)
+{
+ FILE *file;
+
+ if (!filename || !ETFileInfo)
+ return FALSE;
+
+ if ( (file=fopen(filename,"r"))==NULL )
+ {
+ gchar *filename_utf8 = filename_to_display(filename);
+ Log_Print(_("ERROR while opening file: '%s' (%s)."),filename_utf8,g_strerror(errno));
+ g_free(filename_utf8);
+ return FALSE;
+ }
+ fclose(file);
+
+ ETFileInfo->version = 0;
+ ETFileInfo->bitrate = 0;
+ ETFileInfo->samplerate = 0;
+ ETFileInfo->mode = 0;
+ ETFileInfo->size = Get_File_Size(filename);
+ ETFileInfo->duration = 0;
+
+ return TRUE;
+}
+
+
+/*
+ * This function generates a new file name using path of the old file and the new name
+ * - ETFile -> old_file_name : "/path_to_file/old_name.ext"
+ * - new_file_name_utf8 : "new_name.ext"
+ * Returns "/path_to_file/new_name.ext" into allocated data (in UTF-8)!
+ * Notes :
+ * - filenames (basemane) musn't exceed 255 characters (i.e. : "new_name.ext")
+ * - ogg filename musn't exceed 255-6 characters as we use mkstemp
+ */
+#if 1
+gchar *ET_File_Name_Generate (ET_File *ETFile, gchar *new_file_name_utf8)
+{
+ gchar *dirname_utf8;
+
+ if (ETFile && ETFile->FileNameNew->data && new_file_name_utf8
+ && (dirname_utf8=g_path_get_dirname(((File_Name *)ETFile->FileNameNew->data)->value_utf8)) )
+ {
+ gchar *extension;
+ gchar *new_file_name_path_utf8;
+
+ // Convert filename extension (lower/upper)
+ extension = ET_File_Name_Format_Extension(ETFile);
+
+ // Check length of filename (limit ~255 characters)
+ //ET_File_Name_Check_Length(ETFile,new_file_name_utf8);
+
+ // If filemame starts with /, it's a full filename with path but without extension
+ if (g_path_is_absolute(new_file_name_utf8))
+ {
+ // We just add the extension
+ new_file_name_path_utf8 = g_strconcat(new_file_name_utf8,extension,NULL);
+ }else
+ {
+ // New path (with filename)
+ if ( strcmp(dirname_utf8,G_DIR_SEPARATOR_S)==0 ) // Root directory?
+ new_file_name_path_utf8 = g_strconcat(dirname_utf8,new_file_name_utf8,extension,NULL);
+ else
+ new_file_name_path_utf8 = g_strconcat(dirname_utf8,G_DIR_SEPARATOR_S,new_file_name_utf8,extension,NULL);
+ }
+
+ g_free(extension);
+ return new_file_name_path_utf8; // in UTF-8
+ }else
+ {
+ return NULL;
+ }
+}
+#else
+/* FOR TESTING */
+/* Returns filename in file system encoding */
+gchar *ET_File_Name_Generate (ET_File *ETFile, gchar *new_file_name_utf8)
+{
+ gchar *dirname;
+
+ if (ETFile && ETFile->FileNameNew->data && new_file_name_utf8
+ && (dirname=g_path_get_dirname(((File_Name *)ETFile->FileNameNew->data)->value)) )
+ {
+ gchar *extension;
+ gchar *new_file_name_path;
+ gchar *new_file_name;
+
+ new_file_name = filename_from_display(new_file_name_utf8);
+
+ // Convert filename extension (lower/upper)
+ extension = ET_File_Name_Format_Extension(ETFile);
+
+ // Check length of filename (limit ~255 characters)
+ //ET_File_Name_Check_Length(ETFile,new_file_name_utf8);
+
+ // If filemame starts with /, it's a full filename with path but without extension
+ if (g_path_is_absolute(new_file_name))
+ {
+ // We just add the extension
+ new_file_name_path = g_strconcat(new_file_name,extension,NULL);
+ }else
+ {
+ // New path (with filename)
+ if ( strcmp(dirname,G_DIR_SEPARATOR_S)==0 ) // Root directory?
+ new_file_name_path = g_strconcat(dirname,new_file_name,extension,NULL);
+ else
+ new_file_name_path = g_strconcat(dirname,G_DIR_SEPARATOR_S,new_file_name,extension,NULL);
+ }
+
+ g_free(dirname);
+ g_free(new_file_name);
+ g_free(extension);
+ return new_file_name_path; // in file system encoding
+ }else
+ {
+ return NULL;
+ }
+}
+#endif
+
+
+gchar *ET_File_Name_Format_Extension (ET_File *ETFile)
+{
+ // Convert filename extension (lower/upper/no change)
+ if (FILENAME_EXTENSION_LOWER_CASE)
+ return g_utf8_strdown(ETFile->ETFileDescription->Extension,-1);
+
+ else if (FILENAME_EXTENSION_UPPER_CASE)
+ return g_utf8_strup(ETFile->ETFileDescription->Extension,-1);
+
+ else // FILENAME_EXTENSION_NO_CHANGE
+ return g_strdup(ETFile->ETFileExtension);
+}
+
+
+/*
+ * Check if the basename+extension of the file doesn't exceed following limits :
+ * - ogg filenames musn't exceed 255-6 characters (because we use mkstemp)
+ * - other filenames musn't exceed 255 characters
+ * Parameters:
+ * - 'filename_utf8' filename without the extension
+ */
+void ET_File_Name_Check_Length (ET_File *ETFile, gchar *filename_utf8)
+{
+ ET_File_Description *ETFileDescription;
+ gchar *basename;
+ gint exceed_size;
+
+
+ if (!ETFile || !filename_utf8) return;
+
+ basename = g_path_get_basename(filename_utf8); // If it contains directories...
+
+ ETFileDescription = ETFile->ETFileDescription;
+ switch (ETFileDescription->FileType)
+ {
+#ifdef ENABLE_OGG
+ case OGG_FILE:
+ if ( (exceed_size = (strlen(basename) - 245)) > 0 ) // 255 - 4 (extension) - 6 (mkstemp)
+ {
+ Log_Print(_("The filename '%s' exceeds %d characters and will be truncated!\n"), filename_utf8, 245);
+ filename_utf8[strlen(filename_utf8) - exceed_size] = '\0';
+ }
+ break;
+#endif
+ default:
+ if ( (exceed_size = (strlen(basename) - 251)) > 0 ) // 255 - 4 (extension)
+ {
+ Log_Print(_("The filename '%s' exceeds %d characters and will be truncated!\n"), filename_utf8, 251);
+ filename_utf8[strlen(filename_utf8) - exceed_size] = '\0';
+ }
+ break;
+ }
+ g_free(basename);
+}
+
+
+/*
+ * Used to replace the illegal characters in the filename
+ * Paremeter 'filename' musn't contain the path, else directories separators would be replaced!
+ */
+gboolean ET_File_Name_Convert_Character (gchar *filename_utf8)
+{
+ gchar *character;
+
+ if (!filename_utf8)
+ return FALSE;
+
+ // Convert automatically the directory separator ('/' on LINUX and '\' on WIN32) to '-'.
+ while ( (character=g_utf8_strchr(filename_utf8, -1, G_DIR_SEPARATOR))!=NULL )
+ *character = '-';
+
+#ifdef WIN32
+ // Convert character '\' on WIN32 to '-'.
+ while ( (character=g_utf8_strchr(filename_utf8, -1, '\\'))!=NULL )
+ *character = '-';
+ // Convert character '/' on WIN32 to '-'. (May be converted to '\', after...)
+ while ( (character=g_utf8_strchr(filename_utf8, -1, '/'))!=NULL )
+ *character = '-';
+#endif
+
+ // Convert other illegal characters on FAT32/16 filesystems and ISO9660 and Joliet (CD-ROM filesystems)
+ if (REPLACE_ILLEGAL_CHARACTERS_IN_FILENAME)
+ {
+ // Commented as we display unicode values as "\351" for "é"
+ //while ( (character=g_utf8_strchr(filename_utf8, -1, '\\'))!=NULL )
+ // *character = ',';
+ while ( (character=g_utf8_strchr(filename_utf8, -1, ':'))!=NULL )
+ *character = '-';
+ //while ( (character=g_utf8_strchr(filename_utf8, -1, ';'))!=NULL )
+ // *character = '-';
+ while ( (character=g_utf8_strchr(filename_utf8, -1, '*'))!=NULL )
+ *character = '+';
+ while ( (character=g_utf8_strchr(filename_utf8, -1, '?'))!=NULL )
+ *character = '_';
+ while ( (character=g_utf8_strchr(filename_utf8, -1, '\"'))!=NULL )
+ *character = '\'';
+ while ( (character=g_utf8_strchr(filename_utf8, -1, '<'))!=NULL )
+ *character = '(';
+ while ( (character=g_utf8_strchr(filename_utf8, -1, '>'))!=NULL )
+ *character = ')';
+ while ( (character=g_utf8_strchr(filename_utf8, -1, '|'))!=NULL )
+ *character = '-';
+ }
+ return TRUE;
+}
+
+
+/*
+ * Returns the number of file in the directory of the selected file.
+ * Parameter "path" should be in UTF-8
+ */
+guint ET_Get_Number_Of_Files_In_Directory (gchar *path_utf8)
+{
+ GList *etfilelist;
+ guint count = 0;
+
+ if (!path_utf8) return count;
+
+ etfilelist = g_list_first(ETCore->ETFileList);
+ while (etfilelist)
+ {
+ ET_File *ETFile = (ET_File *)etfilelist->data;
+ gchar *cur_filename_utf8 = ((File_Name *)((GList *)ETFile->FileNameCur)->data)->value_utf8;
+ gchar *dirname_utf8 = g_path_get_dirname(cur_filename_utf8);
+
+ if (g_utf8_collate(dirname_utf8, path_utf8) == 0)
+ count++;
+
+ g_free(dirname_utf8 );
+
+ etfilelist = etfilelist->next;
+ }
+
+ return count;
+}
+
+/*
+ * Try to valide a full string : if some characters are invalid, we skip them to
+ * return a string with only the valid characters.
+ */
+gchar *ET_Utf8_Validate_Full_String (gchar *string_to_validate)
+{
+ gchar *stv;
+ gchar *stv_end;
+ gchar *tmp, *tmp1;
+
+ if (!string_to_validate)
+ return NULL;
+
+ stv = g_strdup(string_to_validate);
+ // Remove all invalid character
+ while ( !g_utf8_validate(stv,-1,(const gchar **)&stv_end) )
+ {
+ // Not UTF-8 validated
+ tmp = tmp1 = stv_end;
+ tmp++;
+ while (*tmp)
+ {
+ if (*tmp)
+ *(tmp1++) = *(tmp++);
+ }
+ *tmp1 = '\0';
+ }
+ /*
+ // Truncate at first invalid character
+ if ( !g_utf8_validate(stv,-1,(const gchar **)&stv_end) )
+ {
+ // Not UTF-8 validated
+ *stv_end = '\0';
+ }*/
+
+ return stv;
+}
+
+/***********************
+ * Debugging functions *
+ ***********************/
+
+void ET_Debug_Print_File (ET_File *ETFile, gchar *file, gint line, gchar *function)
+{
+ GList *etfilelist = NULL;
+
+ if (ETFile)
+ etfilelist = g_list_append(etfilelist,ETFile);
+ ET_Debug_Print_File_List(etfilelist,file,line,function);
+ g_list_free(etfilelist);
+}
+
+
+/*
+ * Functions for DEBUGGING ONLY
+ * ET_Print_File_List => show list of filename
+ * Parameters: ETFileList : the list of files to display
+ * file = __FILE__
+ * line = __LINE__
+ * function = __FUNCTION__
+ */
+void ET_Debug_Print_File_List (GList *ETFileList, gchar *file, gint line, gchar *function)
+{
+ gint efl_item = 1;
+ gint fnl_item = 1;
+ gint ftl_item = 1;
+ gint etfilelist_length;
+ gint filenamelist_length;
+ gint filetaglist_length;
+ GList *etfilelist;
+ GList *filenamelist;
+ GList *filetaglist;
+
+
+ g_print("\n#### File list from %s:%d - start ####\n",file,line);
+ g_print("#### Function : %s ####\n",function);
+ if (ETFileList)
+ etfilelist = g_list_first(ETFileList);
+ else
+ etfilelist = g_list_first(ETCore->ETFileList);
+ etfilelist_length = g_list_length(etfilelist);
+ while (etfilelist)
+ {
+ g_print("#> ETFile %d/%d (%p)\n",efl_item,etfilelist_length,(ET_File *)etfilelist->data);
+ g_print("|--- IndexKey : '%d'\n",((ET_File *)etfilelist->data)->IndexKey);
+ g_print("|--- file_cur : '%s'\n",((File_Name *)((ET_File *)etfilelist->data)->FileNameCur->data)->value_utf8);
+ g_print("|--- file_new : '%s'\n",((File_Name *)((ET_File *)etfilelist->data)->FileNameNew->data)->value_utf8);
+ g_print("|--- saved : '%d'\n",((File_Name *)((ET_File *)etfilelist->data)->FileNameNew->data)->saved);
+
+ filenamelist = g_list_first( ((ET_File *)etfilelist->data)->FileNameList );
+ filenamelist_length = g_list_length(filenamelist);
+ fnl_item = 1;
+ while (filenamelist)
+ {
+ g_print("|--> File_Name : %d/%d %s\n",fnl_item,filenamelist_length,(filenamelist==((ET_File *)etfilelist->data)->FileNameNew)?"<<CURRENT>>":"");
+ g_print("| |-> key : '%d'\n",((File_Name *)filenamelist->data)->key);
+ g_print("| |-> filename: '%s'\n",((File_Name *)filenamelist->data)->value_utf8);
+
+ filenamelist = filenamelist->next;
+ fnl_item++;
+ }
+
+ g_print("|\n");
+
+ filetaglist = g_list_first( ((ET_File *)etfilelist->data)->FileTagList );
+ filetaglist_length = g_list_length(filetaglist);
+ ftl_item = 1;
+ while (filetaglist)
+ {
+ g_print("|--> File_Tag : %d/%d %s\n",ftl_item,filetaglist_length,(filetaglist==((ET_File *)etfilelist->data)->FileTag)?"<<CURRENT>>":"");
+ g_print("| |-> key : '%d'\n",((File_Tag *)filetaglist->data)->key);
+ g_print("| |-> saved : '%d'\n",((File_Tag *)filetaglist->data)->saved);
+ g_print("| |-> title : '%s'\n",((File_Tag *)filetaglist->data)->title);
+ g_print("| |-> artist : '%s'\n",((File_Tag *)filetaglist->data)->artist);
+ g_print("| |-> album : '%s'\n",((File_Tag *)filetaglist->data)->album);
+ g_print("| |-> disc_number : '%s'\n",((File_Tag *)filetaglist->data)->disc_number);
+ g_print("| |-> year : '%s'\n",((File_Tag *)filetaglist->data)->year);
+ g_print("| |-> track : '%s'\n",((File_Tag *)filetaglist->data)->track);
+ g_print("| |-> track_total : '%s'\n",((File_Tag *)filetaglist->data)->track_total);
+ g_print("| |-> genre : '%s'\n",((File_Tag *)filetaglist->data)->genre);
+ g_print("| |-> comment : '%s'\n",((File_Tag *)filetaglist->data)->comment);
+ g_print("| |-> composer : '%s'\n",((File_Tag *)filetaglist->data)->composer);
+ g_print("| |-> orig_artist : '%s'\n",((File_Tag *)filetaglist->data)->orig_artist);
+ g_print("| |-> copyright : '%s'\n",((File_Tag *)filetaglist->data)->copyright);
+ g_print("| |-> url : '%s'\n",((File_Tag *)filetaglist->data)->url);
+ g_print("| |-> encoded_by : '%s'\n",((File_Tag *)filetaglist->data)->encoded_by);
+
+ filetaglist = filetaglist->next;
+ ftl_item++;
+ }
+
+ g_print("|\n");
+
+ etfilelist = etfilelist->next;
+ efl_item++;
+ }
+ g_print("#### File list from %s:%d - end ####\n",file,line);
+}
+
+// Ex : ET_Debug_Print_Artist_Album_List(__FILE__,__LINE__,__FUNCTION__);
+void ET_Debug_Print_Artist_Album_List (gchar *file, gint line, gchar *function)
+{
+ GList *ArtistList;
+ GList *AlbumList;
+ GList *etfilelist;
+ ET_File *etfile;
+ gint artist_item;
+ gint artist_length;
+ gint album_item;
+ gint album_length;
+ gint etfile_item;
+ gint etfile_length;
+
+ g_print("\n#### Artist Album list from %s:%d - start ####\n",file,line);
+ g_print("#### Function : %s ####\n",function);
+ ArtistList = g_list_first(ETCore->ETArtistAlbumFileList);
+ artist_length = g_list_length(ArtistList);
+ artist_item = 1;
+ while (ArtistList)
+ {
+ g_print("#> Artist %d/%d \n",artist_item,artist_length);
+ AlbumList = g_list_first((GList *)ArtistList->data);
+ album_length = g_list_length(AlbumList);
+ album_item = 1;
+ while (AlbumList)
+ {
+ g_print(" |-> Album %d/%d \n",album_item,album_length);
+ etfilelist = g_list_first((GList *)AlbumList->data);
+ etfile_length = g_list_length(etfilelist);
+ etfile_item = 1;
+ while (etfilelist)
+ {
+ etfile = (ET_File *)etfilelist->data;
+ g_print(" | |-> ETFile %d/%d (%p)\n",etfile_item,etfile_length,etfile);
+ g_print(" | | |-> file : '%s'\n",((File_Name *)etfile->FileNameCur->data)->value_utf8);
+ g_print(" | | |-> artist : '%s'\n",((File_Tag *)etfile->FileTag->data)->artist);
+ g_print(" | | |-> album : '%s'\n",((File_Tag *)etfile->FileTag->data)->album);
+
+ etfilelist = etfilelist->next;
+ etfile_item++;
+ }
+
+ AlbumList = AlbumList->next;
+ album_item++;
+ }
+
+ ArtistList = ArtistList->next;
+ artist_item++;
+ }
+ g_print("#### Artist Album list from %s:%d - end ####\n",file,line);
+}
+
+
+#include "time.h"
+void ET_Debug_Print_Time (gchar *msg)
+{
+ struct tm *tms;
+ time_t nowtime;
+ char str[50];
+ //char *pstr = str;
+
+ nowtime = time(NULL);
+ tms = localtime(&nowtime);
+ strftime(str,sizeof(str),"%X",tms); // Time without date in current locale
+ //strftime(str,sizeof(str),"%x",ptr); // Date without time in current locale
+
+ if (msg)
+ {
+ g_print("## %s %s ##\n",msg,str);
+ }else
+ {
+ g_print("## %s ##\n",str);
+ }
+}