unit uFilterListUI;
{$INCLUDE 'Filters.inc'}

interface

uses
  Windows, SysUtils, Classes, Generics.Collections,
  {$INCLUDE 'DelphiFarUnits.inc'} ,
  uFilter,
  uConsts, uMessages;

type
  TFilterListDialog = class(TFarDialog)
  private
    fList: TFilterList;
    fFrame: TFarDialog.TDoubleBox;
    fListboxFilters: TFarDialog.TListbox;
    fButtonOK: TFarDialog.TButton;
    fButtonCancel: TFarDialog.TButton;
    fButtonNew: TFarDialog.TButton;
    fButtonCopy: TFarDialog.TButton;
    fButtonDelete: TFarDialog.TButton;
    fButtonEdit: TFarDialog.TButton;
    fButtonUp: TFarDialog.TButton;
    fButtonDown: TFarDialog.TButton;
  protected
    procedure Clear; override;
    procedure Resize(ScreenWidth, ScreenHeight: integer); override;
    function DialogProc(Msg, Param1, Param2: TIntPtr; var DlgProcResult: TIntPtr): boolean; override;
    procedure ListboxFiltersChange(Sender: TFarDialog.TListbox; NewItemIndex: TIntPtr; var AllowChange: boolean);
    procedure ListboxFiltersKeyPress(Sender: TFarDialog.TControl; Key, ControlState: DWORD; var Handled: Boolean);
    procedure ButtonUpClick(Sender: TFarDialog.TButton; var StopProcessing: boolean);
    procedure ButtonDownClick(Sender: TFarDialog.TButton; var StopProcessing: boolean);
    procedure ButtonNewClick(Sender: TFarDialog.TButton; var StopProcessing: boolean);
    procedure ButtonCopyClick(Sender: TFarDialog.TButton; var StopProcessing: boolean);
    procedure ButtonEditClick(Sender: TFarDialog.TButton; var StopProcessing: boolean);
    procedure ButtonDeleteClick(Sender: TFarDialog.TButton; var StopProcessing: boolean);
    procedure UpdateButtons(SelectedItemIndex: integer); overload;
    procedure UpdateButtons; overload;
    function GetFilterAsText(Item: TFilter): string;
    procedure DeleteFilter(Index: integer);
    procedure MoveFilter(Index: integer; Up: boolean);
    procedure EditFilter(Index: integer);
    procedure CopyFilter(Index: integer);
    procedure NewFilter(Index: integer);
    property Frame: TFarDialog.TDoubleBox read fFrame;
    property ListboxFilters: TFarDialog.TListbox read fListboxFilters;
    property ButtonOK: TFarDialog.TButton read fButtonOK;
    property ButtonCancel: TFarDialog.TButton read fButtonCancel;
    property ButtonUp: TFarDialog.TButton read fButtonUp;
    property ButtonDown: TFarDialog.TButton read fButtonDown;
    property ButtonNew: TFarDialog.TButton read fButtonNew;
    property ButtonCopy: TFarDialog.TButton read fButtonCopy;
    property ButtonDelete: TFarDialog.TButton read fButtonDelete;
    property ButtonEdit: TFarDialog.TButton read fButtonEdit;
  public
    constructor Create;
    destructor Destroy; override;
    function Execute: boolean;
    property List: TFilterList read fList write fList;
      // Note: Contents of the list will be modified even if the dialog is cancelled
  end;

implementation

{ TFilterListDialog }

procedure TFilterListDialog.ButtonCopyClick(Sender: TFarDialog.TButton; var StopProcessing: boolean);
begin
  CopyFilter(ListboxFilters.ItemIndex);
  StopProcessing := True;
end;

procedure TFilterListDialog.ButtonDeleteClick(Sender: TFarDialog.TButton; var StopProcessing: boolean);
begin
  DeleteFilter(ListboxFilters.ItemIndex);
  StopProcessing := True;
end;

procedure TFilterListDialog.ButtonDownClick(Sender: TFarDialog.TButton; var StopProcessing: boolean);
begin
  MoveFilter(ListboxFilters.ItemIndex, False);
  StopProcessing := True;
