unit FileListBox;

interface

uses
  System.Drawing, System.Collections, System.ComponentModel,
  System.Windows.Forms, {System.Data,} System.IO;

type
  SelectedFileChangedEventHandler = procedure(sender: System.Object; e: System.EventArgs) of object;
  FileDoubleClickedEventHandler = procedure(sender: System.Object; e: System.EventArgs; filename :String) of object;

type
  TFileListBox = class(System.Windows.Forms.UserControl)
  {$REGION 'Designer Managed Code'}
  strict private
    /// <summary>
    /// Required designer variable.
    /// </summary>
    Components: System.ComponentModel.Container;
    ListBox1: System.Windows.Forms.ListBox;
    /// <summary>
    /// Required method for Designer support - do not modify
    /// the contents of this method with the code editor.
    /// </summary>
    procedure InitializeComponent;
    procedure ListBox1_DoubleClick(sender: System.Object; e: System.EventArgs);
    procedure ListBox1_SelectedIndexChanged(sender: System.Object; e: System.EventArgs);
    procedure ListBox1_MouseDown(sender: System.Object; e: System.Windows.Forms.MouseEventArgs);
    procedure ListBox1_MouseUp(sender: System.Object; e: System.Windows.Forms.MouseEventArgs);
    procedure ListBox1_MouseEnter(sender: System.Object; e: System.EventArgs);
    procedure ListBox1_MouseHover(sender: System.Object; e: System.EventArgs);
    procedure ListBox1_MouseLeave(sender: System.Object; e: System.EventArgs);
    procedure ListBox1_MouseMove(sender: System.Object; e: System.Windows.Forms.MouseEventArgs);
  {$ENDREGION}

  {$REGION 'Prywatne pola i metody'}
  strict private
  	//pola wewnetrzne
		listaKatalogow :array of String;
		listaPlikow :array of String;
		listaDyskow :array of String;
		pokazujDwieKropki :Boolean;
		//pola przechowujace ustawienia komponentu
		sciezkaKatalogu :String;
		uwzglednijKatalogi :Boolean;
		uwzglednijPliki :Boolean;
		uwzglednijDyski :Boolean;
		uwzglednijKatalogNadrzedny :Boolean;
		filtr :String;
    mozliweZmienianieKatalogow :Boolean;
    //metody
    procedure PobierzZawartoscKatalogu;
  {$ENDREGION}

  {$REGION 'Wlasnosci'}
  protected
    procedure setDirectoryPath(ADirectoryPath :String);
    procedure setFilter(AFilter :String);
    procedure setDoubleDotVisible(ADoubleDotVisible :Boolean);
		procedure setDirectoriesVisible(ADirectoriesVisible :Boolean);
    procedure setFilesVisible(AFilesVisible :Boolean);
    procedure setDrivesVisible(ADrivesVisible :Boolean);
    function getFileName :String;

  published
    [Category('Directory'),Description('The DirectoryPath determines relative or absolute path of directory visible at component.')]
    property DirectoryPath :String read sciezkaKatalogu write setDirectoryPath;

		[Category('Directory'),Description('The Filter determines the mask of files in the list.')]
		property Filter :String read filtr write setFilter;

		[Category('Directory'),Description('The DoubleDotVisible determines if symbol of ***** directory should be displayed.')]
		property DoubleDotVisible :Boolean read uwzglednijKatalogNadrzedny write setDoubleDotVisible;

		[Category('Directory'),Description('The DirectoriesVisible determines if list of directories should be displayed.')]
		property DirectoriesVisible :Boolean read uwzglednijKatalogi write setDirectoriesVisible;

		[Category('Directory'),Description('The FilesVisible determines if list of files should be displayed.')]
    property FilesVisible :Boolean read uwzglednijPliki write setFilesVisible;

		[Category('Directory'),Description('The DrivesVisible determines if list of drives should be displayed.')]
		property DrivesVisible :Boolean read uwzglednijDyski write setDrivesVisible;

		[Category('Directory'),Description('The DirectoryChangeAllowed determines if user can change current directory by double-clicking on directory in the list.')]
    property DirectoryChangeAllowed :Boolean read mozliweZmienianieKatalogow write mozliweZmienianieKatalogow;

		[Category('Directory'),Description('The FileName expose the full path and filename to selected item.')]
		property FileName :String read getFileName;
  {$ENDREGION}

  {$REGION 'Zdarzenia'}
  //SelectedFileChanged
  strict private
    FSelectedFileChanged :SelectedFileChangedEventHandler;
  published
    [Category('Directory'), Description('Occures when file selectecion is changed.')]
    property SelectedFileChanged :SelectedFileChangedEventHandler add FSelectedFileChanged remove FSelectedFileChanged;
  strict protected
    procedure OnSelectedFileChanged(e: System.EventArgs); virtual;

  //DirectoryPathChanged
  strict private
    FDirectoryPathChanged :EventHandler;
  published
    [Category('Directory'), Description('Occures when directory presented in component is changed.')]
    property DirectoryPathChanged :EventHandler add FDirectoryPathChanged remove FDirectoryPathChanged;
  strict protected
    procedure OnDirectoryPathChanged(e: System.EventArgs); virtual;

  //FileDoubleClicked
  strict private
    FFileDoubleClicked :FileDoubleClickedEventHandler;
  published
    [Category('Directory'), Description('Occures when one of the files in the list is double clicked.')]
    property FileDoubleClicked :FileDoubleClickedEventHandler add FFileDoubleClicked remove FFileDoubleClicked;
  strict protected
    procedure OnFileDoubleClicked(e: System.EventArgs; filename :String); virtual;
  {$ENDREGION}

  strict protected
    /// <summary>
    /// Clean up any resources being used.
    /// </summary>
    procedure Dispose(Disposing: Boolean); override;
  private
    wartownik :FileSystemWatcher;
    procedure UstawWartownika;
    procedure PrzyZmianie(source :System.Object; e :FileSystemEventArgs);
    procedure PrzyZmianie_Renamed(source :System.Object; e :RenamedEventArgs);
  public
    constructor Create;
    procedure Refresh; override;
  end;

  [assembly: RuntimeRequiredAttribute(TypeOf(TFileListBox))]

