aboutsummaryrefslogblamecommitdiffstats
path: root/Game/Code/Classes/USongs.pas
blob: 554a6e1e54d73991f59b68470b3ca5b642797fc7 (plain) (tree)


















































































                                                                                        

                                              









































                                                                                                    
 
































                                                                       



























                                               
                                         




                         
        
















                                                    
  


                                                                                    
                                                                                                                                                        







                                 
                                  








                                                
 












                                            





                                                 
   


                                 
     

                                                                       
 



                                                
                                                                                              





                                                     








                                                                                                          
                            








                                                 
                                                                    
















                                                                                                          


                            
                                        
 







                                      
                

































































































                                                                                                   
                 

























                                                                            
                    































































                                                                                                              
                                                                                                                        

























                                                                                                          
                                                                                                                      
























                                                                                                                
                                                                                                                      




























                                                                                                                                      
                                                                                                                      


























                                                                                                                                        
                                                                                                                      
























                                                                                                            
                                                                                                                      



























                                                                                                                                                                                                
                                                                                                                        




























                                                                                                                                                                                                  
                                                                                                                        







                                                                                         
 























                                                                                                     
                      




                                                 

                                            





                                                                               
         




                                                                                

                                             

                                                           
         





                                                        
                                        





                           
         






                                      
                                 






                                                             
                     








                                                                                        
                        






                                                                     
            







                                          

                                                           






                                                         

                                                           










                                                                             
                                












































                                                                                        
           
                
         









                                                                                
unit USongs;

interface

{$IFDEF FPC}
  {$MODE Delphi}
{$ENDIF}

{$I switches.inc}

{$IFDEF DARWIN}
	{$IFDEF DEBUG}
		{$DEFINE USE_PSEUDO_THREAD}
	{$ENDIF}
{$ENDIF}

uses
    {$IFDEF MSWINDOWS}
      Windows,
      DirWatch,
    {$ELSE}
      {$IFNDEF DARWIN}
        syscall,
      {$ENDIF}
      baseunix,
      UnixType,
    {$ENDIF}
    SysUtils,
    Classes,
    UPlatform,
    ULog,
    UTexture,
    UCommon,
    {$IFDEF DARWIN}
      cthreads,
    {$ENDIF}
    {$IFDEF USE_PSEUDO_THREAD}
      PseudoThread,
    {$ENDIF}
    USong,
    UCatCovers;

type

  TBPM = record
    BPM:        real;
    StartBeat:  real;
  end;

  TScore = record
    Name:       widestring;
    Score:      integer;
    Length:     string;
  end;

	
	{$IFDEF USE_PSEUDO_THREAD}
	TSongs = class( TPseudoThread )
	{$ELSE}
  TSongs = class( TThread )
	{$ENDIF}
  private
    fNotify   ,
    fWatch    : longint;
    fParseSongDirectory : boolean;
    fProcessing         : boolean;
    {$ifdef MSWINDOWS}
      fDirWatch           : TDirectoryWatch;
    {$endif}
    procedure int_LoadSongList;
    procedure DoDirChanged(Sender: TObject);
  protected
    procedure Execute; override;
  public
//    Song      : array of TSong; // array of songs
    SongList  : TList; // array of songs
    Selected  : integer;        // selected song index
    constructor create();
    destructor  destroy(); override;


    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 Sort(Order: integer);
    function  FindSongFile(Dir, Mask: widestring): widestring;
    property  Processing : boolean read fProcessing;
  end;


  TCatSongs = class
    Song:       array of TSong; // array of categories with songs
    Selected:   integer; // selected song index
    Order:      integer; // order type (0=title)
    CatNumShow: integer; // Category Number being seen
    CatCount:   integer; //Number of Categorys

    procedure Refresh; // refreshes arrays by recreating them from Songs array
//    procedure Sort(Order: integer);
    procedure ShowCategory(Index: integer); // expands all songs in category
    procedure HideCategory(Index: integer); // hides all songs in category
    procedure ClickCategoryButton(Index: integer); // uses ShowCategory and HideCategory when needed
    procedure ShowCategoryList; //Hides all Songs And Show the List of all Categorys
    function FindNextVisible(SearchFrom:integer): integer; //Find Next visible Song
    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;
  end;

