home *** CD-ROM | disk | FTP | other *** search
/ QBasic & Borland Pascal & C / Delphi5.iso / Pascal / BPASCAL.700 / D11 / TVSRC.ZIP / STDDLG.PAS < prev    next >
Encoding:
Pascal/Delphi Source File  |  1992-10-01  |  36.7 KB  |  1,467 lines

  1.  
  2. {*******************************************************}
  3. {                                                       }
  4. {       Turbo Pascal Version 7.0                        }
  5. {       Turbo Vision Unit                               }
  6. {                                                       }
  7. {       Copyright (c) 1992 Borland International        }
  8. {                                                       }
  9. {*******************************************************}
  10.  
  11. unit StdDlg;
  12.  
  13. {$O+,F+,V-,X+,I-,S-}
  14.  
  15. interface
  16.  
  17. uses Objects, Drivers, Views, Dialogs, Dos;
  18.  
  19. const
  20.  
  21. { Commands }
  22.  
  23.   cmFileOpen    = 800;   { Returned from TFileDialog when Open pressed }
  24.   cmFileReplace = 801;   { Returned from TFileDialog when Replace pressed }
  25.   cmFileClear   = 802;   { Returned from TFileDialog when Clear pressed }
  26.   cmFileInit    = 803;   { Used by TFileDialog internally }
  27.   cmChDir       = 804;   { Used by TChDirDialog internally }
  28.   cmRevert      = 805;   { Used by TChDirDialog internally }
  29.  
  30. { Messages }
  31.  
  32.   cmFileFocused = 806;    { A new file was focused in the TFileList }
  33.   cmFileDoubleClicked     { A file was selected in the TFileList }
  34.                 = 807;
  35.  
  36. type
  37.  
  38.   { TSearchRec }
  39.  
  40.   {  Record used to store directory information by TFileDialog }
  41.  
  42.   TSearchRec = record
  43.     Attr: Byte;
  44.     Time: Longint;
  45.     Size: Longint;
  46.     Name: string[12];
  47.   end;
  48.  
  49. type
  50.  
  51.   { TFileInputLine is a special input line that is used by      }
  52.   { TFileDialog that will update its contents in response to a  }
  53.   { cmFileFocused command from a TFileList.                     }
  54.  
  55.   PFileInputLine = ^TFileInputLine;
  56.   TFileInputLine = object(TInputLine)
  57.     constructor Init(var Bounds: TRect; AMaxLen: Integer);
  58.     procedure HandleEvent(var Event: TEvent); virtual;
  59.   end;
  60.  
  61.   { TFileCollection is a collection of TSearchRec's.            }
  62.  
  63.   PFileCollection = ^TFileCollection;
  64.   TFileCollection = object(TSortedCollection)
  65.     function Compare(Key1, Key2: Pointer): Integer; virtual;
  66.     procedure FreeItem(Item: Pointer); virtual;
  67.     function GetItem(var S: TStream): Pointer; virtual;
  68.     procedure PutItem(var S: TStream; Item: Pointer); virtual;
  69.   end;
  70.  
  71.   { TSortedListBox is a TListBox that assumes it has a          }
  72.   { TStoredCollection instead of just a TCollection.  It will   }
  73.   { perform an incremental search on the contents.              }
  74.  
  75.   PSortedListBox = ^TSortedListBox;
  76.   TSortedListBox = object(TListBox)
  77.     SearchPos: Word;
  78.     ShiftState: Byte;
  79.     constructor Init(var Bounds: TRect; ANumCols: Word;
  80.       AScrollBar: PScrollBar);
  81.     procedure HandleEvent(var Event: TEvent); virtual;
  82.     function GetKey(var S: String): Pointer; virtual;
  83.     procedure NewList(AList: PCollection); virtual;
  84.   end;
  85.  
  86.   { TFileList is a TSortedList box that assumes it contains     }
  87.   { a TFileCollection as its collection.  It also communicates  }
  88.   { through broadcast messages to TFileInput and TInfoPane      }
  89.   { what file is currently selected.                            }
  90.  
  91.   PFileList = ^TFileList;
  92.   TFileList = object(TSortedListBox)
  93.     constructor Init(var Bounds: TRect; AScrollBar: PScrollBar);
  94.     destructor Done; virtual;
  95.     function DataSize: Word; virtual;
  96.     procedure FocusItem(Item: Integer); virtual;
  97.     procedure GetData(var Rec); virtual;
  98.     function GetText(Item: Integer; MaxLen: Integer): String; virtual;
  99.     function GetKey(var S: String): Pointer; virtual;
  100.     procedure HandleEvent(var Event: TEvent); virtual;
  101.     procedure ReadDirectory(AWildCard: PathStr);
  102.     procedure SetData(var Rec); virtual;
  103.   end;
  104.  
  105.   { TFileInfoPane is a TView that displays the information      }
  106.   { about the currently selected file in the TFileList          }
  107.   { of a TFileDialog.                                           }
  108.  
  109.   PFileInfoPane = ^TFileInfoPane;
  110.   TFileInfoPane = object(TView)
  111.     S: TSearchRec;
  112.     constructor Init(var Bounds: TRect);
  113.     procedure Draw; virtual;
  114.     function GetPalette: PPalette; virtual;
  115.     procedure HandleEvent(var Event: TEvent); virtual;
  116.   end;
  117.  
  118.   { TFileDialog is a standard file name input dialog            }
  119.  
  120.   TWildStr = PathStr;
  121.  
  122. const
  123.   fdOkButton      = $0001;      { Put an OK button in the dialog }
  124.   fdOpenButton    = $0002;      { Put an Open button in the dialog }
  125.   fdReplaceButton = $0004;      { Put a Replace button in the dialog }
  126.   fdClearButton   = $0008;      { Put a Clear button in the dialog }
  127.   fdHelpButton    = $0010;      { Put a Help button in the dialog }
  128.   fdNoLoadDir     = $0100;      { Do not load the current directory }
  129.                                 { contents into the dialog at Init. }
  130.                                 { This means you intend to change the }
  131.                                 { WildCard by using SetData or store }
  132.                                 { the dialog on a stream. }
  133.  
  134. type
  135.  
  136.   PFileDialog = ^TFileDialog;
  137.   TFileDialog = object(TDialog)
  138.     FileName: PFileInputLine;
  139.     FileList: PFileList;
  140.     WildCard: TWildStr;
  141.     Directory: PString;
  142.     constructor Init(AWildCard: TWildStr; const ATitle,
  143.       InputName: String; AOptions: Word; HistoryId: Byte);
  144.     constructor Load(var S: TStream);
  145.     destructor Done; virtual;
  146.     procedure GetData(var Rec); virtual;
  147.     procedure GetFileName(var S: PathStr);
  148.     procedure HandleEvent(var Event: TEvent); virtual;
  149.     procedure SetData(var Rec); virtual;
  150.     procedure Store(var S: TStream);
  151.     function Valid(Command: Word): Boolean; virtual;
  152.   private
  153.     procedure ReadDirectory;
  154.   end;
  155.  
  156.   { TDirEntry }
  157.  
  158.   PDirEntry = ^TDirEntry;
  159.   TDirEntry = record
  160.     DisplayText: PString;
  161.     Directory: PString;
  162.   end;
  163.  
  164.   { TDirCollection is a collection of TDirEntry's used by       }
  165.   { TDirListBox.                                                }
  166.  
  167.   PDirCollection = ^TDirCollection;
  168.   TDirCollection = object(TCollection)
  169.     function GetItem(var S: TStream): Pointer; virtual;
  170.     procedure FreeItem(Item: Pointer); virtual;
  171.     procedure PutItem(var S: TStream; Item: Pointer); virtual;
  172.   end;
  173.  
  174.   { TDirListBox displays a tree of directories for use in the }
  175.   { TChDirDialog.                                               }
  176.  
  177.   PDirListBox = ^TDirListBox;
  178.   TDirListBox = object(TListBox)
  179.     Dir: DirStr;
  180.     Cur: Word;
  181.     constructor Init(var Bounds: TRect; AScrollBar: PScrollBar);
  182.     destructor Done; virtual;
  183.     function GetText(Item: Integer; MaxLen: Integer): String; virtual;
  184.     procedure HandleEvent(var Event: TEvent); virtual;
  185.     function IsSelected(Item: Integer): Boolean; virtual;
  186.     procedure NewDirectory(var ADir: DirStr);
  187.     procedure SetState(AState: Word; Enable: Boolean); virtual;
  188.   end;
  189.  
  190.   { TChDirDialog is a standard change directory dialog.         }
  191.  
  192. const
  193.   cdNormal     = $0000; { Option to use dialog immediately }
  194.   cdNoLoadDir  = $0001; { Option to init the dialog to store on a stream }
  195.   cdHelpButton = $0002; { Put a help button in the dialog }
  196.  
  197. type
  198.  
  199.   PChDirDialog = ^TChDirDialog;
  200.   TChDirDialog = object(TDialog)
  201.     DirInput: PInputLine;
  202.     DirList: PDirListBox;
  203.     OkButton: PButton;
  204.     ChDirButton: PButton;
  205.     constructor Init(AOptions: Word; HistoryId: Word);
  206.     constructor Load(var S: TStream);
  207.     function DataSize: Word; virtual;
  208.     procedure GetData(var Rec); virtual;
  209.     procedure HandleEvent(var Event: TEvent); virtual;
  210.     procedure SetData(var Rec); virtual;
  211.     procedure Store(var S: TStream);
  212.     function Valid(Command: Word): Boolean; virtual;
  213.   private
  214.     procedure SetUpDialog;
  215.   end;
  216.  
  217. const
  218.  
  219.   CInfoPane = #30;
  220.  
  221.   { TStream registration records }
  222.  
  223. const
  224.   RFileInputLine: TStreamRec = (
  225.      ObjType: 60;
  226.      VmtLink: Ofs(TypeOf(TFileInputLine)^);
  227.      Load:    @TFileInputLine.Load;
  228.      Store:   @TFileInputLine.Store
  229.   );
  230.  
  231. const
  232.   RFileCollection: TStreamRec = (
  233.      ObjType: 61;
  234.      VmtLink: Ofs(TypeOf(TFileCollection)^);
  235.      Load:    @TFileCollection.Load;
  236.      Store:   @TFileCollection.Store
  237.   );
  238.  
  239. const
  240.   RFileList: TStreamRec = (
  241.      ObjType: 62;
  242.      VmtLink: Ofs(TypeOf(TFileList)^);
  243.      Load:    @TFileList.Load;
  244.      Store:   @TFileList.Store
  245.   );
  246.  
  247. const
  248.   RFileInfoPane: TStreamRec = (
  249.      ObjType: 63;
  250.      VmtLink: Ofs(TypeOf(TFileInfoPane)^);
  251.      Load:    @TFileInfoPane.Load;
  252.      Store:   @TFileInfoPane.Store
  253.   );
  254.  
  255. const
  256.   RFileDialog: TStreamRec = (
  257.      ObjType: 64;
  258.      VmtLink: Ofs(TypeOf(TFileDialog)^);
  259.      Load:    @TFileDialog.Load;
  260.      Store:   @TFileDialog.Store
  261.   );
  262.  
  263. const
  264.   RDirCollection: TStreamRec = (
  265.      ObjType: 65;
  266.      VmtLink: Ofs(TypeOf(TDirCollection)^);
  267.      Load:    @TDirCollection.Load;
  268.      Store:   @TDirCollection.Store
  269.   );
  270.  
  271. const
  272.   RDirListBox: TStreamRec = (
  273.      ObjType: 66;
  274.      VmtLink: Ofs(TypeOf(TDirListBox)^);
  275.      Load:    @TDirListBox.Load;
  276.      Store:   @TDirListBox.Store
  277.   );
  278.  
  279. const
  280.   RChDirDialog: TStreamRec = (
  281.      ObjType: 67;
  282.      VmtLink: Ofs(TypeOf(TChDirDialog)^);
  283.      Load:    @TChDirDialog.Load;
  284.      Store:   @TChDirDialog.Store
  285.   );
  286.  
  287. const
  288.   RSortedListBox: TStreamRec = (
  289.      ObjType: 68;
  290.      VmtLink: Ofs(TypeOf(TSortedListBox)^);
  291.      Load:    @TSortedListBox.Load;
  292.      Store:   @TSortedListBox.Store
  293.   );
  294.  
  295. procedure RegisterStdDlg;
  296.  
  297. implementation
  298.  
  299. uses App, Memory, HistList, MsgBox;
  300.  
  301. function DriveValid(Drive: Char): Boolean; near; assembler;
  302. asm
  303.     MOV    AH,19H          { Save the current drive in BL }
  304.         INT    21H
  305.         MOV    BL,AL
  306.      MOV    DL,Drive    { Select the given drive }
  307.         SUB    DL,'A'
  308.         MOV    AH,0EH
  309.         INT    21H
  310.         MOV    AH,19H        { Retrieve what DOS thinks is current }
  311.         INT    21H
  312.         MOV    CX,0        { Assume false }
  313.         CMP    AL,DL        { Is the current drive the given drive? }
  314.     JNE    @@1
  315.         MOV    CX,1        { It is, so the drive is valid }
  316.     MOV    DL,BL        { Restore the old drive }
  317.         MOV    AH,0EH
  318.         INT    21H
  319. @@1:    XCHG    AX,CX        { Put the return value into AX }
  320. end;
  321.  
  322. function PathValid(var Path: PathStr): Boolean;
  323. var
  324.   ExpPath: PathStr;
  325.   F: File;
  326.   SR: SearchRec;
  327. begin
  328.   ExpPath := FExpand(Path);
  329.   if Length(ExpPath) <= 3 then PathValid := DriveValid(ExpPath[1])
  330.   else
  331.   begin
  332.     if ExpPath[Length(ExpPath)] = '\' then Dec(ExpPath[0]);
  333.     FindFirst(ExpPath, Directory, SR);
  334.     PathValid := (DosError = 0) and (SR.Attr and Directory <> 0);
  335.   end;
  336. end;
  337.  
  338. function ValidFileName(var FileName: PathStr): Boolean;
  339. const
  340.   IllegalChars = ';,=+<>|"[] \';
  341. var
  342.   Dir: DirStr;
  343.   Name: NameStr;
  344.   Ext: ExtStr;
  345.  
  346. { Contains returns true if S1 contains any characters in S2 }
  347. function Contains(S1, S2: String): Boolean; near; assembler;
  348. asm
  349.     PUSH    DS
  350.         CLD
  351.         LDS    SI,S1
  352.         LES    DI,S2
  353.         MOV    DX,DI
  354.         XOR    AH,AH
  355.         LODSB
  356.         MOV    BX,AX
  357.         OR      BX,BX
  358.         JZ      @@2
  359.         MOV    AL,ES:[DI]
  360.         XCHG    AX,CX
  361. @@1:    PUSH    CX
  362.     MOV    DI,DX
  363.     LODSB
  364.         REPNE    SCASB
  365.         POP    CX
  366.         JE    @@3
  367.     DEC    BX
  368.         JNZ    @@1
  369. @@2:    XOR    AL,AL
  370.     JMP    @@4
  371. @@3:    MOV    AL,1
  372. @@4:    POP    DS
  373. end;
  374.  
  375. begin
  376.   ValidFileName := True;
  377.   FSplit(FileName, Dir, Name, Ext);
  378.   if not ((Dir = '') or PathValid(Dir)) or Contains(Name, IllegalChars) or
  379.     Contains(Dir, IllegalChars) then ValidFileName := False;
  380. end;
  381.  
  382. function GetCurDir: DirStr;
  383. var
  384.   CurDir: DirStr;
  385. begin
  386.   GetDir(0, CurDir);
  387.   if Length(CurDir) > 3 then
  388.   begin
  389.     Inc(CurDir[0]);
  390.     CurDir[Length(CurDir)] := '\';
  391.   end;
  392.   GetCurDir := CurDir;
  393. end;
  394.  
  395. type
  396.   PSearchRec = ^TSearchRec;
  397.  
  398. function IsWild(const S: String): Boolean;
  399. begin
  400.   IsWild := (Pos('?',S) > 0) or (Pos('*',S) > 0);
  401. end;
  402.  
  403. function IsDir(const S: String): Boolean;
  404. var
  405.   SR: SearchRec;
  406. begin
  407.   FindFirst(S, Directory, SR);
  408.   if DosError = 0 then
  409.     IsDir := SR.Attr and Directory <> 0
  410.   else IsDir := False;
  411. end;
  412.  
  413. { TFileInputLine }
  414.  
  415. constructor TFileInputLine.Init(var Bounds: TRect; AMaxLen: Integer);
  416. begin
  417.   TInputLine.Init(Bounds, AMaxLen);
  418.   EventMask := EventMask or evBroadcast;
  419. end;
  420.  
  421. procedure TFileInputLine.HandleEvent(var Event: TEvent);
  422. var
  423.   Dir: DirStr;
  424.   Name: NameStr;
  425.   Ext: ExtStr;
  426. begin
  427.   TInputLine.HandleEvent(Event);
  428.   if (Event.What = evBroadcast) and (Event.Command = cmFileFocused) and
  429.     (State and sfSelected = 0) then
  430.   begin
  431.      if PSearchRec(Event.InfoPtr)^.Attr and Directory <> 0 then
  432.         Data^ := PSearchRec(Event.InfoPtr)^.Name + '\'+
  433.           PFileDialog(Owner)^.WildCard
  434.      else Data^ := PSearchRec(Event.InfoPtr)^.Name;
  435.      DrawView;
  436.   end;
  437. end;
  438.  
  439. { TFileCollection }
  440.  
  441. function TFileCollection.Compare(Key1, Key2: Pointer): Integer;
  442. begin
  443.   if PSearchRec(Key1)^.Name = PSearchRec(Key2)^.Name then Compare := 0
  444.   else if PSearchRec(Key1)^.Name = '..' then Compare := 1
  445.   else if PSearchRec(Key2)^.Name = '..' then Compare := -1
  446.   else if (PSearchRec(Key1)^.Attr and Directory <> 0) and
  447.      (PSearchRec(Key2)^.Attr and Directory = 0) then Compare := 1
  448.   else if (PSearchRec(Key2)^.Attr and Directory <> 0) and
  449.      (PSearchRec(Key1)^.Attr and Directory = 0) then Compare := -1
  450.   else if PSearchRec(Key1)^.Name > PSearchRec(Key2)^.Name then
  451.     Compare := 1
  452.   else Compare := -1;
  453. end;
  454.  
  455. procedure TFileCollection.FreeItem(Item: Pointer);
  456. begin
  457.   Dispose(PSearchRec(Item));
  458. end;
  459.  
  460. function TFileCollection.GetItem(var S: TStream): Pointer;
  461. var
  462.   Item: PSearchRec;
  463. begin
  464.   New(Item);
  465.   S.Read(Item^, SizeOf(TSearchRec));
  466.   GetItem := Item;
  467. end;
  468.  
  469. procedure TFileCollection.PutItem(var S: TStream; Item: Pointer);
  470. begin
  471.   S.Write(Item^, SizeOf(TSearchRec));
  472. end;
  473.  
  474. { TSortedListBox }
  475.  
  476. constructor TSortedListBox.Init(var Bounds: TRect; ANumCols: Word;
  477.   AScrollBar: PScrollBar);
  478. begin
  479.   TListBox.Init(Bounds, ANumCols, AScrollBar);
  480.   SearchPos := 0;
  481.   ShowCursor;
  482.   SetCursor(1,0);
  483. end;
  484.  
  485. procedure TSortedListBox.HandleEvent(var Event: TEvent);
  486. var
  487.   CurString, NewString: String;
  488.   K: Pointer;
  489.   Value, OldPos, OldValue: Integer;
  490.   T: Boolean;
  491.  
  492. function Equal(const S1, S2: String; Count: Word): Boolean;
  493. var
  494.   I: Word;
  495. begin
  496.   Equal := False;
  497.   if (Length(S1) < Count) or (Length(S2) < Count) then Exit;
  498.   for I := 1 to Count do
  499.     if UpCase(S1[I]) <> UpCase(S2[I]) then Exit;
  500.   Equal := True;
  501. end;
  502.  
  503. begin
  504.   OldValue := Focused;
  505.   TListBox.HandleEvent(Event);
  506.   if OldValue <> Focused then SearchPos := 0;
  507.   if Event.What = evKeyDown then
  508.   begin
  509.     if Event.CharCode <> #0 then
  510.     begin
  511.       Value := Focused;
  512.       if Value < Range then CurString := GetText(Value, 255)
  513.       else CurString := '';
  514.       OldPos := SearchPos;
  515.       if Event.KeyCode = kbBack then
  516.       begin
  517.         if SearchPos = 0 then Exit;
  518.         Dec(SearchPos);
  519.         if SearchPos = 0 then ShiftState := GetShiftState;
  520.         CurString[0] := Char(SearchPos);
  521.       end
  522.       else if (Event.CharCode = '.') then SearchPos := Pos('.',CurString)
  523.       else
  524.       begin
  525.         Inc(SearchPos);
  526.         if SearchPos = 1 then ShiftState := GetShiftState;
  527.         CurString[0] := Char(SearchPos);
  528.         CurString[SearchPos] := Event.CharCode;
  529.       end;
  530.       K := GetKey(CurString);
  531.       T := PSortedCollection(List)^.Search(K, Value);
  532.       if Value < Range then
  533.       begin
  534.         if Value < Range then NewString := GetText(Value, 255)
  535.         else NewString := '';
  536.         if Equal(NewString, CurString, SearchPos) then
  537.         begin
  538.           if Value <> OldValue then
  539.           begin
  540.             FocusItem(Value);
  541.             { Assumes ListControl will set the cursor to the first character }
  542.             { of the sfFocused item }
  543.             SetCursor(Cursor.X+SearchPos, Cursor.Y);
  544.           end
  545.           else SetCursor(Cursor.X+(SearchPos-OldPos), Cursor.Y);
  546.         end
  547.         else SearchPos := OldPos;
  548.       end
  549.       else SearchPos := OldPos;
  550.       if (SearchPos <> OldPos) or (Event.CharCode in ['A'..'Z','a'..'z']) then
  551.         ClearEvent(Event);
  552.     end;
  553.   end;
  554. end;
  555.  
  556. function TSortedListBox.GetKey(var S: String): Pointer;
  557. begin
  558.   GetKey := @S;
  559. end;
  560.  
  561. procedure TSortedListBox.NewList(AList: PCollection);
  562. begin
  563.   TListBox.NewList(AList);
  564.   SearchPos := 0;
  565. end;
  566.  
  567. { TFileList }
  568.  
  569. constructor TFileList.Init(var Bounds: TRect; AScrollBar: PScrollBar);
  570. begin
  571.   TSortedListBox.Init(Bounds, 2, AScrollBar);
  572. end;
  573.  
  574. destructor TFileList.Done;
  575. begin
  576.   if List <> nil then Dispose(List, Done);
  577.   TListBox.Done;
  578. end;
  579.  
  580. function TFileList.DataSize: Word;
  581. begin
  582.   DataSize := 0;
  583. end;
  584.  
  585. procedure TFileList.FocusItem(Item: Integer);
  586. begin
  587.   TSortedListBox.FocusItem(Item);
  588.   Message(Owner, evBroadcast, cmFileFocused, List^.At(Item));
  589. end;
  590.  
  591. procedure TFileList.GetData(var Rec);
  592. begin
  593. end;
  594.  
  595. function TFileList.GetKey(var S: String): Pointer;
  596. const
  597.   SR: TSearchRec = ();
  598.  
  599. procedure UpStr(var S: String);
  600. var
  601.   I: Integer;
  602. begin
  603.   for I := 1 to Length(S) do S[I] := UpCase(S[I]);
  604. end;
  605.  
  606. begin
  607.   if (ShiftState and $03 <> 0) or ((S <> '') and (S[1]='.')) then
  608.     SR.Attr := Directory
  609.   else SR.Attr := 0;
  610.   SR.Name := S;
  611.   UpStr(SR.Name);
  612.   GetKey := @SR;
  613. end;
  614.  
  615. function TFileList.GetText(Item: Integer; MaxLen: Integer): String;
  616. var
  617.   S: String;
  618.   SR: PSearchRec;
  619. begin
  620.   SR := PSearchRec(List^.At(Item));
  621.   S := SR^.Name;
  622.   if SR^.Attr and Directory <> 0 then
  623.   begin
  624.     S[Length(S)+1] := '\';
  625.     Inc(S[0]);
  626.   end;
  627.   GetText := S;
  628. end;
  629.  
  630. procedure TFileList.HandleEvent(var Event: TEvent);
  631. begin
  632.   if (Event.What = evMouseDown) and (Event.Double) then
  633.   begin
  634.     Event.What := evCommand;
  635.     Event.Command := cmOK;
  636.     PutEvent(Event);
  637.     ClearEvent(Event);
  638.   end
  639.   else TSortedListBox.HandleEvent(Event);
  640. end;
  641.  
  642. procedure TFileList.ReadDirectory(AWildCard: PathStr);
  643. const
  644.   FindAttr = ReadOnly + Archive;
  645.   AllFiles = '*.*';
  646.   PrevDir  = '..';
  647. var
  648.   S: SearchRec;
  649.   P: PSearchRec;
  650.   FileList: PFileCollection;
  651.   NumFiles: Word;
  652.   CurPath: PathStr;
  653.   Dir: DirStr;
  654.   Name: NameStr;
  655.   Ext: ExtStr;
  656.   Event: TEvent;
  657.   Tmp: PathStr;
  658.   Flag: Integer;
  659. begin
  660.   NumFiles := 0;
  661.   AWildCard := FExpand(AWildCard);
  662.   FSplit(AWildCard, Dir, Name, Ext);
  663.   FileList := New(PFileCollection, Init(5, 5));
  664.   FindFirst(AWildCard, FindAttr, S);
  665.   P := @P;
  666.   while (P <> nil) and (DosError = 0) do
  667.   begin
  668.     if (S.Attr and Directory = 0) then
  669.     begin
  670.       P := MemAlloc(SizeOf(P^));
  671.       if P <> nil then
  672.       begin
  673.         Move(S.Attr, P^, SizeOf(P^));
  674.         FileList^.Insert(P);
  675.       end;
  676.     end;
  677.     FindNext(S);
  678.   end;
  679.   Tmp := Dir + AllFiles;
  680.   FindFirst(Tmp, Directory, S);
  681.   while (P <> nil) and (DosError = 0) do
  682.   begin
  683.     if (S.Attr and Directory <> 0) and (S.Name[1] <> '.') then
  684.     begin
  685.       P := MemAlloc(SizeOf(P^));
  686.       if P <> nil then
  687.       begin
  688.         Move(S.Attr, P^, SizeOf(P^));
  689.         FileList^.Insert(PObject(P));
  690.       end;
  691.     end;
  692.     FindNext(S);
  693.   end;
  694.   if Length(Dir) > 4 then
  695.   begin
  696.     P := MemAlloc(SizeOf(P^));
  697.     if P <> nil then
  698.     begin
  699.       FindFirst(Tmp, Directory, S);
  700.       FindNext(S);
  701.       if (DosError = 0) and (S.Name = PrevDir) then
  702.         Move(S.Attr, P^, SizeOf(P^))
  703.       else
  704.       begin
  705.         P^.Name := PrevDir;
  706.         P^.Size := 0;
  707.         P^.Time := $210000;
  708.         P^.Attr := Directory;
  709.       end;
  710.       FileList^.Insert(PObject(P));
  711.     end;
  712.   end;
  713.   if P = nil then MessageBox('Too many files.', nil, mfOkButton + mfWarning);
  714.   NewList(FileList);
  715.   if List^.Count > 0 then
  716.   begin
  717.     Event.What := evBroadcast;
  718.     Event.Command := cmFileFocused;
  719.     Event.InfoPtr := List^.At(0);
  720.     Owner^.HandleEvent(Event);
  721.   end;
  722. end;
  723.  
  724. procedure TFileList.SetData(var Rec);
  725. begin
  726.   with PFileDialog(Owner)^ do
  727.     Self.ReadDirectory(Directory^ + WildCard);
  728. end;
  729.  
  730. { TFileInfoPane }
  731.  
  732. constructor TFileInfoPane.Init(var Bounds: TRect);
  733. begin
  734.   TView.Init(Bounds);
  735.   EventMask := EventMask or evBroadcast;
  736. end;
  737.  
  738. procedure TFileInfoPane.Draw;
  739. var
  740.   B: TDrawBuffer;
  741.   D: String[9];
  742.   M: String[3];
  743.   PM: Boolean;
  744.   Color: Word;
  745.   Time: DateTime;
  746.   Path: PathStr;
  747.   FmtId: String;
  748.   Params: array[0..7] of LongInt;
  749.   Str: String[80];
  750. const
  751.   sDirectoryLine = ' %-12s %-9s %3s %2d, %4d  %2d:%02d%cm';
  752.   sFileLine      = ' %-12s %-9d %3s %2d, %4d  %2d:%02d%cm';
  753.   Month: array[1..12] of String[3] = 
  754.     ('Jan','Feb','Mar','Apr','May','Jun',
  755.      'Jul','Aug','Sep','Oct','Nov','Dec');
  756. begin
  757.   { Display path }
  758.   Path := FExpand(PFileDialog(Owner)^.Directory^+PFileDialog(Owner)^.WildCard);
  759.   Color := GetColor($01);
  760.   MoveChar(B, ' ', Color, Size.X);
  761.   MoveStr(B[1], Path, Color);
  762.   WriteLine(0, 0, Size.X, 1, B);
  763.  
  764.   { Display file }
  765.   Params[0] := LongInt(@S.Name);
  766.   MoveChar(B, ' ', Color, Size.X);
  767.   Params[0] := LongInt(@S.Name);
  768.   if S.Attr and Directory <> 0 then
  769.   begin
  770.     FmtId := sDirectoryLine;
  771.     D := 'Directory';
  772.     Params[1] := LongInt(@D);
  773.   end else
  774.   begin
  775.     FmtId := sFileLine;
  776.     Params[1] := S.Size;
  777.   end;
  778.   UnpackTime(S.Time, Time);
  779.   M := Month[Time.Month];
  780.   Params[2] := LongInt(@M);
  781.   Params[3] := Time.Day;
  782.   Params[4] := Time.Year;
  783.   PM := Time.Hour >= 12;
  784.   Time.Hour := Time.Hour mod 12;
  785.   if Time.Hour = 0 then Time.Hour := 12;
  786.   Params[5] := Time.Hour;
  787.   Params[6] := Time.Min;
  788.   if PM then Params[7] := Byte('p')
  789.   else Params[7] := Byte('a');
  790.   FormatStr(Str, FmtId, Params);
  791.   MoveStr(B, Str, Color);
  792.   WriteLine(0, 1, Size.X, 1, B);
  793.  
  794.   { Fill in rest of rectangle }
  795.   MoveChar(B, ' ', Color, Size.X);
  796.   WriteLine(0, 2, Size.X, Size.Y-2, B);
  797. end;
  798.  
  799. function TFileInfoPane.GetPalette: PPalette;
  800. const
  801.   P: String[Length(CInfoPane)] = CInfoPane;
  802. begin
  803.   GetPalette := @P;
  804. end;
  805.  
  806. procedure TFileInfoPane.HandleEvent(var Event: TEvent);
  807. begin
  808.   TView.HandleEvent(Event);
  809.   if (Event.What = evBroadcast) and (Event.Command = cmFileFocused) then
  810.   begin
  811.     S := PSearchRec(Event.InfoPtr)^;
  812.     DrawView;
  813.   end;
  814. end;
  815.  
  816. { TFileDialog }
  817.  
  818. constructor TFileDialog.Init(AWildCard: TWildStr; const ATitle,
  819.   InputName: String; AOptions: Word; HistoryId: Byte);
  820. var
  821.   Control: PView;
  822.   R: TRect;
  823.   S: String;
  824.   Opt: Word;
  825.   ACurDir: PathStr;
  826. begin
  827.   R.Assign(15,1,64,20);
  828.   TDialog.Init(R, ATitle);
  829.   Options := Options or ofCentered;
  830.   WildCard := AWildCard;
  831.  
  832.   R.Assign(3,3,31,4);
  833.   FileName := New(PFileInputLine, Init(R, 79));
  834.   FileName^.Data^ := WildCard;
  835.   Insert(FileName);
  836.   R.Assign(2,2,3+CStrLen(InputName),3);
  837.   Control := New(PLabel, Init(R, InputName, FileName));
  838.   Insert(Control);
  839.   R.Assign(31,3,34,4);
  840.   Control := New(PHistory, Init(R, FileName, HistoryId));
  841.   Insert(Control);
  842.  
  843.   R.Assign(3,14,34,15);
  844.   Control := New(PScrollBar, Init(R));
  845.   Insert(Control);
  846.   R.Assign(3,6,34,14);
  847.   FileList := New(PFileList, Init(R, PScrollBar(Control)));
  848.   Insert(FileList);
  849.   R.Assign(2,5,8,6);
  850.   Control := New(PLabel, Init(R, '~F~iles', FileList));
  851.   Insert(Control);
  852.  
  853.   R.Assign(35,3,46,5);
  854.   Opt := bfDefault;
  855.   if AOptions and fdOpenButton <> 0 then
  856.   begin
  857.     Insert(New(PButton, Init(R, '~O~pen', cmFileOpen, Opt)));
  858.     Opt := bfNormal;
  859.     Inc(R.A.Y,3); Inc(R.B.Y,3);
  860.   end;
  861.   if AOptions and fdOkButton <> 0 then
  862.   begin
  863.     Insert(New(PButton, Init(R, 'O~K~', cmFileOpen, Opt)));
  864.     Opt := bfNormal;
  865.     Inc(R.A.Y,3); Inc(R.B.Y,3);
  866.   end;
  867.   if AOptions and fdReplaceButton <> 0 then
  868.   begin
  869.     Insert(New(PButton, Init(R, '~R~eplace',cmFileReplace, Opt)));
  870.     Opt := bfNormal;
  871.     Inc(R.A.Y,3); Inc(R.B.Y,3);
  872.   end;
  873.   if AOptions and fdClearButton <> 0 then
  874.   begin
  875.     Insert(New(PButton, Init(R, '~C~lear',cmFileClear, Opt)));
  876.     Opt := bfNormal;
  877.     Inc(R.A.Y,3); Inc(R.B.Y,3);
  878.   end;
  879.   Insert(New(PButton, Init(R, 'Cancel', cmCancel, bfNormal)));
  880.   Inc(R.A.Y,3); Inc(R.B.Y,3);
  881.   if AOptions and fdHelpButton <> 0 then
  882.   begin
  883.     Insert(New(PButton, Init(R, 'Help',cmHelp, bfNormal)));
  884.     Inc(R.A.Y,3); Inc(R.B.Y,3);
  885.   end;
  886.  
  887.   R.Assign(1,16,48,18);
  888.   Control := New(PFileInfoPane, Init(R));
  889.   Insert(Control);
  890.  
  891.   SelectNext(False);
  892.  
  893.   if AOptions and fdNoLoadDir = 0 then ReadDirectory;
  894. end;
  895.  
  896. constructor TFileDialog.Load(var S: TStream);
  897. var
  898.   ACurDir: DirStr;
  899.   ViewId: Word;
  900. begin
  901.   TDialog.Load(S);
  902.   S.Read(WildCard, SizeOf(TWildStr));
  903.   GetSubViewPtr(S, FileName);
  904.   GetSubViewPtr(S, FileList);
  905.  
  906.   ReadDirectory;
  907. end;
  908.  
  909. destructor TFileDialog.Done;
  910. begin
  911.   DisposeStr(Directory);
  912.   TDialog.Done;
  913. end;
  914.  
  915. procedure TFileDialog.GetData(var Rec);
  916. begin
  917.   GetFilename(PathStr(Rec));
  918. end;
  919.  
  920. procedure TFileDialog.GetFileName(var S: PathStr);
  921. var
  922.   Path: PathStr;
  923.   Name: NameStr;
  924.   Ext: ExtStr;
  925.   TPath: PathStr;
  926.   TName: NameStr;
  927.   TExt: NameStr;
  928.  
  929. function LTrim(const S: String): String;
  930. var
  931.   I: Integer;
  932. begin
  933.   I := 1;
  934.   while (I < Length(S)) and (S[I] = ' ') do Inc(I);
  935.   LTrim := Copy(S, I, 255);
  936. end;
  937.  
  938. function RTrim(const S: String): String;
  939. var
  940.   I: Integer;
  941. begin
  942.   I := Length(S);
  943.   while S[I] = ' ' do Dec(I);
  944.   RTrim := Copy(S, 1, I);
  945. end;
  946.  
  947. function RelativePath(var S: PathStr): Boolean;
  948. var
  949.   I,J: Integer;
  950.   P: PathStr;
  951. begin
  952.   S := LTrim(RTrim(S));
  953.   if (S <> '') and ((S[1] = '\') or (S[2] = ':')) then RelativePath := False
  954.   else RelativePath := True;
  955. end;
  956.  
  957. function NoWildChars(S: String): String; near; assembler;
  958. asm
  959.     PUSH    DS
  960.     LDS    SI,S
  961.         XOR     AX,AX
  962.     LODSB
  963.     XCHG    AX,CX
  964.         LES     DI,@Result
  965.         INC     DI
  966.         JCXZ    @@3
  967. @@1:    LODSB
  968.     CMP    AL,'?'
  969.     JE    @@2
  970.     CMP    AL,'*'
  971.     JE    @@2
  972.     STOSB
  973. @@2:    LOOP    @@1
  974. @@3:    XCHG    AX,DI
  975.     MOV    DI,WORD PTR @Result
  976.     SUB    AX,DI
  977.         DEC     AX
  978.         STOSB
  979.     POP    DS
  980. end;
  981.  
  982. begin
  983.   S := FileName^.Data^;
  984.   if RelativePath(S) then S := FExpand(Directory^ + S)
  985.   else S := FExpand(S);
  986.   FSplit(S, Path, Name, Ext);
  987.   if ((Name = '') or (Ext = '')) and not IsDir(S) then
  988.   begin
  989.     FSplit(WildCard, TPath, TName, TExt);
  990.     if ((Name = '') and (Ext = '')) then S := Path + TName + TExt
  991.     else if Name = '' then S := Path + TName + Ext
  992.     else if Ext = '' then
  993.     begin
  994.       if IsWild(Name) then S := Path + Name + TExt
  995.       else S := Path + Name + NoWildChars(TExt);
  996.     end;
  997.   end;
  998. end;
  999.  
  1000. procedure TFileDialog.HandleEvent(var Event: TEvent);
  1001. begin
  1002.   TDialog.HandleEvent(Event);
  1003.   if Event.What = evCommand then
  1004.     case Event.Command of
  1005.       cmFileOpen, cmFileReplace, cmFileClear:
  1006.         begin
  1007.           EndModal(Event.Command);
  1008.           ClearEvent(Event);
  1009.         end;
  1010.     end;
  1011. end;
  1012.  
  1013. procedure TFileDialog.SetData(var Rec);
  1014. begin
  1015.   TDialog.SetData(Rec);
  1016.   if (PathStr(Rec) <> '') and (IsWild(TWildStr(Rec))) then
  1017.   begin
  1018.     Valid(cmFileInit);
  1019.     FileName^.Select;
  1020.   end;
  1021. end;
  1022.  
  1023. procedure TFileDialog.ReadDirectory;
  1024. begin
  1025.   FileList^.ReadDirectory(WildCard);
  1026.   Directory := NewStr(GetCurDir);
  1027. end;
  1028.  
  1029. procedure TFileDialog.Store(var S: TStream);
  1030. begin
  1031.   TDialog.Store(S);
  1032.   S.Write(WildCard, SizeOf(TWildStr));
  1033.   PutSubViewPtr(S, FileName);
  1034.   PutSubViewPtr(S, FileList);
  1035. end;
  1036.  
  1037. function TFileDialog.Valid(Command: Word): Boolean;
  1038. var
  1039.   T: Boolean;
  1040.   FName: PathStr;
  1041.   Dir: DirStr;
  1042.   Name: NameStr;
  1043.   Ext: ExtStr;
  1044.  
  1045. function CheckDirectory(var S: PathStr): Boolean;
  1046. begin
  1047.   if not PathValid(S) then
  1048.   begin
  1049.     MessageBox('Invalid drive or directory.', nil, mfError + mfOkButton);
  1050.     FileName^.Select;
  1051.     CheckDirectory := False;
  1052.   end else CheckDirectory := True;
  1053. end;
  1054.  
  1055. begin
  1056.   if Command = 0 then
  1057.   begin
  1058.     Valid := True;
  1059.     Exit;
  1060.   end else Valid := False;
  1061.   if TDialog.Valid(Command) then
  1062.   begin
  1063.     GetFileName(FName);
  1064.     if (Command <> cmCancel) and (Command <> cmFileClear) then
  1065.     begin
  1066.       if IsWild(FName) then
  1067.       begin
  1068.         FSplit(FName, Dir, Name, Ext);
  1069.         if CheckDirectory(Dir) then
  1070.         begin
  1071.           DisposeStr(Directory);
  1072.           Directory := NewStr(Dir);
  1073.           WildCard := Name+Ext;
  1074.           if Command <> cmFileInit then FileList^.Select;
  1075.           FileList^.ReadDirectory(Directory^+WildCard);
  1076.         end
  1077.       end
  1078.       else if IsDir(FName) then
  1079.       begin
  1080.         if CheckDirectory(FName) then
  1081.         begin
  1082.           DisposeStr(Directory);
  1083.       Directory := NewSTr(FName+'\');
  1084.       if Command <> cmFileInit then FileList^.Select;
  1085.       FileList^.ReadDirectory(Directory^+WildCard);
  1086.         end
  1087.       end else if ValidFileName(FName) then Valid := True
  1088.       else
  1089.       begin
  1090.         MessageBox('Invalid file name.', nil, mfError + mfOkButton);
  1091.         Valid := False;
  1092.       end
  1093.     end
  1094.     else Valid := True;
  1095.   end;
  1096. end;
  1097.  
  1098. { TDirCollection }
  1099.  
  1100. function TDirCollection.GetItem(var S: TStream): Pointer;
  1101. var
  1102.   DirItem: PDirEntry;
  1103. begin
  1104.   New(DirItem);
  1105.   DirItem^.DisplayText := S.ReadStr;
  1106.   DirItem^.Directory := S.ReadStr;
  1107.   GetItem := DirItem;
  1108. end;
  1109.  
  1110. procedure TDirCollection.FreeItem(Item: Pointer);
  1111. var
  1112.   DirItem: PDirEntry absolute Item;
  1113. begin
  1114.   DisposeStr(DirItem^.DisplayText);
  1115.   DisposeStr(DirItem^.Directory);
  1116.   Dispose(DirItem);
  1117. end;
  1118.  
  1119. procedure TDirCollection.PutItem(var S: TStream; Item: Pointer);
  1120. var
  1121.   DirItem: PDirEntry absolute Item;
  1122. begin
  1123.   S.WriteStr(DirItem^.DisplayText);
  1124.   S.WriteStr(DirItem^.Directory);
  1125. end;
  1126.  
  1127. { TDirListBox }
  1128.  
  1129. const
  1130.   DrivesS: String[6] = 'Drives';
  1131.   Drives: PString = @DrivesS;
  1132.  
  1133. constructor TDirListBox.Init(var Bounds: TRect; AScrollBar:
  1134.   PScrollBar);
  1135. begin
  1136.   TListBox.Init(Bounds, 1, AScrollBar);
  1137.   Dir := '';
  1138. end;
  1139.  
  1140. destructor TDirListBox.Done;
  1141. begin
  1142.   if List <> nil then Dispose(List, Done);
  1143.   TListBox.Done;
  1144. end;
  1145.  
  1146. function TDirListBox.GetText(Item: Integer; MaxLen: Integer): String;
  1147. begin
  1148.   GetText := PDirEntry(List^.At(Item))^.DisplayText^;
  1149. end;
  1150.  
  1151. procedure TDirListBox.HandleEvent(var Event: TEvent);
  1152. begin
  1153.   if (Event.What = evMouseDown) and (Event.Double) then
  1154.   begin
  1155.     Event.What := evCommand;
  1156.     Event.Command := cmChangeDir;
  1157.     PutEvent(Event);
  1158.     ClearEvent(Event);
  1159.   end
  1160.   else TListBox.HandleEvent(Event);
  1161. end;
  1162.  
  1163. function TDirListBox.IsSelected(Item: Integer): Boolean;
  1164. begin
  1165.   IsSelected := Item = Cur;
  1166. end;
  1167.  
  1168. procedure TDirListBox.NewDirectory(var ADir: DirStr);
  1169. const
  1170.   PathDir            = '└─┬';
  1171.   FirstDir           =   '└┬─';
  1172.   MiddleDir          =   ' ├─';
  1173.   LastDir            =   ' └─';
  1174.   IndentSize         = '  ';
  1175. var
  1176.   AList: PCollection;
  1177.   NewDir, Dirct: DirStr;
  1178.   C, OldC: Char;
  1179.   S, Indent: String[80];
  1180.   P: PString;
  1181.   isFirst: Boolean;
  1182.   SR: SearchRec;
  1183.   I: Integer;
  1184.   DirEntry: PDirEntry;
  1185.  
  1186. function NewDirEntry(const DisplayText, Directory: String): PDirEntry; near;
  1187. var
  1188.   DirEntry: PDirEntry;
  1189. begin
  1190.   New(DirEntry);
  1191.   DirEntry^.DisplayText := NewStr(DisplayText);
  1192.   DirEntry^.Directory := NewStr(Directory);
  1193.   NewDirEntry := DirEntry;
  1194. end;
  1195.  
  1196. function GetCurDrive: Char; near; assembler;
  1197. asm
  1198.     MOV    AH,19H
  1199.         INT    21H
  1200.         ADD    AL,'A'
  1201. end;
  1202.  
  1203. begin
  1204.   Dir := ADir;
  1205.   AList := New(PDirCollection, Init(5,5));
  1206.   AList^.Insert(NewDirEntry(Drives^,Drives^));
  1207.   if Dir = Drives^ then
  1208.   begin
  1209.     isFirst := True;
  1210.     OldC := ' ';
  1211.     for C := 'A' to 'Z' do
  1212.     begin
  1213.       if (C < 'C') or DriveValid(C) then
  1214.       begin
  1215.         if OldC <> ' ' then
  1216.     begin
  1217.           if isFirst then
  1218.       begin
  1219.         S := FirstDir + OldC;
  1220.             isFirst := False;
  1221.           end
  1222.           else S := MiddleDir + OldC;
  1223.       AList^.Insert(NewDirEntry(S, OldC + ':\'));
  1224.         end;
  1225.         if C = GetCurDrive then Cur := AList^.Count;
  1226.         OldC := C;
  1227.       end;
  1228.     end;
  1229.     if OldC <> ' ' then AList^.Insert(NewDirEntry(LastDir + OldC, OldC + ':\'));
  1230.   end
  1231.   else
  1232.   begin
  1233.     Indent := IndentSize;
  1234.     NewDir := Dir;
  1235.     Dirct := Copy(NewDir,1,3);
  1236.     AList^.Insert(NewDirEntry(PathDir + Dirct, Dirct));
  1237.     NewDir := Copy(NewDir,4,255);
  1238.     while NewDir <> '' do
  1239.     begin
  1240.       I := Pos('\',NewDir);
  1241.       if I <> 0 then
  1242.       begin
  1243.         S := Copy(NewDir,1,I-1);
  1244.         Dirct := Dirct + S;
  1245.         AList^.Insert(NewDirEntry(Indent + PathDir + S, Dirct));
  1246.         NewDir := Copy(NewDir,I+1,255);
  1247.       end
  1248.       else
  1249.       begin
  1250.         Dirct := Dirct + NewDir;
  1251.         AList^.Insert(NewDirEntry(Indent + PathDir + NewDir, Dirct));
  1252.         NewDir := '';
  1253.       end;
  1254.       Indent := Indent + IndentSize;
  1255.       Dirct := Dirct + '\';
  1256.     end;
  1257.     Cur := AList^.Count-1;
  1258.     isFirst := True;
  1259.     NewDir := Dirct + '*.*';
  1260.     FindFirst(NewDir, Directory, SR);
  1261.     while DosError = 0 do
  1262.     begin
  1263.       if (SR.Attr and Directory <> 0) and (SR.Name[1] <> '.') then
  1264.       begin
  1265.         if isFirst then
  1266.     begin
  1267.       S := FirstDir;
  1268.       isFirst := False;
  1269.     end else S := MiddleDir;
  1270.         AList^.Insert(NewDirEntry(Indent + S + SR.Name, Dirct + SR.Name));
  1271.       end;
  1272.       FindNext(SR);
  1273.     end;
  1274.     P := PDirEntry(AList^.At(AList^.Count-1))^.DisplayText;
  1275.     I := Pos('└',P^);
  1276.     if I = 0 then
  1277.     begin
  1278.       I := Pos('├',P^);
  1279.       if I <> 0 then P^[I] := '└';
  1280.     end else
  1281.     begin
  1282.       P^[I+1] := '─';
  1283.       P^[I+2] := '─';
  1284.     end;
  1285.   end;
  1286.   NewList(AList);
  1287.   FocusItem(Cur);
  1288. end;
  1289.  
  1290. procedure TDirListBox.SetState(AState: Word; Enable: Boolean);
  1291. begin
  1292.   TListBox.SetState(AState, Enable);
  1293.   if AState and sfFocused <> 0 then
  1294.     PChDirDialog(Owner)^.ChDirButton^.MakeDefault(Enable);
  1295. end;
  1296.  
  1297. { TChDirDialog }
  1298.  
  1299. constructor TChDirDialog.Init(AOptions: Word; HistoryId: Word);
  1300. var
  1301.   R: TRect;
  1302.   Control: PView;
  1303.   CurDir: DirStr;
  1304. begin
  1305.   R.Assign(16, 2, 64, 20);
  1306.   TDialog.Init(R, 'Change Directory');
  1307.  
  1308.   Options := Options or ofCentered;
  1309.  
  1310.   R.Assign(3, 3, 30, 4);
  1311.   DirInput := New(PInputLine, Init(R, 68));
  1312.   Insert(DirInput);
  1313.   R.Assign(2, 2, 17, 3);
  1314.   Control := New(PLabel, Init(R, 'Directory ~n~ame', DirInput));
  1315.   Insert(Control);
  1316.   R.Assign(30, 3, 33, 4);
  1317.   Control := New(PHistory, Init(R, DirInput, HistoryId));
  1318.   Insert(Control);
  1319.  
  1320.   R.Assign(32, 6, 33, 16);
  1321.   Control := New(PScrollBar, Init(R));
  1322.   Insert(Control);
  1323.   R.Assign(3, 6, 32, 16);
  1324.   DirList := New(PDirListBox, Init(R, PScrollBar(Control)));
  1325.   Insert(DirList);
  1326.   R.Assign(2, 5, 17, 6);
  1327.   Control := New(PLabel, Init(R, 'Directory ~t~ree', DirList));
  1328.   Insert(Control);
  1329.  
  1330.   R.Assign(35, 6, 45, 8);
  1331.   OkButton := New(PButton, Init(R, 'O~K~', cmOK, bfDefault));
  1332.   Insert(OkButton);
  1333.   Inc(R.A.Y,3); Inc(R.B.Y,3);
  1334.   ChDirButton := New(PButton, Init(R, '~C~hdir', cmChangeDir, bfNormal));
  1335.   Insert(ChDirButton);
  1336.   Inc(R.A.Y,3); Inc(R.B.Y,3);
  1337.   Insert(New(PButton, Init(R, '~R~evert', cmRevert, bfNormal)));
  1338.   if AOptions and cdHelpButton <> 0 then
  1339.   begin
  1340.     Inc(R.A.Y,3); Inc(R.B.Y,3);
  1341.     Insert(New(PButton, Init(R, 'Help', cmHelp, bfNormal)));
  1342.   end;
  1343.  
  1344.   if AOptions and cdNoLoadDir = 0 then SetUpDialog;
  1345.  
  1346.   SelectNext(False);
  1347. end;
  1348.  
  1349. constructor TChDirDialog.Load(var S: TStream);
  1350. var
  1351.   CurDir: DirStr;
  1352. begin
  1353.   TDialog.Load(S);
  1354.   GetSubViewPtr(S, DirList);
  1355.   GetSubViewPtr(S, DirInput);
  1356.   GetSubViewPtr(S, OkButton);
  1357.   GetSubViewPtr(S, ChDirbutton);
  1358.   SetUpDialog;
  1359. end;
  1360.  
  1361. function TChDirDialog.DataSize: Word;
  1362. begin
  1363.   DataSize := 0;
  1364. end;
  1365.  
  1366. procedure TChDirDialog.GetData(var Rec);
  1367. begin
  1368. end;
  1369.  
  1370. procedure TChDirDialog.HandleEvent(var Event: TEvent);
  1371. var
  1372.   CurDir: DirStr;
  1373.   P: PDirEntry;
  1374. begin
  1375.   TDialog.HandleEvent(Event);
  1376.   case Event.What of
  1377.     evCommand:
  1378.       begin
  1379.         case Event.Command of
  1380.           cmRevert: GetDir(0,CurDir);
  1381.           cmChangeDir:
  1382.             begin
  1383.               P := DirList^.List^.At(DirList^.Focused);
  1384.               if (P^.Directory^ = Drives^) or DriveValid(P^.Directory^[1]) then
  1385.                 CurDir := P^.Directory^
  1386.               else Exit;
  1387.             end;
  1388.         else
  1389.           Exit;
  1390.         end;
  1391.         if (Length(CurDir) > 3) and (CurDir[Length(CurDir)] = '\') then
  1392.           CurDir := Copy(CurDir,1,Length(CurDir)-1);
  1393.         DirList^.NewDirectory(CurDir);
  1394.         DirInput^.Data^ := CurDir;
  1395.         DirInput^.DrawView;
  1396.         DirList^.Select;
  1397.         ClearEvent(Event);
  1398.       end;
  1399.   end;
  1400. end;
  1401.  
  1402. procedure TChDirDialog.SetData(var Rec);
  1403. begin
  1404. end;
  1405.  
  1406. procedure TChDirDialog.SetUpDialog;
  1407. var
  1408.   CurDir: DirStr;
  1409. begin
  1410.   if DirList <> nil then
  1411.   begin
  1412.     CurDir := GetCurDir;
  1413.     DirList^.NewDirectory(CurDir);
  1414.     if (Length(CurDir) > 3) and (CurDir[Length(CurDir)] = '\') then
  1415.       CurDir := Copy(CurDir,1,Length(CurDir)-1);
  1416.     if DirInput <> nil then
  1417.     begin
  1418.       DirInput^.Data^ := CurDir;
  1419.       DirInput^.DrawView;
  1420.     end;
  1421.   end;
  1422. end;
  1423.  
  1424. procedure TChDirDialog.Store(var S: TStream);
  1425. begin
  1426.   TDialog.Store(S);
  1427.   PutSubViewPtr(S, DirList);
  1428.   PutSubViewPtr(S, DirInput);
  1429.   PutSubViewPtr(S, OkButton);
  1430.   PutSubViewPtr(S, ChDirButton);
  1431. end;
  1432.  
  1433. function TChDirDialog.Valid(Command: Word): Boolean;
  1434. var
  1435.   P: PathStr;
  1436. begin
  1437.   Valid := True;
  1438.   if Command = cmOk then
  1439.   begin
  1440.     P := FExpand(DirInput^.Data^);
  1441.     if (Length(P) > 3) and (P[Length(P)] = '\') then Dec(P[0]);
  1442.     {$I-}
  1443.     ChDir(P);
  1444.     if IOResult <> 0 then
  1445.     begin
  1446.       MessageBox('Invalid directory.', nil, mfError + mfOkButton);
  1447.       Valid := False;
  1448.     end;
  1449.     {$I+}
  1450.   end;
  1451. end;
  1452.  
  1453. procedure RegisterStdDlg;
  1454. begin
  1455.   RegisterType(RFileInputLine);
  1456.   RegisterType(RFileCollection);
  1457.   RegisterType(RFileList);
  1458.   RegisterType(RFileInfoPane);
  1459.   RegisterType(RFileDialog);
  1460.   RegisterType(RDirCollection);
  1461.   RegisterType(RDirListBox);
  1462.   RegisterType(RSortedListBox);
  1463.   RegisterType(RChDirDialog);
  1464. end;
  1465.  
  1466. end.
  1467.