{
   Double Commander
   -------------------------------------------------------------------------
   Dialog window when copying/moving files confirming filenames and other options

   Copyright (C) 2009-2019  Alexander Koblov (alexx2000@mail.ru)

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program. If not, see <http://www.gnu.org/licenses/>.
}

unit fCopyMoveDlg;

{$mode objfpc}{$H+}

interface

uses
  SysUtils, Classes, Controls, Forms, StdCtrls, Buttons, ExtCtrls, Menus,
  ActnList, KASPathEdit, uFileSource, uFileViewNotebook, uFileSourceOperation,
  uFileSourceOperationOptionsUI, uOperationsManager, uFormCommands;

type

  TCopyMoveDlgType = (cmdtCopy, cmdtMove);

  TCurrentCopyDlgNameSelectionStep = (ccdnssWholeCompleteFilename, ccdnssJustFilenameNoExt, ccdnssJustFilenameWithExt, ccdnssJustExtension, ccdnssJustPath, ccdnssInvalid); //Note: ccdnssInvalid *must* be the last one.

  { TfrmCopyDlg }

  TfrmCopyDlg = class(TForm, IFormCommands)
    btnCancel: TBitBtn;
    btnOK: TBitBtn;
    btnAddToQueue: TBitBtn;
    btnOptions: TButton;
    btnSaveOptions: TButton;
    edtDst: TKASPathEdit;
    grpOptions: TGroupBox;
    lblCopySrc: TLabel;
    mnuQueue2: TMenuItem;
    mnuQueue3: TMenuItem;
    mnuQueue4: TMenuItem;
    mnuQueue5: TMenuItem;
    mnuQueue1: TMenuItem;
    mnuNewQueue: TMenuItem;
    pmQueuePopup: TPopupMenu;
    pnlButtons: TPanel;
    pnlOptions: TPanel;
    pnlSelector: TPanel;
    btnCreateSpecialQueue: TBitBtn;
    procedure btnCreateSpecialQueueClick(Sender: TObject);
    procedure btnOKClick(Sender: TObject);
    procedure btnOptionsClick(Sender: TObject);
    procedure btnSaveOptionsClick(Sender: TObject);
    procedure btnStartModeClick(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure FormKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
    procedure FormPaint(Sender: TObject);
    procedure frmCopyDlgShow(Sender: TObject);
    procedure mnuNewQueueClick(Sender: TObject);
    procedure mnuQueueNumberClick(Sender: TObject);
    procedure pnlOptionsResize(Sender: TObject);

  private
    FCommands: TFormCommands;
    FDialogType: TCopyMoveDlgType;
    noteb: TFileViewNotebook;
    FFileSource: IFileSource;
    FOperationOptionsUIClass: TFileSourceOperationOptionsUIClass;
    FOperationOptionsUI: TFileSourceOperationOptionsUI;
    FCurrentCopyDlgNameSelectionStep: TCurrentCopyDlgNameSelectionStep;

    function GetQueueIdentifier: TOperationsManagerQueueIdentifier;
    procedure SetQueueIdentifier(AValue: TOperationsManagerQueueIdentifier);
    function ShowTabsSelector: integer;
    procedure TabsSelector(Sender: TObject);
    procedure TabsSelectorMouseDown(Sender: TObject; {%H-}Button: TMouseButton;
                                    {%H-}Shift: TShiftState; {%H-}X, {%H-}Y: Integer);
    procedure ShowOptions(bShow: Boolean);
    procedure UpdateSize;

    property {%H-}Commands: TFormCommands read FCommands implements IFormCommands;

  public
    constructor Create(TheOwner: TComponent; DialogType: TCopyMoveDlgType;
                       AFileSource: IFileSource;
                       AOperationOptionsUIClass: TFileSourceOperationOptionsUIClass); reintroduce;
    constructor Create(TheOwner: TComponent); override;
    procedure SetOperationOptions(Operation: TFileSourceOperation);

    property QueueIdentifier: TOperationsManagerQueueIdentifier read GetQueueIdentifier write SetQueueIdentifier;

  published
    procedure cm_AddToQueue(const Params: array of String);
    procedure cm_ToggleSelectionInName(const {%H-}Params: array of string);
  end;


implementation

{$R *.lfm}

uses
  LazUTF8, fMain, LCLType, LCLVersion, uGlobs, uLng, uHotkeyManager, DCStrUtils;

const
  HotkeysCategory = 'Copy/Move Dialog';

var
  FQueueIdentifier: TOperationsManagerQueueIdentifier = SingleQueueId;

constructor TfrmCopyDlg.Create(TheOwner: TComponent; DialogType: TCopyMoveDlgType;
                               AFileSource: IFileSource;
                               AOperationOptionsUIClass: TFileSourceOperationOptionsUIClass);
begin
  FDialogType := DialogType;
  FFileSource := AFileSource;
  FOperationOptionsUIClass := AOperationOptionsUIClass;
  FCommands := TFormCommands.Create(Self);
  inherited Create(TheOwner);
end;

constructor TfrmCopyDlg.Create(TheOwner: TComponent);
begin
  Create(TheOwner, cmdtCopy, nil, nil);
end;

procedure TfrmCopyDlg.SetOperationOptions(Operation: TFileSourceOperation);
begin
  if Assigned(FOperationOptionsUI) then
    FOperationOptionsUI.SetOperationOptions(Operation);
end;

procedure TfrmCopyDlg.cm_AddToQueue(const Params: array of String);
var
  Value: Integer;
  sQueueId: String;
begin
  if FQueueIdentifier = ModalQueueId then Exit;
  if GetParamValue(Params, 'queueid', sQueueId) and TryStrToInt(sQueueId, Value) then
    begin
      if Value < 0 then
        mnuNewQueue.Click
      else
        FQueueIdentifier := Value
    end
  else
    FQueueIdentifier := SingleQueueId;
  ModalResult := btnAddToQueue.ModalResult;
end;

{ TfrmCopyDlg.cm_ToggleSelectionInName }
procedure TfrmCopyDlg.cm_ToggleSelectionInName(const Params: array of string);
var
  iInitialStart, iInitialLength, iFullLength, iPath, iExtension, iSelStart, iSelLenght: integer;

begin
  iFullLength := UTF8Length(edtDst.Text);
  iPath := UTF8Length(ExtractFilePath(edtDst.Text));
  iExtension := UTF8Length(ExtractFileExt(edtDst.Text));

  iInitialStart := edtDst.SelStart;
  iInitialLength := edtDst.SelLength;

  if iFullLength = 0 then
    exit;

  repeat
    FCurrentCopyDlgNameSelectionStep := TCurrentCopyDlgNameSelectionStep((ord(FCurrentCopyDlgNameSelectionStep)+1) mod ord(ccdnssInvalid));

    case FCurrentCopyDlgNameSelectionStep of
      ccdnssJustFilenameNoExt:
      begin
        iSelStart := iPath;
        iSelLenght := iFullLength - (iPath+iExtension);
      end;

      ccdnssJustFilenameWithExt:
      begin
        iSelStart := iPath;
        iSelLenght := iFullLength - iPath;
      end;

      ccdnssJustExtension:
      begin
        iSelStart := succ(iPath + (iFullLength - (iPath+iExtension)));
        iSelLenght := pred(iExtension);
      end;

      ccdnssJustPath:
      begin
        iSelStart := 0;
        iSelLenght := pred(iPath);
      end;

      else //Which includes also "ccdnssWholeCompleteFilename".
        begin
          iSelStart := 0;
          iSelLenght := iFullLength;
        end;
    end;
  until ((iSelStart <> iInitialStart) OR (iInitialLength <> iSelLenght)) AND (iSelLenght > 0);

  edtDst.SelStart := iSelStart;
  edtDst.SelLength := iSelLenght;
end;

procedure TfrmCopyDlg.TabsSelector(Sender: TObject);
begin
  edtDst.Text := noteb[(Sender as TButton).tag].CurrentPath;
end;

procedure TfrmCopyDlg.TabsSelectorMouseDown(Sender: TObject; Button: TMouseButton;
                                            Shift: TShiftState; X, Y: Integer);
begin
  edtDst.Text := noteb[(Sender as TButton).tag].CurrentPath;
end;

function TfrmCopyDlg.ShowTabsSelector: integer;
var
  btnS, btnL: TButton;
  i, tc: PtrInt;
  st: TStringList;
  s: String;
begin
  noteb := frmMain.NotActiveNotebook;

  if noteb.PageCount = 1 then
    begin
      Result:=0;
      exit;
    end;

  tc := noteb.PageCount;
  st := TStringList.Create;
  try
    for i:=0 to tc-1 do
    if noteb.View[i].Visible then
      begin
        s:=noteb[i].CurrentPath;
        if st.IndexOf(s)=-1 then
          begin
            st.Add(s);
            st.Objects[st.Count-1]:=TObject(i);
          end;
      end;

    tc := st.Count;
    btnL := nil;
    if tc>10 then tc:=10;
    for i:=0 to tc-1 do
      begin
        btnS:= TButton.Create(Self);
        btnS.TabOrder:= i;
        btns.Parent:=pnlSelector;
        btns.Tag:=PtrInt(st.Objects[i]);
        if i < 9 then
          btns.Caption := IntToStr(i+1) + ' - ' + noteb.Page[PtrInt(st.Objects[i])].Caption
        else
          btns.Caption := '0 - ' + noteb.Page[PtrInt(st.Objects[i])].Caption;

        btnS.OnClick := @TabsSelector;
        btnS.OnMouseDown := @TabsSelectorMouseDown;

        btns.AutoSize:=True;
        btns.Left := 0;
        btns.Top := 0;
        btns.Anchors :=[akLeft,akTop,akBottom];
        btns.Visible := True;

        if btnL <> nil then
        begin
          btns.AnchorSideLeft.Control := btnL;
          btns.AnchorSideLeft.Side := asrRight;
        end;

        btnL := btnS;
        if (Self.Width < (btnL.Left+btnL.Width+200)) then // 200 = Ok + Cancel
          Self.Width := (btnL.Left+btnL.Width+200);
      end;

  finally
    st.Free;
  end;
end;

function TfrmCopyDlg.GetQueueIdentifier: TOperationsManagerQueueIdentifier;
begin
  Result:= FQueueIdentifier;
end;

procedure TfrmCopyDlg.SetQueueIdentifier(AValue: TOperationsManagerQueueIdentifier);
begin
  FQueueIdentifier:= AValue;
end;

procedure TfrmCopyDlg.frmCopyDlgShow(Sender: TObject);
begin
  case FDialogType of
    cmdtCopy:
      begin
        Caption := rsDlgCp;
      end;

    cmdtMove:
      begin
        Caption := rsDlgMv;
      end;
  end;

  if gShowCopyTabSelectPanel then
    ShowTabsSelector;

  edtDst.SelectAll;
  FCurrentCopyDlgNameSelectionStep := ccdnssWholeCompleteFilename;
  edtDst.SetFocus;

  btnCreateSpecialQueue.Left:= btnAddToQueue.BoundsRect.Right;
end;

procedure TfrmCopyDlg.mnuNewQueueClick(Sender: TObject);
begin
  FQueueIdentifier := OperationsManager.GetNewQueueIdentifier;
  ModalResult := btnAddToQueue.ModalResult;
end;

procedure TfrmCopyDlg.mnuQueueNumberClick(Sender: TObject);
var
  NewQueueNumber: TOperationsManagerQueueIdentifier;
begin
  if TryStrToInt(Copy((Sender as TMenuItem).Name, 9, 1), NewQueueNumber) then
  begin
    FQueueIdentifier := NewQueueNumber;
    ModalResult := btnAddToQueue.ModalResult;
  end;
end;

procedure TfrmCopyDlg.pnlOptionsResize(Sender: TObject);
begin
  UpdateSize;
end;

procedure TfrmCopyDlg.FormKeyDown(Sender: TObject; var Key: Word;
  Shift: TShiftState);
begin
  if gShowCopyTabSelectPanel and (Integer(Key) - VK_1 < pnlSelector.ControlCount) then
  begin
    if (ssAlt in Shift) or (edtDst.Focused = False) then
    begin
      if (Key >= VK_1) and (Key <= VK_9) then
      begin
        TButton(pnlSelector.Controls[Key - VK_1]).Click;
        Key := 0;
      end;

      if (Key = VK_0) and (pnlSelector.ControlCount = 10) then
      begin
        TButton(pnlSelector.Controls[9]).Click;
        Key := 0;
      end;
    end;
  end;

  {$IF lcl_fullversion < 093100}
  case Key of
    VK_ESCAPE: // Must handle before drag manager. Lazarus bug 0020676.
      begin
        ModalResult := mrCancel;
        Key := 0;
      end;
  end;
  {$ENDIF}
end;

procedure TfrmCopyDlg.FormPaint(Sender: TObject);
begin
  OnPaint := nil;
  AutoSize := False;
  Constraints.MinWidth := 0;
end;

procedure TfrmCopyDlg.btnCreateSpecialQueueClick(Sender: TObject);
begin
  btnCreateSpecialQueue.PopupMenu.PopUp;
end;

procedure TfrmCopyDlg.btnOKClick(Sender: TObject);
begin
  if FQueueIdentifier <> ModalQueueId then
    FQueueIdentifier := FreeOperationsQueueId;
end;

procedure TfrmCopyDlg.btnOptionsClick(Sender: TObject);
begin
  Constraints.MinWidth := Width;
  ShowOptions(not pnlOptions.Visible);
  btnOptions.Enabled := not btnOptions.Enabled;
  ClientWidth := pnlOptions.Width + ChildSizing.LeftRightSpacing * 2;
  pnlOptions.Anchors := pnlOptions.Anchors + [akRight];
  MoveToDefaultPosition;
  Constraints.MinWidth := 0;
end;

procedure TfrmCopyDlg.btnSaveOptionsClick(Sender: TObject);
begin
  if Assigned(FOperationOptionsUI) then
    FOperationOptionsUI.SaveOptions;
end;

procedure TfrmCopyDlg.btnStartModeClick(Sender: TObject);
begin
  btnOK.PopupMenu.PopUp;
end;

procedure TfrmCopyDlg.FormCreate(Sender: TObject);
var
  HMForm: THMForm;
  Hotkey: THotkey;
begin
  Constraints.MinWidth := Width;
  pnlSelector.Visible := gShowCopyTabSelectPanel;

  btnOK.Caption := rsDlgOpStart;
  if FQueueIdentifier <= FreeOperationsQueueId then FQueueIdentifier:= SingleQueueId;
  btnAddToQueue.Caption:= btnAddToQueue.Caption + ' #' + IntToStr(FQueueIdentifier);

  // Fix align of options panel and dialog size at start.
  if not pnlSelector.Visible then
    pnlOptions.Top := pnlOptions.Top -
                      (pnlSelector.Height +
                       pnlSelector.BorderSpacing.Top +
                       pnlSelector.BorderSpacing.Bottom);

  // Operation options.
  if Assigned(FOperationOptionsUIClass) then
  begin
    FOperationOptionsUI := FOperationOptionsUIClass.Create(Self, FFileSource);
    FOperationOptionsUI.Parent := grpOptions;
    FOperationOptionsUI.Align  := alClient;
  end
  else
    btnOptions.Visible := False;
  pnlOptions.Visible:= false;

  HMForm := HotMan.Register(Self, HotkeysCategory);
  Hotkey := HMForm.Hotkeys.FindByCommand('cm_AddToQueue');

  if Assigned(Hotkey) then
    btnAddToQueue.Caption := btnAddToQueue.Caption + ' (' + ShortcutsToText(Hotkey.Shortcuts) + ')';
end;

procedure TfrmCopyDlg.FormDestroy(Sender: TObject);
begin
  HotMan.UnRegister(Self);
end;

procedure TfrmCopyDlg.ShowOptions(bShow: Boolean);
begin
  pnlOptions.Visible := bShow;
  UpdateSize;
end;

procedure TfrmCopyDlg.UpdateSize;
begin
  if pnlOptions.Visible then
    Self.Height := pnlOptions.Top + pnlOptions.Height +
                   pnlOptions.BorderSpacing.Top + pnlOptions.BorderSpacing.Bottom
  else
    Self.Height := pnlOptions.Top;
end;

initialization
  TFormCommands.RegisterCommandsForm(TfrmCopyDlg, HotkeysCategory, @rsHotkeyCategoryCopyMoveDialog);

end.