var
  Songs:      TSongs; // all songs
  CatSongs:   TCatSongs; // categorized songs

const
     IN_ACCESS		    = $00000001;	//* File was accessed */
     IN_MODIFY		    = $00000002;	//* File was modified */
     IN_ATTRIB		    = $00000004;	//* Metadata changed */
     IN_CLOSE_WRITE		= $00000008;	//* Writtable file was closed */
     IN_CLOSE_NOWRITE	= $00000010;	//* Unwrittable file closed */
     IN_OPEN			    = $00000020;	//* File was opened */
     IN_MOVED_FROM		= $00000040;	//* File was moved from X */
     IN_MOVED_TO		  = $00000080;	//* File was moved to Y */
     IN_CREATE		    = $00000100;	//* Subfile was created */
     IN_DELETE		    = $00000200;	//* Subfile was deleted */
     IN_DELETE_SELF		= $00000400;	//* Self was deleted */


implementation

uses StrUtils,
     UGraphic,
     UCovers,
     UFiles,
     UMain,
     UIni;

{$IFDEF DARWIN}
function AnsiContainsText(const AText, ASubText: string): Boolean;
begin
  Result := AnsiPos(AnsiUppercase(ASubText), AnsiUppercase(AText)) > 0;
end;
{$ENDIF}

constructor TSongs.create();
begin
  // do not start thread BEFORE initialization (suspended = true)
  inherited create( true );
  self.freeonterminate := true;

  SongList := TList.create();

  {$ifdef MSWINDOWS}
    fDirWatch := TDirectoryWatch.create(nil);
    fDirWatch.OnChange     := DoDirChanged;
    fDirWatch.Directory    := SongPath;
    fDirWatch.WatchSubDirs := true;
    fDirWatch.active       := true;
  {$ENDIF}

  // now we can start the thread
  Resume();
end;

destructor TSongs.destroy();
begin
  freeandnil( SongList );
end;

procedure TSongs.DoDirChanged(Sender: TObject);
begin
  LoadSongList();
end;

procedure TSongs.Execute();
var
  fChangeNotify : THandle;
begin
{$IFDEF USE_PSEUDO_THREAD}
  int_LoadSongList();
{$ELSE}
  fParseSongDirectory := true;

  while not self.terminated do
  begin

    if fParseSongDirectory then
    begin
      debugWriteln( 'int_LoadSongList' );
      int_LoadSongList();
    end;

    self.suspend;
  end;
{$ENDIF}
end;

procedure TSongs.int_LoadSongList;
const
  cUSNGPath = '/usr/share/games/ultrastar-ng/songs';
begin
  try
    fProcessing := true;

    Log.LogError('SongList', 'Searching For Songs');

    // browse directories
    BrowseDir(SongPath);

    if UserSongPath <> SongPath then
      BrowseDir(UserSongPath);

(*
    if ( cUSNGPath <> SongPath     ) AND
       ( cUSNGPath <> UserSongPath ) then
      BrowseDir( cUSNGPath );                       // todo : JB this is REAL messy,
*)                                                    // we should have some sort of path manager that lets us specify X number of extra paths to search

    if assigned( CatSongs ) then
      CatSongs.Refresh;

    if assigned( CatCovers ) then
      CatCovers.Load;

    if assigned( Covers ) then
      Covers.Load('covers.cache');

    if assigned(ScreenSong)  then
    begin
      ScreenSong.GenerateThumbnails();
      ScreenSong.OnShow; // refresh ScreenSong
    end;

  finally
    Log.LogError('SongList', 'Search Complete');

    fParseSongDirectory := false;
    fProcessing         := false;
  end;
end;


procedure TSongs.LoadSongList;
begin
  fParseSongDirectory := true;
  self.resume;
end;

procedure TSongs.BrowseDir(Dir: widestring);
begin
 BrowseTXTFiles(Dir);
 BrowseXMLFiles(Dir);
end;

procedure TSongs.BrowseTXTFiles(Dir: widestring);
var
  i       : Integer;
  Files   : TDirectoryEntryArray;
  lSong   : TSong;