implementation

uses
  System.Globalization;

{$AUTOBOX ON}

{$REGION 'Windows Form Designer generated code'}
/// <summary>
/// Required method for Designer support -- do not modify
/// the contents of this method with the code editor.
/// </summary>
procedure TFileListBox.InitializeComponent;
begin
  Self.ListBox1 := System.Windows.Forms.ListBox.Create;
  Self.SuspendLayout;
  // 
  // ListBox1
  // 
  Self.ListBox1.Dock := System.Windows.Forms.DockStyle.Fill;
  Self.ListBox1.HorizontalScrollbar := True;
  Self.ListBox1.ItemHeight := 16;
  Self.ListBox1.Location := System.Drawing.Point.Create(0, 0);
  Self.ListBox1.Name := 'ListBox1';
  Self.ListBox1.Size := System.Drawing.Size.Create(376, 340);
  Self.ListBox1.TabIndex := 0;
  Include(Self.ListBox1.MouseDown, Self.ListBox1_MouseDown);
  Include(Self.ListBox1.DoubleClick, Self.ListBox1_DoubleClick);
  Include(Self.ListBox1.MouseHover, Self.ListBox1_MouseHover);
  Include(Self.ListBox1.MouseUp, Self.ListBox1_MouseUp);
  Include(Self.ListBox1.MouseMove, Self.ListBox1_MouseMove);
  Include(Self.ListBox1.MouseEnter, Self.ListBox1_MouseEnter);
  Include(Self.ListBox1.MouseLeave, Self.ListBox1_MouseLeave);
  Include(Self.ListBox1.SelectedIndexChanged, Self.ListBox1_SelectedIndexChanged);
  // 
  // TFileListBox
  // 
  Self.Controls.Add(Self.ListBox1);
  Self.Name := 'TFileListBox';
  Self.Size := System.Drawing.Size.Create(376, 344);
  Self.ResumeLayout(False);
end;
{$ENDREGION}

constructor TFileListBox.Create;
begin
  inherited Create;
  //
  // Required for Windows Form Designer support
  //
  InitializeComponent;

  //inicjacja prywatnych pol klasy
	pokazujDwieKropki:=True;
	sciezkaKatalogu:=nil;
	uwzglednijKatalogi:=True;
	uwzglednijPliki:=True;
	uwzglednijDyski:=True;
	uwzglednijKatalogNadrzedny:=True;
	filtr:=nil;
  mozliweZmienianieKatalogow:=True;

  //zapelnianie listy
  PobierzZawartoscKatalogu;
  UstawWartownika;
end;

procedure TFileListBox.ListBox1_MouseMove(sender: System.Object; e: System.Windows.Forms.MouseEventArgs);
begin
  OnMouseMove(e);
end;

procedure TFileListBox.ListBox1_MouseLeave(sender: System.Object; e: System.EventArgs);
begin
  OnMouseLeave(e);
end;

procedure TFileListBox.ListBox1_MouseHover(sender: System.Object; e: System.EventArgs);
begin
  OnMouseHover(e);
end;

procedure TFileListBox.ListBox1_MouseEnter(sender: System.Object; e: System.EventArgs);
begin
  OnMouseEnter(e);
end;

procedure TFileListBox.ListBox1_MouseUp(sender: System.Object; e: System.Windows.Forms.MouseEventArgs);
begin
  OnMouseUp(e);
