unit _TemplateFileList;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, _TemplateFile, StdCtrls, ExtCtrls, Menus, Buttons,
  ComCtrls, TypeDefs, VirtualTrees;

type
  TNewFileSelectingEvent = procedure(FileInfo: TFileInfo; var allowed: Boolean) of object;

  TForm_TemplateFileList = class(TForm_TemplateFile)
    panel_files: TPanel;
    splitter_content: TSplitter;
    panel_content: TPanel;
    filepopup: TPopupMenu;
    popup_separator2: TMenuItem;
    popup_linkshere: TMenuItem;
    popup_separator: TMenuItem;
    popup_import: TMenuItem;
    popup_export: TMenuItem;
    importd: TOpenDialog;
    exportd: TSaveDialog;
    FilePages: TPageControl;
    tab_files: TTabSheet;
    panel_extension: TPanel;
    label_sort2: TLabel;
    label_sort1: TLabel;
    label_extension: TLabel;
    btn_sort_id_asc: TSpeedButton;
    btn_sort_id_desc: TSpeedButton;
    btn_sort_name_asc: TSpeedButton;
    btn_sort_name_desc: TSpeedButton;
    btn_sort_ext_asc: TSpeedButton;
    btn_sort_ext_desc: TSpeedButton;
    combo_extension: TComboBox;
    check_zerobyte: TCheckBox;
    edit_filtername: TEdit;
    check_filtername: TCheckBox;
    filelist: TListBox;
    tab_meta: TTabSheet;
    filelist_meta: TVirtualStringTree;
    procedure NewCon(ID: Integer);

    procedure check_filternameClick(Sender: TObject);
    procedure check_zerobyteClick(Sender: TObject);
    procedure combo_extensionClick(Sender: TObject);
    procedure btn_sortClick(Sender: TObject);
    procedure listClick(Sender: TObject);
    procedure listMouseDown(Sender: TObject; Button: TMouseButton;
      Shift: TShiftState; X, Y: Integer);

    procedure popup_importClick(Sender: TObject);
    procedure popup_exportClick(Sender: TObject);
    procedure popup_opentool(Sender: TObject);
    procedure popup_linkshereClick(Sender: TObject);
    procedure filepopupPopup(Sender: TObject);
    procedure filelist_metaGetText(Sender: TBaseVirtualTree; Node: PVirtualNode;
      Column: TColumnIndex; TextType: TVSTTextType; var CellText: WideString);
    procedure filelist_metaInitChildren(Sender: TBaseVirtualTree;
      Node: PVirtualNode; var ChildCount: Cardinal);
    procedure filelist_metaPaintText(Sender: TBaseVirtualTree;
      const TargetCanvas: TCanvas; Node: PVirtualNode; Column: TColumnIndex;
      TextType: TVSTTextType);
    procedure filelist_metaFocusChanging(Sender: TBaseVirtualTree; OldNode,
      NewNode: PVirtualNode; OldColumn, NewColumn: TColumnIndex;
      var Allowed: Boolean);
    procedure filelist_metaFocusChanged(Sender: TBaseVirtualTree;
      Node: PVirtualNode; Column: TColumnIndex);
  private
    FSortBy: TSortType;
    FAllowedExts: String;
    FAllowMultiSelect: Boolean;
    FOnNewFileSelecting: TNewFileSelectingEvent;
    procedure SetAllowedExts(exts: String);
    procedure SetMultiSelect(allow: Boolean);
  public
    constructor Create(AOwner: TComponent); override;
    procedure RecreateExtList;
    procedure LoadFileNames;
    procedure SetFileFilters(pattern, extension: String; zerobytes: Boolean);
    property AllowedExts: String read FAllowedExts write SetAllowedExts;
    property AllowMultiSelect: Boolean read FAllowMultiSelect write SetMultiSelect;
    property OnNewFileSelecting: TNewFileSelectingEvent read FOnNewFileSelecting write FOnNewFileSelecting;
  end;

implementation
{$R *.dfm}
uses ConnectionManager, Exporters, Functions, StrUtils, WhatLinksHere, Main,
  _BaseTemplate, _MetaTypes, Data, _MetaManager, _FileTypes;

type
  PNodeData = ^TNodeData;

  TNodeData = record
    Field: TObject;
  end;

function AddVSTEntry(AVST: TCustomVirtualStringTree; ANode: PVirtualNode;
  ARecord: TNodeData): PVirtualNode;
var
  Data: PNodeData;
begin
  Result := AVST.AddChild(ANode);
  Data   := AVST.GetNodeData(Result);
  AVST.ValidateNode(Result, False);
  Data^ := ARecord;
end;



procedure TForm_TemplateFileList.RecreateExtList;
var
  i:    Integer;
  exts: TStrings;
