diff options
author | Daniel Wilhelm <daniel@wili.li> | 2014-04-18 17:14:37 +0200 |
---|---|---|
committer | Daniel Wilhelm <daniel@wili.li> | 2014-04-18 17:14:37 +0200 |
commit | 8bf668665b107469086f16cb8ad23e47d479d2b4 (patch) | |
tree | 66a91ef06a8caa7cd6819dcbe1860693d3eda8d5 /library/icon_buffer.cpp | |
parent | 3.21 (diff) | |
download | FreeFileSync-8bf668665b107469086f16cb8ad23e47d479d2b4.tar.gz FreeFileSync-8bf668665b107469086f16cb8ad23e47d479d2b4.tar.bz2 FreeFileSync-8bf668665b107469086f16cb8ad23e47d479d2b4.zip |
4.0
Diffstat (limited to 'library/icon_buffer.cpp')
-rw-r--r-- | library/icon_buffer.cpp | 533 |
1 files changed, 337 insertions, 196 deletions
diff --git a/library/icon_buffer.cpp b/library/icon_buffer.cpp index 2c17f6da..b8ee66cd 100644 --- a/library/icon_buffer.cpp +++ b/library/icon_buffer.cpp @@ -5,19 +5,16 @@ // ************************************************************************** #include "icon_buffer.h" -#include <wx/msgdlg.h> -#include <map> -#include <vector> #include <queue> #include <set> -#include <wx/log.h> -#include "../shared/i18n.h" #include "../shared/boost_thread_wrap.h" //include <boost/thread.hpp> #include "../shared/loki/ScopeGuard.h" #include <boost/thread/once.hpp> #ifdef FFS_WIN #include <wx/msw/wrapwin.h> //includes "windows.h" +#include "../shared/dll_loader.h" +#include "../shared/Thumbnail/thumbnail.h" #elif defined FFS_LINUX #include <giomm/file.h> @@ -25,50 +22,32 @@ #include <gtkmm/main.h> #endif - using namespace zen; -const size_t BUFFER_SIZE_MAX = 800; //maximum number of icons to buffer - - -#ifdef FFS_WIN -Zstring getFileExtension(const Zstring& filename) -{ - const Zstring shortName = afterLast(filename, Zchar('\\')); //warning: using windows file name separator! - - return shortName.find(Zchar('.')) != Zstring::npos ? - filename.AfterLast(Zchar('.')) : - Zstring(); -} +const size_t BUFFER_SIZE_MAX = 800; //maximum number of icons to buffer -namespace -{ -std::set<Zstring, LessFilename> exceptions; //thread-safe! -boost::once_flag once = BOOST_ONCE_INIT; // -} -//test for extension for icons that physically have to be retrieved from disc -bool isPriceyExtension(const Zstring& extension) +int zen::IconBuffer::cvrtSize(IconSize sz) //get size in pixel { - boost::call_once(once, []() + switch (sz) { - exceptions.insert(L"exe"); - exceptions.insert(L"lnk"); - exceptions.insert(L"ico"); - exceptions.insert(L"ani"); - exceptions.insert(L"cur"); - exceptions.insert(L"url"); - exceptions.insert(L"msc"); - exceptions.insert(L"scr"); - }); - - return exceptions.find(extension) != exceptions.end(); -} + case SIZE_SMALL: +#ifdef FFS_WIN + return 16; +#elif defined FFS_LINUX + return 24; #endif + case SIZE_MEDIUM: + return 48; + case SIZE_LARGE: + return 128; + } + assert(false); + return 0; +} -//################################################################################################################################################ class IconHolder //handle HICON/GdkPixbuf ownership WITHOUT ref-counting to allow thread-safe usage (in contrast to wxIcon) { public: @@ -107,7 +86,7 @@ public: void swap(IconHolder& other) { std::swap(handle_, other.handle_); } //throw() - wxIcon toWxIcon() const //copy HandleType, caller needs to take ownership! + wxIcon toWxIcon(int expectedSize) const //copy HandleType, caller needs to take ownership! { if (handle_ == NULL) return wxNullIcon; @@ -116,8 +95,33 @@ public: wxIcon newIcon; //attention: wxIcon uses reference counting! #ifdef FFS_WIN - newIcon.SetHICON(clone.handle_); // - newIcon.SetSize(IconBuffer::ICON_SIZE, IconBuffer::ICON_SIZE); //icon is actually scaled to this size (just in case referenced HICON differs) + newIcon.SetHICON(clone.handle_); + + { //this block costs ~0.04 ms + ICONINFO icoInfo = {}; + if (::GetIconInfo(clone.handle_, &icoInfo)) + { + ::DeleteObject(icoInfo.hbmMask); //nice potential for a GDI leak! + LOKI_ON_BLOCK_EXIT2(::DeleteObject(icoInfo.hbmColor)); // + + BITMAP bmpInfo = {}; + if (::GetObject(icoInfo.hbmColor, //__in HGDIOBJ hgdiobj, + sizeof(BITMAP), //__in int cbBuffer, + &bmpInfo) != 0) // __out LPVOID lpvObject + { + const int maxExtent = std::max(bmpInfo.bmWidth, bmpInfo.bmHeight); + if (maxExtent > expectedSize) + { + bmpInfo.bmWidth = bmpInfo.bmWidth * expectedSize / maxExtent; //scale those Vista jumbo 256x256 icons down! + bmpInfo.bmHeight = bmpInfo.bmHeight * expectedSize / maxExtent; // + } + newIcon.SetSize(bmpInfo.bmWidth, bmpInfo.bmHeight); //wxIcon is stretched to this size + } + } + } + //no stretching for now + //newIcon.SetSize(defaultSize, defaultSize); //icon is stretched to this size if referenced HICON differs + #elif defined FFS_LINUX // newIcon.SetPixbuf(clone.handle_); // transfer ownership!! #endif // @@ -127,31 +131,206 @@ public: private: HandleType handle_; + struct ConversionToBool { int dummy; }; + +public: + //use member pointer as implicit conversion to bool (C++ Templates - Vandevoorde/Josuttis; chapter 20) + operator int ConversionToBool::* () const { return handle_ != NULL ? &ConversionToBool::dummy : NULL; } }; -IconHolder getAssociatedIcon(const Zstring& filename) -{ #ifdef FFS_WIN - //despite what docu says about SHGetFileInfo() it can't handle all relative filenames, e.g. "\DirName" - //but no problem, directory formatting takes care that filenames are always absolute! +namespace +{ +Zstring getFileExtension(const Zstring& filename) +{ + const Zstring shortName = afterLast(filename, Zchar('\\')); //warning: using windows file name separator! + + return shortName.find(Zchar('.')) != Zstring::npos ? + filename.AfterLast(Zchar('.')) : + Zstring(); +} + +std::set<Zstring, LessFilename> priceyExtensions; //thread-safe! +boost::once_flag initExtensionsOnce = BOOST_ONCE_INIT; // - SHFILEINFO fileInfo = {}; //initialize hIcon -> fix for weird error: SHGetFileInfo() might return successfully WITHOUT filling fileInfo.hIcon!! - //bug report: https://sourceforge.net/tracker/?func=detail&aid=2768004&group_id=234430&atid=1093080 +//test for extension for non-thumbnail icons that physically have to be retrieved from disc +bool isCheapExtension(const Zstring& extension) +{ + boost::call_once(initExtensionsOnce, []() + { + priceyExtensions.insert(L"exe"); + priceyExtensions.insert(L"lnk"); + priceyExtensions.insert(L"ico"); + priceyExtensions.insert(L"ani"); + priceyExtensions.insert(L"cur"); + priceyExtensions.insert(L"url"); + priceyExtensions.insert(L"msc"); + priceyExtensions.insert(L"scr"); + }); + return priceyExtensions.find(extension) == priceyExtensions.end(); +} + + +bool vistaOrLater() +{ + OSVERSIONINFO osvi = {}; + osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); + + //IFileOperation is supported with Vista and later + if (::GetVersionEx(&osvi)) + return osvi.dwMajorVersion > 5; + //XP has majorVersion == 5, minorVersion == 1 + //Vista has majorVersion == 6, minorVersion == 0 + //version overview: http://msdn.microsoft.com/en-us/library/ms724834(VS.85).aspx + return false; +} + + +bool wereVistaOrLater = false; +boost::once_flag initVistaFlagOnce = BOOST_ONCE_INIT; + + +int getShilIconType(IconBuffer::IconSize sz) +{ + boost::call_once(initVistaFlagOnce, []() + { + wereVistaOrLater = vistaOrLater(); + }); + + switch (sz) + { + case IconBuffer::SIZE_SMALL: + return SHIL_SMALL; //16x16, but the size can be customized by the user. + case IconBuffer::SIZE_MEDIUM: + return SHIL_EXTRALARGE; //typically 48x48, but the size can be customized by the user. + case IconBuffer::SIZE_LARGE: + return wereVistaOrLater ? SHIL_JUMBO //normally 256x256 pixels -> will be scaled down by IconHolder + : SHIL_EXTRALARGE; //XP doesn't have jumbo icons + } + return SHIL_SMALL; +} + + +util::DllFun<thumb::GetIconByIndexFct> getIconByIndex; +boost::once_flag initGetIconByIndexOnce = BOOST_ONCE_INIT; + + +IconHolder getIconByAttribute(LPCWSTR pszPath, DWORD dwFileAttributes, IconBuffer::IconSize sz) +{ //NOTE: CoInitializeEx()/CoUninitialize() needs to be called for THIS thread! - ::SHGetFileInfo(filename.c_str(), //zen::removeLongPathPrefix(fileName), //::SHGetFileInfo() can't handle \\?\-prefix! - 0, - &fileInfo, - sizeof(fileInfo), - SHGFI_ICON | SHGFI_SMALLICON); + SHFILEINFO fileInfo = {}; //initialize hIcon + DWORD_PTR imgList = ::SHGetFileInfo(pszPath, //Windows 7 doesn't like this parameter to be an empty string + dwFileAttributes, + &fileInfo, + sizeof(fileInfo), + SHGFI_SYSICONINDEX | SHGFI_USEFILEATTRIBUTES); + //no need to IUnknown::Release() imgList! + if (!imgList) + return NULL; + + boost::call_once(initGetIconByIndexOnce, []() //thread-safe init + { + getIconByIndex = util::DllFun<thumb::GetIconByIndexFct>(thumb::getDllName(), thumb::getIconByIndexFctName); + }); + return getIconByIndex ? static_cast<HICON>(getIconByIndex(fileInfo.iIcon, getShilIconType(sz))) : NULL; +} + + +IconHolder getAssociatedIconByExt(const Zstring& extension, IconBuffer::IconSize sz) +{ + //no read-access to disk! determine icon by extension + return getIconByAttribute((Zstr("dummy.") + extension).c_str(), FILE_ATTRIBUTE_NORMAL, sz); +} + + +util::DllFun<thumb::GetThumbnailFct> getThumbnailIcon; +boost::once_flag initThumbnailOnce = BOOST_ONCE_INIT; +} +#endif +//################################################################################################################################################ + + +IconHolder getThumbnail(const Zstring& filename, int requestedSize) //return 0 on failure +{ +#ifdef FFS_WIN + using namespace thumb; - return IconHolder(fileInfo.hIcon); //pass icon ownership (may be 0) + boost::call_once(initThumbnailOnce, []() //note: "getThumbnail" function itself is already thread-safe + { + getThumbnailIcon = util::DllFun<GetThumbnailFct>(getDllName(), getThumbnailFctName); + }); + return getThumbnailIcon ? static_cast< ::HICON>(getThumbnailIcon(filename.c_str(), requestedSize)) : NULL; #elif defined FFS_LINUX //call Gtk::Main::init_gtkmm_internals() on application startup!! try { + Glib::RefPtr<Gdk::Pixbuf> iconPixbuf = Gdk::Pixbuf::create_from_file(filename.c_str(), requestedSize, requestedSize); + if (iconPixbuf) + return IconHolder(iconPixbuf->gobj_copy()); //copy and pass icon ownership (may be 0) + } + catch (const Glib::Error&) {} + + return IconHolder(); +#endif +} + + +IconHolder getAssociatedIcon(const Zstring& filename, IconBuffer::IconSize sz) +{ + //1. try to load thumbnails + switch (sz) + { + case IconBuffer::SIZE_SMALL: + break; + case IconBuffer::SIZE_MEDIUM: + case IconBuffer::SIZE_LARGE: + { + IconHolder ico = getThumbnail(filename, IconBuffer::cvrtSize(sz)); + if (ico) + return ico; + //else: fallback to non-thumbnail icon + } + break; + } + + //2. retrieve file icons +#ifdef FFS_WIN + //perf: optimize fallback case for SIZE_MEDIUM and SIZE_LARGE: + const Zstring& extension = getFileExtension(filename); + if (isCheapExtension(extension)) //"pricey" extensions are stored with fullnames and are read from disk, while cheap ones require just the extension + return getAssociatedIconByExt(extension, sz); + //result will not be buffered under extension name, but full filename; this is okay, since we're in SIZE_MEDIUM or SIZE_LARGE context, + //which means the access to get thumbnail failed: thumbnail failure is not dependent from extension in general! + + SHFILEINFO fileInfo = {}; + DWORD_PTR imgList = ::SHGetFileInfo(filename.c_str(), //zen::removeLongPathPrefix(fileName), //::SHGetFileInfo() can't handle \\?\-prefix! + 0, + &fileInfo, + sizeof(fileInfo), + SHGFI_SYSICONINDEX); + //Quote: "The IImageList pointer type, such as that returned in the ppv parameter, can be cast as an HIMAGELIST as + // needed; for example, for use in a list view. Conversely, an HIMAGELIST can be cast as a pointer to an IImageList." + //http://msdn.microsoft.com/en-us/library/windows/desktop/bb762185(v=vs.85).aspx + + if (!imgList) + return NULL; + //imgList->Release(); //empiric study: crash on XP if we release this! Seems we do not own it... -> also no GDI leak on Win7 -> okay + //another comment on http://msdn.microsoft.com/en-us/library/bb762179(v=VS.85).aspx describes exact same behavior on Win7/XP + + boost::call_once(initGetIconByIndexOnce, []() //thread-safe init + { + getIconByIndex = util::DllFun<thumb::GetIconByIndexFct>(thumb::getDllName(), thumb::getIconByIndexFctName); + }); + return getIconByIndex ? static_cast<HICON>(getIconByIndex(fileInfo.iIcon, getShilIconType(sz))) : NULL; + +#elif defined FFS_LINUX + const int requestedSize = IconBuffer::cvrtSize(sz); + //call Gtk::Main::init_gtkmm_internals() on application startup!! + try + { Glib::RefPtr<Gio::File> fileObj = Gio::File::create_for_path(filename.c_str()); //never fails Glib::RefPtr<Gio::FileInfo> fileInfo = fileObj->query_info(G_FILE_ATTRIBUTE_STANDARD_ICON); if (fileInfo) @@ -162,7 +341,7 @@ IconHolder getAssociatedIcon(const Zstring& filename) Glib::RefPtr<Gtk::IconTheme> iconTheme = Gtk::IconTheme::get_default(); if (iconTheme) { - Gtk::IconInfo iconInfo = iconTheme->lookup_icon(gicon, IconBuffer::ICON_SIZE, Gtk::ICON_LOOKUP_USE_BUILTIN); //this may fail if icon is not installed on system + Gtk::IconInfo iconInfo = iconTheme->lookup_icon(gicon, requestedSize, Gtk::ICON_LOOKUP_USE_BUILTIN); //this may fail if icon is not installed on system if (iconInfo) { Glib::RefPtr<Gdk::Pixbuf> iconPixbuf = iconInfo.load_icon(); //render icon into Pixbuf @@ -180,9 +359,9 @@ IconHolder getAssociatedIcon(const Zstring& filename) Glib::RefPtr<Gtk::IconTheme> iconTheme = Gtk::IconTheme::get_default(); if (iconTheme) { - Glib::RefPtr<Gdk::Pixbuf> iconPixbuf = iconTheme->load_icon("misc", IconBuffer::ICON_SIZE, Gtk::ICON_LOOKUP_USE_BUILTIN); + Glib::RefPtr<Gdk::Pixbuf> iconPixbuf = iconTheme->load_icon("misc", requestedSize, Gtk::ICON_LOOKUP_USE_BUILTIN); if (!iconPixbuf) - iconPixbuf = iconTheme->load_icon("text-x-generic", IconBuffer::ICON_SIZE, Gtk::ICON_LOOKUP_USE_BUILTIN); + iconPixbuf = iconTheme->load_icon("text-x-generic", requestedSize, Gtk::ICON_LOOKUP_USE_BUILTIN); if (iconPixbuf) return IconHolder(iconPixbuf->gobj_copy()); //copy and pass icon ownership (may be 0) } @@ -194,91 +373,44 @@ IconHolder getAssociatedIcon(const Zstring& filename) #endif } -#ifdef FFS_WIN -IconHolder getAssociatedIconByExt(const Zstring& extension) -{ - SHFILEINFO fileInfo = {}; //initialize hIcon -> fix for weird error: SHGetFileInfo() might return successfully WITHOUT filling fileInfo.hIcon!! - - //no read-access to disk! determine icon by extension - ::SHGetFileInfo((Zstr("dummy.") + extension).c_str(), //Windows Seven doesn't like this parameter to be without short name - FILE_ATTRIBUTE_NORMAL, - &fileInfo, - sizeof(fileInfo), - SHGFI_ICON | SHGFI_SMALLICON | SHGFI_USEFILEATTRIBUTES); - - return IconHolder(fileInfo.hIcon); //pass icon ownership (may be 0) -} -#endif - -wxIcon getDirectoryIcon() +IconHolder getDirectoryIcon(IconBuffer::IconSize sz) { #ifdef FFS_WIN - wxIcon folderIcon; - - SHFILEINFO fileInfo = {}; //initialize hIcon - - //NOTE: CoInitializeEx()/CoUninitialize() needs to be called for THIS thread! - if (::SHGetFileInfo(Zstr("dummy"), //Windows Seven doesn't like this parameter to be an empty string - FILE_ATTRIBUTE_DIRECTORY, - &fileInfo, - sizeof(fileInfo), - SHGFI_ICON | SHGFI_SMALLICON | SHGFI_USEFILEATTRIBUTES) && - - fileInfo.hIcon != 0) //fix for weird error: SHGetFileInfo() might return successfully WITHOUT filling fileInfo.hIcon!! - { - folderIcon.SetHICON(fileInfo.hIcon); //transfer ownership! - folderIcon.SetSize(IconBuffer::ICON_SIZE, IconBuffer::ICON_SIZE); - } - - return folderIcon; - + return getIconByAttribute(L"dummy", //Windows 7 doesn't like this parameter to be an empty string! + FILE_ATTRIBUTE_DIRECTORY, sz); #elif defined FFS_LINUX - return ::getAssociatedIcon(Zstr("/usr/")).toWxIcon(); //all directories will look like "/usr/" + return ::getAssociatedIcon(Zstr("/usr/"), sz); //all directories will look like "/usr/" #endif } -wxIcon getFileIcon() +IconHolder getFileIcon(IconBuffer::IconSize sz) { - wxIcon fileIcon; - #ifdef FFS_WIN - SHFILEINFO fileInfo = {}; //initialize hIcon - - //NOTE: CoInitializeEx()/CoUninitialize() needs to be called for THIS thread! - if (::SHGetFileInfo(Zstr("dummy"), //Windows Seven doesn't like this parameter to be an empty string - FILE_ATTRIBUTE_NORMAL, - &fileInfo, - sizeof(fileInfo), - SHGFI_ICON | SHGFI_SMALLICON | SHGFI_USEFILEATTRIBUTES) && - - fileInfo.hIcon != 0) //fix for weird error: SHGetFileInfo() might return successfully WITHOUT filling fileInfo.hIcon!! - { - fileIcon.SetHICON(fileInfo.hIcon); //transfer ownership! - fileIcon.SetSize(IconBuffer::ICON_SIZE, IconBuffer::ICON_SIZE); - } - + return getIconByAttribute(L"dummy", FILE_ATTRIBUTE_NORMAL, sz); #elif defined FFS_LINUX + const int requestedSize = IconBuffer::cvrtSize(sz); try { Glib::RefPtr<Gtk::IconTheme> iconTheme = Gtk::IconTheme::get_default(); if (iconTheme) { - Glib::RefPtr<Gdk::Pixbuf> iconPixbuf = iconTheme->load_icon("misc", IconBuffer::ICON_SIZE, Gtk::ICON_LOOKUP_USE_BUILTIN); + Glib::RefPtr<Gdk::Pixbuf> iconPixbuf = iconTheme->load_icon("misc", requestedSize, Gtk::ICON_LOOKUP_USE_BUILTIN); if (!iconPixbuf) - iconPixbuf = iconTheme->load_icon("text-x-generic", IconBuffer::ICON_SIZE, Gtk::ICON_LOOKUP_USE_BUILTIN); + iconPixbuf = iconTheme->load_icon("text-x-generic", requestedSize, Gtk::ICON_LOOKUP_USE_BUILTIN); if (iconPixbuf) - fileIcon.SetPixbuf(iconPixbuf->gobj_copy()); // transfer ownership!! + return IconHolder(iconPixbuf->gobj_copy()); // transfer ownership!! } } catch (const Glib::Error&) {} + + return IconHolder(); #endif - return fileIcon; } +//################################################################################################################################################ -//################################################################################################################################################ //---------------------- Shared Data ------------------------- struct WorkLoad { @@ -318,73 +450,64 @@ typedef std::queue<Zstring> IconDbSequence; //entryName class Buffer { public: - bool requestFileIcon(const Zstring& fileName, wxIcon* icon = NULL); - void insertIntoBuffer(const Zstring& entryName, const IconHolder& icon); //called by worker thread - -private: - boost::mutex lockBuffer; - NameIconMap iconMappping; //use synchronisation when accessing this! - IconDbSequence iconSequence; //save sequence of buffer entry to delete oldest elements -}; -//------------------------------------------------------------ - - -bool Buffer::requestFileIcon(const Zstring& fileName, wxIcon* icon) //context of main AND worker thread -{ - boost::lock_guard<boost::mutex> dummy(lockBuffer); - -#ifdef FFS_WIN - //"pricey" extensions are stored with fullnames and are read from disk, while cheap ones require just the extension - const Zstring extension = getFileExtension(fileName); - const Zstring searchString = isPriceyExtension(extension) ? fileName : extension; - auto iter = iconMappping.find(searchString); -#elif defined FFS_LINUX - auto iter = iconMappping.find(fileName); -#endif + bool requestFileIcon(const Zstring& fileName, IconHolder* icon = NULL) + { + boost::lock_guard<boost::mutex> dummy(lockBuffer); - if (iter == iconMappping.end()) + auto iter = iconMappping.find(fileName); + if (iter != iconMappping.end()) + { + if (icon != NULL) + *icon = iter->second; + return true; + } return false; + } - if (icon != NULL) - *icon = iter->second.toWxIcon(); - return true; -} - - -void Buffer::insertIntoBuffer(const Zstring& entryName, const IconHolder& icon) //called by worker thread -{ - boost::lock_guard<boost::mutex> dummy(lockBuffer); + void insertIntoBuffer(const Zstring& entryName, const IconHolder& icon) //called by worker thread + { + boost::lock_guard<boost::mutex> dummy(lockBuffer); - //thread saftey: icon uses ref-counting! But is NOT shared with main thread! - auto rc = iconMappping.insert(std::make_pair(entryName, icon)); - if (rc.second) //if insertion took place - iconSequence.push(entryName); //note: sharing Zstring with IconDB!!! + //thread saftey: icon uses ref-counting! But is NOT shared with main thread! + auto rc = iconMappping.insert(std::make_pair(entryName, icon)); + if (rc.second) //if insertion took place + iconSequence.push(entryName); //note: sharing Zstring with IconDB!!! - assert(iconMappping.size() == iconSequence.size()); + assert(iconMappping.size() == iconSequence.size()); - //remove elements if buffer becomes too big: - if (iconMappping.size() > BUFFER_SIZE_MAX) //limit buffer size: critical because GDI resources are limited (e.g. 10000 on XP per process) - { - //remove oldest element - iconMappping.erase(iconSequence.front()); - iconSequence.pop(); + //remove elements if buffer becomes too big: + if (iconMappping.size() > BUFFER_SIZE_MAX) //limit buffer size: critical because GDI resources are limited (e.g. 10000 on XP per process) + { + //remove oldest element + iconMappping.erase(iconSequence.front()); + iconSequence.pop(); + } } -} + +private: + boost::mutex lockBuffer; + NameIconMap iconMappping; //use synchronisation when accessing this! + IconDbSequence iconSequence; //save sequence of buffer entry to delete oldest elements +}; +//------------------------------------------------------------- //################################################################################################################################################ class WorkerThread //lifetime is part of icon buffer { public: WorkerThread(const std::shared_ptr<WorkLoad>& workload, - const std::shared_ptr<Buffer>& buffer) : + const std::shared_ptr<Buffer>& buffer, + IconBuffer::IconSize sz) : workload_(workload), - buffer_(buffer) {} + buffer_(buffer), + icoSize(sz) {} void operator()(); //thread entry private: std::shared_ptr<WorkLoad> workload_; //main/worker thread may access different shared_ptr instances safely (even though they have the same target!) std::shared_ptr<Buffer> buffer_; //http://www.boost.org/doc/libs/1_43_0/libs/smart_ptr/shared_ptr.htm?sess=8153b05b34d890e02d48730db1ff7ddc#ThreadSafety + const IconBuffer::IconSize icoSize; }; @@ -392,9 +515,18 @@ void WorkerThread::operator()() //thread entry { //failure to initialize COM for each thread is a source of hard to reproduce bugs: https://sourceforge.net/tracker/?func=detail&aid=3160472&group_id=234430&atid=1093080 #ifdef FFS_WIN - ::CoInitializeEx(NULL, COINIT_MULTITHREADED | COINIT_DISABLE_OLE1DDE); - Loki::ScopeGuard dummy = Loki::MakeGuard([]() { ::CoUninitialize(); }); - (void)dummy; + //Prerequisites, see thumbnail.h + + //1. Initialize COM + ::CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE); + LOKI_ON_BLOCK_EXIT2(::CoUninitialize()); + + //2. Initialize system image list + typedef BOOL (WINAPI *FileIconInitFun)(BOOL fRestoreCache); + const util::DllFun<FileIconInitFun> fileIconInit(L"Shell32.dll", reinterpret_cast<LPCSTR>(660)); + assert(fileIconInit); + if (fileIconInit) + fileIconInit(false); //TRUE to restore the system image cache from disk; FALSE otherwise. #endif while (true) @@ -406,20 +538,9 @@ void WorkerThread::operator()() //thread entry if (buffer_->requestFileIcon(fileName)) continue; //icon already in buffer: skip -#ifdef FFS_WIN - const Zstring extension = getFileExtension(fileName); - if (isPriceyExtension(extension)) //"pricey" extensions are stored with fullnames and are read from disk, while cheap ones require just the extension - buffer_->insertIntoBuffer(fileName, getAssociatedIcon(fileName)); - else //no read-access to disk! determine icon by extension - buffer_->insertIntoBuffer(extension, getAssociatedIconByExt(extension)); - -#elif defined FFS_LINUX - const IconHolder newIcon = getAssociatedIcon(fileName); - buffer_->insertIntoBuffer(fileName, newIcon); -#endif + buffer_->insertIntoBuffer(fileName, getAssociatedIcon(fileName, icoSize)); } } - //######################### redirect to impl ##################################################### struct IconBuffer::Pimpl @@ -435,9 +556,13 @@ struct IconBuffer::Pimpl }; -IconBuffer::IconBuffer() : pimpl(new Pimpl) +IconBuffer::IconBuffer(IconSize sz) : + pimpl(new Pimpl), + icoSize(sz), + genDirIcon(::getDirectoryIcon(sz).toWxIcon(cvrtSize(icoSize))), + genFileIcon(::getFileIcon(sz).toWxIcon(cvrtSize(icoSize))) { - pimpl->worker = boost::thread(WorkerThread(pimpl->workload, pimpl->buffer)); + pimpl->worker = boost::thread(WorkerThread(pimpl->workload, pimpl->buffer, sz)); } @@ -449,24 +574,40 @@ IconBuffer::~IconBuffer() } -IconBuffer& IconBuffer::getInstance() +bool IconBuffer::requestFileIcon(const Zstring& filename, wxIcon* icon) { - static IconBuffer instance; - return instance; -} + auto getIcon = [&](const Zstring& entryName) -> bool + { + if (!icon) + return pimpl->buffer->requestFileIcon(entryName); -bool IconBuffer::requestFileIcon(const Zstring& fileName, wxIcon* icon) { return pimpl->buffer->requestFileIcon(fileName, icon); } + IconHolder heldIcon; + if (!pimpl->buffer->requestFileIcon(entryName, &heldIcon)) + return false; + *icon = heldIcon.toWxIcon(cvrtSize(icoSize)); + return true; + }; -void IconBuffer::setWorkload(const std::vector<Zstring>& load) { pimpl->workload->setWorkload(load); } +#ifdef FFS_WIN + //perf: let's read icons which don't need file access right away! No async delay justified! + if (icoSize == IconBuffer::SIZE_SMALL) //non-thumbnail view, we need file type icons only! + { + const Zstring& extension = getFileExtension(filename); + if (isCheapExtension(extension)) //"pricey" extensions are stored with fullnames and are read from disk, while cheap ones require just the extension + { + if (!getIcon(extension)) + { + IconHolder heldIcon = getAssociatedIconByExt(extension, icoSize); //fast! + pimpl->buffer->insertIntoBuffer(extension, heldIcon); + if (icon) + *icon = heldIcon.toWxIcon(cvrtSize(icoSize)); + } + return true; + } + } +#endif -const wxIcon& IconBuffer::getDirectoryIcon() -{ - static wxIcon dirIcon = ::getDirectoryIcon(); - return dirIcon; + return getIcon(filename); } -const wxIcon& IconBuffer::getFileIcon() -{ - static wxIcon fileIcon = ::getFileIcon(); - return fileIcon; -} +void IconBuffer::setWorkload(const std::vector<Zstring>& load) { pimpl->workload->setWorkload(load); } |