summaryrefslogtreecommitdiff
path: root/library/iconBuffer.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'library/iconBuffer.cpp')
-rw-r--r--library/iconBuffer.cpp145
1 files changed, 119 insertions, 26 deletions
diff --git a/library/iconBuffer.cpp b/library/iconBuffer.cpp
index d386c85a..21f85372 100644
--- a/library/iconBuffer.cpp
+++ b/library/iconBuffer.cpp
@@ -7,10 +7,82 @@
#include <map>
#include <queue>
#include <stdexcept>
+#include <set>
using FreeFileSync::IconBuffer;
+const wxIcon& IconBuffer::getDirectoryIcon() //one folder icon should be sufficient...
+{
+ static wxIcon folderIcon;
+
+ static bool isInitalized = false;
+ if (!isInitalized)
+ {
+ isInitalized = true;
+
+ SHFILEINFO fileInfo;
+ fileInfo.hIcon = 0; //initialize hIcon
+
+ //NOTE: CoInitializeEx()/CoUninitialize() implicitly called by wxWidgets on program startup!
+ if (::SHGetFileInfo(DefaultStr("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);
+ folderIcon.SetSize(IconBuffer::ICON_SIZE, IconBuffer::ICON_SIZE);
+ }
+ }
+ return folderIcon;
+}
+
+namespace
+{
+Zstring getFileExtension(const Zstring& filename)
+{
+ const Zstring shortName = filename.AfterLast(DefaultChar('\\')); //Zstring::AfterLast() returns the whole string if ch not found
+ const size_t pos = shortName.Find(DefaultChar('.'), true);
+ return pos == Zstring::npos ?
+ Zstring() :
+ Zstring(shortName.c_str() + pos + 1);
+}
+
+
+struct CmpFilenameWin
+{
+ bool operator()(const Zstring& a, const Zstring& b) const
+ {
+ return a.CmpNoCase(b) < 0;
+ }
+};
+
+
+//test for extension for icons that physically have to be retrieved from disc
+bool isPriceyExtension(const Zstring& extension)
+{
+ static std::set<Zstring, CmpFilenameWin> exceptions;
+ static bool isInitalized = false;
+ if (!isInitalized)
+ {
+ isInitalized = true;
+ exceptions.insert(DefaultStr("exe"));
+ exceptions.insert(DefaultStr("lnk"));
+ exceptions.insert(DefaultStr("ico"));
+ exceptions.insert(DefaultStr("ani"));
+ exceptions.insert(DefaultStr("cur"));
+ exceptions.insert(DefaultStr("url"));
+ exceptions.insert(DefaultStr("msc"));
+ exceptions.insert(DefaultStr("scr"));
+ }
+ return exceptions.find(extension) != exceptions.end();
+}
+}
+//################################################################################################################################################
+
typedef std::vector<DefaultChar> BasicString; //simple thread safe string class: std::vector is guaranteed to not use reference counting, Effective STL, item 13
@@ -112,7 +184,7 @@ wxThread::ExitCode WorkerThread::Entry()
if (threadExitIsRequested) //no mutex here: atomicity is not prob for a bool, but visibility (e.g. caching in registers)
return 0; //shouldn't be a problem nevertheless because of implicit memory barrier caused by mutex.Lock() in .Wait()
- //do work: get the file icon.
+ //do work: get the file icons
doWork();
}
}
@@ -139,29 +211,25 @@ void WorkerThread::doWork()
workload.pop_back();
}
- if (iconBuffer->requestIcon(Zstring(&fileName[0]))) //thread safety: Zstring okay, won't be reference-counted in requestIcon(), fileName is NOT empty
- break; //icon already in buffer: enter waiting state
+ if (iconBuffer->requestFileIcon(Zstring(&fileName[0]))) //thread safety: Zstring okay, won't be reference-counted in requestIcon(), fileName is NOT empty
+ continue; //icon already in buffer: skip
//despite what docu says about SHGetFileInfo() it can't handle all relative filenames, e.g. "\DirName"
- const unsigned int MAX_SIZE = 10000;
- DefaultChar fullName[MAX_SIZE];
- const DWORD rv = ::GetFullPathName(
- &fileName[0], //__in LPCTSTR lpFileName,
- MAX_SIZE, //__in DWORD nBufferLength,
- fullName, //__out LPTSTR lpBuffer,
- NULL); //__out LPTSTR *lpFilePart
- if (rv < MAX_SIZE && rv != 0)
- {
- //load icon
- SHFILEINFO fileInfo;
- fileInfo.hIcon = 0; //initialize hIcon
+ //but no problem, directory formatting takes care that filenames are always absolute!
- if (::SHGetFileInfo(fullName, //NOTE: CoInitializeEx()/CoUninitialize() implicitly called by wxWidgets on program startup!
+ //load icon
+ SHFILEINFO fileInfo;
+ fileInfo.hIcon = 0; //initialize hIcon
+
+ const Zstring extension = getFileExtension(&fileName[0]); //thread-safe: no sharing!
+ if (isPriceyExtension(extension)) //"pricey" extensions are stored with fullnames and are read from disk, while cheap ones require just the extension
+ {
+ //NOTE: CoInitializeEx()/CoUninitialize() implicitly called by wxWidgets on program startup!
+ if (::SHGetFileInfo(&fileName[0], //FreeFileSync::removeLongPathPrefix(&fileName[0]), //::SHGetFileInfo() can't handle \\?\-prefix!
0,
&fileInfo,
sizeof(fileInfo),
SHGFI_ICON | SHGFI_SMALLICON) &&
-
fileInfo.hIcon != 0) //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
@@ -170,7 +238,8 @@ void WorkerThread::doWork()
newIcon.SetHICON(fileInfo.hIcon);
newIcon.SetSize(IconBuffer::ICON_SIZE, IconBuffer::ICON_SIZE);
- iconBuffer->insertIntoBuffer(&fileName[0], newIcon); //thread safety: icon may be deleted only within insertIntoBuffer()
+ iconBuffer->insertIntoBuffer(&fileName[0], newIcon); //thread safety: icon buffer is written by this thread and this call only, so
+ //newIcon can safely go out of scope without race-condition because of ref-counting
//freeing of icon handle seems to happen somewhere beyond wxIcon destructor
//if (!DestroyIcon(fileInfo.hIcon))
@@ -178,16 +247,32 @@ void WorkerThread::doWork()
continue;
}
}
+ else //no read-access to disk! determine icon by extension
+ {
+ if (::SHGetFileInfo((Zstring(DefaultStr("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) &&
+ fileInfo.hIcon != 0) //fix for weird error: SHGetFileInfo() might return successfully WITHOUT filling fileInfo.hIcon!!
+ {
+ wxIcon newIcon; //attention: wxIcon uses reference counting!
+ newIcon.SetHICON(fileInfo.hIcon);
+ newIcon.SetSize(IconBuffer::ICON_SIZE, IconBuffer::ICON_SIZE);
+
+ iconBuffer->insertIntoBuffer(extension.c_str(), newIcon); //thread safety: icon buffer is written by this thread and this call only, so
+ continue;
+ }
+ }
+
//if loading of icon fails for whatever reason, just save a dummy icon to avoid re-loading
iconBuffer->insertIntoBuffer(&fileName[0], wxNullIcon);
}
}
//---------------------------------------------------------------------------------------------------
-
-typedef Zstring FileName;
-class IconDB : public std::map<FileName, wxIcon> {};
-class IconDbSequence : public std::queue<FileName> {};
+class IconDB : public std::map<Zstring, wxIcon> {}; // entryName/icon
+class IconDbSequence : public std::queue<Zstring> {}; // entryName
//---------------------------------------------------------------------------------------------------
@@ -213,11 +298,16 @@ IconBuffer::~IconBuffer()
}
-bool IconBuffer::requestIcon(const Zstring& fileName, wxIcon* icon)
+bool IconBuffer::requestFileIcon(const Zstring& fileName, wxIcon* icon)
{
+ const Zstring extension = getFileExtension(fileName);
+
wxCriticalSectionLocker dummy(*lockIconDB);
- IconDB::const_iterator i = buffer->find(fileName);
+ IconDB::const_iterator i = buffer->find( //"pricey" extensions are stored with fullnames and are read from disk, while cheap ones require just the extension
+ isPriceyExtension(extension) ?
+ fileName :
+ extension);
if (i != buffer->end())
{
if (icon != NULL)
@@ -235,13 +325,13 @@ void IconBuffer::setWorkload(const std::vector<Zstring>& load)
}
-void IconBuffer::insertIntoBuffer(const DefaultChar* fileName, const wxIcon& icon) //called by worker thread
+void IconBuffer::insertIntoBuffer(const DefaultChar* entryName, const wxIcon& icon) //called by worker thread
{
if (icon.IsOk()) //this check won't hurt
{
wxCriticalSectionLocker dummy(*lockIconDB);
- const Zstring fileNameZ = fileName;
+ const Zstring fileNameZ = entryName;
const std::pair<IconDB::iterator, bool> rc = buffer->insert(IconDB::value_type(fileNameZ, icon));
@@ -260,3 +350,6 @@ void IconBuffer::insertIntoBuffer(const DefaultChar* fileName, const wxIcon& ico
}
}
+
+
+
bgstack15