source: trunk/Components/CustomDirOutline.pas@ 91

Last change on this file since 91 was 15, checked in by RBRi, 19 years ago

+ components stuff

  • Property svn:eol-style set to native
File size: 10.5 KB
Line 
1Unit CustomDirOutline;
2
3// This is a small enhancement of the sample TDirectoryOutline
4// Changes are:
5// 1) Leaf/open/close bitmaps are used as inherited from TOutline
6// instead of being specially loaded. THerefore they can be changed
7// 2) Fix to Click method to make root directory selectable
8// 3) Added Reload method
9// 4) Does not change or use current directory
10// 5) Has ChangeToParent, AtRoot, and ChangeToRoot methods
11Interface
12
13Uses
14 SysUtils, Classes, Graphics, StdCtrls, Forms, Dialogs,
15 CustomOutline, Outline;
16
17type
18 TCustomDirOutline = Class( TCustomOutline )
19 Protected
20 FDirectory: String;
21 FDrive: Char;
22 FOnChange: TNotifyEvent;
23 FLookAhead: boolean;
24 Procedure SetDrive( NewDrive: Char );
25 Procedure SetDirectory( Const NewDir: String );
26 Procedure FillLevel( Node: TOutlineNode );
27 Procedure CheckForSomeDirs( Node: TOutlineNode );
28 Procedure BuildTree; Virtual;
29 Procedure WalkTree( Const Dir: String );
30 Procedure SetupShow; Override;
31 Procedure BuildOneLevel( ParentLevel: Longint ); Virtual;
32 Procedure Change; Virtual;
33 Public
34 Procedure Expand( Index: Longint ); Override;
35 Procedure SetupComponent; Override;
36 Destructor Destroy; Override;
37 Procedure ItemFocus( Index: longint ); Override;
38 Procedure Reload;
39 Public
40 Property Drive: Char read FDrive write SetDrive;
41 // Note unlike original TDirOutline, setting this property
42 // does *not* allow relative paths.
43 Property Directory: String read FDirectory write SetDirectory;
44
45 // Returns true if already at a root dir
46 Function AtRoot: boolean;
47 // Returns true if could be done
48 Function ChangeToParent: boolean;
49 Function Parent: string;
50 Procedure ChangeToRoot;
51
52 Property Lines;
53 published
54 // If this property is false, all dirs will have a + symbol
55 // until they are expanded
56 // If true, the control will look into each dir and see if there
57 // are any subdirs to correct show or hide the +
58 property LookAhead: boolean read FLookAhead write FLookAhead;
59 Property OnChange:TNotifyEvent read FOnChange write FOnChange;
60 End;
61
62Exports TCustomDirOutline, 'User', 'CustomDirOutline.bmp';
63
64Implementation
65
66// Returns true if already at a root dir
67Function TCustomDirOutline.AtRoot: boolean;
68Var
69 TestString: string;
70Begin
71 TestString:= Directory;
72 System.Delete( TestString, 1, 2 ); // remove x: off the start
73
74 Result:= ( TestString='' ) or ( TestString='\' );
75End;
76
77Function TCustomDirOutline.Parent: string;
78Var
79 i: longint;
80Begin
81 Result:= '';
82 if AtRoot then
83 exit;
84 Result:= Directory;
85 if Result[ length( Result ) ]='\' then
86 System.Delete( Result, length( Result ), 1 );
87 for i:= length( Result ) downto 2 do
88 begin
89 if Result[ i ]='\' then
90 begin
91 Result:= copy( Result, 1, i );
92 exit;
93 end;
94 end;
95End;
96
97// Returns true if could be done
98Function TCustomDirOutline.ChangeToParent: boolean;
99Begin
100 Result:= false;
101 if AtRoot then
102 exit;
103 Directory:= Parent;
104 Result:= true;
105End;
106
107Procedure TCustomDirOutline.ChangeToRoot;
108Begin
109 Directory:= copy( Directory, 1, 3 );
110End;
111
112Procedure TCustomDirOutline.Change;
113Begin
114 If FOnChange <> Nil Then
115 FOnChange( Self );
116End;
117
118// Looks at the path for the given node and adds one directory
119// if there is one.
120Procedure TCustomDirOutline.CheckForSomeDirs(Node:TOutlineNode);
121Var
122 Root:ShortString;
123 SearchRec: TSearchRec;
124 Status:Integer;
125Begin
126 Node.Clear;
127 Root:=Node.FullPath;
128 If Root[Length(Root)] In ['\','/'] Then dec(Root[0]);
129
130 Status:=FindFirst(Root+'\*.*',faDirectory,SearchRec);
131 While Status=0 Do
132 Begin
133 If SearchRec.Attr And faDirectory = faDirectory Then
134 Begin
135 If ((SearchRec.Name<>'.')And(SearchRec.Name<>'..')) Then //no .. and .
136 Begin
137 // Found a directory
138 // All we care about is adding one node if needed
139 AddChild(Node.Index,SearchRec.Name);
140 FindClose( SearchRec );
141 exit;
142 End;
143 End;
144 Status:=FindNext(SearchRec);
145 End;
146
147end;
148
149Procedure TCustomDirOutline.FillLevel(Node:TOutlineNode);
150Var
151 TempIndex:Longint;
152 Root:ShortString;
153 SearchRec: TSearchRec;
154 Status:Integer;
155 s,s1:String;
156Begin
157 // We always start from scratch. So it's up to date.
158 Node.Clear;
159 Root:=Node.FullPath;
160 If Root[Length(Root)] In ['\','/'] Then dec(Root[0]);
161
162 Status:=FindFirst(Root+'\*.*',faDirectory,SearchRec);
163 While Status=0 Do
164 Begin
165 If SearchRec.Attr And faDirectory = faDirectory Then
166 Begin
167 If ((SearchRec.Name<>'.')And(SearchRec.Name<>'..')) Then //no .. and .
168 Begin
169 If Node.HasItems Then //must sort
170 Begin
171 TempIndex:=Node.GetFirstChild;
172 s:=SearchRec.Name;
173 UpcaseStr(s);
174 If TempIndex<>-1 Then
175 Begin
176 s1:=Items[TempIndex].Text;
177 UpcaseStr(s1);
178 End;
179 While (TempIndex<>-1)And(s1<s) Do
180 Begin
181 TempIndex:=Node.GetNextChild(TempIndex);
182 If TempIndex<>-1 Then
183 Begin
184 s1:=Items[TempIndex].Text;
185 UpcaseStr(s1);
186 End;
187 End;
188 If TempIndex<>-1 Then Insert(TempIndex, SearchRec.Name)
189 Else Add(Node.GetLastChild, SearchRec.Name);
190 End
191 Else AddChild(Node.Index,SearchRec.Name);
192 End;
193 End;
194 Status:=FindNext(SearchRec);
195 End;
196
197end;
198
199
200Procedure TCustomDirOutline.BuildOneLevel(ParentLevel:Longint);
201Var Index:LongInt;
202 RootNode:TOutlineNode;
203 FList:TList;
204 t:longint;
205Begin
206 FillLevel(Items[ParentLevel]);
207
208 RootNode := Items[ParentLevel];
209 FList:= TList.Create;
210 Index:= RootNode.GetFirstChild;
211 While Index<>-1 Do
212 Begin
213 FList.Add(Items[Index]);
214 Index:=RootNode.GetNextChild(Index);
215 End;
216
217 // Depending on look ahead, either look for any directories at the
218 // next level to correctly set the +, or
219 // go and put dummy entries so the + will always show up
220 For t:=0 To FList.Count-1 Do
221 if FLookAhead then
222 CheckForSomeDirs(TOutlineNode(FList[t]))
223 else
224 AddChild( TOutlineNode( FList[t] ).Index, 'dummy');
225
226 FList.Destroy;
227End;
228
229Procedure TCustomDirOutline.SetupComponent;
230Begin
231 Inherited SetupComponent;
232 BorderStyle:= bsNone;
233 PlusMinusSize.CX:= 14;
234 PlusMinusSize.CY:= 14;
235 ShowPlusMinus:= False;
236 FLookAhead:= false;
237 Name:='DirectoryOutline';
238End;
239
240Destructor TCustomDirOutline.Destroy;
241Begin
242 Inherited Destroy;
243End;
244
245Procedure TCustomDirOutline.ItemFocus( Index: longint );
246Begin
247 inherited Click;
248 Try
249 If SelectedItem=-1 Then
250 Beep(1200,400);
251 if SelectedItem=1 then
252 // Selecting root dir... FullPath will not be quite enough...
253 Directory:=FDrive+':\'
254 else
255 Directory :=Items[SelectedItem].FullPath;
256 Except
257 End;
258End;
259
260Procedure TCustomDirOutline.SetDrive(NewDrive:Char);
261Begin
262 FDrive:=Upcase(NewDrive);
263 FDirectory:=FDrive+':\';
264 If Not (csLoading In ComponentState) Then
265 BuildTree;
266End;
267
268Procedure TCustomDirOutline.SetDirectory(Const NewDir:String);
269Var
270 TempPath: ShortString;
271 Node:TOutlineNode;
272
273 Function FindNode(Node:TOutlineNode):TOutlineNode;
274 Var s:String;
275 t:LongInt;
276 Node1:TOutlineNode;
277 Begin
278 s:=Node.FullPath;
279 UpcaseStr(s);
280 If s=TempPath Then
281 Begin
282 result:=Node;
283 exit;
284 End;
285
286 For t:=0 To Node.ItemCount-1 Do
287 Begin
288 Node1:=Node.Items[t];
289 Node1:=FindNode(Node1);
290 If Node1<>Nil Then
291 Begin
292 Result:=Node1;
293 exit;
294 End;
295 End;
296 Result:=Nil;
297 End;
298
299Begin
300 If NewDir = '' Then
301 exit;
302
303 If NewDir[ Length( NewDir ) ] In ['\','/'] Then
304 Dec( NewDir[ 0 ] ) ;
305
306 TempPath := NewDir;
307
308 FDirectory:= TempPath;
309 If FDirectory[ 1 ] <> Drive Then
310 Drive:= FDirectory[ 1 ]
311 Else
312 Begin
313 WalkTree( TempPath );
314 Change;
315 End;
316
317 TempPath:= FDirectory;
318 UpcaseStr( TempPath );
319 Node:= FindNode( Items[ 1 ] ); // start at drive
320 If Node <> Nil Then
321 If SelectedNode <> Node Then
322 SetAndShowSelectedItem( Node.Index );
323
324// WARNING! Because when an item is expanded, all it's subnodes will be
325// deleted and recreated (re-reading the directory) it is very important
326// that node of the parent nodes not be expanded while setting the selected node
327
328// This code works because WalkTree has already expanded all the nodes
329// (except the one we actually want to select, and expanding that one
330// does not matter). And I changed TCustomOutline.SetAndShowSelectedItem
331// to not expand if already expanded.
332
333End;
334
335Procedure TCustomDirOutline.SetupShow;
336Var CurDir:String;
337Begin
338 Inherited SetupShow;
339
340 If FDrive=#0 Then //test if unassigned
341 Begin
342 {$I-}
343 GetDir(0, CurDir);
344 {$I+}
345 If IoResult<>0 Then exit;
346 FDrive := Upcase(CurDir[1]);
347 FDirectory := CurDir;
348 End;
349
350 BuildTree;
351End;
352
353Procedure TCustomDirOutline.BuildTree;
354Var
355 RootIndex: Longint;
356Begin
357 Clear;
358 If FDrive=#0 Then
359 exit;
360 RootIndex:= Add( 0, Drive+':' );
361 WalkTree( FDirectory );
362 Change;
363End;
364
365Procedure TCustomDirOutline.WalkTree(Const Dir:String);
366Var
367 b:LongInt;
368 CurPath,NextDir,s:ShortString;
369 TempItem,TempIndex: Longint;
370begin
371 TempItem := 1; { start at root }
372
373 CurPath := Dir;
374 b:=Pos(':',CurPath);
375 If b>0 then
376 CurPath:=Copy(CurPath,b+1,255);
377 If CurPath<>'' Then
378 If CurPath[1]='\' Then
379 System.Delete(CurPath,1,1);
380
381 NextDir := CurPath;
382 Repeat
383 b:=Pos('\',CurPath);
384 If b=0 Then
385 b:=Pos('/',CurPath);
386 If b > 0 then
387 Begin
388 NextDir:=Copy(CurPath,1,b-1);
389 CurPath:=Copy(CurPath,b+1,255);
390 End
391 Else
392 Begin
393 NextDir:=CurPath;
394 CurPath:='';
395 End;
396
397 // Expands this dir, forcing it's subdirs to be read
398 Items[TempItem].Expanded:=True;
399
400 TempIndex:=Items[TempItem].GetFirstChild;
401 UpcaseStr(NextDir);
402 If CurPath='' Then
403 TempIndex:=-1
404 Else While TempIndex<>-1 Do
405 Begin
406 s:=Items[TempIndex].Text;
407 UpcaseStr(s);
408 If s=NextDir Then Break;
409 TempIndex:=Items[TempItem].GetNextChild(TempIndex);
410 End;
411 If TempIndex<>-1 Then
412 TempItem:=TempIndex
413 Else
414 CurPath:=''; //break
415 Until CurPath='';
416End;
417
418Procedure TCustomDirOutline.Expand(Index:Longint);
419Begin
420 BuildOneLevel(Index);
421 Inherited Expand(Index);
422End;
423
424Procedure TCustomDirOutline.Reload;
425Var
426 OldDir: string;
427Begin
428 OldDir:= Directory;
429 BuildTree;
430 Directory:= OldDir;
431End;
432
433initialization
434 RegisterClasses( [ TCustomDirOutline ] );
435
436end.
Note: See TracBrowser for help on using the repository browser.