aboutsummaryrefslogtreecommitdiffstats
path: root/src/base/USongs.pas
diff options
context:
space:
mode:
Diffstat (limited to 'src/base/USongs.pas')
-rw-r--r--src/base/USongs.pas419
1 files changed, 221 insertions, 198 deletions
diff --git a/src/base/USongs.pas b/src/base/USongs.pas
index c4871f18..49b84425 100644
--- a/src/base/USongs.pas
+++ b/src/base/USongs.pas
@@ -40,32 +40,35 @@ interface
{$ENDIF}
uses
+ SysUtils,
+ Classes,
{$IFDEF MSWINDOWS}
Windows,
DirWatch,
{$ELSE}
{$IFNDEF DARWIN}
- syscall,
+ syscall,
{$ENDIF}
baseunix,
UnixType,
{$ENDIF}
- SysUtils,
- Classes,
UPlatform,
ULog,
UTexture,
UCommon,
- {$IFDEF DARWIN}
- cthreads,
- {$ENDIF}
{$IFDEF USE_PSEUDO_THREAD}
- PseudoThread,
+ PseudoThread,
{$ENDIF}
+ UPath,
USong,
UCatCovers;
type
+ TSongFilter = (
+ fltAll,
+ fltTitle,
+ fltArtist
+ );
TBPM = record
BPM: real;
@@ -73,11 +76,13 @@ type
end;
TScore = record
- Name: widestring;
+ Name: UTF8String;
Score: integer;
Length: string;
end;
+ TPathDynArray = array of IPath;
+
{$IFDEF USE_PSEUDO_THREAD}
TSongs = class(TPseudoThread)
{$ELSE}
@@ -102,11 +107,11 @@ type
procedure LoadSongList; // load all songs
- procedure BrowseDir(Dir: widestring); // should return number of songs in the future
- procedure BrowseTXTFiles(Dir: widestring);
- procedure BrowseXMLFiles(Dir: widestring);
+ procedure FindFilesByExtension(const Dir: IPath; const Ext: IPath; Recursive: Boolean; var Files: TPathDynArray);
+ procedure BrowseDir(Dir: IPath); // should return number of songs in the future
+ procedure BrowseTXTFiles(Dir: IPath);
+ procedure BrowseXMLFiles(Dir: IPath);
procedure Sort(Order: integer);
- function FindSongFile(Dir, Mask: widestring): widestring;
property Processing: boolean read fProcessing;
end;
@@ -128,7 +133,7 @@ type
function VisibleSongs: integer; // returns number of visible songs (for tabs)
function VisibleIndex(Index: integer): integer; // returns visible song index (skips invisible)
- function SetFilter(FilterStr: string; const fType: byte): cardinal;
+ function SetFilter(FilterStr: UTF8String; Filter: TSongFilter): cardinal;
end;
var
@@ -156,9 +161,12 @@ uses
UCovers,
UFiles,
UGraphic,
+ UMain,
UIni,
- UPath,
- UNote;
+ UPathUtils,
+ UNote,
+ UFilesystem,
+ UUnicodeUtils;
constructor TSongs.Create();
begin
@@ -232,7 +240,7 @@ begin
// browse directories
for I := 0 to SongPaths.Count-1 do
- BrowseDir(SongPaths[I]);
+ BrowseDir(SongPaths[I] as IPath);
if assigned(CatSongs) then
CatSongs.Refresh;
@@ -264,84 +272,92 @@ begin
Resume();
end;
-procedure TSongs.BrowseDir(Dir: widestring);
+procedure TSongs.BrowseDir(Dir: IPath);
begin
BrowseTXTFiles(Dir);
BrowseXMLFiles(Dir);
end;
-procedure TSongs.BrowseTXTFiles(Dir: widestring);
+procedure TSongs.FindFilesByExtension(const Dir: IPath; const Ext: IPath; Recursive: Boolean; var Files: TPathDynArray);
var
- i: integer;
- Files: TDirectoryEntryArray;
- lSong: TSong;
+ Iter: IFileIterator;
+ FileInfo: TFileInfo;
+ FileName: IPath;
begin
-
- try
- Files := Platform.DirectoryFindFiles(Dir, '.txt', true)
- except
- Log.LogError('Couldn''t deal with directory/file: ' + Dir + ' in TSongs.BrowseTXTFiles')
- end;
-
- for i := 0 to Length(Files) - 1 do
+ // search for all files and directories
+ Iter := FileSystem.FileFind(Dir.Append('*'), faAnyFile);
+ while (Iter.HasNext) do
begin
- if Files[i].IsDirectory then
+ FileInfo := Iter.Next;
+ FileName := FileInfo.Name;
+ if ((FileInfo.Attr and faDirectory) <> 0) then
begin
- BrowseTXTFiles(Dir + Files[i].Name + PathDelim); //Recursive Call
+ if Recursive and (not FileName.Equals('.')) and (not FileName.Equals('..')) then
+ FindFilesByExtension(Dir.Append(FileName), Ext, true, Files);
end
else
begin
- lSong := TSong.create(Dir + Files[i].Name);
-
- if lSong.Analyse then
- SongList.add(lSong)
- else
+ if (Ext.Equals(FileName.GetExtension(), true)) then
begin
- Log.LogError('AnalyseFile failed for "' + Files[i].Name + '".');
- freeandnil(lSong);
+ SetLength(Files, Length(Files)+1);
+ Files[High(Files)] := Dir.Append(FileName);
end;
-
end;
end;
- SetLength(Files, 0);
-
end;
-procedure TSongs.BrowseXMLFiles(Dir: widestring);
+procedure TSongs.BrowseTXTFiles(Dir: IPath);
var
- i: integer;
- Files: TDirectoryEntryArray;
- lSong: TSong;
+ I: integer;
+ Files: TPathDynArray;
+ Song: TSong;
+ Extension: IPath;
begin
+ SetLength(Files, 0);
+ Extension := Path('.txt');
+ FindFilesByExtension(Dir, Extension, true, Files);
- try
- Files := Platform.DirectoryFindFiles(Dir, '.xml', true)
- except
- Log.LogError('Couldn''t deal with directory/file: ' + Dir + ' in TSongs.BrowseXMLFiles')
- end;
-
- for i := 0 to Length(Files) - 1 do
+ for I := 0 to High(Files) do
begin
- if Files[i].IsDirectory then
- begin
- BrowseXMLFiles(Dir + Files[i].Name + PathDelim); // Recursive Call
- end
+ Song := TSong.Create(Files[I]);
+
+ if Song.Analyse then
+ SongList.Add(Song)
else
begin
- lSong := TSong.create(Dir + Files[i].Name);
+ Log.LogError('AnalyseFile failed for "' + Files[I].ToNative + '".');
+ FreeAndNil(Song);
+ end;
+ end;
- if lSong.AnalyseXML then
- SongList.add(lSong)
- else
- begin
- Log.LogError('AnalyseFile failed for "' + Files[i].Name + '".');
- freeandnil(lSong);
- end;
+ SetLength(Files, 0);
+end;
+
+procedure TSongs.BrowseXMLFiles(Dir: IPath);
+var
+ I: integer;
+ Files: TPathDynArray;
+ Song: TSong;
+ Extension: IPath;
+begin
+ SetLength(Files, 0);
+ Extension := Path('.xml');
+ FindFilesByExtension(Dir, Extension, true, Files);
+
+ for I := 0 to High(Files) do
+ begin
+ Song := TSong.Create(Files[I]);
+ if Song.AnalyseXML then
+ SongList.Add(Song)
+ else
+ begin
+ Log.LogError('AnalyseFile failed for "' + Files[I].ToNative + '".');
+ FreeAndNil(Song);
end;
end;
- SetLength(Files, 0);
+ SetLength(Files, 0);
end;
(*
@@ -350,32 +366,32 @@ end;
function CompareByEdition(Song1, Song2: Pointer): integer;
begin
- Result := CompareText(TSong(Song1).Edition, TSong(Song2).Edition);
+ Result := UTF8CompareText(TSong(Song1).Edition, TSong(Song2).Edition);
end;
function CompareByGenre(Song1, Song2: Pointer): integer;
begin
- Result := CompareText(TSong(Song1).Genre, TSong(Song2).Genre);
+ Result := UTF8CompareText(TSong(Song1).Genre, TSong(Song2).Genre);
end;
function CompareByTitle(Song1, Song2: Pointer): integer;
begin
- Result := CompareText(TSong(Song1).Title, TSong(Song2).Title);
+ Result := UTF8CompareText(TSong(Song1).Title, TSong(Song2).Title);
end;
function CompareByArtist(Song1, Song2: Pointer): integer;
begin
- Result := CompareText(TSong(Song1).Artist, TSong(Song2).Artist);
+ Result := UTF8CompareText(TSong(Song1).Artist, TSong(Song2).Artist);
end;
function CompareByFolder(Song1, Song2: Pointer): integer;
begin
- Result := CompareText(TSong(Song1).Folder, TSong(Song2).Folder);
+ Result := UTF8CompareText(TSong(Song1).Folder, TSong(Song2).Folder);
end;
function CompareByLanguage(Song1, Song2: Pointer): integer;
begin
- Result := CompareText(TSong(Song1).Language, TSong(Song2).Language);
+ Result := UTF8CompareText(TSong(Song1).Language, TSong(Song2).Language);
end;
procedure TSongs.Sort(Order: integer);
@@ -412,18 +428,6 @@ begin
MergeSort(SongList, CompareFunc);
end;
-function TSongs.FindSongFile(Dir, Mask: widestring): widestring;
-var
- SR: TSearchRec; // for parsing song directory
-begin
- Result := '';
- if FindFirst(Dir + Mask, faDirectory, SR) = 0 then
- begin
- Result := SR.Name;
- end; // if
- FindClose(SR);
-end;
-
procedure TCatSongs.SortSongs();
begin
case Ini.Sorting of
@@ -469,14 +473,14 @@ procedure TCatSongs.Refresh;
var
SongIndex: integer;
CurSong: TSong;
- CatIndex: integer; // index of current song in Song
- Letter: char; // current letter for sorting using letter
- CurCategory: string; // current edition for sorting using edition, genre etc.
- Order: integer; // number used for ordernum
- LetterTmp: char;
- CatNumber: integer; // Number of Song in Category
-
- procedure AddCategoryButton(const CategoryName: string);
+ CatIndex: integer; // index of current song in Song
+ Letter: UCS4Char; // current letter for sorting using letter
+ CurCategory: UTF8String; // current edition for sorting using edition, genre etc.
+ Order: integer; // number used for ordernum
+ LetterTmp: UCS4Char;
+ CatNumber: integer; // Number of Song in Category
+
+ procedure AddCategoryButton(const CategoryName: UTF8String);
var
PrevCatBtnIndex: integer;
begin
@@ -511,7 +515,7 @@ begin
// Note: do NOT set Letter to ' ', otherwise no category-button will be
// created for songs beginning with ' ' if songs of this category exist.
// TODO: trim song-properties so ' ' will not occur as first chararcter.
- Letter := #0;
+ Letter := 0;
// clear song-list
for SongIndex := 0 to Songs.SongList.Count - 1 do
@@ -530,91 +534,110 @@ begin
// if tabs are on, add section buttons for each new section
if (Ini.Tabs = 1) then
begin
- if (Ini.Sorting = sEdition) and
- (CompareText(CurCategory, CurSong.Edition) <> 0) then
- begin
- CurCategory := CurSong.Edition;
-
- // add Category Button
- AddCategoryButton(CurCategory);
- end
-
- else if (Ini.Sorting = sGenre) and
- (CompareText(CurCategory, CurSong.Genre) <> 0) then
- begin
- CurCategory := CurSong.Genre;
- // add Genre Button
- AddCategoryButton(CurCategory);
- end
-
- else if (Ini.Sorting = sLanguage) and
- (CompareText(CurCategory, CurSong.Language) <> 0) then
- begin
- CurCategory := CurSong.Language;
- // add Language Button
- AddCategoryButton(CurCategory);
- end
+ case (Ini.Sorting) of
+ sEdition: begin
+ if (CompareText(CurCategory, CurSong.Edition) <> 0) then
+ begin
+ CurCategory := CurSong.Edition;
+
+ // add Category Button
+ AddCategoryButton(CurCategory);
+ end;
+ end;
- else if (Ini.Sorting = sTitle) and
- (Length(CurSong.Title) >= 1) and
- (Letter <> UpperCase(CurSong.Title)[1]) then
- begin
- Letter := Uppercase(CurSong.Title)[1];
- // add a letter Category Button
- AddCategoryButton(Letter);
- end
+ sGenre: begin
+ if (CompareText(CurCategory, CurSong.Genre) <> 0) then
+ begin
+ CurCategory := CurSong.Genre;
+ // add Genre Button
+ AddCategoryButton(CurCategory);
+ end;
+ end;
- else if (Ini.Sorting = sArtist) and
- (Length(CurSong.Artist) >= 1) and
- (Letter <> UpperCase(CurSong.Artist)[1]) then
- begin
- Letter := UpperCase(CurSong.Artist)[1];
- // add a letter Category Button
- AddCategoryButton(Letter);
- end
+ sLanguage: begin
+ if (CompareText(CurCategory, CurSong.Language) <> 0) then
+ begin
+ CurCategory := CurSong.Language;
+ // add Language Button
+ AddCategoryButton(CurCategory);
+ end
+ end;
- else if (Ini.Sorting = sFolder) and
- (CompareText(CurCategory, CurSong.Folder) <> 0) then
- begin
- CurCategory := CurSong.Folder;
- // add folder tab
- AddCategoryButton(CurCategory);
- end
+ sTitle: begin
+ if (Length(CurSong.Title) >= 1) then
+ begin
+ LetterTmp := UCS4UpperCase(UTF8ToUCS4String(CurSong.Title)[0]);
+ if (Letter <> LetterTmp) then
+ begin
+ Letter := LetterTmp;
+ // add a letter Category Button
+ AddCategoryButton(UCS4ToUTF8String(Letter));
+ end;
+ end;
+ end;
- else if (Ini.Sorting = sTitle2) and
- (Length(CurSong.Title) >= 1) then
- begin
- // pack all numbers into a category named '#'
- if (CurSong.Title[1] >= '0') and (CurSong.Title[1] <= '9') then
- LetterTmp := '#'
- else
- LetterTmp := UpperCase(CurSong.Title)[1];
+ sArtist: begin
+ if (Length(CurSong.Artist) >= 1) then
+ begin
+ LetterTmp := UCS4UpperCase(UTF8ToUCS4String(CurSong.Artist)[0]);
+ if (Letter <> LetterTmp) then
+ begin
+ Letter := LetterTmp;
+ // add a letter Category Button
+ AddCategoryButton(UCS4ToUTF8String(Letter));
+ end;
+ end;
+ end;
- if (Letter <> LetterTmp) then
- begin
- Letter := LetterTmp;
- // add a letter Category Button
- AddCategoryButton(Letter);
+ sFolder: begin
+ if (UTF8CompareText(CurCategory, CurSong.Folder) <> 0) then
+ begin
+ CurCategory := CurSong.Folder;
+ // add folder tab
+ AddCategoryButton(CurCategory);
+ end;
end;
- end
- else if (Ini.Sorting = sArtist2) and
- (Length(CurSong.Artist)>=1) then
- begin
- // pack all numbers into a category named '#'
- if (CurSong.Artist[1] >= '0') and (CurSong.Artist[1] <= '9') then
- LetterTmp := '#'
- else
- LetterTmp := UpperCase(CurSong.Artist)[1];
+ sTitle2: begin
+ if (Length(CurSong.Title) >= 1) then
+ begin
+ LetterTmp := UTF8ToUCS4String(CurSong.Title)[0];
+ // pack all numbers into a category named '#'
+ if (LetterTmp in [Ord('0') .. Ord('9')]) then
+ LetterTmp := Ord('#')
+ else
+ LetterTmp := UCS4UpperCase(LetterTmp);
+
+ if (Letter <> LetterTmp) then
+ begin
+ Letter := LetterTmp;
+ // add a letter Category Button
+ AddCategoryButton(UCS4ToUTF8String(Letter));
+ end;
+ end;
+ end;
- if (Letter <> LetterTmp) then
- begin
- Letter := LetterTmp;
- // add a letter Category Button
- AddCategoryButton(Letter);
+ sArtist2: begin
+ if (Length(CurSong.Artist) >= 1) then
+ begin
+ LetterTmp := UTF8ToUCS4String(CurSong.Artist)[0];
+ // pack all numbers into a category named '#'
+ if (LetterTmp in [Ord('0') .. Ord('9')]) then
+ LetterTmp := Ord('#')
+ else
+ LetterTmp := UCS4UpperCase(LetterTmp);
+
+ if (Letter <> LetterTmp) then
+ begin
+ Letter := LetterTmp;
+ // add a letter Category Button
+ AddCategoryButton(UCS4ToUTF8String(Letter));
+ end;
+ end;
end;
- end;
- end;
+
+ end; // case (Ini.Sorting)
+ end; // if (Ini.Tabs = 1)
CatIndex := Length(Song);
SetLength(Song, CatIndex+1);
@@ -761,58 +784,58 @@ begin
end;
end;
-function TCatSongs.SetFilter(FilterStr: string; const fType: byte): cardinal;
+function TCatSongs.SetFilter(FilterStr: UTF8String; Filter: TSongFilter): cardinal;
var
I, J: integer;
- cString: string;
- SearchStr: array of string;
+ TmpString: UTF8String;
+ WordArray: array of UTF8String;
begin
-{
- fType: 0: All
- 1: Title
- 2: Artist
-}
FilterStr := Trim(FilterStr);
- if FilterStr<>'' then
+ if (FilterStr <> '') then
begin
Result := 0;
- // Create Search Array
- SetLength(SearchStr, 1);
+
+ // initialize word array
+ SetLength(WordArray, 1);
+
+ // Copy words to SearchStr
I := Pos(' ', FilterStr);
while (I <> 0) do
begin
- SetLength(SearchStr, Length(SearchStr) + 1);
- cString := Copy(FilterStr, 1, I - 1);
- if (cString <> ' ') and (cString <> '') then
- SearchStr[High(SearchStr) - 1] := cString;
- Delete (FilterStr, 1, I);
+ WordArray[High(WordArray)] := Copy(FilterStr, 1, I-1);
+ SetLength(WordArray, Length(WordArray) + 1);
- I := Pos (' ', FilterStr);
+ FilterStr := TrimLeft(Copy(FilterStr, I+1, Length(FilterStr)-I));
+ I := Pos(' ', FilterStr);
end;
- // Copy last Word
- if (FilterStr <> ' ') and (FilterStr <> '') then
- SearchStr[High(SearchStr)] := FilterStr;
+
+ // Copy last word
+ WordArray[High(WordArray)] := FilterStr;
for I := 0 to High(Song) do
begin
if not Song[i].Main then
begin
- case fType of
- 0: cString := Song[I].Artist + ' ' + Song[i].Title + ' ' + Song[i].Folder;
- 1: cString := Song[I].Title;
- 2: cString := Song[I].Artist;
+ case Filter of
+ fltAll:
+ TmpString := Song[I].Artist + ' ' + Song[i].Title + ' ' + Song[i].Folder;
+ fltTitle:
+ TmpString := Song[I].Title;
+ fltArtist:
+ TmpString := Song[I].Artist;
end;
- Song[i].Visible:=true;
- // Look for every Searched Word
- for J := 0 to High(SearchStr) do
+ Song[i].Visible := true;
+ // Look for every searched word
+ for J := 0 to High(WordArray) do
begin
- Song[i].Visible := Song[i].Visible and AnsiContainsText(cString, SearchStr[J])
+ Song[i].Visible := Song[i].Visible and
+ UTF8ContainsText(TmpString, WordArray[J])
end;
if Song[i].Visible then
Inc(Result);
end
else
- Song[i].Visible:=false;
+ Song[i].Visible := false;
end;
CatNumShow := -2;
end
@@ -820,7 +843,7 @@ begin
begin
for i := 0 to High(Song) do
begin
- Song[i].Visible := (Ini.Tabs=1) = Song[i].Main;
+ Song[i].Visible := (Ini.Tabs = 1) = Song[i].Main;
CatNumShow := -1;
end;
Result := 0;