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