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