summaryrefslogtreecommitdiff
path: root/library
diff options
context:
space:
mode:
authorDaniel Wilhelm <daniel@wili.li>2014-04-18 16:55:48 +0200
committerDaniel Wilhelm <daniel@wili.li>2014-04-18 16:55:48 +0200
commitdaea231de0ae28fc8343f29f09d0457cc0591461 (patch)
treea1d572442d2c903e40741a859ad47c8b0d740969 /library
parent1.13 (diff)
downloadFreeFileSync-daea231de0ae28fc8343f29f09d0457cc0591461.tar.gz
FreeFileSync-daea231de0ae28fc8343f29f09d0457cc0591461.tar.bz2
FreeFileSync-daea231de0ae28fc8343f29f09d0457cc0591461.zip
1.14
Diffstat (limited to 'library')
-rw-r--r--library/CustomGrid.cpp94
-rw-r--r--library/CustomGrid.h19
-rw-r--r--library/fileHandling.cpp288
-rw-r--r--library/fileHandling.h33
-rw-r--r--library/globalFunctions.cpp4
-rw-r--r--library/globalFunctions.h3
-rw-r--r--library/misc.cpp5
-rw-r--r--library/multithreading.cpp31
-rw-r--r--library/multithreading.h1
-rw-r--r--library/processXml.cpp102
-rw-r--r--library/processXml.h6
-rw-r--r--library/resources.cpp30
-rw-r--r--library/sorting.h185
-rw-r--r--library/statusHandler.cpp28
-rw-r--r--library/statusHandler.h4
-rw-r--r--library/zstring.cpp396
-rw-r--r--library/zstring.h580
17 files changed, 1547 insertions, 262 deletions
diff --git a/library/CustomGrid.cpp b/library/CustomGrid.cpp
index 1abf73a7..25ceb537 100644
--- a/library/CustomGrid.cpp
+++ b/library/CustomGrid.cpp
@@ -2,6 +2,7 @@
#include "globalFunctions.h"
#include "resources.h"
#include <wx/dc.h>
+#include "../algorithm.h"
const unsigned int MinimumRows = 15;
@@ -18,6 +19,7 @@ public:
gridData(0),
lastNrRows(MinimumRows) {}
+
~CustomGridTableBase() {}
@@ -27,6 +29,7 @@ public:
this->gridData = gridData;
}
+
void SetGridIdentifier(int id)
{
gridIdentifier = id;
@@ -43,13 +46,13 @@ public:
return MinimumRows; //grid is initialized with this number of rows
}
+
virtual bool IsEmptyCell( int row, int col )
{
return (GetValue(row, col) == wxEmptyString);
}
- inline
wxString evaluateCmpResult(const CompareFilesResult result, const bool selectedForSynchronization)
{
if (selectedForSynchronization)
@@ -106,7 +109,7 @@ public:
case 2: //file size
return _("<Directory>");
case 3: //date
- return gridLine.fileDescrLeft.lastWriteTime;
+ return wxEmptyString;
}
}
else if (gridLine.fileDescrLeft.objType == FileDescrLine::TYPE_FILE)
@@ -120,10 +123,13 @@ public:
case 2: //file size
return globalFunctions::includeNumberSeparator(fileSize = gridLine.fileDescrLeft.fileSize.ToString());
case 3: //date
- return gridLine.fileDescrLeft.lastWriteTime;
+ return FreeFileSync::utcTimeToLocalString(gridLine.fileDescrLeft.lastWriteTimeRaw);
}
}
}
+ else
+ assert (false);
+
break;
case 2:
@@ -140,7 +146,7 @@ public:
case 2: //file size
return _("<Directory>");
case 3: //date
- return gridLine.fileDescrRight.lastWriteTime;
+ return wxEmptyString;
}
}
else if (gridLine.fileDescrRight.objType == FileDescrLine::TYPE_FILE)
@@ -154,20 +160,24 @@ public:
case 2: //file size
return globalFunctions::includeNumberSeparator(fileSize = gridLine.fileDescrRight.fileSize.ToString());
case 3: //date
- return gridLine.fileDescrRight.lastWriteTime;
+ return FreeFileSync::utcTimeToLocalString(gridLine.fileDescrRight.lastWriteTimeRaw);
}
}
}
+ else
+ assert (false);
+
break;
case 3:
if (col < 1)
- {
- return evaluateCmpResult(gridLine.cmpResult, gridLine.selectedForSynchronization);;
- }
+ return evaluateCmpResult(gridLine.cmpResult, gridLine.selectedForSynchronization);
+ else
+ assert (false);
break;
default:
+ assert (false);
break;
}
}
@@ -176,6 +186,7 @@ public:
return wxEmptyString;
}
+
virtual void SetValue( int row, int col, const wxString& value )
{
assert (false); //should not be used, since values are retrieved directly from gridRefUI
@@ -319,11 +330,10 @@ CustomGrid::CustomGrid(wxWindow *parent,
const wxString& name) :
wxGrid(parent, id, pos, size, style, name),
scrollbarsEnabled(true),
- m_gridLeft(0), m_gridRight(0), m_gridMiddle(0),
- gridDataTable(0),
+ m_gridLeft(NULL), m_gridRight(NULL), m_gridMiddle(NULL),
+ gridDataTable(NULL),
currentSortColumn(-1),
- sortMarker(0)
-{}
+ sortMarker(NULL) {}
CustomGrid::~CustomGrid() {}
@@ -335,7 +345,7 @@ bool CustomGrid::CreateGrid(int numRows, int numCols, wxGrid::wxGridSelectionMod
//This is done in CreateGrid instead of SetTable method since source code is generated and wxFormbuilder invokes CreatedGrid by default.
gridDataTable = new CustomGridTableBase(numRows, numCols);
- SetTable(gridDataTable, true, selmode); //give ownership to CustomGrid: gridDataTable is deleted automatically in CustomGrid destructor
+ SetTable(gridDataTable, true, selmode); //give ownership to wxGrid: gridDataTable is deleted automatically in wxGrid destructor
return true;
}
@@ -356,7 +366,7 @@ void CustomGrid::SetScrollbar(int orientation, int position, int thumbSize, int
}
-//ensure that all grids are properly aligned: add some extra window space to grids that have no horizontal scrollbar
+//workaround: ensure that all grids are properly aligned: add some extra window space to grids that have no horizontal scrollbar
void CustomGrid::adjustGridHeights() //m_gridLeft, m_gridRight, m_gridMiddle are not NULL in this context
{
int y1 = 0;
@@ -372,17 +382,17 @@ void CustomGrid::adjustGridHeights() //m_gridLeft, m_gridRight, m_gridMiddle are
{
int yMax = max(y1, max(y2, y3));
- if (leadingPanel == 1) //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 (leadingPanel == 2)
+ if (::leadGrid == m_gridRight)
m_gridRight->SetMargins(0, 0);
else if (y2 < yMax)
m_gridRight->SetMargins(0, 50);
- if (leadingPanel == 3)
+ if (::leadGrid == m_gridMiddle)
m_gridMiddle->SetMargins(0, 0);
else if (y3 < yMax)
m_gridMiddle->SetMargins(0, 50);
@@ -400,26 +410,29 @@ void CustomGrid::DoPrepareDC(wxDC& dc)
wxScrollHelper::DoPrepareDC(dc);
int x, y = 0;
- if (leadingPanel == 1 && this == m_gridLeft) //avoid back coupling
+ if (this == ::leadGrid) //avoid back coupling
{
- GetViewStart(&x, &y);
- m_gridRight->Scroll(x, y);
- m_gridMiddle->Scroll(-1, y); //scroll in y-direction only
- adjustGridHeights(); //keep here to ensure m_gridLeft, m_gridRight, m_gridMiddle != NULL
- }
- else if (leadingPanel == 2 && this == m_gridRight) //avoid back coupling
- {
- GetViewStart(&x, &y);
- m_gridLeft->Scroll(x, y);
- m_gridMiddle->Scroll(-1, y);
- adjustGridHeights(); //keep here to ensure m_gridLeft, m_gridRight, m_gridMiddle != NULL
- }
- else if (leadingPanel == 3 && this == m_gridMiddle) //avoid back coupling
- {
- GetViewStart(&x, &y);
- m_gridLeft->Scroll(-1, y);
- m_gridRight->Scroll(-1, y);
- adjustGridHeights(); //keep here to ensure m_gridLeft, m_gridRight, m_gridMiddle != NULL
+ if (this == m_gridLeft)
+ {
+ GetViewStart(&x, &y);
+ m_gridRight->Scroll(x, y);
+ m_gridMiddle->Scroll(-1, y); //scroll in y-direction only
+ adjustGridHeights(); //keep here to ensure m_gridLeft, m_gridRight, m_gridMiddle != NULL
+ }
+ else if (this == m_gridRight)
+ {
+ GetViewStart(&x, &y);
+ m_gridLeft->Scroll(x, y);
+ m_gridMiddle->Scroll(-1, y);
+ adjustGridHeights(); //keep here to ensure m_gridLeft, m_gridRight, m_gridMiddle != NULL
+ }
+ else if (this == m_gridMiddle)
+ {
+ GetViewStart(&x, &y);
+ m_gridLeft->Scroll(-1, y);
+ m_gridRight->Scroll(-1, y);
+ adjustGridHeights(); //keep here to ensure m_gridLeft, m_gridRight, m_gridMiddle != NULL
+ }
}
}
@@ -427,12 +440,10 @@ void CustomGrid::DoPrepareDC(wxDC& dc)
//these classes will scroll together, hence the name ;)
void CustomGrid::setScrollFriends(CustomGrid* gridLeft, CustomGrid* gridRight, CustomGrid* gridMiddle)
{
- assert(gridLeft);
- assert(gridRight);
- assert(gridMiddle);
+ assert(gridLeft && gridRight && gridMiddle);
- m_gridLeft = gridLeft;
- m_gridRight = gridRight;
+ m_gridLeft = gridLeft;
+ m_gridRight = gridRight;
m_gridMiddle = gridMiddle;
assert(gridDataTable);
@@ -479,3 +490,4 @@ void CustomGrid::DrawColLabel(wxDC& dc, int col)
dc.DrawBitmap(*sortMarker, GetColRight(col) - 16 - 2, 2, true); //respect 2-pixel border
}
}
+
diff --git a/library/CustomGrid.h b/library/CustomGrid.h
index 3547b090..bd2f3fa8 100644
--- a/library/CustomGrid.h
+++ b/library/CustomGrid.h
@@ -12,7 +12,7 @@ class CustomGridTableBase;
//##################################################################################
-extern int leadingPanel;
+extern const wxGrid* leadGrid; //this global variable is not very nice...
class CustomGrid : public wxGrid
{
@@ -26,27 +26,20 @@ public:
~CustomGrid();
- bool CreateGrid(int numRows, int numCols, wxGrid::wxGridSelectionModes selmode = wxGrid::wxGridSelectCells);
-
- void deactivateScrollbars();
-
+ virtual bool CreateGrid(int numRows, int numCols, wxGrid::wxGridSelectionModes selmode = wxGrid::wxGridSelectCells);
//overwrite virtual method to finally get rid of the scrollbars
- void SetScrollbar(int orientation, int position, int thumbSize, int range, bool refresh = true);
-
+ virtual void SetScrollbar(int orientation, int position, int thumbSize, int range, bool refresh = true);
//this method is called when grid view changes: useful for parallel updating of multiple grids
- void DoPrepareDC(wxDC& dc);
+ virtual void DoPrepareDC(wxDC& dc);
+ virtual void DrawColLabel(wxDC& dc, int col);
+ void deactivateScrollbars();
void setScrollFriends(CustomGrid* gridLeft, CustomGrid* gridRight, CustomGrid* gridMiddle);
-
void setGridDataTable(GridView* gridRefUI, FileCompareResult* gridData);
-
void updateGridSizes();
-
//set sort direction indicator on UI
void setSortMarker(const int sortColumn, const wxBitmap* bitmap = &wxNullBitmap);
- void DrawColLabel(wxDC& dc, int col);
-
private:
void adjustGridHeights();
diff --git a/library/fileHandling.cpp b/library/fileHandling.cpp
index e46a2b69..eb14b2f6 100644
--- a/library/fileHandling.cpp
+++ b/library/fileHandling.cpp
@@ -4,9 +4,16 @@
#include "resources.h"
#ifdef FFS_WIN
-#include <windows.h>
+#include <wx/msw/wrapwin.h> //includes "windows.h"
#endif // FFS_WIN
+inline
+bool endsWithPathSeparator(const wxChar* name)
+{
+ size_t len = wxStrlen(name);
+ return len && (name[len - 1] == GlobalResources::FILE_NAME_SEPARATOR);
+}
+
class RecycleBin
{
@@ -26,13 +33,13 @@ public:
return recycleBinAvailable;
}
- bool moveToRecycleBin(const wxString& filename)
+ bool moveToRecycleBin(const Zstring& filename)
{
if (!recycleBinAvailable) //this method should ONLY be called if recycle bin is available
- throw RuntimeException(_("Initialization of Recycle Bin failed! It cannot be used!"));
+ throw RuntimeException(_("Initialization of Recycle Bin failed!"));
#ifdef FFS_WIN
- wxString filenameDoubleNull = filename + wxChar(0);
+ Zstring filenameDoubleNull = filename + wxChar(0);
SHFILEOPSTRUCT fileOp;
fileOp.hwnd = NULL;
@@ -68,21 +75,21 @@ bool FreeFileSync::recycleBinExists()
inline
-bool moveToRecycleBin(const wxString& filename) throw(RuntimeException)
+bool moveToRecycleBin(const Zstring& filename) throw(RuntimeException)
{
return recyclerInstance.moveToRecycleBin(filename);
}
inline
-void FreeFileSync::removeFile(const wxString& filename, const bool useRecycleBin)
+void FreeFileSync::removeFile(const Zstring& filename, const bool useRecycleBin)
{
if (!wxFileExists(filename)) return; //this is NOT an error situation: the manual deletion relies on it!
if (useRecycleBin)
{
if (!moveToRecycleBin(filename))
- throw FileError(wxString(_("Error moving to recycle bin: ")) + wxT("\"") + filename + wxT("\""));
+ throw FileError(wxString(_("Error moving to recycle bin:")) + wxT(" \"") + filename.c_str() + wxT("\""));
return;
}
@@ -90,103 +97,126 @@ void FreeFileSync::removeFile(const wxString& filename, const bool useRecycleBin
if (!SetFileAttributes(
filename.c_str(), //address of filename
FILE_ATTRIBUTE_NORMAL //attributes to set
- )) throw FileError(wxString(_("Error deleting file: ")) + wxT("\"") + filename + wxT("\""));
+ )) throw FileError(wxString(_("Error deleting file:")) + wxT(" \"") + filename.c_str() + wxT("\""));
#endif //FFS_WIN
if (!wxRemoveFile(filename))
- throw FileError(wxString(_("Error deleting file: ")) + wxT("\"") + filename + wxT("\""));
+ throw FileError(wxString(_("Error deleting file:")) + wxT(" \"") + filename.c_str() + wxT("\""));
}
-void FreeFileSync::removeDirectory(const wxString& directory, const bool useRecycleBin)
+void FreeFileSync::removeDirectory(const Zstring& directory, const bool useRecycleBin)
{
if (!wxDirExists(directory)) return; //this is NOT an error situation: the manual deletion relies on it!
if (useRecycleBin)
{
if (!moveToRecycleBin(directory))
- throw FileError(wxString(_("Error moving to recycle bin: ")) + wxT("\"") + directory + wxT("\""));
+ throw FileError(wxString(_("Error moving to recycle bin:")) + wxT(" \"") + directory.c_str() + wxT("\""));
return;
}
- wxArrayString fileList;
- wxArrayString dirList;
+ vector<Zstring> fileList;
+ vector<Zstring> dirList;
try
- {
- //should be executed in own scope so that directory access does not disturb deletion!
+ { //should be executed in own scope so that directory access does not disturb deletion!
getAllFilesAndDirs(directory, fileList, dirList);
}
catch (const FileError& e)
{
- throw FileError(wxString(_("Error deleting directory: ")) + wxT("\"") + directory + wxT("\""));
+ throw FileError(wxString(_("Error deleting directory:")) + wxT(" \"") + directory.c_str() + wxT("\""));
}
- for (unsigned int j = 0; j < fileList.GetCount(); ++j)
- removeFile(fileList[j], useRecycleBin);
+ for (unsigned int j = 0; j < fileList.size(); ++j)
+ removeFile(fileList[j], false);
- dirList.Insert(directory, 0); //this directory will be deleted last
+ dirList.insert(dirList.begin(), directory); //this directory will be deleted last
- for (int j = int(dirList.GetCount()) - 1; j >= 0 ; --j)
+ for (int j = int(dirList.size()) - 1; j >= 0 ; --j)
{
#ifdef FFS_WIN
if (!SetFileAttributes(
- dirList[j].c_str(), // address of directory name
- FILE_ATTRIBUTE_NORMAL // attributes to set
- )) throw FileError(wxString(_("Error deleting directory: ")) + wxT("\"") + dirList[j] + wxT("\""));
+ dirList[j].c_str(), // address of directory name
+ FILE_ATTRIBUTE_NORMAL)) // attributes to set
+ throw FileError(wxString(_("Error deleting directory:")) + wxT(" \"") + dirList[j].c_str() + wxT("\""));
#endif // FFS_WIN
if (!wxRmdir(dirList[j]))
- throw FileError(wxString(_("Error deleting directory: ")) + wxT("\"") + dirList[j] + wxT("\""));
+ throw FileError(wxString(_("Error deleting directory:")) + wxT(" \"") + dirList[j].c_str() + wxT("\""));
}
}
-void FreeFileSync::createDirectory(const wxString& directory, int level)
+void FreeFileSync::createDirectory(const Zstring& directory, const int level)
{
if (wxDirExists(directory))
return;
- if (level == 50) //catch endless loop
+ if (level == 50) //catch endless recursion
return;
//try to create directory
if (wxMkdir(directory))
return;
- //if not successfull try to create containing folders first
- wxString createFirstDir = wxDir(directory).GetName().BeforeLast(GlobalResources::FILE_NAME_SEPARATOR);
+ //if not successfull try to create parent folders first
+ Zstring parentDir;
+ if (endsWithPathSeparator(directory.c_str())) //may be valid on first level of recursion at most! Usually never true!
+ {
+ parentDir = directory.BeforeLast(GlobalResources::FILE_NAME_SEPARATOR);
+ parentDir = parentDir.BeforeLast(GlobalResources::FILE_NAME_SEPARATOR);
+ }
+ else
+ parentDir = directory.BeforeLast(GlobalResources::FILE_NAME_SEPARATOR);
+
+ if (parentDir.empty()) return;
//call function recursively
- if (createFirstDir.IsEmpty()) return;
- createDirectory(createFirstDir, level + 1);
+ createDirectory(parentDir, level + 1);
//now creation should be possible
if (!wxMkdir(directory))
{
if (level == 0)
- throw FileError(wxString(_("Error creating directory ")) + wxT("\"") + directory + wxT("\""));
+ throw FileError(wxString(_("Error creating directory:")) + wxT(" \"") + directory.c_str() + wxT("\""));
}
}
+void FreeFileSync::copyFolderAttributes(const Zstring& source, const Zstring& target)
+{
+#ifdef FFS_WIN
+ DWORD attr = GetFileAttributes(source.c_str()); // address of the name of a file or directory
+ if (attr == 0xFFFFFFFF)
+ throw FileError(wxString(_("Error reading file attributes:")) + wxT(" \"") + source.c_str() + wxT("\""));
+
+ if (!SetFileAttributes(
+ target.c_str(), // address of filename
+ attr)) // address of attributes to set
+ throw FileError(wxString(_("Error writing file attributes:")) + wxT(" \"") + target.c_str() + wxT("\""));
+#elif defined FFS_LINUX
+//Linux doesn't use attributes for files or folders
+#endif
+}
+
+
class GetAllFilesSimple : public wxDirTraverser
{
public:
- GetAllFilesSimple(wxArrayString& files, wxArrayString& subDirectories) :
+ GetAllFilesSimple(vector<Zstring>& files, vector<Zstring>& subDirectories) :
m_files(files),
m_dirs(subDirectories) {}
-
wxDirTraverseResult OnDir(const wxString& dirname)
{
- m_dirs.Add(dirname);
+ m_dirs.push_back(dirname);
return wxDIR_CONTINUE;
}
wxDirTraverseResult OnFile(const wxString& filename)
{
- m_files.Add(filename);
+ m_files.push_back(filename);
return wxDIR_CONTINUE;
}
@@ -197,22 +227,198 @@ public:
}
private:
- wxArrayString& m_files;
- wxArrayString& m_dirs;
+ vector<Zstring>& m_files;
+ vector<Zstring>& m_dirs;
};
-void FreeFileSync::getAllFilesAndDirs(const wxString& sourceDir, wxArrayString& files, wxArrayString& directories) throw(FileError)
+void FreeFileSync::getAllFilesAndDirs(const Zstring& sourceDir, vector<Zstring>& files, vector<Zstring>& directories) throw(FileError)
{
- files.Clear();
- directories.Clear();
+ files.clear();
+ directories.clear();
//get all files and directories from current directory (and subdirectories)
wxDir dir(sourceDir);
GetAllFilesSimple traverser(files, directories);
if (dir.Traverse(traverser) == (size_t)-1)
- throw FileError(wxString(_("Error traversing directory ")) + wxT("\"") + sourceDir + wxT("\""));
+ throw FileError(wxString(_("Error traversing directory:")) + wxT(" \"") + sourceDir.c_str() + wxT("\""));
+}
+
+
+#ifdef FFS_WIN
+class CloseOnExit
+{
+public:
+ CloseOnExit(HANDLE searchHandle) : m_searchHandle(searchHandle) {}
+
+ ~CloseOnExit()
+ {
+ FindClose(m_searchHandle);
+ }
+
+private:
+ HANDLE m_searchHandle;
+};
+
+
+inline
+void getWin32FileInformation(const WIN32_FIND_DATA& input, FreeFileSync::FileInfo& output)
+{
+ //convert UTC FILETIME to ANSI C format (number of seconds since Jan. 1st 1970 UTC)
+ wxULongLong writeTimeLong(input.ftLastWriteTime.dwHighDateTime, input.ftLastWriteTime.dwLowDateTime);
+ writeTimeLong /= 10000000; //reduce precision to 1 second (FILETIME has unit 10^-7 s)
+ writeTimeLong -= wxULongLong(2, 3054539008UL); //timeshift between ansi C time and FILETIME in seconds == 11644473600s
+ output.lastWriteTimeRaw = writeTimeLong.GetLo(); //it should fit into a 32bit variable now
+ assert(writeTimeLong.GetHi() == 0);
+
+ output.fileSize = wxULongLong(input.nFileSizeHigh, input.nFileSizeLow);
+}
+
+#elif defined FFS_LINUX
+class EnhancedFileTraverser : public wxDirTraverser
+{
+public:
+ EnhancedFileTraverser(FreeFileSync::FullDetailFileTraverser* sink) : m_sink(sink) {}
+
+ virtual wxDirTraverseResult OnFile(const wxString& filename)
+ {
+ 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("\""));
+
+ FreeFileSync::FileInfo details;
+ details.lastWriteTimeRaw = fileInfo.st_mtime; //UTC time(ANSI C format); unit: 1 second
+ details.fileSize = fileInfo.st_size;
+
+ return m_sink->OnFile(filename, details);
+ }
+
+ virtual wxDirTraverseResult OnDir(const wxString& dirname)
+ {
+ return m_sink->OnDir(dirname);
+ }
+
+ virtual wxDirTraverseResult OnOpenError(const wxString& errorText)
+ {
+ return m_sink->OnError(errorText);
+ }
+
+private:
+ FreeFileSync::FullDetailFileTraverser* m_sink;
+};
+#endif
+
+
+bool FreeFileSync::traverseInDetail(const Zstring& directory, FullDetailFileTraverser* sink, const int level)
+{
+#ifdef FFS_WIN
+ if (level == 50) //catch endless recursion
+ {
+ if (sink->OnError(Zstring(_("Error traversing directory:")) + wxT(" ") + directory) == wxDIR_STOP)
+ return false;
+ else
+ return true;
+ }
+
+ Zstring directoryFormatted = directory;
+ if (!endsWithPathSeparator(directoryFormatted.c_str()))
+ directoryFormatted += GlobalResources::FILE_NAME_SEPARATOR;
+
+ const Zstring filespec = directoryFormatted + wxT("*.*");
+
+ 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...
+ {
+ switch (sink->OnDir(fullName))
+ {
+ case wxDIR_IGNORE:
+ break;
+ case wxDIR_CONTINUE:
+ if (!traverseInDetail(fullName, sink, level + 1))
+ return false;
+ else
+ break;
+ case wxDIR_STOP:
+ return false;
+ default:
+ assert(false);
+ break;
+ }
+ }
+ else //a file...
+ {
+ FileInfo details;
+ getWin32FileInformation(fileMetaData, details);
+
+ switch (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
+
+ 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)
+ return false;
+ else
+ return true;
+ }
+#elif defined FFS_LINUX
+
+ //use standard file traverser and enrich output with additional file information
+ //could be improved with own traversing algorithm for optimized performance
+ EnhancedFileTraverser traverser(sink);
+
+ wxDir dir(directory);
+ if (dir.IsOpened())
+ dir.Traverse(traverser);
+
+ return true;
+#else
+ adapt this
+#endif
}
+
+
+
+
+
diff --git a/library/fileHandling.h b/library/fileHandling.h
index a4f43391..cd86333b 100644
--- a/library/fileHandling.h
+++ b/library/fileHandling.h
@@ -3,7 +3,7 @@
#include "globalFunctions.h"
#include <wx/dir.h>
-
+#include "zstring.h"
class FileError //Exception class used to notify file/directory copy/delete errors
{
@@ -22,15 +22,38 @@ private:
namespace FreeFileSync
{
- void getAllFilesAndDirs(const wxString& sourceDir, wxArrayString& files, wxArrayString& directories) throw(FileError);
+ void getAllFilesAndDirs(const Zstring& sourceDir, vector<Zstring>& files, vector<Zstring>& directories) throw(FileError);
//recycler
bool recycleBinExists(); //test existence of Recycle Bin API on current system
//file handling
- void removeDirectory(const wxString& directory, const bool useRecycleBin);
- void removeFile(const wxString& filename, const bool useRecycleBin);
- void createDirectory(const wxString& directory, int level = 0); //level is used internally only
+ 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!
+ time_t lastWriteTimeRaw; //number of seconds since Jan. 1st 1970 UTC
+ };
+
+ //traverser interface
+ class FullDetailFileTraverser
+ {
+ public:
+ virtual ~FullDetailFileTraverser() {}
+ virtual wxDirTraverseResult OnFile(const Zstring& filename, const FileInfo& details) = 0;
+ virtual wxDirTraverseResult OnDir(const Zstring& dirname) = 0;
+ 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
+//################################
}
diff --git a/library/globalFunctions.cpp b/library/globalFunctions.cpp
index 7e99e036..7f8b57c0 100644
--- a/library/globalFunctions.cpp
+++ b/library/globalFunctions.cpp
@@ -73,7 +73,7 @@ int globalFunctions::wxStringToInt(const wxString& number)
if (number.ToLong(&result))
return result;
else
- throw RuntimeException(_("Error when converting wxString to long"));
+ throw RuntimeException(wxString(_("Conversion error:")) + wxT(" wxString -> long"));
}
@@ -84,7 +84,7 @@ double globalFunctions::wxStringToDouble(const wxString& number)
if (number.ToDouble(&result))
return result;
else
- throw RuntimeException(_("Error when converting wxString to double"));
+ throw RuntimeException(wxString(_("Conversion error:")) + wxT(" wxString -> double"));
}
diff --git a/library/globalFunctions.h b/library/globalFunctions.h
index add3c79d..f46a5906 100644
--- a/library/globalFunctions.h
+++ b/library/globalFunctions.h
@@ -44,9 +44,6 @@ namespace globalFunctions
int readInt(wxInputStream& stream); //read int from file stream
void writeInt(wxOutputStream& stream, const int number); //write int to filestream
-
- void startPerformance(); //helper method for quick performance measurement
- void stopPerformance();
}
diff --git a/library/misc.cpp b/library/misc.cpp
index f7aea8bd..050be108 100644
--- a/library/misc.cpp
+++ b/library/misc.cpp
@@ -75,6 +75,9 @@ void CustomLocale::setLanguage(const int language)
case wxLANGUAGE_DUTCH:
languageFile = "dutch.lng";
break;
+ case wxLANGUAGE_CHINESE_SIMPLIFIED:
+ languageFile = "chinese_simple.lng";
+ break;
default:
languageFile.clear();
currentLanguage = wxLANGUAGE_ENGLISH;
@@ -123,7 +126,7 @@ void CustomLocale::setLanguage(const int language)
langFile.close();
}
else
- wxMessageBox(wxString(_("Could not read language file ")) + wxT("\"") + wxString(languageFile.c_str(), wxConvUTF8) + wxT("\""), _("An exception occured!"), wxOK | wxICON_ERROR);
+ wxMessageBox(wxString(_("Error reading file:")) + wxT(" \"") + wxString(languageFile.c_str(), wxConvUTF8) + wxT("\""), _("An exception occured!"), wxOK | wxICON_ERROR);
}
else
; //if languageFile is empty texts will be english per default
diff --git a/library/multithreading.cpp b/library/multithreading.cpp
index 753e6651..bf5918d2 100644
--- a/library/multithreading.cpp
+++ b/library/multithreading.cpp
@@ -1,36 +1,9 @@
#include "multithreading.h"
#include <wx/utils.h>
-#include <wx/app.h>
-#include <wx/timer.h>
-//#include "windows.h"
+//#include <wx/msw/wrapwin.h> //includes "windows.h"
//MessageBox(0, "hi", "", 0);
-void updateUiNow()
-{
- //process UI events and prevent application from "not responding" -> NO performance issue!
- wxTheApp->Yield();
-
- // while (wxTheApp->Pending())
- // wxTheApp->Dispatch();
-}
-
-
-bool updateUiIsAllowed()
-{
- static wxLongLong lastExec = 0;
-
- wxLongLong newExec = wxGetLocalTimeMillis();
-
- if (newExec - lastExec >= UI_UPDATE_INTERVAL) //perform ui updates not more often than necessary
- {
- lastExec = newExec;
- return true;
- }
- return false;
-}
-
-
/*choreography:
------------- ---------------
@@ -97,7 +70,7 @@ public:
threadHandler->readyToReceiveResult.Lock();
threadHandler->receivingResult.Signal(); // kind of a double notice that work is completed
- threadHandler->workDone = true; // workaround for wxCondition bug (wxWidgets v2.8.9, signal might geht lost)
+ threadHandler->workDone = true; // Workaround for wxWidgets: bug in wxCondition (wxWidgets v2.8.9, signal might geht lost)
threadHandler->readyToReceiveResult.Unlock();
}
diff --git a/library/multithreading.h b/library/multithreading.h
index 68ad7492..bf0da145 100644
--- a/library/multithreading.h
+++ b/library/multithreading.h
@@ -2,6 +2,7 @@
#define MULTITHREADING_H_INCLUDED
#include "statusHandler.h"
+#include <wx/thread.h>
class WorkerThread;
diff --git a/library/processXml.cpp b/library/processXml.cpp
index 2ce22b3e..04048124 100644
--- a/library/processXml.cpp
+++ b/library/processXml.cpp
@@ -115,10 +115,10 @@ XmlGuiConfig xmlAccess::readGuiConfig(const wxString& filename)
XmlGuiConfig outputCfg;
if (!inputFile.loadedSuccessfully())
- throw FileError(wxString(_("Could not open configuration file ")) + wxT("\"") + filename + wxT("\""));
+ throw FileError(wxString(_("Error reading file:")) + wxT(" \"") + filename + wxT("\""));
if (!inputFile.readXmlGuiConfig(outputCfg)) //read GUI layout configuration
- throw FileError(wxString(_("Error parsing configuration file ")) + wxT("\"") + filename + wxT("\""));
+ throw FileError(wxString(_("Error parsing configuration file:")) + wxT(" \"") + filename + wxT("\""));
return outputCfg;
}
@@ -132,10 +132,10 @@ XmlBatchConfig xmlAccess::readBatchConfig(const wxString& filename)
XmlBatchConfig outputCfg;
if (!inputFile.loadedSuccessfully())
- throw FileError(wxString(_("Could not open configuration file ")) + wxT("\"") + filename + wxT("\""));
+ throw FileError(wxString(_("Error reading file:")) + wxT(" \"") + filename + wxT("\""));
if (!inputFile.readXmlBatchConfig(outputCfg))
- throw FileError(wxString(_("Error parsing configuration file ")) + wxT("\"") + filename + wxT("\""));
+ throw FileError(wxString(_("Error parsing configuration file:")) + wxT(" \"") + filename + wxT("\""));
return outputCfg;
}
@@ -149,10 +149,10 @@ XmlGlobalSettings xmlAccess::readGlobalSettings()
XmlGlobalSettings outputCfg;
if (!inputFile.loadedSuccessfully())
- throw FileError(wxString(_("Could not open configuration file ")) + wxT("\"") + FreeFileSync::GLOBAL_CONFIG_FILE + wxT("\""));
+ throw FileError(wxString(_("Error reading file:")) + wxT(" \"") + FreeFileSync::GLOBAL_CONFIG_FILE + wxT("\""));
if (!inputFile.readXmlGlobalSettings(outputCfg))
- throw FileError(wxString(_("Error parsing configuration file ")) + wxT("\"") + FreeFileSync::GLOBAL_CONFIG_FILE + wxT("\""));
+ throw FileError(wxString(_("Error parsing configuration file:")) + wxT(" \"") + FreeFileSync::GLOBAL_CONFIG_FILE + wxT("\""));
return outputCfg;
}
@@ -165,7 +165,7 @@ void xmlAccess::writeGuiConfig(const wxString& filename, const XmlGuiConfig& inp
//populate and write XML tree
if ( !outputFile.writeXmlGuiConfig(inputCfg) || //add GUI layout configuration settings
!outputFile.writeToFile()) //save XML
- throw FileError(wxString(_("Could not write configuration file ")) + wxT("\"") + filename + wxT("\""));
+ throw FileError(wxString(_("Error writing file:")) + wxT(" \"") + filename + wxT("\""));
return;
}
@@ -177,7 +177,7 @@ void xmlAccess::writeBatchConfig(const wxString& filename, const XmlBatchConfig&
//populate and write XML tree
if ( !outputFile.writeXmlBatchConfig(inputCfg) || //add GUI layout configuration settings
!outputFile.writeToFile()) //save XML
- throw FileError(wxString(_("Could not write configuration file ")) + wxT("\"") + filename + wxT("\""));
+ throw FileError(wxString(_("Error writing file:")) + wxT(" \"") + filename + wxT("\""));
return;
}
@@ -189,7 +189,7 @@ void xmlAccess::writeGlobalSettings(const XmlGlobalSettings& inputCfg)
//populate and write XML tree
if ( !outputFile.writeXmlGlobalSettings(inputCfg) || //add GUI layout configuration settings
!outputFile.writeToFile()) //save XML
- throw FileError(wxString(_("Could not write configuration file ")) + wxT("\"") + FreeFileSync::GLOBAL_CONFIG_FILE + wxT("\""));
+ throw FileError(wxString(_("Error writing file:")) + wxT(" \"") + FreeFileSync::GLOBAL_CONFIG_FILE + wxT("\""));
return;
}
@@ -338,10 +338,10 @@ bool XmlConfigInput::readXmlMainConfig(MainConfiguration& mainCfg, vector<Folder
FolderPair newPair;
if (!readXmlElementValue(tempString, folderPair, "Left")) return false;
- newPair.leftDirectory = wxString::FromUTF8(tempString.c_str());
+ newPair.leftDirectory = wxString::FromUTF8(tempString.c_str()).c_str();
if (!readXmlElementValue(tempString, folderPair, "Right")) return false;
- newPair.rightDirectory = wxString::FromUTF8(tempString.c_str());
+ newPair.rightDirectory = wxString::FromUTF8(tempString.c_str()).c_str();
directoryPairs.push_back(newPair);
folderPair = folderPair->NextSiblingElement();
@@ -462,7 +462,6 @@ bool XmlConfigInput::readXmlGlobalSettings(XmlGlobalSettings& outputCfg)
outputCfg.gui.columnWidthLeft.push_back(stringToInt(width));
else
break;
-
leftColumn = leftColumn->NextSiblingElement();
}
@@ -474,9 +473,31 @@ bool XmlConfigInput::readXmlGlobalSettings(XmlGlobalSettings& outputCfg)
outputCfg.gui.columnWidthRight.push_back(stringToInt(width));
else
break;
-
rightColumn = rightColumn->NextSiblingElement();
}
+
+ //read column positions
+ TiXmlElement* leftColumnPos = TiXmlHandle(mainWindow).FirstChild("LeftColumnPositions").FirstChild("Position").ToElement();
+ while (leftColumnPos)
+ {
+ const char* width = leftColumnPos->GetText();
+ if (width) //may be NULL!!
+ outputCfg.gui.columnPositionsLeft.push_back(stringToInt(width));
+ else
+ break;
+ leftColumnPos = leftColumnPos->NextSiblingElement();
+ }
+
+ TiXmlElement* rightColumnPos = TiXmlHandle(mainWindow).FirstChild("RightColumnPositions").FirstChild("Position").ToElement();
+ while (rightColumnPos)
+ {
+ const char* width = rightColumnPos->GetText();
+ if (width) //may be NULL!!
+ outputCfg.gui.columnPositionsRight.push_back(stringToInt(width));
+ else
+ break;
+ rightColumnPos = rightColumnPos->NextSiblingElement();
+ }
}
@@ -589,8 +610,8 @@ bool XmlConfigOutput::writeXmlMainConfig(const MainConfiguration& mainCfg, const
TiXmlElement* folderPair = new TiXmlElement("Pair");
folders->LinkEndChild(folderPair);
- addXmlElement(folderPair, "Left", string((i->leftDirectory).ToUTF8()));
- addXmlElement(folderPair, "Right", string((i->rightDirectory).ToUTF8()));
+ addXmlElement(folderPair, "Left", string(wxString(i->leftDirectory.c_str()).ToUTF8()));
+ addXmlElement(folderPair, "Right", string(wxString(i->rightDirectory.c_str()).ToUTF8()));
}
//###########################################################
@@ -726,6 +747,18 @@ bool XmlConfigOutput::writeXmlGlobalSettings(const XmlGlobalSettings& inputCfg)
for (unsigned int i = 0; i < inputCfg.gui.columnWidthRight.size(); ++i)
addXmlElement(rightColumn, "Width", inputCfg.gui.columnWidthRight[i]);
+ //write column positions
+ TiXmlElement* leftColumnPos = new TiXmlElement("LeftColumnPositions");
+ mainWindow->LinkEndChild(leftColumnPos);
+
+ for (unsigned int i = 0; i < inputCfg.gui.columnPositionsLeft.size(); ++i)
+ addXmlElement(leftColumnPos, "Position", inputCfg.gui.columnPositionsLeft[i]);
+
+ TiXmlElement* rightColumnPos = new TiXmlElement("RightColumnPositions");
+ mainWindow->LinkEndChild(rightColumnPos);
+
+ for (unsigned int i = 0; i < inputCfg.gui.columnPositionsRight.size(); ++i)
+ addXmlElement(rightColumnPos, "Position", inputCfg.gui.columnPositionsRight[i]);
//###################################################################
//write batch settings
@@ -735,3 +768,42 @@ bool XmlConfigOutput::writeXmlGlobalSettings(const XmlGlobalSettings& inputCfg)
return true;
}
+
+
+int xmlAccess::retrieveSystemLanguage() //map language dialects
+{
+ const int lang = wxLocale::GetSystemLanguage();
+
+ switch (lang)
+ {
+ case wxLANGUAGE_GERMAN_AUSTRIAN:
+ case wxLANGUAGE_GERMAN_BELGIUM:
+ case wxLANGUAGE_GERMAN_LIECHTENSTEIN:
+ case wxLANGUAGE_GERMAN_LUXEMBOURG:
+ case wxLANGUAGE_GERMAN_SWISS:
+ return wxLANGUAGE_GERMAN;
+
+ case wxLANGUAGE_FRENCH_BELGIAN:
+ case wxLANGUAGE_FRENCH_CANADIAN:
+ case wxLANGUAGE_FRENCH_LUXEMBOURG:
+ case wxLANGUAGE_FRENCH_MONACO:
+ case wxLANGUAGE_FRENCH_SWISS:
+ return wxLANGUAGE_FRENCH;
+
+ //case wxLANGUAGE_JAPANESE:
+
+ case wxLANGUAGE_DUTCH_BELGIAN:
+ return wxLANGUAGE_DUTCH;
+
+ case wxLANGUAGE_CHINESE:
+ case wxLANGUAGE_CHINESE_TRADITIONAL:
+ case wxLANGUAGE_CHINESE_HONGKONG:
+ case wxLANGUAGE_CHINESE_MACAU:
+ case wxLANGUAGE_CHINESE_SINGAPORE:
+ case wxLANGUAGE_CHINESE_TAIWAN:
+ return wxLANGUAGE_CHINESE_SIMPLIFIED;
+
+ default:
+ return lang;
+ }
+}
diff --git a/library/processXml.h b/library/processXml.h
index e359e7b0..bc81556e 100644
--- a/library/processXml.h
+++ b/library/processXml.h
@@ -3,7 +3,6 @@
#include "../FreeFileSync.h"
#include "tinyxml/tinyxml.h"
-#include <wx/intl.h>
using namespace FreeFileSync;
@@ -42,13 +41,14 @@ namespace xmlAccess
bool silent;
};
+ int retrieveSystemLanguage();
struct XmlGlobalSettings
{
struct _Global
{
_Global() :
- programLanguage(wxLocale::GetSystemLanguage()),
+ programLanguage(retrieveSystemLanguage()),
#ifdef FFS_WIN
dstCheckActive(true),
#endif
@@ -77,6 +77,8 @@ namespace xmlAccess
bool isMaximized;
vector<int> columnWidthLeft;
vector<int> columnWidthRight;
+ vector<int> columnPositionsLeft;
+ vector<int> columnPositionsRight;
} gui;
//struct _Batch
diff --git a/library/resources.cpp b/library/resources.cpp
index 68eeb4d6..9169bf85 100644
--- a/library/resources.cpp
+++ b/library/resources.cpp
@@ -3,6 +3,7 @@
#include <wx/zipstrm.h>
#include <wx/image.h>
#include <wx/icon.h>
+#include <wx/mstream.h>
#include "globalFunctions.h"
#ifdef FFS_WIN
@@ -111,6 +112,24 @@ GlobalResources::~GlobalResources()
}
+void loadAnimFromZip(wxZipInputStream& zipInput, wxAnimation* animation)
+{
+ //Workaround for wxWidgets:
+ //construct seekable input stream (zip-input stream is non-seekable) for wxAnimation::Load()
+ //luckily this method call is very fast: below measurement precision!
+ vector<unsigned char> data;
+ data.reserve(10000);
+
+ int newValue = 0;
+ while ((newValue = zipInput.GetC()) != wxEOF)
+ data.push_back(newValue);
+
+ wxMemoryInputStream seekAbleStream(&data.front(), data.size()); //stream does not take ownership of data
+
+ animation->Load(seekAbleStream, wxANIMATION_TYPE_GIF);
+}
+
+
void GlobalResources::load()
{
wxFileInputStream input(wxT("Resources.dat"));
@@ -121,21 +140,22 @@ void GlobalResources::load()
wxZipInputStream resourceFile(input);
- wxZipEntry* entry;
+ wxZipEntry* entry = NULL;
map<wxString, wxBitmap*>::iterator bmp;
while ((entry = resourceFile.GetNextEntry()))
{
wxString name = entry->GetName();
- //just to be sure: search if entry is available in map
+ //search if entry is available in map
if ((bmp = bitmapResource.find(name)) != bitmapResource.end())
*(bmp->second) = wxBitmap(wxImage(resourceFile, wxBITMAP_TYPE_PNG));
+ else if (name == wxT("money.gif"))
+ loadAnimFromZip(resourceFile, animationMoney);
+ else if (name == wxT("working.gif"))
+ loadAnimFromZip(resourceFile, animationSync);
}
}
- animationMoney->LoadFile(wxT("Resources.a01"));
- animationSync->LoadFile(wxT("Resources.a02"));
-
#ifdef FFS_WIN
programIcon = new wxIcon(wxT("ffsIcon1"));
#else
diff --git a/library/sorting.h b/library/sorting.h
index bf643176..560e246a 100644
--- a/library/sorting.h
+++ b/library/sorting.h
@@ -3,6 +3,7 @@
#include "../FreeFileSync.h"
#include "resources.h"
+#include "globalFunctions.h"
enum SideToSort
@@ -12,34 +13,6 @@ enum SideToSort
};
-template <bool sortAscending>
-inline
-bool cmpString(const wxString& a, const wxString& b)
-{
- if (a.IsEmpty())
- return false; //if a and b are empty: false, if a empty, b not empty: also false, since empty rows should appear at the end
- else if (b.IsEmpty())
- return true; //empty rows after filled rows: return true
-
- //if a and b not empty:
- if (sortAscending)
- return (a < b);
- else
- return (a > b);
-}
-
-
-template <bool sortAscending>
-inline
-bool cmpLargeInt(const wxULongLong& a, const wxULongLong& b)
-{
- if (sortAscending)
- return (a < b);
- else
- return (a > b);
-}
-
-
template <SideToSort side>
inline
void getDescrLine(const FileCompareLine& a, const FileCompareLine& b, const FileDescrLine*& descrLineA, const FileDescrLine*& descrLineB)
@@ -54,49 +27,68 @@ void getDescrLine(const FileCompareLine& a, const FileCompareLine& b, const File
descrLineA = &a.fileDescrRight;
descrLineB = &b.fileDescrRight;
}
- else assert(false);
+ else
+ assert(false);
}
+template <bool sortAscending>
inline
-wxChar formatChar(const wxChar& c)
+bool stringSmallerThan(const wxChar* stringA, const wxChar* stringB)
{
- return c;
- //return wxTolower(c); <- this is slow as hell! sorting slower by factor 10
+#ifdef FFS_WIN //case-insensitive comparison!
+ return sortAscending ?
+ FreeFileSync::compareStringsWin32(stringA, stringB) < 0 : //way faster than wxString::CmpNoCase() in windows build!!!
+ FreeFileSync::compareStringsWin32(stringA, stringB) > 0;
+#else
+ while (*stringA == *stringB)
+ {
+ if (*stringA == wxChar(0)) //strings are equal
+ return false;
+
+ ++stringA;
+ ++stringB;
+ }
+ return sortAscending ? *stringA < *stringB : *stringA > *stringB; //wxChar(0) is handled correctly
+#endif
}
inline
int compareString(const wxChar* stringA, const wxChar* stringB, const int lengthA, const int lengthB)
{
+#ifdef FFS_WIN //case-insensitive comparison!
+ return FreeFileSync::compareStringsWin32(stringA, stringB, lengthA, lengthB); //way faster than wxString::CmpNoCase() in the windows build!!!
+#else
int i = 0;
if (lengthA == lengthB)
{
for (i = 0; i < lengthA; ++i)
{
- if (formatChar(stringA[i]) != formatChar(stringB[i]))
+ if (stringA[i] != stringB[i])
break;
}
- return i == lengthA ? 0 : formatChar(stringA[i]) < formatChar(stringB[i]) ? -1 : 1;
+ return i == lengthA ? 0 : stringA[i] < stringB[i] ? -1 : 1;
}
else if (lengthA < lengthB)
{
for (i = 0; i < lengthA; ++i)
{
- if (formatChar(stringA[i]) != formatChar(stringB[i]))
+ if (stringA[i] != stringB[i])
break;
}
- return i == lengthA ? -1 : formatChar(stringA[i]) < formatChar(stringB[i]) ? -1 : 1;
+ return i == lengthA ? -1 : stringA[i] < stringB[i] ? -1 : 1;
}
else
{
for (i = 0; i < lengthB; ++i)
{
- if (formatChar(stringA[i]) != formatChar(stringB[i]))
+ if (stringA[i] != stringB[i])
break;
}
- return i == lengthB ? 1 : formatChar(stringA[i]) < formatChar(stringB[i]) ? -1 : 1;
+ return i == lengthB ? 1 : stringA[i] < stringB[i] ? -1 : 1;
}
+#endif
}
@@ -119,43 +111,23 @@ bool sortByFileName(const FileCompareLine& a, const FileCompareLine& b)
return true;
else
{
- const wxChar* stringA = NULL;
- const wxChar* stringB = NULL;
- int lenghtA = 0;
- int lenghtB = 0;
+ const wxChar* stringA = descrLineA->relativeName.c_str();
+ const wxChar* stringB = descrLineB->relativeName.c_str();
- int pos = descrLineA->relativeName.Find(GlobalResources::FILE_NAME_SEPARATOR, true); //start search beginning from end
- if (pos == wxNOT_FOUND)
- {
- stringA = descrLineA->relativeName.c_str();
- lenghtA = descrLineA->relativeName.Len();
- }
- else
- {
- stringA = descrLineA->relativeName.c_str() + pos + 1;
- lenghtA = descrLineA->relativeName.Len() - (pos + 1);
- }
+ size_t pos = descrLineA->relativeName.Find(GlobalResources::FILE_NAME_SEPARATOR, true); //start search beginning from end
+ if (pos != string::npos)
+ stringA += pos + 1;
pos = descrLineB->relativeName.Find(GlobalResources::FILE_NAME_SEPARATOR, true); //start search beginning from end
- if (pos == wxNOT_FOUND)
- {
- stringB = descrLineB->relativeName.c_str();
- lenghtB = descrLineB->relativeName.Len();
- }
- else
- {
- stringB = descrLineB->relativeName.c_str() + pos + 1;
- lenghtB = descrLineB->relativeName.Len() - (pos + 1);
- }
+ if (pos != string::npos)
+ stringB += pos + 1;
- int rv = compareString(stringA, stringB, lenghtA, lenghtB);
- return sortAscending ? (rv == -1) : (rv == 1);
+ return stringSmallerThan<sortAscending>(stringA, stringB);
}
}
template <bool sortAscending, SideToSort side>
-inline
bool sortByRelativeName(const FileCompareLine& a, const FileCompareLine& b)
{
const FileDescrLine* descrLineA = NULL;
@@ -163,67 +135,59 @@ bool sortByRelativeName(const FileCompareLine& a, const FileCompareLine& b)
getDescrLine<side>(a, b, descrLineA, descrLineB);
//extract relative name and filename
- const wxChar* relStringA = NULL;
- const wxChar* fileStringA = NULL;
- int relLenghtA = 0;
+ const wxChar* relStringA = descrLineA->relativeName.c_str(); //mustn't be NULL for CompareString() API to work correctly
+ const wxChar* fileStringA = relStringA;
+ int relLengthA = 0;
int fileLengthA = 0;
+
if (descrLineA->objType == FileDescrLine::TYPE_DIRECTORY)
- {
- relStringA = descrLineA->relativeName.c_str();
- relLenghtA = descrLineA->relativeName.Len();
- }
+ relLengthA = descrLineA->relativeName.length();
else if (descrLineA->objType == FileDescrLine::TYPE_FILE)
{
- relLenghtA = descrLineA->relativeName.Find(GlobalResources::FILE_NAME_SEPARATOR, true); //start search beginning from end
- if (relLenghtA == wxNOT_FOUND)
+ relLengthA = descrLineA->relativeName.Find(GlobalResources::FILE_NAME_SEPARATOR, true); //start search beginning from end
+ if (relLengthA == wxNOT_FOUND)
{
- relLenghtA = 0;
- fileStringA = descrLineA->relativeName.c_str();
- fileLengthA = descrLineA->relativeName.Len();
+ relLengthA = 0;
+ fileLengthA = descrLineA->relativeName.length();
}
else
{
- relStringA = descrLineA->relativeName.c_str();
- fileStringA = descrLineA->relativeName.c_str() + relLenghtA + 1;
- fileLengthA = descrLineA->relativeName.Len() - (relLenghtA + 1);
+ fileStringA += relLengthA + 1;
+ fileLengthA = descrLineA->relativeName.length() - (relLengthA + 1);
}
}
else
return false; //empty rows should be on end of list
- const wxChar* relStringB = NULL;
- const wxChar* fileStringB = NULL;
- int relLenghtB = 0;
+ const wxChar* relStringB = descrLineB->relativeName.c_str(); //mustn't be NULL for CompareString() API to work correctly
+ const wxChar* fileStringB = relStringB;
+ int relLengthB = 0;
int fileLengthB = 0;
+
if (descrLineB->objType == FileDescrLine::TYPE_DIRECTORY)
- {
- relStringB = descrLineB->relativeName.c_str();
- relLenghtB = descrLineB->relativeName.Len();
- }
+ relLengthB = descrLineB->relativeName.length();
else if (descrLineB->objType == FileDescrLine::TYPE_FILE)
{
- relLenghtB = descrLineB->relativeName.Find(GlobalResources::FILE_NAME_SEPARATOR, true); //start search beginning from end
- if (relLenghtB == wxNOT_FOUND)
+ relLengthB = descrLineB->relativeName.Find(GlobalResources::FILE_NAME_SEPARATOR, true); //start search beginning from end
+ if (relLengthB == wxNOT_FOUND)
{
- relLenghtB = 0;
- fileStringB = descrLineB->relativeName.c_str();
- fileLengthB = descrLineB->relativeName.Len();
+ relLengthB = 0;
+ fileLengthB = descrLineB->relativeName.length();
}
else
{
- relStringB = descrLineB->relativeName.c_str();
- fileStringB = descrLineB->relativeName.c_str() + relLenghtB + 1;
- fileLengthB = descrLineB->relativeName.Len() - (relLenghtB + 1);
+ fileStringB += relLengthB + 1;
+ fileLengthB = descrLineB->relativeName.length() - (relLengthB + 1);
}
}
else
return true; //empty rows should be on end of list
//compare relative names without filenames first
- int rv = compareString(relStringA, relStringB, relLenghtA, relLenghtB);
+ int rv = compareString(relStringA, relStringB, relLengthA, relLengthB);
if (rv != 0)
- return sortAscending ? (rv == -1) : (rv == 1);
+ return sortAscending ? (rv < 0) : (rv > 0);
else //compare the filenames
{
if (descrLineB->objType == FileDescrLine::TYPE_DIRECTORY) //directories shall appear before files
@@ -231,8 +195,9 @@ bool sortByRelativeName(const FileCompareLine& a, const FileCompareLine& b)
else if (descrLineA->objType == FileDescrLine::TYPE_DIRECTORY)
return true;
- rv = compareString(fileStringA, fileStringB, fileLengthA, fileLengthB);
- return sortAscending ? (rv == -1) : (rv == 1);
+ return sortAscending ?
+ compareString(fileStringA, fileStringB, fileLengthA, fileLengthB) < 0 :
+ compareString(fileStringA, fileStringB, fileLengthA, fileLengthB) > 0;
}
}
@@ -254,8 +219,10 @@ bool sortByFileSize(const FileCompareLine& a, const FileCompareLine& b)
return false;
else if (descrLineB->objType == FileDescrLine::TYPE_DIRECTORY)
return true;
- else //use unformatted filesizes and sort by size
- return cmpLargeInt<sortAscending>(descrLineA->fileSize, descrLineB->fileSize);
+ else
+ return sortAscending ?
+ descrLineA->fileSize < descrLineB->fileSize :
+ descrLineA->fileSize > descrLineB->fileSize;
}
@@ -267,7 +234,19 @@ bool sortByDate(const FileCompareLine& a, const FileCompareLine& b)
const FileDescrLine* descrLineB = NULL;
getDescrLine<side>(a, b, descrLineA, descrLineB);
- return cmpString<sortAscending>(descrLineA->lastWriteTime, descrLineB->lastWriteTime);
+ //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
+ else if (descrLineA->objType == FileDescrLine::TYPE_DIRECTORY)
+ return false;
+ else if (descrLineB->objType == FileDescrLine::TYPE_DIRECTORY)
+ return true;
+ else
+ return sortAscending ?
+ descrLineA->lastWriteTimeRaw < descrLineB->lastWriteTimeRaw :
+ descrLineA->lastWriteTimeRaw > descrLineB->lastWriteTimeRaw;
}
diff --git a/library/statusHandler.cpp b/library/statusHandler.cpp
new file mode 100644
index 00000000..e19c9904
--- /dev/null
+++ b/library/statusHandler.cpp
@@ -0,0 +1,28 @@
+#include "statusHandler.h"
+#include <wx/app.h>
+#include <wx/timer.h>
+
+
+void updateUiNow()
+{
+ //process UI events and prevent application from "not responding" -> NO performance issue!
+ wxTheApp->Yield();
+
+ // while (wxTheApp->Pending())
+ // wxTheApp->Dispatch();
+}
+
+
+bool updateUiIsAllowed()
+{
+ static wxLongLong lastExec = 0;
+
+ wxLongLong newExec = wxGetLocalTimeMillis();
+
+ if (newExec - lastExec >= UI_UPDATE_INTERVAL) //perform ui updates not more often than necessary
+ {
+ lastExec = newExec;
+ return true;
+ }
+ return false;
+}
diff --git a/library/statusHandler.h b/library/statusHandler.h
index 70f8c2d8..4c12035d 100644
--- a/library/statusHandler.h
+++ b/library/statusHandler.h
@@ -2,7 +2,7 @@
#define STATUSHANDLER_H_INCLUDED
#include <wx/string.h>
-#include <wx/thread.h>
+
const int UI_UPDATE_INTERVAL = 100; //perform ui updates not more often than necessary, 100 seems to be a good value with only a minimal performance loss
@@ -21,7 +21,7 @@ public:
enum Response
{
- CONTINUE_NEXT = 10,
+ IGNORE_ERROR = 10,
RETRY
};
virtual Response reportError(const wxString& text) = 0;
diff --git a/library/zstring.cpp b/library/zstring.cpp
new file mode 100644
index 00000000..b84c9512
--- /dev/null
+++ b/library/zstring.cpp
@@ -0,0 +1,396 @@
+#include "zstring.h"
+#include <wx/intl.h>
+#include "globalFunctions.h"
+
+
+#ifdef FFS_WIN
+#include <wx/msw/wrapwin.h> //includes "windows.h"
+#endif //FFS_WIN
+
+
+#ifdef __WXDEBUG__
+int allocCount = 0; //test Zstring for memory leaks
+
+void testZstringForMemoryLeak()
+{
+ if (allocCount != 0)
+#ifdef FFS_WIN
+ MessageBox(NULL, wxT("Fatal Error! Allocation problem with Zstring! (No problem if it occures while Unit testing only!)"), wxString::Format(wxT("%i"), allocCount), 0);
+#else
+ throw;
+#endif //FFS_WIN
+}
+#endif
+
+
+#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
+
+ if (rv == 0)
+ throw RuntimeException(wxString(_("Error comparing strings!")));
+ else
+ return rv - 2;
+}
+#endif
+
+
+size_t Zstring::Replace(const defaultChar* old, const defaultChar* replacement, bool replaceAll)
+{
+ const size_t oldLen = defaultLength(old);
+ const size_t replacementLen = defaultLength(replacement);
+ size_t uiCount = 0; //count of replacements made
+
+ size_t pos = 0;
+ while (true)
+ {
+ pos = find(old, pos);
+ if (pos == npos)
+ break;
+
+ replace(pos, oldLen, replacement, replacementLen);
+ pos += replacementLen; //move past the string that was replaced
+
+ ++uiCount; //increase replace count
+
+ // stop now?
+ if (!replaceAll)
+ break;
+ }
+ return uiCount;
+}
+
+
+bool matchesHelper(const defaultChar* string, const defaultChar* mask)
+{
+ for (defaultChar ch; (ch = *mask) != 0; ++mask)
+ {
+ switch (ch)
+ {
+ case defaultChar('?'):
+ if (*string == 0)
+ return false;
+ else
+ ++string;
+ break;
+
+ case defaultChar('*'):
+ //advance to next non-*/? char
+ do
+ {
+ ++mask;
+ ch = *mask;
+ }
+ while (ch == defaultChar('*') || ch == defaultChar('?'));
+ //if match ends with '*':
+ if (ch == defaultChar(0))
+ return true;
+
+ ++mask;
+ while ((string = defaultStrFind(string, ch)) != NULL)
+ {
+ if (matchesHelper(string + 1, mask))
+ return true;
+ ++string;
+ }
+ return false;
+ break;
+
+ default:
+ if (*string != ch)
+ return false;
+ else
+ ++string;
+ }
+ }
+ return *string == 0;
+}
+
+
+bool Zstring::Matches(const defaultChar* mask) const
+{
+ return matchesHelper(c_str(), mask);
+}
+
+
+Zstring& Zstring::Trim(bool fromRight)
+{
+ const size_t thisLen = length();
+ if (thisLen == 0)
+ return *this;
+
+ if (fromRight)
+ {
+ const defaultChar* cursor = data + thisLen - 1;
+ while (cursor != data - 1 && defaultIsWhiteSpace(*cursor)) //break when pointing one char further than last skipped element
+ --cursor;
+ ++cursor;
+
+ const size_t newLength = cursor - data;
+ if (newLength != thisLen)
+ {
+ if (descr->refCount > 1) //allocate new string
+ *this = Zstring(data, newLength);
+ else //overwrite this string
+ descr->length = newLength;
+ }
+ }
+ else
+ {
+ defaultChar* cursor = data;
+ defaultChar ch;
+ while ((ch = *cursor) != 0 && defaultIsWhiteSpace(ch))
+ ++cursor;
+
+ const size_t diff = cursor - data;
+ if (diff)
+ {
+ if (descr->refCount > 1) //allocate new string
+ *this = Zstring(cursor, thisLen - diff);
+ else
+ { //overwrite this string
+ data = cursor; //no problem when deallocating data, since descr points to begin of allocated area
+ descr->capacity -= diff;
+ descr->length -= diff;
+ }
+ }
+ }
+
+ return *this;
+}
+
+
+Zstring& Zstring::MakeLower()
+{
+ const size_t thisLen = length();
+ if (thisLen == 0)
+ return *this;
+
+ if (descr->refCount > 1) //allocate new string
+ {
+ StringDescriptor* newDescr;
+ defaultChar* newData;
+ const size_t newCapacity = getCapacityToAllocate(thisLen);
+ allocate(1, thisLen, newCapacity, newDescr, newData);
+
+ for (unsigned int i = 0; i < thisLen; ++i)
+ newData[i] = defaultToLower(data[i]);
+ newData[thisLen] = 0;
+
+ decRef();
+ descr = newDescr;
+ data = newData;
+ }
+ else
+ { //overwrite this string
+ for (unsigned int i = 0; i < thisLen; ++i)
+ data[i] = defaultToLower(data[i]);
+ }
+
+ return *this;
+}
+
+
+//###############################################################
+//std::string functions
+Zstring Zstring::substr(size_t pos, size_t len) const
+{
+ if (len == npos)
+ {
+ assert(pos <= length());
+ return Zstring(c_str() + pos, length() - pos); //reference counting not used: different length
+ }
+ else
+ {
+ assert(length() - pos >= len);
+ return Zstring(c_str() + pos, len);
+ }
+}
+
+
+size_t Zstring::rfind(const defaultChar ch, size_t pos) const
+{
+ const size_t thisLen = length();
+ if (thisLen == 0)
+ return npos;
+
+ if (pos == npos)
+ pos = thisLen - 1;
+ else
+ assert(pos <= length());
+
+ do //pos points to last char of the string
+ {
+ if (data[pos] == ch)
+ return pos;
+ }
+ while (--pos != static_cast<size_t>(-1));
+ return npos;
+}
+
+
+Zstring& Zstring::replace(size_t pos1, size_t n1, const defaultChar* str, size_t n2)
+{
+ assert(str < c_str() || c_str() + length() < str); //str mustn't point to data in this string
+ assert(n1 <= length() - pos1);
+
+ if (n2 != 0)
+ {
+ const size_t oldLen = length();
+ if (oldLen == 0)
+ {
+ assert(n1 == 0);
+ return *this = Zstring(str, n2);
+ }
+
+ const size_t newLen = oldLen - n1 + n2;
+ if (n1 < n2 || descr->refCount > 1)
+ { //allocate a new string
+ const size_t newCapacity = getCapacityToAllocate(newLen);
+
+ StringDescriptor* newDescr;
+ defaultChar* newData;
+ allocate(1, newLen, newCapacity, newDescr, newData);
+ //StringDescriptor* newDescr = new StringDescriptor(1, newLen, newCapacity);
+ //defaultChar* newData = new defaultChar[newCapacity + 1];
+
+ //assemble new string with replacement
+ memcpy(newData, data, pos1 * sizeof(defaultChar));
+ memcpy(newData + pos1, str, n2 * sizeof(defaultChar));
+ memcpy(newData + pos1 + n2, data + pos1 + n1, (oldLen - pos1 - n1) * sizeof(defaultChar));
+ newData[newLen] = 0;
+
+ decRef();
+ data = newData;
+ descr = newDescr;
+ }
+ else
+ { //overwrite current string
+ memcpy(data + pos1, str, n2 * sizeof(defaultChar));
+ if (n1 > n2)
+ {
+ memmove(data + pos1 + n2, data + pos1 + n1, (oldLen - pos1 - n1) * sizeof(defaultChar));
+ data[newLen] = 0;
+ descr->length = newLen;
+ }
+ }
+ }
+ return *this;
+}
+
+
+Zstring& Zstring::operator=(const defaultChar* source)
+{
+ const size_t sourceLen = defaultLength(source);
+ if (sourceLen == 0)
+ return *this = Zstring();
+
+ if (descr->refCount > 1 || descr->capacity < sourceLen) //allocate new string
+ *this = Zstring(source, sourceLen);
+ else
+ { //overwrite this string
+ memcpy(data, source, sourceLen * sizeof(defaultChar));
+ data[sourceLen] = 0;
+ descr->length = sourceLen;
+ }
+ return *this;
+}
+
+
+Zstring& Zstring::operator+=(const Zstring& other)
+{
+ const size_t otherLen = other.length();
+ if (otherLen != 0)
+ {
+ const size_t thisLen = length();
+ const size_t newLen = thisLen + otherLen;
+ copyBeforeWrite(newLen);
+
+ memcpy(data + thisLen, other.c_str(), otherLen * sizeof(defaultChar));
+ data[newLen] = 0;
+ descr->length = newLen;
+ }
+ return *this;
+}
+
+
+Zstring& Zstring::operator+=(const defaultChar* other)
+{
+ const size_t otherLen = defaultLength(other);
+ if (otherLen != 0)
+ {
+ const size_t thisLen = length();
+ const size_t newLen = thisLen + otherLen;
+ copyBeforeWrite(newLen);
+
+ memcpy(data + thisLen, other, otherLen * sizeof(defaultChar));
+ data[newLen] = 0;
+ descr->length = newLen;
+ }
+ return *this;
+}
+
+
+Zstring& Zstring::operator+=(defaultChar ch)
+{
+ const size_t oldLen = length();
+ copyBeforeWrite(oldLen + 1);
+ data[oldLen] = ch;
+ data[oldLen + 1] = 0;
+ ++descr->length;
+ return *this;
+}
+
+
+void Zstring::copyBeforeWrite(const size_t capacityNeeded)
+{
+ assert(capacityNeeded != 0);
+
+ if (descr->refCount > 1)
+ { //allocate a new string
+ const size_t oldLength = length();
+ const size_t newCapacity = getCapacityToAllocate(capacityNeeded);
+
+ StringDescriptor* newDescr;
+ defaultChar* newData;
+ allocate(1, oldLength, newCapacity, newDescr, newData);
+ //StringDescriptor* newDescr = new StringDescriptor(1, oldLength, newCapacity);
+ //defaultChar* newData = new defaultChar[newCapacity + 1];
+
+ if (oldLength)
+ {
+ assert(oldLength <= newCapacity);
+ memcpy(newData, data, oldLength * sizeof(defaultChar));
+ newData[oldLength] = 0;
+ }
+ decRef();
+ descr = newDescr;
+ data = newData;
+ }
+ else if (descr->capacity < capacityNeeded)
+ { //try to resize the current string (allocate anew if necessary)
+ const size_t newCapacity = getCapacityToAllocate(capacityNeeded);
+
+ descr = (StringDescriptor*) realloc(descr, sizeof(StringDescriptor) + (newCapacity + 1) * sizeof(defaultChar));
+ if (descr == NULL)
+ throw; //std::bad_alloc& e
+ data = (defaultChar*)(descr + 1);
+ descr->capacity = newCapacity;
+ }
+}
diff --git a/library/zstring.h b/library/zstring.h
new file mode 100644
index 00000000..00590d4f
--- /dev/null
+++ b/library/zstring.h
@@ -0,0 +1,580 @@
+/***************************************************************
+ * Purpose: High performance string class
+ * Author: ZenJu (zhnmju123@gmx.de)
+ * Created: Jan. 2009
+ **************************************************************/
+
+#ifndef ZSTRING_H_INCLUDED
+#define ZSTRING_H_INCLUDED
+
+#include <wx/string.h>
+
+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);
+#endif //FFS_WIN
+}
+
+#ifdef FFS_WIN
+#define ZSTRING_WIDE_CHAR //use wide character strings
+
+#elif defined FFS_LINUX
+#define ZSTRING_CHAR //use char strings
+#endif
+
+
+#ifdef ZSTRING_CHAR
+typedef char defaultChar;
+#elif defined ZSTRING_WIDE_CHAR
+typedef wchar_t defaultChar;
+#endif
+
+class Zstring
+{
+public:
+ Zstring();
+ Zstring(const defaultChar* source); //string is copied: O(length)
+ Zstring(const defaultChar* source, size_t length); //string is copied: O(length)
+ Zstring(const Zstring& source); //reference-counting => O(1)
+ Zstring(const wxString& source); //string is copied: O(length)
+ ~Zstring();
+
+ operator const defaultChar*() const; //implicit conversion to C string
+ operator const wxString() const; //implicit conversion to wxString
+
+ //wxWidgets functions
+ bool StartsWith(const defaultChar* begin) const;
+ bool StartsWith(const Zstring& begin) const;
+ bool EndsWith(const defaultChar* end) const;
+ bool EndsWith(const Zstring& end) const;
+#ifdef FFS_WIN
+ int CmpNoCase(const defaultChar* other) const;
+ int CmpNoCase(const Zstring& other) const;
+#endif
+ int Cmp(const defaultChar* other) const;
+ int Cmp(const Zstring& other) const;
+ size_t Replace(const defaultChar* old, const defaultChar* replacement, bool replaceAll = true);
+ Zstring AfterLast(defaultChar ch) const;
+ Zstring BeforeLast(defaultChar ch) const;
+ size_t Find(defaultChar ch, bool fromEnd) const;
+ bool Matches(const defaultChar* mask) const;
+ Zstring& Trim(bool fromRight); //from right or left
+ Zstring& MakeLower();
+
+ //std::string functions
+ size_t length() const;
+ const defaultChar* c_str() const;
+ Zstring substr(size_t pos = 0, size_t len = npos) const;
+ bool empty() const;
+ int compare(const defaultChar* other) const;
+ int compare(const Zstring& other) const;
+ int compare(const size_t pos1, const size_t n1, const defaultChar* other) const;
+ size_t find(const defaultChar* str, const size_t pos = 0 ) const;
+ size_t find(const defaultChar ch, const size_t pos = 0) const;
+ size_t rfind(const defaultChar ch, size_t pos = npos) const;
+ Zstring& replace(size_t pos1, size_t n1, const defaultChar* str, size_t n2);
+
+ Zstring& operator=(const Zstring& source);
+ Zstring& operator=(const defaultChar* source);
+
+ bool operator==(const Zstring& other) const;
+ bool operator==(const defaultChar* other) const;
+
+ Zstring& operator+=(const Zstring& other);
+ Zstring& operator+=(const defaultChar* other);
+ Zstring& operator+=(defaultChar ch);
+
+ Zstring operator+(const Zstring& string2) const;
+ Zstring operator+(const defaultChar* string2) const;
+ Zstring operator+(const defaultChar ch) const;
+
+ static const size_t npos = static_cast<size_t>(-1);
+
+private:
+ void initAndCopy(const defaultChar* source, size_t length);
+ void incRef(); //support for reference-counting
+ void decRef(); //
+ void copyBeforeWrite(const size_t capacityNeeded); //and copy-on-write
+
+ struct StringDescriptor
+ {
+ StringDescriptor(const unsigned int refC, const size_t len, const size_t cap) : refCount(refC), length(len), capacity(cap) {}
+ unsigned int refCount;
+ size_t length;
+ size_t capacity; //allocated length without null-termination
+ };
+ static void allocate(const unsigned int newRefCount, const size_t newLength, const size_t newCapacity, Zstring::StringDescriptor*& newDescr, defaultChar*& newData);
+
+ StringDescriptor* descr;
+ defaultChar* data;
+};
+
+
+//#######################################################################################
+//begin of implementation
+
+#ifdef ZSTRING_CHAR
+inline
+size_t defaultLength(const char* input)
+{
+ return strlen(input);
+}
+
+inline
+int defaultCompare(const char* str1, const char* str2)
+{
+ return strcmp(str1, str2);
+}
+
+inline
+int defaultCompare(const char* str1, const char* str2, const size_t count)
+{
+ return strncmp(str1, str2, count);
+}
+
+inline
+char* defaultStrFind(const char* str1, const char* str2)
+{
+ return strstr(str1, str2);
+}
+
+inline
+char* defaultStrFind(const char* str1, int ch)
+{
+ return strchr(str1, ch);
+}
+
+inline
+bool defaultIsWhiteSpace(const char ch)
+{
+ // some compilers (e.g. VC++ 6.0) return true for a call to isspace('\xEA') => exclude char(128) to char(255)
+ return (ch < 128) && isspace(ch) != 0;
+}
+
+inline
+char defaultToLower(const char ch)
+{
+ return tolower(ch);
+}
+
+#elif defined ZSTRING_WIDE_CHAR
+inline
+size_t defaultLength(const wchar_t* input)
+{
+ return wcslen(input);
+}
+
+inline
+int defaultCompare(const wchar_t* str1, const wchar_t* str2)
+{
+ return wcscmp(str1, str2);
+}
+
+inline
+int defaultCompare(const wchar_t* str1, const wchar_t* str2, const size_t count)
+{
+ return wcsncmp(str1, str2, count);
+}
+
+inline
+wchar_t* defaultStrFind(const wchar_t* str1, const wchar_t* str2)
+{
+ return wcsstr(str1, str2);
+}
+
+inline
+wchar_t* defaultStrFind(const wchar_t* str1, int ch)
+{
+ return wcschr(str1, ch);
+}
+
+inline
+bool defaultIsWhiteSpace(const wchar_t ch)
+{
+ // some compilers (e.g. VC++ 6.0) return true for a call to isspace('\xEA') => exclude char(128) to char(255)
+ return (ch < 128 || ch > 255) && iswspace(ch) != 0;
+}
+
+inline
+wchar_t defaultToLower(const wchar_t ch)
+{
+ return towlower(ch);
+}
+#endif
+
+
+#ifdef __WXDEBUG__
+extern int allocCount; //test Zstring for memory leaks
+void testZstringForMemoryLeak();
+#endif
+
+
+inline
+void Zstring::allocate(const unsigned int newRefCount,
+ const size_t newLength,
+ const size_t newCapacity,
+ StringDescriptor*& newDescr,
+ defaultChar*& newData)
+{ //allocate and set data for new string
+ if (newCapacity)
+ {
+ newDescr = (StringDescriptor*) malloc( sizeof(StringDescriptor) + (newCapacity + 1) * sizeof(defaultChar));
+ if (newDescr == NULL)
+ throw; //std::bad_alloc& e
+ newData = (defaultChar*)(newDescr + 1);
+ }
+ else
+ {
+ newDescr = (StringDescriptor*) malloc( sizeof(StringDescriptor));
+ if (newDescr == NULL)
+ throw; //std::bad_alloc& e
+ newData = NULL;
+ }
+
+ newDescr->refCount = newRefCount;
+ newDescr->length = newLength;
+ newDescr->capacity = newCapacity;
+
+#ifdef __WXDEBUG__
+ ++allocCount; //test Zstring for memory leaks
+
+ static bool isRegistered = false;
+ if (!isRegistered)
+ {
+ isRegistered = true;
+ atexit(testZstringForMemoryLeak);
+ }
+#endif
+}
+
+
+inline
+Zstring::Zstring()
+{
+ allocate(1, 0, 0, descr, data);
+}
+
+
+inline
+Zstring::Zstring(const defaultChar* source)
+{
+ initAndCopy(source, defaultLength(source));
+}
+
+
+inline
+Zstring::Zstring(const defaultChar* source, size_t length)
+{
+ initAndCopy(source, length);
+}
+
+
+inline
+Zstring::Zstring(const Zstring& source)
+{
+ descr = source.descr;
+ data = source.data;
+ incRef(); //reference counting!
+}
+
+
+inline
+Zstring::Zstring(const wxString& source)
+{
+ initAndCopy(source.c_str(), source.length());
+}
+
+
+inline
+Zstring::~Zstring()
+{
+ decRef();
+}
+
+
+inline
+size_t getCapacityToAllocate(const size_t length)
+{
+ return (length + (19 - length % 16)); //allocate some additional length to speed up concatenation
+}
+
+
+inline
+void Zstring::initAndCopy(const defaultChar* source, size_t length)
+{
+ const size_t newCapacity = getCapacityToAllocate(length);
+ allocate(1, length, newCapacity, descr, data);
+ memcpy(data, source, length * sizeof(defaultChar));
+ data[length] = 0;
+}
+
+
+inline
+void Zstring::incRef()
+{
+ assert(descr);
+ ++descr->refCount;
+}
+
+
+inline
+void Zstring::decRef()
+{
+ assert(descr && descr->refCount >= 1);
+ 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
+#ifdef __WXDEBUG__
+ --allocCount; //test Zstring for memory leaks
+#endif
+ }
+}
+
+
+#ifdef FFS_WIN
+inline
+int Zstring::CmpNoCase(const defaultChar* other) const
+{
+ return FreeFileSync::compareStringsWin32(c_str(), other); //way faster than wxString::CmpNoCase()!!
+}
+
+
+inline
+int Zstring::CmpNoCase(const Zstring& other) const
+{
+ return FreeFileSync::compareStringsWin32(c_str(), other.c_str()); //way faster than wxString::CmpNoCase()!!
+}
+#endif
+
+
+inline
+Zstring::operator const defaultChar*() const
+{
+ return c_str();
+}
+
+
+inline
+Zstring& Zstring::operator=(const Zstring& source)
+{
+ if (this != &source)
+ {
+ decRef();
+ descr = source.descr;
+ data = source.data;
+ incRef();
+ }
+ return *this;
+}
+
+
+inline
+size_t Zstring::Find(defaultChar ch, bool fromEnd) const
+{
+ if (fromEnd)
+ return rfind(ch, npos);
+ else
+ return find(ch, 0);
+}
+
+
+// get all characters after the last occurence of ch
+// (returns the whole string if ch not found)
+inline
+Zstring Zstring::AfterLast(defaultChar ch) const
+{
+ size_t pos = rfind(ch, npos);
+ if (pos == npos )
+ return *this;
+ else
+ return c_str() + pos + 1;
+}
+
+
+// get all characters before the last occurence of ch
+// (returns empty string if ch not found)
+inline
+Zstring Zstring::BeforeLast(defaultChar ch) const
+{
+ size_t pos = rfind(ch, npos);
+
+ if (pos != npos && pos != 0 )
+ return Zstring(data, pos); //data is non-empty string in this context: else ch would not have been found!
+ else
+ return Zstring();
+}
+
+
+inline
+bool Zstring::StartsWith(const defaultChar* begin) const
+{
+ const size_t beginLength = defaultLength(begin);
+ if (length() < beginLength)
+ return false;
+ return compare(0, beginLength, begin) == 0;
+}
+
+
+inline
+bool Zstring::StartsWith(const Zstring& begin) const
+{
+ const size_t beginLength = begin.length();
+ if (length() < beginLength)
+ return false;
+ return compare(0, beginLength, begin) == 0;
+}
+
+
+inline
+bool Zstring::EndsWith(const defaultChar* end) const
+{
+ const size_t thisLength = length();
+ const size_t endLength = defaultLength(end);
+ if (thisLength < endLength)
+ return false;
+ return compare(thisLength - endLength, endLength, end) == 0;
+}
+
+
+inline
+bool Zstring::EndsWith(const Zstring& end) const
+{
+ const size_t thisLength = length();
+ const size_t endLength = end.length();
+ if (thisLength < endLength)
+ return false;
+ return compare(thisLength - endLength, endLength, end) == 0;
+}
+
+
+inline
+size_t Zstring::find(const defaultChar* str, const size_t pos) const
+{
+ assert(pos <= length());
+ const defaultChar* thisStr = c_str();
+ const defaultChar* found = defaultStrFind(thisStr + pos, str);
+ return found == NULL ? npos : found - thisStr;
+}
+
+
+inline
+size_t Zstring::find(const defaultChar ch, const size_t pos) const
+{
+ assert(pos <= length());
+ const defaultChar* thisStr = c_str();
+ const defaultChar* found = defaultStrFind(thisStr + pos, ch);
+ return found == NULL ? npos : found - thisStr;
+}
+
+
+inline
+Zstring::operator const wxString() const
+{
+ return wxString(c_str());
+}
+
+
+inline
+int Zstring::Cmp(const defaultChar* other) const
+{
+ return compare(other);
+}
+
+
+inline
+int Zstring::Cmp(const Zstring& other) const
+{
+ return defaultCompare(c_str(), other.c_str()); //overload using strcmp(char*, char*) should be fastest!
+}
+
+
+inline
+bool Zstring::operator==(const Zstring& other) const
+{
+ return defaultCompare(c_str(), other.c_str()) == 0; //overload using strcmp(char*, char*) should be fastest!
+}
+
+
+inline
+bool Zstring::operator==(const defaultChar* other) const
+{
+ return compare(other) == 0;
+}
+
+
+inline
+int Zstring::compare(const Zstring& other) const
+{
+ return defaultCompare(c_str(), other.c_str()); //overload using strcmp(char*, char*) should be fastest!
+}
+
+
+inline
+int Zstring::compare(const defaultChar* other) const
+{
+ return defaultCompare(c_str(), other); //overload using strcmp(char*, char*) should be fastest!
+}
+
+
+inline
+int Zstring::compare(const size_t pos1, const size_t n1, const defaultChar* other) const
+{
+ assert(length() - pos1 >= n1);
+ return defaultCompare(c_str() + pos1, other, n1);
+}
+
+
+inline
+size_t Zstring::length() const
+{
+ return descr->length;
+}
+
+
+inline
+const defaultChar* Zstring::c_str() const
+{
+ if (length())
+ return data;
+ else
+#ifdef ZSTRING_CHAR
+ return "";
+#elif defined ZSTRING_WIDE_CHAR
+ return L"";
+#endif
+}
+
+
+inline
+bool Zstring::empty() const
+{
+ return length() == 0;
+}
+
+
+inline
+Zstring Zstring::operator+(const Zstring& string2) const
+{
+ return Zstring(*this)+=string2;
+}
+
+
+inline
+Zstring Zstring::operator+(const defaultChar* string2) const
+{
+ return Zstring(*this)+=string2;
+}
+
+
+inline
+Zstring Zstring::operator+(const defaultChar ch) const
+{
+ return Zstring(*this)+=ch;
+}
+
+
+#endif // ZSTRING_H_INCLUDED
bgstack15