{************************************************} { } { 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 := 'תתתת'; 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); KillStatusBox; C.Done; 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.