end;

procedure TFilterListDialog.ButtonEditClick(Sender: TFarDialog.TButton; var StopProcessing: boolean);
begin
  EditFilter(ListboxFilters.ItemIndex);
  StopProcessing := True;
end;

procedure TFilterListDialog.ButtonNewClick(Sender: TFarDialog.TButton; var StopProcessing: boolean);
begin
  NewFilter(ListboxFilters.ItemIndex);
  StopProcessing := True;
end;

procedure TFilterListDialog.ButtonUpClick(Sender: TFarDialog.TButton; var StopProcessing: boolean);
begin
  MoveFilter(ListboxFilters.ItemIndex, True);
  StopProcessing := True;
end;

procedure TFilterListDialog.Clear;
begin
  inherited;
  fFrame := nil;
  fListboxFilters := nil;
  fButtonOK := nil;
  fButtonCancel := nil;
  fButtonNew := nil;
  fButtonCopy := nil;
  fButtonDelete := nil;
  fButtonEdit := nil;
  fButtonUp := nil;
  fButtonDown := nil;
end;

procedure TFilterListDialog.CopyFilter(Index: integer);
var
  Filter: TFilter;
begin
  if (Index < 0) or (Index >= List.Count) then
    Exit;
  Filter := List[Index].Clone;
  try
    if Filter.Configure then
    begin
      List.Insert(Index, Filter);
      ListboxFilters.Items.Insert(Index, GetFilterAsText(Filter));
      Filter := nil;
      UpdateButtons;
      ActiveControl := ListboxFilters;
    end;
  finally
    FreeAndNil(Filter);
  end;
end;

constructor TFilterListDialog.Create;
begin
  inherited Create(PLUGIN_FILTERLIST_GUID);
  fFrame := AddDoubleBox(0, 0, 0, 0, GetMsg(MTitle));
  fListboxFilters := AddListbox(0, 0, 0, 0, '');
  fListboxFilters.OnChange := ListboxFiltersChange;
  fListboxFilters.OnKeyPress := ListboxFiltersKeyPress;
  fButtonOK := AddButton(0, 0, GetMsg(MOk));
  fButtonOK.Default := True;
  fButtonCancel := AddButton(0, 0, GetMsg(MCancel));
  fButtonUp := AddButton(0, 0, GetMsg(MUp));
  fButtonUp.NoClose := True;
  fButtonUp.OnClick := ButtonUpClick;
  fButtonDown := AddButton(0, 0, GetMsg(MDown));
  fButtonDown.NoClose := True;
  fButtonDown.OnClick := ButtonDownClick;
  fButtonNew := AddButton(0, 0, GetMsg(MNew));
  fButtonNew.NoClose := True;
  fButtonNew.OnClick := ButtonNewClick;
  fButtonCopy := AddButton(0, 0, GetMsg(MCopy));
  fButtonCopy.NoClose := True;
  fButtonCopy.OnClick := ButtonCopyClick;
  fButtonDelete := AddButton(0, 0, GetMsg(MDelete));
  fButtonDelete.NoClose := True;
  fButtonDelete.OnClick := ButtonDeleteClick;
  fButtonEdit := AddButton(0, 0, GetMsg(MEdit));
  fButtonEdit.NoClose := True;
  fButtonEdit.OnClick := ButtonEditClick;
end;

procedure TFilterListDialog.DeleteFilter(Index: integer);
begin
  if (Index < 0) or (Index >= List.Count) then
    Exit;
  ListboxFilters.Items[Index].Free;
  List.Delete(Index);
  UpdateButtons;
  ActiveControl := ListboxFilters;
end;

destructor TFilterListDialog.Destroy;
begin
  inherited;
end;