begin
  combo_extension.Items.Clear;
  if FConnectionID > -1 then
  begin
    combo_extension.Items.Add('_All files_ (' +
      IntToStr(ConManager.Connection[FConnectionID].GetFileCount) + ')');
    exts := nil;
    exts := ConManager.Connection[FConnectionID].GetExtensionsList(EF_ExtCount);
    for i := 0 to exts.Count - 1 do
      if Length(FAllowedExts) > 0 then
      begin
        if Pos(MidStr(exts.Strings[i],1,4), FAllowedExts) > 0 then
          combo_extension.Items.Add(exts.Strings[i]);
      end
      else
        combo_extension.Items.Add(exts.Strings[i]);
    combo_extension.ItemIndex := 0;
    combo_extensionClick(Self);
    exts.Free;
  end;
end;

procedure TForm_TemplateFileList.LoadFileNames;
var
  Extension: String;
  no_zero_bytes: Boolean;
  pattern: String;
  files: TStrings;
  root: TExtensions;
  i: Integer;
  data: TNodeData;
  node: PVirtualNode;
begin
  if FConnectionID > -1 then
  begin
    Extension := MidStr(combo_extension.Items.Strings[combo_extension.ItemIndex], 1, 4);
    no_zero_bytes := not check_zerobyte.Checked;
    pattern := '';
    if check_filtername.Checked then
      pattern := edit_filtername.Text;
    if Extension = '_All' then
      if Length(FAllowedExts) > 0 then
        Extension := FAllowedExts
      else
        Extension := '';

    files := nil;
    files := ConManager.Connection[FConnectionID].GetFilesList(extension, pattern, no_zero_bytes, FSortBy);

    filelist.Visible := False;
    filelist.Items.Clear;
    if files.Count > 0 then
      filelist.Items.AddStrings(files);
    filelist.Visible := True;

    //VST
    filelist_meta.Clear;
    filelist_meta.BeginUpdate;
{    root := ConManager.Connection[FConnectionID].MetaData.Root;
    for i := 0 to High(root) do
    begin
      data.Field := root[i];
      node := AddVSTEntry(filelist_meta, nil, data);
      filelist_meta.HasChildren[node] := True;
    end;
    filelist_meta.EndUpdate;
}  end;
end;


procedure TForm_TemplateFileList.NewCon(ID: Integer);
begin
  RecreateExtList;
end;

procedure TForm_TemplateFileList.popup_exportClick(Sender: TObject);
var
  id: Integer;
  ext: String;
begin
  id := ConManager.Connection[FConnectionID].ExtractFileIDOfName(filelist.Items.Strings[filelist.ItemIndex]);
  ext := RightStr(filelist.Items.Strings[filelist.ItemIndex], 4);
  exportd.Filter := 'Files of matching extension (*.' + ext + ')|*.' + ext + '|All files|*.*';
  exportd.DefaultExt := ext;
  if exportd.Execute then
    ExportDatFile(FConnectionID, id, exportd.FileName);
end;

procedure TForm_TemplateFileList.popup_importClick(Sender: TObject);
var
  id: Integer;
  finfo: TFileInfo;
  fs: TFileStream;
begin
  if CR_EditDat in ConManager.Connection[FConnectionID].ChangeRights then
  begin
    id := ConManager.Connection[FConnectionID].ExtractFileIDOfName(filelist.Items.Strings[filelist.ItemIndex]);
    finfo := ConManager.Connection[FConnectionID].GetFileInfo(id);

    importd.Filter := 'Files of matching extension (*.' + finfo.Extension + ')|*.' +
          finfo.Extension + '|All files|*.*';
    if importd.Execute then
    begin
      fs := TFileStream.Create(importd.FileName, fmOpenRead);
      if fs.Size <> finfo.Size then
      begin
        if not (CR_ResizeDat in ConManager.Connection[FConnectionID].ChangeRights) then
        begin
          ShowMessage('Can''t import ' + ExtractFilename(importd.FileName) +
            ', file has to have same size as file in .dat with this backend.' + CrLf +
            'Size of file in .dat: ' + FormatFileSize(finfo.Size) + CrLf +
            'Size of chosen file: ' + FormatFileSize(fs.Size));
          Exit;
        end else begin
          if MessageBox(Self.Handle,
              PChar('File has different size from the file in the .dat.' + CrLf +
                    'Size of file in .dat: ' + FormatFileSize(finfo.Size) + CrLf +
                    'Size of chosen file: ' + FormatFileSize(fs.Size) + CrLf +
                    'Replace anyway?'), PChar('Different size'), MB_YESNO + MB_ICONWARNING) = ID_NO then
          begin
            Exit;
          end;
        end;
      end;
      ConManager.Connection[FConnectionID].UpdateDatFile(id, fs);
      Self.listClick(Self);
      fs.Free;
    end;
  end else begin
    ShowMessage('Editing .dat-contents not allowed with this backend.');
  end;
