summaryrefslogtreecommitdiff
path: root/library/iconBuffer.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'library/iconBuffer.cpp')
-rw-r--r--library/iconBuffer.cpp346
1 files changed, 223 insertions, 123 deletions
diff --git a/library/iconBuffer.cpp b/library/iconBuffer.cpp
index 338f53df..fa0b1673 100644
--- a/library/iconBuffer.cpp
+++ b/library/iconBuffer.cpp
@@ -7,7 +7,6 @@
#include "iconBuffer.h"
#include <wx/thread.h>
#include <wx/bitmap.h>
-#include <wx/msw/wrapwin.h> //includes "windows.h"
#include <wx/msgdlg.h>
#include <wx/icon.h>
#include <map>
@@ -15,42 +14,34 @@
#include <stdexcept>
#include <set>
-using FreeFileSync::IconBuffer;
+#ifdef FFS_WIN
+#include <wx/msw/wrapwin.h> //includes "windows.h"
+#elif defined FFS_LINUX
+#include <giomm/file.h>
+#include <gtkmm/icontheme.h>
+#include <gtkmm/main.h>
+#endif
-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) &&
+using FreeFileSync::IconBuffer;
- 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
{
+struct CmpFilename
+{
+ bool operator()(const Zstring& a, const Zstring& b) const
+ {
+ return a.cmpFileName(b) < 0;
+ }
+};
+
+
+#ifdef FFS_WIN
Zstring getFileExtension(const Zstring& filename)
{
- const Zstring shortName = filename.AfterLast(DefaultChar('\\')); //Zstring::AfterLast() returns the whole string if ch not found
+ const Zstring shortName = filename.AfterLast(DefaultChar('\\')); //warning: using windows file name separator!
const size_t pos = shortName.Find(DefaultChar('.'), true);
return pos == Zstring::npos ?
Zstring() :
@@ -58,19 +49,10 @@ Zstring getFileExtension(const Zstring& filename)
}
-struct CmpFilenameWin
-{
- bool operator()(const Zstring& a, const Zstring& b) const
- {
- return a.cmpFileName(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 std::set<Zstring, CmpFilename> exceptions;
static bool isInitalized = false;
if (!isInitalized)
{
@@ -86,39 +68,198 @@ bool isPriceyExtension(const Zstring& extension)
}
return exceptions.find(extension) != exceptions.end();
}
+#endif
}
//################################################################################################################################################
-class IconBuffer::IconHolder //handle HICON ownership WITHOUT ref-counting to allow a deep-copy (in contrast to wxIcon)
+class IconBuffer::IconHolder //handle HICON/GdkPixbuf ownership WITHOUT ref-counting to allow thread-safe usage (in contrast to wxIcon)
{
public:
- IconHolder(HICON handle = 0) : handle_(handle) {}
+#ifdef FFS_WIN
+ typedef HICON HandleType;
+#elif defined FFS_LINUX
+ typedef GdkPixbuf* HandleType;
+#endif
+
+ IconHolder(HandleType handle = 0) : handle_(handle) {} //take ownership!
+
+ //icon holder has value semantics!
+ IconHolder(const IconHolder& other) : handle_(other.handle_ == 0 ? 0 :
+#ifdef FFS_WIN
+ ::CopyIcon(other.handle_)
+#elif defined FFS_LINUX
+ gdk_pixbuf_copy(other.handle_) //create new Pix buf with reference count 1 or return 0 on error
+#endif
+ ) {}
+
+ IconHolder& operator=(const IconHolder& other)
+ {
+ IconHolder(other).swap(*this);
+ return *this;
+ }
~IconHolder()
{
if (handle_ != 0)
+#ifdef FFS_WIN
::DestroyIcon(handle_);
+#elif defined FFS_LINUX
+ g_object_unref(handle_);
+#endif
}
- HICON clone() const //copy HICON, caller needs to take ownership!
+ void swap(IconHolder& other) //throw()
{
- return handle_ != 0 ? ::CopyIcon(handle_) : 0;
+ std::swap(handle_, other.handle_);
}
- void swap(IconHolder& other) //throw()
+ wxIcon toWxIcon() const //copy HandleType, caller needs to take ownership!
{
- std::swap(handle_, other.handle_);
+ IconHolder clone(*this);
+ if (clone.handle_ != 0)
+ {
+ 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)
+#elif defined FFS_LINUX //
+ newIcon.SetPixbuf(clone.handle_); // transfer ownership!!
+#endif //
+ clone.handle_ = 0; //
+ return newIcon;
+ }
+ return wxNullIcon;
}
private:
- IconHolder(const IconHolder&);
- IconHolder& operator=(const IconHolder&);
-
- HICON handle_;
+ HandleType handle_;
};
+const wxIcon& IconBuffer::getDirectoryIcon() //one folder icon should be sufficient...
+{
+ static wxIcon folderIcon;
+
+ static bool isInitalized = false;
+ if (!isInitalized)
+ {
+ isInitalized = true;
+
+#ifdef FFS_WIN
+ 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); //transfer ownership!
+ folderIcon.SetSize(IconBuffer::ICON_SIZE, IconBuffer::ICON_SIZE);
+ }
+
+#elif defined FFS_LINUX
+ folderIcon = getAssociatedIcon(DefaultStr("/usr/")).toWxIcon(); //all directories will look like "/usr/"
+#endif
+ }
+ return folderIcon;
+}
+
+
+IconBuffer::IconHolder IconBuffer::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!
+
+ SHFILEINFO fileInfo;
+ fileInfo.hIcon = 0; //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
+
+ //NOTE: CoInitializeEx()/CoUninitialize() implicitly called by wxWidgets on program startup!
+ ::SHGetFileInfo(filename.c_str(), //FreeFileSync::removeLongPathPrefix(fileName), //::SHGetFileInfo() can't handle \\?\-prefix!
+ 0,
+ &fileInfo,
+ sizeof(fileInfo),
+ SHGFI_ICON | SHGFI_SMALLICON);
+
+ return IconHolder(fileInfo.hIcon); //pass icon ownership (may be 0)
+
+#elif defined FFS_LINUX
+ static struct RunOnce
+ {
+ RunOnce()
+ {
+ Gtk::Main::init_gtkmm_internals();
+ }
+ } dummy;
+
+ 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)
+ {
+ Glib::RefPtr<Gio::Icon> gicon = fileInfo->get_icon();
+ if (gicon)
+ {
+ Glib::RefPtr<Gtk::IconTheme> iconTheme = Gtk::IconTheme::get_default();
+ if (iconTheme)
+ {
+ Gtk::IconInfo iconInfo = iconTheme->lookup_icon(gicon, ICON_SIZE, 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
+ if (iconPixbuf)
+ return IconHolder(iconPixbuf->gobj_copy()); //copy and pass icon ownership (may be 0)
+ }
+ }
+ }
+ }
+ }
+ catch (const Glib::Error&) {}
+
+
+ //fallback: icon lookup may fail because some icons are currently not present on system
+ Glib::RefPtr<Gtk::IconTheme> iconTheme = Gtk::IconTheme::get_default();
+ if (iconTheme)
+ {
+ Glib::RefPtr<Gdk::Pixbuf> iconPixbuf = iconTheme->load_icon("misc", ICON_SIZE, Gtk::ICON_LOOKUP_USE_BUILTIN);
+ if (!iconPixbuf)
+ iconPixbuf = iconTheme->load_icon("text-x-generic", ICON_SIZE, Gtk::ICON_LOOKUP_USE_BUILTIN);
+ if (iconPixbuf)
+ return IconHolder(iconPixbuf->gobj_copy()); //copy and pass icon ownership (may be 0)
+ }
+
+ //fallback fallback
+ return IconHolder();
+#endif
+}
+
+
+#ifdef FFS_WIN
+IconBuffer::IconHolder IconBuffer::getAssociatedIconByExt(const Zstring& extension)
+{
+ SHFILEINFO fileInfo;
+ fileInfo.hIcon = 0; //initialize hIcon -> fix for weird error: SHGetFileInfo() might return successfully WITHOUT filling fileInfo.hIcon!!
+
+ //no read-access to disk! determine icon by extension
+ ::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);
+
+ return IconHolder(fileInfo.hIcon); //pass icon ownership (may be 0)
+}
+#endif
+
+
//---------------------------------------------------------------------------------------------------
typedef std::vector<DefaultChar> BasicString; //simple thread safe string class: std::vector is guaranteed to not use reference counting, Effective STL, item 13
//avoid reference-counted objects as shared data: NOT THREADSAFE!!! (implicitly shared variables: ref-count + c-string)
@@ -144,10 +285,9 @@ private:
wxCriticalSection lockWorkload; //use for locking shared data
std::vector<FileName> workload; //processes last elements of vector first!
bool threadHasMutex;
- bool threadExitIsRequested;
//------------------------------------------------------------
- //event: icon buffer -> woker thread
+ //signal event: icon buffer(main thread) -> worker thread
wxMutex threadIsListening;
wxCondition continueWork; //wake up thread
@@ -156,9 +296,8 @@ private:
IconBuffer::WorkerThread::WorkerThread(IconBuffer* iconBuff) :
- wxThread(wxTHREAD_JOINABLE),
+ wxThread(wxTHREAD_DETACHED), //we're using the thread encapsulated in a static object => use "detached" to avoid main thread waiting for this thread on exit(which in turn would prevent deletion of static object...ect.) => deadlock!
threadHasMutex(false),
- threadExitIsRequested(false),
threadIsListening(),
continueWork(threadIsListening),
iconBuffer(iconBuff)
@@ -195,12 +334,8 @@ void IconBuffer::WorkerThread::setWorkload(const std::vector<Zstring>& load) //(
void IconBuffer::WorkerThread::quitThread()
{
- {
- wxMutexLocker dummy(threadIsListening); //wait until thread is in waiting state
- threadExitIsRequested = true; //no sharing conflicts in this situation
- continueWork.Signal(); //exit thread
- }
- Wait(); //wait until thread has exitted
+ setWorkload(std::vector<Zstring>());
+ Delete(); //gracefully terminate a detached thread...
}
@@ -208,22 +343,20 @@ wxThread::ExitCode IconBuffer::WorkerThread::Entry()
{
try
{
- wxMutexLocker dummy(threadIsListening); //this lock needs to be called from WITHIN the thread => calling it from constructor(Main thread) would be useless
+ //this lock needs to be called from WITHIN the thread => calling it from constructor(Main thread) would be useless (signal direction: main -> thread)
+ wxMutexLocker dummy(threadIsListening); //this mutex STAYS locked all the time except of continueWork.Wait()!
{
- //this mutex STAYS locked all the time except of continueWork.Wait()!
wxCriticalSectionLocker dummy2(lockWorkload);
threadHasMutex = true;
}
while (true)
{
- continueWork.Wait(); //waiting for continueWork.Signal(); unlocks Mutex "threadIsListening"
+ continueWork.WaitTimeout(100); //waiting for continueWork.Signal(); unlocks Mutex "threadIsListening"
- //no mutex needed in this context
- 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()
+ if (TestDestroy())
+ return 0;
- //do work: get the file icons
doWork();
}
}
@@ -237,60 +370,43 @@ wxThread::ExitCode IconBuffer::WorkerThread::Entry()
void IconBuffer::WorkerThread::doWork()
{
- Zstring fileName;
-
//do work: get the file icon.
while (true)
{
+ Zstring fileName;
{
wxCriticalSectionLocker dummy(lockWorkload);
if (workload.empty())
break; //enter waiting state
- fileName = &workload.back()[0]; //deep copy: fileName is NOT empty (includes NULL-termination)
+ fileName = &workload.back()[0]; //deep copy (includes NULL-termination)
workload.pop_back();
}
if (iconBuffer->requestFileIcon(fileName)) //thread safety: Zstring okay, won't be reference-counted in requestIcon()
continue; //icon already in buffer: skip
- //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!
-
- //load icon
- SHFILEINFO fileInfo;
- fileInfo.hIcon = 0; //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
-
+#ifdef FFS_WIN
const Zstring extension = getFileExtension(fileName); //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!
- ::SHGetFileInfo(fileName.c_str(), //FreeFileSync::removeLongPathPrefix(fileName), //::SHGetFileInfo() can't handle \\?\-prefix!
- 0,
- &fileInfo,
- sizeof(fileInfo),
- SHGFI_ICON | SHGFI_SMALLICON);
-
- IconBuffer::IconHolder newIcon(fileInfo.hIcon); //pass icon ownership (may be 0)
+ const IconHolder newIcon = IconBuffer::getAssociatedIcon(fileName);
iconBuffer->insertIntoBuffer(fileName, newIcon);
}
else //no read-access to disk! determine icon by extension
{
- ::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);
-
- IconBuffer::IconHolder newIcon(fileInfo.hIcon); //pass icon ownership (may be 0)
+ const IconHolder newIcon = IconBuffer::getAssociatedIconByExt(extension);
iconBuffer->insertIntoBuffer(extension, newIcon);
}
+#elif defined FFS_LINUX
+ const IconHolder newIcon = IconBuffer::getAssociatedIcon(fileName);
+ iconBuffer->insertIntoBuffer(fileName, newIcon);
+#endif
}
}
//---------------------------------------------------------------------------------------------------
-class IconBuffer::IconDB : public std::map<Zstring, IconBuffer::CountedIconPtr> {}; //entryName/icon -> ATTENTION: consider ref-counting for this shared data structure!!!
+class IconBuffer::IconDB : public std::map<Zstring, IconBuffer::IconHolder> {}; //entryName/icon -> ATTENTION: avoid ref-counting for this shared data structure!!! (== don't copy instances between threads)
class IconBuffer::IconDbSequence : public std::queue<Zstring> {}; //entryName
//---------------------------------------------------------------------------------------------------
@@ -312,42 +428,29 @@ IconBuffer::IconBuffer() :
IconBuffer::~IconBuffer()
{
- //keep non-inline destructor for std::auto_ptr to work with forward declarations
-
worker->quitThread();
}
bool IconBuffer::requestFileIcon(const Zstring& fileName, wxIcon* icon)
{
+ wxCriticalSectionLocker dummy(*lockIconDB);
+
+#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);
+ IconDB::const_iterator i = buffer->find(isPriceyExtension(extension) ? fileName : extension);
+#elif defined FFS_LINUX
+ IconDB::const_iterator i = buffer->find(fileName);
+#endif
- wxCriticalSectionLocker dummy(*lockIconDB);
+ if (i == buffer->end())
+ return false;
- 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)
- {
- HICON clonedIcon = i->second->clone(); //thread safety: make deep copy!
- if (clonedIcon != 0)
- {
- //create wxIcon from handle
- wxIcon newIcon; //attention: wxIcon uses reference counting!
- newIcon.SetHICON(clonedIcon); //transfer ownership!!
- newIcon.SetSize(IconBuffer::ICON_SIZE, IconBuffer::ICON_SIZE);
- *icon = newIcon;
- }
- else
- *icon = wxNullIcon;
- }
- return true;
- }
+ if (icon != NULL)
+ *icon = i->second.toWxIcon();
- return false;
+ return true;
}
@@ -357,16 +460,14 @@ void IconBuffer::setWorkload(const std::vector<Zstring>& load)
}
-void IconBuffer::insertIntoBuffer(const DefaultChar* entryName, IconHolder& icon) //called by worker thread
+void IconBuffer::insertIntoBuffer(const DefaultChar* entryName, const IconHolder& icon) //called by worker thread
{
wxCriticalSectionLocker dummy(*lockIconDB);
//thread safety, ref-counting: (implicitly) make deep copy!
const Zstring fileNameZ = entryName;
- const IconBuffer::CountedIconPtr newIcon(new IconBuffer::IconHolder); //exception safety!
- newIcon->swap(icon); //
- const std::pair<IconDB::iterator, bool> rc = buffer->insert(IconDB::value_type(fileNameZ, newIcon)); //thread saftey: icon uses ref-counting! But is NOT shared with main thread!
+ const std::pair<IconDB::iterator, bool> rc = buffer->insert(std::make_pair(fileNameZ, icon)); //thread saftey: icon uses ref-counting! But is NOT shared with main thread!
if (rc.second) //if insertion took place
bufSequence->push(fileNameZ); //note: sharing Zstring with IconDB!!!
@@ -381,4 +482,3 @@ void IconBuffer::insertIntoBuffer(const DefaultChar* entryName, IconHolder& icon
bufSequence->pop();
}
}
-
bgstack15