function TFilterListDialog.DialogProc(Msg, Param1, Param2: TIntPtr; var DlgProcResult: TIntPtr): boolean;
begin
  case Msg of
    DN_INITDIALOG:
      begin
        ListboxFilters.ItemIndex := 0;
        UpdateButtons;
        Result := True;
      end;
    DN_LISTCHANGE:
      Result := HandleLISTCHANGE(Param1, Param2, DlgProcResult);
    DN_BTNCLICK:
      Result := HandleBTNCLICK(Param1, Param2, DlgProcResult);
    {$IFDEF FAR3_UP}
    DN_CONTROLINPUT:
    {$ELSE}
    DN_KEY:
    {$ENDIF}
      Result := HandleCONTROLINPUT(Param1, Param2, DlgProcResult);
    else
      Result := inherited;
  end;
end;

procedure TFilterListDialog.EditFilter(Index: integer);
begin
  if (Index < 0) or (Index >= List.Count) then
    Exit;
  if not List[Index].Configure then
    Exit;
  ListboxFilters.Items[Index].Text := GetFilterAsText(List[Index]);
  UpdateButtons;
  ActiveControl := ListboxFilters;
end;

function TFilterListDialog.Execute: boolean;
var
  Button: TFarDialog.TButton;
  i: integer;
begin
  Result := False;
  Resize(Screen.Width, Screen.Height);
  ListboxFilters.Items.Clear;
  for i := 0 to Pred(List.Count) do
  begin
    ListboxFilters.Items.Add(GetFilterAsText(List[i]));
  end;
  if BuildDialog(Frame.Width + 6, Frame.Height + 2) then
  begin
    if inherited Execute(Button) then
    begin
      if Button = ButtonOK then
      begin
        Result := True;
      end;
    end;
  end;
end;

function TFilterListDialog.GetFilterAsText(Item: TFilter): string;
var
  Wildcard: string;
  MaxWildcardLength: integer;
  Caption: string;
  MaxCaptionLength: integer;
begin
  MaxCaptionLength := ListboxFilters.Width div 2;
  MaxWildcardLength := ListboxFilters.Width - 6 - MaxCaptionLength;
  Wildcard := Item.FileMaskStr;
  Caption := Item.Description;
  if Caption = '' then
    Caption := Item.Name;
  Result := Format('%-*.*s %*s', [MaxCaptionLength, MaxCaptionLength, Caption, MaxWildcardLength, Wildcard]);
end;

procedure TFilterListDialog.ListboxFiltersChange(Sender: TFarDialog.TListbox; NewItemIndex: TIntPtr; var AllowChange: boolean);
begin
  UpdateButtons(NewItemIndex);
end;

procedure TFilterListDialog.ListboxFiltersKeyPress(Sender: TFarDialog.TControl; Key, ControlState: DWORD; var Handled: Boolean);
var
  SelectedItemIndex: integer;
  ShiftState: DWORD;
begin
  ShiftState := ControlStateToShiftKeys(ControlState);
  SelectedItemIndex := ListboxFilters.ItemIndex;
  // New filter
  if (Key = VK_INSERT)
    or ((Key = VK_F4) and (ShiftState = KEY_SHIFT))
    or ((Key = Ord('N')) and ((ShiftState = KEY_CTRL) or (ShiftState = KEY_RCTRL)))
  then
  begin
    NewFilter(SelectedItemIndex);
    Handled := True;
    Exit;
  end;
  // All other functions must use a valid filter
  if (SelectedItemIndex < 0) or (SelectedItemIndex >= List.Count) then
    Exit;
  // Copy filter
  if ((Key = VK_F5) and (ShiftState = 0))
    or ((Key = Ord('C')) and ((ShiftState = KEY_CTRL) or (ShiftState = KEY_RCTRL)))
  then
  begin
    CopyFilter(SelectedItemIndex);
    Handled := True;
    Exit;
  end;
  // Edit filter
  if ((Key = VK_F4) and (ShiftState = 0))
    or ((Key = Ord('E')) and ((ShiftState = KEY_CTRL) or (ShiftState = KEY_RCTRL)))
  then
  begin
    EditFilter(SelectedItemIndex);
    Handled := True;
    Exit;
  end;
  // Delete filter
  if (Key = VK_DELETE)
    or (Key = VK_BACK)
    or (Key = VK_F8)
  then
  begin
    DeleteFilter(SelectedItemIndex);
    Handled := True;
    Exit;
  end;
  // Move Up
  if (Key = VK_UP) and ((ShiftState = KEY_CTRL) or (ShiftState = KEY_RCTRL))
  then
  begin
    MoveFilter(SelectedItemIndex, True);
    Handled := True;
    Exit;
  end;
  // Move down
  if (Key = VK_DOWN) and ((ShiftState = KEY_CTRL) or (ShiftState = KEY_RCTRL))
  then
  begin
    MoveFilter(SelectedItemIndex, False);
    Handled := True;
    Exit;
  end;