begin
     // to - do : new Song management
	  {Files    := Platform.DirectoryFindFiles( Dir, '.txt', true);

		for i := 0 to Length(Files)-1 do
		begin
		  if Files[i].IsDirectory then
			begin
			  BrowseTXTFiles( Dir + Files[i].Name + PathDelim );  //Recursive Call
			end
			else
			begin
        lSong := TSong.create( Dir + Files[i].Name );

        if NOT lSong.Analyse then
				  begin
					  Log.LogError('AnalyseFile failed for "' + Files[i].Name + '".');
            freeandnil( lSong );
				  end
          else SongList.add( lSong );

			end;
		end;
		SetLength( Files, 0);
                           }
end;

procedure TSongs.BrowseXMLFiles(Dir: widestring);
var
  i       : Integer;
  Files   : TDirectoryEntryArray;
  lSong   : TSong;
begin

	  {Files := Platform.DirectoryFindFiles( Dir, '.xml', true);

		for i := 0 to Length(Files)-1 do
		begin
		  if Files[i].IsDirectory then
			begin
			  BrowseXMLFiles( Dir + Files[i].Name + PathDelim ); //Recursive Call
			end
			else
			begin
        lSong := TSong.create( Dir + Files[i].Name );

        if NOT lSong.AnalyseXML then
				  begin
					  Log.LogError('AnalyseFile failed for "' + Files[i].Name + '".');
            freeandnil( lSong );
				  end
          else SongList.add( lSong );

			end;
		end;
		SetLength( Files, 0);  }

end;

procedure TSongs.Sort(Order: integer);
var
  S:    integer;
  S2:   integer;
  TempSong:   TSong;
