Reactos
at master 783 lines 18 kB view raw
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 35Entry::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 51Entry::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 68Entry::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 97Entry::~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 119Entry* 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 143void 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 164Root::Root() 165{ 166 memset(this, 0, sizeof(Root)); 167} 168 169Root::~Root() 170{ 171 if (_entry) { 172 _entry->free_subentries(); 173 delete _entry; 174 } 175} 176 177 178 // sort order for different directory/file types 179enum 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 189static 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... 203static 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 228static int compareNothing(const void* arg1, const void* arg2) 229{ 230 return -1; 231} 232 233static 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 245static 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 278static 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 299static 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 312static 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 321void 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 353void 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 365int 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 431int 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 443BOOL 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 https://web.archive.org/web/20021105062620/http://www.geocities.com/SiliconValley/2060/articles/shell-helpers.html) 460static 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 519HRESULT 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 581HRESULT 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 639bool 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 743void 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 761Entry* 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 780Entry* Root::read_tree(LPCITEMIDLIST pidl, int scan_flags) 781{ 782 return _entry->read_tree(pidl, _sort_order); 783}