end;

procedure TFilterListDialog.MoveFilter(Index: integer; Up: boolean);
var
  OtherIndex: integer;
begin
  if (Index < 0) or (Index >= List.Count) then
    Exit;
  if Up then
  begin
    if Index < 1 then
      Exit;
    OtherIndex := Pred(Index);
  end
  else
  begin
    if Index >= Pred(List.Count) then
      Exit;
    OtherIndex := Succ(Index);
  end;
  ListboxFilters.Items.Exchange(Index, OtherIndex);
  List.Exchange(Index, OtherIndex);
  ListboxFilters.ItemIndex := OtherIndex;
  ActiveControl := ListboxFilters;
end;

procedure TFilterListDialog.NewFilter(Index: integer);
var
  FilterClasses: TStringList;
  Filter: TFilter;
  FilterIndex: integer;
  i: integer;
begin
  Filter := nil;
  try
    FilterClasses := TStringList.Create;
    try
      for i := 0 to Pred(TRegisteredFilters.Count) do
        FilterClasses.Add(TRegisteredFilters.Items[i].Name);
      if FilterClasses.Count <= 0 then
        raise EFilterError.Create(GetMsg(MNoFiltersRegistered));
      if FilterClasses.Count = 1 then
      begin
        Filter := TRegisteredFilters.Items[0].Create;
      end
      else
      begin
        if ShowMenu(PLUGIN_MENU_FILTERCLASS, GetMsg(MSelectFilterType), '', FilterClasses, FilterIndex) then
          Filter := TRegisteredFilters.Items[FilterIndex].Create
        else
          Exit;
      end;
    finally
      FreeAndNil(FilterClasses);
    end;
    if Filter.Configure then
    begin
      List.Insert(Index, Filter);
      ListboxFilters.Items.Insert(Index, GetFilterAsText(Filter));
      Filter := nil;
      UpdateButtons;
      ActiveControl := ListboxFilters;
    end;
  finally
    FreeAndNil(Filter);
  end;
end;

procedure TFilterListDialog.Resize(ScreenWidth, ScreenHeight: integer);
begin
  inherited;
  Frame.Left := 3;
  Frame.Width := ScreenWidth - 10;
  Frame.Top := 1;
  Frame.Height := ScreenHeight - 4;
  ListboxFilters.AlignTo(Frame, 2, 1, -4, -5);
  ButtonOK.Top := Frame.Top + Frame.Height - 2;
  ButtonCancel.Top := ButtonOK.Top;
  ButtonUp.Top := ButtonOK.Top;
  ButtonDown.Top := ButtonOK.Top;
  ButtonNew.Top := ButtonOK.Top - 1;
  ButtonCopy.Top := ButtonOK.Top - 1;
  ButtonDelete.Top := ButtonOK.Top - 1;
  ButtonEdit.Top := ButtonOK.Top - 1;
end;

procedure TFilterListDialog.UpdateButtons;
begin
  UpdateButtons(ListboxFilters.ItemIndex);
end;

procedure TFilterListDialog.UpdateButtons(SelectedItemIndex: integer);
begin
  ButtonDelete.Enabled := (List.Count > 0) and (SelectedItemIndex >= 0);
  ButtonEdit.Enabled := (List.Count > 0) and (SelectedItemIndex >= 0);
  ButtonUp.Enabled := (List.Count > 0) and (SelectedItemIndex > 0);
  ButtonDown.Enabled := (List.Count > 0) and (SelectedItemIndex < Pred(List.Count));
end;

end.
