summaryrefslogtreecommitdiff
path: root/library
diff options
context:
space:
mode:
authorDaniel Wilhelm <daniel@wili.li>2014-04-18 17:08:42 +0200
committerDaniel Wilhelm <daniel@wili.li>2014-04-18 17:08:42 +0200
commitc32707148292d104c66276b43796d6057c8c7a5d (patch)
treebb83513f4aff24153e21a4ec92e34e4c27651b1f /library
parent3.9 (diff)
downloadFreeFileSync-c32707148292d104c66276b43796d6057c8c7a5d.tar.gz
FreeFileSync-c32707148292d104c66276b43796d6057c8c7a5d.tar.bz2
FreeFileSync-c32707148292d104c66276b43796d6057c8c7a5d.zip
3.10
Diffstat (limited to 'library')
-rw-r--r--library/custom_grid.cpp133
-rw-r--r--library/custom_grid.h10
-rw-r--r--library/db_file.cpp13
-rw-r--r--library/db_file.h8
-rw-r--r--library/dir_lock.cpp177
-rw-r--r--library/dir_lock.h4
-rw-r--r--library/error_log.h2
-rw-r--r--library/filter.cpp151
-rw-r--r--library/filter.h4
-rw-r--r--library/icon_buffer.cpp91
-rw-r--r--library/icon_buffer.h20
-rw-r--r--library/process_xml.cpp14
-rw-r--r--library/process_xml.h4
-rw-r--r--library/status_handler.h6
14 files changed, 453 insertions, 184 deletions
diff --git a/library/custom_grid.cpp b/library/custom_grid.cpp
index 2537f529..b510717f 100644
--- a/library/custom_grid.cpp
+++ b/library/custom_grid.cpp
@@ -18,6 +18,7 @@
#include <wx/dcclient.h>
#include "icon_buffer.h"
#include <wx/icon.h>
+#include <wx/tooltip.h>
#ifdef FFS_WIN
#include <wx/timer.h>
@@ -380,12 +381,12 @@ protected:
virtual void visit(const SymLinkMapping& linkObj)
{
iconName = linkObj.getLinkType<side>() == LinkDescriptor::TYPE_DIR ?
- DefaultStr("folder") :
+ Zstr("folder") :
linkObj.getFullName<side>();
}
virtual void visit(const DirMapping& dirObj)
{
- iconName = DefaultStr("folder");
+ iconName = Zstr("folder");
}
Zstring iconName;
@@ -580,10 +581,9 @@ CustomGrid::CustomGrid(wxWindow *parent,
isLeading(false),
m_marker(-1, ASCENDING)
{
- //set color of selections
- wxColour darkBlue(40, 35, 140);
- SetSelectionBackground(darkBlue);
- SetSelectionForeground(*wxWHITE);
+ //wxColour darkBlue(40, 35, 140); -> user default colors instead!
+ //SetSelectionBackground(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT));
+ //SetSelectionForeground(*wxWHITE);
}
@@ -975,6 +975,22 @@ void CustomGrid::DrawColLabel(wxDC& dc, int col)
}
+std::pair<int, int> CustomGrid::mousePosToCell(wxPoint pos)
+{
+ int x = -1;
+ int y = -1;
+ CalcUnscrolledPosition(pos.x, pos.y, &x, &y);
+
+ std::pair<int, int> output(-1, -1);
+ if (x >= 0 && y >= 0)
+ {
+ output.first = YToRow(y);
+ output.second = XToCol(x);
+ }
+ return output;
+}
+
+
std::set<size_t> CustomGrid::getAllSelectedRows() const
{
std::set<size_t> output;
@@ -1075,7 +1091,7 @@ public:
if (!fileName.empty())
{
//first check if it is a directory icon:
- if (fileName == DefaultStr("folder"))
+ if (fileName == Zstr("folder"))
{
dc.DrawIcon(IconBuffer::getDirectoryIcon(), rectShrinked.GetX() + LEFT_BORDER, rectShrinked.GetY());
m_loadIconSuccess[row] = true; //save status of last icon load -> used for async. icon loading
@@ -1159,8 +1175,57 @@ CustomGridRim::CustomGridRim(wxWindow *parent,
const wxSize& size,
long style,
const wxString& name) :
- CustomGrid(parent, id, pos, size, style, name), fileIconsAreEnabled(false)
-{}
+ CustomGrid(parent, id, pos, size, style, name), fileIconsAreEnabled(false) {}
+
+
+template <SelectedSide side>
+void CustomGridRim::setTooltip(const wxMouseEvent& event)
+{
+ const int hoveredRow = mousePosToCell(event.GetPosition()).first;
+
+ wxString toolTip;
+ if (hoveredRow >= 0 && getGridDataTable() != NULL)
+ {
+ const FileSystemObject* const fsObj = getGridDataTable()->getRawData(hoveredRow);
+ if (fsObj && !fsObj->isEmpty<side>())
+ {
+ struct AssembleTooltip : public FSObjectVisitor
+ {
+ AssembleTooltip(wxString& tipMsg) : tipMsg_(tipMsg) {}
+
+ virtual void visit(const FileMapping& fileObj)
+ {
+ tipMsg_ = zToWx(fileObj.getShortName<side>()) + wxT("\n") +
+ _("Size") + wxT(": ") + ffs3::formatFilesizeToShortString(fileObj.getFileSize<side>()) + wxT("\n") +
+ _("Date") + wxT(": ") + ffs3::utcTimeToLocalString(fileObj.getLastWriteTime<side>());
+ }
+
+ virtual void visit(const SymLinkMapping& linkObj)
+ {
+ tipMsg_ = zToWx(linkObj.getShortName<side>()) + wxT("\n") +
+ _("Date") + wxT(": ") + ffs3::utcTimeToLocalString(linkObj.getLastWriteTime<side>());
+ }
+
+ virtual void visit(const DirMapping& dirObj)
+ {
+ tipMsg_ = zToWx(dirObj.getShortName<side>());
+ }
+
+ wxString& tipMsg_;
+ } assembler(toolTip);
+ fsObj->accept(assembler);
+ }
+ }
+
+ wxToolTip* tt = GetGridWindow()->GetToolTip();
+ if (!tt)
+ //wxWidgets bug: tooltip multiline property is defined by first tooltip text containing newlines or not (same is true for maximum width)
+ GetGridWindow()->SetToolTip(new wxToolTip(wxT("a b\n\
+ a b"))); //ugly, but is working (on Windows)
+ tt = GetGridWindow()->GetToolTip(); //should be bound by now
+ if (tt && tt->GetTip() != toolTip)
+ tt->SetTip(toolTip);
+}
void CustomGridRim::updateGridSizes()
@@ -1425,12 +1490,9 @@ CustomGridRim::VisibleRowRange CustomGridRim::getVisibleRows()
if (height >= 0)
{
- int topRowY = -1;
- CalcUnscrolledPosition(0, 0, &dummy, &topRowY);
-
- if (topRowY >= 0)
+ const int topRow = mousePosToCell(wxPoint(0, 0)).first;
+ if (topRow >= 0)
{
- const int topRow = YToRow(topRowY);
const int rowCount = static_cast<int>(ceil(height / static_cast<double>(GetDefaultRowSize()))); // = height / rowHeight rounded up
const int bottomRow = topRow + rowCount - 1;
@@ -1535,7 +1597,17 @@ CustomGridLeft::CustomGridLeft(wxWindow *parent,
long style,
const wxString& name) :
CustomGridRim(parent, id, pos, size, style, name),
- gridDataTable(NULL) {}
+ gridDataTable(NULL)
+{
+ GetGridWindow()->Connect(wxEVT_MOTION, wxMouseEventHandler(CustomGridLeft::OnMouseMovement), NULL, this); //row-based tooltip
+}
+
+
+void CustomGridLeft::OnMouseMovement(wxMouseEvent& event)
+{
+ CustomGridRim::setTooltip<LEFT_SIDE>(event);
+ event.Skip();
+}
bool CustomGridLeft::CreateGrid(int numRows, int numCols, wxGrid::wxGridSelectionModes selmode)
@@ -1583,7 +1655,17 @@ CustomGridRight::CustomGridRight(wxWindow *parent,
long style,
const wxString& name) :
CustomGridRim(parent, id, pos, size, style, name),
- gridDataTable(NULL) {}
+ gridDataTable(NULL)
+{
+ GetGridWindow()->Connect(wxEVT_MOTION, wxMouseEventHandler(CustomGridRight::OnMouseMovement), NULL, this); //row-based tooltip
+}
+
+
+void CustomGridRight::OnMouseMovement(wxMouseEvent& event)
+{
+ CustomGridRim::setTooltip<RIGHT_SIDE>(event);
+ event.Skip();
+}
bool CustomGridRight::CreateGrid(int numRows, int numCols, wxGrid::wxGridSelectionModes selmode)
@@ -1717,16 +1799,21 @@ void CustomGridMiddle::setGridDataTable(const GridView* gridDataView) //called o
void CustomGridMiddle::OnMouseMovement(wxMouseEvent& event)
{
- const int highlightedRowOld = highlightedRow;
+ const int rowOld = highlightedRow;
+ const BlockPosition posOld = highlightedPos;
+
if (selectionRowBegin == -1) //change highlightning only if currently not dragging mouse
{
highlightedRow = mousePosToRow(event.GetPosition(), &highlightedPos);
- if (highlightedRow >= 0)
+
+ if (rowOld != highlightedRow)
+ {
+ RefreshCell(highlightedRow, 0);
+ RefreshCell(rowOld, 0);
+ }
+ else if (posOld != highlightedPos)
RefreshCell(highlightedRow, 0);
- if ( highlightedRowOld >= 0 &&
- highlightedRow != highlightedRowOld)
- RefreshCell(highlightedRowOld, 0);
//handle tooltip
showToolTip(highlightedRow, GetGridWindow()->ClientToScreen(event.GetPosition()));
@@ -1830,7 +1917,7 @@ void CustomGridMiddle::OnLeftMouseDown(wxMouseEvent& event)
void CustomGridMiddle::OnLeftMouseUp(wxMouseEvent& event)
{
- const int rowEndFiltering = mousePosToRow(event.GetPosition());
+ const int rowEndFiltering = mousePosToCell(event.GetPosition()).first;
if (0 <= selectionRowBegin && 0 <= rowEndFiltering)
{
@@ -1874,7 +1961,7 @@ void CustomGridMiddle::OnLeftMouseUp(wxMouseEvent& event)
}
-int CustomGridMiddle::mousePosToRow(const wxPoint pos, BlockPosition* block)
+int CustomGridMiddle::mousePosToRow(wxPoint pos, BlockPosition* block)
{
int row = -1;
int x = -1;
diff --git a/library/custom_grid.h b/library/custom_grid.h
index 6c35ffbe..206841dd 100644
--- a/library/custom_grid.h
+++ b/library/custom_grid.h
@@ -13,6 +13,8 @@
#include <map>
#include <memory>
#include <set>
+#include "../file_hierarchy.h"
+
class CustomGridTableRim;
class CustomGridTableLeft;
@@ -85,8 +87,8 @@ public:
protected:
void RefreshCell(int row, int col);
-
virtual void DrawColLabel(wxDC& dc, int col);
+ std::pair<int, int> mousePosToCell(wxPoint pos); //returns (row/column) pair
private:
virtual void setGridDataTable(const ffs3::GridView* gridDataView) = 0;
@@ -161,6 +163,10 @@ public:
void enableFileIcons(const bool value);
+protected:
+ template <ffs3::SelectedSide side>
+ void setTooltip(const wxMouseEvent& event);
+
private:
CustomGridTableRim* getGridDataTable();
virtual const CustomGridTableRim* getGridDataTable() const = 0;
@@ -198,6 +204,7 @@ public:
virtual bool CreateGrid(int numRows, int numCols, wxGrid::wxGridSelectionModes selmode = wxGrid::wxGridSelectCells);
private:
+ void OnMouseMovement(wxMouseEvent& event);
virtual void setGridDataTable(const ffs3::GridView* gridDataView);
virtual const CustomGridTableRim* getGridDataTable() const;
@@ -221,6 +228,7 @@ public:
virtual bool CreateGrid(int numRows, int numCols, wxGrid::wxGridSelectionModes selmode = wxGrid::wxGridSelectCells);
private:
+ void OnMouseMovement(wxMouseEvent& event);
virtual void setGridDataTable(const ffs3::GridView* gridDataView);
virtual const CustomGridTableRim* getGridDataTable() const;
diff --git a/library/db_file.cpp b/library/db_file.cpp
index 1daa51f5..195d7df6 100644
--- a/library/db_file.cpp
+++ b/library/db_file.cpp
@@ -189,7 +189,7 @@ public:
DbStreamData loadFile(const Zstring& filename) //throw (FileError)
{
if (!ffs3::fileExists(filename))
- throw FileError(wxString(_("Initial synchronization:")) + wxT(" \n\n") +
+ throw FileErrorDatabaseNotExisting(wxString(_("Initial synchronization:")) + wxT(" \n\n") +
_("One of the FreeFileSync database files is not yet existing:") + wxT(" \n") +
wxT("\"") + zToWx(filename) + wxT("\""));
@@ -217,14 +217,15 @@ std::pair<DirInfoPtr, DirInfoPtr> ffs3::loadFromDisk(const BaseDirMapping& baseM
//find associated DirInfo-streams
DirectoryTOC::const_iterator dbLeft = dbEntriesLeft.second.find(dbEntriesRight.first); //find left db-entry that corresponds to right database
+ DirectoryTOC::const_iterator dbRight = dbEntriesRight.second.find(dbEntriesLeft.first); //find left db-entry that corresponds to right database
+
if (dbLeft == dbEntriesLeft.second.end())
- throw FileError(wxString(_("Initial synchronization:")) + wxT(" \n\n") +
+ throw FileErrorDatabaseNotExisting(wxString(_("Initial synchronization:")) + wxT(" \n\n") +
_("One of the FreeFileSync database entries within the following file is not yet existing:") + wxT(" \n") +
wxT("\"") + zToWx(fileNameLeft) + wxT("\""));
- DirectoryTOC::const_iterator dbRight = dbEntriesRight.second.find(dbEntriesLeft.first); //find left db-entry that corresponds to right database
if (dbRight == dbEntriesRight.second.end())
- throw FileError(wxString(_("Initial synchronization:")) + wxT(" \n\n") +
+ throw FileErrorDatabaseNotExisting(wxString(_("Initial synchronization:")) + wxT(" \n\n") +
_("One of the FreeFileSync database entries within the following file is not yet existing:") + wxT(" \n") +
wxT("\"") + zToWx(fileNameRight) + wxT("\""));
@@ -397,8 +398,8 @@ bool entryExisting(const DirectoryTOC& table, const util::UniqueId& newKey, cons
void ffs3::saveToDisk(const BaseDirMapping& baseMapping) //throw (FileError)
{
//transactional behaviour! write to tmp files first
- const Zstring fileNameLeftTmp = baseMapping.getDBFilename<LEFT_SIDE>() + DefaultStr(".tmp");
- const Zstring fileNameRightTmp = baseMapping.getDBFilename<RIGHT_SIDE>() + DefaultStr(".tmp");;
+ const Zstring fileNameLeftTmp = baseMapping.getDBFilename<LEFT_SIDE>() + Zstr(".tmp");
+ const Zstring fileNameRightTmp = baseMapping.getDBFilename<RIGHT_SIDE>() + Zstr(".tmp");;
//delete old tmp file, if necessary -> throws if deletion fails!
removeFile(fileNameLeftTmp); //
diff --git a/library/db_file.h b/library/db_file.h
index 8e1f9c94..ea9d68ad 100644
--- a/library/db_file.h
+++ b/library/db_file.h
@@ -20,7 +20,13 @@ struct DirInformation
};
typedef boost::shared_ptr<const DirInformation> DirInfoPtr;
-std::pair<DirInfoPtr, DirInfoPtr> loadFromDisk(const BaseDirMapping& baseMapping); //throw (FileError) -> return value always bound!
+class FileErrorDatabaseNotExisting : public FileError
+{
+public:
+ FileErrorDatabaseNotExisting(const wxString& message) : FileError(message) {}
+};
+
+std::pair<DirInfoPtr, DirInfoPtr> loadFromDisk(const BaseDirMapping& baseMapping); //throw (FileError, FileErrorDatabaseNotExisting) -> return value always bound!
}
#endif // DBFILE_H_INCLUDED
diff --git a/library/dir_lock.cpp b/library/dir_lock.cpp
index c5ea37b1..54ae8636 100644
--- a/library/dir_lock.cpp
+++ b/library/dir_lock.cpp
@@ -6,21 +6,25 @@
#include <wx/timer.h>
#include <boost/shared_ptr.hpp>
#include <boost/weak_ptr.hpp>
-#include <boost/thread.hpp>
+#include "../shared/boost_thread_wrap.h" //include <boost/thread.hpp>
#include "../shared/loki/ScopeGuard.h"
#include <wx/msgdlg.h>
#include "../shared/system_constants.h"
#include "../shared/guid.h"
#include "../shared/file_io.h"
#include <utility>
+#include "../shared/serialize.h"
#ifdef FFS_WIN
+#include <tlhelp32.h>
#include <wx/msw/wrapwin.h> //includes "windows.h"
#include "../shared/long_path_prefix.h"
#elif defined FFS_LINUX
+#include "../shared/file_handling.h"
#include <sys/stat.h>
#include <cerrno>
+#include <unistd.h>
#endif
using namespace ffs3;
@@ -32,13 +36,15 @@ namespace
const size_t EMIT_LIFE_SIGN_INTERVAL = 5000; //show life sign; unit [ms]
const size_t POLL_LIFE_SIGN_INTERVAL = 6000; //poll for life sign; unit [ms]
const size_t DETECT_EXITUS_INTERVAL = 30000; //assume abandoned lock; unit [ms]
+
+typedef Zbase<Zchar, StorageDeepCopy> BasicString; //thread safe string class
}
class LifeSigns
{
public:
- LifeSigns(const Zstring& lockfilename) : //throw()!!! siehe SharedDirLock()
- lockfilename_(lockfilename.c_str()) //ref-counting structure is used by thread: make deep copy!
+ LifeSigns(const BasicString& lockfilename) : //throw()!!! siehe SharedDirLock()
+ lockfilename_(lockfilename) //thread safety: make deep copy!
{
threadObj = boost::thread(boost::cref(*this)); //localize all thread logic to this class!
}
@@ -72,7 +78,7 @@ public:
const char buffer[1] = {' '};
#ifdef FFS_WIN
- const HANDLE fileHandle = ::CreateFile(applyLongPathPrefix(lockfilename_).c_str(),
+ const HANDLE fileHandle = ::CreateFile(applyLongPathPrefix(lockfilename_.c_str()).c_str(),
FILE_APPEND_DATA,
FILE_SHARE_READ,
NULL,
@@ -115,7 +121,7 @@ private:
LifeSigns& operator=(const LifeSigns&); //
boost::thread threadObj;
- const Zstring lockfilename_; //used by worker thread only! Not ref-counted!
+ const BasicString lockfilename_; //used by worker thread only! Not ref-counted!
};
@@ -157,7 +163,8 @@ wxULongLong getLockFileSize(const Zstring& filename) //throw (FileError, ErrorNo
const DWORD lastError = ::GetLastError();
const wxString errorMessage = wxString(_("Error reading file attributes:")) + wxT("\n\"") + zToWx(filename) + wxT("\"") +
wxT("\n\n") + getLastErrorFormatted(lastError);
- if (lastError == ERROR_FILE_NOT_FOUND)
+ if ( lastError == ERROR_FILE_NOT_FOUND ||
+ lastError == ERROR_PATH_NOT_FOUND)
throw ErrorNotExisting(errorMessage);
else
throw FileError(errorMessage);
@@ -187,29 +194,138 @@ wxULongLong getLockFileSize(const Zstring& filename) //throw (FileError, ErrorNo
Zstring deleteAbandonedLockName(const Zstring& lockfilename)
{
- const size_t pos = lockfilename.Find(common::FILE_NAME_SEPARATOR, true); //search from end
-
- return pos == Zstring::npos ? DefaultStr("Del.") + lockfilename :
-
+ const size_t pos = lockfilename.rfind(common::FILE_NAME_SEPARATOR); //search from end
+ return pos == Zstring::npos ? Zstr("Del.") + lockfilename :
Zstring(lockfilename.c_str(), pos + 1) + //include path separator
- DefaultStr("Del.") +
+ Zstr("Del.") +
lockfilename.AfterLast(common::FILE_NAME_SEPARATOR); //returns the whole string if ch not found
}
-void writeLockId(const Zstring& lockfilename) //throw (FileError)
+namespace
+{
+inline
+wxString readString(wxInputStream& stream) //read string from file stream
+{
+ const size_t strLength = util::readNumber<size_t>(stream);
+ boost::scoped_array<wxChar> buffer(new wxChar[strLength]);
+ stream.Read(buffer.get(), sizeof(wxChar) * strLength);
+ return wxString(buffer.get(), strLength);
+}
+
+
+inline
+void writeString(wxOutputStream& stream, const wxString& str) //write string to filestream
+{
+ util::writeNumber<size_t>(stream, str.length());
+ stream.Write(str.c_str(), sizeof(wxChar) * str.length());
+}
+
+
+struct LockInformation
+{
+ LockInformation()
+ {
+#ifdef FFS_WIN
+ processId = ::GetCurrentProcessId();
+#elif defined FFS_LINUX
+ processId = ::getpid();
+#endif
+ computerId = ::wxGetFullHostName();
+ }
+
+ LockInformation(wxInputStream& stream) : //read
+ lockId(util::UniqueId(stream))
+ {
+ processId = util::readNumber<ProcessId>(stream);
+ computerId = readString(stream);
+ }
+
+ void toStream(wxOutputStream& stream) const //write
+ {
+ lockId.toStream(stream);
+ util::writeNumber<ProcessId>(stream, processId);
+ writeString(stream, computerId);
+ }
+
+#ifdef FFS_WIN
+ typedef DWORD ProcessId;
+#elif defined FFS_LINUX
+ typedef pid_t ProcessId;
+#endif
+
+ util::UniqueId lockId;
+ ProcessId processId;
+ wxString computerId;
+};
+
+
+//true: process not available, false: cannot say anything
+enum ProcessStatus
+{
+ PROC_STATUS_NOT_RUNNING,
+ PROC_STATUS_RUNNING,
+ PROC_STATUS_NO_IDEA
+};
+ProcessStatus getProcessStatus(LockInformation::ProcessId procId, const wxString& computerId)
+{
+ if (::wxGetFullHostName() != computerId || computerId.empty())
+ return PROC_STATUS_NO_IDEA; //lock owned by different computer
+
+#ifdef FFS_WIN
+ //note: ::OpenProcess() is no option as it may successfully return for crashed processes!
+ HANDLE snapshot = ::CreateToolhelp32Snapshot(
+ TH32CS_SNAPPROCESS, //__in DWORD dwFlags,
+ procId); //__in DWORD th32ProcessID
+ if (snapshot == INVALID_HANDLE_VALUE)
+ return PROC_STATUS_NO_IDEA;
+ boost::shared_ptr<void> dummy(snapshot, ::CloseHandle);
+
+ PROCESSENTRY32 processEntry = {};
+ processEntry.dwSize = sizeof(processEntry);
+
+ if (!::Process32First(snapshot, //__in HANDLE hSnapshot,
+ &processEntry)) //__inout LPPROCESSENTRY32 lppe
+ return PROC_STATUS_NO_IDEA;
+
+ do
+ {
+ if (processEntry.th32ProcessID == procId)
+ return PROC_STATUS_RUNNING; //process still running
+ }
+ while(::Process32Next(snapshot, &processEntry));
+
+ return PROC_STATUS_NOT_RUNNING;
+
+#elif defined FFS_LINUX
+ if (procId <= 0 || procId >= 65536)
+ return PROC_STATUS_NO_IDEA; //invalid process id
+
+ return ffs3::dirExists(Zstr("/proc/") + Zstring::fromNumber(procId)) ? PROC_STATUS_RUNNING : PROC_STATUS_NOT_RUNNING;
+#endif
+}
+
+
+void writeLockInfo(const Zstring& lockfilename) //throw (FileError)
{
//write GUID at the beginning of the file: this ID is a universal identifier for this lock (no matter what the path is, considering symlinks ,etc.)
FileOutputStream lockFile(lockfilename); //throw FileError()
- util::UniqueId().toStream(lockFile); //
+ LockInformation().toStream(lockFile);
}
-util::UniqueId retrieveLockId(const Zstring& lockfilename) //throw (FileError, ErrorNotExisting)
+LockInformation retrieveLockInfo(const Zstring& lockfilename) //throw (FileError, ErrorNotExisting)
{
//read GUID from beginning of file
FileInputStream lockFile(lockfilename); //throw (FileError, ErrorNotExisting)
- return util::UniqueId(lockFile); //
+ return LockInformation(lockFile);
+}
+
+
+util::UniqueId retrieveLockId(const Zstring& lockfilename) //throw (FileError, ErrorNotExisting)
+{
+ return retrieveLockInfo(lockfilename).lockId;
+}
}
@@ -217,12 +333,13 @@ void waitOnDirLock(const Zstring& lockfilename, DirLockCallback* callback) //thr
{
Zstring infoMsg;
infoMsg = wxToZ(_("Waiting while directory is locked (%x)..."));
- infoMsg.Replace(DefaultStr("%x"), DefaultStr("\"") + lockfilename + DefaultStr("\""));
- if (callback) callback->updateStatusText(infoMsg);
+ infoMsg.Replace(Zstr("%x"), Zstr("\"") + lockfilename + Zstr("\""));
+ if (callback) callback->reportInfo(infoMsg);
//---------------------------------------------------------------
try
{
- const util::UniqueId lockId = retrieveLockId(lockfilename); //throw (FileError, ErrorNotExisting)
+ const LockInformation lockInfo = retrieveLockInfo(lockfilename); //throw (FileError, ErrorNotExisting)
+ const bool lockOwnderDead = getProcessStatus(lockInfo.processId, lockInfo.computerId) == PROC_STATUS_NOT_RUNNING; //convenience optimization: if we know the owning process crashed, we needn't wait DETECT_EXITUS_INTERVAL sec
wxULongLong fileSizeOld;
wxLongLong lockSilentStart = wxGetLocalTimeMillis();
@@ -238,24 +355,20 @@ void waitOnDirLock(const Zstring& lockfilename, DirLockCallback* callback) //thr
fileSizeOld = fileSizeNew;
lockSilentStart = currentTime;
}
- else if (currentTime - lockSilentStart > DETECT_EXITUS_INTERVAL)
+
+ if ( lockOwnderDead || //no need to wait any longer...
+ currentTime - lockSilentStart > DETECT_EXITUS_INTERVAL)
{
DirLock dummy(deleteAbandonedLockName(lockfilename), callback); //throw (FileError)
//now that the lock is in place check existence again: meanwhile another process may have deleted and created a new lock!
- if (retrieveLockId(lockfilename) != lockId) //throw (FileError, ErrorNotExisting)
+ if (retrieveLockId(lockfilename) != lockInfo.lockId) //throw (FileError, ErrorNotExisting)
return; //another process has placed a new lock, leave scope: the wait for the old lock is technically over...
if (getLockFileSize(lockfilename) != fileSizeOld) //throw (FileError, ErrorNotExisting)
continue; //belated lifesign
- //---------------------------------------------------------------
- Zstring infoMsg2 = wxToZ(_("Removing abandoned directory lock (%x)..."));
- infoMsg2.Replace(DefaultStr("%x"), DefaultStr("\"") + lockfilename + DefaultStr("\""));
- if (callback) callback->updateStatusText(infoMsg2);
- //---------------------------------------------------------------
-
::deleteLockFile(lockfilename); //throw (FileError)
return;
}
@@ -276,11 +389,11 @@ void waitOnDirLock(const Zstring& lockfilename, DirLockCallback* callback) //thr
remainingSeconds = std::max(0L, remainingSeconds);
Zstring remSecMsg = wxToZ(_("%x sec"));
- remSecMsg.Replace(DefaultStr("%x"), numberToZstring(remainingSeconds));
- callback->updateStatusText(infoMsg + DefaultStr(" ") + remSecMsg);
+ remSecMsg.Replace(Zstr("%x"), Zstring::fromNumber(remainingSeconds));
+ callback->reportInfo(infoMsg + Zstr(" ") + remSecMsg);
}
else
- callback->updateStatusText(infoMsg); //emit a message in any case (might clear other one)
+ callback->reportInfo(infoMsg); //emit a message in any case (might clear other one)
}
}
}
@@ -341,7 +454,7 @@ bool tryLock(const Zstring& lockfilename) //throw (FileError)
Loki::ScopeGuard guardLockFile = Loki::MakeGuard(::releaseLock, lockfilename);
//write UUID at the beginning of the file: this ID is a universal identifier for this lock (no matter what the path is, considering symlinks ,etc.)
- writeLockId(lockfilename); //throw (FileError)
+ writeLockInfo(lockfilename); //throw (FileError)
guardLockFile.Dismiss(); //lockfile created successfully
return true;
@@ -358,7 +471,7 @@ public:
while (!::tryLock(lockfilename)) //throw (FileError)
::waitOnDirLock(lockfilename, callback); //
- emitLifeSigns.reset(new LifeSigns(lockfilename)); //throw()! ownership of lockfile not yet managed!
+ emitLifeSigns.reset(new LifeSigns(lockfilename.c_str())); //throw()! ownership of lockfile not yet managed!
}
~SharedDirLock()
@@ -378,7 +491,7 @@ private:
};
-class DirLock::LockAdmin //administrate all locks of this process to avoid deadlock by recursion
+class DirLock::LockAdmin //administrate all locks held by this process to avoid deadlock by recursion
{
public:
static LockAdmin& instance()
diff --git a/library/dir_lock.h b/library/dir_lock.h
index e3b6597c..fba65d2b 100644
--- a/library/dir_lock.h
+++ b/library/dir_lock.h
@@ -10,7 +10,7 @@ struct DirLockCallback //while waiting for the lock
{
virtual ~DirLockCallback() {}
virtual void requestUiRefresh() = 0; //allowed to throw exceptions
- virtual void updateStatusText(const Zstring& text) = 0;
+ virtual void reportInfo(const Zstring& text) = 0;
};
/*
@@ -18,7 +18,7 @@ RAII structure to place a directory lock against other FFS processes:
- recursive locking supported, even with alternate lockfile names, e.g. via symlinks, network mounts etc.
- ownership shared between all object instances refering to a specific lock location(= UUID)
- can be copied safely and efficiently! (ref-counting)
- - detects and resolves abandoned locks
+ - detects and resolves abandoned locks (instantly if lock is associated with local pc, else after 30 seconds)
- race-free (Windows, almost on Linux)
*/
class DirLock
diff --git a/library/error_log.h b/library/error_log.h
index dc3ef580..876f78b9 100644
--- a/library/error_log.h
+++ b/library/error_log.h
@@ -9,8 +9,8 @@
#include <wx/string.h>
#include <vector>
+#include "../shared/zstring.h"
-class Zstring;
namespace ffs3
{
diff --git a/library/filter.cpp b/library/filter.cpp
index c4bc69d7..7697ea4b 100644
--- a/library/filter.cpp
+++ b/library/filter.cpp
@@ -59,11 +59,11 @@ BaseFilter::FilterRef BaseFilter::loadFilter(wxInputStream& stream)
const Zstring uniqueClassId = util::readString(stream);
//read actual object
- if (uniqueClassId == DefaultStr("NullFilter"))
+ if (uniqueClassId == Zstr("NullFilter"))
return NullFilter::load(stream);
- else if (uniqueClassId == DefaultStr("NameFilter"))
+ else if (uniqueClassId == Zstr("NameFilter"))
return NameFilter::load(stream);
- else if (uniqueClassId == DefaultStr("CombinedFilter"))
+ else if (uniqueClassId == Zstr("CombinedFilter"))
return CombinedFilter::load(stream);
else
throw std::logic_error("Programming Error: Unknown filter!");
@@ -78,15 +78,15 @@ void addFilterEntry(const Zstring& filtername, std::set<Zstring>& fileFilter, st
#ifdef FFS_WIN
//Windows does NOT distinguish between upper/lower-case
- filterFormatted.MakeUpper();
+ MakeUpper(filterFormatted);
#elif defined FFS_LINUX
//Linux DOES distinguish between upper/lower-case: nothing to do here
#endif
- static const Zstring sepAsterisk = Zstring() + common::FILE_NAME_SEPARATOR + DefaultChar('*');
- static const Zstring sepQuestionMark = Zstring() + common::FILE_NAME_SEPARATOR + DefaultChar('?');
- static const Zstring asteriskSep = Zstring(DefaultStr("*")) + common::FILE_NAME_SEPARATOR;
- static const Zstring questionMarkSep = Zstring(DefaultStr("?")) + common::FILE_NAME_SEPARATOR;
+ static const Zstring sepAsterisk = Zstring(common::FILE_NAME_SEPARATOR) + Zchar('*');
+ static const Zstring sepQuestionMark = Zstring(common::FILE_NAME_SEPARATOR) + Zchar('?');
+ static const Zstring asteriskSep = Zstring(Zchar('*')) + common::FILE_NAME_SEPARATOR;
+ static const Zstring questionMarkSep = Zstring(Zchar('?')) + common::FILE_NAME_SEPARATOR;
//--------------------------------------------------------------------------------------------------
//add some syntactic sugar: handle beginning of filtername
@@ -127,49 +127,77 @@ void addFilterEntry(const Zstring& filtername, std::set<Zstring>& fileFilter, st
}
-class MatchFound : public std::unary_function<Zstring, bool>
+namespace
{
-public:
- MatchFound(const Zstring& name) : name_(name) {}
-
- bool operator()(const Zstring& mask) const
+template <class T>
+inline
+const T* cStringFind(const T* str1, T ch) //strchr()
+{
+ while (*str1 != ch) //ch is allowed to be 0 by contract! must return end of string in this case
{
- return Zstring::Matches(name_.c_str(), mask.c_str());
+ if (*str1 == 0)
+ return NULL;
+ ++str1;
}
-private:
- const Zstring& name_;
-};
+ return str1;
+}
-inline
-bool matchesFilter(const Zstring& name, const std::set<Zstring>& filter)
+bool matchesMask(const Zchar* string, const Zchar* mask)
{
-#ifdef FFS_WIN //Windows does NOT distinguish between upper/lower-case
- Zstring nameFormatted = name;
- nameFormatted.MakeUpper();
-#elif defined FFS_LINUX //Linux DOES distinguish between upper/lower-case
- const Zstring& nameFormatted = name; //nothing to do here
-#endif
+ for (Zchar ch; (ch = *mask) != 0; ++mask, ++string)
+ {
+ switch (ch)
+ {
+ case Zchar('?'):
+ if (*string == 0)
+ return false;
+ break;
- return std::find_if(filter.begin(), filter.end(), MatchFound(nameFormatted)) != filter.end();
-}
+ case Zchar('*'):
+ //advance to next non-*/? char
+ do
+ {
+ ++mask;
+ ch = *mask;
+ }
+ while (ch == Zchar('*') || ch == Zchar('?'));
+ //if match ends with '*':
+ if (ch == 0)
+ return true;
+
+ ++mask;
+ while ((string = cStringFind(string, ch)) != NULL)
+ {
+ ++string;
+ if (matchesMask(string, mask))
+ return true;
+ }
+ return false;
+ default:
+ if (*string != ch)
+ return false;
+ }
+ }
+ return *string == 0;
+}
//returns true if string matches at least the beginning of mask
inline
-bool matchesMaskBegin(const DefaultChar* string, const DefaultChar* mask)
+bool matchesMaskBegin(const Zchar* string, const Zchar* mask)
{
- for (DefaultChar ch; (ch = *mask) != 0; ++mask, ++string)
+ for (Zchar ch; (ch = *mask) != 0; ++mask, ++string)
{
if (*string == 0)
return true;
switch (ch)
{
- case DefaultChar('?'):
+ case Zchar('?'):
break;
- case DefaultChar('*'):
+ case Zchar('*'):
return true;
default:
@@ -179,18 +207,33 @@ bool matchesMaskBegin(const DefaultChar* string, const DefaultChar* mask)
}
return *string == 0;
}
+}
+
+
+class MatchFound : public std::unary_function<Zstring, bool>
+{
+public:
+ MatchFound(const Zstring& name) : name_(name) {}
+
+ bool operator()(const Zstring& mask) const
+ {
+ return matchesMask(name_, mask);
+ }
+private:
+ const Zstring& name_;
+};
inline
-bool matchesFilterBegin(const Zstring& name, const std::set<Zstring>& filter)
+bool matchesFilter(const Zstring& nameFormatted, const std::set<Zstring>& filter)
{
-#ifdef FFS_WIN //Windows does NOT distinguish between upper/lower-case
- Zstring nameFormatted = name;
- nameFormatted.MakeUpper();
-#elif defined FFS_LINUX //Linux DOES distinguish between upper/lower-case
- const Zstring& nameFormatted = name; //nothing to do here
-#endif
+ return std::find_if(filter.begin(), filter.end(), MatchFound(nameFormatted)) != filter.end();
+}
+
+inline
+bool matchesFilterBegin(const Zstring& nameFormatted, const std::set<Zstring>& filter)
+{
return std::find_if(filter.begin(), filter.end(),
boost::bind(matchesMaskBegin, nameFormatted.c_str(), _1)) != filter.end();
}
@@ -201,10 +244,10 @@ std::vector<Zstring> compoundStringToFilter(const Zstring& filterString)
//delimiters may be ';' or '\n'
std::vector<Zstring> output;
- const std::vector<Zstring> filterPreProcessing = filterString.Tokenize(wxT(';'));
+ const std::vector<Zstring> filterPreProcessing = filterString.Split(Zchar(';'));
for (std::vector<Zstring>::const_iterator i = filterPreProcessing.begin(); i != filterPreProcessing.end(); ++i)
{
- const std::vector<Zstring> newEntries = i->Tokenize(wxT('\n'));
+ const std::vector<Zstring> newEntries = i->Split(Zchar('\n'));
output.insert(output.end(), newEntries.begin(), newEntries.end());
}
@@ -231,8 +274,15 @@ NameFilter::NameFilter(const Zstring& includeFilter, const Zstring& excludeFilte
bool NameFilter::passFileFilter(const Zstring& relFilename) const
{
- return matchesFilter(relFilename, filterFileIn) && //process include filters
- !matchesFilter(relFilename, filterFileEx); //process exclude filters
+#ifdef FFS_WIN //Windows does NOT distinguish between upper/lower-case
+ Zstring nameFormatted = relFilename;
+ MakeUpper(nameFormatted);
+#elif defined FFS_LINUX //Linux DOES distinguish between upper/lower-case
+ const Zstring& nameFormatted = relFilename; //nothing to do here
+#endif
+
+ return matchesFilter(nameFormatted, filterFileIn) && //process include filters
+ !matchesFilter(nameFormatted, filterFileEx); //process exclude filters
}
@@ -240,18 +290,25 @@ bool NameFilter::passDirFilter(const Zstring& relDirname, bool* subObjMightMatch
{
assert(subObjMightMatch == NULL || *subObjMightMatch == true); //check correct usage
- if (matchesFilter(relDirname, filterFolderEx)) //process exclude filters
+#ifdef FFS_WIN //Windows does NOT distinguish between upper/lower-case
+ Zstring nameFormatted = relDirname;
+ MakeUpper(nameFormatted);
+#elif defined FFS_LINUX //Linux DOES distinguish between upper/lower-case
+ const Zstring& nameFormatted = relDirname; //nothing to do here
+#endif
+
+ if (matchesFilter(nameFormatted, filterFolderEx)) //process exclude filters
{
if (subObjMightMatch)
*subObjMightMatch = false; //exclude subfolders/subfiles as well
return false;
}
- if (!matchesFilter(relDirname, filterFolderIn)) //process include filters
+ if (!matchesFilter(nameFormatted, filterFolderIn)) //process include filters
{
if (subObjMightMatch)
{
- const Zstring& subNameBegin = relDirname + common::FILE_NAME_SEPARATOR; //const-ref optimization
+ const Zstring& subNameBegin = nameFormatted + common::FILE_NAME_SEPARATOR; //const-ref optimization
*subObjMightMatch = matchesFilterBegin(subNameBegin, filterFileIn) || //might match a file in subdirectory
matchesFilterBegin(subNameBegin, filterFolderIn); //or another subdirectory
@@ -265,7 +322,7 @@ bool NameFilter::passDirFilter(const Zstring& relDirname, bool* subObjMightMatch
bool NameFilter::isNull() const
{
- static NameFilter output(DefaultStr("*"), Zstring());
+ static NameFilter output(Zstr("*"), Zstring());
return *this == output;
}
@@ -294,7 +351,7 @@ bool NameFilter::cmpLessSameType(const BaseFilter& other) const
Zstring NameFilter::uniqueClassIdentifier() const
{
- return DefaultStr("NameFilter");
+ return Zstr("NameFilter");
}
diff --git a/library/filter.h b/library/filter.h
index 9497eb8d..91caff7d 100644
--- a/library/filter.h
+++ b/library/filter.h
@@ -183,7 +183,7 @@ bool NullFilter::cmpLessSameType(const BaseFilter& other) const
inline
Zstring NullFilter::uniqueClassIdentifier() const
{
- return DefaultStr("NullFilter");
+ return Zstr("NullFilter");
}
@@ -227,7 +227,7 @@ bool CombinedFilter::cmpLessSameType(const BaseFilter& other) const
inline
Zstring CombinedFilter::uniqueClassIdentifier() const
{
- return DefaultStr("CombinedFilter");
+ return Zstr("CombinedFilter");
}
diff --git a/library/icon_buffer.cpp b/library/icon_buffer.cpp
index 81d14146..20ff60f7 100644
--- a/library/icon_buffer.cpp
+++ b/library/icon_buffer.cpp
@@ -9,7 +9,6 @@
#include <map>
#include <queue>
#include <set>
-#include <boost/thread.hpp>
#ifdef FFS_WIN
#include <wx/msw/wrapwin.h> //includes "windows.h"
@@ -24,43 +23,38 @@
using ffs3::IconBuffer;
-namespace
-{
#ifdef FFS_WIN
-Zstring getFileExtension(const Zstring& filename)
+IconBuffer::BasicString IconBuffer::getFileExtension(const BasicString& filename)
{
- 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() :
- Zstring(shortName.c_str() + pos + 1);
+ const BasicString shortName = filename.AfterLast(Zchar('\\')); //warning: using windows file name separator!
+
+ return shortName.find(Zchar('.')) != BasicString::npos ?
+ filename.AfterLast(Zchar('.')) :
+ BasicString();
}
//test for extension for icons that physically have to be retrieved from disc
-bool isPriceyExtension(const Zstring& extension)
+bool IconBuffer::isPriceyExtension(const IconBuffer::BasicString& extension)
{
- static std::set<Zstring, LessFilename> exceptions;
- static bool isInitalized = false;
- if (!isInitalized)
+ static std::set<BasicString, LessFilename> exceptions;
+ if (exceptions.empty())
{
- 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"));
+ exceptions.insert(Zstr("exe"));
+ exceptions.insert(Zstr("lnk"));
+ exceptions.insert(Zstr("ico"));
+ exceptions.insert(Zstr("ani"));
+ exceptions.insert(Zstr("cur"));
+ exceptions.insert(Zstr("url"));
+ exceptions.insert(Zstr("msc"));
+ exceptions.insert(Zstr("scr"));
}
return exceptions.find(extension) != exceptions.end();
}
#endif
-}
-//################################################################################################################################################
+//################################################################################################################################################
class IconBuffer::IconHolder //handle HICON/GdkPixbuf ownership WITHOUT ref-counting to allow thread-safe usage (in contrast to wxIcon)
{
public:
@@ -139,7 +133,7 @@ const wxIcon& IconBuffer::getDirectoryIcon() //one folder icon should be suffici
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
+ if (::SHGetFileInfo(Zstr("dummy"), //Windows Seven doesn't like this parameter to be an empty string
FILE_ATTRIBUTE_DIRECTORY,
&fileInfo,
sizeof(fileInfo),
@@ -152,14 +146,14 @@ const wxIcon& IconBuffer::getDirectoryIcon() //one folder icon should be suffici
}
#elif defined FFS_LINUX
- folderIcon = getAssociatedIcon(DefaultStr("/usr/")).toWxIcon(); //all directories will look like "/usr/"
+ folderIcon = getAssociatedIcon(Zstr("/usr/")).toWxIcon(); //all directories will look like "/usr/"
#endif
}
return folderIcon;
}
-IconBuffer::IconHolder IconBuffer::getAssociatedIcon(const Zstring& filename)
+IconBuffer::IconHolder IconBuffer::getAssociatedIcon(const BasicString& filename)
{
#ifdef FFS_WIN
//despite what docu says about SHGetFileInfo() it can't handle all relative filenames, e.g. "\DirName"
@@ -231,15 +225,14 @@ IconBuffer::IconHolder IconBuffer::getAssociatedIcon(const Zstring& filename)
#endif
}
-
#ifdef FFS_WIN
-IconBuffer::IconHolder IconBuffer::getAssociatedIconByExt(const Zstring& extension)
+IconBuffer::IconHolder IconBuffer::getAssociatedIconByExt(const BasicString& 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
+ ::SHGetFileInfo((Zstr("dummy.") + extension).c_str(), //Windows Seven doesn't like this parameter to be without short name
FILE_ATTRIBUTE_NORMAL,
&fileInfo,
sizeof(fileInfo),
@@ -250,12 +243,6 @@ IconBuffer::IconHolder IconBuffer::getAssociatedIconByExt(const Zstring& extensi
#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)
-//---------------------------------------------------------------------------------------------------
-
-
class IconBuffer::WorkerThread
{
public:
@@ -308,7 +295,7 @@ void IconBuffer::WorkerThread::setWorkload(const std::vector<Zstring>& load) //(
shared.workload.clear();
for (std::vector<Zstring>::const_iterator i = load.begin(); i != load.end(); ++i)
- shared.workload.push_back(FileName(i->c_str(), i->c_str() + i->length() + 1)); //make DEEP COPY from Zstring (include null-termination)!
+ shared.workload.push_back(FileName(i->c_str())); //make DEEP COPY from Zstring
}
shared.condition.notify_one();
@@ -344,20 +331,20 @@ void IconBuffer::WorkerThread::doWork()
//do work: get the file icon.
while (true)
{
- Zstring fileName;
+ BasicString fileName;
{
boost::lock_guard<boost::mutex> dummy(shared.mutex);
if (shared.workload.empty())
break; //enter waiting state
- fileName = &shared.workload.back()[0]; //deep copy (includes NULL-termination)
+ fileName = shared.workload.back(); //deep copy
shared.workload.pop_back();
}
- if (iconBuffer.requestFileIcon(fileName)) //thread safety: Zstring okay, won't be reference-counted in requestIcon()
+ if (iconBuffer.requestFileIcon(fileName.c_str())) //thread safety: Zstring okay, won't be reference-counted in requestIcon()
continue; //icon already in buffer: skip
#ifdef FFS_WIN
- const Zstring extension = getFileExtension(fileName); //thread-safe: no sharing!
+ const BasicString 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
{
const IconHolder newIcon = IconBuffer::getAssociatedIcon(fileName);
@@ -377,8 +364,8 @@ void IconBuffer::WorkerThread::doWork()
//---------------------------------------------------------------------------------------------------
-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
+class IconBuffer::IconDB : public std::map<BasicString, IconBuffer::IconHolder> {}; //entryName/icon -> ATTENTION: avoid ref-counting for this shared data structure!
+class IconBuffer::IconDbSequence : public std::queue<BasicString> {}; //entryName
//---------------------------------------------------------------------------------------------------
@@ -405,10 +392,11 @@ bool IconBuffer::requestFileIcon(const Zstring& fileName, wxIcon* icon)
#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);
+ const BasicString extension = getFileExtension(fileName.c_str());
+ const BasicString searchString = isPriceyExtension(extension) ? fileName.c_str() : extension.c_str();
+ IconDB::const_iterator i = buffer->find(searchString);
#elif defined FFS_LINUX
- IconDB::const_iterator i = buffer->find(fileName);
+ IconDB::const_iterator i = buffer->find(fileName.c_str());
#endif
if (i == buffer->end())
@@ -427,17 +415,14 @@ void IconBuffer::setWorkload(const std::vector<Zstring>& load)
}
-void IconBuffer::insertIntoBuffer(const DefaultChar* entryName, const IconHolder& icon) //called by worker thread
+void IconBuffer::insertIntoBuffer(const BasicString& entryName, const IconHolder& icon) //called by worker thread
{
boost::lock_guard<boost::mutex> dummy(lockIconDB);
- //thread safety, ref-counting: (implicitly) make deep copy!
- const Zstring fileNameZ = entryName;
-
- 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!
-
+ //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(entryName, icon));
if (rc.second) //if insertion took place
- bufSequence->push(fileNameZ); //note: sharing Zstring with IconDB!!!
+ bufSequence->push(entryName); //note: sharing Zstring with IconDB!!!
assert(buffer->size() == bufSequence->size());
diff --git a/library/icon_buffer.h b/library/icon_buffer.h
index 9dc1ee55..3e021445 100644
--- a/library/icon_buffer.h
+++ b/library/icon_buffer.h
@@ -11,7 +11,7 @@
#include "../shared/zstring.h"
#include <memory>
#include <wx/icon.h>
-#include <boost/thread/mutex.hpp>
+#include "../shared/boost_thread_wrap.h" //include <boost/thread.hpp>
namespace ffs3
@@ -42,16 +42,26 @@ private:
class IconHolder;
class IconDbSequence;
+//---------------------------------------------------------------------------------------------------
+typedef Zbase<Zchar, StorageDeepCopy> BasicString; //thread safe string class
+//avoid reference-counted objects for shared data: NOT THREADSAFE!!! (implicitly shared variable: ref-count)
+//---------------------------------------------------------------------------------------------------
+
//methods used by worker thread
- void insertIntoBuffer(const DefaultChar* entryName, const IconHolder& icon);
+ void insertIntoBuffer(const BasicString& entryName, const IconHolder& icon);
+
+ static IconHolder getAssociatedIcon(const BasicString& filename);
+ static IconHolder getAssociatedIconByExt(const BasicString& extension);
- static IconHolder getAssociatedIcon(const Zstring& filename);
- static IconHolder getAssociatedIconByExt(const Zstring& extension);
+#ifdef FFS_WIN
+static BasicString getFileExtension(const BasicString& filename);
+static bool isPriceyExtension(const BasicString& extension);
+#endif
//---------------------- Shared Data -------------------------
boost::mutex lockIconDB;
std::auto_ptr<IconDB> buffer; //use synchronisation when accessing this!
- std::auto_ptr<IconDbSequence> bufSequence; //save sequence of buffer entry to delete oldest elements (implicitly shared by sharing Zstring with IconDB!!!)
+ std::auto_ptr<IconDbSequence> bufSequence; //save sequence of buffer entry to delete oldest elements
//------------------------------------------------------------
class WorkerThread;
diff --git a/library/process_xml.cpp b/library/process_xml.cpp
index e81fb3f9..c7597247 100644
--- a/library/process_xml.cpp
+++ b/library/process_xml.cpp
@@ -415,9 +415,6 @@ void FfsXmlParser::readXmlGlobalSettings(xmlAccess::XmlGlobalSettings& outputCfg
//try to read program language setting
readXmlElementLogging("Language", global, outputCfg.programLanguage);
- //ignore +/- 1 hour due to DST change
- readXmlElementLogging("IgnoreOneHourDifference", global, outputCfg.ignoreOneHourDiff);
-
//copy locked files using VSS
readXmlElementLogging("CopyLockedFiles", global, outputCfg.copyLockedFiles);
@@ -465,6 +462,11 @@ void FfsXmlParser::readXmlGlobalSettings(xmlAccess::XmlGlobalSettings& outputCfg
readXmlElementLogging("RespectCaseOnSearch", mainWindow, outputCfg.gui.textSearchRespectCase);
+ size_t folderPairMax = 0;
+ readXmlElementLogging("FolderPairsMax", mainWindow, folderPairMax);
+ outputCfg.gui.addFolderPairCountMax = std::max(static_cast<size_t>(2), folderPairMax) - 1; //map folderPairMax to additionalFolderPairMax
+
+
//###########################################################
//read column attributes
readXmlAttributeLogging("AutoAdjust", TiXmlHandleConst(mainWindow).FirstChild("LeftColumns").ToElement(), outputCfg.gui.autoAdjustColumnsLeft);
@@ -824,9 +826,6 @@ bool writeXmlGlobalSettings(const xmlAccess::XmlGlobalSettings& inputCfg, TiXmlD
//program language
addXmlElement("Language", inputCfg.programLanguage, global);
- //ignore +/- 1 hour due to DST change
- addXmlElement("IgnoreOneHourDifference", inputCfg.ignoreOneHourDiff, global);
-
//copy locked files using VSS
addXmlElement("CopyLockedFiles", inputCfg.copyLockedFiles, global);
@@ -888,6 +887,9 @@ bool writeXmlGlobalSettings(const xmlAccess::XmlGlobalSettings& inputCfg, TiXmlD
addXmlElement("RespectCaseOnSearch", inputCfg.gui.textSearchRespectCase, mainWindow);
+ addXmlElement("FolderPairsMax", inputCfg.gui.addFolderPairCountMax + 1 /*add main pair*/, mainWindow);
+
+
//write column attributes
TiXmlElement* leftColumn = new TiXmlElement("LeftColumns");
mainWindow->LinkEndChild(leftColumn);
diff --git a/library/process_xml.h b/library/process_xml.h
index 22465739..24a2fe9b 100644
--- a/library/process_xml.h
+++ b/library/process_xml.h
@@ -117,14 +117,12 @@ struct XmlGlobalSettings
//Shared (GUI/BATCH) settings
XmlGlobalSettings() :
programLanguage(retrieveSystemLanguage()),
- ignoreOneHourDiff(false),
copyLockedFiles(true),
copyFilePermissions(false),
fileTimeTolerance(2), //default 2s: FAT vs NTFS
verifyFileCopy(false) {}
int programLanguage;
- bool ignoreOneHourDiff; //ignore +/- 1 hour due to DST change
bool copyLockedFiles; //VSS usage
bool copyFilePermissions;
@@ -157,6 +155,7 @@ struct XmlGlobalSettings
#endif
showFileIconsLeft(true),
showFileIconsRight(true),
+ addFolderPairCountMax(5),
lastUpdateCheck(0)
{
//default external apps will be translated "on the fly"!!!
@@ -204,6 +203,7 @@ struct XmlGlobalSettings
bool showFileIconsLeft;
bool showFileIconsRight;
+ size_t addFolderPairCountMax;
long lastUpdateCheck; //time of last update check
} gui;
diff --git a/library/status_handler.h b/library/status_handler.h
index a1226c54..c15a80ba 100644
--- a/library/status_handler.h
+++ b/library/status_handler.h
@@ -8,8 +8,7 @@
#define STATUSHANDLER_H_INCLUDED
#include <wx/longlong.h>
-
-class Zstring;
+#include "../shared/zstring.h"
const int UI_UPDATE_INTERVAL = 100; //perform ui updates not more often than necessary, 100 seems to be a good value with only a minimal performance loss
@@ -53,9 +52,10 @@ public:
//these methods have to be implemented in the derived classes to handle error and status information
virtual void initNewProcess(int objectsTotal, wxLongLong dataTotal, Process processID) = 0; //informs about the total amount of data that will be processed from now on
- virtual void updateStatusText(const Zstring& text) = 0;
virtual void updateProcessedData(int objectsProcessed, wxLongLong dataProcessed) = 0; //called periodically after data was processed
+ virtual void reportInfo(const Zstring& text) = 0;
+
//this method is triggered repeatedly by requestUiRefresh() and can be used to refresh the ui by dispatching pending events
virtual void forceUiRefresh() = 0;
bgstack15