dos_compilers/Borland Turbo Pascal v7/EXAMPLES/TVFM/TOOLS.PAS
2024-07-02 08:21:37 -07:00

1284 lines
30 KiB
Plaintext
Raw Blame History

{************************************************}
{ }
{ Turbo Vision File Manager Demo }
{ Copyright (c) 1992 by Borland International }
{ }
{************************************************}
unit Tools;
{$X+,V-}
interface
uses Drivers, Objects, Views, Dialogs, Memory, App, MsgBox,
Globals, FileCopy, Gauges, Dos;
type
String2 = String[2];
String4 = String[4];
TConfigHeader = String[24];
{ Used to display status messages }
PStatusBox = ^TStatusBox;
TStatusBox = object(TDialog)
procedure HandleEvent(var Event: TEvent); virtual;
end;
{ buffered file copy object }
PCopier = ^TCopier;
TCopier = object(TFileCopy)
procedure ReadMsg(const FName: FNameStr; Progress: Longint); virtual;
procedure WriteMsg(const FName: FNameStr; Progress: Longint); virtual;
function IOError(const FName: FNameStr; ECode:Integer) : erAction; virtual;
end;
{ generate a cmOK if double clicked }
POkListBox = ^TOkListBox;
TOkListBox = object(TListBox)
procedure SelectItem(Item: Integer); virtual;
end;
{ ShowStatusBox displays a status dialog, using StatusMsg as the string }
{ to display. The status box responds to the cmStatusUpdate command by }
{ redrawing the text. }
procedure ShowStatusBox;
{ KillStatusBox removes the status box from the screen }
procedure KillStatusBox;
{ Return True if the passed list contains any tagged files }
function HasTaggedFiles(P: PFileList) : Boolean;
{ Return the path and filename (no extension) of the exe }
function GetExeBaseName: String;
{ Convert strings to upper and lower case }
procedure UpperCase(var s: String);
procedure LowerCase(var s: String);
{ Return a right justified number (in an 8 character field) }
function RJustNum(L: Longint): String;
{ Pad right end of string to Len bytes }
function Pad(s: String; Len: Byte): String;
{ Return a fully trimmed copy of Original }
function FullTrim(const Original: String): String;
{ Return string value of W, optionally with leading zero if Pad=True }
function TwoDigit(W: Word; Pad: Boolean): String2;
{ Return 4 digit string representation of W }
function FourDigit(W: Word): String4;
{ Return a string version of the Date/Time longint. Opts=$01 adds the }
{ date portion. Opts=$02 adds time, Opts=$03 adds both }
function FormatDateTime(DT: Longint; Opts: Word): String;
{ Return the 4 character string representation of the attribute word }
function FormatAttr(Attr: Word): String4;
{ Return True if file is a .BAT, .COM, or .EXE }
function IsExecutable(const FileName: FNameStr): Boolean;
{ Execute the passed file, asks for parameters }
procedure ExecuteFile(FileName: FNameStr);
{ View passed file as Hex, Text, or with Custom Viewer }
procedure ViewAsHex(const FileName: FNameStr);
procedure ViewAsText(const FileName: FNameStr);
procedure ViewCustom(const FileName: FNameStr);
{ Return True if the passed drive letter is valid }
function DriveValid(Drive: Char): Boolean;
{ Return a selected drive letter from listbox of valid drives }
function SelectDrive: Char;
{ Invalidate the passed directory by issuing a cmInvalidDir broadcast }
procedure InvalidateDir(Path: FNameStr);
{ Copy either tagged or current file to a destination path }
procedure HandleFileCopy(const Path: FNameStr; P: PFileList; Current: Integer);
{ Delete file if user confirms the deletion, return error code }
function SafeDelete(FileName: FNameStr): Integer;
{ Handle deleting one or multiple files from a file list }
procedure HandleFileDelete(const Path: FNameStr; List: PFileList;
Current: Integer);
{ Present the Rename file dialog }
procedure RenameFile(const Path: FNameStr; F: PFileRec);
{ Present the Change Attribute dialog }
procedure ChangeAttr(const Path: FNameStr; F:PFileRec);
{ Allow user to specify what viewer program to use }
procedure InstallViewer;
{ Allow user to specify the display options }
procedure SetDisplayPrefs;
{ Save and load the configuration file }
procedure SaveConfig;
procedure ReadConfig;
{ Execute the passed string literally }
procedure RunDosCommand(Command: String);
{ Return a TFileNameRec built from the passed filespec. This structure }
{ allows for easier comparisons by other procedures }
function NewFileNameRec(const Path: FNameStr): PFileNameRec;
{ Perform a drag & drop copy }
procedure DragDropCopy(Mover: PFileMover; Dest: PathStr);
{ return true if this name matches the wildcard }
function WildCardMatch(const Name, Card: FNameStr): Boolean;
const
StatusMsg : String = '';
implementation
uses ViewHex, ViewText, Strings, Equ, Assoc;
const
StatusBox : PStatusBox = nil;
StatusPMsg : PString = @StatusMsg;
ConfigHeader : TConfigHeader = 'TVFM Configuration File'#26;
{ General utility procedures }
procedure ShowStatusBox;
var
R: TRect;
P: PView;
begin
if StatusBox <> nil then exit;
R.Assign(0,0,40,5);
StatusBox := New(PStatusBox, Init(R, 'Status'));
with StatusBox^ do
begin
Options := Options or ofCentered;
Options := Options and (not ofBuffered);
Flags := Flags and (not wfClose) and (not wfMove);
R.Assign(2,2,38,3);
P := New(PParamText, Init(R, ^C'%s', 1));
Insert(P);
end;
StatusMsg := '';
StatusPMsg := @StatusMsg;
StatusBox^.SetData(StatusPMsg);
Desktop^.Insert(StatusBox);
end;
procedure ShowCopyStatusBox(MaxSize: Longint);
var
R: TRect;
P: PView;
begin
if StatusBox <> nil then exit;
R.Assign(0,0,40,7);
StatusBox := New(PStatusBox, Init(R, 'Status'));
with StatusBox^ do
begin
Options := Options or ofCentered;
Options := Options and (not ofBuffered);
Flags := Flags and (not wfClose) and (not wfMove);
R.Assign(2,2,38,3);
P := New(PParamText, Init(R, ^C'%s', 1));
Insert(P);
R.Assign(5,4,34,5);
Insert(New(PBarGauge, Init(R, MaxSize)));
R.Assign(2,4,4,5);
Insert(New(PStaticText, Init(R, '0%')));
R.Assign(35,4,39,5);
Insert(New(PStaticText, Init(R, '100%')));
end;
StatusMsg := '';
StatusPMsg := @StatusMsg;
StatusBox^.SetData(StatusPMsg);
Desktop^.Insert(StatusBox);
end;
procedure KillStatusBox;
begin
if StatusBox <> nil then
begin
Dispose(StatusBox, Done);
StatusBox := nil;
end;
end;
{ Return TRUE if the passed list has tagged files in it }
function HasTaggedFiles(P: PFileList) : Boolean;
var
Has: Boolean;
i: Integer;
begin
Has := False;
i := 0;
while (i < P^.Count) and (not Has) do
begin
Has := PFileRec(P^.At(i))^.Tagged;
Inc(i);
end;
HasTaggedFiles := Has;
end;
function GetExeBaseName : String;
var
ExeFileName: FNameStr;
D: DirStr;
N: NameStr;
E: ExtStr;
begin
ExeFileName := ParamStr(0);
if ExeFileName = '' then
ExeFileName := FSearch(EXEName, GetEnv('PATH'));
ExeFileName := FExpand(ExeFileName);
FSplit(ExeFileName, D, N, E);
GetExeBaseName := D + N;
end;
procedure UpperCase(var s:string);
var
i : Integer;
begin
for i := 1 to Length(s) do
s[i] := Upcase(s[i]);
end;
procedure LowerCase(var s:string);
var
i : Integer;
begin
for i := 1 to Length(s) do
if s[i] in ['A'..'Z'] then Inc(s[i], 32);
end;
function RJustNum(L: Longint): String;
var
s: String;
begin
FormatStr(s, '%8d', L);
RJustNum := s;
end;
function Pad(s: String; Len: Byte): String;
begin
if Length(s) < Len then
FillChar(s[Succ(Length(s))], Len-Length(s), ' ');
s[0] := Char(Len);
Pad := s;
end;
function FullTrim(const Original: String): String;
var
S: String;
begin
S := Original;
while (S[0] > #0) and (S[Length(S)] = #32) do Dec(S[0]); { trim left }
while (S[0] > #0) and (S[1] = #32) do
begin
Move(S[2], S[1], Pred(Length(S)));
Dec(S[0]);
end;
FullTrim := S;
end;
function TwoDigit(W: Word; Pad: Boolean) : String2;
var
s: String2;
begin
Str(W:2, s);
if Pad and (s[1] = ' ') then s[1] := '0';
TwoDigit := s;
end;
function FourDigit(W: Word) : String4;
var
s: String4;
begin
Str(W:4, s);
FourDigit := s;
end;
function FormatDateTime(DT: Longint; Opts: Word): String;
var
s: String;
t: DateTime;
begin
UnpackTime(DT, t);
s := '';
if (Opts and 1) <> 0 then { add the date }
begin
s := s + TwoDigit(t.Month, False) + '-' + TwoDigit(t.Day, True);
s := s + '-' + Copy(FourDigit(t.Year),3,2);
end;
if (Opts and 2) <> 0 then { add the time }
begin
if s <> '' then s := s + ' ';
s := s + TwoDigit(t.Hour, True) + ':' + TwoDigit(t.Min, True) + ':' +
TwoDigit(t.Sec, True);
end;
FormatDateTime := s;
end;
function FormatAttr(Attr: Word): String4;
var
s: String4;
begin
s := '<27><><EFBFBD><EFBFBD>';
if Attr and Archive = Archive then s[1] := 'A';
if Attr and ReadOnly = ReadOnly then s[2] := 'R';
if Attr and SysFile = SysFile then s[3] := 'S';
if Attr and Hidden = Hidden then s[4] := 'H';
FormatAttr := s;
end;
function IsExecutable(const FileName: FNameStr): Boolean;
var
D: DirStr;
N: NameStr;
E: ExtStr;
begin
FSplit(FExpand(FileName), D, N, E);
IsExecutable := (E = '.EXE') or (E = '.COM') or (E = '.BAT');
end;
procedure ExecuteFile(FileName: FNameStr);
var
D: PDialog;
R: TRect;
P: PView;
Dir: DirStr;
Name: FNameStr;
E: ExtStr;
Event: TEvent;
Params: string[80];
Command: string[80];
L: array[0..2] of Longint;
ParamPos: Integer;
Association: PAssociation;
begin
FSplit(FExpand(FileName), Dir, Name, E);
Name := Name + E;
Association := nil;
Command := '';
Params := '';
{ Does an association exist for this file? }
if not IsExecutable(FileName) then
begin
Association := GetAssociatedCommand(E);
if Association <> nil then Command := Association^.Cmd^;
if Command = '' then
begin
L[0] := Longint(@FileName);
MessageBox(RezStrings^.Get(sNoAssociation), @L, mfError +
mfOKButton);
Exit;
end
else
begin
ParamPos := Pos(' ', Command);
if ParamPos > 0 then
begin
Params := Copy(Command, ParamPos + 1, $FF);
Delete(Command, ParamPos, $FF);
Params := Params + ' ' + FileName;
end
else
Params := FileName;
end;
end
else
begin
Command := FileName;
Params := '';
end;
R.Assign(0,0,50,8);
D:= New(PDialog, Init(R, 'Execute Program'));
with D^ do
begin
Options := Options or ofCentered;
R.Assign(2,2,15,3);
Insert(New(PStaticText, Init(R, ' Executing:')));
R.Assign(15,2,48,3);
Insert(New(PStaticText, Init(R, Command)));
R.Assign(15,3,48,4);
P := New(PInputLine, Init(R, 80));
Insert(P);
R.Assign(2,3,15,4);
Insert(New(PLabel, Init(R, '~P~arameters', P)));
R.Assign(12,5,24,7);
Insert(New(PButton, Init(R, '~O~K', cmOK, bfDefault)));
R.Move(14,0);
Insert(New(PButton, Init(R, '~C~ancel', cmCancel, bfNormal)));
SelectNext(False);
end;
if ( (Association <> nil) and (not Association^.Prompt)) or
(Application^.ExecuteDialog(D, @Params) = cmOK) then
begin
DoneSysError;
DoneEvents;
DoneVideo;
DoneDosMem;
SwapVectors;
if E = '.BAT' then
begin
Command := GetEnv('COMSPEC');
Params := '/c ' + FileName + Params;
end;
Exec(Command, Params);
SwapVectors;
PrintStr(RezStrings^.Get(sPressAnyKey));
Event.What := evNothing;
repeat
GetKeyEvent(Event);
until Event.What <> evNothing;
InitDosMem;
InitVideo;
InitEvents;
InitSysError;
Application^.Redraw;
if DosError <> 0 then
begin
L[0] := DosError;
L[1] := Longint(@Command);
MessageBox(RezStrings^.Get(sExecErr), @L, mfError + mfOKButton);
end else
begin
L[0] := DosExitCode and $FF;
if L[0] <> 0 then
MessageBox(RezStrings^.Get(sExecRetCode), @L,
mfInformation + mfOKButton);
end;
end;
end;
{ view file procedures }
procedure ViewAsHex(const FileName: FNameStr);
var
H: PHexWindow;
R: TRect;
begin
R.Assign(0,0,72,15);
H := New(PHexWindow, Init(R, FileName));
H^.Options := H^.Options or ofCentered;
Desktop^.Insert(H);
end;
procedure ViewAsText(const FileName: FNameStr);
var
T: PTextWindow;
R: TRect;
begin
R.Assign(0,0,72,15);
T := New(PTextWindow, Init(R, FileName));
T^.Options := T^.Options or ofCentered;
Desktop^.Insert(T);
end;
procedure ViewCustom(const FileName: FNameStr);
var
Params : FNameStr;
Command : FNameStr;
D: DirStr;
N: NameStr;
E: ExtStr;
L : array[0..1] of Longint;
Msg: String;
PS: PString;
begin
{ create the program name }
if FullTrim(Viewer) = '' then
begin
MessageBox(RezStrings^.Get(sNoViewerErr), nil, mfError + mfOKButton);
Exit;
end;
FSplit(Viewer, D, N, E);
DoneSysError;
DoneEvents;
DoneVideo;
DoneDosMem;
SwapVectors;
if E = '.BAT' then
begin
Command := GetEnv('COMSPEC');
Params := '/c ' + Viewer + ' ' + FileName;
end
else
begin
Command := Viewer;
Params := FileName;
end;
Exec(Command, Params);
SwapVectors;
InitDosMem;
InitVideo;
InitEvents;
InitSysError;
Application^.Redraw;
if DosError <> 0 then
begin
L[0] := DosError;
L[1] := Longint( @Viewer );
MessageBox(RezStrings^.Get(sInvokeErr), @L, mfError + mfOKButton);
end;
end;
function DriveValid(Drive: Char): Boolean; assembler;
asm
MOV AH,19H { Save the current drive in BL }
INT 21H
MOV BL,AL
MOV DL,Drive { Select the given drive }
SUB DL,'A'
MOV AH,0EH
INT 21H
MOV AH,19H { Retrieve what DOS thinks is current }
INT 21H
MOV CX,0 { Assume false }
CMP AL,DL { Is the current drive the given drive? }
JNE @@1
MOV CX,1 { It is, so the drive is valid }
MOV DL,BL { Restore the old drive }
MOV AH,0EH
INT 21H
@@1: XCHG AX,CX { Put the return value into AX }
end;
{ Return a redirected device entry into the specified buffers }
function GetRedirEntry(Entry: Word; Local, Net: Pointer): Boolean; assembler;
asm
PUSH DS
LDS SI,Local
LES DI,Net
MOV AX,5F02h
MOV BX,Entry
INT 21h
POP DS
SBB AL,AL
INC AL
end;
{ return a list of redirected devices (drives only) }
function RedirDeviceList: PDeviceCollection;
var
List: PDeviceCollection;
Device: PDeviceRec;
P: PChar;
I: Word;
LocalName: array[0..15] of char;
NetworkName: array[0..127] of char;
begin
List := nil;
{$IFNDEF DPMI}
List := New(PDeviceCollection, Init(10,10));
for I := 0 to 99 do
begin
if GetRedirEntry(I, @LocalName, @NetworkName) then
begin
if (LocalName[0] in ['D'..'Z']) and (LocalName[1] = ':') then
begin
New(Device);
Device^.LocalName := LocalName[0];
P := @NetworkName[2];
Device^.NetworkName := NewStr( StrPas(P) );
List^.Insert(Device);
end;
end
else Break;
end;
if List^.Count = 0 then
begin
Dispose(List, Done);
List := nil;
end;
{$ENDIF}
RedirDeviceList := List;
end;
function ValidDriveList: PStringCollection;
var
DriveList: PStringCollection;
DeviceList: PDeviceCollection;
Drive: Char;
Device: PDeviceRec;
S: String;
function DriveMatch(P: PDeviceRec): Boolean; far;
begin
DriveMatch := Drive = P^.LocalName;
end;
begin
DriveList := New(PStringCollection, Init(26,0));
DeviceList := RedirDeviceList;
for Drive := 'A' to 'Z' do
begin
if DriveValid(Drive) then
begin
S := Drive + ':';
if DeviceList <> nil then
begin
Device := DeviceList^.FirstThat(@DriveMatch);
if Device <> nil then S := S + ' ' + Device^.NetworkName^;
end;
DriveList^.Insert(NewStr(S));
end;
end;
if DriveList^.Count = 0 then
begin
Dispose(DriveList, Done);
DriveList := nil;
end;
ValidDriveList := DriveList;
if DeviceList <> nil then Dispose(DeviceList, Done);
end;
function SelectDrive : Char;
var
R: TRect;
D: PDialog;
LB: PListBox;
SB: PScrollBar;
P: PString;
DriveList: PStringCollection;
CurDir: String;
function IsCurrentDirectory(Dir: PString): Boolean; far;
begin
IsCurrentDirectory := Dir^[1] = CurDir[1];
end;
begin
GetDir(0, CurDir); { save this value }
SelectDrive := ' ';
DriveList := ValidDriveList;
if DriveList = nil then
begin
MessageBox(RezStrings^.Get(sNoDrivesErr), nil, mfError + mfOKButton);
Exit;
end;
R.Assign(0, 0, 53, 13);
D := New(PDialog, Init(R, 'Select Drive'));
with D^ do
begin
Options := Options or ofCentered;
R.Assign(50, 3, 51, 9);
SB := New(PScrollBar, Init(R));
Insert(SB);
R.Assign(2, 3, 50, 9);
LB := New(POkListBox, Init(R, 1, SB));
Insert(LB);
LB^.NewList(DriveList);
R.Assign(2, 2, 19, 3);
Insert(New(PLabel, Init(R, '~D~rives', LB)));
R.Assign(12, 10, 24, 12);
Insert(New(PButton, Init(R, '~O~K', cmOK, bfDefault)));
R.Move(16, 0);
Insert(New(PButton, Init(R, '~C~ancel', cmCancel, bfNormal)));
SelectNext(False);
end;
P := DriveList^.FirstThat(@IsCurrentDirectory);
if P <> nil then
LB^.FocusItem(DriveList^.IndexOf(P));
if Desktop^.ExecView(D) = cmOK then
begin
P := DriveList^.At(LB^.Focused);
if P <> nil then SelectDrive := P^[1];
end;
Dispose(DriveList, Done);
Dispose(D, Done);
end;
procedure InvalidateDir(Path: FNameStr);
begin
Message(Desktop, evBroadcast, cmInvalidDir, @Path);
end;
procedure HandleFileCopy(const Path: FNameStr; P: PFileList; Current: Integer);
var
Dest, S, D: string[80];
C: TCopier;
Dlg: PDialog;
TotalSize: Longint;
procedure CopyTagged(F: PFileRec); far;
begin
if F^.Tagged then
begin
S := Path + '\' + F^.Name + F^.Ext;
D := Dest + F^.Name + F^.Ext;
C.CopyFile(S, D, coNormal);
end;
end;
procedure AddSizes(F: PFileRec); far;
begin
if F^.Tagged then Inc(TotalSize, F^.Size);
end;
procedure CopySingle(F: PFileRec);
begin
S := Path + '\' + F^.Name + F^.Ext;
D := Dest + F^.Name + F^.Ext;
C.CopyFile(S, D, coNormal);
end;
begin
Dest := '';
Dlg := PDialog( RezFile.Get('CopyDialog') );
Application^.ExecuteDialog(Dlg, @Dest);
if Dest = '' then Exit;
Dest := FExpand(Dest);
if (Dest[Length(Dest)] <> '\') and (Dest[Length(Dest)] <> ':') then
Dest := Dest + '\';
C.Init(20);
TotalSize := 0;
if HasTaggedFiles(P) then P^.ForEach(@AddSizes)
else TotalSize := PFileRec(P^.At(Current))^.Size;
ShowCopyStatusBox(TotalSize);
if HasTaggedFiles(P) then P^.ForEach(@CopyTagged)
else CopySingle( PFileRec( P^.At(Current) ) );
C.Done;
KillStatusBox;
if Dest[Length(Dest)] = '\' then Dec(Dest[0]);
InvalidateDir(Dest);
end;
function SafeDelete(FileName: FNameStr): Integer;
var
R: Word;
F: File;
C: Word;
L: Longint;
D: PDialog;
Params: array[0..1] of Pointer;
Name : FNameStr;
Msg : String;
Attr: Word;
begin
SafeDelete := -1;
C := cmYes; { default value }
Assign(F, FileName);
GetFAttr(F, Attr);
if DosError <> 0 then
begin
Params[0] := Pointer(L);
Params[1] := @FileName;
MessageBox(RezStrings^.Get(sAccessErr), @Params, mfError + mfOKButton);
SafeDelete := L;
Exit;
end;
if (Attr and ReadOnly) <> 0 then Msg := RezStrings^.Get(sFileIsReadOnly)
else Msg := '';
Params[0] := @FileName;
Params[1] := @Msg;
if ConfirmDelete then
begin
D := PDialog( RezFile.Get('ConfirmDelete') );
C := Application^.ExecuteDialog(D, @Params);
end;
if C = cmYes then
begin
{ if file was read-only, clear that attribute }
if (Attr and ReadOnly) <> 0 then
begin
SetFAttr(F, Attr and (not ReadOnly));
if DosError <> 0 then
begin
L := DosError;
Params[0] := @Msg;
Params[1] := Pointer(L);
MessageBox(RezStrings^.Get(sSetAttrErr), @Params, mfError+mfOKButton);
SafeDelete := DosError;
Exit;
end;
end;
{ delete the file }
{$I-}
Erase(F);
{$I+}
L := IOResult;
if L <> 0 then
begin
Params[0] := @Msg;
Params[1] := Pointer(L);
MessageBox(RezStrings^.Get(sDeleteErr), @Params, mfError+mfOKButton);
SafeDelete := L;
Exit;
end
else
SafeDelete := 0;
end;
end;
function RemoveDeadFiles(P: PFileList): Integer;
var
F : PFileRec;
i : Integer;
Count: Integer;
begin
Count := 0;
i := 0;
while i < P^.Count do
begin
F := P^.At(i);
if F^.Name[1] = #0 then
begin
if F^.Tagged then
begin
F^.Toggle;
Message(Desktop, evBroadcast, cmTagChanged, F);
end;
Inc(Count);
P^.AtFree(i);
end
else inc(i);
end;
RemoveDeadFiles := Count;
end;
function DeleteMultFiles(Path: FNameStr; List: PFileList): Boolean;
var
F: PFileRec;
N: FNameStr;
procedure DeleteIfTagged(F: PFileRec); far;
begin
if F^.Tagged then
begin
N := Path + '\' + F^.Name + F^.Ext;
StatusMsg := RezStrings^.Get(sDeleting) + N;
Message(StatusBox, evBroadcast, cmStatusUpdate, nil);
if SafeDelete(N) = 0 then F^.Name[1] := #0; { mark as deleted }
end;
end;
begin
ConfirmDelete := False;
StatusMsg := '';
ShowStatusBox;
List^.ForEach(@DeleteIfTagged);
KillStatusBox;
DeleteMultFiles := RemoveDeadFiles(List) > 0;
ConfirmDelete := True;
end;
procedure HandleFileDelete(const Path: FNameStr; List: PFileList;
Current: Integer);
var
D: PDialog;
Command: Word;
F: PFileRec;
begin
F := List^.At(Current);
Command := cmNo; { default to only deleting current file }
if HasTaggedFiles(List) then
begin
D := PDialog( RezFile.Get('DeleteWhich') );
Command := Application^.ExecuteDialog(D, nil);
end;
if Command = cmNo then { only delete the current file }
begin
F := List^.At(Current);
if SafeDelete(Path + '\' + F^.Name + F^.Ext) = 0 then
InvalidateDir(Path);
end
else if Command = cmYes then { delete all marked files }
begin
if DeleteMultFiles(Path, List) then
InvalidateDir(Path);
end;
end;
procedure RenameFile(const Path: FNameStr; F: PFileRec);
var
D: PRenameDialog;
Dir: DirStr;
N: NameStr;
E: ExtStr;
begin
D := New(PRenameDialog, Init(Path + '\' + F^.Name + F^.Ext));
if D <> nil then
begin
if Application^.ExecuteDialog(D, nil) = cmOK then
begin
FSplit(D^.NewName, Dir, N, E);
F^.Name := N;
F^.Ext := E;
InvalidateDir(Path);
end;
end;
end;
procedure ChangeAttr(const Path: FNameStr; F: PFileRec);
var
D: PAttrDialog;
begin
D := New(PAttrDialog, Init(Path + '\' + F^.Name + F^.Ext));
if D <> nil then
begin
if Application^.ExecuteDialog(D, nil) = cmOK then
begin
F^.Attr := D^.NewAttr;
InvalidateDir(Path);
end;
end
else
MessageBox(RezStrings^.Get(sReadAttrErr), nil,
mfError + mfOKButton);
end;
procedure InstallViewer;
var
VPath: FNameStr;
Valid, Done: Boolean;
L: Longint;
begin
VPath := Viewer;
Valid := False;
Done := False;
while (not Valid) and (not Done) do
begin
if InputBox(RezStrings^.Get(sCustomViewer), RezStrings^.Get(sPathAndName),
VPath, SizeOf(FNameStr) - 1) = cmOK then
begin
UpperCase(VPath);
VPath := FSearch(VPath, GetEnv('PATH'));
if VPath = '' then
begin
MessageBox(RezStrings^.Get(sCantLocateOnPath), nil,
mfError + mfOKButton);
end
else if not IsExecutable(VPath) then
begin
L := Longint(@VPath);
MessageBox(RezStrings^.Get(sFileNotAnExe), @L, mfError+mfOKButton);
end
else Valid := True;
end
else Done := True;
end;
if Valid then Viewer := VPath;
end;
procedure SetDisplayPrefs;
var
D: PDialog;
SaveMask: string[12];
begin
D := PDialog( RezFile.Get('DisplayPref') );
SaveMask := ConfigRec.FileMask;
if Application^.ExecuteDialog(D, @ConfigRec) = cmOK then
begin
Uppercase(ConfigRec.FileMask);
if ConfigRec.ShowHidden > 0 then
UnwantedFiles := VolumeID or Directory
else
UnwantedFiles := VolumeID or Directory or SysFile or Hidden;
if ConfigRec.FileMask <> SaveMask then
Message(Desktop, evBroadcast, cmRescan, nil)
else
Message(Desktop, evBroadcast, cmRefreshDisplay, nil);
end;
end;
procedure SaveConfig;
var
Result: Longint;
F: PDosStream;
Pal: PString;
begin
F := New(PDosStream, Init(GetExeBaseName + CFGExt, stCreate));
Result := F^.Status;
if Result <> 0 then
begin
MessageBox(RezStrings^.Get(sWriteCfgErr), @Result, mfError+mfOKButton);
Exit;
end;
F^.Write(ConfigHeader[1], SizeOf(TConfigHeader) - 1);
F^.Write(ConfigRec, SizeOf(TConfigRec));
F^.Write(Viewer, SizeOf(FNameStr));
Pal := @Application^.GetPalette^;
F^.WriteStr(Pal);
WriteAssociationList(F^);
Dispose(F, Done);
end;
procedure ReadConfig;
var
Result: Longint;
F: PDosStream;
Header: TConfigHeader;
Pal: PString;
begin
F := New(PDosStream, Init(GetExeBaseName + CFGExt, stOpenRead));
Result := F^.Status;
if Result <> 0 then Exit;
F^.Read(Header[1], SizeOf(TConfigHeader) - 1);
Header[0] := Char( SizeOf(TConfigHeader) -1 );
if Header <> ConfigHeader then
begin
MessageBox(RezStrings^.Get(sInvalidCfgErr), nil, mfError + mfOKButton);
Exit;
end;
F^.Read(ConfigRec, SizeOf(TConfigRec));
F^.Read(Viewer, SizeOf(FNameStr));
Pal := F^.ReadStr;
if Pal <> nil then
begin
Application^.GetPalette^ := Pal^;
DoneMemory;
Application^.ReDraw;
DisposeStr(Pal);
end;
ReadAssociationList(F^);
Dispose(F, Done);
end;
procedure RunDosCommand(Command: String);
var
D: PDialog;
Event: TEvent;
begin
D := PDialog( RezFile.Get('RunDialog') );
if (Application^.ExecuteDialog(D, @Command) = cmOK) and
(FullTrim(Command) <> '') then
begin
DoneSysError;
DoneEvents;
DoneVideo;
DoneDosMem;
SwapVectors;
Exec(GetEnv('COMSPEC'), '/C' + Command);
SwapVectors;
PrintStr(RezStrings^.Get(sPressAnyKey));
repeat
GetKeyEvent(Event);
until Event.What <> evNothing;
InitDosMem;
InitVideo;
InitEvents;
InitSysError;
Application^.Redraw;
end;
end;
function NewFileNameRec(const Path: FNameStr): PFileNameRec;
var
D: DirStr;
N: NameStr;
E: ExtStr;
P: PFileNameRec;
begin
FSplit(Path, D, N, E);
New(P);
P^.Dir := D;
P^.Name := N;
P^.Ext := E;
NewFileNameRec := P;
end;
procedure DragDropCopy(Mover: PFileMover; Dest: PathStr);
var
C: TCopier;
TotalSize: Longint;
procedure AddSizes(F: PFileRec); far;
begin
Inc(TotalSize, F^.Size);
end;
procedure CopyFiles(F: PFileRec); far;
begin
C.CopyFile(Mover^.Dir + '\' + F^.Name + F^.Ext,
Dest + '\' + F^.Name + F^.Ext, coNormal);
end;
begin
if Mover^.Dir = Dest then
begin
MessageBox('Files cannot be copied to same directory.',nil,
mfError + mfOKButton);
Exit;
end;
if MessageBox('Copy files to ' + Dest, nil, mfConfirmation +
mfOKCancel) <> cmOK then Exit;
C.Init(20);
TotalSize := 0;
Mover^.Items^.ForEach(@AddSizes);
ShowCopyStatusBox(TotalSize);
Mover^.Items^.ForEach(@CopyFiles);
C.Done;
KillStatusBox;
InvalidateDir(Dest);
end;
function WildCardMatch(const Name, Card: FNameStr): Boolean;
var
I, J: Integer;
begin
WildCardMatch := False;
J := 1;
I := 1;
while J <= Length(Card) do
case Card[J] of
'*':
begin
while (J <= Length(Card)) and (Card[J] <> '.') do Inc(J);
while (I <= Length(Name)) and (Name[I] <> '.') do Inc(I);
end;
'?':
begin
Inc(J);
Inc(I);
end;
'.':
begin
if I <= Length(Name) then
if Name[I] <> '.' then
Exit
else
Inc(I);
Inc(J);
end;
else
if (I > Length(Name)) or (Card[J] <> Name[I]) then Exit;
Inc(I);
Inc(J);
end;
WildCardMatch := (I > Length(Name)) and (J > Length(Card));
end;
{ TStatusBox }
procedure TStatusBox.HandleEvent(var Event:TEvent);
begin
inherited HandleEvent(Event);
if (Event.What=evBroadcast) and (Event.Command = cmStatusUpdate) then
DrawView;
end;
{ TCopier }
procedure TCopier.ReadMsg(const FName: FNameStr; Progress: Longint);
begin
StatusMsg := RezStrings^.Get(sReading) + FName;
Message(StatusBox, evBroadcast, cmStatusUpdate, nil);
Message(StatusBox, evBroadcast, cmAddGauge, Pointer(Progress div 2));
end;
procedure TCopier.WriteMsg(const FName: FNameStr; Progress: Longint);
begin
StatusMsg := RezStrings^.Get(sWriting) + FName;
Message(StatusBox, evBroadcast, cmStatusUpdate, nil);
Message(StatusBox, evBroadcast, cmAddGauge, Pointer(Progress div 2));
end;
function TCopier.IOError(const FName: FNameStr; ECode: Integer) : erAction;
var
Msg: String;
D: PDialog;
R: TRect;
P: PView;
begin
Msg := ErrorMsg(ECode);
R.Assign(0,0,55,7);
D := New(PDialog, Init(R, FName));
with D^ do
begin
Options := Options or ofCentered;
R.Assign(2,2,52,3);
Insert(New(PStaticText, Init(R, Msg)));
R.Assign(20,4,32,6);
Insert(New(PButton, Init(R, '~R~etry', cmOK, bfDefault)));
R.Move(14,0);
Insert(New(PButton, Init(R, '~A~bort', cmCancel, bfNormal)));
SelectNext(False);
end;
if Application^.ExecuteDialog(D, nil) = cmOK then IOError := erRetry
else IOError := erAbort;
end;
{ TOkListBox }
procedure TOkListBox.SelectItem(Item: Integer);
var
E: TEvent;
begin
inherited SelectItem(Item);
with E do
begin
What := evCommand;
Command := cmOk;
InfoPtr := nil;
end;
PutEvent(E);
end;
end.