{$I-,F+,O+,X+}
unit FMPrim;

interface

uses Objects, PanelRec, FileMngr;

type
  POptList = ^TOptList;
  TOptList = object(TPanelCollection)
    procedure FreeItem(Item: Pointer); virtual;
    procedure Insert(Item: Pointer); virtual;
  end;

procedure FMDeleteItemsPrim(FManager: PPanel);
procedure FMDeletePrim(FManager: PPanel; One: Boolean);
procedure FMMkDirPrim(FManager: PPanel);
procedure CalcSelBytes(AList: PCollection; ClusterSize: Longint;
  BytesFree: Longint; var SelBytes: Longint; var AtLeastOne: Boolean);
procedure Optimize(AList: PCollection; ClusterSize: Longint;
  BytesFree: Longint; var Optimized: Boolean);
procedure OptimalFill(AList: PCollection; ADrive: Char;
  const ActionStr: String; var Optimized, Aborted: Boolean);
procedure FMCopyPrim(FManager: PPanel; const ADest: FNameStr;
  One: Boolean);
procedure FMRenMovPrim(FManager: PPanel; const ADest: FNameStr;
  One: Boolean);

implementation

uses Dos, Drivers, Views, Memory, Dialogs, HistList, App, MsgBox, DirPrim,
  Disks, DiskBase, DiskPrim, FMAction, EditFile, EditMsg, Archives, History,
  HeapUMB, LFN, DosTools, TPString, Consts, Commands, Defaults;

procedure DirExpectedMsgBox;
begin
  MessageBox(^C'Valid directory name expected.',
    nil, mfError + mfOkButton);
end;

procedure TOptList.FreeItem(Item: Pointer);
begin
end;

procedure TOptList.Insert(Item: Pointer);
var
  I: Integer;
begin
  if not Search(Item, I) then AtInsert(I, Item);
end;

procedure FMDeleteItemsPrim(FManager: PPanel);
var
  I: Integer;
  SR: PPanelRec;
  S: FNameStr;
begin
  with FManager^ do
  begin
    if (List^.Count = 0) or (Status >= stTree) then Exit;
    if (SelCount > 0) and (Settings and seIgnoreSelection = 0) then
    begin
      GetItemStr(Focused, S);
      I := 0;
      while I < List^.Count do
      begin
        SR := List^.At(I);
        if SR^.Selected and (SR^.Name[1] <> '.') then
        begin
          SetSelect(SR, False);
          List^.AtFree(I);
          if FilesBBS^.Count > I then FilesBBS^.AtFree(I);
        end
        else Inc(I);
      end;
      GetItemNum(S, I);
    end
    else
    begin
      I := Focused;
      SR := List^.At(I);
      if SR^.Name[1] <> '.' then
      begin
        SetSelect(SR, False);
        List^.AtFree(I);
        if FilesBBS^.Count > I then FilesBBS^.AtFree(I);
      end;
    end;
    SetRange(List^.Count);
    FocusItem(I);
  end;
end;

procedure FMDeletePrim(FManager: PPanel; One: Boolean);
var
  I: Integer;
  SR: PPanelRec;
  L: Longint;
  S, SS: String;
