move network tools
[reactos.git] / reactos / subsys / system / explorer / shell / entries.cpp
1 /*
2 * Copyright 2003, 2004, 2005 Martin Fuchs
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 */
18
19
20 //
21 // Explorer clone
22 //
23 // entries.cpp
24 //
25 // Martin Fuchs, 23.07.2003
26 //
27
28
29 #include <precomp.h>
30
31 //#include "entries.h"
32
33
34 // allocate and initialise a directory entry
35 Entry::Entry(ENTRY_TYPE etype)
36 : _etype(etype)
37 {
38 _up = NULL;
39 _next = NULL;
40 _down = NULL;
41 _expanded = false;
42 _scanned = false;
43 _bhfi_valid = false;
44 _level = 0;
45 _icon_id = ICID_UNKNOWN;
46 _display_name = _data.cFileName;
47 _type_name = NULL;
48 _content = NULL;
49 }
50
51 Entry::Entry(Entry* parent, ENTRY_TYPE etype)
52 : _up(parent),
53 _etype(etype)
54 {
55 _next = NULL;
56 _down = NULL;
57 _expanded = false;
58 _scanned = false;
59 _bhfi_valid = false;
60 _level = 0;
61 _icon_id = ICID_UNKNOWN;
62 _shell_attribs = 0;
63 _display_name = _data.cFileName;
64 _type_name = NULL;
65 _content = NULL;
66 }
67
68 Entry::Entry(const Entry& other)
69 {
70 _next = NULL;
71 _down = NULL;
72 _up = NULL;
73
74 assert(!other._next);
75 assert(!other._down);
76 assert(!other._up);
77
78 _expanded = other._expanded;
79 _scanned = other._scanned;
80 _level = other._level;
81
82 _data = other._data;
83
84 _shell_attribs = other._shell_attribs;
85 _display_name = other._display_name==other._data.cFileName? _data.cFileName: _tcsdup(other._display_name);
86 _type_name = other._type_name? _tcsdup(other._type_name): NULL;
87 _content = other._content? _tcsdup(other._content): NULL;
88
89 _etype = other._etype;
90 _icon_id = other._icon_id;
91
92 _bhfi = other._bhfi;
93 _bhfi_valid = other._bhfi_valid;
94 }
95
96 // free a directory entry
97 Entry::~Entry()
98 {
99 if (_icon_id > ICID_NONE)
100 g_Globals._icon_cache.free_icon(_icon_id);
101
102 if (_display_name != _data.cFileName)
103 free(_display_name);
104
105 if (_type_name)
106 free(_type_name);
107
108 if (_content)
109 free(_content);
110 }
111
112
113 // read directory tree and expand to the given location
114 Entry* Entry::read_tree(const void* path, SORT_ORDER sortOrder, int scan_flags)
115 {
116 CONTEXT("Entry::read_tree()");
117
118 WaitCursor wait;
119
120 Entry* entry = this;
121
122 for(const void*p=path; p && entry; ) {
123 entry->smart_scan(sortOrder, scan_flags);
124
125 if (entry->_down)
126 entry->_expanded = true;
127
128 Entry* found = entry->find_entry(p);
129 p = entry->get_next_path_component(p);
130
131 entry = found;
132 }
133
134 return entry;
135 }
136
137
138 void Entry::read_directory_base(SORT_ORDER sortOrder, int scan_flags)
139 {
140 CONTEXT("Entry::read_directory_base()");
141
142 // call into subclass
143 read_directory(scan_flags);
144
145 #ifndef ROSSHELL
146 if (g_Globals._prescan_nodes) { //@todo _prescan_nodes should not be used for reading the start menu.
147 for(Entry*entry=_down; entry; entry=entry->_next)
148 if (entry->_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
149 entry->read_directory(scan_flags);
150 entry->sort_directory(sortOrder);
151 }
152 }
153 #endif
154
155 sort_directory(sortOrder);
156 }
157
158
159 Root::Root()
160 {
161 memset(this, 0, sizeof(Root));
162 }
163
164 Root::~Root()
165 {
166 if (_entry) {
167 _entry->free_subentries();
168 delete _entry;
169 }
170 }
171
172
173 // sort order for different directory/file types
174 enum TYPE_ORDER {
175 TO_DIR,
176 TO_DOT,
177 TO_DOTDOT,
178 TO_OTHER_DIR,
179 TO_VIRTUAL_FOLDER,
180 TO_FILE
181 };
182
183 // distinguish between ".", ".." and any other directory names
184 static TYPE_ORDER TypeOrderFromDirname(LPCTSTR name)
185 {
186 if (name[0] == '.') {
187 if (name[1] == '\0')
188 return TO_DOT; // "."
189
190 if (name[1]=='.' && name[2]=='\0')
191 return TO_DOTDOT; // ".."
192 }
193
194 return TO_OTHER_DIR; // any other directory
195 }
196
197 // directories first...
198 static int compareType(const Entry* entry1, const Entry* entry2)
199 {
200 const WIN32_FIND_DATA* fd1 = &entry1->_data;
201 const WIN32_FIND_DATA* fd2 = &entry2->_data;
202
203 TYPE_ORDER order1 = fd1->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY? TO_DIR: TO_FILE;
204 TYPE_ORDER order2 = fd2->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY? TO_DIR: TO_FILE;
205
206 // Handle "." and ".." as special case and move them at the very first beginning.
207 if (order1==TO_DIR && order2==TO_DIR) {
208 order1 = TypeOrderFromDirname(fd1->cFileName);
209 order2 = TypeOrderFromDirname(fd2->cFileName);
210
211 // Move virtual folders after physical folders
212 if (!(entry1->_shell_attribs & SFGAO_FILESYSTEM))
213 order1 = TO_VIRTUAL_FOLDER;
214
215 if (!(entry2->_shell_attribs & SFGAO_FILESYSTEM))
216 order2 = TO_VIRTUAL_FOLDER;
217 }
218
219 return order2==order1? 0: order1<order2? -1: 1;
220 }
221
222
223 static int compareNothing(const void* arg1, const void* arg2)
224 {
225 return -1;
226 }
227
228 static int compareName(const void* arg1, const void* arg2)
229 {
230 const Entry* entry1 = *(const Entry**)arg1;
231 const Entry* entry2 = *(const Entry**)arg2;
232
233 int cmp = compareType(entry1, entry2);
234 if (cmp)
235 return cmp;
236
237 return lstrcmpi(entry1->_data.cFileName, entry2->_data.cFileName);
238 }
239
240 static int compareExt(const void* arg1, const void* arg2)
241 {
242 const Entry* entry1 = *(const Entry**)arg1;
243 const Entry* entry2 = *(const Entry**)arg2;
244 const TCHAR *name1, *name2, *ext1, *ext2;
245
246 int cmp = compareType(entry1, entry2);
247 if (cmp)
248 return cmp;
249
250 name1 = entry1->_data.cFileName;
251 name2 = entry2->_data.cFileName;
252
253 ext1 = _tcsrchr(name1, TEXT('.'));
254 ext2 = _tcsrchr(name2, TEXT('.'));
255
256 if (ext1)
257 ++ext1;
258 else
259 ext1 = TEXT("");
260
261 if (ext2)
262 ++ext2;
263 else
264 ext2 = TEXT("");
265
266 cmp = lstrcmpi(ext1, ext2);
267 if (cmp)
268 return cmp;
269
270 return lstrcmpi(name1, name2);
271 }
272
273 static int compareSize(const void* arg1, const void* arg2)
274 {
275 const Entry* entry1 = *(const Entry**)arg1;
276 const Entry* entry2 = *(const Entry**)arg2;
277
278 int cmp = compareType(entry1, entry2);
279 if (cmp)
280 return cmp;
281
282 cmp = entry2->_data.nFileSizeHigh - entry1->_data.nFileSizeHigh;
283
284 if (cmp < 0)
285 return -1;
286 else if (cmp > 0)
287 return 1;
288
289 cmp = entry2->_data.nFileSizeLow - entry1->_data.nFileSizeLow;
290
291 return cmp<0? -1: cmp>0? 1: 0;
292 }
293
294 static int compareDate(const void* arg1, const void* arg2)
295 {
296 const Entry* entry1 = *(const Entry**)arg1;
297 const Entry* entry2 = *(const Entry**)arg2;
298
299 int cmp = compareType(entry1, entry2);
300 if (cmp)
301 return cmp;
302
303 return CompareFileTime(&entry2->_data.ftLastWriteTime, &entry1->_data.ftLastWriteTime);
304 }
305
306
307 static int (*sortFunctions[])(const void* arg1, const void* arg2) = {
308 compareNothing, // SORT_NONE
309 compareName, // SORT_NAME
310 compareExt, // SORT_EXT
311 compareSize, // SORT_SIZE
312 compareDate // SORT_DATE
313 };
314
315
316 void Entry::sort_directory(SORT_ORDER sortOrder)
317 {
318 if (sortOrder != SORT_NONE) {
319 Entry* entry = _down;
320 Entry** array, **p;
321 int len;
322
323 len = 0;
324 for(entry=_down; entry; entry=entry->_next)
325 ++len;
326
327 if (len) {
328 array = (Entry**) alloca(len*sizeof(Entry*));
329
330 p = array;
331 for(entry=_down; entry; entry=entry->_next)
332 *p++ = entry;
333
334 // call qsort with the appropriate compare function
335 qsort(array, len, sizeof(array[0]), sortFunctions[sortOrder]);
336
337 _down = array[0];
338
339 for(p=array; --len; p++)
340 (*p)->_next = p[1];
341
342 (*p)->_next = 0;
343 }
344 }
345 }
346
347
348 void Entry::smart_scan(SORT_ORDER sortOrder, int scan_flags)
349 {
350 CONTEXT("Entry::smart_scan()");
351
352 if (!_scanned) {
353 free_subentries();
354 read_directory_base(sortOrder, scan_flags); ///@todo We could use IShellFolder2::GetDefaultColumn to determine sort order.
355 }
356 }
357
358
359
360 int Entry::extract_icon(ICONCACHE_FLAGS flags)
361 {
362 TCHAR path[MAX_PATH];
363
364 ICON_ID icon_id = ICID_NONE;
365
366 if (_etype!=ET_SHELL && get_path(path, COUNTOF(path))) // not for ET_SHELL to display the correct desktop icon
367 if (!(flags & ICF_MIDDLE)) // not for ICF_MIDDLE to extract 24x24 icons because SHGetFileInfo() doesn't support this icon size
368 icon_id = g_Globals._icon_cache.extract(path, flags);
369
370 if (icon_id == ICID_NONE) {
371 if (!(flags & ICF_OVERLAYS)) {
372 IExtractIcon* pExtract;
373 if (SUCCEEDED(GetUIObjectOf(0, IID_IExtractIcon, (LPVOID*)&pExtract))) {
374 unsigned gil_flags;
375 int idx;
376
377 if (flags & ICF_OPEN)
378 gil_flags |= GIL_OPENICON;
379
380 if (SUCCEEDED(pExtract->GetIconLocation(GIL_FORSHELL, path, COUNTOF(path), &idx, &gil_flags))) {
381 if (gil_flags & GIL_NOTFILENAME)
382 icon_id = g_Globals._icon_cache.extract(pExtract, path, idx, flags);
383 else {
384 if (idx == -1)
385 idx = 0; // special case for some control panel applications ("System")
386
387 icon_id = g_Globals._icon_cache.extract(path, idx, flags);
388 }
389
390 /* using create_absolute_pidl() [see below] results in more correct icons for some control panel applets (NVidia display driver).
391 if (icon_id == ICID_NONE) {
392 SHFILEINFO sfi;
393
394 if (SHGetFileInfo(path, 0, &sfi, sizeof(sfi), SHGFI_ICON|SHGFI_SMALLICON))
395 icon_id = g_Globals._icon_cache.add(sfi.hIcon)._id;
396 } */
397 /*
398 if (icon_id == ICID_NONE) {
399 LPBYTE b = (LPBYTE) alloca(0x10000);
400 SHFILEINFO sfi;
401
402 FILE* file = fopen(path, "rb");
403 if (file) {
404 int l = fread(b, 1, 0x10000, file);
405 fclose(file);
406
407 if (l)
408 icon_id = g_Globals._icon_cache.add(CreateIconFromResourceEx(b, l, TRUE, 0x00030000, 16, 16, LR_DEFAULTCOLOR));
409 }
410 } */
411 }
412 }
413 }
414
415 if (icon_id == ICID_NONE) {
416 SHFILEINFO sfi;
417
418 const ShellPath& pidl_abs = create_absolute_pidl();
419 LPCITEMIDLIST pidl = pidl_abs;
420
421 int shgfi_flags = SHGFI_PIDL;
422
423 if (!(flags & (ICF_LARGE|ICF_MIDDLE)))
424 shgfi_flags |= SHGFI_SMALLICON;
425
426 if (flags & ICF_OPEN)
427 shgfi_flags |= SHGFI_OPENICON;
428
429 if (flags & ICF_SYSCACHE) {
430 assert(!(flags&ICF_OVERLAYS));
431
432 HIMAGELIST himlSys = (HIMAGELIST) SHGetFileInfo((LPCTSTR)pidl, 0, &sfi, sizeof(sfi), SHGFI_SYSICONINDEX|shgfi_flags);
433 if (himlSys)
434 icon_id = g_Globals._icon_cache.add(sfi.iIcon);
435 } else {
436 if (flags & ICF_OVERLAYS)
437 shgfi_flags |= SHGFI_ADDOVERLAYS;
438
439 if (SHGetFileInfo((LPCTSTR)pidl, 0, &sfi, sizeof(sfi), SHGFI_ICON|shgfi_flags))
440 icon_id = g_Globals._icon_cache.add(sfi.hIcon);
441 }
442 }
443 }
444
445 return icon_id;
446 }
447
448 int Entry::safe_extract_icon(ICONCACHE_FLAGS flags)
449 {
450 try {
451 return extract_icon(flags);
452 } catch(COMException&) {
453 // ignore unexpected exceptions while extracting icons
454 }
455
456 return ICID_NONE;
457 }
458
459
460 BOOL Entry::launch_entry(HWND hwnd, UINT nCmdShow)
461 {
462 TCHAR cmd[MAX_PATH];
463
464 if (!get_path(cmd, COUNTOF(cmd)))
465 return FALSE;
466
467 // add path to the recent file list
468 SHAddToRecentDocs(SHARD_PATH, cmd);
469
470 // start program, open document...
471 return launch_file(hwnd, cmd, nCmdShow);
472 }
473
474
475 // local replacement implementation for SHBindToParent()
476 // (derived from http://www.geocities.com/SiliconValley/2060/articles/shell-helpers.html)
477 static HRESULT my_SHBindToParent(LPCITEMIDLIST pidl, REFIID riid, VOID** ppv, LPCITEMIDLIST* ppidlLast)
478 {
479 HRESULT hr;
480
481 if (!ppv)
482 return E_POINTER;
483
484 // There must be at least one item ID.
485 if (!pidl || !pidl->mkid.cb)
486 return E_INVALIDARG;
487
488 // Get the desktop folder as root.
489 ShellFolder desktop;
490 /* IShellFolderPtr desktop;
491 hr = SHGetDesktopFolder(&desktop);
492 if (FAILED(hr))
493 return hr; */
494
495 // Walk to the penultimate item ID.
496 LPCITEMIDLIST marker = pidl;
497 for (;;)
498 {
499 LPCITEMIDLIST next = reinterpret_cast<LPCITEMIDLIST>(
500 marker->mkid.abID - sizeof(marker->mkid.cb) + marker->mkid.cb);
501 if (!next->mkid.cb)
502 break;
503 marker = next;
504 }
505
506 if (marker == pidl)
507 {
508 // There was only a single item ID, so bind to the root folder.
509 hr = desktop->QueryInterface(riid, ppv);
510 }
511 else
512 {
513 // Copy the ID list, truncating the last item.
514 int length = marker->mkid.abID - pidl->mkid.abID;
515 if (LPITEMIDLIST parent_id = reinterpret_cast<LPITEMIDLIST>(
516 malloc(length + sizeof(pidl->mkid.cb))))
517 {
518 LPBYTE raw_data = reinterpret_cast<LPBYTE>(parent_id);
519 memcpy(raw_data, pidl, length);
520 memset(raw_data + length, 0, sizeof(pidl->mkid.cb));
521 hr = desktop->BindToObject(parent_id, 0, riid, ppv);
522 free(parent_id);
523 }
524 else
525 return E_OUTOFMEMORY;
526 }
527
528 // Return a pointer to the last item ID.
529 if (ppidlLast)
530 *ppidlLast = marker;
531
532 return hr;
533 }
534 #define USE_MY_SHBINDTOPARENT
535
536 HRESULT Entry::do_context_menu(HWND hwnd, const POINT& pos, CtxMenuInterfaces& cm_ifs)
537 {
538 ShellPath shell_path = create_absolute_pidl();
539 LPCITEMIDLIST pidl_abs = shell_path;
540
541 if (!pidl_abs)
542 return S_FALSE; // no action for registry entries, etc.
543
544 #ifdef USE_MY_SHBINDTOPARENT
545 IShellFolder* parentFolder;
546 LPCITEMIDLIST pidlLast;
547
548 // get and use the parent folder to display correct context menu in all cases -> correct "Properties" dialog for directories, ...
549 HRESULT hr = my_SHBindToParent(pidl_abs, IID_IShellFolder, (LPVOID*)&parentFolder, &pidlLast);
550
551 if (SUCCEEDED(hr)) {
552 hr = ShellFolderContextMenu(parentFolder, hwnd, 1, &pidlLast, pos.x, pos.y, cm_ifs);
553
554 parentFolder->Release();
555 }
556
557 return hr;
558 #else
559 static DynamicFct<HRESULT(WINAPI*)(LPCITEMIDLIST, REFIID, LPVOID*, LPCITEMIDLIST*)> SHBindToParent(TEXT("SHELL32"), "SHBindToParent");
560
561 if (SHBindToParent) {
562 IShellFolder* parentFolder;
563 LPCITEMIDLIST pidlLast;
564
565 // get and use the parent folder to display correct context menu in all cases -> correct "Properties" dialog for directories, ...
566 HRESULT hr = (*SHBindToParent)(pidl_abs, IID_IShellFolder, (LPVOID*)&parentFolder, &pidlLast);
567
568 if (SUCCEEDED(hr)) {
569 hr = ShellFolderContextMenu(parentFolder, hwnd, 1, &pidlLast, pos.x, pos.y, cm_ifs);
570
571 parentFolder->Release();
572 }
573
574 return hr;
575 } else {
576 /**@todo use parent folder instead of desktop folder
577 Entry* dir = _up;
578
579 ShellPath parent_path;
580
581 if (dir)
582 parent_path = dir->create_absolute_pidl();
583 else
584 parent_path = DesktopFolderPath();
585
586 ShellPath shell_path = create_relative_pidl(parent_path);
587 LPCITEMIDLIST pidl = shell_path;
588
589 ShellFolder parent_folder = parent_path;
590 return ShellFolderContextMenu(parent_folder, hwnd, 1, &pidl, pos.x, pos.y);
591 */
592 return ShellFolderContextMenu(GetDesktopFolder(), hwnd, 1, &pidl_abs, pos.x, pos.y, cm_ifs);
593 }
594 #endif
595 }
596
597
598 HRESULT Entry::GetUIObjectOf(HWND hWnd, REFIID riid, LPVOID* ppvOut)
599 {
600 TCHAR path[MAX_PATH];
601 /*
602 if (!get_path(path, COUNTOF(path)))
603 return E_FAIL;
604
605 ShellPath shell_path(path);
606
607 IShellFolder* pFolder;
608 LPCITEMIDLIST pidl_last = NULL;
609
610 static DynamicFct<HRESULT(WINAPI*)(LPCITEMIDLIST, REFIID, LPVOID*, LPCITEMIDLIST*)> SHBindToParent(TEXT("SHELL32"), "SHBindToParent");
611
612 if (!SHBindToParent)
613 return E_NOTIMPL;
614
615 HRESULT hr = (*SHBindToParent)(shell_path, IID_IShellFolder, (LPVOID*)&pFolder, &pidl_last);
616 if (FAILED(hr))
617 return hr;
618
619 ShellFolder shell_folder(pFolder);
620
621 shell_folder->Release();
622
623 return shell_folder->GetUIObjectOf(hWnd, 1, &pidl_last, riid, NULL, ppvOut);
624 */
625 if (!_up)
626 return E_INVALIDARG;
627
628 if (!_up->get_path(path, COUNTOF(path)))
629 return E_FAIL;
630
631 ShellPath shell_path(path);
632 ShellFolder shell_folder(shell_path);
633
634 #ifdef UNICODE
635 LPWSTR wname = _data.cFileName;
636 #else
637 WCHAR wname[MAX_PATH];
638 MultiByteToWideChar(CP_ACP, 0, _data.cFileName, -1, wname, COUNTOF(wname));
639 #endif
640
641 LPITEMIDLIST pidl_last = NULL;
642 HRESULT hr = shell_folder->ParseDisplayName(hWnd, NULL, wname, NULL, &pidl_last, NULL);
643
644 if (FAILED(hr))
645 return hr;
646
647 hr = shell_folder->GetUIObjectOf(hWnd, 1, (LPCITEMIDLIST*)&pidl_last, riid, NULL, ppvOut);
648
649 ShellMalloc()->Free((void*)pidl_last);
650
651 return hr;
652 }
653
654
655 // get full path of specified directory entry
656 bool Entry::get_path_base ( PTSTR path, size_t path_count, ENTRY_TYPE etype ) const
657 {
658 int level = 0;
659 size_t len = 0;
660 size_t l = 0;
661 LPCTSTR name = NULL;
662 TCHAR buffer[MAX_PATH];
663
664 if (!path || path_count==0)
665 return false;
666
667 const Entry* entry;
668 if ( path_count > 1 )
669 {
670 for(entry=this; entry; level++) {
671 l = 0;
672
673 if (entry->_etype == etype) {
674 name = entry->_data.cFileName;
675
676 for(LPCTSTR s=name; *s && *s!=TEXT('/') && *s!=TEXT('\\'); s++)
677 ++l;
678
679 if (!entry->_up)
680 break;
681 } else {
682 if (entry->get_path(buffer, COUNTOF(buffer))) {
683 l = _tcslen(buffer);
684 name = buffer;
685
686 /* special handling of drive names */
687 if (l>0 && buffer[l-1]=='\\' && path[0]=='\\')
688 --l;
689
690 if ( len+l >= path_count )
691 {
692 if ( l + 1 > path_count )
693 len = 0;
694 else
695 len = path_count - l - 1;
696 }
697 memmove(path+l, path, len*sizeof(TCHAR));
698 if ( l+1 >= path_count )
699 l = path_count - 1;
700 memcpy(path, name, l*sizeof(TCHAR));
701 len += l;
702 }
703
704 entry = NULL;
705 break;
706 }
707
708 if (l > 0) {
709 if ( len+l+1 >= path_count )
710 {
711 /* compare to 2 here because of terminator plus the '\\' we prepend */
712 if ( l + 2 > path_count )
713 len = 0;
714 else
715 len = path_count - l - 2;
716 }
717 memmove(path+l+1, path, len*sizeof(TCHAR));
718 /* compare to 2 here because of terminator plus the '\\' we prepend */
719 if ( l+2 >= path_count )
720 l = path_count - 2;
721 memcpy(path+1, name, l*sizeof(TCHAR));
722 len += l+1;
723
724 #ifndef _NO_WIN_FS
725 if (etype == ET_WINDOWS && entry->_up && !(entry->_up->_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) // a NTFS stream?
726 path[0] = TEXT(':');
727 else
728 #endif
729 path[0] = TEXT('\\');
730 }
731
732 entry = entry->_up;
733 }
734
735 if (entry) {
736 if ( len+l >= path_count )
737 {
738 if ( l + 1 > path_count )
739 len = 0;
740 else
741 len = path_count - l - 1;
742 }
743 memmove(path+l, path, len*sizeof(TCHAR));
744 if ( l+1 >= path_count )
745 l = path_count - 1;
746 memcpy(path, name, l*sizeof(TCHAR));
747 len += l;
748 }
749
750 if ( !level && (len+1 < path_count) )
751 path[len++] = TEXT('\\');
752 }
753
754 path[len] = TEXT('\0');
755
756 return true;
757 }
758
759 // recursively free all child entries
760 void Entry::free_subentries()
761 {
762 Entry *entry, *next=_down;
763
764 if (next) {
765 _down = 0;
766
767 do {
768 entry = next;
769 next = entry->_next;
770
771 entry->free_subentries();
772 delete entry;
773 } while(next);
774 }
775 }
776
777
778 Entry* Root::read_tree(LPCTSTR path, int scan_flags)
779 {
780 Entry* entry;
781
782 if (path && *path)
783 entry = _entry->read_tree(path, _sort_order);
784 else {
785 entry = _entry->read_tree(NULL, _sort_order);
786
787 _entry->smart_scan();
788
789 if (_entry->_down)
790 _entry->_expanded = true;
791 }
792
793 return entry;
794 }
795
796
797 Entry* Root::read_tree(LPCITEMIDLIST pidl, int scan_flags)
798 {
799 return _entry->read_tree(pidl, _sort_order);
800 }