end;

procedure TFileListBox.ListBox1_MouseDown(sender: System.Object; e: System.Windows.Forms.MouseEventArgs);
begin
  OnMouseDown(e);
end;

procedure TFileListBox.ListBox1_SelectedIndexChanged(sender: System.Object; e: System.EventArgs);
begin
  OnClick(e);
  OnSelectedFileChanged(e);
end;

procedure TFileListBox.ListBox1_DoubleClick(sender: System.Object; e: System.EventArgs);
var
  przesuniecie,numer,poczatekPlikow,poczatekDyskow :Integer;
  nowaSciezkaKatalogu,sciezkaPliku :String;
begin
  if not mozliweZmienianieKatalogow then Exit;

  //ustalanie pozycji grup elementow
  przesuniecie:=0;
  if pokazujDwieKropki and uwzglednijKatalogi then przesuniecie:=1;
	numer:=listBox1.SelectedIndex-przesuniecie; //+1 dla [..]
  poczatekPlikow:=0;
	if uwzglednijKatalogi then poczatekPlikow:=Length(listaKatalogow);
  poczatekDyskow:=poczatekPlikow;
  if uwzglednijPliki then poczatekDyskow:=poczatekDyskow+Length(listaPlikow);

	//zmiana katalogu
	nowaSciezkaKatalogu:=nil;
	if numer=-1 then nowaSciezkaKatalogu:=sciezkaKatalogu+'\..';
	if (numer>=0) and (numer<poczatekPlikow) then nowaSciezkaKatalogu:=listaKatalogow[numer];
	if numer>=poczatekDyskow then nowaSciezkaKatalogu:=listaDyskow[numer-poczatekDyskow];
	if (nowaSciezkaKatalogu<>nil) and (Directory.Exists(nowaSciezkaKatalogu)) then
		begin
		sciezkaKatalogu:=Path.GetFullPath(nowaSciezkaKatalogu);
		PobierzZawartoscKatalogu;
    OnDirectoryPathChanged(EventArgs.Empty);
    wartownik.Path:=sciezkaKatalogu;
		end;

  //podwojne klikniecie pliku
  sciezkaPliku:=nil;
  if (numer>=poczatekPlikow) and (numer<poczatekDyskow) then sciezkaPliku:=listaPlikow[numer-poczatekPlikow];
  if (sciezkaPliku<>nil) and (System.IO.File.Exists(sciezkaPliku)) then
    OnFileDoubleClicked(e,Path.GetFullPath(sciezkaPliku));
end;

procedure TFileListBox.Dispose(Disposing: Boolean);
begin
  if Disposing then
  begin
    if Components <> nil then
      Components.Dispose();
  end;
  inherited Dispose(Disposing);
end;

procedure TFileListBox.PobierzZawartoscKatalogu;
type TablicaObiektow = array of TObject;
var
  i :Integer;
  sciezka :String;
begin
if sciezkaKatalogu=nil then sciezkaKatalogu:=Directory.GetCurrentDirectory;

pokazujDwieKropki:=(sciezkaKatalogu<>Path.GetPathRoot(sciezkaKatalogu))
                   and uwzglednijKatalogNadrzedny;

if (not Directory.Exists(sciezkaKatalogu)) then
		raise Exception.Create('Katalog '+sciezkaKatalogu+' nie istnieje!');

ListBox1.Items.Clear;

if uwzglednijKatalogi then
		begin
		if pokazujDwieKropki then ListBox1.Items.Add('[..]');
		listaKatalogow:=Directory.GetDirectories(sciezkaKatalogu);
    System.Array.Sort(listaKatalogow);
  	{for i:=0 to Length(listaKatalogow)-1 do
    	listBox1.Items.Add('['+Path.GetFileName(listaKatalogow[i])+']');}
    for sciezka in listaKatalogow do
      listBox1.Items.Add('['+Path.GetFileName(sciezka)+']');
		end;
if uwzglednijPliki then
		begin
    if (filtr<>nil) then
      listaPlikow:=Directory.GetFiles(sciezkaKatalogu,filtr)
      else
      listaPlikow:=Directory.GetFiles(sciezkaKatalogu);
      System.Array.Sort(listaPlikow);
      {for i:=0 to Length(listaPlikow)-1 do
    	  listBox1.Items.Add(Path.GetFileName(listaPlikow[i]));}
      for sciezka in listaPlikow do
        listBox1.Items.Add(Path.GetFileName(sciezka));
		end;
if uwzglednijDyski then
		begin
		listaDyskow:=Directory.GetLogicalDrives();
		{for i:=0 to Length(listaDyskow)-1 do
	    listBox1.Items.Add('<'+listaDyskow[i].Substring(0,2)+'>');}
    for sciezka in listaDyskow do
        listBox1.Items.Add('<'+sciezka.Substring(0,2)+'>');
		end;