begin
  with FManager^ do
  begin
    if (List^.Count = 0) or (Status >= stInfo) then Exit;
    DeleteStatus := dsDelete;
    if not One and (SelCount > 0) and
      (Settings and seIgnoreSelection = 0) then
    begin
      L := SelCount;
      if Options and ofValidate <> 0 then
      begin
        if L = 1 then S := 'drive' else S := 'drives';
        if MessageBox(^C'OK to delete the contents of '#13^C +
          '%d selected ' + S + ' ?', @L,
          mfConfirmation + mfOkCancel) = cmCancel then Exit;
      end
      else if Status = stTree then
      begin
        S := ^C'OK to delete %d selected director';
        if L = 1 then S := S + 'y' else S := S + 'ies';
        S := S + ' ?';
        if L <> 1 then System.Insert(#13^C, S, 14);
        if MessageBox(
          S, @L, mfConfirmation + mfOkCancel) = cmCancel then Exit;
      end
      else
      begin
        if L = 1 then S := 'file' else S := 'files';
        GetListDir(SS);
        if MessageBox(^C'OK to delete %d selected ' + S + ' from'#13^C +
          TrimmedName(SS, 32) + ' ?', @L,
          mfConfirmation + mfOkCancel) = cmCancel then Exit;
      end;
      DoneSelList;
      InitSelList;
      I := 0;
      if InArchive then Archiver^.DeletePrim(False)
      else while (I < List^.Count) and (DeleteStatus <> dsCancel) do
      begin
        SR := List^.At(I);
        if SR^.Selected then
        begin
          GetRecStr(SR, S);
          if SR^.Attr and Directory <> 0 then
            DirDeletePrim(S, CRCList, True)
          else FDeletePrim(S);
          if not (DeleteStatus in [dsSkip, dsSkipAll, dsCancel]) then
          begin
            SetSelect(SR, False);
            if FMOptions and foUnsel <> 0 then FocusItem(Focused);
          end;
        end;
        Inc(I);
      end;
    end
    else
    begin
      SR := List^.At(Focused);
      if SR^.Attr and Directory <> 0 then
      begin
        if (SR^.Attr and VolumeID <> 0) and (SR^.Name[1] <> '.') then Exit;
        GetRecStr(SR, S);
        if (S[1] = '\') and (S = RootName(S)) then
          SS := ' contents of the'#13^C'network resource'#13^C
        else if S[0] < #4 then SS := ' contents of the'#13^C'drive '
        else if SR^.Name[1] = '.' then
          if InArchive and (SR^.Attr and VolumeID <> 0) then
            SS := ' contents'#13^C'of the archive'#13^C
          else SS := ' contents'#13^C'of the current directory'#13^C
        else SS := ' directory'#13^C;
        if MessageBox(^C'OK to delete the' + SS +
          TrimmedName(S, 32) + ' ?', nil,
          mfConfirmation + mfOkCancel) = cmCancel then Exit;
        if InArchive then Archiver^.DeletePrim(True)
        else if SR^.Name[1] = '.' then
          ClearDirs(AddedSlash(S), CRCList, True)
        else DirDeletePrim(S, CRCList, True);
      end
      else if Status < stTree then
      begin
        GetRecStr(SR, S);
        if MessageBox(^C'OK to delete the file'#13^C +
          TrimmedName(S, 32) + ' ?', nil,
          mfConfirmation + mfOkCancel) = cmCancel then Exit;
        if InArchive then Archiver^.DeletePrim(True)
        else FDeletePrim(S);
      end;
    end;
    Message(Desktop, evBroadcast, cmUpdateDisk, nil);
  end;
end;

procedure FMMkDirPrim(FManager: PPanel);
var
  C: Word;
  I: Integer;
  D: PDialog;
  R: TRect;
  Control: PView;
  S, SS: FNameStr;
begin
  with FManager^ do
  begin
    R.Assign(0, 0, 40, 8);
    D := InitSimpleDialog(R, 'Make directory', hcMkDirDialog);
    with D^ do
    begin
      Options := Options or ofCentered;
      R.Assign(3, 3, Size.X - 6, 4);
      Control := New(PInputLine, Init(R, 255));
      Insert(Control);
      StandardHistory(Control, FNameHistoryId);
      R.Assign(2, 2, Size.X - 3, 3);
      Insert(New(PLabel, Init(R, '~C~reate the directory', Control)));
    end;
    S := '';
    C := Application^.ExecuteDialog(D, @S, False);
    TrimSt(S);
    if (C <> cmCancel) and (S <> '') then
    begin
      if not (S[1] in ['.', '\']) and
         not ((S[0] >= #2) and (S[2] = ':')) then
      begin
        if Status = stTree then GetItemStr(Focused, SS)
        else SS := ListDir^;
        S := AddedSlash(SS) + S;
      end;
      S := ClearedSlash(lFExpand(S));
      CopyStatus := dsOverwrite;
      CancelSysErrors := False;
      MkDirPrim(S, nil);
      if CancelSysErrors then Exit;
      Message(Desktop, evBroadcast, cmUpdateDisk, nil);
      GetItemNum(S, I);
      GetItemStr(I, SS);
      if CompUCString(S, SS) = Equal then FocusItem(I);
    end;
  end;
end;

procedure GetCopyDirData(Panel: PPanel; D: PDialog;
  var Data: TCopyDirData; var ToName: FNameStr;
  var Command: Word);
begin
  Data.ToDir := ToName;
  if Panel^.Filter = '*.*' then Data.DirFilter := DirFilter
  else Data.DirFilter := Panel^.Filter;
  Data.NewMask := NewMask;
  Command := Application^.ExecuteDialog(D, @Data, False);
  if Command = cmCancel then Exit;
  ToName := Data.ToDir;
  DirFilter := Data.DirFilter;
  NewMask := Data.NewMask;
end;

procedure CalcSelBytes(AList: PCollection; ClusterSize: Longint;
  BytesFree: Longint; var SelBytes: Longint; var AtLeastOne: Boolean);
var
  I: Integer;
  L, II: Longint;
  SR: PPanelRec;
begin
  SelBytes := 0;
  AtLeastOne := False;
  for I := 0 to AList^.Count - 1 do
  begin
    SR := AList^.At(I);
    if SR^.Selected then
    begin
      II := SR^.Size div ClusterSize;
      L := II * ClusterSize;
      if L < SR^.Size then Inc(SelBytes, L + ClusterSize)
      else Inc(SelBytes, L);
      if SR^.Size <= BytesFree then AtLeastOne := True;
    end;
  end;
end;

procedure Optimize(AList: PCollection; ClusterSize: Longint;
  BytesFree: Longint; var Optimized: Boolean);
var
  I: Integer;
  L, II, LocSelBytes: Longint;
  SR: PPanelRec;
  OptList: POptList;
begin
  OptList := New(POptList, Init(32, 32, nil));
  for I := 0 to AList^.Count - 1 do
  begin
    SR := AList^.At(I);
    if SR^.Selected then OptList^.Insert(SR);
  end;
  LocSelBytes := 0;
  for I := 0 to OptList^.Count - 1 do
  begin
    SR := OptList^.At(I);
    II := SR^.Size div ClusterSize;
    L := II * ClusterSize;
    if L < SR^.Size then Inc(L, ClusterSize);
    if LocSelBytes + L <= BytesFree then
    begin
      Byte(SR^.Selected) := 255;
      Inc(LocSelBytes, L);
    end
    else Byte(SR^.Selected) := 1;
  end;
  Dispose(OptList, Done);
  Optimized := True;
end;

procedure OptimalFill(AList: PCollection; ADrive: Char;
  const ActionStr: String; var Optimized, Aborted: Boolean);
var
  AtLeastOne: Boolean;
  C: Word;
  ClusterSize, BytesFree, SelBytes: Longint;
begin
  Optimized := False;
  if ADrive <> '\' then
  begin
    Aborted := True;
    CancelSysErrors := False;
    BytesFree := FreeDiskSpace(Byte(ADrive) - $40);
    if CancelSysErrors then Exit;
    if BytesFree < 0 then BytesFree := MaxLongint;
    ClusterSize := GetClusterSize(Byte(ADrive) - $40);
    if CancelSysErrors then Exit;
    CalcSelBytes(AList, ClusterSize, BytesFree, SelBytes, AtLeastOne);
    if (SelBytes > BytesFree) and AtLeastOne then
    begin
      C := MessageBox(^C'There is not enough free space'#13^C +
        'on drive ' + ADrive + ': to ' + ActionStr +' all selected'#13^C +
        'files. Use Optimal Fill method ?',
        nil, mfConfirmation + mfYesNoCancel);
      if C = cmCancel then Exit;
      if C = cmYes then Optimize(AList, ClusterSize, BytesFree, Optimized);
    end;
  end;
  Aborted := False;
end;

procedure CountSelTotals(FManager: PPanel; Optimized: Boolean);
var
  I: Integer;
  SR: PPanelRec;
  Name: FNameStr;
begin
  with FManager^ do
  begin
    I := 0;
    while (I < List^.Count) and (CopyStatus <> dsCancel) do
    begin
      SR := List^.At(I);
      if (not Optimized and SR^.Selected) or
         (Optimized and (Byte(SR^.Selected) = 255)) then
      begin
        if SR^.Attr and Directory <> 0 then
        begin
          GetRecStr(SR, Name);
          DirCountPrim(Name, DirFilter, CRCList);
        end
        else
        begin
          BytesTotal := BytesTotal + SR^.Size;
          Inc(FilesTotal);
        end;
      end;
      Inc(I);
    end;
  end;
end;

procedure FixToName(const FromName: FNameStr; var ToName: FNameStr);
begin
  if not (ToName[1] in ['.', '\']) and
     not ((ToName[0] >= #2) and (ToName[2] = ':')) then
    ToName := AddedSlash(PrevDir(FromName)) + ToName;
end;

procedure SelFixToName(IsDir, Wild: Boolean;
  const ADest, ToData: FNameStr; var ToName: FNameStr);
begin
  if (IsDir and (ADest = '') and
    not (ToData[Length(ToData)] in [':', '\']) and
    (ToData[1] <> '.') and not Wild) and
    (DeviceDest(ToName) = '') then ToName := PrevDir(ToName);
end;

procedure SingleFixToName(IsDir, Wild, Drives, CompPrev: Boolean;
  SR: PPanelRec; const FromName, ADest, ToData: FNameStr;
  var ToName: FNameStr);
begin
  if ((SR^.Name[1] = '.') or (IsDir and (ADest = '') and
      (not CompPrev or (CompUCString(
        PrevDir(FromName), PrevDir(ToName)) <> Equal)) and not
      (ToData[Length(ToData)] in [':', '\']) and
      (ToData[1] <> '.') and not Wild and
      (SR^.Name[1] <> ':') and Drives)) and
      (DeviceDest(ToName) = '') then ToName := PrevDir(ToName);
end;

procedure SetDirLabel(const ActionStr: String;
  const FromName: FNameStr; SR: PPanelRec; InArchive: Boolean;
  var ALabel: String);
var
  S: String;
begin
  if FromName[1] = '\' then
    S := 'network resource'
  else if (FromName[0] = #2) and (FromName[2] = ':') then
    S := 'drive ' + FromName
  else if FromName[1] = '.' then
    if InArchive and (SR^.Attr and VolumeID <> 0) then
      S := 'archive'
    else S := 'current directory'
  else
  begin
    if ActionStr[1] = 'C' then ALabel := ActionStr
    else ALabel := 'Rename or move';
    ALabel := ALabel + ' the directory "' +
      TrimmedName(FromName, 29 - Length(ALabel)) + '"';
    S := '';
  end;
  if S <> '' then ALabel := ActionStr + ' the contents of the ' + S;
  ALabel := ALabel + ' to';
end;

procedure SetCopyRenFilter(Panel: PPanel);
begin
{  DisposeStr(CopyRenameFilter.Text);}
  DisposeStrUMB(CopyRenameFilter.Text);
  Move(Panel^.AdvancedFilter, CopyRenameFilter, SizeOf(TFilterRec));
{  CopyRenameFilter.Text := NewStr(Panel^.AdvancedFilter.Text^);}
  CopyRenameFilter.Text := NewStrUMB(Panel^.AdvancedFilter.Text^);
  CopyRenameFilter.UseForLists := False;
end;

procedure CopyToListPrim(FManager: PPanel; const Name, Mask: FNameStr);
var
  T: lText;
begin
  if not ThisFileExists(Name) then
  begin
    LogEvent := lcDiskWrite;
    DrawStatusLine('Creating list file ' + TrimmedName(Name, 48) + '.');
    lAssignText(T, Name);
    Rewrite(T.T);
    if IOResult <> 0 then
    begin
      CouldNotCreateMsgBox(Name);
      Exit;
    end;
    Close(T.T);
    InOutRes := 0;
  end;
  AppendSelListPrim(FManager, Name, Mask, '', True, True, False);
  with FManager^ do
    if (SelCount <> 0) and (Settings and seIgnoreSelection = 0) then
      UnselectAll;
  UpdateFiles(Name);
end;

procedure FMCopyPrim(FManager: PPanel; const ADest: FNameStr; One: Boolean);
var
  IsDir, Wild, Optimized, Aborted, AppendSel, FocusName: Boolean;
  C: Word;
  I: Integer;
  L: Longint;
  D: PDialog;
  SR: PPanelRec;
  R: TRect;
  CopyDirData: TCopyDirData;
  S, FromName, ToName: FNameStr;
label
  Start, NoPrompt;
begin
  Optimized := False;
  AppendSel := False;
  with FManager^ do
  begin
Start:
    if (Status >= stInfo) or (List^.Count = 0) then Exit;
    FocusName := False;
    CopyStatus := dsOverwrite;
    TryAll := False;
    if not One and (SelCount > 0) and
      (Settings and seIgnoreSelection = 0) then
    begin
      L := SelCount;
      if Options and ofValidate <> 0 then
        FromName := 'Copy the contents of %d selected drives to'
      else FromName := 'Copy %d selected files to';
      if L = 1 then System.Delete(FromName, Length(FromName) - 3, 1);
      FormatStr(S, FromName, L);
      IsDir := (Options and ofValidate <> 0) or DirSelected;
    end
    else
    begin
      SR := List^.At(Focused);
      if (SR^.Attr and VolumeID <> 0) and (SR^.Name[1] <> '.') then Exit;
      if SR^.Name[1] = ':' then FromName := RootName(ListDir^)
      else if SR^.Name[1] = '\' then GetRecStr(SR, FromName)
      else AddNameExt('', SR, FromName);
      IsDir := SR^.Attr and Directory <> 0;
      if IsDir then SetDirLabel('Copy', FromName, SR, InArchive, S)
      else S := 'Copy "' + TrimmedName(FromName, 39) + '" to'
    end;

    if ADest <> '' then ToName := ADest
    else if One or (Opposite^.State and sfVisible = 0) or
      (Opposite^.Status >= stInfo) then
        if One or (SelCount = 0) then ToName := FromName
        else ToName := ''
    else if Opposite^.InArchive and not InArchive then
    with Opposite^.Archiver^ do
    begin
      ToName := GetTopName;
      if ArchiveRec^.Extension <> nil then
        ToName := ArchiveRec^.Extension^ + ':' + ToName;
    end
    else if (Opposite^.Settings and seList <> 0) and not InArchive then
      ToName := 'LIST:' + Opposite^.FileListName^
    else
    begin
      if Opposite^.InArchive then ToName := Opposite^.ListDir^
      else Opposite^.GetListDir(ToName);
      ToName := AddedSlash(ToName);
    end;
    if (One or (SelCount = 0) or (Settings and seIgnoreSelection <> 0)) and
       (ToName <> '') and (ToName[Length(ToName)] = '\') and
       (Options and ofValidate = 0) and not (SR^.Name[1] in [':', '.']) and
       not InArchive and not Opposite^.InArchive then
    begin
      HistoryAdd(FNameHistoryId,
        AddedSlash(ListDir^) + JustFileName(FromName), True);
      ToName := ToName + JustFileName(FromName);
    end;
    if (ADest <> '') and (FMOptions and foDragPrompt = 0) and
       not Optimized then Goto NoPrompt;
    R.Assign(0, 0, 58, 8 + 4 * Byte(IsDir));
    if IsDir then D := InitDirCopyDialog(R, S, InArchive)
    else D := InitCopyDialog(R, S, False, False);
    if IsDir then GetCopyDirData(FManager, D, CopyDirData, ToName, C)
    else C := Application^.ExecuteDialog(D, @ToName, False);
    TrimSt(ToName);
    if (C <> cmCancel) and (ToName <> '') then
    begin
NoPrompt:
      Wild := IsWild(ToName);
      if (Pos(':', ToName) > 2) and not InArchive and
         (ToName[1] <> '+') then
      begin
        if IsDir then FromName := DirFilter
        else FromName := '';
        if UCPos('LIST:', ToName) = 1 then CopyToListPrim(FManager,
          Trim(System.Copy(ToName, 6, 255)), FromName)
        else CopyToArchive(FManager, One, False, ToName, FromName, nil);
        Exit;
      end;

      if (FManager^.Settings and seAdvancedFilter <> 0) and
         (CopyRenameFilter.Text = nil) then SetCopyRenFilter(FManager);

      if not One and (SelCount > 0) and
        (Settings and seIgnoreSelection = 0) then
      begin
        if (Length(ToName) > 1) and (ToName[1] = '+') then
        begin
          System.Delete(ToName, 1, 1);
          TrimSt(ToName);
          AppendSel := True;
        end
        else if not ((ToName[0] = #2) and (ToName[2] = ':')) and
          not Wild then ToName := AddedSlash(ToName);

        if (FMOptions and foCheckFreeSpace <> 0) and not AppendSel then
        begin
          S := lFExpand(ToName);
          if DeviceDest(S) = '' then
          begin
            OptimalFill(List, S[1], 'copy', Optimized, Aborted);
            if Aborted then Exit;
          end;
        end;

        DoneSelList;
        InitSelList;
        if InArchive then
        begin
          if IsDir then FromName := DirFilter
          else FromName := '';
          Archiver^.CopyPrim(ToName, FromName, False);
        end
        else
        begin
          ResetTotals;
          if FileCopyOptions and fsDisableCopyStat = 0 then
          begin
            CountSelTotals(FManager, Optimized);
            if CopyStatus = dsCancel then Exit;
          end;
          S := ToName;
          I := 0;
          while (I < List^.Count) and (CopyStatus <> dsCancel) do
          begin
            if AppendSel then CopyStatus := dsAppend;
            SR := List^.At(I);
            if (not Optimized and SR^.Selected) or
               (Optimized and (Byte(SR^.Selected) = 255)) then
            begin
              IsDir := SR^.Attr and Directory <> 0;
              GetRecStr(SR, FromName);
              ToName := S;
              FixToName(FromName, ToName);
              CancelSysErrors := False;
              ValidNames(FromName, ToName);
              if CancelSysErrors then Break;
              SelFixToName(IsDir, Wild, ADest, CopyDirData.ToDir, ToName);
              if IsDir then DirCopyPrim(FromName, ToName,
                DirFilter, NewMask, CRCList)
              else FCopyPrim(FromName, ToName);
              if (CopyStatus <> dsCancel) and (not Skipped or
                 (FileCopyOptions and fsUnselectSkipped <> 0)) then
              begin
                SetSelect(SR, False);
                if FMOptions and foUnsel <> 0 then FocusItem(Focused);
              end;
            end;
            Inc(I);
          end;
          if Optimized and (CopyStatus <> dsCancel) then
          begin
            Message(Desktop, evBroadcast, cmUpdateDisk, nil);
            Goto Start;
          end;
        end;
      end
      else if InArchive then
      begin
        if IsDir then Archiver^.CopyPrim(ToName, DirFilter, True)
        else Archiver^.CopyPrim(ToName, '', True);
        ToName := AddedSlash(ToName) + JustFileName(FromName);
        FocusName := True;
      end
      else
      begin
        if (Length(ToName) > 1) and (ToName[1] = '+') then
        begin
          System.Delete(ToName, 1, 1);
          TrimSt(ToName);
          CopyStatus := dsAppend;
        end;
        GetRecStr(SR, FromName);
        FixToName(FromName, ToName);
        CancelSysErrors := False;
        ValidNames(FromName, ToName);
        if CancelSysErrors then Exit;
        SingleFixToName(IsDir, Wild, Options and ofValidate <> 0, False,
          SR, FromName, ADest, CopyDirData.ToDir, ToName);
        ResetTotals;
        if IsDir then
        begin
          if Wild and (SR^.Name[1] = '.') then
          begin
            DirExpectedMsgBox;
            Exit;
          end;
          if FileCopyOptions and fsDisableCopyStat = 0 then
          begin
            DirCountPrim(FromName, DirFilter, CRCList);
            if CopyStatus = dsCancel then Exit;
          end;
          if (Settings and seUnfold <> 0) and
             (SR^.Name[1] = '.') then CopyFilesByMask(
               AddedSlash(FromName), ToName, DirFilter, NewMask, CRCList)
          else DirCopyPrim(FromName, ToName, DirFilter, NewMask, CRCList);
        end
        else
        begin
          if FileCopyOptions and fsDisableCopyStat = 0 then
          begin
            BytesTotal := SR^.Size;
            FilesTotal := 1;
          end;
          ToName := FCopyPrim(FromName, ToName);
        end;
        FocusName := CopyStatus <> dsCancel;
      end;
      Message(Desktop, evBroadcast, cmUpdateDisk, nil);
      if FocusName then
      begin
        FocusFileName(ToName);
        Opposite^.FocusFileName(ToName);
      end;
    end;
  end;
end;

procedure FMRenMovPrim(FManager: PPanel; const ADest: FNameStr;
  One: Boolean);
var
  IsDir, Wild, Optimized, Aborted, AppendSel, FocusName: Boolean;
  C: Word;
  I: Integer;
  L: Longint;
  D: PDialog;
  SR: PPanelRec;
  R: TRect;
  RenMovDirData: TCopyDirData;
  S, FromName, ToName: FNameStr;
label
  Start, NoPrompt;
begin
  Optimized := False;
  AppendSel := False;
  with FManager^ do
  begin
Start:
    if (Status >= stInfo) or (List^.Count = 0) then Exit;
    FocusName := False;
    CopyStatus := dsOverwrite;
    DeleteStatus := dsDelete;
    TryAll := False;
    if not One and (SelCount > 0) and
      (Settings and seIgnoreSelection = 0) then
    begin
      L := SelCount;
      if Options and ofValidate <> 0 then
      begin
        FromName := 'Move the contents of %d selected drives to';
        if L = 1 then System.Delete(FromName, Length(FromName) - 3, 1);
        FormatStr(S, FromName, L);
        IsDir := True;
      end
      else
      begin
        FromName := 'Rename or move %d selected files to';
        if L = 1 then System.Delete(FromName, Length(FromName) - 3, 1);
        FormatStr(S, FromName, L);
        IsDir := DirSelected;
      end;
    end
    else
    begin
      SR := List^.At(Focused);
      if SR^.Name[1] = ':' then FromName := RootName(ListDir^)
      else if SR^.Name[1] = '\' then GetRecStr(SR, FromName)
      else AddNameExt('', SR, FromName);
      IsDir := SR^.Attr and Directory <> 0;
      if IsDir then SetDirLabel('Move', FromName, SR, False, S)
      else S := 'Rename or move "' + TrimmedName(FromName, 29) + '" to'
    end;

    if ADest <> '' then ToName := ADest
    else if One or (Opposite^.State and sfVisible = 0) or
      (Opposite^.Status >= stInfo) then
        if One or (SelCount = 0) then ToName := FromName
        else ToName := ''
    else if Opposite^.InArchive and not InArchive then
    with Opposite^.Archiver^ do
    begin
      ToName := GetTopName;
      if ArchiveRec^.Extension <> nil then
        ToName := ArchiveRec^.Extension^ + ':' + ToName;
    end
    else
    begin
      if Opposite^.InArchive then ToName := Opposite^.ListDir^
      else Opposite^.GetListDir(ToName);
      ToName := AddedSlash(ToName);
    end;
    if (One or (SelCount = 0) or (Settings and seIgnoreSelection <> 0)) and
       (ToName <> '') and (ToName[Length(ToName)] = '\') and
       (Options and ofValidate = 0) and not (SR^.Name[1] in [':', '.']) and
       not InArchive and not Opposite^.InArchive then
    begin
      HistoryAdd(FNameHistoryId,
        AddedSlash(ListDir^) + JustFileName(FromName), True);
      ToName := ToName + JustFileName(FromName);
    end;
    if (ADest <> '') and (FMOptions and foDragPrompt = 0) and
       not Optimized then Goto NoPrompt;
    R.Assign(0, 0, 58, 8 + 4 * Byte(IsDir));
    if IsDir then D := InitDirRenMovDialog(R, S, InArchive)
    else D := InitRenMovDialog(R, S, False, False);
    if IsDir then GetCopyDirData(FManager, D, RenMovDirData, ToName, C)
    else C := Application^.ExecuteDialog(D, @ToName, False);
    TrimSt(ToName);
    if (C <> cmCancel) and (ToName <> '') then
    begin
NoPrompt:
      Wild := IsWild(ToName);
      if (Pos(':', ToName) > 2) and not InArchive and
         (ToName[1] <> '+') then
      begin
        if IsDir then FromName := DirFilter
        else FromName := '';
        CopyToArchive(FManager, One, True, ToName, FromName, nil);
        Exit;
      end;

      if (FManager^.Settings and seAdvancedFilter <> 0) and
         (CopyRenameFilter.Text = nil) then SetCopyRenFilter(FManager);

      if not One and (SelCount > 0) and
        (Settings and seIgnoreSelection = 0) then
      begin
        if (Length(ToName) > 1) and (ToName[1] = '+') then
        begin
          System.Delete(ToName, 1, 1);
          TrimSt(ToName);
          AppendSel := True;
        end
        else if not ((ToName[0] = #2) and (ToName[2] = ':')) and
          not Wild then ToName := AddedSlash(ToName);

        if (FMOptions and foCheckFreeSpace <> 0) and not AppendSel then
        begin
          S := lFExpand(ToName);
          if (ListDir^[1] <> S[1]) and (DeviceDest(S) = '') then
          begin
            OptimalFill(List, S[1], 'move', Optimized, Aborted);
            if Aborted then Exit;
          end;
        end;

        DoneSelList;
        InitSelList;
        ResetTotals;
        if FileCopyOptions and fsDisableCopyStat = 0 then
        begin
          CountSelTotals(FManager, Optimized);
          if CopyStatus = dsCancel then Exit;
        end;
        S := ToName;
        I := 0;
        while (I < List^.Count) and
          (CopyStatus <> dsCancel) and (DeleteStatus <> dsCancel) do
        begin
          if AppendSel then CopyStatus := dsAppend;
          SR := List^.At(I);
          if (not Optimized and SR^.Selected) or
             (Optimized and (Byte(SR^.Selected) = 255)) then
          begin
            IsDir := SR^.Attr and Directory <> 0;
            GetRecStr(SR, FromName);
            ToName := S;
            FixToName(FromName, ToName);
            CancelSysErrors := False;
            ValidNames(FromName, ToName);
            if CancelSysErrors then Break;
            SelFixToName(IsDir, Wild, ADest, RenMovDirData.ToDir, ToName);
            if IsDir then DirRenMovPrim(FromName, ToName, DirFilter,
              NewMask, CRCList, True)
            else FRenMovPrim(FromName, ToName);
            if (CopyStatus <> dsCancel) and (not Skipped or
               (FileCopyOptions and fsUnselectSkipped <> 0)) then
            begin
              SetSelect(SR, False);
              if FMOptions and foUnsel <> 0 then FocusItem(Focused);
            end;
          end;
          Inc(I);
        end;
        if Optimized and (CopyStatus <> dsCancel) and
           (DeleteStatus <> dsCancel) then
        begin
          Message(Desktop, evBroadcast, cmUpdateDisk, nil);
          Goto Start;
        end;
      end
      else
      begin
        if (Length(ToName) > 1) and (ToName[1] = '+') then
        begin
          System.Delete(ToName, 1, 1);
          TrimSt(ToName);
          CopyStatus := dsAppend;
        end;
        GetRecStr(SR, FromName);
        FixToName(FromName, ToName);
        CancelSysErrors := False;
        ValidNames(FromName, ToName);
        if CancelSysErrors then Exit;
        SingleFixToName(IsDir, Wild, Options and ofValidate <> 0, True,
          SR, FromName, ADest, RenMovDirData.ToDir, ToName);
        ResetTotals;
        if IsDir then
        begin
          if Wild and (SR^.Name[1] = '.') then
          begin
            DirExpectedMsgBox;
            Exit;
          end;
          if FileCopyOptions and fsDisableCopyStat = 0 then
          begin
            DirCountPrim(FromName, DirFilter, CRCList);
            if CopyStatus = dsCancel then Exit;
          end;
          if (Settings and seUnfold <> 0) and (SR^.Name[1] = '.') then
            MoveFilesByMask(AddedSlash(FromName), ClearedSlash(ToName),
              DirFilter, NewMask, CRCList)
          else DirRenMovPrim(FromName, ToName, DirFilter, NewMask,
            CRCList, SR^.Name[1] <> '.');
        end
        else
        begin
          if FileCopyOptions and fsDisableCopyStat = 0 then
          begin
            BytesTotal := SR^.Size;
            FilesTotal := 1;
          end;
          ToName := FRenMovPrim(FromName, ToName);
        end;
        FocusName :=
          (CopyStatus <> dsCancel) and (DeleteStatus <> dsCancel);
      end;
      Message(Desktop, evBroadcast, cmUpdateDisk, nil);
      if FocusName then
      begin
        FocusFileName(ToName);
        Opposite^.FocusFileName(ToName);
      end;
    end;
  end;
end;

end.