summaryrefslogtreecommitdiff
path: root/library
diff options
context:
space:
mode:
Diffstat (limited to 'library')
-rw-r--r--library/CustomGrid.cpp459
-rw-r--r--library/CustomGrid.h4
-rw-r--r--library/ShadowCopy/shadow.cpp11
-rw-r--r--library/customButton.cpp300
-rw-r--r--library/customButton.h42
-rw-r--r--library/fileError.h25
-rw-r--r--library/fileHandling.cpp1075
-rw-r--r--library/fileHandling.h71
-rw-r--r--library/filter.cpp369
-rw-r--r--library/filter.h19
-rw-r--r--library/globalFunctions.cpp180
-rw-r--r--library/globalFunctions.h162
-rw-r--r--library/iconBuffer.cpp10
-rw-r--r--library/localization.cpp275
-rw-r--r--library/localization.h56
-rw-r--r--library/multithreading.cpp87
-rw-r--r--library/pch.h4
-rw-r--r--library/processXml.cpp417
-rw-r--r--library/processXml.h53
-rw-r--r--library/resources.cpp13
-rw-r--r--library/resources.h9
-rw-r--r--library/shadow.cpp148
-rw-r--r--library/shadow.h31
-rw-r--r--library/statistics.cpp6
-rw-r--r--library/statistics.h4
-rw-r--r--library/zstring.cpp390
-rw-r--r--library/zstring.h712
27 files changed, 962 insertions, 3970 deletions
diff --git a/library/CustomGrid.cpp b/library/CustomGrid.cpp
index 4cb82961..e2384526 100644
--- a/library/CustomGrid.cpp
+++ b/library/CustomGrid.cpp
@@ -1,5 +1,5 @@
#include "customGrid.h"
-#include "../shared/globalFunctions.h"
+#include "../shared/systemConstants.h"
#include "resources.h"
#include <wx/dc.h>
#include "../algorithm.h"
@@ -73,7 +73,7 @@ public:
virtual int GetNumberRows()
{
if (gridDataView)
- return std::max(gridDataView->elementsOnView(), MIN_ROW_COUNT);
+ return std::max(gridDataView->rowsOnView(), MIN_ROW_COUNT);
else
return 0; //grid is initialized with zero number of rows
}
@@ -180,12 +180,11 @@ public:
}
- const FileCompareLine* getRawData(const unsigned int row) const
+ const FileSystemObject* getRawData(const unsigned int row) const
{
- if (gridDataView && row < gridDataView->elementsOnView())
- {
- return &(*gridDataView)[row];
- }
+ if (gridDataView)
+ return gridDataView->getObject(row); //returns NULL if request is not valid or not data found
+
return NULL;
}
@@ -212,6 +211,8 @@ private:
class CustomGridTableRim : public CustomGridTable
{
public:
+ virtual ~CustomGridTableRim() {}
+
virtual int GetNumberCols()
{
return columnPositions.size();
@@ -240,53 +241,55 @@ public:
virtual Zstring getFileName(const unsigned int row) const = 0;
-private:
- std::vector<xmlAccess::ColumnTypes> columnPositions;
-};
-
-
-class CustomGridTableLeft : public CustomGridTableRim
-{
-public:
- virtual wxString GetValue(int row, int col)
+protected:
+ template <SelectedSide side>
+ wxString GetValueSub(int row, int col)
{
- const FileCompareLine* gridLine = getRawData(row);
- if (gridLine)
+ const FileSystemObject* fsObj = getRawData(row);
+ if (fsObj)
{
- if (gridLine->fileDescrLeft.objType == FileDescrLine::TYPE_DIRECTORY)
+ if (!fsObj->isEmpty<side>())
{
- switch (getTypeAtPos(col))
+ const DirMapping* dirObj = dynamic_cast<const DirMapping*> (fsObj);
+ if (dirObj != NULL)
{
- case xmlAccess::FULL_PATH:
- return wxString(gridLine->fileDescrLeft.fullName.c_str());
- case xmlAccess::FILENAME:
- return wxEmptyString;
- case xmlAccess::REL_PATH:
- return gridLine->fileDescrLeft.relativeName.c_str();
- case xmlAccess::DIRECTORY:
- return gridDataView->getFolderPair(row).leftDirectory.c_str();
- case xmlAccess::SIZE: //file size
- return _("<Directory>");
- case xmlAccess::DATE: //date
- return wxEmptyString;
+ switch (getTypeAtPos(col))
+ {
+ case xmlAccess::FULL_PATH:
+ return dirObj->getFullName<side>().c_str();
+ case xmlAccess::FILENAME:
+ return wxEmptyString;
+ case xmlAccess::REL_PATH:
+ return dirObj->getRelativeName<side>().c_str();
+ case xmlAccess::DIRECTORY:
+ return dirObj->getBaseDirPf<side>().c_str();
+ case xmlAccess::SIZE: //file size
+ return _("<Directory>");
+ case xmlAccess::DATE: //date
+ return wxEmptyString;
+ }
}
- }
- else if (gridLine->fileDescrLeft.objType == FileDescrLine::TYPE_FILE)
- {
- switch (getTypeAtPos(col))
+ else
{
- case xmlAccess::FULL_PATH:
- return wxString(gridLine->fileDescrLeft.fullName.c_str()).BeforeLast(globalFunctions::FILE_NAME_SEPARATOR);
- case xmlAccess::FILENAME: //filename
- return wxString(gridLine->fileDescrLeft.relativeName.c_str()).AfterLast(globalFunctions::FILE_NAME_SEPARATOR);
- case xmlAccess::REL_PATH: //relative path
- return wxString(gridLine->fileDescrLeft.relativeName.c_str()).BeforeLast(globalFunctions::FILE_NAME_SEPARATOR);
- case xmlAccess::DIRECTORY:
- return gridDataView->getFolderPair(row).leftDirectory.c_str();
- case xmlAccess::SIZE: //file size
- return FreeFileSync::includeNumberSeparator(gridLine->fileDescrLeft.fileSize.ToString());
- case xmlAccess::DATE: //date
- return FreeFileSync::utcTimeToLocalString(gridLine->fileDescrLeft.lastWriteTimeRaw, gridLine->fileDescrLeft.fullName);
+ const FileMapping* fileObj = dynamic_cast<const FileMapping*>(fsObj);
+ if (fileObj != NULL)
+ {
+ switch (getTypeAtPos(col))
+ {
+ case xmlAccess::FULL_PATH:
+ return fileObj->getFullName<side>().BeforeLast(globalFunctions::FILE_NAME_SEPARATOR).c_str();
+ case xmlAccess::FILENAME: //filename
+ return fileObj->getShortName<side>().c_str();
+ case xmlAccess::REL_PATH: //relative path
+ return fileObj->getParentRelativeName<side>().c_str();
+ case xmlAccess::DIRECTORY:
+ return fileObj->getBaseDirPf<side>().c_str();
+ case xmlAccess::SIZE: //file size
+ return FreeFileSync::includeNumberSeparator(fileObj->getFileSize<side>().ToString());
+ case xmlAccess::DATE: //date
+ return FreeFileSync::utcTimeToLocalString(fileObj->getLastWriteTime<side>(), fileObj->getFullName<side>());
+ }
+ }
}
}
}
@@ -295,112 +298,63 @@ public:
}
- virtual Zstring getFileName(const unsigned int row) const
- {
- const FileCompareLine* gridLine = getRawData(row);
- if (gridLine)
- return Zstring(gridLine->fileDescrLeft.fullName);
- else
- return Zstring();
- }
-
-
private:
virtual const wxColour& getRowColor(int row) //rows that are filtered out are shown in different color
{
- const FileCompareLine* gridLine = getRawData(row);
- if (gridLine)
+ const FileSystemObject* fsObj = getRawData(row);
+ if (fsObj)
{
//mark filtered rows
- if (!gridLine->selectedForSynchronization)
+ if (!fsObj->selectedForSynchronization)
return COLOR_BLUE;
//mark directories
- else if (gridLine->fileDescrLeft.objType == FileDescrLine::TYPE_DIRECTORY)
+ else if (dynamic_cast<const DirMapping*>(fsObj) != NULL)
return COLOR_GREY;
else
return *wxWHITE;
}
return *wxWHITE;
}
+
+ std::vector<xmlAccess::ColumnTypes> columnPositions;
};
-class CustomGridTableRight : public CustomGridTableRim
+class CustomGridTableLeft : public CustomGridTableRim
{
public:
+
virtual wxString GetValue(int row, int col)
{
- const FileCompareLine* gridLine = getRawData(row);
- if (gridLine)
- {
- if (gridLine->fileDescrRight.objType == FileDescrLine::TYPE_DIRECTORY)
- {
- switch (getTypeAtPos(col))
- {
- case xmlAccess::FULL_PATH:
- return wxString(gridLine->fileDescrRight.fullName.c_str());
- case xmlAccess::FILENAME: //filename
- return wxEmptyString;
- case xmlAccess::REL_PATH: //relative path
- return gridLine->fileDescrRight.relativeName.c_str();
- case xmlAccess::DIRECTORY:
- return gridDataView->getFolderPair(row).rightDirectory.c_str();
- case xmlAccess::SIZE: //file size
- return _("<Directory>");
- case xmlAccess::DATE: //date
- return wxEmptyString;
- }
- }
- else if (gridLine->fileDescrRight.objType == FileDescrLine::TYPE_FILE)
- {
- switch (getTypeAtPos(col))
- {
- case xmlAccess::FULL_PATH:
- return wxString(gridLine->fileDescrRight.fullName.c_str()).BeforeLast(globalFunctions::FILE_NAME_SEPARATOR);
- case xmlAccess::FILENAME: //filename
- return wxString(gridLine->fileDescrRight.relativeName.c_str()).AfterLast(globalFunctions::FILE_NAME_SEPARATOR);
- case xmlAccess::REL_PATH: //relative path
- return wxString(gridLine->fileDescrRight.relativeName.c_str()).BeforeLast(globalFunctions::FILE_NAME_SEPARATOR);
- case xmlAccess::DIRECTORY:
- return gridDataView->getFolderPair(row).rightDirectory.c_str();
- case xmlAccess::SIZE: //file size
- return FreeFileSync::includeNumberSeparator(gridLine->fileDescrRight.fileSize.ToString());
- case xmlAccess::DATE: //date
- return FreeFileSync::utcTimeToLocalString(gridLine->fileDescrRight.lastWriteTimeRaw, gridLine->fileDescrRight.fullName);
- }
- }
- }
- //if data is not found:
- return wxEmptyString;
+ return CustomGridTableRim::GetValueSub<LEFT_SIDE>(row, col);
}
-
virtual Zstring getFileName(const unsigned int row) const
{
- const FileCompareLine* gridLine = getRawData(row);
- if (gridLine)
- return Zstring(gridLine->fileDescrRight.fullName);
+ const FileSystemObject* fsObj = getRawData(row);
+ if (fsObj && !fsObj->isEmpty<LEFT_SIDE>())
+ return fsObj->getFullName<LEFT_SIDE>();
else
return Zstring();
}
+};
-private:
- virtual const wxColour& getRowColor(int row) //rows that are filtered out are shown in different color
+class CustomGridTableRight : public CustomGridTableRim
+{
+public:
+ virtual wxString GetValue(int row, int col)
{
- const FileCompareLine* gridLine = getRawData(row);
- if (gridLine)
- {
- //mark filtered rows
- if (!gridLine->selectedForSynchronization)
- return COLOR_BLUE;
- //mark directories
- else if (gridLine->fileDescrRight.objType == FileDescrLine::TYPE_DIRECTORY)
- return COLOR_GREY;
- else
- return *wxWHITE;
- }
- return *wxWHITE;
+ return CustomGridTableRim::GetValueSub<RIGHT_SIDE>(row, col);
+ }
+
+ virtual Zstring getFileName(const unsigned int row) const
+ {
+ const FileSystemObject* fsObj = getRawData(row);
+ if (fsObj && !fsObj->isEmpty<RIGHT_SIDE>())
+ return fsObj->getFullName<RIGHT_SIDE>();
+ else
+ return Zstring();
}
};
@@ -423,8 +377,16 @@ public:
return wxEmptyString;
}
- virtual wxString GetValue(int row, int col)
+ virtual wxString GetValue(int row, int col) //method used for exporting .csv file only!
{
+ const FileSystemObject* fsObj = getRawData(row);
+ if (fsObj)
+ {
+ if (syncPreviewActive) //synchronization preview
+ return getSymbol(getSyncOperation(*fsObj));
+ else
+ return getSymbol(fsObj->getCategory());
+ }
return wxEmptyString;
}
@@ -441,30 +403,31 @@ public:
private:
virtual const wxColour& getRowColor(int row) //rows that are filtered out are shown in different color
{
- const FileCompareLine* gridLine = getRawData(row);
- if (gridLine)
+ const FileSystemObject* fsObj = getRawData(row);
+ if (fsObj)
{
//mark filtered rows
- if (!gridLine->selectedForSynchronization)
+ if (!fsObj->selectedForSynchronization)
return COLOR_BLUE;
if (syncPreviewActive) //synchronization preview
{
- switch (gridLine->syncDir)
+ switch (fsObj->syncDir)
{
case SYNC_DIR_LEFT:
return COLOR_SYNC_BLUE;
case SYNC_DIR_RIGHT:
return COLOR_SYNC_GREEN;
case SYNC_DIR_NONE:
- return *wxWHITE;
- case SYNC_UNRESOLVED_CONFLICT:
- return COLOR_YELLOW;
+ if (fsObj->getCategory() == FILE_CONFLICT)
+ return COLOR_YELLOW;
+ else
+ return *wxWHITE;
}
}
else //comparison results view
{
- switch (gridLine->cmpResult)
+ switch (fsObj->getCategory())
{
case FILE_LEFT_SIDE_ONLY:
case FILE_RIGHT_SIDE_ONLY:
@@ -539,7 +502,8 @@ void CustomGrid::initSettings(CustomGridLeft* gridLeft,
Connect(wxEVT_SCROLLWIN_THUMBRELEASE, wxEventHandler(CustomGrid::onGridAccess), NULL, this);
Connect(wxEVT_GRID_LABEL_LEFT_CLICK, wxEventHandler(CustomGrid::onGridAccess), NULL, this);
GetGridWindow()->Connect(wxEVT_LEFT_DOWN, wxEventHandler(CustomGrid::onGridAccess), NULL, this);
- GetGridWindow()->Connect(wxEVT_RIGHT_DOWN, wxEventHandler(CustomGrid::onGridAccess), NULL, this);
+ GetGridWindow()->Connect(wxEVT_RIGHT_DOWN, wxEventHandler(CustomGrid::onGridAccess), NULL, this);
+
GetGridWindow()->Connect(wxEVT_ENTER_WINDOW, wxEventHandler(CustomGrid::adjustGridHeights), NULL, this);
}
@@ -573,7 +537,7 @@ inline
void moveCursorWhileSelecting(const int anchor, const int oldPos, const int newPos, wxGrid* grid)
{ //note: all positions are valid in this context!
- grid->SetGridCursor(newPos, grid->GetGridCursorCol());
+ grid->SetGridCursor( newPos, grid->GetGridCursorCol());
grid->MakeCellVisible(newPos, grid->GetGridCursorCol());
if (oldPos < newPos)
@@ -595,23 +559,24 @@ void moveCursorWhileSelecting(const int anchor, const int oldPos, const int newP
}
-inline
-void additionalGridCommands(wxEvent& event, wxGrid* grid)
+void execGridCommands(wxEvent& event, wxGrid* grid)
{
static int anchorRow = 0;
- assert(grid->GetNumberRows() != 0);
+ if ( grid->GetNumberRows() == 0 ||
+ grid->GetNumberCols() == 0)
+ return;
- try
+ const wxKeyEvent* keyEvent = dynamic_cast<const wxKeyEvent*> (&event);
+ if (keyEvent)
{
- const wxKeyEvent& keyEvent = dynamic_cast<const wxKeyEvent&> (event);
+ //ensure cursorOldPos is always a valid row!
+ const int cursorOldPos = std::max(std::min(grid->GetGridCursorRow(), grid->GetNumberRows() - 1), 0);
+ const int cursorOldColumn = std::max(std::min(grid->GetGridCursorCol(), grid->GetNumberCols() - 1), 0);
- if (keyEvent.ShiftDown())
+ if (keyEvent->ShiftDown())
{
- //ensure cursorOldPos is always a valid row!
- const int cursorOldPos = std::max(std::min(grid->GetGridCursorRow(), grid->GetNumberRows() - 1), 0);
-
//support for shift + PageUp and shift + PageDown
- switch (keyEvent.GetKeyCode())
+ switch (keyEvent->GetKeyCode())
{
case WXK_UP: //move grid cursor also
{
@@ -627,6 +592,23 @@ void additionalGridCommands(wxEvent& event, wxGrid* grid)
}
return; //no event.Skip()
+ case WXK_LEFT: //move grid cursor also
+ {
+ const int cursorColumn = std::max(cursorOldColumn - 1, 0);
+ grid->SetGridCursor(cursorOldPos, cursorColumn);
+ grid->MakeCellVisible(cursorOldPos, cursorColumn);
+ }
+ return; //no event.Skip()
+
+ case WXK_RIGHT: //move grid cursor also
+ {
+ const int cursorColumn = std::min(cursorOldColumn + 1, grid->GetNumberCols() - 1);
+ grid->SetGridCursor(cursorOldPos, cursorColumn);
+ grid->MakeCellVisible(cursorOldPos, cursorColumn);
+ }
+ return; //no event.Skip()
+
+
case WXK_PAGEUP:
case WXK_NUMPAD_PAGEUP:
{
@@ -665,8 +647,59 @@ void additionalGridCommands(wxEvent& event, wxGrid* grid)
}
else //button without shift is pressed
{
- switch (keyEvent.GetKeyCode())
+ switch (keyEvent->GetKeyCode())
+ {
+ case WXK_UP: //move grid cursor also
{
+ const int cursorNewPos = std::max(cursorOldPos - 1, 0);
+ grid->SetGridCursor(cursorNewPos, grid->GetGridCursorCol());
+ grid->MakeCellVisible(cursorNewPos, grid->GetGridCursorCol());
+ }
+ return; //no event.Skip()
+ case WXK_DOWN: //move grid cursor also
+ {
+ const int cursorNewPos = std::min(cursorOldPos + 1, grid->GetNumberRows() - 1);
+ grid->SetGridCursor(cursorNewPos, grid->GetGridCursorCol());
+ grid->MakeCellVisible(cursorNewPos, grid->GetGridCursorCol());
+ }
+ return; //no event.Skip()
+
+ case WXK_LEFT: //move grid cursor also
+ {
+ const int cursorColumn = std::max(cursorOldColumn - 1, 0);
+ grid->SetGridCursor(cursorOldPos, cursorColumn);
+ grid->MakeCellVisible(cursorOldPos, cursorColumn);
+ }
+ return; //no event.Skip()
+ case WXK_RIGHT: //move grid cursor also
+ {
+ const int cursorColumn = std::min(cursorOldColumn + 1, grid->GetNumberCols() - 1);
+ grid->SetGridCursor(cursorOldPos, cursorColumn);
+ grid->MakeCellVisible(cursorOldPos, cursorColumn);
+ }
+ return; //no event.Skip()
+
+
+ case WXK_PAGEUP:
+ case WXK_NUMPAD_PAGEUP:
+ {
+ const int rowsPerPage = grid->GetGridWindow()->GetSize().GetHeight() / grid->GetDefaultRowSize();
+ const int cursorNewPos = std::max(cursorOldPos - rowsPerPage, 0);
+ grid->SetGridCursor(cursorNewPos, grid->GetGridCursorCol());
+ grid->MakeCellVisible(cursorNewPos, grid->GetGridCursorCol());
+ }
+ return; //no event.Skip()
+
+ case WXK_PAGEDOWN:
+ case WXK_NUMPAD_PAGEDOWN:
+ {
+ const int rowsPerPage = grid->GetGridWindow()->GetSize().GetHeight() / grid->GetDefaultRowSize();
+ const int cursorNewPos = std::min(cursorOldPos + rowsPerPage, grid->GetNumberRows() - 1);
+ grid->SetGridCursor(cursorNewPos, grid->GetGridCursorCol());
+ grid->MakeCellVisible(cursorNewPos, grid->GetGridCursorCol());
+ }
+ return; //no event.Skip()
+
case WXK_HOME:
case WXK_NUMPAD_HOME:
grid->SetGridCursor(0, grid->GetGridCursorCol());
@@ -681,62 +714,58 @@ void additionalGridCommands(wxEvent& event, wxGrid* grid)
}
}
}
- catch (std::bad_cast&) {}
anchorRow = grid->GetGridCursorRow();
- event.Skip();
+ event.Skip(); //let event delegate!
}
inline
bool gridsShouldBeCleared(const wxEvent& event)
{
- try
+ const wxMouseEvent* mouseEvent = dynamic_cast<const wxMouseEvent*>(&event);
+ if (mouseEvent)
{
- const wxMouseEvent& mouseEvent = dynamic_cast<const wxMouseEvent&> (event);
-
- if (mouseEvent.ControlDown() || mouseEvent.ShiftDown())
+ if (mouseEvent->ControlDown() || mouseEvent->ShiftDown())
return false;
- if (mouseEvent.ButtonDown(wxMOUSE_BTN_LEFT))
+ if (mouseEvent->ButtonDown(wxMOUSE_BTN_LEFT))
return true;
return false;
}
- catch (std::bad_cast&) {}
-
- try
+ else
{
- const wxKeyEvent& keyEvent = dynamic_cast<const wxKeyEvent&> (event);
+ const wxKeyEvent* keyEvent = dynamic_cast<const wxKeyEvent*>(&event);
+ if (keyEvent)
+ {
+ if (keyEvent->ControlDown() || keyEvent->ShiftDown())
+ return false;
- if (keyEvent.ControlDown() || keyEvent.ShiftDown())
- return false;
+ switch (keyEvent->GetKeyCode())
+ {
+ case WXK_TAB:
+ case WXK_RETURN:
+ case WXK_ESCAPE:
+ case WXK_NUMPAD_ENTER:
+ case WXK_LEFT:
+ case WXK_UP:
+ case WXK_RIGHT:
+ case WXK_DOWN:
+ case WXK_PAGEUP:
+ case WXK_PAGEDOWN:
+ case WXK_NUMPAD_PAGEUP:
+ case WXK_NUMPAD_PAGEDOWN:
+ case WXK_HOME:
+ case WXK_END:
+ case WXK_NUMPAD_HOME:
+ case WXK_NUMPAD_END:
+ return true;
+ }
- switch (keyEvent.GetKeyCode())
- {
- case WXK_SPACE:
- case WXK_TAB:
- case WXK_RETURN:
- case WXK_ESCAPE:
- case WXK_NUMPAD_ENTER:
- case WXK_LEFT:
- case WXK_UP:
- case WXK_RIGHT:
- case WXK_DOWN:
- case WXK_PAGEUP:
- case WXK_PAGEDOWN:
- case WXK_NUMPAD_PAGEUP:
- case WXK_NUMPAD_PAGEDOWN:
- case WXK_HOME:
- case WXK_END:
- case WXK_NUMPAD_HOME:
- case WXK_NUMPAD_END:
- return true;
+ return false;
}
-
- return false;
}
- catch (std::bad_cast&) {}
return false;
}
@@ -766,8 +795,8 @@ void CustomGrid::onGridAccess(wxEvent& event)
m_gridLeft->GetGridRowLabelWindow()->Update();
m_gridRight->GetGridRowLabelWindow()->Update();
- //support for additional short-cuts
- additionalGridCommands(event, this); //event.Skip is handled here!
+ //support for custom short-cuts (overwriting wxWidgets functionality!)
+ execGridCommands(event, this); //event.Skip is handled here!
}
@@ -831,9 +860,9 @@ void CustomGrid::DrawColLabel(wxDC& dc, int col)
}
-std::set<int> CustomGrid::getAllSelectedRows() const
+std::set<unsigned int> CustomGrid::getAllSelectedRows() const
{
- std::set<int> output;
+ std::set<unsigned int> output;
const wxArrayInt selectedRows = this->GetSelectedRows();
if (!selectedRows.IsEmpty())
@@ -1526,8 +1555,8 @@ void CustomGridMiddle::OnLeaveWindow(wxMouseEvent& event)
void CustomGridMiddle::showToolTip(int rowNumber, wxPoint pos)
{
- const FileCompareLine* const rowData = gridDataTable->getRawData(rowNumber);
- if (rowData == NULL) //if invalid row...
+ const FileSystemObject* const fsObj = gridDataTable->getRawData(rowNumber);
+ if (fsObj == NULL) //if invalid row...
{
toolTip->hide();
return;
@@ -1535,58 +1564,60 @@ void CustomGridMiddle::showToolTip(int rowNumber, wxPoint pos)
if (gridDataTable->syncPreviewIsActive()) //synchronization preview
{
- switch (getSyncOperation(*rowData))
+ const SyncOperation syncOp = getSyncOperation(*fsObj);
+ switch (syncOp)
{
case SO_CREATE_NEW_LEFT:
- toolTip->show(_("Copy from right to left"), pos, GlobalResources::getInstance().bitmapSyncCreateLeftAct);
+ toolTip->show(getDescription(syncOp), pos, GlobalResources::getInstance().bitmapSyncCreateLeftAct);
break;
case SO_CREATE_NEW_RIGHT:
- toolTip->show(_("Copy from left to right"), pos, GlobalResources::getInstance().bitmapSyncCreateRightAct);
+ toolTip->show(getDescription(syncOp), pos, GlobalResources::getInstance().bitmapSyncCreateRightAct);
break;
case SO_DELETE_LEFT:
- toolTip->show(_("Delete files/folders existing on left side only"), pos, GlobalResources::getInstance().bitmapSyncDeleteLeftAct);
+ toolTip->show(getDescription(syncOp), pos, GlobalResources::getInstance().bitmapSyncDeleteLeftAct);
break;
case SO_DELETE_RIGHT:
- toolTip->show(_("Delete files/folders existing on right side only"), pos, GlobalResources::getInstance().bitmapSyncDeleteRightAct);
+ toolTip->show(getDescription(syncOp), pos, GlobalResources::getInstance().bitmapSyncDeleteRightAct);
break;
case SO_OVERWRITE_LEFT:
- toolTip->show(_("Copy from right to left overwriting"), pos, GlobalResources::getInstance().bitmapSyncDirLeftAct);
+ toolTip->show(getDescription(syncOp), pos, GlobalResources::getInstance().bitmapSyncDirLeftAct);
break;
case SO_OVERWRITE_RIGHT:
- toolTip->show(_("Copy from left to right overwriting"), pos, GlobalResources::getInstance().bitmapSyncDirRightAct);
+ toolTip->show(getDescription(syncOp), pos, GlobalResources::getInstance().bitmapSyncDirRightAct);
break;
case SO_DO_NOTHING:
- toolTip->show(_("Do nothing"), pos, GlobalResources::getInstance().bitmapSyncDirNoneAct);
+ toolTip->show(getDescription(syncOp), pos, GlobalResources::getInstance().bitmapSyncDirNoneAct);
break;
case SO_UNRESOLVED_CONFLICT:
- toolTip->show(rowData->conflictDescription.get(), pos, GlobalResources::getInstance().bitmapConflictAct);
+ toolTip->show(fsObj->getConflictDescription(), pos, GlobalResources::getInstance().bitmapConflictAct);
break;
};
}
else
{
- switch (rowData->cmpResult)
+ const CompareFilesResult cmpRes = fsObj->getCategory();
+ switch (cmpRes)
{
case FILE_LEFT_SIDE_ONLY:
- toolTip->show(_("Files/folders that exist on left side only"), pos, GlobalResources::getInstance().bitmapLeftOnlyAct);
+ toolTip->show(getDescription(cmpRes), pos, GlobalResources::getInstance().bitmapLeftOnlyAct);
break;
case FILE_RIGHT_SIDE_ONLY:
- toolTip->show(_("Files/folders that exist on right side only"), pos, GlobalResources::getInstance().bitmapRightOnlyAct);
+ toolTip->show(getDescription(cmpRes), pos, GlobalResources::getInstance().bitmapRightOnlyAct);
break;
case FILE_LEFT_NEWER:
- toolTip->show(_("Files that exist on both sides, left one is newer"), pos, GlobalResources::getInstance().bitmapLeftNewerAct);
+ toolTip->show(getDescription(cmpRes), pos, GlobalResources::getInstance().bitmapLeftNewerAct);
break;
case FILE_RIGHT_NEWER:
- toolTip->show(_("Files that exist on both sides, right one is newer"), pos, GlobalResources::getInstance().bitmapRightNewerAct);
+ toolTip->show(getDescription(cmpRes), pos, GlobalResources::getInstance().bitmapRightNewerAct);
break;
case FILE_DIFFERENT:
- toolTip->show(_("Files that exist on both sides and have different content"), pos, GlobalResources::getInstance().bitmapDifferentAct);
+ toolTip->show(getDescription(cmpRes), pos, GlobalResources::getInstance().bitmapDifferentAct);
break;
case FILE_EQUAL:
- toolTip->show(_(""), pos, GlobalResources::getInstance().bitmapEqualAct);
+ toolTip->show(getDescription(cmpRes), pos, GlobalResources::getInstance().bitmapEqualAct);
break;
case FILE_CONFLICT:
- toolTip->show(rowData->conflictDescription.get(), pos, GlobalResources::getInstance().bitmapConflictAct);
+ toolTip->show(fsObj->getConflictDescription(), pos, GlobalResources::getInstance().bitmapConflictAct);
break;
}
}
@@ -1711,8 +1742,8 @@ void GridCellRendererMiddle::Draw(wxGrid& grid,
bool isSelected)
{
//retrieve grid data
- const FileCompareLine* const rowData = m_gridMiddle->gridDataTable->getRawData(row);
- if (rowData != NULL) //if valid row...
+ const FileSystemObject* const fsObj = m_gridMiddle->gridDataTable->getRawData(row);
+ if (fsObj != NULL) //if valid row...
{
if (rect.GetWidth() > CHECK_BOX_WIDTH)
{
@@ -1730,13 +1761,13 @@ void GridCellRendererMiddle::Draw(wxGrid& grid,
//HIGHLIGHTNING:
if (rowIsHighlighted && m_gridMiddle->highlightedPos == CustomGridMiddle::BLOCKPOS_CHECK_BOX)
{
- if (rowData->selectedForSynchronization)
+ if (fsObj->selectedForSynchronization)
dc.DrawLabel(wxEmptyString, *GlobalResources::getInstance().bitmapCheckBoxTrueFocus, rectShrinked, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL);
else
dc.DrawLabel(wxEmptyString, *GlobalResources::getInstance().bitmapCheckBoxFalseFocus, rectShrinked, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL);
}
//default
- else if (rowData->selectedForSynchronization)
+ else if (fsObj->selectedForSynchronization)
dc.DrawLabel(wxEmptyString, *GlobalResources::getInstance().bitmapCheckBoxTrue, rectShrinked, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL);
else
dc.DrawLabel(wxEmptyString, *GlobalResources::getInstance().bitmapCheckBoxFalse, rectShrinked, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL);
@@ -1758,24 +1789,24 @@ void GridCellRendererMiddle::Draw(wxGrid& grid,
case CustomGridMiddle::BLOCKPOS_CHECK_BOX:
break;
case CustomGridMiddle::BLOCKPOS_LEFT:
- dc.DrawLabel(wxEmptyString, getSyncOpImage(rowData->cmpResult, true, SYNC_DIR_LEFT), rectShrinked, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL);
+ dc.DrawLabel(wxEmptyString, getSyncOpImage(fsObj->getCategory(), true, SYNC_DIR_LEFT), rectShrinked, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL);
break;
case CustomGridMiddle::BLOCKPOS_MIDDLE:
- dc.DrawLabel(wxEmptyString, getSyncOpImage(rowData->cmpResult, true, SYNC_DIR_NONE), rectShrinked, wxALIGN_CENTER | wxALIGN_CENTER_VERTICAL);
+ dc.DrawLabel(wxEmptyString, getSyncOpImage(fsObj->getCategory(), true, SYNC_DIR_NONE), rectShrinked, wxALIGN_CENTER | wxALIGN_CENTER_VERTICAL);
break;
case CustomGridMiddle::BLOCKPOS_RIGHT:
- dc.DrawLabel(wxEmptyString, getSyncOpImage(rowData->cmpResult, true, SYNC_DIR_RIGHT), rectShrinked, wxALIGN_RIGHT | wxALIGN_CENTER_VERTICAL);
+ dc.DrawLabel(wxEmptyString, getSyncOpImage(fsObj->getCategory(), true, SYNC_DIR_RIGHT), rectShrinked, wxALIGN_RIGHT | wxALIGN_CENTER_VERTICAL);
break;
}
else //default
{
- const wxBitmap& syncOpIcon = getSyncOpImage(rowData->cmpResult, rowData->selectedForSynchronization, rowData->syncDir);
+ const wxBitmap& syncOpIcon = getSyncOpImage(fsObj->getCategory(), fsObj->selectedForSynchronization, fsObj->syncDir);
dc.DrawLabel(wxEmptyString, syncOpIcon, rectShrinked, wxALIGN_CENTER | wxALIGN_CENTER_VERTICAL);
}
}
else //comparison results view
{
- switch (rowData->cmpResult)
+ switch (fsObj->getCategory())
{
case FILE_LEFT_SIDE_ONLY:
dc.DrawLabel(wxEmptyString, *GlobalResources::getInstance().bitmapLeftOnlySmall, rectShrinked, wxALIGN_CENTER | wxALIGN_CENTER_VERTICAL);
diff --git a/library/CustomGrid.h b/library/CustomGrid.h
index a71b985a..08ec5f55 100644
--- a/library/CustomGrid.h
+++ b/library/CustomGrid.h
@@ -6,6 +6,7 @@
#include "processXml.h"
#include <map>
#include <memory>
+#include <set>
class CustomGridTableRim;
class CustomGridTableLeft;
@@ -19,6 +20,7 @@ class CustomGridLeft;
class CustomGridMiddle;
class CustomGridRight;
+
namespace FreeFileSync
{
class GridView;
@@ -59,7 +61,7 @@ public:
CustomGridRight* gridRight,
const FreeFileSync::GridView* gridDataView);
- std::set<int> getAllSelectedRows() const;
+ std::set<unsigned int> getAllSelectedRows() const;
//set sort direction indicator on UI
typedef int SortColumn;
diff --git a/library/ShadowCopy/shadow.cpp b/library/ShadowCopy/shadow.cpp
index e10a4eb8..edc62e8b 100644
--- a/library/ShadowCopy/shadow.cpp
+++ b/library/ShadowCopy/shadow.cpp
@@ -82,6 +82,9 @@ bool shadow::createShadowCopy(const wchar_t* volumeName,
//wait for shadow copy writers to complete
hr = pWriteMetaData->Wait();
+ if (SUCCEEDED(hr))
+ pWriteMetaData->QueryStatus(&hr, NULL); //check if the async operation succeeded...
+
pWriteMetaData->Release();
if (FAILED(hr))
{
@@ -126,6 +129,9 @@ bool shadow::createShadowCopy(const wchar_t* volumeName,
}
hr = pPrepare->Wait();
+ if (SUCCEEDED(hr))
+ pPrepare->QueryStatus(&hr, NULL); //check if the async operation succeeded...
+
pPrepare->Release();
if (FAILED(hr))
{
@@ -144,7 +150,10 @@ bool shadow::createShadowCopy(const wchar_t* volumeName,
}
hr = pDoShadowCopy->Wait();
- pDoShadowCopy->Release();
+ if (SUCCEEDED(hr))
+ pDoShadowCopy->QueryStatus(&hr, NULL); //check if the async operation succeeded...
+
+ pDoShadowCopy->Release();
if (FAILED(hr))
{
releaseShadowCopy(pBackupComponents);
diff --git a/library/customButton.cpp b/library/customButton.cpp
deleted file mode 100644
index fc686d3f..00000000
--- a/library/customButton.cpp
+++ /dev/null
@@ -1,300 +0,0 @@
-#include "customButton.h"
-#include <wx/dcmemory.h>
-#include <wx/image.h>
-
-wxButtonWithImage::wxButtonWithImage(wxWindow *parent,
- wxWindowID id,
- const wxString& label,
- const wxPoint& pos,
- const wxSize& size,
- long style,
- const wxValidator& validator,
- const wxString& 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, unsigned spaceAfter)
-{
- bitmapFront = bitmap;
- m_spaceAfter = spaceAfter;
- refreshButtonLabel();
-}
-
-
-void wxButtonWithImage::setTextLabel(const wxString& text)
-{
- textLabel = text;
- wxBitmapButton::SetLabel(text);
- refreshButtonLabel();
-}
-
-
-void wxButtonWithImage::setBitmapBack(const wxBitmap& bitmap, unsigned spaceBefore)
-{
- bitmapBack = bitmap;
- m_spaceBefore = spaceBefore;
- refreshButtonLabel();
-}
-
-
-void makeWhiteTransparent(const wxColor exceptColor, wxImage& image)
-{
- unsigned char* alphaData = image.GetAlpha();
- if (alphaData)
- {
- assert(exceptColor.Red() != 255);
-
- unsigned char exCol = exceptColor.Red(); //alpha value can be extracted from any one of (red/green/blue)
- unsigned char* imageStart = image.GetData();
-
- unsigned char* j = alphaData;
- const unsigned char* const rowEnd = j + image.GetWidth() * image.GetHeight();
- while (j != rowEnd)
- {
- const unsigned char* imagePixel = imageStart + (j - alphaData) * 3; //each pixel consists of three chars
- //exceptColor(red,green,blue) becomes fully opaque(255), while white(255,255,255) becomes transparent(0)
- *(j++) = ((255 - imagePixel[0]) * wxIMAGE_ALPHA_OPAQUE) / (255 - exCol);
- }
- }
-}
-
-
-wxSize getSizeNeeded(const wxString& text, wxFont& font)
-{
- wxCoord width, height;
- wxMemoryDC dc;
-
- wxString textFormatted = text;
- textFormatted.Replace(wxT("&"), wxT(""), false); //remove accelerator
- dc.GetMultiLineTextExtent(textFormatted, &width, &height , NULL, &font);
- return wxSize(width, height);
-}
-
-/*
-inline
-void linearInterpolationHelper(const int shift, wxImage& img)
-{
- unsigned char* const data = img.GetData();
- const int width = img.GetWidth();
- if (width < 2)
- return;
-
- const float intensity = 0.25;
-
- for (int y = 1; y < img.GetHeight() - 1; ++y)
- {
- float back = 0;
- float middle = 0;
- float front = 0;
-
- unsigned char* location = data + 3 * (y * width) + shift;
- const unsigned char* const endPos = location + 3 * width;
-
- middle = (*location + *(location - 3 * width) + *(location + 3 * width)) / 3;;
- front = (*(location + 3) + *(location + 3 * (1 - width)) + *(location + 3 * (1 + width))) / 3;
- *location += ((middle + front) / 2 - *location) * intensity;
- location += 3;
-
- while (location < endPos - 3)
- {
- back = middle;
- middle = front;
- front = (*(location + 3) + *(location + 3 * (1 - width)) + *(location + 3 * (1 + width))) / 3;
- *location += ((back + middle + front) / 3 - *location) * intensity;
- location += 3;
- }
-
- back = middle;
- middle = front;
- *location += ((back + middle) / 2 - *location) * intensity;
- }
-}
-
-
-void linearInterpolation(wxImage& img)
-{
- linearInterpolationHelper(0, img); //red channel
- linearInterpolationHelper(1, img); //green channel
- linearInterpolationHelper(2, img); //blue channel
-}
-*/
-
-wxBitmap wxButtonWithImage::createBitmapFromText(const wxString& text)
-{
- if (text.empty())
- return wxBitmap();
-
- wxFont currentFont = wxBitmapButton::GetFont();
- wxColor textColor = wxBitmapButton::GetForegroundColour();
-
- wxSize sizeNeeded = getSizeNeeded(text, currentFont);
- wxBitmap newBitmap(sizeNeeded.GetWidth(), sizeNeeded.GetHeight());
-
- wxMemoryDC dc;
- dc.SelectObject(newBitmap);
-
- //set up white background
- dc.SetBackground(*wxWHITE_BRUSH);
- dc.Clear();
-
- //find position of accelerator
- int indexAccel = -1;
- size_t accelPos;
- wxString textLabelFormatted = text;
- if ((accelPos = text.find(wxT("&"))) != wxString::npos)
- {
- textLabelFormatted.Replace(wxT("&"), wxT(""), false); //remove accelerator
- indexAccel = accelPos;
- }
-
- dc.SetTextForeground(textColor);
- dc.SetTextBackground(*wxWHITE);
- dc.SetFont(currentFont);
- dc.DrawLabel(textLabelFormatted, wxNullBitmap, wxRect(0, 0, newBitmap.GetWidth(), newBitmap.GetHeight()), wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL, indexAccel);
-
- dc.SelectObject(wxNullBitmap);
-
- //add alpha channel to image
- wxImage finalImage(newBitmap.ConvertToImage());
- finalImage.SetAlpha();
-
- //linearInterpolation(finalImage);
-
- //make white background transparent
- makeWhiteTransparent(textColor, finalImage);
-
- return wxBitmap(finalImage);
-}
-
-
-//copy one image into another, allowing arbitrary overlapping! (pos may contain negative numbers)
-void writeToImage(const wxImage& source, const wxPoint pos, wxImage& target)
-{
- //determine startpositions in source and target image, as well as width and height to be copied
- wxPoint posSrc, posTrg;
- int width, height;
-
- //X-axis
- if (pos.x < 0)
- {
- posSrc.x = -pos.x;
- posTrg.x = 0;
- width = std::min(pos.x + source.GetWidth(), target.GetWidth());
- }
- else
- {
- posSrc.x = 0;
- posTrg.x = pos.x;
- width = std::min(target.GetWidth() - pos.x, source.GetWidth());
- }
-
- //Y-axis
- if (pos.y < 0)
- {
- posSrc.y = -pos.y;
- posTrg.y = 0;
- height = std::min(pos.y + source.GetHeight(), target.GetHeight());
- }
- else
- {
- posSrc.y = 0;
- posTrg.y = pos.y;
- height = std::min(target.GetHeight() - pos.y, source.GetHeight());
- }
-
-
- if (width > 0 && height > 0)
- {
- //copy source to target respecting overlapping parts
- const unsigned char* sourcePtr = source.GetData() + 3 * (posSrc.x + posSrc.y * source.GetWidth());
- const unsigned char* const sourcePtrEnd = source.GetData() + 3 * (posSrc.x + (posSrc.y + height) * source.GetWidth());
- unsigned char* targetPtr = target.GetData() + 3 * (posTrg.x + posTrg.y * target.GetWidth());
-
- while (sourcePtr < sourcePtrEnd)
- {
- memcpy(targetPtr, sourcePtr, 3 * width);
- sourcePtr += 3 * source.GetWidth();
- targetPtr += 3 * target.GetWidth();
- }
-
- //handle different cases concerning alpha channel
- if (source.HasAlpha())
- {
- if (!target.HasAlpha())
- {
- target.SetAlpha();
- unsigned char* alpha = target.GetAlpha();
- memset(alpha, wxIMAGE_ALPHA_OPAQUE, target.GetWidth() * target.GetHeight());
- }
-
- //copy alpha channel
- const unsigned char* sourcePtr = source.GetAlpha() + (posSrc.x + posSrc.y * source.GetWidth());
- const unsigned char* const sourcePtrEnd = source.GetAlpha() + (posSrc.x + (posSrc.y + height) * source.GetWidth());
- unsigned char* targetPtr = target.GetAlpha() + (posTrg.x + posTrg.y * target.GetWidth());
-
- while (sourcePtr < sourcePtrEnd)
- {
- memcpy(targetPtr, sourcePtr, width);
- sourcePtr += source.GetWidth();
- targetPtr += target.GetWidth();
- }
- }
- else if (target.HasAlpha())
- {
- unsigned char* targetPtr = target.GetAlpha() + (posTrg.x + posTrg.y * target.GetWidth());
- const unsigned char* const targetPtrEnd = target.GetAlpha() + (posTrg.x + (posTrg.y + height) * target.GetWidth());
-
- while (targetPtr < targetPtrEnd)
- {
- memset(targetPtr, wxIMAGE_ALPHA_OPAQUE, width);
- targetPtr += target.GetWidth();
- }
- }
- }
-}
-
-
-void wxButtonWithImage::refreshButtonLabel()
-{
- wxBitmap bitmapText = createBitmapFromText(textLabel);
-
- //calculate dimensions of new button
- const int height = std::max(std::max(bitmapFront.GetHeight(), bitmapText.GetHeight()), bitmapBack.GetHeight());
- const int width = bitmapFront.GetWidth() + m_spaceAfter + bitmapText.GetWidth() + m_spaceBefore + bitmapBack.GetWidth();
-
- //create a transparent image
- wxImage transparentImage(width, height, false);
- transparentImage.SetAlpha();
- unsigned char* alpha = transparentImage.GetAlpha();
- memset(alpha, wxIMAGE_ALPHA_TRANSPARENT, width * height);
-
- //wxDC::DrawLabel() unfortunately isn't working for transparent images on Linux, so we need to use custom image-concatenation
- if (bitmapFront.IsOk())
- writeToImage(wxImage(bitmapFront.ConvertToImage()),
- wxPoint(0, (transparentImage.GetHeight() - bitmapFront.GetHeight()) / 2),
- transparentImage);
-
- if (bitmapText.IsOk())
- writeToImage(wxImage(bitmapText.ConvertToImage()),
- wxPoint(bitmapFront.GetWidth() + m_spaceAfter, (transparentImage.GetHeight() - bitmapText.GetHeight()) / 2),
- transparentImage);
-
- if (bitmapBack.IsOk())
- writeToImage(wxImage(bitmapBack.ConvertToImage()),
- wxPoint(bitmapFront.GetWidth() + m_spaceAfter + bitmapText.GetWidth() + m_spaceBefore, (transparentImage.GetHeight() - bitmapBack.GetHeight()) / 2),
- transparentImage);
-
- //adjust button size
- wxSize minSize = GetMinSize();
-
- //SetMinSize() instead of SetSize() is needed here for wxWindows layout determination to work corretly
- wxBitmapButton::SetMinSize(wxSize(std::max(width + 10, minSize.GetWidth()), std::max(height + 5, minSize.GetHeight())));
-
- //finally set bitmap
- wxBitmapButton::SetBitmapLabel(wxBitmap(transparentImage));
-}
diff --git a/library/customButton.h b/library/customButton.h
deleted file mode 100644
index ce43828a..00000000
--- a/library/customButton.h
+++ /dev/null
@@ -1,42 +0,0 @@
-/***************************************************************
- * Purpose: wxButton with bitmap label
- * Author: ZenJu (zhnmju123@gmx.de)
- * Created: Feb. 2009
- **************************************************************/
-
-#ifndef CUSTOMBUTTON_H_INCLUDED
-#define CUSTOMBUTTON_H_INCLUDED
-
-#include <wx/bmpbuttn.h>
-
-
-//wxButtonWithImage behaves like wxButton but optionally adds bitmap labels
-class wxButtonWithImage : public wxBitmapButton
-{
-public:
- wxButtonWithImage(wxWindow *parent,
- wxWindowID id,
- const wxString& label,
- const wxPoint& pos = wxDefaultPosition,
- const wxSize& size = wxDefaultSize,
- long style = 0,
- const wxValidator& validator = wxDefaultValidator,
- const wxString& name = wxButtonNameStr);
-
- void setBitmapFront(const wxBitmap& bitmap, unsigned spaceAfter = 0);
- void setTextLabel( const wxString& text);
- 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;
-};
-
-
-#endif // CUSTOMBUTTON_H_INCLUDED
diff --git a/library/fileError.h b/library/fileError.h
deleted file mode 100644
index 214cdecb..00000000
--- a/library/fileError.h
+++ /dev/null
@@ -1,25 +0,0 @@
-#ifndef FILEERROR_H_INCLUDED
-#define FILEERROR_H_INCLUDED
-
-#include "zstring.h"
-#include "fileError.h"
-
-namespace FreeFileSync
-{
- class FileError //Exception class used to notify file/directory copy/delete errors
- {
- public:
- FileError(const Zstring& message) :
- errorMessage(message) {}
-
- const Zstring& show() const
- {
- return errorMessage;
- }
-
- private:
- Zstring errorMessage;
- };
-}
-
-#endif // FILEERROR_H_INCLUDED
diff --git a/library/fileHandling.cpp b/library/fileHandling.cpp
deleted file mode 100644
index 06431b81..00000000
--- a/library/fileHandling.cpp
+++ /dev/null
@@ -1,1075 +0,0 @@
-#include "fileHandling.h"
-#include <wx/intl.h>
-#include <wx/msgdlg.h>
-#include "../algorithm.h"
-#include <wx/filename.h>
-#include "globalFunctions.h"
-
-#ifdef FFS_WIN
-#include <wx/msw/wrapwin.h> //includes "windows.h"
-#include "shadow.h"
-
-#elif defined FFS_LINUX
-#include <sys/stat.h>
-#include <time.h>
-#include <utime.h>
-#include <fstream>
-#include <unistd.h>
-#include <dirent.h>
-#include <errno.h>
-#endif
-
-using FreeFileSync::FileError;
-
-
-class RecycleBin
-{
-public:
- static const RecycleBin& getInstance()
- {
- static RecycleBin instance; //lazy creation of RecycleBin
- return instance;
- }
-
- bool recycleBinExists() const
- {
- return recycleBinAvailable;
- }
-
- bool moveToRecycleBin(const Zstring& filename) const;
-
-private:
- RecycleBin() :
- recycleBinAvailable(false)
- {
-#ifdef FFS_WIN
- recycleBinAvailable = true;
-#endif // FFS_WIN
- }
-
- ~RecycleBin() {}
-
-private:
- bool recycleBinAvailable;
-};
-
-
-bool RecycleBin::moveToRecycleBin(const Zstring& filename) const
-{
- if (!recycleBinAvailable) //this method should ONLY be called if recycle bin is available
- throw RuntimeException(_("Initialization of Recycle Bin failed!"));
-
-#ifdef FFS_WIN
- Zstring filenameDoubleNull = filename + wxChar(0);
-
- SHFILEOPSTRUCT fileOp;
- fileOp.hwnd = NULL;
- fileOp.wFunc = FO_DELETE;
- fileOp.pFrom = filenameDoubleNull.c_str();
- fileOp.pTo = NULL;
- fileOp.fFlags = FOF_ALLOWUNDO | FOF_NOCONFIRMATION | FOF_SILENT | FOF_NOERRORUI;
- fileOp.fAnyOperationsAborted = false;
- fileOp.hNameMappings = NULL;
- fileOp.lpszProgressTitle = NULL;
-
- if (SHFileOperation(&fileOp) != 0 || fileOp.fAnyOperationsAborted) return false;
-#endif
-
- return true;
-}
-
-
-bool FreeFileSync::recycleBinExists()
-{
- return RecycleBin::getInstance().recycleBinExists();
-}
-
-
-inline
-bool moveToRecycleBin(const Zstring& filename) throw(RuntimeException)
-{
- return RecycleBin::getInstance().moveToRecycleBin(filename);
-}
-
-
-bool FreeFileSync::fileExists(const Zstring& filename)
-{ //symbolic links (broken or not) are also treated as existing files!
-#ifdef FFS_WIN
- // we must use GetFileAttributes() instead of the ANSI C functions because
- // it can cope with network (UNC) paths unlike them
- const DWORD ret = ::GetFileAttributes(filename.c_str());
-
- return (ret != INVALID_FILE_ATTRIBUTES) && !(ret & FILE_ATTRIBUTE_DIRECTORY);
-
-#elif defined FFS_LINUX
- struct stat fileInfo;
- return (lstat(filename.c_str(), &fileInfo) == 0 &&
- (S_ISLNK(fileInfo.st_mode) || S_ISREG(fileInfo.st_mode)));
-#endif
-}
-
-
-void FreeFileSync::removeFile(const Zstring& filename, const bool useRecycleBin)
-{
- //no error situation if file is not existing! manual deletion relies on it!
-#ifdef FFS_WIN
- if (GetFileAttributes(filename.c_str()) == INVALID_FILE_ATTRIBUTES)
- return; //neither file nor any other object with that name existing
-
-#elif defined FFS_LINUX
- struct stat fileInfo;
- if (lstat(filename.c_str(), &fileInfo) != 0)
- return; //neither file nor any other object (e.g. broken symlink) with that name existing
-#endif
-
- if (useRecycleBin)
- {
- if (!moveToRecycleBin(filename))
- 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
- {
- 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());
- }
-#elif defined FFS_LINUX
- if (unlink(filename.c_str()) != 0)
- {
- Zstring errorMessage = Zstring(_("Error deleting file:")) + wxT("\n\"") + filename + wxT("\"");
- throw FileError(errorMessage + wxT("\n\n") + FreeFileSync::getLastErrorFormatted());
- }
-#endif
-}
-
-
-class FilesDirsOnlyTraverser : public FreeFileSync::FullDetailFileTraverser
-{
-public:
- FilesDirsOnlyTraverser(std::vector<Zstring>& files, std::vector<Zstring>& dirs) :
- m_files(files),
- m_dirs(dirs) {}
-
- virtual wxDirTraverseResult OnFile(const Zstring& filename, const FreeFileSync::FileInfo& details)
- {
- m_files.push_back(filename);
- return wxDIR_CONTINUE;
- }
- virtual wxDirTraverseResult OnDir(const Zstring& dirname)
- {
- m_dirs.push_back(dirname);
- return wxDIR_IGNORE; //DON'T traverse into subdirs, removeDirectory works recursively!
- }
- virtual wxDirTraverseResult OnError(const Zstring& errorText)
- {
- throw FileError(errorText);
- }
-
-private:
- std::vector<Zstring>& m_files;
- std::vector<Zstring>& m_dirs;
-};
-
-
-void FreeFileSync::removeDirectory(const Zstring& directory, const bool useRecycleBin)
-{
- //no error situation if directory is not existing! manual deletion relies on it!
-#ifdef FFS_WIN
- const DWORD dirAttr = GetFileAttributes(directory.c_str()); //name of a file or directory
- if (dirAttr == INVALID_FILE_ATTRIBUTES)
- return; //neither directory nor any other object with that name existing
-
-#elif defined FFS_LINUX
- struct stat dirInfo;
- if (lstat(directory.c_str(), &dirInfo) != 0)
- return; //neither directory nor any other object (e.g. broken symlink) with that name existing
-#endif
-
- if (useRecycleBin)
- {
- if (!moveToRecycleBin(directory))
- throw FileError(Zstring(_("Error moving to Recycle Bin:")) + wxT("\n\"") + directory + wxT("\""));
- return;
- }
-
-//attention: check if directory is a symlink! Do NOT traverse into it deleting contained files!!!
-#ifdef FFS_WIN
- if (dirAttr & FILE_ATTRIBUTE_REPARSE_POINT)
- { //remove symlink directly, support for \\?\-prefix
- if (RemoveDirectory(directory.c_str()) == 0)
- {
- Zstring errorMessage = Zstring(_("Error deleting directory:")) + wxT("\n\"") + directory + wxT("\"");
- throw FileError(errorMessage + wxT("\n\n") + FreeFileSync::getLastErrorFormatted());
- }
- return;
- }
-
-#elif defined FFS_LINUX
- if (S_ISLNK(dirInfo.st_mode))
- {
- if (unlink(directory.c_str()) != 0)
- {
- Zstring errorMessage = Zstring(_("Error deleting directory:")) + wxT("\n\"") + directory + wxT("\"");
- throw FileError(errorMessage + wxT("\n\n") + FreeFileSync::getLastErrorFormatted());
- }
- return;
- }
-#endif
-
- std::vector<Zstring> fileList;
- std::vector<Zstring> dirList;
-
- //get all files and directories from current directory (WITHOUT subdirectories!)
- FilesDirsOnlyTraverser traverser(fileList, dirList);
- FreeFileSync::traverseInDetail(directory, false, &traverser); //don't traverse into symlinks to directories
-
- //delete files
- for (std::vector<Zstring>::const_iterator j = fileList.begin(); j != fileList.end(); ++j)
- FreeFileSync::removeFile(*j, false);
-
- //delete directories recursively
- for (std::vector<Zstring>::const_iterator j = dirList.begin(); j != dirList.end(); ++j)
- FreeFileSync::removeDirectory(*j, false); //call recursively to correctly handle symbolic links
-
- //parent directory is deleted last
-#ifdef FFS_WIN
- //initialize file attributes
- if (!SetFileAttributes(
- directory.c_str(), // address of directory name
- FILE_ATTRIBUTE_NORMAL)) // attributes to set
- {
- Zstring errorMessage = Zstring(_("Error deleting directory:")) + wxT("\n\"") + directory + wxT("\"");
- throw FileError(errorMessage + wxT("\n\n") + FreeFileSync::getLastErrorFormatted());
- }
-
- //remove directory, support for \\?\-prefix
- if (!RemoveDirectory(directory.c_str()))
- {
- Zstring errorMessage = Zstring(_("Error deleting directory:")) + wxT("\n\"") + directory + wxT("\"");
- throw FileError(errorMessage + wxT("\n\n") + FreeFileSync::getLastErrorFormatted());
- }
-#else
- if (rmdir(directory.c_str()) != 0)
- {
- Zstring errorMessage = Zstring(_("Error deleting directory:")) + wxT("\n\"") + directory + wxT("\"");
- throw FileError(errorMessage + wxT("\n\n") + FreeFileSync::getLastErrorFormatted());
- }
-#endif
-}
-
-
-#ifdef FFS_WIN
-class CloseHandleOnExit
-{
-public:
- CloseHandleOnExit(HANDLE fileHandle) : fileHandle_(fileHandle) {}
-
- ~CloseHandleOnExit()
- {
- CloseHandle(fileHandle_);
- }
-
-private:
- HANDLE fileHandle_;
-};
-
-
-class KernelDllHandler //dynamically load windows API functions
-{
- typedef DWORD (WINAPI *GetFinalPath)(
- HANDLE hFile,
- LPTSTR lpszFilePath,
- DWORD cchFilePath,
- DWORD dwFlags);
-
-public:
- static const KernelDllHandler& getInstance() //lazy creation of KernelDllHandler
- {
- static KernelDllHandler instance;
- return instance;
- }
-
- GetFinalPath getFinalPathNameByHandle;
-
-private:
- KernelDllHandler() :
- getFinalPathNameByHandle(NULL),
- hKernel(NULL)
- {
- //get a handle to the DLL module containing required functionality
- hKernel = ::LoadLibrary(wxT("kernel32.dll"));
- if (hKernel)
- getFinalPathNameByHandle = reinterpret_cast<GetFinalPath>(::GetProcAddress(hKernel, "GetFinalPathNameByHandleW")); //load unicode version!
- }
-
- ~KernelDllHandler()
- {
- if (hKernel) ::FreeLibrary(hKernel);
- }
-
- HINSTANCE hKernel;
-};
-
-
-Zstring resolveDirectorySymlink(const Zstring& dirLinkName) //get full target path of symbolic link to a directory
-{
- //open handle to target of symbolic link
- HANDLE hDir = CreateFile(dirLinkName.c_str(),
- 0,
- FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
- NULL,
- OPEN_EXISTING,
- FILE_FLAG_BACKUP_SEMANTICS,
- NULL);
- if (hDir == INVALID_HANDLE_VALUE)
- return Zstring();
-
- CloseHandleOnExit dummy(hDir);
-
- if (KernelDllHandler::getInstance().getFinalPathNameByHandle == NULL )
- throw FileError(Zstring(_("Error loading library function:")) + wxT("\n\"") + wxT("GetFinalPathNameByHandleW") + wxT("\""));
-
- const unsigned BUFFER_SIZE = 10000;
- TCHAR targetPath[BUFFER_SIZE];
-
- const DWORD rv = KernelDllHandler::getInstance().getFinalPathNameByHandle(
- hDir,
- targetPath,
- BUFFER_SIZE,
- 0);
-
- if (rv >= BUFFER_SIZE || rv == 0)
- return Zstring();
-
- return targetPath;
-}
-#endif
-
-
-void createDirectoryRecursively(const Zstring& directory, const Zstring& templateDir, const bool copyDirectorySymLinks, const int level)
-{
- if (wxDirExists(directory.c_str()))
- return;
-
- if (level == 50) //catch endless recursion
- return;
-
- //try to create parent folders first
- const Zstring dirParent = directory.BeforeLast(FreeFileSync::FILE_NAME_SEPARATOR);
- if (!dirParent.empty() && !wxDirExists(dirParent))
- {
- //call function recursively
- const Zstring templateParent = templateDir.BeforeLast(FreeFileSync::FILE_NAME_SEPARATOR);
- createDirectoryRecursively(dirParent, templateParent, false, level + 1); //don't create symbolic links in recursion!
- }
-
- //now creation should be possible
-#ifdef FFS_WIN
- const DWORD templateAttr = ::GetFileAttributes(templateDir.c_str()); //replaces wxDirExists(): also returns successful for broken symlinks
- if (templateAttr == INVALID_FILE_ATTRIBUTES) //fallback
- {
- if (CreateDirectory(
- directory.c_str(), // pointer to a directory path string
- NULL) == 0 && level == 0)
- {
- const Zstring errorMessage = Zstring(_("Error creating directory:")) + wxT("\n\"") + directory + wxT("\"");
- throw FileError(errorMessage + wxT("\n\n") + FreeFileSync::getLastErrorFormatted());
- }
- }
- else
- {
- //symbolic link handling
- if (!copyDirectorySymLinks && templateAttr & FILE_ATTRIBUTE_REPARSE_POINT) //create directory based on target of symbolic link
- {
- //get target directory of symbolic link
- const Zstring targetPath = resolveDirectorySymlink(templateDir);
- if (targetPath.empty())
- {
- if (level == 0)
- throw FileError(Zstring(_("Error resolving symbolic link:")) + wxT("\n\"") + templateDir + wxT("\""));
- }
- else
- {
- if (CreateDirectoryEx( // this function automatically copies symbolic links if encountered
- targetPath.c_str(), // pointer to path string of template directory
- directory.c_str(), // pointer to a directory path string
- NULL) == 0 && level == 0)
- {
- const Zstring errorMessage = Zstring(_("Error creating directory:")) + wxT("\n\"") + directory + wxT("\"");
- throw FileError(errorMessage + wxT("\n\n") + FreeFileSync::getLastErrorFormatted());
- }
- }
- }
- else //in all other cases
- {
- if (CreateDirectoryEx( // this function automatically copies symbolic links if encountered
- templateDir.c_str(), // pointer to path string of template directory
- directory.c_str(), // pointer to a directory path string
- NULL) == 0 && level == 0)
- {
- const Zstring errorMessage = Zstring(_("Error creating directory:")) + wxT("\n\"") + directory + wxT("\"");
- throw FileError(errorMessage + wxT("\n\n") + FreeFileSync::getLastErrorFormatted());
- }
- }
- }
-#elif defined FFS_LINUX
- //symbolic link handling
- if (copyDirectorySymLinks)
- {
- //test if templateDir is a symbolic link
- struct stat linkInfo;
- if (lstat(templateDir.c_str(), &linkInfo) == 0 && S_ISLNK(linkInfo.st_mode))
- {
- //copy symbolic link
- const int BUFFER_SIZE = 10000;
- char buffer[BUFFER_SIZE];
- const int bytesWritten = readlink(templateDir.c_str(), buffer, BUFFER_SIZE);
- if (bytesWritten < 0 || bytesWritten == BUFFER_SIZE)
- {
- Zstring errorMessage = Zstring(_("Error resolving symbolic link:")) + wxT("\n\"") + templateDir + wxT("\"");
- if (bytesWritten < 0) errorMessage += Zstring(wxT("\n\n")) + FreeFileSync::getLastErrorFormatted();
- throw FileError(errorMessage);
- }
- //set null-terminating char
- buffer[bytesWritten] = 0;
-
- if (symlink(buffer, directory.c_str()) != 0)
- {
- Zstring errorMessage = Zstring(_("Error creating directory:")) + wxT("\n\"") + directory + wxT("\"");
- throw FileError(errorMessage + wxT("\n\n") + FreeFileSync::getLastErrorFormatted());
- }
- return; //symlink created successfully
- }
- }
-
- //default directory creation
- if (mkdir(directory.c_str(), 0755) != 0 && level == 0)
- {
- Zstring errorMessage = Zstring(_("Error creating directory:")) + wxT("\n\"") + directory + wxT("\"");
- throw FileError(errorMessage + wxT("\n\n") + FreeFileSync::getLastErrorFormatted());
- }
-
-//copy directory permissions: not sure if this is a good idea: if a directory is read-only copying/sync'ing of files will fail...
- /*
- if (templateDirExists)
- {
- struct stat fileInfo;
- if (stat(templateDir.c_str(), &fileInfo) != 0) //read permissions from template directory
- throw FileError(Zstring(_("Error reading file attributes:")) + wxT("\n\"") + templateDir + wxT("\""));
-
- // reset the umask as we want to create the directory with exactly the same permissions as the template
- wxCHANGE_UMASK(0);
-
- if (mkdir(directory.c_str(), fileInfo.st_mode) != 0 && level == 0)
- throw FileError(Zstring(_("Error creating directory:")) + wxT("\n\"") + directory + wxT("\""));
- }
- else
- {
- if (mkdir(directory.c_str(), 0777) != 0 && level == 0)
- throw FileError(Zstring(_("Error creating directory:")) + wxT("\n\"") + directory + wxT("\""));
- }
- */
-#endif
-}
-
-
-void FreeFileSync::createDirectory(const Zstring& directory, const Zstring& templateDir, const bool copyDirectorySymLinks)
-{
- //remove trailing separator
- Zstring dirFormatted;
- if (FreeFileSync::endsWithPathSeparator(directory))
- dirFormatted = directory.BeforeLast(FreeFileSync::FILE_NAME_SEPARATOR);
- else
- dirFormatted = directory;
-
- Zstring templateFormatted;
- if (FreeFileSync::endsWithPathSeparator(templateDir))
- templateFormatted = templateDir.BeforeLast(FreeFileSync::FILE_NAME_SEPARATOR);
- else
- templateFormatted = templateDir;
-
- createDirectoryRecursively(dirFormatted, templateFormatted, copyDirectorySymLinks, 0);
-}
-
-
-#ifdef FFS_WIN
-
-#ifndef COPY_FILE_COPY_SYMLINK
-const DWORD COPY_FILE_COPY_SYMLINK = 0x00000800;
-#endif
-
-DWORD CALLBACK copyCallbackInternal(
- LARGE_INTEGER totalFileSize,
- LARGE_INTEGER totalBytesTransferred,
- LARGE_INTEGER streamSize,
- LARGE_INTEGER streamBytesTransferred,
- DWORD dwStreamNumber,
- DWORD dwCallbackReason,
- HANDLE hSourceFile,
- HANDLE hDestinationFile,
- LPVOID lpData)
-{
- using FreeFileSync::CopyFileCallback;
-
- //small performance optimization: it seems this callback function is called for every 64 kB (depending on cluster size).
- static unsigned int callNr = 0;
- if (++callNr % 50 == 0) //reduce by factor of 50 =^ 10-20 calls/sec
- {
- if (lpData != NULL)
- {
- //some odd check for some possible(?) error condition
- if (totalBytesTransferred.HighPart < 0) //let's see if someone answers the call...
- wxMessageBox(wxT("You've just discovered a bug in WIN32 API function \"CopyFileEx\"! \n\n\
- Please write a mail to the author of FreeFileSync at zhnmju123@gmx.de and simply state that\n\
- \"totalBytesTransferred.HighPart can be below zero\"!\n\n\
- This will then be handled in future versions of FreeFileSync.\n\nThanks -ZenJu"), wxT("Warning"));
-
-
- CopyFileCallback* callback = static_cast<CopyFileCallback*>(lpData);
-
- switch (callback->updateCopyStatus(wxULongLong(totalBytesTransferred.HighPart, totalBytesTransferred.LowPart)))
- {
- case CopyFileCallback::CONTINUE:
- break;
- case CopyFileCallback::CANCEL:
- return PROGRESS_CANCEL;
- }
- }
- }
-
- return PROGRESS_CONTINUE;
-}
-
-
-void FreeFileSync::copyFile(const Zstring& sourceFile,
- const Zstring& targetFile,
- const bool copyFileSymLinks,
- ShadowCopy* shadowCopyHandler,
- CopyFileCallback* callback)
-{
- DWORD copyFlags = COPY_FILE_FAIL_IF_EXISTS;
-
- if (copyFileSymLinks) //copy symbolic links instead of the files pointed at
- copyFlags |= COPY_FILE_COPY_SYMLINK;
-
- if (!::CopyFileEx( //same performance as CopyFile()
- sourceFile.c_str(),
- targetFile.c_str(),
- copyCallbackInternal,
- callback,
- NULL,
- copyFlags))
- {
- const DWORD lastError = ::GetLastError();
-
- //if file is locked (try to) use Windows Volume Shadow Copy Service
- if (lastError == ERROR_SHARING_VIOLATION && shadowCopyHandler != NULL)
- {
- const Zstring shadowFilename(shadowCopyHandler->makeShadowCopy(sourceFile));
- FreeFileSync::copyFile(shadowFilename, //transferred bytes is automatically reset when new file is copied
- targetFile,
- copyFileSymLinks,
- shadowCopyHandler,
- callback);
- return;
- }
-
- const Zstring errorMessage = Zstring(_("Error copying file:")) + wxT("\n\"") + sourceFile + wxT("\" -> \"") + targetFile + wxT("\"");
- throw FileError(errorMessage + wxT("\n\n") + FreeFileSync::getLastErrorFormatted(lastError));
- }
-}
-
-
-#elif defined FFS_LINUX
-struct MemoryAllocator
-{
- MemoryAllocator()
- {
- buffer = new char[bufferSize];
- }
-
- ~MemoryAllocator()
- {
- delete [] buffer;
- }
-
- static const unsigned int bufferSize = 512 * 1024;
- char* buffer;
-};
-
-
-void FreeFileSync::copyFile(const Zstring& sourceFile,
- const Zstring& targetFile,
- const bool copyFileSymLinks,
- CopyFileCallback* callback)
-{
- using FreeFileSync::CopyFileCallback;
-
- try
- {
- if (FreeFileSync::fileExists(targetFile.c_str()))
- throw FileError(Zstring(_("Error copying file:")) + wxT("\n\"") + sourceFile + wxT("\" -> \"") + targetFile + wxT("\"\n")
- + _("Target file already existing!"));
-
- //symbolic link handling
- if (copyFileSymLinks)
- {
- //test if sourceFile is a symbolic link
- struct stat linkInfo;
- if (lstat(sourceFile.c_str(), &linkInfo) != 0)
- {
- Zstring errorMessage = Zstring(_("Error reading file attributes:")) + wxT("\n\"") + sourceFile + wxT("\"");
- throw FileError(errorMessage + wxT("\n\n") + FreeFileSync::getLastErrorFormatted());
- }
-
- if (S_ISLNK(linkInfo.st_mode))
- {
- //copy symbolic link
- const unsigned BUFFER_SIZE = 10000;
- char buffer[BUFFER_SIZE];
- const int bytesWritten = readlink(sourceFile.c_str(), buffer, BUFFER_SIZE - 1);
- if (bytesWritten < 0)
- {
- Zstring errorMessage = Zstring(_("Error resolving symbolic link:")) + wxT("\n\"") + sourceFile + wxT("\"");
- throw FileError(errorMessage + wxT("\n\n") + FreeFileSync::getLastErrorFormatted());
- }
- //set null-terminating char
- buffer[bytesWritten] = 0;
-
- if (symlink(buffer, targetFile.c_str()) != 0)
- {
- Zstring errorMessage = Zstring(_("Error writing file:")) + wxT("\n\"") + targetFile + wxT("\"");
- throw FileError(errorMessage + wxT("\n\n") + FreeFileSync::getLastErrorFormatted());
- }
-
- return; //symlink created successfully
- }
- }
-
- //begin of regular file copy
- struct stat fileInfo;
- if (stat(sourceFile.c_str(), &fileInfo) != 0) //read file attributes from source file (resolve symlinks)
- {
- Zstring errorMessage = Zstring(_("Error reading file attributes:")) + wxT("\n\"") + sourceFile + wxT("\"");
- throw FileError(errorMessage + wxT("\n\n") + FreeFileSync::getLastErrorFormatted());
- }
-
- //open sourceFile for reading
- std::ifstream fileIn(sourceFile.c_str(), std::ios_base::binary);
- if (fileIn.fail())
- throw FileError(Zstring(_("Error opening file:")) + wxT("\n\"") + sourceFile + wxT("\""));
-
- //create targetFile and open it for writing
- std::ofstream fileOut(targetFile.c_str(), std::ios_base::binary);
- if (fileOut.fail())
- throw FileError(Zstring(_("Error opening file:")) + wxT("\n\"") + targetFile + wxT("\""));
-
- //copy contents of sourceFile to targetFile
- wxULongLong totalBytesTransferred;
- static MemoryAllocator memory;
- while (true)
- {
- fileIn.read(memory.buffer, memory.bufferSize);
- if (fileIn.eof()) //end of file? fail bit is set in this case also!
- {
- fileOut.write(memory.buffer, fileIn.gcount());
- if (fileOut.bad())
- throw FileError(Zstring(_("Error writing file:")) + wxT("\n\"") + targetFile + wxT("\""));
- break;
- }
- else if (fileIn.fail())
- throw FileError(Zstring(_("Error reading file:")) + wxT("\n\"") + sourceFile + wxT("\""));
-
-
- fileOut.write(memory.buffer, memory.bufferSize);
- if (fileOut.bad())
- throw FileError(Zstring(_("Error writing file:")) + wxT("\n\"") + targetFile + wxT("\""));
-
- totalBytesTransferred += memory.bufferSize;
-
- //invoke callback method to update progress indicators
- if (callback != NULL)
- {
- switch (callback->updateCopyStatus(totalBytesTransferred))
- {
- case CopyFileCallback::CONTINUE:
- break;
- case CopyFileCallback::CANCEL:
- fileOut.close();
- wxRemoveFile(targetFile.c_str()); //don't handle error situations!
- return;
- }
- }
- }
-
- //close streams before changing attributes
- fileIn.close();
- fileOut.close();
-
- //adapt file modification time:
- struct utimbuf newTimes;
- time(&newTimes.actime); //set file access time to current time
- newTimes.modtime = fileInfo.st_mtime;
- if (utime(targetFile.c_str(), &newTimes) != 0)
- {
- Zstring errorMessage = Zstring(_("Error changing modification time:")) + wxT("\n\"") + targetFile + wxT("\"");
- throw FileError(errorMessage + wxT("\n\n") + FreeFileSync::getLastErrorFormatted());
- }
-
- //set file access rights
- if (chmod(targetFile.c_str(), fileInfo.st_mode) != 0)
- {
- Zstring errorMessage = Zstring(_("Error writing file attributes:")) + wxT("\n\"") + targetFile + wxT("\"");
- throw FileError(errorMessage + wxT("\n\n") + FreeFileSync::getLastErrorFormatted());
- }
- }
- catch (...)
- { //try to delete target file if error occured, or exception was thrown in callback function
- if (FreeFileSync::fileExists(targetFile.c_str()))
- wxRemoveFile(targetFile.c_str()); //don't handle error situations!
-
- throw;
- }
-}
-#endif
-
-
-#ifdef FFS_WIN
-class CloseFindHandleOnExit
-{
-public:
- CloseFindHandleOnExit(HANDLE searchHandle) : searchHandle_(searchHandle) {}
-
- ~CloseFindHandleOnExit()
- {
- FindClose(searchHandle_);
- }
-
-private:
- HANDLE searchHandle_;
-};
-
-
-inline
-void setWin32FileInformation(const FILETIME& lastWriteTime, const DWORD fileSizeHigh, const DWORD fileSizeLow, FreeFileSync::FileInfo& output)
-{
- //convert UTC FILETIME to ANSI C format (number of seconds since Jan. 1st 1970 UTC)
- wxLongLong writeTimeLong(wxInt32(lastWriteTime.dwHighDateTime), lastWriteTime.dwLowDateTime);
- writeTimeLong /= 10000000; //reduce precision to 1 second (FILETIME has unit 10^-7 s)
- writeTimeLong -= wxLongLong(2, 3054539008UL); //timeshift between ansi C time and FILETIME in seconds == 11644473600s
- output.lastWriteTimeRaw = writeTimeLong;
-
- output.fileSize = wxULongLong(fileSizeHigh, fileSizeLow);
-}
-
-
-inline
-bool setWin32FileInformationFromSymlink(const Zstring linkName, FreeFileSync::FileInfo& output)
-{
- //open handle to target of symbolic link
- HANDLE hFile = CreateFile(linkName.c_str(),
- 0,
- FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
- NULL,
- OPEN_EXISTING,
- FILE_FLAG_BACKUP_SEMANTICS,
- NULL);
- if (hFile == INVALID_HANDLE_VALUE)
- return false;
-
- CloseHandleOnExit dummy(hFile);
-
- BY_HANDLE_FILE_INFORMATION fileInfoByHandle;
-
- if (!GetFileInformationByHandle(
- hFile,
- &fileInfoByHandle))
- return false;
-
- //write output
- setWin32FileInformation(fileInfoByHandle.ftLastWriteTime, fileInfoByHandle.nFileSizeHigh, fileInfoByHandle.nFileSizeLow, output);
-
- return true;
-}
-
-
-#elif defined FFS_LINUX
-class CloseDirOnExit
-{
-public:
- CloseDirOnExit(DIR* dir) : m_dir(dir) {}
-
- ~CloseDirOnExit()
- {
- closedir(m_dir); //no error handling here
- }
-
-private:
- DIR* m_dir;
-};
-#endif
-
-
-template <bool traverseDirectorySymlinks>
-class TraverseRecursively
-{
-public:
- TraverseRecursively(FreeFileSync::FullDetailFileTraverser* sink) : m_sink(sink) {}
-
- bool traverse(const Zstring& directory, const int level)
- {
- if (level == 100) //catch endless recursion
- {
- if (m_sink->OnError(Zstring(_("Error traversing directory:")) + wxT("\n\"") + directory + wxT("\"")) == wxDIR_STOP)
- return false;
- else
- return true;
- }
-
-#ifdef FFS_WIN
- //ensure directoryFormatted ends with backslash
- const Zstring directoryFormatted = FreeFileSync::endsWithPathSeparator(directory) ?
- directory :
- directory + FreeFileSync::FILE_NAME_SEPARATOR;
-
- WIN32_FIND_DATA fileMetaData;
- HANDLE searchHandle = FindFirstFile((directoryFormatted + DefaultChar('*')).c_str(), //pointer to name of file to search for
- &fileMetaData); //pointer to returned information
-
- if (searchHandle == INVALID_HANDLE_VALUE)
- {
- 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;
- }
- CloseFindHandleOnExit dummy(searchHandle);
-
- do
- { //don't return "." and ".."
- const wxChar* const 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... (for directory symlinks this flag is set too!)
- {
- switch (m_sink->OnDir(fullName))
- {
- case wxDIR_IGNORE:
- break;
- case wxDIR_CONTINUE:
- //traverse into symbolic links, junctions, etc. if requested only:
- if (traverseDirectorySymlinks || (~fileMetaData.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT))
- if (!this->traverse(fullName, level + 1))
- return false;
- break;
- case wxDIR_STOP:
- return false;
- default:
- assert(false);
- }
- }
- else //a file...
- {
- FreeFileSync::FileInfo details;
-
- if (fileMetaData.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) //dereference symlinks!
- {
- if (!setWin32FileInformationFromSymlink(fullName, details)) //broken symlink
- {
- details.lastWriteTimeRaw = 0; //we are not interested in the modifiation time of the link
- details.fileSize = 0;
- }
- }
- else
- setWin32FileInformation(fileMetaData.ftLastWriteTime, fileMetaData.nFileSizeHigh, fileMetaData.nFileSizeLow, details);
-
- if (m_sink->OnFile(fullName, details) == wxDIR_STOP)
- return false;
- }
- }
- while (FindNextFile(searchHandle, // handle to search
- &fileMetaData)); // pointer to structure for data on found file
-
- 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
- DIR* dirObj = opendir(directory.c_str());
- if (dirObj == NULL)
- {
- Zstring errorMessage = Zstring(_("Error traversing directory:")) + wxT("\n\"") + directory+ wxT("\"") ;
- if (m_sink->OnError(errorMessage + wxT("\n\n") + FreeFileSync::getLastErrorFormatted()) == wxDIR_STOP)
- return false;
- else
- return true;
- }
- CloseDirOnExit dummy(dirObj);
-
- struct dirent* dirEntry;
- while (!(errno = 0) && (dirEntry = readdir(dirObj)) != NULL) //set errno to 0 as unfortunately this isn't done when readdir() returns NULL when it is finished
- {
- //don't return "." and ".."
- const wxChar* const name = dirEntry->d_name;
- if ( name[0] == wxChar('.') &&
- ((name[1] == wxChar('.') && name[2] == wxChar('\0')) ||
- name[1] == wxChar('\0')))
- continue;
-
- const Zstring fullName = FreeFileSync::endsWithPathSeparator(directory) ? //e.g. "/"
- directory + name :
- directory + FreeFileSync::FILE_NAME_SEPARATOR + name;
-
- struct stat fileInfo;
- if (lstat(fullName.c_str(), &fileInfo) != 0) //lstat() does not resolve symlinks
- {
- const Zstring errorMessage = Zstring(_("Error reading file attributes:")) + wxT("\n\"") + fullName + wxT("\"");
- if (m_sink->OnError(errorMessage + wxT("\n\n") + FreeFileSync::getLastErrorFormatted()) == wxDIR_STOP)
- return false;
- continue;
- }
-
- const bool isSymbolicLink = S_ISLNK(fileInfo.st_mode);
- if (isSymbolicLink) //dereference symbolic links
- {
- if (stat(fullName.c_str(), &fileInfo) != 0) //stat() resolves symlinks
- {
- //a broken symbolic link
- FreeFileSync::FileInfo details;
- details.lastWriteTimeRaw = 0; //we are not interested in the modifiation time of the link
- details.fileSize = 0;
-
- if (m_sink->OnFile(fullName, details) == wxDIR_STOP)
- return false;
- continue;
- }
- }
-
-
- if (S_ISDIR(fileInfo.st_mode)) //a directory... (note: symbolic links need to be dereferenced to test if they point to a directory!)
- {
- switch (m_sink->OnDir(fullName))
- {
- case wxDIR_IGNORE:
- break;
- case wxDIR_CONTINUE:
- if (traverseDirectorySymlinks || !isSymbolicLink) //traverse into symbolic links if requested only
- {
- if (!this->traverse(fullName, level + 1))
- return false;
- }
- break;
- case wxDIR_STOP:
- return false;
- default:
- assert(false);
- }
- }
- else //a file...
- {
- FreeFileSync::FileInfo details;
- details.lastWriteTimeRaw = fileInfo.st_mtime; //UTC time(ANSI C format); unit: 1 second
- details.fileSize = fileInfo.st_size;
-
- if (m_sink->OnFile(fullName, details) == wxDIR_STOP)
- return false;
- }
- }
-
- if (errno == 0)
- return true; //everything okay
-
- //else: we have a problem... report it:
- const Zstring errorMessage = Zstring(_("Error traversing directory:")) + wxT("\n\"") + directory + wxT("\"") ;
- if (m_sink->OnError(errorMessage + wxT("\n\n") + FreeFileSync::getLastErrorFormatted()) == wxDIR_STOP)
- return false;
- else
- return true;
-#endif
- }
-
-private:
- FreeFileSync::FullDetailFileTraverser* m_sink;
-};
-
-
-void FreeFileSync::traverseInDetail(const Zstring& directory,
- const bool traverseDirectorySymlinks,
- FullDetailFileTraverser* sink)
-{
- Zstring directoryFormatted = directory;
-#ifdef FFS_LINUX //remove trailing slash
- if (directoryFormatted.size() > 1 && FreeFileSync::endsWithPathSeparator(directoryFormatted))
- directoryFormatted = directoryFormatted.BeforeLast(FreeFileSync::FILE_NAME_SEPARATOR);
-#endif
-
- if (traverseDirectorySymlinks)
- {
- TraverseRecursively<true> filewalker(sink);
- filewalker.traverse(directoryFormatted, 0);
- }
- else
- {
- TraverseRecursively<false> filewalker(sink);
- filewalker.traverse(directoryFormatted, 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() + FreeFileSync::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
deleted file mode 100644
index 3abdbe07..00000000
--- a/library/fileHandling.h
+++ /dev/null
@@ -1,71 +0,0 @@
-#ifndef RECYCLER_H_INCLUDED
-#define RECYCLER_H_INCLUDED
-
-#include <wx/dir.h>
-#include "zstring.h"
-#include "fileError.h"
-
-
-namespace FreeFileSync
-{
- struct FileInfo
- {
- wxULongLong fileSize; //unit: bytes!
- wxLongLong 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;
- };
-
- //custom traverser with detail information about files
- void traverseInDetail(const Zstring& directory, const bool traverseDirectorySymlinks, FullDetailFileTraverser* sink);
-
- bool fileExists(const Zstring& filename); //replaces wxFileExists()!
-
- //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 Zstring& templateDir, const bool copyDirectorySymLinks);
-
-
- class CopyFileCallback //callback functionality
- {
- public:
- virtual ~CopyFileCallback() {}
-
- enum Response
- {
- CONTINUE,
- CANCEL
- };
- virtual Response updateCopyStatus(const wxULongLong& totalBytesTransferred) = 0;
- };
-
- class ShadowCopy;
-#ifdef FFS_WIN
- void copyFile(const Zstring& sourceFile,
- const Zstring& targetFile,
- const bool copyFileSymLinks,
- ShadowCopy* shadowCopyHandler = NULL, //supply handler for making shadow copies
- CopyFileCallback* callback = NULL);
-
-#elif defined FFS_LINUX
- void copyFile(const Zstring& sourceFile,
- const Zstring& targetFile,
- const bool copyFileSymLinks,
- CopyFileCallback* callback);
-#endif
-}
-
-
-#endif // RECYCLER_H_INCLUDED
diff --git a/library/filter.cpp b/library/filter.cpp
index bd281c7c..30cd9341 100644
--- a/library/filter.cpp
+++ b/library/filter.cpp
@@ -3,113 +3,123 @@
#include <wx/string.h>
#include <set>
#include <vector>
-#include "../shared/globalFunctions.h"
+#include "../shared/systemConstants.h"
#include "../structures.h"
+#include <boost/bind.hpp>
using FreeFileSync::FilterProcess;
-void compoundStringToTable(const Zstring& compoundInput, const DefaultChar* delimiter, std::vector<Zstring>& output)
+inline
+void addFilterEntry(const Zstring& filtername, std::set<Zstring>& fileFilter, std::set<Zstring>& directoryFilter)
{
- output.clear();
- Zstring input(compoundInput);
+ Zstring filterFormatted = filtername;
+
+#ifdef FFS_WIN
+ //Windows does NOT distinguish between upper/lower-case
+ filterFormatted.MakeLower();
+#elif defined FFS_LINUX
+ //Linux DOES distinguish between upper/lower-case: nothing to do here
+#endif
- //make sure input ends with delimiter - no problem with empty strings here
- if (!input.EndsWith(delimiter))
- input += delimiter;
+ static const Zstring sepAsterisk = Zstring() + globalFunctions::FILE_NAME_SEPARATOR + wxT('*');
+ static const Zstring sepQuestionMark = Zstring() + globalFunctions::FILE_NAME_SEPARATOR + wxT('?');
+ static const Zstring asteriskSep = Zstring(wxT("*")) + globalFunctions::FILE_NAME_SEPARATOR;
+ static const Zstring questionMarkSep = Zstring(wxT("?")) + globalFunctions::FILE_NAME_SEPARATOR;
- unsigned int indexStart = 0;
- unsigned int indexEnd = 0;
- while ((indexEnd = input.find(delimiter, indexStart)) != Zstring::npos)
+ //add some syntactic sugar: handle beginning of filtername
+ if (filterFormatted.StartsWith(globalFunctions::FILE_NAME_SEPARATOR))
+ { //remove leading separators (keep BEFORE test for Zstring::empty()!)
+ filterFormatted = filterFormatted.AfterFirst(globalFunctions::FILE_NAME_SEPARATOR);
+ }
+ else if (filterFormatted.StartsWith(asteriskSep) || // *\abc
+ filterFormatted.StartsWith(questionMarkSep)) // ?\abc
{
- if (indexStart != indexEnd) //do not add empty strings
- {
- Zstring newEntry = input.substr(indexStart, indexEnd - indexStart);
+ addFilterEntry(Zstring(filterFormatted.c_str() + 1), fileFilter, directoryFilter); //prevent further recursion by prefix
+ }
- newEntry.Trim(true); //remove whitespace characters from right
- newEntry.Trim(false); //remove whitespace characters from left
- if (!newEntry.empty())
- output.push_back(newEntry);
- }
- indexStart = indexEnd + 1;
+ //even more syntactic sugar: handle end of filtername
+ if (filterFormatted.EndsWith(globalFunctions::FILE_NAME_SEPARATOR))
+ {
+ const Zstring candidate = filterFormatted.BeforeLast(globalFunctions::FILE_NAME_SEPARATOR);
+ if (!candidate.empty())
+ directoryFilter.insert(candidate); //only relevant for directory filtering
+ }
+ else if (filterFormatted.EndsWith(sepAsterisk) || // abc\*
+ filterFormatted.EndsWith(sepQuestionMark)) // abc\?
+ {
+ fileFilter.insert( filterFormatted);
+ directoryFilter.insert(filterFormatted);
+
+ const Zstring candidate = filterFormatted.BeforeLast(globalFunctions::FILE_NAME_SEPARATOR);
+ if (!candidate.empty())
+ directoryFilter.insert(candidate); //only relevant for directory filtering
+ }
+ else if (!filterFormatted.empty())
+ {
+ fileFilter. insert(filterFormatted);
+ directoryFilter.insert(filterFormatted);
}
}
-std::vector<Zstring> compoundStringToFilter(const Zstring& filterString)
+class MatchFound : public std::unary_function<const DefaultChar*, bool>
{
- //delimiters may be ';' or '\n'
- std::vector<Zstring> filterList;
- std::vector<Zstring> filterPreProcessing;
- compoundStringToTable(filterString, wxT(";"), filterPreProcessing);
+public:
+ MatchFound(const DefaultChar* name) : name_(name) {}
- for (std::vector<Zstring>::const_iterator i = filterPreProcessing.begin(); i != filterPreProcessing.end(); ++i)
+ bool operator()(const DefaultChar* mask) const
{
- std::vector<Zstring> newEntries;
- compoundStringToTable(*i, wxT("\n"), newEntries);
-
- filterList.insert(filterList.end(), newEntries.begin(), newEntries.end());
+ return Zstring::Matches(name_, mask);
}
-
- return filterList;
-}
+private:
+ const DefaultChar* name_;
+};
inline
-void addFilterEntry(const Zstring& filtername, std::set<Zstring>& fileFilter, std::set<Zstring>& directoryFilter)
+bool matchesFilter(const DefaultChar* name, const std::set<Zstring>& filter)
{
- Zstring filterFormatted = filtername;
-
-#ifdef FFS_WIN
- //Windows does NOT distinguish between upper/lower-case
- filterFormatted.MakeLower();
-#elif defined FFS_LINUX
- //Linux DOES distinguish between upper/lower-case
-//nothing to do here
+#ifdef FFS_WIN //Windows does NOT distinguish between upper/lower-case
+ Zstring nameFormatted = name;
+ nameFormatted.MakeLower();
+#elif defined FFS_LINUX //Linux DOES distinguish between upper/lower-case
+ const DefaultChar* const nameFormatted = name; //nothing to do here
#endif
- //remove leading separators (keep BEFORE test for Zstring::empty()!)
- if (filterFormatted.length() > 0 && *filterFormatted.c_str() == globalFunctions::FILE_NAME_SEPARATOR)
- filterFormatted = Zstring(filterFormatted.c_str() + 1);
+ return std::find_if(filter.begin(), filter.end(), MatchFound(nameFormatted)) != filter.end();
+}
- if (!filterFormatted.empty())
+
+//returns true if string matches at least the beginning of mask
+inline
+bool matchesMaskBegin(const DefaultChar* string, const DefaultChar* mask)
+{
+ for (DefaultChar ch; (ch = *mask) != 0; ++mask, ++string)
{
- //Test if filterFormatted ends with globalFunctions::FILE_NAME_SEPARATOR, ignoring '*' and '?'.
- //If so, treat as filter for directory and add to directoryFilter.
- const DefaultChar* filter = filterFormatted.c_str();
- int i = filterFormatted.length() - 1;
- while (filter[i] == DefaultChar('*') || filter[i] == DefaultChar('?'))
+ if (*string == 0)
+ return true;
+
+ switch (ch)
{
- --i;
+ case DefaultChar('?'):
+ break;
- if (i == -1)
- break;
- }
+ case DefaultChar('*'):
+ return true;
- if (i >= 0 && filter[i] == globalFunctions::FILE_NAME_SEPARATOR) //last FILE_NAME_SEPARATOR found
- {
- if (i != int(filterFormatted.length()) - 1) // "name\*"
- {
- fileFilter.insert(filterFormatted);
- directoryFilter.insert(filterFormatted);
- }
- //else // "name\"
-
- if (i > 0) // "name\*" or "name\": add "name" to directory filter
- directoryFilter.insert(Zstring(filterFormatted.c_str(), i));
- }
- else
- {
- fileFilter.insert(filterFormatted);
- directoryFilter.insert(filterFormatted);
+ default:
+ if (*string != ch)
+ return false;
}
}
+ return *string == 0;
}
inline
-bool matchesFilter(const DefaultChar* name, const std::set<Zstring>& filter)
+bool matchesFilterBegin(const DefaultChar* name, const std::set<Zstring>& filter)
{
#ifdef FFS_WIN //Windows does NOT distinguish between upper/lower-case
Zstring nameFormatted = name;
@@ -118,13 +128,25 @@ bool matchesFilter(const DefaultChar* name, const std::set<Zstring>& filter)
const DefaultChar* const nameFormatted = name; //nothing to do here
#endif
- for (std::set<Zstring>::const_iterator j = filter.begin(); j != filter.end(); ++j)
- if (Zstring::Matches(nameFormatted, j->c_str()))
- return true;
-
- return false;
+ return std::find_if(filter.begin(), filter.end(),
+ boost::bind(matchesMaskBegin, nameFormatted, _1)) != filter.end();
}
+
+std::vector<Zstring> compoundStringToFilter(const Zstring& filterString)
+{
+ //delimiters may be ';' or '\n'
+ std::vector<Zstring> output;
+
+ const std::vector<Zstring> filterPreProcessing = filterString.Tokenize(wxT(';'));
+ for (std::vector<Zstring>::const_iterator i = filterPreProcessing.begin(); i != filterPreProcessing.end(); ++i)
+ {
+ const std::vector<Zstring> newEntries = i->Tokenize(wxT('\n'));
+ output.insert(output.end(), newEntries.begin(), newEntries.end());
+ }
+
+ return output;
+}
//##############################################################
@@ -138,102 +160,173 @@ FilterProcess::FilterProcess(const wxString& includeFilter, const wxString& excl
const std::vector<Zstring> excludeList = compoundStringToFilter(excludeFilter.c_str());
//setup include/exclude filters for files and directories
- for (std::vector<Zstring>::const_iterator i = includeList.begin(); i != includeList.end(); ++i)
- addFilterEntry(*i, filterFileIn, filterFolderIn);
-
- for (std::vector<Zstring>::const_iterator i = excludeList.begin(); i != excludeList.end(); ++i)
- addFilterEntry(*i, filterFileEx, filterFolderEx);
+ std::for_each(includeList.begin(), includeList.end(), boost::bind(addFilterEntry, _1, boost::ref(filterFileIn), boost::ref(filterFolderIn)));
+ std::for_each(excludeList.begin(), excludeList.end(), boost::bind(addFilterEntry, _1, boost::ref(filterFileEx), boost::ref(filterFolderEx)));
}
-bool FilterProcess::matchesFileFilterIncl(const DefaultChar* relFilename) const
+bool FilterProcess::passFileFilter(const DefaultChar* relFilename) const
{
- return matchesFilter(relFilename, filterFileIn); //process include filters
+ return matchesFilter(relFilename, filterFileIn) && //process include filters
+ !matchesFilter(relFilename, filterFileEx); //process exclude filters
}
-bool FilterProcess::matchesFileFilterExcl(const DefaultChar* relFilename) const
+bool FilterProcess::passDirFilter(const DefaultChar* relDirname, bool* subObjMightMatch) const
{
- return matchesFilter(relFilename, filterFileEx); //process exclude filters
+ if (matchesFilter(relDirname, filterFolderEx)) //process exclude filters
+ {
+ if (subObjMightMatch)
+ *subObjMightMatch = false; //exclude subfolders/subfiles as well
+ return false;
+ }
+
+ if (!matchesFilter(relDirname, filterFolderIn)) //process include filters
+ {
+ if (subObjMightMatch)
+ {
+ Zstring subNameBegin(relDirname);
+ subNameBegin += globalFunctions::FILE_NAME_SEPARATOR;
+
+ *subObjMightMatch = matchesFilterBegin(subNameBegin, filterFileIn) || //might match a file in subdirectory
+ matchesFilterBegin(subNameBegin, filterFolderIn); //or another subdirectory
+ }
+ return false;
+ }
+
+ assert(subObjMightMatch == NULL || *subObjMightMatch == true);
+ return true;
}
-bool FilterProcess::matchesDirFilterIncl(const DefaultChar* relDirname) const
+class FilterData
{
- return matchesFilter(relDirname, filterFolderIn); //process include filters
-}
+public:
+ FilterData(const FilterProcess& filterProcIn) : filterProc(filterProcIn) {}
+
+ void operator()(FreeFileSync::FileMapping& fileObj)
+ {
+ const Zstring relName = fileObj.isEmpty<FreeFileSync::LEFT_SIDE>() ?
+ fileObj.getRelativeName<FreeFileSync::RIGHT_SIDE>() :
+ fileObj.getRelativeName<FreeFileSync::LEFT_SIDE>();
+
+ fileObj.selectedForSynchronization = filterProc.passFileFilter(relName);
+ }
+
+ void operator()(FreeFileSync::DirMapping& dirObj)
+ {
+ const Zstring relName = dirObj.isEmpty<FreeFileSync::LEFT_SIDE>() ?
+ dirObj.getRelativeName<FreeFileSync::RIGHT_SIDE>() :
+ dirObj.getRelativeName<FreeFileSync::LEFT_SIDE>();
+
+ bool subObjMightMatch = true;
+ dirObj.selectedForSynchronization = filterProc.passDirFilter(relName, &subObjMightMatch);
+
+ if (subObjMightMatch) //use same logic like directory traversing here: evaluate filter in subdirs only if objects could match
+ filterProc.filterAll(dirObj); //process sub-dirs/files
+ else
+ FilterProcess::setActiveStatus(false, dirObj); //exclude all files dirs in subfolders
+ }
+private:
+ const FilterProcess& filterProc;
+};
-bool FilterProcess::matchesDirFilterExcl(const DefaultChar* relDirname) const
+
+void FilterProcess::filterAll(FreeFileSync::HierarchyObject& baseDirectory) const
{
- return matchesFilter(relDirname, filterFolderEx); //process exclude filters
+ //files
+ std::for_each(baseDirectory.subFiles.begin(), baseDirectory.subFiles.end(), FilterData(*this));
+
+ //directories
+ std::for_each(baseDirectory.subDirs.begin(), baseDirectory.subDirs.end(), FilterData(*this));
+
+ //recursion happens in FilterData
}
-void FilterProcess::filterGridData(FreeFileSync::FolderComparison& folderCmp) const
+template <bool include>
+struct SetSelected
{
- //execute filtering...
- for (FolderComparison::iterator j = folderCmp.begin(); j != folderCmp.end(); ++j)
+ void operator()(FreeFileSync::FileSystemObject& fsObj) const
{
- FileComparison& fileCmp = j->fileCmp;
+ fsObj.selectedForSynchronization = include;
+ }
+};
- for (FileComparison::iterator i = fileCmp.begin(); i != fileCmp.end(); ++i)
- {
- const FileDescrLine& fileDescr = i->fileDescrLeft.objType != FileDescrLine::TYPE_NOTHING ?
- i->fileDescrLeft :
- i->fileDescrRight;
-
- if (fileDescr.objType == FileDescrLine::TYPE_FILE)
- {
- if ( !matchesFileFilterIncl(fileDescr.relativeName.c_str()) ||
- matchesFileFilterExcl(fileDescr.relativeName.c_str()))
- {
- i->selectedForSynchronization = false;
- continue;
- }
- }
- else if (fileDescr.objType == FileDescrLine::TYPE_DIRECTORY)
- {
- if ( !matchesDirFilterIncl(fileDescr.relativeName.c_str()) ||
- matchesDirFilterExcl(fileDescr.relativeName.c_str()))
- {
- i->selectedForSynchronization = false;
- continue;
- }
- }
- else
- assert(false);
-
- i->selectedForSynchronization = true;
- }
- }
+template <bool include>
+void inOrExcludeAllRows(FreeFileSync::HierarchyObject& hierObj)
+{
+ //directories
+ std::for_each(hierObj.subDirs.begin(), hierObj.subDirs.end(), SetSelected<include>());
+ //files
+ std::for_each(hierObj.subFiles.begin(), hierObj.subFiles.end(), SetSelected<include>());
+ //recurse into sub-dirs
+ std::for_each(hierObj.subDirs.begin(), hierObj.subDirs.end(), inOrExcludeAllRows<include>);
}
-template <bool includeRows>
-inline
-void inOrExcludeAllRows(FreeFileSync::FolderComparison& folderCmp)
+void FilterProcess::setActiveStatus(bool newStatus, FreeFileSync::FolderComparison& folderCmp)
{
- //remove all filters on folderCmp
- for (FreeFileSync::FolderComparison::iterator j = folderCmp.begin(); j != folderCmp.end(); ++j)
+ if (newStatus)
+ std::for_each(folderCmp.begin(), folderCmp.end(), inOrExcludeAllRows<true>); //include all rows
+ else
+ std::for_each(folderCmp.begin(), folderCmp.end(), inOrExcludeAllRows<false>); //exclude all rows
+}
+
+
+void FilterProcess::setActiveStatus(bool newStatus, FreeFileSync::FileSystemObject& fsObj)
+{
+ fsObj.selectedForSynchronization = newStatus;
+
+ DirMapping* dirObj = dynamic_cast<DirMapping*>(&fsObj);
+ if (dirObj) //process subdirectories also!
{
- FreeFileSync::FileComparison& fileCmp = j->fileCmp;
- for (FreeFileSync::FileComparison::iterator i = fileCmp.begin(); i != fileCmp.end(); ++i)
- i->selectedForSynchronization = includeRows;
+ if (newStatus)
+ inOrExcludeAllRows<true>(*dirObj);
+ else
+ inOrExcludeAllRows<false>(*dirObj);
}
}
-void FilterProcess::includeAllRowsOnGrid(FreeFileSync::FolderComparison& folderCmp)
+const FilterProcess& FilterProcess::nullFilter() //filter equivalent to include '*', exclude ''
+{
+ static FilterProcess output(wxT("*"), wxEmptyString);
+ return output;
+}
+
+
+bool FilterProcess::operator==(const FilterProcess& other) const
+{
+ return filterFileIn == other.filterFileIn &&
+ filterFolderIn == other.filterFolderIn &&
+ filterFileEx == other.filterFileEx &&
+ filterFolderEx == other.filterFolderEx;
+}
+
+
+bool FilterProcess::operator!=(const FilterProcess& other) const
{
- //remove all filters on currentGridData
- inOrExcludeAllRows<true>(folderCmp);
+ return !(*this == other);
}
-void FilterProcess::excludeAllRowsOnGrid(FreeFileSync::FolderComparison& folderCmp)
+bool FilterProcess::operator<(const FilterProcess& other) const
{
- //exclude all rows on currentGridData
- inOrExcludeAllRows<false>(folderCmp);
+ if (filterFileIn != other.filterFileIn)
+ return filterFileIn < other.filterFileIn;
+
+ if (filterFolderIn != other.filterFolderIn)
+ return filterFolderIn < other.filterFolderIn;
+
+ if (filterFileEx != other.filterFileEx)
+ return filterFileEx < other.filterFileEx;
+
+ if (filterFolderEx != other.filterFolderEx)
+ return filterFolderEx < other.filterFolderEx;
+
+ return false; //vectors equal
}
+
diff --git a/library/filter.h b/library/filter.h
index 53e4d9b1..0adf81cd 100644
--- a/library/filter.h
+++ b/library/filter.h
@@ -4,7 +4,7 @@
#include <wx/string.h>
#include "../shared/zstring.h"
#include <set>
-#include "../structures.h"
+#include "../fileHierarchy.h"
namespace FreeFileSync
@@ -14,15 +14,18 @@ namespace FreeFileSync
public:
FilterProcess(const wxString& includeFilter, const wxString& excludeFilter);
- bool matchesFileFilterIncl(const DefaultChar* relFilename) const;
- bool matchesFileFilterExcl(const DefaultChar* relFilename) const;
- bool matchesDirFilterIncl(const DefaultChar* relDirname) const;
- bool matchesDirFilterExcl(const DefaultChar* relDirname) const;
+ bool passFileFilter(const DefaultChar* relFilename) const;
+ bool passDirFilter(const DefaultChar* relDirname, bool* subObjMightMatch) const; //subObjMightMatch: file/dir in subdirectories could(!) match
+ //note: variable is only set if passDirFilter returns false!
+ void filterAll(HierarchyObject& baseDirectory) const; //filter complete data: files and dirs
- void filterGridData(FolderComparison& folderCmp) const;
+ static void setActiveStatus(bool newStatus, FolderComparison& folderCmp); //activate or deactivate all rows
+ static void setActiveStatus(bool newStatus, FileSystemObject& fsObj); //activate or deactivate row
- static void includeAllRowsOnGrid(FolderComparison& folderCmp);
- static void excludeAllRowsOnGrid(FolderComparison& folderCmp);
+ static const FilterProcess& nullFilter(); //filter equivalent to include '*', exclude ''
+ bool operator==(const FilterProcess& other) const;
+ bool operator!=(const FilterProcess& other) const;
+ bool operator<(const FilterProcess& other) const;
private:
std::set<Zstring> filterFileIn;
diff --git a/library/globalFunctions.cpp b/library/globalFunctions.cpp
deleted file mode 100644
index 2f5efab7..00000000
--- a/library/globalFunctions.cpp
+++ /dev/null
@@ -1,180 +0,0 @@
-#include "globalFunctions.h"
-#include <wx/msgdlg.h>
-#include <wx/file.h>
-#include <fstream>
-#include "../structures.h"
-#include <wx/stream.h>
-#include <wx/stopwatch.h>
-
-
-wxString globalFunctions::numberToWxString(const unsigned int number)
-{
- return wxString::Format(wxT("%u"), number);
-}
-
-
-wxString globalFunctions::numberToWxString(const int number)
-{
- return wxString::Format(wxT("%i"), number);
-}
-
-
-wxString globalFunctions::numberToWxString(const float number)
-{
- return wxString::Format(wxT("%f"), number);
-}
-
-
-int globalFunctions::stringToInt(const std::string& number)
-{
- return atoi(number.c_str());
-}
-
-
-long globalFunctions::stringToLong(const std::string& number)
-{
- return atol(number.c_str());
-}
-
-
-double globalFunctions::stringToDouble(const std::string& number)
-{
- return atof(number.c_str());
-}
-
-
-int globalFunctions::wxStringToInt(const wxString& number)
-{
- long result = 0;
- if (number.ToLong(&result))
- return result;
- else
- return 0; //don't throw exceptions here: wxEmptyString shall be interpreted as 0
- //throw RuntimeException(wxString(_("Conversion error:")) + wxT(" wxString -> long"));
-}
-
-
-double globalFunctions::wxStringToDouble(const wxString& number)
-{
- double result = 0;
- if (number.ToDouble(&result))
- return result;
- else
- return 0; //don't throw exceptions here: wxEmptyString shall be interpreted as 0
- //throw RuntimeException(wxString(_("Conversion error:")) + wxT(" wxString -> double"));
-}
-
-
-wxString globalFunctions::includeNumberSeparator(const wxString& number)
-{
- wxString output(number);
- for (int i = output.size() - 3; i > 0; i -= 3)
- output.insert(i, FreeFileSync::THOUSANDS_SEPARATOR);
- return output;
-}
-
-
-int globalFunctions::readInt(std::ifstream& stream)
-{
- int result = 0;
- char* buffer = reinterpret_cast<char*>(&result);
- stream.read(buffer, sizeof(int));
- return result;
-}
-
-
-void globalFunctions::writeInt(std::ofstream& stream, const int number)
-{
- const char* buffer = reinterpret_cast<const char*>(&number);
- stream.write(buffer, sizeof(int));
-}
-
-
-int globalFunctions::readInt(wxInputStream& stream)
-{
- int result = 0;
- char* buffer = reinterpret_cast<char*>(&result);
- stream.Read(buffer, sizeof(int));
- return result;
-}
-
-
-void globalFunctions::writeInt(wxOutputStream& stream, const int number)
-{
- const char* buffer = reinterpret_cast<const char*>(&number);
- stream.Write(buffer, sizeof(int));
-}
-
-
-//############################################################################
-Performance::Performance() :
- resultWasShown(false),
- timer(new wxStopWatch)
-{
- timer->Start();
-}
-
-
-Performance::~Performance()
-{
- if (!resultWasShown)
- showResult();
-}
-
-
-void Performance::showResult()
-{
- resultWasShown = true;
- wxMessageBox(globalFunctions::numberToWxString(unsigned(timer->Time())) + wxT(" ms"));
- timer->Start(); //reset timer
-}
-
-
-//############################################################################
-DebugLog::DebugLog() :
- lineCount(0),
- logFile(NULL)
-{
- logFile = new wxFile;
- logfileName = assembleFileName();
- logFile->Open(logfileName.c_str(), wxFile::write);
-}
-
-
-DebugLog::~DebugLog()
-{
- delete logFile; //automatically closes file handle
-}
-
-
-wxString DebugLog::assembleFileName()
-{
- wxString tmp = wxDateTime::Now().FormatISOTime();
- tmp.Replace(wxT(":"), wxEmptyString);
- return wxString(wxT("DEBUG_")) + wxDateTime::Now().FormatISODate() + wxChar('_') + tmp + wxT(".log");
-}
-
-
-void DebugLog::write(const wxString& logText)
-{
- ++lineCount;
- if (lineCount % 50000 == 0) //prevent logfile from becoming too big
- {
- logFile->Close();
- wxRemoveFile(logfileName);
-
- logfileName = assembleFileName();
- logFile->Open(logfileName.c_str(), wxFile::write);
- }
-
- logFile->Write(wxString(wxT("[")) + wxDateTime::Now().FormatTime() + wxT("] "));
- logFile->Write(logText + wxChar('\n'));
-}
-
-//DebugLog logDebugInfo;
-
-
-wxString getCodeLocation(const wxString file, const int line)
-{
- return wxString(file).AfterLast(FreeFileSync::FILE_NAME_SEPARATOR) + wxT(", LINE ") + globalFunctions::numberToWxString(line) + wxT(" | ");
-}
diff --git a/library/globalFunctions.h b/library/globalFunctions.h
deleted file mode 100644
index 689fa5f1..00000000
--- a/library/globalFunctions.h
+++ /dev/null
@@ -1,162 +0,0 @@
-#ifndef GLOBALFUNCTIONS_H_INCLUDED
-#define GLOBALFUNCTIONS_H_INCLUDED
-
-#include <string>
-#include <algorithm>
-#include <vector>
-#include <set>
-#include <wx/string.h>
-#include <wx/longlong.h>
-#include <memory>
-#include <sstream>
-
-class wxInputStream;
-class wxOutputStream;
-class wxStopWatch;
-
-
-namespace globalFunctions
-{
- inline
- int round(double d) //little rounding function
- {
- return static_cast<int>(d < 0 ? d - .5 : d + .5);
- }
-
- template <class T>
- inline
- T abs(const T& d) //absolute value
- {
- return(d < 0 ? -d : d);
- }
-
- template <class T>
- inline std::string numberToString(const T& number) //convert number to string the C++ way
- {
- std::stringstream ss;
- ss << number;
- return ss.str();
- }
-
- wxString numberToWxString(const unsigned int number); //convert number to wxString
- wxString numberToWxString(const int number); //convert number to wxString
- wxString numberToWxString(const float number); //convert number to wxString
-
- int stringToInt( const std::string& number); //convert String to number
- long stringToLong( const std::string& number); //convert String to number
- double stringToDouble(const std::string& number); //convert String to number
-
- int wxStringToInt( const wxString& number); //convert wxString to number
- double wxStringToDouble(const wxString& number); //convert wxString to number
-
- wxString includeNumberSeparator(const wxString& number);
-
- int readInt(std::ifstream& stream); //read int from file stream
- void writeInt(std::ofstream& stream, const int number); //write int to filestream
-
- int readInt(wxInputStream& stream); //read int from file stream
- void writeInt(wxOutputStream& stream, const int number); //write int to filestream
-
- inline
- wxLongLong convertToSigned(const wxULongLong number)
- {
- return wxLongLong(number.GetHi(), number.GetLo());
- }
-
-
- //Note: the following lines are a performance optimization for deleting elements from a vector. It is incredibly faster to create a new
-//vector and leave specific elements out than to delete row by row and force recopying of most elements for each single deletion (linear vs quadratic runtime)
- template <class T>
- void removeRowsFromVector(const std::set<int>& rowsToRemove, std::vector<T>& grid)
- {
- if (rowsToRemove.size() > 0)
- {
- std::vector<T> temp;
-
- std::set<int>::const_iterator rowToSkipIndex = rowsToRemove.begin();
- int rowToSkip = *rowToSkipIndex;
-
- for (int i = 0; i < int(grid.size()); ++i)
- {
- if (i != rowToSkip)
- temp.push_back(grid[i]);
- else
- {
- ++rowToSkipIndex;
- if (rowToSkipIndex != rowsToRemove.end())
- rowToSkip = *rowToSkipIndex;
- }
- }
- grid.swap(temp);
- }
- }
-
-
- template <class T>
- inline
- void mergeVectors(const std::vector<T>& input, std::vector<T>& changing)
- {
- for (typename std::vector<T>::const_iterator i = input.begin(); i != input.end(); ++i) //nested dependent typename: see Meyers effective C++
- changing.push_back(*i);
- }
-}
-
-
-//############################################################################
-class Performance
-{
-public:
- wxDEPRECATED(Performance()); //generate compiler warnings as a reminder to remove code after measurements
- ~Performance();
- void showResult();
-
-private:
- bool resultWasShown;
- std::auto_ptr<wxStopWatch> timer;
-};
-
-//two macros for quick performance measurements
-#define PERF_START Performance a;
-#define PERF_STOP a.showResult();
-
-
-//############################################################################
-class wxFile;
-class DebugLog
-{
-public:
- wxDEPRECATED(DebugLog());
- ~DebugLog();
- void write(const wxString& logText);
-
-private:
- wxString assembleFileName();
- wxString logfileName;
- int lineCount;
- wxFile* logFile; //logFile.close(); <- not needed
-};
-extern DebugLog logDebugInfo;
-wxString getCodeLocation(const wxString file, const int line);
-
-//small macro for writing debug information into a logfile
-#define WRITE_DEBUG_LOG(x) logDebugInfo.write(getCodeLocation(__TFILE__, __LINE__) + x);
-//speed alternative: wxLogDebug(wxT("text")) + DebugView
-
-
-//############################################################################
-class RuntimeException //Exception class used to notify of general runtime exceptions
-{
-public:
- RuntimeException(const wxString& txt) : errorMessage(txt) {}
-
- wxString show() const
- {
- return errorMessage;
- }
-
-private:
- wxString errorMessage;
-};
-
-
-#endif // GLOBALFUNCTIONS_H_INCLUDED
diff --git a/library/iconBuffer.cpp b/library/iconBuffer.cpp
index f73164e0..cf21104e 100644
--- a/library/iconBuffer.cpp
+++ b/library/iconBuffer.cpp
@@ -1,12 +1,12 @@
#include "iconBuffer.h"
#include <wx/thread.h>
-#include "../shared/globalFunctions.h"
#include <wx/bitmap.h>
#include <wx/msw/wrapwin.h> //includes "windows.h"
#include <wx/msgdlg.h>
#include <wx/icon.h>
#include <map>
#include <queue>
+#include <stdexcept>
using FreeFileSync::IconBuffer;
@@ -53,10 +53,10 @@ WorkerThread::WorkerThread(IconBuffer* iconBuff) :
iconBuffer(iconBuff)
{
if (Create() != wxTHREAD_NO_ERROR)
- throw RuntimeException(wxString(wxT("Error creating icon buffer worker thread!")));
+ throw std::runtime_error("Error creating icon buffer worker thread!");
if (Run() != wxTHREAD_NO_ERROR)
- throw RuntimeException(wxString(wxT("Error starting icon buffer worker thread!")));
+ throw std::runtime_error("Error starting icon buffer worker thread!");
//wait until thread has aquired mutex
bool hasMutex = false;
@@ -115,9 +115,9 @@ wxThread::ExitCode WorkerThread::Entry()
doWork();
}
}
- catch (RuntimeException& e) //exceptions must be catched per thread
+ catch (const std::exception& e) //exceptions must be catched per thread
{
- wxMessageBox(e.show());
+ wxMessageBox(wxString::From8BitData(e.what()), _("An exception occured!"), wxOK | wxICON_ERROR);
return 0;
}
}
diff --git a/library/localization.cpp b/library/localization.cpp
deleted file mode 100644
index 9b5918a8..00000000
--- a/library/localization.cpp
+++ /dev/null
@@ -1,275 +0,0 @@
-#include "localization.h"
-#include <wx/msgdlg.h>
-#include "../structures.h"
-#include "globalFunctions.h"
-#include <fstream>
-#include <set>
-#include <map>
-#include "resources.h"
-
-using FreeFileSync::CustomLocale;
-using FreeFileSync::LocalizationInfo;
-
-//_("Browse") <- dummy string for wxDirPickerCtrl to be recognized by automatic text extraction!
-
-
-const std::vector<FreeFileSync::LocInfoLine>& LocalizationInfo::getMapping()
-{
- static LocalizationInfo instance;
- return instance.locMapping;
-}
-
-
-LocalizationInfo::LocalizationInfo()
-{
- FreeFileSync::LocInfoLine newEntry;
-
- newEntry.languageID = wxLANGUAGE_GERMAN;
- newEntry.languageName = wxT("Deutsch");
- newEntry.languageFile = "Languages/german.lng";
- newEntry.translatorName = wxT("ZenJu");
- newEntry.languageFlag = GlobalResources::getInstance().bitmapGermany;
- locMapping.push_back(newEntry);
-
- newEntry.languageID = wxLANGUAGE_ENGLISH;
- newEntry.languageName = wxT("English");
- newEntry.languageFile = "";
- newEntry.translatorName = wxT("ZenJu");
- newEntry.languageFlag = GlobalResources::getInstance().bitmapEngland;
- locMapping.push_back(newEntry);
-
- newEntry.languageID = wxLANGUAGE_SPANISH;
- newEntry.languageName = wxT("Español");
- newEntry.languageFile = "Languages/spanish.lng";
- newEntry.translatorName = wxT("David Rodríguez");
- newEntry.languageFlag = GlobalResources::getInstance().bitmapSpain;
- locMapping.push_back(newEntry);
-
- newEntry.languageID = wxLANGUAGE_FRENCH;
- newEntry.languageName = wxT("Français");
- newEntry.languageFile = "Languages/french.lng";
- newEntry.translatorName = wxT("Jean-François Hartmann");
- newEntry.languageFlag = GlobalResources::getInstance().bitmapFrance;
- locMapping.push_back(newEntry);
-
- newEntry.languageID = wxLANGUAGE_ITALIAN;
- newEntry.languageName = wxT("Italiano");
- newEntry.languageFile = "Languages/italian.lng";
- newEntry.translatorName = wxT("Emmo");
- newEntry.languageFlag = GlobalResources::getInstance().bitmapItaly;
- locMapping.push_back(newEntry);
-
- newEntry.languageID = wxLANGUAGE_HUNGARIAN;
- newEntry.languageName = wxT("Magyar");
- newEntry.languageFile = "Languages/hungarian.lng";
- newEntry.translatorName = wxT("Demon");
- newEntry.languageFlag = GlobalResources::getInstance().bitmapHungary;
- locMapping.push_back(newEntry);
-
- newEntry.languageID = wxLANGUAGE_DUTCH;
- newEntry.languageName = wxT("Nederlands");
- newEntry.languageFile = "Languages/dutch.lng";
- newEntry.translatorName = wxT("M.D. Vrakking");
- newEntry.languageFlag = GlobalResources::getInstance().bitmapHolland;
- locMapping.push_back(newEntry);
-
- newEntry.languageID = wxLANGUAGE_RUSSIAN;
- newEntry.languageName = wxT("Pусский язык");
- newEntry.languageFile = "Languages/russian.lng";
- newEntry.translatorName = wxT("Svobodniy");
- newEntry.languageFlag = GlobalResources::getInstance().bitmapRussia;
- locMapping.push_back(newEntry);
-
- newEntry.languageID = wxLANGUAGE_POLISH;
- newEntry.languageName = wxT("Polski");
- newEntry.languageFile = "Languages/polish.lng";
- newEntry.translatorName = wxT("Wojtek Pietruszewski");
- newEntry.languageFlag = GlobalResources::getInstance().bitmapPoland;
- locMapping.push_back(newEntry);
-
- newEntry.languageID = wxLANGUAGE_PORTUGUESE;
- newEntry.languageName = wxT("Português");
- newEntry.languageFile = "Languages/portuguese.lng";
- newEntry.translatorName = wxT("QuestMark");
- newEntry.languageFlag = GlobalResources::getInstance().bitmapPortugal;
- locMapping.push_back(newEntry);
-
- newEntry.languageID = wxLANGUAGE_PORTUGUESE_BRAZILIAN;
- newEntry.languageName = wxT("Português do Brasil");
- newEntry.languageFile = "Languages/portuguese_br.lng";
- newEntry.translatorName = wxT("Edison Aranha");
- newEntry.languageFlag = GlobalResources::getInstance().bitmapBrazil;
- locMapping.push_back(newEntry);
-
- newEntry.languageID = wxLANGUAGE_SLOVENIAN;
- newEntry.languageName = wxT("Slovenščina");
- newEntry.languageFile = "Languages/slovenian.lng";
- newEntry.translatorName = wxT("Matej Badalic");
- newEntry.languageFlag = GlobalResources::getInstance().bitmapSlovakia;
- locMapping.push_back(newEntry);
-
- newEntry.languageID = wxLANGUAGE_JAPANESE;
- newEntry.languageName = wxT("日本語");
- newEntry.languageFile = "Languages/japanese.lng";
- newEntry.translatorName = wxT("Tilt");
- newEntry.languageFlag = GlobalResources::getInstance().bitmapJapan;
- locMapping.push_back(newEntry);
-
- newEntry.languageID = wxLANGUAGE_CHINESE_SIMPLIFIED;
- newEntry.languageName = wxT("简体中文");
- newEntry.languageFile = "Languages/chinese_simple.lng";
- newEntry.translatorName = wxT("Misty Wu");
- newEntry.languageFlag = GlobalResources::getInstance().bitmapChina;
- locMapping.push_back(newEntry);
-}
-
-
-
-typedef wxString TextOriginal;
-typedef wxString TextTranslation;
-
-class Translation : public std::map<TextOriginal, TextTranslation> {};
-
-
-CustomLocale::CustomLocale() :
- wxLocale(),
- currentLanguage(wxLANGUAGE_ENGLISH)
-{
- translationDB = new Translation;
-}
-
-
-CustomLocale::~CustomLocale()
-{
- delete translationDB;
-}
-
-
-inline
-void exchangeEscapeChars(wxString& data)
-{
- wxString output;
-
- const wxChar* input = data.c_str();
-
- wxChar value;
- while ((value = *input) != wxChar(0))
- {
- //read backslash
- if (value == wxChar('\\'))
- {
- //read next character
- ++input;
- if ((value = *input) == wxChar(0))
- break;
-
- switch (value)
- {
- case wxChar('\\'):
- output += wxChar('\\');
- break;
- case wxChar('n'):
- output += wxChar('\n');
- break;
- case wxChar('t'):
- output += wxChar('\t');
- break;
- case wxChar('\"'):
- output += wxChar('\"');
- break;
- default:
- output += value;
- }
- }
- else
- output += value;
-
- ++input;
- }
- data = output;
-}
-
-
-void CustomLocale::setLanguage(const int language)
-{
- //default: english
- std::string languageFile;
- currentLanguage = wxLANGUAGE_ENGLISH;
-
- //(try to) retrieve language filename
- for (std::vector<LocInfoLine>::const_iterator i = LocalizationInfo::getMapping().begin(); i != LocalizationInfo::getMapping().end(); ++i)
- if (language == i->languageID)
- {
- languageFile = i->languageFile;
- currentLanguage = i->languageID;
- break;
- }
-
-
- static bool initialized = false; //wxLocale is a static global too!
- if (!initialized)
- {
- initialized = true;
- wxLocale::Init(currentLanguage, wxLOCALE_LOAD_DEFAULT | wxLOCALE_CONV_ENCODING);
- }
-
- //load language file into buffer
- translationDB->clear();
- const int bufferSize = 100000;
- char temp[bufferSize];
- if (!languageFile.empty())
- {
- std::ifstream langFile(languageFile.c_str(), std::ios::binary);
- if (langFile)
- {
- wxString original;
-
- //Delimiter:
- //----------
- //Linux: 0xa \n
- //Mac: 0xd \r
- //Win: 0xd 0xa \r\n <- language files are in Windows format
- for (int rowNumber = 0; langFile.getline(temp, bufferSize, 0xD); ++rowNumber) //specify delimiter explicitly
- {
- langFile.get(); //discard the 0xa character
-
- wxString formattedString = wxString::FromUTF8(temp);
- //wxString formattedString = wxString::From8BitData(temp);
-
- exchangeEscapeChars(formattedString);
-
- if (rowNumber%2 == 0)
- original = formattedString;
- else
- {
- if (!formattedString.empty())
- {
- const wxString translation = formattedString;
- translationDB->insert(std::pair<TextOriginal, TextTranslation>(original, translation));
- }
- }
- }
- langFile.close();
- }
- else
- wxMessageBox(wxString(_("Error reading file:")) + wxT(" \"") + wxString(languageFile.c_str(), wxConvUTF8) + wxT("\""), _("Error"), wxOK | wxICON_ERROR);
- }
- else
- ; //if languageFile is empty texts will be english per default
-
- //these global variables need to be redetermined on language selection
- FreeFileSync::DECIMAL_POINT = _(".");
- FreeFileSync::THOUSANDS_SEPARATOR = _(",");
-}
-
-
-const wxChar* CustomLocale::GetString(const wxChar* szOrigString, const wxChar* szDomain) const
-{
- //look for translation in buffer table
- const Translation::const_iterator i = translationDB->find(szOrigString);
- if (i != translationDB->end())
- return i->second.c_str();
-
- //fallback
- return szOrigString;
-}
diff --git a/library/localization.h b/library/localization.h
deleted file mode 100644
index 7a63fd9c..00000000
--- a/library/localization.h
+++ /dev/null
@@ -1,56 +0,0 @@
-#ifndef MISC_H_INCLUDED
-#define MISC_H_INCLUDED
-
-#include <wx/intl.h>
-#include <wx/bitmap.h>
-#include <vector>
-
-class Translation;
-
-
-namespace FreeFileSync
-{
- struct LocInfoLine
- {
- int languageID;
- wxString languageName;
- std::string languageFile;
- wxString translatorName;
- wxBitmap* languageFlag;
- };
-
-
- class LocalizationInfo
- {
- public:
- static const std::vector<LocInfoLine>& getMapping();
-
- private:
- LocalizationInfo();
-
- std::vector<LocInfoLine> locMapping;
- };
-
-
- class CustomLocale : public wxLocale
- {
- public:
- CustomLocale();
- ~CustomLocale();
-
- void setLanguage(const int language);
-
- int getLanguage() const
- {
- return currentLanguage;
- }
-
- const wxChar* GetString(const wxChar* szOrigString, const wxChar* szDomain = NULL) const;
-
- private:
- Translation* translationDB;
- int currentLanguage;
- };
-}
-
-#endif // MISC_H_INCLUDED
diff --git a/library/multithreading.cpp b/library/multithreading.cpp
index 6c2612f1..899a426b 100644
--- a/library/multithreading.cpp
+++ b/library/multithreading.cpp
@@ -159,3 +159,90 @@ void UpdateWhileExecuting::execute(StatusHandler* statusUpdater)
}
}
+
+
+// ------------------------------------------------------
+// |Pattern: workload queue and multiple worker threads |
+// ------------------------------------------------------
+//typedef std::vector<DirectoryDescrType*> Workload;
+//
+//class ThreadSorting : public wxThread
+//{
+//public:
+// ThreadSorting(wxCriticalSection& syncWorkload, Workload& workload) :
+// wxThread(wxTHREAD_JOINABLE),
+// syncWorkload_(syncWorkload),
+// workload_(workload)
+// {
+// if (Create() != wxTHREAD_NO_ERROR)
+// throw RuntimeException(wxString(wxT("Error creating thread for sorting!")));
+// }
+//
+// ~ThreadSorting() {}
+//
+//
+// ExitCode Entry()
+// {
+// while (true)
+// {
+// DirectoryDescrType* descr = NULL;
+// { //see if there is work to do...
+// wxCriticalSectionLocker dummy(syncWorkload_);
+// if (workload_.empty())
+// return 0;
+// else
+// {
+// descr = workload_.back();
+// workload_.pop_back();
+// }
+// }
+// //do work
+// std::sort(descr->begin(), descr->end());
+// }
+// }
+//
+//private:
+// wxCriticalSection& syncWorkload_;
+// Workload& workload_;
+//};
+//
+//
+//void DirectoryDescrBuffer::preFillBuffers(const std::vector<FolderPairCfg>& fpConfigFormatted)
+//{
+// //assemble workload
+// ...
+//
+// //we use binary search when comparing the directory structures: so sort() first
+// const int CPUCount = wxThread::GetCPUCount();
+// if (CPUCount >= 2) //do it the multithreaded way:
+// {
+// wxCriticalSection syncWorkload;
+//
+// typedef std::vector<boost::shared_ptr<ThreadSorting> > ThreadContainer;
+// ThreadContainer sortThreads;
+// sortThreads.reserve(CPUCount);
+//
+// //start CPUCount worker threads
+// for (size_t i = 0; i < std::min(static_cast<size_t>(CPUCount), workload.size()); ++i)
+// {
+// boost::shared_ptr<ThreadSorting> newWorker(new ThreadSorting(syncWorkload, workload));
+//
+// if (newWorker->Run() != wxTHREAD_NO_ERROR)
+// throw RuntimeException(wxString(wxT("Error starting thread for sorting!")));
+//
+// sortThreads.push_back(newWorker);
+// }
+//
+// //wait until all worker are finished
+// for (ThreadContainer::iterator i = sortThreads.begin(); i != sortThreads.end(); ++i)
+// {
+// if ((*i)->Wait() != 0)
+// throw RuntimeException(wxString(wxT("Error waiting for thread (sorting)!")));
+// }
+// }
+// else //single threaded
+// {
+// for (Workload::iterator i = workload.begin(); i != workload.end(); ++i)
+// std::sort((*i)->begin(), (*i)->end());
+// }
+//}
diff --git a/library/pch.h b/library/pch.h
index 22ed251f..966bc103 100644
--- a/library/pch.h
+++ b/library/pch.h
@@ -33,6 +33,7 @@ do NOT use in release build!
#endif //FFS_LINUX
//other wxWidgets headers
+#include <wx/log.h>
#include <wx/grid.h>
#include <wx/animate.h>
#include <wx/app.h>
@@ -89,6 +90,9 @@ do NOT use in release build!
#include "../shared/tinyxml/tinyxml.h"
#include <sys/stat.h>
+//Boost
+#include <boost/shared_ptr.hpp>
+
#ifdef FFS_WIN
#include <wx/msw/wrapwin.h> //includes "windows.h"
#endif //FFS_WIN
diff --git a/library/processXml.cpp b/library/processXml.cpp
index 43d7fdb9..e49e0b35 100644
--- a/library/processXml.cpp
+++ b/library/processXml.cpp
@@ -26,8 +26,11 @@ public:
void readXmlGlobalSettings(xmlAccess::XmlGlobalSettings& outputCfg);
private:
+ //read alternate configuration (optional) => might point to NULL
+ void readXmlAlternateConfig(const TiXmlElement& folderPair, FolderPairEnh& enhPair);
+
//read basic FreefileSync settings (used by commandline and GUI), return true if ALL values have been retrieved successfully
- void readXmlMainConfig(MainConfiguration& mainCfg, std::vector<FolderPair>& directoryPairs);
+ void readXmlMainConfig(MainConfiguration& mainCfg);
};
@@ -37,8 +40,10 @@ bool writeXmlGuiConfig(const xmlAccess::XmlGuiConfig& outputCfg, TiXmlDocument&
bool writeXmlBatchConfig(const xmlAccess::XmlBatchConfig& outputCfg, TiXmlDocument& doc);
//write global settings
bool writeXmlGlobalSettings(const xmlAccess::XmlGlobalSettings& outputCfg, TiXmlDocument& doc);
+//write alternate configuration (optional) => might point to NULL
+void writeXmlAlternateConfig(const FolderPairEnh& enhPair, TiXmlElement& parent);
//write basic FreefileSync settings (used by commandline and GUI), return true if everything was written successfully
-bool writeXmlMainConfig(const MainConfiguration& mainCfg, const std::vector<FolderPair>& directoryPairs, TiXmlDocument& doc);
+bool writeXmlMainConfig(const MainConfiguration& mainCfg, TiXmlDocument& doc);
void xmlAccess::readGuiConfig(const wxString& filename, xmlAccess::XmlGuiConfig& config)
@@ -215,6 +220,17 @@ bool readXmlElement(const std::string& name, const TiXmlElement* parent , FreeFi
}
+bool readXmlElement(const std::string& name, const TiXmlElement* parent, Zstring& output)
+{
+ wxString dummy;
+ if (!xmlAccess::readXmlElement(name, parent, dummy))
+ return false;
+
+ output = dummy.c_str();
+ return true;
+}
+
+
bool readXmlAttribute(const std::string& name, const TiXmlElement* node, xmlAccess::ColumnTypes& output)
{
int dummy;
@@ -229,59 +245,129 @@ bool readXmlAttribute(const std::string& name, const TiXmlElement* node, xmlAcce
//################################################################################################################
-void FfsXmlParser::readXmlMainConfig(MainConfiguration& mainCfg, std::vector<FolderPair>& directoryPairs)
+void FfsXmlParser::readXmlAlternateConfig(const TiXmlElement& folderPair, FolderPairEnh& enhPair)
+{
+ //read folder pairs
+ readXmlElementLogging("Left", &folderPair, enhPair.leftDirectory);
+ readXmlElementLogging("Right", &folderPair, enhPair.rightDirectory);
+
+
+//###########################################################
+ //alternate sync configuration
+ const TiXmlElement* syncConfig = TiXmlHandleConst(&folderPair).FirstChild("AlternateSyncConfig").ToElement();
+ if (syncConfig)
+ {
+ AlternateSyncConfig* altSyncCfg = new AlternateSyncConfig;
+ enhPair.altSyncConfig.reset(altSyncCfg);
+
+ const TiXmlElement* syncDirections = TiXmlHandleConst(syncConfig).FirstChild("Synchronization").FirstChild("Directions").ToElement();
+
+ //read sync configuration
+ readXmlElementLogging("LeftOnly", syncDirections, altSyncCfg->syncConfiguration.exLeftSideOnly);
+ readXmlElementLogging("RightOnly", syncDirections, altSyncCfg->syncConfiguration.exRightSideOnly);
+ readXmlElementLogging("LeftNewer", syncDirections, altSyncCfg->syncConfiguration.leftNewer);
+ readXmlElementLogging("RightNewer", syncDirections, altSyncCfg->syncConfiguration.rightNewer);
+ readXmlElementLogging("Different", syncDirections, altSyncCfg->syncConfiguration.different);
+ readXmlElementLogging("Conflict", syncDirections, altSyncCfg->syncConfiguration.conflict);
+
+ const TiXmlElement* miscSettings = TiXmlHandleConst(&folderPair).FirstChild("AlternateSyncConfig").FirstChild("Miscellaneous").ToElement();
+ readXmlElementLogging("DeletionPolicy", miscSettings, altSyncCfg->handleDeletion);
+ readXmlElementLogging("CustomDeletionFolder", miscSettings, altSyncCfg->customDeletionDirectory);
+ }
+
+//###########################################################
+ //alternate filter configuration
+ const TiXmlElement* filterCfg = TiXmlHandleConst(&folderPair).FirstChild("AlternateFilter").ToElement();
+ if (filterCfg)
+ {
+ AlternateFilter* altFilterCfg = new AlternateFilter;
+ enhPair.altFilter.reset(altFilterCfg);
+
+ //read filter settings
+ readXmlElementLogging("Include", filterCfg, altFilterCfg->includeFilter);
+ readXmlElementLogging("Exclude", filterCfg, altFilterCfg->excludeFilter);
+ }
+}
+
+
+void FfsXmlParser::readXmlMainConfig(MainConfiguration& mainCfg)
{
TiXmlHandleConst hRoot(root); //custom const handle: TiXml API seems broken in this regard
+//###########################################################
const TiXmlElement* cmpSettings = hRoot.FirstChild("MainConfig").FirstChild("Comparison").ToElement();
- const TiXmlElement* syncConfig = hRoot.FirstChild("MainConfig").FirstChild("Synchronization").FirstChild("Directions").ToElement();
- const TiXmlElement* miscSettings = hRoot.FirstChild("MainConfig").FirstChild("Miscellaneous").ToElement();
- const TiXmlElement* filter = TiXmlHandleConst(miscSettings).FirstChild("Filter").ToElement();
-//###########################################################
//read compare variant
readXmlElementLogging("Variant", cmpSettings, mainCfg.compareVar);
- //read folder pair(s)
- const TiXmlElement* folderPair = TiXmlHandleConst(cmpSettings).FirstChild("Folders").FirstChild("Pair").ToElement();
-
- //read folder pairs
- directoryPairs.clear();
- while (folderPair)
- {
- wxString temp;
- wxString temp2;
- readXmlElementLogging("Left", folderPair, temp);
- readXmlElementLogging("Right", folderPair, temp2);
- directoryPairs.push_back(FolderPair(temp.c_str(), temp2.c_str()));
+ //max. allowed file time deviation
+ readXmlElementLogging("FileTimeTolerance", cmpSettings, mainCfg.hidden.fileTimeTolerance);
- folderPair = folderPair->NextSiblingElement();
- }
+ //traverse into symbolic links (to folders)
+ readXmlElementLogging("TraverseDirectorySymlinks", cmpSettings, mainCfg.hidden.traverseDirectorySymlinks);
//###########################################################
+ const TiXmlElement* syncDirections = hRoot.FirstChild("MainConfig").FirstChild("Synchronization").FirstChild("Directions").ToElement();
+
//read sync configuration
- readXmlElementLogging("LeftOnly", syncConfig, mainCfg.syncConfiguration.exLeftSideOnly);
- readXmlElementLogging("RightOnly", syncConfig, mainCfg.syncConfiguration.exRightSideOnly);
- readXmlElementLogging("LeftNewer", syncConfig, mainCfg.syncConfiguration.leftNewer);
- readXmlElementLogging("RightNewer", syncConfig, mainCfg.syncConfiguration.rightNewer);
- readXmlElementLogging("Different", syncConfig, mainCfg.syncConfiguration.different);
+ readXmlElementLogging("LeftOnly", syncDirections, mainCfg.syncConfiguration.exLeftSideOnly);
+ readXmlElementLogging("RightOnly", syncDirections, mainCfg.syncConfiguration.exRightSideOnly);
+ readXmlElementLogging("LeftNewer", syncDirections, mainCfg.syncConfiguration.leftNewer);
+ readXmlElementLogging("RightNewer", syncDirections, mainCfg.syncConfiguration.rightNewer);
+ readXmlElementLogging("Different", syncDirections, mainCfg.syncConfiguration.different);
+ readXmlElementLogging("Conflict", syncDirections, mainCfg.syncConfiguration.conflict);
+
+//###########################################################
+ const TiXmlElement* syncConfig = hRoot.FirstChild("MainConfig").FirstChild("Synchronization").ToElement();
+
+ //copy symbolic links to files
+ readXmlElementLogging("CopyFileSymlinks", syncConfig, mainCfg.hidden.copyFileSymlinks);
+
+ //verify file copying
+ readXmlElementLogging("VerifyCopiedFiles", syncConfig, mainCfg.hidden.verifyFileCopy);
//###########################################################
+ const TiXmlElement* miscSettings = hRoot.FirstChild("MainConfig").FirstChild("Miscellaneous").ToElement();
+
+ //misc
+ readXmlElementLogging("DeletionPolicy", miscSettings, mainCfg.handleDeletion);
+ readXmlElementLogging("CustomDeletionFolder", miscSettings, mainCfg.customDeletionDirectory);
+//###########################################################
+ const TiXmlElement* filter = TiXmlHandleConst(miscSettings).FirstChild("Filter").ToElement();
+
//read filter settings
readXmlElementLogging("Active", filter, mainCfg.filterIsActive);
readXmlElementLogging("Include", filter, mainCfg.includeFilter);
readXmlElementLogging("Exclude", filter, mainCfg.excludeFilter);
+
//###########################################################
- //other
- readXmlElementLogging("DeletionPolicy", miscSettings, mainCfg.handleDeletion);
- readXmlElementLogging("CustomDeletionFolder", miscSettings, mainCfg.customDeletionDirectory);
+ const TiXmlElement* pairs = hRoot.FirstChild("MainConfig").FirstChild("FolderPairs").FirstChild("Pair").ToElement();
+
+ //read main folder pair
+ if (pairs)
+ {
+ readXmlElementLogging("Left", pairs, mainCfg.mainFolderPair.leftDirectory);
+ readXmlElementLogging("Right", pairs, mainCfg.mainFolderPair.rightDirectory);
+ pairs = pairs->NextSiblingElement();
+ }
+
+ //read additional folder pairs
+ mainCfg.additionalPairs.clear();
+ while (pairs)
+ {
+ FolderPairEnh newPair;
+ readXmlAlternateConfig(*pairs, newPair);
+ mainCfg.additionalPairs.push_back(newPair);
+
+ pairs = pairs->NextSiblingElement();
+ }
}
void FfsXmlParser::readXmlGuiConfig(xmlAccess::XmlGuiConfig& outputCfg)
{
//read main config
- readXmlMainConfig(outputCfg.mainCfg, outputCfg.directoryPairs);
+ readXmlMainConfig(outputCfg.mainCfg);
//read GUI specific config data
const TiXmlElement* guiConfig = TiXmlHandleConst(root).FirstChild("GuiConfig").ToElement();
@@ -299,7 +385,7 @@ void FfsXmlParser::readXmlGuiConfig(xmlAccess::XmlGuiConfig& outputCfg)
void FfsXmlParser::readXmlBatchConfig(xmlAccess::XmlBatchConfig& outputCfg)
{
//read main config
- readXmlMainConfig(outputCfg.mainCfg, outputCfg.directoryPairs);
+ readXmlMainConfig(outputCfg.mainCfg);
//read batch specific config
const TiXmlElement* batchConfig = TiXmlHandleConst(root).FirstChild("BatchConfig").ToElement();
@@ -318,32 +404,28 @@ void FfsXmlParser::readXmlGlobalSettings(xmlAccess::XmlGlobalSettings& outputCfg
//try to read program language setting
readXmlElementLogging("Language", global, outputCfg.programLanguage);
- //max. allowed file time deviation
- readXmlElementLogging("FileTimeTolerance", global, outputCfg.fileTimeTolerance);
-
//ignore +/- 1 hour due to DST change
readXmlElementLogging("IgnoreOneHourDifference", global, outputCfg.ignoreOneHourDiff);
- //traverse into symbolic links (to folders)
- readXmlElementLogging("TraverseDirectorySymlinks", global, outputCfg.traverseDirectorySymlinks);
-
- //copy symbolic links to files
- readXmlElementLogging("CopyFileSymlinks", global, outputCfg.copyFileSymlinks);
-
//last update check
readXmlElementLogging("LastCheckForUpdates", global, outputCfg.lastUpdateCheck);
- const TiXmlElement* warnings = TiXmlHandleConst(root).FirstChild("Shared").FirstChild("Warnings").ToElement();
+ const TiXmlElement* optionalDialogs = TiXmlHandleConst(root).FirstChild("Shared").FirstChild("ShowOptionalDialogs").ToElement();
//folder dependency check
- readXmlElementLogging("CheckForDependentFolders", warnings, outputCfg.warnings.warningDependentFolders);
+ readXmlElementLogging("CheckForDependentFolders", optionalDialogs, outputCfg.optDialogs.warningDependentFolders);
//significant difference check
- readXmlElementLogging("CheckForSignificantDifference", warnings, outputCfg.warnings.warningSignificantDifference);
+ readXmlElementLogging("CheckForSignificantDifference", optionalDialogs, outputCfg.optDialogs.warningSignificantDifference);
//check free disk space
- readXmlElementLogging("CheckForFreeDiskSpace", warnings, outputCfg.warnings.warningNotEnoughDiskSpace);
+ readXmlElementLogging("CheckForFreeDiskSpace", optionalDialogs, outputCfg.optDialogs.warningNotEnoughDiskSpace);
//check for unresolved conflicts
- readXmlElementLogging("CheckForUnresolvedConflicts", warnings, outputCfg.warnings.warningUnresolvedConflicts);
+ readXmlElementLogging("CheckForUnresolvedConflicts", optionalDialogs, outputCfg.optDialogs.warningUnresolvedConflicts);
+
+ readXmlElementLogging("PopupOnConfigChange", optionalDialogs, outputCfg.optDialogs.popupOnConfigChange);
+
+ readXmlElementLogging("SummaryBeforeSync", optionalDialogs, outputCfg.optDialogs.showSummaryBeforeSync);
+
//gui specific global settings (optional)
const TiXmlElement* mainWindow = TiXmlHandleConst(root).FirstChild("Gui").FirstChild("Windows").FirstChild("Main").ToElement();
@@ -359,8 +441,6 @@ void FfsXmlParser::readXmlGlobalSettings(xmlAccess::XmlGlobalSettings& outputCfg
readXmlElementLogging("ManualDeletionUseRecycler", mainWindow, outputCfg.gui.useRecyclerForManualDeletion);
readXmlElementLogging("ShowFileIconsLeft", mainWindow, outputCfg.gui.showFileIconsLeft);
readXmlElementLogging("ShowFileIconsRight", mainWindow, outputCfg.gui.showFileIconsRight);
- readXmlElementLogging("PopupOnConfigChange", mainWindow, outputCfg.gui.popupOnConfigChange);
- readXmlElementLogging("SummaryBeforeSync", mainWindow, outputCfg.gui.showSummaryBeforeSync);
//###########################################################
//read column attributes
@@ -416,10 +496,24 @@ void FfsXmlParser::readXmlGlobalSettings(xmlAccess::XmlGlobalSettings& outputCfg
const TiXmlElement* gui = TiXmlHandleConst(root).FirstChild("Gui").ToElement();
- //commandline for file manager integration
- readXmlElementLogging("FileManager", gui, outputCfg.gui.commandLineFileManager);
-
-
+ //external applications
+ const TiXmlElement* extApps = TiXmlHandleConst(gui).FirstChild("ExternalApplications").FirstChild("Commandline").ToElement();
+ if (extApps)
+ {
+ outputCfg.gui.externelApplications.clear();
+ while (extApps)
+ {
+ wxString description;
+ wxString cmdLine;
+
+ readXmlAttributeLogging("Description", extApps, description);
+ const char* text = extApps->GetText();
+ if (text) cmdLine = wxString::FromUTF8(text); //read commandline
+ outputCfg.gui.externelApplications.push_back(std::make_pair(description, cmdLine));
+
+ extApps = extApps->NextSiblingElement();
+ }
+ }
//load config file history
const TiXmlElement* cfgHistory = TiXmlHandleConst(root).FirstChild("Gui").FirstChild("ConfigHistory").ToElement();
@@ -449,7 +543,7 @@ void addXmlElement(const std::string& name, const CompareVariant variant, TiXmlE
}
-void addXmlElement(TiXmlElement* parent, const std::string& name, const SyncDirectionCfg value)
+void addXmlElement(const std::string& name, const SyncDirectionCfg value, TiXmlElement* parent)
{
switch (value)
{
@@ -500,14 +594,80 @@ void addXmlElement(const std::string& name, const FreeFileSync::DeletionPolicy v
}
+void addXmlElement(const std::string& name, const Zstring& value, TiXmlElement* parent)
+{
+ xmlAccess::addXmlElement(name, wxString(value.c_str()), parent);
+}
+
+
void addXmlAttribute(const std::string& name, const xmlAccess::ColumnTypes value, TiXmlElement* node)
{
xmlAccess::addXmlAttribute(name, static_cast<int>(value), node);
}
-bool writeXmlMainConfig(const MainConfiguration& mainCfg, const std::vector<FolderPair>& directoryPairs, TiXmlDocument& doc)
+void writeXmlAlternateConfig(const FolderPairEnh& enhPair, TiXmlElement& parent)
{
+ //write folder pairs
+ TiXmlElement* newfolderPair = new TiXmlElement("Pair");
+ parent.LinkEndChild(newfolderPair);
+
+ addXmlElement("Left", enhPair.leftDirectory, newfolderPair);
+ addXmlElement("Right", enhPair.rightDirectory, newfolderPair);
+
+
+ //alternate sync configuration
+ const AlternateSyncConfig* altSyncConfig = enhPair.altSyncConfig.get();
+ if (altSyncConfig)
+ {
+ TiXmlElement* syncCfg = new TiXmlElement("AlternateSyncConfig");
+ newfolderPair->LinkEndChild(syncCfg);
+
+ TiXmlElement* syncSettings = new TiXmlElement("Synchronization");
+ syncCfg->LinkEndChild(syncSettings);
+
+ //write sync configuration
+ TiXmlElement* syncDirections = new TiXmlElement("Directions");
+ syncSettings->LinkEndChild(syncDirections);
+
+ ::addXmlElement("LeftOnly", altSyncConfig->syncConfiguration.exLeftSideOnly, syncDirections);
+ ::addXmlElement("RightOnly", altSyncConfig->syncConfiguration.exRightSideOnly, syncDirections);
+ ::addXmlElement("LeftNewer", altSyncConfig->syncConfiguration.leftNewer, syncDirections);
+ ::addXmlElement("RightNewer", altSyncConfig->syncConfiguration.rightNewer, syncDirections);
+ ::addXmlElement("Different", altSyncConfig->syncConfiguration.different, syncDirections);
+ ::addXmlElement("Conflict", altSyncConfig->syncConfiguration.conflict, syncDirections);
+
+
+ TiXmlElement* miscSettings = new TiXmlElement("Miscellaneous");
+ syncCfg->LinkEndChild(miscSettings);
+
+ //misc
+ addXmlElement("DeletionPolicy", altSyncConfig->handleDeletion, miscSettings);
+ xmlAccess::addXmlElement("CustomDeletionFolder", altSyncConfig->customDeletionDirectory, miscSettings);
+ }
+
+//###########################################################
+ //alternate filter configuration
+ const AlternateFilter* altFilter = enhPair.altFilter.get();
+ if (altFilter)
+ {
+ TiXmlElement* filterCfg = new TiXmlElement("AlternateFilter");
+ newfolderPair->LinkEndChild(filterCfg);
+
+ //write filter settings
+ xmlAccess::addXmlElement("Include", altFilter->includeFilter, filterCfg);
+ xmlAccess::addXmlElement("Exclude", altFilter->excludeFilter, filterCfg);
+ }
+}
+
+
+bool writeXmlMainConfig(const MainConfiguration& mainCfg, TiXmlDocument& doc)
+{
+ //reset hidden settings: we don't want implicit behaviour! Hidden settings are loaded but changes are not saved!
+ MainConfiguration mainCfgLocal = mainCfg;
+ mainCfgLocal.hidden = HiddenSettings();
+
+
TiXmlElement* root = doc.RootElement();
if (!root) return false;
@@ -519,35 +679,35 @@ bool writeXmlMainConfig(const MainConfiguration& mainCfg, const std::vector<Fold
settings->LinkEndChild(cmpSettings);
//write compare algorithm
- ::addXmlElement("Variant", mainCfg.compareVar, cmpSettings);
+ ::addXmlElement("Variant", mainCfgLocal.compareVar, cmpSettings);
- //write folder pair(s)
- TiXmlElement* folders = new TiXmlElement("Folders");
- cmpSettings->LinkEndChild(folders);
-
- //write folder pairs
- for (std::vector<FolderPair>::const_iterator i = directoryPairs.begin(); i != directoryPairs.end(); ++i)
- {
- TiXmlElement* folderPair = new TiXmlElement("Pair");
- folders->LinkEndChild(folderPair);
+ //max. allowed file time deviation
+ xmlAccess::addXmlElement("FileTimeTolerance", mainCfgLocal.hidden.fileTimeTolerance, cmpSettings);
- xmlAccess::addXmlElement("Left", wxString(i->leftDirectory.c_str()), folderPair);
- xmlAccess::addXmlElement("Right", wxString(i->rightDirectory.c_str()), folderPair);
- }
+ //traverse into symbolic links (to folders)
+ xmlAccess::addXmlElement("TraverseDirectorySymlinks", mainCfgLocal.hidden.traverseDirectorySymlinks, cmpSettings);
//###########################################################
TiXmlElement* syncSettings = new TiXmlElement("Synchronization");
settings->LinkEndChild(syncSettings);
//write sync configuration
- TiXmlElement* syncConfig = new TiXmlElement("Directions");
- syncSettings->LinkEndChild(syncConfig);
+ TiXmlElement* syncDirections = new TiXmlElement("Directions");
+ syncSettings->LinkEndChild(syncDirections);
+
+ ::addXmlElement("LeftOnly", mainCfgLocal.syncConfiguration.exLeftSideOnly, syncDirections);
+ ::addXmlElement("RightOnly", mainCfgLocal.syncConfiguration.exRightSideOnly, syncDirections);
+ ::addXmlElement("LeftNewer", mainCfgLocal.syncConfiguration.leftNewer, syncDirections);
+ ::addXmlElement("RightNewer", mainCfgLocal.syncConfiguration.rightNewer, syncDirections);
+ ::addXmlElement("Different", mainCfgLocal.syncConfiguration.different, syncDirections);
+ ::addXmlElement("Conflict", mainCfgLocal.syncConfiguration.conflict, syncDirections);
+
+//###########################################################
+ //copy symbolic links to files
+ xmlAccess::addXmlElement("CopyFileSymlinks", mainCfgLocal.hidden.copyFileSymlinks, syncSettings);
- ::addXmlElement(syncConfig, "LeftOnly", mainCfg.syncConfiguration.exLeftSideOnly);
- ::addXmlElement(syncConfig, "RightOnly", mainCfg.syncConfiguration.exRightSideOnly);
- ::addXmlElement(syncConfig, "LeftNewer", mainCfg.syncConfiguration.leftNewer);
- ::addXmlElement(syncConfig, "RightNewer", mainCfg.syncConfiguration.rightNewer);
- ::addXmlElement(syncConfig, "Different", mainCfg.syncConfiguration.different);
+ //verify file copying
+ xmlAccess::addXmlElement("VerifyCopiedFiles", mainCfgLocal.hidden.verifyFileCopy, syncSettings);
//###########################################################
TiXmlElement* miscSettings = new TiXmlElement("Miscellaneous");
@@ -557,14 +717,28 @@ bool writeXmlMainConfig(const MainConfiguration& mainCfg, const std::vector<Fold
TiXmlElement* filter = new TiXmlElement("Filter");
miscSettings->LinkEndChild(filter);
- xmlAccess::addXmlElement("Active", mainCfg.filterIsActive, filter);
- xmlAccess::addXmlElement("Include", mainCfg.includeFilter, filter);
- xmlAccess::addXmlElement("Exclude", mainCfg.excludeFilter, filter);
+ xmlAccess::addXmlElement("Active", mainCfgLocal.filterIsActive, filter);
+ xmlAccess::addXmlElement("Include", mainCfgLocal.includeFilter, filter);
+ xmlAccess::addXmlElement("Exclude", mainCfgLocal.excludeFilter, filter);
//other
- addXmlElement("DeletionPolicy", mainCfg.handleDeletion, miscSettings);
- xmlAccess::addXmlElement("CustomDeletionFolder", mainCfg.customDeletionDirectory, miscSettings);
+ addXmlElement("DeletionPolicy", mainCfgLocal.handleDeletion, miscSettings);
+ xmlAccess::addXmlElement("CustomDeletionFolder", mainCfgLocal.customDeletionDirectory, miscSettings);
+
//###########################################################
+ TiXmlElement* pairs = new TiXmlElement("FolderPairs");
+ settings->LinkEndChild(pairs);
+
+ //write main folder pair
+ TiXmlElement* mainPair = new TiXmlElement("Pair");
+ pairs->LinkEndChild(mainPair);
+
+ addXmlElement("Left", mainCfgLocal.mainFolderPair.leftDirectory, mainPair);
+ addXmlElement("Right", mainCfgLocal.mainFolderPair.rightDirectory, mainPair);
+
+ //write additional folder pairs
+ for (std::vector<FolderPairEnh>::const_iterator i = mainCfgLocal.additionalPairs.begin(); i != mainCfgLocal.additionalPairs.end(); ++i)
+ writeXmlAlternateConfig(*i, *pairs);
return true;
}
@@ -573,7 +747,7 @@ bool writeXmlMainConfig(const MainConfiguration& mainCfg, const std::vector<Fold
bool writeXmlGuiConfig(const xmlAccess::XmlGuiConfig& inputCfg, TiXmlDocument& doc)
{
//write main config
- if (!writeXmlMainConfig(inputCfg.mainCfg, inputCfg.directoryPairs, doc))
+ if (!writeXmlMainConfig(inputCfg.mainCfg, doc))
return false;
//write GUI specific config
@@ -596,7 +770,7 @@ bool writeXmlGuiConfig(const xmlAccess::XmlGuiConfig& inputCfg, TiXmlDocument& d
bool writeXmlBatchConfig(const xmlAccess::XmlBatchConfig& inputCfg, TiXmlDocument& doc)
{
//write main config
- if (!writeXmlMainConfig(inputCfg.mainCfg, inputCfg.directoryPairs, doc))
+ if (!writeXmlMainConfig(inputCfg.mainCfg, doc))
return false;
//write GUI specific config
@@ -627,36 +801,32 @@ bool writeXmlGlobalSettings(const xmlAccess::XmlGlobalSettings& inputCfg, TiXmlD
//program language
xmlAccess::addXmlElement("Language", inputCfg.programLanguage, global);
- //max. allowed file time deviation
- xmlAccess::addXmlElement("FileTimeTolerance", inputCfg.fileTimeTolerance, global);
-
//ignore +/- 1 hour due to DST change
xmlAccess::addXmlElement("IgnoreOneHourDifference", inputCfg.ignoreOneHourDiff, global);
- //traverse into symbolic links (to folders)
- xmlAccess::addXmlElement("TraverseDirectorySymlinks", inputCfg.traverseDirectorySymlinks, global);
-
- //copy symbolic links to files
- xmlAccess::addXmlElement("CopyFileSymlinks", inputCfg.copyFileSymlinks, global);
-
//last update check
xmlAccess::addXmlElement("LastCheckForUpdates", inputCfg.lastUpdateCheck, global);
- //warnings
- TiXmlElement* warnings = new TiXmlElement("Warnings");
- global->LinkEndChild(warnings);
+
+ //optional dialogs
+ TiXmlElement* optionalDialogs = new TiXmlElement("ShowOptionalDialogs");
+ global->LinkEndChild(optionalDialogs);
//warning: dependent folders
- xmlAccess::addXmlElement("CheckForDependentFolders", inputCfg.warnings.warningDependentFolders, warnings);
+ xmlAccess::addXmlElement("CheckForDependentFolders", inputCfg.optDialogs.warningDependentFolders, optionalDialogs);
//significant difference check
- xmlAccess::addXmlElement("CheckForSignificantDifference", inputCfg.warnings.warningSignificantDifference, warnings);
+ xmlAccess::addXmlElement("CheckForSignificantDifference", inputCfg.optDialogs.warningSignificantDifference, optionalDialogs);
//check free disk space
- xmlAccess::addXmlElement("CheckForFreeDiskSpace", inputCfg.warnings.warningNotEnoughDiskSpace, warnings);
+ xmlAccess::addXmlElement("CheckForFreeDiskSpace", inputCfg.optDialogs.warningNotEnoughDiskSpace, optionalDialogs);
//check for unresolved conflicts
- xmlAccess::addXmlElement("CheckForUnresolvedConflicts", inputCfg.warnings.warningUnresolvedConflicts, warnings);
+ xmlAccess::addXmlElement("CheckForUnresolvedConflicts", inputCfg.optDialogs.warningUnresolvedConflicts, optionalDialogs);
+
+ xmlAccess::addXmlElement("PopupOnConfigChange", inputCfg.optDialogs.popupOnConfigChange, optionalDialogs);
+
+ xmlAccess::addXmlElement("SummaryBeforeSync", inputCfg.optDialogs.showSummaryBeforeSync, optionalDialogs);
//###################################################################
@@ -683,8 +853,6 @@ bool writeXmlGlobalSettings(const xmlAccess::XmlGlobalSettings& inputCfg, TiXmlD
xmlAccess::addXmlElement("ManualDeletionUseRecycler", inputCfg.gui.useRecyclerForManualDeletion, mainWindow);
xmlAccess::addXmlElement("ShowFileIconsLeft", inputCfg.gui.showFileIconsLeft, mainWindow);
xmlAccess::addXmlElement("ShowFileIconsRight", inputCfg.gui.showFileIconsRight, mainWindow);
- xmlAccess::addXmlElement("PopupOnConfigChange", inputCfg.gui.popupOnConfigChange, mainWindow);
- xmlAccess::addXmlElement("SummaryBeforeSync", inputCfg.gui.showSummaryBeforeSync, mainWindow);
//write column attributes
TiXmlElement* leftColumn = new TiXmlElement("LeftColumns");
@@ -737,8 +905,18 @@ bool writeXmlGlobalSettings(const xmlAccess::XmlGlobalSettings& inputCfg, TiXmlD
xmlAccess::addXmlElement("SelectedTabBottomLeft", inputCfg.gui.selectedTabBottomLeft, mainWindow);
- //commandline for file manager integration
- xmlAccess::addXmlElement("FileManager", inputCfg.gui.commandLineFileManager, gui);
+ //external applications
+ TiXmlElement* extApp = new TiXmlElement("ExternalApplications");
+ gui->LinkEndChild(extApp);
+
+ for (xmlAccess::ExternalApps::const_iterator i = inputCfg.gui.externelApplications.begin(); i != inputCfg.gui.externelApplications.end(); ++i)
+ {
+ TiXmlElement* newEntry = new TiXmlElement("Commandline");
+ extApp->LinkEndChild(newEntry);
+
+ xmlAccess::addXmlAttribute("Description", i->first, newEntry);
+ newEntry->LinkEndChild(new TiXmlText(i->second.ToUTF8())); //commandline
+ }
//write config file history
TiXmlElement* cfgHistory = new TiXmlElement("ConfigHistory");
@@ -769,10 +947,47 @@ bool xmlAccess::recycleBinAvailable()
}
-void xmlAccess::WarningMessages::resetWarnings()
+void xmlAccess::OptionalDialogs::resetDialogs()
{
warningDependentFolders = true;
warningSignificantDifference = true;
warningNotEnoughDiskSpace = true;
warningUnresolvedConflicts = true;
+ popupOnConfigChange = true;
+ showSummaryBeforeSync = true;
+}
+
+
+xmlAccess::XmlGuiConfig convertBatchToGui(const xmlAccess::XmlBatchConfig& batchCfg)
+{
+ xmlAccess::XmlGuiConfig output;
+ output.mainCfg = batchCfg.mainCfg;
+ return output;
+}
+
+
+void xmlAccess::readGuiOrBatchConfig(const wxString& filename, XmlGuiConfig& config) //throw (xmlAccess::XmlError);
+{
+ if (xmlAccess::getXmlType(filename) != xmlAccess::XML_BATCH_CONFIG)
+ {
+ xmlAccess::readGuiConfig(filename, config);
+ return;
+ }
+
+ //convert batch config to gui config
+ xmlAccess::XmlBatchConfig batchCfg;
+ try
+ {
+ xmlAccess::readBatchConfig(filename, batchCfg); //throw (xmlAccess::XmlError);
+ }
+ catch (const xmlAccess::XmlError& e)
+ {
+ if (e.getSeverity() != xmlAccess::XmlError::WARNING)
+ throw;
+
+ config = convertBatchToGui(batchCfg); //do work despite parsing errors, then re-throw
+ throw; //
+ }
+
+ config = convertBatchToGui(batchCfg);
}
diff --git a/library/processXml.h b/library/processXml.h
index 9fad5cd7..868b859b 100644
--- a/library/processXml.h
+++ b/library/processXml.h
@@ -32,8 +32,12 @@ namespace xmlAccess
};
typedef std::vector<ColumnAttrib> ColumnAttributes;
-//---------------------------------------------------------------------
+ typedef wxString Description;
+ typedef wxString Commandline;
+ typedef std::vector<std::pair<Description, Commandline> > ExternalApps;
+
+//---------------------------------------------------------------------
struct XmlGuiConfig
{
XmlGuiConfig() :
@@ -42,7 +46,6 @@ namespace xmlAccess
syncPreviewEnabled(true) {} //initialize values
FreeFileSync::MainConfiguration mainCfg;
- std::vector<FreeFileSync::FolderPair> directoryPairs;
bool hideFilteredElements;
bool ignoreErrors; //reaction on error situation during synchronization
@@ -51,7 +54,6 @@ namespace xmlAccess
bool operator==(const XmlGuiConfig& other) const
{
return mainCfg == other.mainCfg &&
- directoryPairs == other.directoryPairs &&
hideFilteredElements == other.hideFilteredElements &&
ignoreErrors == other.ignoreErrors &&
syncPreviewEnabled == other.syncPreviewEnabled;
@@ -69,7 +71,6 @@ namespace xmlAccess
XmlBatchConfig() : silent(false), handleError(ON_ERROR_POPUP) {}
FreeFileSync::MainConfiguration mainCfg;
- std::vector<FreeFileSync::FolderPair> directoryPairs;
bool silent;
OnError handleError; //reaction on error situation during synchronization
@@ -80,19 +81,21 @@ namespace xmlAccess
bool recycleBinAvailable();
- struct WarningMessages
+ struct OptionalDialogs
{
- WarningMessages()
+ OptionalDialogs()
{
- resetWarnings();
+ resetDialogs();
}
- void resetWarnings();
+ void resetDialogs();
bool warningDependentFolders;
bool warningSignificantDifference;
bool warningNotEnoughDiskSpace;
bool warningUnresolvedConflicts;
+ bool popupOnConfigChange;
+ bool showSummaryBeforeSync;
};
@@ -102,20 +105,14 @@ namespace xmlAccess
//Shared (GUI/BATCH) settings
XmlGlobalSettings() :
programLanguage(retrieveSystemLanguage()),
- fileTimeTolerance(2), //default 2s: FAT vs NTFS
- ignoreOneHourDiff(true),
- traverseDirectorySymlinks(false),
- copyFileSymlinks(true),
+ ignoreOneHourDiff(false),
lastUpdateCheck(0) {}
int programLanguage;
- unsigned int fileTimeTolerance; //max. allowed file time deviation
bool ignoreOneHourDiff; //ignore +/- 1 hour due to DST change
- bool traverseDirectorySymlinks;
- bool copyFileSymlinks; //copy symbolic link instead of target file
- long lastUpdateCheck; //time of last update check
+ long lastUpdateCheck; //time of last update check
- WarningMessages warnings;
+ OptionalDialogs optDialogs;
//---------------------------------------------------------------------
struct _Gui
@@ -128,11 +125,6 @@ namespace xmlAccess
isMaximized(false),
autoAdjustColumnsLeft(false),
autoAdjustColumnsRight(false),
-#ifdef FFS_WIN
- commandLineFileManager(wxT("explorer /select, %name")),
-#elif defined FFS_LINUX
- commandLineFileManager(wxT("konqueror \"%dir\"")),
-#endif
cfgHistoryMax(10),
folderHistLeftMax(12),
folderHistRightMax(12),
@@ -140,9 +132,14 @@ namespace xmlAccess
deleteOnBothSides(false),
useRecyclerForManualDeletion(recycleBinAvailable()), //enable if OS supports it; else user will have to activate first and then get an error message
showFileIconsLeft(true),
- showFileIconsRight(true),
- popupOnConfigChange(true),
- showSummaryBeforeSync(true) {}
+ showFileIconsRight(true)
+ {
+#ifdef FFS_WIN
+ externelApplications.push_back(std::make_pair(wxT("Open with Explorer"), wxT("explorer /select, %name")));
+#elif defined FFS_LINUX
+ externelApplications.push_back(std::make_pair(wxT("Open with Konqueror"), wxT("konqueror \"%dir\"")));
+#endif
+ }
int widthNotMaximized;
int heightNotMaximized;
@@ -156,7 +153,7 @@ namespace xmlAccess
bool autoAdjustColumnsLeft;
bool autoAdjustColumnsRight;
- wxString commandLineFileManager;
+ ExternalApps externelApplications;
std::vector<wxString> cfgFileHistory;
unsigned int cfgHistoryMax;
@@ -173,8 +170,6 @@ namespace xmlAccess
bool useRecyclerForManualDeletion;
bool showFileIconsLeft;
bool showFileIconsRight;
- bool popupOnConfigChange;
- bool showSummaryBeforeSync;
} gui;
//---------------------------------------------------------------------
@@ -210,6 +205,8 @@ namespace xmlAccess
void readBatchConfig(const wxString& filename, XmlBatchConfig& config); //throw (xmlAccess::XmlError);
void readGlobalSettings( XmlGlobalSettings& config); //throw (xmlAccess::XmlError);
+ void readGuiOrBatchConfig(const wxString& filename, XmlGuiConfig& config); //throw (xmlAccess::XmlError);
+
void writeGuiConfig( const XmlGuiConfig& outputCfg, const wxString& filename); //throw (xmlAccess::XmlError);
void writeBatchConfig( const XmlBatchConfig& outputCfg, const wxString& filename); //throw (xmlAccess::XmlError);
void writeGlobalSettings(const XmlGlobalSettings& outputCfg); //throw (xmlAccess::XmlError);
diff --git a/library/resources.cpp b/library/resources.cpp
index 3e7b5a4e..1b9ce12d 100644
--- a/library/resources.cpp
+++ b/library/resources.cpp
@@ -4,8 +4,8 @@
#include <wx/image.h>
#include <wx/icon.h>
#include <wx/mstream.h>
-#include "../shared/globalFunctions.h"
-//#include "../shared/systemFunctions.h"
+#include "../shared/systemConstants.h"
+#include <memory>
#include "../shared/standardPaths.h"
@@ -58,6 +58,8 @@ GlobalResources::GlobalResources()
bitmapResource[wxT("equal.png")] = (bitmapEqual = new wxBitmap(wxNullBitmap));
bitmapResource[wxT("equalAct.png")] = (bitmapEqualAct = new wxBitmap(wxNullBitmap));
bitmapResource[wxT("equalDeact.png")] = (bitmapEqualDeact = new wxBitmap(wxNullBitmap));
+ bitmapResource[wxT("conflict.png")] = (bitmapConflict = new wxBitmap(wxNullBitmap));
+ bitmapResource[wxT("conflictGrey.png")] = (bitmapConflictGrey = new wxBitmap(wxNullBitmap));
bitmapResource[wxT("conflictAct.png")] = (bitmapConflictAct = new wxBitmap(wxNullBitmap));
bitmapResource[wxT("conflictDeact.png")] = (bitmapConflictDeact = new wxBitmap(wxNullBitmap));
bitmapResource[wxT("include.png")] = (bitmapInclude = new wxBitmap(wxNullBitmap));
@@ -65,6 +67,7 @@ GlobalResources::GlobalResources()
bitmapResource[wxT("filter active.png")] = (bitmapFilterOn = new wxBitmap(wxNullBitmap));
bitmapResource[wxT("filter not active.png")] = (bitmapFilterOff = new wxBitmap(wxNullBitmap));
bitmapResource[wxT("filter_small.png")] = (bitmapFilterSmall = new wxBitmap(wxNullBitmap));
+ bitmapResource[wxT("filterSmallGrey.png")] = (bitmapFilterSmallGrey = 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));
@@ -72,6 +75,9 @@ GlobalResources::GlobalResources()
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("saveSmall.png")] = (bitmapSaveSmall = new wxBitmap(wxNullBitmap));
+ bitmapResource[wxT("loadSmall.png")] = (bitmapLoadSmall = new wxBitmap(wxNullBitmap));
+ bitmapResource[wxT("newSmall.png")] = (bitmapNewSmall = 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));
@@ -108,6 +114,8 @@ GlobalResources::GlobalResources()
bitmapResource[wxT("recycler.png")] = (bitmapRecycler = new wxBitmap(wxNullBitmap));
bitmapResource[wxT("shift.png")] = (bitmapShift = new wxBitmap(wxNullBitmap));
bitmapResource[wxT("syncConfig.png")] = (bitmapSyncCfg = new wxBitmap(wxNullBitmap));
+ bitmapResource[wxT("syncConfigSmall.png")] = (bitmapSyncCfgSmall = new wxBitmap(wxNullBitmap));
+ bitmapResource[wxT("syncConfigSmallGrey.png")] = (bitmapSyncCfgSmallGrey = new wxBitmap(wxNullBitmap));
bitmapResource[wxT("cmpConfig.png")] = (bitmapCmpCfg = new wxBitmap(wxNullBitmap));
bitmapResource[wxT("syncPreview.png")] = (bitmapPreview = new wxBitmap(wxNullBitmap));
bitmapResource[wxT("syncPreviewDisabl.png")] = (bitmapPreviewDisabled = new wxBitmap(wxNullBitmap));
@@ -119,6 +127,7 @@ GlobalResources::GlobalResources()
bitmapResource[wxT("france.png")] = (bitmapFrance = new wxBitmap(wxNullBitmap));
bitmapResource[wxT("germany.png")] = (bitmapGermany = new wxBitmap(wxNullBitmap));
bitmapResource[wxT("hungary.png")] = (bitmapHungary = new wxBitmap(wxNullBitmap));
+ bitmapResource[wxT("taiwan.png")] = (bitmapTaiwan = new wxBitmap(wxNullBitmap));
bitmapResource[wxT("italy.png")] = (bitmapItaly = new wxBitmap(wxNullBitmap));
bitmapResource[wxT("japan.png")] = (bitmapJapan = new wxBitmap(wxNullBitmap));
bitmapResource[wxT("poland.png")] = (bitmapPoland = new wxBitmap(wxNullBitmap));
diff --git a/library/resources.h b/library/resources.h
index 5f4e51f0..804dea7a 100644
--- a/library/resources.h
+++ b/library/resources.h
@@ -54,6 +54,8 @@ public:
wxBitmap* bitmapDifferent;
wxBitmap* bitmapDifferentAct;
wxBitmap* bitmapDifferentDeact;
+ wxBitmap* bitmapConflict;
+ wxBitmap* bitmapConflictGrey;
wxBitmap* bitmapConflictAct;
wxBitmap* bitmapConflictDeact;
wxBitmap* bitmapInclude;
@@ -61,6 +63,7 @@ public:
wxBitmap* bitmapFilterOn;
wxBitmap* bitmapFilterOff;
wxBitmap* bitmapFilterSmall;
+ wxBitmap* bitmapFilterSmallGrey;
wxBitmap* bitmapWarning;
wxBitmap* bitmapWarningSmall;
wxBitmap* bitmapError;
@@ -68,6 +71,9 @@ public:
wxBitmap* bitmapSmallDown;
wxBitmap* bitmapSave;
wxBitmap* bitmapLoad;
+ wxBitmap* bitmapSaveSmall;
+ wxBitmap* bitmapLoadSmall;
+ wxBitmap* bitmapNewSmall;
wxBitmap* bitmapFFS;
wxBitmap* bitmapFFSPaused;
wxBitmap* bitmapDeleteFile;
@@ -104,6 +110,8 @@ public:
wxBitmap* bitmapRecycler;
wxBitmap* bitmapShift;
wxBitmap* bitmapSyncCfg;
+ wxBitmap* bitmapSyncCfgSmall;
+ wxBitmap* bitmapSyncCfgSmallGrey;
wxBitmap* bitmapCmpCfg;
wxBitmap* bitmapPreview;
wxBitmap* bitmapPreviewDisabled;
@@ -115,6 +123,7 @@ public:
wxBitmap* bitmapFrance;
wxBitmap* bitmapGermany;
wxBitmap* bitmapHungary;
+ wxBitmap* bitmapTaiwan;
wxBitmap* bitmapItaly;
wxBitmap* bitmapJapan;
wxBitmap* bitmapPoland;
diff --git a/library/shadow.cpp b/library/shadow.cpp
deleted file mode 100644
index c353bcb2..00000000
--- a/library/shadow.cpp
+++ /dev/null
@@ -1,148 +0,0 @@
-#include "shadow.h"
-#include <wx/msw/wrapwin.h> //includes "windows.h"
-#include <wx/intl.h>
-#include "../structures.h"
-
-using FreeFileSync::ShadowCopy;
-
-
-class ShadowlDllHandler //dynamically load windows API functions
-{
- typedef bool (*CreateShadowCopyFct)( //volumeName must end with "\", while shadowVolName does not end with "\"
- const wchar_t* volumeName,
- wchar_t* shadowVolName,
- unsigned int shadowBufferLen,
- void** backupHandle,
- wchar_t* errorMessage,
- unsigned int errorBufferLen);
-
- typedef void (*ReleaseShadowCopyFct)(void* backupHandle);
-
-public:
- static const ShadowlDllHandler& getInstance()
- {
- static ShadowlDllHandler instance;
- return instance;
- }
-
- CreateShadowCopyFct createShadowCopy;
- ReleaseShadowCopyFct releaseShadowCopy;
-
-private:
- ShadowlDllHandler() :
- createShadowCopy(NULL),
- releaseShadowCopy(NULL),
- hShadow(NULL)
- {
- //get a handle to the DLL module containing the required functionality
- hShadow = ::LoadLibrary(L"Shadow.dll");
- if (hShadow)
- {
- createShadowCopy = reinterpret_cast<CreateShadowCopyFct>(::GetProcAddress(hShadow, "createShadowCopy"));
- releaseShadowCopy = reinterpret_cast<ReleaseShadowCopyFct>(::GetProcAddress(hShadow, "releaseShadowCopy"));
- }
- }
-
- ~ShadowlDllHandler()
- {
- if (hShadow) ::FreeLibrary(hShadow);
- }
-
- HINSTANCE hShadow;
-};
-
-
-ShadowCopy::ShadowCopy() :
- backupHandle(NULL) {}
-
-
-ShadowCopy::~ShadowCopy()
-{
- if (backupHandle != NULL)
- ShadowlDllHandler::getInstance().releaseShadowCopy(backupHandle);
-}
-
-
-bool ShadowCopy::isOkay()
-{
- //check that all functions could be loaded
- return ShadowlDllHandler::getInstance().createShadowCopy != NULL &&
- ShadowlDllHandler::getInstance().releaseShadowCopy != NULL;
-}
-
-
-Zstring ShadowCopy::makeShadowCopy(const Zstring& inputFile) throw(FreeFileSync::FileError)
-{
- //check if shadow copy dll was loaded correctly
- if (!isOkay())
- throw FileError(Zstring(_("Error copying locked file %x!")).Replace(wxT("%x"), Zstring(wxT("\"")) + inputFile + wxT("\"")) + wxT("\n\n") +
- _("Error starting Volume Shadow Copy Service!") + wxT("\n") +
- _("Please copy the appropriate \"Shadow.dll\" (located in \"Shadow.zip\" archive) into the FreeFileSync installation directory to enable this feature."));
-
-
- wchar_t volumeNameRaw[1000];
-
- if (!GetVolumePathName(inputFile.c_str(), //__in LPCTSTR lpszFileName,
- volumeNameRaw, //__out LPTSTR lpszVolumePathName,
- 1000)) //__in DWORD cchBufferLength
- throw FileError(Zstring(_("Error copying locked file %x!")).Replace(wxT("%x"), Zstring(wxT("\"")) + inputFile + wxT("\"")) + wxT("\n\n") +
- _("Could not determine volume name for file:") + wxT("\n\"") + inputFile + wxT("\""));
-
- Zstring volumeNameFormatted = volumeNameRaw;
- if (!volumeNameFormatted.EndsWith(FreeFileSync::FILE_NAME_SEPARATOR))
- volumeNameFormatted += FreeFileSync::FILE_NAME_SEPARATOR;
-
- if (volumeNameFormatted != realVolumeLast)
- {
- //release old shadow copy
- if (backupHandle != NULL)
- {
- ShadowlDllHandler::getInstance().releaseShadowCopy(backupHandle);
- backupHandle = NULL;
- }
- realVolumeLast.clear(); //...if next call fails...
- shadowVolumeLast.clear(); //...if next call fails...
-
- //start shadow volume copy service:
- wchar_t shadowVolName[1000];
- void* backupHandleTmp = NULL;
- wchar_t errorMessage[1000];
-
- if (!ShadowlDllHandler::getInstance().createShadowCopy(
- volumeNameFormatted.c_str(),
- shadowVolName,
- 1000,
- &backupHandleTmp,
- errorMessage,
- 1000))
- throw FileError(Zstring(_("Error copying locked file %x!")).Replace(wxT("%x"), Zstring(wxT("\"")) + inputFile + wxT("\"")) + wxT("\n\n") +
- _("Error starting Volume Shadow Copy Service!") + wxT("\n") +
- wxT("(") + errorMessage + wxT(")"));
-
- realVolumeLast = volumeNameFormatted;
- shadowVolumeLast = Zstring(shadowVolName) + FreeFileSync::FILE_NAME_SEPARATOR; //shadowVolName NEVER has a trailing backslash
- backupHandle = backupHandleTmp;
- }
-
- const size_t pos = inputFile.find(volumeNameFormatted);
- if (pos == Zstring::npos)
- {
- Zstring msg = _("Volume name %x not part of filename %y!");
- msg.Replace(wxT("%x"), Zstring(wxT("\"")) + volumeNameFormatted + wxT("\""), false);
- msg.Replace(wxT("%y"), Zstring(wxT("\"")) + inputFile + wxT("\""), false);
- throw FileError(Zstring(_("Error copying locked file %x!")).Replace(wxT("%x"), Zstring(wxT("\"")) + inputFile + wxT("\"")) + wxT("\n\n") +
- msg);
- }
-
- //return filename alias on shadow copy volume
- return shadowVolumeLast + Zstring(inputFile.c_str() + pos + volumeNameFormatted.length());
-}
-
-
-
-
-
-
-
-
-
diff --git a/library/shadow.h b/library/shadow.h
deleted file mode 100644
index ded9d746..00000000
--- a/library/shadow.h
+++ /dev/null
@@ -1,31 +0,0 @@
-#ifndef SHADOW_H_INCLUDED
-#define SHADOW_H_INCLUDED
-
-#ifndef FFS_WIN
-#warning //this header should be used in the windows build only!
-#endif
-
-#include "zstring.h"
-#include "fileError.h"
-
-
-namespace FreeFileSync
-{
- class ShadowCopy //buffer access to Windows Volume Shadow Copy Service
- {
- public:
- ShadowCopy();
- ~ShadowCopy();
-
- Zstring makeShadowCopy(const Zstring& inputFile) throw(FileError); //returns filename on shadow copy
-
- private:
- bool isOkay();
-
- Zstring realVolumeLast; //buffer last volume name
- Zstring shadowVolumeLast; //buffer last created shadow volume
- void* backupHandle;
- };
-}
-
-#endif // SHADOW_H_INCLUDED
diff --git a/library/statistics.cpp b/library/statistics.cpp
index f317c114..08bb5dee 100644
--- a/library/statistics.cpp
+++ b/library/statistics.cpp
@@ -124,14 +124,14 @@ void Statistics::addMeasurement(const int objectsCurrent, const double dataCurre
//remove all records earlier than "currentTime - windowSize"
const long newBegin = newEntry.time - windowMax;
- while (measurements.size() > 0 && measurements.front().time < newBegin)
+ while (!measurements.empty() && measurements.front().time < newBegin)
measurements.pop_front();
}
wxString Statistics::getRemainingTime() const
{
- if (measurements.size() > 0)
+ if (!measurements.empty())
{
//find start of records "window"
const record backElement = measurements.back();
@@ -158,7 +158,7 @@ wxString Statistics::getRemainingTime() const
wxString Statistics::getBytesPerSecond() const
{
- if (measurements.size() > 0)
+ if (!measurements.empty())
{
//find start of records "window"
const long frontTime = measurements.back().time - windowSizeBPS;
diff --git a/library/statistics.h b/library/statistics.h
index f0eafad8..fe247b47 100644
--- a/library/statistics.h
+++ b/library/statistics.h
@@ -61,8 +61,8 @@ private:
struct record
{
int objects;
- double data;
- long time;
+ double data; //unit: bytes
+ long time; //unit: milliseconds
};
std::list<record> measurements;
diff --git a/library/zstring.cpp b/library/zstring.cpp
deleted file mode 100644
index b26ee451..00000000
--- a/library/zstring.cpp
+++ /dev/null
@@ -1,390 +0,0 @@
-#include "zstring.h"
-#include "globalFunctions.h"
-
-#ifdef FFS_WIN
-#include <wx/msw/wrapwin.h> //includes "windows.h"
-#endif //FFS_WIN
-
-
-
-#ifdef __WXDEBUG__
-AllocationCount::~AllocationCount()
-{
- if (count != 0)
-#ifdef FFS_WIN
- MessageBox(NULL, wxT("Fatal Error! Allocation problem with Zstring! (No problem if it occurs while Unit testing only!)"), wxString::Format(wxT("%i"), count), 0);
-#else
- std::abort();
-#endif
-}
-
-
-AllocationCount& AllocationCount::getInstance()
-{
- static AllocationCount global;
- return global;
-}
-#endif
-
-
-#ifdef FFS_WIN
-int FreeFileSync::compareStringsWin32(const wchar_t* a, const wchar_t* b, const int aCount, const int bCount)
-{
- //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; //convert to C-style string compare result
-}
-#endif
-
-
-Zstring& Zstring::Replace(const DefaultChar* old, const DefaultChar* replacement, bool replaceAll)
-{
- const size_t oldLen = defaultLength(old);
- const size_t replacementLen = defaultLength(replacement);
-
- 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
-
- // stop now?
- if (!replaceAll)
- break;
- }
- return *this;
-}
-
-
-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;
-
- default:
- if (*string != ch)
- return false;
- else
- ++string;
- }
- }
- return *string == 0;
-}
-
-
-bool Zstring::Matches(const DefaultChar* mask) const
-{
- return matchesHelper(c_str(), mask);
-}
-
-
-bool Zstring::Matches(const DefaultChar* name, const DefaultChar* mask)
-{
- return matchesHelper(name, 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 strin
- {
- descr->length = newLength;
- data[newLength] = DefaultChar(0);
- }
- }
- }
- 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;
- allocate(thisLen, 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);
-
- const size_t oldLen = length();
- if (oldLen == 0)
- {
- assert(pos1 == 0 && n1 == 0);
- return *this = Zstring(str, n2);
- }
-
- const size_t newLen = oldLen - n1 + n2;
- if (newLen > oldLen || descr->refCount > 1)
- { //allocate a new string
- StringDescriptor* newDescr;
- DefaultChar* newData;
- allocate(newLen, newDescr, newData);
-
- //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: case "n2 == 0" is handled implicitly
- {
- memcpy(data + pos1, str, n2 * sizeof(DefaultChar));
- if (newLen < oldLen)
- {
- 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();
- assert(oldLength <= getCapacityToAllocate(capacityNeeded));
-
- StringDescriptor* newDescr;
- DefaultChar* newData;
- allocate(capacityNeeded, newDescr, newData);
- newDescr->length = oldLength;
-
- if (oldLength)
- {
- 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();
- data = (DefaultChar*)(descr + 1);
- descr->capacity = newCapacity;
- }
-}
diff --git a/library/zstring.h b/library/zstring.h
deleted file mode 100644
index bca50862..00000000
--- a/library/zstring.h
+++ /dev/null
@@ -1,712 +0,0 @@
-/***************************************************************
- * Purpose: High performance string class
- * Author: ZenJu (zhnmju123@gmx.de)
- * Created: Jan. 2009
- **************************************************************/
-
-#ifndef ZSTRING_H_INCLUDED
-#define ZSTRING_H_INCLUDED
-
-#include <cstring>
-#include <cctype>
-#include <assert.h>
-#include <new>
-
-
-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, const int aCount = -1, const int bCount = -1);
-#endif //FFS_WIN
-}
-
-
-#ifdef ZSTRING_CHAR
-typedef char DefaultChar; //use char strings
-#elif defined ZSTRING_WIDE_CHAR
-typedef wchar_t DefaultChar; //use wide character strings
-#endif
-
-class Zsubstr;
-
-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();
-
- operator const DefaultChar*() const; //implicit conversion to C string
-
- //wxWidgets functions
- bool StartsWith(const DefaultChar* begin) const;
- bool StartsWith(const Zstring& begin) const;
- bool EndsWith(const DefaultChar* end) 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;
- Zstring& 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;
- static bool Matches(const DefaultChar* name, const DefaultChar* mask);
- 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; //allocate new string
- Zsubstr zsubstr(size_t pos = 0) const; //use ref-counting!
- bool empty() const;
- void clear();
- 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);
- size_t size() const;
-
- Zstring& operator=(const Zstring& source);
- Zstring& operator=(const DefaultChar* source);
-
- bool operator == (const Zstring& other) const;
- bool operator == (const DefaultChar* other) const;
- bool operator < (const Zstring& other) const;
- bool operator < (const DefaultChar* other) const;
- bool operator != (const Zstring& other) const;
- bool operator != (const DefaultChar* other) const;
-
- DefaultChar operator[](const size_t pos) const;
-
- Zstring& operator+=(const Zstring& other);
- Zstring& operator+=(const DefaultChar* other);
- Zstring& operator+=(DefaultChar ch);
-
- const Zstring operator+(const Zstring& string2) const;
- const Zstring operator+(const DefaultChar* string2) const;
- 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() const; //support for reference-counting
- void decRef(); //
- void copyBeforeWrite(const size_t capacityNeeded); //and copy-on-write
-
- struct StringDescriptor
- {
- mutable unsigned int refCount;
- size_t length;
- size_t capacity; //allocated length without null-termination
- };
- static void allocate(const size_t newLength, StringDescriptor*& newDescr, DefaultChar*& newData);
-
- StringDescriptor* descr;
- DefaultChar* data;
-};
-
-
-class Zsubstr //ref-counted substring of a Zstring
-{
-public:
- Zsubstr();
- Zsubstr(const Zstring& ref, const size_t pos);
-
- const DefaultChar* c_str() const;
- size_t length() const;
- bool StartsWith(const Zstring& begin) const;
- size_t findFromEnd(const DefaultChar ch) const;
-
-private:
- Zstring m_ref;
- const DefaultChar* m_data;
- size_t m_length;
-};
-
-
-//#######################################################################################
-//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 ((unsigned char)ch < 128) && isspace((unsigned char)ch) != 0;
-}
-
-inline
-char defaultToLower(const char ch)
-{
- return tolower((unsigned char)ch); //caution: although tolower() has int as input parameter it expects unsigned chars!
-}
-
-#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
-const wchar_t* defaultStrFind(const wchar_t* str1, const wchar_t* str2)
-{
- return wcsstr(str1, str2);
-}
-
-inline
-const 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__
-class AllocationCount //small test for memory leaks in Zstring
-{
-public:
- void inc()
- {
- ++count;
- }
-
- void dec()
- {
- --count;
- }
-
- static AllocationCount& getInstance();
-
-private:
- AllocationCount() : count(0) {}
- ~AllocationCount();
-
- int count;
-};
-#endif
-
-
-inline
-size_t getCapacityToAllocate(const size_t length)
-{
- return (length + (19 - length % 16)); //allocate some additional length to speed up concatenation
-}
-
-
-inline
-void Zstring::allocate(const size_t newLength,
- StringDescriptor*& newDescr,
- DefaultChar*& newData)
-{ //allocate and set data for new string
- const size_t newCapacity = getCapacityToAllocate(newLength);
- assert(newCapacity);
-
- newDescr = static_cast<StringDescriptor*>(operator new [] (sizeof(StringDescriptor) + (newCapacity + 1) * sizeof(DefaultChar)));
- newData = reinterpret_cast<DefaultChar*>(newDescr + 1);
-
- newDescr->refCount = 1;
- newDescr->length = newLength;
- newDescr->capacity = newCapacity;
-
-#ifdef __WXDEBUG__
- AllocationCount::getInstance().inc(); //test Zstring for memory leaks
-#endif
-}
-
-
-inline
-Zstring::Zstring()
-{
- //static (dummy) empty Zstring
-#ifdef ZSTRING_CHAR
- static Zstring emptyString("");
-#elif defined ZSTRING_WIDE_CHAR
- static Zstring emptyString(L"");
-#endif
-
- emptyString.incRef();
- descr = emptyString.descr;
- data = emptyString.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()
-{
- decRef();
-}
-
-
-inline
-void Zstring::initAndCopy(const DefaultChar* source, size_t length)
-{
- allocate(length, descr, data);
- memcpy(data, source, length * sizeof(DefaultChar));
- data[length] = 0;
-}
-
-
-inline
-void Zstring::incRef() const
-{
- assert(descr);
- ++descr->refCount;
-}
-
-
-inline
-void Zstring::decRef()
-{
- assert(descr && descr->refCount >= 1); //descr points to the begin of the allocated memory block
- if (--descr->refCount == 0)
- {
- operator delete [] (descr); //this must NEVER be changed!! E.g. Trim() relies on descr being start of allocated memory block
- descr = NULL;
-#ifdef __WXDEBUG__
- AllocationCount::getInstance().dec(); //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(), length(), other.length()); //way faster than wxString::CmpNoCase()!!
-}
-#endif
-
-
-inline
-Zstring::operator const DefaultChar*() const
-{
- return c_str();
-}
-
-
-inline
-Zstring& Zstring::operator=(const Zstring& source)
-{
- source.incRef(); //implicitly handle case "this == &source" and avoid this check
- decRef(); //
- descr = source.descr;
- data = source.data;
-
- 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 DefaultChar end) const
-{
- const size_t thisLength = length();
- if (thisLength < 1)
- return false;
- return *(c_str() + thisLength - 1) == end;
-}
-
-
-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
-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 length() != other.length() ? false : defaultCompare(c_str(), other.c_str()) == 0;
-}
-
-
-inline
-bool Zstring::operator == (const DefaultChar* other) const
-{
- return defaultCompare(c_str(), other) == 0; //overload using strcmp(char*, char*) should be fastest!
-}
-
-
-inline
-bool Zstring::operator < (const Zstring& other) const
-{
- return defaultCompare(c_str(), other.c_str()) < 0;
-}
-
-
-inline
-bool Zstring::operator < (const DefaultChar* other) const
-{
- return defaultCompare(c_str(), other) < 0; //overload using strcmp(char*, char*) should be fastest!
-}
-
-
-inline
-bool Zstring::operator != (const Zstring& other) const
-{
- return length() != other.length() ? true: defaultCompare(c_str(), other.c_str()) != 0;
-}
-
-
-inline
-bool Zstring::operator != (const DefaultChar* other) const
-{
- return defaultCompare(c_str(), other) != 0; //overload using strcmp(char*, char*) should be fastest!
-}
-
-
-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
-size_t Zstring::size() const
-{
- return descr->length;
-}
-
-
-inline
-const DefaultChar* Zstring::c_str() const
-{
- return data;
-}
-
-
-inline
-bool Zstring::empty() const
-{
- return descr->length == 0;
-}
-
-
-inline
-void Zstring::clear()
-{
- *this = Zstring();
-}
-
-
-inline
-DefaultChar Zstring::operator[](const size_t pos) const
-{
- assert(pos < length());
- return data[pos];
-}
-
-
-inline
-const Zstring Zstring::operator+(const Zstring& string2) const
-{
- return Zstring(*this)+=string2;
-}
-
-
-inline
-const Zstring Zstring::operator+(const DefaultChar* string2) const
-{
- return Zstring(*this)+=string2;
-}
-
-
-inline
-const Zstring Zstring::operator+(const DefaultChar ch) const
-{
- return Zstring(*this)+=ch;
-}
-
-//##################### Zsubstr #############################
-inline
-Zsubstr Zstring::zsubstr(size_t pos) const
-{
- assert(pos <= length());
- return Zsubstr(*this, pos); //return reference counted string
-}
-
-
-inline
-Zsubstr::Zsubstr()
-{
- m_data = m_ref.c_str();
- m_length = 0;
-}
-
-
-inline
-Zsubstr::Zsubstr(const Zstring& ref, const size_t pos) :
- m_ref(ref),
- m_data(ref.c_str() + pos),
- m_length(ref.length() - pos) {}
-
-
-inline
-const DefaultChar* Zsubstr::c_str() const
-{
- return m_data;
-}
-
-
-inline
-size_t Zsubstr::length() const
-{
- return m_length;
-}
-
-
-inline
-bool Zsubstr::StartsWith(const Zstring& begin) const
-{
- const size_t beginLength = begin.length();
- if (length() < beginLength)
- return false;
-
- return defaultCompare(m_data, begin.c_str(), beginLength) == 0;
-}
-
-
-inline
-size_t Zsubstr::findFromEnd(const DefaultChar ch) const
-{
- size_t pos = length();
- while (--pos != static_cast<size_t>(-1))
- {
- if (m_data[pos] == ch)
- return pos;
- }
- return Zstring::npos;
-}
-
-
-
-#endif // ZSTRING_H_INCLUDED
bgstack15