home *** CD-ROM | disk | FTP | other *** search
/ PC Format Collection 48 / SENT14D.ISO / tech / delphi / disk15 / sampsrc.pak / DIROUTLN.PAS < prev    next >
Encoding:
Pascal/Delphi Source File  |  1995-08-24  |  9.4 KB  |  344 lines

  1. unit DirOutln;
  2.  
  3. { Directory outline component }
  4.  
  5. interface
  6.  
  7. uses Classes, Forms, Controls, Outline, SysUtils, Graphics, Grids, StdCtrls,
  8.      Menus;
  9.  
  10. type
  11.   TTextCase = (tcLowerCase, tcUpperCase, tcAsIs);
  12.   TCaseFunction = function(const AString: string): string;
  13.  
  14.   TDirectoryOutline = class(TCustomOutline)
  15.   private
  16.     FDrive: Char;
  17.     FDirectory: TFileName;
  18.     FOnChange: TNotifyEvent;
  19.     FTextCase: TTextCase;
  20.     FCaseFunction: TCaseFunction;
  21.   protected
  22.     procedure SetDrive(NewDrive: Char);
  23.     procedure SetDirectory(const NewDirectory: TFileName);
  24.     procedure SetTextCase(NewTextCase: TTextCase);
  25.     procedure AssignCaseProc;
  26.     procedure BuildOneLevel(RootItem: Longint); virtual;
  27.     procedure BuildTree; virtual;
  28.     procedure BuildSubTree(RootItem: Longint); virtual;
  29.     procedure Change; virtual;
  30.     procedure Click; override;
  31.     procedure CreateWnd; override;
  32.     procedure Expand(Index: Longint); override;
  33.     procedure Loaded; override;
  34.     procedure WalkTree(const Dest: string);
  35.   public
  36.     constructor Create(AOwner: TComponent); override;
  37.     function ForceCase(const AString: string): string;
  38.     property Drive: Char  read FDrive write SetDrive;
  39.     property Directory: TFileName  read FDirectory write SetDirectory;
  40.     property Lines stored False;
  41.   published
  42.     property Align;
  43.     property BorderStyle;
  44.     property Color;
  45.     property Ctl3D;
  46.     property DragCursor;
  47.     property DragMode;
  48.     property Enabled;
  49.     property Font;
  50.     property ItemHeight;
  51.     property OnChange: TNotifyEvent  read FOnChange write FOnChange;
  52.     property OnClick;
  53.     property OnCollapse;
  54.     property OnDblClick;
  55.     property OnDragDrop;
  56.     property OnDragOver;
  57.     property OnDrawItem;
  58.     property OnEndDrag;
  59.     property OnEnter;
  60.     property OnExit;
  61.     property OnExpand;
  62.     property OnKeyDown;
  63.     property OnKeyPress;
  64.     property OnKeyUp;
  65.     property OnMouseDown;
  66.     property OnMouseMove;
  67.     property OnMouseUp;
  68.     property Options default [ooStretchBitmaps, ooDrawFocusRect];
  69.     property ParentColor;
  70.     property ParentCtl3D;
  71.     property ParentFont;
  72.     property ParentShowHint;
  73.     property PictureClosed;
  74.     property PictureLeaf;
  75.     property PictureOpen;
  76.     property PopupMenu;
  77.     property ScrollBars;
  78.     property Style;
  79.     property ShowHint;
  80.     property TabOrder;
  81.     property TabStop;
  82.     property TextCase: TTextCase  read FTextCase write SetTextCase default tcLowerCase;
  83.     property Visible;
  84.   end;
  85.  
  86. function SameLetter(Letter1, Letter2: Char): Boolean;
  87.  
  88. procedure Register;
  89.  
  90. implementation
  91.  
  92. const
  93.   InvalidIndex = -1;
  94.  
  95. procedure Register;
  96. begin
  97.   RegisterComponents('Samples', [TDirectoryOutline]);
  98. end;
  99.  
  100. constructor TDirectoryOutline.Create(AOwner: TComponent);
  101. begin
  102.   inherited Create(AOwner);
  103.   PictureLeaf := PictureClosed;
  104.   Options := [ooStretchBitmaps, ooDrawFocusRect];
  105.   TextCase := tcLowerCase;
  106.   AssignCaseProc;
  107. end;
  108.  
  109. procedure TDirectoryOutline.AssignCaseProc;
  110. begin
  111.   case TextCase of
  112.     tcLowerCase: FCaseFunction := AnsiLowerCase;
  113.     tcUpperCase: FCaseFunction := AnsiUpperCase;
  114.     else FCaseFunction := nil;
  115.   end;
  116. end;
  117.  
  118. type
  119.   PNodeInfo = ^TNodeInfo;
  120.   TNodeInfo = record
  121.     RootName: TFileName;
  122.     SearchRec: TSearchRec;
  123.     DosError: Integer;
  124.     RootNode: TOutlineNode;
  125.     TempChild, NewChild: Longint;
  126.   end;
  127.  
  128. procedure TDirectoryOutline.BuildOneLevel(RootItem: Longint);
  129. var
  130.   NodeInfo: PNodeInfo;
  131. begin
  132.   New(NodeInfo);
  133.   try
  134.     with NodeInfo^ do
  135.     begin
  136.       RootName := Items[RootItem].FullPath;
  137.       if RootName[Length(RootName)] <> '\' then
  138.         RootName := Concat(RootName, '\');
  139.       RootName := Concat(RootName, '*.*');
  140.       DosError := FindFirst(RootName, faDirectory, SearchRec);
  141.       while DosError = 0 do
  142.       begin
  143.         if (SearchRec.Attr and faDirectory = faDirectory) and (SearchRec.Name[1] <> '.') then
  144.         begin
  145.           SearchRec.Name := ForceCase(SearchRec.Name);
  146.           RootNode := Items[RootItem];
  147.           if RootNode.HasItems then { if has children, must alphabetize }
  148.           begin
  149.             TempChild := RootNode.GetFirstChild;
  150.             while (TempChild <> InvalidIndex) and (Items[TempChild].Text < SearchRec.Name) do
  151.               TempChild := RootNode.GetNextChild(TempChild);
  152.             if TempChild <> InvalidIndex then
  153.               NewChild := Insert(TempChild, SearchRec.Name)
  154.             else NewChild := Add(RootNode.GetLastChild, SearchRec.Name);
  155.           end
  156.           else NewChild := AddChild(RootItem, SearchRec.Name); { if first child, just add }
  157.         end;
  158.         DosError := FindNext(SearchRec);
  159.       end;
  160.     end;
  161.     Items[RootItem].Data := Pointer(1); { make non-nil so we know we've been here }
  162.   finally
  163.     Dispose(NodeInfo);
  164.   end;
  165. end;
  166.  
  167. procedure TDirectoryOutline.BuildTree;
  168. var
  169.   RootNode: Longint;
  170. begin
  171.   Clear;
  172.   RootNode := AddChild(0, ForceCase(Drive + ':'));
  173.   WalkTree(FDirectory);
  174.   Change;
  175. end;
  176.  
  177. procedure TDirectoryOutline.BuildSubTree(RootItem: Longint);
  178. var
  179.   TempRoot: Longint;
  180.   RootNode: TOutlineNode;
  181. begin
  182.   BuildOneLevel(RootItem);
  183.   RootNode := Items[RootItem];
  184.   TempRoot := RootNode.GetFirstChild;
  185.   while TempRoot <> InvalidIndex do
  186.   begin
  187.     BuildSubTree(TempRoot);
  188.     TempRoot := RootNode.GetNextChild(TempRoot);
  189.   end;
  190. end;
  191.  
  192. procedure TDirectoryOutline.Change;
  193. begin
  194.   if Assigned(FOnChange) then FOnChange(Self);
  195. end;
  196.  
  197. procedure TDirectoryOutline.Click;
  198. begin
  199.   inherited Click;
  200.   Directory := Items[SelectedItem].FullPath;
  201. end;
  202.  
  203. procedure TDirectoryOutline.CreateWnd;
  204. var
  205.   CurrentPath: TFileName;
  206. begin
  207.   inherited CreateWnd;
  208.   if FDrive = #0 then
  209.   begin
  210.     GetDir(0, CurrentPath);
  211.     FDrive := ForceCase(CurrentPath)[1];
  212.     FDirectory := ForceCase(CurrentPath);
  213.   end;
  214.   if not (csLoading in ComponentState) then BuildTree;
  215. end;
  216.  
  217. procedure TDirectoryOutline.Expand(Index: Longint);
  218. begin
  219.   if Items[Index].Data = nil then { if we've not previously expanded }
  220.     BuildOneLevel(Index);
  221.   inherited Expand(Index); { call the event handler }
  222. end;
  223.  
  224. function TDirectoryOutline.ForceCase(const AString: string): string;
  225. begin
  226.   if Assigned(FCaseFunction) then
  227.     Result := FCaseFunction(AString)
  228.   else Result := AString;
  229. end;
  230.  
  231. procedure TDirectoryOutline.Loaded;
  232. begin
  233.   inherited Loaded;
  234.   AssignCaseProc;
  235.   BuildTree;
  236. end;
  237.  
  238. procedure TDirectoryOutline.SetDirectory(const NewDirectory: TFileName);
  239. var
  240.   TempPath: TFileName;
  241. begin
  242.   if Length(NewDirectory) > 0 then  { ignore empty directory }
  243.   begin
  244.     TempPath := ForceCase(ExpandFileName(NewDirectory)); { expand to full path }
  245.     if (Length(TempPath) > 3) and (TempPath[Length(TempPath)] = '\') then
  246.       TempPath[0] := Char(Length(TempPath) - 1);  {remove trailing backslash}
  247.     if CompareStr(TempPath, FDirectory) <> 0 then { is it a dir change? }
  248.     begin
  249.       FDirectory := TempPath; { set new directory }
  250.       ChDir(FDirectory); { go there }
  251.       if TempPath[1] <> Drive then { check to see if we changed drives, too }
  252.         Drive := TempPath[1] { change drive/build list if needed }
  253.       else
  254.       begin
  255.         WalkTree(TempPath);
  256.         Change; { otherwise, we're done }
  257.       end;
  258.     end;
  259.   end;
  260. end;
  261.  
  262. procedure TDirectoryOutline.SetDrive(NewDrive: Char);
  263. begin
  264.   if UpCase(NewDrive) in ['A'..'Z'] then { disallow all but drive letters}
  265.   begin
  266.     if (FDrive = #0) or not SameLetter(NewDrive, FDrive) then { update if no current drive or change }
  267.     begin
  268.       FDrive := NewDrive;
  269.       ChDir(FDrive + ':');
  270.       GetDir(0, FDirectory); { always returns uppercase...yuck! }
  271.       FDirectory := ForceCase(FDirectory); { use correct case }
  272.       if not (csLoading in ComponentState) then BuildTree; { this ends up calling Change }
  273.     end;
  274.   end;
  275. end;
  276.  
  277. procedure TDirectoryOutline.SetTextCase(NewTextCase: TTextCase);
  278. var
  279.   CurrentPath: TFileName;
  280. begin
  281.   if NewTextCase <> FTextCase then
  282.   begin
  283.     FTextCase := NewTextCase;
  284.     AssignCaseProc;
  285.     if NewTextCase = tcAsIs then
  286.     begin
  287.       GetDir(0, CurrentPath);
  288.       FDrive := CurrentPath[1];
  289.       FDirectory := CurrentPath;
  290.     end;
  291.     if not (csLoading in ComponentState) then BuildTree;
  292.   end;
  293. end;
  294.  
  295. procedure TDirectoryOutline.WalkTree(const Dest: string);
  296. var
  297.   TempPath, NextDir: TFileName;
  298.   SlashPos: Integer;
  299.   TempItem: Longint;
  300.  
  301.   function GetChildNamed(const Name: string): Longint;
  302.   begin
  303.     Items[TempItem].Expanded := True;
  304.     Result := Items[TempItem].GetFirstChild;
  305.     while Result <> InvalidIndex do
  306.     begin
  307.       if Items[Result].Text = Name then Exit;
  308.       Result := Items[TempItem].GetNextChild(Result);
  309.     end;
  310.   end;
  311.  
  312. begin
  313.   TempItem := 1; { start at root }
  314.   TempPath := ForceCase(Dest);
  315.   if Pos(':', TempPath) > 0 then
  316.     TempPath := Copy(TempPath, Pos(':', TempPath) + 1, Length(TempPath));
  317.   if TempPath[1] = '\' then System.Delete(TempPath, 1, 1);
  318.   SlashPos := Pos('\', TempPath);
  319.   NextDir := TempPath;
  320.   while Length(TempPath) > 0 do
  321.   begin
  322.     SlashPos := Pos('\', TempPath);
  323.     if SlashPos > 0 then
  324.     begin
  325.       NextDir := Copy(TempPath, 1, SlashPos - 1);
  326.       TempPath := Copy(TempPath, SlashPos + 1, Length(TempPath));
  327.     end
  328.     else
  329.     begin
  330.       NextDir := TempPath;
  331.       TempPath := '';
  332.     end;
  333.     TempItem := GetChildNamed(NextDir);
  334.   end;
  335.   SelectedItem := TempItem;
  336. end;
  337.  
  338. function SameLetter(Letter1, Letter2: Char): Boolean;
  339. begin
  340.   Result := UpCase(Letter1) = UpCase(Letter2);
  341. end;
  342.  
  343. end.
  344.