diff options
author | Daniel Wilhelm <daniel@wili.li> | 2014-04-18 16:56:34 +0200 |
---|---|---|
committer | Daniel Wilhelm <daniel@wili.li> | 2014-04-18 16:56:34 +0200 |
commit | 9084fa27f0f43cfa31dbc3a7ef87e2600c2dc3ca (patch) | |
tree | 61e2edc315a164d6fa3940b7de4b14dda0a9838c /library | |
parent | 1.15 (diff) | |
download | FreeFileSync-9084fa27f0f43cfa31dbc3a7ef87e2600c2dc3ca.tar.gz FreeFileSync-9084fa27f0f43cfa31dbc3a7ef87e2600c2dc3ca.tar.bz2 FreeFileSync-9084fa27f0f43cfa31dbc3a7ef87e2600c2dc3ca.zip |
1.16
Diffstat (limited to 'library')
-rw-r--r-- | library/CustomGrid.cpp | 252 | ||||
-rw-r--r-- | library/CustomGrid.h | 17 | ||||
-rw-r--r-- | library/customButton.cpp | 20 | ||||
-rw-r--r-- | library/customButton.h | 6 | ||||
-rw-r--r-- | library/fileHandling.cpp | 342 | ||||
-rw-r--r-- | library/fileHandling.h | 35 | ||||
-rw-r--r-- | library/globalFunctions.cpp | 2 | ||||
-rw-r--r-- | library/misc.cpp | 12 | ||||
-rw-r--r-- | library/processXml.cpp | 216 | ||||
-rw-r--r-- | library/processXml.h | 90 | ||||
-rw-r--r-- | library/resources.cpp | 6 | ||||
-rw-r--r-- | library/resources.h | 4 | ||||
-rw-r--r-- | library/sorting.h | 109 | ||||
-rw-r--r-- | library/statusHandler.h | 13 | ||||
-rw-r--r-- | library/zstring.cpp | 28 | ||||
-rw-r--r-- | library/zstring.h | 9 |
16 files changed, 731 insertions, 430 deletions
diff --git a/library/CustomGrid.cpp b/library/CustomGrid.cpp index 95300a2b..cf9adc6e 100644 --- a/library/CustomGrid.cpp +++ b/library/CustomGrid.cpp @@ -5,8 +5,6 @@ #include "../algorithm.h" #include "resources.h" -using namespace xmlAccess; - const unsigned int MIN_ROW_COUNT = 15; @@ -16,8 +14,11 @@ class CustomGridTable : public wxGridTableBase public: CustomGridTable() : wxGridTableBase(), - lightBlue(80, 110, 255), - lightGrey(212, 208, 200), + blue(80, 110, 255), + grey(212, 208, 200), + lightRed(235, 57, 61), + lightBlue(63, 206, 233), + lightGreen(54, 218, 2), gridRefUI(NULL), gridData(NULL), lastNrRows(0), @@ -186,29 +187,32 @@ public: } - void setupColumns(const std::vector<xmlAccess::XmlGlobalSettings::ColumnTypes>& positions) + void setupColumns(const std::vector<xmlAccess::ColumnTypes>& positions) { columnPositions = positions; updateGridSizes(); //add or remove columns } - XmlGlobalSettings::ColumnTypes getTypeAtPos(unsigned pos) const + xmlAccess::ColumnTypes getTypeAtPos(unsigned pos) const { if (pos < columnPositions.size()) return columnPositions[pos]; else - return XmlGlobalSettings::ColumnTypes(1000); + return xmlAccess::ColumnTypes(1000); } protected: virtual const wxColour& getRowColor(int row) = 0; //rows that are filtered out are shown in different color - std::vector<xmlAccess::XmlGlobalSettings::ColumnTypes> columnPositions; + std::vector<xmlAccess::ColumnTypes> columnPositions; + wxColour blue; + wxColour grey; + wxColour lightRed; wxColour lightBlue; - wxColour lightGrey; + wxColour lightGreen; GridView* gridRefUI; //(very fast) access to underlying grid data :) FileCompareResult* gridData; int lastNrRows; @@ -230,10 +234,10 @@ public: //mark filtered rows if (!cmpLine.selectedForSynchronization) - return lightBlue; + return blue; //mark directories else if (cmpLine.fileDescrLeft.objType == FileDescrLine::TYPE_DIRECTORY) - return lightGrey; + return grey; else return *wxWHITE; } @@ -254,13 +258,15 @@ public: { switch (getTypeAtPos(col)) { - case XmlGlobalSettings::FILENAME: //filename + case xmlAccess::FULL_NAME: + return gridLine.fileDescrLeft.fullName.c_str(); + case xmlAccess::FILENAME: //filename return wxEmptyString; - case XmlGlobalSettings::REL_PATH: //relative path + case xmlAccess::REL_PATH: //relative path return gridLine.fileDescrLeft.relativeName.c_str(); - case XmlGlobalSettings::SIZE: //file size + case xmlAccess::SIZE: //file size return _("<Directory>"); - case XmlGlobalSettings::DATE: //date + case xmlAccess::DATE: //date return wxEmptyString; } } @@ -268,16 +274,18 @@ public: { switch (getTypeAtPos(col)) { - case XmlGlobalSettings::FILENAME: //filename + case xmlAccess::FULL_NAME: + return gridLine.fileDescrLeft.fullName.c_str(); + case xmlAccess::FILENAME: //filename return gridLine.fileDescrLeft.relativeName.AfterLast(GlobalResources::FILE_NAME_SEPARATOR).c_str(); - case XmlGlobalSettings::REL_PATH: //relative path + case xmlAccess::REL_PATH: //relative path return gridLine.fileDescrLeft.relativeName.BeforeLast(GlobalResources::FILE_NAME_SEPARATOR).c_str(); - case XmlGlobalSettings::SIZE: //file size + case xmlAccess::SIZE: //file size { wxString fileSize = gridLine.fileDescrLeft.fileSize.ToString(); //tmp string return globalFunctions::includeNumberSeparator(fileSize); } - case XmlGlobalSettings::DATE: //date + case xmlAccess::DATE: //date return FreeFileSync::utcTimeToLocalString(gridLine.fileDescrLeft.lastWriteTimeRaw); } } @@ -320,8 +328,7 @@ public: } - //virtual impl. - 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 { if (gridRefUI && unsigned(row) < gridRefUI->size()) { @@ -329,15 +336,27 @@ public: //mark filtered rows if (!cmpLine.selectedForSynchronization) - return lightBlue; + return blue; else - return *wxWHITE; + switch (cmpLine.cmpResult) + { + case FILE_LEFT_SIDE_ONLY: + case FILE_RIGHT_SIDE_ONLY: + return lightGreen; + case FILE_LEFT_NEWER: + case FILE_RIGHT_NEWER: + return lightBlue; + case FILE_DIFFERENT: + return lightRed; + default: + return *wxWHITE; + } } return *wxWHITE; } - //virtual impl. - wxString GetValue(int row, int col) + + virtual wxString GetValue(int row, int col) { if (gridRefUI) { @@ -377,8 +396,7 @@ public: CustomGridTableRight() : CustomGridTable() {} ~CustomGridTableRight() {} - //virtual impl. - 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 { if (gridRefUI && unsigned(row) < gridRefUI->size()) { @@ -386,10 +404,10 @@ public: //mark filtered rows if (!cmpLine.selectedForSynchronization) - return lightBlue; + return blue; //mark directories else if (cmpLine.fileDescrRight.objType == FileDescrLine::TYPE_DIRECTORY) - return lightGrey; + return grey; else return *wxWHITE; } @@ -409,13 +427,15 @@ public: { switch (getTypeAtPos(col)) { - case XmlGlobalSettings::FILENAME: //filename + case xmlAccess::FULL_NAME: + return gridLine.fileDescrRight.fullName.c_str(); + case xmlAccess::FILENAME: //filename return wxEmptyString; - case XmlGlobalSettings::REL_PATH: //relative path + case xmlAccess::REL_PATH: //relative path return gridLine.fileDescrRight.relativeName.c_str(); - case XmlGlobalSettings::SIZE: //file size + case xmlAccess::SIZE: //file size return _("<Directory>"); - case XmlGlobalSettings::DATE: //date + case xmlAccess::DATE: //date return wxEmptyString; } } @@ -423,16 +443,18 @@ public: { switch (getTypeAtPos(col)) { - case XmlGlobalSettings::FILENAME: //filename + case xmlAccess::FULL_NAME: + return gridLine.fileDescrRight.fullName.c_str(); + case xmlAccess::FILENAME: //filename return gridLine.fileDescrRight.relativeName.AfterLast(GlobalResources::FILE_NAME_SEPARATOR).c_str(); - case XmlGlobalSettings::REL_PATH: //relative path + case xmlAccess::REL_PATH: //relative path return gridLine.fileDescrRight.relativeName.BeforeLast(GlobalResources::FILE_NAME_SEPARATOR).c_str(); - case XmlGlobalSettings::SIZE: //file size + case xmlAccess::SIZE: //file size { wxString fileSize = gridLine.fileDescrRight.fileSize.ToString(); //tmp string return globalFunctions::includeNumberSeparator(fileSize); } - case XmlGlobalSettings::DATE: //date + case xmlAccess::DATE: //date return FreeFileSync::utcTimeToLocalString(gridLine.fileDescrRight.lastWriteTimeRaw); } } @@ -454,6 +476,7 @@ CustomGrid::CustomGrid(wxWindow *parent, long style, const wxString& name) : wxGrid(parent, id, pos, size, style, name), + leadGrid(NULL), scrollbarsEnabled(true), m_gridLeft(NULL), m_gridRight(NULL), m_gridMiddle(NULL), gridDataTable(NULL), @@ -514,17 +537,17 @@ void CustomGrid::adjustGridHeights() //m_gridLeft, m_gridRight, m_gridMiddle are { int yMax = std::max(y1, std::max(y2, y3)); - if (::leadGrid == m_gridLeft) //do not handle case (y1 == yMax) here!!! Avoid back coupling! + if (leadGrid == m_gridLeft) //do not handle case (y1 == yMax) here!!! Avoid back coupling! m_gridLeft->SetMargins(0, 0); else if (y1 < yMax) m_gridLeft->SetMargins(0, 50); - if (::leadGrid == m_gridRight) + if (leadGrid == m_gridRight) m_gridRight->SetMargins(0, 0); else if (y2 < yMax) m_gridRight->SetMargins(0, 50); - if (::leadGrid == m_gridMiddle) + if (leadGrid == m_gridMiddle) m_gridMiddle->SetMargins(0, 0); else if (y3 < yMax) m_gridMiddle->SetMargins(0, 50); @@ -536,6 +559,12 @@ void CustomGrid::adjustGridHeights() //m_gridLeft, m_gridRight, m_gridMiddle are } +void CustomGrid::setLeadGrid(const wxGrid* newLead) +{ + leadGrid = newLead; +} + + void CustomGrid::updateGridSizes() { assert(gridDataTable); @@ -559,11 +588,66 @@ void CustomGrid::DrawColLabel(wxDC& dc, int col) } -void CustomGrid::setColumnAttributes(const xmlAccess::XmlGlobalSettings::ColumnAttributes& attr) +xmlAccess::ColumnAttributes CustomGrid::getDefaultColumnAttributes() +{ + xmlAccess::ColumnAttributes defaultColumnSettings; + + xmlAccess::ColumnAttrib newEntry; + + newEntry.type = xmlAccess::FULL_NAME; + newEntry.visible = false; + newEntry.position = 0; + newEntry.width = 150; + defaultColumnSettings.push_back(newEntry); + + newEntry.type = xmlAccess::FILENAME; + newEntry.visible = true; + newEntry.position = 1; + newEntry.width = 138; + defaultColumnSettings.push_back(newEntry); + + newEntry.type = xmlAccess::REL_PATH; + newEntry.position = 2; + newEntry.width = 118; + defaultColumnSettings.push_back(newEntry); + + newEntry.type = xmlAccess::SIZE; + newEntry.position = 3; + newEntry.width = 67; + defaultColumnSettings.push_back(newEntry); + + newEntry.type = xmlAccess::DATE; + newEntry.position = 4; + newEntry.width = 113; + defaultColumnSettings.push_back(newEntry); + + return defaultColumnSettings; +} + + +xmlAccess::ColumnAttributes CustomGrid::getColumnAttributes() +{ + std::sort(columnSettings.begin(), columnSettings.end(), xmlAccess::sortByPositionAndVisibility); + + xmlAccess::ColumnAttributes output; + xmlAccess::ColumnAttrib newEntry; + for (unsigned int i = 0; i < columnSettings.size(); ++i) + { + newEntry = columnSettings[i]; + if (newEntry.visible) + newEntry.width = GetColSize(i); //hidden columns are sorted to the end of vector! + output.push_back(newEntry); + } + + return output; +} + + +void CustomGrid::setColumnAttributes(const xmlAccess::ColumnAttributes& attr) { //remove special alignment for column "size" for (int i = 0; i < GetNumberCols(); ++i) - if (getTypeAtPos(i) == XmlGlobalSettings::SIZE) + if (getTypeAtPos(i) == xmlAccess::SIZE) { wxGridCellAttr* cellAttributes = GetOrCreateCellAttr(0, i); cellAttributes->SetAlignment(wxALIGN_LEFT,wxALIGN_CENTRE); @@ -571,44 +655,23 @@ void CustomGrid::setColumnAttributes(const xmlAccess::XmlGlobalSettings::ColumnA break; } //---------------------------------------------------------------------------------- - setSortMarker(-1); //clear sorting marker columnSettings.clear(); if (attr.size() == 0) { //default settings: - xmlAccess::XmlGlobalSettings::ColumnAttrib newEntry; - newEntry.type = xmlAccess::XmlGlobalSettings::FILENAME; - newEntry.visible = true; - newEntry.position = 0; - newEntry.width = 138; - columnSettings.push_back(newEntry); - - newEntry.type = xmlAccess::XmlGlobalSettings::REL_PATH; - newEntry.position = 1; - newEntry.width = 118; - columnSettings.push_back(newEntry); - - newEntry.type = xmlAccess::XmlGlobalSettings::SIZE; - newEntry.position = 2; - newEntry.width = 67; - columnSettings.push_back(newEntry); - - newEntry.type = xmlAccess::XmlGlobalSettings::DATE; - newEntry.position = 3; - newEntry.width = 113; - columnSettings.push_back(newEntry); + columnSettings = getDefaultColumnAttributes(); } else { - for (unsigned int i = 0; i < COLUMN_TYPE_COUNT; ++i) + for (unsigned int i = 0; i < xmlAccess::COLUMN_TYPE_COUNT; ++i) { - XmlGlobalSettings::ColumnAttrib newEntry; + xmlAccess::ColumnAttrib newEntry; if (i < attr.size()) newEntry = attr[i]; else { - newEntry.type = xmlAccess::XmlGlobalSettings::FILENAME; + newEntry.type = xmlAccess::FILENAME; newEntry.visible = true; newEntry.position = i; newEntry.width = 100; @@ -616,18 +679,17 @@ void CustomGrid::setColumnAttributes(const xmlAccess::XmlGlobalSettings::ColumnA columnSettings.push_back(newEntry); } - sort(columnSettings.begin(), columnSettings.end(), xmlAccess::sortByType); - for (unsigned int i = 0; i < COLUMN_TYPE_COUNT; ++i) //just be sure that each type exists only once - columnSettings[i].type = XmlGlobalSettings::ColumnTypes(i); + std::sort(columnSettings.begin(), columnSettings.end(), xmlAccess::sortByType); + for (unsigned int i = 0; i < xmlAccess::COLUMN_TYPE_COUNT; ++i) //just be sure that each type exists only once + columnSettings[i].type = xmlAccess::ColumnTypes(i); - sort(columnSettings.begin(), columnSettings.end(), xmlAccess::sortByPositionOnly); - for (unsigned int i = 0; i < COLUMN_TYPE_COUNT; ++i) //just be sure that positions are numbered correctly + std::sort(columnSettings.begin(), columnSettings.end(), xmlAccess::sortByPositionOnly); + for (unsigned int i = 0; i < xmlAccess::COLUMN_TYPE_COUNT; ++i) //just be sure that positions are numbered correctly columnSettings[i].position = i; - - sort(columnSettings.begin(), columnSettings.end(), xmlAccess::sortByPositionAndVisibility); } - std::vector<XmlGlobalSettings::ColumnTypes> newPositions; + std::sort(columnSettings.begin(), columnSettings.end(), xmlAccess::sortByPositionAndVisibility); + std::vector<xmlAccess::ColumnTypes> newPositions; for (unsigned int i = 0; i < columnSettings.size() && columnSettings[i].visible; ++i) //hidden columns are sorted to the end of vector! newPositions.push_back(columnSettings[i].type); @@ -642,7 +704,7 @@ void CustomGrid::setColumnAttributes(const xmlAccess::XmlGlobalSettings::ColumnA //-------------------------------------------------------------------------------------------------------- //set special alignment for column "size" for (int i = 0; i < GetNumberCols(); ++i) - if (getTypeAtPos(i) == XmlGlobalSettings::SIZE) + if (getTypeAtPos(i) == xmlAccess::SIZE) { wxGridCellAttr* cellAttributes = GetOrCreateCellAttr(0, i); cellAttributes->SetAlignment(wxALIGN_RIGHT,wxALIGN_CENTRE); @@ -655,42 +717,26 @@ void CustomGrid::setColumnAttributes(const xmlAccess::XmlGlobalSettings::ColumnA } -xmlAccess::XmlGlobalSettings::ColumnAttributes CustomGrid::getColumnAttributes() -{ - sort(columnSettings.begin(), columnSettings.end(), xmlAccess::sortByPositionAndVisibility); - - xmlAccess::XmlGlobalSettings::ColumnAttributes output; - xmlAccess::XmlGlobalSettings::ColumnAttrib newEntry; - for (unsigned int i = 0; i < columnSettings.size(); ++i) - { - newEntry = columnSettings[i]; - if (newEntry.visible) //hidden columns are sorted to the end of vector! - newEntry.width = GetColSize(i); - output.push_back(newEntry); - } - - return output; -} - - -XmlGlobalSettings::ColumnTypes CustomGrid::getTypeAtPos(unsigned pos) const +xmlAccess::ColumnTypes CustomGrid::getTypeAtPos(unsigned pos) const { assert(gridDataTable); return gridDataTable->getTypeAtPos(pos); } -wxString CustomGrid::getTypeName(XmlGlobalSettings::ColumnTypes colType) +wxString CustomGrid::getTypeName(xmlAccess::ColumnTypes colType) { switch (colType) { - case XmlGlobalSettings::FILENAME: + case xmlAccess::FULL_NAME: + return _("Full name"); + case xmlAccess::FILENAME: return _("Filename"); - case XmlGlobalSettings::REL_PATH: + case xmlAccess::REL_PATH: return _("Relative path"); - case XmlGlobalSettings::SIZE: + case xmlAccess::SIZE: return _("Size"); - case XmlGlobalSettings::DATE: + case xmlAccess::DATE: return _("Date"); default: return wxEmptyString; @@ -725,7 +771,7 @@ void CustomGridLeft::DoPrepareDC(wxDC& dc) wxScrollHelper::DoPrepareDC(dc); int x, y = 0; - if (this == ::leadGrid) //avoid back coupling + if (this == leadGrid) //avoid back coupling { GetViewStart(&x, &y); m_gridMiddle->Scroll(-1, y); //scroll in y-direction only @@ -764,7 +810,7 @@ void CustomGridMiddle::DoPrepareDC(wxDC& dc) wxScrollHelper::DoPrepareDC(dc); int x, y = 0; - if (this == ::leadGrid) //avoid back coupling + if (this == leadGrid) //avoid back coupling { GetViewStart(&x, &y); m_gridLeft->Scroll(-1, y); @@ -836,7 +882,7 @@ void CustomGridRight::DoPrepareDC(wxDC& dc) wxScrollHelper::DoPrepareDC(dc); int x, y = 0; - if (this == ::leadGrid) //avoid back coupling + if (this == leadGrid) //avoid back coupling { GetViewStart(&x, &y); m_gridLeft->Scroll(x, y); diff --git a/library/CustomGrid.h b/library/CustomGrid.h index 97642159..8f392975 100644 --- a/library/CustomGrid.h +++ b/library/CustomGrid.h @@ -13,8 +13,6 @@ class CustomGridTable; class CustomGridTableMiddle; //################################################################################## -extern const wxGrid* leadGrid; //this global variable is not very nice... - class CustomGrid : public wxGrid { public: @@ -46,22 +44,23 @@ public: void setSortMarker(const int sortColumn, const wxBitmap* bitmap = &wxNullBitmap); //set visibility, position and width of columns - void setColumnAttributes(const xmlAccess::XmlGlobalSettings::ColumnAttributes& attr); - xmlAccess::XmlGlobalSettings::ColumnAttributes getColumnAttributes(); + static xmlAccess::ColumnAttributes getDefaultColumnAttributes(); + xmlAccess::ColumnAttributes getColumnAttributes(); + void setColumnAttributes(const xmlAccess::ColumnAttributes& attr); - xmlAccess::XmlGlobalSettings::ColumnTypes getTypeAtPos(unsigned pos) const; + xmlAccess::ColumnTypes getTypeAtPos(unsigned pos) const; - static wxString getTypeName(xmlAccess::XmlGlobalSettings::ColumnTypes colType); + static wxString getTypeName(xmlAccess::ColumnTypes colType); - static const unsigned COLUMN_TYPE_COUNT = 4; + void setLeadGrid(const wxGrid* newLead); protected: //set visibility, position and width of columns - xmlAccess::XmlGlobalSettings::ColumnAttributes columnSettings; - + xmlAccess::ColumnAttributes columnSettings; void adjustGridHeights(); + const wxGrid* leadGrid; //grid that has user focus bool scrollbarsEnabled; CustomGrid* m_gridLeft; CustomGrid* m_gridRight; diff --git a/library/customButton.cpp b/library/customButton.cpp index 5cfa8a5a..02cfdad0 100644 --- a/library/customButton.cpp +++ b/library/customButton.cpp @@ -10,15 +10,18 @@ wxButtonWithImage::wxButtonWithImage(wxWindow *parent, long style, const wxValidator& validator, const wxString& name) : - wxBitmapButton(parent, id, wxNullBitmap, pos, size, style | wxBU_AUTODRAW, validator, name) + wxBitmapButton(parent, id, wxNullBitmap, pos, size, style | wxBU_AUTODRAW, validator, name), + m_spaceAfter(0), + m_spaceBefore(0) { setTextLabel(label); } -void wxButtonWithImage::setBitmapFront(const wxBitmap& bitmap) +void wxButtonWithImage::setBitmapFront(const wxBitmap& bitmap, unsigned spaceAfter) { - bitmapFront = bitmap; + bitmapFront = bitmap; + m_spaceAfter = spaceAfter; refreshButtonLabel(); } @@ -31,9 +34,10 @@ void wxButtonWithImage::setTextLabel(const wxString& text) } -void wxButtonWithImage::setBitmapBack(const wxBitmap& bitmap) +void wxButtonWithImage::setBitmapBack(const wxBitmap& bitmap, unsigned spaceBefore) { - bitmapBack = bitmap; + bitmapBack = bitmap; + m_spaceBefore = spaceBefore; refreshButtonLabel(); } @@ -258,7 +262,7 @@ void wxButtonWithImage::refreshButtonLabel() //calculate dimensions of new button const int height = std::max(std::max(bitmapFront.GetHeight(), bitmapText.GetHeight()), bitmapBack.GetHeight()); - const int width = bitmapFront.GetWidth() + bitmapText.GetWidth() + bitmapBack.GetWidth(); + const int width = bitmapFront.GetWidth() + m_spaceAfter + bitmapText.GetWidth() + m_spaceBefore + bitmapBack.GetWidth(); //create a transparent image wxImage transparentImage(width, height, false); @@ -274,12 +278,12 @@ void wxButtonWithImage::refreshButtonLabel() if (bitmapText.IsOk()) writeToImage(wxImage(bitmapText.ConvertToImage()), - wxPoint(bitmapFront.GetWidth(), (transparentImage.GetHeight() - bitmapText.GetHeight()) / 2), + wxPoint(bitmapFront.GetWidth() + m_spaceAfter, (transparentImage.GetHeight() - bitmapText.GetHeight()) / 2), transparentImage); if (bitmapBack.IsOk()) writeToImage(wxImage(bitmapBack.ConvertToImage()), - wxPoint(bitmapFront.GetWidth() + bitmapText.GetWidth(), (transparentImage.GetHeight() - bitmapBack.GetHeight()) / 2), + wxPoint(bitmapFront.GetWidth() + m_spaceAfter + bitmapText.GetWidth() + m_spaceBefore, (transparentImage.GetHeight() - bitmapBack.GetHeight()) / 2), transparentImage); //adjust button size diff --git a/library/customButton.h b/library/customButton.h index 1f3f50ec..ce43828a 100644 --- a/library/customButton.h +++ b/library/customButton.h @@ -23,16 +23,18 @@ public: const wxValidator& validator = wxDefaultValidator, const wxString& name = wxButtonNameStr); - void setBitmapFront(const wxBitmap& bitmap); + void setBitmapFront(const wxBitmap& bitmap, unsigned spaceAfter = 0); void setTextLabel( const wxString& text); - void setBitmapBack( const wxBitmap& bitmap); + void setBitmapBack( const wxBitmap& bitmap, unsigned spaceBefore = 0); private: wxBitmap createBitmapFromText(const wxString& text); void refreshButtonLabel(); wxBitmap bitmapFront; + unsigned m_spaceAfter; wxString textLabel; + unsigned m_spaceBefore; wxBitmap bitmapBack; }; diff --git a/library/fileHandling.cpp b/library/fileHandling.cpp index 688d3640..cda81ef5 100644 --- a/library/fileHandling.cpp +++ b/library/fileHandling.cpp @@ -1,19 +1,13 @@ #include "fileHandling.h" #include <wx/intl.h> #include <wx/msgdlg.h> -#include "resources.h" +#include "../algorithm.h" +#include <wx/filename.h> #ifdef FFS_WIN #include <wx/msw/wrapwin.h> //includes "windows.h" #endif // FFS_WIN -inline -bool endsWithPathSeparator(const Zstring& name) -{ - const size_t len = name.length(); - return len && (name[len - 1] == GlobalResources::FILE_NAME_SEPARATOR); -} - class RecycleBin { @@ -89,19 +83,30 @@ void FreeFileSync::removeFile(const Zstring& filename, const bool useRecycleBin) if (useRecycleBin) { if (!moveToRecycleBin(filename)) - throw FileError(Zstring(_("Error moving to recycle bin:")) + wxT(" \"") + filename + wxT("\"")); + throw FileError(Zstring(_("Error moving to Recycle Bin:")) + wxT("\n\"") + filename + wxT("\"")); return; } #ifdef FFS_WIN + //initialize file attributes if (!SetFileAttributes( - filename.c_str(), //address of filename - FILE_ATTRIBUTE_NORMAL //attributes to set - )) throw FileError(Zstring(_("Error deleting file:")) + wxT(" \"") + filename + wxT("\"")); -#endif //FFS_WIN + filename.c_str(), //address of filename + FILE_ATTRIBUTE_NORMAL)) //attributes to set + { + Zstring errorMessage = Zstring(_("Error deleting file:")) + wxT("\n\"") + filename + wxT("\""); + throw FileError(errorMessage + wxT("\n\n") + FreeFileSync::getLastErrorFormatted()); + } + //remove file, support for \\?\-prefix + if (DeleteFile(filename.c_str()) == 0) + { + Zstring errorMessage = Zstring(_("Error deleting file:")) + wxT("\n\"") + filename + wxT("\""); + throw FileError(errorMessage + wxT("\n\n") + FreeFileSync::getLastErrorFormatted()); + } +#else if (!wxRemoveFile(filename.c_str())) - throw FileError(Zstring(_("Error deleting file:")) + wxT(" \"") + filename + wxT("\"")); + throw FileError(Zstring(_("Error deleting file:")) + wxT("\n\"") + filename + wxT("\"")); +#endif } @@ -112,53 +117,65 @@ void FreeFileSync::removeDirectory(const Zstring& directory, const bool useRecyc if (useRecycleBin) { if (!moveToRecycleBin(directory)) - throw FileError(Zstring(_("Error moving to recycle bin:")) + wxT(" \"") + directory + wxT("\"")); + throw FileError(Zstring(_("Error moving to Recycle Bin:")) + wxT("\n\"") + directory + wxT("\"")); return; } std::vector<Zstring> fileList; std::vector<Zstring> dirList; - try - { //should be executed in own scope so that directory access does not disturb deletion! - getAllFilesAndDirs(directory, fileList, dirList); - } - catch (const FileError& e) - { - throw FileError(Zstring(_("Error deleting directory:")) + wxT(" \"") + directory + wxT("\"")); - } + getAllFilesAndDirs(directory, fileList, dirList); for (unsigned int j = 0; j < fileList.size(); ++j) removeFile(fileList[j], false); - dirList.insert(dirList.begin(), directory); //this directory will be deleted last + dirList.insert(dirList.begin(), directory); //parent directory will be deleted last for (int j = int(dirList.size()) - 1; j >= 0 ; --j) { #ifdef FFS_WIN + //initialize file attributes if (!SetFileAttributes( dirList[j].c_str(), // address of directory name FILE_ATTRIBUTE_NORMAL)) // attributes to set - throw FileError(Zstring(_("Error deleting directory:")) + wxT(" \"") + dirList[j] + wxT("\"")); -#endif // FFS_WIN + { + Zstring errorMessage = Zstring(_("Error deleting directory:")) + wxT("\n\"") + dirList[j] + wxT("\""); + throw FileError(errorMessage + wxT("\n\n") + FreeFileSync::getLastErrorFormatted()); + } + //remove directory, support for \\?\-prefix + if (RemoveDirectory(dirList[j].c_str()) == 0) + { + Zstring errorMessage = Zstring(_("Error deleting directory:")) + wxT("\n\"") + dirList[j] + wxT("\""); + throw FileError(errorMessage + wxT("\n\n") + FreeFileSync::getLastErrorFormatted()); + } +#else if (!wxRmdir(dirList[j].c_str())) - throw FileError(Zstring(_("Error deleting directory:")) + wxT(" \"") + dirList[j] + wxT("\"")); + throw FileError(Zstring(_("Error deleting directory:")) + wxT("\n\"") + dirList[j] + wxT("\"")); +#endif } } void FreeFileSync::createDirectory(const Zstring& directory, const int level) { - if (wxDirExists(directory)) + if (wxDirExists(directory.c_str())) return; if (level == 50) //catch endless recursion return; - //try to create directory - if (wxMkdir(directory.c_str())) + //try to create directory, support for \\?\-prefix +#ifdef FFS_WIN + if (CreateDirectory( + directory.c_str(), // pointer to a directory path string + NULL // pointer to a security descriptor + ) != 0) + return; +#else + if (wxMkdir(directory.c_str())) //wxMkDir has different signature under Linux! return; +#endif //if not successfull try to create parent folders first Zstring parentDir; @@ -176,11 +193,25 @@ void FreeFileSync::createDirectory(const Zstring& directory, const int level) createDirectory(parentDir, level + 1); //now creation should be possible - if (!wxMkdir(directory.c_str())) +#ifdef FFS_WIN + if (CreateDirectory( + directory.c_str(), // pointer to a directory path string + NULL // pointer to a security descriptor + ) == 0) + { + if (level == 0) + { + Zstring errorMessage = Zstring(_("Error creating directory:")) + wxT("\n\"") + directory + wxT("\""); + throw FileError(errorMessage + wxT("\n\n") + FreeFileSync::getLastErrorFormatted()); + } + } +#else + if (!wxMkdir(directory.c_str())) //wxMkDir has different signature under Linux! { if (level == 0) - throw FileError(Zstring(_("Error creating directory:")) + wxT(" \"") + directory + wxT("\"")); + throw FileError(Zstring(_("Error creating directory:")) + wxT("\n\"") + directory + wxT("\"")); } +#endif } @@ -189,41 +220,44 @@ void FreeFileSync::copyFolderAttributes(const Zstring& source, const Zstring& ta #ifdef FFS_WIN DWORD attr = GetFileAttributes(source.c_str()); // address of the name of a file or directory if (attr == 0xFFFFFFFF) - throw FileError(Zstring(_("Error reading folder attributes:")) + wxT(" \"") + source + wxT("\"")); + { + Zstring errorMessage = Zstring(_("Error reading folder attributes:")) + wxT("\n\"") + source + wxT("\""); + throw FileError(errorMessage + wxT("\n\n") + FreeFileSync::getLastErrorFormatted()); + } if (!SetFileAttributes( target.c_str(), // address of filename attr)) // address of attributes to set - throw FileError(Zstring(_("Error writing folder attributes:")) + wxT(" \"") + target + wxT("\"")); + { + Zstring errorMessage = Zstring(_("Error writing folder attributes:")) + wxT("\n\"") + target + wxT("\""); + throw FileError(errorMessage + wxT("\n\n") + FreeFileSync::getLastErrorFormatted()); + } #elif defined FFS_LINUX //Linux doesn't use attributes for files or folders #endif } -class GetAllFilesSimple : public wxDirTraverser +class FilesDirsOnlyTraverser : public FreeFileSync::FullDetailFileTraverser { public: - GetAllFilesSimple(std::vector<Zstring>& files, std::vector<Zstring>& subDirectories) : + FilesDirsOnlyTraverser(std::vector<Zstring>& files, std::vector<Zstring>& dirs) : m_files(files), - m_dirs(subDirectories) {} + m_dirs(dirs) {} - wxDirTraverseResult OnDir(const wxString& dirname) + virtual wxDirTraverseResult OnFile(const Zstring& filename, const FreeFileSync::FileInfo& details) { - m_dirs.push_back(dirname.c_str()); + m_files.push_back(filename); return wxDIR_CONTINUE; } - - wxDirTraverseResult OnFile(const wxString& filename) + virtual wxDirTraverseResult OnDir(const Zstring& dirname) { - m_files.push_back(filename.c_str()); + m_dirs.push_back(dirname); return wxDIR_CONTINUE; } - - wxDirTraverseResult OnOpenError(const wxString& openerrorname) + virtual wxDirTraverseResult OnError(const Zstring& errorText) { - wxMessageBox(openerrorname, _("Error")); - return wxDIR_IGNORE; + throw FileError(errorText); } private: @@ -238,11 +272,8 @@ void FreeFileSync::getAllFilesAndDirs(const Zstring& sourceDir, std::vector<Zstr directories.clear(); //get all files and directories from current directory (and subdirectories) - wxDir dir(sourceDir.c_str()); - GetAllFilesSimple traverser(files, directories); - - if (dir.Traverse(traverser) == (size_t)-1) - throw FileError(Zstring(_("Error traversing directory:")) + wxT(" \"") + sourceDir + wxT("\"")); + FilesDirsOnlyTraverser traverser(files, directories); + traverseInDetail(sourceDir, false, &traverser); } @@ -285,7 +316,7 @@ public: { struct stat fileInfo; if (stat(filename.c_str(), &fileInfo) != 0) - return m_sink->OnError(Zstring(_("Could not retrieve file info for:")) + wxT(" \"") + filename.c_str() + wxT("\"")); + return m_sink->OnError(Zstring(_("Could not retrieve file info for:")) + wxT("\n\"") + filename.c_str() + wxT("\"")); FreeFileSync::FileInfo details; details.lastWriteTimeRaw = fileInfo.st_mtime; //UTC time(ANSI C format); unit: 1 second @@ -310,115 +341,162 @@ private: #endif -bool FreeFileSync::traverseInDetail(const Zstring& directory, FullDetailFileTraverser* sink, const int level) +class TraverseRecursively { -#ifdef FFS_WIN - if (level == 50) //catch endless recursion +public: + TraverseRecursively(const bool traverseSymbolicLinks, FreeFileSync::FullDetailFileTraverser* sink) : + m_traverseSymbolicLinks(traverseSymbolicLinks), + m_sink(sink) {} + + bool traverse(const Zstring& directory, const int level) { - if (sink->OnError(Zstring(_("Error traversing directory:")) + wxT(" ") + directory) == wxDIR_STOP) - return false; - else - return true; - } +#ifdef FFS_WIN + if (level == 50) //catch endless recursion + { + if (m_sink->OnError(Zstring(_("Error traversing directory:")) + wxT("\n\"") + directory + wxT("\"")) == wxDIR_STOP) + return false; + else + return true; + } - Zstring directoryFormatted = directory; - if (!endsWithPathSeparator(directoryFormatted)) - directoryFormatted += GlobalResources::FILE_NAME_SEPARATOR; + Zstring directoryFormatted = directory; + if (!FreeFileSync::endsWithPathSeparator(directoryFormatted)) + directoryFormatted += GlobalResources::FILE_NAME_SEPARATOR; - const Zstring filespec = directoryFormatted + wxT("*.*"); + const Zstring filespec = directoryFormatted + DefaultChar('*'); - WIN32_FIND_DATA fileMetaData; - HANDLE searchHandle = FindFirstFile(filespec.c_str(), //pointer to name of file to search for - &fileMetaData); //pointer to returned information + WIN32_FIND_DATA fileMetaData; + HANDLE searchHandle = FindFirstFile(filespec.c_str(), //pointer to name of file to search for + &fileMetaData); //pointer to returned information - if (searchHandle == INVALID_HANDLE_VALUE) - { - if (GetLastError() == ERROR_FILE_NOT_FOUND) - return true; - //else: we have a problem... - if (sink->OnError(Zstring(_("Error traversing directory:")) + wxT(" ") + directoryFormatted) == wxDIR_STOP) - return false; - else - return true; - } - CloseOnExit dummy(searchHandle); - - do - { //don't return "." and ".." - const wxChar* name = fileMetaData.cFileName; - if ( name[0] == wxChar('.') && - ((name[1] == wxChar('.') && name[2] == wxChar('\0')) || - name[1] == wxChar('\0'))) - continue; - - const Zstring fullName = directoryFormatted + name; - if (fileMetaData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) //a directory... + if (searchHandle == INVALID_HANDLE_VALUE) { - switch (sink->OnDir(fullName)) + const DWORD lastError = GetLastError(); + if (lastError == ERROR_FILE_NOT_FOUND) + return true; + + //else: we have a problem... report it: + Zstring errorMessage = Zstring(_("Error traversing directory:")) + wxT("\n\"") + directory + wxT("\" ") ; + if (m_sink->OnError(errorMessage + wxT("\n\n") + FreeFileSync::getLastErrorFormatted(lastError)) == wxDIR_STOP) + return false; + else + return true; + } + CloseOnExit dummy(searchHandle); + + do + { //don't return "." and ".." + const wxChar* name = fileMetaData.cFileName; + if ( name[0] == wxChar('.') && + ((name[1] == wxChar('.') && name[2] == wxChar('\0')) || + name[1] == wxChar('\0'))) + continue; + + const Zstring fullName = directoryFormatted + name; + + if (fileMetaData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) //a directory... { - case wxDIR_IGNORE: - break; - case wxDIR_CONTINUE: - if (!traverseInDetail(fullName, sink, level + 1)) + switch (m_sink->OnDir(fullName)) + { + case wxDIR_IGNORE: + break; + case wxDIR_CONTINUE: + //traverse into symbolic links, junctions, etc. if requested only: + if (m_traverseSymbolicLinks || (~fileMetaData.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)) + if (!this->traverse(fullName, level + 1)) + return false; + break; + case wxDIR_STOP: return false; - else + default: + assert(false); break; - case wxDIR_STOP: - return false; - default: - assert(false); - break; + } } - } - else //a file... - { - FileInfo details; - getWin32FileInformation(fileMetaData, details); - - switch (sink->OnFile(fullName, details)) + else //a file... { - case wxDIR_IGNORE: - case wxDIR_CONTINUE: - break; - case wxDIR_STOP: - return false; - default: - assert(false); - break; + FreeFileSync::FileInfo details; + getWin32FileInformation(fileMetaData, details); + + switch (m_sink->OnFile(fullName, details)) + { + case wxDIR_IGNORE: + case wxDIR_CONTINUE: + break; + case wxDIR_STOP: + return false; + default: + assert(false); + break; + } } } - } - while (FindNextFile(searchHandle, // handle to search - &fileMetaData)); // pointer to structure for data on found file + while (FindNextFile(searchHandle, // handle to search + &fileMetaData)); // pointer to structure for data on found file - if (GetLastError() == ERROR_NO_MORE_FILES) - return true; //everything okay - else //an error occured - { - if (sink->OnError(Zstring(_("Error traversing directory:")) + wxT(" ") + directoryFormatted) == wxDIR_STOP) + const DWORD lastError = GetLastError(); + if (lastError == ERROR_NO_MORE_FILES) + return true; //everything okay + + //else: we have a problem... report it: + Zstring errorMessage = Zstring(_("Error traversing directory:")) + wxT("\n\"") + directory + wxT("\" ") ; + if (m_sink->OnError(errorMessage + wxT("\n\n") + FreeFileSync::getLastErrorFormatted(lastError)) == wxDIR_STOP) return false; else return true; - } #elif defined FFS_LINUX - //use standard file traverser and enrich output with additional file information - //could be improved with custom traversing algorithm for optimized performance - EnhancedFileTraverser traverser(sink); + //use standard file traverser and enrich output with additional file information + //could be improved with custom traversing algorithm for optimized performance + EnhancedFileTraverser traverser(m_sink); - wxDir dir(directory.c_str()); - if (dir.IsOpened()) - dir.Traverse(traverser); + wxDir dir(directory.c_str()); + if (dir.IsOpened()) + dir.Traverse(traverser); - return true; + return true; #else - adapt this + adapt this #endif + } + +private: + const bool m_traverseSymbolicLinks; + FreeFileSync::FullDetailFileTraverser* m_sink; +}; + + +void FreeFileSync::traverseInDetail(const Zstring& directory, + const bool traverseSymbolicLinks, + FullDetailFileTraverser* sink) +{ + TraverseRecursively filewalker(traverseSymbolicLinks, sink); + filewalker.traverse(directory, 0); } +#ifdef FFS_WIN +inline +Zstring getDriveName(const Zstring& directoryName) //GetVolume() doesn't work under Linux! +{ + const Zstring volumeName = wxFileName(directoryName.c_str()).GetVolume().c_str(); + if (volumeName.empty()) + return Zstring(); + return volumeName + wxFileName::GetVolumeSeparator().c_str() + GlobalResources::FILE_NAME_SEPARATOR; +} +bool FreeFileSync::isFatDrive(const Zstring& directoryName) +{ + const Zstring driveName = getDriveName(directoryName); + if (driveName.empty()) + return false; + wxChar fileSystem[32]; + if (!GetVolumeInformation(driveName.c_str(), NULL, 0, NULL, NULL, NULL, fileSystem, 32)) + return false; + return Zstring(fileSystem).StartsWith(wxT("FAT")); +} +#endif //FFS_WIN diff --git a/library/fileHandling.h b/library/fileHandling.h index 5578ce91..7a1b1842 100644 --- a/library/fileHandling.h +++ b/library/fileHandling.h @@ -8,7 +8,8 @@ class FileError //Exception class used to notify file/directory copy/delete errors { public: - FileError(const Zstring& txt) : errorMessage(txt) {} + FileError(const Zstring& message) : + errorMessage(message) {} Zstring show() const { @@ -22,20 +23,6 @@ private: namespace FreeFileSync { - void getAllFilesAndDirs(const Zstring& sourceDir, std::vector<Zstring>& files, std::vector<Zstring>& directories) throw(FileError); - - //recycler - bool recycleBinExists(); //test existence of Recycle Bin API on current system - - //file handling - void removeDirectory(const Zstring& directory, const bool useRecycleBin); - void removeFile(const Zstring& filename, const bool useRecycleBin); - void createDirectory(const Zstring& directory, const int level = 0); //level is used internally only - void copyFolderAttributes(const Zstring& source, const Zstring& target); - -//################################ - //custom traverser with detail information about files - struct FileInfo { wxULongLong fileSize; //unit: bytes! @@ -52,8 +39,22 @@ namespace FreeFileSync virtual wxDirTraverseResult OnError(const Zstring& errorText) = 0; }; - bool traverseInDetail(const Zstring& directory, FullDetailFileTraverser* sink, const int level = 0); //return value and level are used internally only -//################################ + //custom traverser with detail information about files + void traverseInDetail(const Zstring& directory, const bool traverseSymbolicLinks, FullDetailFileTraverser* sink); + void getAllFilesAndDirs(const Zstring& sourceDir, std::vector<Zstring>& files, std::vector<Zstring>& directories) throw(FileError); + + //recycler + bool recycleBinExists(); //test existence of Recycle Bin API on current system + + //file handling + void removeDirectory(const Zstring& directory, const bool useRecycleBin); + void removeFile(const Zstring& filename, const bool useRecycleBin); + void createDirectory(const Zstring& directory, const int level = 0); //level is used internally only + void copyFolderAttributes(const Zstring& source, const Zstring& target); + +#ifdef FFS_WIN + bool isFatDrive(const Zstring& directoryName); +#endif //FFS_WIN } diff --git a/library/globalFunctions.cpp b/library/globalFunctions.cpp index eed6dfe9..aeeeed45 100644 --- a/library/globalFunctions.cpp +++ b/library/globalFunctions.cpp @@ -179,7 +179,7 @@ wxString DebugLog::assembleFileName() void DebugLog::write(const wxString& logText) { ++lineCount; - if (lineCount % 10000 == 0) //prevent logfile from becoming too big + if (lineCount % 50000 == 0) //prevent logfile from becoming too big { logFile->Close(); wxRemoveFile(logfileName); diff --git a/library/misc.cpp b/library/misc.cpp index f5c562c7..0b0bf60b 100644 --- a/library/misc.cpp +++ b/library/misc.cpp @@ -25,23 +25,23 @@ void exchangeEscapeChars(wxString& data) switch (value) { case wxChar('\\'): - output+= wxChar('\\'); + output += wxChar('\\'); break; case wxChar('n'): - output+= wxChar('\n'); + output += wxChar('\n'); break; case wxChar('t'): - output+= wxChar('\t'); + output += wxChar('\t'); break; case wxChar('\"'): - output+= wxChar('\"'); + output += wxChar('\"'); break; default: - output+= value; + output += value; } } else - output+= value; + output += value; ++input; } diff --git a/library/processXml.cpp b/library/processXml.cpp index 80ac190b..84783452 100644 --- a/library/processXml.cpp +++ b/library/processXml.cpp @@ -4,8 +4,6 @@ #include <wx/intl.h> #include "globalFunctions.h" -using namespace globalFunctions; -using namespace xmlAccess; //small helper functions bool readXmlElementValue(std::string& output, const TiXmlElement* parent, const std::string& name); @@ -23,7 +21,7 @@ void addXmlElement(TiXmlElement* parent, const std::string& name, const bool val class XmlConfigInput { public: - XmlConfigInput(const wxString& fileName, const XmlType type); + XmlConfigInput(const wxString& fileName, const xmlAccess::XmlType type); ~XmlConfigInput() {} bool loadedSuccessfully() @@ -32,11 +30,11 @@ public: } //read gui settings, all values retrieved are optional, so check for initial values! (== -1) - bool readXmlGuiConfig(XmlGuiConfig& outputCfg); + bool readXmlGuiConfig(xmlAccess::XmlGuiConfig& outputCfg); //read batch settings, all values retrieved are optional - bool readXmlBatchConfig(XmlBatchConfig& outputCfg); + bool readXmlBatchConfig(xmlAccess::XmlBatchConfig& outputCfg); //read global settings, valid for both GUI and batch mode, independent from configuration - bool readXmlGlobalSettings(XmlGlobalSettings& outputCfg); + bool readXmlGlobalSettings(xmlAccess::XmlGlobalSettings& outputCfg); private: //read basic FreefileSync settings (used by commandline and GUI), return true if ALL values have been retrieved successfully @@ -50,17 +48,17 @@ private: class XmlConfigOutput { public: - XmlConfigOutput(const wxString& fileName, const XmlType type); + XmlConfigOutput(const wxString& fileName, const xmlAccess::XmlType type); ~XmlConfigOutput() {} bool writeToFile(); //write gui settings - bool writeXmlGuiConfig(const XmlGuiConfig& outputCfg); + bool writeXmlGuiConfig(const xmlAccess::XmlGuiConfig& outputCfg); //write batch settings - bool writeXmlBatchConfig(const XmlBatchConfig& outputCfg); + bool writeXmlBatchConfig(const xmlAccess::XmlBatchConfig& outputCfg); //write global settings - bool writeXmlGlobalSettings(const XmlGlobalSettings& outputCfg); + bool writeXmlGlobalSettings(const xmlAccess::XmlGlobalSettings& outputCfg); private: //write basic FreefileSync settings (used by commandline and GUI), return true if everything was written successfully @@ -71,7 +69,7 @@ private: }; -XmlType xmlAccess::getXmlType(const wxString& filename) +xmlAccess::XmlType xmlAccess::getXmlType(const wxString& filename) { if (!wxFileExists(filename)) return XML_OTHER; @@ -107,9 +105,12 @@ XmlType xmlAccess::getXmlType(const wxString& filename) } -XmlGuiConfig xmlAccess::readGuiConfig(const wxString& filename) +xmlAccess::XmlGuiConfig xmlAccess::readGuiConfig(const wxString& filename) { //load XML + if (!wxFileExists(filename)) + throw FileError(Zstring(_("The file does not exist:")) + wxT(" \"") + filename.c_str() + wxT("\"")); + XmlConfigInput inputFile(filename, XML_GUI_CONFIG); XmlGuiConfig outputCfg; @@ -124,9 +125,12 @@ XmlGuiConfig xmlAccess::readGuiConfig(const wxString& filename) } -XmlBatchConfig xmlAccess::readBatchConfig(const wxString& filename) +xmlAccess::XmlBatchConfig xmlAccess::readBatchConfig(const wxString& filename) { //load XML + if (!wxFileExists(filename)) + throw FileError(Zstring(_("The file does not exist:")) + wxT(" \"") + filename.c_str() + wxT("\"")); + XmlConfigInput inputFile(filename, XML_BATCH_CONFIG); XmlBatchConfig outputCfg; @@ -141,9 +145,12 @@ XmlBatchConfig xmlAccess::readBatchConfig(const wxString& filename) } -XmlGlobalSettings xmlAccess::readGlobalSettings() +xmlAccess::XmlGlobalSettings xmlAccess::readGlobalSettings() { //load XML + if (!wxFileExists(FreeFileSync::GLOBAL_CONFIG_FILE)) + throw FileError(Zstring(_("The file does not exist:")) + wxT(" \"") + FreeFileSync::GLOBAL_CONFIG_FILE.c_str() + wxT("\"")); + XmlConfigInput inputFile(FreeFileSync::GLOBAL_CONFIG_FILE, XML_GLOBAL_SETTINGS); XmlGlobalSettings outputCfg; @@ -175,7 +182,7 @@ void xmlAccess::writeBatchConfig(const wxString& filename, const XmlBatchConfig& XmlConfigOutput outputFile(filename, XML_BATCH_CONFIG); //populate and write XML tree - if ( !outputFile.writeXmlBatchConfig(outputCfg) || //add GUI layout configuration settings + if ( !outputFile.writeXmlBatchConfig(outputCfg) || //add batch configuration settings !outputFile.writeToFile()) //save XML throw FileError(Zstring(_("Error writing file:")) + wxT(" \"") + filename.c_str() + wxT("\"")); return; @@ -194,7 +201,7 @@ void xmlAccess::writeGlobalSettings(const XmlGlobalSettings& outputCfg) } -XmlConfigInput::XmlConfigInput(const wxString& fileName, const XmlType type) : +XmlConfigInput::XmlConfigInput(const wxString& fileName, const xmlAccess::XmlType type) : loadSuccess(false) { if (!wxFileExists(fileName)) //avoid wxWidgets error message when wxFFile receives not existing file @@ -217,11 +224,11 @@ XmlConfigInput::XmlConfigInput(const wxString& fileName, const XmlType type) : const char* cfgType = root->Attribute("XmlType"); if (cfgType) { - if (type == XML_GUI_CONFIG) + if (type == xmlAccess::XML_GUI_CONFIG) loadSuccess = std::string(cfgType) == "GUI"; - else if (type == XML_BATCH_CONFIG) + else if (type == xmlAccess::XML_BATCH_CONFIG) loadSuccess = std::string(cfgType) == "BATCH"; - else if (type == XML_GLOBAL_SETTINGS) + else if (type == xmlAccess::XML_GLOBAL_SETTINGS) loadSuccess = std::string(cfgType) == "GLOBAL"; } } @@ -255,7 +262,7 @@ bool readXmlElementValue(int& output, const TiXmlElement* parent, const std::str std::string temp; if (readXmlElementValue(temp, parent, name)) { - output = stringToInt(temp); + output = globalFunctions::stringToInt(temp); return true; } else @@ -308,6 +315,25 @@ bool readXmlElementValue(bool& output, const TiXmlElement* parent, const std::st } +bool readXmlElementValue(xmlAccess::OnError& output, const TiXmlElement* parent, const std::string& name) +{ + std::string dummy; + if (readXmlElementValue(dummy, parent, name)) + { + if (dummy == "Ignore") + output = xmlAccess::ON_ERROR_IGNORE; + else if (dummy == "Exit") + output = xmlAccess::ON_ERROR_EXIT; + else //treat all other input as popup + output = xmlAccess::ON_ERROR_POPUP; + + return true; + } + else + return false; +} + + bool XmlConfigInput::readXmlMainConfig(MainConfiguration& mainCfg, std::vector<FolderPair>& directoryPairs) { TiXmlElement* root = doc.RootElement(); @@ -366,13 +392,12 @@ bool XmlConfigInput::readXmlMainConfig(MainConfiguration& mainCfg, std::vector<F //########################################################### //other readXmlElementValue(mainCfg.useRecycleBin, miscSettings, "UseRecycler"); - readXmlElementValue(mainCfg.ignoreErrors, miscSettings, "IgnoreErrors"); return true; } -bool XmlConfigInput::readXmlGuiConfig(XmlGuiConfig& outputCfg) +bool XmlConfigInput::readXmlGuiConfig(xmlAccess::XmlGuiConfig& outputCfg) { //read main config if (!readXmlMainConfig(outputCfg.mainCfg, outputCfg.directoryPairs)) @@ -391,11 +416,18 @@ bool XmlConfigInput::readXmlGuiConfig(XmlGuiConfig& outputCfg) readXmlElementValue(outputCfg.hideFilteredElements, mainWindow, "HideFiltered"); } + + TiXmlElement* guiConfig = hRoot.FirstChild("GuiConfig").ToElement(); + if (guiConfig) + { + readXmlElementValue(outputCfg.ignoreErrors, guiConfig, "IgnoreErrors"); + } + return true; } -bool XmlConfigInput::readXmlBatchConfig(XmlBatchConfig& outputCfg) +bool XmlConfigInput::readXmlBatchConfig(xmlAccess::XmlBatchConfig& outputCfg) { //read main config if (!readXmlMainConfig(outputCfg.mainCfg, outputCfg.directoryPairs)) @@ -411,15 +443,15 @@ bool XmlConfigInput::readXmlBatchConfig(XmlBatchConfig& outputCfg) TiXmlElement* batchConfig = hRoot.FirstChild("BatchConfig").ToElement(); if (batchConfig) { - //read application window size and position readXmlElementValue(outputCfg.silent, batchConfig, "Silent"); + readXmlElementValue(outputCfg.handleError, batchConfig, "HandleError"); } return true; } -bool XmlConfigInput::readXmlGlobalSettings(XmlGlobalSettings& outputCfg) +bool XmlConfigInput::readXmlGlobalSettings(xmlAccess::XmlGlobalSettings& outputCfg) { TiXmlElement* root = doc.RootElement(); if (!root) return false; @@ -427,19 +459,30 @@ bool XmlConfigInput::readXmlGlobalSettings(XmlGlobalSettings& outputCfg) TiXmlHandle hRoot(root); //read global settings - TiXmlElement* global = hRoot.FirstChild("Global").ToElement(); - if (!global) return false; + TiXmlElement* global = hRoot.FirstChild("Shared").ToElement(); + if (global) + { + //program language + readXmlElementValue(outputCfg.shared.programLanguage, global, "Language"); - //program language - readXmlElementValue(outputCfg.global.programLanguage, global, "Language"); + //traverse into symbolic links (to folders) + readXmlElementValue(outputCfg.shared.traverseSymbolicLinks, global, "TraverseSymbolicLinks"); #ifdef FFS_WIN - //daylight saving time check - readXmlElementValue(outputCfg.global.handleDstOnFat32, global, "HandleDaylightSavingTimeOnFAT"); + //daylight saving time check + readXmlElementValue(outputCfg.shared.handleDstOnFat32, global, "HandleDaylightSavingTimeOnFAT"); #endif + } + + TiXmlElement* warnings = hRoot.FirstChild("Shared").FirstChild("Warnings").ToElement(); + if (warnings) + { + //folder dependency check + readXmlElementValue(outputCfg.shared.warningDependentFolders, warnings, "CheckForDependentFolders"); - //folder dependency check - readXmlElementValue(outputCfg.global.folderDependCheckActive, global, "FolderDependencyCheckActive"); + //significant difference check + readXmlElementValue(outputCfg.shared.warningSignificantDifference, warnings, "CheckForSignificantDifference"); + } //gui specific global settings (optional) TiXmlElement* mainWindow = hRoot.FirstChild("Gui").FirstChild("Windows").FirstChild("Main").ToElement(); @@ -452,6 +495,9 @@ bool XmlConfigInput::readXmlGlobalSettings(XmlGlobalSettings& outputCfg) readXmlElementValue(outputCfg.gui.posYNotMaximized, mainWindow, "PosY"); readXmlElementValue(outputCfg.gui.isMaximized, mainWindow, "Maximized"); + readXmlElementValue(outputCfg.gui.deleteOnBothSides, mainWindow, "ManualDeletionOnBothSides"); + readXmlElementValue(outputCfg.gui.useRecyclerForManualDeletion, mainWindow, "ManualDeletionUseRecycler"); + //########################################################### //read column attributes TiXmlElement* leftColumn = TiXmlHandle(mainWindow).FirstChild("LeftColumns").FirstChild("Column").ToElement(); @@ -464,11 +510,11 @@ bool XmlConfigInput::readXmlGlobalSettings(XmlGlobalSettings& outputCfg) if (visible && position && width) //may be NULL!! { - XmlGlobalSettings::ColumnAttrib newAttrib; - newAttrib.type = XmlGlobalSettings::ColumnTypes(colType); + xmlAccess::ColumnAttrib newAttrib; + newAttrib.type = xmlAccess::ColumnTypes(colType); newAttrib.visible = std::string(visible) != std::string("false"); - newAttrib.position = stringToInt(position); - newAttrib.width = stringToInt(width); + newAttrib.position = globalFunctions::stringToInt(position); + newAttrib.width = globalFunctions::stringToInt(width); outputCfg.gui.columnAttribLeft.push_back(newAttrib); } else @@ -488,11 +534,11 @@ bool XmlConfigInput::readXmlGlobalSettings(XmlGlobalSettings& outputCfg) if (visible && position && width) //may be NULL!! { - XmlGlobalSettings::ColumnAttrib newAttrib; - newAttrib.type = XmlGlobalSettings::ColumnTypes(colType); + xmlAccess::ColumnAttrib newAttrib; + newAttrib.type = xmlAccess::ColumnTypes(colType); newAttrib.visible = std::string(visible) != std::string("false"); - newAttrib.position = stringToInt(position); - newAttrib.width = stringToInt(width); + newAttrib.position = globalFunctions::stringToInt(position); + newAttrib.width = globalFunctions::stringToInt(width); outputCfg.gui.columnAttribRight.push_back(newAttrib); } else @@ -516,6 +562,12 @@ bool XmlConfigInput::readXmlGlobalSettings(XmlGlobalSettings& outputCfg) TiXmlElement* cfgHistory = hRoot.FirstChild("Gui").FirstChild("History").ToElement(); if (cfgHistory) { + //load max. history size + const char* histSizeMax = cfgHistory->Attribute("MaximumSize"); + if (histSizeMax) //may be NULL! + outputCfg.gui.cfgHistoryMaxItems = globalFunctions::stringToInt(histSizeMax); + + //load history elements TiXmlElement* cfgFile = TiXmlHandle(cfgHistory).FirstChild("File").ToElement(); while (cfgFile) { @@ -540,7 +592,7 @@ bool XmlConfigInput::readXmlGlobalSettings(XmlGlobalSettings& outputCfg) } -XmlConfigOutput::XmlConfigOutput(const wxString& fileName, const XmlType type) : +XmlConfigOutput::XmlConfigOutput(const wxString& fileName, const xmlAccess::XmlType type) : m_fileName(fileName) { TiXmlBase::SetCondenseWhiteSpace(false); //do not condense whitespace characters @@ -549,11 +601,11 @@ XmlConfigOutput::XmlConfigOutput(const wxString& fileName, const XmlType type) : doc.LinkEndChild(decl); TiXmlElement* root = new TiXmlElement("FreeFileSync"); - if (type == XML_GUI_CONFIG) + if (type == xmlAccess::XML_GUI_CONFIG) root->SetAttribute("XmlType", "GUI"); //xml configuration type - else if (type == XML_BATCH_CONFIG) + else if (type == xmlAccess::XML_BATCH_CONFIG) root->SetAttribute("XmlType", "BATCH"); - else if (type == XML_GLOBAL_SETTINGS) + else if (type == xmlAccess::XML_GLOBAL_SETTINGS) root->SetAttribute("XmlType", "GLOBAL"); else assert(false); @@ -587,7 +639,7 @@ void addXmlElement(TiXmlElement* parent, const std::string& name, const std::str void addXmlElement(TiXmlElement* parent, const std::string& name, const int value) { - addXmlElement(parent, name, numberToString(value)); + addXmlElement(parent, name, globalFunctions::numberToString(value)); } @@ -613,6 +665,19 @@ void addXmlElement(TiXmlElement* parent, const std::string& name, const bool val } +void addXmlElement(TiXmlElement* parent, const std::string& name, const xmlAccess::OnError value) +{ + if (value == xmlAccess::ON_ERROR_IGNORE) + addXmlElement(parent, name, std::string("Ignore")); + else if (value == xmlAccess::ON_ERROR_EXIT) + addXmlElement(parent, name, std::string("Exit")); + else if (value == xmlAccess::ON_ERROR_POPUP) + addXmlElement(parent, name, std::string("Popup")); + else + assert(false); +} + + bool XmlConfigOutput::writeXmlMainConfig(const MainConfiguration& mainCfg, const std::vector<FolderPair>& directoryPairs) { TiXmlElement* root = doc.RootElement(); @@ -670,14 +735,13 @@ bool XmlConfigOutput::writeXmlMainConfig(const MainConfiguration& mainCfg, const //other addXmlElement(miscSettings, "UseRecycler", mainCfg.useRecycleBin); - addXmlElement(miscSettings, "IgnoreErrors", mainCfg.ignoreErrors); //########################################################### return true; } -bool XmlConfigOutput::writeXmlGuiConfig(const XmlGuiConfig& outputCfg) +bool XmlConfigOutput::writeXmlGuiConfig(const xmlAccess::XmlGuiConfig& outputCfg) { //write main config if (!writeXmlMainConfig(outputCfg.mainCfg, outputCfg.directoryPairs)) @@ -687,22 +751,24 @@ bool XmlConfigOutput::writeXmlGuiConfig(const XmlGuiConfig& outputCfg) TiXmlElement* root = doc.RootElement(); if (!root) return false; - TiXmlElement* guiLayout = new TiXmlElement("GuiConfig"); - root->LinkEndChild(guiLayout); + TiXmlElement* guiConfig = new TiXmlElement("GuiConfig"); + root->LinkEndChild(guiConfig); TiXmlElement* windows = new TiXmlElement("Windows"); - guiLayout->LinkEndChild(windows); + guiConfig->LinkEndChild(windows); TiXmlElement* mainWindow = new TiXmlElement("Main"); windows->LinkEndChild(mainWindow); addXmlElement(mainWindow, "HideFiltered", outputCfg.hideFilteredElements); + addXmlElement(guiConfig, "IgnoreErrors", outputCfg.ignoreErrors); + return true; } -bool XmlConfigOutput::writeXmlBatchConfig(const XmlBatchConfig& outputCfg) +bool XmlConfigOutput::writeXmlBatchConfig(const xmlAccess::XmlBatchConfig& outputCfg) { //write main config if (!writeXmlMainConfig(outputCfg.mainCfg, outputCfg.directoryPairs)) @@ -716,31 +782,42 @@ bool XmlConfigOutput::writeXmlBatchConfig(const XmlBatchConfig& outputCfg) root->LinkEndChild(batchConfig); addXmlElement(batchConfig, "Silent", outputCfg.silent); + addXmlElement(batchConfig, "HandleError", outputCfg.handleError); return true; } -bool XmlConfigOutput::writeXmlGlobalSettings(const XmlGlobalSettings& outputCfg) +bool XmlConfigOutput::writeXmlGlobalSettings(const xmlAccess::XmlGlobalSettings& outputCfg) { TiXmlElement* root = doc.RootElement(); if (!root) return false; //################################################################### //write global settings - TiXmlElement* global = new TiXmlElement("Global"); + TiXmlElement* global = new TiXmlElement("Shared"); root->LinkEndChild(global); //program language - addXmlElement(global, "Language", outputCfg.global.programLanguage); + addXmlElement(global, "Language", outputCfg.shared.programLanguage); #ifdef FFS_WIN //daylight saving time check - addXmlElement(global, "HandleDaylightSavingTimeOnFAT", outputCfg.global.handleDstOnFat32); + addXmlElement(global, "HandleDaylightSavingTimeOnFAT", outputCfg.shared.handleDstOnFat32); #endif - //folder dependency check - addXmlElement(global, "FolderDependencyCheckActive", outputCfg.global.folderDependCheckActive); + //traverse into symbolic links (to folders) + addXmlElement(global, "TraverseSymbolicLinks", outputCfg.shared.traverseSymbolicLinks); + + //warnings + TiXmlElement* warnings = new TiXmlElement("Warnings"); + global->LinkEndChild(warnings); + + //warning: dependent folders + addXmlElement(warnings, "CheckForDependentFolders", outputCfg.shared.warningDependentFolders); + + //significant difference check + addXmlElement(warnings, "CheckForSignificantDifference", outputCfg.shared.warningSignificantDifference); //################################################################### @@ -763,17 +840,20 @@ bool XmlConfigOutput::writeXmlGlobalSettings(const XmlGlobalSettings& outputCfg) addXmlElement(mainWindow, "PosY", outputCfg.gui.posYNotMaximized); addXmlElement(mainWindow, "Maximized", outputCfg.gui.isMaximized); + addXmlElement(mainWindow, "ManualDeletionOnBothSides", outputCfg.gui.deleteOnBothSides); + addXmlElement(mainWindow, "ManualDeletionUseRecycler", outputCfg.gui.useRecyclerForManualDeletion); + //write column attributes TiXmlElement* leftColumn = new TiXmlElement("LeftColumns"); mainWindow->LinkEndChild(leftColumn); - XmlGlobalSettings::ColumnAttributes columnAtrribLeftCopy = outputCfg.gui.columnAttribLeft; //can't change const vector + xmlAccess::ColumnAttributes columnAtrribLeftCopy = outputCfg.gui.columnAttribLeft; //can't change const vector sort(columnAtrribLeftCopy.begin(), columnAtrribLeftCopy.end(), xmlAccess::sortByType); for (unsigned int i = 0; i < columnAtrribLeftCopy.size(); ++i) { TiXmlElement* subElement = new TiXmlElement("Column"); leftColumn->LinkEndChild(subElement); - const XmlGlobalSettings::ColumnAttrib& colAttrib = columnAtrribLeftCopy[i]; + const xmlAccess::ColumnAttrib& colAttrib = columnAtrribLeftCopy[i]; if (colAttrib.visible) subElement->SetAttribute("Visible", "true"); else subElement->SetAttribute("Visible", "false"); subElement->SetAttribute("Position", colAttrib.position); @@ -782,14 +862,14 @@ bool XmlConfigOutput::writeXmlGlobalSettings(const XmlGlobalSettings& outputCfg) TiXmlElement* rightColumn = new TiXmlElement("RightColumns"); mainWindow->LinkEndChild(rightColumn); - XmlGlobalSettings::ColumnAttributes columnAtrribRightCopy = outputCfg.gui.columnAttribRight; + xmlAccess::ColumnAttributes columnAtrribRightCopy = outputCfg.gui.columnAttribRight; sort(columnAtrribRightCopy.begin(), columnAtrribRightCopy.end(), xmlAccess::sortByType); for (unsigned int i = 0; i < columnAtrribRightCopy.size(); ++i) { TiXmlElement* subElement = new TiXmlElement("Column"); rightColumn->LinkEndChild(subElement); - const XmlGlobalSettings::ColumnAttrib& colAttrib = columnAtrribRightCopy[i]; + const xmlAccess::ColumnAttrib& colAttrib = columnAtrribRightCopy[i]; if (colAttrib.visible) subElement->SetAttribute("Visible", "true"); else subElement->SetAttribute("Visible", "false"); subElement->SetAttribute("Position", colAttrib.position); @@ -803,10 +883,11 @@ bool XmlConfigOutput::writeXmlGlobalSettings(const XmlGlobalSettings& outputCfg) TiXmlElement* cfgHistory = new TiXmlElement("History"); gui->LinkEndChild(cfgHistory); + cfgHistory->SetAttribute("MaximumSize", globalFunctions::numberToString(outputCfg.gui.cfgHistoryMaxItems)); + for (unsigned int i = 0; i < outputCfg.gui.cfgFileHistory.size(); ++i) addXmlElement(cfgHistory, "File", std::string(outputCfg.gui.cfgFileHistory[i].ToUTF8())); - //################################################################### //write global batch settings @@ -863,9 +944,14 @@ int xmlAccess::retrieveSystemLanguage() //case wxLANGUAGE_JAPANESE: //case wxLANGUAGE_POLISH: - default: - //all the rest: wxLANGUAGE_ENGLISH; return lang; } } + + +void xmlAccess::XmlGlobalSettings::_Shared::resetWarnings() +{ + warningDependentFolders = true; + warningSignificantDifference = true; +} diff --git a/library/processXml.h b/library/processXml.h index 5f3f8ef9..7b838958 100644 --- a/library/processXml.h +++ b/library/processXml.h @@ -9,6 +9,13 @@ using namespace FreeFileSync; namespace xmlAccess { + enum OnError + { + ON_ERROR_POPUP, + ON_ERROR_IGNORE, + ON_ERROR_EXIT + }; + enum XmlType { XML_GUI_CONFIG, @@ -17,28 +24,49 @@ namespace xmlAccess XML_OTHER }; - XmlType getXmlType(const wxString& filename); + enum ColumnTypes + { + FILENAME = 0, + REL_PATH, + SIZE, + DATE, + FULL_NAME + }; + const unsigned COLUMN_TYPE_COUNT = 5; + struct ColumnAttrib + { + ColumnTypes type; + bool visible; + unsigned position; + int width; + }; + typedef std::vector<ColumnAttrib> ColumnAttributes; + + XmlType getXmlType(const wxString& filename); +//--------------------------------------------------------------------- struct XmlGuiConfig { - XmlGuiConfig() : hideFilteredElements(false) {} //initialize values + XmlGuiConfig() : hideFilteredElements(false), ignoreErrors(false) {} //initialize values MainConfiguration mainCfg; std::vector<FolderPair> directoryPairs; bool hideFilteredElements; + bool ignoreErrors; //reaction on error situation during synchronization }; struct XmlBatchConfig { - XmlBatchConfig() : silent(false) {} + XmlBatchConfig() : silent(false), handleError(ON_ERROR_POPUP) {} MainConfiguration mainCfg; std::vector<FolderPair> directoryPairs; bool silent; + OnError handleError; //reaction on error situation during synchronization }; int retrieveSystemLanguage(); @@ -47,40 +75,30 @@ namespace xmlAccess struct XmlGlobalSettings { //--------------------------------------------------------------------- -//internal structures: - enum ColumnTypes - { - FILENAME = 0, - REL_PATH, - SIZE, - DATE - }; - - struct ColumnAttrib - { - ColumnTypes type; - bool visible; - unsigned position; - int width; - }; - typedef std::vector<ColumnAttrib> ColumnAttributes; - -//--------------------------------------------------------------------- - struct _Global + struct _Shared { - _Global() : + _Shared() : programLanguage(retrieveSystemLanguage()), #ifdef FFS_WIN handleDstOnFat32(true), #endif - folderDependCheckActive(true) {} + traverseSymbolicLinks(false) + { + resetWarnings(); + } int programLanguage; #ifdef FFS_WIN bool handleDstOnFat32; #endif - bool folderDependCheckActive; - } global; + bool traverseSymbolicLinks; + + //warnings + void resetWarnings(); + + bool warningDependentFolders; + bool warningSignificantDifference; + } shared; //--------------------------------------------------------------------- struct _Gui @@ -92,10 +110,13 @@ namespace xmlAccess posYNotMaximized(wxDefaultCoord), isMaximized(false), #ifdef FFS_WIN - commandLineFileManager(wxT("explorer /select, %x")) + commandLineFileManager(wxT("explorer /select, %name")), #elif defined FFS_LINUX - commandLineFileManager(wxT("konqueror \"%path\"")) + commandLineFileManager(wxT("konqueror \"%path\"")), #endif + cfgHistoryMaxItems(10), + deleteOnBothSides(false), + useRecyclerForManualDeletion(FreeFileSync::recycleBinExists()) //enable if OS supports it; else user will have to activate first and then get an error message {} int widthNotMaximized; @@ -106,8 +127,11 @@ namespace xmlAccess ColumnAttributes columnAttribLeft; ColumnAttributes columnAttribRight; - std::vector<wxString> cfgFileHistory; wxString commandLineFileManager; + std::vector<wxString> cfgFileHistory; + unsigned cfgHistoryMaxItems; + bool deleteOnBothSides; + bool useRecyclerForManualDeletion; } gui; //--------------------------------------------------------------------- @@ -116,21 +140,21 @@ namespace xmlAccess inline - bool sortByType(const XmlGlobalSettings::ColumnAttrib& a, const XmlGlobalSettings::ColumnAttrib& b) + bool sortByType(const ColumnAttrib& a, const ColumnAttrib& b) { return a.type < b.type; } inline - bool sortByPositionOnly(const XmlGlobalSettings::ColumnAttrib& a, const XmlGlobalSettings::ColumnAttrib& b) + bool sortByPositionOnly(const ColumnAttrib& a, const ColumnAttrib& b) { return a.position < b.position; } inline - bool sortByPositionAndVisibility(const XmlGlobalSettings::ColumnAttrib& a, const XmlGlobalSettings::ColumnAttrib& b) + bool sortByPositionAndVisibility(const ColumnAttrib& a, const ColumnAttrib& b) { if (a.visible == false) //hidden elements shall appear at end of vector return false; diff --git a/library/resources.cpp b/library/resources.cpp index c60c259c..4a9d0369 100644 --- a/library/resources.cpp +++ b/library/resources.cpp @@ -64,10 +64,14 @@ GlobalResources::GlobalResources() bitmapResource[wxT("filter active.png")] = (bitmapFilterOn = new wxBitmap(wxNullBitmap)); bitmapResource[wxT("filter not active.png")] = (bitmapFilterOff = new wxBitmap(wxNullBitmap)); bitmapResource[wxT("warning.png")] = (bitmapWarning = new wxBitmap(wxNullBitmap)); + bitmapResource[wxT("warningSmall.png")] = (bitmapWarningSmall = new wxBitmap(wxNullBitmap)); + bitmapResource[wxT("error.png")] = (bitmapError = new wxBitmap(wxNullBitmap)); bitmapResource[wxT("small arrow up.png"]) = (bitmapSmallUp = new wxBitmap(wxNullBitmap)); bitmapResource[wxT("small arrow down.png")] = (bitmapSmallDown = new wxBitmap(wxNullBitmap)); bitmapResource[wxT("save.png")] = (bitmapSave = new wxBitmap(wxNullBitmap)); + bitmapResource[wxT("load.png")] = (bitmapLoad = new wxBitmap(wxNullBitmap)); bitmapResource[wxT("FFS.png")] = (bitmapFFS = new wxBitmap(wxNullBitmap)); + bitmapResource[wxT("FFS paused.png")] = (bitmapFFSPaused = new wxBitmap(wxNullBitmap)); bitmapResource[wxT("deleteFile.png")] = (bitmapDeleteFile = new wxBitmap(wxNullBitmap)); bitmapResource[wxT("gpl.png")] = (bitmapGPL = new wxBitmap(wxNullBitmap)); bitmapResource[wxT("statusPause.png")] = (bitmapStatusPause = new wxBitmap(wxNullBitmap)); @@ -102,6 +106,8 @@ GlobalResources::GlobalResources() //init all the other resource files animationMoney = new wxAnimation(wxNullAnimation); animationSync = new wxAnimation(wxNullAnimation); + + programIcon = &wxNullIcon; } diff --git a/library/resources.h b/library/resources.h index 4eec185e..24e8d027 100644 --- a/library/resources.h +++ b/library/resources.h @@ -63,10 +63,14 @@ public: wxBitmap* bitmapFilterOn; wxBitmap* bitmapFilterOff; wxBitmap* bitmapWarning; + wxBitmap* bitmapWarningSmall; + wxBitmap* bitmapError; wxBitmap* bitmapSmallUp; wxBitmap* bitmapSmallDown; wxBitmap* bitmapSave; + wxBitmap* bitmapLoad; wxBitmap* bitmapFFS; + wxBitmap* bitmapFFSPaused; wxBitmap* bitmapDeleteFile; wxBitmap* bitmapGPL; wxBitmap* bitmapStatusPause; diff --git a/library/sorting.h b/library/sorting.h index fabdad1f..23c219b9 100644 --- a/library/sorting.h +++ b/library/sorting.h @@ -106,24 +106,34 @@ bool sortByFileName(const FileCompareLine& a, const FileCompareLine& b) return false; //empty rows always last else if (descrLineB->objType == FileDescrLine::TYPE_NOTHING) return true; //empty rows always last - else if (descrLineA->objType == FileDescrLine::TYPE_DIRECTORY) - return false; - else if (descrLineB->objType == FileDescrLine::TYPE_DIRECTORY) - return true; + + + if (descrLineA->objType == FileDescrLine::TYPE_DIRECTORY) //sort directories by relative name + { + if (descrLineB->objType == FileDescrLine::TYPE_DIRECTORY) + return stringSmallerThan<sortAscending>(descrLineA->relativeName.c_str(), descrLineB->relativeName.c_str()); + else + return false; + } else { - const wxChar* stringA = descrLineA->relativeName.c_str(); - const wxChar* stringB = descrLineB->relativeName.c_str(); + if (descrLineB->objType == FileDescrLine::TYPE_DIRECTORY) + return true; + else + { + const wxChar* stringA = descrLineA->relativeName.c_str(); + const wxChar* stringB = descrLineB->relativeName.c_str(); - size_t pos = descrLineA->relativeName.Find(GlobalResources::FILE_NAME_SEPARATOR, true); //start search beginning from end - if (pos != std::string::npos) - stringA += pos + 1; + size_t pos = descrLineA->relativeName.Find(GlobalResources::FILE_NAME_SEPARATOR, true); //start search beginning from end + if (pos != std::string::npos) + stringA += pos + 1; - pos = descrLineB->relativeName.Find(GlobalResources::FILE_NAME_SEPARATOR, true); //start search beginning from end - if (pos != std::string::npos) - stringB += pos + 1; + pos = descrLineB->relativeName.Find(GlobalResources::FILE_NAME_SEPARATOR, true); //start search beginning from end + if (pos != std::string::npos) + stringB += pos + 1; - return stringSmallerThan<sortAscending>(stringA, stringB); + return stringSmallerThan<sortAscending>(stringA, stringB); + } } } @@ -205,7 +215,7 @@ bool sortByRelativeName(const FileCompareLine& a, const FileCompareLine& b) template <bool sortAscending, SideToSort side> inline -bool sortByFileSize(const FileCompareLine& a, const FileCompareLine& b) +bool sortByFullName(const FileCompareLine& a, const FileCompareLine& b) { const FileDescrLine* descrLineA = NULL; const FileDescrLine* descrLineB = NULL; @@ -216,14 +226,50 @@ bool sortByFileSize(const FileCompareLine& a, const FileCompareLine& b) return false; //empty rows always last else if (descrLineB->objType == FileDescrLine::TYPE_NOTHING) return true; //empty rows always last - else if (descrLineA->objType == FileDescrLine::TYPE_DIRECTORY) - return false; - else if (descrLineB->objType == FileDescrLine::TYPE_DIRECTORY) - return true; else +#ifdef FFS_WIN //case-insensitive comparison! + return sortAscending ? + FreeFileSync::compareStringsWin32(descrLineA->fullName.c_str(), descrLineB->fullName.c_str()) < 0 : //way faster than wxString::CmpNoCase() in windows build!!! + FreeFileSync::compareStringsWin32(descrLineA->fullName.c_str(), descrLineB->fullName.c_str()) > 0; +#else return sortAscending ? - descrLineA->fileSize < descrLineB->fileSize : - descrLineA->fileSize > descrLineB->fileSize; + descrLineA->fullName.Cmp(descrLineB->fullName) < 0 : + descrLineA->fullName.Cmp(descrLineB->fullName) > 0; +#endif +} + + +template <bool sortAscending, SideToSort side> +inline +bool sortByFileSize(const FileCompareLine& a, const FileCompareLine& b) +{ + const FileDescrLine* descrLineA = NULL; + const FileDescrLine* descrLineB = NULL; + getDescrLine<side>(a, b, descrLineA, descrLineB); + + //presort types: first files, then directories then empty rows + if (descrLineA->objType == FileDescrLine::TYPE_NOTHING) + return false; //empty rows always last + else if (descrLineB->objType == FileDescrLine::TYPE_NOTHING) + return true; //empty rows always last + + + if (descrLineA->objType == FileDescrLine::TYPE_DIRECTORY) //sort directories by relative name + { + if (descrLineB->objType == FileDescrLine::TYPE_DIRECTORY) + return stringSmallerThan<sortAscending>(descrLineA->relativeName.c_str(), descrLineB->relativeName.c_str()); + else + return false; + } + else + { + if (descrLineB->objType == FileDescrLine::TYPE_DIRECTORY) + return true; + else + return sortAscending ? + descrLineA->fileSize > descrLineB->fileSize : //sortAscending == true shall result in list beginning with largest files first + descrLineA->fileSize < descrLineB->fileSize; + } } @@ -240,14 +286,23 @@ bool sortByDate(const FileCompareLine& a, const FileCompareLine& b) return false; //empty rows always last else if (descrLineB->objType == FileDescrLine::TYPE_NOTHING) return true; //empty rows always last - else if (descrLineA->objType == FileDescrLine::TYPE_DIRECTORY) - return false; - else if (descrLineB->objType == FileDescrLine::TYPE_DIRECTORY) - return true; + + if (descrLineA->objType == FileDescrLine::TYPE_DIRECTORY) //sort directories by relative name + { + if (descrLineB->objType == FileDescrLine::TYPE_DIRECTORY) + return stringSmallerThan<sortAscending>(descrLineA->relativeName.c_str(), descrLineB->relativeName.c_str()); + else + return false; + } else - return sortAscending ? - descrLineA->lastWriteTimeRaw < descrLineB->lastWriteTimeRaw : - descrLineA->lastWriteTimeRaw > descrLineB->lastWriteTimeRaw; + { + if (descrLineB->objType == FileDescrLine::TYPE_DIRECTORY) + return true; + else + return sortAscending ? + descrLineA->lastWriteTimeRaw < descrLineB->lastWriteTimeRaw : + descrLineA->lastWriteTimeRaw > descrLineB->lastWriteTimeRaw; + } } diff --git a/library/statusHandler.h b/library/statusHandler.h index bae1de8e..208a2df8 100644 --- a/library/statusHandler.h +++ b/library/statusHandler.h @@ -10,7 +10,7 @@ bool updateUiIsAllowed(); //test if a specific amount of time is over void updateUiNow(); //do the updating -//interfaces for status updates (can be implemented by UI or commandline) +//interfaces for status updates (can be implemented by GUI or Batch mode) //overwrite virtual methods for respective functionality class ErrorHandler @@ -24,15 +24,14 @@ public: IGNORE_ERROR = 10, RETRY }; - virtual Response reportError(const Zstring& text) = 0; + virtual Response reportError(const Zstring& errorMessage) = 0; }; class StatusHandler { public: - StatusHandler() : - abortRequested(false) {} + StatusHandler() : abortRequested(false) {} virtual ~StatusHandler() {} //identifiers of different processes @@ -48,7 +47,6 @@ public: virtual void updateStatusText(const Zstring& text) = 0; virtual void initNewProcess(int objectsTotal, double dataTotal, Process processID) = 0; //informs about the total amount of data that will be processed from now on virtual void updateProcessedData(int objectsProcessed, double dataProcessed) = 0; //called periodically after data was processed - virtual ErrorHandler::Response reportError(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; @@ -66,6 +64,11 @@ public: abortRequested = true; } + //error handling: + virtual ErrorHandler::Response reportError(const Zstring& errorMessage) = 0; //recoverable error situation + virtual void reportFatalError(const Zstring& errorMessage) = 0; //non-recoverable error situation, implement abort! + virtual void reportWarning(const Zstring& warningMessage, bool& dontShowAgain) = 0; + protected: virtual void abortThisProcess() = 0; diff --git a/library/zstring.cpp b/library/zstring.cpp index 35704b01..27633392 100644 --- a/library/zstring.cpp +++ b/library/zstring.cpp @@ -24,29 +24,23 @@ void testZstringForMemoryLeak() #ifdef FFS_WIN -int FreeFileSync::compareStringsWin32(const wchar_t* a, const wchar_t* b) -{ - return lstrcmpi( - a, //address of first string - b); //address of second string -} - - -//equivalent implementation, but slightly(!!!) slower: int FreeFileSync::compareStringsWin32(const wchar_t* a, const wchar_t* b, const int aCount, const int bCount) { - int rv = CompareString( - LOCALE_USER_DEFAULT, //locale identifier - NORM_IGNORECASE, //comparison-style options - a, //pointer to first string - aCount, //size, in bytes or characters, of first string - b, //pointer to second string - bCount); //size, in bytes or characters, of second string + //DON'T use lstrcmpi() here! It uses word sort, which unfortunately is NOT always a strict weak sorting function for some locales (e.g. swedish) + //Use CompareString() with "SORT_STRINGSORT" instead!!! + + const int rv = CompareString( + LOCALE_USER_DEFAULT, //locale identifier + NORM_IGNORECASE | SORT_STRINGSORT, //comparison-style options + a, //pointer to first string + aCount, //size, in bytes or characters, of first string + b, //pointer to second string + bCount); //size, in bytes or characters, of second string if (rv == 0) throw RuntimeException(wxString(wxT("Error comparing strings!"))); else - return rv - 2; + return rv - 2; //convert to C-style string compare result } #endif diff --git a/library/zstring.h b/library/zstring.h index 9013dd22..60ba464c 100644 --- a/library/zstring.h +++ b/library/zstring.h @@ -14,8 +14,7 @@ namespace FreeFileSync { #ifdef FFS_WIN //super fast case-insensitive string comparison: way faster than wxString::CmpNoCase()!!! - int compareStringsWin32(const wchar_t* a, const wchar_t* b); - int compareStringsWin32(const wchar_t* a, const wchar_t* b, const int aCount, const int bCount); + int compareStringsWin32(const wchar_t* a, const wchar_t* b, const int aCount = -1, const int bCount = -1); #endif //FFS_WIN } @@ -322,11 +321,11 @@ void Zstring::incRef() const inline void Zstring::decRef() { - assert(descr && descr->refCount >= 1); + assert(descr && descr->refCount >= 1); //descr points to the begin of the allocated memory block if (--descr->refCount == 0) { - assert(descr); //descr points to the begin of the allocated memory block free(descr); //this must NEVER be changed!! E.g. Trim() relies on descr being start of allocated memory block + descr = 0; #ifdef __WXDEBUG__ --allocCount; //test Zstring for memory leaks #endif @@ -345,7 +344,7 @@ int Zstring::CmpNoCase(const DefaultChar* other) const inline int Zstring::CmpNoCase(const Zstring& other) const { - return FreeFileSync::compareStringsWin32(c_str(), other.c_str()); //way faster than wxString::CmpNoCase()!! + return FreeFileSync::compareStringsWin32(c_str(), other.c_str(), length(), other.length()); //way faster than wxString::CmpNoCase()!! } #endif |