end;


procedure TForm_TemplateFileList.popup_linkshereClick(Sender: TObject);
begin
  Form_WhatLinksHere.ConID := FConnectionID;
  Form_WhatLinksHere.FileID := FSelectedFile.ID;
  Form_WhatLinksHere.SenderForm := Self;
  Form_WhatLinksHere.Show;
end;

procedure TForm_TemplateFileList.popup_opentool(Sender: TObject);
var
  sender_name, context: String;
  id: Integer;
begin
  sender_name := TComponent(Sender).Name;
  id := ConManager.Connection[FConnectionID].ExtractFileIDOfName(filelist.Items.Strings[filelist.ItemIndex]);
  context := MidStr(sender_name, Pos('_', sender_name) + 1, Length(sender_name) - Pos('_', sender_name));
  Form_Main.open_child(context, FConnectionID, id);
end;


procedure TForm_TemplateFileList.filepopupPopup(Sender: TObject);
var
  ext: String;
  i: Integer;
begin
  ext := RightStr(filelist.Items.Strings[filelist.ItemIndex], 4);
  for i := 0 to High(ToolList) do
  begin
    filepopup.Items.Items[i].Enabled := True;
    if Length(ToolList[i].exts) > 0 then
      if Pos(ext, ToolList[i].exts) = 0 then
        filepopup.Items.Items[i].Enabled := False;
  end;
end;


procedure TForm_TemplateFileList.check_zerobyteClick(Sender: TObject);
begin
  LoadFileNames;
end;

procedure TForm_TemplateFileList.btn_sortClick(Sender: TObject);
begin
  if btn_sort_id_asc.Down then
    FSortBy := ST_IDAsc
  else if btn_sort_id_desc.Down then
    FSortBy := ST_IDDesc
  else if btn_sort_name_asc.Down then
    FSortBy := ST_NameAsc
  else if btn_sort_name_desc.Down then
    FSortBy := ST_NameDesc
  else if btn_sort_ext_asc.Down then
    FSortBy := ST_ExtAsc
  else if btn_sort_ext_desc.Down then
    FSortBy := ST_ExtDesc;
  LoadFileNames;
end;

procedure TForm_TemplateFileList.check_filternameClick(Sender: TObject);
begin
  edit_filtername.Enabled := not check_filtername.Checked;
  LoadFileNames;
end;

procedure TForm_TemplateFileList.combo_extensionClick(Sender: TObject);
begin
  LoadFileNames;
end;

procedure TForm_TemplateFileList.listClick(Sender: TObject);
var
  fileid: Integer;
begin
  if filelist.ItemIndex > -1 then
  begin
    fileid := ConManager.Connection[FConnectionID].ExtractFileIDOfName(
          filelist.Items.Strings[filelist.ItemIndex]);
    FSelectedFile := ConManager.Connection[FConnectionID].GetFileInfo(fileid);
    if Assigned(FOnNewFileSelected) then
      FOnNewFileSelected(FSelectedFile);
  end;
end;

