diff options
Diffstat (limited to 'library')
-rw-r--r-- | library/CustomGrid.cpp | 314 | ||||
-rw-r--r-- | library/CustomGrid.h | 10 | ||||
-rw-r--r-- | library/FreeFileSync.ico | bin | 94198 -> 82726 bytes | |||
-rw-r--r-- | library/dbFile.cpp | 59 | ||||
-rw-r--r-- | library/dbFile.h | 6 | ||||
-rw-r--r-- | library/filter.cpp | 3 | ||||
-rw-r--r-- | library/iconBuffer.cpp | 346 | ||||
-rw-r--r-- | library/iconBuffer.h | 25 | ||||
-rw-r--r-- | library/multithreading.cpp | 254 | ||||
-rw-r--r-- | library/multithreading.h | 41 | ||||
-rw-r--r-- | library/pch.h | 4 | ||||
-rw-r--r-- | library/processXml.cpp | 54 | ||||
-rw-r--r-- | library/statistics.cpp | 14 |
13 files changed, 543 insertions, 587 deletions
diff --git a/library/CustomGrid.cpp b/library/CustomGrid.cpp index 5fc993ce..a9846f74 100644 --- a/library/CustomGrid.cpp +++ b/library/CustomGrid.cpp @@ -16,11 +16,11 @@ #include "../synchronization.h" #include "../shared/customTooltip.h" #include <wx/dcclient.h> +#include "iconBuffer.h" +#include <wx/icon.h> #ifdef FFS_WIN #include <wx/timer.h> -#include <wx/icon.h> -#include "iconBuffer.h" #include "statusHandler.h" #include <cmath> @@ -53,14 +53,6 @@ class CustomGridTable : public wxGridTableBase public: CustomGridTable(int initialRows = 0, int initialCols = 0) : //note: initialRows/initialCols MUST match with GetNumberRows()/GetNumberCols() at initialization!!! wxGridTableBase(), - COLOR_BLUE( 80, 110, 255), - COLOR_GREY( 212, 208, 200), - COLOR_CMP_RED( 249, 163, 165), - COLOR_CMP_BLUE( 144, 232, 246), - COLOR_CMP_GREEN( 147, 253, 159), - COLOR_SYNC_BLUE( 201, 203, 247), - COLOR_SYNC_GREEN(197, 248, 190), - COLOR_YELLOW( 247, 252, 62), gridDataView(NULL), lastNrRows(initialRows), lastNrCols(initialCols) {} @@ -162,7 +154,7 @@ public: virtual wxGridCellAttr* GetAttr(int row, int col, wxGridCellAttr::wxAttrKind kind) { - const wxColour& color = getRowColor(row); + const wxColour color = getRowColor(row); //add color to some rows wxGridCellAttr* result = wxGridTableBase::GetAttr(row, col, kind); @@ -197,24 +189,36 @@ public: } protected: - const wxColour COLOR_BLUE; - const wxColour COLOR_GREY; - const wxColour COLOR_CMP_RED; - const wxColour COLOR_CMP_BLUE; - const wxColour COLOR_CMP_GREEN; - const wxColour COLOR_SYNC_BLUE; - const wxColour COLOR_SYNC_GREEN; - const wxColour COLOR_YELLOW; + static const wxColour COLOR_BLUE; + static const wxColour COLOR_GREY; + static const wxColour COLOR_ORANGE; + static const wxColour COLOR_CMP_RED; + static const wxColour COLOR_CMP_BLUE; + static const wxColour COLOR_CMP_GREEN; + static const wxColour COLOR_SYNC_BLUE; + static const wxColour COLOR_SYNC_GREEN; + static const wxColour COLOR_YELLOW; const GridView* gridDataView; //(very fast) access to underlying grid data :) private: - virtual const wxColour& getRowColor(int row) = 0; //rows that are filtered out are shown in different color + virtual const wxColour getRowColor(int row) = 0; //rows that are filtered out are shown in different color int lastNrRows; int lastNrCols; }; +//see http://www.latiumsoftware.com/en/articles/00015.php#12 for "safe" colors +const wxColour CustomGridTable::COLOR_ORANGE( 238, 201, 0); +const wxColour CustomGridTable::COLOR_BLUE( 80, 110, 255); +const wxColour CustomGridTable::COLOR_GREY( 212, 208, 200); +const wxColour CustomGridTable::COLOR_CMP_RED( 249, 163, 165); +const wxColour CustomGridTable::COLOR_CMP_BLUE( 144, 232, 246); +const wxColour CustomGridTable::COLOR_CMP_GREEN( 147, 253, 159); +const wxColour CustomGridTable::COLOR_SYNC_BLUE( 201, 203, 247); +const wxColour CustomGridTable::COLOR_SYNC_GREEN(197, 248, 190); +const wxColour CustomGridTable::COLOR_YELLOW( 247, 252, 62); + class CustomGridTableRim : public CustomGridTable { @@ -259,60 +263,143 @@ protected: { if (!fsObj->isEmpty<side>()) { - const DirMapping* dirObj = dynamic_cast<const DirMapping*> (fsObj); - if (dirObj != NULL) + struct GetValue : public FSObjectVisitor { - switch (getTypeAtPos(col)) + GetValue(xmlAccess::ColumnTypes colType) : colType_(colType) {} + virtual void visit(const FileMapping& fileObj) { - case xmlAccess::FULL_PATH: - return zToWx(dirObj->getFullName<side>()); - case xmlAccess::FILENAME: - return wxEmptyString; - case xmlAccess::REL_PATH: - return zToWx(dirObj->getRelativeName<side>()); - case xmlAccess::DIRECTORY: - return zToWx(dirObj->getBaseDirPf<side>()); - case xmlAccess::SIZE: //file size - return _("<Directory>"); - case xmlAccess::DATE: //date - return wxEmptyString; - case xmlAccess::EXTENSION: //file extension - return wxEmptyString; + switch (colType_) + { + case xmlAccess::FULL_PATH: + value = zToWx(fileObj.getFullName<side>().BeforeLast(globalFunctions::FILE_NAME_SEPARATOR)); + break; + case xmlAccess::FILENAME: //filename + value = zToWx(fileObj.getShortName<side>()); + break; + case xmlAccess::REL_PATH: //relative path + value = zToWx(fileObj.getParentRelativeName()); + break; + case xmlAccess::DIRECTORY: + value = zToWx(fileObj.getBaseDirPf<side>()); + break; + case xmlAccess::SIZE: //file size + value = FreeFileSync::numberToStringSep(fileObj.getFileSize<side>()); + break; + case xmlAccess::DATE: //date + value = FreeFileSync::utcTimeToLocalString(fileObj.getLastWriteTime<side>()); + break; + case xmlAccess::EXTENSION: //file extension + value = zToWx(fileObj.getExtension<side>()); + break; + } } - } - else - { - const FileMapping* fileObj = dynamic_cast<const FileMapping*>(fsObj); - if (fileObj != NULL) + + virtual void visit(const SymLinkMapping& linkObj) { - switch (getTypeAtPos(col)) + switch (colType_) { case xmlAccess::FULL_PATH: - return zToWx(fileObj->getFullName<side>().BeforeLast(globalFunctions::FILE_NAME_SEPARATOR)); + value = zToWx(linkObj.getFullName<side>().BeforeLast(globalFunctions::FILE_NAME_SEPARATOR)); + break; case xmlAccess::FILENAME: //filename - return zToWx(fileObj->getShortName<side>()); + value = zToWx(linkObj.getShortName<side>()); + break; case xmlAccess::REL_PATH: //relative path - return zToWx(fileObj->getParentRelativeName()); + value = zToWx(linkObj.getParentRelativeName()); + break; case xmlAccess::DIRECTORY: - return zToWx(fileObj->getBaseDirPf<side>()); + value = zToWx(linkObj.getBaseDirPf<side>()); + break; case xmlAccess::SIZE: //file size - return FreeFileSync::numberToWxString(fileObj->getFileSize<side>(), true); + value = _("<Symlink>"); + break; case xmlAccess::DATE: //date - return FreeFileSync::utcTimeToLocalString(fileObj->getLastWriteTime<side>()); + value = FreeFileSync::utcTimeToLocalString(linkObj.getLastWriteTime<side>()); + break; case xmlAccess::EXTENSION: //file extension - return zToWx(fileObj->getExtension<side>()); + value = wxEmptyString; + break; } } - } + + virtual void visit(const DirMapping& dirObj) + { + switch (colType_) + { + case xmlAccess::FULL_PATH: + value = zToWx(dirObj.getFullName<side>()); + break; + case xmlAccess::FILENAME: + value = wxEmptyString; + break; + case xmlAccess::REL_PATH: + value = zToWx(dirObj.getRelativeName<side>()); + break; + case xmlAccess::DIRECTORY: + value = zToWx(dirObj.getBaseDirPf<side>()); + break; + case xmlAccess::SIZE: //file size + value = _("<Directory>"); + break; + case xmlAccess::DATE: //date + value = wxEmptyString; + break; + case xmlAccess::EXTENSION: //file extension + value = wxEmptyString; + break; + } + } + xmlAccess::ColumnTypes colType_; + wxString value; + } getVal(getTypeAtPos(col)); + fsObj->accept(getVal); + return getVal.value; } } //if data is not found: return wxEmptyString; } + template <SelectedSide side> + Zstring getIconFileImpl(size_t row) const //return "folder" if row points to a folder + { + const FileSystemObject* fsObj = getRawData(row); + if (fsObj && !fsObj->isEmpty<side>()) + { + struct GetIcon : public FSObjectVisitor + { + virtual void visit(const FileMapping& fileObj) + { + //Optimization: if filename exists on both sides, always use left side's file: + //Icon should be the same on both sides anyway... + if (!fileObj.isEmpty<LEFT_SIDE>() && !fileObj.isEmpty<RIGHT_SIDE>()) + iconName = fileObj.getFullName<LEFT_SIDE>(); + else + iconName = fileObj.getFullName<side>(); + } + virtual void visit(const SymLinkMapping& linkObj) + { + iconName = linkObj.getLinkType<side>() == LinkDescriptor::TYPE_DIR ? + DefaultStr("folder") : + linkObj.getFullName<side>(); + } + virtual void visit(const DirMapping& dirObj) + { + iconName = DefaultStr("folder"); + } + + Zstring iconName; + } getIcon; + fsObj->accept(getIcon); + return getIcon.iconName; + } + + return Zstring(); + } + private: - virtual const wxColour& getRowColor(int row) //rows that are filtered out are shown in different color + virtual const wxColour getRowColor(int row) //rows that are filtered out are shown in different color { const FileSystemObject* fsObj = getRawData(row); if (fsObj) @@ -320,11 +407,27 @@ private: //mark filtered rows if (!fsObj->isActive()) return COLOR_BLUE; - //mark directories - else if (isDirectoryMapping(*fsObj)) - return COLOR_GREY; - else - return *wxWHITE; + + //mark directories and symlinks + struct GetRowColor : public FSObjectVisitor + { + virtual void visit(const FileMapping& fileObj) + { + rowColor = *wxWHITE; + } + virtual void visit(const SymLinkMapping& linkObj) + { + rowColor = COLOR_ORANGE; + } + virtual void visit(const DirMapping& dirObj) + { + rowColor = COLOR_GREY; + } + + wxColour rowColor; + } getCol; + fsObj->accept(getCol); + return getCol.rowColor; } return *wxWHITE; } @@ -344,16 +447,7 @@ public: virtual Zstring getIconFile(size_t row) const //return "folder" if row points to a folder { - const FileSystemObject* fsObj = getRawData(row); - if (fsObj && !fsObj->isEmpty<LEFT_SIDE>()) - { - if (isDirectoryMapping(*fsObj)) //it's a directory icon - return DefaultStr("folder"); - else - return fsObj->getFullName<LEFT_SIDE>(); - } - - return Zstring(); + return getIconFileImpl<LEFT_SIDE>(row); } }; @@ -368,23 +462,7 @@ public: virtual Zstring getIconFile(size_t row) const //return "folder" if row points to a folder { - const FileSystemObject* fsObj = getRawData(row); - if (fsObj && !fsObj->isEmpty<RIGHT_SIDE>()) - { - if (isDirectoryMapping(*fsObj)) //it's a directory icon - return DefaultStr("folder"); - else - { - //Optimization: if filename exists on both sides, always use left side's file: - //Icon should be the same on both sides anyway... - if (!fsObj->isEmpty<LEFT_SIDE>()) - return fsObj->getFullName<LEFT_SIDE>(); - else - return fsObj->getFullName<RIGHT_SIDE>(); - } - } - - return Zstring(); + return getIconFileImpl<RIGHT_SIDE>(row); } }; @@ -431,7 +509,7 @@ public: } private: - virtual const wxColour& getRowColor(int row) //rows that are filtered out are shown in different color + virtual const wxColour getRowColor(int row) //rows that are filtered out are shown in different color { const FileSystemObject* fsObj = getRawData(row); if (fsObj) @@ -502,10 +580,6 @@ CustomGrid::CustomGrid(wxWindow *parent, isLeading(false), m_marker(-1, ASCENDING) { - SetLayoutDirection(wxLayout_LeftToRight); // - GetGridWindow()->SetLayoutDirection(wxLayout_LeftToRight); //avoid mirroring this dialog in RTL languages like Hebrew or Arabic - GetGridColLabelWindow()->SetLayoutDirection(wxLayout_LeftToRight); // - //set color of selections wxColour darkBlue(40, 35, 140); SetSelectionBackground(darkBlue); @@ -550,6 +624,12 @@ void CustomGrid::initSettings(CustomGridLeft* gridLeft, } +void CustomGrid::release() //release connection to FreeFileSync::GridView +{ + setGridDataTable(NULL); +} + + bool CustomGrid::isLeadGrid() const { return isLeading; @@ -574,7 +654,6 @@ void CustomGrid::OnPaintGrid(wxEvent& event) } -inline void moveCursorWhileSelecting(const int anchor, const int oldPos, const int newPos, wxGrid* grid) { //note: all positions are valid in this context! @@ -951,7 +1030,6 @@ std::set<size_t> CustomGrid::getAllSelectedRows() const //############################################################################################ //CustomGrid specializations -#ifdef FFS_WIN template <bool showFileIcons> class GridCellRenderer : public wxGridCellStringRenderer { @@ -1039,8 +1117,8 @@ public: m_loadIconSuccess[row] = iconLoaded && iconDrawnFully; } } + return; } - return; } //default @@ -1072,7 +1150,6 @@ private: static const int LEFT_BORDER = 2; }; -#endif //---------------------------------------------------------------------------------------- @@ -1082,10 +1159,7 @@ CustomGridRim::CustomGridRim(wxWindow *parent, const wxSize& size, long style, const wxString& name) : - CustomGrid(parent, id, pos, size, style, name) -#ifdef FFS_WIN - , fileIconsAreEnabled(false) -#endif + CustomGrid(parent, id, pos, size, style, name), fileIconsAreEnabled(false) {} @@ -1164,14 +1238,15 @@ xmlAccess::ColumnAttributes CustomGridRim::getColumnAttributes() void CustomGridRim::setColumnAttributes(const xmlAccess::ColumnAttributes& attr) { //remove special alignment for column "size" - for (int i = 0; i < GetNumberCols(); ++i) - if (getTypeAtPos(i) == xmlAccess::SIZE) - { - wxGridCellAttr* cellAttributes = GetOrCreateCellAttr(0, i); - cellAttributes->SetAlignment(wxALIGN_LEFT,wxALIGN_CENTRE); - SetColAttr(i, cellAttributes); - break; - } + if (GetLayoutDirection() != wxLayout_RightToLeft) //don't change for RTL languages + for (int i = 0; i < GetNumberCols(); ++i) + if (getTypeAtPos(i) == xmlAccess::SIZE) + { + wxGridCellAttr* cellAttributes = GetOrCreateCellAttr(0, i); + cellAttributes->SetAlignment(wxALIGN_LEFT,wxALIGN_CENTRE); + SetColAttr(i, cellAttributes); + break; + } //---------------------------------------------------------------------------------- columnSettings.clear(); @@ -1222,14 +1297,15 @@ void CustomGridRim::setColumnAttributes(const xmlAccess::ColumnAttributes& attr) //-------------------------------------------------------------------------------------------------------- //set special alignment for column "size" - for (int i = 0; i < GetNumberCols(); ++i) - if (getTypeAtPos(i) == xmlAccess::SIZE) - { - wxGridCellAttr* cellAttributes = GetOrCreateCellAttr(0, i); - cellAttributes->SetAlignment(wxALIGN_RIGHT, wxALIGN_CENTRE); - SetColAttr(i, cellAttributes); //make filesize right justified on grids - break; - } + if (GetLayoutDirection() != wxLayout_RightToLeft) //don't change for RTL languages + for (int i = 0; i < GetNumberCols(); ++i) + if (getTypeAtPos(i) == xmlAccess::SIZE) + { + wxGridCellAttr* cellAttributes = GetOrCreateCellAttr(0, i); + cellAttributes->SetAlignment(wxALIGN_RIGHT, wxALIGN_CENTRE); + SetColAttr(i, cellAttributes); //make filesize right justified on grids + break; + } ClearSelection(); ForceRefresh(); @@ -1328,7 +1404,6 @@ void CustomGridRim::autoSizeColumns() //performance optimized column resizer (a } -#ifdef FFS_WIN void CustomGridRim::enableFileIcons(const bool value) { fileIconsAreEnabled = value; @@ -1449,7 +1524,6 @@ void IconUpdater::loadIconsAsynchronously(wxEvent& event) //loads all (not yet) //event.Skip(); } -#endif //---------------------------------------------------------------------------------------- @@ -1591,6 +1665,11 @@ CustomGridMiddle::CustomGridMiddle(wxWindow *parent, gridDataTable(NULL), toolTip(new CustomTooltip) { + SetLayoutDirection(wxLayout_LeftToRight); // + GetGridWindow()->SetLayoutDirection(wxLayout_LeftToRight); //avoid mirroring this dialog in RTL languages like Hebrew or Arabic + GetGridColLabelWindow()->SetLayoutDirection(wxLayout_LeftToRight); // + + //connect events for dynamic selection of sync direction GetGridWindow()->Connect(wxEVT_MOTION, wxMouseEventHandler(CustomGridMiddle::OnMouseMovement), NULL, this); GetGridWindow()->Connect(wxEVT_LEAVE_WINDOW, wxMouseEventHandler(CustomGridMiddle::OnLeaveWindow), NULL, this); @@ -1848,6 +1927,11 @@ void CustomGridMiddle::enableSyncPreview(bool value) { assert(gridDataTable); gridDataTable->enableSyncPreview(value); + + if (value) + GetGridColLabelWindow()->SetToolTip(_("Synchronization Preview")); + else + GetGridColLabelWindow()->SetToolTip(_("Comparison Result")); } diff --git a/library/CustomGrid.h b/library/CustomGrid.h index 5f1823c0..0cace1a9 100644 --- a/library/CustomGrid.h +++ b/library/CustomGrid.h @@ -60,11 +60,13 @@ public: virtual ~CustomGrid() {} - void initSettings(CustomGridLeft* gridLeft, + void initSettings(CustomGridLeft* gridLeft, //create connection with FreeFileSync::GridView CustomGridMiddle* gridMiddle, CustomGridRight* gridRight, const FreeFileSync::GridView* gridDataView); + void release(); //release connection to FreeFileSync::GridView + std::set<size_t> getAllSelectedRows() const; //set sort direction indicator on UI @@ -112,7 +114,6 @@ class GridCellRenderer; //----------------------------------------------------------- -#ifdef FFS_WIN class IconUpdater : private wxEvtHandler //update file icons periodically: use SINGLE instance to coordinate left and right grid at once { public: @@ -127,7 +128,6 @@ private: std::auto_ptr<wxTimer> m_timer; //user timer event to periodically update icons: better than idle event because also active when scrolling! :) }; -#endif //############## SPECIALIZATIONS ################### @@ -159,15 +159,12 @@ public: void autoSizeColumns(); //performance optimized column resizer void autoSizeColumns(int col, bool doRefresh = true); // -#ifdef FFS_WIN void enableFileIcons(const bool value); -#endif private: CustomGridTableRim* getGridDataTable(); virtual const CustomGridTableRim* getGridDataTable() const = 0; -#ifdef FFS_WIN //asynchronous icon loading void getIconsToBeLoaded(std::vector<Zstring>& newLoad); //loads all (not yet) drawn icons @@ -183,7 +180,6 @@ private: LoadSuccess loadIconSuccess; //save status of last icon load when drawing on GUI bool fileIconsAreEnabled; -#endif xmlAccess::ColumnAttributes columnSettings; //set visibility, position and width of columns }; diff --git a/library/FreeFileSync.ico b/library/FreeFileSync.ico Binary files differindex bceb6ee4..bfa8ffc4 100644 --- a/library/FreeFileSync.ico +++ b/library/FreeFileSync.ico diff --git a/library/dbFile.cpp b/library/dbFile.cpp index 7a000197..1919dd6d 100644 --- a/library/dbFile.cpp +++ b/library/dbFile.cpp @@ -29,7 +29,7 @@ namespace { //------------------------------------------------------------------------------------------------------------------------------- const char FILE_FORMAT_DESCR[] = "FreeFileSync"; -const int FILE_FORMAT_VER = 3; +const int FILE_FORMAT_VER = 4; //------------------------------------------------------------------------------------------------------------------------------- @@ -86,18 +86,22 @@ public: } private: - void execute(DirContainer& dirCont) + void execute(DirContainer& dirCont) const { - unsigned int fileCount = readNumberC<unsigned int>(); + size_t fileCount = readNumberC<size_t>(); while (fileCount-- != 0) readSubFile(dirCont); - unsigned int dirCount = readNumberC<unsigned int>(); + size_t symlinkCount = readNumberC<size_t>(); + while (symlinkCount-- != 0) + readSubLink(dirCont); + + size_t dirCount = readNumberC<size_t>(); while (dirCount-- != 0) readSubDirectory(dirCont); } - void readSubFile(DirContainer& 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 @@ -116,7 +120,22 @@ private: wxULongLong(sizeHigh, sizeLow))); } - void readSubDirectory(DirContainer& dirCont) + + 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 long modHigh = readNumberC<long>(); + const unsigned long modLow = readNumberC<unsigned long>(); + const Zstring targetPath = readStringC(); //file name + const LinkDescriptor::LinkType linkType = static_cast<LinkDescriptor::LinkType>(readNumberC<int>()); + + dirCont.addSubLink(shortName, + LinkDescriptor(wxLongLong(modHigh, modLow), targetPath, linkType)); + } + + + void readSubDirectory(DirContainer& dirCont) const { const Zstring shortName = readStringC(); //directory name DirContainer& subDir = dirCont.addSubDir(shortName); @@ -254,16 +273,20 @@ public: } private: - template<typename Iterator, typename Function> - friend Function std::for_each(Iterator, Iterator, Function); + friend class Utility::Proxy<SaveDirInfo<side> >; //friend declaration of std::for_each is NOT sufficient as implementation is compiler dependent! void execute(const HierarchyObject& hierObj) { - writeNumberC<unsigned int>(std::count_if(hierObj.useSubFiles().begin(), hierObj.useSubFiles().end(), IsNonEmpty<side>())); //number of (existing) files - std::for_each(hierObj.useSubFiles().begin(), hierObj.useSubFiles().end(), *this); + Utility::Proxy<SaveDirInfo<side> > prx(*this); //grant std::for_each access to private parts of this class - writeNumberC<unsigned int>(std::count_if(hierObj.useSubDirs().begin(), hierObj.useSubDirs().end(), IsNonEmpty<side>())); //number of (existing) directories - std::for_each(hierObj.useSubDirs().begin(), hierObj.useSubDirs().end(), *this); + writeNumberC<size_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<size_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<size_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); } void operator()(const FileMapping& fileMap) @@ -281,6 +304,18 @@ private: } } + void operator()(const SymLinkMapping& linkObj) + { + if (!linkObj.isEmpty<side>()) + { + writeStringC(linkObj.getObjShortName()); + writeNumberC<long>( linkObj.getLastWriteTime<side>().GetHi()); //last modification time + writeNumberC<unsigned long>(linkObj.getLastWriteTime<side>().GetLo()); // + writeStringC(linkObj.getTargetPath<side>()); + writeNumberC<int>(linkObj.getLinkType<side>()); + } + } + void operator()(const DirMapping& dirMap) { if (!dirMap.isEmpty<side>()) diff --git a/library/dbFile.h b/library/dbFile.h index e7db0393..9fa49c51 100644 --- a/library/dbFile.h +++ b/library/dbFile.h @@ -13,7 +13,13 @@ namespace FreeFileSync { void saveToDisk(const BaseDirMapping& baseMapping); //throw (FileError) +struct DirInformation +{ + BaseFilter::FilterRef filter; //filter settings (used when retrieving directory data) + DirContainer baseDirContainer; //hierarchical directory information +}; typedef boost::shared_ptr<const DirInformation> DirInfoPtr; + std::pair<DirInfoPtr, DirInfoPtr> loadFromDisk(const BaseDirMapping& baseMapping); //throw (FileError) -> return value always bound! } diff --git a/library/filter.cpp b/library/filter.cpp index 931532c3..a64c3a31 100644 --- a/library/filter.cpp +++ b/library/filter.cpp @@ -251,8 +251,7 @@ bool NameFilter::passDirFilter(const Zstring& relDirname, bool* subObjMightMatch { if (subObjMightMatch) { - Zstring subNameBegin = relDirname; - subNameBegin += globalFunctions::FILE_NAME_SEPARATOR; + const Zstring& subNameBegin = relDirname + globalFunctions::FILE_NAME_SEPARATOR; //const-ref optimization *subObjMightMatch = matchesFilterBegin(subNameBegin, filterFileIn) || //might match a file in subdirectory matchesFilterBegin(subNameBegin, filterFolderIn); //or another subdirectory diff --git a/library/iconBuffer.cpp b/library/iconBuffer.cpp index 338f53df..fa0b1673 100644 --- a/library/iconBuffer.cpp +++ b/library/iconBuffer.cpp @@ -7,7 +7,6 @@ #include "iconBuffer.h" #include <wx/thread.h> #include <wx/bitmap.h> -#include <wx/msw/wrapwin.h> //includes "windows.h" #include <wx/msgdlg.h> #include <wx/icon.h> #include <map> @@ -15,42 +14,34 @@ #include <stdexcept> #include <set> -using FreeFileSync::IconBuffer; +#ifdef FFS_WIN +#include <wx/msw/wrapwin.h> //includes "windows.h" +#elif defined FFS_LINUX +#include <giomm/file.h> +#include <gtkmm/icontheme.h> +#include <gtkmm/main.h> +#endif -const wxIcon& IconBuffer::getDirectoryIcon() //one folder icon should be sufficient... -{ - static wxIcon folderIcon; - static bool isInitalized = false; - if (!isInitalized) - { - isInitalized = true; - - SHFILEINFO fileInfo; - fileInfo.hIcon = 0; //initialize hIcon - - //NOTE: CoInitializeEx()/CoUninitialize() implicitly called by wxWidgets on program startup! - if (::SHGetFileInfo(DefaultStr("dummy"), //Windows Seven doesn't like this parameter to be an empty string - FILE_ATTRIBUTE_DIRECTORY, - &fileInfo, - sizeof(fileInfo), - SHGFI_ICON | SHGFI_SMALLICON | SHGFI_USEFILEATTRIBUTES) && +using FreeFileSync::IconBuffer; - fileInfo.hIcon != 0) //fix for weird error: SHGetFileInfo() might return successfully WITHOUT filling fileInfo.hIcon!! - { - folderIcon.SetHICON(fileInfo.hIcon); - folderIcon.SetSize(IconBuffer::ICON_SIZE, IconBuffer::ICON_SIZE); - } - } - return folderIcon; -} namespace { +struct CmpFilename +{ + bool operator()(const Zstring& a, const Zstring& b) const + { + return a.cmpFileName(b) < 0; + } +}; + + +#ifdef FFS_WIN Zstring getFileExtension(const Zstring& filename) { - const Zstring shortName = filename.AfterLast(DefaultChar('\\')); //Zstring::AfterLast() returns the whole string if ch not found + const Zstring shortName = filename.AfterLast(DefaultChar('\\')); //warning: using windows file name separator! const size_t pos = shortName.Find(DefaultChar('.'), true); return pos == Zstring::npos ? Zstring() : @@ -58,19 +49,10 @@ Zstring getFileExtension(const Zstring& filename) } -struct CmpFilenameWin -{ - bool operator()(const Zstring& a, const Zstring& b) const - { - return a.cmpFileName(b) < 0; - } -}; - - //test for extension for icons that physically have to be retrieved from disc bool isPriceyExtension(const Zstring& extension) { - static std::set<Zstring, CmpFilenameWin> exceptions; + static std::set<Zstring, CmpFilename> exceptions; static bool isInitalized = false; if (!isInitalized) { @@ -86,39 +68,198 @@ bool isPriceyExtension(const Zstring& extension) } return exceptions.find(extension) != exceptions.end(); } +#endif } //################################################################################################################################################ -class IconBuffer::IconHolder //handle HICON ownership WITHOUT ref-counting to allow a deep-copy (in contrast to wxIcon) +class IconBuffer::IconHolder //handle HICON/GdkPixbuf ownership WITHOUT ref-counting to allow thread-safe usage (in contrast to wxIcon) { public: - IconHolder(HICON handle = 0) : handle_(handle) {} +#ifdef FFS_WIN + typedef HICON HandleType; +#elif defined FFS_LINUX + typedef GdkPixbuf* HandleType; +#endif + + IconHolder(HandleType handle = 0) : handle_(handle) {} //take ownership! + + //icon holder has value semantics! + IconHolder(const IconHolder& other) : handle_(other.handle_ == 0 ? 0 : +#ifdef FFS_WIN + ::CopyIcon(other.handle_) +#elif defined FFS_LINUX + gdk_pixbuf_copy(other.handle_) //create new Pix buf with reference count 1 or return 0 on error +#endif + ) {} + + IconHolder& operator=(const IconHolder& other) + { + IconHolder(other).swap(*this); + return *this; + } ~IconHolder() { if (handle_ != 0) +#ifdef FFS_WIN ::DestroyIcon(handle_); +#elif defined FFS_LINUX + g_object_unref(handle_); +#endif } - HICON clone() const //copy HICON, caller needs to take ownership! + void swap(IconHolder& other) //throw() { - return handle_ != 0 ? ::CopyIcon(handle_) : 0; + std::swap(handle_, other.handle_); } - void swap(IconHolder& other) //throw() + wxIcon toWxIcon() const //copy HandleType, caller needs to take ownership! { - std::swap(handle_, other.handle_); + IconHolder clone(*this); + if (clone.handle_ != 0) + { + wxIcon newIcon; //attention: wxIcon uses reference counting! +#ifdef FFS_WIN + newIcon.SetHICON(clone.handle_); // + newIcon.SetSize(IconBuffer::ICON_SIZE, IconBuffer::ICON_SIZE); //icon is actually scaled to this size (just in case referenced HICON differs) +#elif defined FFS_LINUX // + newIcon.SetPixbuf(clone.handle_); // transfer ownership!! +#endif // + clone.handle_ = 0; // + return newIcon; + } + return wxNullIcon; } private: - IconHolder(const IconHolder&); - IconHolder& operator=(const IconHolder&); - - HICON handle_; + HandleType handle_; }; +const wxIcon& IconBuffer::getDirectoryIcon() //one folder icon should be sufficient... +{ + static wxIcon folderIcon; + + static bool isInitalized = false; + if (!isInitalized) + { + isInitalized = true; + +#ifdef FFS_WIN + SHFILEINFO fileInfo; + fileInfo.hIcon = 0; //initialize hIcon + + //NOTE: CoInitializeEx()/CoUninitialize() implicitly called by wxWidgets on program startup! + if (::SHGetFileInfo(DefaultStr("dummy"), //Windows Seven doesn't like this parameter to be an empty string + FILE_ATTRIBUTE_DIRECTORY, + &fileInfo, + sizeof(fileInfo), + SHGFI_ICON | SHGFI_SMALLICON | SHGFI_USEFILEATTRIBUTES) && + + fileInfo.hIcon != 0) //fix for weird error: SHGetFileInfo() might return successfully WITHOUT filling fileInfo.hIcon!! + { + folderIcon.SetHICON(fileInfo.hIcon); //transfer ownership! + folderIcon.SetSize(IconBuffer::ICON_SIZE, IconBuffer::ICON_SIZE); + } + +#elif defined FFS_LINUX + folderIcon = getAssociatedIcon(DefaultStr("/usr/")).toWxIcon(); //all directories will look like "/usr/" +#endif + } + return folderIcon; +} + + +IconBuffer::IconHolder IconBuffer::getAssociatedIcon(const Zstring& filename) +{ +#ifdef FFS_WIN + //despite what docu says about SHGetFileInfo() it can't handle all relative filenames, e.g. "\DirName" + //but no problem, directory formatting takes care that filenames are always absolute! + + SHFILEINFO fileInfo; + fileInfo.hIcon = 0; //initialize hIcon -> fix for weird error: SHGetFileInfo() might return successfully WITHOUT filling fileInfo.hIcon!! + //bug report: https://sourceforge.net/tracker/?func=detail&aid=2768004&group_id=234430&atid=1093080 + + //NOTE: CoInitializeEx()/CoUninitialize() implicitly called by wxWidgets on program startup! + ::SHGetFileInfo(filename.c_str(), //FreeFileSync::removeLongPathPrefix(fileName), //::SHGetFileInfo() can't handle \\?\-prefix! + 0, + &fileInfo, + sizeof(fileInfo), + SHGFI_ICON | SHGFI_SMALLICON); + + return IconHolder(fileInfo.hIcon); //pass icon ownership (may be 0) + +#elif defined FFS_LINUX + static struct RunOnce + { + RunOnce() + { + Gtk::Main::init_gtkmm_internals(); + } + } dummy; + + try + { + Glib::RefPtr<Gio::File> fileObj = Gio::File::create_for_path(filename.c_str()); //never fails + Glib::RefPtr<Gio::FileInfo> fileInfo = fileObj->query_info(G_FILE_ATTRIBUTE_STANDARD_ICON); + if (fileInfo) + { + Glib::RefPtr<Gio::Icon> gicon = fileInfo->get_icon(); + if (gicon) + { + Glib::RefPtr<Gtk::IconTheme> iconTheme = Gtk::IconTheme::get_default(); + if (iconTheme) + { + Gtk::IconInfo iconInfo = iconTheme->lookup_icon(gicon, ICON_SIZE, Gtk::ICON_LOOKUP_USE_BUILTIN); //this may fail if icon is not installed on system + if (iconInfo) + { + Glib::RefPtr<Gdk::Pixbuf> iconPixbuf = iconInfo.load_icon(); //render icon into Pixbuf + if (iconPixbuf) + return IconHolder(iconPixbuf->gobj_copy()); //copy and pass icon ownership (may be 0) + } + } + } + } + } + catch (const Glib::Error&) {} + + + //fallback: icon lookup may fail because some icons are currently not present on system + Glib::RefPtr<Gtk::IconTheme> iconTheme = Gtk::IconTheme::get_default(); + if (iconTheme) + { + Glib::RefPtr<Gdk::Pixbuf> iconPixbuf = iconTheme->load_icon("misc", ICON_SIZE, Gtk::ICON_LOOKUP_USE_BUILTIN); + if (!iconPixbuf) + iconPixbuf = iconTheme->load_icon("text-x-generic", ICON_SIZE, Gtk::ICON_LOOKUP_USE_BUILTIN); + if (iconPixbuf) + return IconHolder(iconPixbuf->gobj_copy()); //copy and pass icon ownership (may be 0) + } + + //fallback fallback + return IconHolder(); +#endif +} + + +#ifdef FFS_WIN +IconBuffer::IconHolder IconBuffer::getAssociatedIconByExt(const Zstring& extension) +{ + SHFILEINFO fileInfo; + fileInfo.hIcon = 0; //initialize hIcon -> fix for weird error: SHGetFileInfo() might return successfully WITHOUT filling fileInfo.hIcon!! + + //no read-access to disk! determine icon by extension + ::SHGetFileInfo((Zstring(DefaultStr("dummy.")) + extension).c_str(), //Windows Seven doesn't like this parameter to be without short name + FILE_ATTRIBUTE_NORMAL, + &fileInfo, + sizeof(fileInfo), + SHGFI_ICON | SHGFI_SMALLICON | SHGFI_USEFILEATTRIBUTES); + + return IconHolder(fileInfo.hIcon); //pass icon ownership (may be 0) +} +#endif + + //--------------------------------------------------------------------------------------------------- typedef std::vector<DefaultChar> BasicString; //simple thread safe string class: std::vector is guaranteed to not use reference counting, Effective STL, item 13 //avoid reference-counted objects as shared data: NOT THREADSAFE!!! (implicitly shared variables: ref-count + c-string) @@ -144,10 +285,9 @@ private: wxCriticalSection lockWorkload; //use for locking shared data std::vector<FileName> workload; //processes last elements of vector first! bool threadHasMutex; - bool threadExitIsRequested; //------------------------------------------------------------ - //event: icon buffer -> woker thread + //signal event: icon buffer(main thread) -> worker thread wxMutex threadIsListening; wxCondition continueWork; //wake up thread @@ -156,9 +296,8 @@ private: IconBuffer::WorkerThread::WorkerThread(IconBuffer* iconBuff) : - wxThread(wxTHREAD_JOINABLE), + wxThread(wxTHREAD_DETACHED), //we're using the thread encapsulated in a static object => use "detached" to avoid main thread waiting for this thread on exit(which in turn would prevent deletion of static object...ect.) => deadlock! threadHasMutex(false), - threadExitIsRequested(false), threadIsListening(), continueWork(threadIsListening), iconBuffer(iconBuff) @@ -195,12 +334,8 @@ void IconBuffer::WorkerThread::setWorkload(const std::vector<Zstring>& load) //( void IconBuffer::WorkerThread::quitThread() { - { - wxMutexLocker dummy(threadIsListening); //wait until thread is in waiting state - threadExitIsRequested = true; //no sharing conflicts in this situation - continueWork.Signal(); //exit thread - } - Wait(); //wait until thread has exitted + setWorkload(std::vector<Zstring>()); + Delete(); //gracefully terminate a detached thread... } @@ -208,22 +343,20 @@ wxThread::ExitCode IconBuffer::WorkerThread::Entry() { try { - wxMutexLocker dummy(threadIsListening); //this lock needs to be called from WITHIN the thread => calling it from constructor(Main thread) would be useless + //this lock needs to be called from WITHIN the thread => calling it from constructor(Main thread) would be useless (signal direction: main -> thread) + wxMutexLocker dummy(threadIsListening); //this mutex STAYS locked all the time except of continueWork.Wait()! { - //this mutex STAYS locked all the time except of continueWork.Wait()! wxCriticalSectionLocker dummy2(lockWorkload); threadHasMutex = true; } while (true) { - continueWork.Wait(); //waiting for continueWork.Signal(); unlocks Mutex "threadIsListening" + continueWork.WaitTimeout(100); //waiting for continueWork.Signal(); unlocks Mutex "threadIsListening" - //no mutex needed in this context - if (threadExitIsRequested) //no mutex here: atomicity is not prob for a bool, but visibility (e.g. caching in registers) - return 0; //shouldn't be a problem nevertheless because of implicit memory barrier caused by mutex.Lock() in .Wait() + if (TestDestroy()) + return 0; - //do work: get the file icons doWork(); } } @@ -237,60 +370,43 @@ wxThread::ExitCode IconBuffer::WorkerThread::Entry() void IconBuffer::WorkerThread::doWork() { - Zstring fileName; - //do work: get the file icon. while (true) { + Zstring fileName; { wxCriticalSectionLocker dummy(lockWorkload); if (workload.empty()) break; //enter waiting state - fileName = &workload.back()[0]; //deep copy: fileName is NOT empty (includes NULL-termination) + fileName = &workload.back()[0]; //deep copy (includes NULL-termination) workload.pop_back(); } if (iconBuffer->requestFileIcon(fileName)) //thread safety: Zstring okay, won't be reference-counted in requestIcon() continue; //icon already in buffer: skip - //despite what docu says about SHGetFileInfo() it can't handle all relative filenames, e.g. "\DirName" - //but no problem, directory formatting takes care that filenames are always absolute! - - //load icon - SHFILEINFO fileInfo; - fileInfo.hIcon = 0; //initialize hIcon -> fix for weird error: SHGetFileInfo() might return successfully WITHOUT filling fileInfo.hIcon!! - //bug report: https://sourceforge.net/tracker/?func=detail&aid=2768004&group_id=234430&atid=1093080 - +#ifdef FFS_WIN const Zstring extension = getFileExtension(fileName); //thread-safe: no sharing! if (isPriceyExtension(extension)) //"pricey" extensions are stored with fullnames and are read from disk, while cheap ones require just the extension { - //NOTE: CoInitializeEx()/CoUninitialize() implicitly called by wxWidgets on program startup! - ::SHGetFileInfo(fileName.c_str(), //FreeFileSync::removeLongPathPrefix(fileName), //::SHGetFileInfo() can't handle \\?\-prefix! - 0, - &fileInfo, - sizeof(fileInfo), - SHGFI_ICON | SHGFI_SMALLICON); - - IconBuffer::IconHolder newIcon(fileInfo.hIcon); //pass icon ownership (may be 0) + const IconHolder newIcon = IconBuffer::getAssociatedIcon(fileName); iconBuffer->insertIntoBuffer(fileName, newIcon); } else //no read-access to disk! determine icon by extension { - ::SHGetFileInfo((Zstring(DefaultStr("dummy.")) + extension).c_str(), //Windows Seven doesn't like this parameter to be without short name - FILE_ATTRIBUTE_NORMAL, - &fileInfo, - sizeof(fileInfo), - SHGFI_ICON | SHGFI_SMALLICON | SHGFI_USEFILEATTRIBUTES); - - IconBuffer::IconHolder newIcon(fileInfo.hIcon); //pass icon ownership (may be 0) + const IconHolder newIcon = IconBuffer::getAssociatedIconByExt(extension); iconBuffer->insertIntoBuffer(extension, newIcon); } +#elif defined FFS_LINUX + const IconHolder newIcon = IconBuffer::getAssociatedIcon(fileName); + iconBuffer->insertIntoBuffer(fileName, newIcon); +#endif } } //--------------------------------------------------------------------------------------------------- -class IconBuffer::IconDB : public std::map<Zstring, IconBuffer::CountedIconPtr> {}; //entryName/icon -> ATTENTION: consider ref-counting for this shared data structure!!! +class IconBuffer::IconDB : public std::map<Zstring, IconBuffer::IconHolder> {}; //entryName/icon -> ATTENTION: avoid ref-counting for this shared data structure!!! (== don't copy instances between threads) class IconBuffer::IconDbSequence : public std::queue<Zstring> {}; //entryName //--------------------------------------------------------------------------------------------------- @@ -312,42 +428,29 @@ IconBuffer::IconBuffer() : IconBuffer::~IconBuffer() { - //keep non-inline destructor for std::auto_ptr to work with forward declarations - worker->quitThread(); } bool IconBuffer::requestFileIcon(const Zstring& fileName, wxIcon* icon) { + wxCriticalSectionLocker dummy(*lockIconDB); + +#ifdef FFS_WIN + //"pricey" extensions are stored with fullnames and are read from disk, while cheap ones require just the extension const Zstring extension = getFileExtension(fileName); + IconDB::const_iterator i = buffer->find(isPriceyExtension(extension) ? fileName : extension); +#elif defined FFS_LINUX + IconDB::const_iterator i = buffer->find(fileName); +#endif - wxCriticalSectionLocker dummy(*lockIconDB); + if (i == buffer->end()) + return false; - IconDB::const_iterator i = buffer->find( //"pricey" extensions are stored with fullnames and are read from disk, while cheap ones require just the extension - isPriceyExtension(extension) ? - fileName : - extension); - if (i != buffer->end()) - { - if (icon != NULL) - { - HICON clonedIcon = i->second->clone(); //thread safety: make deep copy! - if (clonedIcon != 0) - { - //create wxIcon from handle - wxIcon newIcon; //attention: wxIcon uses reference counting! - newIcon.SetHICON(clonedIcon); //transfer ownership!! - newIcon.SetSize(IconBuffer::ICON_SIZE, IconBuffer::ICON_SIZE); - *icon = newIcon; - } - else - *icon = wxNullIcon; - } - return true; - } + if (icon != NULL) + *icon = i->second.toWxIcon(); - return false; + return true; } @@ -357,16 +460,14 @@ void IconBuffer::setWorkload(const std::vector<Zstring>& load) } -void IconBuffer::insertIntoBuffer(const DefaultChar* entryName, IconHolder& icon) //called by worker thread +void IconBuffer::insertIntoBuffer(const DefaultChar* entryName, const IconHolder& icon) //called by worker thread { wxCriticalSectionLocker dummy(*lockIconDB); //thread safety, ref-counting: (implicitly) make deep copy! const Zstring fileNameZ = entryName; - const IconBuffer::CountedIconPtr newIcon(new IconBuffer::IconHolder); //exception safety! - newIcon->swap(icon); // - const std::pair<IconDB::iterator, bool> rc = buffer->insert(IconDB::value_type(fileNameZ, newIcon)); //thread saftey: icon uses ref-counting! But is NOT shared with main thread! + const std::pair<IconDB::iterator, bool> rc = buffer->insert(std::make_pair(fileNameZ, icon)); //thread saftey: icon uses ref-counting! But is NOT shared with main thread! if (rc.second) //if insertion took place bufSequence->push(fileNameZ); //note: sharing Zstring with IconDB!!! @@ -381,4 +482,3 @@ void IconBuffer::insertIntoBuffer(const DefaultChar* entryName, IconHolder& icon bufSequence->pop(); } } - diff --git a/library/iconBuffer.h b/library/iconBuffer.h index cb2d809f..4a19af3d 100644 --- a/library/iconBuffer.h +++ b/library/iconBuffer.h @@ -7,14 +7,9 @@ #ifndef ICONBUFFER_H_INCLUDED #define ICONBUFFER_H_INCLUDED -#ifndef FFS_WIN -header should be used in the windows build only! -#endif - #include <vector> #include "../shared/zstring.h" #include <memory> -#include <boost/shared_ptr.hpp> class wxCriticalSection; class wxIcon; @@ -32,24 +27,27 @@ public: bool requestFileIcon(const Zstring& fileName, wxIcon* icon = NULL); //returns false if icon is not in buffer void setWorkload(const std::vector<Zstring>& load); //(re-)set new workload of icons to be retrieved; - static const int ICON_SIZE = 16; //size in pixel - static const size_t BUFFER_SIZE = 800; //maximum number if icons to buffer +#ifdef FFS_WIN + static const int ICON_SIZE = 16; //size in pixel +#elif defined FFS_LINUX + static const int ICON_SIZE = 24; //size in pixel +#endif private: IconBuffer(); ~IconBuffer(); - class WorkerThread; - friend class WorkerThread; + static const size_t BUFFER_SIZE = 800; //maximum number of icons to buffer class IconDB; class IconHolder; class IconDbSequence; - typedef boost::shared_ptr<IconHolder> CountedIconPtr; - //methods used by worker thread - void insertIntoBuffer(const DefaultChar* entryName, IconHolder& icon); //icon is invalidated by this call!! + void insertIntoBuffer(const DefaultChar* entryName, const IconHolder& icon); + + static IconHolder getAssociatedIcon(const Zstring& filename); + static IconHolder getAssociatedIconByExt(const Zstring& extension); //---------------------- Shared Data ------------------------- std::auto_ptr<wxCriticalSection> lockIconDB; @@ -57,7 +55,8 @@ private: std::auto_ptr<IconDbSequence> bufSequence; //save sequence of buffer entry to delete oldest elements (implicitly shared by sharing Zstring with IconDB!!!) //------------------------------------------------------------ - std::auto_ptr<WorkerThread> worker; + class WorkerThread; + WorkerThread* worker; //detached thread => destroys itself }; } diff --git a/library/multithreading.cpp b/library/multithreading.cpp deleted file mode 100644 index 252884a2..00000000 --- a/library/multithreading.cpp +++ /dev/null @@ -1,254 +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-2010 ZenJu (zhnmju123 AT gmx.de) * -// ************************************************************************** -// -#include "multithreading.h" -#include "statusHandler.h" -#include <wx/utils.h> - -//#include <wx/msw/wrapwin.h> //includes "windows.h" -//MessageBox(0, "hi", "", 0); - -/*choreography: - - ------------- --------------- - |main thread| |worker thread| - ------------- --------------- - -1. Instantiation (Constructor) -------------------------------- - create thread - enter waiting state -2. waitUntilReady -------------------------------- - wait until thread is ready - -3. Call execute -------------------------------- - send signal to start - start processing - update UI while thread works - finish processing - wait until main thread is ready to receive signal - receive signal - enter waiting state -4. Termination (Destructor) -------------------------------- - wait until thread is in wait state - set exit flag - signal thread to continue (and exit) -*/ -class WorkerThread : public wxThread -{ - friend class UpdateWhileExecuting; - -public: - WorkerThread(UpdateWhileExecuting* handler) : - wxThread(wxTHREAD_DETACHED), - readyToBeginProcessing(), - beginProcessing(readyToBeginProcessing), - threadIsInitialized(false), - threadExitIsRequested(false), - threadHandler(handler) - { } - - - ~WorkerThread() {} - - - ExitCode Entry() - { - readyToBeginProcessing.Lock(); //this lock needs to be called IN the thread => calling it from constructor(Main thread) would be useless - sharedData.Enter(); - threadIsInitialized = true; - sharedData.Leave(); - - while (true) - { - beginProcessing.Wait(); - - //no mutex needed in this context - if (threadExitIsRequested) - return 0; - - //actual (long running) work is done in this method - threadHandler->longRunner(); - - threadHandler->readyToReceiveResult.Lock(); - threadHandler->receivingResult.Signal(); // kind of a double notice that work is completed - threadHandler->workDone = true; // Workaround for wxWidgets: bug in wxCondition (wxWidgets v2.8.9, signal might geht lost) - threadHandler->readyToReceiveResult.Unlock(); - } - - return 0; - } - -private: - wxMutex readyToBeginProcessing; - wxCondition beginProcessing; - - //shared data - wxCriticalSection sharedData; - bool threadIsInitialized; - bool threadExitIsRequested; - - UpdateWhileExecuting* threadHandler; -}; - - -UpdateWhileExecuting::UpdateWhileExecuting() : - readyToReceiveResult(), - receivingResult(readyToReceiveResult), - workDone(false) -{ - //mutex needs to be initially locked for condition receivingResult to work properly - readyToReceiveResult.Lock(); - - - theWorkerThread = new WorkerThread(this); - - theWorkerThread->Create(); - theWorkerThread->Run(); - - //wait until the thread has locked readyToBeginProcessing - bool threadInitialized = false; - while (!threadInitialized) - { - theWorkerThread->sharedData.Enter(); - threadInitialized = theWorkerThread->threadIsInitialized; - theWorkerThread->sharedData.Leave(); - wxMilliSleep(5); - } //-> it's not nice, but works and is no issue -} - - -UpdateWhileExecuting::~UpdateWhileExecuting() -{ - //wait until thread is ready, then start and exit immediately - readyToReceiveResult.Unlock(); //avoid possible deadlock, when thread might be waiting to send the signal - - theWorkerThread->readyToBeginProcessing.Lock(); - //set flag to exit thread - theWorkerThread->threadExitIsRequested = true; - theWorkerThread->beginProcessing.Signal(); - - theWorkerThread->readyToBeginProcessing.Unlock(); - //theWorkerThread deletes itself! -} - - -void UpdateWhileExecuting::waitUntilReady() -{ - readyToReceiveResult.Unlock(); //avoid possible deadlock, when thread might be waiting to send the signal (if abort was pressed) - - theWorkerThread->readyToBeginProcessing.Lock(); - - workDone = false; //no mutex needed here (worker thread that changes this variable is in waiting state) -} -// /|\ \|/ must be called directly after each other - -void UpdateWhileExecuting::execute(StatusHandler* statusUpdater) -{ - readyToReceiveResult.Lock(); - - theWorkerThread->beginProcessing.Signal(); - theWorkerThread->readyToBeginProcessing.Unlock(); - - while (receivingResult.WaitTimeout(UI_UPDATE_INTERVAL) == wxCOND_TIMEOUT) - { - statusUpdater->requestUiRefresh(false); //don't allow throwing exception within this call - - if (workDone) //workaround for a bug in wxWidgets v2.8.9 class wxCondition: signals might get lost - break; //no mutex for workDone needed here: it is changed only when mainthread is in WaitTimeout() - } -} - - - -// ------------------------------------------------------ -// |Pattern: workload queue and multiple worker threads | -// ------------------------------------------------------ -//typedef std::vector<DirectoryDescrType*> Workload; -// -//class ThreadSorting : public wxThread -//{ -//public: -// ThreadSorting(wxCriticalSection& syncWorkload, Workload& workload) : -// wxThread(wxTHREAD_JOINABLE), -// syncWorkload_(syncWorkload), -// workload_(workload) -// { -// if (Create() != wxTHREAD_NO_ERROR) -// throw RuntimeException(wxString(wxT("Error creating thread for sorting!"))); -// } -// -// ~ThreadSorting() {} -// -// -// ExitCode Entry() -// { -// while (true) -// { -// DirectoryDescrType* descr = NULL; -// { //see if there is work to do... -// wxCriticalSectionLocker dummy(syncWorkload_); -// if (workload_.empty()) -// return 0; -// else -// { -// descr = workload_.back(); -// workload_.pop_back(); -// } -// } -// //do work -// std::sort(descr->begin(), descr->end()); -// } -// } -// -//private: -// wxCriticalSection& syncWorkload_; -// Workload& workload_; -//}; -// -// -//void DirectoryDescrBuffer::preFillBuffers(const std::vector<FolderPairCfg>& fpConfigFormatted) -//{ -// //assemble workload -// ... -// -// //we use binary search when comparing the directory structures: so sort() first -// const int CPUCount = wxThread::GetCPUCount(); -// if (CPUCount >= 2) //do it the multithreaded way: -// { -// wxCriticalSection syncWorkload; -// -// typedef std::vector<boost::shared_ptr<ThreadSorting> > ThreadContainer; -// ThreadContainer sortThreads; -// sortThreads.reserve(CPUCount); -// -// //start CPUCount worker threads -// for (size_t i = 0; i < std::min(static_cast<size_t>(CPUCount), workload.size()); ++i) -// { -// boost::shared_ptr<ThreadSorting> newWorker(new ThreadSorting(syncWorkload, workload)); -// -// if (newWorker->Run() != wxTHREAD_NO_ERROR) -// throw RuntimeException(wxString(wxT("Error starting thread for sorting!"))); -// -// sortThreads.push_back(newWorker); -// } -// -// //wait until all worker are finished -// for (ThreadContainer::iterator i = sortThreads.begin(); i != sortThreads.end(); ++i) -// { -// if ((*i)->Wait() != 0) -// throw RuntimeException(wxString(wxT("Error waiting for thread (sorting)!"))); -// } -// } -// else //single threaded -// { -// for (Workload::iterator i = workload.begin(); i != workload.end(); ++i) -// std::sort((*i)->begin(), (*i)->end()); -// } -//} diff --git a/library/multithreading.h b/library/multithreading.h deleted file mode 100644 index 9017bf41..00000000 --- a/library/multithreading.h +++ /dev/null @@ -1,41 +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-2010 ZenJu (zhnmju123 AT gmx.de) * -// ************************************************************************** -// -#ifndef MULTITHREADING_H_INCLUDED -#define MULTITHREADING_H_INCLUDED - -#include <wx/thread.h> - -class StatusHandler; -class WorkerThread; - - -//class handling execution of a method while updating the UI -class UpdateWhileExecuting -{ - friend class WorkerThread; - -public: - UpdateWhileExecuting(); - - virtual ~UpdateWhileExecuting(); - - void waitUntilReady(); - void execute(StatusHandler* statusUpdater); - - -private: - //implement a longrunning method without dependencies (e.g. copy file function); share input/output parameters as instance variables - virtual void longRunner() = 0; - - WorkerThread* theWorkerThread; - - wxMutex readyToReceiveResult; - wxCondition receivingResult; - bool workDone; //workaround for a bug in wxWidgets v2.8.9 class wxCondition: signals might get lost -}; - -#endif // MULTITHREADING_H_INCLUDED diff --git a/library/pch.h b/library/pch.h index 1fb71f00..573ae73f 100644 --- a/library/pch.h +++ b/library/pch.h @@ -18,6 +18,10 @@ do NOT use in release build! #define WX_PRECOMP #endif +#ifdef _MSC_VER +#pragma warning(disable:4996) //"warning C4996: 'std::copy': Function call with parameters that may be unsafe" +#endif + #include <wx/wxprec.h> //##################################################### diff --git a/library/processXml.cpp b/library/processXml.cpp index 4ecc0350..c9b641fb 100644 --- a/library/processXml.cpp +++ b/library/processXml.cpp @@ -215,7 +215,26 @@ bool readXmlElement(const std::string& name, const TiXmlElement* parent , FreeFi return true; } + return false; +} + + +bool readXmlElement(const std::string& name, const TiXmlElement* parent , FreeFileSync::SymLinkHandling& output) +{ + std::string dummy; + if (xmlAccess::readXmlElement(name, parent, dummy)) + { + if (dummy == "Ignore") + output = FreeFileSync::SYMLINK_IGNORE; + else if (dummy == "UseDirectly") + output = FreeFileSync::SYMLINK_USE_DIRECTLY; + else if (dummy == "FollowLink") + output = FreeFileSync::SYMLINK_FOLLOW_LINK; + else + return false; + return true; + } return false; } @@ -233,7 +252,7 @@ bool readXmlElement(const std::string& name, const TiXmlElement* parent, Zstring bool readXmlAttribute(const std::string& name, const TiXmlElement* node, xmlAccess::ColumnTypes& output) { - int dummy; + int dummy = 0; if (xmlAccess::readXmlAttribute(name, node, dummy)) { output = static_cast<xmlAccess::ColumnTypes>(dummy); @@ -303,10 +322,7 @@ void FfsXmlParser::readXmlMainConfig(MainConfiguration& mainCfg) readXmlElementLogging("FileTimeTolerance", cmpSettings, mainCfg.hidden.fileTimeTolerance); //include symbolic links at all? - readXmlElementLogging("IncludeSymlinks", cmpSettings, mainCfg.processSymlinks); - - //traverse into symbolic links (to folders) - readXmlElementLogging("TraverseDirectorySymlinks", cmpSettings, mainCfg.traverseDirectorySymlinks); + readXmlElementLogging("HandleSymlinks", cmpSettings, mainCfg.handleSymlinks); //########################################################### const TiXmlElement* syncCfg = hRoot.FirstChild("MainConfig").FirstChild("Synchronization").ToElement(); @@ -324,9 +340,6 @@ void FfsXmlParser::readXmlMainConfig(MainConfiguration& mainCfg) //########################################################### const TiXmlElement* syncConfig = hRoot.FirstChild("MainConfig").FirstChild("Synchronization").ToElement(); - //copy symbolic links to files - readXmlElementLogging("CopyFileSymlinks", syncConfig, mainCfg.copyFileSymlinks); - //verify file copying readXmlElementLogging("VerifyCopiedFiles", syncConfig, mainCfg.hidden.verifyFileCopy); @@ -607,6 +620,23 @@ void addXmlElement(const std::string& name, const FreeFileSync::DeletionPolicy v } +void addXmlElement(const std::string& name, const FreeFileSync::SymLinkHandling value, TiXmlElement* parent) +{ + switch (value) + { + case FreeFileSync::SYMLINK_IGNORE: + xmlAccess::addXmlElement(name, std::string("Ignore"), parent); + break; + case FreeFileSync::SYMLINK_USE_DIRECTLY: + xmlAccess::addXmlElement(name, std::string("UseDirectly"), parent); + break; + case FreeFileSync::SYMLINK_FOLLOW_LINK: + xmlAccess::addXmlElement(name, std::string("FollowLink"), parent); + break; + } +} + + void addXmlElement(const std::string& name, const Zstring& value, TiXmlElement* parent) { xmlAccess::addXmlElement(name, wxString(zToWx(value)), parent); @@ -696,10 +726,7 @@ bool writeXmlMainConfig(const MainConfiguration& mainCfg, TiXmlDocument& doc) addXmlElement("FileTimeTolerance", mainCfgLocal.hidden.fileTimeTolerance, cmpSettings); //include symbolic links at all? - addXmlElement("IncludeSymlinks", mainCfgLocal.processSymlinks, cmpSettings); - - //traverse into symbolic links (to folders) - addXmlElement("TraverseDirectorySymlinks", mainCfgLocal.traverseDirectorySymlinks, cmpSettings); + addXmlElement("HandleSymlinks", mainCfgLocal.handleSymlinks, cmpSettings); //########################################################### TiXmlElement* syncSettings = new TiXmlElement("Synchronization"); @@ -719,9 +746,6 @@ bool writeXmlMainConfig(const MainConfiguration& mainCfg, TiXmlDocument& doc) addXmlElement("Conflict", mainCfgLocal.syncConfiguration.conflict, syncDirections); //########################################################### - //copy symbolic links to files - addXmlElement("CopyFileSymlinks", mainCfgLocal.copyFileSymlinks, syncSettings); - //verify file copying addXmlElement("VerifyCopiedFiles", mainCfgLocal.hidden.verifyFileCopy, syncSettings); diff --git a/library/statistics.cpp b/library/statistics.cpp index fc140add..3a6970d9 100644 --- a/library/statistics.cpp +++ b/library/statistics.cpp @@ -31,11 +31,12 @@ RetrieveStatistics::~RetrieveStatistics() for (std::vector<statEntry>::const_iterator i = data.begin(); i != data.end(); ++i) { - outputFile.Write(FreeFileSync::numberToWxString(static_cast<int>(i->time), false)); + using globalFunctions::numberToString; + outputFile.Write(numberToString(i->time)); outputFile.Write(wxT(";")); - outputFile.Write(FreeFileSync::numberToWxString(i->objects, false)); + outputFile.Write(numberToString(i->objects)); outputFile.Write(wxT(";")); - outputFile.Write(FreeFileSync::numberToWxString(static_cast<int>(i->value), false)); + outputFile.Write(numberToString(i->value)); outputFile.Write(wxT("\n")); } } @@ -63,6 +64,10 @@ bool isNull(double number) inline wxString Statistics::formatRemainingTime(double timeInMs) const { +#ifndef _MSC_VER +#warning adapt units "%x sec" +#endif + bool unitSec = true; double remainingTime = timeInMs / 1000; wxString unit = _(" sec"); @@ -83,7 +88,6 @@ wxString Statistics::formatRemainingTime(double timeInMs) const } } - int formattedTime = globalFunctions::round(remainingTime); //reduce precision to 5 seconds @@ -104,7 +108,7 @@ wxString Statistics::formatRemainingTime(double timeInMs) const } remainingTimeLast = formattedTime; - return FreeFileSync::numberToWxString(formattedTime, false) + unit; + return globalFunctions::numberToString(formattedTime) + unit; //+ wxT("(") + globalFunctions::numberToWxString(globalFunctions::round(timeInMs / 1000)) + wxT(")"); } |