home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / vp21beta.zip / ATVSRC.RAR / STDDLG.PAS < prev    next >
Pascal/Delphi Source File  |  2000-08-15  |  38KB  |  1,455 lines

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