From c32707148292d104c66276b43796d6057c8c7a5d Mon Sep 17 00:00:00 2001 From: Daniel Wilhelm Date: Fri, 18 Apr 2014 17:08:42 +0200 Subject: 3.10 --- library/custom_grid.cpp | 133 +++++++++++++++++++++++++++++------ library/custom_grid.h | 10 ++- library/db_file.cpp | 13 ++-- library/db_file.h | 8 ++- library/dir_lock.cpp | 177 ++++++++++++++++++++++++++++++++++++++--------- library/dir_lock.h | 4 +- library/error_log.h | 2 +- library/filter.cpp | 151 +++++++++++++++++++++++++++------------- library/filter.h | 4 +- library/icon_buffer.cpp | 91 ++++++++++-------------- library/icon_buffer.h | 20 ++++-- library/process_xml.cpp | 14 ++-- library/process_xml.h | 4 +- library/status_handler.h | 6 +- 14 files changed, 453 insertions(+), 184 deletions(-) (limited to 'library') 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 #include "icon_buffer.h" #include +#include #ifdef FFS_WIN #include @@ -380,12 +381,12 @@ protected: virtual void visit(const SymLinkMapping& linkObj) { iconName = linkObj.getLinkType() == LinkDescriptor::TYPE_DIR ? - DefaultStr("folder") : + Zstr("folder") : linkObj.getFullName(); } 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 CustomGrid::mousePosToCell(wxPoint pos) +{ + int x = -1; + int y = -1; + CalcUnscrolledPosition(pos.x, pos.y, &x, &y); + + std::pair output(-1, -1); + if (x >= 0 && y >= 0) + { + output.first = YToRow(y); + output.second = XToCol(x); + } + return output; +} + + std::set CustomGrid::getAllSelectedRows() const { std::set 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 +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()) + { + struct AssembleTooltip : public FSObjectVisitor + { + AssembleTooltip(wxString& tipMsg) : tipMsg_(tipMsg) {} + + virtual void visit(const FileMapping& fileObj) + { + tipMsg_ = zToWx(fileObj.getShortName()) + wxT("\n") + + _("Size") + wxT(": ") + ffs3::formatFilesizeToShortString(fileObj.getFileSize()) + wxT("\n") + + _("Date") + wxT(": ") + ffs3::utcTimeToLocalString(fileObj.getLastWriteTime()); + } + + virtual void visit(const SymLinkMapping& linkObj) + { + tipMsg_ = zToWx(linkObj.getShortName()) + wxT("\n") + + _("Date") + wxT(": ") + ffs3::utcTimeToLocalString(linkObj.getLastWriteTime()); + } + + virtual void visit(const DirMapping& dirObj) + { + tipMsg_ = zToWx(dirObj.getShortName()); + } + + 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(ceil(height / static_cast(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(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(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 #include #include +#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 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 + 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 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() + DefaultStr(".tmp"); - const Zstring fileNameRightTmp = baseMapping.getDBFilename() + DefaultStr(".tmp");; + const Zstring fileNameLeftTmp = baseMapping.getDBFilename() + Zstr(".tmp"); + const Zstring fileNameRightTmp = baseMapping.getDBFilename() + 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 DirInfoPtr; -std::pair loadFromDisk(const BaseDirMapping& baseMapping); //throw (FileError) -> return value always bound! +class FileErrorDatabaseNotExisting : public FileError +{ +public: + FileErrorDatabaseNotExisting(const wxString& message) : FileError(message) {} +}; + +std::pair 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 #include #include -#include +#include "../shared/boost_thread_wrap.h" //include #include "../shared/loki/ScopeGuard.h" #include #include "../shared/system_constants.h" #include "../shared/guid.h" #include "../shared/file_io.h" #include +#include "../shared/serialize.h" #ifdef FFS_WIN +#include #include //includes "windows.h" #include "../shared/long_path_prefix.h" #elif defined FFS_LINUX +#include "../shared/file_handling.h" #include #include +#include #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 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(stream); + boost::scoped_array 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(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(stream); + computerId = readString(stream); + } + + void toStream(wxOutputStream& stream) const //write + { + lockId.toStream(stream); + util::writeNumber(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 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 #include +#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& 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& fileFilter, st } -class MatchFound : public std::unary_function +namespace { -public: - MatchFound(const Zstring& name) : name_(name) {} - - bool operator()(const Zstring& mask) const +template +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& 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 +{ +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& filter) +bool matchesFilter(const Zstring& nameFormatted, const std::set& 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& filter) +{ return std::find_if(filter.begin(), filter.end(), boost::bind(matchesMaskBegin, nameFormatted.c_str(), _1)) != filter.end(); } @@ -201,10 +244,10 @@ std::vector compoundStringToFilter(const Zstring& filterString) //delimiters may be ';' or '\n' std::vector output; - const std::vector filterPreProcessing = filterString.Tokenize(wxT(';')); + const std::vector filterPreProcessing = filterString.Split(Zchar(';')); for (std::vector::const_iterator i = filterPreProcessing.begin(); i != filterPreProcessing.end(); ++i) { - const std::vector newEntries = i->Tokenize(wxT('\n')); + const std::vector 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 #include #include -#include #ifdef FFS_WIN #include //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 exceptions; - static bool isInitalized = false; - if (!isInitalized) + static std::set 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 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& load) //( shared.workload.clear(); for (std::vector::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 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 {}; //entryName/icon -> ATTENTION: avoid ref-counting for this shared data structure!!! (== don't copy instances between threads) -class IconBuffer::IconDbSequence : public std::queue {}; //entryName +class IconBuffer::IconDB : public std::map {}; //entryName/icon -> ATTENTION: avoid ref-counting for this shared data structure! +class IconBuffer::IconDbSequence : public std::queue {}; //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& 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 dummy(lockIconDB); - //thread safety, ref-counting: (implicitly) make deep copy! - const Zstring fileNameZ = entryName; - - const std::pair 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 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 #include -#include +#include "../shared/boost_thread_wrap.h" //include namespace ffs3 @@ -42,16 +42,26 @@ private: class IconHolder; class IconDbSequence; +//--------------------------------------------------------------------------------------------------- +typedef Zbase 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 buffer; //use synchronisation when accessing this! - std::auto_ptr bufSequence; //save sequence of buffer entry to delete oldest elements (implicitly shared by sharing Zstring with IconDB!!!) + std::auto_ptr 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(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 - -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; -- cgit