diff options
author | Daniel Wilhelm <daniel@wili.li> | 2014-04-18 17:11:56 +0200 |
---|---|---|
committer | Daniel Wilhelm <daniel@wili.li> | 2014-04-18 17:11:56 +0200 |
commit | 98ecf620f7de377dc8ae9ad7fbd1e3b24477e138 (patch) | |
tree | faadc6d8822c20cd3bc6f50b2a98e6c580585949 /library | |
parent | 3.16 (diff) | |
download | FreeFileSync-98ecf620f7de377dc8ae9ad7fbd1e3b24477e138.tar.gz FreeFileSync-98ecf620f7de377dc8ae9ad7fbd1e3b24477e138.tar.bz2 FreeFileSync-98ecf620f7de377dc8ae9ad7fbd1e3b24477e138.zip |
3.17
Diffstat (limited to 'library')
-rw-r--r-- | library/binary.cpp | 6 | ||||
-rw-r--r-- | library/binary.h | 6 | ||||
-rw-r--r-- | library/cmp_filetime.h | 16 | ||||
-rw-r--r-- | library/custom_grid.cpp | 35 | ||||
-rw-r--r-- | library/custom_grid.h | 26 | ||||
-rw-r--r-- | library/db_file.cpp | 259 | ||||
-rw-r--r-- | library/db_file.h | 11 | ||||
-rw-r--r-- | library/detect_renaming.h | 4 | ||||
-rw-r--r-- | library/dir_lock.cpp | 69 | ||||
-rw-r--r-- | library/dir_lock.h | 3 | ||||
-rw-r--r-- | library/error_log.cpp | 4 | ||||
-rw-r--r-- | library/error_log.h | 2 | ||||
-rw-r--r-- | library/hard_filter.cpp (renamed from library/filter.cpp) | 96 | ||||
-rw-r--r-- | library/hard_filter.h (renamed from library/filter.h) | 81 | ||||
-rw-r--r-- | library/icon_buffer.cpp | 364 | ||||
-rw-r--r-- | library/icon_buffer.h | 41 | ||||
-rw-r--r-- | library/lock_holder.h | 52 | ||||
-rw-r--r-- | library/norm_filter.h | 98 | ||||
-rw-r--r-- | library/process_xml.cpp | 820 | ||||
-rw-r--r-- | library/process_xml.h | 44 | ||||
-rw-r--r-- | library/resources.cpp | 4 | ||||
-rw-r--r-- | library/soft_filter.cpp | 10 | ||||
-rw-r--r-- | library/soft_filter.h | 104 | ||||
-rw-r--r-- | library/statistics.cpp | 67 | ||||
-rw-r--r-- | library/status_handler.cpp | 4 | ||||
-rw-r--r-- | library/status_handler.h | 69 |
26 files changed, 1386 insertions, 909 deletions
diff --git a/library/binary.cpp b/library/binary.cpp index 72fc220a..3a202711 100644 --- a/library/binary.cpp +++ b/library/binary.cpp @@ -8,7 +8,7 @@ #include "../shared/file_io.h" #include <vector> #include <wx/stopwatch.h> - +#include "../shared/int64.h" inline void setMinSize(std::vector<char>& buffer, size_t minSize) @@ -66,7 +66,7 @@ private: } -bool ffs3::filesHaveSameContent(const Zstring& filename1, const Zstring& filename2, CompareCallback& callback) +bool zen::filesHaveSameContent(const Zstring& filename1, const Zstring& filename2, CompareCallback& callback) { FileInput file1(filename1); //throw (FileError) FileInput file2(filename2); //throw (FileError) @@ -76,7 +76,7 @@ bool ffs3::filesHaveSameContent(const Zstring& filename1, const Zstring& filenam static std::vector<char> memory1; static std::vector<char> memory2; - wxLongLong bytesCompared; + zen::UInt64 bytesCompared; wxLongLong lastDelayViolation = wxGetLocalTimeMillis(); diff --git a/library/binary.h b/library/binary.h index 847c7169..b796ddbb 100644 --- a/library/binary.h +++ b/library/binary.h @@ -8,10 +8,10 @@ #define BINARY_H_INCLUDED #include "../shared/zstring.h" -#include <wx/longlong.h> #include "../shared/file_error.h" +#include "../shared/int64.h" -namespace ffs3 +namespace zen { //callback functionality for status updates while comparing @@ -19,7 +19,7 @@ class CompareCallback { public: virtual ~CompareCallback() {} - virtual void updateCompareStatus(const wxLongLong& totalBytesTransferred) = 0; + virtual void updateCompareStatus(zen::UInt64 totalBytesTransferred) = 0; }; bool filesHaveSameContent(const Zstring& filename1, const Zstring& filename2, CompareCallback& callback); //throw FileError diff --git a/library/cmp_filetime.h b/library/cmp_filetime.h index b95eae5f..24b331c6 100644 --- a/library/cmp_filetime.h +++ b/library/cmp_filetime.h @@ -1,28 +1,26 @@ #ifndef CMP_FILETIME_H_INCLUDED #define CMP_FILETIME_H_INCLUDED -#include <wx/longlong.h> #include <wx/stopwatch.h> +#include "../shared/int64.h" - -namespace ffs3 +namespace zen { //--------------------------------------------------------------------------------------------------------------- inline -bool sameFileTime(const wxLongLong& a, const wxLongLong& b, size_t tolerance) +bool sameFileTime(const Int64& a, const Int64& b, size_t tolerance) { if (a < b) - return b <= a + tolerance; + return b <= a + static_cast<int>(tolerance); else - return a <= b + tolerance; + return a <= b + static_cast<int>(tolerance); } //--------------------------------------------------------------------------------------------------------------- class CmpFileTime { public: - CmpFileTime(size_t tolerance) : - tolerance_(tolerance) {} + CmpFileTime(size_t tolerance) : tolerance_(tolerance) {} enum Result { @@ -33,7 +31,7 @@ public: TIME_RIGHT_INVALID }; - Result getResult(const wxLongLong& lhs, const wxLongLong& rhs) const + Result getResult(const Int64& lhs, const Int64& rhs) const { if (lhs == rhs) return TIME_EQUAL; diff --git a/library/custom_grid.cpp b/library/custom_grid.cpp index d8ced2a3..172ad2e1 100644 --- a/library/custom_grid.cpp +++ b/library/custom_grid.cpp @@ -21,6 +21,7 @@ #include <wx/icon.h> #include <wx/tooltip.h> #include <wx/settings.h> +#include "../shared/i18n.h" #ifdef FFS_WIN #include <wx/timer.h> @@ -31,7 +32,7 @@ #include <gtk/gtk.h> #endif -using namespace ffs3; +using namespace zen; const size_t MIN_ROW_COUNT = 15; @@ -294,10 +295,10 @@ protected: value = zToWx(fileObj.getBaseDirPf<side>()); break; case xmlAccess::SIZE: //file size - value = ffs3::numberToStringSep(fileObj.getFileSize<side>()); + value = zen::toStringSep(fileObj.getFileSize<side>()); break; case xmlAccess::DATE: //date - value = ffs3::utcTimeToLocalString(fileObj.getLastWriteTime<side>()); + value = zen::utcTimeToLocalString(fileObj.getLastWriteTime<side>()); break; case xmlAccess::EXTENSION: //file extension value = zToWx(fileObj.getExtension<side>()); @@ -325,7 +326,7 @@ protected: value = _("<Symlink>"); break; case xmlAccess::DATE: //date - value = ffs3::utcTimeToLocalString(linkObj.getLastWriteTime<side>()); + value = zen::utcTimeToLocalString(linkObj.getLastWriteTime<side>()); break; case xmlAccess::EXTENSION: //file extension value = wxEmptyString; @@ -675,7 +676,7 @@ void CustomGrid::initSettings(CustomGridLeft* gridLeft, } -void CustomGrid::release() //release connection to ffs3::GridView +void CustomGrid::release() //release connection to zen::GridView { assert(getGridDataTable()); getGridDataTable()->setGridDataTable(NULL); //kind of "disable" griddatatable; don't delete it with SetTable(NULL)! May be used by wxGridCellStringRenderer @@ -1352,14 +1353,14 @@ void CustomGridRim::setTooltip(const wxMouseEvent& event) virtual void visit(const FileMapping& fileObj) { tipMsg_ = zToWx(fileObj.getRelativeName<side>()) + wxT("\n") + - _("Size") + wxT(": ") + ffs3::formatFilesizeToShortString(fileObj.getFileSize<side>()) + wxT("\n") + - _("Date") + wxT(": ") + ffs3::utcTimeToLocalString(fileObj.getLastWriteTime<side>()); + _("Size") + wxT(": ") + zen::formatFilesizeToShortString(fileObj.getFileSize<side>()) + wxT("\n") + + _("Date") + wxT(": ") + zen::utcTimeToLocalString(fileObj.getLastWriteTime<side>()); } virtual void visit(const SymLinkMapping& linkObj) { tipMsg_ = zToWx(linkObj.getRelativeName<side>()) + wxT("\n") + - _("Date") + wxT(": ") + ffs3::utcTimeToLocalString(linkObj.getLastWriteTime<side>()); + _("Date") + wxT(": ") + zen::utcTimeToLocalString(linkObj.getLastWriteTime<side>()); } virtual void visit(const DirMapping& dirObj) @@ -1698,7 +1699,7 @@ void CustomGridRim::getIconsToBeLoaded(std::vector<Zstring>& newLoad) //loads al if (!fileName.empty()) { //test if they are already loaded in buffer: - if (ffs3::IconBuffer::getInstance().requestFileIcon(fileName)) + if (zen::IconBuffer::getInstance().requestFileIcon(fileName)) { //exists in buffer: refresh Row for (int k = 0; k < totalCols; ++k) @@ -1746,7 +1747,7 @@ void IconUpdater::loadIconsAsynchronously(wxEvent& event) //loads all (not yet) //merge vectors newLoad.insert(newLoad.end(), iconsLeft.begin(), iconsLeft.end()); - ffs3::IconBuffer::getInstance().setWorkload(newLoad); + zen::IconBuffer::getInstance().setWorkload(newLoad); //event.Skip(); } @@ -1775,10 +1776,10 @@ bool CustomGridLeft::CreateGrid(int numRows, int numCols, wxGrid::wxGridSelectio } -void CustomGridLeft::initSettings(CustomGridLeft* gridLeft, //create connection with ffs3::GridView +void CustomGridLeft::initSettings(CustomGridLeft* gridLeft, //create connection with zen::GridView CustomGridMiddle* gridMiddle, CustomGridRight* gridRight, - const ffs3::GridView* gridDataView) + const zen::GridView* gridDataView) { //set underlying grid data assert(getGridDataTable()); @@ -1823,10 +1824,10 @@ bool CustomGridRight::CreateGrid(int numRows, int numCols, wxGrid::wxGridSelecti } -void CustomGridRight::initSettings(CustomGridLeft* gridLeft, //create connection with ffs3::GridView +void CustomGridRight::initSettings(CustomGridLeft* gridLeft, //create connection with zen::GridView CustomGridMiddle* gridMiddle, CustomGridRight* gridRight, - const ffs3::GridView* gridDataView) + const zen::GridView* gridDataView) { //set underlying grid data assert(getGridDataTable()); @@ -1921,10 +1922,10 @@ bool CustomGridMiddle::CreateGrid(int numRows, int numCols, wxGrid::wxGridSelect } -void CustomGridMiddle::initSettings(CustomGridLeft* gridLeft, //create connection with ffs3::GridView +void CustomGridMiddle::initSettings(CustomGridLeft* gridLeft, //create connection with zen::GridView CustomGridMiddle* gridMiddle, CustomGridRight* gridRight, - const ffs3::GridView* gridDataView) + const zen::GridView* gridDataView) { //set underlying grid data assert(getGridDataTable()); @@ -2345,7 +2346,7 @@ void CustomGridMiddle::DrawColLabel(wxDC& dc, int col) } -const wxBitmap& ffs3::getSyncOpImage(SyncOperation syncOp) +const wxBitmap& zen::getSyncOpImage(SyncOperation syncOp) { switch (syncOp) //evaluate comparison result and sync direction { diff --git a/library/custom_grid.h b/library/custom_grid.h index e8138bfc..1301486f 100644 --- a/library/custom_grid.h +++ b/library/custom_grid.h @@ -30,7 +30,7 @@ class CustomGridMiddle; class CustomGridRight; -namespace ffs3 +namespace zen { class GridView; @@ -63,12 +63,12 @@ public: virtual ~CustomGrid() {} - virtual void initSettings(CustomGridLeft* gridLeft, //create connection with ffs3::GridView + virtual void initSettings(CustomGridLeft* gridLeft, //create connection with zen::GridView CustomGridMiddle* gridMiddle, CustomGridRight* gridRight, - const ffs3::GridView* gridDataView); + const zen::GridView* gridDataView); - void release(); //release connection to ffs3::GridView + void release(); //release connection to zen::GridView std::set<size_t> getAllSelectedRows() const; @@ -165,7 +165,7 @@ public: void enableFileIcons(const bool value); protected: - template <ffs3::SelectedSide side> + template <zen::SelectedSide side> void setTooltip(const wxMouseEvent& event); void setOtherGrid(CustomGridRim* other); //call during initialization! @@ -211,10 +211,10 @@ public: virtual bool CreateGrid(int numRows, int numCols, wxGrid::wxGridSelectionModes selmode = wxGrid::wxGridSelectCells); - virtual void initSettings(CustomGridLeft* gridLeft, //create connection with ffs3::GridView + virtual void initSettings(CustomGridLeft* gridLeft, //create connection with zen::GridView CustomGridMiddle* gridMiddle, CustomGridRight* gridRight, - const ffs3::GridView* gridDataView); + const zen::GridView* gridDataView); private: void OnMouseMovement(wxMouseEvent& event); @@ -234,10 +234,10 @@ public: virtual bool CreateGrid(int numRows, int numCols, wxGrid::wxGridSelectionModes selmode = wxGrid::wxGridSelectCells); - virtual void initSettings(CustomGridLeft* gridLeft, //create connection with ffs3::GridView + virtual void initSettings(CustomGridLeft* gridLeft, //create connection with zen::GridView CustomGridMiddle* gridMiddle, CustomGridRight* gridRight, - const ffs3::GridView* gridDataView); + const zen::GridView* gridDataView); private: void OnMouseMovement(wxMouseEvent& event); @@ -261,10 +261,10 @@ public: virtual bool CreateGrid(int numRows, int numCols, wxGrid::wxGridSelectionModes selmode = wxGrid::wxGridSelectCells); - virtual void initSettings(CustomGridLeft* gridLeft, //create connection with ffs3::GridView + virtual void initSettings(CustomGridLeft* gridLeft, //create connection with zen::GridView CustomGridMiddle* gridMiddle, CustomGridRight* gridRight, - const ffs3::GridView* gridDataView); + const zen::GridView* gridDataView); void enableSyncPreview(bool value); @@ -346,7 +346,7 @@ extern const wxEventType FFS_SYNC_DIRECTION_EVENT; //define new event type class FFSSyncDirectionEvent : public wxCommandEvent { public: - FFSSyncDirectionEvent(const int from, const int to, const ffs3::SyncDirection dir) : + FFSSyncDirectionEvent(const int from, const int to, const zen::SyncDirection dir) : wxCommandEvent(FFS_SYNC_DIRECTION_EVENT), rowFrom(from), rowTo(to), @@ -359,7 +359,7 @@ public: const int rowFrom; const int rowTo; - const ffs3::SyncDirection direction; + const zen::SyncDirection direction; }; typedef void (wxEvtHandler::*FFSSyncDirectionEventFunction)(FFSSyncDirectionEvent&); diff --git a/library/db_file.cpp b/library/db_file.cpp index 43ebf283..47e03b66 100644 --- a/library/db_file.cpp +++ b/library/db_file.cpp @@ -7,29 +7,30 @@ #include "db_file.h" #include <wx/wfstream.h> #include <wx/zstream.h> +#include <wx/mstream.h> #include "../shared/global_func.h" #include "../shared/file_error.h" #include "../shared/string_conv.h" #include "../shared/file_handling.h" -#include <wx/mstream.h> #include "../shared/serialize.h" #include "../shared/file_io.h" #include "../shared/loki/ScopeGuard.h" #include "../shared/i18n.h" +#include <boost/bind.hpp> #ifdef FFS_WIN #include <wx/msw/wrapwin.h> //includes "windows.h" #include "../shared/long_path_prefix.h" #endif -using namespace ffs3; +using namespace zen; namespace { //------------------------------------------------------------------------------------------------------------------------------- const char FILE_FORMAT_DESCR[] = "FreeFileSync"; -const int FILE_FORMAT_VER = 5; +const int FILE_FORMAT_VER = 6; //------------------------------------------------------------------------------------------------------------------------------- @@ -67,19 +68,17 @@ private: //####################################################################################################################################### - - -class ReadDirInfo : public util::ReadInputStream +class ReadDirInfo : public zen::ReadInputStream { public: ReadDirInfo(wxInputStream& stream, const wxString& errorObjName, DirInformation& dirInfo) : ReadInputStream(stream, errorObjName) { //|------------------------------------------------------------------------------------- - //| ensure 32/64 bit portability: used fixed size data types only e.g. boost::uint32_t | + //| ensure 32/64 bit portability: use fixed size data types only e.g. boost::uint32_t | //|------------------------------------------------------------------------------------- - //read filter settings - dirInfo.filter = BaseFilter::loadFilter(getStream()); + //read filter settings -> currently not required, but persisting it doesn't hurt + dirInfo.filter = HardFilter::loadFilter(getStream()); check(); //start recursion @@ -89,56 +88,48 @@ public: private: void execute(DirContainer& dirCont) const { - boost::uint32_t fileCount = readNumberC<boost::uint32_t>(); - while (fileCount-- != 0) + while (readNumberC<bool>()) readSubFile(dirCont); - boost::uint32_t symlinkCount = readNumberC<boost::uint32_t>(); - while (symlinkCount-- != 0) + while (readNumberC<bool>()) readSubLink(dirCont); - boost::uint32_t dirCount = readNumberC<boost::uint32_t>(); - while (dirCount-- != 0) + while (readNumberC<bool>()) readSubDirectory(dirCont); } void readSubFile(DirContainer& dirCont) const { //attention: order of function argument evaluation is undefined! So do it one after the other... - const Zstring shortName = readStringC(); //file name - - const boost::int32_t modHigh = readNumberC<boost::int32_t>(); - const boost::uint32_t modLow = readNumberC<boost::uint32_t>(); + const Zstring shortName = readStringC<Zstring>(); //file name - const boost::uint32_t sizeHigh = readNumberC<boost::uint32_t>(); - const boost::uint32_t sizeLow = readNumberC<boost::uint32_t>(); + const boost::int64_t modTime = readNumberC<boost::int64_t>(); + const boost::uint64_t fileSize = readNumberC<boost::uint64_t>(); //const util::FileID fileIdentifier(stream_); //check(); dirCont.addSubFile(shortName, - FileDescriptor(wxLongLong(modHigh, modLow), - wxULongLong(sizeHigh, sizeLow))); + FileDescriptor(modTime, fileSize)); } void readSubLink(DirContainer& dirCont) const { //attention: order of function argument evaluation is undefined! So do it one after the other... - const Zstring shortName = readStringC(); //file name - const boost::int32_t modHigh = readNumberC<boost::int32_t>(); - const boost::uint32_t modLow = readNumberC<boost::uint32_t>(); - const Zstring targetPath = readStringC(); //file name + const Zstring shortName = readStringC<Zstring>(); //file name + const boost::int64_t modTime = readNumberC<boost::int64_t>(); + const Zstring targetPath = readStringC<Zstring>(); //file name const LinkDescriptor::LinkType linkType = static_cast<LinkDescriptor::LinkType>(readNumberC<boost::int32_t>()); dirCont.addSubLink(shortName, - LinkDescriptor(wxLongLong(modHigh, modLow), targetPath, linkType)); + LinkDescriptor(modTime, targetPath, linkType)); } void readSubDirectory(DirContainer& dirCont) const { - const Zstring shortName = readStringC(); //directory name + const Zstring shortName = readStringC<Zstring>(); //directory name DirContainer& subDir = dirCont.addSubDir(shortName); execute(subDir); //recurse } @@ -161,17 +152,19 @@ Partner-ID 567 _/ \_ Partner-ID 123 ... ... */ -class ReadFileStream : public util::ReadInputStream +class ReadFileStream : public zen::ReadInputStream { public: - ReadFileStream(wxInputStream& stream, const wxString& filename, DbStreamData& output) : ReadInputStream(stream, filename) + ReadFileStream(wxInputStream& stream, const wxString& filename, DbStreamData& output, int& versionId) : ReadInputStream(stream, filename) { //|------------------------------------------------------------------------------------- //| ensure 32/64 bit portability: used fixed size data types only e.g. boost::uint32_t | //|------------------------------------------------------------------------------------- - if (readNumberC<boost::int32_t>() != FILE_FORMAT_VER) //read file format version + 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(); @@ -197,7 +190,7 @@ public: DbStreamData loadFile(const Zstring& filename) //throw (FileError) { - if (!ffs3::fileExists(filename)) + 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("\"")); @@ -208,12 +201,13 @@ DbStreamData loadFile(const Zstring& filename) //throw (FileError) wxZlibInputStream input(uncompressed, wxZLIB_ZLIB); DbStreamData output; - ReadFileStream(input, zToWx(filename), output); + int versionId = 0; + ReadFileStream (input, zToWx(filename), output, versionId); return output; } -std::pair<DirInfoPtr, DirInfoPtr> ffs3::loadFromDisk(const BaseDirMapping& baseMapping) //throw (FileError) +std::pair<DirInfoPtr, DirInfoPtr> zen::loadFromDisk(const BaseDirMapping& baseMapping) //throw (FileError) { const Zstring fileNameLeft = baseMapping.getDBFilename<LEFT_SIDE>(); const Zstring fileNameRight = baseMapping.getDBFilename<RIGHT_SIDE>(); @@ -257,87 +251,128 @@ std::pair<DirInfoPtr, DirInfoPtr> ffs3::loadFromDisk(const BaseDirMapping& baseM //------------------------------------------------------------------------------------------------------------------------- - template <SelectedSide side> -struct IsNonEmpty -{ - bool operator()(const FileSystemObject& fsObj) const - { - return !fsObj.isEmpty<side>(); - } -}; - - -template <SelectedSide side> -class SaveDirInfo : public util::WriteOutputStream +class SaveDirInfo : public WriteOutputStream { public: - SaveDirInfo(const BaseDirMapping& baseMapping, const wxString& errorObjName, wxOutputStream& stream) : WriteOutputStream(errorObjName, stream) + SaveDirInfo(const BaseDirMapping& baseMapping, const DirContainer* oldDirInfo, const wxString& errorObjName, wxOutputStream& stream) : WriteOutputStream(errorObjName, stream) { //save filter settings baseMapping.getFilter()->saveFilter(getStream()); check(); //start recursion - execute(baseMapping); + execute(baseMapping, oldDirInfo); } private: - friend class util::ProxyForEach<SaveDirInfo<side> >; //friend declaration of std::for_each is NOT sufficient as implementation is compiler dependent! - - void execute(const HierarchyObject& hierObj) + void execute(const HierarchyObject& hierObj, const DirContainer* oldDirInfo) { - util::ProxyForEach<SaveDirInfo<side> > prx(*this); //grant std::for_each access to private parts of this class - - writeNumberC<boost::uint32_t>(std::count_if(hierObj.useSubFiles().begin(), hierObj.useSubFiles().end(), IsNonEmpty<side>())); //number of (existing) files - std::for_each(hierObj.useSubFiles().begin(), hierObj.useSubFiles().end(), prx); - - writeNumberC<boost::uint32_t>(std::count_if(hierObj.useSubLinks().begin(), hierObj.useSubLinks().end(), IsNonEmpty<side>())); //number of (existing) files - std::for_each(hierObj.useSubLinks().begin(), hierObj.useSubLinks().end(), prx); - - writeNumberC<boost::uint32_t>(std::count_if(hierObj.useSubDirs().begin(), hierObj.useSubDirs().end(), IsNonEmpty<side>())); //number of (existing) directories - std::for_each(hierObj.useSubDirs().begin(), hierObj.useSubDirs().end(), prx); + std::for_each(hierObj.useSubFiles().begin(), hierObj.useSubFiles().end(), boost::bind(&SaveDirInfo::processFile, this, _1, oldDirInfo)); + writeNumberC<bool>(false); //mark last entry + std::for_each(hierObj.useSubLinks().begin(), hierObj.useSubLinks().end(), boost::bind(&SaveDirInfo::processLink, this, _1, oldDirInfo)); + writeNumberC<bool>(false); //mark last entry + std::for_each(hierObj.useSubDirs ().begin(), hierObj.useSubDirs ().end(), boost::bind(&SaveDirInfo::processDir, this, _1, oldDirInfo)); + writeNumberC<bool>(false); //mark last entry } - void operator()(const FileMapping& fileMap) + void processFile(const FileMapping& fileMap, const DirContainer* oldDirInfo) { - if (!fileMap.isEmpty<side>()) + const Zstring shortName = fileMap.getObjShortName(); + + if (fileMap.getCategory() == FILE_EQUAL) //data in sync: write current state + { + if (!fileMap.isEmpty<side>()) + { + writeNumberC<bool>(true); //mark beginning of entry + writeStringC(shortName); + writeNumberC<boost::int64_t >(to<boost::int64_t>(fileMap.getLastWriteTime<side>())); //last modification time + writeNumberC<boost::uint64_t>(to<boost::uint64_t>(fileMap.getFileSize<side>())); //filesize + } + } + else //not in sync: reuse last synchronous state { - writeStringC(fileMap.getShortName<side>()); //file name - writeNumberC<boost::int32_t> (fileMap.getLastWriteTime<side>().GetHi()); //last modification time - writeNumberC<boost::uint32_t>(fileMap.getLastWriteTime<side>().GetLo()); // - writeNumberC<boost::uint32_t>(fileMap.getFileSize<side>().GetHi()); //filesize - writeNumberC<boost::uint32_t>(fileMap.getFileSize<side>().GetLo()); // - - //fileMap.getFileID<side>().toStream(stream_); //unique file identifier - //check(); + if (oldDirInfo) //no data is also a "synchronous state"! + { + DirContainer::FileList::const_iterator iter = oldDirInfo->files.find(shortName); + if (iter != oldDirInfo->files.end()) + { + writeNumberC<bool>(true); //mark beginning of entry + writeStringC(shortName); + writeNumberC<boost::int64_t >(to<boost::int64_t>(iter->second.lastWriteTimeRaw)); //last modification time + writeNumberC<boost::uint64_t>(to<boost::uint64_t>(iter->second.fileSize)); //filesize + } + } } } - void operator()(const SymLinkMapping& linkObj) + void processLink(const SymLinkMapping& linkObj, const DirContainer* oldDirInfo) { - if (!linkObj.isEmpty<side>()) + const Zstring shortName = linkObj.getObjShortName(); + + if (linkObj.getCategory() == FILE_EQUAL) //data in sync: write current state + { + if (!linkObj.isEmpty<side>()) + { + writeNumberC<bool>(true); //mark beginning of entry + writeStringC(shortName); + writeNumberC<boost::int64_t>(to<boost::int64_t>(linkObj.getLastWriteTime<side>())); //last modification time + writeStringC(linkObj.getTargetPath<side>()); + writeNumberC<boost::int32_t>(linkObj.getLinkType<side>()); + } + } + else //not in sync: reuse last synchronous state { - writeStringC(linkObj.getShortName<side>()); - writeNumberC<boost::int32_t> (linkObj.getLastWriteTime<side>().GetHi()); //last modification time - writeNumberC<boost::uint32_t>(linkObj.getLastWriteTime<side>().GetLo()); // - writeStringC(linkObj.getTargetPath<side>()); - writeNumberC<boost::int32_t>(linkObj.getLinkType<side>()); + if (oldDirInfo) //no data is also a "synchronous state"! + { + DirContainer::LinkList::const_iterator iter = oldDirInfo->links.find(shortName); + if (iter != oldDirInfo->links.end()) + { + writeNumberC<bool>(true); //mark beginning of entry + writeStringC(shortName); + writeNumberC<boost::int64_t>(to<boost::int64_t>(iter->second.lastWriteTimeRaw)); //last modification time + writeStringC(iter->second.targetPath); + writeNumberC<boost::int32_t>(iter->second.type); + } + } } } - void operator()(const DirMapping& dirMap) + void processDir(const DirMapping& dirMap, const DirContainer* oldDirInfo) { - if (!dirMap.isEmpty<side>()) + const Zstring shortName = dirMap.getObjShortName(); + + const DirContainer* subDirInfo = NULL; + if (oldDirInfo) //no data is also a "synchronous state"! + { + DirContainer::DirList::const_iterator iter = oldDirInfo->dirs.find(shortName); + if (iter != oldDirInfo->dirs.end()) + subDirInfo = &iter->second; + } + + if (dirMap.getCategory() == FILE_EQUAL) //data in sync: write current state { - writeStringC(dirMap.getShortName<side>()); //directory name - execute(dirMap); //recurse + if (!dirMap.isEmpty<side>()) + { + writeNumberC<bool>(true); //mark beginning of entry + writeStringC(shortName); + execute(dirMap, subDirInfo); //recurse + } + } + else //not in sync: reuse last synchronous state + { + if (subDirInfo) //no data is also a "synchronous state"! + { + writeNumberC<bool>(true); //mark beginning of entry + writeStringC(shortName); + } + execute(dirMap, subDirInfo); //recurse } } }; -class WriteFileStream : public util::WriteOutputStream +class WriteFileStream : public WriteOutputStream { public: WriteFileStream(const DbStreamData& input, const wxString& filename, wxOutputStream& stream) : WriteOutputStream(filename, stream) @@ -383,7 +418,7 @@ void saveFile(const DbStreamData& dbStream, const Zstring& filename) //throw (Fi } //(try to) hide database file #ifdef FFS_WIN - ::SetFileAttributes(ffs3::applyLongPathPrefix(filename).c_str(), FILE_ATTRIBUTE_HIDDEN); + ::SetFileAttributes(zen::applyLongPathPrefix(filename).c_str(), FILE_ATTRIBUTE_HIDDEN); #endif } @@ -395,13 +430,13 @@ bool entryExisting(const DirectoryTOC& table, const UniqueId& newKey, const Memo return false; if (!newValue.get() || !iter->second.get()) - return !newValue.get() && !iter->second.get(); + return newValue.get() == iter->second.get(); return *newValue == *iter->second; } -void ffs3::saveToDisk(const BaseDirMapping& baseMapping) //throw (FileError) +void zen::saveToDisk(const BaseDirMapping& baseMapping) //throw (FileError) { //transactional behaviour! write to tmp files first const Zstring fileNameLeftTmp = baseMapping.getDBFilename<LEFT_SIDE>() + Zstr(".tmp"); @@ -437,11 +472,47 @@ void ffs3::saveToDisk(const BaseDirMapping& baseMapping) //throw (FileError) dbEntriesRight.first = util::generateGUID(); } + + //(try to) read old DirInfo + std::auto_ptr<DirInformation> oldDirInfoLeft; + 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::auto_ptr<DirInformation> dirInfoTmp(new DirInformation); + ReadDirInfo(buffer, zToWx(baseMapping.getDBFilename<LEFT_SIDE>()), *dirInfoTmp); //read file/dir information + oldDirInfoLeft = dirInfoTmp; + } + } + catch(FileError&) {} //if error occurs: just overwrite old file! User is already informed about issues right after comparing! + + std::auto_ptr<DirInformation> oldDirInfoRight; + try + { + 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::auto_ptr<DirInformation> dirInfoTmp(new DirInformation); + ReadDirInfo(buffer, zToWx(baseMapping.getDBFilename<RIGHT_SIDE>()), *dirInfoTmp); //read file/dir information + oldDirInfoRight = dirInfoTmp; + } + } + catch(FileError&) {} + + //create new database entries MemoryStreamPtr dbEntryLeft(new std::vector<char>); { wxMemoryOutputStream buffer; - SaveDirInfo<LEFT_SIDE>(baseMapping, zToWx(baseMapping.getDBFilename<LEFT_SIDE>()), 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()); // } @@ -449,7 +520,8 @@ void ffs3::saveToDisk(const BaseDirMapping& baseMapping) //throw (FileError) MemoryStreamPtr dbEntryRight(new std::vector<char>); { wxMemoryOutputStream buffer; - SaveDirInfo<RIGHT_SIDE>(baseMapping, zToWx(baseMapping.getDBFilename<RIGHT_SIDE>()), 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()); // } @@ -462,15 +534,14 @@ void ffs3::saveToDisk(const BaseDirMapping& baseMapping) //throw (FileError) if (!updateRequiredLeft && !updateRequiredRight) return; } - - dbEntriesLeft.second[dbEntriesRight.first] = dbEntryLeft; - dbEntriesRight.second[dbEntriesLeft.first] = dbEntryRight; + dbEntriesLeft .second[dbEntriesRight.first] = dbEntryLeft; + dbEntriesRight.second[dbEntriesLeft .first] = dbEntryRight; //write (temp-) files... - Loki::ScopeGuard guardTempFileLeft = Loki::MakeGuard(&ffs3::removeFile, fileNameLeftTmp); + Loki::ScopeGuard guardTempFileLeft = Loki::MakeGuard(&zen::removeFile, fileNameLeftTmp); saveFile(dbEntriesLeft, fileNameLeftTmp); //throw (FileError) - Loki::ScopeGuard guardTempFileRight = Loki::MakeGuard(&ffs3::removeFile, fileNameRightTmp); + Loki::ScopeGuard guardTempFileRight = Loki::MakeGuard(&zen::removeFile, fileNameRightTmp); saveFile(dbEntriesRight, fileNameRightTmp); //throw (FileError) //operation finished: rename temp files -> this should work transactionally: diff --git a/library/db_file.h b/library/db_file.h index 8b88473d..179ebd57 100644 --- a/library/db_file.h +++ b/library/db_file.h @@ -7,24 +7,21 @@ #ifndef DBFILE_H_INCLUDED #define DBFILE_H_INCLUDED +#include "../shared/file_error.h" #include "../file_hierarchy.h" -namespace ffs3 +namespace zen { void saveToDisk(const BaseDirMapping& baseMapping); //throw (FileError) struct DirInformation { - BaseFilter::FilterRef filter; //filter settings (used when retrieving directory data) + HardFilter::FilterRef filter; //filter settings (used when retrieving directory data) DirContainer baseDirContainer; //hierarchical directory information }; typedef boost::shared_ptr<const DirInformation> DirInfoPtr; -class FileErrorDatabaseNotExisting : public FileError -{ -public: - FileErrorDatabaseNotExisting(const wxString& message) : FileError(message) {} -}; +DEFINE_NEW_FILE_ERROR(FileErrorDatabaseNotExisting); std::pair<DirInfoPtr, DirInfoPtr> loadFromDisk(const BaseDirMapping& baseMapping); //throw (FileError, FileErrorDatabaseNotExisting) -> return value always bound! } diff --git a/library/detect_renaming.h b/library/detect_renaming.h index ab974cf9..9f360f8e 100644 --- a/library/detect_renaming.h +++ b/library/detect_renaming.h @@ -12,13 +12,13 @@ //identify a file "create and delete"-operation as a file renaming! -namespace ffs3 +namespace zen { typedef FileMapping* CreateOnLeft; typedef FileMapping* DeleteOnLeft; typedef FileMapping* CreateOnRight; typedef FileMapping* DeleteOnRight; -void getRenameCandidates(ffs3::BaseDirMapping& baseMapping, //in +void getRenameCandidates(zen::BaseDirMapping& baseMapping, //in std::vector<std::pair<CreateOnLeft, DeleteOnLeft> >& renameOnLeft, //out std::vector<std::pair<CreateOnRight, DeleteOnRight> >& renameOnRight); //out throw()! } diff --git a/library/dir_lock.cpp b/library/dir_lock.cpp index 521f1c6f..8b9032b4 100644 --- a/library/dir_lock.cpp +++ b/library/dir_lock.cpp @@ -4,12 +4,11 @@ #include <wx/timer.h> #include <wx/log.h> #include <wx/msgdlg.h> -#include <boost/cstdint.hpp> #include <boost/shared_ptr.hpp> #include <boost/weak_ptr.hpp> #include "../shared/string_conv.h" #include "../shared/i18n.h" -#include "../shared/system_func.h" +#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" @@ -18,6 +17,7 @@ #include "../shared/assert_static.h" #include "../shared/serialize.h" #include "../shared/build_info.h" +#include "../shared/int64.h" #ifdef FFS_WIN #include <tlhelp32.h> @@ -31,7 +31,7 @@ #include <unistd.h> #endif -using namespace ffs3; +using namespace zen; using namespace std::rel_ops; @@ -153,16 +153,18 @@ void deleteLockFile(const Zstring& filename) //throw (FileError) } -wxULongLong getLockFileSize(const Zstring& filename) //throw (FileError, ErrorNotExisting) +zen::UInt64 getLockFileSize(const Zstring& filename) //throw (FileError, ErrorNotExisting) { #ifdef FFS_WIN - WIN32_FIND_DATA fileMetaData; - const HANDLE searchHandle = ::FindFirstFile(applyLongPathPrefix(filename).c_str(), &fileMetaData); + WIN32_FIND_DATA fileInfo = {}; + const HANDLE searchHandle = ::FindFirstFile(applyLongPathPrefix(filename).c_str(), &fileInfo); if (searchHandle == INVALID_HANDLE_VALUE) { const DWORD lastError = ::GetLastError(); - const wxString errorMessage = wxString(_("Error reading file attributes:")) + wxT("\n\"") + zToWx(filename) + wxT("\"") + - wxT("\n\n") + getLastErrorFormatted(lastError); + + wxString errorMessage = wxString(_("Error reading file attributes:")) + wxT("\n\"") + zToWx(filename) + wxT("\""); + errorMessage += wxT("\n\n") + getLastErrorFormatted(lastError); + if (lastError == ERROR_FILE_NOT_FOUND || lastError == ERROR_PATH_NOT_FOUND) throw ErrorNotExisting(errorMessage); @@ -172,22 +174,24 @@ wxULongLong getLockFileSize(const Zstring& filename) //throw (FileError, ErrorNo ::FindClose(searchHandle); - return wxULongLong(fileMetaData.nFileSizeHigh, fileMetaData.nFileSizeLow); + return zen::UInt64(fileInfo.nFileSizeLow, fileInfo.nFileSizeHigh); #elif defined FFS_LINUX - struct stat fileInfo; + struct stat fileInfo = {}; if (::stat(filename.c_str(), &fileInfo) != 0) //follow symbolic links { const int lastError = errno; - const wxString errorMessage = wxString(_("Error reading file attributes:")) + wxT("\n\"") + zToWx(filename) + wxT("\"") + - wxT("\n\n") + getLastErrorFormatted(lastError); + + wxString errorMessage = wxString(_("Error reading file attributes:")) + wxT("\n\"") + zToWx(filename) + wxT("\""); + errorMessage += wxT("\n\n") + getLastErrorFormatted(lastError); + if (lastError == ENOENT) throw ErrorNotExisting(errorMessage); else throw FileError(errorMessage); } - return fileInfo.st_size; + return zen::UInt64(fileInfo.st_size); #endif } @@ -208,7 +212,7 @@ namespace inline std::string readString(wxInputStream& stream) //throw (std::exception) { - const boost::uint32_t strLength = util::readNumber<boost::uint32_t>(stream); + const boost::uint32_t strLength = readPOD<boost::uint32_t>(stream); std::string output; if (strLength > 0) { @@ -223,7 +227,7 @@ inline void writeString(wxOutputStream& stream, const std::string& str) //write string to filestream { const boost::uint32_t strLength = static_cast<boost::uint32_t>(str.length()); - util::writeNumber<boost::uint32_t>(stream, strLength); + writePOD<boost::uint32_t>(stream, strLength); stream.Write(str.c_str(), strLength); } @@ -257,13 +261,13 @@ struct LockInformation { char formatDescr[sizeof(LOCK_FORMAT_DESCR)] = {}; stream.Read(formatDescr, sizeof(LOCK_FORMAT_DESCR)); //file format header - const int lockFileVersion = util::readNumber<boost::int32_t>(stream); // + const int lockFileVersion = readPOD<boost::int32_t>(stream); // (void)lockFileVersion; //some format checking here? lockId = readString(stream); - procDescr.processId = static_cast<ProcessId>(util::readNumber<boost::uint64_t>(stream)); //possible loss of precision (32/64 bit process) covered by buildId + procDescr.processId = static_cast<ProcessId>(readPOD<boost::uint64_t>(stream)); //possible loss of precision (32/64 bit process) covered by buildId procDescr.computerId = readString(stream); } @@ -272,10 +276,10 @@ struct LockInformation assert_static(sizeof(ProcessId) <= sizeof(boost::uint64_t)); //ensure portability stream.Write(LOCK_FORMAT_DESCR, sizeof(LOCK_FORMAT_DESCR)); - util::writeNumber<boost::int32_t>(stream, LOCK_FORMAT_VER); + writePOD<boost::int32_t>(stream, LOCK_FORMAT_VER); writeString(stream, lockId); - util::writeNumber<boost::uint64_t>(stream, procDescr.processId); + writePOD<boost::uint64_t>(stream, procDescr.processId); writeString(stream, procDescr.computerId); } @@ -337,7 +341,7 @@ ProcessStatus getProcessStatus(const LockInformation::ProcessDescription& procDe if (procDescr.processId <= 0 || procDescr.processId >= 65536) return PROC_STATUS_NO_IDEA; //invalid process id - return ffs3::dirExists(Zstr("/proc/") + Zstring::fromNumber(procDescr.processId)) ? PROC_STATUS_RUNNING : PROC_STATUS_NOT_RUNNING; + return zen::dirExists(Zstr("/proc/") + Zstring::fromNumber(procDescr.processId)) ? PROC_STATUS_RUNNING : PROC_STATUS_NOT_RUNNING; #endif } @@ -377,13 +381,13 @@ void waitOnDirLock(const Zstring& lockfilename, DirLockCallback* callback) //thr const LockInformation lockInfo = retrieveLockInfo(lockfilename); //throw (FileError, ErrorNotExisting) const bool lockOwnderDead = getProcessStatus(lockInfo.procDescr) == PROC_STATUS_NOT_RUNNING; //convenience optimization: if we know the owning process crashed, we needn't wait DETECT_EXITUS_INTERVAL sec - wxULongLong fileSizeOld; + zen::UInt64 fileSizeOld; wxLongLong lockSilentStart = wxGetLocalTimeMillis(); while (true) { - const wxULongLong fileSizeNew = ::getLockFileSize(lockfilename); //throw (FileError, ErrorNotExisting) - const wxLongLong currentTime = wxGetLocalTimeMillis(); + const zen::UInt64 fileSizeNew = ::getLockFileSize(lockfilename); //throw (FileError, ErrorNotExisting) + wxLongLong currentTime = wxGetLocalTimeMillis(); if (fileSizeNew != fileSizeOld) { @@ -421,10 +425,10 @@ void waitOnDirLock(const Zstring& lockfilename, DirLockCallback* callback) //thr { if (currentTime - lockSilentStart > EMIT_LIFE_SIGN_INTERVAL) //one signal missed: it's likely this is an abandoned lock: { - long remainingSeconds = ((DETECT_EXITUS_INTERVAL - (wxGetLocalTimeMillis() - lockSilentStart)) / 1000).GetLo(); - remainingSeconds = std::max(0L, remainingSeconds); + long remainingSeconds = ((DETECT_EXITUS_INTERVAL - (wxGetLocalTimeMillis() - lockSilentStart)) / 1000).ToLong(); + remainingSeconds = std::max(0L, remainingSeconds); - Zstring remSecMsg = wxToZ(_("%x sec")); + Zstring remSecMsg = wxToZ(_P("1 sec", "%x sec", remainingSeconds)); remSecMsg.Replace(Zstr("%x"), Zstring::fromNumber(remainingSeconds)); callback->reportInfo(infoMsg + Zstr(" ") + remSecMsg); } @@ -467,8 +471,10 @@ bool tryLock(const Zstring& lockfilename) //throw (FileError) if (::GetLastError() == ERROR_FILE_EXISTS) return false; else - throw FileError(wxString(_("Error setting directory lock:")) + wxT("\n\"") + zToWx(lockfilename) + wxT("\"") + - wxT("\n\n") + getLastErrorFormatted()); + { + wxString errorMessage = wxString(_("Error setting directory lock:")) + wxT("\n\"") + zToWx(lockfilename) + wxT("\""); + throw FileError(errorMessage + wxT("\n\n") + getLastErrorFormatted()); + } } ::CloseHandle(fileHandle); @@ -481,8 +487,11 @@ bool tryLock(const Zstring& lockfilename) //throw (FileError) if (errno == EEXIST) return false; else - throw FileError(wxString(_("Error setting directory lock:")) + wxT("\n\"") + zToWx(lockfilename) + wxT("\"") + - wxT("\n\n") + getLastErrorFormatted()); + { + wxString errorMessage = wxString(_("Error setting directory lock:")) + wxT("\n\"") + zToWx(lockfilename) + wxT("\""); + throw FileError(errorMessage + wxT("\n\n") + getLastErrorFormatted()); + } + } ::close(fileHandle); #endif diff --git a/library/dir_lock.h b/library/dir_lock.h index 9ae4da08..91b61990 100644 --- a/library/dir_lock.h +++ b/library/dir_lock.h @@ -19,12 +19,13 @@ RAII structure to place a directory lock against other FFS processes: - 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 (instantly if lock is associated with local pc, else after 30 seconds) + - temporary locks created during abandoned lock resolution keep "lockfilename"'s extension - race-free (Windows, almost on Linux(NFS)) */ class DirLock { public: - DirLock(const Zstring& lockfilename, DirLockCallback* callback = NULL); //throw (FileError) + DirLock(const Zstring& lockfilename, DirLockCallback* callback = NULL); //throw (FileError), callback only used during construction private: class LockAdmin; diff --git a/library/error_log.cpp b/library/error_log.cpp index 483d670b..a78b1c9a 100644 --- a/library/error_log.cpp +++ b/library/error_log.cpp @@ -9,10 +9,10 @@ #include "../shared/i18n.h" -using ffs3::ErrorLogging; +using zen::ErrorLogging; -void ErrorLogging::logMsg(const wxString& message, ffs3::MessageType type) +void ErrorLogging::logMsg(const wxString& message, zen::MessageType type) { Entry newEntry; newEntry.type = type; diff --git a/library/error_log.h b/library/error_log.h index ff4d3aee..1acc0f8c 100644 --- a/library/error_log.h +++ b/library/error_log.h @@ -13,7 +13,7 @@ #include "../shared/zstring.h" -namespace ffs3 +namespace zen { enum MessageType { diff --git a/library/filter.cpp b/library/hard_filter.cpp index 70933b13..46ccc9ca 100644 --- a/library/filter.cpp +++ b/library/hard_filter.cpp @@ -4,7 +4,7 @@ // * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * // ************************************************************************** // -#include "filter.h" +#include "hard_filter.h" #include "../shared/zstring.h" #include <wx/string.h> #include <set> @@ -16,47 +16,34 @@ #include "../shared/loki/LokiTypeInfo.h" #include "../shared/serialize.h" -using ffs3::BaseFilter; -using ffs3::NameFilter; +using namespace zen; //-------------------------------------------------------------------------------------------------- -bool BaseFilter::operator==(const BaseFilter& other) const +bool zen::operator<(const HardFilter& lhs, const HardFilter& rhs) { - return !(*this < other) && !(other < *this); -} - - -bool BaseFilter::operator!=(const BaseFilter& other) const -{ - return !(*this == other); -} - - -bool BaseFilter::operator<(const BaseFilter& other) const -{ - if (Loki::TypeInfo(typeid(*this)) != typeid(other)) - return Loki::TypeInfo(typeid(*this)) < typeid(other); + if (Loki::TypeInfo(typeid(lhs)) != typeid(rhs)) + return Loki::TypeInfo(typeid(lhs)) < typeid(rhs); //this and other are same type: - return cmpLessSameType(other); + return lhs.cmpLessSameType(rhs); } -void BaseFilter::saveFilter(wxOutputStream& stream) const //serialize derived object +void HardFilter::saveFilter(wxOutputStream& stream) const //serialize derived object { //save type information - util::writeString(stream, uniqueClassIdentifier()); + writeString(stream, uniqueClassIdentifier()); //save actual object save(stream); } -BaseFilter::FilterRef BaseFilter::loadFilter(wxInputStream& stream) +HardFilter::FilterRef HardFilter::loadFilter(wxInputStream& stream) { //read type information - const Zstring uniqueClassId = util::readString(stream); + const Zstring uniqueClassId = readString<Zstring>(stream); //read actual object if (uniqueClassId == Zstr("NullFilter")) @@ -83,10 +70,10 @@ void addFilterEntry(const Zstring& filtername, std::set<Zstring>& fileFilter, st //Linux DOES distinguish between upper/lower-case: nothing to do here #endif - 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; + 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; //-------------------------------------------------------------------------------------------------- //add some syntactic sugar: handle beginning of filtername @@ -143,14 +130,14 @@ const T* cStringFind(const T* str1, T ch) //strchr() } -bool matchesMask(const Zchar* string, const Zchar* mask) +bool matchesMask(const Zchar* str, const Zchar* mask) { - for (Zchar ch; (ch = *mask) != 0; ++mask, ++string) + for (Zchar ch; (ch = *mask) != 0; ++mask, ++str) { switch (ch) { case Zchar('?'): - if (*string == 0) + if (*str == 0) return false; break; @@ -162,34 +149,34 @@ bool matchesMask(const Zchar* string, const Zchar* mask) ch = *mask; } while (ch == Zchar('*') || ch == Zchar('?')); - //if match ends with '*': + //if mask ends with '*': if (ch == 0) return true; ++mask; - while ((string = cStringFind(string, ch)) != NULL) + while ((str = cStringFind(str, ch)) != NULL) { - ++string; - if (matchesMask(string, mask)) + ++str; + if (matchesMask(str, mask)) return true; } return false; default: - if (*string != ch) + if (*str != ch) return false; } } - return *string == 0; + return *str == 0; } //returns true if string matches at least the beginning of mask inline -bool matchesMaskBegin(const Zchar* string, const Zchar* mask) +bool matchesMaskBegin(const Zchar* str, const Zchar* mask) { - for (Zchar ch; (ch = *mask) != 0; ++mask, ++string) + for (Zchar ch; (ch = *mask) != 0; ++mask, ++str) { - if (*string == 0) + if (*str == 0) return true; switch (ch) @@ -201,11 +188,11 @@ bool matchesMaskBegin(const Zchar* string, const Zchar* mask) return true; default: - if (*string != ch) + if (*str != ch) return false; } } - return *string == 0; + return *str == 0; } } @@ -244,11 +231,18 @@ std::vector<Zstring> compoundStringToFilter(const Zstring& filterString) //delimiters may be ';' or '\n' std::vector<Zstring> output; - const std::vector<Zstring> filterPreProcessing = filterString.Split(Zchar(';')); - for (std::vector<Zstring>::const_iterator i = filterPreProcessing.begin(); i != filterPreProcessing.end(); ++i) + const std::vector<Zstring> blocks = filterString.Split(Zchar(';')); + for (std::vector<Zstring>::const_iterator i = blocks.begin(); i != blocks.end(); ++i) { - const std::vector<Zstring> newEntries = i->Split(Zchar('\n')); - output.insert(output.end(), newEntries.begin(), newEntries.end()); + const std::vector<Zstring> blocks2 = i->Split(Zchar('\n')); + + for (std::vector<Zstring>::const_iterator j = blocks2.begin(); j != blocks2.end(); ++j) + { + Zstring entry = *j; + entry.Trim(); + if (!entry.empty()) + output.push_back(entry); + } } return output; @@ -327,7 +321,7 @@ bool NameFilter::isNull() const } -bool NameFilter::cmpLessSameType(const BaseFilter& other) const +bool NameFilter::cmpLessSameType(const HardFilter& other) const { assert(typeid(*this) == typeid(other)); //always given in this context! @@ -357,15 +351,15 @@ Zstring NameFilter::uniqueClassIdentifier() const void NameFilter::save(wxOutputStream& stream) const { - util::writeString(stream, includeFilterTmp); - util::writeString(stream, excludeFilterTmp); + writeString(stream, includeFilterTmp); + writeString(stream, excludeFilterTmp); } -BaseFilter::FilterRef NameFilter::load(wxInputStream& stream) //"constructor" +HardFilter::FilterRef NameFilter::load(wxInputStream& stream) //"constructor" { - const Zstring include = util::readString(stream); - const Zstring exclude = util::readString(stream); + const Zstring include = readString<Zstring>(stream); + const Zstring exclude = readString<Zstring>(stream); return FilterRef(new NameFilter(include, exclude)); } diff --git a/library/filter.h b/library/hard_filter.h index c1be61eb..4290e07a 100644 --- a/library/filter.h +++ b/library/hard_filter.h @@ -12,56 +12,60 @@ #include <boost/shared_ptr.hpp> #include <wx/stream.h> -namespace ffs3 +namespace zen { //------------------------------------------------------------------ -/* class hierarchy: +/* +Semantics of HardFilter: +1. using it creates a NEW folder hierarchy! -> must be considered by <Automatic>-mode! (fortunately it turns out, doing nothing already has perfect semantics :) +2. it applies equally to both sides => it always matches either both sides or none! => can be used while traversing a single folder! - BaseFilter (interface) + class hierarchy: + + HardFilter (interface) /|\ _________|_____________ | | | NullFilter NameFilter CombinedFilter */ -/* -Semantics of BaseFilter: -1. using it creates a NEW folder hierarchy! -> must be respected by <Automatic>-mode! -2. it applies equally to both sides => it always matches either both sides or none! => can be used while traversing a single folder! -*/ - -class BaseFilter //interface for filtering +class HardFilter //interface for filtering { public: - virtual ~BaseFilter() {} + virtual ~HardFilter() {} //filtering virtual bool passFileFilter(const Zstring& relFilename) const = 0; - virtual bool passDirFilter(const Zstring& relDirname, bool* subObjMightMatch) const = 0; + virtual bool passDirFilter (const Zstring& relDirname, bool* subObjMightMatch) const = 0; //subObjMightMatch: file/dir in subdirectories could(!) match //note: variable is only set if passDirFilter returns false! virtual bool isNull() const = 0; //filter is equivalent to NullFilter, but may be technically slower - //comparison - bool operator<(const BaseFilter& other) const; - bool operator==(const BaseFilter& other) const; - bool operator!=(const BaseFilter& other) const; - - typedef boost::shared_ptr<const BaseFilter> FilterRef; //always bound by design! + typedef boost::shared_ptr<const HardFilter> FilterRef; //always bound by design! //serialization void saveFilter(wxOutputStream& stream) const; //serialize derived object static FilterRef loadFilter(wxInputStream& stream); //CAVEAT!!! adapt this method for each new derivation!!! private: + friend bool operator< (const HardFilter& lhs, const HardFilter& rhs); + virtual void save(wxOutputStream& stream) const = 0; //serialization virtual Zstring uniqueClassIdentifier() const = 0; //get identifier, used for serialization - virtual bool cmpLessSameType(const BaseFilter& other) const = 0; //typeid(*this) == typeid(other) in this context! + virtual bool cmpLessSameType(const HardFilter& other) const = 0; //typeid(*this) == typeid(other) in this context! }; +inline bool operator==(const HardFilter& lhs, const HardFilter& rhs) { return !(lhs < rhs) && !(rhs < lhs); } +inline bool operator!=(const HardFilter& lhs, const HardFilter& rhs) { return !(lhs == rhs); } + + +//small helper method: merge two hard filters (thereby remove Null-filters) +HardFilter::FilterRef combineFilters(const HardFilter::FilterRef& first, + const HardFilter::FilterRef& second); -class NullFilter : public BaseFilter //no filtering at all + +class NullFilter : public HardFilter //no filtering at all { public: virtual bool passFileFilter(const Zstring& relFilename) const; @@ -69,15 +73,15 @@ public: virtual bool isNull() const; private: - friend class BaseFilter; + friend class HardFilter; virtual void save(wxOutputStream& stream) const {} virtual Zstring uniqueClassIdentifier() const; static FilterRef load(wxInputStream& stream); //"serial constructor" - virtual bool cmpLessSameType(const BaseFilter& other) const; + virtual bool cmpLessSameType(const HardFilter& other) const; }; -class NameFilter : public BaseFilter //standard filter by filename +class NameFilter : public HardFilter //standard filter by filename { public: NameFilter(const Zstring& includeFilter, const Zstring& excludeFilter); @@ -87,11 +91,11 @@ public: virtual bool isNull() const; private: - friend class BaseFilter; + friend class HardFilter; virtual void save(wxOutputStream& stream) const; virtual Zstring uniqueClassIdentifier() const; static FilterRef load(wxInputStream& stream); //"serial constructor" - virtual bool cmpLessSameType(const BaseFilter& other) const; + virtual bool cmpLessSameType(const HardFilter& other) const; std::set<Zstring> filterFileIn; //upper case (windows) std::set<Zstring> filterFolderIn; // @@ -103,7 +107,7 @@ private: }; -class CombinedFilter : public BaseFilter //combine two filters to match if and only if both match +class CombinedFilter : public HardFilter //combine two filters to match if and only if both match { public: CombinedFilter(const FilterRef& first, const FilterRef& second) : first_(first), second_(second) {} @@ -113,22 +117,17 @@ public: virtual bool isNull() const; private: - friend class BaseFilter; + friend class HardFilter; virtual void save(wxOutputStream& stream) const; virtual Zstring uniqueClassIdentifier() const; static FilterRef load(wxInputStream& stream); //"serial constructor" - virtual bool cmpLessSameType(const BaseFilter& other) const; + virtual bool cmpLessSameType(const HardFilter& other) const; const FilterRef first_; const FilterRef second_; }; -//small helper method: remove Null-filters -BaseFilter::FilterRef combineFilters(const BaseFilter::FilterRef& first, - const BaseFilter::FilterRef& second); - - @@ -147,7 +146,7 @@ BaseFilter::FilterRef combineFilters(const BaseFilter::FilterRef& first, //---------------Inline Implementation--------------------------------------------------- inline -BaseFilter::FilterRef NullFilter::load(wxInputStream& stream) //"serial constructor" +HardFilter::FilterRef NullFilter::load(wxInputStream& stream) //"serial constructor" { return FilterRef(new NullFilter); } @@ -176,7 +175,7 @@ bool NullFilter::isNull() const inline -bool NullFilter::cmpLessSameType(const BaseFilter& other) const +bool NullFilter::cmpLessSameType(const HardFilter& other) const { assert(typeid(*this) == typeid(other)); //always given in this context! return false; @@ -214,7 +213,7 @@ bool CombinedFilter::isNull() const inline -bool CombinedFilter::cmpLessSameType(const BaseFilter& other) const +bool CombinedFilter::cmpLessSameType(const HardFilter& other) const { assert(typeid(*this) == typeid(other)); //always given in this context! @@ -243,7 +242,7 @@ void CombinedFilter::save(wxOutputStream& stream) const inline -BaseFilter::FilterRef CombinedFilter::load(wxInputStream& stream) //"constructor" +HardFilter::FilterRef CombinedFilter::load(wxInputStream& stream) //"constructor" { FilterRef first = loadFilter(stream); FilterRef second = loadFilter(stream); @@ -253,13 +252,13 @@ BaseFilter::FilterRef CombinedFilter::load(wxInputStream& stream) //"constructor inline -BaseFilter::FilterRef combineFilters(const BaseFilter::FilterRef& first, - const BaseFilter::FilterRef& second) +HardFilter::FilterRef combineFilters(const HardFilter::FilterRef& first, + const HardFilter::FilterRef& second) { if (first->isNull()) { if (second->isNull()) - return BaseFilter::FilterRef(new NullFilter); + return HardFilter::FilterRef(new NullFilter); else return second; } @@ -268,7 +267,7 @@ BaseFilter::FilterRef combineFilters(const BaseFilter::FilterRef& first, if (second->isNull()) return first; else - return BaseFilter::FilterRef(new CombinedFilter(first, second)); + return HardFilter::FilterRef(new CombinedFilter(first, second)); } } diff --git a/library/icon_buffer.cpp b/library/icon_buffer.cpp index 14883deb..1b1706d2 100644 --- a/library/icon_buffer.cpp +++ b/library/icon_buffer.cpp @@ -7,10 +7,12 @@ #include "icon_buffer.h" #include <wx/msgdlg.h> #include <map> +#include <vector> #include <queue> #include <set> #include <wx/log.h> #include "../shared/i18n.h" +#include "../shared/boost_thread_wrap.h" //include <boost/thread.hpp> #ifdef FFS_WIN #include <wx/msw/wrapwin.h> //includes "windows.h" @@ -22,11 +24,20 @@ #endif -using ffs3::IconBuffer; +using namespace zen; + + + +const size_t BUFFER_SIZE_MAX = 800; //maximum number of icons to buffer + +//--------------------------------------------------------------------------------------------------- +typedef Zbase<Zchar, StorageDeepCopy> BasicString; //thread safe string class +//avoid reference-counted objects for shared data: NOT THREADSAFE!!! (implicitly shared variable: ref-count) +//--------------------------------------------------------------------------------------------------- #ifdef FFS_WIN -IconBuffer::BasicString IconBuffer::getFileExtension(const BasicString& filename) +BasicString getFileExtension(const BasicString& filename) { const BasicString shortName = filename.AfterLast(Zchar('\\')); //warning: using windows file name separator! @@ -37,7 +48,7 @@ IconBuffer::BasicString IconBuffer::getFileExtension(const BasicString& filename //test for extension for icons that physically have to be retrieved from disc -bool IconBuffer::isPriceyExtension(const IconBuffer::BasicString& extension) +bool isPriceyExtension(const BasicString& extension) { static std::set<BasicString, LessFilename> exceptions; //not thread-safe, but called from worker thread only! if (exceptions.empty()) @@ -57,7 +68,7 @@ bool IconBuffer::isPriceyExtension(const IconBuffer::BasicString& extension) //################################################################################################################################################ -class IconBuffer::IconHolder //handle HICON/GdkPixbuf ownership WITHOUT ref-counting to allow thread-safe usage (in contrast to wxIcon) +class IconHolder //handle HICON/GdkPixbuf ownership WITHOUT ref-counting to allow thread-safe usage (in contrast to wxIcon) { public: #ifdef FFS_WIN @@ -121,7 +132,88 @@ private: }; -const wxIcon& IconBuffer::getDirectoryIcon() //one folder icon should be sufficient... +IconHolder getAssociatedIcon(const BasicString& filename) +{ +#ifdef FFS_WIN + //despite what docu says about SHGetFileInfo() it can't handle all relative filenames, e.g. "\DirName" + //but no problem, directory formatting takes care that filenames are always absolute! + + SHFILEINFO fileInfo = {}; //initialize hIcon -> fix for weird error: SHGetFileInfo() might return successfully WITHOUT filling fileInfo.hIcon!! + //bug report: https://sourceforge.net/tracker/?func=detail&aid=2768004&group_id=234430&atid=1093080 + + //NOTE: CoInitializeEx()/CoUninitialize() needs to be called for THIS thread! + ::SHGetFileInfo(filename.c_str(), //zen::removeLongPathPrefix(fileName), //::SHGetFileInfo() can't handle \\?\-prefix! + 0, + &fileInfo, + sizeof(fileInfo), + SHGFI_ICON | SHGFI_SMALLICON); + + return IconHolder(fileInfo.hIcon); //pass icon ownership (may be 0) + +#elif defined FFS_LINUX + //call Gtk::Main::init_gtkmm_internals() on application startup!! + try + { + Glib::RefPtr<Gio::File> fileObj = Gio::File::create_for_path(filename.c_str()); //never fails + Glib::RefPtr<Gio::FileInfo> fileInfo = fileObj->query_info(G_FILE_ATTRIBUTE_STANDARD_ICON); + if (fileInfo) + { + Glib::RefPtr<Gio::Icon> gicon = fileInfo->get_icon(); + if (gicon) + { + Glib::RefPtr<Gtk::IconTheme> iconTheme = Gtk::IconTheme::get_default(); + if (iconTheme) + { + Gtk::IconInfo iconInfo = iconTheme->lookup_icon(gicon, IconBuffer::ICON_SIZE, Gtk::ICON_LOOKUP_USE_BUILTIN); //this may fail if icon is not installed on system + if (iconInfo) + { + Glib::RefPtr<Gdk::Pixbuf> iconPixbuf = iconInfo.load_icon(); //render icon into Pixbuf + if (iconPixbuf) + return IconHolder(iconPixbuf->gobj_copy()); //copy and pass icon ownership (may be 0) + } + } + } + } + } + catch (const Glib::Error&) {} + + try //fallback: icon lookup may fail because some icons are currently not present on system + { + Glib::RefPtr<Gtk::IconTheme> iconTheme = Gtk::IconTheme::get_default(); + if (iconTheme) + { + Glib::RefPtr<Gdk::Pixbuf> iconPixbuf = iconTheme->load_icon("misc", IconBuffer::ICON_SIZE, Gtk::ICON_LOOKUP_USE_BUILTIN); + if (!iconPixbuf) + iconPixbuf = iconTheme->load_icon("text-x-generic", IconBuffer::ICON_SIZE, Gtk::ICON_LOOKUP_USE_BUILTIN); + if (iconPixbuf) + return IconHolder(iconPixbuf->gobj_copy()); //copy and pass icon ownership (may be 0) + } + } + catch (const Glib::Error&) {} + + //fallback fallback + return IconHolder(); +#endif +} + +#ifdef FFS_WIN +IconHolder getAssociatedIconByExt(const BasicString& extension) +{ + SHFILEINFO fileInfo = {}; //initialize hIcon -> fix for weird error: SHGetFileInfo() might return successfully WITHOUT filling fileInfo.hIcon!! + + //no read-access to disk! determine icon by extension + ::SHGetFileInfo((Zstr("dummy.") + extension).c_str(), //Windows Seven doesn't like this parameter to be without short name + FILE_ATTRIBUTE_NORMAL, + &fileInfo, + sizeof(fileInfo), + SHGFI_ICON | SHGFI_SMALLICON | SHGFI_USEFILEATTRIBUTES); + + return IconHolder(fileInfo.hIcon); //pass icon ownership (may be 0) +} +#endif + + +const wxIcon& getDirectoryIcon() //one folder icon should be sufficient... { static wxIcon folderIcon; @@ -147,14 +239,14 @@ const wxIcon& IconBuffer::getDirectoryIcon() //one folder icon should be suffici } #elif defined FFS_LINUX - folderIcon = getAssociatedIcon(Zstr("/usr/")).toWxIcon(); //all directories will look like "/usr/" + folderIcon = ::getAssociatedIcon(Zstr("/usr/")).toWxIcon(); //all directories will look like "/usr/" #endif } return folderIcon; } -const wxIcon& IconBuffer::getFileIcon() //in case one folder icon is sufficient... +const wxIcon& getFileIcon() //in case one folder icon is sufficient... { static wxIcon fileIcon; @@ -185,9 +277,9 @@ const wxIcon& IconBuffer::getFileIcon() //in case one folder icon is suffic Glib::RefPtr<Gtk::IconTheme> iconTheme = Gtk::IconTheme::get_default(); if (iconTheme) { - Glib::RefPtr<Gdk::Pixbuf> iconPixbuf = iconTheme->load_icon("misc", ICON_SIZE, Gtk::ICON_LOOKUP_USE_BUILTIN); + Glib::RefPtr<Gdk::Pixbuf> iconPixbuf = iconTheme->load_icon("misc", IconBuffer::ICON_SIZE, Gtk::ICON_LOOKUP_USE_BUILTIN); if (!iconPixbuf) - iconPixbuf = iconTheme->load_icon("text-x-generic", ICON_SIZE, Gtk::ICON_LOOKUP_USE_BUILTIN); + iconPixbuf = iconTheme->load_icon("text-x-generic", IconBuffer::ICON_SIZE, Gtk::ICON_LOOKUP_USE_BUILTIN); if (iconPixbuf) fileIcon.SetPixbuf(iconPixbuf->gobj_copy()); // transfer ownership!! } @@ -199,114 +291,89 @@ const wxIcon& IconBuffer::getFileIcon() //in case one folder icon is suffic } -IconBuffer::IconHolder IconBuffer::getAssociatedIcon(const BasicString& filename) +//################################################################################################################################################ +class Buffer { -#ifdef FFS_WIN - //despite what docu says about SHGetFileInfo() it can't handle all relative filenames, e.g. "\DirName" - //but no problem, directory formatting takes care that filenames are always absolute! +public: + //methods used by gui and worker thread + bool requestFileIcon(const Zstring& fileName, wxIcon* icon = NULL); - SHFILEINFO fileInfo = {}; //initialize hIcon -> fix for weird error: SHGetFileInfo() might return successfully WITHOUT filling fileInfo.hIcon!! - //bug report: https://sourceforge.net/tracker/?func=detail&aid=2768004&group_id=234430&atid=1093080 + //methods used by worker thread + void insertIntoBuffer(const BasicString& entryName, const IconHolder& icon); - //NOTE: CoInitializeEx()/CoUninitialize() needs to be called for THIS thread! - ::SHGetFileInfo(filename.c_str(), //ffs3::removeLongPathPrefix(fileName), //::SHGetFileInfo() can't handle \\?\-prefix! - 0, - &fileInfo, - sizeof(fileInfo), - SHGFI_ICON | SHGFI_SMALLICON); +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 + //--------------------------------------------------------------------------------------------------- - return IconHolder(fileInfo.hIcon); //pass icon ownership (may be 0) + //---------------------- Shared Data ------------------------- + boost::mutex lockIconDB; + IconDB iconBuffer; //use synchronisation when accessing this! + IconDbSequence iconSequence; //save sequence of buffer entry to delete oldest elements + //------------------------------------------------------------ +}; -#elif defined FFS_LINUX - //call Gtk::Main::init_gtkmm_internals() on application startup!! - try - { - Glib::RefPtr<Gio::File> fileObj = Gio::File::create_for_path(filename.c_str()); //never fails - Glib::RefPtr<Gio::FileInfo> fileInfo = fileObj->query_info(G_FILE_ATTRIBUTE_STANDARD_ICON); - if (fileInfo) - { - Glib::RefPtr<Gio::Icon> gicon = fileInfo->get_icon(); - if (gicon) - { - Glib::RefPtr<Gtk::IconTheme> iconTheme = Gtk::IconTheme::get_default(); - if (iconTheme) - { - Gtk::IconInfo iconInfo = iconTheme->lookup_icon(gicon, ICON_SIZE, Gtk::ICON_LOOKUP_USE_BUILTIN); //this may fail if icon is not installed on system - if (iconInfo) - { - Glib::RefPtr<Gdk::Pixbuf> iconPixbuf = iconInfo.load_icon(); //render icon into Pixbuf - if (iconPixbuf) - return IconHolder(iconPixbuf->gobj_copy()); //copy and pass icon ownership (may be 0) - } - } - } - } - } - catch (const Glib::Error&) {} - try //fallback: icon lookup may fail because some icons are currently not present on system - { - Glib::RefPtr<Gtk::IconTheme> iconTheme = Gtk::IconTheme::get_default(); - if (iconTheme) - { - Glib::RefPtr<Gdk::Pixbuf> iconPixbuf = iconTheme->load_icon("misc", ICON_SIZE, Gtk::ICON_LOOKUP_USE_BUILTIN); - if (!iconPixbuf) - iconPixbuf = iconTheme->load_icon("text-x-generic", ICON_SIZE, Gtk::ICON_LOOKUP_USE_BUILTIN); - if (iconPixbuf) - return IconHolder(iconPixbuf->gobj_copy()); //copy and pass icon ownership (may be 0) - } - } - catch (const Glib::Error&) {} +bool Buffer::requestFileIcon(const Zstring& fileName, wxIcon* icon) +{ + boost::lock_guard<boost::mutex> dummy(lockIconDB); - //fallback fallback - return IconHolder(); +#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); +#elif defined FFS_LINUX + IconDB::const_iterator i = iconBuffer.find(fileName.c_str()); #endif -} -#ifdef FFS_WIN -IconBuffer::IconHolder IconBuffer::getAssociatedIconByExt(const BasicString& extension) -{ - SHFILEINFO fileInfo = {}; //initialize hIcon -> fix for weird error: SHGetFileInfo() might return successfully WITHOUT filling fileInfo.hIcon!! + if (i == iconBuffer.end()) + return false; - //no read-access to disk! determine icon by extension - ::SHGetFileInfo((Zstr("dummy.") + extension).c_str(), //Windows Seven doesn't like this parameter to be without short name - FILE_ATTRIBUTE_NORMAL, - &fileInfo, - sizeof(fileInfo), - SHGFI_ICON | SHGFI_SMALLICON | SHGFI_USEFILEATTRIBUTES); + if (icon != NULL) + *icon = i->second.toWxIcon(); - return IconHolder(fileInfo.hIcon); //pass icon ownership (may be 0) + return true; } -#endif -namespace -{ -//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 -struct ThreadInitializer +void Buffer::insertIntoBuffer(const BasicString& entryName, const IconHolder& icon) //called by worker thread { - ThreadInitializer() - { -#ifdef FFS_WIN - ::CoInitializeEx(NULL, COINIT_MULTITHREADED); -#endif - } + boost::lock_guard<boost::mutex> dummy(lockIconDB); + + //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)); + if (rc.second) //if insertion took place + iconSequence.push(entryName); //note: sharing Zstring with IconDB!!! + + assert(iconBuffer.size() == iconSequence.size()); - ~ThreadInitializer() + //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) { -#ifdef FFS_WIN - ::CoUninitialize(); -#endif + //remove oldest element + iconBuffer.erase(iconSequence.front()); + iconSequence.pop(); } -}; } -class IconBuffer::WorkerThread +//################################################################################################################################################ +class WorkerThread { public: - WorkerThread(IconBuffer& iconBuff); - ~WorkerThread(); + 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 @@ -326,28 +393,13 @@ private: } shared; //------------------------------------------------------------ - IconBuffer& iconBuffer; + Buffer& iconBuffer; boost::thread threadObj; }; -IconBuffer::WorkerThread::WorkerThread(IconBuffer& iconBuff) : - iconBuffer(iconBuff) -{ - threadObj = boost::thread(boost::ref(*this)); //localize all thread logic to this class! -} - - -IconBuffer::WorkerThread::~WorkerThread() -{ - setWorkload(std::vector<Zstring>()); //make sure interruption point is always reached! - threadObj.interrupt(); - threadObj.join(); -} - - -void IconBuffer::WorkerThread::setWorkload(const std::vector<Zstring>& load) //(re-)set new workload of icons to be retrieved +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); @@ -362,9 +414,16 @@ void IconBuffer::WorkerThread::setWorkload(const std::vector<Zstring>& load) //( } -void IconBuffer::WorkerThread::operator()() //thread entry +void WorkerThread::operator()() //thread entry { - ThreadInitializer dummy1; + //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 +#ifdef FFS_WIN + struct ThreadInitializer + { + ThreadInitializer () { ::CoInitializeEx(NULL, COINIT_MULTITHREADED); } + ~ThreadInitializer() { ::CoUninitialize(); } + } dummy1; +#endif try { @@ -395,7 +454,7 @@ void IconBuffer::WorkerThread::operator()() //thread entry } -void IconBuffer::WorkerThread::doWork() +void WorkerThread::doWork() { //do work: get the file icon. while (true) @@ -416,90 +475,47 @@ void IconBuffer::WorkerThread::doWork() 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); + const IconHolder newIcon = getAssociatedIcon(fileName); iconBuffer.insertIntoBuffer(fileName, newIcon); } else //no read-access to disk! determine icon by extension { - const IconHolder newIcon = IconBuffer::getAssociatedIconByExt(extension); + const IconHolder newIcon = getAssociatedIconByExt(extension); iconBuffer.insertIntoBuffer(extension, newIcon); } #elif defined FFS_LINUX - const IconHolder newIcon = IconBuffer::getAssociatedIcon(fileName); + const IconHolder newIcon = getAssociatedIcon(fileName); iconBuffer.insertIntoBuffer(fileName, newIcon); #endif } } -//--------------------------------------------------------------------------------------------------- -class IconBuffer::IconDB : public std::map<BasicString, IconBuffer::IconHolder, LessFilename> {}; //entryName/icon -> ATTENTION: avoid ref-counting for this shared data structure! -class IconBuffer::IconDbSequence : public std::queue<BasicString> {}; //entryName -//--------------------------------------------------------------------------------------------------- +//######################### redirect to impl ##################################################### - -IconBuffer& IconBuffer::getInstance() +struct IconBuffer::Pimpl { - static IconBuffer instance; - return instance; -} + Pimpl() : buffer(), worker(buffer) {} //might throw exceptions! + Buffer buffer; + WorkerThread worker; +}; -IconBuffer::IconBuffer() : - buffer( new IconDB), - bufSequence(new IconDbSequence), - worker( new WorkerThread(*this)) //might throw exceptions! -{} +IconBuffer::IconBuffer() : pimpl(new Pimpl) {} IconBuffer::~IconBuffer() {} //auto_ptr<>: keep destructor non-inline +const wxIcon& IconBuffer::getDirectoryIcon() { return ::getDirectoryIcon(); } -bool IconBuffer::requestFileIcon(const Zstring& fileName, wxIcon* icon) -{ - boost::lock_guard<boost::mutex> dummy(lockIconDB); +const wxIcon& IconBuffer::getFileIcon() { return ::getFileIcon(); } -#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 = buffer->find(searchString); -#elif defined FFS_LINUX - IconDB::const_iterator i = buffer->find(fileName.c_str()); -#endif - - if (i == buffer->end()) - return false; - - if (icon != NULL) - *icon = i->second.toWxIcon(); - - return true; -} - - -void IconBuffer::setWorkload(const std::vector<Zstring>& load) +IconBuffer& IconBuffer::getInstance() { - worker->setWorkload(load); + static IconBuffer instance; + return instance; } +bool IconBuffer::requestFileIcon(const Zstring& fileName, wxIcon* icon) { return pimpl->buffer.requestFileIcon(fileName, icon); } -void IconBuffer::insertIntoBuffer(const BasicString& entryName, const IconHolder& icon) //called by worker thread -{ - boost::lock_guard<boost::mutex> dummy(lockIconDB); - - //thread saftey: icon uses ref-counting! But is NOT shared with main thread! - const std::pair<IconDB::iterator, bool> rc = buffer->insert(std::make_pair(entryName, icon)); - if (rc.second) //if insertion took place - bufSequence->push(entryName); //note: sharing Zstring with IconDB!!! - - assert(buffer->size() == bufSequence->size()); - - //remove elements if buffer becomes too big: - if (buffer->size() > BUFFER_SIZE_MAX) //limit buffer size: critical because GDI resources are limited (e.g. 10000 on XP per process) - { - //remove oldest element - buffer->erase(bufSequence->front()); - bufSequence->pop(); - } -} +void IconBuffer::setWorkload(const std::vector<Zstring>& load) { return pimpl->worker.setWorkload(load); } diff --git a/library/icon_buffer.h b/library/icon_buffer.h index b00a566d..5ab2740c 100644 --- a/library/icon_buffer.h +++ b/library/icon_buffer.h @@ -7,21 +7,18 @@ #ifndef ICONBUFFER_H_INCLUDED #define ICONBUFFER_H_INCLUDED -#include <vector> #include "../shared/zstring.h" #include <memory> #include <wx/icon.h> -#include "../shared/boost_thread_wrap.h" //include <boost/thread.hpp> -namespace ffs3 +namespace zen { - class IconBuffer { public: - static const wxIcon& getDirectoryIcon(); //one folder icon should be sufficient... - static const wxIcon& getFileIcon(); //in case one folder icon is sufficient... + static const wxIcon& getDirectoryIcon(); //one icon should be sufficient... + static const wxIcon& getFileIcon(); // static IconBuffer& getInstance(); bool requestFileIcon(const Zstring& fileName, wxIcon* icon = NULL); //returns false if icon is not in buffer @@ -37,36 +34,8 @@ private: IconBuffer(); ~IconBuffer(); - static const size_t BUFFER_SIZE_MAX = 800; //maximum number of icons to buffer - - class IconDB; - class IconHolder; - class IconDbSequence; - - //--------------------------------------------------------------------------------------------------- - typedef Zbase<Zchar, StorageDeepCopy> BasicString; //thread safe string class - //avoid reference-counted objects for shared data: NOT THREADSAFE!!! (implicitly shared variable: ref-count) - //--------------------------------------------------------------------------------------------------- - - //methods used by worker thread - void insertIntoBuffer(const BasicString& entryName, const IconHolder& icon); - - static IconHolder getAssociatedIcon(const BasicString& filename); - static IconHolder getAssociatedIconByExt(const BasicString& extension); - -#ifdef FFS_WIN - static BasicString getFileExtension(const BasicString& filename); - static bool isPriceyExtension(const BasicString& extension); -#endif - - //---------------------- Shared Data ------------------------- - boost::mutex lockIconDB; - std::auto_ptr<IconDB> buffer; //use synchronisation when accessing this! - std::auto_ptr<IconDbSequence> bufSequence; //save sequence of buffer entry to delete oldest elements - //------------------------------------------------------------ - - class WorkerThread; - std::auto_ptr<WorkerThread> worker; + struct Pimpl; + std::auto_ptr<Pimpl> pimpl; }; } diff --git a/library/lock_holder.h b/library/lock_holder.h new file mode 100644 index 00000000..b20646c3 --- /dev/null +++ b/library/lock_holder.h @@ -0,0 +1,52 @@ +#ifndef LOCK_HOLDER_H_INCLUDED +#define LOCK_HOLDER_H_INCLUDED + +#include <map> +#include "../shared/Zstring.h" +#include "dir_lock.h" +#include "status_handler.h" + +namespace zen +{ +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 +{ +public: + void addDir(const Zstring& dirnameFmt, ProcessCallback& procCallback) //resolved dirname ending with path separator + { + 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 + + 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); } + private: + ProcessCallback& pc_; + } callback(procCallback); + + try + { + lockHolder.insert(std::make_pair(dirnameFmt, DirLock(dirnameFmt + Zstr("sync.") + LOCK_FILE_ENDING, &callback))); + } + catch (const FileError& e) + { + bool dummy = false; //this warning shall not be shown but logged only + procCallback.reportWarning(e.msg(), dummy); + } + } + +private: + typedef std::map<Zstring, DirLock, LessFilename> DirnameLockMap; + DirnameLockMap lockHolder; +}; + +} + + +#endif // LOCK_HOLDER_H_INCLUDED diff --git a/library/norm_filter.h b/library/norm_filter.h new file mode 100644 index 00000000..2e55b43f --- /dev/null +++ b/library/norm_filter.h @@ -0,0 +1,98 @@ +// ************************************************************************** +// * This file is part of the FreeFileSync project. It is distributed under * +// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * +// * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * +// ************************************************************************** +// +#ifndef NORM_FILTER_H_INCLUDED +#define NORM_FILTER_H_INCLUDED + +#include "hard_filter.h" +#include "soft_filter.h" + +namespace zen +{ + +struct NormalizedFilter //grade-a filter: global/local filter settings combined, units resolved, ready for use +{ + NormalizedFilter(const HardFilter::FilterRef& hf, const SoftFilter& sf) : nameFilter(hf), timeSizeFilter(sf) {} + + //"hard" filter: relevant during comparison, physically skips files + HardFilter::FilterRef nameFilter; + //"soft" filter: relevant after comparison; equivalent to user selection + SoftFilter timeSizeFilter; +}; +namespace +{ +//combine global and local filters via "logical and" +NormalizedFilter normalizeFilters(const FilterConfig& global, const FilterConfig& local); + +inline +bool isNullFilter(const FilterConfig& filterCfg) +{ + return NameFilter(filterCfg.includeFilter, filterCfg.excludeFilter).isNull() && + SoftFilter(filterCfg.timeSpan, filterCfg.unitTimeSpan, + filterCfg.sizeMin, filterCfg.unitSizeMin, + filterCfg.sizeMax, filterCfg.unitSizeMax).isNull(); +} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +// ----------------------- implementation ----------------------- +NormalizedFilter normalizeFilters(const FilterConfig& global, const FilterConfig& local) +{ + HardFilter::FilterRef globalName(new NameFilter(global.includeFilter, global.excludeFilter)); + HardFilter::FilterRef localName (new NameFilter(local .includeFilter, local .excludeFilter)); + + SoftFilter globalTimeSize(global.timeSpan, global.unitTimeSpan, + global.sizeMin, global.unitSizeMin, + global.sizeMax, global.unitSizeMax); + + SoftFilter localTimeSize(local.timeSpan, local.unitTimeSpan, + local.sizeMin, local.unitSizeMin, + local.sizeMax, local.unitSizeMax); + + return NormalizedFilter(combineFilters(globalName, localName), + combineFilters(globalTimeSize, localTimeSize)); +} +} +} + +#endif //NORM_FILTER_H_INCLUDED diff --git a/library/process_xml.cpp b/library/process_xml.cpp index 9f95f5a3..0e591116 100644 --- a/library/process_xml.cpp +++ b/library/process_xml.cpp @@ -5,141 +5,96 @@ // ************************************************************************** // #include "process_xml.h" -#include "../shared/xml_base.h" #include "../shared/i18n.h" #include "../shared/global_func.h" #include "../shared/standard_paths.h" #include "../shared/string_conv.h" #include "../shared/file_handling.h" -using namespace ffs3; +using namespace zen; using namespace xmlAccess; //functionally needed!!! -class FfsXmlParser : public xmlAccess::XmlParser +XmlType xmlAccess::getXmlType(const wxString& filename) //throw() { -public: - FfsXmlParser(const TiXmlElement* rootElement) : xmlAccess::XmlParser(rootElement) {} - - //read gui settings, all values retrieved are optional, so check for initial values! (== -1) - void readXmlGuiConfig(xmlAccess::XmlGuiConfig& outputCfg); - //read batch settings, all values retrieved are optional - void readXmlBatchConfig(xmlAccess::XmlBatchConfig& outputCfg); - //read global settings, valid for both GUI and batch mode, independent from configuration - void readXmlGlobalSettings(xmlAccess::XmlGlobalSettings& outputCfg); - -private: - //read alternate configuration (optional) => might point to NULL - void readXmlLocalConfig(const TiXmlElement& folderPair, FolderPairEnh& enhPair); - - //read basic FreefileSync settings (used by commandline and GUI), return true if ALL values have been retrieved successfully - void readXmlMainConfig(MainConfiguration& mainCfg); -}; - - -//write gui settings -bool writeXmlGuiConfig(const xmlAccess::XmlGuiConfig& outputCfg, TiXmlDocument& doc); -//write batch settings -bool writeXmlBatchConfig(const xmlAccess::XmlBatchConfig& outputCfg, TiXmlDocument& doc); -//write global settings -bool writeXmlGlobalSettings(const xmlAccess::XmlGlobalSettings& outputCfg, TiXmlDocument& doc); -//write alternate configuration (optional) => might point to NULL -void writeXmlLocalConfig(const FolderPairEnh& enhPair, TiXmlElement& parent); -//write basic FreefileSync settings (used by commandline and GUI), return true if everything was written successfully -bool writeXmlMainConfig(const MainConfiguration& mainCfg, TiXmlDocument& doc); - - -void xmlAccess::readConfig(const wxString& filename, xmlAccess::XmlGuiConfig& config) -{ - //load XML - if (!fileExists(wxToZ(filename))) - throw XmlError(wxString(_("File does not exist:")) + wxT("\n\"") + filename + wxT("\"")); - - TiXmlDocument doc; - loadXmlDocument(filename, XML_GUI_CONFIG, doc); //throw (XmlError) - - FfsXmlParser parser(doc.RootElement()); - parser.readXmlGuiConfig(config); //read GUI layout configuration - - if (parser.errorsOccurred()) - throw XmlError(wxString(_("Error parsing configuration file:")) + wxT("\n\"") + filename + wxT("\"\n\n") + - parser.getErrorMessageFormatted(), XmlError::WARNING); -} - - -void xmlAccess::readConfig(const wxString& filename, xmlAccess::XmlBatchConfig& config) -{ - //load XML - if (!fileExists(wxToZ(filename))) - throw XmlError(wxString(_("File does not exist:")) + wxT("\n\"") + filename + wxT("\"")); - - TiXmlDocument doc; - loadXmlDocument(filename, XML_BATCH_CONFIG, doc); //throw (XmlError) - - FfsXmlParser parser(doc.RootElement()); - parser.readXmlBatchConfig(config); //read GUI layout configuration + try + { + TiXmlDocument doc; + loadXmlDocument(filename, doc); //throw (XmlError) + return getXmlType(doc); + } + catch (const XmlError&) {} - if (parser.errorsOccurred()) - throw XmlError(wxString(_("Error parsing configuration file:")) + wxT("\n\"") + filename + wxT("\"\n\n") + - parser.getErrorMessageFormatted(), XmlError::WARNING); + return XML_TYPE_OTHER; } -void xmlAccess::readConfig(xmlAccess::XmlGlobalSettings& config) +XmlType xmlAccess::getXmlType(const TiXmlDocument& doc) //throw() { - //load XML - if (!fileExists(wxToZ(getGlobalConfigFile()))) - throw XmlError(wxString(_("File does not exist:")) + wxT("\n\"") + getGlobalConfigFile() + wxT("\"")); - - TiXmlDocument doc; - loadXmlDocument(getGlobalConfigFile(), XML_GLOBAL_SETTINGS, doc); //throw (XmlError) - - FfsXmlParser parser(doc.RootElement()); - parser.readXmlGlobalSettings(config); //read GUI layout configuration - - if (parser.errorsOccurred()) - throw XmlError(wxString(_("Error parsing configuration file:")) + wxT("\n\"") + getGlobalConfigFile() + wxT("\"\n\n") + - parser.getErrorMessageFormatted(), XmlError::WARNING); + const TiXmlElement* root = doc.RootElement(); + if (root && root->ValueStr() == std::string("FreeFileSync")) + { + const char* cfgType = root->Attribute("XmlType"); + if (cfgType) + { + const std::string type(cfgType); + + if (type == "GUI") + return XML_TYPE_GUI; + else if (type == "BATCH") + return XML_TYPE_BATCH; + else if (type == "GLOBAL") + return XML_TYPE_GLOBAL; + } + } + return XML_TYPE_OTHER; } -void xmlAccess::writeConfig(const XmlGuiConfig& outputCfg, const wxString& filename) +void xmlAccess::initXmlDocument(TiXmlDocument& doc, XmlType type) //throw() { - TiXmlDocument doc; - getDefaultXmlDocument(XML_GUI_CONFIG, doc); + TiXmlDeclaration* decl = new TiXmlDeclaration("1.0", "UTF-8", ""); //delete won't be necessary later; ownership passed to TiXmlDocument! + doc.LinkEndChild(decl); - //populate and write XML tree - if (!writeXmlGuiConfig(outputCfg, doc)) //add GUI layout configuration settings - throw XmlError(wxString(_("Error writing file:")) + wxT("\n\"") + filename + wxT("\"")); + TiXmlElement* root = new TiXmlElement("FreeFileSync"); + doc.LinkEndChild(root); - saveXmlDocument(filename, doc); //throw (XmlError) + switch (type) + { + case XML_TYPE_GUI: + addXmlAttribute("XmlType", "GUI", doc.RootElement()); + break; + case XML_TYPE_BATCH: + addXmlAttribute("XmlType", "BATCH", doc.RootElement()); + break; + case XML_TYPE_GLOBAL: + addXmlAttribute("XmlType", "GLOBAL", doc.RootElement()); + break; + case XML_TYPE_OTHER: + break; + } } -void xmlAccess::writeConfig(const XmlBatchConfig& outputCfg, const wxString& filename) +class FfsXmlErrorLogger : public xmlAccess::XmlErrorLogger { - TiXmlDocument doc; - getDefaultXmlDocument(XML_BATCH_CONFIG, doc); - - //populate and write XML tree - if (!writeXmlBatchConfig(outputCfg, doc)) //add batch configuration settings - throw XmlError(wxString(_("Error writing file:")) + wxT("\n\"") + filename + wxT("\"")); - - saveXmlDocument(filename, doc); //throw (XmlError) -} - +public: + //read gui settings, all values retrieved are optional, so check for initial values! (== -1) + void readConfig(const TiXmlElement* root, xmlAccess::XmlGuiConfig& outputCfg); + //read batch settings, all values retrieved are optional + void readConfig(const TiXmlElement* root, xmlAccess::XmlBatchConfig& outputCfg); + //read global settings, valid for both GUI and batch mode, independent from configuration + void readConfig(const TiXmlElement* root, xmlAccess::XmlGlobalSettings& outputCfg); -void xmlAccess::writeConfig(const XmlGlobalSettings& outputCfg) -{ - TiXmlDocument doc; - getDefaultXmlDocument(XML_GLOBAL_SETTINGS, doc); +private: + //read alternate configuration (optional) => might point to NULL + void readConfig(const TiXmlElement& folderPair, FolderPairEnh& enhPair); + void readFilter(const TiXmlElement& parent, FilterConfig& output); - //populate and write XML tree - if (!writeXmlGlobalSettings(outputCfg, doc)) //add GUI layout configuration settings - throw XmlError(wxString(_("Error writing file:")) + wxT("\n\"") + getGlobalConfigFile() + wxT("\"")); + //read basic FreefileSync settings (used by commandline and GUI) + void readConfig(const TiXmlElement* root, MainConfiguration& mainCfg); +}; - saveXmlDocument(getGlobalConfigFile(), doc); //throw (XmlError) -} bool readXmlElement(const std::string& name, const TiXmlElement* parent, CompareVariant& output) @@ -148,9 +103,9 @@ bool readXmlElement(const std::string& name, const TiXmlElement* parent, Compare if (xmlAccess::readXmlElement(name, parent, dummy)) { if (dummy == "ByTimeAndSize") - output = ffs3::CMP_BY_TIME_SIZE; + output = zen::CMP_BY_TIME_SIZE; else if (dummy == "ByContent") - output = ffs3::CMP_BY_CONTENT; + output = zen::CMP_BY_CONTENT; else return false; @@ -199,17 +154,35 @@ bool readXmlElement(const std::string& name, const TiXmlElement* parent, xmlAcce } -bool readXmlElement(const std::string& name, const TiXmlElement* parent , ffs3::DeletionPolicy& output) +bool readXmlElement(const std::string& name, const TiXmlElement* parent , OnGuiError& output) +{ + std::string dummy; + if (xmlAccess::readXmlElement(name, parent, dummy)) + { + if (dummy == "Popup") + output = ON_GUIERROR_POPUP; + else if (dummy == "Ignore") + output = ON_GUIERROR_IGNORE; + else + return false; + + return true; + } + return false; +} + + +bool readXmlElement(const std::string& name, const TiXmlElement* parent , zen::DeletionPolicy& output) { std::string dummy; if (xmlAccess::readXmlElement(name, parent, dummy)) { if (dummy == "DeletePermanently") - output = ffs3::DELETE_PERMANENTLY; + output = zen::DELETE_PERMANENTLY; else if (dummy == "MoveToRecycleBin") - output = ffs3::MOVE_TO_RECYCLE_BIN; + output = zen::MOVE_TO_RECYCLE_BIN; else if (dummy == "MoveToCustomDirectory") - output = ffs3::MOVE_TO_CUSTOM_DIRECTORY; + output = zen::MOVE_TO_CUSTOM_DIRECTORY; else return false; @@ -219,17 +192,63 @@ bool readXmlElement(const std::string& name, const TiXmlElement* parent , ffs3:: } -bool readXmlElement(const std::string& name, const TiXmlElement* parent , ffs3::SymLinkHandling& output) +bool readXmlElement(const std::string& name, const TiXmlElement* parent , zen::SymLinkHandling& output) { std::string dummy; if (xmlAccess::readXmlElement(name, parent, dummy)) { if (dummy == "Ignore") - output = ffs3::SYMLINK_IGNORE; + output = zen::SYMLINK_IGNORE; else if (dummy == "UseDirectly") - output = ffs3::SYMLINK_USE_DIRECTLY; + output = zen::SYMLINK_USE_DIRECTLY; else if (dummy == "FollowLink") - output = ffs3::SYMLINK_FOLLOW_LINK; + output = zen::SYMLINK_FOLLOW_LINK; + else + return false; + + return true; + } + return false; +} + + +bool readXmlElement(const std::string& name, const TiXmlElement* parent , zen::UnitTime& output) +{ + std::string dummy; + if (xmlAccess::readXmlElement(name, parent, dummy)) + { + if (dummy == "Inactive") + output = zen::UTIME_NONE; + else if (dummy == "Second") + output = zen::UTIME_SEC; + else if (dummy == "Minute") + output = zen::UTIME_MIN; + else if (dummy == "Hour") + output = zen::UTIME_HOUR; + else if (dummy == "Day") + output = zen::UTIME_DAY; + else + return false; + + return true; + } + return false; +} + + +bool readXmlElement(const std::string& name, const TiXmlElement* parent , zen::UnitSize& output) +{ + std::string dummy; + if (xmlAccess::readXmlElement(name, parent, dummy)) + { + if (dummy == "Inactive") + output = zen::USIZE_NONE; + else if (dummy == "Byte") + output = zen::USIZE_BYTE; + else if (dummy == "KB") + output = zen::USIZE_KB; + else if (dummy == "MB") + output = zen::USIZE_MB; else return false; @@ -239,6 +258,27 @@ bool readXmlElement(const std::string& name, const TiXmlElement* parent , ffs3:: } +bool readXmlElement(const std::string& name, const TiXmlElement* parent , zen::SyncConfig::Variant& output) +{ + std::string dummy; + if (!xmlAccess::readXmlElement(name, parent, dummy)) + return false; + + if (dummy == "Automatic") + output = SyncConfig::AUTOMATIC; + else if (dummy == "Mirror") + output = SyncConfig::MIRROR; + else if (dummy == "Update") + output = SyncConfig::UPDATE; + else if (dummy == "Custom") + output = SyncConfig::CUSTOM; + else + return false; + + return true; +} + + bool readXmlElement(const std::string& name, const TiXmlElement* parent, Zstring& output) { wxString dummy; @@ -263,8 +303,27 @@ bool readXmlAttribute(const std::string& name, const TiXmlElement* node, xmlAcce } +void FfsXmlErrorLogger::readFilter(const TiXmlElement& parent, FilterConfig& output) +{ + //read filter settings + readXmlElementLogging("Include", &parent, output.includeFilter); + readXmlElementLogging("Exclude", &parent, output.excludeFilter); + + //migration "strategy": no error checking on these... :P + + readXmlElementLogging("TimeSpan", &parent, output.timeSpan); + readXmlElementLogging("UnitTimeSpan", &parent, output.unitTimeSpan); + + readXmlElementLogging("SizeMin", &parent, output.sizeMin); + readXmlElementLogging("UnitSizeMin", &parent, output.unitSizeMin); + + readXmlElementLogging("SizeMax", &parent, output.sizeMax); + readXmlElementLogging("UnitSizeMax", &parent, output.unitSizeMax); +} + + //################################################################################################################ -void FfsXmlParser::readXmlLocalConfig(const TiXmlElement& folderPair, FolderPairEnh& enhPair) +void FfsXmlErrorLogger::readConfig(const TiXmlElement& folderPair, FolderPairEnh& enhPair) { //read folder pairs readXmlElementLogging("Left", &folderPair, enhPair.leftDirectory); @@ -273,46 +332,43 @@ void FfsXmlParser::readXmlLocalConfig(const TiXmlElement& folderPair, FolderPair //########################################################### //alternate sync configuration - const TiXmlElement* altSyncConfig = TiXmlHandleConst(&folderPair).FirstChild("AlternateSyncConfig").ToElement(); - if (altSyncConfig) + const TiXmlElement* xmlAltSyncCfg = TiXmlHandleConst(&folderPair).FirstChild("AlternateSyncConfig").ToElement(); + if (xmlAltSyncCfg) { AlternateSyncConfig* altSyncCfg = new AlternateSyncConfig; enhPair.altSyncConfig.reset(altSyncCfg); - const TiXmlElement* syncCfg = TiXmlHandleConst(altSyncConfig).FirstChild("Synchronization").ToElement(); - const TiXmlElement* syncDirections = TiXmlHandleConst(syncCfg).FirstChild("Directions").ToElement(); + const TiXmlElement* xmlSyncDirections = TiXmlHandleConst(xmlAltSyncCfg).FirstChild("CustomDirections").ToElement(); //read sync configuration - readXmlElementLogging("Automatic", syncCfg, altSyncCfg->syncConfiguration.automatic); - readXmlElementLogging("LeftOnly", syncDirections, altSyncCfg->syncConfiguration.exLeftSideOnly); - readXmlElementLogging("RightOnly", syncDirections, altSyncCfg->syncConfiguration.exRightSideOnly); - readXmlElementLogging("LeftNewer", syncDirections, altSyncCfg->syncConfiguration.leftNewer); - readXmlElementLogging("RightNewer", syncDirections, altSyncCfg->syncConfiguration.rightNewer); - readXmlElementLogging("Different", syncDirections, altSyncCfg->syncConfiguration.different); - readXmlElementLogging("Conflict", syncDirections, altSyncCfg->syncConfiguration.conflict); - - const TiXmlElement* miscSettings = TiXmlHandleConst(&folderPair).FirstChild("AlternateSyncConfig").FirstChild("Miscellaneous").ToElement(); - readXmlElementLogging("DeletionPolicy", miscSettings, altSyncCfg->handleDeletion); - readXmlElementLogging("CustomDeletionFolder", miscSettings, altSyncCfg->customDeletionDirectory); + readXmlElementLogging("Variant", xmlAltSyncCfg, altSyncCfg->syncConfiguration.var); + + readXmlElementLogging("LeftOnly", xmlSyncDirections, altSyncCfg->syncConfiguration.custom.exLeftSideOnly); + readXmlElementLogging("RightOnly", xmlSyncDirections, altSyncCfg->syncConfiguration.custom.exRightSideOnly); + readXmlElementLogging("LeftNewer", xmlSyncDirections, altSyncCfg->syncConfiguration.custom.leftNewer); + readXmlElementLogging("RightNewer", xmlSyncDirections, altSyncCfg->syncConfiguration.custom.rightNewer); + readXmlElementLogging("Different", xmlSyncDirections, altSyncCfg->syncConfiguration.custom.different); + readXmlElementLogging("Conflict", xmlSyncDirections, altSyncCfg->syncConfiguration.custom.conflict); + + readXmlElementLogging("DeletionPolicy", xmlAltSyncCfg, altSyncCfg->handleDeletion); + readXmlElementLogging("CustomDeletionFolder", xmlAltSyncCfg, altSyncCfg->customDeletionDirectory); } //########################################################### //alternate filter configuration const TiXmlElement* filterCfg = TiXmlHandleConst(&folderPair).FirstChild("LocalFilter").ToElement(); if (filterCfg) - { - //read filter settings - readXmlElementLogging("Include", filterCfg, enhPair.localFilter.includeFilter); - readXmlElementLogging("Exclude", filterCfg, enhPair.localFilter.excludeFilter); - } + readFilter(*filterCfg, enhPair.localFilter); } -void FfsXmlParser::readXmlMainConfig(MainConfiguration& mainCfg) +void FfsXmlErrorLogger::readConfig(const TiXmlElement* root, MainConfiguration& mainCfg) { - TiXmlHandleConst hRoot(getRoot()); //custom const handle: TiXml API seems broken in this regard + TiXmlHandleConst hRoot(root); //custom const handle: TiXml API seems broken in this regard //########################################################### + const TiXmlElement* xmlMainConfig = hRoot.FirstChild("MainConfig").ToElement(); + const TiXmlElement* cmpSettings = hRoot.FirstChild("MainConfig").FirstChild("Comparison").ToElement(); //read compare variant @@ -322,34 +378,31 @@ void FfsXmlParser::readXmlMainConfig(MainConfiguration& mainCfg) readXmlElementLogging("HandleSymlinks", cmpSettings, mainCfg.handleSymlinks); //########################################################### - const TiXmlElement* syncCfg = hRoot.FirstChild("MainConfig").FirstChild("Synchronization").ToElement(); - const TiXmlElement* syncDirections = TiXmlHandleConst(syncCfg).FirstChild("Directions").ToElement(); + const TiXmlElement* xmlSyncCfg = hRoot.FirstChild("MainConfig").FirstChild("SyncConfig").ToElement(); + const TiXmlElement* syncDirections = TiXmlHandleConst(xmlSyncCfg).FirstChild("CustomDirections").ToElement(); //read sync configuration - readXmlElementLogging("Automatic", syncCfg, mainCfg.syncConfiguration.automatic); - readXmlElementLogging("LeftOnly", syncDirections, mainCfg.syncConfiguration.exLeftSideOnly); - readXmlElementLogging("RightOnly", syncDirections, mainCfg.syncConfiguration.exRightSideOnly); - readXmlElementLogging("LeftNewer", syncDirections, mainCfg.syncConfiguration.leftNewer); - readXmlElementLogging("RightNewer", syncDirections, mainCfg.syncConfiguration.rightNewer); - readXmlElementLogging("Different", syncDirections, mainCfg.syncConfiguration.different); - readXmlElementLogging("Conflict", syncDirections, mainCfg.syncConfiguration.conflict); + readXmlElementLogging("Variant", xmlSyncCfg, mainCfg.syncConfiguration.var); - //########################################################### - const TiXmlElement* miscSettings = hRoot.FirstChild("MainConfig").FirstChild("Miscellaneous").ToElement(); + readXmlElementLogging("LeftOnly", syncDirections, mainCfg.syncConfiguration.custom.exLeftSideOnly); + readXmlElementLogging("RightOnly", syncDirections, mainCfg.syncConfiguration.custom.exRightSideOnly); + readXmlElementLogging("LeftNewer", syncDirections, mainCfg.syncConfiguration.custom.leftNewer); + readXmlElementLogging("RightNewer", syncDirections, mainCfg.syncConfiguration.custom.rightNewer); + readXmlElementLogging("Different", syncDirections, mainCfg.syncConfiguration.custom.different); + readXmlElementLogging("Conflict", syncDirections, mainCfg.syncConfiguration.custom.conflict); + //########################################################### //misc - readXmlElementLogging("DeletionPolicy", miscSettings, mainCfg.handleDeletion); - readXmlElementLogging("CustomDeletionFolder", miscSettings, mainCfg.customDeletionDirectory); + readXmlElementLogging("DeletionPolicy", xmlSyncCfg, mainCfg.handleDeletion); + readXmlElementLogging("CustomDeletionFolder", xmlSyncCfg, mainCfg.customDeletionDirectory); //########################################################### - const TiXmlElement* filter = TiXmlHandleConst(miscSettings).FirstChild("Filter").ToElement(); + const TiXmlElement* filter = TiXmlHandleConst(xmlMainConfig).FirstChild("GlobalFilter").ToElement(); //read filter settings - Zstring includeFilter; - Zstring excludeFilter; - readXmlElementLogging("Include", filter, includeFilter); - readXmlElementLogging("Exclude", filter, excludeFilter); - - mainCfg.globalFilter = FilterConfig(includeFilter, excludeFilter); + if (filter) + readFilter(*filter, mainCfg.globalFilter); + else + logError("GlobalFilter"); //########################################################### const TiXmlElement* pairs = hRoot.FirstChild("MainConfig").FirstChild("FolderPairs").FirstChild("Pair").ToElement(); @@ -360,7 +413,7 @@ void FfsXmlParser::readXmlMainConfig(MainConfiguration& mainCfg) while (pairs) { FolderPairEnh newPair; - readXmlLocalConfig(*pairs, newPair); + readConfig(*pairs, newPair); if (firstLoop) //read first folder pair { @@ -375,31 +428,27 @@ void FfsXmlParser::readXmlMainConfig(MainConfiguration& mainCfg) } -void FfsXmlParser::readXmlGuiConfig(xmlAccess::XmlGuiConfig& outputCfg) +void FfsXmlErrorLogger::readConfig(const TiXmlElement* root, xmlAccess::XmlGuiConfig& outputCfg) { //read main config - readXmlMainConfig(outputCfg.mainCfg); + readConfig(root, outputCfg.mainCfg); //read GUI specific config data - const TiXmlElement* guiConfig = TiXmlHandleConst(getRoot()).FirstChild("GuiConfig").ToElement(); - - readXmlElementLogging("HideFiltered", guiConfig, outputCfg.hideFilteredElements); - - xmlAccess::OnError errorHand = ON_ERROR_POPUP; - readXmlElementLogging("HandleError", guiConfig, errorHand); - outputCfg.ignoreErrors = errorHand == xmlAccess::ON_ERROR_IGNORE; + const TiXmlElement* guiConfig = TiXmlHandleConst(root).FirstChild("GuiConfig").ToElement(); + readXmlElementLogging("HideFiltered", guiConfig, outputCfg.hideFilteredElements); + readXmlElementLogging("HandleError", guiConfig, outputCfg.handleError); readXmlElementLogging("SyncPreviewActive", guiConfig, outputCfg.syncPreviewEnabled); } -void FfsXmlParser::readXmlBatchConfig(xmlAccess::XmlBatchConfig& outputCfg) +void FfsXmlErrorLogger::readConfig(const TiXmlElement* root, xmlAccess::XmlBatchConfig& outputCfg) { //read main config - readXmlMainConfig(outputCfg.mainCfg); + readConfig(root, outputCfg.mainCfg); //read batch specific config - const TiXmlElement* batchConfig = TiXmlHandleConst(getRoot()).FirstChild("BatchConfig").ToElement(); + const TiXmlElement* batchConfig = TiXmlHandleConst(root).FirstChild("BatchConfig").ToElement(); readXmlElementLogging("Silent", batchConfig, outputCfg.silent); readXmlElementLogging("LogfileDirectory", batchConfig, outputCfg.logFileDirectory); @@ -408,10 +457,10 @@ void FfsXmlParser::readXmlBatchConfig(xmlAccess::XmlBatchConfig& outputCfg) } -void FfsXmlParser::readXmlGlobalSettings(xmlAccess::XmlGlobalSettings& outputCfg) +void FfsXmlErrorLogger::readConfig(const TiXmlElement* root, xmlAccess::XmlGlobalSettings& outputCfg) { //read global settings - const TiXmlElement* global = TiXmlHandleConst(getRoot()).FirstChild("Shared").ToElement(); + const TiXmlElement* global = TiXmlHandleConst(root).FirstChild("Shared").ToElement(); //try to read program language setting readXmlElementLogging("Language", global, outputCfg.programLanguage); @@ -429,7 +478,7 @@ void FfsXmlParser::readXmlGlobalSettings(xmlAccess::XmlGlobalSettings& outputCfg readXmlElementLogging("FileTimeTolerance", global, outputCfg.fileTimeTolerance); - const TiXmlElement* optionalDialogs = TiXmlHandleConst(getRoot()).FirstChild("Shared").FirstChild("ShowOptionalDialogs").ToElement(); + const TiXmlElement* optionalDialogs = TiXmlHandleConst(root).FirstChild("Shared").FirstChild("ShowOptionalDialogs").ToElement(); //folder dependency check readXmlElementLogging("CheckForDependentFolders", optionalDialogs, outputCfg.optDialogs.warningDependentFolders); @@ -452,7 +501,7 @@ void FfsXmlParser::readXmlGlobalSettings(xmlAccess::XmlGlobalSettings& outputCfg //gui specific global settings (optional) - const TiXmlElement* gui = TiXmlHandleConst(getRoot()).FirstChild("Gui").ToElement(); + const TiXmlElement* gui = TiXmlHandleConst(root).FirstChild("Gui").ToElement(); const TiXmlElement* mainWindow = TiXmlHandleConst(gui).FirstChild("Windows").FirstChild("Main").ToElement(); //read application window size and position @@ -508,19 +557,9 @@ void FfsXmlParser::readXmlGlobalSettings(xmlAccess::XmlGlobalSettings& outputCfg rightColumn = rightColumn->NextSiblingElement(); } - //load folder history elements - const TiXmlElement* historyLeft = TiXmlHandleConst(mainWindow).FirstChild("FolderHistoryLeft").ToElement(); - //load max. history size - readXmlAttributeLogging("MaximumSize", historyLeft, outputCfg.gui.folderHistLeftMax); - //load config history elements - readXmlElementLogging("Folder", historyLeft, outputCfg.gui.folderHistoryLeft); - - - const TiXmlElement* historyRight = TiXmlHandleConst(mainWindow).FirstChild("FolderHistoryRight").ToElement(); - //load max. history size - readXmlAttributeLogging("MaximumSize", historyRight, outputCfg.gui.folderHistRightMax); - //load config history elements - readXmlElementLogging("Folder", historyRight, outputCfg.gui.folderHistoryRight); + readXmlElementLogging("FolderHistoryLeft", mainWindow, outputCfg.gui.folderHistoryLeft); + readXmlElementLogging("FolderHistoryRight", mainWindow, outputCfg.gui.folderHistoryRight); + readXmlElementLogging("MaximumHistorySize", mainWindow, outputCfg.gui.folderHistMax); readXmlElementLogging("Perspective", mainWindow, outputCfg.gui.guiPerspectiveLast); @@ -562,10 +601,10 @@ void addXmlElement(const std::string& name, const CompareVariant variant, TiXmlE { switch (variant) { - case ffs3::CMP_BY_TIME_SIZE: + case zen::CMP_BY_TIME_SIZE: xmlAccess::addXmlElement(name, std::string("ByTimeAndSize"), parent); break; - case ffs3::CMP_BY_CONTENT: + case zen::CMP_BY_CONTENT: xmlAccess::addXmlElement(name, std::string("ByContent"), parent); break; } @@ -606,40 +645,117 @@ void addXmlElement(const std::string& name, const xmlAccess::OnError value, TiXm } -void addXmlElement(const std::string& name, const ffs3::DeletionPolicy value, TiXmlElement* parent) +void addXmlElement(const std::string& name, const OnGuiError value, TiXmlElement* parent) { switch (value) { - case ffs3::DELETE_PERMANENTLY: + case ON_GUIERROR_IGNORE: + xmlAccess::addXmlElement(name, std::string("Ignore"), parent); + break; + case ON_GUIERROR_POPUP: + xmlAccess::addXmlElement(name, std::string("Popup"), parent); + break; + } +} + + +void addXmlElement(const std::string& name, const zen::DeletionPolicy value, TiXmlElement* parent) +{ + switch (value) + { + case zen::DELETE_PERMANENTLY: xmlAccess::addXmlElement(name, std::string("DeletePermanently"), parent); break; - case ffs3::MOVE_TO_RECYCLE_BIN: + case zen::MOVE_TO_RECYCLE_BIN: xmlAccess::addXmlElement(name, std::string("MoveToRecycleBin"), parent); break; - case ffs3::MOVE_TO_CUSTOM_DIRECTORY: + case zen::MOVE_TO_CUSTOM_DIRECTORY: xmlAccess::addXmlElement(name, std::string("MoveToCustomDirectory"), parent); break; } } -void addXmlElement(const std::string& name, const ffs3::SymLinkHandling value, TiXmlElement* parent) +void addXmlElement(const std::string& name, const zen::SymLinkHandling value, TiXmlElement* parent) { switch (value) { - case ffs3::SYMLINK_IGNORE: + case zen::SYMLINK_IGNORE: xmlAccess::addXmlElement(name, std::string("Ignore"), parent); break; - case ffs3::SYMLINK_USE_DIRECTLY: + case zen::SYMLINK_USE_DIRECTLY: xmlAccess::addXmlElement(name, std::string("UseDirectly"), parent); break; - case ffs3::SYMLINK_FOLLOW_LINK: + case zen::SYMLINK_FOLLOW_LINK: xmlAccess::addXmlElement(name, std::string("FollowLink"), parent); break; } } +void addXmlElement(const std::string& name, const zen::UnitTime value, TiXmlElement* parent) +{ + switch (value) + { + case zen::UTIME_NONE: + xmlAccess::addXmlElement(name, std::string("Inactive"), parent); + break; + case zen::UTIME_SEC: + xmlAccess::addXmlElement(name, std::string("Second"), parent); + break; + case zen::UTIME_MIN: + xmlAccess::addXmlElement(name, std::string("Minute"), parent); + break; + case zen::UTIME_HOUR: + xmlAccess::addXmlElement(name, std::string("Hour"), parent); + break; + case zen::UTIME_DAY: + xmlAccess::addXmlElement(name, std::string("Day"), parent); + break; + } +} + + +void addXmlElement(const std::string& name, zen::UnitSize value, TiXmlElement* parent) +{ + switch (value) + { + case zen::USIZE_NONE: + xmlAccess::addXmlElement(name, std::string("Inactive"), parent); + break; + case zen::USIZE_BYTE: + xmlAccess::addXmlElement(name, std::string("Byte"), parent); + break; + case zen::USIZE_KB: + xmlAccess::addXmlElement(name, std::string("KB"), parent); + break; + case zen::USIZE_MB: + xmlAccess::addXmlElement(name, std::string("MB"), parent); + break; + } +} + + +void addXmlElement(const std::string& name, SyncConfig::Variant value, TiXmlElement* parent) +{ + switch (value) + { + case SyncConfig::AUTOMATIC: + xmlAccess::addXmlElement(name, std::string("Automatic"), parent); + break; + case SyncConfig::MIRROR: + xmlAccess::addXmlElement(name, std::string("Mirror"), parent); + break; + case SyncConfig::UPDATE: + xmlAccess::addXmlElement(name, std::string("Update"), parent); + break; + case SyncConfig::CUSTOM: + xmlAccess::addXmlElement(name, std::string("Custom"), parent); + break; + } +} + + void addXmlElement(const std::string& name, const Zstring& value, TiXmlElement* parent) { xmlAccess::addXmlElement(name, wxString(zToWx(value)), parent); @@ -652,6 +768,22 @@ void addXmlAttribute(const std::string& name, const xmlAccess::ColumnTypes value } +void writeFilter(const FilterConfig& input, TiXmlElement& parent) +{ + addXmlElement("Include", input.includeFilter, &parent); + addXmlElement("Exclude", input.excludeFilter, &parent); + + addXmlElement("TimeSpan", input.timeSpan, &parent); + addXmlElement("UnitTimeSpan", input.unitTimeSpan, &parent); + + addXmlElement("SizeMin", input.sizeMin, &parent); + addXmlElement("UnitSizeMin", input.unitSizeMin, &parent); + + addXmlElement("SizeMax", input.sizeMax, &parent); + addXmlElement("UnitSizeMax", input.unitSizeMax, &parent); +} + + void writeXmlLocalConfig(const FolderPairEnh& enhPair, TiXmlElement& parent) { //write folder pairs @@ -666,32 +798,25 @@ void writeXmlLocalConfig(const FolderPairEnh& enhPair, TiXmlElement& parent) const AlternateSyncConfig* altSyncConfig = enhPair.altSyncConfig.get(); if (altSyncConfig) { - TiXmlElement* syncCfg = new TiXmlElement("AlternateSyncConfig"); - newfolderPair->LinkEndChild(syncCfg); - - TiXmlElement* syncSettings = new TiXmlElement("Synchronization"); - syncCfg->LinkEndChild(syncSettings); + TiXmlElement* xmlAltSyncCfg = new TiXmlElement("AlternateSyncConfig"); + newfolderPair->LinkEndChild(xmlAltSyncCfg); //write sync configuration - addXmlElement("Automatic", altSyncConfig->syncConfiguration.automatic, syncSettings); - - TiXmlElement* syncDirections = new TiXmlElement("Directions"); - syncSettings->LinkEndChild(syncDirections); + addXmlElement("Variant", altSyncConfig->syncConfiguration.var, xmlAltSyncCfg); - addXmlElement("LeftOnly", altSyncConfig->syncConfiguration.exLeftSideOnly, syncDirections); - addXmlElement("RightOnly", altSyncConfig->syncConfiguration.exRightSideOnly, syncDirections); - addXmlElement("LeftNewer", altSyncConfig->syncConfiguration.leftNewer, syncDirections); - addXmlElement("RightNewer", altSyncConfig->syncConfiguration.rightNewer, syncDirections); - addXmlElement("Different", altSyncConfig->syncConfiguration.different, syncDirections); - addXmlElement("Conflict", altSyncConfig->syncConfiguration.conflict, syncDirections); + TiXmlElement* syncDirections = new TiXmlElement("CustomDirections"); + xmlAltSyncCfg->LinkEndChild(syncDirections); - - TiXmlElement* miscSettings = new TiXmlElement("Miscellaneous"); - syncCfg->LinkEndChild(miscSettings); + addXmlElement("LeftOnly", altSyncConfig->syncConfiguration.custom.exLeftSideOnly, syncDirections); + addXmlElement("RightOnly", altSyncConfig->syncConfiguration.custom.exRightSideOnly, syncDirections); + addXmlElement("LeftNewer", altSyncConfig->syncConfiguration.custom.leftNewer, syncDirections); + addXmlElement("RightNewer", altSyncConfig->syncConfiguration.custom.rightNewer, syncDirections); + addXmlElement("Different", altSyncConfig->syncConfiguration.custom.different, syncDirections); + addXmlElement("Conflict", altSyncConfig->syncConfiguration.custom.conflict, syncDirections); //misc - addXmlElement("DeletionPolicy", altSyncConfig->handleDeletion, miscSettings); - addXmlElement("CustomDeletionFolder", altSyncConfig->customDeletionDirectory, miscSettings); + addXmlElement("DeletionPolicy", altSyncConfig->handleDeletion, xmlAltSyncCfg); + addXmlElement("CustomDeletionFolder", altSyncConfig->customDeletionDirectory, xmlAltSyncCfg); } //########################################################### @@ -700,22 +825,18 @@ void writeXmlLocalConfig(const FolderPairEnh& enhPair, TiXmlElement& parent) newfolderPair->LinkEndChild(filterCfg); //write filter settings - addXmlElement("Include", enhPair.localFilter.includeFilter, filterCfg); - addXmlElement("Exclude", enhPair.localFilter.excludeFilter, filterCfg); + writeFilter(enhPair.localFilter, *filterCfg); } -bool writeXmlMainConfig(const MainConfiguration& mainCfg, TiXmlDocument& doc) +void writeConfig(const MainConfiguration& mainCfg, TiXmlElement& root) { - TiXmlElement* root = doc.RootElement(); - if (!root) return false; - - TiXmlElement* settings = new TiXmlElement("MainConfig"); - root->LinkEndChild(settings); + TiXmlElement* xmlMainCfg = new TiXmlElement("MainConfig"); + root.LinkEndChild(xmlMainCfg); //########################################################### TiXmlElement* cmpSettings = new TiXmlElement("Comparison"); - settings->LinkEndChild(cmpSettings); + xmlMainCfg->LinkEndChild(cmpSettings); //write compare algorithm addXmlElement("Variant", mainCfg.compareVar, cmpSettings); @@ -724,40 +845,36 @@ bool writeXmlMainConfig(const MainConfiguration& mainCfg, TiXmlDocument& doc) addXmlElement("HandleSymlinks", mainCfg.handleSymlinks, cmpSettings); //########################################################### - TiXmlElement* syncSettings = new TiXmlElement("Synchronization"); - settings->LinkEndChild(syncSettings); + TiXmlElement* xmlSyncCfg = new TiXmlElement("SyncConfig"); + xmlMainCfg->LinkEndChild(xmlSyncCfg); //write sync configuration - addXmlElement("Automatic", mainCfg.syncConfiguration.automatic, syncSettings); + addXmlElement("Variant", mainCfg.syncConfiguration.var, xmlSyncCfg); - TiXmlElement* syncDirections = new TiXmlElement("Directions"); - syncSettings->LinkEndChild(syncDirections); + TiXmlElement* syncDirections = new TiXmlElement("CustomDirections"); + xmlSyncCfg->LinkEndChild(syncDirections); - addXmlElement("LeftOnly", mainCfg.syncConfiguration.exLeftSideOnly, syncDirections); - addXmlElement("RightOnly", mainCfg.syncConfiguration.exRightSideOnly, syncDirections); - addXmlElement("LeftNewer", mainCfg.syncConfiguration.leftNewer, syncDirections); - addXmlElement("RightNewer", mainCfg.syncConfiguration.rightNewer, syncDirections); - addXmlElement("Different", mainCfg.syncConfiguration.different, syncDirections); - addXmlElement("Conflict", mainCfg.syncConfiguration.conflict, syncDirections); + addXmlElement("LeftOnly", mainCfg.syncConfiguration.custom.exLeftSideOnly, syncDirections); + addXmlElement("RightOnly", mainCfg.syncConfiguration.custom.exRightSideOnly, syncDirections); + addXmlElement("LeftNewer", mainCfg.syncConfiguration.custom.leftNewer, syncDirections); + addXmlElement("RightNewer", mainCfg.syncConfiguration.custom.rightNewer, syncDirections); + addXmlElement("Different", mainCfg.syncConfiguration.custom.different, syncDirections); + addXmlElement("Conflict", mainCfg.syncConfiguration.custom.conflict, syncDirections); //########################################################### - TiXmlElement* miscSettings = new TiXmlElement("Miscellaneous"); - settings->LinkEndChild(miscSettings); - //write filter settings - TiXmlElement* filter = new TiXmlElement("Filter"); - miscSettings->LinkEndChild(filter); + TiXmlElement* filter = new TiXmlElement("GlobalFilter"); + xmlMainCfg->LinkEndChild(filter); - addXmlElement("Include", mainCfg.globalFilter.includeFilter, filter); - addXmlElement("Exclude", mainCfg.globalFilter.excludeFilter, filter); + writeFilter(mainCfg.globalFilter, *filter); //other - addXmlElement("DeletionPolicy", mainCfg.handleDeletion, miscSettings); - addXmlElement("CustomDeletionFolder", mainCfg.customDeletionDirectory, miscSettings); + addXmlElement("DeletionPolicy", mainCfg.handleDeletion, xmlSyncCfg); + addXmlElement("CustomDeletionFolder", mainCfg.customDeletionDirectory, xmlSyncCfg); //########################################################### TiXmlElement* pairs = new TiXmlElement("FolderPairs"); - settings->LinkEndChild(pairs); + xmlMainCfg->LinkEndChild(pairs); //write first folder pair writeXmlLocalConfig(mainCfg.firstPair, *pairs); @@ -765,65 +882,45 @@ bool writeXmlMainConfig(const MainConfiguration& mainCfg, TiXmlDocument& doc) //write additional folder pairs for (std::vector<FolderPairEnh>::const_iterator i = mainCfg.additionalPairs.begin(); i != mainCfg.additionalPairs.end(); ++i) writeXmlLocalConfig(*i, *pairs); - - return true; } -bool writeXmlGuiConfig(const xmlAccess::XmlGuiConfig& inputCfg, TiXmlDocument& doc) +void writeConfig(const xmlAccess::XmlGuiConfig& inputCfg, TiXmlElement& root) { //write main config - if (!writeXmlMainConfig(inputCfg.mainCfg, doc)) - return false; + writeConfig(inputCfg.mainCfg, root); //write GUI specific config - TiXmlElement* root = doc.RootElement(); - if (!root) return false; - TiXmlElement* guiConfig = new TiXmlElement("GuiConfig"); - root->LinkEndChild(guiConfig); + root.LinkEndChild(guiConfig); addXmlElement("HideFiltered", inputCfg.hideFilteredElements, guiConfig); - - addXmlElement("HandleError", inputCfg.ignoreErrors ? xmlAccess::ON_ERROR_IGNORE : xmlAccess::ON_ERROR_POPUP, guiConfig); - + addXmlElement("HandleError", inputCfg.handleError, guiConfig); addXmlElement("SyncPreviewActive", inputCfg.syncPreviewEnabled, guiConfig); - - return true; } -bool writeXmlBatchConfig(const xmlAccess::XmlBatchConfig& inputCfg, TiXmlDocument& doc) +void writeConfig(const xmlAccess::XmlBatchConfig& inputCfg, TiXmlElement& root) { //write main config - if (!writeXmlMainConfig(inputCfg.mainCfg, doc)) - return false; + writeConfig(inputCfg.mainCfg, root); //write GUI specific config - TiXmlElement* root = doc.RootElement(); - if (!root) return false; - TiXmlElement* batchConfig = new TiXmlElement("BatchConfig"); - root->LinkEndChild(batchConfig); + root.LinkEndChild(batchConfig); addXmlElement("Silent", inputCfg.silent, batchConfig); addXmlElement("LogfileDirectory", inputCfg.logFileDirectory, batchConfig); addXmlElement("LogfileCountMax", inputCfg.logFileCountMax, batchConfig); addXmlElement("HandleError", inputCfg.handleError, batchConfig); - - return true; } -bool writeXmlGlobalSettings(const xmlAccess::XmlGlobalSettings& inputCfg, TiXmlDocument& doc) +void writeConfig(const xmlAccess::XmlGlobalSettings& inputCfg, TiXmlElement& root) { - TiXmlElement* root = doc.RootElement(); - if (!root) return false; - - //################################################################### //write global settings TiXmlElement* global = new TiXmlElement("Shared"); - root->LinkEndChild(global); + root.LinkEndChild(global); //program language addXmlElement("Language", inputCfg.programLanguage, global); @@ -870,7 +967,7 @@ bool writeXmlGlobalSettings(const xmlAccess::XmlGlobalSettings& inputCfg, TiXmlD //################################################################### //write global gui settings TiXmlElement* gui = new TiXmlElement("Gui"); - root->LinkEndChild(gui); + root.LinkEndChild(gui); TiXmlElement* windows = new TiXmlElement("Windows"); gui->LinkEndChild(windows); @@ -934,17 +1031,9 @@ bool writeXmlGlobalSettings(const xmlAccess::XmlGlobalSettings& inputCfg, TiXmlD addXmlAttribute("Width", colAttrib.width, subElement); } - //write folder history elements - TiXmlElement* historyLeft = new TiXmlElement("FolderHistoryLeft"); - mainWindow->LinkEndChild(historyLeft); - TiXmlElement* historyRight = new TiXmlElement("FolderHistoryRight"); - mainWindow->LinkEndChild(historyRight); - - addXmlAttribute("MaximumSize", inputCfg.gui.folderHistLeftMax, historyLeft); - addXmlAttribute("MaximumSize", inputCfg.gui.folderHistRightMax, historyRight); - - addXmlElement("Folder", inputCfg.gui.folderHistoryLeft, historyLeft); - addXmlElement("Folder", inputCfg.gui.folderHistoryRight, historyRight); + addXmlElement("FolderHistoryLeft", inputCfg.gui.folderHistoryLeft , mainWindow); + addXmlElement("FolderHistoryRight", inputCfg.gui.folderHistoryRight, mainWindow); + addXmlElement("MaximumHistorySize", inputCfg.gui.folderHistMax , mainWindow); addXmlElement("Perspective", inputCfg.gui.guiPerspectiveLast, mainWindow); @@ -975,15 +1064,13 @@ bool writeXmlGlobalSettings(const xmlAccess::XmlGlobalSettings& inputCfg, TiXmlD //write global batch settings TiXmlElement* batch = new TiXmlElement("Batch"); - root->LinkEndChild(batch); - - return true; + root.LinkEndChild(batch); } wxString xmlAccess::getGlobalConfigFile() { - return ffs3::getConfigDir() + wxT("GlobalSettings.xml"); + return zen::getConfigDir() + wxT("GlobalSettings.xml"); } @@ -1013,10 +1100,15 @@ xmlAccess::XmlBatchConfig xmlAccess::convertGuiToBatch(const xmlAccess::XmlGuiCo XmlBatchConfig output; output.mainCfg = guiCfg.mainCfg; - if (guiCfg.ignoreErrors) - output.handleError = xmlAccess::ON_ERROR_IGNORE; - else - output.handleError = xmlAccess::ON_ERROR_POPUP; + switch (guiCfg.handleError) + { + case ON_GUIERROR_POPUP: + output.handleError = xmlAccess::ON_ERROR_POPUP; + break; + case ON_GUIERROR_IGNORE: + output.handleError = xmlAccess::ON_ERROR_IGNORE; + break; + } return output; } @@ -1031,17 +1123,16 @@ xmlAccess::MergeType xmlAccess::getMergeType(const std::vector<wxString>& filena { switch (xmlAccess::getXmlType(*i)) //throw() { - case XML_GUI_CONFIG: + case XML_TYPE_GUI: guiCfgExists = true; break; - case XML_BATCH_CONFIG: + case XML_TYPE_BATCH: batchCfgExists = true; break; - case XML_GLOBAL_SETTINGS: - case XML_REAL_CONFIG: - case XML_OTHER: + case XML_TYPE_GLOBAL: + case XML_TYPE_OTHER: return MERGE_OTHER; } } @@ -1087,24 +1178,23 @@ void mergeConfigFilesImpl(const std::vector<wxString>& filenames, XmlCfg& config if (filenames.empty()) return; - std::vector<ffs3::MainConfiguration> mainCfgs; + std::vector<zen::MainConfiguration> mainCfgs; std::auto_ptr<XmlError> savedException; for (std::vector<wxString>::const_iterator i = filenames.begin(); i != filenames.end(); ++i) { switch (getXmlType(*i)) { - case XML_GUI_CONFIG: + case XML_TYPE_GUI: mainCfgs.push_back(loadCfgImpl<XmlGuiConfig>(*i, savedException).mainCfg); //throw (xmlAccess::XmlError) break; - case XML_BATCH_CONFIG: + case XML_TYPE_BATCH: mainCfgs.push_back(loadCfgImpl<XmlBatchConfig>(*i, savedException).mainCfg); //throw (xmlAccess::XmlError) break; - case XML_GLOBAL_SETTINGS: - case XML_REAL_CONFIG: - case XML_OTHER: + case XML_TYPE_GLOBAL: + case XML_TYPE_OTHER: break; } } @@ -1136,3 +1226,113 @@ void xmlAccess::convertConfig(const std::vector<wxString>& filenames, XmlBatchCo { mergeConfigFilesImpl(filenames, config); //throw (xmlAccess::XmlError) } + + + + + + + +void xmlAccess::readConfig(const wxString& filename, xmlAccess::XmlGuiConfig& config) +{ + //load XML + if (!fileExists(wxToZ(filename))) + throw XmlError(wxString(_("File does not exist:")) + wxT("\n\"") + filename + wxT("\"")); + + TiXmlDocument doc; + loadXmlDocument(filename, doc); //throw (XmlError) + + if (getXmlType(doc) != XML_TYPE_GUI) //throw() + throw XmlError(wxString(_("Error parsing configuration file:")) + wxT("\n\"") + filename + wxT("\"")); + + FfsXmlErrorLogger logger; + logger.readConfig(doc.RootElement(), config); //read GUI layout configuration + + if (logger.errorsOccurred()) + throw XmlError(wxString(_("Error parsing configuration file:")) + wxT("\n\"") + filename + wxT("\"\n\n") + + logger.getErrorMessageFormatted(), XmlError::WARNING); +} + + +void xmlAccess::readConfig(const wxString& filename, xmlAccess::XmlBatchConfig& config) +{ + //load XML + if (!fileExists(wxToZ(filename))) + throw XmlError(wxString(_("File does not exist:")) + wxT("\n\"") + filename + wxT("\"")); + + TiXmlDocument doc; + loadXmlDocument(filename, doc); //throw (XmlError) + + if (getXmlType(doc) != XML_TYPE_BATCH) //throw() + throw XmlError(wxString(_("Error parsing configuration file:")) + wxT("\n\"") + filename + wxT("\"")); + + FfsXmlErrorLogger logger; + logger.readConfig(doc.RootElement(), config); //read GUI layout configuration + + if (logger.errorsOccurred()) + throw XmlError(wxString(_("Error parsing configuration file:")) + wxT("\n\"") + filename + wxT("\"\n\n") + + logger.getErrorMessageFormatted(), XmlError::WARNING); +} + + +void xmlAccess::readConfig(xmlAccess::XmlGlobalSettings& config) +{ + //load XML + if (!fileExists(wxToZ(getGlobalConfigFile()))) + throw XmlError(wxString(_("File does not exist:")) + wxT("\n\"") + getGlobalConfigFile() + wxT("\"")); + + TiXmlDocument doc; + loadXmlDocument(getGlobalConfigFile(), doc); //throw (XmlError) + + if (getXmlType(doc) != XML_TYPE_GLOBAL) //throw() + throw XmlError(wxString(_("Error parsing configuration file:")) + wxT("\n\"") + getGlobalConfigFile() + wxT("\"")); + + FfsXmlErrorLogger logger; + logger.readConfig(doc.RootElement(), config); //read GUI layout configuration + + if (logger.errorsOccurred()) + throw XmlError(wxString(_("Error parsing configuration file:")) + wxT("\n\"") + getGlobalConfigFile() + wxT("\"\n\n") + + logger.getErrorMessageFormatted(), XmlError::WARNING); +} + + +void xmlAccess::writeConfig(const XmlGuiConfig& outputCfg, const wxString& filename) +{ + TiXmlDocument doc; + initXmlDocument(doc, XML_TYPE_GUI); //throw() + + if (!doc.RootElement()) + throw XmlError(wxString(_("Error writing file:")) + wxT("\n\"") + filename + wxT("\"")); + + writeConfig(outputCfg, *doc.RootElement()); //add GUI layout configuration settings + + saveXmlDocument(filename, doc); //throw (XmlError) +} + + +void xmlAccess::writeConfig(const XmlBatchConfig& outputCfg, const wxString& filename) +{ + TiXmlDocument doc; + initXmlDocument(doc, XML_TYPE_BATCH); //throw() + + if (!doc.RootElement()) + throw XmlError(wxString(_("Error writing file:")) + wxT("\n\"") + filename + wxT("\"")); + + writeConfig(outputCfg, *doc.RootElement()); //add GUI layout configuration settings + + saveXmlDocument(filename, doc); //throw (XmlError) +} + + +void xmlAccess::writeConfig(const XmlGlobalSettings& outputCfg) +{ + TiXmlDocument doc; + initXmlDocument(doc, XML_TYPE_GLOBAL); //throw() + + if (!doc.RootElement()) + throw XmlError(wxString(_("Error writing file:")) + wxT("\n\"") + getGlobalConfigFile() + wxT("\"")); + + writeConfig(outputCfg, *doc.RootElement()); //add GUI layout configuration settings + + saveXmlDocument(getGlobalConfigFile(), doc); //throw (XmlError) +} diff --git a/library/process_xml.h b/library/process_xml.h index d627fc18..de566d15 100644 --- a/library/process_xml.h +++ b/library/process_xml.h @@ -7,13 +7,27 @@ #ifndef PROCESSXML_H_INCLUDED #define PROCESSXML_H_INCLUDED +#include "../shared/xml_base.h" #include "../structures.h" #include "../shared/xml_error.h" -#include "../shared/i18n.h" +#include "../shared/localization.h" namespace xmlAccess { +enum XmlType +{ + XML_TYPE_GUI, + XML_TYPE_BATCH, + XML_TYPE_GLOBAL, + XML_TYPE_OTHER +}; + +XmlType getXmlType(const TiXmlDocument& doc); //throw() +XmlType getXmlType(const wxString& filename); //throw() +void initXmlDocument(TiXmlDocument& doc, XmlType type); //throw() + + enum OnError { ON_ERROR_POPUP, @@ -21,6 +35,12 @@ enum OnError ON_ERROR_EXIT }; +enum OnGuiError +{ + ON_GUIERROR_POPUP, + ON_GUIERROR_IGNORE +}; + enum ColumnTypes { DIRECTORY, //this needs to begin with 0 and be continuous (some code relies on it) @@ -52,20 +72,21 @@ struct XmlGuiConfig { XmlGuiConfig() : hideFilteredElements(false), - ignoreErrors(false), + handleError(ON_GUIERROR_POPUP), syncPreviewEnabled(true) {} //initialize values - ffs3::MainConfiguration mainCfg; + zen::MainConfiguration mainCfg; bool hideFilteredElements; - bool ignoreErrors; //reaction on error situation during synchronization + + OnGuiError handleError; //reaction on error situation during synchronization bool syncPreviewEnabled; bool operator==(const XmlGuiConfig& other) const { return mainCfg == other.mainCfg && hideFilteredElements == other.hideFilteredElements && - ignoreErrors == other.ignoreErrors && + handleError == other.handleError && syncPreviewEnabled == other.syncPreviewEnabled; } @@ -83,7 +104,7 @@ struct XmlBatchConfig logFileCountMax(200), handleError(ON_ERROR_POPUP) {} - ffs3::MainConfiguration mainCfg; + zen::MainConfiguration mainCfg; bool silent; wxString logFileDirectory; @@ -119,7 +140,7 @@ struct XmlGlobalSettings //--------------------------------------------------------------------- //Shared (GUI/BATCH) settings XmlGlobalSettings() : - programLanguage(ffs3::retrieveSystemLanguage()), + programLanguage(zen::retrieveSystemLanguage()), copyLockedFiles(true), copyFilePermissions(false), fileTimeTolerance(2), //default 2s: FAT vs NTFS @@ -145,8 +166,7 @@ struct XmlGlobalSettings isMaximized(false), autoAdjustColumnsLeft(false), autoAdjustColumnsRight(false), - folderHistLeftMax(12), - folderHistRightMax(12), + folderHistMax(12), deleteOnBothSides(false), useRecyclerForManualDeletion(true), //enable if OS supports it; else user will have to activate first and then get an error message #ifdef FFS_WIN @@ -164,7 +184,7 @@ struct XmlGlobalSettings externelApplications.push_back(std::make_pair(wxT("Show in Explorer"), //mark for extraction: _("Show in Explorer") wxT("explorer /select, \"%name\""))); externelApplications.push_back(std::make_pair(wxT("Open with default application"), //mark for extraction: _("Open with default application") - wxT("cmd /c start \"\" \"%name\""))); + wxT("\"%name\""))); #elif defined FFS_LINUX externelApplications.push_back(std::make_pair(wxT("Browse directory"), //mark for extraction: _("Browse directory") wxT("xdg-open \"%dir\""))); @@ -191,10 +211,8 @@ struct XmlGlobalSettings wxString lastUsedConfigFile; std::vector<wxString> folderHistoryLeft; - unsigned int folderHistLeftMax; - std::vector<wxString> folderHistoryRight; - unsigned int folderHistRightMax; + unsigned int folderHistMax; bool deleteOnBothSides; bool useRecyclerForManualDeletion; diff --git a/library/resources.cpp b/library/resources.cpp index d07e66a8..abdfdf51 100644 --- a/library/resources.cpp +++ b/library/resources.cpp @@ -15,7 +15,7 @@ #include <memory> #include "../shared/standard_paths.h" -using namespace ffs3; +using namespace zen; const GlobalResources& GlobalResources::instance() @@ -69,7 +69,7 @@ void loadAnimFromZip(wxZipInputStream& zipInput, wxAnimation* animation) void GlobalResources::load() { - wxFFileInputStream input(ffs3::getResourceDir() + wxT("Resources.dat")); + wxFFileInputStream input(zen::getResourceDir() + wxT("Resources.dat")); if (input.IsOk()) //if not... we don't want to react too harsh here { //activate support for .png files diff --git a/library/soft_filter.cpp b/library/soft_filter.cpp deleted file mode 100644 index 1c191b98..00000000 --- a/library/soft_filter.cpp +++ /dev/null @@ -1,10 +0,0 @@ -// ************************************************************************** -// * This file is part of the FreeFileSync project. It is distributed under * -// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * -// * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * -// ************************************************************************** -// -#include "soft_filter.h" - - - diff --git a/library/soft_filter.h b/library/soft_filter.h index 4e6732a3..82ba0ba2 100644 --- a/library/soft_filter.h +++ b/library/soft_filter.h @@ -4,47 +4,53 @@ // * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * // ************************************************************************** // -#ifndef SOFTFILTER_H_INCLUDED -#define SOFTFILTER_H_INCLUDED +#ifndef SOFT_FILTER_H_INCLUDED +#define SOFT_FILTER_H_INCLUDED -#include "../file_hierarchy.h" -#include <wx/timer.h> +#include <algorithm> +#include <limits> +#include "../structures.h" +#include <wx/stopwatch.h> + +namespace zen +{ /* Semantics of SoftFilter: 1. It potentially may match only one side => it MUST NOT be applied while traversing a single folder to avoid mismatches 2. => it is applied after traversing and just marks rows, (NO deletions after comparison are allowed) -3. => not relevant for <Automatic>-mode! ;) - --> SoftFilter is equivalent to a user temporarily (de-)selecting rows +3. => equivalent to a user temporarily (de-)selecting rows -> not relevant for <Automatic>-mode! ;) */ -namespace ffs3 -{ - class SoftFilter { public: - SoftFilter(size_t timeWindow) : - timeWindow_(timeWindow), - currentTime(wxGetUTCTime()) {} + SoftFilter(size_t timeSpan, UnitTime unitTimeSpan, + size_t sizeMin, UnitSize unitSizeMin, + size_t sizeMax, UnitSize unitSizeMax); - // typedef boost::shared_ptr<const SoftFilter> FilterRef; //always bound by design! + bool matchTime(zen::Int64 writeTime) const { return currentTime - writeTime <= timeSpan_; } + bool matchSize(zen::UInt64 fileSize) const { return sizeMin_ <= fileSize && fileSize <= sizeMax_; } + bool isNull() const; //filter is equivalent to NullFilter, but may be technically slower - bool passFilter(const FileMapping& fileMap) const; - bool passFilter(const DirMapping& dirMap) const; + //small helper method: merge two soft filters + friend SoftFilter combineFilters(const SoftFilter& first, const SoftFilter& second); private: - const size_t timeWindow_; //point in time from "now" (in seconds) for oldest modification date to be allowed - const long currentTime; //number of seconds since GMT 00:00:00 Jan 1st 1970. + 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::FilterRef combineFilters(const SoftFilter& first, -// const SoftFilter& second); -// -// -// -// @@ -62,22 +68,54 @@ private: -//---------------Inline Implementation--------------------------------------------------- + + + + + + + +// ----------------------- implementation ----------------------- +namespace zen +{ inline -bool SoftFilter::passFilter(const FileMapping& fileMap) const +SoftFilter::SoftFilter(size_t timeSpan, UnitTime unitTimeSpan, + size_t sizeMin, UnitSize unitSizeMin, + size_t sizeMax, UnitSize unitSizeMax) : + currentTime(wxGetUTCTime()) { - return (!fileMap.isEmpty<LEFT_SIDE>() && - currentTime <= fileMap.getLastWriteTime<LEFT_SIDE>() + timeWindow_) || - (!fileMap.isEmpty<RIGHT_SIDE>() && - currentTime <= fileMap.getLastWriteTime<RIGHT_SIDE>() + timeWindow_); + zen::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) : + timeSpan_(timeSpan), + sizeMin_ (sizeMin), + sizeMax_ (sizeMax), + currentTime(wxGetUTCTime()) {} + +inline +SoftFilter combineFilters(const SoftFilter& first, const SoftFilter& second) +{ + return SoftFilter(std::min(first.timeSpan_, second.timeSpan_), + std::max(first.sizeMin_, second.sizeMin_), + std::min(first.sizeMax_, second.sizeMax_)); +} inline -bool SoftFilter::passFilter(const DirMapping& dirMap) const +bool SoftFilter::isNull() const //filter is equivalent to NullFilter, but may be technically slower { - return false; + return timeSpan_ == std::numeric_limits<zen::Int64>::max() && + sizeMin_ == 0U && + sizeMax_ == std::numeric_limits<zen::UInt64>::max(); } } -#endif // SOFTFILTER_H_INCLUDED +#endif // SOFT_FILTER_H_INCLUDED diff --git a/library/statistics.cpp b/library/statistics.cpp index d5cce670..c2ba8c0c 100644 --- a/library/statistics.cpp +++ b/library/statistics.cpp @@ -16,6 +16,8 @@ #include "../shared/assert_static.h" +using namespace zen; + RetrieveStatistics::RetrieveStatistics() : timer(new wxStopWatch) {} @@ -31,12 +33,11 @@ RetrieveStatistics::~RetrieveStatistics() for (std::vector<StatEntry>::const_iterator i = data.begin(); i != data.end(); ++i) { - using common::numberToString; - outputFile.Write(numberToString(i->time)); + outputFile.Write(toString<wxString>(i->time)); outputFile.Write(wxT(";")); - outputFile.Write(numberToString(i->objects)); + outputFile.Write(toString<wxString>(i->objects)); outputFile.Write(wxT(";")); - outputFile.Write(numberToString(i->value)); + outputFile.Write(toString<wxString>(i->value)); outputFile.Write(wxT("\n")); } } @@ -64,26 +65,34 @@ bool isNull(T number) } +enum UnitRemTime +{ + URT_SEC, + URT_MIN, + URT_HOUR, + URT_DAY +}; + + inline wxString Statistics::formatRemainingTime(double timeInMs) const { - bool unitSec = true; double remainingTime = timeInMs / 1000; - wxString output = _("%x sec"); + //determine preferred unit + UnitRemTime unit = URT_SEC; if (remainingTime > 55) { - unitSec = false; + unit = URT_MIN; remainingTime /= 60; - output = _("%x min"); if (remainingTime > 59) { + unit = URT_HOUR; remainingTime /= 60; - output = _("%x hour(s)"); if (remainingTime > 23) { + unit = URT_DAY; remainingTime /= 24; - output = _("%x day(s)"); } } } @@ -91,14 +100,14 @@ wxString Statistics::formatRemainingTime(double timeInMs) const int formattedTime = common::round(remainingTime); //reduce precision to 5 seconds - if (unitSec && formattedTime % 5 != 0) + if (unit == URT_SEC && formattedTime % 5 != 0) formattedTime += 5 - formattedTime % 5; //"ceiling" //avoid "jumping back and forth" when fluctuating around .5 if (remainingTimeLast < formattedTime) { - if (unitSec) + if (unit == URT_SEC) { formattedTime = common::round(remainingTime); formattedTime -= formattedTime % 5; //"floor" @@ -108,8 +117,24 @@ wxString Statistics::formatRemainingTime(double timeInMs) const } remainingTimeLast = formattedTime; - output.Replace(wxT("%x"), common::numberToString(formattedTime)); - + //generate output message + wxString output; + switch (unit) + { + case URT_SEC: + output = _P("1 sec", "%x sec", formattedTime); + break; + case URT_MIN: + output = _P("1 min", "%x min", formattedTime); + break; + case URT_HOUR: + output = _P("1 hour", "%x hours", formattedTime); + break; + case URT_DAY: + output = _P("1 day", "%x days", formattedTime); + break; + } + output.Replace(wxT("%x"), zen::toStringSep(formattedTime)); return output; //+ wxT("(") + common::numberToWxString(common::round(timeInMs / 1000)) + wxT(")"); } @@ -144,10 +169,10 @@ void Statistics::addMeasurement(int objectsCurrent, double dataCurrent) //insert new record if (!measurements.empty()) - { - //assert(dataCurrent >= (--measurements.end())->second.data); - measurements.insert(--measurements.end(), newEntry); //use fact that time is monotonously ascending - } + { + //assert(dataCurrent >= (--measurements.end())->second.data); + measurements.insert(--measurements.end(), newEntry); //use fact that time is monotonously ascending + } else measurements.insert(newEntry); @@ -202,7 +227,7 @@ wxString Statistics::getBytesPerSecond() const const double dataDelta = backRecord.second.data - frontRecord.second.data; if (!isNull(timeDelta)) - return ffs3::formatFilesizeToShortString(dataDelta * 1000 / timeDelta) + _("/sec"); + return zen::formatFilesizeToShortString(zen::UInt64(dataDelta * 1000 / timeDelta)) + _("/sec"); } return wxT("-"); //fallback @@ -273,7 +298,7 @@ wxString Statistics::getRemainingTime(const int objectsCurrent, const double dat const double X = dataCurrent - dataLast; dataLast = dataCurrent; - const wxLongLong timeCurrent = wxGetLocalTimeMillis(); + const zen::Int64 timeCurrent = wxGetLocalTimeMillis(); const double F = (timeCurrent - timeLast).ToDouble(); timeLast = timeCurrent; @@ -298,7 +323,7 @@ wxString Statistics::getRemainingTime(const int objectsCurrent, const double dat const double X = dataCurrent - dataLast; //do not set dataLast, timeLast variables here, but write dummy record instead if (!isNull(X)) { - const wxLongLong timeCurrent = wxGetLocalTimeMillis(); + const zen::Int64 timeCurrent = wxGetLocalTimeMillis(); const double F = (timeCurrent - timeLast).ToDouble(); record modifyEntry; diff --git a/library/status_handler.cpp b/library/status_handler.cpp index e81746a4..0a131899 100644 --- a/library/status_handler.cpp +++ b/library/status_handler.cpp @@ -21,8 +21,8 @@ void updateUiNow() bool updateUiIsAllowed() { - static wxLongLong lastExec = 0; - const wxLongLong newExec = wxGetLocalTimeMillis(); + static wxMilliClock_t lastExec = 0; + const wxMilliClock_t newExec = wxGetLocalTimeMillis(); if (newExec - lastExec >= UI_UPDATE_INTERVAL) //perform ui updates not more often than necessary { diff --git a/library/status_handler.h b/library/status_handler.h index 2dde4fd9..c3a016d5 100644 --- a/library/status_handler.h +++ b/library/status_handler.h @@ -7,9 +7,9 @@ #ifndef STATUSHANDLER_H_INCLUDED #define STATUSHANDLER_H_INCLUDED -#include <wx/longlong.h> +#include <wx/string.h> #include "../shared/zstring.h" - +#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 @@ -35,11 +35,10 @@ public: }; -class StatusHandler +//report status during comparison and synchronization +struct ProcessCallback { -public: - StatusHandler() : abortRequested(false) {} - virtual ~StatusHandler() {} + virtual ~ProcessCallback() {} //identifiers of different phases enum Process @@ -51,54 +50,56 @@ 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 updateProcessedData(int objectsProcessed, wxLongLong dataProcessed) = 0; //called periodically after data was processed + 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; //this method is triggered repeatedly by requestUiRefresh() and can be used to refresh the ui by dispatching pending events virtual void forceUiRefresh() = 0; - void requestUiRefresh(bool allowAbort = true); //opportunity to abort must be implemented in a frequently executed method like requestUiRefresh() - void requestAbortion(); //this does NOT call abortThisProcess immediately, but when appropriate (e.g. async. processes finished) - bool abortIsRequested(); + virtual void requestUiRefresh(bool allowExceptions = true) = 0; //opportunity to abort must be implemented in a frequently executed method like requestUiRefresh() + + virtual bool abortIsRequested() = 0; //thanks to Windows C-Api not supporting exceptions we need this one... //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; + virtual void reportWarning (const wxString& warningMessage, bool& warningActive) = 0; +}; -private: - virtual void abortThisProcess() = 0; - bool abortRequested; +//gui may want to abort process +struct AbortCallback +{ + virtual ~AbortCallback() {} + virtual void requestAbortion() = 0; }; - -//############################################################################## -inline -void StatusHandler::requestUiRefresh(bool allowAbort) +//actual callback implementation will have to satisfy "process" and "gui" +class StatusHandler : public ProcessCallback, public AbortCallback { - if (updateUiIsAllowed()) //test if specific time span between ui updates is over - forceUiRefresh(); +public: + StatusHandler() : abortRequested(false) {} - if (abortRequested && allowAbort) - abortThisProcess(); //abort can be triggered by requestAbortion() -} + virtual void requestUiRefresh(bool allowExceptions) + { + if (updateUiIsAllowed()) //test if specific time span between ui updates is over + forceUiRefresh(); + if (abortRequested && allowExceptions) + abortThisProcess(); //abort can be triggered by requestAbortion() + } -inline -void StatusHandler::requestAbortion() -{ - abortRequested = true; -} + 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; + +private: + bool abortRequested; +}; -inline -bool StatusHandler::abortIsRequested() -{ - return abortRequested; -} #endif // STATUSHANDLER_H_INCLUDED |