home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
PC World Komputer 1995 November
/
PCWK1195.iso
/
inne
/
podstawy
/
dos
/
4dos
/
4uzytki
/
4utils86.exe
/
4DESC.PAS
< prev
next >
Wrap
Pascal/Delphi Source File
|
1995-07-09
|
39KB
|
1,108 lines
PROGRAM FileDescEditor;
{$A+,B-,D-,E-,F-,G+,L+,N-,O-,R+,S+,V-,X-}
{$M 8192,0,655360}
(* ----------------------------------------------------------------------
A Simple 4DOS File Description Editor
(c) 1992, 1993 Copyright by
David Frey, & Tom Bowden
Urdorferstrasse 30 1575 Canberra Drive
8952 Schlieren ZH Stone Mountain, GA 30088-3629
Switzerland USA
Code created using Turbo Pascal 7.0, (c) Borland International 1992
DISCLAIMER: This program is freeware: you are allowed to use, copy
and change it free of charge, but you may not sell or hire
4DESC. The copyright remains in our hands.
If you make any (considerable) changes to the source code,
please let us know. (send a copy or a listing).
We would like to see what you have done.
We, David Frey and Tom Bowden, the authors, provide absolutely
no warranty of any kind. The user of this software takes the
entire risk of damages, failures, data losses or other
incidents.
----------------------------------------------------------------------- *)
USES {$IFOPT G+} Test286, {$ENDIF}
Fix, Crt, Dos, Memory, Drivers,
StringDateHandling, DisplayKeyboardAndCursor, HandleINIFile,
DescriptionHandling, dmouse;
CONST DelimiterTable : STRING = ',.();:-!?/[]{}+*=''`"@%&$_£';
VAR EdStart : BYTE; (* column where the description starts *)
ActDir : DirStr; (* current directory *)
StartDir : DirStr; (* directory where we started from *)
ResetDir : BOOLEAN; (* TRUE = return to StartDir on exit *)
StartIndex : INTEGER; (* index of entry at the top of the screen *)
Index : INTEGER; (* index of entry we are editing *)
CutPasteDesc: DescStr; (* cut, resp. pasted description *)
Changed : BOOLEAN; (* TRUE=the descriptions have been edited *)
IORes : INTEGER;
NewDir : DirStr; (* temporary storage for a directory path, *)
NewName : NameExtStr;(* used by view and others *)
FirstParam : STRING[8];
i : BYTE; (* variable for counting (index etc) *)
ShowHelp : BOOLEAN; (* TRUE = start in help mode [/h] *)
Querier : BOOLEAN; (* TRUE = ask user if he wants to save
the descriptions [/dontask] *)
PasteMovesToNextIndex: BOOLEAN; (* TRUE = Paste advances to next index *)
Overwrite : BOOLEAN; (* overwrite / Insert mode *)
s : STRING; (* temporary string variable *)
(*-------------------------------------------------------- Display-Routines *)
PROCEDURE DisplayFileEntry(Index: INTEGER; ox, x: BYTE;
Selection, Hilighted: BOOLEAN);
(* Displays the Index'th file entry. If the description is longer than
DispLen characters, DispLen characters - starting at character x of the
description - will be shown. (this feature is needed for scrolling).
Hilighted = TRUE will hilight the description.
When Selection is TRUE, we want to display the text we just put into
the buffer, ox (old x) gives us the start of the selection.
P.S. Scrolling implies hilighting, but this fact has not been exploited. *)
VAR FileEntry : PFileData;
xs,oxs,t : BYTE;
y,l : BYTE;
s : STRING;
BEGIN
y := 3+Index-StartIndex;
IF (Index >= 0) AND (Index < FileList^.Count) THEN
BEGIN
FileEntry := NILCheck(FileList^.At(Index));
IF x <= DispLen THEN xs := 1
ELSE
IF x <= 2*DispLen THEN xs := DispLen+1
ELSE
IF x <= 3*DispLen THEN xs := 2*DispLen+1
ELSE
IF x <= 4*DispLen THEN xs := 3*DispLen+1
ELSE xs := 4*DispLen+1;
(* I haven't found a simple formula yet, so I'm doing the
job with a table. That's the lazy's man solution .... *)
IF ox <= DispLen THEN oxs := 1
ELSE
IF ox <= 2*DispLen THEN oxs := DispLen+1
ELSE
IF ox <= 3*DispLen THEN oxs := 2*DispLen+1
ELSE
IF ox <= 4*DispLen THEN oxs := 3*DispLen+1
ELSE oxs := 4*DispLen+1;
IF Hilighted THEN
BEGIN TextColor(SelectFg); TextBackGround(SelectBg); END
ELSE
BEGIN
TextBackGround(NormBg);
IF FileEntry^.IsADir THEN TextColor(DirFg)
ELSE TextColor(NormFg)
END;
GotoXY(1,y);
s := FileEntry^.FormatScrollableDescription(xs,DispLen);
IF Selection THEN
BEGIN
IF ox > x THEN BEGIN t := x; x := ox; ox := t; END
ELSE t := x;
IF ox < xs THEN ox := xs;
Write(Copy(s,1,EdStart+ox-xs-1));
TextBackGround(NormFg); TextColor(NormBg); Write(Copy(s,EdStart+ox-xs,x-ox));
TextBackGround(SelectBg);TextColor(SelectFg); Write(Copy(s,EdStart+x-xs,255));
x := t;
END
ELSE Write(s);
l := Length(FileEntry^.GetDesc);
IF l-xs < DispLen THEN
ClrEol
ELSE
BEGIN
TextColor(WarnFg); Write(Chr(16)); TextColor(NormFg);
END;
(* IF x <= DispLen THEN GotoXY(EdStart+x-1,y)
ELSE GotoXY(EdStart+DispLen-1,y) *)
GotoXY(EdStart+x-xs,y);
END
ELSE BEGIN GotoXY(1,y); ClrEol; END;
END; (* DisplayFileEntry *)
PROCEDURE DrawDirLine(UpdateDir: BOOLEAN);
(* Draw the line, which tells us where in the directory tree we are. *)
BEGIN
IF UpdateDir THEN
BEGIN
GetDir(0,ActDir);
IF ActDir[Length(ActDir)] <> '\' THEN ActDir := ActDir + '\';
UpString(ActDir);
END;
TextColor(DirFg); TextBackGround(NormBg);
GotoXY(1,2); Write(ActDir); ClrEol;
END; (* DrawDirLine *)
PROCEDURE ReDrawScreen;
(* Redraws the full screen, needed after shelling out or after printing
the help screen. *)
VAR Index: INTEGER;
BEGIN
(* GetDir(0,ActDir); *)
FOR Index := StartIndex TO StartIndex+MaxLines-4 DO
DisplayFileEntry(Index,0,1,FALSE,FALSE);
END; (* ReDrawScreen *)
(*-------------------------------------------------------- Read-Directory *)
PROCEDURE ReadFiles;
(* Scan the current directory and read in the DESCRIPT.ION file. Build a
file list database and associate the right description.
Warn the user if there are too long descriptions or if there are too
much descriptions. *)
VAR i : BYTE;
ch : WORD;
Dir : PathStr;
BEGIN
Changed := FALSE;
DescLong := FALSE;
Index := 0;
StartIndex := 0;
Dir := FExpand('.');
IF FileList <> NIL THEN
BEGIN
Dispose(FileList,Done); FileList := NIL;
END;
TextColor(StatusFg); TextBackGround(StatusBg);
GotoXY(1,MaxLines);
IF (ScreenWidth-39-Length(Dir)) > 0 THEN
Write(Chars(' ',(ScreenWidth-39-Length(Dir)) DIV 2));
Write('Scanning directory ',Dir,' ..... please wait.');
ClrEol;
FileList := NIL; FileList := New(PFileList,Init(Dir,'*.*',0));
IF FileList = NIL THEN Abort('Unable to allocate FileList');
IF (FileList^.Status = ListTooManyFiles) OR
(FileList^.Status = ListOutofMem) THEN
BEGIN
TextColor(NormFg); TextBackGround(NormBg);
FOR i := 3 TO MaxLines-1 DO
BEGIN
GotoXY(1,i); ClrEol;
END;
IF FileList^.Status = ListTooManyFiles THEN
ReportError('Warning! Too many files in directory, description file will be truncated! (Key)',(CutPasteDesc <> ''),Changed)
ELSE
ReportError('Warning! Out of memory, description file will be truncated! (Key)',(CutPasteDesc <> ''),Changed);
END;
IF FileList^.Count > 0 THEN
BEGIN
DrawMainScreen(Index,FileList^.Count,1,0); DrawDirLine(TRUE);
END;
IF DescLong THEN
BEGIN
TextColor(NormFg); TextBackGround(NormBg);
FOR i := 3 TO MaxLines-1 DO
BEGIN
GotoXY(1,i); ClrEol;
END;
ReportError('Warning! Some descriptions are too long; they will be truncated. Press any key.',(CutPasteDesc <> ''),Changed);
END;
END; (* ReadFiles *)
(*-------------------------------------------------------- Save Descriptions *)
PROCEDURE SaveDescriptions;
(* Save the modified descriptions currently held in memory onto disk.
Rename the old description file into DESCRIPT.OLD and write the
new one out. Any problems occuring at this point (disk full etc),
raise a warning message and cause a deletion of the (half-written)
description file DESCRIPT.ION. In this case the user "only" looses his
new, edited descriptions, but the old ones are stored in the DESCRIPT.OLD
file and can be restored by typing
REN DESCRIPT.OLD DESCRIPT.ION
ATTRIB +H DESCRIPT.ION
If all went fine, the old description file gets deleted. This procedure
minimizes data loss. *)
VAR DescFile : TEXT;
DescSaved : BOOLEAN;
Time : DateTime;
ch : WORD;
FileEntry : PFileData;
PROCEDURE SaveEntry(FileEntry: PFileData); FAR;
(* Save a single description, writes a single line of the description
file. This procedures is called for each entry in the FileEntry list *)
VAR Desc : DescStr;
ProgInfo : STRING;
Dir : DirStr;
BaseName : NameStr;
Ext : ExtStr;
BEGIN
Desc := FileEntry^.GetDesc;
StripLeadingSpaces(Desc); StripTrailingSpaces(Desc);
IF Desc <> '' THEN
BEGIN
StripTrailingSpaces(FileEntry^.Name);
Write(DescFile,FileEntry^.Name);
StripLeadingSpaces(FileEntry^.Ext);
StripTrailingSpaces(FileEntry^.Ext);
IF FileEntry^.Ext <> '' THEN Write(DescFile,FileEntry^.Ext);
Write(DescFile,' ',Desc);
IF DescSaved = FALSE THEN DescSaved := TRUE;
ProgInfo := FileEntry^.GetProgInfo;
IF ProgInfo <> '' THEN Write(DescFile,ProgInfo);
WriteLn(DescFile);
END;
END; (* SaveEntry *)
BEGIN
DescSaved := FALSE;
IF DiskFree(0) < FileList^.Count*SizeOf(TFileData) THEN
ReportError('Probably out of disk space. Nevertheless trying to save DESCRIPT.ION...',(CutPasteDesc <> ''),Changed);
TextColor(StatusFg); TextBackGround(StatusBg);
GotoXY(1,MaxLines);
Write(Chars(' ',((ScreenWidth-41) div 2)),
'Saving descriptions........ please wait.');
ClrEol;
{$I-}
Assign(DescFile,'DESCRIPT.ION'); Rename(DescFile,'DESCRIPT.OLD'); IORes := IOResult;
Assign(DescFile,'DESCRIPT.ION'); SetFAttr(DescFile,Archive); IORes := IOResult;
Rewrite(DescFile);
{$I+}
IF IOResult > 0 THEN
BEGIN
ReportError('Unable to write DESCRIPT.ION !',(CutPasteDesc <> ''),Changed);
{$I-}
Assign(DescFile,'DESCRIPT.OLD'); Rename(DescFile,'DESCRIPT.ION'); IORes := IOResult;
{$I+}
END
ELSE
BEGIN
FileList^.ForEach(@SaveEntry);
{$I-}
Close(DescFile);
{$I+}
IF IOResult > 0 THEN
BEGIN
ReportError('Unable to write DESCRIPT.ION !',(CutPasteDesc <> ''),Changed);
{$I-}
Assign(DescFile,'DESCRIPT.OLD'); Rename(DescFile,'DESCRIPT.ION'); IORes := IOResult;
{$I+}
END
ELSE
BEGIN
IF DescSaved THEN SetFAttr(DescFile, Archive + Hidden)
ELSE Erase(DescFile); (* Don't keep zero-byte file. *)
Changed := FALSE; DrawStatusLine(TRUE,(CutPasteDesc <> ''),Changed,FALSE);
{$I-}
Assign(DescFile,'DESCRIPT.OLD'); Erase(DescFile); IORes := IOResult;
{$I+}
END;
END;
END; (* SaveDescriptions *)
(*-------------------------------------------------------- Edit Descriptions *)
PROCEDURE EditDescriptions;
(* This is the heart of 4DESC: the editing of the descriptions. *)
VAR Key : WORD;
Drv : STRING[3];
LastDrv : CHAR;
x,y,l : BYTE; (* current cursor position *)
ox : BYTE; (* old cursor position *)
EditStr : DescStr;
InShiftState : BOOLEAN;
Cmd : BYTE;
Cursor : WORD;
OldDir : DirStr;
ActFileData : PFileData;
n : NameExtStr;
ReverseFlag : BOOLEAN; (* for sorting *)
f : FILE; (* used for delete *)
PROCEDURE UpdateLineNum(Index: INTEGER);
(* Update the line number indicator in the right corner and redraw
the associated description line *)
BEGIN
TextColor(StatusFg); TextBackGround(StatusBg);
GotoXY(46,1); Write(Index+1:5);
IF Changed THEN DrawStatusLine(FALSE,(CutPasteDesc <> ''),Changed,ReverseFlag);
IF Index < FileList^.Count THEN
BEGIN
EditStr := PFileData(FileList^.At(Index))^.GetDesc;
DisplayFileEntry(Index,0,1,FALSE,TRUE);
END;
ActFileData := NILCheck(FileList^.At(Index));
END;
PROCEDURE UpdateColNum(Col, CurDescLen: BYTE);
(* Update the column number indicator in the right corner *)
VAR x,y: BYTE;
BEGIN
x := WhereX; y := WhereY;
TextColor(StatusFg); TextBackGround(StatusBg);
GotoXY(66,1); Write(Col:3); GotoXY(77,1); Write(CurDescLen:3);
(* TextBackGround(NormBg);
GotoXY(EdStart+40-xs,MaxLines); Write('^'); *)
GotoXY(x,y);
END;
PROCEDURE PrevIndex(VAR Index: INTEGER);
(* Go up one description line (if possible) *)
BEGIN
Index := Max(Index-1,0);
IF Index <= StartIndex THEN
BEGIN
StartIndex := Max(Index-ScreenSize,Index);
RedrawScreen;
END;
UpdateLineNum(Index);
END; (* PrevIndex *)
PROCEDURE NextIndex(VAR Index: INTEGER);
(* Go down one description line (if possible) *)
BEGIN
Index := Min(Index+1,FileList^.Count-1);
IF Index > StartIndex+ScreenSize THEN
BEGIN
StartIndex := Index-ScreenSize;
RedrawScreen;
END;
UpdateLineNum(Index);
END; (* NextIndex *)
PROCEDURE QuerySaveDescriptions;
(* Ask the user if he really wants to save the descriptions. *)
VAR ch: CHAR;
BEGIN
IF Querier THEN
BEGIN
TextColor(StatusFg); TextBackGround(StatusBg);
IF Changed THEN
BEGIN
GotoXY(1,MaxLines);
Write(Chars(' ',(ScreenWidth-58) div 2),
'Descriptions have been edited. Shall they be saved (Y/N) ?');
ClrEol;
ch := ' ';
REPEAT
If KeyPressed Then ch := UpCase(ReadKey)
Else
If MouseLoaded Then
Begin
ButtonReleased(Left);
If ReleaseCount > 0 Then ch := 'Y';
ButtonReleased(Right);
If ReleaseCount > 0 Then ch := 'N';
End;
UNTIL (ch = 'Y') OR (ch = 'N');
Write(' ',ch);
IF ch = 'Y' THEN SaveDescriptions;
END;
END
ELSE SaveDescriptions; (* always save, when not in query mode *)
END; (* QuerySaveDescriptions *)
PROCEDURE DirUp;
(* Go up one directory in the directory tree (if possible) *)
BEGIN
IF Changed THEN QuerySaveDescriptions;
{$I-}
ChDir('..');
{$I+}
IF IOResult = 0 THEN
BEGIN
ReadFiles;
RedrawScreen;
DrawStatusLine(TRUE,(CutPasteDesc <> ''),Changed,ReverseFlag);
Index := 0; UpdateLineNum(Index);
END;
END; (* DirUp *)
PROCEDURE DirDown;
(* Go down one directory in the directory tree (if possible) *)
BEGIN
IF (Index < FileList^.Count) THEN
BEGIN
n := ActFileData^.Name+ActFileData^.Ext;
IF (ActFileData^.IsADir) AND (n[1] <> '.') THEN
BEGIN
IF Changed THEN QuerySaveDescriptions;
{$I-}
ChDir(n);
{$I+}
IF IOResult = 0 THEN
BEGIN
ReadFiles;
RedrawScreen;
END;
DrawStatusLine(TRUE,(CutPasteDesc <> ''),Changed, ReverseFlag);
Index := 0; UpdateLineNum(Index);
END; (* IF Description[Index].Size = DirSize *)
END;
END; (* DirDown *)
PROCEDURE ReSortDirectory;
BEGIN
ReSortFileList; ReverseFlag := FALSE;
DrawStatusLine(FALSE,(CutPasteDesc <> ''),Changed, ReverseFlag);
StartIndex := 0; Index := 0;
RedrawScreen; UpdateLineNum(Index);
END; (* ReSortDirectory *)
FUNCTION IsADelimiter(c: CHAR): BOOLEAN;
(* used by Ctrl-Left resp Ctrl-Right to recognize the end of a word *)
BEGIN
IsADelimiter := (Pos(c,DelimiterTable) > 0);
END;
BEGIN (* EditDescriptions *)
Index := 0; UpdateLineNum(Index);
ResetCursor(Overwrite);
EditStr := ActFileData^.GetDesc;
ReverseFlag := FALSE; InShiftState := FALSE; x := 1;
REPEAT
REPEAT
Key := $0000;
IF KeyPressed THEN Key := GetKey
ELSE
BEGIN
IF MouseLoaded THEN
BEGIN
MouseMotion;
IF VMickey > VMickeysPerKeyPress THEN Key := kbDown
ELSE
IF VMickey < -VMickeysPerKeyPress THEN Key := kbUp
ELSE
IF HMickey > HMickeysPerKeyPress THEN Key := kbRight
ELSE
IF HMickey < -HMickeysPerKeyPress THEN Key := kbLeft
ELSE
BEGIN
ButtonReleased(Left);
IF ReleaseCount > 0 THEN Key := kbEnter;
ButtonReleased(Right);
IF ReleaseCount > 0 THEN Key := kbEsc;
END;
END; (* if mouseloaded *)
END;
UNTIL Key <> $0000;
IF NOT InShiftState THEN ox := x;
(* save the old cursor position for cutting *)
CASE Key OF
kbUp : BEGIN
ActFileData^.AssignDesc(EditStr);
DisplayFileEntry(Index,0,x,FALSE,FALSE);
PrevIndex(Index);
IF x > Length(EditStr) THEN x := Max(Length(EditStr),1);
DisplayFileEntry(Index,0,x,FALSE,FALSE);
InShiftState := FALSE;
END; (* Up *)
kbDown : BEGIN
ActFileData^.AssignDesc(EditStr);
DisplayFileEntry(Index,0,x,FALSE,FALSE);
NextIndex(Index);
IF x > Length(EditStr) THEN x := Max(Length(EditStr),1);
DisplayFileEntry(Index,0,x,FALSE,FALSE);
InShiftState := FALSE;
END; (* Down *)
kbLeft : BEGIN
x := Max(1,x-1);
InShiftState := (GetShiftState AND (kbRightShift+kbLeftShift) <> 0);
END; (* Left *)
kbRight : BEGIN
x := Max(1,Min(1+x,Length(EditStr)+1));
InShiftState := (GetShiftState AND (kbRightShift+kbLeftShift) <> 0);
END; (* Right *)
kbCtrlLeft : BEGIN
DEC(x);
WHILE (x > 0) AND IsADelimiter(EditStr[x]) DO DEC(x);
WHILE (x > 0) AND NOT IsADelimiter(EditStr[x]) DO DEC(x);
INC(x);
InShiftState := (GetShiftState AND (kbRightShift+kbLeftShift) <> 0);
END; (* ^Left *)
kbCtrlRight: BEGIN
l := Length(EditStr);
WHILE (x < l) AND NOT IsADelimiter(EditStr[x]) DO INC(x);
WHILE (x < l) AND IsADelimiter(EditStr[x]) DO INC(x);
IF x = l THEN INC(x);
InShiftState := (GetShiftState AND (kbRightShift+kbLeftShift) <> 0);
END; (* ^Right *)
kbHome : BEGIN
x := 1;
InShiftState := (GetShiftState AND (kbRightShift+kbLeftShift) <> 0);
END; (* Home *)
kbEnd : BEGIN
x := Min(Length(EditStr)+1,MaxDescLen);
InShiftState := (GetShiftState AND (kbRightShift+kbLeftShift) <> 0);
END; (* End *)
kbCtrlHome : BEGIN
Delete(EditStr,1,x);
ActFileData^.AssignDesc(EditStr);
x := 1;
Changed := TRUE; InShiftState := FALSE;
END; (* ^Home *)
kbCtrlEnd : BEGIN
Delete(EditStr,x,MaxDescLen);
ActFileData^.AssignDesc(EditStr);
Changed := TRUE; InShiftState := FALSE;
END; (* ^End *)
kbIns : BEGIN
IF GetShiftState AND kbCtrlShift = kbCtrlShift THEN (* ^Ins: Copy *)
BEGIN
CutPasteDesc := Copy(EditStr,ox,x-ox);
Changed := TRUE;
END
ELSE IF GetShiftState AND (kbRightShift+kbLeftShift) <> 0 THEN (* Shift-Ins: Paste *)
BEGIN
IF CutPasteDesc > '' THEN
BEGIN
EditStr := Copy(EditStr,1,x-1)+CutPasteDesc+Copy(EditStr,x,255);
ActFileData^.AssignDesc(EditStr);
Changed := TRUE;
IF PasteMovesToNextIndex THEN
BEGIN
DisplayFileEntry(Index,0,x,FALSE,FALSE);
NextIndex(Index);
END;
END
END
ELSE
BEGIN
Overwrite := NOT Overwrite; ResetCursor(Overwrite);
END;
END; (* Ins *)
kbDel : BEGIN
IF GetShiftState AND kbCtrlShift = kbCtrlShift THEN (* ^Del: Clear *)
BEGIN
System.Delete(EditStr,ox,x-ox); x := ox;
ActFileData^.AssignDesc(EditStr);
Changed := TRUE; InShiftState := FALSE;
DisplayFileEntry(Index,0,x,FALSE,FALSE);
END
ELSE IF GetShiftState AND (kbRightShift+kbLeftShift) <> 0 THEN (* Shift-Del: Cut *)
BEGIN
CutPasteDesc := Copy(EditStr,ox,x-ox);
Delete(EditStr,ox,x-ox); x := ox;
ActFileData^.AssignDesc(EditStr);
Changed := TRUE; InShiftState := FALSE;
DisplayFileEntry(Index,0,x,FALSE,FALSE);
END
ELSE
BEGIN
IF x <= Length(EditStr) THEN Delete(EditStr,x,1);
ActFileData^.AssignDesc(EditStr);
Changed := TRUE;
END;
END; (* Del *)
kbBack : BEGIN
Delete(EditStr,x-1,1);
ActFileData^.AssignDesc(EditStr);
IF x > 1 THEN
BEGIN
DEC(x);
IF x > Length(EditStr) THEN x := Length(EditStr)+1;
END;
Changed := TRUE; InShiftState := FALSE;
END; (* Backspace *)
kbPgUp : BEGIN
ActFileData^.AssignDesc(EditStr);
x := 1;
DisplayFileEntry(Index,0,x,FALSE,FALSE);
Index := Max(Index-ScreenSize,0);
StartIndex := Index;
RedrawScreen;
UpdateLineNum(Index);
InShiftState := FALSE;
END; (* PgUp *)
kbPgDn : BEGIN
ActFileData^.AssignDesc(EditStr);
Index := Min(Index+ScreenSize,FileList^.Count-1);
StartIndex := Max(Index-ScreenSize,0);
x := 1;
DisplayFileEntry(Index,0,x,FALSE,FALSE);
RedrawScreen;
UpdateLineNum(Index);
InShiftState := FALSE;
END; (* PgDn *)
kbCtrlPgUp : BEGIN
ActFileData^.AssignDesc(EditStr);
x := 1;
DisplayFileEntry(Index,0,x,FALSE,FALSE);
StartIndex := 0; Index := 0;
RedrawScreen;
UpdateLineNum(Index);
ActFileData^.AssignDesc(EditStr);
DisplayFileEntry(Index,0,x,FALSE,FALSE);
IF Length(ActDir) > 3 THEN NextIndex(Index);
InShiftState := FALSE;
END; (* ^PgUp *)
kbCtrlPgDn : BEGIN
ActFileData^.AssignDesc(EditStr);
x := 1;
DisplayFileEntry(Index,0,x,FALSE,FALSE);
StartIndex := Max(FileList^.Count-ScreenSize-1,0);
Index := FileList^.Count-1;
RedrawScreen;
UpdateLineNum(Index);
InShiftState := FALSE;
END; (* ^PgDn *)
kbAltD : BEGIN
EditStr := ''; ActFileData^.AssignDesc('');
Changed := TRUE; InShiftState := FALSE;
x := 1;
IF PasteMovesToNextIndex THEN
BEGIN
DisplayFileEntry(Index,0,x,FALSE,FALSE);
NextIndex(Index);
END;
END; (* Alt-D *)
kbAltM,
kbAltT : BEGIN
CutPasteDesc := ActFileData^.GetDesc;
ActFileData^.AssignDesc(''); EditStr := '';
Changed := TRUE; InShiftState := FALSE;
x := 1;
IF PasteMovesToNextIndex THEN
BEGIN
DisplayFileEntry(Index,0,x,FALSE,FALSE);
NextIndex(Index);
END;
END; (* Alt-M / Alt-T *)
kbAltC : BEGIN
CutPasteDesc := ActFileData^.GetDesc;
x := 1;
InShiftState := FALSE;
DrawStatusLine(TRUE,(CutPasteDesc <> ''),Changed, ReverseFlag);
END; (* Alt-C *)
kbAltP : BEGIN
IF CutPasteDesc > '' THEN
BEGIN
EditStr := CutPasteDesc; ActFileData^.AssignDesc(EditStr);
Changed := TRUE; InShiftState := FALSE;
IF PasteMovesToNextIndex THEN
BEGIN
DisplayFileEntry(Index,0,x,FALSE,FALSE);
NextIndex(Index);
END;
END
END;
kbEnter : BEGIN
ActFileData^.AssignDesc(EditStr);
x := 1;
IF (Index < FileList^.Count) THEN
BEGIN
n := ActFileData^.Name+ActFileData^.Ext;
IF ActFileData^.IsADir THEN
IF (n[1] = '.') AND (n[2] = '.') THEN DirUp
ELSE
IF n[1] <> '.' THEN DirDown;
END;
END; (* Enter = go into directory where the cursor is at *)
kbF1 : BEGIN (* F1: Help *)
ShowHelpPage;
ResetCursor(Overwrite);
DrawMainScreen(Index,FileList^.Count,x,Length(EditStr));
DrawDirLine(FALSE);
RedrawScreen;
UpdateLineNum(Index);
END; (* F1 *)
kbF4 : DirDown; (* F4 *)
kbF5 : DirUp; (* F5 *)
kbAltL,
kbF6 : BEGIN (* F6: Change Drive *)
IF Changed THEN QuerySaveDescriptions;
ASM
mov ah,0eh (* Select Disk *)
mov dl,3
int 21h
add al,'@'
mov LastDrv,al
END;
IF LastDrv > 'Z' THEN LastDrv := 'Z';
TextColor(StatusFg); TextBackGround(StatusBg); Drv := ' :';
GotoXY(1,MaxLines);
Write(Chars(' ',((ScreenWidth-24) div 2)),
'New drive letter (A..',LastDrv,'): ');
ClrEol;
REPEAT
Drv[1] := UpCase(ReadKey);
UNTIL (Drv[1] >= 'A') AND (Drv[1] <= LastDrv);
IF Drv[1] <= 'B' THEN Drv := Drv + '\';
OldDir := ActDir;
{$I-}
ChDir(Drv);
{$I+}
IF IOResult = 0 THEN
BEGIN
GetDir(0,ActDir); IORes := IOResult;
ReadFiles;
IF FileList^.Count = 0 THEN
BEGIN
IF (Length(OldDir) > 3) AND (OldDir[Length(OldDir)] = '\') THEN
Delete(OldDir,Length(OldDir),1);
{$I-}
ChDir(OldDir); IORes := IOResult;
{$I+}
ReportError('There are no files on drive '+Drv+'. Press any key.',(CutPasteDesc <> ''),Changed);
ReadFiles;
END;
RedrawScreen;
Index := 0;
UpdateLineNum(Index);
END
ELSE
ReportError('Drive '+Drv+' not ready! Drive remains unchanged, press a key.',(CutPasteDesc <> ''),Changed);
END; (* Alt-L or F6 *)
kbF2 : BEGIN (* F2: Save *)
SaveDescriptions;
UpdateLineNum(Index);
END; (* F10 or F2 *)
kbAltS,
kbShiftF10: BEGIN (* Shell to [4]DOS *)
IF Changed THEN QuerySaveDescriptions;
DoneMemory;
SetMemTop(HeapPtr);
NormVideo; ClrScr;
WriteLn('Type `Exit'' to return to 4DESC.');
SwapVectors;
Exec(GetEnv('COMSPEC'),'');
SwapVectors;
SetMemTop(HeapEnd);
InitMemory;
IF MouseLoaded THEN MouseReset;
ClrScr;
DrawMainScreen(Index,FileList^.Count,x,Length(EditStr));
DrawStatusLine(TRUE,(CutPasteDesc <> ''),Changed, ReverseFlag);
DrawDirLine(TRUE);
IF DosError > 0 THEN
ReportError('Can''t load command interpreter / program execution failed.',
(CutPasteDesc <> ''),Changed);;
ReadFiles;
RedrawScreen;
UpdateLineNum(Index);
ResetCursor(Overwrite);
END; (* Alt-S or F10 *)
kbF3, (* F3, Alt-V: View File *)
kbAltV, (* Alt-E: Edit File *)
kbAltE : IF (Index < FileList^.Count) THEN
BEGIN
IF NOT ActFileData^.IsADir THEN
BEGIN
NewName := ActFileData^.Name;
StripTrailingSpaces(NewName);
NewName := NewName+ActFileData^.Ext;
NewDir := ActDir; (* I do not want to loose actdir, newdir
is only a "dummy" variable. *)
IF NewDir[Length(NewDir)] = '\' THEN Delete(NewDir,Length(NewDir),1);
DoneMemory;
SetMemTop(HeapPtr);
SwapVectors;
NormVideo; ClrScr;
IF Key = kbAltE THEN
Exec(GetEnv('COMSPEC'),'/c '+EditCmd+' '+NewDir+'\'+NewName)
ELSE
Exec(GetEnv('COMSPEC'),'/c '+ListCmd+' '+NewDir+'\'+NewName);
SwapVectors;
SetMemTop(HeapEnd);
InitMemory;
IF MouseLoaded THEN MouseReset;
ClrScr;
DrawMainScreen(Index,FileList^.Count,x,Length(EditStr));
DrawStatusLine(TRUE,(CutPasteDesc <> ''),Changed, ReverseFlag);
DrawDirLine(FALSE);
IF DosError > 0 THEN ReportError('Can''t load command interpreter/program execution failed.',
(CutPasteDesc <> ''),Changed);
RedrawScreen;
UpdateLineNum(Index);
ResetCursor(Overwrite);
END;
END; (* F3, Alt-V, or Alt-E *)
(* Sorting Options *)
Ord('R')-64 : BEGIN
ReverseFlag := NOT ReverseFlag;
DrawStatusLine(FALSE,(CutPasteDesc <> ''),Changed, ReverseFlag);
END;
Ord('N')-64 : BEGIN
IF NOT ReverseFlag THEN SortKey := SortByName
ELSE SortKey := SortByNameRev;
ReSortDirectory;
END;
Ord('E')-64 : BEGIN
IF NOT ReverseFlag THEN SortKey := SortByExt
ELSE SortKey := SortByExtRev;
ReSortDirectory;
END;
Ord('S')-64 : BEGIN
IF NOT ReverseFlag THEN SortKey := SortBySize
ELSE SortKey := SortBySizeRev;
ReSortDirectory;
END;
Ord('D')-64 : BEGIN
IF NOT ReverseFlag THEN SortKey := SortByDate
ELSE SortKey := SortByDateRev;
ReSortDirectory;
END;
kbF8, kbAltK : BEGIN (* delete File *)
NewName := ActFileData^.Name;
StripTrailingSpaces(NewName);
NewName := NewName+ActFileData^.Ext;
GotoXY(1,MaxLines);
Write('Deleting ',NewName,'...'); ClrEol;
{$I-}
Assign(f,NewName);
Erase(f);
{$I+}
IF IOResult > 0 THEN
ReportError('Can''t delete'+NewName+'!',
(CutPasteDesc <> ''),Changed);
ReadFiles;
RedrawScreen;
UpdateLineNum(Index);
ResetCursor(Overwrite);
END;
ELSE
IF (Ord(Key) > 31) AND (Ord(Key) < 256) THEN
BEGIN
IF NOT Changed THEN Changed := TRUE;
ReverseFlag := FALSE; InShiftState := FALSE;
IF x <= MaxDescLen THEN
BEGIN
IF Overwrite AND (x <= Length(EditStr)) THEN
EditStr[x] := Chr(Key)
ELSE
EditStr := Copy(EditStr,1,x-1)+Chr(Key)+Copy(EditStr,x,255);
ActFileData^.AssignDesc(EditStr);
INC(x); UpdateColNum(x,Length(EditStr));
END;
END; (* all others *)
END; (* case *)
(* Select with the Shift Keys *)
IF InShiftState THEN CutPasteDesc := Copy(EditStr,ox,x-ox);
IF Changed THEN
DrawStatusLine(TRUE,(CutPasteDesc <> ''),Changed, ReverseFlag);
DisplayFileEntry(Index,ox,x,InShiftState,TRUE);
UpdateColNum(x,Length(EditStr));
UNTIL (Key = kbEsc) OR (* Esc = exit to original directory and save *)
(Key = kbF10) OR (* F10 = exit to current directory and save *)
(Key = kbAltX) OR (* Alt-X = exit to current directory and save *)
(Key = kbAltQ); (* Alt-Q = exit to original directory, don't save *)
IF (Key = kbEsc) OR (Key = kbAltQ) THEN ResetDir := TRUE
ELSE ResetDir := FALSE;
IF Changed AND (Key <> kbAltQ) THEN QuerySaveDescriptions;
END; (* EditDescriptions *)
(*-------------------------------------------------------- Main *)
BEGIN
{$I-}
GetDir(0,StartDir); IORes := IOResult;
{$I+}
ShowHelp := FALSE; Querier := TRUE;
IF ParamCount > 0 THEN
BEGIN
FOR i := 1 TO Min(2,ParamCount) DO
BEGIN
FirstParam := ParamStr(i);
IF (FirstParam[1] = '/') OR (FirstParam[1] = '-') THEN
BEGIN
IF NOT Monochrome THEN Monochrome := (UpCase(FirstParam[2]) = 'M');
IF Querier THEN Querier := NOT (UpStr(Copy(FirstParam,2,Length(FirstParam)-1)) = 'DONTASK');
IF NOT ShowHelp THEN ShowHelp := (UpCase(FirstParam[2]) = 'H') OR
(FirstParam[2] = '?');
END;
END; (* for ... do begin *)
NewDir := UpStr(ParamStr(ParamCount));
IF (NewDir[1] <> '/') AND (NewDir[1] <> '-') THEN
BEGIN
{$I-}
ChDir(NewDir); IORes := IOResult;
{$I+}
END;
END; (* if paramcount > 0 *)
(* Read the .INI files *)
InitMemory;
INIStrings := New(PINIStrings,Init); (* Read in the .INI file(s) *)
IF INIFileExists THEN StringDateHandling.EvaluateINIFileSettings;
(* The Date & Time Formats are country-specific and are pre-initialized
in the StringDateHandling initialize-section. Re-Initializing it
with "our" defaults is not what the users wants. *)
DescriptionHandling.EvaluateINIFileSettings;
DisplayKeyboardAndCursor.EvaluateINIFileSettings;
ChooseColors(Monochrome);
dmouse.EvaluateINIFileSettings;
IF UseMouse THEN MouseReset;
DelimiterTable := ReadSettingsString('misc','delimiters',DelimiterTable);
DelimiterTable := ' '+DelimiterTable;
PasteMovesToNextIndex := (ReadSettingsChar('misc','pastemovestonextindex','y') = 'y');
overwrite := (ReadSettingsString('','editmode','overstrike') = 'overstrike');
Dispose(INIStrings,Done); INIStrings := NIL;
EdStart := 25+Length(DateFormat)+Length(TimeFormat);
DispLen := ScreenWidth-EdStart;
Str(DispLen,s); Template:= '%-12s %s %s %s %-'+s+'s';
Changed := FALSE; CutPasteDesc := '';
DrawMainScreen(0,0,0,0);
IF ShowHelp THEN ShowHelpPage;
IF IORes > 0 THEN
ReportError(NewDir+' not found. Directory remains unchanged.',FALSE,FALSE);
ReadFiles;
IF DosError = 0 THEN
BEGIN
RedrawScreen;
EditDescriptions;
END
ELSE
BEGIN
ReportError('Drive '+NewDir+' not ready, exiting (key).',FALSE,FALSE);
ResetDir := TRUE;
END;
Dispose(FileList,Done); FileList := NIL;
DoneMemory;
IF ResetDir THEN
BEGIN
{$I-}
ChDir(StartDir);
IORes := IOResult;
{$I+}
END;
IF MouseLoaded THEN MouseReset;
SetCursorShape(OrigCursor);
NormVideo;
ClrScr;
WriteLn(Header1);
WriteLn(Header2);
WriteLn;
WriteLn('This program is freeware: you are allowed to use, copy it free');
WriteLn('of charge, but you may not sell or hire 4DESC.');
END.