end;

procedure TFileListBox.setDirectoryPath(ADirectoryPath :String);
begin
sciezkaKatalogu:=ADirectoryPath;
PobierzZawartoscKatalogu;
OnDirectoryPathChanged(EventArgs.Empty);
wartownik.Path:=sciezkaKatalogu;
end;

procedure TFileListBox.setFilter(AFilter :String);
begin
filtr:=AFilter;
PobierzZawartoscKatalogu;
end;

procedure TFileListBox.setDoubleDotVisible(ADoubleDotVisible :Boolean);
begin
uwzglednijKatalogNadrzedny:=ADoubleDotVisible;
PobierzZawartoscKatalogu;
end;

procedure TFileListBox.setDirectoriesVisible(ADirectoriesVisible :Boolean);
begin
uwzglednijKatalogi:=ADirectoriesVisible;
PobierzZawartoscKatalogu;
end;

procedure TFileListBox.setFilesVisible(AFilesVisible :Boolean);
begin
uwzglednijPliki:=AFilesVisible;
PobierzZawartoscKatalogu;
end;

procedure TFileListBox.setDrivesVisible(ADrivesVisible :Boolean);
begin
uwzglednijDyski:=ADrivesVisible;
PobierzZawartoscKatalogu;
end;

function TFileListBox.getFileName :String;
var
  przesuniecie,numer,poczatekPlikow,poczatekDyskow :Integer;
  pelnaSciezka :String;
begin
  if listBox1.SelectedIndex<0 then
    begin
    Result:=nil;
    Exit;
    end;

  przesuniecie:=0;
  if pokazujDwieKropki and uwzglednijKatalogi then przesuniecie:=1;
	numer:=listBox1.SelectedIndex-przesuniecie; //+1 dla [..]
  poczatekPlikow:=0;
	if uwzglednijKatalogi then poczatekPlikow:=Length(listaKatalogow);
  poczatekDyskow:=poczatekPlikow;
  if uwzglednijPliki then poczatekDyskow:=poczatekDyskow+Length(listaPlikow);

	pelnaSciezka:=nil; //ta wartosc zostanie gdy brak zaznaczonego elementu
	if (numer=-1) and (przesuniecie=1) then pelnaSciezka:=sciezkaKatalogu+'\..';
	if (numer>=0) and (numer<poczatekPlikow) then pelnaSciezka:=listaKatalogow[numer];
  if (numer>=poczatekPlikow) and (numer<poczatekDyskow) then pelnaSciezka:=listaPlikow[numer-poczatekPlikow];
  if numer>=poczatekDyskow then pelnaSciezka:=listaDyskow[numer-poczatekDyskow];

	if (pelnaSciezka=nil) then pelnaSciezka:='';

  Result:=Path.GetFullPath(pelnaSciezka);
end;

procedure TFileListBox.OnSelectedFileChanged(e: System.EventArgs);
begin
  if Assigned(FSelectedFileChanged) then FSelectedFileChanged(Self,e);
end;

procedure TFileListBox.OnDirectoryPathChanged(e: System.EventArgs);
begin
  if Assigned(FDirectoryPathChanged) then FDirectoryPathChanged(Self,e);
end;

procedure TFileListBox.OnFileDoubleClicked(e: System.EventArgs; filename :String);
begin
  if Assigned(FFileDoubleClicked) then FFileDoubleClicked(Self,e,filename);
end;

procedure TFileListBox.Refresh;
begin
inherited;
PobierzZawartoscKatalogu;
end;

procedure TFileListBox.UstawWartownika;
begin
//nic nie robimy jezeli obiekt już istnieje
if wartownik<>nil then Exit;

//pierwsze uruchomienie
wartownik:=FileSystemWatcher.Create;

wartownik.Path:= sciezkaKatalogu;
wartownik.NotifyFilter:= NotifyFilters.FileName or NotifyFilters.DirectoryName;

//wiazanie zdarzen wartownika z metoda Refresh
Include(wartownik.Changed,PrzyZmianie);
Include(wartownik.Created,PrzyZmianie);
Include(wartownik.Deleted,PrzyZmianie);
Include(wartownik.Renamed,PrzyZmianie_Renamed);

//aktywacja obserwowania
wartownik.EnableRaisingEvents:=True;
end;

procedure TFileListBox.PrzyZmianie(source :System.Object; e :FileSystemEventArgs);
begin
PobierzZawartoscKatalogu;
end;

procedure TFileListBox.PrzyZmianie_Renamed(source :System.Object; e :RenamedEventArgs);
begin
PobierzZawartoscKatalogu;
end;

end.