procedure TForm_TemplateFileList.listMouseDown(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
var
  pt: TPoint;
begin
  pt.X := x;
  pt.Y := y;
  if Shift = [ssRight] then
  begin
    filelist.ItemIndex := filelist.ItemAtPos(pt, true);
    Self.listClick(Self);
  end;
end;



procedure TForm_TemplateFileList.SetAllowedExts(exts: String);
begin
  FAllowedExts := exts;
  RecreateExtList;
end;

procedure TForm_TemplateFileList.SetFileFilters(pattern, extension: String;
  zerobytes: Boolean);
var
  i: Integer;
begin
  if Length(pattern) > 0 then
    Self.edit_filtername.Text := pattern;
  Self.check_filtername.Checked := Length(pattern) > 0;
  if Length(extension) > 0 then
  begin
    for i := 0 to Self.combo_extension.Items.Count - 1 do
      if Pos(extension, Self.combo_extension.Items.Strings[i]) > 0 then
        Break;
    if i < Self.combo_extension.Items.Count then
      Self.combo_extension.ItemIndex := i
    else
      Self.combo_extension.ItemIndex := -1;
  end;
  Self.check_zerobyte.Checked := zerobytes;
  Self.LoadFileNames;
end;

procedure TForm_TemplateFileList.SetMultiSelect(allow: Boolean);
begin
  FAllowMultiSelect := allow;
  filelist.MultiSelect := allow;
end;



constructor TForm_TemplateFileList.Create(AOwner: TComponent);
var
  i: Integer;
  item: TMenuItem;
begin
  inherited;
  filelist_meta.NodeDataSize := SizeOf(TNodeData);
  filelist_meta.Font.Charset := AppSettings.CharSet;
  filelist_meta.Clear;

  FAllowedExts := '';
  FAllowMultiSelect := False;
  FOnNewConnection := NewCon;
  UpdateConList;
  if Length(ToolList) > 0 then
  begin
    for i := 0 to High(ToolList) do
    begin
      item := TMenuItem.Create(filepopup);
      item.Name := 'popup_' + ToolList[i].context;
      item.Caption := 'Open with ' + ToolList[i].name;
      item.OnClick := Self.popup_opentool;
      filepopup.Items.Insert(i, item);
    end;
  end;
end;



procedure TForm_TemplateFileList.filelist_metaFocusChanged(
  Sender: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex);
var
  data: PNodeData;
begin
  inherited;
  data := Sender.GetNodeData(Node);
  if data.Field is TResource then
  begin
    if Assigned(FOnNewFileSelected) then
    begin
      FOnNewFileSelected(TResource(data.Field).FileInfo);
    end;
  end;
end;

procedure TForm_TemplateFileList.filelist_metaFocusChanging(
  Sender: TBaseVirtualTree; OldNode, NewNode: PVirtualNode; OldColumn,
  NewColumn: TColumnIndex; var Allowed: Boolean);
var
  data: PNodeData;
begin
  inherited;
  data := Sender.GetNodeData(NewNode);
  if data.Field is TResource then
  begin
    if Assigned(FOnNewFileSelecting) then
    begin
      FOnNewFileSelecting(TResource(data.Field).FileInfo, Allowed);
    end;
  end;
end;

procedure TForm_TemplateFileList.filelist_metaGetText(Sender: TBaseVirtualTree;
  Node: PVirtualNode; Column: TColumnIndex; TextType: TVSTTextType;
  var CellText: WideString);
var
  data: PNodeData;
begin
  inherited;
{  data := Sender.GetNodeData(Node);
  if data.Field is TExtension then
  begin
    CellText := TExtension(data.Field).Ext;
  end;
  if data.Field is TFile then
  begin
    if TFile(data.Field).FileInfo.Name = '' then
      CellText := TFile(data.Field).FileInfo.Extension + ' (unnamed)'
    else
      CellText := TFile(data.Field).FileInfo.Extension + ': ' + TFile(data.Field).FileInfo.Name;
  end;
}end;


procedure TForm_TemplateFileList.filelist_metaInitChildren(
  Sender: TBaseVirtualTree; Node: PVirtualNode; var ChildCount: Cardinal);
var
  data: PNodeData;
  i: Integer;
  newdata: TNodeData;
  newnode: PVirtualNode;
  id: Integer;
begin
  inherited;
  data := Sender.GetNodeData(Node);
  Self.Cursor := crHourGlass;
  Application.ProcessMessages;
{  if data.Field is TExtension then
  begin
    if TExtension(data.Field).FileCount = 0 then
      TExtension(data.Field).InitList;
    for i := 0 to TExtension(data.Field).FileCount - 1 do
    begin
      id := TExtension(data.Field).Files[i];
      ConManager.Connection[FConnectionID].MetaData.InitFile(id);
      newdata.Field := ConManager.Connection[FConnectionID].MetaData.FileById[id];
      newnode := AddVSTEntry(filelist_meta, Node, newdata);
      if ConManager.Connection[FConnectionID].MetaData.FileById[id].ChildCount > 0 then
        filelist_meta.HasChildren[newnode] := True;
      ChildCount := filelist_meta.ChildCount[node];
    end;
  end;
  if data.Field is TFile then
  begin
    for i := 0 to TFile(data.Field).ChildCount - 1 do
    begin
      id := TFile(data.Field).LinkByIndex[i].DestID;
      ConManager.Connection[FConnectionID].MetaData.InitFile(id);
      newdata.Field := ConManager.Connection[FConnectionID].MetaData.FileById[id];
      newnode := AddVSTEntry(filelist_meta, Node, newdata);
      if ConManager.Connection[FConnectionID].MetaData.FileById[id].ChildCount > 0 then
        filelist_meta.HasChildren[newnode] := True;
    end;
    ChildCount := filelist_meta.ChildCount[node];
  end;
}  Self.Cursor := crDefault;
end;

procedure TForm_TemplateFileList.filelist_metaPaintText(
  Sender: TBaseVirtualTree; const TargetCanvas: TCanvas; Node: PVirtualNode;
  Column: TColumnIndex; TextType: TVSTTextType);
var
  Data: PNodeData;
begin
  Data     := Sender.GetNodeData(Node);
  if Data.Field is TResource then
  begin
    if Length(TResource(Data.Field).FileInfo.Name) = 0 then
      TargetCanvas.Font.Color := $C06060;
    if TResource(Data.Field).FileInfo.Size = 0 then
      TargetCanvas.Font.Color := $2020A0;
  end;
end;

end.
