diff options
author | Daniel Wilhelm <daniel@wili.li> | 2014-04-18 17:12:46 +0200 |
---|---|---|
committer | Daniel Wilhelm <daniel@wili.li> | 2014-04-18 17:12:46 +0200 |
commit | b338e29fd3eaf700f8c8360aa0310048ba941d54 (patch) | |
tree | 122f8ef3790d12cd10275ef7453a9e8053322d78 /library | |
parent | 3.18 (diff) | |
download | FreeFileSync-b338e29fd3eaf700f8c8360aa0310048ba941d54.tar.gz FreeFileSync-b338e29fd3eaf700f8c8360aa0310048ba941d54.tar.bz2 FreeFileSync-b338e29fd3eaf700f8c8360aa0310048ba941d54.zip |
3.19
Diffstat (limited to 'library')
-rw-r--r-- | library/custom_grid.cpp | 42 | ||||
-rw-r--r-- | library/db_file.cpp | 386 | ||||
-rw-r--r-- | library/db_file.h | 2 | ||||
-rw-r--r-- | library/dir_lock.cpp | 69 | ||||
-rw-r--r-- | library/dir_lock.h | 3 | ||||
-rw-r--r-- | library/error_log.cpp | 13 | ||||
-rw-r--r-- | library/error_log.h | 3 | ||||
-rw-r--r-- | library/hard_filter.cpp | 47 | ||||
-rw-r--r-- | library/icon_buffer.cpp | 192 | ||||
-rw-r--r-- | library/icon_buffer.h | 4 | ||||
-rw-r--r-- | library/lock_holder.h | 8 | ||||
-rw-r--r-- | library/process_xml.cpp | 21 | ||||
-rw-r--r-- | library/process_xml.h | 6 | ||||
-rw-r--r-- | library/resources.cpp | 4 | ||||
-rw-r--r-- | library/soft_filter.h | 45 | ||||
-rw-r--r-- | library/statistics.cpp | 4 | ||||
-rw-r--r-- | library/status_handler.h | 51 |
17 files changed, 463 insertions, 437 deletions
diff --git a/library/custom_grid.cpp b/library/custom_grid.cpp index c2b87b5d..97b608fb 100644 --- a/library/custom_grid.cpp +++ b/library/custom_grid.cpp @@ -5,7 +5,6 @@ // ************************************************************************** // #include "custom_grid.h" -#include "../shared/system_constants.h" #include "resources.h" #include <wx/dc.h> #include "../shared/util.h" @@ -283,16 +282,16 @@ protected: switch (colType_) { case xmlAccess::FULL_PATH: - value = zToWx(fileObj.getFullName<side>().BeforeLast(common::FILE_NAME_SEPARATOR)); + value = toWx(fileObj.getFullName<side>().BeforeLast(FILE_NAME_SEPARATOR)); break; case xmlAccess::FILENAME: //filename - value = zToWx(fileObj.getShortName<side>()); + value = toWx(fileObj.getShortName<side>()); break; case xmlAccess::REL_PATH: //relative path - value = zToWx(fileObj.getParentRelativeName()); + value = toWx(fileObj.getParentRelativeName()); break; case xmlAccess::DIRECTORY: - value = zToWx(fileObj.getBaseDirPf<side>()); + value = toWx(fileObj.getBaseDirPf<side>()); break; case xmlAccess::SIZE: //file size value = zen::toStringSep(fileObj.getFileSize<side>()); @@ -301,7 +300,7 @@ protected: value = zen::utcTimeToLocalString(fileObj.getLastWriteTime<side>()); break; case xmlAccess::EXTENSION: //file extension - value = zToWx(fileObj.getExtension<side>()); + value = toWx(fileObj.getExtension<side>()); break; } } @@ -311,16 +310,16 @@ protected: switch (colType_) { case xmlAccess::FULL_PATH: - value = zToWx(linkObj.getFullName<side>().BeforeLast(common::FILE_NAME_SEPARATOR)); + value = toWx(linkObj.getFullName<side>().BeforeLast(FILE_NAME_SEPARATOR)); break; case xmlAccess::FILENAME: //filename - value = zToWx(linkObj.getShortName<side>()); + value = toWx(linkObj.getShortName<side>()); break; case xmlAccess::REL_PATH: //relative path - value = zToWx(linkObj.getParentRelativeName()); + value = toWx(linkObj.getParentRelativeName()); break; case xmlAccess::DIRECTORY: - value = zToWx(linkObj.getBaseDirPf<side>()); + value = toWx(linkObj.getBaseDirPf<side>()); break; case xmlAccess::SIZE: //file size value = _("<Symlink>"); @@ -339,16 +338,16 @@ protected: switch (colType_) { case xmlAccess::FULL_PATH: - value = zToWx(dirObj.getFullName<side>()); + value = toWx(dirObj.getFullName<side>()); break; case xmlAccess::FILENAME: - value = zToWx(dirObj.getShortName<side>()); + value = toWx(dirObj.getShortName<side>()); break; case xmlAccess::REL_PATH: - value = zToWx(dirObj.getParentRelativeName()); + value = toWx(dirObj.getParentRelativeName()); break; case xmlAccess::DIRECTORY: - value = zToWx(dirObj.getBaseDirPf<side>()); + value = toWx(dirObj.getBaseDirPf<side>()); break; case xmlAccess::SIZE: //file size value = _("<Directory>"); @@ -1264,7 +1263,8 @@ public: icon = IconBuffer::getFileIcon(); //better than nothing } - dc.DrawIcon(icon, rectShrinked.GetX() + LEFT_BORDER, rectShrinked.GetY()); + if (icon.IsOk()) + dc.DrawIcon(icon, rectShrinked.GetX() + LEFT_BORDER, rectShrinked.GetY()); //----------------------------------------------------------------------------------------------- //save status of last icon load -> used for async. icon loading @@ -1374,20 +1374,20 @@ void CustomGridRim::setTooltip(const wxMouseEvent& event) virtual void visit(const FileMapping& fileObj) { - tipMsg_ = zToWx(fileObj.getRelativeName<side>()) + wxT("\n") + - _("Size") + wxT(": ") + zen::formatFilesizeToShortString(fileObj.getFileSize<side>()) + wxT("\n") + - _("Date") + wxT(": ") + zen::utcTimeToLocalString(fileObj.getLastWriteTime<side>()); + tipMsg_ = toWx(fileObj.getRelativeName<side>()) + "\n" + + _("Size") + ": " + zen::formatFilesizeToShortString(fileObj.getFileSize<side>()) + "\n" + + _("Date") + ": " + zen::utcTimeToLocalString(fileObj.getLastWriteTime<side>()); } virtual void visit(const SymLinkMapping& linkObj) { - tipMsg_ = zToWx(linkObj.getRelativeName<side>()) + wxT("\n") + - _("Date") + wxT(": ") + zen::utcTimeToLocalString(linkObj.getLastWriteTime<side>()); + tipMsg_ = toWx(linkObj.getRelativeName<side>()) + "\n" + + _("Date") + ": " + zen::utcTimeToLocalString(linkObj.getLastWriteTime<side>()); } virtual void visit(const DirMapping& dirObj) { - tipMsg_ = zToWx(dirObj.getRelativeName<side>()); + tipMsg_ = toWx(dirObj.getRelativeName<side>()); } wxString& tipMsg_; diff --git a/library/db_file.cpp b/library/db_file.cpp index 1d48dbdf..ec1c4464 100644 --- a/library/db_file.cpp +++ b/library/db_file.cpp @@ -30,10 +30,29 @@ namespace { //------------------------------------------------------------------------------------------------------------------------------- const char FILE_FORMAT_DESCR[] = "FreeFileSync"; -const int FILE_FORMAT_VER = 6; +const int FILE_FORMAT_VER = 7; //------------------------------------------------------------------------------------------------------------------------------- +template <SelectedSide side> inline +Zstring getDBFilename(const BaseDirMapping& baseMap, bool tempfile = false) +{ + //Linux and Windows builds are binary incompatible: char/wchar_t case, sensitive/insensitive + //32 and 64 bit db files ARE designed to be binary compatible! + //Give db files different names. + //make sure they end with ".ffs_db". These files will not be included into comparison when located in base sync directories +#ifdef FFS_WIN + Zstring dbname = Zstring(Zstr("sync")) + (tempfile ? Zstr(".tmp") : Zstr("")) + SYNC_DB_FILE_ENDING; +#elif defined FFS_LINUX + //files beginning with dots are hidden e.g. in Nautilus + Zstring dbname = Zstring(Zstr(".sync")) + (tempfile ? Zstr(".tmp") : Zstr("")) + SYNC_DB_FILE_ENDING; +#endif + + return baseMap.getBaseDir<side>() + dbname; +} + + + class FileInputStreamDB : public FileInputStream { public: @@ -45,7 +64,7 @@ public: Read(formatDescr, sizeof(formatDescr)); //throw (FileError) if (!std::equal(FILE_FORMAT_DESCR, FILE_FORMAT_DESCR + sizeof(FILE_FORMAT_DESCR), formatDescr)) - throw FileError(wxString(_("Incompatible synchronization database format:")) + wxT(" \n") + wxT("\"") + zToWx(filename) + wxT("\"")); + throw FileError(_("Incompatible synchronization database format:") + " \n" + "\"" + filename + "\""); } private: @@ -138,115 +157,162 @@ private: namespace { typedef std::string UniqueId; -typedef std::shared_ptr<std::vector<char> > MemoryStreamPtr; //byte stream representing DirInformation -typedef std::map<UniqueId, MemoryStreamPtr> DirectoryTOC; //list of streams ordered by a UUID pointing to their partner database -typedef std::pair<UniqueId, DirectoryTOC> DbStreamData; //header data: UUID representing this database, item data: list of dir-streams +typedef std::shared_ptr<std::vector<char> > MemoryStreamPtr; //byte stream representing DirInformation +typedef std::map<UniqueId, MemoryStreamPtr> StreamMapping; //list of streams ordered by session UUID } -/* Example -left side right side ---------- ---------- -DB-ID 123 <-\ /-> DB-ID 567 - \/ -Partner-ID 111 /\ Partner-ID 222 -Partner-ID 567 _/ \_ Partner-ID 123 - ... ... -*/ class ReadFileStream : public zen::ReadInputStream { public: - ReadFileStream(wxInputStream& stream, const wxString& filename, DbStreamData& output, int& versionId) : ReadInputStream(stream, filename) + ReadFileStream(wxInputStream& stream, const wxString& filename, StreamMapping& streamList, bool leftSide) : ReadInputStream(stream, filename) { //|------------------------------------------------------------------------------------- //| ensure 32/64 bit portability: used fixed size data types only e.g. boost::uint32_t | //|------------------------------------------------------------------------------------- boost::int32_t version = readNumberC<boost::int32_t>(); - if (version != FILE_FORMAT_VER) //read file format version - throw FileError(wxString(_("Incompatible synchronization database format:")) + wxT(" \n") + wxT("\"") + filename + wxT("\"")); - versionId = version; - //read DB id - const CharArray tmp = readArrayC(); - output.first.assign(tmp->begin(), tmp->end()); +#ifndef _MSC_VER +#warning remove this check after migration! +#endif + if (version != 6) //migrate! + + if (version != FILE_FORMAT_VER) //read file format version + throw FileError(_("Incompatible synchronization database format:") + " \n" + "\"" + filename.c_str() + "\""); + - DirectoryTOC& dbList = output.second; - dbList.clear(); +#ifndef _MSC_VER +#warning remove this case after migration! +#endif - boost::uint32_t dbCount = readNumberC<boost::uint32_t>(); //number of databases: one for each sync-pair - while (dbCount-- != 0) + if (version == 6) { - //DB id of partner databases - const CharArray tmp2 = readArrayC(); - const std::string partnerID(tmp2->begin(), tmp2->end()); + streamList.clear(); + + //read DB id + const CharArray tmp = readArrayC(); + std::string mainId(tmp->begin(), tmp->end()); + + boost::uint32_t dbCount = readNumberC<boost::uint32_t>(); //number of databases: one for each sync-pair + while (dbCount-- != 0) + { + //DB id of partner databases + const CharArray tmp2 = readArrayC(); + const std::string partnerID(tmp2->begin(), tmp2->end()); - CharArray buffer = readArrayC(); //read db-entry stream (containing DirInformation) + CharArray buffer = readArrayC(); //read db-entry stream (containing DirInformation) - dbList.insert(std::make_pair(partnerID, buffer)); + if (leftSide) + streamList.insert(std::make_pair(partnerID + mainId, buffer)); + else + streamList.insert(std::make_pair(mainId + partnerID, buffer)); + } + } + else + { + streamList.clear(); + + boost::uint32_t dbCount = readNumberC<boost::uint32_t>(); //number of databases: one for each sync-pair + while (dbCount-- != 0) + { + //DB id of partner databases + const CharArray tmp2 = readArrayC(); + const std::string sessionID(tmp2->begin(), tmp2->end()); + + CharArray buffer = readArrayC(); //read db-entry stream (containing DirInformation) + + streamList.insert(std::make_pair(sessionID, buffer)); + } } } }; +namespace +{ +StreamMapping loadStreams(const Zstring& filename, -DbStreamData loadFile(const Zstring& filename) //throw (FileError) +#ifndef _MSC_VER +#warning remove this parameter after migration! +#endif + bool leftSide) //throw (FileError) { if (!zen::fileExists(filename)) - throw FileErrorDatabaseNotExisting(wxString(_("Initial synchronization:")) + wxT(" \n\n") + - _("One of the FreeFileSync database files is not yet existing:") + wxT(" \n") + - wxT("\"") + zToWx(filename) + wxT("\"")); + throw FileErrorDatabaseNotExisting(_("Initial synchronization:") + " \n\n" + + _("One of the FreeFileSync database files is not yet existing:") + " \n" + + "\"" + filename + "\""); - //read format description (uncompressed) - FileInputStreamDB uncompressed(filename); //throw (FileError) + try + { + //read format description (uncompressed) + FileInputStreamDB uncompressed(filename); //throw (FileError) - wxZlibInputStream input(uncompressed, wxZLIB_ZLIB); + wxZlibInputStream input(uncompressed, wxZLIB_ZLIB); - DbStreamData output; - int versionId = 0; - ReadFileStream (input, zToWx(filename), output, versionId); - return output; + StreamMapping streamList; + ReadFileStream(input, toWx(filename), streamList, leftSide); + return streamList; + } + catch (const std::bad_alloc&) //this is most likely caused by a corrupted database file + { + throw FileError(_("Error reading from synchronization database:") + " (bad_alloc)"); + } } -std::pair<DirInfoPtr, DirInfoPtr> zen::loadFromDisk(const BaseDirMapping& baseMapping) //throw (FileError) +DirInfoPtr parseStream(const std::vector<char>& stream, const Zstring& fileName) //throw FileError -> return value always bound! { - const Zstring fileNameLeft = baseMapping.getDBFilename<LEFT_SIDE>(); - const Zstring fileNameRight = baseMapping.getDBFilename<RIGHT_SIDE>(); - try { - //read file data: db ID + mapping of partner-ID/DirInfo-stream - const DbStreamData dbEntriesLeft = ::loadFile(fileNameLeft); - const DbStreamData dbEntriesRight = ::loadFile(fileNameRight); - - //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 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("\"")); + //read streams into DirInfo + auto dirInfo = std::make_shared<DirInformation>(); + wxMemoryInputStream buffer(&stream[0], stream.size()); //convert char-array to inputstream: no copying, ownership not transferred + ReadDirInfo(buffer, toWx(fileName), *dirInfo); //throw FileError + return dirInfo; + } + catch (const std::bad_alloc&) //this is most likely caused by a corrupted database file + { + throw FileError(_("Error reading from synchronization database:") + " (bad_alloc)"); + } +} +} - if (dbRight == dbEntriesRight.second.end()) - 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("\"")); - //read streams into DirInfo - std::shared_ptr<DirInformation> dirInfoLeft(new DirInformation); - wxMemoryInputStream buffer(&(*dbLeft->second)[0], dbLeft->second->size()); //convert char-array to inputstream: no copying, ownership not transferred - ReadDirInfo(buffer, zToWx(fileNameLeft), *dirInfoLeft); //read file/dir information +std::pair<DirInfoPtr, DirInfoPtr> zen::loadFromDisk(const BaseDirMapping& baseMapping) //throw (FileError) +{ + const Zstring fileNameLeft = getDBFilename<LEFT_SIDE>(baseMapping); + const Zstring fileNameRight = getDBFilename<RIGHT_SIDE>(baseMapping); - std::shared_ptr<DirInformation> dirInfoRight(new DirInformation); - wxMemoryInputStream buffer2(&(*dbRight->second)[0], dbRight->second->size()); //convert char-array to inputstream: no copying, ownership not transferred - ReadDirInfo(buffer2, zToWx(fileNameRight), *dirInfoRight); //read file/dir information + //read file data: list of session ID + DirInfo-stream + const StreamMapping streamListLeft = ::loadStreams(fileNameLeft, true); //throw (FileError) + const StreamMapping streamListRight = ::loadStreams(fileNameRight, false); //throw (FileError) - return std::make_pair(dirInfoLeft, dirInfoRight); - } - catch (const std::bad_alloc&) //this is most likely caused by a corrupted database file + //find associated session: there can be at most one session within intersection of left and right ids + StreamMapping::const_iterator streamLeft = streamListLeft .end(); + StreamMapping::const_iterator streamRight = streamListRight.end(); + for (auto iterLeft = streamListLeft.begin(); iterLeft != streamListLeft.end(); ++iterLeft) { - throw FileError(wxString(_("Error reading from synchronization database:")) + wxT(" (bad_alloc)")); + auto iterRight = streamListRight.find(iterLeft->first); + if (iterRight != streamListRight.end()) + { + streamLeft = iterLeft; + streamRight = iterRight; + break; + } } + + if (streamLeft == streamListLeft .end() || + streamRight == streamListRight.end() || + !streamLeft ->second.get() || + !streamRight->second.get()) + throw FileErrorDatabaseNotExisting(_("Initial synchronization:") + " \n\n" + + _("No matching synchronization session found in database files:") + " \n" + + "\"" + fileNameLeft + "\"\n" + + "\"" + fileNameRight + "\""); + //read streams into DirInfo + DirInfoPtr dirInfoLeft = parseStream(*streamLeft ->second, fileNameLeft); //throw FileError + DirInfoPtr dirInfoRight = parseStream(*streamRight->second, fileNameRight); //throw FileError + + return std::make_pair(dirInfoLeft, dirInfoRight); } @@ -375,21 +441,16 @@ private: class WriteFileStream : public WriteOutputStream { public: - WriteFileStream(const DbStreamData& input, const wxString& filename, wxOutputStream& stream) : WriteOutputStream(filename, stream) + WriteFileStream(const StreamMapping& streamList, const wxString& filename, wxOutputStream& stream) : WriteOutputStream(filename, stream) { //save file format version writeNumberC<boost::int32_t>(FILE_FORMAT_VER); - //write DB id - writeArrayC(std::vector<char>(input.first.begin(), input.first.end())); - - const DirectoryTOC& dbList = input.second; + writeNumberC<boost::uint32_t>(static_cast<boost::uint32_t>(streamList.size())); //number of database records: one for each sync-pair - writeNumberC<boost::uint32_t>(static_cast<boost::uint32_t>(dbList.size())); //number of database records: one for each sync-pair - - for (DirectoryTOC::const_iterator i = dbList.begin(); i != dbList.end(); ++i) + for (StreamMapping::const_iterator i = streamList.begin(); i != streamList.end(); ++i) { - //DB id of partner database + //sync session id writeArrayC(std::vector<char>(i->first.begin(), i->first.end())); //write DirInformation stream @@ -400,7 +461,7 @@ public: //save/load DirContainer -void saveFile(const DbStreamData& dbStream, const Zstring& filename) //throw (FileError) +void saveFile(const StreamMapping& streamList, const Zstring& filename) //throw (FileError) { { //write format description (uncompressed) @@ -414,7 +475,7 @@ void saveFile(const DbStreamData& dbStream, const Zstring& filename) //throw (Fi 6 1,77 MB - 613 ms 9 (maximal compression) 1,74 MB - 3330 ms */ - WriteFileStream(dbStream, zToWx(filename), output); + WriteFileStream(streamList, toWx(filename), output); } //(try to) hide database file #ifdef FFS_WIN @@ -423,133 +484,132 @@ void saveFile(const DbStreamData& dbStream, const Zstring& filename) //throw (Fi } -bool entryExisting(const DirectoryTOC& table, const UniqueId& newKey, const MemoryStreamPtr& newValue) +bool equalEntry(const MemoryStreamPtr& lhs, const MemoryStreamPtr& rhs) { - DirectoryTOC::const_iterator iter = table.find(newKey); - if (iter == table.end()) - return false; - - if (!newValue.get() || !iter->second.get()) - return newValue.get() == iter->second.get(); + if (!lhs.get() || !rhs.get()) + return lhs.get() == rhs.get(); - return *newValue == *iter->second; + return *lhs == *rhs; } void zen::saveToDisk(const BaseDirMapping& baseMapping) //throw (FileError) { //transactional behaviour! write to tmp files first - const Zstring fileNameLeftTmp = baseMapping.getDBFilename<LEFT_SIDE>() + Zstr(".tmp"); - const Zstring fileNameRightTmp = baseMapping.getDBFilename<RIGHT_SIDE>() + Zstr(".tmp");; + const Zstring dbNameLeftTmp = getDBFilename<LEFT_SIDE >(baseMapping, true); + const Zstring dbNameRightTmp = getDBFilename<RIGHT_SIDE>(baseMapping, true); + + const Zstring dbNameLeft = getDBFilename<LEFT_SIDE >(baseMapping); + const Zstring dbNameRight = getDBFilename<RIGHT_SIDE>(baseMapping); //delete old tmp file, if necessary -> throws if deletion fails! - removeFile(fileNameLeftTmp); // - removeFile(fileNameRightTmp); //throw (FileError) + removeFile(dbNameLeftTmp); // + removeFile(dbNameRightTmp); //throw (FileError) - //load old database files... + //(try to) load old database files... + StreamMapping streamListLeft; + StreamMapping streamListRight; - //read file data: db ID + mapping of partner-ID/DirInfo-stream: may throw! - DbStreamData dbEntriesLeft; - try - { - dbEntriesLeft = ::loadFile(baseMapping.getDBFilename<LEFT_SIDE>()); - } - catch(FileError&) + try //read file data: list of session ID + DirInfo-stream { - //if error occurs: just overwrite old file! User is already informed about issues right after comparing! - //dbEntriesLeft has empty mapping, but already a DB-ID! - dbEntriesLeft.first = util::generateGUID(); + streamListLeft = ::loadStreams(dbNameLeft, true); } - - //read file data: db ID + mapping of partner-ID/DirInfo-stream: may throw! - DbStreamData dbEntriesRight; + catch(FileError&) {} //if error occurs: just overwrite old file! User is already informed about issues right after comparing! try { - dbEntriesRight = ::loadFile(baseMapping.getDBFilename<RIGHT_SIDE>()); + streamListRight = ::loadStreams(dbNameRight, false); } - catch(FileError&) + catch(FileError&) {} + + //find associated session: there can be at most one session within intersection of left and right ids + StreamMapping::iterator streamLeft = streamListLeft .end(); + StreamMapping::iterator streamRight = streamListRight.end(); + for (auto iterLeft = streamListLeft.begin(); iterLeft != streamListLeft.end(); ++iterLeft) { - dbEntriesRight.first = util::generateGUID(); + auto iterRight = streamListRight.find(iterLeft->first); + if (iterRight != streamListRight.end()) + { + streamLeft = iterLeft; + streamRight = iterRight; + break; + } } - //(try to) read old DirInfo - std::shared_ptr<DirInformation> oldDirInfoLeft; + DirInfoPtr oldDirInfoLeft; + DirInfoPtr oldDirInfoRight; try { - DirectoryTOC::const_iterator iter = dbEntriesLeft.second.find(dbEntriesRight.first); - if (iter != dbEntriesLeft.second.end()) - if (iter->second.get()) - { - const std::vector<char>& memStream = *iter->second; - wxMemoryInputStream buffer(&memStream[0], memStream.size()); //convert char-array to inputstream: no copying, ownership not transferred - std::shared_ptr<DirInformation> dirInfoTmp = std::make_shared<DirInformation>(); - ReadDirInfo(buffer, zToWx(baseMapping.getDBFilename<LEFT_SIDE>()), *dirInfoTmp); //read file/dir information - oldDirInfoLeft = dirInfoTmp; - } + if (streamLeft != streamListLeft .end() && + streamRight != streamListRight.end() && + streamLeft ->second.get() && + streamRight->second.get()) + { + oldDirInfoLeft = parseStream(*streamLeft ->second, dbNameLeft); //throw FileError + oldDirInfoRight = parseStream(*streamRight->second, dbNameRight); //throw FileError + } } - catch(FileError&) {} //if error occurs: just overwrite old file! User is already informed about issues right after comparing! - - std::shared_ptr<DirInformation> oldDirInfoRight; - try + catch(FileError&) { - DirectoryTOC::const_iterator iter = dbEntriesRight.second.find(dbEntriesLeft.first); - if (iter != dbEntriesRight.second.end()) - if (iter->second.get()) - { - const std::vector<char>& memStream = *iter->second; - wxMemoryInputStream buffer(&memStream[0], memStream.size()); //convert char-array to inputstream: no copying, ownership not transferred - std::shared_ptr<DirInformation> dirInfoTmp = std::make_shared<DirInformation>(); - ReadDirInfo(buffer, zToWx(baseMapping.getDBFilename<RIGHT_SIDE>()), *dirInfoTmp); //read file/dir information - oldDirInfoRight = dirInfoTmp; - } + //if error occurs: just overwrite old file! User is already informed about issues right after comparing! + oldDirInfoLeft .reset(); //read both or none! + oldDirInfoRight.reset(); // } - catch(FileError&) {} - //create new database entries - MemoryStreamPtr dbEntryLeft(new std::vector<char>); + MemoryStreamPtr newStreamLeft = std::make_shared<std::vector<char>>(); { wxMemoryOutputStream buffer; - DirContainer* oldDir = oldDirInfoLeft.get() ? &oldDirInfoLeft->baseDirContainer : NULL; - SaveDirInfo<LEFT_SIDE>(baseMapping, oldDir, zToWx(baseMapping.getDBFilename<LEFT_SIDE>()), buffer); - dbEntryLeft->resize(buffer.GetSize()); //convert output stream to char-array - buffer.CopyTo(&(*dbEntryLeft)[0], buffer.GetSize()); // + const DirContainer* oldDir = oldDirInfoLeft.get() ? &oldDirInfoLeft->baseDirContainer : NULL; + SaveDirInfo<LEFT_SIDE>(baseMapping, oldDir, toWx(dbNameLeft), buffer); + newStreamLeft->resize(buffer.GetSize()); //convert output stream to char-array + buffer.CopyTo(&(*newStreamLeft)[0], buffer.GetSize()); // } - MemoryStreamPtr dbEntryRight(new std::vector<char>); + MemoryStreamPtr newStreamRight = std::make_shared<std::vector<char>>(); { wxMemoryOutputStream buffer; - DirContainer* oldDir = oldDirInfoRight.get() ? &oldDirInfoRight->baseDirContainer : NULL; - SaveDirInfo<RIGHT_SIDE>(baseMapping, oldDir, zToWx(baseMapping.getDBFilename<RIGHT_SIDE>()), buffer); - dbEntryRight->resize(buffer.GetSize()); //convert output stream to char-array - buffer.CopyTo(&(*dbEntryRight)[0], buffer.GetSize()); // + const DirContainer* oldDir = oldDirInfoRight.get() ? &oldDirInfoRight->baseDirContainer : NULL; + SaveDirInfo<RIGHT_SIDE>(baseMapping, oldDir, toWx(dbNameRight), buffer); + newStreamRight->resize(buffer.GetSize()); //convert output stream to char-array + buffer.CopyTo(&(*newStreamRight)[0], buffer.GetSize()); // } - //create/update DirInfo-streams + //check if there is some work to do at all { - const bool updateRequiredLeft = !entryExisting(dbEntriesLeft. second, dbEntriesRight.first, dbEntryLeft); - const bool updateRequiredRight = !entryExisting(dbEntriesRight.second, dbEntriesLeft. first, dbEntryRight); + const bool updateRequiredLeft = streamLeft == streamListLeft .end() || !equalEntry(newStreamLeft, streamLeft ->second); + const bool updateRequiredRight = streamRight == streamListRight.end() || !equalEntry(newStreamRight, streamRight->second); //some users monitor the *.ffs_db file with RTS => don't touch the file if it isnt't strictly needed if (!updateRequiredLeft && !updateRequiredRight) return; } - dbEntriesLeft .second[dbEntriesRight.first] = dbEntryLeft; - dbEntriesRight.second[dbEntriesLeft .first] = dbEntryRight; + + //create/update DirInfo-streams + std::string sessionID = util::generateGUID(); + + //erase old session data + if (streamLeft != streamListLeft.end()) + streamListLeft.erase(streamLeft); + if (streamRight != streamListRight.end()) + streamListRight.erase(streamRight); + + //fill in new + streamListLeft .insert(std::make_pair(sessionID, newStreamLeft)); + streamListRight.insert(std::make_pair(sessionID, newStreamRight)); //write (temp-) files... - Loki::ScopeGuard guardTempFileLeft = Loki::MakeGuard(&zen::removeFile, fileNameLeftTmp); - saveFile(dbEntriesLeft, fileNameLeftTmp); //throw (FileError) + Loki::ScopeGuard guardTempFileLeft = Loki::MakeGuard(&zen::removeFile, dbNameLeftTmp); + saveFile(streamListLeft, dbNameLeftTmp); //throw (FileError) - Loki::ScopeGuard guardTempFileRight = Loki::MakeGuard(&zen::removeFile, fileNameRightTmp); - saveFile(dbEntriesRight, fileNameRightTmp); //throw (FileError) + Loki::ScopeGuard guardTempFileRight = Loki::MakeGuard(&zen::removeFile, dbNameRightTmp); + saveFile(streamListRight, dbNameRightTmp); //throw (FileError) //operation finished: rename temp files -> this should work transactionally: //if there were no write access, creation of temp files would have failed - removeFile(baseMapping.getDBFilename<LEFT_SIDE>()); - removeFile(baseMapping.getDBFilename<RIGHT_SIDE>()); - renameFile(fileNameLeftTmp, baseMapping.getDBFilename<LEFT_SIDE>()); //throw (FileError); - renameFile(fileNameRightTmp, baseMapping.getDBFilename<RIGHT_SIDE>()); //throw (FileError); + removeFile(dbNameLeft); + removeFile(dbNameRight); + renameFile(dbNameLeftTmp, dbNameLeft); //throw (FileError); + renameFile(dbNameRightTmp, dbNameRight); //throw (FileError); guardTempFileLeft. Dismiss(); //no need to delete temp file anymore guardTempFileRight.Dismiss(); // diff --git a/library/db_file.h b/library/db_file.h index 5c03ee96..a68e6bcf 100644 --- a/library/db_file.h +++ b/library/db_file.h @@ -12,6 +12,8 @@ namespace zen { +const Zstring SYNC_DB_FILE_ENDING = Zstr(".ffs_db"); + void saveToDisk(const BaseDirMapping& baseMapping); //throw (FileError) struct DirInformation diff --git a/library/dir_lock.cpp b/library/dir_lock.cpp index 835db4ef..1775026b 100644 --- a/library/dir_lock.cpp +++ b/library/dir_lock.cpp @@ -11,7 +11,6 @@ #include "../shared/last_error.h" #include "../shared/boost_thread_wrap.h" //include <boost/thread.hpp> #include "../shared/loki/ScopeGuard.h" -#include "../shared/system_constants.h" #include "../shared/guid.h" #include "../shared/file_io.h" #include "../shared/assert_static.h" @@ -47,20 +46,12 @@ const int LOCK_FORMAT_VER = 1; //lock file format version typedef Zbase<Zchar, StorageDeepCopy> BasicString; //thread safe string class } +//worker thread class LifeSigns { public: 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! - } - - ~LifeSigns() - { - threadObj.interrupt(); //thread lifetime is subset of this instances's life - threadObj.join(); - } + lockfilename_(lockfilename) {} //thread safety: make deep copy! void operator()() const //thread entry { @@ -129,11 +120,8 @@ public: } private: - LifeSigns(const LifeSigns&); //just be sure this ref-counting Zstring doesn't bite - LifeSigns& operator=(const LifeSigns&); // - - boost::thread threadObj; - const BasicString lockfilename_; //used by worker thread only! Not ref-counted! + //make sure this instance is safely copyable! + const BasicString lockfilename_; //thread local! Not ref-counted! }; @@ -147,8 +135,7 @@ void deleteLockFile(const Zstring& filename) //throw (FileError) if (::unlink(filename.c_str()) != 0) #endif { - wxString errorMessage = wxString(_("Error deleting file:")) + wxT("\n\"") + zToWx(filename) + wxT("\""); - throw FileError(errorMessage + wxT("\n\n") + getLastErrorFormatted()); + throw FileError(_("Error deleting file:") + "\n\"" + filename + "\"" + "\n\n" + getLastErrorFormatted()); } } @@ -162,8 +149,7 @@ zen::UInt64 getLockFileSize(const Zstring& filename) //throw (FileError, ErrorNo { const DWORD lastError = ::GetLastError(); - wxString errorMessage = wxString(_("Error reading file attributes:")) + wxT("\n\"") + zToWx(filename) + wxT("\""); - errorMessage += wxT("\n\n") + getLastErrorFormatted(lastError); + std::wstring errorMessage = _("Error reading file attributes:") + "\n\"" + filename + "\"" + "\n\n" + getLastErrorFormatted(lastError); if (lastError == ERROR_FILE_NOT_FOUND || lastError == ERROR_PATH_NOT_FOUND) @@ -182,8 +168,7 @@ zen::UInt64 getLockFileSize(const Zstring& filename) //throw (FileError, ErrorNo { const int lastError = errno; - wxString errorMessage = wxString(_("Error reading file attributes:")) + wxT("\n\"") + zToWx(filename) + wxT("\""); - errorMessage += wxT("\n\n") + getLastErrorFormatted(lastError); + std::wstring errorMessage = _("Error reading file attributes:") + "\n\"" + filename + "\"" + "\n\n" + getLastErrorFormatted(lastError); if (lastError == ENOENT) throw ErrorNotExisting(errorMessage); @@ -196,13 +181,13 @@ zen::UInt64 getLockFileSize(const Zstring& filename) //throw (FileError, ErrorNo } -Zstring deleteAbandonedLockName(const Zstring& lockfilename) +Zstring deleteAbandonedLockName(const Zstring& lockfilename) //make sure to NOT change file ending! { - const size_t pos = lockfilename.rfind(common::FILE_NAME_SEPARATOR); //search from end + const size_t pos = lockfilename.rfind(FILE_NAME_SEPARATOR); //search from end return pos == Zstring::npos ? Zstr("Del.") + lockfilename : Zstring(lockfilename.c_str(), pos + 1) + //include path separator Zstr("Del.") + - lockfilename.AfterLast(common::FILE_NAME_SEPARATOR); //returns the whole string if ch not found + lockfilename.AfterLast(FILE_NAME_SEPARATOR); //returns the whole string if ch not found } @@ -319,6 +304,7 @@ ProcessStatus getProcessStatus(const LockInformation::ProcessDescription& procDe 0); //__in DWORD th32ProcessID if (snapshot == INVALID_HANDLE_VALUE) return PROC_STATUS_NO_IDEA; + Loki::ScopeGuard dummy = Loki::MakeGuard(::CloseHandle, snapshot); (void)dummy; //silence warning "unused variable" @@ -371,10 +357,10 @@ std::string retrieveLockId(const Zstring& lockfilename) //throw (FileError, Erro void waitOnDirLock(const Zstring& lockfilename, DirLockCallback* callback) //throw (FileError) { - Zstring infoMsg; - infoMsg = wxToZ(_("Waiting while directory is locked (%x)...")); - infoMsg.Replace(Zstr("%x"), Zstr("\"") + lockfilename + Zstr("\"")); - if (callback) callback->reportInfo(infoMsg); + std::wstring infoMsg = _("Waiting while directory is locked (%x)..."); + replace(infoMsg, L"%x", std::wstring(L"\"") + lockfilename + "\""); + if (callback) + callback->reportInfo(infoMsg); //--------------------------------------------------------------- try { @@ -428,16 +414,15 @@ void waitOnDirLock(const Zstring& lockfilename, DirLockCallback* callback) //thr long remainingSeconds = ((DETECT_EXITUS_INTERVAL - (wxGetLocalTimeMillis() - lockSilentStart)) / 1000).ToLong(); remainingSeconds = std::max(0L, remainingSeconds); - Zstring remSecMsg = wxToZ(_P("1 sec", "%x sec", remainingSeconds)); - remSecMsg.Replace(Zstr("%x"), Zstring::fromNumber(remainingSeconds)); - callback->reportInfo(infoMsg + Zstr(" ") + remSecMsg); + std::wstring remSecMsg = _P("1 sec", "%x sec", remainingSeconds); + replace(remSecMsg, L"%x", toString<std::wstring>(remainingSeconds)); + callback->reportInfo(infoMsg + " " + remSecMsg); } else callback->reportInfo(infoMsg); //emit a message in any case (might clear other one) } } } - } catch (const ErrorNotExisting&) { @@ -471,10 +456,7 @@ bool tryLock(const Zstring& lockfilename) //throw (FileError) if (::GetLastError() == ERROR_FILE_EXISTS) return false; else - { - wxString errorMessage = wxString(_("Error setting directory lock:")) + wxT("\n\"") + zToWx(lockfilename) + wxT("\""); - throw FileError(errorMessage + wxT("\n\n") + getLastErrorFormatted()); - } + throw FileError(_("Error setting directory lock:") + "\n\"" + lockfilename + "\"" + "\n\n" + getLastErrorFormatted()); } ::CloseHandle(fileHandle); @@ -487,11 +469,7 @@ bool tryLock(const Zstring& lockfilename) //throw (FileError) if (errno == EEXIST) return false; else - { - wxString errorMessage = wxString(_("Error setting directory lock:")) + wxT("\n\"") + zToWx(lockfilename) + wxT("\""); - throw FileError(errorMessage + wxT("\n\n") + getLastErrorFormatted()); - } - + throw FileError(_("Error setting directory lock:") + "\n\"" + lockfilename + "\"" + "\n\n" + getLastErrorFormatted()); } ::close(fileHandle); #endif @@ -516,12 +494,13 @@ public: while (!::tryLock(lockfilename)) //throw (FileError) ::waitOnDirLock(lockfilename, callback); // - emitLifeSigns.reset(new LifeSigns(lockfilename.c_str())); //throw()! ownership of lockfile not yet managed! + threadObj = boost::thread(LifeSigns(lockfilename.c_str())); } ~SharedDirLock() { - emitLifeSigns.reset(); + threadObj.interrupt(); //thread lifetime is subset of this instances's life + threadObj.join(); ::releaseLock(lockfilename_); //throw () } @@ -532,7 +511,7 @@ private: const Zstring lockfilename_; - std::auto_ptr<LifeSigns> emitLifeSigns; + boost::thread threadObj; }; diff --git a/library/dir_lock.h b/library/dir_lock.h index 5d51d698..f4720e61 100644 --- a/library/dir_lock.h +++ b/library/dir_lock.h @@ -1,7 +1,6 @@ #ifndef DIR_LOCK_H_INCLUDED #define DIR_LOCK_H_INCLUDED -#include "../shared/zstring.h" #include "../shared/file_error.h" #include <memory> @@ -10,7 +9,7 @@ struct DirLockCallback //while waiting for the lock { virtual ~DirLockCallback() {} virtual void requestUiRefresh() = 0; //allowed to throw exceptions - virtual void reportInfo(const Zstring& text) = 0; + virtual void reportInfo(const std::wstring& text) = 0; }; /* diff --git a/library/error_log.cpp b/library/error_log.cpp index a78b1c9a..eef8572a 100644 --- a/library/error_log.cpp +++ b/library/error_log.cpp @@ -73,14 +73,19 @@ wxString ErrorLogging::formatMessage(const Entry& msg) break; } - const wxString prefix = wxString(wxT("[")) + wxDateTime(msg.time).FormatTime() + wxT("] ") + typeName + wxT(": "); + const wxString prefix = wxString(L"[") + wxDateTime(msg.time).FormatTime() + L"] " + typeName + L": "; wxString formattedText = prefix; - for (wxString::const_iterator i = msg.message.begin(); i != msg.message.end(); ++i) + for (auto i = msg.message.begin(); i != msg.message.end(); ++i) if (*i == wxChar('\n')) { - formattedText += wxString(wxChar('\n')).Pad(prefix.size(), wxChar(' '), true); - while (*++i == wxChar('\n')) //remove duplicate newlines + formattedText += L'\n'; + + wxString blanks; + blanks.resize(prefix.size(), L' '); + formattedText += blanks; + + while (*++i == L'\n') //remove duplicate newlines ; --i; } diff --git a/library/error_log.h b/library/error_log.h index 1acc0f8c..323a4297 100644 --- a/library/error_log.h +++ b/library/error_log.h @@ -10,7 +10,6 @@ #include <wx/string.h> #include <vector> #include <map> -#include "../shared/zstring.h" namespace zen @@ -37,7 +36,7 @@ private: { MessageType type; time_t time; - wxString message; + wxString message; }; static wxString formatMessage(const Entry& msg); diff --git a/library/hard_filter.cpp b/library/hard_filter.cpp index 194fe41c..e96d32fa 100644 --- a/library/hard_filter.cpp +++ b/library/hard_filter.cpp @@ -10,7 +10,6 @@ #include <set> #include <stdexcept> #include <vector> -#include "../shared/system_constants.h" #include "../structures.h" #include <boost/bind.hpp> #include "../shared/loki/LokiTypeInfo.h" @@ -58,7 +57,6 @@ HardFilter::FilterRef HardFilter::loadFilter(wxInputStream& stream) //-------------------------------------------------------------------------------------------------- -inline void addFilterEntry(const Zstring& filtername, std::set<Zstring>& fileFilter, std::set<Zstring>& directoryFilter) { Zstring filterFormatted = filtername; @@ -70,17 +68,17 @@ void addFilterEntry(const Zstring& filtername, std::set<Zstring>& fileFilter, st //Linux DOES distinguish between upper/lower-case: nothing to do here #endif - const Zstring sepAsterisk = Zstring(common::FILE_NAME_SEPARATOR) + Zchar('*'); - const Zstring sepQuestionMark = Zstring(common::FILE_NAME_SEPARATOR) + Zchar('?'); - const Zstring asteriskSep = Zstring(Zstr('*')) + common::FILE_NAME_SEPARATOR; - const Zstring questionMarkSep = Zstring(Zstr('?')) + common::FILE_NAME_SEPARATOR; + const Zstring sepAsterisk = Zstring(FILE_NAME_SEPARATOR) + Zchar('*'); + const Zstring sepQuestionMark = Zstring(FILE_NAME_SEPARATOR) + Zchar('?'); + const Zstring asteriskSep = Zstring(Zstr('*')) + FILE_NAME_SEPARATOR; + const Zstring questionMarkSep = Zstring(Zstr('?')) + FILE_NAME_SEPARATOR; //-------------------------------------------------------------------------------------------------- //add some syntactic sugar: handle beginning of filtername - if (filterFormatted.StartsWith(common::FILE_NAME_SEPARATOR)) + if (filterFormatted.StartsWith(FILE_NAME_SEPARATOR)) { //remove leading separators (keep BEFORE test for Zstring::empty()!) - filterFormatted = filterFormatted.AfterFirst(common::FILE_NAME_SEPARATOR); + filterFormatted = filterFormatted.AfterFirst(FILE_NAME_SEPARATOR); } else if (filterFormatted.StartsWith(asteriskSep) || // *\abc filterFormatted.StartsWith(questionMarkSep)) // ?\abc @@ -90,9 +88,9 @@ void addFilterEntry(const Zstring& filtername, std::set<Zstring>& fileFilter, st //-------------------------------------------------------------------------------------------------- //even more syntactic sugar: handle end of filtername - if (filterFormatted.EndsWith(common::FILE_NAME_SEPARATOR)) + if (filterFormatted.EndsWith(FILE_NAME_SEPARATOR)) { - const Zstring candidate = filterFormatted.BeforeLast(common::FILE_NAME_SEPARATOR); + const Zstring candidate = filterFormatted.BeforeLast(FILE_NAME_SEPARATOR); if (!candidate.empty()) directoryFilter.insert(candidate); //only relevant for directory filtering } @@ -102,7 +100,7 @@ void addFilterEntry(const Zstring& filtername, std::set<Zstring>& fileFilter, st fileFilter.insert( filterFormatted); directoryFilter.insert(filterFormatted); - const Zstring candidate = filterFormatted.BeforeLast(common::FILE_NAME_SEPARATOR); + const Zstring candidate = filterFormatted.BeforeLast(FILE_NAME_SEPARATOR); if (!candidate.empty()) directoryFilter.insert(candidate); //only relevant for directory filtering } @@ -235,19 +233,20 @@ std::vector<Zstring> compoundStringToFilter(const Zstring& filterString) //delimiters may be ';' or '\n' std::vector<Zstring> output; - const std::vector<Zstring> blocks = filterString.Split(Zchar(';')); - for (std::vector<Zstring>::const_iterator i = blocks.begin(); i != blocks.end(); ++i) + const std::vector<Zstring> blocks = split(filterString, Zchar(';')); + std::for_each(blocks.begin(), blocks.end(), + [&](const Zstring& item) { - const std::vector<Zstring> blocks2 = i->Split(Zchar('\n')); + const std::vector<Zstring> blocks2 = split(item, Zchar('\n')); - for (std::vector<Zstring>::const_iterator j = blocks2.begin(); j != blocks2.end(); ++j) + std::for_each(blocks2.begin(), blocks2.end(), + [&](Zstring entry) { - Zstring entry = *j; - entry.Trim(); + trim(entry); if (!entry.empty()) output.push_back(entry); - } - } + }); + }); return output; } @@ -261,12 +260,12 @@ NameFilter::NameFilter(const Zstring& includeFilter, const Zstring& excludeFilte //load filter into vectors of strings //delimiters may be ';' or '\n' - const std::vector<Zstring> includeList = compoundStringToFilter(includeFilter); - const std::vector<Zstring> excludeList = compoundStringToFilter(excludeFilter); + const std::vector<Zstring>& includeList = compoundStringToFilter(includeFilter); + const std::vector<Zstring>& excludeList = compoundStringToFilter(excludeFilter); //setup include/exclude filters for files and directories - std::for_each(includeList.begin(), includeList.end(), boost::bind(addFilterEntry, _1, boost::ref(filterFileIn), boost::ref(filterFolderIn))); - std::for_each(excludeList.begin(), excludeList.end(), boost::bind(addFilterEntry, _1, boost::ref(filterFileEx), boost::ref(filterFolderEx))); + std::for_each(includeList.begin(), includeList.end(), [&](const Zstring& entry) { addFilterEntry(entry, filterFileIn, filterFolderIn); }); + std::for_each(excludeList.begin(), excludeList.end(), [&](const Zstring& entry) { addFilterEntry(entry, filterFileEx, filterFolderEx); }); } @@ -306,7 +305,7 @@ bool NameFilter::passDirFilter(const Zstring& relDirname, bool* subObjMightMatch { if (subObjMightMatch) { - const Zstring& subNameBegin = nameFormatted + common::FILE_NAME_SEPARATOR; //const-ref optimization + const Zstring& subNameBegin = nameFormatted + FILE_NAME_SEPARATOR; //const-ref optimization *subObjMightMatch = matchesFilterBegin(subNameBegin, filterFileIn) || //might match a file in subdirectory matchesFilterBegin(subNameBegin, filterFolderIn); //or another subdirectory diff --git a/library/icon_buffer.cpp b/library/icon_buffer.cpp index 1b1706d2..91487498 100644 --- a/library/icon_buffer.cpp +++ b/library/icon_buffer.cpp @@ -26,8 +26,6 @@ using namespace zen; - - const size_t BUFFER_SIZE_MAX = 800; //maximum number of icons to buffer //--------------------------------------------------------------------------------------------------- @@ -80,7 +78,7 @@ public: IconHolder(HandleType handle = 0) : handle_(handle) {} //take ownership! //icon holder has value semantics! - IconHolder(const IconHolder& other) : handle_(other.handle_ == 0 ? 0 : + IconHolder(const IconHolder& other) : handle_(other.handle_ == NULL ? NULL : #ifdef FFS_WIN ::CopyIcon(other.handle_) #elif defined FFS_LINUX @@ -96,7 +94,7 @@ public: ~IconHolder() { - if (handle_ != 0) + if (handle_ != NULL) #ifdef FFS_WIN ::DestroyIcon(handle_); #elif defined FFS_LINUX @@ -111,7 +109,7 @@ public: wxIcon toWxIcon() const //copy HandleType, caller needs to take ownership! { - if (handle_ == 0) + if (handle_ == NULL) return wxNullIcon; IconHolder clone(*this); @@ -123,7 +121,7 @@ public: #elif defined FFS_LINUX // newIcon.SetPixbuf(clone.handle_); // transfer ownership!! #endif // - clone.handle_ = 0; // + clone.handle_ = NULL; // return newIcon; } @@ -213,7 +211,7 @@ IconHolder getAssociatedIconByExt(const BasicString& extension) #endif -const wxIcon& getDirectoryIcon() //one folder icon should be sufficient... +const wxIcon& getDirectoryIcon() { static wxIcon folderIcon; @@ -246,7 +244,7 @@ const wxIcon& getDirectoryIcon() //one folder icon should be sufficient... } -const wxIcon& getFileIcon() //in case one folder icon is sufficient... +const wxIcon& getFileIcon() { static wxIcon fileIcon; @@ -292,128 +290,87 @@ const wxIcon& getFileIcon() //in case one folder icon is sufficient... //################################################################################################################################################ -class Buffer +//---------------------- Shared Data ------------------------- +struct WorkLoad { -public: - //methods used by gui and worker thread - bool requestFileIcon(const Zstring& fileName, wxIcon* icon = NULL); + std::vector<BasicString> filesToLoad; //processes last elements of vector first! + boost::mutex mutex; + boost::condition_variable condition; //signal event: data for processing available +}; - //methods used by worker thread - void insertIntoBuffer(const BasicString& entryName, const IconHolder& icon); +typedef std::map<BasicString, IconHolder, LessFilename> NameIconMap; //entryName/icon -> ATTENTION: avoid ref-counting for this shared data structure! +typedef std::queue<BasicString> IconDbSequence; //entryName -private: - //--------------------------------------------------------------------------------------------------- - typedef std::map<BasicString, IconHolder, LessFilename> IconDB; //entryName/icon -> ATTENTION: avoid ref-counting for this shared data structure! - typedef std::queue<BasicString> IconDbSequence; //entryName - //--------------------------------------------------------------------------------------------------- - - //---------------------- Shared Data ------------------------- - boost::mutex lockIconDB; - IconDB iconBuffer; //use synchronisation when accessing this! +struct Buffer +{ + boost::mutex lockAccess; + NameIconMap iconMappping; //use synchronisation when accessing this! IconDbSequence iconSequence; //save sequence of buffer entry to delete oldest elements - //------------------------------------------------------------ }; +//------------------------------------------------------------ -bool Buffer::requestFileIcon(const Zstring& fileName, wxIcon* icon) +bool requestFileIcon(Buffer& buf, const Zstring& fileName, wxIcon* icon = NULL) { - boost::lock_guard<boost::mutex> dummy(lockIconDB); + boost::lock_guard<boost::mutex> dummy(buf.lockAccess); #ifdef FFS_WIN //"pricey" extensions are stored with fullnames and are read from disk, while cheap ones require just the extension - const BasicString extension = getFileExtension(fileName.c_str()); - const BasicString searchString = isPriceyExtension(extension) ? fileName.c_str() : extension.c_str(); - IconDB::const_iterator i = iconBuffer.find(searchString); + const BasicString extension = getFileExtension(BasicString(fileName)); + const BasicString searchString = isPriceyExtension(extension) ? BasicString(fileName) : extension; + auto iter = buf.iconMappping.find(searchString); #elif defined FFS_LINUX - IconDB::const_iterator i = iconBuffer.find(fileName.c_str()); + auto iter = buf.iconMappping.find(BasicString(fileName)); #endif - if (i == iconBuffer.end()) + if (iter == buf.iconMappping.end()) return false; if (icon != NULL) - *icon = i->second.toWxIcon(); - + *icon = iter->second.toWxIcon(); return true; } -void Buffer::insertIntoBuffer(const BasicString& entryName, const IconHolder& icon) //called by worker thread +void insertIntoBuffer(Buffer& buf, const BasicString& entryName, const IconHolder& icon) //called by worker thread { - boost::lock_guard<boost::mutex> dummy(lockIconDB); + boost::lock_guard<boost::mutex> dummy(buf.lockAccess); //thread saftey: icon uses ref-counting! But is NOT shared with main thread! - const std::pair<IconDB::iterator, bool> rc = iconBuffer.insert(std::make_pair(entryName, icon)); + auto rc = buf.iconMappping.insert(std::make_pair(entryName, icon)); if (rc.second) //if insertion took place - iconSequence.push(entryName); //note: sharing Zstring with IconDB!!! + buf.iconSequence.push(entryName); //note: sharing Zstring with IconDB!!! - assert(iconBuffer.size() == iconSequence.size()); + assert(buf.iconMappping.size() == buf.iconSequence.size()); //remove elements if buffer becomes too big: - if (iconBuffer.size() > BUFFER_SIZE_MAX) //limit buffer size: critical because GDI resources are limited (e.g. 10000 on XP per process) + if (buf.iconMappping.size() > BUFFER_SIZE_MAX) //limit buffer size: critical because GDI resources are limited (e.g. 10000 on XP per process) { //remove oldest element - iconBuffer.erase(iconSequence.front()); - iconSequence.pop(); + buf.iconMappping.erase(buf.iconSequence.front()); + buf.iconSequence.pop(); } } - - //################################################################################################################################################ -class WorkerThread + +class WorkerThread //lifetime is part of icon buffer { public: - WorkerThread(Buffer& iconBuff) : iconBuffer(iconBuff) - { - threadObj = boost::thread(boost::ref(*this)); //localize all thread logic to this class! - } - - ~WorkerThread() - { - setWorkload(std::vector<Zstring>()); //make sure interruption point is always reached! - threadObj.interrupt(); - threadObj.join(); - } - - void setWorkload(const std::vector<Zstring>& load); //(re-)set new workload of icons to be retrieved + WorkerThread(const std::shared_ptr<WorkLoad>& workload, + const std::shared_ptr<Buffer>& buffer) : + workload_(workload), + buffer_(buffer) {} void operator()(); //thread entry private: void doWork(); - //---------------------- Shared Data ------------------------- - typedef BasicString FileName; - - struct SharedData - { - std::vector<FileName> workload; //processes last elements of vector first! - boost::mutex mutex; - boost::condition_variable condition; //signal event: data for processing available - } shared; - //------------------------------------------------------------ - - Buffer& iconBuffer; - - boost::thread threadObj; + std::shared_ptr<WorkLoad> workload_; + std::shared_ptr<Buffer> buffer_; }; -void WorkerThread::setWorkload(const std::vector<Zstring>& load) //(re-)set new workload of icons to be retrieved -{ - { - boost::lock_guard<boost::mutex> dummy(shared.mutex); - - 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->size())); //make DEEP COPY from Zstring - } - - shared.condition.notify_one(); - //condition handling, see: http://www.boost.org/doc/libs/1_43_0/doc/html/thread/synchronization.html#thread.synchronization.condvar_ref -} - - void WorkerThread::operator()() //thread entry { //failure to initialize COM for each thread is a source of hard to reproduce bugs: https://sourceforge.net/tracker/?func=detail&aid=3160472&group_id=234430&atid=1093080 @@ -430,9 +387,9 @@ void WorkerThread::operator()() //thread entry while (true) { { - boost::unique_lock<boost::mutex> dummy(shared.mutex); - while(shared.workload.empty()) - shared.condition.wait(dummy); //interruption point! + boost::unique_lock<boost::mutex> dummy(workload_->mutex); + while(workload_->filesToLoad.empty()) + workload_->condition.wait(dummy); //interruption point! //shared.condition.timed_wait(dummy, boost::get_system_time() + boost::posix_time::milliseconds(100)); } @@ -461,14 +418,14 @@ void WorkerThread::doWork() { BasicString fileName; { - boost::lock_guard<boost::mutex> dummy(shared.mutex); - if (shared.workload.empty()) + boost::lock_guard<boost::mutex> dummy(workload_->mutex); + if (workload_->filesToLoad.empty()) break; //enter waiting state - fileName = shared.workload.back(); //deep copy - shared.workload.pop_back(); + fileName = workload_->filesToLoad.back(); //deep copy + workload_->filesToLoad.pop_back(); } - if (iconBuffer.requestFileIcon(fileName.c_str())) //thread safety: Zstring okay, won't be reference-counted in requestIcon() + if (requestFileIcon(*buffer_, Zstring(fileName))) //thread safety: Zstring okay, won't be reference-counted in requestIcon() continue; //icon already in buffer: skip #ifdef FFS_WIN @@ -476,16 +433,16 @@ void WorkerThread::doWork() if (isPriceyExtension(extension)) //"pricey" extensions are stored with fullnames and are read from disk, while cheap ones require just the extension { const IconHolder newIcon = getAssociatedIcon(fileName); - iconBuffer.insertIntoBuffer(fileName, newIcon); + insertIntoBuffer(*buffer_, fileName, newIcon); } else //no read-access to disk! determine icon by extension { const IconHolder newIcon = getAssociatedIconByExt(extension); - iconBuffer.insertIntoBuffer(extension, newIcon); + insertIntoBuffer(*buffer_, extension, newIcon); } #elif defined FFS_LINUX const IconHolder newIcon = getAssociatedIcon(fileName); - iconBuffer.insertIntoBuffer(fileName, newIcon); + insertIntoBuffer(*buffer_, fileName, newIcon); #endif } } @@ -495,16 +452,30 @@ void WorkerThread::doWork() struct IconBuffer::Pimpl { - Pimpl() : buffer(), worker(buffer) {} //might throw exceptions! + Pimpl() : + workload(std::make_shared<WorkLoad>()), + buffer(std::make_shared<Buffer>()) {} + + + std::shared_ptr<WorkLoad> workload; + std::shared_ptr<Buffer> buffer; - Buffer buffer; - WorkerThread worker; + boost::thread worker; }; -IconBuffer::IconBuffer() : pimpl(new Pimpl) {} +IconBuffer::IconBuffer() : pimpl(new Pimpl) +{ + pimpl->worker = boost::thread(WorkerThread(pimpl->workload, pimpl->buffer)); +} + -IconBuffer::~IconBuffer() {} //auto_ptr<>: keep destructor non-inline +IconBuffer::~IconBuffer() +{ + setWorkload(std::vector<Zstring>()); //make sure interruption point is always reached! + pimpl->worker.interrupt(); + pimpl->worker.join(); +} const wxIcon& IconBuffer::getDirectoryIcon() { return ::getDirectoryIcon(); } @@ -516,6 +487,19 @@ IconBuffer& IconBuffer::getInstance() return instance; } -bool IconBuffer::requestFileIcon(const Zstring& fileName, wxIcon* icon) { return pimpl->buffer.requestFileIcon(fileName, icon); } +bool IconBuffer::requestFileIcon(const Zstring& fileName, wxIcon* icon) { return ::requestFileIcon(*pimpl->buffer, fileName, icon); } + +void IconBuffer::setWorkload(const std::vector<Zstring>& load) +{ + { + boost::lock_guard<boost::mutex> dummy(pimpl->workload->mutex); -void IconBuffer::setWorkload(const std::vector<Zstring>& load) { return pimpl->worker.setWorkload(load); } + pimpl->workload->filesToLoad.clear(); + + std::transform(load.begin(), load.end(), std::back_inserter(pimpl->workload->filesToLoad), + [](const Zstring& file) { return BasicString(file); }); //make DEEP COPY from Zstring + } + + pimpl->workload->condition.notify_one(); + //condition handling, see: http://www.boost.org/doc/libs/1_43_0/doc/html/thread/synchronization.html#thread.synchronization.condvar_ref +} diff --git a/library/icon_buffer.h b/library/icon_buffer.h index 5ab2740c..87f0cd4a 100644 --- a/library/icon_buffer.h +++ b/library/icon_buffer.h @@ -17,7 +17,7 @@ namespace zen class IconBuffer { public: - static const wxIcon& getDirectoryIcon(); //one icon should be sufficient... + static const wxIcon& getDirectoryIcon(); //generic icons static const wxIcon& getFileIcon(); // static IconBuffer& getInstance(); @@ -35,7 +35,7 @@ private: ~IconBuffer(); struct Pimpl; - std::auto_ptr<Pimpl> pimpl; + std::unique_ptr<Pimpl> pimpl; }; } diff --git a/library/lock_holder.h b/library/lock_holder.h index 38323695..dc88ce71 100644 --- a/library/lock_holder.h +++ b/library/lock_holder.h @@ -8,7 +8,7 @@ namespace zen { -const Zstring LOCK_FILE_ENDING = Zstr("ffs_lock"); //intermediate locks created by DirLock use this extension, too! +const Zstring LOCK_FILE_ENDING = Zstr(".ffs_lock"); //intermediate locks created by DirLock use this extension, too! //convenience class for creating and holding locks for a number of directories class LockHolder @@ -18,21 +18,21 @@ public: { if (dirnameFmt.empty()) return; if (lockHolder.find(dirnameFmt) != lockHolder.end()) return; - assert(dirnameFmt.EndsWith(common::FILE_NAME_SEPARATOR)); //this is really the contract, formatting does other things as well, e.g. macro substitution + assert(dirnameFmt.EndsWith(FILE_NAME_SEPARATOR)); //this is really the contract, formatting does other things as well, e.g. macro substitution class WaitOnLockHandler : public DirLockCallback { public: WaitOnLockHandler(ProcessCallback& pc) : pc_(pc) {} virtual void requestUiRefresh() { pc_.requestUiRefresh(); } //allowed to throw exceptions - virtual void reportInfo(const Zstring& text) { pc_.reportInfo(text); } + virtual void reportInfo(const std::wstring& text) { pc_.reportInfo(text); } private: ProcessCallback& pc_; } callback(procCallback); try { - lockHolder.insert(std::make_pair(dirnameFmt, DirLock(dirnameFmt + Zstr("sync.") + LOCK_FILE_ENDING, &callback))); + lockHolder.insert(std::make_pair(dirnameFmt, DirLock(dirnameFmt + Zstr("sync") + LOCK_FILE_ENDING, &callback))); } catch (const FileError& e) { diff --git a/library/process_xml.cpp b/library/process_xml.cpp index 1b5f6360..69dad7f3 100644 --- a/library/process_xml.cpp +++ b/library/process_xml.cpp @@ -88,6 +88,7 @@ void xmlAccess::OptionalDialogs::resetDialogs() warningNotEnoughDiskSpace = true; warningUnresolvedConflicts = true; warningSyncDatabase = true; + warningRecyclerMissing = true; popupOnConfigChange = true; showSummaryBeforeSync = true; } @@ -747,6 +748,7 @@ void readConfig(const XmlIn& in, XmlGlobalSettings& config) inOpt["CheckForFreeDiskSpace"](config.optDialogs.warningNotEnoughDiskSpace); inOpt["CheckForUnresolvedConflicts"](config.optDialogs.warningUnresolvedConflicts); inOpt["NotifyDatabaseError"](config.optDialogs.warningSyncDatabase); + inOpt["CheckMissingRecycleBin"](config.optDialogs.warningRecyclerMissing); inOpt["PopupOnConfigChange"](config.optDialogs.popupOnConfigChange); inOpt["SummaryBeforeSync" ](config.optDialogs.showSummaryBeforeSync); @@ -761,6 +763,8 @@ void readConfig(const XmlIn& in, XmlGlobalSettings& config) inWnd["PosY" ](config.gui.dlgPos.y); inWnd["Maximized"](config.gui.isMaximized); + inWnd["MaxFolderPairsVisible"](config.gui.maxFolderPairsVisible); + inWnd["ManualDeletionOnBothSides"](config.gui.deleteOnBothSides); inWnd["ManualDeletionUseRecycler"](config.gui.useRecyclerForManualDeletion); inWnd["RespectCaseOnSearch" ](config.gui.textSearchRespectCase); @@ -796,10 +800,8 @@ void readConfig(const XmlIn& in, XmlGlobalSettings& config) inGui["ExternalApplications"](config.gui.externelApplications); //load config file history - XmlIn inHist = inGui["ConfigHistory"]; - - inHist.attribute("LastUsed", config.gui.lastUsedConfigFile); - inHist(config.gui.cfgFileHistory); + inGui["LastConfigActive"](config.gui.lastUsedConfigFiles); + inGui["ConfigHistory"](config.gui.cfgFileHistory); //last update check inGui["LastUpdateCheck"](config.gui.lastUpdateCheck); @@ -812,7 +814,7 @@ void readConfig(const XmlIn& in, XmlGlobalSettings& config) template <class ConfigType> void readConfig(const wxString& filename, XmlType type, ConfigType& config) { - if (!fileExists(wxToZ(filename))) + if (!fileExists(toZ(filename))) throw FfsXmlError(wxString(_("File does not exist:")) + wxT("\n\"") + filename + wxT("\"")); XmlDoc doc; @@ -1003,6 +1005,7 @@ void writeConfig(const XmlGlobalSettings& config, XmlOut& out) outOpt["CheckForFreeDiskSpace"](config.optDialogs.warningNotEnoughDiskSpace); outOpt["CheckForUnresolvedConflicts"](config.optDialogs.warningUnresolvedConflicts); outOpt["NotifyDatabaseError"](config.optDialogs.warningSyncDatabase); + outOpt["CheckMissingRecycleBin"](config.optDialogs.warningRecyclerMissing); outOpt["PopupOnConfigChange"](config.optDialogs.popupOnConfigChange); outOpt["SummaryBeforeSync" ](config.optDialogs.showSummaryBeforeSync); @@ -1017,6 +1020,8 @@ void writeConfig(const XmlGlobalSettings& config, XmlOut& out) outWnd["PosY" ](config.gui.dlgPos.y); outWnd["Maximized"](config.gui.isMaximized); + outWnd["MaxFolderPairsVisible"](config.gui.maxFolderPairsVisible); + outWnd["ManualDeletionOnBothSides"](config.gui.deleteOnBothSides); outWnd["ManualDeletionUseRecycler"](config.gui.useRecyclerForManualDeletion); outWnd["RespectCaseOnSearch" ](config.gui.textSearchRespectCase); @@ -1048,10 +1053,8 @@ void writeConfig(const XmlGlobalSettings& config, XmlOut& out) outGui["ExternalApplications"](config.gui.externelApplications); //load config file history - XmlOut outHist = outGui["ConfigHistory"]; - - outHist.attribute("LastUsed", config.gui.lastUsedConfigFile); - outHist(config.gui.cfgFileHistory); + outGui["LastConfigActive"](config.gui.lastUsedConfigFiles); + outGui["ConfigHistory"](config.gui.cfgFileHistory); //last update check outGui["LastUpdateCheck"](config.gui.lastUpdateCheck); diff --git a/library/process_xml.h b/library/process_xml.h index 2e57c433..d7437825 100644 --- a/library/process_xml.h +++ b/library/process_xml.h @@ -125,6 +125,7 @@ struct OptionalDialogs bool warningNotEnoughDiskSpace; bool warningUnresolvedConflicts; bool warningSyncDatabase; + bool warningRecyclerMissing; bool popupOnConfigChange; bool showSummaryBeforeSync; }; @@ -159,6 +160,7 @@ struct XmlGlobalSettings dlgPos(wxDefaultCoord, wxDefaultCoord), dlgSize(wxDefaultCoord, wxDefaultCoord), isMaximized(false), + maxFolderPairsVisible(6), autoAdjustColumnsLeft(false), autoAdjustColumnsRight(false), folderHistMax(12), @@ -191,6 +193,8 @@ struct XmlGlobalSettings wxSize dlgSize; bool isMaximized; + int maxFolderPairsVisible; + ColumnAttributes columnAttribLeft; ColumnAttributes columnAttribRight; @@ -200,7 +204,7 @@ struct XmlGlobalSettings ExternalApps externelApplications; std::vector<wxString> cfgFileHistory; - wxString lastUsedConfigFile; + std::vector<wxString> lastUsedConfigFiles; std::vector<wxString> folderHistoryLeft; std::vector<wxString> folderHistoryRight; diff --git a/library/resources.cpp b/library/resources.cpp index abdfdf51..179033a7 100644 --- a/library/resources.cpp +++ b/library/resources.cpp @@ -11,7 +11,6 @@ #include <wx/icon.h> #include <wx/mstream.h> #include "../shared/string_conv.h" -#include "../shared/system_constants.h" #include <memory> #include "../shared/standard_paths.h" @@ -102,9 +101,6 @@ void GlobalResources::load() //for compatibility it seems we need to stick with a "real" icon *programIcon = wxIcon(wxT("A_PROGRAM_ICON")); #else - //#include "FreeFileSync.xpm" - //*programIcon = wxIcon(FreeFileSync_xpm); - //use big logo bitmap for better quality programIcon->CopyFromBitmap(getImage(wxT("FreeFileSync.png"))); #endif diff --git a/library/soft_filter.h b/library/soft_filter.h index 82ba0ba2..0a406907 100644 --- a/library/soft_filter.h +++ b/library/soft_filter.h @@ -28,22 +28,25 @@ public: size_t sizeMin, UnitSize unitSizeMin, size_t sizeMax, UnitSize unitSizeMax); - bool matchTime(zen::Int64 writeTime) const { return currentTime - writeTime <= timeSpan_; } - bool matchSize(zen::UInt64 fileSize) const { return sizeMin_ <= fileSize && fileSize <= sizeMax_; } + bool matchTime(Int64 writeTime) const { return currentTime - writeTime <= timeSpan_; } + bool matchSize(UInt64 fileSize) const { return sizeMin_ <= fileSize && fileSize <= sizeMax_; } + bool matchFolder() const { return timeSpan_ == std::numeric_limits<Int64>::max(); } + //if date filter is active we deactivate all folders: effectively gets rid of empty folders! + bool isNull() const; //filter is equivalent to NullFilter, but may be technically slower //small helper method: merge two soft filters friend SoftFilter combineFilters(const SoftFilter& first, const SoftFilter& second); private: - SoftFilter(zen::Int64 timeSpan, - zen::UInt64 sizeMin, - zen::UInt64 sizeMax); - - zen::Int64 timeSpan_; //unit: seconds - zen::UInt64 sizeMin_; //unit: bytes - zen::UInt64 sizeMax_; //unit: bytes - zen::Int64 currentTime; + SoftFilter(Int64 timeSpan, + UInt64 sizeMin, + UInt64 sizeMax); + + Int64 timeSpan_; //unit: seconds + UInt64 sizeMin_; //unit: bytes + UInt64 sizeMax_; //unit: bytes + Int64 currentTime; }; } @@ -84,18 +87,18 @@ SoftFilter::SoftFilter(size_t timeSpan, UnitTime unitTimeSpan, size_t sizeMax, UnitSize unitSizeMax) : currentTime(wxGetUTCTime()) { - zen::resolveUnits(timeSpan, unitTimeSpan, - sizeMin, unitSizeMin, - sizeMax, unitSizeMax, - timeSpan_, //unit: seconds - sizeMin_, //unit: bytes - sizeMax_); //unit: bytes + resolveUnits(timeSpan, unitTimeSpan, + sizeMin, unitSizeMin, + sizeMax, unitSizeMax, + timeSpan_, //unit: seconds + sizeMin_, //unit: bytes + sizeMax_); //unit: bytes } inline -SoftFilter::SoftFilter(zen::Int64 timeSpan, - zen::UInt64 sizeMin, - zen::UInt64 sizeMax) : +SoftFilter::SoftFilter(Int64 timeSpan, + UInt64 sizeMin, + UInt64 sizeMax) : timeSpan_(timeSpan), sizeMin_ (sizeMin), sizeMax_ (sizeMax), @@ -112,9 +115,9 @@ SoftFilter combineFilters(const SoftFilter& first, const SoftFilter& second) inline bool SoftFilter::isNull() const //filter is equivalent to NullFilter, but may be technically slower { - return timeSpan_ == std::numeric_limits<zen::Int64>::max() && + return timeSpan_ == std::numeric_limits<Int64>::max() && sizeMin_ == 0U && - sizeMax_ == std::numeric_limits<zen::UInt64>::max(); + sizeMax_ == std::numeric_limits<UInt64>::max(); } } diff --git a/library/statistics.cpp b/library/statistics.cpp index c2ba8c0c..812c869b 100644 --- a/library/statistics.cpp +++ b/library/statistics.cpp @@ -136,7 +136,6 @@ wxString Statistics::formatRemainingTime(double timeInMs) const } output.Replace(wxT("%x"), zen::toStringSep(formattedTime)); return output; - //+ wxT("(") + common::numberToWxString(common::round(timeInMs / 1000)) + wxT(")"); } @@ -227,7 +226,8 @@ wxString Statistics::getBytesPerSecond() const const double dataDelta = backRecord.second.data - frontRecord.second.data; if (!isNull(timeDelta)) - return zen::formatFilesizeToShortString(zen::UInt64(dataDelta * 1000 / timeDelta)) + _("/sec"); + if (dataDelta > 0) //may be negative if user cancels copying + return zen::formatFilesizeToShortString(zen::UInt64(dataDelta * 1000 / timeDelta)) + _("/sec"); } return wxT("-"); //fallback diff --git a/library/status_handler.h b/library/status_handler.h index c3a016d5..a7984790 100644 --- a/library/status_handler.h +++ b/library/status_handler.h @@ -8,7 +8,7 @@ #define STATUSHANDLER_H_INCLUDED #include <wx/string.h> -#include "../shared/zstring.h" +#include <string> #include "../shared/int64.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 @@ -18,21 +18,6 @@ void updateUiNow(); //do the updating //interfaces for status updates (can be implemented by GUI or Batch mode) -//overwrite virtual methods for respective functionality - -class ErrorHandler -{ -public: - ErrorHandler() {} - virtual ~ErrorHandler() {} - - enum Response - { - IGNORE_ERROR = 10, - RETRY - }; - virtual Response reportError(const Zstring& errorMessage) = 0; -}; //report status during comparison and synchronization @@ -51,21 +36,29 @@ struct ProcessCallback //these methods have to be implemented in the derived classes to handle error and status information virtual void initNewProcess(int objectsTotal, zen::Int64 dataTotal, Process processID) = 0; //informs about the total amount of data that will be processed from now on - virtual void updateProcessedData(int objectsProcessed, zen::Int64 dataProcessed) = 0; //called periodically after data was processed - virtual void reportInfo(const Zstring& text) = 0; + //called periodically after data was processed: expected(!) to update GUI! + virtual void reportInfo(const wxString& 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; + //note: this one must NOT throw in order to properly allow undoing setting of statistics! + //it is in general paired with a call to requestUiRefresh() to compensate! + virtual void updateProcessedData(int objectsProcessed, zen::Int64 dataProcessed) = 0; //throw() - virtual void requestUiRefresh(bool allowExceptions = true) = 0; //opportunity to abort must be implemented in a frequently executed method like requestUiRefresh() + //opportunity to abort must be implemented in a frequently executed method like requestUiRefresh() + virtual void requestUiRefresh() = 0; //throw ? - virtual bool abortIsRequested() = 0; //thanks to Windows C-Api not supporting exceptions we need this one... + //this method is triggered repeatedly by requestUiRefresh() and can be used to refresh the ui by dispatching pending events + virtual void forceUiRefresh() = 0; //error handling: - virtual ErrorHandler::Response reportError(const wxString& errorMessage) = 0; //recoverable error situation - virtual void reportFatalError(const wxString& errorMessage) = 0; //non-recoverable error situation, implement abort! - virtual void reportWarning (const wxString& warningMessage, bool& warningActive) = 0; + enum Response + { + IGNORE_ERROR = 10, + RETRY + }; + virtual Response reportError (const wxString& errorMessage) = 0; //recoverable error situation + virtual void reportFatalError(const wxString& errorMessage) = 0; //non-recoverable error situation, implement abort! + virtual void reportWarning (const wxString& warningMessage, bool& warningActive) = 0; }; @@ -83,18 +76,18 @@ class StatusHandler : public ProcessCallback, public AbortCallback public: StatusHandler() : abortRequested(false) {} - virtual void requestUiRefresh(bool allowExceptions) + virtual void requestUiRefresh() { if (updateUiIsAllowed()) //test if specific time span between ui updates is over forceUiRefresh(); - if (abortRequested && allowExceptions) + if (abortRequested) abortThisProcess(); //abort can be triggered by requestAbortion() } - virtual void requestAbortion() { abortRequested = true; } //this does NOT call abortThisProcess immediately, but when appropriate (e.g. async. processes finished) - virtual bool abortIsRequested() { return abortRequested; } virtual void abortThisProcess() = 0; + virtual void requestAbortion() { abortRequested = true; } //this does NOT call abortThisProcess immediately, but when appropriate (e.g. async. processes finished) + bool abortIsRequested() { return abortRequested; } private: bool abortRequested; |