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
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

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)");
// 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
}
}

View file

@ -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;

View file

@ -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);
}
}
}

View file

@ -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<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()");
@ -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<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 (read_icons) {
entry->_hIcon = extract_icon(_folder, pidls[n], static_cast<ShellEntry*>(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<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 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();
}
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;

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

View file

@ -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*);
};

View file

@ -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);

View file

@ -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();
};