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

interface

uses FileMngr;

procedure ShiftDeletePrim(FManager: PPanel);
procedure ShiftCopyPrim(FManager: PPanel);
procedure ShiftRenMovPrim(FManager: PPanel);

implementation

uses Dos, Objects, Drivers, Views, Memory, Dialogs, HistList, App, MsgBox,
  Swaps, ClipPrim, PanelRec, DirPrim, DiskBase, DiskPrim, Archives, History,
  LFN, TPString, Consts, Commands, Defaults;

function ClipActionMsgBox(const ActionStr, DestStr: String): Word;
begin
  ClipActionMsgBox := MessageBox(
    ^C'OK to ' + ActionStr + ' all selected files in the Clipboard ' +
    DestStr, nil, mfConfirmation + mfOkCancel);
end;

function DirActionMsgBox(const ActionStr, NameStr, DestStr: String): Word;
var
  S: String;
begin
  if (NameStr[1] = '\') and (NameStr = RootName(NameStr)) then
    S := 'contents of the'#13^C'network resource '
  else if (NameStr[0] = #2) and (NameStr[2] = ':') then
    S := 'contents of the'#13^C'drive '
  else S := 'directory'#13^C;
  S := ^C'OK to ' + ActionStr + ' the ' + S +
    TrimmedName(NameStr, 32);
  if DestStr <> '' then S := S + #13^C'to ' +
    TrimmedName(DestStr, 29);
  S := S + ' ?';
  DirActionMsgBox := MessageBox(S, nil, mfConfirmation + mfOkCancel);
end;

function FilterActionMsgBox(const ActionStr,
  SourceStr, DestStr, FilterStr: String): Word;
var
  N: Integer;
  S, SS: String;
begin
  if DestStr = '' then N := 27
  else if Length(DestStr) < 12 then N := 24 - Length(DestStr)
  else N := 12;
  S := TrimmedName(SourceStr, N);
  if DestStr <> '' then
  begin
    if Length(S) < 12 then N := 24 - Length(S)
    else N := 12;
    SS := TrimmedName(DestStr, N);
  end;
  S := ^C'OK to ' + ActionStr + ' all files matching'#13^C'"' +
    TrimmedName(FilterStr, 34) + '"'#13^C'from ' + S;
  if DestStr <> '' then S := S + ' to ' + SS;
  S := S + ' ?';
  FilterActionMsgBox := MessageBox(S, nil, mfConfirmation + mfOkCancel);
end;

procedure ValidDirName(var S: FNameStr; ClearSlash: Boolean);
begin
  if (S <> '') and (S[1] in ['\', '.']) then S := lFExpand(S);
  if ClearSlash then S := ClearedSlash(S);
end;

procedure ShiftDeletePrim(FManager: PPanel);
var
  Clip: Boolean;
  P: Byte;
  I: Integer;
  D: PDialog;
  R: TRect;
  Control: PView;
  ActionStr: String[7];
  Dir, Name, Ext: String;
  S, SS: FNameStr;
  CS: String;
begin
  with FManager^ do
  begin
    R.Assign(0, 0, 40, 8);
    D := InitSimpleDialog(R, 'Delete', hcShiftDeleteDialog);
    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, '~D~elete', Control)));
    end;
    S := '';
    if Application^.ExecuteDialog(D, @S, False) = cmCancel then Exit;
    ActionStr := 'delete';
    TrimSt(S);
    if S = '' then
    begin
      if DiskClipStream = nil then Exit;
      if ClipActionMsgBox(ActionStr, '?') = cmCancel then Exit;
      Message(Desktop, evBroadcast, cmUpdateViews, nil);
      I := ClipArea^.KB;
      Clip := True;
    end
    else Clip := False;

    CS := '';
    repeat
      if Clip then
      begin
	if CS = '' then
	begin
	  if I > ClipArea^.KK then Break;
	  GetClipLine(I, CS);
	  Inc(I);
	end;
	TrimSt(CS);
	P := Pos(' ', CS);
	if P = 0 then
	begin
	  S := CS;
	  CS := '';
	end
	else
	begin
	  S := System.Copy(CS, 1, P - 1);
	  System.Delete(CS, 1, P);
	end;
	if S = '' then Continue;
      end;
      DeleteStatus := dsDelete;
      ValidDirName(S, True);
      CancelSysErrors := False;
      if DirPresent(S) then
      begin
	if not Clip and
	  (DirActionMsgBox(ActionStr, S, '') = cmCancel) then Exit;
	DirDeletePrim(lFExpand(S), nil, True);
      end
      else if CancelSysErrors then Break
      else
      begin
	GetListDir(SS);
	lFSplit(S, Dir, Name, Ext);
	if IsWild(S) then
	begin
	  if Dir = '' then Dir := SS[1] + ':\';
	  if not Clip and
	    (FilterActionMsgBox(ActionStr, ClearedSlash(Dir), '',
	      Name + Ext) = cmCancel) then Exit;
	  DeleteFilesByMask(AddedSlash(Dir), Name + Ext, nil);
	end
	else
	begin
	  if Dir = '' then Dir := SS + '\';
	  if DeviceDest(S) = '' then S := Dir + Name + Ext;
	  if not Clip and (MessageBox(^C'OK to delete the file'#13^C +
	    TrimmedName(S, 32) + ' ?',
	    nil, mfConfirmation + mfOkCancel) = cmCancel) then Exit;
	  FDeletePrim(S);
	end;
      end;
      if not Clip or (DeleteStatus = dsCancel) then Break;
    until False;
    Message(Desktop, evBroadcast, cmUpdateDisk, nil);
  end;
end;

function GetShiftData(FManager: PPanel;
  var Data: TShiftCopyDirData;
  var FromName, ToName, DD: FNameStr; RenMov: Boolean): Word;
var
  C: Word;
  D: PDialog;
  R: TRect;
begin
  with FManager^ do
  begin
    R.Assign(0, 0, 58, 12);
    if RenMov then D := InitShiftRenMovDialog(R)
    else D := InitShiftCopyDialog(R);
    Data.FromDir := '';
    if (Opposite^.State and sfVisible <> 0) and
       (Opposite^.Status < stInfo) then
    begin
      Opposite^.GetListDir(Data.ToDir);
      Data.ToDir := AddedSlash(Data.ToDir);
    end
    else Data.ToDir := '';
    C := Application^.ExecuteDialog(D, @Data, False);
    if C <> cmCancel then
    begin
      FromName := Data.FromDir;
      ToName := Data.ToDir;
      TrimSt(FromName);
      TrimSt(ToName);
      if ToName = '' then GetCurDir(0, ToName);
      DD := DeviceDest(ToName);
    end;
    GetShiftData := C;
  end;
end;

procedure CountClipTotals;
var
  Wild: Boolean;
  I, P: Integer;
  S, Dir, Name: String;
  SR: lSearchRec;
begin
  I := ClipArea^.KB;
  S := '';
  repeat
    if S = '' then
    begin
      if I > ClipArea^.KK then Break;
      GetClipLine(I, S);
      Inc(I);
    end;
    TrimSt(S);
    P := Pos(' ', S);
    if P = 0 then
    begin
      Name := S;
      S := '';
    end
    else
    begin
      Name := System.Copy(S, 1, P - 1);
      System.Delete(S, 1, P);
    end;
    if Name = '' then Continue;

    ValidDirName(Name, True);
    Wild := IsWild(Name);
    if Wild then
    begin
      Dir := JustPathName(Name);
      if Dir = '' then
      begin
	GetCurDir(0, Dir);
	Dir := Dir[1] + ':\';
      end;
      DirCountPrim(Dir, JustFileName(Name), nil);
    end
    else
    begin
      CancelSysErrors := False;
      lFindFirst(Name, AnyFile and not VolumeId, SR);
      lFindClose(SR);
      if CancelSysErrors then Break;
      if DosError = 0 then
	if SR.SR.Attr and Directory <> 0 then
	  DirCountPrim(Name, '*.*', nil)
	else
	begin
	  BytesTotal := BytesTotal + SR.SR.Size;
	  Inc(FilesTotal);
	end;
    end;
  until CopyStatus = dsCancel;
end;

procedure SplitFromName(var FromName, FrDr, FrNm, FrEx, AFilter: FNameStr;
  IsDir, FromWild: Boolean);
begin
  if IsDir and not ((FromName[0] = #2) and (FromName[2] = ':')) then
    FromName := AddedSlash(lFExpand(FromName))
  else if (FromName[0] > #2) and (FromName[2] = ':') and
    (FromName[3] <> '\') then FromName := lFExpand(FromName);
  lFSplit(FromName, FrDr, FrNm, FrEx);
  if FromWild then
  begin
    AFilter := FrNm + FrEx;
    if FrDr = '' then
    begin
      GetCurDir(0, FrDr);
      FrDr := FrDr[1] + ':\';
    end
    else if Pos(':', FrDr) = 0 then
      FrDr := AddedSlash(lFExpand(FrDr));
  end
  else AFilter := '*.*';
end;

procedure SplitToName(var ToName, ToDr, ToNm, ToEx, AMask: FNameStr;
  const DD: FNameStr; IsDir, FromWild, ToWild: Boolean);
begin
  if DD <> '' then ToName := DD
  else
  begin
    ValidDirName(ToName, False);
    if ((IsDir or FromWild) and not ToWild) or
       ((ToName[0] = #2) and (ToName[2] = ':')) then
      ToName := AddedSlash(lFExpand(ToName))
    else if (ToName[0] > #2) and (ToName[2] = ':') and
      (ToName[3] <> '\') then ToName := lFExpand(ToName);
  end;
  lFSplit(ToName, ToDr, ToNm, ToEx);
  if ToWild then
  begin
    AMask := ToNm + ToEx;
    ToNm := '';
    ToEx := '';
  end
  else AMask := '';
end;

procedure ShiftCopyPrim(FManager: PPanel);
var
  IsDir, FromWild, ToWild, Clip: Boolean;
  P: Byte;
  I: Integer;
  ActionStr: String[7];
  CopyDirData: TShiftCopyDirData;
  AFilter, AMask: String;
  FrDr, ToDr, FrNm, ToNm, FrEx, ToEx: String;
  S, DD, FromName, ToName: FNameStr;
  CS: String;
begin
  with FManager^ do
  begin
    CopyStatus := dsOverwrite;
    TryAll := False;
    if GetShiftData(FManager,
      CopyDirData, FromName, ToName, DD, False) = cmCancel then Exit;
    ActionStr := 'copy';

    if FromName = '' then
    begin
      if DiskClipStream = nil then Exit;
      if DD <> '' then S := DD
      else
      begin
	S := ToName;
	ValidDirName(S, False);
      end;
      if ClipActionMsgBox(ActionStr,
	'to'#13^C + TrimmedName(S, 32) + ' ?') = cmCancel then Exit;
      Message(Desktop, evBroadcast, cmUpdateViews, nil);
      if Pos(':', ToName) > 2 then
      begin
	ClipboardToArchive(False, ToName);
	Exit;
      end;
      I := ClipArea^.KB;
      Clip := True;
    end
    else Clip := False;

    ResetTotals;
    if Clip and (FileCopyOptions and fsDisableCopyStat = 0) then
    begin
      CountClipTotals;
      if CopyStatus = dsCancel then Exit;
    end;

    CS := '';
    repeat
      if Clip then
      begin
	if CS = '' then
	begin
	  if I > ClipArea^.KK then Break;
	  GetClipLine(I, CS);
	  Inc(I);
	end;
	TrimSt(CS);
	P := Pos(' ', CS);
	if P = 0 then
	begin
	  FromName := CS;
	  CS := '';
	end
	else
	begin
	  FromName := System.Copy(CS, 1, P - 1);
	  System.Delete(CS, 1, P);
	end;
	if FromName = '' then Continue;
	ToName := CopyDirData.ToDir;
	TrimSt(ToName);
      end;

      FromWild := IsWild(FromName);
      ToWild := IsWild(ToName);

      ValidDirName(FromName, True);
      CancelSysErrors := False;
      IsDir := DirPresent(FromName);
      if CancelSysErrors then Break;
      SplitFromName(FromName, FrDr, FrNm, FrEx, AFilter, IsDir, FromWild);
      SplitToName(ToName, ToDr, ToNm, ToEx, AMask, DD,
	IsDir, FromWild, ToWild);

      if IsDir then
      begin
	FromName := ClearedSlash(FrDr);
	if DD = '' then ToName := AddedSlash(ToDr + ToNm + ToEx);
	IsDir := not (CopyDirData.ToDir[Length(CopyDirData.ToDir)] in
	  [':', '\']) and (CopyDirData.ToDir[1] <> '.');
	S := ToDr + ToNm + ToEx + AMask;
	if IsDir then S := ClearedSlash(S);
	if not Clip and
	  (DirActionMsgBox(ActionStr, FromName, S) = cmCancel) then Exit;
	if DD = '' then
	begin
	  CancelSysErrors := False;
	  ValidNames(FromName, ToName);
	  if CancelSysErrors then Break;
	  if IsDir then ToName := PrevDir(ToName);
	end;
	if not Clip and (FileCopyOptions and fsDisableCopyStat = 0) then
	begin
	  DirCountPrim(FromName, AFilter, nil);
	  if CopyStatus = dsCancel then Exit;
	end;
	DirCopyPrim(FromName, ToName, AFilter, AMask, nil);
      end
      else if FromWild then
      begin
	if not Clip and
	  (FilterActionMsgBox(ActionStr, ClearedSlash(FrDr),
	    ToDr + ToNm + ToEx + AMask, AFilter) = cmCancel) then Exit;
	if not Clip and (FileCopyOptions and fsDisableCopyStat = 0) then
	begin
	  DirCountPrim(FrDr, AFilter, nil);
	  if CopyStatus = dsCancel then Exit;
	end;
	if DD = '' then
	  CopyFilesByMask(AddedSlash(FrDr), ToDr, AFilter, AMask, nil)
	else CopyFilesByMask(AddedSlash(FrDr), ToName, AFilter, AMask, nil);
      end
      else
      begin
	CancelSysErrors := False;
	FromName := lFExpand(FromName);
	if DD = '' then ValidNames(FromName, ToName);
	if CancelSysErrors then Break;
	FCopyPrim(FromName, ToName);
      end;
      if not Clip or (CopyStatus = dsCancel) then Break;
    until False;
    Message(Desktop, evBroadcast, cmUpdateDisk, nil);
  end;
end;

procedure ShiftRenMovPrim(FManager: PPanel);
var
  IsDir, FromWild, ToWild, DoRename, DoSearch, Clip: Boolean;
  P: Byte;
  I: Integer;
  ActionStr: String[7];
  CopyDirData: TShiftCopyDirData;
  AFilter, AMask: String;
  FrDr, ToDr, FrNm, ToNm, FrEx, ToEx: String;
  S, DD, FromName, ToName: FNameStr;
  CS: String;
begin
  with FManager^ do
  begin
    CopyStatus := dsOverwrite;
    DeleteStatus := dsDelete;
    TryAll := False;
    DoSearch := False;
    if GetShiftData(FManager,
      CopyDirData, FromName, ToName, DD, True) = cmCancel then Exit;

    if FromName = '' then
    begin
      if DiskClipStream = nil then Exit;
      if DD <> '' then S := DD
      else
      begin
	S := ToName;
	ValidDirName(S, False);
      end;
      if ClipActionMsgBox('move',
	'to'#13^C + TrimmedName(S, 32) + ' ?') = cmCancel then Exit;
      Message(Desktop, evBroadcast, cmUpdateViews, nil);
      if Pos(':', ToName) > 2 then
      begin
	ClipboardToArchive(True, ToName);
	Exit;
      end;
      I := ClipArea^.KB;
      Clip := True;
    end
    else Clip := False;

    ResetTotals;
    if Clip and (FileCopyOptions and fsDisableCopyStat = 0) then
    begin
      CountClipTotals;
      if CopyStatus = dsCancel then Exit;
    end;

    CS := '';
    repeat
      if Clip then
      begin
	if CS = '' then
	begin
	  if I > ClipArea^.KK then Break;
	  GetClipLine(I, CS);
	  Inc(I);
	end;
	TrimSt(CS);
	P := Pos(' ', CS);
	if P = 0 then
	begin
	  FromName := CS;
	  CS := '';
	end
	else
	begin
	  FromName := System.Copy(CS, 1, P - 1);
	  System.Delete(CS, 1, P);
	end;
	if FromName = '' then Continue;
	ToName := CopyDirData.ToDir;
	TrimSt(ToName);
      end;

      FromWild := IsWild(FromName);
      ToWild := IsWild(ToName);

      ValidDirName(FromName, True);
      CancelSysErrors := False;
      IsDir := DirPresent(FromName);
      if CancelSysErrors then Break;
      SplitFromName(FromName, FrDr, FrNm, FrEx, AFilter, IsDir, FromWild);
      SplitToName(ToName, ToDr, ToNm, ToEx, AMask, DD,
	IsDir, FromWild, ToWild);

      if IsDir then
      begin
	FromName := ClearedSlash(FrDr);
	if DD = '' then ToName := AddedSlash(ToDr + ToNm + ToEx);
	DoRename := PrevDir(FromName) = PrevDir(ToName);
	if not DoRename then ToName := AddedSlash(ToName);
	if DoRename then ActionStr := 'rename'
	else ActionStr := 'move';
	IsDir := not DoRename and not
	  (CopyDirData.ToDir[Length(CopyDirData.ToDir)] in [':', '\']) and
	  (CopyDirData.ToDir[1] <> '.');
	S := ToDr + ToNm + ToEx + AMask;
	if IsDir or DoRename then S := ClearedSlash(S);
	if not Clip and
	  (DirActionMsgBox(ActionStr, FromName, S) = cmCancel) then Exit;
	if DD = '' then
	begin
	  CancelSysErrors := False;
	  if not DoRename then ValidNames(FromName, ToName);
	  if CancelSysErrors then Break;
	  if IsDir then ToName := PrevDir(ToName);
	end;
	if not Clip and not DoRename and
	  (FileCopyOptions and fsDisableCopyStat = 0) then
	begin
	  DirCountPrim(FromName, AFilter, nil);
	  if CopyStatus = dsCancel then Exit;
	end;
	DirRenMovPrim(FromName, ToName, AFilter, AMask, nil, True);
      end
      else if FromWild then
      begin
	DoRename := (ToDr + ToNm + ToEx = '') or (FrDr = ToDr);
	if DoRename then
	begin
	  ToDr := FrDr;
	  ActionStr := 'rename';
	end
	else
	begin
	  if Pos(':', ToDr) = 0 then
	    ToDr := AddedSlash(lFExpand(ToDr));
	  ActionStr := 'move';
	end;
	if not Clip and
	  (FilterActionMsgBox(ActionStr, ClearedSlash(FrDr),
	    ToDr + ToNm + ToEx + AMask, AFilter) = cmCancel) then Exit;
	if not Clip and not DoRename and
	  (FileCopyOptions and fsDisableCopyStat = 0) then
	begin
	  DirCountPrim(FrDr, AFilter, nil);
	  if CopyStatus = dsCancel then Exit;
	end;
	if DD <> '' then MoveFilesByMask(AddedSlash(FrDr), ToName,
	  AFilter, AMask, nil)
	else if FrDr = ToDr then
	  RenameFilesByMask(AddedSlash(FrDr), AFilter, AMask, nil)
	else MoveFilesByMask(AddedSlash(FrDr), ClearedSlash(ToDr),
	  AFilter, AMask, nil);
      end
      else
      begin
	CancelSysErrors := False;
	FromName := lFExpand(FromName);
	if DD = '' then ValidNames(FromName, ToName);
	if CancelSysErrors then Break;
	FRenMovPrim(FromName, ToName);
	DoSearch := not Clip and (CopyStatus <> dsCancel) and
	  (DeleteStatus <> dsCancel) and (UCPos(ListDir^, ToName) <> 0);
      end;
      if not Clip or (CopyStatus = dsCancel) or
	(DeleteStatus = dsCancel) then Break;
    until False;
    Message(Desktop, evBroadcast, cmUpdateDisk, nil);
    if DoSearch then
    begin
      GetItemNum(ClearedSlash(ToName), I);
      FocusItem(I);
    end;
  end;
end;

end.