lazy icon extraction for start menu

svn path=/trunk/; revision=7422
This commit is contained in:
Martin Fuchs 2004-01-03 11:43:41 +00:00
parent b1f2f1ed55
commit 7fbcfcbc71
10 changed files with 188 additions and 78 deletions

View file

@ -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 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. 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 02.01.2004 m. fuchs reimplemented start menu as light weight version
03.01.2004 m. fuchs lazy icon extraction for start menu

View file

@ -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)"); CONTEXT("Entry::read_directory(SORT_ORDER)");
// call into subclass // 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) for(Entry*entry=_down; entry; entry=entry->_next)
if (entry->_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { if (entry->_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
entry->read_directory(); entry->read_directory(read_icons);
entry->sort_directory(sortOrder); 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()"); CONTEXT("Entry::smart_scan()");
if (!_scanned) { if (!_scanned) {
free_subentries(); 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
} }
} }

View file

@ -73,12 +73,12 @@ public:
void free_subentries(); 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); Entry* read_tree(const void* path, SORT_ORDER sortOrder);
void sort_directory(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 const void* get_next_path_component(const void*) {return NULL;}
virtual Entry* find_entry(const void*) {return NULL;} virtual Entry* find_entry(const void*) {return NULL;}
virtual void get_path(PTSTR path) const = 0; virtual void get_path(PTSTR path) const = 0;

View file

@ -501,12 +501,14 @@ void Pane::draw_item(LPDRAWITEMSTRUCT dis, Entry* entry, int calcWidthCol)
if (cx > IMAGE_WIDTH) if (cx > IMAGE_WIDTH)
cx = IMAGE_WIDTH; cx = IMAGE_WIDTH;
if (entry->_hIcon && entry->_hIcon!=(HICON)-1) if (entry->_hIcon != (HICON)-1) {
DrawIconEx(dis->hDC, img_pos, dis->rcItem.top, entry->_hIcon, cx, GetSystemMetrics(SM_CYSMICON), 0, 0, DI_NORMAL); if (entry->_hIcon)
else DrawIconEx(dis->hDC, img_pos, dis->rcItem.top, entry->_hIcon, cx, GetSystemMetrics(SM_CYSMICON), 0, 0, DI_NORMAL);
ImageList_DrawEx(_himl, img, dis->hDC, else
img_pos, dis->rcItem.top, cx, ImageList_DrawEx(_himl, img, dis->hDC,
IMAGE_HEIGHT, bkcolor, CLR_DEFAULT, ILD_NORMAL); img_pos, dis->rcItem.top, cx,
IMAGE_HEIGHT, bkcolor, CLR_DEFAULT, ILD_NORMAL);
}
} }
} }

View file

