summaryrefslogtreecommitdiff
path: root/library
diff options
context:
space:
mode:
authorDaniel Wilhelm <daniel@wili.li>2014-04-18 16:57:45 +0200
committerDaniel Wilhelm <daniel@wili.li>2014-04-18 16:57:45 +0200
commit2a3ebac62eb6dd88122c0f447ea90ce368373d3a (patch)
treefae5c18deaecfb6f39d4d66dd3de8ce730b2025b /library
parent1.17 (diff)
downloadFreeFileSync-2a3ebac62eb6dd88122c0f447ea90ce368373d3a.tar.gz
FreeFileSync-2a3ebac62eb6dd88122c0f447ea90ce368373d3a.tar.bz2
FreeFileSync-2a3ebac62eb6dd88122c0f447ea90ce368373d3a.zip
1.18
Diffstat (limited to 'library')
-rw-r--r--library/CustomGrid.cpp1006
-rw-r--r--library/CustomGrid.h153
-rw-r--r--library/FreeFileSync.xpm2
-rw-r--r--library/fileHandling.cpp94
-rw-r--r--library/fileHandling.h5
-rw-r--r--library/filter.cpp241
-rw-r--r--library/filter.h15
-rw-r--r--library/globalFunctions.cpp18
-rw-r--r--library/globalFunctions.h43
-rw-r--r--library/localization.cpp (renamed from library/misc.cpp)53
-rw-r--r--library/localization.h32
-rw-r--r--library/misc.h54
-rw-r--r--library/multithreading.cpp1
-rw-r--r--library/multithreading.h2
-rw-r--r--library/processXml.cpp113
-rw-r--r--library/processXml.h62
-rw-r--r--library/resources.cpp24
-rw-r--r--library/resources.h15
-rw-r--r--library/sorting.h325
-rw-r--r--library/statistics.cpp320
-rw-r--r--library/statistics.h67
-rw-r--r--library/statusHandler.h8
-rw-r--r--library/zstring.cpp28
-rw-r--r--library/zstring.h84
24 files changed, 1668 insertions, 1097 deletions
diff --git a/library/CustomGrid.cpp b/library/CustomGrid.cpp
index b8737343..faa75b2d 100644
--- a/library/CustomGrid.cpp
+++ b/library/CustomGrid.cpp
@@ -5,39 +5,58 @@
#include "../algorithm.h"
#include "resources.h"
#include <typeinfo>
+#include "../ui/gridView.h"
#ifdef FFS_WIN
#include <wx/icon.h>
#include <wx/msw/wrapwin.h> //includes "windows.h"
-#endif // FFS_WIN
+
+#elif defined FFS_LINUX
+#include <gtk/gtk.h>
+#endif
+
+
+using namespace FreeFileSync;
const unsigned int MIN_ROW_COUNT = 15;
//class containing pure grid data: basically the same as wxGridStringTable, but adds cell formatting
+
+/*
+class hierarchy:
+ CustomGridTable
+ /|\
+ ________________|________________
+ | |
+ CustomGridTableRim |
+ /|\ |
+ __________|__________ |
+ | | |
+CustomGridTableLeft CustomGridTableRight CustomGridTableMiddle
+*/
+
class CustomGridTable : public wxGridTableBase
{
public:
- CustomGridTable() :
+ CustomGridTable(int initialRows = 0, int initialCols = 0) : //note: initialRows/initialCols MUST match with GetNumberRows()/GetNumberCols() at initialization!!!
wxGridTableBase(),
- blue(80, 110, 255),
- grey(212, 208, 200),
- lightRed(235, 57, 61),
- lightBlue(63, 206, 233),
- lightGreen(54, 218, 2),
- gridRefUI(NULL),
- gridData(NULL),
- lastNrRows(0),
- lastNrCols(0) {}
+ COLOR_BLUE( 80, 110, 255),
+ COLOR_GREY( 212, 208, 200),
+ COLOR_LIGHT_RED( 235, 57, 61),
+ COLOR_LIGHT_BLUE( 63, 206, 233),
+ COLOR_LIGHT_GREEN(54, 218, 2),
+ gridDataView(NULL),
+ lastNrRows(initialRows),
+ lastNrCols(initialCols) {}
virtual ~CustomGridTable() {}
- void setGridDataTable(GridView* gridRefUI, FileCompareResult* gridData)
+ void setGridDataTable(GridView* gridDataView)
{
- this->gridRefUI = gridRefUI;
- this->gridData = gridData;
+ this->gridDataView = gridDataView;
}
@@ -46,57 +65,27 @@ public:
virtual int GetNumberRows()
{
- if (gridRefUI)
- return std::max(gridRefUI->size(), MIN_ROW_COUNT);
+ if (gridDataView)
+ return std::max(gridDataView->elementsOnView(), MIN_ROW_COUNT);
else
return 0; //grid is initialized with zero number of rows
}
- virtual int GetNumberCols() //virtual used by middle grid!
- {
- return columnPositions.size();
- }
-
-
virtual bool IsEmptyCell( int row, int col )
{
- return (GetValue(row, col) == wxEmptyString);
- }
-
-
- virtual wxString GetValue(int row, int col) = 0;
-
-
- virtual void SetValue(int row, int col, const wxString& value)
- {
- assert (false); //should not be used, since values are retrieved directly from gridRefUI
- }
-
- virtual void Clear()
- {
- assert (false); // we don't want to use this, since the visible grid is directly connected to gridRefUI}
- }
+ return false; //avoid overlapping cells
- virtual bool InsertRows(size_t pos = 0, size_t numRows = 1)
- {
- assert (false); // we don't want to use this, since the visible grid is directly connected to gridRefUI}
- return true;
+ //return (GetValue(row, col) == wxEmptyString);
}
- virtual bool AppendRows(size_t numRows = 1)
- {
- assert (false); // we don't want to use this, since the visible grid is directly connected to gridRefUI}
- return true;
- }
- virtual bool DeleteRows(size_t pos = 0, size_t numRows = 1)
+ virtual void SetValue(int row, int col, const wxString& value)
{
- assert (false); // we don't want to use this, since the visible grid is directly connected to gridRefUI}
- return true;
+ assert (false); //should not be used, since values are retrieved directly from gridDataView
}
- //update dimensions of grid: no need for InsertRows, AppendRows, DeleteRows anymore!!!
+ //update dimensions of grid: no need for InsertRows(), AppendRows(), DeleteRows() anymore!!!
void updateGridSizes()
{
const int currentNrRows = GetNumberRows();
@@ -156,12 +145,6 @@ public:
//###########################################################################
- virtual wxString GetColLabelValue( int col )
- {
- return CustomGrid::getTypeName(getTypeAtPos(col));
- }
-
-
virtual wxGridCellAttr* GetAttr(int row, int col, wxGridCellAttr::wxAttrKind kind)
{
const wxColour& color = getRowColor(row);
@@ -190,6 +173,46 @@ public:
}
+ const FileCompareLine* getRawData(const unsigned int row) const
+ {
+ if (gridDataView && row < gridDataView->elementsOnView())
+ {
+ return &(*gridDataView)[row];
+ }
+ return NULL;
+ }
+
+protected:
+ const wxColour COLOR_BLUE;
+ const wxColour COLOR_GREY;
+ const wxColour COLOR_LIGHT_RED;
+ const wxColour COLOR_LIGHT_BLUE;
+ const wxColour COLOR_LIGHT_GREEN;
+
+ const GridView* gridDataView; //(very fast) access to underlying grid data :)
+
+private:
+ virtual const wxColour& getRowColor(int row) = 0; //rows that are filtered out are shown in different color
+
+ int lastNrRows;
+ int lastNrCols;
+};
+
+
+class CustomGridTableRim : public CustomGridTable
+{
+public:
+ virtual int GetNumberCols()
+ {
+ return columnPositions.size();
+ }
+
+ virtual wxString GetColLabelValue( int col )
+ {
+ return CustomGridRim::getTypeName(getTypeAtPos(col));
+ }
+
+
void setupColumns(const std::vector<xmlAccess::ColumnTypes>& positions)
{
columnPositions = positions;
@@ -206,269 +229,225 @@ public:
}
- const FileCompareLine* getRawData(const unsigned int row)
- {
- if (gridRefUI && row < gridRefUI->size())
- {
- const FileCompareLine& cmpLine = (*gridData)[(*gridRefUI)[row]];
- return &cmpLine;
- }
- return NULL;
- }
-
-
-protected:
- virtual const wxColour& getRowColor(int row) = 0; //rows that are filtered out are shown in different color
-
+private:
std::vector<xmlAccess::ColumnTypes> columnPositions;
-
- wxColour blue;
- wxColour grey;
- wxColour lightRed;
- wxColour lightBlue;
- wxColour lightGreen;
- GridView* gridRefUI; //(very fast) access to underlying grid data :)
- FileCompareResult* gridData;
- int lastNrRows;
- int lastNrCols;
};
-class CustomGridTableLeft : public CustomGridTable
+class CustomGridTableLeft : public CustomGridTableRim
{
public:
- CustomGridTableLeft() : CustomGridTable() {}
- ~CustomGridTableLeft() {}
+ virtual wxString GetValue(int row, int col)
+ {
+ const FileCompareLine* gridLine = getRawData(row);
+ if (gridLine)
+ {
+ if (gridLine->fileDescrLeft.objType == FileDescrLine::TYPE_DIRECTORY)
+ {
+ switch (getTypeAtPos(col))
+ {
+ case xmlAccess::FULL_NAME:
+ return 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;
+ }
+ }
+ else if (gridLine->fileDescrLeft.objType == FileDescrLine::TYPE_FILE)
+ {
+ switch (getTypeAtPos(col))
+ {
+ case xmlAccess::FULL_NAME:
+ return gridLine->fileDescrLeft.fullName.c_str();
+ case xmlAccess::FILENAME: //filename
+ return wxString(gridLine->fileDescrLeft.relativeName.c_str()).AfterLast(GlobalResources::FILE_NAME_SEPARATOR);
+ case xmlAccess::REL_PATH: //relative path
+ return wxString(gridLine->fileDescrLeft.relativeName.c_str()).BeforeLast(GlobalResources::FILE_NAME_SEPARATOR);
+ case xmlAccess::DIRECTORY:
+ return gridDataView->getFolderPair(row).leftDirectory.c_str();
+ case xmlAccess::SIZE: //file size
+ return globalFunctions::includeNumberSeparator(gridLine->fileDescrLeft.fileSize.ToString());
+ case xmlAccess::DATE: //date
+ return FreeFileSync::utcTimeToLocalString(gridLine->fileDescrLeft.lastWriteTimeRaw);
+ }
+ }
+ }
+ //if data is not found:
+ return wxEmptyString;
+ }
+private:
virtual const wxColour& getRowColor(int row) //rows that are filtered out are shown in different color
{
- if (gridRefUI && unsigned(row) < gridRefUI->size())
+ const FileCompareLine* gridLine = getRawData(row);
+ if (gridLine)
{
- const FileCompareLine cmpLine = (*gridData)[(*gridRefUI)[row]];
-
//mark filtered rows
- if (!cmpLine.selectedForSynchronization)
- return blue;
+ if (!gridLine->selectedForSynchronization)
+ return COLOR_BLUE;
//mark directories
- else if (cmpLine.fileDescrLeft.objType == FileDescrLine::TYPE_DIRECTORY)
- return grey;
+ else if (gridLine->fileDescrLeft.objType == FileDescrLine::TYPE_DIRECTORY)
+ return COLOR_GREY;
else
return *wxWHITE;
}
return *wxWHITE;
}
+};
- //virtual impl.
- wxString GetValue(int row, int col)
+class CustomGridTableRight : public CustomGridTableRim
+{
+public:
+ virtual wxString GetValue(int row, int col)
{
- if (gridRefUI)
+ const FileCompareLine* gridLine = getRawData(row);
+ if (gridLine)
{
- if (unsigned(row) < gridRefUI->size())
+ if (gridLine->fileDescrRight.objType == FileDescrLine::TYPE_DIRECTORY)
{
- const FileCompareLine& gridLine = (*gridData)[(*gridRefUI)[row]];
-
- if (gridLine.fileDescrLeft.objType == FileDescrLine::TYPE_DIRECTORY)
+ switch (getTypeAtPos(col))
{
- switch (getTypeAtPos(col))
- {
- case xmlAccess::FULL_NAME:
- return gridLine.fileDescrLeft.fullName.c_str();
- case xmlAccess::FILENAME: //filename
- return wxEmptyString;
- case xmlAccess::REL_PATH: //relative path
- return gridLine.fileDescrLeft.relativeName.c_str();
- case xmlAccess::SIZE: //file size
- return _("<Directory>");
- case xmlAccess::DATE: //date
- return wxEmptyString;
- }
+ case xmlAccess::FULL_NAME:
+ return 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.fileDescrLeft.objType == FileDescrLine::TYPE_FILE)
+ }
+ else if (gridLine->fileDescrRight.objType == FileDescrLine::TYPE_FILE)
+ {
+ switch (getTypeAtPos(col))
{
- switch (getTypeAtPos(col))
- {
- case xmlAccess::FULL_NAME:
- return gridLine.fileDescrLeft.fullName.c_str();
- case xmlAccess::FILENAME: //filename
- return wxString(gridLine.fileDescrLeft.relativeName.c_str()).AfterLast(GlobalResources::FILE_NAME_SEPARATOR);
- case xmlAccess::REL_PATH: //relative path
- return wxString(gridLine.fileDescrLeft.relativeName.c_str()).BeforeLast(GlobalResources::FILE_NAME_SEPARATOR);
- case xmlAccess::SIZE: //file size
- {
- wxString fileSize = gridLine.fileDescrLeft.fileSize.ToString(); //tmp string
- return globalFunctions::includeNumberSeparator(fileSize);
- }
- case xmlAccess::DATE: //date
- return FreeFileSync::utcTimeToLocalString(gridLine.fileDescrLeft.lastWriteTimeRaw);
- }
+ case xmlAccess::FULL_NAME:
+ return gridLine->fileDescrRight.fullName.c_str();
+ case xmlAccess::FILENAME: //filename
+ return wxString(gridLine->fileDescrRight.relativeName.c_str()).AfterLast(GlobalResources::FILE_NAME_SEPARATOR);
+ case xmlAccess::REL_PATH: //relative path
+ return wxString(gridLine->fileDescrRight.relativeName.c_str()).BeforeLast(GlobalResources::FILE_NAME_SEPARATOR);
+ case xmlAccess::DIRECTORY:
+ return gridDataView->getFolderPair(row).rightDirectory.c_str();
+ case xmlAccess::SIZE: //file size
+ return globalFunctions::includeNumberSeparator(gridLine->fileDescrRight.fileSize.ToString());
+ case xmlAccess::DATE: //date
+ return FreeFileSync::utcTimeToLocalString(gridLine->fileDescrRight.lastWriteTimeRaw);
}
}
}
//if data is not found:
return wxEmptyString;
}
+
+private:
+ virtual const wxColour& getRowColor(int row) //rows that are filtered out are shown in different color
+ {
+ 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;
+ }
};
class CustomGridTableMiddle : public CustomGridTable
{
public:
- CustomGridTableMiddle() : CustomGridTable()
- {
- lastNrCols = 1; //ensure CustomGridTable::updateGridSizes() is working correctly
- }
+//middle grid is created (first wxWidgets internal call to GetNumberCols()) with one column
+ CustomGridTableMiddle() : CustomGridTable(0, GetNumberCols()) {} //attention: static binding to virtual GetNumberCols() in a Constructor!
- ~CustomGridTableMiddle() {}
-
- //virtual impl.
- int GetNumberCols()
+ virtual int GetNumberCols()
{
return 1;
}
-
- virtual const wxColour& getRowColor(int row) //rows that are filtered out are shown in different color
+ virtual wxString GetColLabelValue( int col )
{
- if (gridRefUI && unsigned(row) < gridRefUI->size())
- {
- const FileCompareLine cmpLine = (*gridData)[(*gridRefUI)[row]];
-
- //mark filtered rows
- if (!cmpLine.selectedForSynchronization)
- return blue;
- else
- switch (cmpLine.cmpResult)
- {
- case FILE_LEFT_SIDE_ONLY:
- case FILE_RIGHT_SIDE_ONLY:
- return lightGreen;
- case FILE_LEFT_NEWER:
- case FILE_RIGHT_NEWER:
- return lightBlue;
- case FILE_DIFFERENT:
- return lightRed;
- default:
- return *wxWHITE;
- }
- }
- return *wxWHITE;
+ return wxEmptyString;
}
virtual wxString GetValue(int row, int col)
{
- if (gridRefUI)
+ const FileCompareLine* gridLine = getRawData(row);
+ if (gridLine)
{
- if (unsigned(row) < gridRefUI->size())
+ switch (gridLine->cmpResult)
{
- const FileCompareLine& gridLine = (*gridData)[(*gridRefUI)[row]];
-
- switch (gridLine.cmpResult)
- {
- case FILE_LEFT_SIDE_ONLY:
- return wxT("<|");
- case FILE_RIGHT_SIDE_ONLY:
- return wxT("|>");
- case FILE_RIGHT_NEWER:
- return wxT(">>");
- case FILE_LEFT_NEWER:
- return wxT("<<");
- case FILE_DIFFERENT:
- return wxT("!=");
- case FILE_EQUAL:
- return wxT("==");
- default:
- assert (false);
- return wxEmptyString;
- }
+ case FILE_LEFT_SIDE_ONLY:
+ return wxT("<|");
+ case FILE_RIGHT_SIDE_ONLY:
+ return wxT("|>");
+ case FILE_RIGHT_NEWER:
+ return wxT(">>");
+ case FILE_LEFT_NEWER:
+ return wxT("<<");
+ case FILE_DIFFERENT:
+ return wxT("!=");
+ case FILE_EQUAL:
+ return wxT("==");
+ default:
+ assert (false);
+ return wxEmptyString;
}
}
//if data is not found:
return wxEmptyString;
}
-};
-
-
-class CustomGridTableRight : public CustomGridTable
-{
-public:
- CustomGridTableRight() : CustomGridTable() {}
- ~CustomGridTableRight() {}
+private:
virtual const wxColour& getRowColor(int row) //rows that are filtered out are shown in different color
{
- if (gridRefUI && unsigned(row) < gridRefUI->size())
+ const FileCompareLine* gridLine = getRawData(row);
+ if (gridLine)
{
- const FileCompareLine cmpLine = (*gridData)[(*gridRefUI)[row]];
-
//mark filtered rows
- if (!cmpLine.selectedForSynchronization)
- return blue;
- //mark directories
- else if (cmpLine.fileDescrRight.objType == FileDescrLine::TYPE_DIRECTORY)
- return grey;
+ if (!gridLine->selectedForSynchronization)
+ return COLOR_BLUE;
else
- return *wxWHITE;
- }
- return *wxWHITE;
- }
-
- //virtual impl.
- wxString GetValue(int row, int col)
- {
- if (gridRefUI)
- {
- if (unsigned(row) < gridRefUI->size())
- {
- const FileCompareLine& gridLine = (*gridData)[(*gridRefUI)[row]];
-
- if (gridLine.fileDescrRight.objType == FileDescrLine::TYPE_DIRECTORY)
- {
- switch (getTypeAtPos(col))
- {
- case xmlAccess::FULL_NAME:
- return 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::SIZE: //file size
- return _("<Directory>");
- case xmlAccess::DATE: //date
- return wxEmptyString;
- }
- }
- else if (gridLine.fileDescrRight.objType == FileDescrLine::TYPE_FILE)
+ switch (gridLine->cmpResult)
{
- switch (getTypeAtPos(col))
- {
- case xmlAccess::FULL_NAME:
- return gridLine.fileDescrRight.fullName.c_str();
- case xmlAccess::FILENAME: //filename
- return wxString(gridLine.fileDescrRight.relativeName.c_str()).AfterLast(GlobalResources::FILE_NAME_SEPARATOR);
- case xmlAccess::REL_PATH: //relative path
- return wxString(gridLine.fileDescrRight.relativeName.c_str()).BeforeLast(GlobalResources::FILE_NAME_SEPARATOR);
- case xmlAccess::SIZE: //file size
- {
- wxString fileSize = gridLine.fileDescrRight.fileSize.ToString(); //tmp string
- return globalFunctions::includeNumberSeparator(fileSize);
- }
- case xmlAccess::DATE: //date
- return FreeFileSync::utcTimeToLocalString(gridLine.fileDescrRight.lastWriteTimeRaw);
- }
+ case FILE_LEFT_SIDE_ONLY:
+ case FILE_RIGHT_SIDE_ONLY:
+ return COLOR_LIGHT_GREEN;
+ case FILE_LEFT_NEWER:
+ case FILE_RIGHT_NEWER:
+ return COLOR_LIGHT_BLUE;
+ case FILE_DIFFERENT:
+ return COLOR_LIGHT_RED;
+ default:
+ return *wxWHITE;
}
- }
}
- //if data is not found:
- return wxEmptyString;
+ return *wxWHITE;
}
};
-
-
//########################################################################################################
+
CustomGrid::CustomGrid(wxWindow *parent,
wxWindowID id,
const wxPoint& pos,
@@ -476,10 +455,10 @@ CustomGrid::CustomGrid(wxWindow *parent,
long style,
const wxString& name) :
wxGrid(parent, id, pos, size, style, name),
- leadGrid(NULL),
- scrollbarsEnabled(true),
- m_gridLeft(NULL), m_gridMiddle(NULL), m_gridRight(NULL),
- gridDataTable(NULL),
+ m_gridLeft(NULL),
+ m_gridMiddle(NULL),
+ m_gridRight(NULL),
+ isLeading(false),
currentSortColumn(-1),
sortMarker(NULL)
{
@@ -500,32 +479,14 @@ CustomGrid::CustomGrid(wxWindow *parent,
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_ENTER_WINDOW, wxEventHandler(CustomGrid::adjustGridHeights), NULL, this);
}
-void CustomGrid::initSettings(const bool enableScrollbars,
- const bool showFileIcons,
- CustomGrid* gridLeft,
- CustomGrid* gridRight,
- CustomGrid* gridMiddle,
- GridView* gridRefUI,
- FileCompareResult* gridData)
+bool CustomGrid::isLeadGrid() const
{
- scrollbarsEnabled = enableScrollbars;
-
- //these grids will scroll together
- assert(gridLeft && gridRight && gridMiddle);
- m_gridLeft = gridLeft;
- m_gridRight = gridRight;
- m_gridMiddle = gridMiddle;
-
- //set underlying grid data
- assert(gridDataTable);
- gridDataTable->setGridDataTable(gridRefUI, gridData);
-
- this->initGridRenderer(showFileIcons);
-
- GetGridWindow()->Connect(wxEVT_ENTER_WINDOW, wxEventHandler(CustomGrid::adjustGridHeights), NULL, this);
+ return isLeading;
}
@@ -705,14 +666,17 @@ void additionalGridCommands(wxEvent& event, wxGrid* grid)
void CustomGrid::onGridAccess(wxEvent& event)
{
- if (leadGrid != this)
+ if (!isLeading)
{
- leadGrid = this;
+ isLeading = true;
- //notify grids of new user focus
- m_gridLeft->leadGrid = this;
- m_gridMiddle->leadGrid = this;
- m_gridRight->leadGrid = this;
+ //notify other grids of new user focus
+ if (m_gridLeft != this)
+ m_gridLeft->isLeading = false;
+ if (m_gridMiddle != this)
+ m_gridMiddle->isLeading = false;
+ if (m_gridRight != this)
+ m_gridRight->isLeading = false;
wxGrid::SetFocus();
}
@@ -728,119 +692,194 @@ void CustomGrid::onGridAccess(wxEvent& event)
}
-const wxGrid* CustomGrid::getLeadGrid()
+//workaround: ensure that all grids are properly aligned: add some extra window space to grids that have no horizontal scrollbar
+void CustomGrid::adjustGridHeights(wxEvent& event)
{
- return leadGrid;
+ if (m_gridLeft && m_gridRight && m_gridMiddle)
+ {
+ int y1 = 0;
+ int y2 = 0;
+ int y3 = 0;
+ int dummy = 0;
+
+ m_gridLeft->GetViewStart(&dummy, &y1);
+ m_gridRight->GetViewStart(&dummy, &y2);
+ m_gridMiddle->GetViewStart(&dummy, &y3);
+
+ if (y1 != y2 || y2 != y3)
+ {
+ int yMax = std::max(y1, std::max(y2, y3));
+
+ if (m_gridLeft->isLeadGrid()) //do not handle case (y1 == yMax) here!!! Avoid back coupling!
+ m_gridLeft->SetMargins(0, 0);
+ else if (y1 < yMax)
+ m_gridLeft->SetMargins(0, 30);
+
+ if (m_gridRight->isLeadGrid())
+ m_gridRight->SetMargins(0, 0);
+ else if (y2 < yMax)
+ m_gridRight->SetMargins(0, 30);
+
+ if (m_gridMiddle->isLeadGrid())
+ m_gridMiddle->SetMargins(0, 0);
+ else if (y3 < yMax)
+ m_gridMiddle->SetMargins(0, 30);
+
+ m_gridLeft->ForceRefresh();
+ m_gridRight->ForceRefresh();
+ m_gridMiddle->ForceRefresh();
+ }
+ }
}
-bool CustomGrid::isLeadGrid()
+void CustomGrid::setSortMarker(const int sortColumn, const wxBitmap* bitmap)
{
- return leadGrid == static_cast<const wxGrid*>(this);
+ currentSortColumn = sortColumn;
+ sortMarker = bitmap;
}
-//overwrite virtual method to finally get rid of the scrollbars
-void CustomGrid::SetScrollbar(int orientation, int position, int thumbSize, int range, bool refresh)
+void CustomGrid::DrawColLabel(wxDC& dc, int col)
{
- if (scrollbarsEnabled)
- wxWindow::SetScrollbar(orientation, position, thumbSize, range, refresh);
- else
- wxWindow::SetScrollbar(orientation, 0, 0, 0, refresh);
+ wxGrid::DrawColLabel(dc, col);
+
+ if (col == currentSortColumn)
+ dc.DrawBitmap(*sortMarker, GetColRight(col) - 16 - 2, 2, true); //respect 2-pixel border
}
+//############################################################################################
+//CustomGrid specializations
-//workaround: ensure that all grids are properly aligned: add some extra window space to grids that have no horizontal scrollbar
-void CustomGrid::adjustGridHeights(wxEvent& event) //m_gridLeft, m_gridRight, m_gridMiddle are not NULL in this context
+template <bool leftSide, bool showFileIcons>
+class GridCellRenderer : public wxGridCellStringRenderer
{
- int y1 = 0;
- int y2 = 0;
- int y3 = 0;
- int dummy = 0;
+public:
+ GridCellRenderer(CustomGridTableRim* gridDataTable) : m_gridDataTable(gridDataTable) {};
- m_gridLeft->GetViewStart(&dummy, &y1);
- m_gridRight->GetViewStart(&dummy, &y2);
- m_gridMiddle->GetViewStart(&dummy, &y3);
- if (y1 != y2 || y2 != y3)
+ virtual void Draw(wxGrid& grid,
+ wxGridCellAttr& attr,
+ wxDC& dc,
+ const wxRect& rect,
+ int row, int col,
+ bool isSelected)
{
- int yMax = std::max(y1, std::max(y2, y3));
-
- if (leadGrid == m_gridLeft) //do not handle case (y1 == yMax) here!!! Avoid back coupling!
- m_gridLeft->SetMargins(0, 0);
- else if (y1 < yMax)
- m_gridLeft->SetMargins(0, 30);
-
- if (leadGrid == m_gridRight)
- m_gridRight->SetMargins(0, 0);
- else if (y2 < yMax)
- m_gridRight->SetMargins(0, 30);
-
- if (leadGrid == m_gridMiddle)
- m_gridMiddle->SetMargins(0, 0);
- else if (y3 < yMax)
- m_gridMiddle->SetMargins(0, 30);
-
- m_gridLeft->ForceRefresh();
- m_gridRight->ForceRefresh();
- m_gridMiddle->ForceRefresh();
- }
-}
+#ifdef FFS_WIN
+ //############## show windows explorer file icons ######################
+ if (showFileIcons) //evaluate at compile time
+ {
+ const int ICON_SIZE = 16; //size in pixel
-void CustomGrid::updateGridSizes()
-{
- assert(gridDataTable);
- gridDataTable->updateGridSizes();
-}
+ if ( m_gridDataTable->getTypeAtPos(col) == xmlAccess::FILENAME &&
+ rect.GetWidth() >= ICON_SIZE)
+ {
+ //retrieve grid data
+ const FileCompareLine* rowData = m_gridDataTable->getRawData(row);
+ if (rowData) //valid row
+ {
+ const DefaultChar* filename;
+ if (leftSide) //evaluate at compile time
+ filename = rowData->fileDescrLeft.fullName.c_str();
+ else
+ filename = rowData->fileDescrRight.fullName.c_str();
+ if (*filename != DefaultChar(0)) //test if filename is empty
+ {
+ // Get the file icon.
+ SHFILEINFO fileInfo;
+ fileInfo.hIcon = 0; //initialize hIcon
-void CustomGrid::setSortMarker(const int sortColumn, const wxBitmap* bitmap)
-{
- currentSortColumn = sortColumn;
- sortMarker = bitmap;
-}
+ if (SHGetFileInfo(filename, //NOTE: CoInitializeEx()/CoUninitialize() implicitly called by wxWidgets on program startup!
+ 0,
+ &fileInfo,
+ sizeof(fileInfo),
+ SHGFI_ICON | SHGFI_SMALLICON))
+ {
+ //clear area where icon will be placed
+ wxRect rectShrinked(rect);
+ rectShrinked.SetWidth(ICON_SIZE + 2); //add 2 pixel border
+ wxGridCellRenderer::Draw(grid, attr, dc, rectShrinked, row, col, isSelected);
+ //draw icon
+ if (fileInfo.hIcon != 0) //fix for weird error: SHGetFileInfo() might return successfully WITHOUT filling fileInfo.hIcon!!
+ { //bug report: https://sourceforge.net/tracker/?func=detail&aid=2768004&group_id=234430&atid=1093080
+ wxIcon icon;
+ icon.SetHICON(static_cast<WXHICON>(fileInfo.hIcon));
+ icon.SetSize(ICON_SIZE, ICON_SIZE);
-void CustomGrid::DrawColLabel(wxDC& dc, int col)
-{
- wxGrid::DrawColLabel(dc, col);
+ dc.DrawIcon(icon, rectShrinked.GetX() + 2, rectShrinked.GetY());
- if (col == currentSortColumn)
- dc.DrawBitmap(*sortMarker, GetColRight(col) - 16 - 2, 2, true); //respect 2-pixel border
+ if (!DestroyIcon(fileInfo.hIcon))
+ throw RuntimeException(wxString(wxT("Error deallocating Icon handle!\n\n")) + FreeFileSync::getLastErrorFormatted());
+ }
+
+ //draw rest
+ rectShrinked.SetWidth(rect.GetWidth() - ICON_SIZE - 2);
+ rectShrinked.SetX(rect.GetX() + ICON_SIZE + 2);
+ wxGridCellStringRenderer::Draw(grid, attr, dc, rectShrinked, row, col, isSelected);
+ return;
+ }
+ }
+ }
+ }
+ }
+ //default
+ wxGridCellStringRenderer::Draw(grid, attr, dc, rect, row, col, isSelected);
+
+#elif defined FFS_LINUX
+ wxGridCellStringRenderer::Draw(grid, attr, dc, rect, row, col, isSelected);
+#endif
+ }
+
+private:
+ const CustomGridTableRim* const m_gridDataTable;
+};
+
+
+
+void CustomGridRim::updateGridSizes()
+{
+ assert(gridDataTable);
+ gridDataTable->updateGridSizes();
}
-xmlAccess::ColumnAttributes CustomGrid::getDefaultColumnAttributes()
+xmlAccess::ColumnAttributes CustomGridRim::getDefaultColumnAttributes()
{
xmlAccess::ColumnAttributes defaultColumnSettings;
xmlAccess::ColumnAttrib newEntry;
-
newEntry.type = xmlAccess::FULL_NAME;
newEntry.visible = false;
newEntry.position = 0;
newEntry.width = 150;
defaultColumnSettings.push_back(newEntry);
- newEntry.type = xmlAccess::FILENAME;
- newEntry.visible = true;
+ newEntry.type = xmlAccess::DIRECTORY;
newEntry.position = 1;
- newEntry.width = 138;
+ newEntry.width = 140;
defaultColumnSettings.push_back(newEntry);
newEntry.type = xmlAccess::REL_PATH;
+ newEntry.visible = true;
newEntry.position = 2;
newEntry.width = 118;
defaultColumnSettings.push_back(newEntry);
- newEntry.type = xmlAccess::SIZE;
+ newEntry.type = xmlAccess::FILENAME;
newEntry.position = 3;
- newEntry.width = 67;
+ newEntry.width = 138;
defaultColumnSettings.push_back(newEntry);
- newEntry.type = xmlAccess::DATE;
+ newEntry.type = xmlAccess::SIZE;
newEntry.position = 4;
+ newEntry.width = 70;
+ defaultColumnSettings.push_back(newEntry);
+
+ newEntry.type = xmlAccess::DATE;
+ newEntry.position = 5;
newEntry.width = 113;
defaultColumnSettings.push_back(newEntry);
@@ -848,7 +887,7 @@ xmlAccess::ColumnAttributes CustomGrid::getDefaultColumnAttributes()
}
-xmlAccess::ColumnAttributes CustomGrid::getColumnAttributes()
+xmlAccess::ColumnAttributes CustomGridRim::getColumnAttributes()
{
std::sort(columnSettings.begin(), columnSettings.end(), xmlAccess::sortByPositionAndVisibility);
@@ -866,7 +905,7 @@ xmlAccess::ColumnAttributes CustomGrid::getColumnAttributes()
}
-void CustomGrid::setColumnAttributes(const xmlAccess::ColumnAttributes& attr)
+void CustomGridRim::setColumnAttributes(const xmlAccess::ColumnAttributes& attr)
{
//remove special alignment for column "size"
for (int i = 0; i < GetNumberCols(); ++i)
@@ -940,14 +979,14 @@ void CustomGrid::setColumnAttributes(const xmlAccess::ColumnAttributes& attr)
}
-xmlAccess::ColumnTypes CustomGrid::getTypeAtPos(unsigned pos) const
+xmlAccess::ColumnTypes CustomGridRim::getTypeAtPos(unsigned pos) const
{
assert(gridDataTable);
return gridDataTable->getTypeAtPos(pos);
}
-wxString CustomGrid::getTypeName(xmlAccess::ColumnTypes colType)
+wxString CustomGridRim::getTypeName(xmlAccess::ColumnTypes colType)
{
switch (colType)
{
@@ -957,6 +996,8 @@ wxString CustomGrid::getTypeName(xmlAccess::ColumnTypes colType)
return _("Filename");
case xmlAccess::REL_PATH:
return _("Relative path");
+ case xmlAccess::DIRECTORY:
+ return _("Directory");
case xmlAccess::SIZE:
return _("Size");
case xmlAccess::DATE:
@@ -966,139 +1007,121 @@ wxString CustomGrid::getTypeName(xmlAccess::ColumnTypes colType)
}
}
-//############################################################################################
-//CustomGrid specializations
+//----------------------------------------------------------------------------------------
CustomGridLeft::CustomGridLeft(wxWindow *parent,
wxWindowID id,
const wxPoint& pos,
const wxSize& size,
long style,
const wxString& name) :
- CustomGrid(parent, id, pos, size, style, name) {}
+ CustomGridRim(parent, id, pos, size, style, name) {}
-template <bool leftSide, bool showFileIcons>
-class GridCellRenderer : public wxGridCellStringRenderer
+bool CustomGridLeft::CreateGrid(int numRows, int numCols, wxGrid::wxGridSelectionModes selmode)
{
-public:
- GridCellRenderer(CustomGridTable* gridDataTable) : m_gridDataTable(gridDataTable) {};
-
-
- virtual void Draw(wxGrid& grid,
- wxGridCellAttr& attr,
- wxDC& dc,
- const wxRect& rect,
- int row, int col,
- bool isSelected)
- {
-#ifdef FFS_WIN
- //############## show windows explorer file icons ######################
-
- if (showFileIcons) //evaluate at compile time
- {
- const int ICON_SIZE = 16; //size in pixel
-
- if ( m_gridDataTable->getTypeAtPos(col) == xmlAccess::FILENAME &&
- rect.GetWidth() >= ICON_SIZE)
- {
- //retrieve grid data
- const FileCompareLine* rowData = m_gridDataTable->getRawData(row);
- if (rowData) //valid row
- {
- const DefaultChar* filename;
- if (leftSide) //evaluate at compile time
- filename = rowData->fileDescrLeft.fullName.c_str();
- else
- filename = rowData->fileDescrRight.fullName.c_str();
+ //use custom wxGridTableBase class for management of large sets of formatted data.
+ //This is done in CreateGrid instead of SetTable method since source code is generated and wxFormbuilder invokes CreatedGrid by default.
+ gridDataTable = new CustomGridTableLeft;
+ SetTable(gridDataTable, true, wxGrid::wxGridSelectRows); //give ownership to wxGrid: gridDataTable is deleted automatically in wxGrid destructor
- if (*filename != 0) //test if filename is empty
- {
- // Get the file icon.
- SHFILEINFO fileInfo;
- if (SHGetFileInfo(filename,
- 0,
- &fileInfo,
- sizeof(fileInfo),
- SHGFI_ICON | SHGFI_SMALLICON))
- {
- wxIcon icon;
- icon.SetHICON((WXHICON)fileInfo.hIcon);
- icon.SetSize(ICON_SIZE, ICON_SIZE);
+ return true;
+}
- //clear area where icon will be placed
- wxRect rectShrinked(rect);
- rectShrinked.SetWidth(ICON_SIZE + 2); //add 2 pixel border
- dc.SetPen(*wxWHITE_PEN);
- dc.SetBrush(*wxWHITE_BRUSH);
- dc.DrawRectangle(rectShrinked);
+void CustomGridLeft::initSettings(const bool showFileIcons,
+ CustomGrid* gridLeft,
+ CustomGrid* gridRight,
+ CustomGrid* gridMiddle,
+ GridView* gridDataView)
+{
+ //these grids will scroll together
+ m_gridLeft = gridLeft;
+ m_gridRight = gridRight;
+ m_gridMiddle = gridMiddle;
- //draw icon
- dc.DrawIcon(icon, rectShrinked.GetX() + 2, rectShrinked.GetY());
+ //set underlying grid data
+ assert(gridDataTable);
+ gridDataTable->setGridDataTable(gridDataView);
- rectShrinked.SetWidth(rect.GetWidth() - ICON_SIZE - 2);
- rectShrinked.SetX(rect.GetX() + ICON_SIZE + 2);
- wxGridCellStringRenderer::Draw(grid, attr, dc, rectShrinked, row, col, isSelected);
+ if (showFileIcons)
+ SetDefaultRenderer(new GridCellRenderer<true, true>(gridDataTable)); //SetDefaultRenderer takes ownership!
+ else
+ SetDefaultRenderer(new GridCellRenderer<true, false>(gridDataTable));
+}
- if (!DestroyIcon(fileInfo.hIcon))
- throw RuntimeException(wxString(wxT("Error deallocating Icon handle!\n\n")) + FreeFileSync::getLastErrorFormatted());
- return;
- }
- }
- }
- }
- }
- //default
- wxGridCellStringRenderer::Draw(grid, attr, dc, rect, row, col, isSelected);
+//this method is called when grid view changes: useful for parallel updating of multiple grids
+void CustomGridLeft::DoPrepareDC(wxDC& dc)
+{
+ wxScrollHelper::DoPrepareDC(dc);
-#elif defined FFS_LINUX
- wxGridCellStringRenderer::Draw(grid, attr, dc, rect, row, col, isSelected);
-#endif
+ int x, y = 0;
+ if (isLeadGrid()) //avoid back coupling
+ {
+ GetViewStart(&x, &y);
+ m_gridMiddle->Scroll(-1, y); //scroll in y-direction only
+ m_gridRight->Scroll(x, y);
}
+}
-private:
- CustomGridTable* m_gridDataTable;
-};
+//----------------------------------------------------------------------------------------
+CustomGridRight::CustomGridRight(wxWindow *parent,
+ wxWindowID id,
+ const wxPoint& pos,
+ const wxSize& size,
+ long style,
+ const wxString& name) :
+ CustomGridRim(parent, id, pos, size, style, name) {}
-bool CustomGridLeft::CreateGrid(int numRows, int numCols, wxGrid::wxGridSelectionModes selmode)
+bool CustomGridRight::CreateGrid(int numRows, int numCols, wxGrid::wxGridSelectionModes selmode)
{
- //use custom wxGridTableBase class for management of large sets of formatted data.
- //This is done in CreateGrid instead of SetTable method since source code is generated and wxFormbuilder invokes CreatedGrid by default.
- gridDataTable = new CustomGridTableLeft();
+ gridDataTable = new CustomGridTableRight;
SetTable(gridDataTable, true, wxGrid::wxGridSelectRows); //give ownership to wxGrid: gridDataTable is deleted automatically in wxGrid destructor
return true;
}
+void CustomGridRight::initSettings(const bool showFileIcons,
+ CustomGrid* gridLeft,
+ CustomGrid* gridRight,
+ CustomGrid* gridMiddle,
+ GridView* gridDataView)
+{
+ //these grids will scroll together
+ m_gridLeft = gridLeft;
+ m_gridRight = gridRight;
+ m_gridMiddle = gridMiddle;
+
+ //set underlying grid data
+ assert(gridDataTable);
+ gridDataTable->setGridDataTable(gridDataView);
+
+ if (showFileIcons)
+ SetDefaultRenderer(new GridCellRenderer<false, true>(gridDataTable)); //SetDefaultRenderer takes ownership!
+ else
+ SetDefaultRenderer(new GridCellRenderer<false, false>(gridDataTable));
+}
+
+
//this method is called when grid view changes: useful for parallel updating of multiple grids
-void CustomGridLeft::DoPrepareDC(wxDC& dc)
+void CustomGridRight::DoPrepareDC(wxDC& dc)
{
wxScrollHelper::DoPrepareDC(dc);
int x, y = 0;
- if (this == leadGrid) //avoid back coupling
+ if (isLeadGrid()) //avoid back coupling
{
GetViewStart(&x, &y);
- m_gridMiddle->Scroll(-1, y); //scroll in y-direction only
- m_gridRight->Scroll(x, y);
+ m_gridLeft->Scroll(x, y);
+ m_gridMiddle->Scroll(-1, y);
}
}
-void CustomGridLeft::initGridRenderer(const bool showFileIcons)
-{
- if (showFileIcons)
- SetDefaultRenderer(new GridCellRenderer<true, true>(gridDataTable)); //SetDefaultRenderer takes ownership!
- else
- SetDefaultRenderer(new GridCellRenderer<true, false>(gridDataTable));
-}
-
-
//----------------------------------------------------------------------------------------
CustomGridMiddle::CustomGridMiddle(wxWindow *parent,
wxWindowID id,
@@ -1106,7 +1129,8 @@ CustomGridMiddle::CustomGridMiddle(wxWindow *parent,
const wxSize& size,
long style,
const wxString& name) :
- CustomGrid(parent, id, pos, size, style, name)
+ CustomGrid(parent, id, pos, size, style, name),
+ gridDataTable(NULL)
{
const wxString header = _("Legend");
wxString toolTip = header + wxT("\n") +
@@ -1121,6 +1145,42 @@ CustomGridMiddle::CustomGridMiddle(wxWindow *parent,
}
+void CustomGridMiddle::initSettings(CustomGrid* gridLeft,
+ CustomGrid* gridRight,
+ CustomGrid* gridMiddle,
+ FreeFileSync::GridView* gridDataView)
+{
+ //these grids will scroll together
+ m_gridLeft = gridLeft;
+ m_gridRight = gridRight;
+ m_gridMiddle = gridMiddle;
+
+ //set underlying grid data
+ assert(gridDataTable);
+ gridDataTable->setGridDataTable(gridDataView);
+
+#ifdef FFS_LINUX //get rid of scrollbars; Linux: change policy for GtkScrolledWindow
+ GtkWidget* gridWidget = wxWindow::m_widget;
+ GtkScrolledWindow* scrolledWindow = GTK_SCROLLED_WINDOW(gridWidget);
+ gtk_scrolled_window_set_policy(scrolledWindow, GTK_POLICY_NEVER, GTK_POLICY_NEVER);
+#endif
+}
+
+
+#ifdef FFS_WIN //get rid of scrollbars; Windows: overwrite virtual method
+void CustomGridMiddle::SetScrollbar(int orientation, int position, int thumbSize, int range, bool refresh)
+{
+ wxWindow::SetScrollbar(orientation, 0, 0, 0, refresh);
+}
+#endif
+
+void CustomGridMiddle::updateGridSizes()
+{
+ assert(gridDataTable);
+ gridDataTable->updateGridSizes();
+}
+
+
class GridCellRendererMiddle : public wxGridCellStringRenderer
{
public:
@@ -1148,12 +1208,10 @@ public:
//clean first block of rect that will receive image of checkbox
rectShrinked.SetWidth(shift);
- dc.SetPen(*wxWHITE_PEN);
- dc.SetBrush(*wxWHITE_BRUSH);
- dc.DrawRectangle(rectShrinked);
+ wxGridCellRenderer::Draw(grid, attr, dc, rectShrinked, row, col, isSelected);
//print image into first block
- rectShrinked.SetX(1);
+ rectShrinked.SetX(rect.GetX() + 1);
if (rowData->selectedForSynchronization)
dc.DrawLabel(wxEmptyString, *globalResource.bitmapCheckBoxTrue, rectShrinked, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL);
else
@@ -1161,18 +1219,18 @@ public:
//print second block (default): display compare result
rectShrinked.SetWidth(rect.GetWidth() - shift);
- rectShrinked.SetX(shift);
+ rectShrinked.SetX(rect.GetX() + shift);
wxGridCellStringRenderer::Draw(grid, attr, dc, rectShrinked, row, col, isSelected);
}
private:
- CustomGridTable* m_gridDataTable;
+ const CustomGridTable* const m_gridDataTable;
};
bool CustomGridMiddle::CreateGrid(int numRows, int numCols, wxGrid::wxGridSelectionModes selmode)
{
- gridDataTable = new CustomGridTableMiddle();
+ gridDataTable = new CustomGridTableMiddle;
SetTable(gridDataTable, true, wxGrid::wxGridSelectRows); //give ownership to wxGrid: gridDataTable is deleted automatically in wxGrid destructor
//display checkboxes (representing bool values) if row is enabled for synchronization
@@ -1188,7 +1246,7 @@ void CustomGridMiddle::DoPrepareDC(wxDC& dc)
wxScrollHelper::DoPrepareDC(dc);
int x, y = 0;
- if (this == leadGrid) //avoid back coupling
+ if (isLeadGrid()) //avoid back coupling
{
GetViewStart(&x, &y);
m_gridLeft->Scroll(-1, y);
@@ -1196,45 +1254,3 @@ void CustomGridMiddle::DoPrepareDC(wxDC& dc)
}
}
-
-//----------------------------------------------------------------------------------------
-CustomGridRight::CustomGridRight(wxWindow *parent,
- wxWindowID id,
- const wxPoint& pos,
- const wxSize& size,
- long style,
- const wxString& name) :
- CustomGrid(parent, id, pos, size, style, name) {}
-
-
-bool CustomGridRight::CreateGrid(int numRows, int numCols, wxGrid::wxGridSelectionModes selmode)
-{
- gridDataTable = new CustomGridTableRight();
- SetTable(gridDataTable, true, wxGrid::wxGridSelectRows); //give ownership to wxGrid: gridDataTable is deleted automatically in wxGrid destructor
-
- return true;
-}
-
-
-//this method is called when grid view changes: useful for parallel updating of multiple grids
-void CustomGridRight::DoPrepareDC(wxDC& dc)
-{
- wxScrollHelper::DoPrepareDC(dc);
-
- int x, y = 0;
- if (this == leadGrid) //avoid back coupling
- {
- GetViewStart(&x, &y);
- m_gridLeft->Scroll(x, y);
- m_gridMiddle->Scroll(-1, y);
- }
-}
-
-
-void CustomGridRight::initGridRenderer(const bool showFileIcons)
-{
- if (showFileIcons)
- SetDefaultRenderer(new GridCellRenderer<false, true>(gridDataTable)); //SetDefaultRenderer takes ownership!
- else
- SetDefaultRenderer(new GridCellRenderer<false, false>(gridDataTable));
-}
diff --git a/library/CustomGrid.h b/library/CustomGrid.h
index 14d62255..802db231 100644
--- a/library/CustomGrid.h
+++ b/library/CustomGrid.h
@@ -3,15 +3,33 @@
#include <vector>
#include <wx/grid.h>
-#include "../FreeFileSync.h"
+#include "../structures.h"
#include "processXml.h"
-using namespace FreeFileSync;
-
class CustomGridTable;
+class CustomGridTableRim;
+class CustomGridTableMiddle;
+
+namespace FreeFileSync
+{
+ class GridView;
+}
//##################################################################################
+/*
+class hierarchy:
+ CustomGrid
+ /|\
+ ____________|____________
+ | |
+ CustomGridRim |
+ /|\ |
+ ________|_______ |
+ | | |
+CustomGridLeft CustomGridRight CustomGridMiddle
+*/
+
class CustomGrid : public wxGrid
{
public:
@@ -24,27 +42,46 @@ public:
virtual ~CustomGrid() {}
- //overwrite virtual method to finally get rid of the scrollbars
- virtual void SetScrollbar(int orientation, int position, int thumbSize, int range, bool refresh = true);
-
virtual void DrawColLabel(wxDC& dc, int col);
- void initSettings(const bool enableScrollbars,
- const bool showFileIcons,
- CustomGrid* gridLeft,
- CustomGrid* gridRight,
- CustomGrid* gridMiddle,
- GridView* gridRefUI,
- FileCompareResult* gridData);
+ //set sort direction indicator on UI
+ void setSortMarker(const int sortColumn, const wxBitmap* bitmap = &wxNullBitmap);
+
+ bool isLeadGrid() const;
- virtual void initGridRenderer(const bool showFileIcons) = 0;
+protected:
+ CustomGrid* m_gridLeft;
+ CustomGrid* m_gridMiddle;
+ CustomGrid* m_gridRight;
+
+private:
+ void onGridAccess(wxEvent& event);
+ void adjustGridHeights(wxEvent& event);
+
+ bool isLeading; //identify grid that has user focus
+ int currentSortColumn;
+ const wxBitmap* sortMarker;
+};
+
+
+//############## SPECIALIZATIONS ###################
+class CustomGridRim : public CustomGrid
+{
+public:
+ CustomGridRim(wxWindow *parent,
+ wxWindowID id,
+ const wxPoint& pos,
+ const wxSize& size,
+ long style,
+ const wxString& name) :
+ CustomGrid(parent, id, pos, size, style, name),
+ gridDataTable(NULL) {}
+
+ ~CustomGridRim() {}
//notify wxGrid that underlying table size has changed
void updateGridSizes();
- //set sort direction indicator on UI
- void setSortMarker(const int sortColumn, const wxBitmap* bitmap = &wxNullBitmap);
-
//set visibility, position and width of columns
static xmlAccess::ColumnAttributes getDefaultColumnAttributes();
xmlAccess::ColumnAttributes getColumnAttributes();
@@ -54,30 +91,15 @@ public:
static wxString getTypeName(xmlAccess::ColumnTypes colType);
- const wxGrid* getLeadGrid();
- bool isLeadGrid();
-
protected:
- void onGridAccess(wxEvent& event);
- void adjustGridHeights(wxEvent& event);
+ CustomGridTableRim* gridDataTable;
+private:
xmlAccess::ColumnAttributes columnSettings; //set visibility, position and width of columns
-
- const wxGrid* leadGrid; //grid that has user focus
- bool scrollbarsEnabled;
- CustomGrid* m_gridLeft;
- CustomGrid* m_gridMiddle;
- CustomGrid* m_gridRight;
-
- CustomGridTable* gridDataTable;
-
- int currentSortColumn;
- const wxBitmap* sortMarker;
};
-//############## SPECIALIZATIONS ###################
-class CustomGridLeft : public CustomGrid
+class CustomGridLeft : public CustomGridRim
{
public:
CustomGridLeft(wxWindow *parent,
@@ -89,12 +111,41 @@ public:
~CustomGridLeft() {}
+ virtual bool CreateGrid(int numRows, int numCols, wxGrid::wxGridSelectionModes selmode = wxGrid::wxGridSelectCells);
+
+ void initSettings(const bool showFileIcons, //workaround: though this coding better belongs into a constructor
+ CustomGrid* gridLeft, //this is not possible due to source code generation (information not available at time of construction)
+ CustomGrid* gridRight,
+ CustomGrid* gridMiddle,
+ FreeFileSync::GridView* gridDataView);
+
//this method is called when grid view changes: useful for parallel updating of multiple grids
virtual void DoPrepareDC(wxDC& dc);
+};
+
+
+class CustomGridRight : public CustomGridRim
+{
+public:
+ CustomGridRight(wxWindow *parent,
+ wxWindowID id,
+ const wxPoint& pos = wxDefaultPosition,
+ const wxSize& size = wxDefaultSize,
+ long style = wxWANTS_CHARS,
+ const wxString& name = wxGridNameStr);
+
+ ~CustomGridRight() {}
virtual bool CreateGrid(int numRows, int numCols, wxGrid::wxGridSelectionModes selmode = wxGrid::wxGridSelectCells);
- virtual void initGridRenderer(const bool showFileIcons);
+ void initSettings(const bool showFileIcons, //workaround: though this coding better belongs into a constructor
+ CustomGrid* gridLeft, //this is not possible due to source code generation (information not available at time of construction)
+ CustomGrid* gridRight,
+ CustomGrid* gridMiddle,
+ FreeFileSync::GridView* gridDataView);
+
+ //this method is called when grid view changes: useful for parallel updating of multiple grids
+ virtual void DoPrepareDC(wxDC& dc);
};
@@ -110,33 +161,25 @@ public:
~CustomGridMiddle() {}
- //this method is called when grid view changes: useful for parallel updating of multiple grids
- virtual void DoPrepareDC(wxDC& dc);
-
virtual bool CreateGrid(int numRows, int numCols, wxGrid::wxGridSelectionModes selmode = wxGrid::wxGridSelectCells);
- virtual void initGridRenderer(const bool showFileIcons) {}
-};
-
+ void initSettings(CustomGrid* gridLeft, //workaround: though this coding better belongs into a constructor
+ CustomGrid* gridRight, //this is not possible due to source code generation (information not available at time of construction)
+ CustomGrid* gridMiddle,
+ FreeFileSync::GridView* gridDataView);
-class CustomGridRight : public CustomGrid
-{
-public:
- CustomGridRight(wxWindow *parent,
- wxWindowID id,
- const wxPoint& pos = wxDefaultPosition,
- const wxSize& size = wxDefaultSize,
- long style = wxWANTS_CHARS,
- const wxString& name = wxGridNameStr);
+ //notify wxGrid that underlying table size has changed
+ void updateGridSizes();
- ~CustomGridRight() {}
+#ifdef FFS_WIN //get rid of scrollbars; Windows: overwrite virtual method
+ virtual void SetScrollbar(int orientation, int position, int thumbSize, int range, bool refresh = true);
+#endif
//this method is called when grid view changes: useful for parallel updating of multiple grids
virtual void DoPrepareDC(wxDC& dc);
- virtual bool CreateGrid(int numRows, int numCols, wxGrid::wxGridSelectionModes selmode = wxGrid::wxGridSelectCells);
-
- virtual void initGridRenderer(const bool showFileIcons);
+private:
+ CustomGridTableMiddle* gridDataTable;
};
#endif // CUSTOMGRID_H_INCLUDED
diff --git a/library/FreeFileSync.xpm b/library/FreeFileSync.xpm
index 4165c5a1..339ccccb 100644
--- a/library/FreeFileSync.xpm
+++ b/library/FreeFileSync.xpm
@@ -1,5 +1,5 @@
/* XPM */
-static char * FreeFileSync_xpm[] = {
+static const char * FreeFileSync_xpm[] = {
"32 32 390 2",
" c None",
". c #005927",
diff --git a/library/fileHandling.cpp b/library/fileHandling.cpp
index 0dccdec7..4f8caec6 100644
--- a/library/fileHandling.cpp
+++ b/library/fileHandling.cpp
@@ -3,6 +3,7 @@
#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"
@@ -67,20 +68,25 @@ private:
bool recycleBinAvailable;
};
-//global instance of recycle bin
-RecycleBin recyclerInstance;
+
+inline
+RecycleBin& getRecycleBin()
+{
+ static RecycleBin instance; //lazy creation of RecycleBin
+ return instance;
+}
bool FreeFileSync::recycleBinExists()
{
- return recyclerInstance.recycleBinExists();
+ return getRecycleBin().recycleBinExists();
}
inline
bool moveToRecycleBin(const Zstring& filename) throw(RuntimeException)
{
- return recyclerInstance.moveToRecycleBin(filename);
+ return getRecycleBin().moveToRecycleBin(filename);
}
@@ -266,18 +272,19 @@ void FreeFileSync::removeDirectory(const Zstring& directory, const bool useRecyc
class CloseHandleOnExit
{
public:
- CloseHandleOnExit(HANDLE searchHandle) : m_searchHandle(searchHandle) {}
+ CloseHandleOnExit(HANDLE fileHandle) : fileHandle_(fileHandle) {}
~CloseHandleOnExit()
{
- FindClose(m_searchHandle);
+ CloseHandle(fileHandle_);
}
private:
- HANDLE m_searchHandle;
+ HANDLE fileHandle_;
};
+
typedef DWORD WINAPI (*GetFinalPath)(
HANDLE hFile,
LPTSTR lpszFilePath,
@@ -295,7 +302,7 @@ public:
//get a handle to the DLL module containing required functionality
hKernel = ::LoadLibrary(wxT("kernel32.dll"));
if (hKernel)
- getFinalPathNameByHandle = (GetFinalPath)(::GetProcAddress(hKernel, "GetFinalPathNameByHandleW")); //load unicode version!
+ getFinalPathNameByHandle = reinterpret_cast<GetFinalPath>(::GetProcAddress(hKernel, "GetFinalPathNameByHandleW")); //load unicode version!
}
~DllHandler()
@@ -309,8 +316,13 @@ private:
HINSTANCE hKernel;
};
-//global instance
-DllHandler dynamicWinApi;
+
+inline
+DllHandler& getDllHandler() //lazy creation of DllHandler
+{
+ static DllHandler instance;
+ return instance;
+}
Zstring resolveDirectorySymlink(const Zstring& dirLinkName) //get full target path of symbolic link to a directory
@@ -328,13 +340,13 @@ Zstring resolveDirectorySymlink(const Zstring& dirLinkName) //get full target pa
CloseHandleOnExit dummy(hDir);
- if (dynamicWinApi.getFinalPathNameByHandle == NULL )
+ if (getDllHandler().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 = dynamicWinApi.getFinalPathNameByHandle(
+ const DWORD rv = getDllHandler().getFinalPathNameByHandle(
hDir,
targetPath,
BUFFER_SIZE,
@@ -638,6 +650,21 @@ void FreeFileSync::copyFile(const Zstring& sourceFile,
#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)
{
@@ -650,6 +677,7 @@ void setWin32FileInformation(const FILETIME& lastWriteTime, const DWORD fileSize
output.fileSize = wxULongLong(fileSizeHigh, fileSizeLow);
}
+
inline
bool setWin32FileInformationFromSymlink(const Zstring linkName, FreeFileSync::FileInfo& output)
{
@@ -679,6 +707,7 @@ bool setWin32FileInformationFromSymlink(const Zstring linkName, FreeFileSync::Fi
return true;
}
+
#elif defined FFS_LINUX
class CloseDirOnExit
{
@@ -713,14 +742,13 @@ public:
}
#ifdef FFS_WIN
- Zstring directoryFormatted = directory;
- if (!FreeFileSync::endsWithPathSeparator(directoryFormatted))
- directoryFormatted += GlobalResources::FILE_NAME_SEPARATOR;
-
- const Zstring filespec = directoryFormatted + DefaultChar('*');
+ //ensure directoryFormatted ends with backslash
+ const Zstring directoryFormatted = FreeFileSync::endsWithPathSeparator(directory) ?
+ directory :
+ directory + GlobalResources::FILE_NAME_SEPARATOR;
WIN32_FIND_DATA fileMetaData;
- HANDLE searchHandle = FindFirstFile(filespec.c_str(), //pointer to name of file to search for
+ 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)
@@ -736,11 +764,11 @@ public:
else
return true;
}
- CloseHandleOnExit dummy(searchHandle);
+ CloseFindHandleOnExit dummy(searchHandle);
do
{ //don't return "." and ".."
- const wxChar* name = fileMetaData.cFileName;
+ const wxChar* const name = fileMetaData.cFileName;
if ( name[0] == wxChar('.') &&
((name[1] == wxChar('.') && name[2] == wxChar('\0')) ||
name[1] == wxChar('\0')))
@@ -800,14 +828,10 @@ public:
return true;
#elif defined FFS_LINUX
- Zstring directoryFormatted = directory;
- if (FreeFileSync::endsWithPathSeparator(directoryFormatted))
- directoryFormatted = directoryFormatted.BeforeLast(GlobalResources::FILE_NAME_SEPARATOR);
-
- DIR* dirObj = opendir(directoryFormatted.c_str());
+ DIR* dirObj = opendir(directory.c_str());
if (dirObj == NULL)
{
- Zstring errorMessage = Zstring(_("Error traversing directory:")) + wxT("\n\"") + directoryFormatted + wxT("\"") ;
+ 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
@@ -819,13 +843,15 @@ public:
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* name = dirEntry->d_name;
+ 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 = directoryFormatted + GlobalResources::FILE_NAME_SEPARATOR + name;
+ const Zstring fullName = FreeFileSync::endsWithPathSeparator(directory) ? //e.g. "/"
+ directory + name :
+ directory + GlobalResources::FILE_NAME_SEPARATOR + name;
struct stat fileInfo;
if (lstat(fullName.c_str(), &fileInfo) != 0) //lstat() does not resolve symlinks
@@ -887,7 +913,7 @@ public:
return true; //everything okay
//else: we have a problem... report it:
- const Zstring errorMessage = Zstring(_("Error traversing directory:")) + wxT("\n\"") + directoryFormatted + wxT("\"") ;
+ 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
@@ -904,15 +930,21 @@ 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(GlobalResources::FILE_NAME_SEPARATOR);
+#endif
+
if (traverseDirectorySymlinks)
{
TraverseRecursively<true> filewalker(sink);
- filewalker.traverse(directory, 0);
+ filewalker.traverse(directoryFormatted, 0);
}
else
{
TraverseRecursively<false> filewalker(sink);
- filewalker.traverse(directory, 0);
+ filewalker.traverse(directoryFormatted, 0);
}
}
diff --git a/library/fileHandling.h b/library/fileHandling.h
index 6c9a0400..de068d1f 100644
--- a/library/fileHandling.h
+++ b/library/fileHandling.h
@@ -1,7 +1,6 @@
#ifndef RECYCLER_H_INCLUDED
#define RECYCLER_H_INCLUDED
-#include "globalFunctions.h"
#include <wx/dir.h>
#include "zstring.h"
@@ -11,7 +10,7 @@ public:
FileError(const Zstring& message) :
errorMessage(message) {}
- Zstring show() const
+ const Zstring& show() const
{
return errorMessage;
}
@@ -25,7 +24,7 @@ namespace FreeFileSync
{
struct FileInfo
{
- wxULongLong fileSize; //unit: bytes!
+ wxULongLong fileSize; //unit: bytes!
wxLongLong lastWriteTimeRaw; //number of seconds since Jan. 1st 1970 UTC
};
diff --git a/library/filter.cpp b/library/filter.cpp
new file mode 100644
index 00000000..d5255367
--- /dev/null
+++ b/library/filter.cpp
@@ -0,0 +1,241 @@
+#include "filter.h"
+#include "zstring.h"
+#include <wx/string.h>
+#include <set>
+#include <vector>
+#include "resources.h"
+
+
+void compoundStringToTable(const Zstring& compoundInput, const DefaultChar* delimiter, std::vector<Zstring>& output)
+{
+ output.clear();
+ Zstring input(compoundInput);
+
+ //make sure input ends with delimiter - no problem with empty strings here
+ if (!input.EndsWith(delimiter))
+ input += delimiter;
+
+ unsigned int indexStart = 0;
+ unsigned int indexEnd = 0;
+ while ((indexEnd = input.find(delimiter, indexStart)) != Zstring::npos)
+ {
+ if (indexStart != indexEnd) //do not add empty strings
+ {
+ Zstring newEntry = input.substr(indexStart, indexEnd - indexStart);
+
+ 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;
+ }
+}
+
+
+inline
+void mergeVectors(std::vector<Zstring>& changing, const std::vector<Zstring>& input)
+{
+ for (std::vector<Zstring>::const_iterator i = input.begin(); i != input.end(); ++i)
+ changing.push_back(*i);
+}
+
+
+inline
+void formatFilterString(Zstring& filter)
+{
+#ifdef FFS_WIN
+ //Windows does NOT distinguish between upper/lower-case
+ filter.MakeLower();
+#elif defined FFS_LINUX
+ //Linux DOES distinguish between upper/lower-case
+//nothing to do here
+#else
+ adapt;
+#endif
+}
+
+
+std::vector<Zstring> compoundStringToFilter(const Zstring& filterString)
+{
+ //delimiters may be ';' or '\n'
+ std::vector<Zstring> filterList;
+ std::vector<Zstring> filterPreProcessing;
+ compoundStringToTable(filterString, wxT(";"), filterPreProcessing);
+
+ for (std::vector<Zstring>::const_iterator i = filterPreProcessing.begin(); i != filterPreProcessing.end(); ++i)
+ {
+ std::vector<Zstring> newEntries;
+ compoundStringToTable(*i, wxT("\n"), newEntries);
+ mergeVectors(filterList, newEntries);
+ }
+
+ return filterList;
+}
+
+
+inline
+void addFilterEntry(const Zstring& filtername, std::set<Zstring>& fileFilter, std::set<Zstring>& directoryFilter)
+{
+ //Test if filtername ends with GlobalResources::FILE_NAME_SEPARATOR, ignoring '*' and '?'.
+ //If so, treat as filter for directory and add to directoryFilter.
+ if (!filtername.empty())
+ {
+ const DefaultChar* filter = filtername.c_str();
+ int i = filtername.length() - 1;
+ while (filter[i] == DefaultChar('*') || filter[i] == DefaultChar('?'))
+ {
+ --i;
+
+ if (i == -1)
+ break;
+ }
+
+ if (i >= 0 && filter[i] == GlobalResources::FILE_NAME_SEPARATOR) //last FILE_NAME_SEPARATOR found
+ {
+ if (i != int(filtername.length()) - 1) // "name\*"
+ {
+ fileFilter.insert(filtername);
+ directoryFilter.insert(filtername);
+ }
+ //else: "name\" -> not inserted directly
+
+ if (i > 0) // "name\*" or "name\": add "name" to directory filter
+ directoryFilter.insert(Zstring(filtername.c_str(), i));
+ }
+ else
+ {
+ fileFilter.insert(filtername);
+ directoryFilter.insert(filtername);
+ }
+ }
+}
+
+
+inline
+bool matchesFilter(const Zstring& name, const std::set<Zstring>& filter)
+{
+ for (std::set<Zstring>::iterator j = filter.begin(); j != filter.end(); ++j)
+ if (name.Matches(*j))
+ return true;
+
+ return false;
+}
+
+
+void FreeFileSync::filterGridData(FolderComparison& folderCmp, const wxString& includeFilter, const wxString& excludeFilter)
+{
+ //no need for regular expressions! In tests wxRegex was by factor of 10 slower than wxString::Matches()!!
+
+ //load filter into vectors of strings
+ //delimiters may be ';' or '\n'
+ std::vector<Zstring> includeList = compoundStringToFilter(includeFilter.c_str());
+ std::vector<Zstring> excludeList = compoundStringToFilter(excludeFilter.c_str());
+
+//##############################################################
+ //setup include/exclude filters for files and directories
+ std::set<Zstring> filterFileIn;
+ std::set<Zstring> filterFolderIn;
+ for (std::vector<Zstring>::iterator i = includeList.begin(); i != includeList.end(); ++i)
+ {
+ formatFilterString(*i); //format entry
+ addFilterEntry(*i, filterFileIn, filterFolderIn);
+ }
+
+ std::set<Zstring> filterFileEx;
+ std::set<Zstring> filterFolderEx;
+ for (std::vector<Zstring>::iterator i = excludeList.begin(); i != excludeList.end(); ++i)
+ {
+ formatFilterString(*i); //format entry
+ addFilterEntry(*i, filterFileEx, filterFolderEx);
+ }
+
+//##############################################################
+
+ //execute filtering...
+ for (FolderComparison::iterator j = folderCmp.begin(); j != folderCmp.end(); ++j)
+ {
+ FileComparison& fileCmp = j->fileCmp;
+
+ for (FileComparison::iterator i = fileCmp.begin(); i != fileCmp.end(); ++i)
+ {
+ Zstring filenameLeft = i->fileDescrLeft.fullName;
+ Zstring filenameRight = i->fileDescrRight.fullName;
+
+ formatFilterString(filenameLeft);
+ formatFilterString(filenameRight);
+
+
+ //left hand side
+ if (i->fileDescrLeft.objType == FileDescrLine::TYPE_FILE)
+ {
+ if ( !matchesFilter(filenameLeft, filterFileIn) || //process include filters
+ matchesFilter(filenameLeft, filterFileEx)) //process exclude filters
+ {
+ i->selectedForSynchronization = false;
+ continue;
+ }
+ }
+ else if (i->fileDescrLeft.objType == FileDescrLine::TYPE_DIRECTORY)
+ {
+ if ( !matchesFilter(filenameLeft, filterFolderIn) || //process include filters
+ matchesFilter(filenameLeft, filterFolderEx)) //process exclude filters
+ {
+ i->selectedForSynchronization = false;
+ continue;
+ }
+ }
+
+ //right hand side
+ if (i->fileDescrRight.objType == FileDescrLine::TYPE_FILE)
+ {
+ if ( !matchesFilter(filenameRight, filterFileIn) || //process include filters
+ matchesFilter(filenameRight, filterFileEx)) //process exclude filters
+ {
+ i->selectedForSynchronization = false;
+ continue;
+ }
+ }
+ else if (i->fileDescrRight.objType == FileDescrLine::TYPE_DIRECTORY)
+ {
+ if ( !matchesFilter(filenameRight, filterFolderIn) || //process include filters
+ matchesFilter(filenameRight, filterFolderEx)) //process exclude filters
+ {
+ i->selectedForSynchronization = false;
+ continue;
+ }
+ }
+
+ i->selectedForSynchronization = true;
+ }
+ }
+}
+
+
+template <bool includeRows>
+inline
+void inOrExcludeAllRows(FreeFileSync::FolderComparison& folderCmp)
+{
+ //remove all filters on folderCmp
+ for (FreeFileSync::FolderComparison::iterator j = folderCmp.begin(); j != folderCmp.end(); ++j)
+ {
+ FreeFileSync::FileComparison& fileCmp = j->fileCmp;
+ for (FreeFileSync::FileComparison::iterator i = fileCmp.begin(); i != fileCmp.end(); ++i)
+ i->selectedForSynchronization = includeRows;
+ }
+}
+
+
+void FreeFileSync::includeAllRowsOnGrid(FolderComparison& folderCmp)
+{
+ //remove all filters on currentGridData
+ inOrExcludeAllRows<true>(folderCmp);
+}
+
+
+void FreeFileSync::excludeAllRowsOnGrid(FolderComparison& folderCmp)
+{
+ //exclude all rows on currentGridData
+ inOrExcludeAllRows<false>(folderCmp);
+}
diff --git a/library/filter.h b/library/filter.h
new file mode 100644
index 00000000..3d0598e1
--- /dev/null
+++ b/library/filter.h
@@ -0,0 +1,15 @@
+#ifndef FFS_FILTER_H_INCLUDED
+#define FFS_FILTER_H_INCLUDED
+
+#include "../structures.h"
+
+
+namespace FreeFileSync
+{
+ void filterGridData(FolderComparison& folderCmp, const wxString& includeFilter, const wxString& excludeFilter);
+ void includeAllRowsOnGrid(FolderComparison& folderCmp);
+ void excludeAllRowsOnGrid(FolderComparison& folderCmp);
+}
+
+
+#endif // FFS_FILTER_H_INCLUDED
diff --git a/library/globalFunctions.cpp b/library/globalFunctions.cpp
index 4b387293..7c4a1b92 100644
--- a/library/globalFunctions.cpp
+++ b/library/globalFunctions.cpp
@@ -2,6 +2,7 @@
#include "resources.h"
#include <wx/msgdlg.h>
#include <wx/file.h>
+#include <fstream>
std::string globalFunctions::numberToString(const unsigned int number)
@@ -20,6 +21,14 @@ std::string globalFunctions::numberToString(const int number)
}
+std::string globalFunctions::numberToString(const long number)
+{
+ char result[100];
+ sprintf(result, "%ld", number);
+ return std::string(result);
+}
+
+
std::string globalFunctions::numberToString(const float number)
{
char result[100];
@@ -52,14 +61,18 @@ int globalFunctions::stringToInt(const std::string& number)
}
-inline
+long globalFunctions::stringToLong(const std::string& number)
+{
+ return atol(number.c_str());
+}
+
+
double globalFunctions::stringToDouble(const std::string& number)
{
return atof(number.c_str());
}
-inline
int globalFunctions::wxStringToInt(const wxString& number)
{
long result = 0;
@@ -70,7 +83,6 @@ int globalFunctions::wxStringToInt(const wxString& number)
}
-inline
double globalFunctions::wxStringToDouble(const wxString& number)
{
double result = 0;
diff --git a/library/globalFunctions.h b/library/globalFunctions.h
index 98e8cd1c..622babcb 100644
--- a/library/globalFunctions.h
+++ b/library/globalFunctions.h
@@ -6,10 +6,9 @@
#include <vector>
#include <set>
#include <wx/string.h>
-#include <fstream>
#include <wx/stream.h>
#include <wx/stopwatch.h>
-
+#include <wx/longlong.h>
namespace globalFunctions
{
@@ -28,6 +27,7 @@ namespace globalFunctions
std::string numberToString(const unsigned int number); //convert number to string
std::string numberToString(const int number); //convert number to string
+ std::string numberToString(const long number); //convert number to string
std::string numberToString(const float number); //convert number to string
wxString numberToWxString(const unsigned int number); //convert number to wxString
@@ -35,6 +35,7 @@ namespace globalFunctions
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
@@ -47,6 +48,12 @@ namespace globalFunctions
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());
+ }
}
@@ -54,7 +61,7 @@ namespace globalFunctions
class Performance
{
public:
- wxDEPRECATED(Performance()); //generates compiler warnings as a reminder to remove code after measurements
+ wxDEPRECATED(Performance()); //generate compiler warnings as a reminder to remove code after measurements
~Performance();
void showResult();
@@ -113,26 +120,26 @@ private:
template <class T>
void removeRowsFromVector(std::vector<T>& grid, const std::set<int>& rowsToRemove)
{
- std::vector<T> temp;
- int rowToSkip = -1; //keep it an INT!
-
- std::set<int>::iterator rowToSkipIndex = rowsToRemove.begin();
+ if (rowsToRemove.size() > 0)
+ {
+ std::vector<T> temp;
- if (rowToSkipIndex != rowsToRemove.end())
- rowToSkip = *rowToSkipIndex;
+ std::set<int>::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
+ for (int i = 0; i < int(grid.size()); ++i)
{
- ++rowToSkipIndex;
- if (rowToSkipIndex != rowsToRemove.end())
- rowToSkip = *rowToSkipIndex;
+ if (i != rowToSkip)
+ temp.push_back(grid[i]);
+ else
+ {
+ ++rowToSkipIndex;
+ if (rowToSkipIndex != rowsToRemove.end())
+ rowToSkip = *rowToSkipIndex;
+ }
}
+ grid.swap(temp);
}
- grid.swap(temp);
}
diff --git a/library/misc.cpp b/library/localization.cpp
index 0d5761d6..328f37be 100644
--- a/library/misc.cpp
+++ b/library/localization.cpp
@@ -1,7 +1,39 @@
-#include "misc.h"
+#include "localization.h"
#include <wx/msgdlg.h>
#include "resources.h"
#include "globalFunctions.h"
+#include <fstream>
+#include <set>
+
+//_("Browse") <- dummy string for wxDirPickerCtrl to be recognized by automatic text extraction!
+
+
+struct TranslationLine
+{
+ wxString original;
+ wxString translation;
+
+ bool operator<(const TranslationLine& b) const
+ {
+ return (original < b.original);
+ }
+};
+
+class Translation : public std::set<TranslationLine> {};
+
+
+CustomLocale::CustomLocale() :
+ wxLocale(),
+ currentLanguage(wxLANGUAGE_ENGLISH)
+{
+ translationDB = new Translation;
+}
+
+
+CustomLocale::~CustomLocale()
+{
+ delete translationDB;
+}
inline
@@ -49,12 +81,6 @@ void exchangeEscapeChars(wxString& data)
}
-CustomLocale::CustomLocale() :
- wxLocale(),
- currentLanguage(wxLANGUAGE_ENGLISH)
-{}
-
-
void CustomLocale::setLanguage(const int language)
{
currentLanguage = language;
@@ -89,6 +115,9 @@ void CustomLocale::setLanguage(const int language)
case wxLANGUAGE_PORTUGUESE:
languageFile = "Languages/portuguese.lng";
break;
+ case wxLANGUAGE_PORTUGUESE_BRAZILIAN:
+ languageFile = "Languages/portuguese_br.lng";
+ break;
case wxLANGUAGE_SLOVENIAN:
languageFile = "Languages/slovenian.lng";
break;
@@ -100,7 +129,7 @@ void CustomLocale::setLanguage(const int language)
currentLanguage = wxLANGUAGE_ENGLISH;
}
- static bool initialized = false; //wxLocale is a "static" too!
+ static bool initialized = false; //wxLocale is a global too!
if (!initialized)
{
initialized = true;
@@ -108,7 +137,7 @@ void CustomLocale::setLanguage(const int language)
}
//load language file into buffer
- translationDB.clear();
+ translationDB->clear();
const int bufferSize = 100000;
char temp[bufferSize];
if (!languageFile.empty())
@@ -137,7 +166,7 @@ void CustomLocale::setLanguage(const int language)
else
{
currentLine.translation = formattedString;
- translationDB.insert(currentLine);
+ translationDB->insert(currentLine);
}
}
langFile.close();
@@ -160,8 +189,8 @@ const wxChar* CustomLocale::GetString(const wxChar* szOrigString, const wxChar*
currentLine.original = szOrigString;
//look for translation in buffer table
- Translation::iterator i;
- if ((i = translationDB.find(currentLine)) != translationDB.end())
+ const Translation::iterator i = translationDB->find(currentLine);
+ if (i != translationDB->end())
return i->translation.c_str();
//fallback
diff --git a/library/localization.h b/library/localization.h
new file mode 100644
index 00000000..cf29d06d
--- /dev/null
+++ b/library/localization.h
@@ -0,0 +1,32 @@
+#ifndef MISC_H_INCLUDED
+#define MISC_H_INCLUDED
+
+#include <wx/intl.h>
+
+class Translation;
+
+
+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;
+
+ static const std::string FfsLanguageDat;
+
+private:
+ Translation* translationDB;
+ int currentLanguage;
+};
+
+
+#endif // MISC_H_INCLUDED
diff --git a/library/misc.h b/library/misc.h
deleted file mode 100644
index c3e960ff..00000000
--- a/library/misc.h
+++ /dev/null
@@ -1,54 +0,0 @@
-#ifndef MISC_H_INCLUDED
-#define MISC_H_INCLUDED
-
-#include <wx/string.h>
-#include <set>
-#include <wx/intl.h>
-#include <wx/panel.h>
-
-
-struct TranslationLine
-{
- wxString original;
- wxString translation;
-
- bool operator>(const TranslationLine& b ) const
- {
- return (original > b.original);
- }
- bool operator<(const TranslationLine& b) const
- {
- return (original < b.original);
- }
- bool operator==(const TranslationLine& b) const
- {
- return (original == b.original);
- }
-};
-typedef std::set<TranslationLine> Translation;
-
-
-class CustomLocale : public wxLocale
-{
-public:
- CustomLocale();
- ~CustomLocale() {}
-
- void setLanguage(const int language);
-
- int getLanguage()
- {
- return currentLanguage;
- }
-
- const wxChar* GetString(const wxChar* szOrigString, const wxChar* szDomain = NULL) const;
-
- static const std::string FfsLanguageDat;
-
-private:
- Translation translationDB;
- int currentLanguage;
-};
-
-
-#endif // MISC_H_INCLUDED
diff --git a/library/multithreading.cpp b/library/multithreading.cpp
index 106d1aa7..6c2612f1 100644
--- a/library/multithreading.cpp
+++ b/library/multithreading.cpp
@@ -1,4 +1,5 @@
#include "multithreading.h"
+#include "statusHandler.h"
#include <wx/utils.h>
//#include <wx/msw/wrapwin.h> //includes "windows.h"
diff --git a/library/multithreading.h b/library/multithreading.h
index bf0da145..21c5bcf2 100644
--- a/library/multithreading.h
+++ b/library/multithreading.h
@@ -1,9 +1,9 @@
#ifndef MULTITHREADING_H_INCLUDED
#define MULTITHREADING_H_INCLUDED
-#include "statusHandler.h"
#include <wx/thread.h>
+class StatusHandler;
class WorkerThread;
diff --git a/library/processXml.cpp b/library/processXml.cpp
index 00737633..c16b9d76 100644
--- a/library/processXml.cpp
+++ b/library/processXml.cpp
@@ -3,11 +3,18 @@
#include <wx/ffile.h>
#include <wx/intl.h>
#include "globalFunctions.h"
+#include "tinyxml/tinyxml.h"
#ifdef FFS_WIN
#include <wx/msw/wrapwin.h> //includes "windows.h"
#endif
+using namespace FreeFileSync;
+
+const wxString xmlAccess::LAST_CONFIG_FILE = wxT("LastRun.ffs_gui");
+const wxString xmlAccess::GLOBAL_CONFIG_FILE = wxT("GlobalSettings.xml");
+
+
//small helper functions
bool readXmlElementValue(std::string& output, const TiXmlElement* parent, const std::string& name);
bool readXmlElementValue(int& output, const TiXmlElement* parent, const std::string& name);
@@ -151,18 +158,18 @@ xmlAccess::XmlBatchConfig xmlAccess::readBatchConfig(const wxString& filename)
xmlAccess::XmlGlobalSettings xmlAccess::readGlobalSettings()
{
//load XML
- if (!wxFileExists(FreeFileSync::GLOBAL_CONFIG_FILE))
- throw FileError(Zstring(_("File does not exist:")) + wxT(" \"") + FreeFileSync::GLOBAL_CONFIG_FILE.c_str() + wxT("\""));
+ if (!wxFileExists(xmlAccess::GLOBAL_CONFIG_FILE))
+ throw FileError(Zstring(_("File does not exist:")) + wxT(" \"") + xmlAccess::GLOBAL_CONFIG_FILE.c_str() + wxT("\""));
- XmlConfigInput inputFile(FreeFileSync::GLOBAL_CONFIG_FILE, XML_GLOBAL_SETTINGS);
+ XmlConfigInput inputFile(xmlAccess::GLOBAL_CONFIG_FILE, XML_GLOBAL_SETTINGS);
XmlGlobalSettings outputCfg;
if (!inputFile.loadedSuccessfully())
- throw FileError(Zstring(_("Error reading file:")) + wxT(" \"") + FreeFileSync::GLOBAL_CONFIG_FILE.c_str() + wxT("\""));
+ throw FileError(Zstring(_("Error reading file:")) + wxT(" \"") + xmlAccess::GLOBAL_CONFIG_FILE.c_str() + wxT("\""));
if (!inputFile.readXmlGlobalSettings(outputCfg))
- throw FileError(Zstring(_("Error parsing configuration file:")) + wxT(" \"") + FreeFileSync::GLOBAL_CONFIG_FILE.c_str() + wxT("\""));
+ throw FileError(Zstring(_("Error parsing configuration file:")) + wxT(" \"") + xmlAccess::GLOBAL_CONFIG_FILE.c_str() + wxT("\""));
return outputCfg;
}
@@ -194,12 +201,12 @@ void xmlAccess::writeBatchConfig(const wxString& filename, const XmlBatchConfig&
void xmlAccess::writeGlobalSettings(const XmlGlobalSettings& outputCfg)
{
- XmlConfigOutput outputFile(FreeFileSync::GLOBAL_CONFIG_FILE, XML_GLOBAL_SETTINGS);
+ XmlConfigOutput outputFile(xmlAccess::GLOBAL_CONFIG_FILE, XML_GLOBAL_SETTINGS);
//populate and write XML tree
if ( !outputFile.writeXmlGlobalSettings(outputCfg) || //add GUI layout configuration settings
!outputFile.writeToFile()) //save XML
- throw FileError(Zstring(_("Error writing file:")) + wxT(" \"") + FreeFileSync::GLOBAL_CONFIG_FILE.c_str() + wxT("\""));
+ throw FileError(Zstring(_("Error writing file:")) + wxT(" \"") + xmlAccess::GLOBAL_CONFIG_FILE.c_str() + wxT("\""));
return;
}
@@ -273,6 +280,19 @@ bool readXmlElementValue(int& output, const TiXmlElement* parent, const std::str
}
+bool readXmlElementValue(long& output, const TiXmlElement* parent, const std::string& name)
+{
+ std::string temp;
+ if (readXmlElementValue(temp, parent, name))
+ {
+ output = globalFunctions::stringToLong(temp);
+ return true;
+ }
+ else
+ return false;
+}
+
+
bool readXmlElementValue(CompareVariant& output, const TiXmlElement* parent, const std::string& name)
{
int dummy = 0;
@@ -396,6 +416,7 @@ bool XmlConfigInput::readXmlMainConfig(MainConfiguration& mainCfg, std::vector<F
newPair.rightDirectory = wxString::FromUTF8(tempString.c_str()).c_str();
directoryPairs.push_back(newPair);
+
folderPair = folderPair->NextSiblingElement();
}
@@ -435,17 +456,11 @@ bool XmlConfigInput::readXmlGuiConfig(xmlAccess::XmlGuiConfig& outputCfg)
TiXmlHandle hRoot(root);
- //read GUI layout
- TiXmlElement* mainWindow = hRoot.FirstChild("GuiConfig").FirstChild("Windows").FirstChild("Main").ToElement();
- if (mainWindow)
- {
- readXmlElementValue(outputCfg.hideFilteredElements, mainWindow, "HideFiltered");
- }
-
-
TiXmlElement* guiConfig = hRoot.FirstChild("GuiConfig").ToElement();
if (guiConfig)
{
+ readXmlElementValue(outputCfg.hideFilteredElements, guiConfig, "HideFiltered");
+
readXmlElementValue(outputCfg.ignoreErrors, guiConfig, "IgnoreErrors");
}
@@ -506,6 +521,9 @@ bool XmlConfigInput::readXmlGlobalSettings(xmlAccess::XmlGlobalSettings& outputC
//copy symbolic links to files
readXmlElementValue(outputCfg.shared.copyFileSymlinks, global, "CopyFileSymlinks");
+
+ //last update check
+ readXmlElementValue(outputCfg.shared.lastUpdateCheck, global, "LastCheckForUpdates");
}
TiXmlElement* warnings = hRoot.FirstChild("Shared").FirstChild("Warnings").ToElement();
@@ -516,6 +534,9 @@ bool XmlConfigInput::readXmlGlobalSettings(xmlAccess::XmlGlobalSettings& outputC
//significant difference check
readXmlElementValue(outputCfg.shared.warningSignificantDifference, warnings, "CheckForSignificantDifference");
+
+ //check free disk space
+ readXmlElementValue(outputCfg.shared.warningNotEnoughDiskSpace, warnings, "CheckForFreeDiskSpace");
}
//gui specific global settings (optional)
@@ -532,6 +553,7 @@ bool XmlConfigInput::readXmlGlobalSettings(xmlAccess::XmlGlobalSettings& outputC
readXmlElementValue(outputCfg.gui.deleteOnBothSides, mainWindow, "ManualDeletionOnBothSides");
readXmlElementValue(outputCfg.gui.useRecyclerForManualDeletion, mainWindow, "ManualDeletionUseRecycler");
readXmlElementValue(outputCfg.gui.showFileIcons, mainWindow, "ShowFileIcons");
+ readXmlElementValue(outputCfg.gui.popupOnConfigChange, mainWindow, "PopupOnConfigChange");
//###########################################################
//read column attributes
@@ -585,10 +607,28 @@ bool XmlConfigInput::readXmlGlobalSettings(xmlAccess::XmlGlobalSettings& outputC
//load folder history elements
const TiXmlElement* historyLeft = TiXmlHandle(mainWindow).FirstChild("FolderHistoryLeft").ToElement();
+ if (historyLeft)
+ {
+ //load max. history size
+ const char* histSizeMax = historyLeft->Attribute("MaximumSize");
+ if (histSizeMax) //may be NULL!
+ outputCfg.gui.folderHistLeftMax = globalFunctions::stringToInt(histSizeMax);
+
+ //load config history elements
+ readXmlElementTable(outputCfg.gui.folderHistoryLeft, historyLeft, "Folder");
+ }
+
const TiXmlElement* historyRight = TiXmlHandle(mainWindow).FirstChild("FolderHistoryRight").ToElement();
+ if (historyRight)
+ {
+ //load max. history size
+ const char* histSizeMax = historyRight->Attribute("MaximumSize");
+ if (histSizeMax) //may be NULL!
+ outputCfg.gui.folderHistRightMax = globalFunctions::stringToInt(histSizeMax);
- readXmlElementTable(outputCfg.gui.folderHistoryLeft, historyLeft, "Folder");
- readXmlElementTable(outputCfg.gui.folderHistoryRight, historyRight, "Folder");
+ //load config history elements
+ readXmlElementTable(outputCfg.gui.folderHistoryRight, historyRight, "Folder");
+ }
}
TiXmlElement* gui = hRoot.FirstChild("Gui").ToElement();
@@ -607,7 +647,7 @@ bool XmlConfigInput::readXmlGlobalSettings(xmlAccess::XmlGlobalSettings& outputC
//load max. history size
const char* histSizeMax = cfgHistory->Attribute("MaximumSize");
if (histSizeMax) //may be NULL!
- outputCfg.gui.cfgHistoryMaxItems = globalFunctions::stringToInt(histSizeMax);
+ outputCfg.gui.cfgHistoryMax = globalFunctions::stringToInt(histSizeMax);
//load config history elements
readXmlElementTable(outputCfg.gui.cfgFileHistory, cfgHistory, "File");
@@ -649,7 +689,7 @@ XmlConfigOutput::XmlConfigOutput(const wxString& fileName, const xmlAccess::XmlT
bool XmlConfigOutput::writeToFile()
{
//workaround to get a FILE* from a unicode filename
- wxFFile dummyFile(m_fileName, wxT("wb")); //save in binary mode for Linux portability of config files
+ wxFFile dummyFile(m_fileName, wxT("w")); //no need for "binary" mode here
if (!dummyFile.IsOpened())
return false;
@@ -676,6 +716,12 @@ void addXmlElement(TiXmlElement* parent, const std::string& name, const int valu
}
+void addXmlElement(TiXmlElement* parent, const std::string& name, const long value)
+{
+ addXmlElement(parent, name, globalFunctions::numberToString(value));
+}
+
+
void addXmlElement(TiXmlElement* parent, const std::string& name, const SyncConfiguration::Direction value)
{
if (value == SyncConfiguration::SYNC_DIR_LEFT)
@@ -794,13 +840,7 @@ bool XmlConfigOutput::writeXmlGuiConfig(const xmlAccess::XmlGuiConfig& inputCfg)
TiXmlElement* guiConfig = new TiXmlElement("GuiConfig");
root->LinkEndChild(guiConfig);
- TiXmlElement* windows = new TiXmlElement("Windows");
- guiConfig->LinkEndChild(windows);
-
- TiXmlElement* mainWindow = new TiXmlElement("Main");
- windows->LinkEndChild(mainWindow);
-
- addXmlElement(mainWindow, "HideFiltered", inputCfg.hideFilteredElements);
+ addXmlElement(guiConfig, "HideFiltered", inputCfg.hideFilteredElements);
addXmlElement(guiConfig, "IgnoreErrors", inputCfg.ignoreErrors);
@@ -851,6 +891,9 @@ bool XmlConfigOutput::writeXmlGlobalSettings(const xmlAccess::XmlGlobalSettings&
//copy symbolic links to files
addXmlElement(global, "CopyFileSymlinks", inputCfg.shared.copyFileSymlinks);
+ //last update check
+ addXmlElement(global, "LastCheckForUpdates", inputCfg.shared.lastUpdateCheck);
+
//warnings
TiXmlElement* warnings = new TiXmlElement("Warnings");
global->LinkEndChild(warnings);
@@ -861,6 +904,9 @@ bool XmlConfigOutput::writeXmlGlobalSettings(const xmlAccess::XmlGlobalSettings&
//significant difference check
addXmlElement(warnings, "CheckForSignificantDifference", inputCfg.shared.warningSignificantDifference);
+ //check free disk space
+ addXmlElement(warnings, "CheckForFreeDiskSpace", inputCfg.shared.warningNotEnoughDiskSpace);
+
//###################################################################
//write global gui settings
@@ -884,7 +930,8 @@ bool XmlConfigOutput::writeXmlGlobalSettings(const xmlAccess::XmlGlobalSettings&
addXmlElement(mainWindow, "ManualDeletionOnBothSides", inputCfg.gui.deleteOnBothSides);
addXmlElement(mainWindow, "ManualDeletionUseRecycler", inputCfg.gui.useRecyclerForManualDeletion);
- addXmlElement(mainWindow, "ShowFileIcons", inputCfg.gui.showFileIcons);
+ addXmlElement(mainWindow, "ShowFileIcons" , inputCfg.gui.showFileIcons);
+ addXmlElement(mainWindow, "PopupOnConfigChange" , inputCfg.gui.popupOnConfigChange);
//write column attributes
TiXmlElement* leftColumn = new TiXmlElement("LeftColumns");
@@ -925,7 +972,10 @@ bool XmlConfigOutput::writeXmlGlobalSettings(const xmlAccess::XmlGlobalSettings&
TiXmlElement* historyRight = new TiXmlElement("FolderHistoryRight");
mainWindow->LinkEndChild(historyRight);
+ historyLeft->SetAttribute("MaximumSize", globalFunctions::numberToString(inputCfg.gui.folderHistLeftMax));
addXmlElementTable(historyLeft, "Folder", inputCfg.gui.folderHistoryLeft);
+
+ historyRight->SetAttribute("MaximumSize", globalFunctions::numberToString(inputCfg.gui.folderHistRightMax));
addXmlElementTable(historyRight, "Folder", inputCfg.gui.folderHistoryRight);
@@ -936,7 +986,7 @@ bool XmlConfigOutput::writeXmlGlobalSettings(const xmlAccess::XmlGlobalSettings&
TiXmlElement* cfgHistory = new TiXmlElement("ConfigHistory");
gui->LinkEndChild(cfgHistory);
- cfgHistory->SetAttribute("MaximumSize", globalFunctions::numberToString(inputCfg.gui.cfgHistoryMaxItems));
+ cfgHistory->SetAttribute("MaximumSize", globalFunctions::numberToString(inputCfg.gui.cfgHistoryMax));
addXmlElementTable(cfgHistory, "File", inputCfg.gui.cfgFileHistory);
//###################################################################
@@ -988,10 +1038,6 @@ int xmlAccess::retrieveSystemLanguage()
case wxLANGUAGE_CHINESE_TAIWAN:
return wxLANGUAGE_CHINESE_SIMPLIFIED;
- //variants of wxLANGUAGE_PORTUGUESE
- case wxLANGUAGE_PORTUGUESE_BRAZILIAN:
- return wxLANGUAGE_PORTUGUESE;
-
//variants of wxLANGUAGE_SPANISH
case wxLANGUAGE_SPANISH_ARGENTINA:
case wxLANGUAGE_SPANISH_BOLIVIA:
@@ -1019,6 +1065,8 @@ int xmlAccess::retrieveSystemLanguage()
//case wxLANGUAGE_POLISH:
//case wxLANGUAGE_SLOVENIAN:
//case wxLANGUAGE_HUNGARIAN:
+ //case wxLANGUAGE_PORTUGUESE:
+ //case wxLANGUAGE_PORTUGUESE_BRAZILIAN:
default:
return lang;
@@ -1048,4 +1096,5 @@ void xmlAccess::XmlGlobalSettings::_Shared::resetWarnings()
{
warningDependentFolders = true;
warningSignificantDifference = true;
+ warningNotEnoughDiskSpace = true;
}
diff --git a/library/processXml.h b/library/processXml.h
index c24ad0e7..763edbca 100644
--- a/library/processXml.h
+++ b/library/processXml.h
@@ -1,14 +1,14 @@
#ifndef PROCESSXML_H_INCLUDED
#define PROCESSXML_H_INCLUDED
-#include "../FreeFileSync.h"
-#include "tinyxml/tinyxml.h"
-
-using namespace FreeFileSync;
-
+#include "../structures.h"
+#include "fileHandling.h"
namespace xmlAccess
{
+ extern const wxString LAST_CONFIG_FILE;
+ extern const wxString GLOBAL_CONFIG_FILE;
+
enum OnError
{
ON_ERROR_POPUP,
@@ -30,9 +30,10 @@ namespace xmlAccess
REL_PATH,
SIZE,
DATE,
- FULL_NAME
+ FULL_NAME,
+ DIRECTORY
};
- const unsigned COLUMN_TYPE_COUNT = 5;
+ const unsigned COLUMN_TYPE_COUNT = 6;
struct ColumnAttrib
{
@@ -48,13 +49,28 @@ namespace xmlAccess
struct XmlGuiConfig
{
- XmlGuiConfig() : hideFilteredElements(false), ignoreErrors(false) {} //initialize values
+ XmlGuiConfig() :
+ hideFilteredElements(false),
+ ignoreErrors(false) {} //initialize values
- MainConfiguration mainCfg;
- std::vector<FolderPair> directoryPairs;
+ FreeFileSync::MainConfiguration mainCfg;
+ std::vector<FreeFileSync::FolderPair> directoryPairs;
bool hideFilteredElements;
bool ignoreErrors; //reaction on error situation during synchronization
+
+ bool operator==(const XmlGuiConfig& other) const
+ {
+ return mainCfg == other.mainCfg &&
+ directoryPairs == other.directoryPairs &&
+ hideFilteredElements == other.hideFilteredElements &&
+ ignoreErrors == other.ignoreErrors;
+ }
+
+ bool operator!=(const XmlGuiConfig& other) const
+ {
+ return !(*this == other);
+ }
};
@@ -62,8 +78,8 @@ namespace xmlAccess
{
XmlBatchConfig() : silent(false), handleError(ON_ERROR_POPUP) {}
- MainConfiguration mainCfg;
- std::vector<FolderPair> directoryPairs;
+ FreeFileSync::MainConfiguration mainCfg;
+ std::vector<FreeFileSync::FolderPair> directoryPairs;
bool silent;
OnError handleError; //reaction on error situation during synchronization
@@ -83,7 +99,8 @@ namespace xmlAccess
programLanguage(retrieveSystemLanguage()),
fileTimeTolerance(2), //default 2s: FAT vs NTFS
traverseDirectorySymlinks(false),
- copyFileSymlinks(supportForSymbolicLinks())
+ copyFileSymlinks(supportForSymbolicLinks()),
+ lastUpdateCheck(0)
{
resetWarnings();
}
@@ -92,12 +109,14 @@ namespace xmlAccess
unsigned fileTimeTolerance; //max. allowed file time deviation
bool traverseDirectorySymlinks;
bool copyFileSymlinks; //copy symbolic link instead of target file
+ long lastUpdateCheck; //time of last update check
//warnings
void resetWarnings();
bool warningDependentFolders;
bool warningSignificantDifference;
+ bool warningNotEnoughDiskSpace;
} shared;
//---------------------------------------------------------------------
@@ -114,10 +133,13 @@ namespace xmlAccess
#elif defined FFS_LINUX
commandLineFileManager(wxT("konqueror \"%path\"")),
#endif
- cfgHistoryMaxItems(10),
+ cfgHistoryMax(10),
+ folderHistLeftMax(12),
+ folderHistRightMax(12),
deleteOnBothSides(false),
useRecyclerForManualDeletion(FreeFileSync::recycleBinExists()), //enable if OS supports it; else user will have to activate first and then get an error message
- showFileIcons(true) {}
+ showFileIcons(true),
+ popupOnConfigChange(true) {}
int widthNotMaximized;
int heightNotMaximized;
@@ -127,14 +149,22 @@ namespace xmlAccess
ColumnAttributes columnAttribLeft;
ColumnAttributes columnAttribRight;
+
wxString commandLineFileManager;
+
std::vector<wxString> cfgFileHistory;
- unsigned cfgHistoryMaxItems;
+ unsigned int cfgHistoryMax;
+
std::vector<wxString> folderHistoryLeft;
+ unsigned int folderHistLeftMax;
+
std::vector<wxString> folderHistoryRight;
+ unsigned int folderHistRightMax;
+
bool deleteOnBothSides;
bool useRecyclerForManualDeletion;
bool showFileIcons;
+ bool popupOnConfigChange;
} gui;
//---------------------------------------------------------------------
diff --git a/library/resources.cpp b/library/resources.cpp
index f8624ed3..23d1ac37 100644
--- a/library/resources.cpp
+++ b/library/resources.cpp
@@ -83,7 +83,6 @@ GlobalResources::GlobalResources()
bitmapResource[wxT("statusComparing.png")] = (bitmapStatusComparing = new wxBitmap(wxNullBitmap));
bitmapResource[wxT("statusSyncing.png")] = (bitmapStatusSyncing = new wxBitmap(wxNullBitmap));
bitmapResource[wxT("logo.png")] = (bitmapLogo = new wxBitmap(wxNullBitmap));
- bitmapResource[wxT("finished.png")] = (bitmapFinished = new wxBitmap(wxNullBitmap));
bitmapResource[wxT("statusEdge.png")] = (bitmapStatusEdge = new wxBitmap(wxNullBitmap));
bitmapResource[wxT("add pair.png")] = (bitmapAddFolderPair = new wxBitmap(wxNullBitmap));
bitmapResource[wxT("remove pair.png")] = (bitmapRemoveFolderPair = new wxBitmap(wxNullBitmap));
@@ -105,12 +104,25 @@ GlobalResources::GlobalResources()
bitmapResource[wxT("settings_small.png")] = (bitmapSettingsSmall = new wxBitmap(wxNullBitmap));
bitmapResource[wxT("recycler.png")] = (bitmapRecycler = new wxBitmap(wxNullBitmap));
bitmapResource[wxT("shift.png")] = (bitmapShift = new wxBitmap(wxNullBitmap));
+ bitmapResource[wxT("question.png")] = (bitmapQuestion = new wxBitmap(wxNullBitmap));
+ bitmapResource[wxT("china.png")] = (bitmapChina = new wxBitmap(wxNullBitmap));
+ bitmapResource[wxT("holland.png")] = (bitmapHolland = new wxBitmap(wxNullBitmap));
+ bitmapResource[wxT("england.png")] = (bitmapEngland = new wxBitmap(wxNullBitmap));
+ 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("italy.png")] = (bitmapItaly = new wxBitmap(wxNullBitmap));
+ bitmapResource[wxT("japan.png")] = (bitmapJapan = new wxBitmap(wxNullBitmap));
+ bitmapResource[wxT("poland.png")] = (bitmapPoland = new wxBitmap(wxNullBitmap));
+ bitmapResource[wxT("portugal.png")] = (bitmapPortugal = new wxBitmap(wxNullBitmap));
+ bitmapResource[wxT("brazil.png")] = (bitmapBrazil = new wxBitmap(wxNullBitmap));
+ bitmapResource[wxT("slovakia.png")] = (bitmapSlovakia = new wxBitmap(wxNullBitmap));
+ bitmapResource[wxT("spain.png")] = (bitmapSpain = new wxBitmap(wxNullBitmap));
//init all the other resource files
animationMoney = new wxAnimation(wxNullAnimation);
animationSync = new wxAnimation(wxNullAnimation);
-
- programIcon = &wxNullIcon;
+ programIcon = new wxIcon(wxNullIcon);
}
@@ -159,7 +171,7 @@ void GlobalResources::load()
std::map<wxString, wxBitmap*>::iterator bmp;
while ((entry = resourceFile.GetNextEntry()))
{
- wxString name = entry->GetName();
+ const wxString name = entry->GetName();
//search if entry is available in map
if ((bmp = bitmapResource.find(name)) != bitmapResource.end())
@@ -172,9 +184,9 @@ void GlobalResources::load()
}
#ifdef FFS_WIN
- programIcon = new wxIcon(wxT("ffsIcon1"));
+ *programIcon = wxIcon(wxT("ffsIcon1"));
#else
#include "FreeFileSync.xpm"
- programIcon = new wxIcon(FreeFileSync_xpm);
+ *programIcon = wxIcon(FreeFileSync_xpm);
#endif
}
diff --git a/library/resources.h b/library/resources.h
index 6bac0d86..5de79ec2 100644
--- a/library/resources.h
+++ b/library/resources.h
@@ -82,7 +82,6 @@ public:
wxBitmap* bitmapStatusComparing;
wxBitmap* bitmapStatusSyncing;
wxBitmap* bitmapLogo;
- wxBitmap* bitmapFinished;
wxBitmap* bitmapStatusEdge;
wxBitmap* bitmapAddFolderPair;
wxBitmap* bitmapRemoveFolderPair;
@@ -104,6 +103,20 @@ public:
wxBitmap* bitmapSettingsSmall;
wxBitmap* bitmapRecycler;
wxBitmap* bitmapShift;
+ wxBitmap* bitmapQuestion;
+ wxBitmap* bitmapChina;
+ wxBitmap* bitmapHolland;
+ wxBitmap* bitmapEngland;
+ wxBitmap* bitmapFrance;
+ wxBitmap* bitmapGermany;
+ wxBitmap* bitmapHungary;
+ wxBitmap* bitmapItaly;
+ wxBitmap* bitmapJapan;
+ wxBitmap* bitmapPoland;
+ wxBitmap* bitmapPortugal;
+ wxBitmap* bitmapBrazil;
+ wxBitmap* bitmapSlovakia;
+ wxBitmap* bitmapSpain;
wxAnimation* animationMoney;
wxAnimation* animationSync;
diff --git a/library/sorting.h b/library/sorting.h
deleted file mode 100644
index 171cca6d..00000000
--- a/library/sorting.h
+++ /dev/null
@@ -1,325 +0,0 @@
-#ifndef SORTING_H_INCLUDED
-#define SORTING_H_INCLUDED
-
-#include "../FreeFileSync.h"
-#include "resources.h"
-#include "globalFunctions.h"
-
-using namespace FreeFileSync;
-
-enum SideToSort
-{
- SORT_ON_LEFT,
- SORT_ON_RIGHT,
-};
-
-
-template <SideToSort side>
-inline
-void getDescrLine(const FileCompareLine& a, const FileCompareLine& b, const FileDescrLine*& descrLineA, const FileDescrLine*& descrLineB)
-{
- if (side == SORT_ON_LEFT)
- {
- descrLineA = &a.fileDescrLeft;
- descrLineB = &b.fileDescrLeft;
- }
- else if (side == SORT_ON_RIGHT)
- {
- descrLineA = &a.fileDescrRight;
- descrLineB = &b.fileDescrRight;
- }
- else
- assert(false);
-}
-
-
-template <bool sortAscending>
-inline
-bool stringSmallerThan(const wxChar* stringA, const wxChar* stringB)
-{
-#ifdef FFS_WIN //case-insensitive comparison!
- return sortAscending ?
- FreeFileSync::compareStringsWin32(stringA, stringB) < 0 : //way faster than wxString::CmpNoCase() in windows build!!!
- FreeFileSync::compareStringsWin32(stringA, stringB) > 0;
-#else
- while (*stringA == *stringB)
- {
- if (*stringA == wxChar(0)) //strings are equal
- return false;
-
- ++stringA;
- ++stringB;
- }
- return sortAscending ? *stringA < *stringB : *stringA > *stringB; //wxChar(0) is handled correctly
-#endif
-}
-
-
-inline
-int compareString(const wxChar* stringA, const wxChar* stringB, const int lengthA, const int lengthB)
-{
-#ifdef FFS_WIN //case-insensitive comparison!
- return FreeFileSync::compareStringsWin32(stringA, stringB, lengthA, lengthB); //way faster than wxString::CmpNoCase() in the windows build!!!
-#else
- int i = 0;
- if (lengthA == lengthB)
- {
- for (i = 0; i < lengthA; ++i)
- {
- if (stringA[i] != stringB[i])
- break;
- }
- return i == lengthA ? 0 : stringA[i] < stringB[i] ? -1 : 1;
- }
- else if (lengthA < lengthB)
- {
- for (i = 0; i < lengthA; ++i)
- {
- if (stringA[i] != stringB[i])
- break;
- }
- return i == lengthA ? -1 : stringA[i] < stringB[i] ? -1 : 1;
- }
- else
- {
- for (i = 0; i < lengthB; ++i)
- {
- if (stringA[i] != stringB[i])
- break;
- }
- return i == lengthB ? 1 : stringA[i] < stringB[i] ? -1 : 1;
- }
-#endif
-}
-
-
-template <bool sortAscending, SideToSort side>
-inline
-bool sortByFileName(const FileCompareLine& a, const FileCompareLine& b)
-{
- const FileDescrLine* descrLineA = NULL;
- const FileDescrLine* descrLineB = NULL;
- getDescrLine<side>(a, b, descrLineA, descrLineB);
-
- //presort types: first files, then directories then empty rows
- if (descrLineA->objType == FileDescrLine::TYPE_NOTHING)
- return false; //empty rows always last
- else if (descrLineB->objType == FileDescrLine::TYPE_NOTHING)
- return true; //empty rows always last
-
-
- if (descrLineA->objType == FileDescrLine::TYPE_DIRECTORY) //sort directories by relative name
- {
- if (descrLineB->objType == FileDescrLine::TYPE_DIRECTORY)
- return stringSmallerThan<sortAscending>(descrLineA->relativeName.c_str(), descrLineB->relativeName.c_str());
- else
- return false;
- }
- else
- {
- if (descrLineB->objType == FileDescrLine::TYPE_DIRECTORY)
- return true;
- else
- {
- const wxChar* stringA = descrLineA->relativeName.c_str();
- const wxChar* stringB = descrLineB->relativeName.c_str();
-
- size_t pos = descrLineA->relativeName.findFromEnd(GlobalResources::FILE_NAME_SEPARATOR); //start search beginning from end
- if (pos != std::string::npos)
- stringA += pos + 1;
-
- pos = descrLineB->relativeName.findFromEnd(GlobalResources::FILE_NAME_SEPARATOR); //start search beginning from end
- if (pos != std::string::npos)
- stringB += pos + 1;
-
- return stringSmallerThan<sortAscending>(stringA, stringB);
- }
- }
-}
-
-
-template <bool sortAscending, SideToSort side>
-bool sortByRelativeName(const FileCompareLine& a, const FileCompareLine& b)
-{
- const FileDescrLine* descrLineA = NULL;
- const FileDescrLine* descrLineB = NULL;
- getDescrLine<side>(a, b, descrLineA, descrLineB);
-
- //extract relative name and filename
- const wxChar* relStringA = descrLineA->relativeName.c_str(); //mustn't be NULL for CompareString() API to work correctly
- const wxChar* fileStringA = relStringA;
- int relLengthA = 0;
- int fileLengthA = 0;
-
- if (descrLineA->objType == FileDescrLine::TYPE_DIRECTORY)
- relLengthA = descrLineA->relativeName.length();
- else if (descrLineA->objType == FileDescrLine::TYPE_FILE)
- {
- relLengthA = descrLineA->relativeName.findFromEnd(GlobalResources::FILE_NAME_SEPARATOR); //start search beginning from end
- if (relLengthA == wxNOT_FOUND)
- {
- relLengthA = 0;
- fileLengthA = descrLineA->relativeName.length();
- }
- else
- {
- fileStringA += relLengthA + 1;
- fileLengthA = descrLineA->relativeName.length() - (relLengthA + 1);
- }
- }
- else
- return false; //empty rows should be on end of list
-
-
- const wxChar* relStringB = descrLineB->relativeName.c_str(); //mustn't be NULL for CompareString() API to work correctly
- const wxChar* fileStringB = relStringB;
- int relLengthB = 0;
- int fileLengthB = 0;
-
- if (descrLineB->objType == FileDescrLine::TYPE_DIRECTORY)
- relLengthB = descrLineB->relativeName.length();
- else if (descrLineB->objType == FileDescrLine::TYPE_FILE)
- {
- relLengthB = descrLineB->relativeName.findFromEnd(GlobalResources::FILE_NAME_SEPARATOR); //start search beginning from end
- if (relLengthB == wxNOT_FOUND)
- {
- relLengthB = 0;
- fileLengthB = descrLineB->relativeName.length();
- }
- else
- {
- fileStringB += relLengthB + 1;
- fileLengthB = descrLineB->relativeName.length() - (relLengthB + 1);
- }
- }
- else
- return true; //empty rows should be on end of list
-
- //compare relative names without filenames first
- int rv = compareString(relStringA, relStringB, relLengthA, relLengthB);
- if (rv != 0)
- return sortAscending ? (rv < 0) : (rv > 0);
- else //compare the filenames
- {
- if (descrLineB->objType == FileDescrLine::TYPE_DIRECTORY) //directories shall appear before files
- return false;
- else if (descrLineA->objType == FileDescrLine::TYPE_DIRECTORY)
- return true;
-
- return sortAscending ?
- compareString(fileStringA, fileStringB, fileLengthA, fileLengthB) < 0 :
- compareString(fileStringA, fileStringB, fileLengthA, fileLengthB) > 0;
- }
-}
-
-
-template <bool sortAscending, SideToSort side>
-inline
-bool sortByFullName(const FileCompareLine& a, const FileCompareLine& b)
-{
- const FileDescrLine* descrLineA = NULL;
- const FileDescrLine* descrLineB = NULL;
- getDescrLine<side>(a, b, descrLineA, descrLineB);
-
- //presort types: first files, then directories then empty rows
- if (descrLineA->objType == FileDescrLine::TYPE_NOTHING)
- return false; //empty rows always last
- else if (descrLineB->objType == FileDescrLine::TYPE_NOTHING)
- return true; //empty rows always last
- else
-#ifdef FFS_WIN //case-insensitive comparison!
- return sortAscending ?
- FreeFileSync::compareStringsWin32(descrLineA->fullName.c_str(), descrLineB->fullName.c_str()) < 0 : //way faster than wxString::CmpNoCase() in windows build!!!
- FreeFileSync::compareStringsWin32(descrLineA->fullName.c_str(), descrLineB->fullName.c_str()) > 0;
-#else
- return sortAscending ?
- descrLineA->fullName.Cmp(descrLineB->fullName) < 0 :
- descrLineA->fullName.Cmp(descrLineB->fullName) > 0;
-#endif
-}
-
-
-template <bool sortAscending, SideToSort side>
-inline
-bool sortByFileSize(const FileCompareLine& a, const FileCompareLine& b)
-{
- const FileDescrLine* descrLineA = NULL;
- const FileDescrLine* descrLineB = NULL;
- getDescrLine<side>(a, b, descrLineA, descrLineB);
-
- //presort types: first files, then directories then empty rows
- if (descrLineA->objType == FileDescrLine::TYPE_NOTHING)
- return false; //empty rows always last
- else if (descrLineB->objType == FileDescrLine::TYPE_NOTHING)
- return true; //empty rows always last
-
-
- if (descrLineA->objType == FileDescrLine::TYPE_DIRECTORY) //sort directories by relative name
- {
- if (descrLineB->objType == FileDescrLine::TYPE_DIRECTORY)
- return stringSmallerThan<sortAscending>(descrLineA->relativeName.c_str(), descrLineB->relativeName.c_str());
- else
- return false;
- }
- else
- {
- if (descrLineB->objType == FileDescrLine::TYPE_DIRECTORY)
- return true;
- else
- return sortAscending ?
- descrLineA->fileSize > descrLineB->fileSize : //sortAscending == true shall result in list beginning with largest files first
- descrLineA->fileSize < descrLineB->fileSize;
- }
-}
-
-
-template <bool sortAscending, SideToSort side>
-inline
-bool sortByDate(const FileCompareLine& a, const FileCompareLine& b)
-{
- const FileDescrLine* descrLineA = NULL;
- const FileDescrLine* descrLineB = NULL;
- getDescrLine<side>(a, b, descrLineA, descrLineB);
-
- //presort types: first files, then directories then empty rows
- if (descrLineA->objType == FileDescrLine::TYPE_NOTHING)
- return false; //empty rows always last
- else if (descrLineB->objType == FileDescrLine::TYPE_NOTHING)
- return true; //empty rows always last
-
- if (descrLineA->objType == FileDescrLine::TYPE_DIRECTORY) //sort directories by relative name
- {
- if (descrLineB->objType == FileDescrLine::TYPE_DIRECTORY)
- return stringSmallerThan<sortAscending>(descrLineA->relativeName.c_str(), descrLineB->relativeName.c_str());
- else
- return false;
- }
- else
- {
- if (descrLineB->objType == FileDescrLine::TYPE_DIRECTORY)
- return true;
- else
- return sortAscending ?
- descrLineA->lastWriteTimeRaw < descrLineB->lastWriteTimeRaw :
- descrLineA->lastWriteTimeRaw > descrLineB->lastWriteTimeRaw;
- }
-}
-
-
-template <bool sortAscending>
-inline
-bool sortByCmpResult(const FileCompareLine& a, const FileCompareLine& b)
-{
- //presort result: equal shall appear at end of list
- if (a.cmpResult == FILE_EQUAL)
- return false;
- if (b.cmpResult == FILE_EQUAL)
- return true;
-
- return sortAscending ?
- a.cmpResult < b.cmpResult :
- a.cmpResult > b.cmpResult;
-}
-
-
-#endif // SORTING_H_INCLUDED
diff --git a/library/statistics.cpp b/library/statistics.cpp
new file mode 100644
index 00000000..ec03c59e
--- /dev/null
+++ b/library/statistics.cpp
@@ -0,0 +1,320 @@
+#include "statistics.h"
+
+#include <wx/ffile.h>
+#include "globalFunctions.h"
+#include "statusHandler.h"
+#include "../algorithm.h"
+#include <limits>
+
+
+RetrieveStatistics::~RetrieveStatistics()
+{ //write statistics to a file
+ wxFFile outputFile(wxT("statistics.dat"), wxT("w"));
+
+ outputFile.Write(wxT("Time(ms);Objects;Data\n"));
+
+ for (std::vector<statEntry>::const_iterator i = data.begin(); i != data.end(); ++i)
+ {
+ outputFile.Write(globalFunctions::numberToWxString(int(i->time)));
+ outputFile.Write(wxT(";"));
+ outputFile.Write(globalFunctions::numberToWxString(i->objects));
+ outputFile.Write(wxT(";"));
+ outputFile.Write(globalFunctions::numberToWxString(float(i->value)));
+ outputFile.Write(wxT("\n"));
+ }
+}
+
+
+void RetrieveStatistics::writeEntry(const double value, const int objects)
+{
+ statEntry newEntry;
+ newEntry.value = value;
+ newEntry.objects = objects;
+ newEntry.time = timer.Time();
+ data.push_back(newEntry);
+}
+
+
+//########################################################################################
+
+inline
+bool isNull(const double number)
+{
+ return globalFunctions::abs(number) <= std::numeric_limits<double>::epsilon();
+}
+
+
+inline
+wxString Statistics::formatRemainingTime(const double timeInMs) const
+{
+ bool unitSec = true;
+ double remainingTime = timeInMs / 1000;
+ wxString unit = _(" sec");
+ if (remainingTime > 55)
+ {
+ unitSec = false;
+ remainingTime /= 60;
+ unit = _(" min");
+ if (remainingTime > 59)
+ {
+ remainingTime /= 60;
+ unit = _(" hour(s)");
+ if (remainingTime > 23)
+ {
+ remainingTime /= 24;
+ unit = _(" day(s)");
+ }
+ }
+ }
+
+
+ int formattedTime = globalFunctions::round(remainingTime);
+
+ //reduce precision to 5 seconds
+ if (unitSec && formattedTime % 5 != 0)
+ formattedTime += 5 - formattedTime % 5; //"ceiling"
+
+
+ //avoid "jumping back and forth" when fluctuating around .5
+ if (remainingTimeLast < formattedTime)
+ {
+ if (unitSec)
+ {
+ formattedTime = globalFunctions::round(remainingTime);
+ formattedTime -= formattedTime % 5; //"floor"
+ }
+ else
+ formattedTime = int(remainingTime); //"floor"
+ }
+ remainingTimeLast = formattedTime;
+
+ return globalFunctions::numberToWxString(formattedTime) + unit;
+ //+ wxT("(") + globalFunctions::numberToWxString(globalFunctions::round(timeInMs / 1000)) + wxT(")");
+}
+
+
+Statistics::Statistics(const int totalObjectCount,
+ const double totalDataAmount,
+ const unsigned windowSizeRemainingTime,
+ const unsigned windowSizeBytesPerSecond) :
+ objectsTotal(totalObjectCount),
+ dataTotal(totalDataAmount),
+ windowSizeRemTime(windowSizeRemainingTime),
+ windowSizeBPS(windowSizeBytesPerSecond),
+ windowMax(std::max(windowSizeRemainingTime, windowSizeBytesPerSecond)),
+ remainingTimeLast(256*256*256*100) //something "big"
+{}
+
+
+void Statistics::addMeasurement(const int objectsCurrent, const double dataCurrent)
+{
+ record newEntry;
+ newEntry.objects = objectsCurrent;
+ newEntry.data = dataCurrent;
+ newEntry.time = timer.Time();
+
+ //insert new record
+ measurements.push_back(newEntry);
+
+ //remove all records earlier than "currentTime - windowSize"
+ const long newBegin = newEntry.time - windowMax;
+ while (measurements.size() > 0 && measurements.front().time < newBegin)
+ measurements.pop_front();
+}
+
+
+wxString Statistics::getRemainingTime() const
+{
+ if (measurements.size() > 0)
+ {
+ //find start of records "window"
+ const record backElement = measurements.back();
+ const long frontTime = backElement.time - windowSizeRemTime;
+ std::list<record>::const_iterator frontElement = measurements.end();
+ do
+ {
+ --frontElement;
+ }
+ while (frontElement != measurements.begin() && frontElement->time > frontTime);
+
+ const double timeDelta = backElement.time - frontElement->time;
+ const double dataDelta = backElement.data - frontElement->data;
+
+ const double dataRemaining = dataTotal - backElement.data;
+
+ if (!isNull(dataDelta))
+ return formatRemainingTime(dataRemaining * timeDelta / dataDelta);
+ }
+
+ return wxT("-"); //fallback
+}
+
+
+wxString Statistics::getBytesPerSecond() const
+{
+ if (measurements.size() > 0)
+ {
+ //find start of records "window"
+ const long frontTime = measurements.back().time - windowSizeBPS;
+ std::list<record>::const_iterator frontElement = measurements.end();
+ do
+ {
+ --frontElement;
+ }
+ while (frontElement != measurements.begin() && frontElement->time > frontTime);
+
+ const double timeDelta = measurements.back().time - frontElement->time;
+ const double dataDelta = measurements.back().data - frontElement->data;
+
+ if (!isNull(timeDelta))
+ return FreeFileSync::formatFilesizeToShortString(dataDelta * 1000 / timeDelta) + _("/sec");
+ }
+
+ return wxT("-"); //fallback
+}
+
+
+void Statistics::pauseTimer()
+{
+ timer.Pause();
+}
+
+
+void Statistics::resumeTimer()
+{
+ timer.Resume();
+}
+
+/*
+class for calculation of remaining time:
+----------------------------------------
+"filesize |-> time" is an affine linear function f(x) = z_1 + z_2 x
+
+For given n measurements, sizes x_0, ..., x_n and times f_0, ..., f_n, the function f (as a polynom of degree 1) can be lineary approximated by
+
+z_1 = (r - s * q / p) / ((n + 1) - s * s / p)
+z_2 = (q - s * z_1) / p = (r - (n + 1) z_1) / s
+
+with
+p := x_0^2 + ... + x_n^2
+q := f_0 x_0 + ... + f_n x_n
+r := f_0 + ... + f_n
+s := x_0 + ... + x_n
+
+=> the time to process N files with amount of data D is: N * z_1 + D * z_2
+
+Problem:
+--------
+Times f_0, ..., f_n can be very small so that precision of the PC clock is poor.
+=> Times have to be accumulated to enhance precision:
+Copying of m files with sizes x_i and times f_i (i = 1, ..., m) takes sum_i f(x_i) := m * z_1 + z_2 * sum x_i = sum f_i
+With X defined as the accumulated sizes and F the accumulated times this gives: (in theory...)
+m * z_1 + z_2 * X = F <=>
+z_1 + z_2 * X / m = F / m
+
+=> we obtain a new (artificial) measurement with size X / m and time F / m to be used in the linear approximation above
+
+
+Statistics::Statistics(const int totalObjectCount, const double totalDataAmount, const unsigned recordCount) :
+ objectsTotal(totalObjectCount),
+ dataTotal(totalDataAmount),
+ recordsMax(recordCount),
+ objectsLast(0),
+ dataLast(0),
+ timeLast(wxGetLocalTimeMillis()),
+ z1_current(0),
+ z2_current(0),
+ dummyRecordPresent(false) {}
+
+
+wxString Statistics::getRemainingTime(const int objectsCurrent, const double dataCurrent)
+{
+ //add new measurement point
+ const int m = objectsCurrent - objectsLast;
+ if (m != 0)
+ {
+ objectsLast = objectsCurrent;
+
+ const double X = dataCurrent - dataLast;
+ dataLast = dataCurrent;
+
+ const wxLongLong timeCurrent = wxGetLocalTimeMillis();
+ const double F = (timeCurrent - timeLast).ToDouble();
+ timeLast = timeCurrent;
+
+ record newEntry;
+ newEntry.x_i = X / m;
+ newEntry.f_i = F / m;
+
+ //remove dummy record
+ if (dummyRecordPresent)
+ {
+ measurements.pop_back();
+ dummyRecordPresent = false;
+ }
+
+ //insert new record
+ measurements.push_back(newEntry);
+ if (measurements.size() > recordsMax)
+ measurements.pop_front();
+ }
+ else //dataCurrent increased without processing new objects:
+ { //modify last measurement until m != 0
+ const double X = dataCurrent - dataLast; //do not set dataLast, timeLast variables here, but write dummy record instead
+ if (!isNull(X))
+ {
+ const wxLongLong timeCurrent = wxGetLocalTimeMillis();
+ const double F = (timeCurrent - timeLast).ToDouble();
+
+ record modifyEntry;
+ modifyEntry.x_i = X;
+ modifyEntry.f_i = F;
+
+ //insert dummy record
+ if (!dummyRecordPresent)
+ {
+ measurements.push_back(modifyEntry);
+ if (measurements.size() > recordsMax)
+ measurements.pop_front();
+ dummyRecordPresent = true;
+ }
+ else //modify dummy record
+ measurements.back() = modifyEntry;
+ }
+ }
+
+ //calculate remaining time based on stored measurement points
+ double p = 0;
+ double q = 0;
+ double r = 0;
+ double s = 0;
+ for (std::list<record>::const_iterator i = measurements.begin(); i != measurements.end(); ++i)
+ {
+ const double x_i = i->x_i;
+ const double f_i = i->f_i;
+ p += x_i * x_i;
+ q += f_i * x_i;
+ r += f_i;
+ s += x_i;
+ }
+
+ if (!isNull(p))
+ {
+ const double n = measurements.size();
+ const double tmp = (n - s * s / p);
+
+ if (!isNull(tmp) && !isNull(s))
+ {
+ const double z1 = (r - s * q / p) / tmp;
+ const double z2 = (r - n * z1) / s; //not (n + 1) here, since n already is the number of measurements
+
+ //refresh current values for z1, z2
+ z1_current = z1;
+ z2_current = z2;
+ }
+ }
+
+ return formatRemainingTime((objectsTotal - objectsCurrent) * z1_current + (dataTotal - dataCurrent) * z2_current);
+}
+
+*/
diff --git a/library/statistics.h b/library/statistics.h
new file mode 100644
index 00000000..3d4179db
--- /dev/null
+++ b/library/statistics.h
@@ -0,0 +1,67 @@
+#ifndef STATISTICS_H_INCLUDED
+#define STATISTICS_H_INCLUDED
+
+#include <vector>
+#include <wx/stopwatch.h>
+#include <list>
+
+class RetrieveStatistics
+{
+public:
+ wxDEPRECATED(RetrieveStatistics() {}) //generate compiler warnings as a reminder to remove code after measurements
+ ~RetrieveStatistics();
+
+ void writeEntry(const double value, const int objects);
+
+private:
+ struct statEntry
+ {
+ long time;
+ int objects;
+ double value;
+ };
+
+ std::vector<statEntry> data;
+ wxStopWatch timer;
+};
+
+
+class Statistics
+{
+public:
+ Statistics(const int totalObjectCount,
+ const double totalDataAmount,
+ const unsigned windowSizeRemainingTime, //time in ms
+ const unsigned windowSizeBytesPerSecond); //time in ms
+
+ void addMeasurement(const int objectsCurrent, const double dataCurrent);
+ wxString getRemainingTime() const; //returns the remaining time in milliseconds
+ wxString getBytesPerSecond() const;
+
+ void pauseTimer();
+ void resumeTimer();
+
+private:
+ wxString formatRemainingTime(const double timeInMs) const;
+
+ const int objectsTotal;
+ const double dataTotal;
+
+ const unsigned windowSizeRemTime; //"window width" of statistics used for calculation of remaining time in ms
+ const unsigned windowSizeBPS; //
+ const unsigned windowMax;
+
+ mutable int remainingTimeLast; //used for "smoothening" remaining time
+
+ struct record
+ {
+ int objects;
+ double data;
+ long time;
+ };
+
+ std::list<record> measurements;
+ wxStopWatch timer;
+};
+
+#endif // STATISTICS_H_INCLUDED
diff --git a/library/statusHandler.h b/library/statusHandler.h
index 11517efb..18d9f129 100644
--- a/library/statusHandler.h
+++ b/library/statusHandler.h
@@ -1,7 +1,9 @@
#ifndef STATUSHANDLER_H_INCLUDED
#define STATUSHANDLER_H_INCLUDED
-#include "zstring.h"
+#include <wx/longlong.h>
+
+class Zstring;
const int UI_UPDATE_INTERVAL = 100; //perform ui updates not more often than necessary, 100 seems to be a good value with only a minimal performance loss
@@ -45,8 +47,8 @@ public:
//these methods have to be implemented in the derived classes to handle error and status information
virtual void updateStatusText(const Zstring& text) = 0;
- virtual void initNewProcess(int objectsTotal, double dataTotal, Process processID) = 0; //informs about the total amount of data that will be processed from now on
- virtual void updateProcessedData(int objectsProcessed, double dataProcessed) = 0; //called periodically after data was processed
+ virtual void initNewProcess(int objectsTotal, wxLongLong dataTotal, Process processID) = 0; //informs about the total amount of data that will be processed from now on
+ virtual void updateProcessedData(int objectsProcessed, wxLongLong dataProcessed) = 0; //called periodically after data was processed
//this method is triggered repeatedly by requestUiRefresh() and can be used to refresh the ui by dispatching pending events
virtual void forceUiRefresh() = 0;
diff --git a/library/zstring.cpp b/library/zstring.cpp
index d704e741..abded9d0 100644
--- a/library/zstring.cpp
+++ b/library/zstring.cpp
@@ -2,23 +2,28 @@
#include <wx/intl.h>
#include "globalFunctions.h"
-
#ifdef FFS_WIN
#include <wx/msw/wrapwin.h> //includes "windows.h"
#endif //FFS_WIN
-#ifdef __WXDEBUG__
-int allocCount = 0; //test Zstring for memory leaks
-void testZstringForMemoryLeak()
+#ifdef __WXDEBUG__
+AllocationCount::~AllocationCount()
{
- if (allocCount != 0)
+ if (count != 0)
#ifdef FFS_WIN
- MessageBox(NULL, wxT("Fatal Error! Allocation problem with Zstring! (No problem if it occures while Unit testing only!)"), wxString::Format(wxT("%i"), allocCount), 0);
+ 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
- throw;
-#endif //FFS_WIN
+ std::abort();
+#endif
+}
+
+
+AllocationCount& AllocationCount::getGlobal()
+{
+ static AllocationCount global;
+ return global;
}
#endif
@@ -45,11 +50,10 @@ int FreeFileSync::compareStringsWin32(const wchar_t* a, const wchar_t* b, const
#endif
-size_t Zstring::Replace(const DefaultChar* old, const DefaultChar* replacement, bool replaceAll)
+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 uiCount = 0; //count of replacements made
size_t pos = 0;
while (true)
@@ -61,13 +65,11 @@ size_t Zstring::Replace(const DefaultChar* old, const DefaultChar* replacement,
replace(pos, oldLen, replacement, replacementLen);
pos += replacementLen; //move past the string that was replaced
- ++uiCount; //increase replace count
-
// stop now?
if (!replaceAll)
break;
}
- return uiCount;
+ return *this;
}
diff --git a/library/zstring.h b/library/zstring.h
index 2a10efa9..2b6bc475 100644
--- a/library/zstring.h
+++ b/library/zstring.h
@@ -61,7 +61,7 @@ public:
#endif
int Cmp(const DefaultChar* other) const;
int Cmp(const Zstring& other) const;
- size_t Replace(const DefaultChar* old, const DefaultChar* replacement, bool replaceAll = true);
+ Zstring& 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;
@@ -87,10 +87,12 @@ public:
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;
+ 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;
@@ -98,9 +100,9 @@ public:
Zstring& operator+=(const DefaultChar* other);
Zstring& operator+=(DefaultChar ch);
- Zstring operator+(const Zstring& string2) const;
- Zstring operator+(const DefaultChar* string2) const;
- Zstring operator+(const DefaultChar ch) const;
+ 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);
@@ -235,8 +237,27 @@ wchar_t defaultToLower(const wchar_t ch)
#ifdef __WXDEBUG__
-extern int allocCount; //test Zstring for memory leaks
-void testZstringForMemoryLeak();
+class AllocationCount //small test for memory leaks in Zstring
+{
+public:
+ void inc()
+ {
+ ++count;
+ }
+
+ void dec()
+ {
+ --count;
+ }
+
+ static AllocationCount& getGlobal();
+
+private:
+ AllocationCount() : count(0) {}
+ ~AllocationCount();
+
+ int count;
+};
#endif
@@ -265,14 +286,7 @@ void Zstring::allocate(const size_t newLength,
newDescr->capacity = newCapacity;
#ifdef __WXDEBUG__
- ++allocCount; //test Zstring for memory leaks
-
- static bool isRegistered = false;
- if (!isRegistered)
- {
- isRegistered = true;
- atexit(testZstringForMemoryLeak);
- }
+ AllocationCount::getGlobal().inc(); //test Zstring for memory leaks
#endif
}
@@ -287,7 +301,7 @@ Zstring::Zstring()
static Zstring emptyString(L"");
#endif
- emptyString.incRef(); //implicitly handle case "this == &source" and avoid this check
+ emptyString.incRef();
descr = emptyString.descr;
data = emptyString.data;
}
@@ -347,9 +361,9 @@ void Zstring::decRef()
if (--descr->refCount == 0)
{
free(descr); //this must NEVER be changed!! E.g. Trim() relies on descr being start of allocated memory block
- descr = 0;
+ descr = NULL;
#ifdef __WXDEBUG__
- --allocCount; //test Zstring for memory leaks
+ AllocationCount::getGlobal().dec(); //test Zstring for memory leaks
#endif
}
}
@@ -504,28 +518,42 @@ int Zstring::Cmp(const Zstring& other) const
inline
-bool Zstring::operator==(const Zstring& other) const
+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
+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
+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
+bool Zstring::operator != (const DefaultChar* other) const
{
return defaultCompare(c_str(), other) != 0; //overload using strcmp(char*, char*) should be fastest!
}
@@ -590,21 +618,21 @@ DefaultChar Zstring::operator[](const size_t pos) const
inline
-Zstring Zstring::operator+(const Zstring& string2) const
+const Zstring Zstring::operator+(const Zstring& string2) const
{
return Zstring(*this)+=string2;
}
inline
-Zstring Zstring::operator+(const DefaultChar* string2) const
+const Zstring Zstring::operator+(const DefaultChar* string2) const
{
return Zstring(*this)+=string2;
}
inline
-Zstring Zstring::operator+(const DefaultChar ch) const
+const Zstring Zstring::operator+(const DefaultChar ch) const
{
return Zstring(*this)+=ch;
}
bgstack15