unit CleanLibrary;

{$mode objfpc}{$H+}

interface

uses
  Classes, SysUtils, LResources, Forms, Controls, Graphics, Dialogs, StdCtrls,
  ComCtrls, ExtCtrls;

type
  strArray = array of string;
  ptrStrArray = ^strArray;
  
type

  { TFrmCleanLibrary }

  TFrmCleanLibrary = class(TForm)
    btnBack: TButton;
    btnReset: TButton;
    btnRemove: TButton;
    btnDisplay: TButton;
    btnBack2: TButton;
    btnUnselect: TButton;
    btnRemoveFromDisk: TButton;
    chkFolders: TCheckBox;
    Label3: TLabel;
    lstRemove: TListBox;
    Panel2: TPanel;
    pnlProgress: TPanel;
    txtNotToRemove: TEdit;
    Label1: TLabel;
    dirlistview: TListBox;
    Label2: TLabel;
    Panel1: TPanel;
    procedure btnBack2Click(Sender: TObject);
    procedure btnBackClick(Sender: TObject);
    procedure btnDisplayClick(Sender: TObject);
    procedure btnRemoveClick(Sender: TObject);
    procedure btnRemoveFromDiskClick(Sender: TObject);
    procedure btnResetClick(Sender: TObject);
    procedure btnUnselectClick(Sender: TObject);
    procedure dirlistviewClick(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure lstRemoveClick(Sender: TObject);
  private
    { private declarations }
    procedure Recursive_AddDir
              (dir: String; strExtension: ptrStrArray; var bContainesFiles: Boolean);
  public
  
    { public declarations }
  end; 

var
  FrmCleanLibrary: TFrmCleanLibrary;
  
implementation

uses
  mediacol, functions, config, mainform;
  


//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Procedure TFrmCleanLibrary.FormCreate(Sender: TObject);
Var i: integer;
Begin
  dirlistview.Clear;
  For i:= 0 To MediaCollection.dirlist.Count-1 Do
    Begin
      dirlistview.Items.Add(MediaCollection.dirlist[i]);
    End;
  btnReset.Enabled := false;
  dirlistviewClick(Sender);
  txtNotToRemove.Text := CactusConfig.strCleanLibNotToRemove;
End;

procedure TFrmCleanLibrary.FormDestroy(Sender: TObject);
begin
  CactusConfig.strCleanLibNotToRemove := txtNotToRemove.Text;
end;

//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

procedure TFrmCleanLibrary.lstRemoveClick(Sender: TObject);
begin
  if (lstRemove.ItemIndex = -1 )
  then
    btnUnselect.Enabled := false
  else
    btnUnselect.Enabled := true;
    
  if lstRemove.Items.Count = 0
  then
    btnRemove.Enabled := false
  else
    btnRemove.Enabled := true;
end;

//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

procedure TFrmCleanLibrary.btnResetClick(Sender: TObject);
begin
  FormCreate(Sender);
end;

//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

procedure TFrmCleanLibrary.btnUnselectClick(Sender: TObject);
var
  i: integer;
begin
  i := lstRemove.ItemIndex;
  if i <> -1
  then
  begin
    lstRemove.Items.Delete(i);
    if lstRemove.Items.Count > i-1
    then
      lstRemove.ItemIndex := i;
    lstRemoveClick(Sender);
  end;
end;

//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

procedure TFrmCleanLibrary.dirlistviewClick(Sender: TObject);
var i: integer;
begin
  if (dirlistview.ItemIndex = -1 )
  then
    btnRemove.Enabled := false
  else
    btnRemove.Enabled := true;
    
  if (dirlistview.Items.Count <> 0)
  then
    btnDisplay.Enabled := true;
end;

//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

procedure TFrmCleanLibrary.btnRemoveClick(Sender: TObject);
begin
  if (dirlistview.ItemIndex <> -1 )
  then
  begin
    dirlistview.Items.Delete(dirlistview.ItemIndex);
    btnReset.Enabled := true;
  end;

  if (dirlistview.Items.Count = 0)
  then
    btnDisplay.Enabled := false;
end;

procedure TFrmCleanLibrary.btnRemoveFromDiskClick(Sender: TObject);
var
  i, index: integer;
begin
  pnlProgress.Visible := true;
  btnBack2.Enabled := false;
  btnUnselect.Enabled := false;

  // remove from disk & collection
  for i := 1 to lstRemove.Items.Count do
  begin
    if DirectoryExists(lstRemove.Items[i-1])
    then
      RemoveDir(lstRemove.Items[i-1])
    else
    begin
      DeleteFile(lstRemove.Items[i-1]);
      index := MediaCollection.getIndexByPath(lstRemove.Items[i-1]);
      if index > 0 then
         MediaCollection.remove(index);
    end;
  end;
  lstRemove.Clear;

  lstRemoveClick(Sender);
  btnBack2.Enabled := true;
  pnlProgress.Visible := false;
end;

//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

procedure TFrmCleanLibrary.btnBackClick(Sender: TObject);
begin
  Close;
end;

//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

procedure TFrmCleanLibrary.btnBack2Click(Sender: TObject);
begin
  Panel2.Visible:= false;
  Panel1.Visible:= true;
  btnBack.Cancel:= true;
  btnDisplay.Default:=true;
end;

//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

procedure TFrmCleanLibrary.btnDisplayClick(Sender: TObject);
var
  strExtension: array of string;
  strTmp: string;
  i, j: integer;
  bDummy: Boolean;
begin
  Panel1.Visible:= false;
  Panel2.Visible:= true;
  btnBack2.Cancel:= true;
  btnRemoveFromDisk.Default:= true;
  lstRemove.Clear;
  btnBack2.Enabled:= false;
  pnlProgress.Visible := true;
  
  Application.ProcessMessages;
  
  // enumerate file extensions to skip
  i := 0;
  SetLength(strExtension, 0);
  strTmp := Trim(txtNotToRemove.Text);
  strTmp := lowercase(strTmp);
  while (strlen(PChar(strTmp)) <> 0) do
  begin
    inc(i);
    SetLength(strExtension, i);

    j := Pos(' ', strTmp);
    if j > 0
    then
    begin
      strExtension[i-1] := Copy(strTmp, 1, j-1);
      Delete(strTmp, 1, j)
    end
    else
    begin
      strExtension[i-1] := strTmp;
      strTmp := '';
    end;
  end;
  
  // start searching the folders
  for i := 1 to dirlistview.Items.Count do
    Recursive_AddDir(dirlistview.Items[i-1], @strExtension, bDummy);

  pnlProgress.Visible := false;
  btnBack2.Enabled:= true;
  lstRemoveClick(Sender);
end;

//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Procedure TFrmCleanLibrary.Recursive_AddDir
          (dir: String; strExtension: ptrStrArray; var bContainesFiles: Boolean);

Var
  mp3search,dirsearch: TSearchrec;
  tmps: string;
  i: integer;
  bContinue: Boolean;
  bContainesFiles_lokal: Boolean;
  files, folders: TStringList;
Begin
  dir := IncludeTrailingPathDelimiter(dir);
  bContainesFiles := false;
  files := TStringList.Create;
  folders := TStringList.Create;

  // list files with non-matching extensions
  If FindFirst(dir+'*', faAnyFile - faDirectory, mp3search)=0 Then
    Repeat
    Begin
      if ((mp3search.name = '.') or (mp3search.name = '..')) then
        continue;
      tmps := lowercase(ExtractFileExt(mp3search.name));
      Delete(tmps, 1, 1);
      bContinue := false;
      for i := 1 to Length(strExtension^) do
        if tmps = strExtension^[i-1] then
        begin
          bContainesFiles := true;
          bContinue := true;
          continue;
        end;
      if bContinue then continue;
      tmps := dir + mp3search.name;
      files.Add(tmps);
    End;
    Until FindNext(mp3search)<>0;
  Findclose(mp3search);
  BubbleSort(TStrings(files));
  for i := 1 to files.Count do
    lstRemove.Items.Add(files[i-1]);
  

  // walk into subfolders
  If Findfirst(dir+'*', FaDirectory, dirsearch)=0 Then
    Repeat
      If (dirsearch.name<>'..') And (dirsearch.name<>'.') Then
        if (dirsearch.attr And FaDirectory)=FaDirectory Then
        begin
          Recursive_AddDir(dir+dirsearch.name, strExtension, bContainesFiles_lokal);
          if (NOT bContainesFiles_lokal) and (chkFolders.Checked) then
            folders.Add(IncludeTrailingPathDelimiter(dir+dirsearch.name))
          else
            bContainesFiles := true;
        end;
    Until FindNext(dirsearch)<>0;
  Findclose(dirsearch);
  BubbleSort(TStrings(folders));
  for i := 1 to folders.Count do
    lstRemove.Items.Add(folders[i-1]);
End;


//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

initialization
  {$I cleanlibrary.lrs}

end.

unit CleanLibrary;

{$mode objfpc}{$H+}

interface

uses
  Classes, SysUtils, LResources, Forms, Controls, Graphics, Dialogs, StdCtrls,
  ComCtrls, ExtCtrls;

type
  strArray = array of string;
  ptrStrArray = ^strArray;
  
type

  { TFrmCleanLibrary }

  TFrmCleanLibrary = class(TForm)
    btnBack: TButton;
    btnReset: TButton;
    btnRemove: TButton;
    btnDisplay: TButton;
    btnBack2: TButton;
    btnUnselect: TButton;
    btnRemoveFromDisk: TButton;
    chkFolders: TCheckBox;
    Label3: TLabel;
    lstRemove: TListBox;
    Panel2: TPanel;
    pnlProgress: TPanel;
    txtNotToRemove: TEdit;
    Label1: TLabel;
    dirlistview: TListBox;
    Label2: TLabel;
    Panel1: TPanel;
    procedure btnBack2Click(Sender: TObject);
    procedure btnBackClick(Sender: TObject);
    procedure btnDisplayClick(Sender: TObject);
    procedure btnRemoveClick(Sender: TObject);
    procedure btnRemoveFromDiskClick(Sender: TObject);
    procedure btnResetClick(Sender: TObject);
    procedure btnUnselectClick(Sender: TObject);
    procedure dirlistviewClick(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure lstRemoveClick(Sender: TObject);
  private
    { private declarations }
    procedure Recursive_AddDir
              (dir: String; strExtension: ptrStrArray; var bContainesFiles: Boolean);
  public
  
    { public declarations }
  end; 

var
  FrmCleanLibrary: TFrmCleanLibrary;
  
implementation

uses
  mediacol, functions, config, mainform;
  


//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Procedure TFrmCleanLibrary.FormCreate(Sender: TObject);
Var i: integer;
Begin
  dirlistview.Clear;
  For i:= 0 To MediaCollection.dirlist.Count-1 Do
    Begin
      dirlistview.Items.Add(MediaCollection.dirlist[i]);
    End;
  btnReset.Enabled := false;
  dirlistviewClick(Sender);
  txtNotToRemove.Text := CactusConfig.strCleanLibNotToRemove;
End;

procedure TFrmCleanLibrary.FormDestroy(Sender: TObject);
begin
  CactusConfig.strCleanLibNotToRemove := txtNotToRemove.Text;
end;

//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

procedure TFrmCleanLibrary.lstRemoveClick(Sender: TObject);
begin
  if (lstRemove.ItemIndex = -1 )
  then
    btnUnselect.Enabled := false
  else
    btnUnselect.Enabled := true;
    
  if lstRemove.Items.Count = 0
  then
    btnRemove.Enabled := false
  else
    btnRemove.Enabled := true;
end;

//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

procedure TFrmCleanLibrary.btnResetClick(Sender: TObject);
begin
  FormCreate(Sender);
end;

//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

procedure TFrmCleanLibrary.btnUnselectClick(Sender: TObject);
var
  i: integer;
begin
  i := lstRemove.ItemIndex;
  if i <> -1
  then
  begin
    lstRemove.Items.Delete(i);
    if lstRemove.Items.Count > i-1
    then
      lstRemove.ItemIndex := i;
    lstRemoveClick(Sender);
  end;
end;

//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

procedure TFrmCleanLibrary.dirlistviewClick(Sender: TObject);
var i: integer;
begin
  if (dirlistview.ItemIndex = -1 )
  then
    btnRemove.Enabled := false
  else
    btnRemove.Enabled := true;
    
  if (dirlistview.Items.Count <> 0)
  then
    btnDisplay.Enabled := true;
end;

//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

procedure TFrmCleanLibrary.btnRemoveClick(Sender: TObject);
begin
  if (dirlistview.ItemIndex <> -1 )
  then
  begin
    dirlistview.Items.Delete(dirlistview.ItemIndex);
    btnReset.Enabled := true;
  end;

  if (dirlistview.Items.Count = 0)
  then
    btnDisplay.Enabled := false;
end;

procedure TFrmCleanLibrary.btnRemoveFromDiskClick(Sender: TObject);
var
  i, index: integer;
begin
  pnlProgress.Visible := true;
  btnBack2.Enabled := false;
  btnUnselect.Enabled := false;

  // remove from disk & collection
  for i := 1 to lstRemove.Items.Count do
  begin
    if DirectoryExists(lstRemove.Items[i-1])
    then
      RemoveDir(lstRemove.Items[i-1])
    else
    begin
      DeleteFile(lstRemove.Items[i-1]);
      index := MediaCollection.getIndexByPath(lstRemove.Items[i-1]);
      if index > 0 then
         MediaCollection.remove(index);
    end;
  end;
  lstRemove.Clear;

  lstRemoveClick(Sender);
  btnBack2.Enabled := true;
  pnlProgress.Visible := false;
end;

//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

procedure TFrmCleanLibrary.btnBackClick(Sender: TObject);
begin
  Close;
end;

//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

procedure TFrmCleanLibrary.btnBack2Click(Sender: TObject);
begin
  Panel2.Visible:= false;
  Panel1.Visible:= true;
  btnBack.Cancel:= true;
  btnDisplay.Default:=true;
end;

//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

procedure TFrmCleanLibrary.btnDisplayClick(Sender: TObject);
var
  strExtension: array of string;
  strTmp: string;
  i, j: integer;
  bDummy: Boolean;
begin
  Panel1.Visible:= false;
  Panel2.Visible:= true;
  btnBack2.Cancel:= true;
  btnRemoveFromDisk.Default:= true;
  lstRemove.Clear;
  btnBack2.Enabled:= false;
  pnlProgress.Visible := true;
  
  Application.ProcessMessages;
  
  // enumerate file extensions to skip
  i := 0;
  SetLength(strExtension, 0);
  strTmp := Trim(txtNotToRemove.Text);
  strTmp := lowercase(strTmp);
  while (strlen(PChar(strTmp)) <> 0) do
  begin
    inc(i);
    SetLength(strExtension, i);

    j := Pos(' ', strTmp);
    if j > 0
    then
    begin
      strExtension[i-1] := Copy(strTmp, 1, j-1);
      Delete(strTmp, 1, j)
    end
    else
    begin
      strExtension[i-1] := strTmp;
      strTmp := '';
    end;
  end;
  
  // start searching the folders
  for i := 1 to dirlistview.Items.Count do
    Recursive_AddDir(dirlistview.Items[i-1], @strExtension, bDummy);

  pnlProgress.Visible := false;
  btnBack2.Enabled:= true;
  lstRemoveClick(Sender);
end;

//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Procedure TFrmCleanLibrary.Recursive_AddDir
          (dir: String; strExtension: ptrStrArray; var bContainesFiles: Boolean);

Var
  mp3search,dirsearch: TSearchrec;
  tmps: string;
  i: integer;
  bContinue: Boolean;
  bContainesFiles_lokal: Boolean;
  files, folders: TStringList;
Begin
  dir := IncludeTrailingPathDelimiter(dir);
  bContainesFiles := false;
  files := TStringList.Create;
  folders := TStringList.Create;

  // list files with non-matching extensions
  If FindFirst(dir+'*', faAnyFile - faDirectory, mp3search)=0 Then
    Repeat
    Begin
      if ((mp3search.name = '.') or (mp3search.name = '..')) then
        continue;
      tmps := lowercase(ExtractFileExt(mp3search.name));
      Delete(tmps, 1, 1);
      bContinue := false;
      for i := 1 to Length(strExtension^) do
        if tmps = strExtension^[i-1] then
        begin
          bContainesFiles := true;
          bContinue := true;
          continue;
        end;
      if bContinue then continue;
      tmps := dir + mp3search.name;
      files.Add(tmps);
    End;
    Until FindNext(mp3search)<>0;
  Findclose(mp3search);
  BubbleSort(files);
  for i := 1 to files.Count do
    lstRemove.Items.Add(files[i-1]);
  

  // walk into subfolders
  If Findfirst(dir+'*', FaDirectory, dirsearch)=0 Then
    Repeat
      If (dirsearch.name<>'..') And (dirsearch.name<>'.') Then
        if (dirsearch.attr And FaDirectory)=FaDirectory Then
        begin
          Recursive_AddDir(dir+dirsearch.name, strExtension, bContainesFiles_lokal);
          if (NOT bContainesFiles_lokal) and (chkFolders.Checked) then
            folders.Add(IncludeTrailingPathDelimiter(dir+dirsearch.name))
          else
            bContainesFiles := true;
        end;
    Until FindNext(dirsearch)<>0;
  Findclose(dirsearch);
  BubbleSort(folders);
  for i := 1 to folders.Count do
    lstRemove.Items.Add(folders[i-1]);
End;


//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

initialization
  {$I cleanlibrary.lrs}

end.

