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 >
Pascal/Delphi Source File  |  1995-07-09  |  39KB  |  1,108 lines

  1. PROGRAM FileDescEditor;
  2. {$A+,B-,D-,E-,F-,G+,L+,N-,O-,R+,S+,V-,X-}
  3. {$M 8192,0,655360}
  4.  
  5. (* ----------------------------------------------------------------------
  6.    A Simple 4DOS File Description Editor
  7.  
  8.    (c) 1992, 1993 Copyright by
  9.  
  10.        David Frey,         & Tom Bowden
  11.        Urdorferstrasse 30    1575 Canberra Drive
  12.        8952 Schlieren ZH     Stone Mountain, GA 30088-3629
  13.        Switzerland           USA
  14.  
  15.        Code created using Turbo Pascal 7.0, (c) Borland International 1992
  16.  
  17.    DISCLAIMER: This program is freeware: you are allowed to use, copy
  18.                and change it free of charge, but you may not sell or hire
  19.                4DESC. The copyright remains in our hands.
  20.  
  21.                If you make any (considerable) changes to the source code,
  22.                please let us know. (send a copy or a listing).
  23.                We would like to see what you have done.
  24.  
  25.                We, David Frey and Tom Bowden, the authors, provide absolutely
  26.                no warranty of any kind. The user of this software takes the
  27.                entire risk of damages, failures, data losses or other
  28.                incidents.
  29.  
  30.    ----------------------------------------------------------------------- *)
  31.  
  32. USES {$IFOPT G+} Test286, {$ENDIF}
  33.      Fix, Crt, Dos, Memory, Drivers,
  34.      StringDateHandling, DisplayKeyboardAndCursor, HandleINIFile,
  35.      DescriptionHandling, dmouse;
  36.  
  37. CONST DelimiterTable : STRING = ',.();:-!?/[]{}+*=''`"@%&$_£';
  38.  
  39. VAR  EdStart     : BYTE;      (* column where the description starts     *)
  40.  
  41.      ActDir      : DirStr;    (* current directory                       *)
  42.      StartDir    : DirStr;    (* directory where we started from         *)
  43.      ResetDir    : BOOLEAN;   (* TRUE = return to StartDir on exit       *)
  44.  
  45.      StartIndex  : INTEGER;   (* index of entry at the top of the screen *)
  46.      Index       : INTEGER;   (* index of entry we are editing           *)
  47.  
  48.      CutPasteDesc: DescStr;   (* cut, resp. pasted description           *)
  49.      Changed     : BOOLEAN;   (* TRUE=the descriptions have been edited  *)
  50.      IORes       : INTEGER;
  51.  
  52.      NewDir      : DirStr;    (* temporary storage for a directory path, *)
  53.      NewName     : NameExtStr;(* used by view and others                 *)
  54.  
  55.      FirstParam  : STRING[8];
  56.      i           : BYTE;      (* variable for counting (index etc)       *)
  57.      ShowHelp    : BOOLEAN;   (* TRUE = start in help mode [/h]          *)
  58.      Querier     : BOOLEAN;   (* TRUE = ask user if he wants to save
  59.                                         the descriptions   [/dontask]    *)
  60.      PasteMovesToNextIndex: BOOLEAN; (* TRUE = Paste advances to next index *)
  61.      Overwrite   : BOOLEAN;   (* overwrite / Insert mode                 *)
  62.  
  63.      s           : STRING;    (* temporary string variable               *)
  64.  
  65. (*-------------------------------------------------------- Display-Routines *)
  66. PROCEDURE DisplayFileEntry(Index: INTEGER; ox, x: BYTE;
  67.                            Selection, Hilighted: BOOLEAN);
  68. (* Displays the Index'th file entry. If the description is longer than
  69.    DispLen characters, DispLen characters - starting at character x of the
  70.    description - will be shown. (this feature is needed for scrolling).
  71.    Hilighted = TRUE will hilight the description.
  72.  
  73.    When Selection is TRUE, we want to display the text we just put into
  74.    the buffer, ox (old x) gives us the start of the selection.
  75.  
  76.    P.S. Scrolling implies hilighting, but this fact has not been exploited. *)
  77.  
  78.  VAR FileEntry : PFileData;
  79.      xs,oxs,t  : BYTE;
  80.      y,l       : BYTE;
  81.      s         : STRING;
  82.  
  83.  BEGIN
  84.   y := 3+Index-StartIndex;
  85.   IF (Index >= 0) AND (Index < FileList^.Count) THEN
  86.    BEGIN
  87.     FileEntry := NILCheck(FileList^.At(Index));
  88.  
  89.     IF x <=   DispLen THEN xs := 1
  90.     ELSE
  91.     IF x <= 2*DispLen THEN xs := DispLen+1
  92.     ELSE
  93.     IF x <= 3*DispLen THEN xs := 2*DispLen+1
  94.     ELSE
  95.     IF x <= 4*DispLen THEN xs := 3*DispLen+1
  96.                       ELSE xs := 4*DispLen+1;
  97.     (* I haven't found a simple formula yet, so I'm doing the
  98.        job with a table. That's the lazy's man solution .... *)
  99.  
  100.     IF ox <=   DispLen THEN oxs := 1
  101.     ELSE
  102.     IF ox <= 2*DispLen THEN oxs := DispLen+1
  103.     ELSE
  104.     IF ox <= 3*DispLen THEN oxs := 2*DispLen+1
  105.     ELSE
  106.     IF ox <= 4*DispLen THEN oxs := 3*DispLen+1
  107.                        ELSE oxs := 4*DispLen+1;
  108.  
  109.     IF Hilighted THEN
  110.      BEGIN TextColor(SelectFg); TextBackGround(SelectBg); END
  111.     ELSE
  112.      BEGIN
  113.       TextBackGround(NormBg);
  114.  
  115.       IF FileEntry^.IsADir THEN TextColor(DirFg)
  116.                            ELSE TextColor(NormFg)
  117.      END;
  118.  
  119.     GotoXY(1,y);
  120.  
  121.     s := FileEntry^.FormatScrollableDescription(xs,DispLen);
  122.     IF Selection THEN
  123.      BEGIN
  124.        IF ox > x  THEN BEGIN t := x; x := ox; ox := t; END
  125.                   ELSE t := x;
  126.        IF ox < xs THEN ox := xs;
  127.  
  128.        Write(Copy(s,1,EdStart+ox-xs-1));
  129.        TextBackGround(NormFg);  TextColor(NormBg);   Write(Copy(s,EdStart+ox-xs,x-ox));
  130.        TextBackGround(SelectBg);TextColor(SelectFg); Write(Copy(s,EdStart+x-xs,255));
  131.  
  132.        x := t;
  133.      END
  134.     ELSE Write(s);
  135.  
  136.     l := Length(FileEntry^.GetDesc);
  137.     IF l-xs < DispLen THEN
  138.      ClrEol
  139.     ELSE
  140.      BEGIN
  141.       TextColor(WarnFg); Write(Chr(16)); TextColor(NormFg);
  142.      END;
  143.  
  144. (*    IF x <= DispLen THEN GotoXY(EdStart+x-1,y)
  145.                     ELSE GotoXY(EdStart+DispLen-1,y) *)
  146.     GotoXY(EdStart+x-xs,y);
  147.    END
  148.   ELSE BEGIN GotoXY(1,y); ClrEol; END;
  149.  END;  (* DisplayFileEntry *)
  150.  
  151. PROCEDURE DrawDirLine(UpdateDir: BOOLEAN);
  152. (* Draw the line, which tells us where in the directory tree we are. *)
  153.  
  154. BEGIN
  155.  IF UpdateDir THEN
  156.   BEGIN
  157.    GetDir(0,ActDir);
  158.    IF ActDir[Length(ActDir)] <> '\' THEN ActDir := ActDir + '\';
  159.    UpString(ActDir);
  160.   END;
  161.  TextColor(DirFg); TextBackGround(NormBg);
  162.  GotoXY(1,2); Write(ActDir); ClrEol;
  163. END; (* DrawDirLine *)
  164.  
  165. PROCEDURE ReDrawScreen;
  166. (* Redraws the full screen, needed after shelling out or after printing
  167.    the help screen.                                                     *)
  168.  
  169. VAR Index: INTEGER;
  170.  
  171. BEGIN
  172. (* GetDir(0,ActDir); *)
  173.  FOR Index := StartIndex TO StartIndex+MaxLines-4 DO
  174.   DisplayFileEntry(Index,0,1,FALSE,FALSE);
  175. END; (* ReDrawScreen *)
  176.  
  177.  
  178. (*-------------------------------------------------------- Read-Directory *)
  179. PROCEDURE ReadFiles;
  180. (* Scan the current directory and read in the DESCRIPT.ION file. Build a
  181.    file list database and associate the right description.
  182.  
  183.    Warn the user if there are too long descriptions or if there are too
  184.    much descriptions.                                                     *)
  185.  
  186. VAR i   : BYTE;
  187.     ch  : WORD;
  188.     Dir : PathStr;
  189.  
  190. BEGIN
  191.  Changed    := FALSE;
  192.  DescLong   := FALSE;
  193.  Index      := 0;
  194.  StartIndex := 0;
  195.  Dir := FExpand('.');
  196.  
  197.  IF FileList <> NIL THEN
  198.   BEGIN
  199.    Dispose(FileList,Done); FileList := NIL;
  200.   END;
  201.  
  202.  TextColor(StatusFg); TextBackGround(StatusBg);
  203.  GotoXY(1,MaxLines);
  204.  IF (ScreenWidth-39-Length(Dir)) > 0 THEN
  205.    Write(Chars(' ',(ScreenWidth-39-Length(Dir)) DIV 2));
  206.  Write('Scanning directory ',Dir,' ..... please wait.');
  207.  ClrEol;
  208.  
  209.  FileList := NIL; FileList := New(PFileList,Init(Dir,'*.*',0));
  210.  IF FileList = NIL THEN Abort('Unable to allocate FileList');
  211.  
  212.  IF (FileList^.Status = ListTooManyFiles) OR
  213.     (FileList^.Status = ListOutofMem) THEN
  214.   BEGIN
  215.    TextColor(NormFg); TextBackGround(NormBg);
  216.    FOR i := 3 TO MaxLines-1 DO
  217.     BEGIN
  218.      GotoXY(1,i); ClrEol;
  219.     END;
  220.    IF FileList^.Status = ListTooManyFiles THEN
  221.     ReportError('Warning! Too many files in directory, description file will be truncated! (Key)',(CutPasteDesc <> ''),Changed)
  222.    ELSE
  223.     ReportError('Warning! Out of memory, description file will be truncated! (Key)',(CutPasteDesc <> ''),Changed);
  224.   END;
  225.  
  226.  IF FileList^.Count > 0 THEN
  227.   BEGIN
  228.    DrawMainScreen(Index,FileList^.Count,1,0); DrawDirLine(TRUE);
  229.   END;
  230.  
  231.  IF DescLong THEN
  232.   BEGIN
  233.    TextColor(NormFg); TextBackGround(NormBg);
  234.    FOR i := 3 TO MaxLines-1 DO
  235.     BEGIN
  236.      GotoXY(1,i); ClrEol;
  237.     END;
  238.    ReportError('Warning! Some descriptions are too long; they will be truncated. Press any key.',(CutPasteDesc <> ''),Changed);
  239.   END;
  240. END;  (* ReadFiles *)
  241.  
  242. (*-------------------------------------------------------- Save Descriptions *)
  243. PROCEDURE SaveDescriptions;
  244. (* Save the modified descriptions currently held in memory onto disk.
  245.    Rename the old description file into DESCRIPT.OLD and write the
  246.    new one out. Any problems occuring at this point (disk full etc),
  247.    raise a warning message and cause a deletion of the (half-written)
  248.    description file DESCRIPT.ION. In this case the user "only" looses his
  249.    new, edited descriptions, but the old ones are stored in the DESCRIPT.OLD
  250.    file and can be restored by typing
  251.  
  252.    REN DESCRIPT.OLD DESCRIPT.ION
  253.    ATTRIB +H DESCRIPT.ION
  254.  
  255.    If all went fine, the old description file gets deleted. This procedure
  256.    minimizes data loss.                                                    *)
  257.  
  258. VAR DescFile  : TEXT;
  259.     DescSaved : BOOLEAN;
  260.     Time      : DateTime;
  261.     ch        : WORD;
  262.     FileEntry : PFileData;
  263.  
  264.  
  265.  PROCEDURE SaveEntry(FileEntry: PFileData); FAR;
  266.  (* Save a single description, writes a single line of the description
  267.     file. This procedures is called for each entry in the FileEntry list *)
  268.  
  269.  VAR Desc     : DescStr;
  270.      ProgInfo : STRING;
  271.      Dir      : DirStr;
  272.      BaseName : NameStr;
  273.      Ext      : ExtStr;
  274.  
  275.  BEGIN
  276.   Desc := FileEntry^.GetDesc;
  277.   StripLeadingSpaces(Desc); StripTrailingSpaces(Desc);
  278.   IF Desc <> '' THEN
  279.    BEGIN
  280.     StripTrailingSpaces(FileEntry^.Name);
  281.     Write(DescFile,FileEntry^.Name);
  282.  
  283.     StripLeadingSpaces(FileEntry^.Ext);
  284.     StripTrailingSpaces(FileEntry^.Ext);
  285.     IF FileEntry^.Ext <> '' THEN Write(DescFile,FileEntry^.Ext);
  286.  
  287.     Write(DescFile,' ',Desc);
  288.     IF DescSaved = FALSE THEN DescSaved := TRUE;
  289.  
  290.     ProgInfo :=  FileEntry^.GetProgInfo;
  291.     IF ProgInfo <> '' THEN Write(DescFile,ProgInfo);
  292.     WriteLn(DescFile);
  293.    END;
  294.  END; (* SaveEntry *)
  295.  
  296. BEGIN
  297.  DescSaved := FALSE;
  298.  IF DiskFree(0) < FileList^.Count*SizeOf(TFileData) THEN
  299.    ReportError('Probably out of disk space. Nevertheless trying to save DESCRIPT.ION...',(CutPasteDesc <> ''),Changed);
  300.  TextColor(StatusFg); TextBackGround(StatusBg);
  301.  GotoXY(1,MaxLines);
  302.  Write(Chars(' ',((ScreenWidth-41) div 2)),
  303.        'Saving descriptions........  please wait.');
  304.  ClrEol;
  305.  
  306.  {$I-}
  307.  Assign(DescFile,'DESCRIPT.ION'); Rename(DescFile,'DESCRIPT.OLD'); IORes := IOResult;
  308.  Assign(DescFile,'DESCRIPT.ION'); SetFAttr(DescFile,Archive); IORes := IOResult;
  309.  Rewrite(DescFile);
  310.  {$I+}
  311.  IF IOResult > 0 THEN
  312.   BEGIN
  313.    ReportError('Unable to write DESCRIPT.ION !',(CutPasteDesc <> ''),Changed);
  314.    {$I-}
  315.    Assign(DescFile,'DESCRIPT.OLD'); Rename(DescFile,'DESCRIPT.ION'); IORes := IOResult;
  316.    {$I+}
  317.   END
  318.  ELSE
  319.   BEGIN
  320.    FileList^.ForEach(@SaveEntry);
  321.    {$I-}
  322.    Close(DescFile);
  323.    {$I+}
  324.  
  325.    IF IOResult > 0 THEN
  326.     BEGIN
  327.      ReportError('Unable to write DESCRIPT.ION !',(CutPasteDesc <> ''),Changed);
  328.      {$I-}
  329.      Assign(DescFile,'DESCRIPT.OLD'); Rename(DescFile,'DESCRIPT.ION'); IORes := IOResult;
  330.      {$I+}
  331.     END
  332.    ELSE
  333.     BEGIN
  334.      IF DescSaved THEN SetFAttr(DescFile, Archive + Hidden)
  335.                   ELSE Erase(DescFile);  (* Don't keep zero-byte file. *)
  336.      Changed := FALSE; DrawStatusLine(TRUE,(CutPasteDesc <> ''),Changed,FALSE);
  337.      {$I-}
  338.      Assign(DescFile,'DESCRIPT.OLD'); Erase(DescFile); IORes := IOResult;
  339.      {$I+}
  340.     END;
  341.   END;
  342. END;  (* SaveDescriptions *)
  343.  
  344. (*-------------------------------------------------------- Edit Descriptions *)
  345. PROCEDURE EditDescriptions;
  346. (* This is the heart of 4DESC: the editing of the descriptions. *)
  347.  
  348. VAR Key          : WORD;
  349.     Drv          : STRING[3];
  350.     LastDrv      : CHAR;
  351.     x,y,l        : BYTE;        (* current cursor position *)
  352.     ox           : BYTE;        (* old cursor position *)
  353.     EditStr      : DescStr;
  354.     InShiftState : BOOLEAN;
  355.     Cmd          : BYTE;
  356.  
  357.     Cursor       : WORD;
  358.     OldDir       : DirStr;
  359.     ActFileData  : PFileData;
  360.     n            : NameExtStr;
  361.  
  362.     ReverseFlag  : BOOLEAN;   (* for sorting *)
  363.  
  364.     f            : FILE;      (* used for delete *)
  365.  
  366.  PROCEDURE UpdateLineNum(Index: INTEGER);
  367.  (* Update the line number indicator in the right corner and redraw
  368.     the associated description line                                 *)
  369.  
  370.  BEGIN
  371.   TextColor(StatusFg); TextBackGround(StatusBg);
  372.   GotoXY(46,1); Write(Index+1:5);
  373.  
  374.   IF Changed THEN DrawStatusLine(FALSE,(CutPasteDesc <> ''),Changed,ReverseFlag);
  375.  
  376.   IF Index < FileList^.Count THEN
  377.    BEGIN
  378.     EditStr := PFileData(FileList^.At(Index))^.GetDesc;
  379.     DisplayFileEntry(Index,0,1,FALSE,TRUE);
  380.    END;
  381.  
  382.   ActFileData := NILCheck(FileList^.At(Index));
  383.  END;
  384.  
  385.  PROCEDURE UpdateColNum(Col, CurDescLen: BYTE);
  386.  (* Update the column number indicator in the right corner *)
  387.  
  388.  VAR x,y: BYTE;
  389.  
  390.  BEGIN
  391.   x := WhereX; y := WhereY;
  392.   TextColor(StatusFg); TextBackGround(StatusBg);
  393.   GotoXY(66,1); Write(Col:3); GotoXY(77,1); Write(CurDescLen:3);
  394.  
  395. (*  TextBackGround(NormBg);
  396.     GotoXY(EdStart+40-xs,MaxLines); Write('^'); *)
  397.  
  398.   GotoXY(x,y);
  399.  END;
  400.  
  401.  PROCEDURE PrevIndex(VAR Index: INTEGER);
  402.  (* Go up one description line (if possible) *)
  403.  
  404.  BEGIN
  405.   Index := Max(Index-1,0);
  406.   IF Index <= StartIndex THEN
  407.    BEGIN
  408.     StartIndex := Max(Index-ScreenSize,Index);
  409.     RedrawScreen;
  410.    END;
  411.   UpdateLineNum(Index);
  412.  END; (* PrevIndex *)
  413.  
  414.  PROCEDURE NextIndex(VAR Index: INTEGER);
  415.  (* Go down one description line (if possible) *)
  416.  
  417.  BEGIN
  418.   Index := Min(Index+1,FileList^.Count-1);
  419.   IF Index > StartIndex+ScreenSize THEN
  420.    BEGIN
  421.     StartIndex := Index-ScreenSize;
  422.     RedrawScreen;
  423.    END;
  424.   UpdateLineNum(Index);
  425.  END; (* NextIndex *)
  426.  
  427.  PROCEDURE QuerySaveDescriptions;
  428.  (* Ask the user if he really wants to save the descriptions. *)
  429.  
  430.  VAR ch: CHAR;
  431.  
  432.  BEGIN
  433.   IF Querier THEN
  434.    BEGIN
  435.     TextColor(StatusFg); TextBackGround(StatusBg);
  436.     IF Changed THEN
  437.      BEGIN
  438.       GotoXY(1,MaxLines);
  439.       Write(Chars(' ',(ScreenWidth-58) div 2),
  440.            'Descriptions have been edited. Shall they be saved (Y/N) ?');
  441.       ClrEol;
  442.       ch := ' ';
  443.       REPEAT
  444.         If KeyPressed Then ch := UpCase(ReadKey)
  445.         Else
  446.           If MouseLoaded Then
  447.             Begin
  448.               ButtonReleased(Left);
  449.               If ReleaseCount > 0 Then ch := 'Y';
  450.               ButtonReleased(Right);
  451.               If ReleaseCount > 0 Then ch := 'N';
  452.             End;
  453.       UNTIL (ch = 'Y') OR (ch = 'N');
  454.       Write(' ',ch);
  455.       IF ch = 'Y' THEN SaveDescriptions;
  456.      END;
  457.    END
  458.   ELSE SaveDescriptions; (* always save, when not in query mode *)
  459.  END; (* QuerySaveDescriptions *)
  460.  
  461.  PROCEDURE DirUp;
  462.  (* Go up one directory in the directory tree (if possible) *)
  463.  
  464.  BEGIN
  465.   IF Changed THEN QuerySaveDescriptions;
  466.   {$I-}
  467.   ChDir('..');
  468.   {$I+}
  469.   IF IOResult = 0 THEN
  470.    BEGIN
  471.     ReadFiles;
  472.     RedrawScreen;
  473.     DrawStatusLine(TRUE,(CutPasteDesc <> ''),Changed,ReverseFlag);
  474.     Index := 0; UpdateLineNum(Index);
  475.    END;
  476.  END;  (* DirUp *)
  477.  
  478.  PROCEDURE DirDown;
  479.  (* Go down one directory in the directory tree (if possible) *)
  480.  
  481.  BEGIN
  482.   IF (Index < FileList^.Count) THEN
  483.    BEGIN
  484.     n  := ActFileData^.Name+ActFileData^.Ext;
  485.     IF (ActFileData^.IsADir) AND (n[1] <> '.') THEN
  486.      BEGIN
  487.       IF Changed THEN QuerySaveDescriptions;
  488.       {$I-}
  489.       ChDir(n);
  490.       {$I+}
  491.       IF IOResult = 0 THEN
  492.        BEGIN
  493.         ReadFiles;
  494.         RedrawScreen;
  495.        END;
  496.       DrawStatusLine(TRUE,(CutPasteDesc <> ''),Changed, ReverseFlag);
  497.       Index := 0; UpdateLineNum(Index);
  498.     END;  (* IF Description[Index].Size = DirSize *)
  499.    END;
  500.  END;  (* DirDown *)
  501.  
  502.  PROCEDURE ReSortDirectory;
  503.  
  504.  BEGIN
  505.   ReSortFileList; ReverseFlag := FALSE;
  506.   DrawStatusLine(FALSE,(CutPasteDesc <> ''),Changed, ReverseFlag);
  507.  
  508.   StartIndex := 0; Index := 0;
  509.   RedrawScreen; UpdateLineNum(Index);
  510.  END; (* ReSortDirectory *)
  511.  
  512.  FUNCTION IsADelimiter(c: CHAR): BOOLEAN;
  513.  (* used by Ctrl-Left resp Ctrl-Right to recognize the end of a word *)
  514.  
  515.  BEGIN
  516.   IsADelimiter := (Pos(c,DelimiterTable) > 0);
  517.  END;
  518.  
  519. BEGIN  (* EditDescriptions *)
  520.  Index := 0; UpdateLineNum(Index);
  521.  
  522.  ResetCursor(Overwrite);
  523.  EditStr := ActFileData^.GetDesc;
  524.  ReverseFlag := FALSE; InShiftState := FALSE; x := 1;
  525.  REPEAT
  526.   REPEAT
  527.     Key := $0000;
  528.     IF KeyPressed THEN Key := GetKey
  529.     ELSE
  530.       BEGIN
  531.         IF MouseLoaded THEN
  532.           BEGIN
  533.             MouseMotion;
  534.             IF VMickey > VMickeysPerKeyPress THEN Key := kbDown
  535.             ELSE
  536.               IF VMickey < -VMickeysPerKeyPress THEN Key := kbUp
  537.               ELSE
  538.               IF HMickey >  HMickeysPerKeyPress THEN Key := kbRight
  539.               ELSE
  540.                 IF HMickey < -HMickeysPerKeyPress THEN Key := kbLeft
  541.                 ELSE
  542.                   BEGIN
  543.                     ButtonReleased(Left);
  544.                     IF ReleaseCount > 0 THEN Key := kbEnter;
  545.                     ButtonReleased(Right);
  546.                     IF ReleaseCount > 0 THEN Key := kbEsc;
  547.                   END;
  548.  
  549.           END;  (* if mouseloaded *)
  550.       END;
  551.   UNTIL Key <> $0000;
  552.  
  553.   IF NOT InShiftState THEN ox := x;
  554.   (* save the old cursor position for cutting *)
  555.   CASE Key OF
  556.    kbUp       : BEGIN
  557.                  ActFileData^.AssignDesc(EditStr);
  558.                  DisplayFileEntry(Index,0,x,FALSE,FALSE);
  559.                  PrevIndex(Index);
  560.                  IF x > Length(EditStr) THEN x := Max(Length(EditStr),1);
  561.                  DisplayFileEntry(Index,0,x,FALSE,FALSE);
  562.                  InShiftState := FALSE;
  563.                 END; (* Up *)
  564.  
  565.    kbDown     : BEGIN
  566.                  ActFileData^.AssignDesc(EditStr);
  567.                  DisplayFileEntry(Index,0,x,FALSE,FALSE);
  568.                  NextIndex(Index);
  569.                  IF x > Length(EditStr) THEN x := Max(Length(EditStr),1);
  570.                  DisplayFileEntry(Index,0,x,FALSE,FALSE);
  571.                  InShiftState := FALSE;
  572.                 END; (* Down *)
  573.  
  574.    kbLeft     : BEGIN
  575.                  x := Max(1,x-1);
  576.                  InShiftState := (GetShiftState AND (kbRightShift+kbLeftShift) <> 0);
  577.                 END; (* Left *)
  578.  
  579.    kbRight    : BEGIN
  580.                  x := Max(1,Min(1+x,Length(EditStr)+1));
  581.                  InShiftState := (GetShiftState AND (kbRightShift+kbLeftShift) <> 0);
  582.                 END; (* Right *)
  583.  
  584.    kbCtrlLeft : BEGIN
  585.                  DEC(x);
  586.                  WHILE (x > 0) AND IsADelimiter(EditStr[x]) DO DEC(x);
  587.                  WHILE (x > 0) AND NOT IsADelimiter(EditStr[x]) DO DEC(x);
  588.                  INC(x);
  589.                  InShiftState := (GetShiftState AND (kbRightShift+kbLeftShift) <> 0);
  590.                 END; (* ^Left *)
  591.  
  592.    kbCtrlRight: BEGIN
  593.                  l := Length(EditStr);
  594.                  WHILE (x < l) AND NOT IsADelimiter(EditStr[x]) DO INC(x);
  595.                  WHILE (x < l) AND IsADelimiter(EditStr[x]) DO INC(x);
  596.                  IF x = l THEN INC(x);
  597.                  InShiftState := (GetShiftState AND (kbRightShift+kbLeftShift) <> 0);
  598.                 END; (*  ^Right *)
  599.  
  600.    kbHome     : BEGIN
  601.                  x := 1;
  602.                  InShiftState := (GetShiftState AND (kbRightShift+kbLeftShift) <> 0);
  603.                 END; (* Home *)
  604.  
  605.    kbEnd      : BEGIN
  606.                   x := Min(Length(EditStr)+1,MaxDescLen);
  607.                   InShiftState := (GetShiftState AND (kbRightShift+kbLeftShift) <> 0);
  608.                 END; (* End *)
  609.  
  610.    kbCtrlHome : BEGIN
  611.                  Delete(EditStr,1,x);
  612.                  ActFileData^.AssignDesc(EditStr);
  613.                  x := 1;
  614.                  Changed := TRUE; InShiftState := FALSE;
  615.                 END;  (* ^Home *)
  616.  
  617.    kbCtrlEnd  : BEGIN
  618.                  Delete(EditStr,x,MaxDescLen);
  619.                  ActFileData^.AssignDesc(EditStr);
  620.                  Changed := TRUE; InShiftState := FALSE;
  621.                 END;  (* ^End *)
  622.  
  623.    kbIns      : BEGIN
  624.                  IF GetShiftState AND kbCtrlShift = kbCtrlShift THEN (* ^Ins: Copy *)
  625.                   BEGIN
  626.                    CutPasteDesc := Copy(EditStr,ox,x-ox);
  627.                    Changed := TRUE;
  628.                   END
  629.                  ELSE IF GetShiftState AND (kbRightShift+kbLeftShift) <> 0 THEN (* Shift-Ins: Paste *)
  630.                   BEGIN
  631.                    IF CutPasteDesc > '' THEN
  632.                     BEGIN
  633.                      EditStr := Copy(EditStr,1,x-1)+CutPasteDesc+Copy(EditStr,x,255);
  634.                      ActFileData^.AssignDesc(EditStr);
  635.                      Changed := TRUE;
  636.                      IF PasteMovesToNextIndex THEN
  637.                       BEGIN
  638.                        DisplayFileEntry(Index,0,x,FALSE,FALSE);
  639.                        NextIndex(Index);
  640.                       END;
  641.                     END
  642.                   END
  643.                  ELSE
  644.                   BEGIN
  645.                    Overwrite := NOT Overwrite; ResetCursor(Overwrite);
  646.                   END;
  647.                 END; (* Ins *)
  648.  
  649.    kbDel      : BEGIN
  650.                  IF GetShiftState AND kbCtrlShift = kbCtrlShift THEN (* ^Del: Clear *)
  651.                   BEGIN
  652.                    System.Delete(EditStr,ox,x-ox); x := ox;
  653.                    ActFileData^.AssignDesc(EditStr);
  654.                    Changed := TRUE; InShiftState := FALSE;
  655.                    DisplayFileEntry(Index,0,x,FALSE,FALSE);
  656.                   END
  657.                  ELSE IF GetShiftState AND (kbRightShift+kbLeftShift) <> 0 THEN (* Shift-Del: Cut *)
  658.                   BEGIN
  659.                    CutPasteDesc := Copy(EditStr,ox,x-ox);
  660.                    Delete(EditStr,ox,x-ox); x := ox;
  661.                    ActFileData^.AssignDesc(EditStr);
  662.                    Changed := TRUE; InShiftState := FALSE;
  663.                    DisplayFileEntry(Index,0,x,FALSE,FALSE);
  664.                   END
  665.                  ELSE
  666.                   BEGIN
  667.                    IF x <= Length(EditStr) THEN Delete(EditStr,x,1);
  668.                    ActFileData^.AssignDesc(EditStr);
  669.                    Changed := TRUE;
  670.                   END;
  671.                 END; (* Del *)
  672.  
  673.    kbBack     : BEGIN
  674.                  Delete(EditStr,x-1,1);
  675.                  ActFileData^.AssignDesc(EditStr);
  676.                  IF x > 1 THEN
  677.                   BEGIN
  678.                    DEC(x);
  679.                    IF x > Length(EditStr) THEN x := Length(EditStr)+1;
  680.                   END;
  681.                  Changed := TRUE; InShiftState := FALSE;
  682.                 END; (* Backspace *)
  683.  
  684.    kbPgUp     : BEGIN
  685.                  ActFileData^.AssignDesc(EditStr);
  686.                  x := 1;
  687.                  DisplayFileEntry(Index,0,x,FALSE,FALSE);
  688.                  Index := Max(Index-ScreenSize,0);
  689.                  StartIndex := Index;
  690.                  RedrawScreen;
  691.                  UpdateLineNum(Index);
  692.                  InShiftState := FALSE;
  693.                 END; (* PgUp *)
  694.  
  695.    kbPgDn     : BEGIN
  696.                  ActFileData^.AssignDesc(EditStr);
  697.                  Index := Min(Index+ScreenSize,FileList^.Count-1);
  698.                  StartIndex := Max(Index-ScreenSize,0);
  699.                  x := 1;
  700.                  DisplayFileEntry(Index,0,x,FALSE,FALSE);
  701.                  RedrawScreen;
  702.                  UpdateLineNum(Index);
  703.                  InShiftState := FALSE;
  704.                 END; (* PgDn *)
  705.  
  706.    kbCtrlPgUp : BEGIN
  707.                  ActFileData^.AssignDesc(EditStr);
  708.                  x := 1;
  709.                  DisplayFileEntry(Index,0,x,FALSE,FALSE);
  710.                  StartIndex := 0; Index := 0;
  711.                  RedrawScreen;
  712.                  UpdateLineNum(Index);
  713.  
  714.                  ActFileData^.AssignDesc(EditStr);
  715.                  DisplayFileEntry(Index,0,x,FALSE,FALSE);
  716.                  IF Length(ActDir) > 3 THEN NextIndex(Index);
  717.                  InShiftState := FALSE;
  718.                 END; (* ^PgUp *)
  719.  
  720.    kbCtrlPgDn : BEGIN
  721.                  ActFileData^.AssignDesc(EditStr);
  722.                  x := 1;
  723.                  DisplayFileEntry(Index,0,x,FALSE,FALSE);
  724.                  StartIndex := Max(FileList^.Count-ScreenSize-1,0);
  725.                  Index := FileList^.Count-1;
  726.                  RedrawScreen;
  727.                  UpdateLineNum(Index);
  728.                  InShiftState := FALSE;
  729.                 END; (* ^PgDn *)
  730.  
  731.    kbAltD     : BEGIN
  732.                  EditStr := ''; ActFileData^.AssignDesc('');
  733.                  Changed := TRUE; InShiftState := FALSE;
  734.                  x := 1;
  735.                  IF PasteMovesToNextIndex THEN
  736.                   BEGIN
  737.                    DisplayFileEntry(Index,0,x,FALSE,FALSE);
  738.                    NextIndex(Index);
  739.                   END;
  740.                 END; (* Alt-D *)
  741.  
  742.    kbAltM,
  743.    kbAltT     : BEGIN
  744.                  CutPasteDesc := ActFileData^.GetDesc;
  745.                  ActFileData^.AssignDesc(''); EditStr := '';
  746.                  Changed := TRUE; InShiftState := FALSE;
  747.                  x := 1;
  748.                  IF PasteMovesToNextIndex THEN
  749.                   BEGIN
  750.                    DisplayFileEntry(Index,0,x,FALSE,FALSE);
  751.                    NextIndex(Index);
  752.                   END;
  753.                 END; (* Alt-M / Alt-T *)
  754.  
  755.    kbAltC     : BEGIN
  756.                  CutPasteDesc := ActFileData^.GetDesc;
  757.                  x := 1;
  758.                  InShiftState := FALSE;
  759.                  DrawStatusLine(TRUE,(CutPasteDesc <> ''),Changed, ReverseFlag);
  760.                 END; (* Alt-C *)
  761.  
  762.    kbAltP     : BEGIN
  763.                  IF CutPasteDesc > '' THEN
  764.                   BEGIN
  765.                    EditStr := CutPasteDesc; ActFileData^.AssignDesc(EditStr);
  766.                    Changed := TRUE; InShiftState := FALSE;
  767.                    IF PasteMovesToNextIndex THEN
  768.                     BEGIN
  769.                      DisplayFileEntry(Index,0,x,FALSE,FALSE);
  770.                      NextIndex(Index);
  771.                     END;
  772.                   END
  773.                 END;
  774.  
  775.    kbEnter    : BEGIN
  776.                   ActFileData^.AssignDesc(EditStr);
  777.                   x := 1;
  778.                   IF (Index < FileList^.Count) THEN
  779.                     BEGIN
  780.                       n  := ActFileData^.Name+ActFileData^.Ext;
  781.                       IF ActFileData^.IsADir THEN
  782.                         IF (n[1] = '.') AND (n[2] = '.') THEN DirUp
  783.                           ELSE
  784.                         IF n[1] <> '.' THEN DirDown;
  785.                     END;
  786.                 END; (* Enter = go into directory where the cursor is at *)
  787.  
  788.    kbF1       : BEGIN                                   (* F1: Help *)
  789.                  ShowHelpPage;
  790.                  ResetCursor(Overwrite);
  791.                  DrawMainScreen(Index,FileList^.Count,x,Length(EditStr));
  792.                  DrawDirLine(FALSE);
  793.                  RedrawScreen;
  794.                  UpdateLineNum(Index);
  795.                 END;  (* F1 *)
  796.  
  797.    kbF4       : DirDown; (* F4 *)
  798.    kbF5       : DirUp;   (* F5 *)
  799.  
  800.    kbAltL,
  801.    kbF6       : BEGIN                                   (* F6: Change Drive *)
  802.                  IF Changed THEN QuerySaveDescriptions;
  803.  
  804.                  ASM
  805.                   mov ah,0eh       (* Select Disk *)
  806.                   mov dl,3
  807.                   int 21h
  808.                   add al,'@'
  809.                   mov LastDrv,al
  810.                  END;
  811.  
  812.                  IF LastDrv > 'Z' THEN LastDrv := 'Z';
  813.  
  814.                  TextColor(StatusFg); TextBackGround(StatusBg); Drv := ' :';
  815.                  GotoXY(1,MaxLines);
  816.                  Write(Chars(' ',((ScreenWidth-24) div 2)),
  817.                       'New drive letter (A..',LastDrv,'): ');
  818.                  ClrEol;
  819.                  REPEAT
  820.                   Drv[1] := UpCase(ReadKey);
  821.                  UNTIL (Drv[1] >= 'A') AND (Drv[1] <= LastDrv);
  822.                  IF Drv[1] <= 'B' THEN Drv := Drv + '\';
  823.                  OldDir := ActDir;
  824.                  {$I-}
  825.                  ChDir(Drv);
  826.                  {$I+}
  827.                  IF IOResult = 0 THEN
  828.                   BEGIN
  829.                    GetDir(0,ActDir); IORes := IOResult;
  830.                    ReadFiles;
  831.                    IF FileList^.Count = 0 THEN
  832.                     BEGIN
  833.                      IF (Length(OldDir) > 3) AND (OldDir[Length(OldDir)] = '\') THEN
  834.                         Delete(OldDir,Length(OldDir),1);
  835.                      {$I-}
  836.                      ChDir(OldDir); IORes := IOResult;
  837.                      {$I+}
  838.                      ReportError('There are no files on drive '+Drv+'. Press any key.',(CutPasteDesc <> ''),Changed);
  839.                      ReadFiles;
  840.                     END;
  841.                    RedrawScreen;
  842.                    Index := 0;
  843.                    UpdateLineNum(Index);
  844.                   END
  845.                  ELSE
  846.                   ReportError('Drive '+Drv+' not ready! Drive remains unchanged, press a key.',(CutPasteDesc <> ''),Changed);
  847.                 END;  (* Alt-L or F6 *)
  848.  
  849.    kbF2      : BEGIN                                    (* F2: Save *)
  850.                 SaveDescriptions;
  851.                 UpdateLineNum(Index);
  852.                END; (* F10 or F2 *)
  853.    kbAltS,
  854.    kbShiftF10: BEGIN                                    (* Shell to [4]DOS *)
  855.                 IF Changed THEN QuerySaveDescriptions;
  856.  
  857.                 DoneMemory;
  858.                 SetMemTop(HeapPtr);
  859.  
  860.                 NormVideo; ClrScr;
  861.                 WriteLn('Type `Exit'' to return to 4DESC.');
  862.                 SwapVectors;
  863.                 Exec(GetEnv('COMSPEC'),'');
  864.                 SwapVectors;
  865.  
  866.                 SetMemTop(HeapEnd);
  867.                 InitMemory;
  868.  
  869.                 IF MouseLoaded THEN MouseReset;
  870.  
  871.                 ClrScr;
  872.                 DrawMainScreen(Index,FileList^.Count,x,Length(EditStr));
  873.                 DrawStatusLine(TRUE,(CutPasteDesc <> ''),Changed, ReverseFlag);
  874.                 DrawDirLine(TRUE);
  875.                 IF DosError > 0 THEN
  876.                   ReportError('Can''t load command interpreter / program execution failed.',
  877.                              (CutPasteDesc <> ''),Changed);;
  878.                 ReadFiles;
  879.                 RedrawScreen;
  880.                 UpdateLineNum(Index);
  881.                 ResetCursor(Overwrite);
  882.                END; (* Alt-S or F10 *)
  883.  
  884.    kbF3,                                                (* F3, Alt-V: View File *)
  885.    kbAltV,                                              (* Alt-E: Edit File *)
  886.    kbAltE      : IF (Index < FileList^.Count) THEN
  887.                 BEGIN
  888.                  IF NOT ActFileData^.IsADir THEN
  889.                   BEGIN
  890.                    NewName := ActFileData^.Name;
  891.                    StripTrailingSpaces(NewName);
  892.                    NewName := NewName+ActFileData^.Ext;
  893.                    NewDir := ActDir; (* I do not want to loose actdir, newdir
  894.                                         is only a "dummy" variable. *)
  895.                    IF NewDir[Length(NewDir)] = '\' THEN Delete(NewDir,Length(NewDir),1);
  896.  
  897.                    DoneMemory;
  898.                    SetMemTop(HeapPtr);
  899.                    SwapVectors;
  900.  
  901.                    NormVideo; ClrScr;
  902.  
  903.                    IF Key = kbAltE THEN
  904.                     Exec(GetEnv('COMSPEC'),'/c '+EditCmd+' '+NewDir+'\'+NewName)
  905.                    ELSE
  906.                     Exec(GetEnv('COMSPEC'),'/c '+ListCmd+' '+NewDir+'\'+NewName);
  907.  
  908.                    SwapVectors;
  909.                    SetMemTop(HeapEnd);
  910.                    InitMemory;
  911.  
  912.                    IF MouseLoaded THEN MouseReset;
  913.  
  914.                    ClrScr;
  915.                    DrawMainScreen(Index,FileList^.Count,x,Length(EditStr));
  916.                    DrawStatusLine(TRUE,(CutPasteDesc <> ''),Changed, ReverseFlag);
  917.                    DrawDirLine(FALSE);
  918.                    IF DosError > 0 THEN ReportError('Can''t load command interpreter/program execution failed.',
  919.                                                    (CutPasteDesc <> ''),Changed);
  920.                    RedrawScreen;
  921.                    UpdateLineNum(Index);
  922.                    ResetCursor(Overwrite);
  923.                  END;
  924.                 END; (* F3, Alt-V, or Alt-E *)
  925.    (* Sorting Options *)
  926.    Ord('R')-64  : BEGIN
  927.                    ReverseFlag := NOT ReverseFlag;
  928.                    DrawStatusLine(FALSE,(CutPasteDesc <> ''),Changed, ReverseFlag);
  929.                   END;
  930.    Ord('N')-64  : BEGIN
  931.                    IF NOT ReverseFlag THEN SortKey := SortByName
  932.                                       ELSE SortKey := SortByNameRev;
  933.                    ReSortDirectory;
  934.                   END;
  935.    Ord('E')-64  : BEGIN
  936.                    IF NOT ReverseFlag THEN SortKey := SortByExt
  937.                                       ELSE SortKey := SortByExtRev;
  938.                    ReSortDirectory;
  939.                   END;
  940.    Ord('S')-64  : BEGIN
  941.                    IF NOT ReverseFlag THEN SortKey := SortBySize
  942.                                       ELSE SortKey := SortBySizeRev;
  943.                    ReSortDirectory;
  944.                   END;
  945.  
  946.    Ord('D')-64  : BEGIN
  947.                    IF NOT ReverseFlag THEN SortKey := SortByDate
  948.                                       ELSE SortKey := SortByDateRev;
  949.                    ReSortDirectory;
  950.                   END;
  951.  
  952.    kbF8, kbAltK : BEGIN (* delete File *)
  953.                    NewName := ActFileData^.Name;
  954.                    StripTrailingSpaces(NewName);
  955.                    NewName := NewName+ActFileData^.Ext;
  956.  
  957.                    GotoXY(1,MaxLines);
  958.                    Write('Deleting ',NewName,'...'); ClrEol;
  959.  
  960.                    {$I-}
  961.                    Assign(f,NewName);
  962.                    Erase(f);
  963.                    {$I+}
  964.                    IF IOResult > 0 THEN
  965.                      ReportError('Can''t delete'+NewName+'!',
  966.                                  (CutPasteDesc <> ''),Changed);
  967.  
  968.                    ReadFiles;
  969.                    RedrawScreen;
  970.                    UpdateLineNum(Index);
  971.                    ResetCursor(Overwrite);
  972.                   END;
  973.   ELSE
  974.    IF (Ord(Key) > 31) AND (Ord(Key) < 256) THEN
  975.     BEGIN
  976.      IF NOT Changed THEN Changed := TRUE;
  977.      ReverseFlag := FALSE; InShiftState := FALSE;
  978.  
  979.      IF x <= MaxDescLen THEN
  980.       BEGIN
  981.        IF Overwrite AND (x <= Length(EditStr)) THEN
  982.          EditStr[x] := Chr(Key)
  983.        ELSE
  984.          EditStr := Copy(EditStr,1,x-1)+Chr(Key)+Copy(EditStr,x,255);
  985.  
  986.        ActFileData^.AssignDesc(EditStr);
  987.        INC(x); UpdateColNum(x,Length(EditStr));
  988.       END;
  989.     END; (* all others *)
  990.   END;   (* case *)
  991.  
  992.   (* Select with the Shift Keys *)
  993.   IF InShiftState THEN CutPasteDesc := Copy(EditStr,ox,x-ox);
  994.  
  995.   IF Changed THEN
  996.    DrawStatusLine(TRUE,(CutPasteDesc <> ''),Changed, ReverseFlag);
  997.   DisplayFileEntry(Index,ox,x,InShiftState,TRUE);
  998.   UpdateColNum(x,Length(EditStr));
  999.  UNTIL (Key = kbEsc)  OR  (* Esc   = exit to original directory and save *)
  1000.        (Key = kbF10)  OR  (* F10   = exit to current  directory and save *)
  1001.        (Key = kbAltX) OR  (* Alt-X = exit to current  directory and save *)
  1002.        (Key = kbAltQ);    (* Alt-Q = exit to original directory, don't save *)
  1003.  
  1004.  IF (Key = kbEsc) OR (Key = kbAltQ) THEN ResetDir := TRUE
  1005.                                     ELSE ResetDir := FALSE;
  1006.  
  1007.  IF Changed AND (Key <> kbAltQ) THEN QuerySaveDescriptions;
  1008. END; (* EditDescriptions *)
  1009.  
  1010. (*-------------------------------------------------------- Main *)
  1011. BEGIN
  1012.  {$I-}
  1013.  GetDir(0,StartDir); IORes  := IOResult;
  1014.  {$I+}
  1015.  ShowHelp := FALSE; Querier := TRUE;
  1016.  IF ParamCount > 0 THEN
  1017.   BEGIN
  1018.    FOR i := 1 TO Min(2,ParamCount) DO
  1019.     BEGIN
  1020.      FirstParam := ParamStr(i);
  1021.      IF (FirstParam[1] = '/') OR (FirstParam[1] = '-') THEN
  1022.       BEGIN
  1023.        IF NOT Monochrome THEN Monochrome := (UpCase(FirstParam[2]) = 'M');
  1024.        IF     Querier    THEN Querier    := NOT (UpStr(Copy(FirstParam,2,Length(FirstParam)-1)) = 'DONTASK');
  1025.        IF NOT ShowHelp   THEN ShowHelp   := (UpCase(FirstParam[2]) = 'H') OR
  1026.                                             (FirstParam[2] = '?');
  1027.       END;
  1028.     END;  (* for ... do begin *)
  1029.    NewDir := UpStr(ParamStr(ParamCount));
  1030.    IF (NewDir[1] <> '/') AND (NewDir[1] <> '-') THEN
  1031.     BEGIN
  1032.     {$I-}
  1033.     ChDir(NewDir); IORes := IOResult;
  1034.     {$I+}
  1035.     END;
  1036.   END;  (* if paramcount > 0 *)
  1037.  
  1038.  (* Read the .INI files *)
  1039.  InitMemory;
  1040.  
  1041.  INIStrings := New(PINIStrings,Init); (* Read in the .INI file(s) *)
  1042.  
  1043.  IF INIFileExists THEN StringDateHandling.EvaluateINIFileSettings;
  1044.  (* The Date & Time Formats are country-specific and are pre-initialized
  1045.     in the StringDateHandling initialize-section. Re-Initializing it
  1046.     with "our" defaults is not what the users wants.                     *)
  1047.  
  1048.  DescriptionHandling.EvaluateINIFileSettings;
  1049.  DisplayKeyboardAndCursor.EvaluateINIFileSettings;
  1050.  ChooseColors(Monochrome);
  1051.  
  1052.  dmouse.EvaluateINIFileSettings;
  1053.  IF UseMouse THEN MouseReset;
  1054.  
  1055.  DelimiterTable := ReadSettingsString('misc','delimiters',DelimiterTable);
  1056.  DelimiterTable := ' '+DelimiterTable;
  1057.  
  1058.  PasteMovesToNextIndex :=  (ReadSettingsChar('misc','pastemovestonextindex','y') = 'y');
  1059.  
  1060.  overwrite := (ReadSettingsString('','editmode','overstrike') = 'overstrike');
  1061.  
  1062.  Dispose(INIStrings,Done); INIStrings := NIL;
  1063.  
  1064.  EdStart := 25+Length(DateFormat)+Length(TimeFormat);
  1065.  DispLen := ScreenWidth-EdStart;
  1066.  
  1067.  Str(DispLen,s); Template:= '%-12s %s %s %s %-'+s+'s';
  1068.  Changed := FALSE; CutPasteDesc := '';
  1069.  
  1070.  DrawMainScreen(0,0,0,0);
  1071.  IF ShowHelp THEN ShowHelpPage;
  1072.  IF IORes > 0 THEN
  1073.   ReportError(NewDir+' not found. Directory remains unchanged.',FALSE,FALSE);
  1074.  
  1075.  ReadFiles;
  1076.  IF DosError = 0 THEN
  1077.   BEGIN
  1078.    RedrawScreen;
  1079.    EditDescriptions;
  1080.   END
  1081.  ELSE
  1082.   BEGIN
  1083.    ReportError('Drive '+NewDir+' not ready, exiting (key).',FALSE,FALSE);
  1084.    ResetDir := TRUE;
  1085.   END;
  1086.  
  1087.  Dispose(FileList,Done); FileList := NIL;
  1088.  DoneMemory;
  1089.  
  1090.  IF ResetDir THEN
  1091.    BEGIN
  1092.      {$I-}
  1093.      ChDir(StartDir);
  1094.      IORes := IOResult;
  1095.      {$I+}
  1096.    END;
  1097.  
  1098.  IF MouseLoaded THEN MouseReset;
  1099.  SetCursorShape(OrigCursor);
  1100.  NormVideo;
  1101.  ClrScr;
  1102.  WriteLn(Header1);
  1103.  WriteLn(Header2);
  1104.  WriteLn;
  1105.  WriteLn('This program is freeware: you are allowed to use, copy it free');
  1106.  WriteLn('of charge, but you may not sell or hire 4DESC.');
  1107. END.
  1108.