begin
  {case Order of
    sEdition: // by edition
      begin
        for S2 := 0 to SongList.Count -1 do
          for S := 1 to SongList.Count-1 do
            if CompareText(TSong( SongList[S] ).Edition, TSong( SongList[S-1] ).Edition) < 0 then
            begin
              // zamiana miejscami
              TempSong      := SongList[S-1];
              SongList[S-1] := SongList[S];
              SongList[S]   := TempSong;
            end;
      end;
    sGenre: // by genre
      begin
        for S2 := 0 to SongList.Count-1 do
          for S := 1 to SongList.Count-1 do
            if CompareText(TSong( SongList[S] ).Genre, TSong( SongList[S-1] ).Genre) < 0 then
            begin
              // zamiana miejscami
              TempSong      := SongList[S-1];
              SongList[S-1] := SongList[S];
              SongList[S]   := TempSong;
            end;
      end;
    sTitle: // by title
      begin
        for S2 := 0 to SongList.Count-1 do
          for S := 1 to SongList.Count-1 do
            if CompareText(TSong( SongList[S] ).Title, TSong( SongList[S-1] ).Title) < 0 then
            begin
              // zamiana miejscami
              TempSong      := SongList[S-1];
              SongList[S-1] := SongList[S];
              SongList[S]   := TempSong;
            end;

      end;
    sArtist: // by artist
      begin
        for S2 := 0 to SongList.Count-1 do
          for S := 1 to SongList.Count-1 do
            if CompareText(TSong( SongList[S] ).Artist, TSong( SongList[S-1] ).Artist) < 0 then
            begin
              // zamiana miejscami
              TempSong      := SongList[S-1];
              SongList[S-1] := SongList[S];
              SongList[S]   := TempSong;
            end;
      end;
    sFolder: // by folder
      begin
        for S2 := 0 to SongList.Count-1 do
          for S := 1 to SongList.Count-1 do
            if CompareText(TSong( SongList[S] ).Folder, TSong( SongList[S-1] ).Folder) < 0 then
            begin
              // zamiana miejscami
              TempSong      := SongList[S-1];
              SongList[S-1] := SongList[S];
              SongList[S]   := TempSong;
            end;
      end;
    sTitle2: // by title2
      begin
        for S2 := 0 to SongList.Count-1 do
          for S := 1 to SongList.Count-1 do
            if CompareText(TSong( SongList[S] ).Title, TSong( SongList[S-1] ).Title) < 0 then
            begin
              // zamiana miejscami
              TempSong      := SongList[S-1];
              SongList[S-1] := SongList[S];
              SongList[S]   := TempSong;
            end;

      end;
    sArtist2: // by artist2
      begin
        for S2 := 0 to SongList.Count-1 do
          for S := 1 to SongList.Count-1 do
            if CompareText(TSong( SongList[S] ).Artist, TSong( SongList[S-1] ).Artist) < 0 then
            begin
              // zamiana miejscami
              TempSong      := SongList[S-1];
              SongList[S-1] := SongList[S];
              SongList[S]   := TempSong;
            end;
      end;
    sLanguage: // by Language
      begin
        for S2 := 0 to SongList.Count-1 do
          for S := 1 to SongList.Count-1 do
            if CompareText(TSong( SongList[S] ).Language, TSong( SongList[S-1] ).Language) < 0 then
            begin
              TempSong      := SongList[S-1];
              SongList[S-1] := SongList[S];
              SongList[S]   := TempSong;
            end;
      end;

  end; // case  }
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.Refresh;
var
  S:        integer; // temporary song index
  CatLen:   integer; // length of CatSongs.Song
  Letter:   char; // current letter for sorting using letter
  SS:       string; // current edition for sorting using edition, genre etc.
  Order:    integer; // number used for ordernum
  Letter2:  char; //
  CatNumber:integer; // Number of Song in Category
begin
  CatNumShow := -1;
//  Songs.Sort(0); // by title

{case Ini.Sorting of
    sEdition: begin
          Songs.Sort(sArtist);
          Songs.Sort(sEdition);
        end;
    sGenre: begin
          Songs.Sort(sArtist);
          Songs.Sort(sGenre);
        end;
    sLanguage: begin
          Songs.Sort(sArtist);
          Songs.Sort(sLanguage);
        end;
    sFolder:  begin
          Songs.Sort(sArtist);
          Songs.Sort(sFolder);
        end;
    sTitle:  Songs.Sort(sTitle);
    sArtist:  Songs.Sort(sArtist);
    sTitle2:  Songs.Sort(sTitle2); // by title2
    sArtist2:  Songs.Sort(sArtist2); // by artist2

  end; // case


  Letter := ' ';
  SS := '';
  Order := 0;
  CatNumber := 0;

  //Songs leeren
  SetLength (Song, 0);

  for S := 0 to Songs.SongList.Count-1 do
  begin
    if (Ini.Tabs = 1) then
    if (Ini.Sorting = sEdition) and (CompareText(SS, TSong( Songs.SongList[S] ).Edition) <> 0) then begin
      // add Category Button
      Inc(Order);
      SS := TSong( Songs.SongList[S] ).Edition;
      CatLen := Length(CatSongs.Song);
      SetLength(CatSongs.Song, CatLen+1);
      CatSongs.Song[CatLen].Artist := '[' + SS + ']';
      CatSongs.Song[CatLen].Main := true;
      CatSongs.Song[CatLen].OrderTyp := 0;
      CatSongs.Song[CatLen].OrderNum := Order;



      // 0.4.3
      // if SS = 'Singstar' then CatSongs.Song[CatLen].Cover := CoversPath + 'Singstar.jpg';
      // if SS = 'Singstar Part 2' then CatSongs.Song[CatLen].Cover := CoversPath + 'Singstar.jpg';
      // if SS = 'Singstar German' then CatSongs.Song[CatLen].Cover := CoversPath + 'Singstar.jpg';
      // if SS = 'Singstar Spanish' then CatSongs.Song[CatLen].Cover := CoversPath + 'Singstar.jpg';
      // if SS = 'Singstar Italian' then CatSongs.Song[CatLen].Cover := CoversPath + 'Singstar.jpg';
      // if SS = 'Singstar French' then CatSongs.Song[CatLen].Cover := CoversPath + 'Singstar.jpg';
      // if SS = 'Singstar Party' then CatSongs.Song[CatLen].Cover := CoversPath + 'Singstar Party.jpg';
      // if SS = 'Singstar Popworld' then CatSongs.Song[CatLen].Cover := CoversPath + 'Singstar Popworld.jpg';
      // if SS = 'Singstar 80s' then CatSongs.Song[CatLen].Cover := CoversPath + 'Singstar 80s.jpg';
      // if SS = 'Singstar 80s Polish' then CatSongs.Song[CatLen].Cover := CoversPath + 'Singstar 80s.jpg';
      // if SS = 'Singstar Rocks' then CatSongs.Song[CatLen].Cover := CoversPath + 'Singstar Rocks.jpg';
      // if SS = 'Singstar Anthems' then CatSongs.Song[CatLen].Cover := CoversPath + 'Singstar Anthems.jpg';

      {// cover-patch
      if FileExists(CoversPath + SS + '.jpg') then CatSongs.Song[CatLen].Cover := CoversPath + SS + '.jpg'
      else if FileExists(CoversPath + 'NoCover.jpg') then CatSongs.Song[CatLen].Cover := CoversPath + 'NoCover.jpg';//}{

      CatSongs.Song[CatLen].Cover := CatCovers.GetCover(Ini.Sorting, SS);

      //CatNumber Patch
      if (SS <> '') then
      begin
        Song[CatLen - CatNumber - 1].CatNumber := CatNumber;//Set CatNumber of Categroy
        CatNumber := 0;
      end;

      CatSongs.Song[CatLen].Visible := true;
    end

    else if (Ini.Sorting = sGenre) and (CompareText(SS, TSong( Songs.SongList[S] ).Genre) <> 0) then begin
      // add Genre Button
      Inc(Order);
      SS := TSong( Songs.SongList[S] ).Genre;
      CatLen := Length(CatSongs.Song);
      SetLength(CatSongs.Song, CatLen+1);
      CatSongs.Song[CatLen].Artist := SS;
      CatSongs.Song[CatLen].Main := true;
      CatSongs.Song[CatLen].OrderTyp := 0;
      CatSongs.Song[CatLen].OrderNum := Order;

      {// cover-patch
      if FileExists(CoversPath + SS + '.jpg') then CatSongs.Song[CatLen].Cover := CoversPath + SS + '.jpg'
      else if FileExists(CoversPath + 'NoCover.jpg') then CatSongs.Song[CatLen].Cover := CoversPath + 'NoCover.jpg';}{
      CatSongs.Song[CatLen].Cover := CatCovers.GetCover(Ini.Sorting, SS);

      //CatNumber Patch
      if (SS <> '') then
      begin
        Song[CatLen - CatNumber - 1].CatNumber := CatNumber;//Set CatNumber of Categroy
        CatNumber := 0;
      end;

      CatSongs.Song[CatLen].Visible := true;
    end

    else if (Ini.Sorting = sLanguage) and (CompareText(SS, TSong( Songs.SongList[S] ).Language) <> 0) then begin
      // add Language Button
      Inc(Order);
      SS := TSong( Songs.SongList[S] ).Language;
      CatLen := Length(CatSongs.Song);
      SetLength(CatSongs.Song, CatLen+1);
      CatSongs.Song[CatLen].Artist := SS;
      CatSongs.Song[CatLen].Main := true;
      CatSongs.Song[CatLen].OrderTyp := 0;
      CatSongs.Song[CatLen].OrderNum := Order;

      {// cover-patch
      if FileExists(CoversPath + SS + '.jpg') then CatSongs.Song[CatLen].Cover := CoversPath + SS + '.jpg'
      else if FileExists(CoversPath + 'NoCover.jpg') then CatSongs.Song[CatLen].Cover := CoversPath + 'NoCover.jpg';}{
      CatSongs.Song[CatLen].Cover := CatCovers.GetCover(Ini.Sorting, SS);

      //CatNumber Patch
      if (SS <> '') then
      begin
        Song[CatLen - CatNumber - 1].CatNumber := CatNumber;//Set CatNumber of Categroy
        CatNumber := 0;
      end;

      CatSongs.Song[CatLen].Visible := true;
    end

    else if (Ini.Sorting = sTitle) and
            (Length(TSong( Songs.SongList[S] ).Title)>=1) and
            (Letter <> UpperCase(TSong( Songs.SongList[S] ).Title)[1]) then begin
      // add a letter Category Button
      Inc(Order);
      Letter := Uppercase(TSong( Songs.SongList[S] ).Title)[1];
      CatLen := Length(CatSongs.Song);
      SetLength(CatSongs.Song, CatLen+1);
      CatSongs.Song[CatLen].Artist := '[' + Letter + ']';
      CatSongs.Song[CatLen].Main := true;
      CatSongs.Song[CatLen].OrderTyp := 0;
//      Order := ord(Letter);
      CatSongs.Song[CatLen].OrderNum := Order;


      {// cover-patch
      if FileExists(CoversPath + 'Title' + Letter + '.jpg') then CatSongs.Song[CatLen].Cover := CoversPath + 'Title' + Letter + '.jpg'
      else if FileExists(CoversPath + 'NoCover.jpg') then CatSongs.Song[CatLen].Cover := CoversPath + 'NoCover.jpg';}{
      CatSongs.Song[CatLen].Cover := CatCovers.GetCover(Ini.Sorting, Letter);

      //CatNumber Patch
      if (Letter <> ' ') then
      begin
        Song[CatLen - CatNumber - 1].CatNumber := CatNumber;//Set CatNumber of Categroy
        CatNumber := 0;
      end;

      CatSongs.Song[CatLen].Visible := true;
    end

    else if (Ini.Sorting = sArtist) and (Length(TSong( Songs.SongList[S] ).Artist)>=1) and
            (Letter <> UpperCase(TSong( Songs.SongList[S] ).Artist)[1]) then begin
      // add a letter Category Button
      Inc(Order);
      Letter := UpperCase(TSong( Songs.SongList[S] ).Artist)[1];
      CatLen := Length(CatSongs.Song);
      SetLength(CatSongs.Song, CatLen+1);
      CatSongs.Song[CatLen].Artist := '[' + Letter + ']';
      CatSongs.Song[CatLen].Main := true;
      CatSongs.Song[CatLen].OrderTyp := 0;
//      Order := ord(Letter);
      CatSongs.Song[CatLen].OrderNum := Order;

      {// cover-patch
      if FileExists(CoversPath + 'Artist' + Letter + '.jpg') then CatSongs.Song[CatLen].Cover := CoversPath + 'Artist' + Letter + '.jpg'
      else if FileExists(CoversPath + 'NoCover.jpg') then CatSongs.Song[CatLen].Cover := CoversPath + 'NoCover.jpg';}{
      CatSongs.Song[CatLen].Cover := CatCovers.GetCover(Ini.Sorting, Letter);

      //CatNumber Patch
      if (Letter <> ' ') then
      begin
        Song[CatLen - CatNumber - 1].CatNumber := CatNumber;//Set CatNumber of Categroy
        CatNumber := 0;
      end;

      CatSongs.Song[CatLen].Visible := true;
    end

    else if (Ini.Sorting = sFolder) and (CompareText(SS, TSong( Songs.SongList[S] ).Folder) <> 0) then begin
      // 0.5.0: add folder tab
      Inc(Order);
      SS := TSong( Songs.SongList[S] ).Folder;
      CatLen := Length(CatSongs.Song);
      SetLength(CatSongs.Song, CatLen+1);
      CatSongs.Song[CatLen].Artist := SS;
      CatSongs.Song[CatLen].Main := true;
      CatSongs.Song[CatLen].OrderTyp := 0;
      CatSongs.Song[CatLen].OrderNum := Order;

      {// cover-patch
      if FileExists(CoversPath + SS + '.jpg') then CatSongs.Song[CatLen].Cover := CoversPath + SS + '.jpg'
      else if FileExists(CoversPath + 'NoCover.jpg') then CatSongs.Song[CatLen].Cover := CoversPath + 'NoCover.jpg';}{
      CatSongs.Song[CatLen].Cover := CatCovers.GetCover(Ini.Sorting, SS);

      //CatNumber Patch
      if (SS <> '') then
      begin
        Song[CatLen - CatNumber - 1].CatNumber := CatNumber;//Set CatNumber of Categroy
        CatNumber := 0;
      end;

      CatSongs.Song[CatLen].Visible := true;
    end

    else if (Ini.Sorting = sTitle2) AND (Length(TSong( Songs.SongList[S] ).Title)>=1) then begin
      if (ord(TSong( Songs.SongList[S] ).Title[1]) > 47) and (ord(TSong( Songs.SongList[S] ).Title[1]) < 58) then Letter2 := '#' else Letter2 := UpperCase(TSong( Songs.SongList[S] ).Title)[1];
      if (Letter <> Letter2) then begin
        // add a letter Category Button
        Inc(Order);
        Letter := Letter2;
        CatLen := Length(CatSongs.Song);
        SetLength(CatSongs.Song, CatLen+1);
        CatSongs.Song[CatLen].Artist := '[' + Letter + ']';
        CatSongs.Song[CatLen].Main := true;
        CatSongs.Song[CatLen].OrderTyp := 0;
//      Order := ord(Letter);
        CatSongs.Song[CatLen].OrderNum := Order;

        {// cover-patch
        if FileExists(CoversPath + 'Title' + Letter + '.jpg') then CatSongs.Song[CatLen].Cover := CoversPath + 'Title' + Letter + '.jpg'
        else if FileExists(CoversPath + 'NoCover.jpg') then CatSongs.Song[CatLen].Cover := CoversPath + 'NoCover.jpg';}{
        CatSongs.Song[CatLen].Cover := CatCovers.GetCover(Ini.Sorting, Letter);

        //CatNumber Patch
      if (Letter <> ' ') then
      begin
        Song[CatLen - CatNumber - 1].CatNumber := CatNumber;//Set CatNumber of Categroy
        CatNumber := 0;
      end;

        CatSongs.Song[CatLen].Visible := true;
      end;
    end

    else if (Ini.Sorting = sArtist2) AND (Length(TSong( Songs.SongList[S] ).Artist)>=1) then begin
     if (ord(TSong( Songs.SongList[S] ).Artist[1]) > 47) and (ord(TSong( Songs.SongList[S] ).Artist[1]) < 58) then Letter2 := '#' else Letter2 := UpperCase(TSong( Songs.SongList[S] ).Artist)[1];
       if (Letter <> Letter2) then begin
        // add a letter Category Button
        Inc(Order);
        Letter := Letter2;
        CatLen := Length(CatSongs.Song);
        SetLength(CatSongs.Song, CatLen+1);
        CatSongs.Song[CatLen].Artist := '[' + Letter + ']';
        CatSongs.Song[CatLen].Main := true;
        CatSongs.Song[CatLen].OrderTyp := 0;
//      Order := ord(Letter);
        CatSongs.Song[CatLen].OrderNum := Order;

        {// cover-patch
        if FileExists(CoversPath + 'Artist' + Letter + '.jpg') then CatSongs.Song[CatLen].Cover := CoversPath + 'Artist' + Letter + '.jpg'
        else if FileExists(CoversPath + 'NoCover.jpg') then CatSongs.Song[CatLen].Cover := CoversPath + 'NoCover.jpg';}{
        CatSongs.Song[CatLen].Cover := CatCovers.GetCover(Ini.Sorting, Letter);

        //CatNumber Patch
        if (Letter <> ' ') then
        begin
          Song[CatLen - CatNumber - 1].CatNumber := CatNumber;//Set CatNumber of Categroy
          CatNumber := 0;
        end;

        CatSongs.Song[CatLen].Visible := true;
      end;
    end;


    CatLen := Length(CatSongs.Song);
    SetLength(CatSongs.Song, CatLen+1);

    Inc (CatNumber); //Increase Number in Cat

    CatSongs.Song[CatLen] := TSong( Songs.SongList[S] );
    CatSongs.Song[CatLen].OrderNum := Order; // assigns category
    CatSongs.Song[CatLen].CatNumber := CatNumber;

    if (Ini.Tabs = 0) then CatSongs.Song[CatLen].Visible := true
    else if (Ini.Tabs = 1) then CatSongs.Song[CatLen].Visible := false;
//    if (Ini.Tabs = 1) and (Order = 1) then CatSongs.Song[CatLen].Visible := true; // open first tab
//CatSongs.Song[CatLen].Visible := true;

  end;
//CatNumber Patch - Set CatNumber of Last Category
if (ini.Tabs_at_startup = 1) And (high(Song) >=1) then
  Song[CatLen - CatNumber].CatNumber := CatNumber;//Set CatNumber of Categroy
//CatCount Patch
CatCount := Order;   }
end;

procedure TCatSongs.ShowCategory(Index: integer);
var
  S:    integer; // song
begin       // to - do : new Song management
 { CatNumShow := Index;
  for S := 0 to high(CatSongs.Song) do
  begin
    if (CatSongs.Song[S].OrderNum = Index) AND (Not CatSongs.Song[S].Main) then
      CatSongs.Song[S].Visible := true
    else
      CatSongs.Song[S].Visible := false;
  end;  }
end;

procedure TCatSongs.HideCategory(Index: integer); // hides all songs in category
var
  S:    integer; // song
begin       // to - do : new Song management
  {for S := 0 to high(CatSongs.Song) do begin
    if not CatSongs.Song[S].Main then
      CatSongs.Song[S].Visible := false // hides all at now
  end;  }
end;

procedure TCatSongs.ClickCategoryButton(Index: integer);
var
  Num, S:    integer;
begin
 { Num := CatSongs.Song[Index].OrderNum;
  if Num <> CatNumShow then
    begin
    ShowCategory(Num);
    end
  else begin
    ShowCategoryList;
  end;  }
end;

//Hide Categorys when in Category Hack
procedure TCatSongs.ShowCategoryList;
var
  Num, S:    integer;
begin
  {//Hide All Songs Show All Cats
  for S := 0 to high(CatSongs.Song) do begin
    if CatSongs.Song[S].Main then
      CatSongs.Song[S].Visible := true
    else
      CatSongs.Song[S].Visible := false
  end;
  CatSongs.Selected := CatNumShow; //Show last shown Category
  CatNumShow := -1; }
end;
//Hide Categorys when in Category Hack End

//Wrong song selected when tabs on bug
function TCatSongs.FindNextVisible(SearchFrom:integer): integer;//Find next Visible Song
var
  I: Integer;
  begin
  Result := -1;
  I := SearchFrom + 1; {
  while not CatSongs.Song[I].Visible do
    begin
    Inc (I);
    if (I>high(CatSongs.Song)) then
      I := low(CatSongs.Song);
    if (I = SearchFrom) then //Make One Round and no song found->quit
      break;
    end;   }
  end;
//Wrong song selected when tabs on bug End

function TCatSongs.VisibleSongs: integer;
var
  S:    integer; // song
begin
  Result := 0;
  {for S := 0 to high(CatSongs.Song) do
    if CatSongs.Song[S].Visible = true then Inc(Result);  }
end;

function TCatSongs.VisibleIndex(Index: integer): integer;
var
  S:    integer; // song
begin
  Result := 0;
  {for S := 0 to Index-1 do
    if CatSongs.Song[S].Visible = true then Inc(Result);  }
end;

function TCatSongs.SetFilter(FilterStr: String; const fType: Byte): Cardinal;
var
  I, J: Integer;
  cString: String;
  SearchStr: Array of String;
begin
  {fType: 0: All
          1: Title
          2: Artist}
  {FilterStr := Trim(FilterStr);
  if FilterStr<>'' then begin
    Result := 0;
    //Create Search Array
    SetLength(SearchStr, 1);
    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);

      I := Pos (' ', FilterStr);
    end;
    //Copy last Word
    if (FilterStr <> ' ') AND (FilterStr <> '') then
      SearchStr[High(SearchStr)] := 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;
        end;
        Song[i].Visible:=True;
        //Look for every Searched Word
        For J := 0 to High(SearchStr) do
        begin
          Song[i].Visible := Song[i].Visible AND AnsiContainsText(cString, SearchStr[J])
        end;
        if Song[i].Visible then
          Inc(Result);
      end
      else
        Song[i].Visible:=False;
    end;
    CatNumShow := -2;
  end
  else begin
    for i:=0 to High(Song) do begin
      Song[i].Visible:=(Ini.Tabs=1)=Song[i].Main;
      CatNumShow := -1;
    end;  }
    Result := 0;
  {end; }
end;



// -----------------------------------------------------------------------------




end.