diff --git a/reactos/subsys/system/explorer/doc/changes.txt b/reactos/subsys/system/explorer/doc/changes.txt index 83771a52c21..1a3276c263b 100644 --- a/reactos/subsys/system/explorer/doc/changes.txt +++ b/reactos/subsys/system/explorer/doc/changes.txt @@ -51,3 +51,4 @@ If you search for more information, look into the CVS repository. 20.12.2003 m. fuchs context menu implementation for desktop window 01.01.2004 m. fuchs integrated icons of Everaldo (http://www.everaldo.com) into the start menu. 02.01.2004 m. fuchs reimplemented start menu as light weight version +03.01.2004 m. fuchs lazy icon extraction for start menu diff --git a/reactos/subsys/system/explorer/shell/entries.cpp b/reactos/subsys/system/explorer/shell/entries.cpp index 42a578a1ab5..e3df78e0a53 100644 --- a/reactos/subsys/system/explorer/shell/entries.cpp +++ b/reactos/subsys/system/explorer/shell/entries.cpp @@ -126,17 +126,17 @@ Entry* Entry::read_tree(const void* path, SORT_ORDER sortOrder) } -void Entry::read_directory(SORT_ORDER sortOrder) +void Entry::read_directory(SORT_ORDER sortOrder, bool read_icons) { CONTEXT("Entry::read_directory(SORT_ORDER)"); // call into subclass - read_directory(); + read_directory(read_icons); - if (g_Globals._prescan_nodes) { + if (g_Globals._prescan_nodes) { //@todo _prescan_nodes should not be used for filling the start menu. for(Entry*entry=_down; entry; entry=entry->_next) if (entry->_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { - entry->read_directory(); + entry->read_directory(read_icons); entry->sort_directory(sortOrder); } } @@ -284,13 +284,13 @@ void Entry::sort_directory(SORT_ORDER sortOrder) } -void Entry::smart_scan() +void Entry::smart_scan(bool read_icons) { CONTEXT("Entry::smart_scan()"); if (!_scanned) { free_subentries(); - read_directory(SORT_NAME); // we could use IShellFolder2::GetDefaultColumn to determine sort order + read_directory(SORT_NAME, read_icons); // we could use IShellFolder2::GetDefaultColumn to determine sort order } } diff --git a/reactos/subsys/system/explorer/shell/entries.h b/reactos/subsys/system/explorer/shell/entries.h index 18108c72f91..f30a953f112 100644 --- a/reactos/subsys/system/explorer/shell/entries.h +++ b/reactos/subsys/system/explorer/shell/entries.h @@ -73,12 +73,12 @@ public: void free_subentries(); - void read_directory(SORT_ORDER sortOrder); + void read_directory(SORT_ORDER sortOrder, bool read_icons=true); Entry* read_tree(const void* path, SORT_ORDER sortOrder); void sort_directory(SORT_ORDER sortOrder); - void smart_scan(); + void smart_scan(bool read_icons=true); - virtual void read_directory() {} + virtual void read_directory(bool read_icons=true) {} virtual const void* get_next_path_component(const void*) {return NULL;} virtual Entry* find_entry(const void*) {return NULL;} virtual void get_path(PTSTR path) const = 0; diff --git a/reactos/subsys/system/explorer/shell/pane.cpp b/reactos/subsys/system/explorer/shell/pane.cpp index 4a700b1f7e9..69e9798af4f 100644 --- a/reactos/subsys/system/explorer/shell/pane.cpp +++ b/reactos/subsys/system/explorer/shell/pane.cpp @@ -501,12 +501,14 @@ void Pane::draw_item(LPDRAWITEMSTRUCT dis, Entry* entry, int calcWidthCol) if (cx > IMAGE_WIDTH) cx = IMAGE_WIDTH; - if (entry->_hIcon && entry->_hIcon!=(HICON)-1) - DrawIconEx(dis->hDC, img_pos, dis->rcItem.top, entry->_hIcon, cx, GetSystemMetrics(SM_CYSMICON), 0, 0, DI_NORMAL); - else - ImageList_DrawEx(_himl, img, dis->hDC, - img_pos, dis->rcItem.top, cx, - IMAGE_HEIGHT, bkcolor, CLR_DEFAULT, ILD_NORMAL); + if (entry->_hIcon != (HICON)-1) { + if (entry->_hIcon) + DrawIconEx(dis->hDC, img_pos, dis->rcItem.top, entry->_hIcon, cx, GetSystemMetrics(SM_CYSMICON), 0, 0, DI_NORMAL); + else + ImageList_DrawEx(_himl, img, dis->hDC, + img_pos, dis->rcItem.top, cx, + IMAGE_HEIGHT, bkcolor, CLR_DEFAULT, ILD_NORMAL); + } } } diff --git a/reactos/subsys/system/explorer/shell/shellfs.cpp b/reactos/subsys/system/explorer/shell/shellfs.cpp index 4b7fa60a688..00fe3f7053a 100644 --- a/reactos/subsys/system/explorer/shell/shellfs.cpp +++ b/reactos/subsys/system/explorer/shell/shellfs.cpp @@ -234,8 +234,25 @@ static HICON extract_icon(IShellFolder* folder, LPCITEMIDLIST pidl) return 0; } +static HICON extract_icon(IShellFolder* folder, LPCITEMIDLIST pidl, ShellEntry* entry) +{ + HICON hIcon = extract_icon(folder, pidl); -void ShellDirectory::read_directory() + if (!hIcon) { + ShellPath pidl_abs = static_cast(entry)->create_absolute_pidl(); + LPCITEMIDLIST pidl = pidl_abs; + + SHFILEINFO sfi; + + if (SHGetFileInfo((LPCTSTR)pidl, 0, &sfi, sizeof(sfi), SHGFI_PIDL|SHGFI_ICON|SHGFI_SMALLICON)) + entry->_hIcon = sfi.hIcon; + } + + return hIcon; +} + + +void ShellDirectory::read_directory(bool read_icons) { CONTEXT("ShellDirectory::read_directory()"); @@ -328,23 +345,15 @@ void ShellDirectory::read_directory() // get display icons for files and virtual objects if (!(entry->_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) || !(attribs & SFGAO_FILESYSTEM)) { - entry->_hIcon = extract_icon(_folder, pidls[n]/*, (ShellEntry*)entry*/); - - if (!entry->_hIcon) { - if (!entry->_hIcon) { - ShellPath pidl_abs = static_cast(entry)->create_absolute_pidl(); - LPCITEMIDLIST pidl = pidl_abs; - - SHFILEINFO sfi; - - if (SHGetFileInfo((LPCTSTR)pidl, 0, &sfi, sizeof(sfi), SHGFI_PIDL|SHGFI_ICON|SHGFI_SMALLICON)) - entry->_hIcon = sfi.hIcon; - } + if (read_icons) { + entry->_hIcon = extract_icon(_folder, pidls[n], static_cast(entry)); if (!entry->_hIcon) entry->_hIcon = (HICON)-1; // don't try again later - } - } + } else + entry->_hIcon = 0; + } else + entry->_hIcon = (HICON)-1; // don't try again later entry->_down = NULL; entry->_expanded = false; @@ -385,11 +394,31 @@ Entry* ShellDirectory::find_entry(const void* p) LPITEMIDLIST pidl = (LPITEMIDLIST) p; for(Entry*entry=_down; entry; entry=entry->_next) { - ShellEntry* e = static_cast(entry); + ShellEntry* se = static_cast(entry); - if (e->_pidl && e->_pidl->mkid.cb==pidl->mkid.cb && !memcmp(e->_pidl, pidl, e->_pidl->mkid.cb)) + if (se->_pidl && se->_pidl->mkid.cb==pidl->mkid.cb && !memcmp(se->_pidl, pidl, se->_pidl->mkid.cb)) return entry; } return NULL; } + +int ShellDirectory::extract_icons() +{ + int cnt = 0; + + for(Entry*entry=_down; entry; entry=entry->_next) { + ShellEntry* se = static_cast(entry); + + if (!se->_hIcon) { + se->_hIcon = extract_icon(_folder, se->_pidl, static_cast(se)); + + if (entry->_hIcon) + ++cnt; + else + entry->_hIcon = (HICON)-1; // don't try again later + } + } + + return cnt; +} diff --git a/reactos/subsys/system/explorer/shell/shellfs.h b/reactos/subsys/system/explorer/shell/shellfs.h index 3fbc07fd5fe..a9cca6e90b0 100644 --- a/reactos/subsys/system/explorer/shell/shellfs.h +++ b/reactos/subsys/system/explorer/shell/shellfs.h @@ -97,12 +97,14 @@ struct ShellDirectory : public ShellEntry, public Directory pFolder->Release(); } - virtual void read_directory(); + virtual void read_directory(bool read_icons=true); virtual const void* get_next_path_component(const void*); virtual Entry* find_entry(const void* p); virtual void get_path(PTSTR path) const; + int extract_icons(); + ShellFolder _folder; HWND _hwnd; diff --git a/reactos/subsys/system/explorer/shell/winfs.cpp b/reactos/subsys/system/explorer/shell/winfs.cpp index cdb0de4ff2c..0e0a8adaad6 100644 --- a/reactos/subsys/system/explorer/shell/winfs.cpp +++ b/reactos/subsys/system/explorer/shell/winfs.cpp @@ -119,7 +119,7 @@ int ScanNTFSStreams(Entry* entry, HANDLE hFile) } -void WinDirectory::read_directory() +void WinDirectory::read_directory(bool read_icons) { Entry* first_entry = NULL; Entry* last = NULL; diff --git a/reactos/subsys/system/explorer/shell/winfs.h b/reactos/subsys/system/explorer/shell/winfs.h index 61e6681440d..dd720637668 100644 --- a/reactos/subsys/system/explorer/shell/winfs.h +++ b/reactos/subsys/system/explorer/shell/winfs.h @@ -59,7 +59,7 @@ struct WinDirectory : public WinEntry, public Directory _path = NULL; } - virtual void read_directory(); + virtual void read_directory(bool read_icons=true); virtual const void* get_next_path_component(const void*); virtual Entry* find_entry(const void*); }; diff --git a/reactos/subsys/system/explorer/taskbar/startmenu.cpp b/reactos/subsys/system/explorer/taskbar/startmenu.cpp index 4b305ff972c..5ea0c01af96 100644 --- a/reactos/subsys/system/explorer/taskbar/startmenu.cpp +++ b/reactos/subsys/system/explorer/taskbar/startmenu.cpp @@ -167,6 +167,10 @@ LRESULT StartMenu::Init(LPCREATESTRUCT pcs) #ifdef _LIGHT_STARTMENU ResizeToButtons(); #endif + +#ifdef _LAZY_ICONEXTRACT + PostMessage(_hwnd, PM_UPDATE_ICONS, 0, 0); +#endif } catch(COMException& e) { HandleException(e, pcs->hwndParent); // destroys the start menu window while switching focus } @@ -183,7 +187,11 @@ void StartMenu::AddEntries() if (!dir._scanned) { WaitCursor wait; - dir.smart_scan(); +#ifdef _LAZY_ICONEXTRACT + dir.smart_scan(false); // lazy icon extraction +#else + dir.smart_scan(true); +#endif } AddShellEntries(dir, -1, smd._subfolders); @@ -324,6 +332,12 @@ LRESULT StartMenu::WndProc(UINT nmsg, WPARAM wparam, LPARAM lparam) break;} #endif +#ifdef _LAZY_ICONEXTRACT + case PM_UPDATE_ICONS: + UpdateIcons(); + break; +#endif + case PM_STARTENTRY_LAUNCHED: if (GetWindowStyle(_hwnd) & WS_CAPTION) // don't automatically close floating menus return 0; @@ -345,47 +359,6 @@ LRESULT StartMenu::WndProc(UINT nmsg, WPARAM wparam, LPARAM lparam) } -void StartMenu::Paint(PaintCanvas& canvas) -{ - if (_border_top) - DrawFloatingButton(canvas); - -#ifdef _LIGHT_STARTMENU - ClientRect clnt(_hwnd); - RECT rect = {_border_left, _border_top, clnt.right, STARTMENU_LINE_HEIGHT}; - - int sep_width = rect.right-rect.left - 4; - - FontSelection font(canvas, GetStockFont(DEFAULT_GUI_FONT)); - BkMode bk_mode(canvas, TRANSPARENT); - - for(SMBtnVector::const_iterator it=_buttons.begin(); it!=_buttons.end(); ++it) { - const SMBtnInfo& info = *it; - - if (rect.top > canvas.rcPaint.bottom) - break; - - if (info._id == -1) { // a separator? - rect.bottom = rect.top + STARTMENU_SEP_HEIGHT; - - BrushSelection brush_sel(canvas, GetSysColorBrush(COLOR_BTNSHADOW)); - PatBlt(canvas, rect.left+2, rect.top+STARTMENU_SEP_HEIGHT/2-1, sep_width, 1, PATCOPY); - - SelectBrush(canvas, GetSysColorBrush(COLOR_BTNHIGHLIGHT)); - PatBlt(canvas, rect.left+2, rect.top+STARTMENU_SEP_HEIGHT/2, sep_width, 1, PATCOPY); - } else { - rect.bottom = rect.top + STARTMENU_LINE_HEIGHT; - - if (rect.top >= canvas.rcPaint.top) - DrawStartMenuButton(canvas, rect, info._title, info._hIcon, - info._hasSubmenu, info._enabled, info._id==_selected_id, false); - } - - rect.top = rect.bottom; - } -#endif -} - #ifdef _LIGHT_STARTMENU int StartMenu::ButtonHitTest(POINT pt) @@ -521,6 +494,97 @@ void StartMenu::GetFloatingButonRect(LPRECT prect) } +void StartMenu::Paint(PaintCanvas& canvas) +{ + if (_border_top) + DrawFloatingButton(canvas); + +#ifdef _LIGHT_STARTMENU + ClientRect clnt(_hwnd); + RECT rect = {_border_left, _border_top, clnt.right, STARTMENU_LINE_HEIGHT}; + + int sep_width = rect.right-rect.left - 4; + + FontSelection font(canvas, GetStockFont(DEFAULT_GUI_FONT)); + BkMode bk_mode(canvas, TRANSPARENT); + + for(SMBtnVector::const_iterator it=_buttons.begin(); it!=_buttons.end(); ++it) { + const SMBtnInfo& info = *it; + + if (rect.top > canvas.rcPaint.bottom) + break; + + if (info._id == -1) { // a separator? + rect.bottom = rect.top + STARTMENU_SEP_HEIGHT; + + BrushSelection brush_sel(canvas, GetSysColorBrush(COLOR_BTNSHADOW)); + PatBlt(canvas, rect.left+2, rect.top+STARTMENU_SEP_HEIGHT/2-1, sep_width, 1, PATCOPY); + + SelectBrush(canvas, GetSysColorBrush(COLOR_BTNHIGHLIGHT)); + PatBlt(canvas, rect.left+2, rect.top+STARTMENU_SEP_HEIGHT/2, sep_width, 1, PATCOPY); + } else { + rect.bottom = rect.top + STARTMENU_LINE_HEIGHT; + + if (rect.top >= canvas.rcPaint.top) + DrawStartMenuButton(canvas, rect, info._title, info._hIcon, + info._hasSubmenu, info._enabled, info._id==_selected_id, false); + } + + rect.top = rect.bottom; + } +#endif +} + +#ifdef _LAZY_ICONEXTRACT +void StartMenu::UpdateIcons() +{ + UpdateWindow(_hwnd); + + int icons_extracted = 0; + int icons_refreshed = 0; + + for(StartMenuShellDirs::iterator it=_dirs.begin(); it!=_dirs.end(); ++it) { + ShellDirectory& dir = it->_dir; + + icons_extracted += dir.extract_icons(); + } + + if (icons_extracted) { + for(ShellEntryMap::iterator it1=_entries.begin(); it1!=_entries.end(); ++it1) { + StartMenuEntry& sme = it1->second; + + if (!sme._hIcon) { + sme._hIcon = (HICON)-1; + + for(ShellEntrySet::const_iterator it2=sme._entries.begin(); it2!=sme._entries.end(); ++it2) { + const ShellEntry* sm_entry = *it2; + + if (sm_entry->_hIcon) { + sme._hIcon = sm_entry->_hIcon; + break; + } + } + } + } + + for(SMBtnVector::iterator it=_buttons.begin(); it!=_buttons.end(); ++it) { + SMBtnInfo& info = *it; + + if (info._id>0 && !info._hIcon) { + info._hIcon = _entries[info._id]._hIcon; + ++icons_refreshed; + } + } + } + + if (icons_refreshed) { + InvalidateRect(_hwnd, NULL, TRUE); + UpdateWindow(_hwnd); + } +} +#endif + + // resize child button controls to accomodate for new window size void StartMenu::ResizeButtons(int cx) { @@ -1441,6 +1505,10 @@ void SearchMenu::AddEntries() void RecentStartMenu::AddEntries() { + + ///@todo A cache would really speed up processing of long recent doc lists. + ///@todo Alternativelly we could also use direct file system access instead of iterating in shell namespace. + for(StartMenuShellDirs::iterator it=_dirs.begin(); it!=_dirs.end(); ++it) { StartMenuDirectory& smd = *it; ShellDirectory& dir = smd._dir; @@ -1448,7 +1516,11 @@ void RecentStartMenu::AddEntries() if (!dir._scanned) { WaitCursor wait; - dir.smart_scan(); +#ifdef _LAZY_ICONEXTRACT + dir.smart_scan(false); // lazy icon extraction +#else + dir.smart_scan(true); +#endif } dir.sort_directory(SORT_DATE); diff --git a/reactos/subsys/system/explorer/taskbar/startmenu.h b/reactos/subsys/system/explorer/taskbar/startmenu.h index 0e5f494d03d..21a6ff03505 100644 --- a/reactos/subsys/system/explorer/taskbar/startmenu.h +++ b/reactos/subsys/system/explorer/taskbar/startmenu.h @@ -27,6 +27,7 @@ #define _LIGHT_STARTMENU +#define _LAZY_ICONEXTRACT #define CLASSNAME_STARTMENU TEXT("ReactosStartmenuClass") @@ -47,6 +48,8 @@ #define PM_STARTENTRY_FOCUSED (WM_APP+0x13) #endif +#define PM_UPDATE_ICONS (WM_APP+0x15) + /// StartMenuDirectory is used to store the base directory of start menus. struct StartMenuDirectory @@ -273,6 +276,7 @@ protected: void GetFloatingButonRect(LPRECT prect); void Paint(PaintCanvas& canvas); + void UpdateIcons(); };