@ -234,8 +234,25 @@ static HICON extract_icon(IShellFolder* folder, LPCITEMIDLIST pidl)
return 0; 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<ShellEntry*>(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()"); CONTEXT("ShellDirectory::read_directory()");
@ -328,23 +345,15 @@ void ShellDirectory::read_directory()
// get display icons for files and virtual objects // get display icons for files and virtual objects
if (!(entry->_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) || if (!(entry->_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ||
!(attribs & SFGAO_FILESYSTEM)) { !(attribs & SFGAO_FILESYSTEM)) {
entry->_hIcon = extract_icon(_folder, pidls[n]/*, (ShellEntry*)entry*/); if (read_icons) {
entry->_hIcon = extract_icon(_folder, pidls[n], static_cast<ShellEntry*>(entry));
if (!entry->_hIcon) {
if (!entry->_hIcon) {
ShellPath pidl_abs = static_cast<ShellEntry*>(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 (!entry->_hIcon) if (!entry->_hIcon)
entry->_hIcon = (HICON)-1; // don't try again later 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->_down = NULL;
entry->_expanded = false; entry->_expanded = false;
@ -385,11 +394,31 @@ Entry* ShellDirectory::find_entry(const void* p)
LPITEMIDLIST pidl = (LPITEMIDLIST) p; LPITEMIDLIST pidl = (LPITEMIDLIST) p;
for(Entry*entry=_down; entry; entry=entry->_next) { for(Entry*entry=_down; entry; entry=entry->_next) {
ShellEntry* e = static_cast<ShellEntry*>(entry); ShellEntry* se = static_cast<ShellEntry*>(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 entry;
} }
return NULL; return NULL;
} }
int ShellDirectory::extract_icons()
{
int cnt = 0;
for(Entry*entry=_down; entry; entry=entry->_next) {
ShellEntry* se = static_cast<ShellEntry*>(entry);
if (!se->_hIcon) {
se->_hIcon = extract_icon(_folder, se->_pidl, static_cast<ShellEntry*>(se));
if (entry->_hIcon)
++cnt;
else
entry->_hIcon = (HICON)-1; // don't try again later
}
}
return cnt;
}

View file

@ -97,12 +97,14 @@ struct ShellDirectory : public ShellEntry, public Directory
pFolder->Release(); pFolder->Release();
} }
virtual void read_directory(); virtual void read_directory(bool read_icons=true);
virtual const void* get_next_path_component(const void*); virtual const void* get_next_path_component(const void*);
virtual Entry* find_entry(const void* p); virtual Entry* find_entry(const void* p);
virtual void get_path(PTSTR path) const; virtual void get_path(PTSTR path) const;
int extract_icons();
ShellFolder _folder; ShellFolder _folder;
HWND _hwnd; HWND _hwnd;

View file

@ -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* first_entry = NULL;
Entry* last = NULL; Entry* last = NULL;

View file

@ -59,7 +59,7 @@ struct WinDirectory : public WinEntry, public Directory
_path = NULL; _path = NULL;
} }
virtual void read_directory(); virtual void read_directory(bool read_icons=true);
virtual const void* get_next_path_component(const void*); virtual const void* get_next_path_component(const void*);
virtual Entry* find_entry(const void*); virtual Entry* find_entry(const void*);
}; };

View file

@ -167,6 +167,10 @@ LRESULT StartMenu::Init(LPCREATESTRUCT pcs)
#ifdef _LIGHT_STARTMENU #ifdef _LIGHT_STARTMENU
ResizeToButtons(); ResizeToButtons();
#endif #endif
#ifdef _LAZY_ICONEXTRACT
PostMessage(_hwnd, PM_UPDATE_ICONS, 0, 0);
#endif
} catch(COMException& e) { } catch(COMException& e) {
HandleException(e, pcs->hwndParent); // destroys the start menu window while switching focus HandleException(e, pcs->hwndParent); // destroys the start menu window while switching focus
} }
@ -183,7 +187,11 @@ void StartMenu::AddEntries()
if (!dir._scanned) { if (!dir._scanned) {
WaitCursor wait; 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); AddShellEntries(dir, -1, smd._subfolders);
@ -324,6 +332,12 @@ LRESULT StartMenu::WndProc(UINT nmsg, WPARAM wparam, LPARAM lparam)
break;} break;}
#endif #endif
#ifdef _LAZY_ICONEXTRACT
case PM_UPDATE_ICONS:
UpdateIcons();
break;
#endif
case PM_STARTENTRY_LAUNCHED: case PM_STARTENTRY_LAUNCHED:
if (GetWindowStyle(_hwnd) & WS_CAPTION) // don't automatically close floating menus if (GetWindowStyle(_hwnd) & WS_CAPTION) // don't automatically close floating menus
return 0; 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 #ifdef _LIGHT_STARTMENU
int StartMenu::ButtonHitTest(POINT pt) 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 // resize child button controls to accomodate for new window size
void StartMenu::ResizeButtons(int cx) void StartMenu::ResizeButtons(int cx)
{ {
@ -1441,6 +1505,10 @@ void SearchMenu::AddEntries()
void RecentStartMenu::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) { for(StartMenuShellDirs::iterator it=_dirs.begin(); it!=_dirs.end(); ++it) {
StartMenuDirectory& smd = *it; StartMenuDirectory& smd = *it;
ShellDirectory& dir = smd._dir; ShellDirectory& dir = smd._dir;
@ -1448,7 +1516,11 @@ void RecentStartMenu::AddEntries()
if (!dir._scanned) { if (!dir._scanned) {
WaitCursor wait; 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); dir.sort_directory(SORT_DATE);

View file

@ -27,6 +27,7 @@
#define _LIGHT_STARTMENU #define _LIGHT_STARTMENU
#define _LAZY_ICONEXTRACT
#define CLASSNAME_STARTMENU TEXT("ReactosStartmenuClass") #define CLASSNAME_STARTMENU TEXT("ReactosStartmenuClass")
@ -47,6 +48,8 @@
#define PM_STARTENTRY_FOCUSED (WM_APP+0x13) #define PM_STARTENTRY_FOCUSED (WM_APP+0x13)
#endif #endif
#define PM_UPDATE_ICONS (WM_APP+0x15)
/// StartMenuDirectory is used to store the base directory of start menus. /// StartMenuDirectory is used to store the base directory of start menus.
struct StartMenuDirectory struct StartMenuDirectory
@ -273,6 +276,7 @@ protected:
void GetFloatingButonRect(LPRECT prect); void GetFloatingButonRect(LPRECT prect);
void Paint(PaintCanvas& canvas); void Paint(PaintCanvas& canvas);
void UpdateIcons();
}; };