summaryrefslogtreecommitdiff
path: root/library
diff options
context:
space:
mode:
authorDaniel Wilhelm <daniel@wili.li>2014-04-18 16:59:06 +0200
committerDaniel Wilhelm <daniel@wili.li>2014-04-18 16:59:06 +0200
commit4046be06720932a57a0f49416b0144b2858824d0 (patch)
tree678c37cab05960f48923a23bb46d9e01be89d35a /library
parent1.19 (diff)
downloadFreeFileSync-4046be06720932a57a0f49416b0144b2858824d0.tar.gz
FreeFileSync-4046be06720932a57a0f49416b0144b2858824d0.tar.bz2
FreeFileSync-4046be06720932a57a0f49416b0144b2858824d0.zip
2.0
Diffstat (limited to 'library')
-rw-r--r--library/CustomGrid.cpp949
-rw-r--r--library/CustomGrid.h150
-rw-r--r--library/ShadowCopy/ShadowDll.vcproj219
-rw-r--r--library/ShadowCopy/dllmain.cpp22
-rw-r--r--library/ShadowCopy/shadow.cpp178
-rw-r--r--library/ShadowCopy/shadow.h33
-rw-r--r--library/fileError.h25
-rw-r--r--library/fileHandling.cpp203
-rw-r--r--library/fileHandling.h45
-rw-r--r--library/filter.cpp97
-rw-r--r--library/filter.h6
-rw-r--r--library/globalFunctions.cpp49
-rw-r--r--library/globalFunctions.h88
-rw-r--r--library/iconBuffer.cpp298
-rw-r--r--library/iconBuffer.h51
-rw-r--r--library/localization.cpp208
-rw-r--r--library/localization.h52
-rw-r--r--library/pch.h23
-rw-r--r--library/processXml.cpp117
-rw-r--r--library/processXml.h81
-rw-r--r--library/resources.cpp34
-rw-r--r--library/resources.h32
-rw-r--r--library/shadow.cpp148
-rw-r--r--library/shadow.h31
-rw-r--r--library/statistics.cpp17
-rw-r--r--library/statistics.h13
-rw-r--r--library/zstring.cpp6
-rw-r--r--library/zstring.h60
28 files changed, 2355 insertions, 880 deletions
diff --git a/library/CustomGrid.cpp b/library/CustomGrid.cpp
index 048bc5e0..591230e2 100644
--- a/library/CustomGrid.cpp
+++ b/library/CustomGrid.cpp
@@ -6,16 +6,19 @@
#include "resources.h"
#include <typeinfo>
#include "../ui/gridView.h"
+#include "../synchronization.h"
#ifdef FFS_WIN
+#include <wx/timer.h>
#include <wx/icon.h>
-#include <wx/msw/wrapwin.h> //includes "windows.h"
+#include "iconBuffer.h"
+#include "statusHandler.h"
+#include <cmath>
#elif defined FFS_LINUX
#include <gtk/gtk.h>
#endif
-
using namespace FreeFileSync;
@@ -234,6 +237,7 @@ public:
return xmlAccess::ColumnTypes(1000);
}
+ virtual Zstring getFileName(const unsigned int row) const = 0;
private:
std::vector<xmlAccess::ColumnTypes> columnPositions;
@@ -271,11 +275,11 @@ public:
switch (getTypeAtPos(col))
{
case xmlAccess::FULL_PATH:
- return wxString(gridLine->fileDescrLeft.fullName.c_str()).BeforeLast(GlobalResources::FILE_NAME_SEPARATOR);
+ return wxString(gridLine->fileDescrLeft.fullName.c_str()).BeforeLast(FreeFileSync::FILE_NAME_SEPARATOR);
case xmlAccess::FILENAME: //filename
- return wxString(gridLine->fileDescrLeft.relativeName.c_str()).AfterLast(GlobalResources::FILE_NAME_SEPARATOR);
+ return wxString(gridLine->fileDescrLeft.relativeName.c_str()).AfterLast(FreeFileSync::FILE_NAME_SEPARATOR);
case xmlAccess::REL_PATH: //relative path
- return wxString(gridLine->fileDescrLeft.relativeName.c_str()).BeforeLast(GlobalResources::FILE_NAME_SEPARATOR);
+ return wxString(gridLine->fileDescrLeft.relativeName.c_str()).BeforeLast(FreeFileSync::FILE_NAME_SEPARATOR);
case xmlAccess::DIRECTORY:
return gridDataView->getFolderPair(row).leftDirectory.c_str();
case xmlAccess::SIZE: //file size
@@ -289,6 +293,17 @@ public:
return wxEmptyString;
}
+
+ virtual Zstring getFileName(const unsigned int row) const
+ {
+ const FileCompareLine* gridLine = getRawData(row);
+ if (gridLine)
+ return Zstring(gridLine->fileDescrLeft.fullName);
+ else
+ return Zstring();
+ }
+
+
private:
virtual const wxColour& getRowColor(int row) //rows that are filtered out are shown in different color
{
@@ -340,11 +355,11 @@ public:
switch (getTypeAtPos(col))
{
case xmlAccess::FULL_PATH:
- return wxString(gridLine->fileDescrRight.fullName.c_str()).BeforeLast(GlobalResources::FILE_NAME_SEPARATOR);
+ return wxString(gridLine->fileDescrRight.fullName.c_str()).BeforeLast(FreeFileSync::FILE_NAME_SEPARATOR);
case xmlAccess::FILENAME: //filename
- return wxString(gridLine->fileDescrRight.relativeName.c_str()).AfterLast(GlobalResources::FILE_NAME_SEPARATOR);
+ return wxString(gridLine->fileDescrRight.relativeName.c_str()).AfterLast(FreeFileSync::FILE_NAME_SEPARATOR);
case xmlAccess::REL_PATH: //relative path
- return wxString(gridLine->fileDescrRight.relativeName.c_str()).BeforeLast(GlobalResources::FILE_NAME_SEPARATOR);
+ return wxString(gridLine->fileDescrRight.relativeName.c_str()).BeforeLast(FreeFileSync::FILE_NAME_SEPARATOR);
case xmlAccess::DIRECTORY:
return gridDataView->getFolderPair(row).rightDirectory.c_str();
case xmlAccess::SIZE: //file size
@@ -358,6 +373,17 @@ public:
return wxEmptyString;
}
+
+ virtual Zstring getFileName(const unsigned int row) const
+ {
+ const FileCompareLine* gridLine = getRawData(row);
+ if (gridLine)
+ return Zstring(gridLine->fileDescrRight.fullName);
+ else
+ return Zstring();
+ }
+
+
private:
virtual const wxColour& getRowColor(int row) //rows that are filtered out are shown in different color
{
@@ -476,27 +502,43 @@ CustomGrid::CustomGrid(wxWindow *parent,
m_gridMiddle(NULL),
m_gridRight(NULL),
isLeading(false),
- currentSortColumn(-1),
- sortMarker(NULL)
+ m_marker(-1, ASCENDING)
{
//set color of selections
wxColour darkBlue(40, 35, 140);
SetSelectionBackground(darkBlue);
SetSelectionForeground(*wxWHITE);
+}
- //enhance grid functionality; identify leading grid by keyboard input or scroll action
- Connect(wxEVT_KEY_DOWN, wxEventHandler(CustomGrid::onGridAccess), NULL, this);
- Connect(wxEVT_SCROLLWIN_TOP, wxEventHandler(CustomGrid::onGridAccess), NULL, this);
- Connect(wxEVT_SCROLLWIN_BOTTOM, wxEventHandler(CustomGrid::onGridAccess), NULL, this);
- Connect(wxEVT_SCROLLWIN_LINEUP, wxEventHandler(CustomGrid::onGridAccess), NULL, this);
- Connect(wxEVT_SCROLLWIN_LINEDOWN, wxEventHandler(CustomGrid::onGridAccess), NULL, this);
- Connect(wxEVT_SCROLLWIN_PAGEUP, wxEventHandler(CustomGrid::onGridAccess), NULL, this);
- Connect(wxEVT_SCROLLWIN_PAGEDOWN, wxEventHandler(CustomGrid::onGridAccess), NULL, this);
- Connect(wxEVT_SCROLLWIN_THUMBTRACK, wxEventHandler(CustomGrid::onGridAccess), NULL, this);
- 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);
+void CustomGrid::initSettings(CustomGridLeft* gridLeft,
+ CustomGridMiddle* gridMiddle,
+ CustomGridRight* gridRight,
+ const GridView* gridDataView)
+{
+ assert(this == gridLeft || this == gridRight || this == gridMiddle);
+
+ //these grids will scroll together
+ m_gridLeft = gridLeft;
+ m_gridRight = gridRight;
+ m_gridMiddle = gridMiddle;
+
+ //set underlying grid data
+ setGridDataTable(gridDataView);
+
+ //enhance grid functionality; identify leading grid by keyboard input or scroll action
+ Connect(wxEVT_KEY_DOWN, wxEventHandler(CustomGrid::onGridAccess), NULL, this);
+ Connect(wxEVT_SCROLLWIN_TOP, wxEventHandler(CustomGrid::onGridAccess), NULL, this);
+ Connect(wxEVT_SCROLLWIN_BOTTOM, wxEventHandler(CustomGrid::onGridAccess), NULL, this);
+ Connect(wxEVT_SCROLLWIN_LINEUP, wxEventHandler(CustomGrid::onGridAccess), NULL, this);
+ Connect(wxEVT_SCROLLWIN_LINEDOWN, wxEventHandler(CustomGrid::onGridAccess), NULL, this);
+ Connect(wxEVT_SCROLLWIN_PAGEUP, wxEventHandler(CustomGrid::onGridAccess), NULL, this);
+ Connect(wxEVT_SCROLLWIN_PAGEDOWN, wxEventHandler(CustomGrid::onGridAccess), NULL, this);
+ Connect(wxEVT_SCROLLWIN_THUMBTRACK, wxEventHandler(CustomGrid::onGridAccess), NULL, this);
+ Connect(wxEVT_SCROLLWIN_THUMBRELEASE, wxEventHandler(CustomGrid::onGridAccess), NULL, this);
+ Connect(wxEVT_GRID_LABEL_LEFT_CLICK, wxEventHandler(CustomGrid::onGridAccess), NULL, this);
+ GetGridWindow()->Connect(wxEVT_LEFT_DOWN, wxEventHandler(CustomGrid::onGridAccess), NULL, this);
+ GetGridWindow()->Connect(wxEVT_RIGHT_DOWN, wxEventHandler(CustomGrid::onGridAccess), NULL, this);
GetGridWindow()->Connect(wxEVT_ENTER_WINDOW, wxEventHandler(CustomGrid::adjustGridHeights), NULL, this);
}
@@ -507,58 +549,22 @@ bool CustomGrid::isLeadGrid() const
}
-inline
-bool gridsShouldBeCleared(const wxEvent& event)
+void CustomGrid::RefreshCell(int row, int col)
{
- try
- {
- const wxMouseEvent& mouseEvent = dynamic_cast<const wxMouseEvent&> (event);
-
- if (mouseEvent.ControlDown() || mouseEvent.ShiftDown())
- return false;
-
- if (mouseEvent.ButtonDown(wxMOUSE_BTN_LEFT))
- return true;
+ wxRect rectScrolled(CellToRect(row, col));
- return false;
- }
- catch (std::bad_cast&) {}
-
- try
- {
- const wxKeyEvent& keyEvent = dynamic_cast<const wxKeyEvent&> (event);
+ CalcScrolledPosition(rectScrolled.x, rectScrolled.y, &rectScrolled.x, &rectScrolled.y);
- if (keyEvent.ControlDown() || keyEvent.ShiftDown())
- return false;
+ GetGridWindow()->RefreshRect(rectScrolled); //note: CellToRect() and YToRow work on m_gridWindow NOT on the whole grid!
+}
- switch (keyEvent.GetKeyCode())
- {
- case WXK_SPACE:
- case WXK_TAB:
- case WXK_RETURN:
- case WXK_ESCAPE:
- case WXK_NUMPAD_ENTER:
- case WXK_LEFT:
- case WXK_UP:
- case WXK_RIGHT:
- case WXK_DOWN:
- case WXK_PAGEUP:
- case WXK_PAGEDOWN:
- case WXK_NUMPAD_PAGEUP:
- case WXK_NUMPAD_PAGEDOWN:
- case WXK_HOME:
- case WXK_END:
- case WXK_NUMPAD_HOME:
- case WXK_NUMPAD_END:
- return true;
- default:
- return false;
- }
- }
- catch (std::bad_cast&) {}
+void CustomGrid::DoPrepareDC(wxDC& dc)
+{
+ wxScrollHelper::DoPrepareDC(dc);
- return false;
+ if (isLeadGrid()) //avoid back coupling
+ alignOtherGrids(m_gridLeft, m_gridMiddle, m_gridRight); //scroll other grids
}
@@ -681,29 +687,84 @@ void additionalGridCommands(wxEvent& event, wxGrid* grid)
}
+inline
+bool gridsShouldBeCleared(const wxEvent& event)
+{
+ try
+ {
+ const wxMouseEvent& mouseEvent = dynamic_cast<const wxMouseEvent&> (event);
+
+ if (mouseEvent.ControlDown() || mouseEvent.ShiftDown())
+ return false;
+
+ if (mouseEvent.ButtonDown(wxMOUSE_BTN_LEFT))
+ return true;
+
+ return false;
+ }
+ catch (std::bad_cast&) {}
+
+ try
+ {
+ const wxKeyEvent& keyEvent = dynamic_cast<const wxKeyEvent&> (event);
+
+ if (keyEvent.ControlDown() || keyEvent.ShiftDown())
+ return false;
+
+ switch (keyEvent.GetKeyCode())
+ {
+ case WXK_SPACE:
+ case WXK_TAB:
+ case WXK_RETURN:
+ case WXK_ESCAPE:
+ case WXK_NUMPAD_ENTER:
+ case WXK_LEFT:
+ case WXK_UP:
+ case WXK_RIGHT:
+ case WXK_DOWN:
+ case WXK_PAGEUP:
+ case WXK_PAGEDOWN:
+ case WXK_NUMPAD_PAGEUP:
+ case WXK_NUMPAD_PAGEDOWN:
+ case WXK_HOME:
+ case WXK_END:
+ case WXK_NUMPAD_HOME:
+ case WXK_NUMPAD_END:
+ return true;
+ }
+
+ return false;
+ }
+ catch (std::bad_cast&) {}
+
+ return false;
+}
+
+
void CustomGrid::onGridAccess(wxEvent& event)
{
if (!isLeading)
{
- isLeading = true;
-
//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;
+ m_gridLeft->isLeading = m_gridLeft == this;
+ m_gridMiddle->isLeading = m_gridMiddle == this;
+ m_gridRight->isLeading = m_gridRight == this;
wxGrid::SetFocus();
}
+ //clear grids
if (gridsShouldBeCleared(event))
{
m_gridLeft->ClearSelection();
+ m_gridMiddle->ClearSelection();
m_gridRight->ClearSelection();
}
+ //update row labels NOW (needed when scrolling if buttons keep being pressed)
+ m_gridLeft->GetGridRowLabelWindow()->Update();
+ m_gridRight->GetGridRowLabelWindow()->Update();
+
//support for additional short-cuts
additionalGridCommands(event, this); //event.Skip is handled here!
}
@@ -712,48 +773,46 @@ void CustomGrid::onGridAccess(wxEvent& event)
//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)
{
- if (m_gridLeft && m_gridRight && m_gridMiddle)
- {
- int y1 = 0;
- int y2 = 0;
- int y3 = 0;
- int dummy = 0;
+ //m_gridLeft, m_gridRight, m_gridMiddle not NULL because called after initSettings()
- m_gridLeft->GetViewStart(&dummy, &y1);
- m_gridRight->GetViewStart(&dummy, &y2);
- m_gridMiddle->GetViewStart(&dummy, &y3);
+ int y1 = 0;
+ int y2 = 0;
+ int y3 = 0;
+ int dummy = 0;
- 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();
- }
+ 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();
}
}
-void CustomGrid::setSortMarker(const int sortColumn, const wxBitmap* bitmap)
+void CustomGrid::setSortMarker(SortMarker marker)
{
- currentSortColumn = sortColumn;
- sortMarker = bitmap;
+ m_marker = marker;
}
@@ -761,8 +820,13 @@ void CustomGrid::DrawColLabel(wxDC& dc, int col)
{
wxGrid::DrawColLabel(dc, col);
- if (col == currentSortColumn)
- dc.DrawBitmap(*sortMarker, GetColRight(col) - 16 - 2, 2, true); //respect 2-pixel border
+ if (col == m_marker.first)
+ {
+ if (m_marker.second == ASCENDING)
+ dc.DrawBitmap(*GlobalResources::getInstance().bitmapSmallUp, GetColRight(col) - 16 - 2, 2, true); //respect 2-pixel border
+ else
+ dc.DrawBitmap(*GlobalResources::getInstance().bitmapSmallDown, GetColRight(col) - 16 - 2, 2, true); //respect 2-pixel border
+ }
}
@@ -821,118 +885,119 @@ std::set<int> CustomGrid::getAllSelectedRows() const
//############################################################################################
//CustomGrid specializations
-template <bool leftSide, bool showFileIcons>
+#ifdef FFS_WIN
+template <bool showFileIcons>
class GridCellRenderer : public wxGridCellStringRenderer
{
public:
- GridCellRenderer(CustomGridTableRim* gridDataTable) : m_gridDataTable(gridDataTable) {};
+ GridCellRenderer(CustomGridRim::LoadSuccess& loadIconSuccess, const CustomGridTableRim* gridDataTable) :
+ m_loadIconSuccess(loadIconSuccess),
+ m_gridDataTable(gridDataTable) {}
virtual void Draw(wxGrid& grid,
wxGridCellAttr& attr,
wxDC& dc,
- const wxRect& rect,
+ const wxRect& rect, //unscrolled 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)
+ rect.GetWidth() >= IconBuffer::ICON_SIZE)
{
//retrieve grid data
- const FileCompareLine* rowData = m_gridDataTable->getRawData(row);
- if (rowData) //valid row
+ const Zstring fileName = m_gridDataTable->getFileName(row);
+ if (!fileName.empty())
{
- 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
+ // Partitioning:
+ // _____________________
+ // | 2 pix | icon | rest |
+ // ---------------------
+
+ //clear area where icon will be placed
+ wxRect rectShrinked(rect);
+ rectShrinked.SetWidth(IconBuffer::ICON_SIZE + 2); //add 2 pixel border
+ wxGridCellRenderer::Draw(grid, attr, dc, rectShrinked, row, col, isSelected);
+
+ //try to draw icon
+ wxIcon icon;
+ const bool iconLoaded = IconBuffer::getInstance().requestIcon(fileName, &icon); //returns false if icon is not in buffer
+ if (iconLoaded)
+ dc.DrawIcon(icon, rectShrinked.GetX() + 2, rectShrinked.GetY());
+
+ //-----------------------------------------------------------------------------------------------
+ //only mark as successful if icon was drawn fully!
+ //(attention: when scrolling, rows get partially updated, which can result in the upper half being blank!)
+
+ //rect where icon was placed
+ wxRect iconRect(rect); //unscrolled
+ iconRect.x += 2;
+ iconRect.SetWidth(IconBuffer::ICON_SIZE);
+
+ //convert to scrolled coordinates
+ grid.CalcScrolledPosition(iconRect.x, iconRect.y, &iconRect.x, &iconRect.y);
+
+ bool iconDrawnFully = false;
+ wxRegionIterator regionsInv(grid.GetGridWindow()->GetUpdateRegion());
+ while (regionsInv)
{
- // Get the file icon.
- SHFILEINFO fileInfo;
- fileInfo.hIcon = 0; //initialize hIcon
-
- if (SHGetFileInfo(filename, //NOTE: CoInitializeEx()/CoUninitialize() implicitly called by wxWidgets on program startup!
- 0,
- &fileInfo,
- sizeof(fileInfo),
- SHGFI_ICON | SHGFI_SMALLICON))
+ if (regionsInv.GetRect().Contains(iconRect))
{
- //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);
-
- dc.DrawIcon(icon, rectShrinked.GetX() + 2, rectShrinked.GetY());
-
- 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;
+ iconDrawnFully = true;
+ break;
}
+ ++regionsInv;
}
+ //-----------------------------------------------------------------------------------------------
+
+
+ //save status of last icon load -> used for async. icon loading
+ m_loadIconSuccess[row] = iconLoaded && iconDrawnFully;
+
+ //draw rest
+ wxRect rest(rect); //unscrolled
+ rest.x += IconBuffer::ICON_SIZE + 2;
+ rest.width -= IconBuffer::ICON_SIZE + 2;
+
+ wxGridCellStringRenderer::Draw(grid, attr, dc, rest, 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:
+ CustomGridRim::LoadSuccess& m_loadIconSuccess;
const CustomGridTableRim* const m_gridDataTable;
};
+#endif
//----------------------------------------------------------------------------------------
-void CustomGridRim::initSettings(const bool showFileIcons,
- CustomGrid* gridLeft,
- CustomGrid* gridRight,
- CustomGrid* gridMiddle,
- const 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);
-
- enableFileIcons(showFileIcons);
-}
+CustomGridRim::CustomGridRim(wxWindow *parent,
+ wxWindowID id,
+ const wxPoint& pos,
+ const wxSize& size,
+ long style,
+ const wxString& name) :
+ CustomGrid(parent, id, pos, size, style, name)
+#ifdef FFS_WIN
+ , fileIconsAreEnabled(false)
+#endif
+{}
void CustomGridRim::updateGridSizes()
{
- assert(gridDataTable);
- gridDataTable->updateGridSizes();
+ assert(getGridDataTable());
+ getGridDataTable()->updateGridSizes();
}
@@ -1046,8 +1111,8 @@ void CustomGridRim::setColumnAttributes(const xmlAccess::ColumnAttributes& attr)
newPositions.push_back(columnSettings[i].type);
//set column positions
- assert(gridDataTable);
- gridDataTable->setupColumns(newPositions);
+ assert(getGridDataTable());
+ getGridDataTable()->setupColumns(newPositions);
//set column width (set them after setupColumns!)
for (unsigned int i = 0; i < newPositions.size(); ++i)
@@ -1071,8 +1136,8 @@ void CustomGridRim::setColumnAttributes(const xmlAccess::ColumnAttributes& attr)
xmlAccess::ColumnTypes CustomGridRim::getTypeAtPos(unsigned pos) const
{
- assert(gridDataTable);
- return gridDataTable->getTypeAtPos(pos);
+ assert(getGridDataTable());
+ return getGridDataTable()->getTypeAtPos(pos);
}
@@ -1097,6 +1162,132 @@ wxString CustomGridRim::getTypeName(xmlAccess::ColumnTypes colType)
return wxEmptyString; //dummy
}
+
+CustomGridTableRim* CustomGridRim::getGridDataTable()
+{ //let the non-const call the const version: see Meyers Effective C++
+ return const_cast<CustomGridTableRim*>(static_cast<const CustomGridRim*>(this)->getGridDataTable());
+}
+
+
+#ifdef FFS_WIN
+void CustomGridRim::enableFileIcons(const bool value)
+{
+ fileIconsAreEnabled = value;
+
+ if (value)
+ SetDefaultRenderer(new GridCellRenderer<true>(loadIconSuccess, getGridDataTable())); //SetDefaultRenderer takes ownership!
+ else
+ SetDefaultRenderer(new GridCellRenderer<false>(loadIconSuccess, getGridDataTable()));
+
+ Refresh();
+}
+
+
+CustomGridRim::VisibleRowRange CustomGridRim::getVisibleRows()
+{
+ int dummy = -1;
+ int height = -1;
+ GetGridWindow()->GetClientSize(&dummy, &height);
+
+ if (height >= 0)
+ {
+ int topRowY = -1;
+ CalcUnscrolledPosition(0, 0, &dummy, &topRowY);
+
+ if (topRowY >= 0)
+ {
+ const int topRow = YToRow(topRowY);
+ const int rowCount = static_cast<int>(ceil(height / static_cast<double>(GetDefaultRowSize()))); // = height / rowHeight rounded up
+ const int bottomRow = topRow + rowCount - 1;
+
+ return VisibleRowRange(topRow, bottomRow); //"top" means here top of the screen: => smaller value
+ }
+ }
+
+ return VisibleRowRange(0, 0);
+}
+
+
+void CustomGridRim::getIconsToBeLoaded(std::vector<Zstring>& newLoad) //loads all (not yet) drawn icons
+{
+ newLoad.clear();
+
+ if (fileIconsAreEnabled) //don't check too often! give worker thread some time to fetch data
+ {
+ const CustomGridTableRim* gridDataTable = getGridDataTable();
+ const VisibleRowRange rowsOnScreen = getVisibleRows();
+ const int totalCols = const_cast<CustomGridTableRim*>(gridDataTable)->GetNumberCols();
+ const int totalRows = const_cast<CustomGridTableRim*>(gridDataTable)->GetNumberRows();
+
+ //loop over all visible rows
+ const int firstRow = rowsOnScreen.first;
+ const int lastRow = std::min(int(rowsOnScreen.second), totalRows - 1);
+ const int rowNo = lastRow - firstRow + 1;
+
+ for (int i = 0; i < rowNo; ++i)
+ {
+ //alternate when adding rows: first, last, first + 1, last - 1 ...
+ const int currentRow = i % 2 == 0 ?
+ firstRow + i / 2 :
+ lastRow - (i - 1) / 2;
+
+ LoadSuccess::const_iterator j = loadIconSuccess.find(currentRow);
+ if (j != loadIconSuccess.end() && j->second == false) //find failed attempts to load icon
+ {
+ const Zstring fileName = gridDataTable->getFileName(currentRow);
+ if (!fileName.empty())
+ {
+ //test if they are already loaded in buffer:
+ if (FreeFileSync::IconBuffer::getInstance().requestIcon(fileName))
+ {
+ //exists in buffer: refresh Row
+ for (int k = 0; k < totalCols; ++k)
+ if (gridDataTable->getTypeAtPos(k) == xmlAccess::FILENAME)
+ {
+ RefreshCell(currentRow, k);
+ break;
+ }
+ }
+ else //not yet in buffer: mark for async. loading
+ {
+ newLoad.push_back(fileName);
+ }
+ }
+ }
+ }
+ }
+}
+
+//----------------------------------------------------------------------------------------
+
+
+//update file icons periodically: use SINGLE instance to coordinate left and right grid at once
+IconUpdater::IconUpdater(CustomGridLeft* leftGrid, CustomGridRight* rightGrid) :
+ m_leftGrid(leftGrid),
+ m_rightGrid(rightGrid),
+ m_timer(new wxTimer) //connect timer event for async. icon loading
+{
+ m_timer->Connect(wxEVT_TIMER, wxEventHandler(IconUpdater::loadIconsAsynchronously), NULL, this);
+ m_timer->Start(50); //timer interval
+}
+
+
+void IconUpdater::loadIconsAsynchronously(wxEvent& event) //loads all (not yet) drawn icons
+{
+ std::vector<Zstring> iconsLeft;
+ m_leftGrid->getIconsToBeLoaded(iconsLeft);
+
+ std::vector<Zstring> newLoad;
+ m_rightGrid->getIconsToBeLoaded(newLoad);
+
+ globalFunctions::mergeVectors(iconsLeft, newLoad);
+
+ FreeFileSync::IconBuffer::getInstance().setWorkload(newLoad); //attention: newLoad is invalidated after this call!!!
+
+ //event.Skip();
+}
+#endif
+
//----------------------------------------------------------------------------------------
@@ -1106,7 +1297,8 @@ CustomGridLeft::CustomGridLeft(wxWindow *parent,
const wxSize& size,
long style,
const wxString& name) :
- CustomGridRim(parent, id, pos, size, style, name) {}
+ CustomGridRim(parent, id, pos, size, style, name),
+ gridDataTable(NULL) {}
bool CustomGridLeft::CreateGrid(int numRows, int numCols, wxGrid::wxGridSelectionModes selmode)
@@ -1120,29 +1312,28 @@ bool CustomGridLeft::CreateGrid(int numRows, int numCols, wxGrid::wxGridSelectio
}
-void CustomGridLeft::enableFileIcons(const bool value)
+void CustomGridLeft::setGridDataTable(const GridView* gridDataView)
{
- if (value)
- SetDefaultRenderer(new GridCellRenderer<true, true>(gridDataTable)); //SetDefaultRenderer takes ownership!
- else
- SetDefaultRenderer(new GridCellRenderer<true, false>(gridDataTable));
+ //set underlying grid data
+ assert(gridDataTable);
+ gridDataTable->setGridDataTable(gridDataView);
+}
- Refresh();
+
+const CustomGridTableRim* CustomGridLeft::getGridDataTable() const
+{
+ return gridDataTable;
}
//this method is called when grid view changes: useful for parallel updating of multiple grids
-void CustomGridLeft::DoPrepareDC(wxDC& dc)
+void CustomGridLeft::alignOtherGrids(CustomGrid* gridLeft, CustomGrid* gridMiddle, CustomGrid* gridRight)
{
- wxScrollHelper::DoPrepareDC(dc);
-
- 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);
- }
+ int x = 0;
+ int y = 0;
+ GetViewStart(&x, &y);
+ gridMiddle->Scroll(-1, y); //scroll in y-direction only
+ gridRight->Scroll(x, y);
}
@@ -1153,7 +1344,8 @@ CustomGridRight::CustomGridRight(wxWindow *parent,
const wxSize& size,
long style,
const wxString& name) :
- CustomGridRim(parent, id, pos, size, style, name) {}
+ CustomGridRim(parent, id, pos, size, style, name),
+ gridDataTable(NULL) {}
bool CustomGridRight::CreateGrid(int numRows, int numCols, wxGrid::wxGridSelectionModes selmode)
@@ -1165,33 +1357,49 @@ bool CustomGridRight::CreateGrid(int numRows, int numCols, wxGrid::wxGridSelecti
}
-void CustomGridRight::enableFileIcons(const bool value)
+void CustomGridRight::setGridDataTable(const GridView* gridDataView)
{
- if (value)
- SetDefaultRenderer(new GridCellRenderer<false, true>(gridDataTable)); //SetDefaultRenderer takes ownership!
- else
- SetDefaultRenderer(new GridCellRenderer<false, false>(gridDataTable));
+ //set underlying grid data
+ assert(gridDataTable);
+ gridDataTable->setGridDataTable(gridDataView);
+}
- Refresh();
+
+const CustomGridTableRim* CustomGridRight::getGridDataTable() const
+{
+ return gridDataTable;
}
//this method is called when grid view changes: useful for parallel updating of multiple grids
-void CustomGridRight::DoPrepareDC(wxDC& dc)
+void CustomGridRight::alignOtherGrids(CustomGrid* gridLeft, CustomGrid* gridMiddle, CustomGrid* gridRight)
{
- wxScrollHelper::DoPrepareDC(dc);
-
- int x, y = 0;
- if (isLeadGrid()) //avoid back coupling
- {
- GetViewStart(&x, &y);
- m_gridLeft->Scroll(x, y);
- m_gridMiddle->Scroll(-1, y);
- }
+ int x = 0;
+ int y = 0;
+ GetViewStart(&x, &y);
+ gridLeft->Scroll(x, y);
+ gridMiddle->Scroll(-1, y);
}
//----------------------------------------------------------------------------------------
+class GridCellRendererMiddle : public wxGridCellStringRenderer
+{
+public:
+ GridCellRendererMiddle(const CustomGridMiddle* middleGrid) : m_gridMiddle(middleGrid) {};
+
+ virtual void Draw(wxGrid& grid,
+ wxGridCellAttr& attr,
+ wxDC& dc,
+ const wxRect& rect,
+ int row, int col,
+ bool isSelected);
+
+private:
+ const CustomGridMiddle* const m_gridMiddle;
+};
+
+
//define new event types
const wxEventType FFS_CHECK_ROWS_EVENT = wxNewEventType(); //attention! do NOT place in header to keep (generated) id unique!
const wxEventType FFS_SYNC_DIRECTION_EVENT = wxNewEventType();
@@ -1220,13 +1428,46 @@ CustomGridMiddle::CustomGridMiddle(wxWindow *parent,
{
//connect events for dynamic selection of sync direction
GetGridWindow()->Connect(wxEVT_MOTION, wxMouseEventHandler(CustomGridMiddle::OnMouseMovement), NULL, this);
-
GetGridWindow()->Connect(wxEVT_LEAVE_WINDOW, wxMouseEventHandler(CustomGridMiddle::OnLeaveWindow), NULL, this);
GetGridWindow()->Connect(wxEVT_LEFT_UP, wxMouseEventHandler(CustomGridMiddle::OnLeftMouseUp), NULL, this);
GetGridWindow()->Connect(wxEVT_LEFT_DOWN, wxMouseEventHandler(CustomGridMiddle::OnLeftMouseDown), NULL, this);
}
+bool CustomGridMiddle::CreateGrid(int numRows, int numCols, wxGrid::wxGridSelectionModes selmode)
+{
+ 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
+ SetDefaultRenderer(new GridCellRendererMiddle(this)); //SetDefaultRenderer takes ownership!
+
+ return true;
+}
+
+
+#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::setGridDataTable(const GridView* gridDataView) //called once on startup
+{
+ //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
+}
+
+
void CustomGridMiddle::OnMouseMovement(wxMouseEvent& event)
{
const int highlightedRowOld = highlightedRow;
@@ -1234,25 +1475,17 @@ void CustomGridMiddle::OnMouseMovement(wxMouseEvent& event)
if (selectionRowBegin == -1) //change highlightning only if currently not dragging mouse
{
highlightedRow = mousePosToRow(event.GetPosition(), &highlightedPos);
- if (highlightedRow >= 0) RefreshRow(highlightedRow);
+ if (highlightedRow >= 0)
+ RefreshCell(highlightedRow, 0);
if ( highlightedRowOld >= 0 &&
highlightedRow != highlightedRowOld)
- RefreshRow(highlightedRowOld);
+ RefreshCell(highlightedRowOld, 0);
}
event.Skip();
}
-void CustomGridMiddle::RefreshRow(int row)
-{
- wxRect rectScrolled(CellToRect(row, 0));
- CalcScrolledPosition(rectScrolled.x, rectScrolled.y, &rectScrolled.x, &rectScrolled.y);
-
- GetGridWindow()->Refresh(false, &rectScrolled); //note: CellToRect() and YToRow work on m_gridWindow NOT on the whole grid!
-}
-
-
void CustomGridMiddle::OnLeaveWindow(wxMouseEvent& event)
{
highlightedRow = -1;
@@ -1357,36 +1590,6 @@ int CustomGridMiddle::mousePosToRow(const wxPoint pos, BlockPosition* block)
}
-void CustomGridMiddle::initSettings(CustomGrid* gridLeft,
- CustomGrid* gridRight,
- CustomGrid* gridMiddle,
- const 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::enableSyncPreview(bool value)
{
assert(gridDataTable);
@@ -1427,152 +1630,128 @@ void CustomGridMiddle::updateGridSizes()
}
-class GridCellRendererMiddle : public wxGridCellStringRenderer
+void GridCellRendererMiddle::Draw(wxGrid& grid,
+ wxGridCellAttr& attr,
+ wxDC& dc,
+ const wxRect& rect,
+ int row, int col,
+ bool isSelected)
{
-public:
- GridCellRendererMiddle(const CustomGridMiddle* middleGrid) : m_gridMiddle(middleGrid) {};
-
-
- virtual void Draw(wxGrid& grid,
- wxGridCellAttr& attr,
- wxDC& dc,
- const wxRect& rect,
- int row, int col,
- bool isSelected)
+ //retrieve grid data
+ const FileCompareLine* const rowData = m_gridMiddle->gridDataTable->getRawData(row);
+ if (rowData != NULL) //if valid row...
{
- //retrieve grid data
- const FileCompareLine* const rowData = m_gridMiddle->gridDataTable->getRawData(row);
- if (rowData) //no valid row
+ if (rect.GetWidth() > CHECK_BOX_WIDTH)
{
- if (rect.GetWidth() > CHECK_BOX_WIDTH)
- {
- wxRect rectShrinked(rect);
+ wxRect rectShrinked(rect);
- //clean first block of rect that will receive image of checkbox
- rectShrinked.SetWidth(CHECK_BOX_WIDTH);
- wxGridCellRenderer::Draw(grid, attr, dc, rectShrinked, row, col, isSelected);
+ //clean first block of rect that will receive image of checkbox
+ rectShrinked.SetWidth(CHECK_BOX_WIDTH);
+ wxGridCellRenderer::Draw(grid, attr, dc, rectShrinked, row, col, isSelected);
- //print image into first block
- rectShrinked.SetX(rect.GetX() + 1);
- bool selected = rowData->selectedForSynchronization;
+ //print image into first block
+ rectShrinked.SetX(rect.GetX() + 1);
+ bool selected = rowData->selectedForSynchronization;
- //HIGHLIGHTNING:
- if ( row == m_gridMiddle->highlightedRow &&
- m_gridMiddle->highlightedPos == CustomGridMiddle::BLOCKPOS_CHECK_BOX)
- selected = !selected;
-
- if (selected)
- dc.DrawLabel(wxEmptyString, *globalResource.bitmapCheckBoxTrue, rectShrinked, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL);
- else
- dc.DrawLabel(wxEmptyString, *globalResource.bitmapCheckBoxFalse, rectShrinked, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL);
-
- //clean remaining block of rect that will receive image of checkbox/directions
- rectShrinked.SetWidth(rect.GetWidth() - CHECK_BOX_WIDTH);
- rectShrinked.SetX(rect.GetX() + CHECK_BOX_WIDTH);
- wxGridCellRenderer::Draw(grid, attr, dc, rectShrinked, row, col, isSelected);
-
- //print remaining block
- if (m_gridMiddle->gridDataTable->syncPreviewIsActive()) //synchronization preview
- {
- //print sync direction into second block
+ //HIGHLIGHTNING:
+ if ( row == m_gridMiddle->highlightedRow &&
+ m_gridMiddle->highlightedPos == CustomGridMiddle::BLOCKPOS_CHECK_BOX)
+ selected = !selected;
- //HIGHLIGHTNING:
- if (row == m_gridMiddle->highlightedRow && m_gridMiddle->highlightedPos != CustomGridMiddle::BLOCKPOS_CHECK_BOX)
- switch (m_gridMiddle->highlightedPos)
- {
- case CustomGridMiddle::BLOCKPOS_CHECK_BOX:
- break;
- case CustomGridMiddle::BLOCKPOS_LEFT:
- dc.DrawLabel(wxEmptyString, *globalResource.bitmapSyncDirLeftSmall, rectShrinked, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL);
- break;
- case CustomGridMiddle::BLOCKPOS_MIDDLE:
- dc.DrawLabel(wxEmptyString, *globalResource.bitmapSyncDirNoneSmall, rectShrinked, wxALIGN_CENTER | wxALIGN_CENTER_VERTICAL);
- break;
- case CustomGridMiddle::BLOCKPOS_RIGHT:
- dc.DrawLabel(wxEmptyString, *globalResource.bitmapSyncDirRightSmall, rectShrinked, wxALIGN_RIGHT | wxALIGN_CENTER_VERTICAL);
- break;
- }
- else //default
- switch (rowData->direction)
- {
- case SYNC_DIR_LEFT:
- dc.DrawLabel(wxEmptyString, *globalResource.bitmapSyncDirLeftSmall, rectShrinked, wxALIGN_CENTER | wxALIGN_CENTER_VERTICAL);
- break;
- case SYNC_DIR_RIGHT:
- dc.DrawLabel(wxEmptyString, *globalResource.bitmapSyncDirRightSmall, rectShrinked, wxALIGN_CENTER | wxALIGN_CENTER_VERTICAL);
- break;
- case SYNC_DIR_NONE:
- dc.DrawLabel(wxEmptyString, *globalResource.bitmapSyncDirNoneSmall, rectShrinked, wxALIGN_CENTER | wxALIGN_CENTER_VERTICAL);
- break;
- case SYNC_UNRESOLVED_CONFLICT:
- dc.DrawLabel(wxEmptyString, *globalResource.bitmapConflictSmall, rectShrinked, wxALIGN_CENTER | wxALIGN_CENTER_VERTICAL);
- break;
- }
- }
- else //comparison results view
- {
- switch (rowData->cmpResult)
+ if (selected)
+ dc.DrawLabel(wxEmptyString, *GlobalResources::getInstance().bitmapCheckBoxTrue, rectShrinked, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL);
+ else
+ dc.DrawLabel(wxEmptyString, *GlobalResources::getInstance().bitmapCheckBoxFalse, rectShrinked, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL);
+
+ //clean remaining block of rect that will receive image of checkbox/directions
+ rectShrinked.SetWidth(rect.GetWidth() - CHECK_BOX_WIDTH);
+ rectShrinked.SetX(rect.GetX() + CHECK_BOX_WIDTH);
+ wxGridCellRenderer::Draw(grid, attr, dc, rectShrinked, row, col, isSelected);
+
+ //print remaining block
+ if (m_gridMiddle->gridDataTable->syncPreviewIsActive()) //synchronization preview
+ {
+ //print sync direction into second block
+
+ //HIGHLIGHTNING:
+ if (row == m_gridMiddle->highlightedRow && m_gridMiddle->highlightedPos != CustomGridMiddle::BLOCKPOS_CHECK_BOX)
+ switch (m_gridMiddle->highlightedPos)
{
- case FILE_LEFT_SIDE_ONLY:
- dc.DrawLabel(wxEmptyString, *globalResource.bitmapLeftOnlySmall, rectShrinked, wxALIGN_CENTER | wxALIGN_CENTER_VERTICAL);
- break;
- case FILE_RIGHT_SIDE_ONLY:
- dc.DrawLabel(wxEmptyString, *globalResource.bitmapRightOnlySmall, rectShrinked, wxALIGN_CENTER | wxALIGN_CENTER_VERTICAL);
- break;
- case FILE_LEFT_NEWER:
- dc.DrawLabel(wxEmptyString, *globalResource.bitmapLeftNewerSmall, rectShrinked, wxALIGN_CENTER | wxALIGN_CENTER_VERTICAL);
+ case CustomGridMiddle::BLOCKPOS_CHECK_BOX:
break;
- case FILE_RIGHT_NEWER:
- dc.DrawLabel(wxEmptyString, *globalResource.bitmapRightNewerSmall, rectShrinked, wxALIGN_CENTER | wxALIGN_CENTER_VERTICAL);
+ case CustomGridMiddle::BLOCKPOS_LEFT:
+ dc.DrawLabel(wxEmptyString, getSyncOpImage(rowData->cmpResult, true, SYNC_DIR_LEFT), rectShrinked, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL);
break;
- case FILE_DIFFERENT:
- dc.DrawLabel(wxEmptyString, *globalResource.bitmapDifferentSmall, rectShrinked, wxALIGN_CENTER | wxALIGN_CENTER_VERTICAL);
+ case CustomGridMiddle::BLOCKPOS_MIDDLE:
+ dc.DrawLabel(wxEmptyString, getSyncOpImage(rowData->cmpResult, true, SYNC_DIR_NONE), rectShrinked, wxALIGN_CENTER | wxALIGN_CENTER_VERTICAL);
break;
- case FILE_EQUAL:
- dc.DrawLabel(wxEmptyString, *globalResource.bitmapEqualSmall, rectShrinked, wxALIGN_CENTER | wxALIGN_CENTER_VERTICAL);
- break;
- case FILE_CONFLICT:
- dc.DrawLabel(wxEmptyString, *globalResource.bitmapConflictSmall, rectShrinked, wxALIGN_CENTER | wxALIGN_CENTER_VERTICAL);
+ case CustomGridMiddle::BLOCKPOS_RIGHT:
+ dc.DrawLabel(wxEmptyString, getSyncOpImage(rowData->cmpResult, true, SYNC_DIR_RIGHT), rectShrinked, wxALIGN_RIGHT | wxALIGN_CENTER_VERTICAL);
break;
}
+ else //default
+ {
+ const wxBitmap& syncOpIcon = getSyncOpImage(rowData->cmpResult, rowData->selectedForSynchronization, rowData->direction);
+ dc.DrawLabel(wxEmptyString, syncOpIcon, rectShrinked, wxALIGN_CENTER | wxALIGN_CENTER_VERTICAL);
+ }
+ }
+ else //comparison results view
+ {
+ switch (rowData->cmpResult)
+ {
+ case FILE_LEFT_SIDE_ONLY:
+ dc.DrawLabel(wxEmptyString, *GlobalResources::getInstance().bitmapLeftOnlySmall, rectShrinked, wxALIGN_CENTER | wxALIGN_CENTER_VERTICAL);
+ break;
+ case FILE_RIGHT_SIDE_ONLY:
+ dc.DrawLabel(wxEmptyString, *GlobalResources::getInstance().bitmapRightOnlySmall, rectShrinked, wxALIGN_CENTER | wxALIGN_CENTER_VERTICAL);
+ break;
+ case FILE_LEFT_NEWER:
+ dc.DrawLabel(wxEmptyString, *GlobalResources::getInstance().bitmapLeftNewerSmall, rectShrinked, wxALIGN_CENTER | wxALIGN_CENTER_VERTICAL);
+ break;
+ case FILE_RIGHT_NEWER:
+ dc.DrawLabel(wxEmptyString, *GlobalResources::getInstance().bitmapRightNewerSmall, rectShrinked, wxALIGN_CENTER | wxALIGN_CENTER_VERTICAL);
+ break;
+ case FILE_DIFFERENT:
+ dc.DrawLabel(wxEmptyString, *GlobalResources::getInstance().bitmapDifferentSmall, rectShrinked, wxALIGN_CENTER | wxALIGN_CENTER_VERTICAL);
+ break;
+ case FILE_EQUAL:
+ dc.DrawLabel(wxEmptyString, *GlobalResources::getInstance().bitmapEqualSmall, rectShrinked, wxALIGN_CENTER | wxALIGN_CENTER_VERTICAL);
+ break;
+ case FILE_CONFLICT:
+ dc.DrawLabel(wxEmptyString, *GlobalResources::getInstance().bitmapConflictSmall, rectShrinked, wxALIGN_CENTER | wxALIGN_CENTER_VERTICAL);
+ break;
}
-
- return;
}
- }
- //fallback
- wxGridCellStringRenderer::Draw(grid, attr, dc, rect, row, col, isSelected);
+ return;
+ }
}
-private:
- const CustomGridMiddle* const m_gridMiddle;
-};
+ //fallback
+ wxGridCellStringRenderer::Draw(grid, attr, dc, rect, row, col, isSelected);
+}
-bool CustomGridMiddle::CreateGrid(int numRows, int numCols, wxGrid::wxGridSelectionModes selmode)
+//this method is called when grid view changes: useful for parallel updating of multiple grids
+void CustomGridMiddle::alignOtherGrids(CustomGrid* gridLeft, CustomGrid* gridMiddle, CustomGrid* gridRight)
{
- 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
- SetDefaultRenderer(new GridCellRendererMiddle(this)); //SetDefaultRenderer takes ownership!
-
- return true;
+ int x = 0;
+ int y = 0;
+ GetViewStart(&x, &y);
+ gridLeft->Scroll(-1, y);
+ gridRight->Scroll(-1, y);
}
-//this method is called when grid view changes: useful for parallel updating of multiple grids
-void CustomGridMiddle::DoPrepareDC(wxDC& dc)
+void CustomGridMiddle::DrawColLabel(wxDC& dc, int col)
{
- wxScrollHelper::DoPrepareDC(dc);
+ CustomGrid::DrawColLabel(dc, col);
- int x, y = 0;
- if (isLeadGrid()) //avoid back coupling
- {
- GetViewStart(&x, &y);
- m_gridLeft->Scroll(-1, y);
- m_gridRight->Scroll(-1, y);
- }
+ const wxRect rect(GetColLeft(col), 0, GetColWidth(col), GetColLabelSize());
+
+ if (gridDataTable->syncPreviewIsActive())
+ dc.DrawLabel(wxEmptyString, *GlobalResources::getInstance().bitmapSyncViewSmall, rect, wxALIGN_CENTER_HORIZONTAL | wxALIGN_CENTER_VERTICAL);
+ else
+ dc.DrawLabel(wxEmptyString, *GlobalResources::getInstance().bitmapCmpViewSmall, rect, wxALIGN_CENTER_HORIZONTAL | wxALIGN_CENTER_VERTICAL);
}
diff --git a/library/CustomGrid.h b/library/CustomGrid.h
index 89ec39d0..98e86e11 100644
--- a/library/CustomGrid.h
+++ b/library/CustomGrid.h
@@ -5,12 +5,19 @@
#include <wx/grid.h>
#include "../structures.h"
#include "processXml.h"
+#include <map>
+#include <memory>
-
-class CustomGridTable;
class CustomGridTableRim;
+class CustomGridTableLeft;
+class CustomGridTableRight;
class CustomGridTableMiddle;
class GridCellRendererMiddle;
+class wxTimer;
+class CustomGridRim;
+class CustomGridLeft;
+class CustomGridMiddle;
+class CustomGridRight;
namespace FreeFileSync
{
@@ -43,50 +50,89 @@ public:
virtual ~CustomGrid() {}
- virtual void DrawColLabel(wxDC& dc, int col);
+ void initSettings(CustomGridLeft* gridLeft,
+ CustomGridMiddle* gridMiddle,
+ CustomGridRight* gridRight,
+ const FreeFileSync::GridView* gridDataView);
std::set<int> getAllSelectedRows() const;
//set sort direction indicator on UI
- void setSortMarker(const int sortColumn, const wxBitmap* bitmap = &wxNullBitmap);
+ typedef int SortColumn;
+
+ enum SortDirection
+ {
+ ASCENDING,
+ DESCENDING,
+ };
+
+ typedef std::pair<SortColumn, SortDirection> SortMarker;
+ void setSortMarker(SortMarker marker);
bool isLeadGrid() const;
protected:
- CustomGrid* m_gridLeft;
- CustomGrid* m_gridMiddle;
- CustomGrid* m_gridRight;
+ void RefreshCell(int row, int col);
+
+ virtual void DrawColLabel(wxDC& dc, int col);
private:
+ virtual void setGridDataTable(const FreeFileSync::GridView* gridDataView) = 0;
+
+//this method is called when grid view changes: useful for parallel updating of multiple grids
+ virtual void DoPrepareDC(wxDC& dc);
+
+ virtual void alignOtherGrids(CustomGrid* gridLeft, CustomGrid* gridMiddle, CustomGrid* gridRight) = 0;
+
void onGridAccess(wxEvent& event);
void adjustGridHeights(wxEvent& event);
+ CustomGrid* m_gridLeft;
+ CustomGrid* m_gridMiddle;
+ CustomGrid* m_gridRight;
+
bool isLeading; //identify grid that has user focus
- int currentSortColumn;
- const wxBitmap* sortMarker;
+
+ SortMarker m_marker;
+};
+
+
+template <bool showFileIcons>
+class GridCellRenderer;
+
+
+//-----------------------------------------------------------
+#ifdef FFS_WIN
+class IconUpdater : public wxEvtHandler //update file icons periodically: use SINGLE instance to coordinate left and right grid at once
+{
+public:
+ IconUpdater(CustomGridLeft* leftGrid, CustomGridRight* rightGrid);
+
+private:
+ void loadIconsAsynchronously(wxEvent& event); //loads all (not yet) drawn icons
+
+ CustomGridRim* m_leftGrid;
+ CustomGridRim* m_rightGrid;
+
+ std::auto_ptr<wxTimer> m_timer; //user timer event to periodically update icons: better than idle event because also active when scrolling! :)
};
+#endif
//############## SPECIALIZATIONS ###################
class CustomGridRim : public CustomGrid
{
+ friend class IconUpdater;
+ template <bool showFileIcons>
+ friend class GridCellRenderer;
+
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() {}
-
- 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,
- const FreeFileSync::GridView* gridDataView);
+ const wxString& name);
//notify wxGrid that underlying table size has changed
void updateGridSizes();
@@ -99,12 +145,32 @@ public:
xmlAccess::ColumnTypes getTypeAtPos(unsigned pos) const;
static wxString getTypeName(xmlAccess::ColumnTypes colType);
- virtual void enableFileIcons(const bool value) = 0;
-
-protected:
- CustomGridTableRim* gridDataTable;
+#ifdef FFS_WIN
+ void enableFileIcons(const bool value);
+#endif
private:
+ CustomGridTableRim* getGridDataTable();
+ virtual const CustomGridTableRim* getGridDataTable() const = 0;
+
+#ifdef FFS_WIN
+ //asynchronous icon loading
+ void getIconsToBeLoaded(std::vector<Zstring>& newLoad); //loads all (not yet) drawn icons
+
+ typedef unsigned int FromRow;
+ typedef unsigned int ToRow;
+ typedef std::pair<FromRow, ToRow> VisibleRowRange;
+ VisibleRowRange getVisibleRows();
+
+
+ typedef unsigned int RowNumber;
+ typedef bool IconLoaded;
+ typedef std::map<RowNumber, IconLoaded> LoadSuccess;
+ LoadSuccess loadIconSuccess; //save status of last icon load when drawing on GUI
+
+ bool fileIconsAreEnabled;
+#endif
+
xmlAccess::ColumnAttributes columnSettings; //set visibility, position and width of columns
};
@@ -119,14 +185,16 @@ public:
long style = wxWANTS_CHARS,
const wxString& name = wxGridNameStr);
- ~CustomGridLeft() {}
-
virtual bool CreateGrid(int numRows, int numCols, wxGrid::wxGridSelectionModes selmode = wxGrid::wxGridSelectCells);
- virtual void enableFileIcons(const bool value);
+private:
+ virtual void setGridDataTable(const FreeFileSync::GridView* gridDataView);
+ virtual const CustomGridTableRim* getGridDataTable() const;
//this method is called when grid view changes: useful for parallel updating of multiple grids
- virtual void DoPrepareDC(wxDC& dc);
+ virtual void alignOtherGrids(CustomGrid* gridLeft, CustomGrid* gridMiddle, CustomGrid* gridRight);
+
+ CustomGridTableLeft* gridDataTable;
};
@@ -140,14 +208,16 @@ public:
long style = wxWANTS_CHARS,
const wxString& name = wxGridNameStr);
- ~CustomGridRight() {}
-
virtual bool CreateGrid(int numRows, int numCols, wxGrid::wxGridSelectionModes selmode = wxGrid::wxGridSelectCells);
- virtual void enableFileIcons(const bool value);
+private:
+ virtual void setGridDataTable(const FreeFileSync::GridView* gridDataView);
+ virtual const CustomGridTableRim* getGridDataTable() const;
//this method is called when grid view changes: useful for parallel updating of multiple grids
- virtual void DoPrepareDC(wxDC& dc);
+ virtual void alignOtherGrids(CustomGrid* gridLeft, CustomGrid* gridMiddle, CustomGrid* gridRight);
+
+ CustomGridTableRight* gridDataTable;
};
@@ -163,35 +233,31 @@ public:
long style = wxWANTS_CHARS,
const wxString& name = wxGridNameStr);
- ~CustomGridMiddle() {}
-
virtual bool CreateGrid(int numRows, int numCols, wxGrid::wxGridSelectionModes selmode = wxGrid::wxGridSelectCells);
- 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,
- const FreeFileSync::GridView* gridDataView);
-
void enableSyncPreview(bool value);
//notify wxGrid that underlying table size has changed
void updateGridSizes();
+private:
#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
+ virtual void setGridDataTable(const FreeFileSync::GridView* gridDataView);
+
//this method is called when grid view changes: useful for parallel updating of multiple grids
- virtual void DoPrepareDC(wxDC& dc);
+ virtual void alignOtherGrids(CustomGrid* gridLeft, CustomGrid* gridMiddle, CustomGrid* gridRight);
+
+ virtual void DrawColLabel(wxDC& dc, int col);
-private:
void OnMouseMovement(wxMouseEvent& event);
void OnLeaveWindow(wxMouseEvent& event);
void OnLeftMouseDown(wxMouseEvent& event);
void OnLeftMouseUp(wxMouseEvent& event);
//small helper methods
- void RefreshRow(int row);
enum BlockPosition //each cell can be divided into four blocks concerning mouse selections
{
BLOCKPOS_CHECK_BOX,
diff --git a/library/ShadowCopy/ShadowDll.vcproj b/library/ShadowCopy/ShadowDll.vcproj
new file mode 100644
index 00000000..637aad0c
--- /dev/null
+++ b/library/ShadowCopy/ShadowDll.vcproj
@@ -0,0 +1,219 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+ ProjectType="Visual C++"
+ Version="9,00"
+ Name="ShadowDll"
+ ProjectGUID="{70394AEF-5897-4911-AFA1-82EAF0581EFA}"
+ RootNamespace="ShadowDll"
+ Keyword="Win32Proj"
+ TargetFrameworkVersion="196613"
+ >
+ <Platforms>
+ <Platform
+ Name="Win32"
+ />
+ </Platforms>
+ <ToolFiles>
+ </ToolFiles>
+ <Configurations>
+ <Configuration
+ Name="Debug|Win32"
+ OutputDirectory="obj\$(SolutionDir)$(ConfigurationName)"
+ IntermediateDirectory="obj\$(ConfigurationName)"
+ ConfigurationType="2"
+ CharacterSet="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_USRDLL;SHADOWDLL_EXPORTS"
+ MinimalRebuild="true"
+ BasicRuntimeChecks="3"
+ RuntimeLibrary="3"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ DebugInformationFormat="4"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ OutputFile="Shadow.dll"
+ LinkIncremental="2"
+ GenerateDebugInformation="true"
+ SubSystem="2"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release|Win32"
+ OutputDirectory="obj\$(ConfigurationName)"
+ IntermediateDirectory="obj\$(ConfigurationName)"
+ ConfigurationType="2"
+ CharacterSet="1"
+ WholeProgramOptimization="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="2"
+ EnableIntrinsicFunctions="true"
+ PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_USRDLL;SHADOWDLL_EXPORTS"
+ RuntimeLibrary="2"
+ EnableFunctionLevelLinking="true"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ DebugInformationFormat="3"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ OutputFile="Shadow.dll"
+ LinkIncremental="1"
+ GenerateDebugInformation="true"
+ SubSystem="2"
+ OptimizeReferences="2"
+ EnableCOMDATFolding="2"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ </Configurations>
+ <References>
+ </References>
+ <Files>
+ <Filter
+ Name="Quelldateien"
+ Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx"
+ UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"
+ >
+ <File
+ RelativePath=".\dllmain.cpp"
+ >
+ <FileConfiguration
+ Name="Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ CompileAsManaged="0"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ CompileAsManaged="0"
+ />
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath=".\shadow.cpp"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Headerdateien"
+ Filter="h;hpp;hxx;hm;inl;inc;xsd"
+ UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"
+ >
+ <File
+ RelativePath=".\shadow.h"
+ >
+ </File>
+ </Filter>
+ <File
+ RelativePath=".\lib\vssapi.lib"
+ >
+ </File>
+ </Files>
+ <Globals>
+ </Globals>
+</VisualStudioProject>
diff --git a/library/ShadowCopy/dllmain.cpp b/library/ShadowCopy/dllmain.cpp
new file mode 100644
index 00000000..834b4f88
--- /dev/null
+++ b/library/ShadowCopy/dllmain.cpp
@@ -0,0 +1,22 @@
+// dllmain.cpp : Definiert den Einstiegspunkt für die DLL-Anwendung.
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+
+
+BOOL APIENTRY DllMain( HMODULE hModule,
+ DWORD ul_reason_for_call,
+ LPVOID lpReserved
+ )
+{
+ switch (ul_reason_for_call)
+ {
+ case DLL_PROCESS_ATTACH:
+ case DLL_THREAD_ATTACH:
+ case DLL_THREAD_DETACH:
+ case DLL_PROCESS_DETACH:
+ break;
+ }
+ return TRUE;
+}
+
diff --git a/library/ShadowCopy/shadow.cpp b/library/ShadowCopy/shadow.cpp
new file mode 100644
index 00000000..e10a4eb8
--- /dev/null
+++ b/library/ShadowCopy/shadow.cpp
@@ -0,0 +1,178 @@
+#include "shadow.h"
+
+#define WIN32_LEAN_AND_MEAN
+#include "windows.h"
+#include "inc/vss.h"
+#include "inc/vswriter.h"
+#include "inc/vsbackup.h"
+#include <algorithm>
+#include <string>
+#include <cstdio>
+#include <comdef.h>
+
+//typedef GUID VSS_ID;
+
+
+void writeString(const wchar_t* input, wchar_t* output, unsigned int outputBufferLen)
+{
+ const unsigned int newSize = min(wcslen(input) + 1, outputBufferLen); //including null-termination
+ memcpy(output, input, newSize * sizeof(wchar_t));
+}
+
+
+std::wstring numberToHexString(const long number)
+{
+ wchar_t result[100];
+ swprintf(result, 100, L"0x%08x", number);
+ return std::wstring(result);
+}
+
+
+void writeErrorMsg(const wchar_t* input, HRESULT hr, wchar_t* output, unsigned int outputBufferLen)
+{
+ std::wstring formattedMsg(input);
+ formattedMsg += L" (";
+ formattedMsg += numberToHexString(hr);
+ formattedMsg += L": ";
+ formattedMsg += _com_error(hr).ErrorMessage();
+ formattedMsg += L")";
+
+ writeString(formattedMsg.c_str(), output, outputBufferLen);
+}
+
+
+bool shadow::createShadowCopy(const wchar_t* volumeName,
+ wchar_t* shadowVolName,
+ unsigned int shadowBufferLen,
+ void** backupHandle,
+ wchar_t* errorMessage,
+ unsigned int errorBufferLen)
+{
+ //MessageBox(0, L"backup err", L"", 0); */
+ *backupHandle = NULL;
+ HRESULT hr = NULL;
+
+ IVssBackupComponents* pBackupComponents = NULL;
+ if (FAILED(hr = CreateVssBackupComponents(&pBackupComponents)))
+ {
+ if (hr == E_ACCESSDENIED)
+ writeErrorMsg(L"The caller does not have sufficient backup privileges or is not an administrator.", hr, errorMessage, errorBufferLen);
+ else
+ writeErrorMsg(L"Error calling \"CreateVssBackupComponents\".", hr, errorMessage, errorBufferLen);
+ return false;
+ }
+
+
+ if (FAILED(hr = pBackupComponents->InitializeForBackup()))
+ {
+ releaseShadowCopy(pBackupComponents);
+ writeErrorMsg(L"Error calling \"InitializeForBackup\".", hr, errorMessage, errorBufferLen);
+ return false;
+ }
+
+
+ IVssAsync* pWriteMetaData = NULL;
+ if (FAILED(hr = pBackupComponents->GatherWriterMetadata( &pWriteMetaData )))
+ {
+ releaseShadowCopy(pBackupComponents);
+ writeErrorMsg(L"\"Shadow.dll\" might be incompatible with this Operating System!\n\
+ Error calling \"GatherWriterMetadata\".", hr, errorMessage, errorBufferLen);
+ return false;
+ }
+
+ //wait for shadow copy writers to complete
+ hr = pWriteMetaData->Wait();
+ pWriteMetaData->Release();
+ if (FAILED(hr))
+ {
+ releaseShadowCopy(pBackupComponents);
+ writeErrorMsg(L"Error calling \"ppWriteMetaData->Wait\".", hr, errorMessage, errorBufferLen);
+ return false;
+ }
+
+
+ VSS_ID snapshotSetId = {0};
+ if (FAILED(hr = pBackupComponents->StartSnapshotSet( &snapshotSetId )))
+ {
+ releaseShadowCopy(pBackupComponents);
+ writeErrorMsg(L"Error calling \"StartSnapshotSet\".", hr, errorMessage, errorBufferLen);
+ return false;
+ }
+
+
+ VSS_ID SnapShotId = {0};
+ if (FAILED(hr = pBackupComponents->AddToSnapshotSet(const_cast<wchar_t*>(volumeName), GUID_NULL, &SnapShotId)))
+ {
+ releaseShadowCopy(pBackupComponents);
+ writeErrorMsg(L"Error calling \"AddToSnapshotSet\".", hr, errorMessage, errorBufferLen);
+ return false;
+ }
+
+
+ if (FAILED(hr = pBackupComponents->SetBackupState( false, false, VSS_BT_FULL )))
+ {
+ releaseShadowCopy(pBackupComponents);
+ writeErrorMsg(L"Error calling \"SetBackupState\".", hr, errorMessage, errorBufferLen);
+ return false;
+ }
+
+
+ IVssAsync* pPrepare = NULL;
+ if (FAILED(hr = pBackupComponents->PrepareForBackup( &pPrepare )))
+ {
+ releaseShadowCopy(pBackupComponents);
+ writeErrorMsg(L"Error calling \"PrepareForBackup\".", hr, errorMessage, errorBufferLen);
+ return false;
+ }
+
+ hr = pPrepare->Wait();
+ pPrepare->Release();
+ if (FAILED(hr))
+ {
+ releaseShadowCopy(pBackupComponents);
+ writeErrorMsg(L"Error calling \"pPrepare->Wait\".", hr, errorMessage, errorBufferLen);
+ return false;
+ }
+
+
+ IVssAsync* pDoShadowCopy = NULL;
+ if (FAILED(hr = pBackupComponents->DoSnapshotSet( &pDoShadowCopy )))
+ {
+ releaseShadowCopy(pBackupComponents);
+ writeErrorMsg(L"Error calling \"DoSnapshotSet\".", hr, errorMessage, errorBufferLen);
+ return false;
+ }
+
+ hr = pDoShadowCopy->Wait();
+ pDoShadowCopy->Release();
+ if (FAILED(hr))
+ {
+ releaseShadowCopy(pBackupComponents);
+ writeErrorMsg(L"Error calling \"pPrepare->Wait\".", hr, errorMessage, errorBufferLen);
+ return false;
+ }
+
+ VSS_SNAPSHOT_PROP props;
+ if (FAILED(hr = pBackupComponents->GetSnapshotProperties( SnapShotId, &props )))
+ {
+ releaseShadowCopy(pBackupComponents);
+ writeErrorMsg(L"Error calling \"GetSnapshotProperties\".", hr, errorMessage, errorBufferLen);
+ return false;
+ }
+
+ //finally: write volume name of newly created shadow copy
+ writeString(props.m_pwszSnapshotDeviceObject, shadowVolName, shadowBufferLen);
+
+ VssFreeSnapshotProperties(&props);
+
+ *backupHandle = pBackupComponents;
+
+ return true;
+}
+
+
+void shadow::releaseShadowCopy(void* backupHandle)
+{
+ if (backupHandle != NULL)
+ static_cast<IVssBackupComponents*>(backupHandle)->Release();
+}
diff --git a/library/ShadowCopy/shadow.h b/library/ShadowCopy/shadow.h
new file mode 100644
index 00000000..e25c6a32
--- /dev/null
+++ b/library/ShadowCopy/shadow.h
@@ -0,0 +1,33 @@
+#ifndef SHADOWCOPY_H
+#define SHADOWCOPY_H
+
+#ifdef SHADOWDLL_EXPORTS
+#define SHADOWDLL_API extern "C" __declspec(dllexport)
+#else
+#define SHADOWDLL_API extern "C" __declspec(dllimport)
+#endif
+
+
+namespace shadow
+{
+ //COM needs to be initialized before calling any of these functions! CoInitializeEx/CoUninitialize
+
+
+ //volumeName must end with "\", while shadowVolName does not end with "\"
+ SHADOWDLL_API
+ bool createShadowCopy(const wchar_t* volumeName,
+ wchar_t* shadowVolName,
+ unsigned int shadowBufferLen,
+ void** backupHandle,
+ wchar_t* errorMessage,
+ unsigned int errorBufferLen);
+
+
+ //don't forget to release the backupHandle after shadow copy is not needed anymore!
+ SHADOWDLL_API
+ void releaseShadowCopy(void* backupHandle);
+}
+
+
+
+#endif //SHADOWCOPY_H
diff --git a/library/fileError.h b/library/fileError.h
new file mode 100644
index 00000000..214cdecb
--- /dev/null
+++ b/library/fileError.h
@@ -0,0 +1,25 @@
+#ifndef FILEERROR_H_INCLUDED
+#define FILEERROR_H_INCLUDED
+
+#include "zstring.h"
+#include "fileError.h"
+
+namespace FreeFileSync
+{
+ class FileError //Exception class used to notify file/directory copy/delete errors
+ {
+ public:
+ FileError(const Zstring& message) :
+ errorMessage(message) {}
+
+ const Zstring& show() const
+ {
+ return errorMessage;
+ }
+
+ private:
+ Zstring errorMessage;
+ };
+}
+
+#endif // FILEERROR_H_INCLUDED
diff --git a/library/fileHandling.cpp b/library/fileHandling.cpp
index b6d3d9d7..06431b81 100644
--- a/library/fileHandling.cpp
+++ b/library/fileHandling.cpp
@@ -7,6 +7,7 @@
#ifdef FFS_WIN
#include <wx/msw/wrapwin.h> //includes "windows.h"
+#include "shadow.h"
#elif defined FFS_LINUX
#include <sys/stat.h>
@@ -18,75 +19,76 @@
#include <errno.h>
#endif
+using FreeFileSync::FileError;
+
class RecycleBin
{
public:
- RecycleBin() :
- recycleBinAvailable(false)
+ static const RecycleBin& getInstance()
{
-#ifdef FFS_WIN
- recycleBinAvailable = true;
-#endif // FFS_WIN
+ static RecycleBin instance; //lazy creation of RecycleBin
+ return instance;
}
- ~RecycleBin() {}
-
- bool recycleBinExists()
+ bool recycleBinExists() const
{
return recycleBinAvailable;
}
- bool moveToRecycleBin(const Zstring& filename)
- {
- if (!recycleBinAvailable) //this method should ONLY be called if recycle bin is available
- throw RuntimeException(_("Initialization of Recycle Bin failed!"));
+ bool moveToRecycleBin(const Zstring& filename) const;
+private:
+ RecycleBin() :
+ recycleBinAvailable(false)
+ {
#ifdef FFS_WIN
- Zstring filenameDoubleNull = filename + wxChar(0);
-
- SHFILEOPSTRUCT fileOp;
- fileOp.hwnd = NULL;
- fileOp.wFunc = FO_DELETE;
- fileOp.pFrom = filenameDoubleNull.c_str();
- fileOp.pTo = NULL;
- fileOp.fFlags = FOF_ALLOWUNDO | FOF_NOCONFIRMATION | FOF_SILENT | FOF_NOERRORUI;
- fileOp.fAnyOperationsAborted = false;
- fileOp.hNameMappings = NULL;
- fileOp.lpszProgressTitle = NULL;
-
- if (SHFileOperation(&fileOp //pointer to an SHFILEOPSTRUCT structure that contains information the function needs to carry out
- ) != 0 || fileOp.fAnyOperationsAborted) return false;
-#else
- assert(false);
-#endif
-
- return true;
+ recycleBinAvailable = true;
+#endif // FFS_WIN
}
+ ~RecycleBin() {}
+
private:
bool recycleBinAvailable;
};
-inline
-RecycleBin& getRecycleBin()
+bool RecycleBin::moveToRecycleBin(const Zstring& filename) const
{
- static RecycleBin instance; //lazy creation of RecycleBin
- return instance;
+ if (!recycleBinAvailable) //this method should ONLY be called if recycle bin is available
+ throw RuntimeException(_("Initialization of Recycle Bin failed!"));
+
+#ifdef FFS_WIN
+ Zstring filenameDoubleNull = filename + wxChar(0);
+
+ SHFILEOPSTRUCT fileOp;
+ fileOp.hwnd = NULL;
+ fileOp.wFunc = FO_DELETE;
+ fileOp.pFrom = filenameDoubleNull.c_str();
+ fileOp.pTo = NULL;
+ fileOp.fFlags = FOF_ALLOWUNDO | FOF_NOCONFIRMATION | FOF_SILENT | FOF_NOERRORUI;
+ fileOp.fAnyOperationsAborted = false;
+ fileOp.hNameMappings = NULL;
+ fileOp.lpszProgressTitle = NULL;
+
+ if (SHFileOperation(&fileOp) != 0 || fileOp.fAnyOperationsAborted) return false;
+#endif
+
+ return true;
}
bool FreeFileSync::recycleBinExists()
{
- return getRecycleBin().recycleBinExists();
+ return RecycleBin::getInstance().recycleBinExists();
}
inline
bool moveToRecycleBin(const Zstring& filename) throw(RuntimeException)
{
- return getRecycleBin().moveToRecycleBin(filename);
+ return RecycleBin::getInstance().moveToRecycleBin(filename);
}
@@ -286,7 +288,7 @@ private:
class KernelDllHandler //dynamically load windows API functions
{
- typedef DWORD WINAPI (*GetFinalPath)(
+ typedef DWORD (WINAPI *GetFinalPath)(
HANDLE hFile,
LPTSTR lpszFilePath,
DWORD cchFilePath,
@@ -365,11 +367,11 @@ void createDirectoryRecursively(const Zstring& directory, const Zstring& templat
return;
//try to create parent folders first
- const Zstring dirParent = directory.BeforeLast(GlobalResources::FILE_NAME_SEPARATOR);
+ const Zstring dirParent = directory.BeforeLast(FreeFileSync::FILE_NAME_SEPARATOR);
if (!dirParent.empty() && !wxDirExists(dirParent))
{
//call function recursively
- const Zstring templateParent = templateDir.BeforeLast(GlobalResources::FILE_NAME_SEPARATOR);
+ const Zstring templateParent = templateDir.BeforeLast(FreeFileSync::FILE_NAME_SEPARATOR);
createDirectoryRecursively(dirParent, templateParent, false, level + 1); //don't create symbolic links in recursion!
}
@@ -488,13 +490,13 @@ void FreeFileSync::createDirectory(const Zstring& directory, const Zstring& temp
//remove trailing separator
Zstring dirFormatted;
if (FreeFileSync::endsWithPathSeparator(directory))
- dirFormatted = directory.BeforeLast(GlobalResources::FILE_NAME_SEPARATOR);
+ dirFormatted = directory.BeforeLast(FreeFileSync::FILE_NAME_SEPARATOR);
else
dirFormatted = directory;
Zstring templateFormatted;
if (FreeFileSync::endsWithPathSeparator(templateDir))
- templateFormatted = templateDir.BeforeLast(GlobalResources::FILE_NAME_SEPARATOR);
+ templateFormatted = templateDir.BeforeLast(FreeFileSync::FILE_NAME_SEPARATOR);
else
templateFormatted = templateDir;
@@ -502,7 +504,95 @@ void FreeFileSync::createDirectory(const Zstring& directory, const Zstring& temp
}
-#ifdef FFS_LINUX
+#ifdef FFS_WIN
+
+#ifndef COPY_FILE_COPY_SYMLINK
+const DWORD COPY_FILE_COPY_SYMLINK = 0x00000800;
+#endif
+
+DWORD CALLBACK copyCallbackInternal(
+ LARGE_INTEGER totalFileSize,
+ LARGE_INTEGER totalBytesTransferred,
+ LARGE_INTEGER streamSize,
+ LARGE_INTEGER streamBytesTransferred,
+ DWORD dwStreamNumber,
+ DWORD dwCallbackReason,
+ HANDLE hSourceFile,
+ HANDLE hDestinationFile,
+ LPVOID lpData)
+{
+ using FreeFileSync::CopyFileCallback;
+
+ //small performance optimization: it seems this callback function is called for every 64 kB (depending on cluster size).
+ static unsigned int callNr = 0;
+ if (++callNr % 50 == 0) //reduce by factor of 50 =^ 10-20 calls/sec
+ {
+ if (lpData != NULL)
+ {
+ //some odd check for some possible(?) error condition
+ if (totalBytesTransferred.HighPart < 0) //let's see if someone answers the call...
+ wxMessageBox(wxT("You've just discovered a bug in WIN32 API function \"CopyFileEx\"! \n\n\
+ Please write a mail to the author of FreeFileSync at zhnmju123@gmx.de and simply state that\n\
+ \"totalBytesTransferred.HighPart can be below zero\"!\n\n\
+ This will then be handled in future versions of FreeFileSync.\n\nThanks -ZenJu"), wxT("Warning"));
+
+
+ CopyFileCallback* callback = static_cast<CopyFileCallback*>(lpData);
+
+ switch (callback->updateCopyStatus(wxULongLong(totalBytesTransferred.HighPart, totalBytesTransferred.LowPart)))
+ {
+ case CopyFileCallback::CONTINUE:
+ break;
+ case CopyFileCallback::CANCEL:
+ return PROGRESS_CANCEL;
+ }
+ }
+ }
+
+ return PROGRESS_CONTINUE;
+}
+
+
+void FreeFileSync::copyFile(const Zstring& sourceFile,
+ const Zstring& targetFile,
+ const bool copyFileSymLinks,
+ ShadowCopy* shadowCopyHandler,
+ CopyFileCallback* callback)
+{
+ DWORD copyFlags = COPY_FILE_FAIL_IF_EXISTS;
+
+ if (copyFileSymLinks) //copy symbolic links instead of the files pointed at
+ copyFlags |= COPY_FILE_COPY_SYMLINK;
+
+ if (!::CopyFileEx( //same performance as CopyFile()
+ sourceFile.c_str(),
+ targetFile.c_str(),
+ copyCallbackInternal,
+ callback,
+ NULL,
+ copyFlags))
+ {
+ const DWORD lastError = ::GetLastError();
+
+ //if file is locked (try to) use Windows Volume Shadow Copy Service
+ if (lastError == ERROR_SHARING_VIOLATION && shadowCopyHandler != NULL)
+ {
+ const Zstring shadowFilename(shadowCopyHandler->makeShadowCopy(sourceFile));
+ FreeFileSync::copyFile(shadowFilename, //transferred bytes is automatically reset when new file is copied
+ targetFile,
+ copyFileSymLinks,
+ shadowCopyHandler,
+ callback);
+ return;
+ }
+
+ const Zstring errorMessage = Zstring(_("Error copying file:")) + wxT("\n\"") + sourceFile + wxT("\" -> \"") + targetFile + wxT("\"");
+ throw FileError(errorMessage + wxT("\n\n") + FreeFileSync::getLastErrorFormatted(lastError));
+ }
+}
+
+
+#elif defined FFS_LINUX
struct MemoryAllocator
{
MemoryAllocator()
@@ -523,9 +613,10 @@ struct MemoryAllocator
void FreeFileSync::copyFile(const Zstring& sourceFile,
const Zstring& targetFile,
const bool copyFileSymLinks,
- CopyFileCallback callback,
- void* data)
+ CopyFileCallback* callback)
{
+ using FreeFileSync::CopyFileCallback;
+
try
{
if (FreeFileSync::fileExists(targetFile.c_str()))
@@ -609,8 +700,18 @@ void FreeFileSync::copyFile(const Zstring& sourceFile,
totalBytesTransferred += memory.bufferSize;
//invoke callback method to update progress indicators
- if (callback)
- callback(totalBytesTransferred, data);
+ if (callback != NULL)
+ {
+ switch (callback->updateCopyStatus(totalBytesTransferred))
+ {
+ case CopyFileCallback::CONTINUE:
+ break;
+ case CopyFileCallback::CANCEL:
+ fileOut.close();
+ wxRemoveFile(targetFile.c_str()); //don't handle error situations!
+ return;
+ }
+ }
}
//close streams before changing attributes
@@ -741,7 +842,7 @@ public:
//ensure directoryFormatted ends with backslash
const Zstring directoryFormatted = FreeFileSync::endsWithPathSeparator(directory) ?
directory :
- directory + GlobalResources::FILE_NAME_SEPARATOR;
+ directory + FreeFileSync::FILE_NAME_SEPARATOR;
WIN32_FIND_DATA fileMetaData;
HANDLE searchHandle = FindFirstFile((directoryFormatted + DefaultChar('*')).c_str(), //pointer to name of file to search for
@@ -847,7 +948,7 @@ public:
const Zstring fullName = FreeFileSync::endsWithPathSeparator(directory) ? //e.g. "/"
directory + name :
- directory + GlobalResources::FILE_NAME_SEPARATOR + name;
+ directory + FreeFileSync::FILE_NAME_SEPARATOR + name;
struct stat fileInfo;
if (lstat(fullName.c_str(), &fileInfo) != 0) //lstat() does not resolve symlinks
@@ -929,7 +1030,7 @@ void FreeFileSync::traverseInDetail(const Zstring& directory,
Zstring directoryFormatted = directory;
#ifdef FFS_LINUX //remove trailing slash
if (directoryFormatted.size() > 1 && FreeFileSync::endsWithPathSeparator(directoryFormatted))
- directoryFormatted = directoryFormatted.BeforeLast(GlobalResources::FILE_NAME_SEPARATOR);
+ directoryFormatted = directoryFormatted.BeforeLast(FreeFileSync::FILE_NAME_SEPARATOR);
#endif
if (traverseDirectorySymlinks)
@@ -954,7 +1055,7 @@ Zstring getDriveName(const Zstring& directoryName) //GetVolume() doesn't work un
if (volumeName.empty())
return Zstring();
- return volumeName + wxFileName::GetVolumeSeparator().c_str() + GlobalResources::FILE_NAME_SEPARATOR;
+ return volumeName + wxFileName::GetVolumeSeparator().c_str() + FreeFileSync::FILE_NAME_SEPARATOR;
}
diff --git a/library/fileHandling.h b/library/fileHandling.h
index de068d1f..3abdbe07 100644
--- a/library/fileHandling.h
+++ b/library/fileHandling.h
@@ -3,21 +3,7 @@
#include <wx/dir.h>
#include "zstring.h"
-
-class FileError //Exception class used to notify file/directory copy/delete errors
-{
-public:
- FileError(const Zstring& message) :
- errorMessage(message) {}
-
- const Zstring& show() const
- {
- return errorMessage;
- }
-
-private:
- Zstring errorMessage;
-};
+#include "fileError.h"
namespace FreeFileSync
@@ -50,15 +36,34 @@ namespace FreeFileSync
void removeDirectory(const Zstring& directory, const bool useRecycleBin);
void removeFile(const Zstring& filename, const bool useRecycleBin);
void createDirectory(const Zstring& directory, const Zstring& templateDir, const bool copyDirectorySymLinks);
-#ifdef FFS_LINUX
- //callback function for status updates whily copying
- typedef void (*CopyFileCallback)(const wxULongLong& totalBytesTransferred, void* data);
+
+ class CopyFileCallback //callback functionality
+ {
+ public:
+ virtual ~CopyFileCallback() {}
+
+ enum Response
+ {
+ CONTINUE,
+ CANCEL
+ };
+ virtual Response updateCopyStatus(const wxULongLong& totalBytesTransferred) = 0;
+ };
+
+ class ShadowCopy;
+#ifdef FFS_WIN
void copyFile(const Zstring& sourceFile,
const Zstring& targetFile,
const bool copyFileSymLinks,
- CopyFileCallback callback = NULL,
- void* data = NULL);
+ ShadowCopy* shadowCopyHandler = NULL, //supply handler for making shadow copies
+ CopyFileCallback* callback = NULL);
+
+#elif defined FFS_LINUX
+ void copyFile(const Zstring& sourceFile,
+ const Zstring& targetFile,
+ const bool copyFileSymLinks,
+ CopyFileCallback* callback);
#endif
}
diff --git a/library/filter.cpp b/library/filter.cpp
index d9ba8c5f..65513630 100644
--- a/library/filter.cpp
+++ b/library/filter.cpp
@@ -4,6 +4,7 @@
#include <set>
#include <vector>
#include "resources.h"
+#include "globalFunctions.h"
using FreeFileSync::FilterProcess;
@@ -36,14 +37,6 @@ void compoundStringToTable(const Zstring& compoundInput, const DefaultChar* deli
}
-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);
-}
-
-
std::vector<Zstring> compoundStringToFilter(const Zstring& filterString)
{
//delimiters may be ';' or '\n'
@@ -55,7 +48,8 @@ std::vector<Zstring> compoundStringToFilter(const Zstring& filterString)
{
std::vector<Zstring> newEntries;
compoundStringToTable(*i, wxT("\n"), newEntries);
- mergeVectors(filterList, newEntries);
+
+ globalFunctions::mergeVectors(newEntries, filterList);
}
return filterList;
@@ -76,12 +70,12 @@ void addFilterEntry(const Zstring& filtername, std::set<Zstring>& fileFilter, st
#endif
//remove leading separators (keep BEFORE test for Zstring::empty()!)
- if (filterFormatted.length() > 0 && *filterFormatted.c_str() == GlobalResources::FILE_NAME_SEPARATOR)
+ if (filterFormatted.length() > 0 && *filterFormatted.c_str() == FreeFileSync::FILE_NAME_SEPARATOR)
filterFormatted = Zstring(filterFormatted.c_str() + 1);
if (!filterFormatted.empty())
{
- //Test if filterFormatted ends with GlobalResources::FILE_NAME_SEPARATOR, ignoring '*' and '?'.
+ //Test if filterFormatted ends with FreeFileSync::FILE_NAME_SEPARATOR, ignoring '*' and '?'.
//If so, treat as filter for directory and add to directoryFilter.
const DefaultChar* filter = filterFormatted.c_str();
int i = filterFormatted.length() - 1;
@@ -93,7 +87,7 @@ void addFilterEntry(const Zstring& filtername, std::set<Zstring>& fileFilter, st
break;
}
- if (i >= 0 && filter[i] == GlobalResources::FILE_NAME_SEPARATOR) //last FILE_NAME_SEPARATOR found
+ if (i >= 0 && filter[i] == FreeFileSync::FILE_NAME_SEPARATOR) //last FILE_NAME_SEPARATOR found
{
if (i != int(filterFormatted.length()) - 1) // "name\*"
{
@@ -124,7 +118,7 @@ bool matchesFilter(const DefaultChar* name, const std::set<Zstring>& filter)
const DefaultChar* const nameFormatted = name; //nothing to do here
#endif
- for (std::set<Zstring>::iterator j = filter.begin(); j != filter.end(); ++j)
+ for (std::set<Zstring>::const_iterator j = filter.begin(); j != filter.end(); ++j)
if (Zstring::Matches(nameFormatted, *j))
return true;
@@ -140,39 +134,43 @@ FilterProcess::FilterProcess(const wxString& includeFilter, const wxString& excl
//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());
+ const std::vector<Zstring> includeList = compoundStringToFilter(includeFilter.c_str());
+ const std::vector<Zstring> excludeList = compoundStringToFilter(excludeFilter.c_str());
//setup include/exclude filters for files and directories
- for (std::vector<Zstring>::iterator i = includeList.begin(); i != includeList.end(); ++i)
+ for (std::vector<Zstring>::const_iterator i = includeList.begin(); i != includeList.end(); ++i)
addFilterEntry(*i, filterFileIn, filterFolderIn);
- for (std::vector<Zstring>::iterator i = excludeList.begin(); i != excludeList.end(); ++i)
+ for (std::vector<Zstring>::const_iterator i = excludeList.begin(); i != excludeList.end(); ++i)
addFilterEntry(*i, filterFileEx, filterFolderEx);
}
-bool FilterProcess::matchesFileFilter(const DefaultChar* relFilename) const
+bool FilterProcess::matchesFileFilterIncl(const DefaultChar* relFilename) const
+{
+ return matchesFilter(relFilename, filterFileIn); //process include filters
+}
+
+
+bool FilterProcess::matchesFileFilterExcl(const DefaultChar* relFilename) const
+{
+ return matchesFilter(relFilename, filterFileEx); //process exclude filters
+}
+
+
+bool FilterProcess::matchesDirFilterIncl(const DefaultChar* relDirname) const
{
- if ( matchesFilter(relFilename, filterFileIn) && //process include filters
- !matchesFilter(relFilename, filterFileEx)) //process exclude filters
- return true;
- else
- return false;
+ return matchesFilter(relDirname, filterFolderIn); //process include filters
}
-bool FilterProcess::matchesDirFilter(const DefaultChar* relDirname) const
+bool FilterProcess::matchesDirFilterExcl(const DefaultChar* relDirname) const
{
- if ( matchesFilter(relDirname, filterFolderIn) && //process include filters
- !matchesFilter(relDirname, filterFolderEx)) //process exclude filters
- return true;
- else
- return false;
+ return matchesFilter(relDirname, filterFolderEx); //process exclude filters
}
-void FilterProcess::filterGridData(FolderComparison& folderCmp) const
+void FilterProcess::filterGridData(FreeFileSync::FolderComparison& folderCmp) const
{
//execute filtering...
for (FolderComparison::iterator j = folderCmp.begin(); j != folderCmp.end(); ++j)
@@ -181,40 +179,31 @@ void FilterProcess::filterGridData(FolderComparison& folderCmp) const
for (FileComparison::iterator i = fileCmp.begin(); i != fileCmp.end(); ++i)
{
- //left hand side
- if (i->fileDescrLeft.objType == FileDescrLine::TYPE_FILE)
- {
- if (!matchesFileFilter(i->fileDescrLeft.relativeName.c_str()))
- {
- i->selectedForSynchronization = false;
- continue;
- }
- }
- else if (i->fileDescrLeft.objType == FileDescrLine::TYPE_DIRECTORY)
- {
- if (!matchesDirFilter(i->fileDescrLeft.relativeName.c_str()))
- {
- i->selectedForSynchronization = false;
- continue;
- }
- }
- //right hand side
- else if (i->fileDescrRight.objType == FileDescrLine::TYPE_FILE)
+
+ const FileDescrLine& fileDescr = i->fileDescrLeft.objType != FileDescrLine::TYPE_NOTHING ?
+ i->fileDescrLeft :
+ i->fileDescrRight;
+
+ if (fileDescr.objType == FileDescrLine::TYPE_FILE)
{
- if (!matchesFileFilter(i->fileDescrRight.relativeName.c_str()))
+ if ( !matchesFileFilterIncl(fileDescr.relativeName.c_str()) ||
+ matchesFileFilterExcl(fileDescr.relativeName.c_str()))
{
i->selectedForSynchronization = false;
continue;
}
}
- else if (i->fileDescrRight.objType == FileDescrLine::TYPE_DIRECTORY)
+ else if (fileDescr.objType == FileDescrLine::TYPE_DIRECTORY)
{
- if (!matchesDirFilter(i->fileDescrRight.relativeName.c_str()))
+ if ( !matchesDirFilterIncl(fileDescr.relativeName.c_str()) ||
+ matchesDirFilterExcl(fileDescr.relativeName.c_str()))
{
i->selectedForSynchronization = false;
continue;
}
}
+ else
+ assert(false);
i->selectedForSynchronization = true;
}
@@ -236,14 +225,14 @@ void inOrExcludeAllRows(FreeFileSync::FolderComparison& folderCmp)
}
-void FilterProcess::includeAllRowsOnGrid(FolderComparison& folderCmp)
+void FilterProcess::includeAllRowsOnGrid(FreeFileSync::FolderComparison& folderCmp)
{
//remove all filters on currentGridData
inOrExcludeAllRows<true>(folderCmp);
}
-void FilterProcess::excludeAllRowsOnGrid(FolderComparison& folderCmp)
+void FilterProcess::excludeAllRowsOnGrid(FreeFileSync::FolderComparison& folderCmp)
{
//exclude all rows on currentGridData
inOrExcludeAllRows<false>(folderCmp);
diff --git a/library/filter.h b/library/filter.h
index dd716680..85df591c 100644
--- a/library/filter.h
+++ b/library/filter.h
@@ -11,8 +11,10 @@ namespace FreeFileSync
public:
FilterProcess(const wxString& includeFilter, const wxString& excludeFilter);
- bool matchesFileFilter(const DefaultChar* relFilename) const;
- bool matchesDirFilter(const DefaultChar* relDirname) const;
+ bool matchesFileFilterIncl(const DefaultChar* relFilename) const;
+ bool matchesFileFilterExcl(const DefaultChar* relFilename) const;
+ bool matchesDirFilterIncl(const DefaultChar* relDirname) const;
+ bool matchesDirFilterExcl(const DefaultChar* relDirname) const;
void filterGridData(FolderComparison& folderCmp) const;
diff --git a/library/globalFunctions.cpp b/library/globalFunctions.cpp
index 7c4a1b92..33932d67 100644
--- a/library/globalFunctions.cpp
+++ b/library/globalFunctions.cpp
@@ -1,40 +1,10 @@
#include "globalFunctions.h"
-#include "resources.h"
#include <wx/msgdlg.h>
#include <wx/file.h>
#include <fstream>
-
-
-std::string globalFunctions::numberToString(const unsigned int number)
-{
- char result[100];
- sprintf(result, "%u", number);
- return std::string(result);
-}
-
-
-std::string globalFunctions::numberToString(const int number)
-{
- char result[100];
- sprintf(result, "%d", number);
- return std::string(result);
-}
-
-
-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];
- sprintf(result, "%f", number);
- return std::string(result);
-}
+#include "../structures.h"
+#include <wx/stream.h>
+#include <wx/stopwatch.h>
wxString globalFunctions::numberToWxString(const unsigned int number)
@@ -97,7 +67,7 @@ wxString globalFunctions::includeNumberSeparator(const wxString& number)
{
wxString output(number);
for (int i = output.size() - 3; i > 0; i -= 3)
- output.insert(i, GlobalResources::THOUSANDS_SEPARATOR);
+ output.insert(i, FreeFileSync::THOUSANDS_SEPARATOR);
return output;
}
@@ -136,9 +106,10 @@ void globalFunctions::writeInt(wxOutputStream& stream, const int number)
//############################################################################
Performance::Performance() :
- resultWasShown(false)
+ resultWasShown(false),
+ timer(new wxStopWatch)
{
- timer.Start();
+ timer->Start();
}
@@ -152,8 +123,8 @@ Performance::~Performance()
void Performance::showResult()
{
resultWasShown = true;
- wxMessageBox(globalFunctions::numberToWxString(unsigned(timer.Time())) + wxT(" ms"));
- timer.Start(); //reset timer
+ wxMessageBox(globalFunctions::numberToWxString(unsigned(timer->Time())) + wxT(" ms"));
+ timer->Start(); //reset timer
}
@@ -203,5 +174,5 @@ void DebugLog::write(const wxString& logText)
wxString getCodeLocation(const wxString file, const int line)
{
- return wxString(file).AfterLast(GlobalResources::FILE_NAME_SEPARATOR) + wxT(", LINE ") + globalFunctions::numberToWxString(line) + wxT(" | ");
+ return wxString(file).AfterLast(FreeFileSync::FILE_NAME_SEPARATOR) + wxT(", LINE ") + globalFunctions::numberToWxString(line) + wxT(" | ");
}
diff --git a/library/globalFunctions.h b/library/globalFunctions.h
index 622babcb..689fa5f1 100644
--- a/library/globalFunctions.h
+++ b/library/globalFunctions.h
@@ -6,9 +6,14 @@
#include <vector>
#include <set>
#include <wx/string.h>
-#include <wx/stream.h>
-#include <wx/stopwatch.h>
#include <wx/longlong.h>
+#include <memory>
+#include <sstream>
+
+class wxInputStream;
+class wxOutputStream;
+class wxStopWatch;
+
namespace globalFunctions
{
@@ -25,10 +30,13 @@ namespace globalFunctions
return(d < 0 ? -d : d);
}
- 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
+ template <class T>
+ inline std::string numberToString(const T& number) //convert number to string the C++ way
+ {
+ std::stringstream ss;
+ ss << number;
+ return ss.str();
+ }
wxString numberToWxString(const unsigned int number); //convert number to wxString
wxString numberToWxString(const int number); //convert number to wxString
@@ -54,6 +62,43 @@ namespace globalFunctions
{
return wxLongLong(number.GetHi(), number.GetLo());
}
+
+
+ //Note: the following lines are a performance optimization for deleting elements from a vector. It is incredibly faster to create a new
+//vector and leave specific elements out than to delete row by row and force recopying of most elements for each single deletion (linear vs quadratic runtime)
+ template <class T>
+ void removeRowsFromVector(const std::set<int>& rowsToRemove, std::vector<T>& grid)
+ {
+ if (rowsToRemove.size() > 0)
+ {
+ std::vector<T> temp;
+
+ std::set<int>::const_iterator rowToSkipIndex = rowsToRemove.begin();
+ int rowToSkip = *rowToSkipIndex;
+
+ for (int i = 0; i < int(grid.size()); ++i)
+ {
+ if (i != rowToSkip)
+ temp.push_back(grid[i]);
+ else
+ {
+ ++rowToSkipIndex;
+ if (rowToSkipIndex != rowsToRemove.end())
+ rowToSkip = *rowToSkipIndex;
+ }
+ }
+ grid.swap(temp);
+ }
+ }
+
+
+ template <class T>
+ inline
+ void mergeVectors(const std::vector<T>& input, std::vector<T>& changing)
+ {
+ for (typename std::vector<T>::const_iterator i = input.begin(); i != input.end(); ++i) //nested dependent typename: see Meyers effective C++
+ changing.push_back(*i);
+ }
}
@@ -67,7 +112,7 @@ public:
private:
bool resultWasShown;
- wxStopWatch timer;
+ std::auto_ptr<wxStopWatch> timer;
};
//two macros for quick performance measurements
@@ -106,7 +151,6 @@ public:
wxString show() const
{
-
return errorMessage;
}
@@ -115,32 +159,4 @@ private:
};
-//Note: the following lines are a performance optimization for deleting elements from a vector. It is incredibly faster to create a new
-//vector and leave specific elements out than to delete row by row and force recopying of most elements for each single deletion (linear vs quadratic runtime)
-template <class T>
-void removeRowsFromVector(std::vector<T>& grid, const std::set<int>& rowsToRemove)
-{
- if (rowsToRemove.size() > 0)
- {
- std::vector<T> temp;
-
- 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
- {
- ++rowToSkipIndex;
- if (rowToSkipIndex != rowsToRemove.end())
- rowToSkip = *rowToSkipIndex;
- }
- }
- grid.swap(temp);
- }
-}
-
-
#endif // GLOBALFUNCTIONS_H_INCLUDED
diff --git a/library/iconBuffer.cpp b/library/iconBuffer.cpp
new file mode 100644
index 00000000..a969f689
--- /dev/null
+++ b/library/iconBuffer.cpp
@@ -0,0 +1,298 @@
+#include "iconBuffer.h"
+#include <wx/thread.h>
+#include "globalFunctions.h"
+#include <wx/utils.h>
+#include <wx/bitmap.h>
+#include <wx/msw/wrapwin.h> //includes "windows.h"
+#include <wx/msgdlg.h>
+#include "../algorithm.h"
+#include <wx/icon.h>
+#include <map>
+#include <queue>
+
+using FreeFileSync::IconBuffer;
+
+
+class BasicString //simple thread safe string class
+{
+public:
+ BasicString()
+ {
+ data = new DefaultChar[1]; //compliance with delete []
+ *data = 0; //compliance with c_str()
+ }
+
+ BasicString(const Zstring& other) //make DEEP COPY from Zstring!
+ {
+ data = new DefaultChar[other.length() + 1];
+ memcpy(data, other.c_str(), (other.length() + 1) * sizeof(DefaultChar));
+ }
+
+ BasicString(const BasicString& other)
+ {
+ const size_t otherLen = defaultLength(other.c_str());
+ data = new DefaultChar[otherLen + 1];
+ memcpy(data, other.c_str(), (otherLen + 1) * sizeof(DefaultChar));
+ }
+
+ ~BasicString()
+ {
+ delete [] data;
+ }
+
+ BasicString& operator=(const BasicString& other)
+ { //exception safety; handle self-assignment implicitly
+ const size_t otherLen = defaultLength(other.c_str());
+ DefaultChar* dataNew = new DefaultChar[otherLen + 1];
+ memcpy(dataNew, other.c_str(), (otherLen + 1) * sizeof(DefaultChar));
+
+ delete data;
+ data = dataNew;
+
+ return *this;
+ }
+
+ const DefaultChar* c_str() const
+ {
+ return data;
+ }
+
+private:
+ DefaultChar* data;
+};
+
+
+class WorkerThread : public wxThread
+{
+public:
+ WorkerThread(IconBuffer* iconBuff);
+
+ void setWorkload(const std::vector<Zstring>& load); //(re-)set new workload of icons to be retrieved
+ void quitThread();
+
+ virtual ExitCode Entry();
+
+private:
+ void doWork();
+
+//---------------------- Shared Data -------------------------
+ typedef BasicString FileName;
+
+ wxCriticalSection lockWorkload; //use for locking shared data
+ std::vector<FileName> workload; //processes last elements of vector first!
+ bool threadHasMutex;
+ bool threadExitIsRequested;
+//------------------------------------------------------------
+
+ //event: icon buffer -> woker thread
+ wxMutex threadIsListening;
+ wxCondition continueWork; //wake up thread
+
+ IconBuffer* iconBuffer;
+};
+
+
+WorkerThread::WorkerThread(IconBuffer* iconBuff) :
+ wxThread(wxTHREAD_JOINABLE),
+ threadHasMutex(false),
+ threadExitIsRequested(false),
+ threadIsListening(),
+ continueWork(threadIsListening),
+ iconBuffer(iconBuff)
+{
+ if (Create() != wxTHREAD_NO_ERROR)
+ throw RuntimeException(wxString(wxT("Error creating icon buffer worker thread!")));
+
+ if (Run() != wxTHREAD_NO_ERROR)
+ throw RuntimeException(wxString(wxT("Error starting icon buffer worker thread!")));
+
+ //wait until thread has aquired mutex
+ bool hasMutex = false;
+ while (!hasMutex)
+ {
+ wxMilliSleep(5);
+ wxCriticalSectionLocker dummy(lockWorkload);
+ hasMutex = threadHasMutex;
+ } //polling -> it's not nice, but works and is no issue
+}
+
+
+void WorkerThread::setWorkload(const std::vector<Zstring>& load) //(re-)set new workload of icons to be retrieved
+{
+ wxCriticalSectionLocker dummy(lockWorkload);
+
+ workload.clear();
+ for (std::vector<Zstring>::const_iterator i = load.begin(); i != load.end(); ++i)
+ workload.push_back(*i); //make DEEP COPY from Zstring!
+
+ if (!workload.empty())
+ continueWork.Signal(); //wake thread IF he is waiting
+}
+
+
+void WorkerThread::quitThread()
+{
+ wxMutexLocker dummy(threadIsListening); //wait until thread is in waiting state
+ threadExitIsRequested = true; //no sharing conflicts in this situation
+ continueWork.Signal(); //exit thread
+}
+
+
+wxThread::ExitCode WorkerThread::Entry()
+{
+ try
+ {
+ wxMutexLocker dummy(threadIsListening); //this lock needs to be called from WITHIN the thread => calling it from constructor(Main thread) would be useless
+ { //this mutex STAYS locked all the time except of continueWork.Wait()!
+ wxCriticalSectionLocker dummy(lockWorkload);
+ threadHasMutex = true;
+ }
+
+ while (true)
+ {
+ continueWork.Wait(); //waiting for continueWork.Signal(); unlocks Mutex "threadIsListening"
+
+ //no mutex needed in this context
+ if (threadExitIsRequested) //no mutex here: atomicity is not prob for a bool, but visibility (e.g. caching in registers)
+ return 0; //shouldn't be a problem nevertheless because of implicit memory barrier caused by mutex.Lock() in .Wait()
+
+ //do work: get the file icon.
+ doWork();
+ }
+ }
+ catch (RuntimeException& e) //exceptions must be catched per thread
+ {
+ wxMessageBox(e.show());
+ return 0;
+ }
+}
+
+
+void WorkerThread::doWork()
+{
+ BasicString fileName; //don't use Zstring: reference-counted objects are NOT THREADSAFE!!! e.g. double deletion might happen
+
+ //do work: get the file icon.
+ while (true)
+ {
+ {
+ wxCriticalSectionLocker dummy(lockWorkload);
+ if (workload.empty())
+ break; //enter waiting state
+ fileName = workload.back();
+ workload.pop_back();
+ }
+
+ if (iconBuffer->requestIcon(Zstring(fileName.c_str()))) //thread safety: Zstring okay, won't be reference-counted in requestIcon()
+ break; //icon already in buffer: enter waiting state
+
+ //load icon
+ SHFILEINFO fileInfo;
+ fileInfo.hIcon = 0; //initialize hIcon
+
+ if (SHGetFileInfo(fileName.c_str(), //NOTE: CoInitializeEx()/CoUninitialize() implicitly called by wxWidgets on program startup!
+ 0,
+ &fileInfo,
+ sizeof(fileInfo),
+ SHGFI_ICON | SHGFI_SMALLICON) &&
+
+ 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 newIcon; //attention: wxIcon uses reference counting!
+ newIcon.SetHICON(fileInfo.hIcon);
+ newIcon.SetSize(IconBuffer::ICON_SIZE, IconBuffer::ICON_SIZE);
+
+ iconBuffer->insertIntoBuffer(fileName.c_str(), newIcon); //thread safety: icon may be deleted only within insertIntoBuffer()
+
+ //freeing of icon handle seems to happen somewhere beyond wxIcon destructor
+ //if (!DestroyIcon(fileInfo.hIcon))
+ // throw RuntimeException(wxString(wxT("Error deallocating Icon handle!\n\n")) + FreeFileSync::getLastErrorFormatted());
+
+ }
+ else
+ {
+ //if loading of icon fails for whatever reason, just save a dummy icon to avoid re-loading
+ iconBuffer->insertIntoBuffer(fileName.c_str(), wxNullIcon);
+ }
+ }
+}
+
+//---------------------------------------------------------------------------------------------------
+
+typedef Zstring FileName;
+class IconDB : public std::map<FileName, wxIcon> {};
+class IconDbSequence : public std::queue<FileName> {};
+
+//---------------------------------------------------------------------------------------------------
+
+
+IconBuffer& IconBuffer::getInstance()
+{
+ static IconBuffer instance;
+ return instance;
+}
+
+
+IconBuffer::IconBuffer() :
+ lockIconDB( new wxCriticalSection),
+ buffer( new IconDB),
+ bufSequence(new IconDbSequence),
+ worker( new WorkerThread(this)) //might throw exceptions!
+{}
+
+
+IconBuffer::~IconBuffer()
+{
+ worker->quitThread();
+ worker->Wait(); //wait until thread has exitted
+}
+
+
+bool IconBuffer::requestIcon(const Zstring& fileName, wxIcon* icon)
+{
+ wxCriticalSectionLocker dummy(*lockIconDB);
+ IconDB::const_iterator i = buffer->find(fileName);
+
+ if (i != buffer->end())
+ {
+ if (icon != NULL)
+ *icon = i->second;
+ return true;
+ }
+
+ return false;
+}
+
+
+void IconBuffer::setWorkload(const std::vector<Zstring>& load)
+{
+ worker->setWorkload(load);
+}
+
+
+void IconBuffer::insertIntoBuffer(const DefaultChar* fileName, const wxIcon& icon) //called by worker thread
+{
+ if (icon.IsOk()) //this check won't hurt
+ {
+ wxCriticalSectionLocker dummy(*lockIconDB);
+
+ const Zstring fileNameZ = fileName;
+
+ const std::pair<IconDB::iterator, bool> rc = buffer->insert(IconDB::value_type(fileNameZ, icon));
+
+ if (rc.second) //if insertion took place
+ bufSequence->push(fileNameZ);
+
+ assert(buffer->size() == bufSequence->size());
+
+ //remove elements if buffer becomes too big:
+ if (buffer->size() > 1000) //limit buffer size: critical because large buffers seem to cause various wxIcon/wxBitmap issues!
+ {
+ //remove oldest element
+ buffer->erase(bufSequence->front());
+ bufSequence->pop();
+ }
+ }
+}
+
diff --git a/library/iconBuffer.h b/library/iconBuffer.h
new file mode 100644
index 00000000..ba905d22
--- /dev/null
+++ b/library/iconBuffer.h
@@ -0,0 +1,51 @@
+#ifndef ICONBUFFER_H_INCLUDED
+#define ICONBUFFER_H_INCLUDED
+
+#ifndef FFS_WIN
+#warning //this header should be used in the windows build only!
+#endif
+
+#include <vector>
+#include "zstring.h"
+#include <memory>
+
+class wxCriticalSection;
+class WorkerThread;
+class IconDB;
+class IconDbSequence;
+class wxIcon;
+
+
+namespace FreeFileSync
+{
+ class IconBuffer
+ {
+ friend class ::WorkerThread;
+
+ public:
+ static IconBuffer& getInstance();
+
+ bool requestIcon(const Zstring& fileName, wxIcon* icon = NULL); //returns false if icon is not in buffer
+ void setWorkload(const std::vector<Zstring>& load); //(re-)set new workload of icons to be retrieved;
+
+ static const int ICON_SIZE = 16; //size in pixel
+
+ private:
+ IconBuffer();
+ ~IconBuffer();
+
+ //methods used by worker thread
+ void insertIntoBuffer(const DefaultChar* fileName, const wxIcon& icon);
+
+//---------------------- Shared Data -------------------------
+ std::auto_ptr<wxCriticalSection> lockIconDB;
+ std::auto_ptr<IconDB> buffer; //use synchronisation when accessing this!
+//------------------------------------------------------------
+
+ std::auto_ptr<IconDbSequence> bufSequence; //save sequence of buffer entry to delte olderst elements
+
+ std::auto_ptr<WorkerThread> worker;
+ };
+}
+
+#endif // ICONBUFFER_H_INCLUDED
diff --git a/library/localization.cpp b/library/localization.cpp
index ef433012..9b5918a8 100644
--- a/library/localization.cpp
+++ b/library/localization.cpp
@@ -1,25 +1,134 @@
-#include "localization.h"
+#include "localization.h"
#include <wx/msgdlg.h>
-#include "resources.h"
+#include "../structures.h"
#include "globalFunctions.h"
#include <fstream>
#include <set>
+#include <map>
+#include "resources.h"
+
+using FreeFileSync::CustomLocale;
+using FreeFileSync::LocalizationInfo;
//_("Browse") <- dummy string for wxDirPickerCtrl to be recognized by automatic text extraction!
-struct TranslationLine
+const std::vector<FreeFileSync::LocInfoLine>& LocalizationInfo::getMapping()
{
- wxString original;
- wxString translation;
+ static LocalizationInfo instance;
+ return instance.locMapping;
+}
- bool operator<(const TranslationLine& b) const
- {
- return (original < b.original);
- }
-};
-class Translation : public std::set<TranslationLine> {};
+LocalizationInfo::LocalizationInfo()
+{
+ FreeFileSync::LocInfoLine newEntry;
+
+ newEntry.languageID = wxLANGUAGE_GERMAN;
+ newEntry.languageName = wxT("Deutsch");
+ newEntry.languageFile = "Languages/german.lng";
+ newEntry.translatorName = wxT("ZenJu");
+ newEntry.languageFlag = GlobalResources::getInstance().bitmapGermany;
+ locMapping.push_back(newEntry);
+
+ newEntry.languageID = wxLANGUAGE_ENGLISH;
+ newEntry.languageName = wxT("English");
+ newEntry.languageFile = "";
+ newEntry.translatorName = wxT("ZenJu");
+ newEntry.languageFlag = GlobalResources::getInstance().bitmapEngland;
+ locMapping.push_back(newEntry);
+
+ newEntry.languageID = wxLANGUAGE_SPANISH;
+ newEntry.languageName = wxT("Español");
+ newEntry.languageFile = "Languages/spanish.lng";
+ newEntry.translatorName = wxT("David Rodríguez");
+ newEntry.languageFlag = GlobalResources::getInstance().bitmapSpain;
+ locMapping.push_back(newEntry);
+
+ newEntry.languageID = wxLANGUAGE_FRENCH;
+ newEntry.languageName = wxT("Français");
+ newEntry.languageFile = "Languages/french.lng";
+ newEntry.translatorName = wxT("Jean-François Hartmann");
+ newEntry.languageFlag = GlobalResources::getInstance().bitmapFrance;
+ locMapping.push_back(newEntry);
+
+ newEntry.languageID = wxLANGUAGE_ITALIAN;
+ newEntry.languageName = wxT("Italiano");
+ newEntry.languageFile = "Languages/italian.lng";
+ newEntry.translatorName = wxT("Emmo");
+ newEntry.languageFlag = GlobalResources::getInstance().bitmapItaly;
+ locMapping.push_back(newEntry);
+
+ newEntry.languageID = wxLANGUAGE_HUNGARIAN;
+ newEntry.languageName = wxT("Magyar");
+ newEntry.languageFile = "Languages/hungarian.lng";
+ newEntry.translatorName = wxT("Demon");
+ newEntry.languageFlag = GlobalResources::getInstance().bitmapHungary;
+ locMapping.push_back(newEntry);
+
+ newEntry.languageID = wxLANGUAGE_DUTCH;
+ newEntry.languageName = wxT("Nederlands");
+ newEntry.languageFile = "Languages/dutch.lng";
+ newEntry.translatorName = wxT("M.D. Vrakking");
+ newEntry.languageFlag = GlobalResources::getInstance().bitmapHolland;
+ locMapping.push_back(newEntry);
+
+ newEntry.languageID = wxLANGUAGE_RUSSIAN;
+ newEntry.languageName = wxT("PуÑÑкий Ñзык");
+ newEntry.languageFile = "Languages/russian.lng";
+ newEntry.translatorName = wxT("Svobodniy");
+ newEntry.languageFlag = GlobalResources::getInstance().bitmapRussia;
+ locMapping.push_back(newEntry);
+
+ newEntry.languageID = wxLANGUAGE_POLISH;
+ newEntry.languageName = wxT("Polski");
+ newEntry.languageFile = "Languages/polish.lng";
+ newEntry.translatorName = wxT("Wojtek Pietruszewski");
+ newEntry.languageFlag = GlobalResources::getInstance().bitmapPoland;
+ locMapping.push_back(newEntry);
+
+ newEntry.languageID = wxLANGUAGE_PORTUGUESE;
+ newEntry.languageName = wxT("Português");
+ newEntry.languageFile = "Languages/portuguese.lng";
+ newEntry.translatorName = wxT("QuestMark");
+ newEntry.languageFlag = GlobalResources::getInstance().bitmapPortugal;
+ locMapping.push_back(newEntry);
+
+ newEntry.languageID = wxLANGUAGE_PORTUGUESE_BRAZILIAN;
+ newEntry.languageName = wxT("Português do Brasil");
+ newEntry.languageFile = "Languages/portuguese_br.lng";
+ newEntry.translatorName = wxT("Edison Aranha");
+ newEntry.languageFlag = GlobalResources::getInstance().bitmapBrazil;
+ locMapping.push_back(newEntry);
+
+ newEntry.languageID = wxLANGUAGE_SLOVENIAN;
+ newEntry.languageName = wxT("SlovenÅ¡Äina");
+ newEntry.languageFile = "Languages/slovenian.lng";
+ newEntry.translatorName = wxT("Matej Badalic");
+ newEntry.languageFlag = GlobalResources::getInstance().bitmapSlovakia;
+ locMapping.push_back(newEntry);
+
+ newEntry.languageID = wxLANGUAGE_JAPANESE;
+ newEntry.languageName = wxT("日本語");
+ newEntry.languageFile = "Languages/japanese.lng";
+ newEntry.translatorName = wxT("Tilt");
+ newEntry.languageFlag = GlobalResources::getInstance().bitmapJapan;
+ locMapping.push_back(newEntry);
+
+ newEntry.languageID = wxLANGUAGE_CHINESE_SIMPLIFIED;
+ newEntry.languageName = wxT("简体中文");
+ newEntry.languageFile = "Languages/chinese_simple.lng";
+ newEntry.translatorName = wxT("Misty Wu");
+ newEntry.languageFlag = GlobalResources::getInstance().bitmapChina;
+ locMapping.push_back(newEntry);
+}
+
+
+
+typedef wxString TextOriginal;
+typedef wxString TextTranslation;
+
+class Translation : public std::map<TextOriginal, TextTranslation> {};
CustomLocale::CustomLocale() :
@@ -83,51 +192,19 @@ void exchangeEscapeChars(wxString& data)
void CustomLocale::setLanguage(const int language)
{
- currentLanguage = language;
-
+ //default: english
std::string languageFile;
- switch (language)
- {
- case wxLANGUAGE_CHINESE_SIMPLIFIED:
- languageFile = "Languages/chinese_simple.lng";
- break;
- case wxLANGUAGE_DUTCH:
- languageFile = "Languages/dutch.lng";
- break;
- case wxLANGUAGE_FRENCH:
- languageFile = "Languages/french.lng";
- break;
- case wxLANGUAGE_GERMAN:
- languageFile = "Languages/german.lng";
- break;
- case wxLANGUAGE_HUNGARIAN:
- languageFile = "Languages/hungarian.lng";
- break;
- case wxLANGUAGE_ITALIAN:
- languageFile = "Languages/italian.lng";
- break;
- case wxLANGUAGE_JAPANESE:
- languageFile = "Languages/japanese.lng";
- break;
- case wxLANGUAGE_POLISH:
- languageFile = "Languages/polish.lng";
- break;
- 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;
- case wxLANGUAGE_SPANISH:
- languageFile = "Languages/spanish.lng";
- break;
- default:
- languageFile.clear();
- currentLanguage = wxLANGUAGE_ENGLISH;
- }
+ currentLanguage = wxLANGUAGE_ENGLISH;
+
+ //(try to) retrieve language filename
+ for (std::vector<LocInfoLine>::const_iterator i = LocalizationInfo::getMapping().begin(); i != LocalizationInfo::getMapping().end(); ++i)
+ if (language == i->languageID)
+ {
+ languageFile = i->languageFile;
+ currentLanguage = i->languageID;
+ break;
+ }
+
static bool initialized = false; //wxLocale is a static global too!
if (!initialized)
@@ -145,7 +222,7 @@ void CustomLocale::setLanguage(const int language)
std::ifstream langFile(languageFile.c_str(), std::ios::binary);
if (langFile)
{
- TranslationLine currentLine;
+ wxString original;
//Delimiter:
//----------
@@ -162,39 +239,36 @@ void CustomLocale::setLanguage(const int language)
exchangeEscapeChars(formattedString);
if (rowNumber%2 == 0)
- currentLine.original = formattedString;
+ original = formattedString;
else
{
if (!formattedString.empty())
{
- currentLine.translation = formattedString;
- translationDB->insert(currentLine);
+ const wxString translation = formattedString;
+ translationDB->insert(std::pair<TextOriginal, TextTranslation>(original, translation));
}
}
}
langFile.close();
}
else
- wxMessageBox(wxString(_("Error reading file:")) + wxT(" \"") + wxString(languageFile.c_str(), wxConvUTF8) + wxT("\""), _("An exception occured!"), wxOK | wxICON_ERROR);
+ wxMessageBox(wxString(_("Error reading file:")) + wxT(" \"") + wxString(languageFile.c_str(), wxConvUTF8) + wxT("\""), _("Error"), wxOK | wxICON_ERROR);
}
else
; //if languageFile is empty texts will be english per default
//these global variables need to be redetermined on language selection
- GlobalResources::DECIMAL_POINT = _(".");
- GlobalResources::THOUSANDS_SEPARATOR = _(",");
+ FreeFileSync::DECIMAL_POINT = _(".");
+ FreeFileSync::THOUSANDS_SEPARATOR = _(",");
}
const wxChar* CustomLocale::GetString(const wxChar* szOrigString, const wxChar* szDomain) const
{
- TranslationLine currentLine;
- currentLine.original = szOrigString;
-
//look for translation in buffer table
- const Translation::iterator i = translationDB->find(currentLine);
+ const Translation::const_iterator i = translationDB->find(szOrigString);
if (i != translationDB->end())
- return i->translation.c_str();
+ return i->second.c_str();
//fallback
return szOrigString;
diff --git a/library/localization.h b/library/localization.h
index cf29d06d..7a63fd9c 100644
--- a/library/localization.h
+++ b/library/localization.h
@@ -2,31 +2,55 @@
#define MISC_H_INCLUDED
#include <wx/intl.h>
+#include <wx/bitmap.h>
+#include <vector>
class Translation;
-class CustomLocale : public wxLocale
+namespace FreeFileSync
{
-public:
- CustomLocale();
- ~CustomLocale();
+ struct LocInfoLine
+ {
+ int languageID;
+ wxString languageName;
+ std::string languageFile;
+ wxString translatorName;
+ wxBitmap* languageFlag;
+ };
+
+
+ class LocalizationInfo
+ {
+ public:
+ static const std::vector<LocInfoLine>& getMapping();
+
+ private:
+ LocalizationInfo();
+
+ std::vector<LocInfoLine> locMapping;
+ };
- void setLanguage(const int language);
- int getLanguage() const
+ class CustomLocale : public wxLocale
{
- return currentLanguage;
- }
+ public:
+ CustomLocale();
+ ~CustomLocale();
- const wxChar* GetString(const wxChar* szOrigString, const wxChar* szDomain = NULL) const;
+ void setLanguage(const int language);
- static const std::string FfsLanguageDat;
+ int getLanguage() const
+ {
+ return currentLanguage;
+ }
-private:
- Translation* translationDB;
- int currentLanguage;
-};
+ const wxChar* GetString(const wxChar* szOrigString, const wxChar* szDomain = NULL) const;
+ private:
+ Translation* translationDB;
+ int currentLanguage;
+ };
+}
#endif // MISC_H_INCLUDED
diff --git a/library/pch.h b/library/pch.h
index 50f9fda3..b561d448 100644
--- a/library/pch.h
+++ b/library/pch.h
@@ -1,8 +1,10 @@
#ifndef FFS_PRECOMPILED_HEADER
#define FFS_PRECOMPILED_HEADER
-DO NOT USE THIS FILE:
-FOR SOME REASON IT CORRUPTS COMPILATION!!!
+//pay attention when using this file: might cause issues!
+#ifndef __WXDEBUG__
+do NOT use in release build!
+#endif
//#####################################################
// basic wxWidgets headers
@@ -12,22 +14,17 @@ FOR SOME REASON IT CORRUPTS COMPILATION!!!
#include <wx/wxprec.h>
-#ifndef WX_PRECOMP
-#include <wx/wx.h>
-#endif
-
//#####################################################
// #include other rarely changing headers here
//STL headers
-#include <vector>
-#include <set>
#include <string>
-#include <stack>
#include <algorithm>
+#include <vector>
+#include <queue>
+#include <stack>
+#include <set>
#include <map>
-
-//C headers
#include <fstream>
#ifdef FFS_LINUX
@@ -36,7 +33,6 @@ FOR SOME REASON IT CORRUPTS COMPILATION!!!
//other wxWidgets headers
#include <wx/grid.h>
-#include <sys/stat.h>
#include <wx/animate.h>
#include <wx/app.h>
#include <wx/arrstr.h>
@@ -90,9 +86,10 @@ FOR SOME REASON IT CORRUPTS COMPILATION!!!
//other
#include "tinyxml/tinyxml.h"
+#include <sys/stat.h>
#ifdef FFS_WIN
-#include <windows.h>
+#include <wx/msw/wrapwin.h> //includes "windows.h"
#endif //FFS_WIN
//#####################################################
diff --git a/library/processXml.cpp b/library/processXml.cpp
index 750bd879..4b8ffe27 100644
--- a/library/processXml.cpp
+++ b/library/processXml.cpp
@@ -11,15 +11,12 @@
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);
bool readXmlElementValue(CompareVariant& output, const TiXmlElement* parent, const std::string& name);
-bool readXmlElementValue(SyncDirection& output, const TiXmlElement* parent, const std::string& name);
+bool readXmlElementValue(SyncDirection& output, const TiXmlElement* parent, const std::string& name);
bool readXmlElementValue(bool& output, const TiXmlElement* parent, const std::string& name);
void addXmlElement(TiXmlElement* parent, const std::string& name, const std::string& value);
@@ -158,18 +155,18 @@ xmlAccess::XmlBatchConfig xmlAccess::readBatchConfig(const wxString& filename)
xmlAccess::XmlGlobalSettings xmlAccess::readGlobalSettings()
{
//load XML
- if (!wxFileExists(xmlAccess::GLOBAL_CONFIG_FILE))
- throw FileError(Zstring(_("File does not exist:")) + wxT(" \"") + xmlAccess::GLOBAL_CONFIG_FILE.c_str() + wxT("\""));
+ if (!wxFileExists(FreeFileSync::getGlobalConfigFile()))
+ throw FileError(Zstring(_("File does not exist:")) + wxT(" \"") + FreeFileSync::getGlobalConfigFile().c_str() + wxT("\""));
- XmlConfigInput inputFile(xmlAccess::GLOBAL_CONFIG_FILE, XML_GLOBAL_SETTINGS);
+ XmlConfigInput inputFile(FreeFileSync::getGlobalConfigFile(), XML_GLOBAL_SETTINGS);
XmlGlobalSettings outputCfg;
if (!inputFile.loadedSuccessfully())
- throw FileError(Zstring(_("Error reading file:")) + wxT(" \"") + xmlAccess::GLOBAL_CONFIG_FILE.c_str() + wxT("\""));
+ throw FileError(Zstring(_("Error reading file:")) + wxT(" \"") + FreeFileSync::getGlobalConfigFile().c_str() + wxT("\""));
if (!inputFile.readXmlGlobalSettings(outputCfg))
- throw FileError(Zstring(_("Error parsing configuration file:")) + wxT(" \"") + xmlAccess::GLOBAL_CONFIG_FILE.c_str() + wxT("\""));
+ throw FileError(Zstring(_("Error parsing configuration file:")) + wxT(" \"") + FreeFileSync::getGlobalConfigFile().c_str() + wxT("\""));
return outputCfg;
}
@@ -201,12 +198,12 @@ void xmlAccess::writeBatchConfig(const wxString& filename, const XmlBatchConfig&
void xmlAccess::writeGlobalSettings(const XmlGlobalSettings& outputCfg)
{
- XmlConfigOutput outputFile(xmlAccess::GLOBAL_CONFIG_FILE, XML_GLOBAL_SETTINGS);
+ XmlConfigOutput outputFile(FreeFileSync::getGlobalConfigFile(), 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(" \"") + xmlAccess::GLOBAL_CONFIG_FILE.c_str() + wxT("\""));
+ throw FileError(Zstring(_("Error writing file:")) + wxT(" \"") + FreeFileSync::getGlobalConfigFile().c_str() + wxT("\""));
return;
}
@@ -509,43 +506,49 @@ bool XmlConfigInput::readXmlGlobalSettings(xmlAccess::XmlGlobalSettings& outputC
if (global)
{
//try to read program language setting
- readXmlElementValue(outputCfg.shared.programLanguage, global, "Language");
+ readXmlElementValue(outputCfg.programLanguage, global, "Language");
//max. allowed file time deviation
int dummy = 0;
if (readXmlElementValue(dummy, global, "FileTimeTolerance"))
- outputCfg.shared.fileTimeTolerance = dummy;
+ outputCfg.fileTimeTolerance = dummy;
//ignore +/- 1 hour due to DST change
- readXmlElementValue(outputCfg.shared.ignoreOneHourDiff, global, "IgnoreOneHourDifference");
+ readXmlElementValue(outputCfg.ignoreOneHourDiff, global, "IgnoreOneHourDifference");
//traverse into symbolic links (to folders)
- readXmlElementValue(outputCfg.shared.traverseDirectorySymlinks, global, "TraverseDirectorySymlinks");
+ readXmlElementValue(outputCfg.traverseDirectorySymlinks, global, "TraverseDirectorySymlinks");
//copy symbolic links to files
- readXmlElementValue(outputCfg.shared.copyFileSymlinks, global, "CopyFileSymlinks");
+ readXmlElementValue(outputCfg.copyFileSymlinks, global, "CopyFileSymlinks");
//last update check
- readXmlElementValue(outputCfg.shared.lastUpdateCheck, global, "LastCheckForUpdates");
+ readXmlElementValue(outputCfg.lastUpdateCheck, global, "LastCheckForUpdates");
}
TiXmlElement* warnings = hRoot.FirstChild("Shared").FirstChild("Warnings").ToElement();
if (warnings)
{
//folder dependency check
- readXmlElementValue(outputCfg.shared.warningDependentFolders, warnings, "CheckForDependentFolders");
+ readXmlElementValue(outputCfg.warnings.warningDependentFolders, warnings, "CheckForDependentFolders");
//significant difference check
- readXmlElementValue(outputCfg.shared.warningSignificantDifference, warnings, "CheckForSignificantDifference");
+ readXmlElementValue(outputCfg.warnings.warningSignificantDifference, warnings, "CheckForSignificantDifference");
//check free disk space
- readXmlElementValue(outputCfg.shared.warningNotEnoughDiskSpace, warnings, "CheckForFreeDiskSpace");
+ readXmlElementValue(outputCfg.warnings.warningNotEnoughDiskSpace, warnings, "CheckForFreeDiskSpace");
//check for unresolved conflicts
- readXmlElementValue(outputCfg.shared.warningUnresolvedConflicts, warnings, "CheckForUnresolvedConflicts");
+ readXmlElementValue(outputCfg.warnings.warningUnresolvedConflicts, warnings, "CheckForUnresolvedConflicts");
+
+ //check for very old dates or dates in the future
+ readXmlElementValue(outputCfg.warnings.warningInvalidDate, warnings, "CheckForInvalidFileDate");
+
+ //check for changed files with same modification date
+ readXmlElementValue(outputCfg.warnings.warningSameDateDiffSize, warnings, "SameDateDifferentFileSize");
- //small reminder that synchronization will be starting immediately
- readXmlElementValue(outputCfg.shared.warningSynchronizationStarting, warnings, "SynchronizationStarting");
+ //check for files that have a difference in file modification date below 1 hour when DST check is active
+ readXmlElementValue(outputCfg.warnings.warningDSTChangeWithinHour, warnings, "FileChangeWithinHour");
}
//gui specific global settings (optional)
@@ -553,16 +556,18 @@ bool XmlConfigInput::readXmlGlobalSettings(xmlAccess::XmlGlobalSettings& outputC
if (mainWindow)
{
//read application window size and position
- readXmlElementValue(outputCfg.gui.widthNotMaximized, mainWindow, "Width");
+ readXmlElementValue(outputCfg.gui.widthNotMaximized, mainWindow, "Width");
readXmlElementValue(outputCfg.gui.heightNotMaximized, mainWindow, "Height");
- readXmlElementValue(outputCfg.gui.posXNotMaximized, mainWindow, "PosX");
- readXmlElementValue(outputCfg.gui.posYNotMaximized, mainWindow, "PosY");
- readXmlElementValue(outputCfg.gui.isMaximized, mainWindow, "Maximized");
+ readXmlElementValue(outputCfg.gui.posXNotMaximized, mainWindow, "PosX");
+ readXmlElementValue(outputCfg.gui.posYNotMaximized, mainWindow, "PosY");
+ readXmlElementValue(outputCfg.gui.isMaximized, mainWindow, "Maximized");
- readXmlElementValue(outputCfg.gui.deleteOnBothSides, mainWindow, "ManualDeletionOnBothSides");
+ readXmlElementValue(outputCfg.gui.deleteOnBothSides, mainWindow, "ManualDeletionOnBothSides");
readXmlElementValue(outputCfg.gui.useRecyclerForManualDeletion, mainWindow, "ManualDeletionUseRecycler");
- readXmlElementValue(outputCfg.gui.showFileIcons, mainWindow, "ShowFileIcons");
- readXmlElementValue(outputCfg.gui.popupOnConfigChange, mainWindow, "PopupOnConfigChange");
+ readXmlElementValue(outputCfg.gui.showFileIconsLeft, mainWindow, "ShowFileIconsLeft");
+ readXmlElementValue(outputCfg.gui.showFileIconsRight, mainWindow, "ShowFileIconsRight");
+ readXmlElementValue(outputCfg.gui.popupOnConfigChange, mainWindow, "PopupOnConfigChange");
+ readXmlElementValue(outputCfg.gui.showSummaryBeforeSync, mainWindow, "SummaryBeforeSync");
//###########################################################
//read column attributes
@@ -638,6 +643,8 @@ bool XmlConfigInput::readXmlGlobalSettings(xmlAccess::XmlGlobalSettings& outputC
//load config history elements
readXmlElementTable(outputCfg.gui.folderHistoryRight, historyRight, "Folder");
}
+
+ readXmlElementValue(outputCfg.gui.selectedTabBottomLeft, mainWindow, "SelectedTabBottomLeft");
}
TiXmlElement* gui = hRoot.FirstChild("Gui").ToElement();
@@ -667,9 +674,6 @@ bool XmlConfigInput::readXmlGlobalSettings(xmlAccess::XmlGlobalSettings& outputC
TiXmlElement* batch = hRoot.FirstChild("Batch").ToElement();
if (!batch) return false;
-
-// if (!readXmlElementValue(outputCfg.dummy, global, "Language")) return false;
-
return true;
}
@@ -889,41 +893,47 @@ bool XmlConfigOutput::writeXmlGlobalSettings(const xmlAccess::XmlGlobalSettings&
root->LinkEndChild(global);
//program language
- addXmlElement(global, "Language", inputCfg.shared.programLanguage);
+ addXmlElement(global, "Language", inputCfg.programLanguage);
//max. allowed file time deviation
- addXmlElement(global, "FileTimeTolerance", int(inputCfg.shared.fileTimeTolerance));
+ addXmlElement(global, "FileTimeTolerance", int(inputCfg.fileTimeTolerance));
//ignore +/- 1 hour due to DST change
- addXmlElement(global, "IgnoreOneHourDifference", inputCfg.shared.ignoreOneHourDiff);
+ addXmlElement(global, "IgnoreOneHourDifference", inputCfg.ignoreOneHourDiff);
//traverse into symbolic links (to folders)
- addXmlElement(global, "TraverseDirectorySymlinks", inputCfg.shared.traverseDirectorySymlinks);
+ addXmlElement(global, "TraverseDirectorySymlinks", inputCfg.traverseDirectorySymlinks);
//copy symbolic links to files
- addXmlElement(global, "CopyFileSymlinks", inputCfg.shared.copyFileSymlinks);
+ addXmlElement(global, "CopyFileSymlinks", inputCfg.copyFileSymlinks);
//last update check
- addXmlElement(global, "LastCheckForUpdates", inputCfg.shared.lastUpdateCheck);
+ addXmlElement(global, "LastCheckForUpdates", inputCfg.lastUpdateCheck);
//warnings
TiXmlElement* warnings = new TiXmlElement("Warnings");
global->LinkEndChild(warnings);
//warning: dependent folders
- addXmlElement(warnings, "CheckForDependentFolders", inputCfg.shared.warningDependentFolders);
+ addXmlElement(warnings, "CheckForDependentFolders", inputCfg.warnings.warningDependentFolders);
//significant difference check
- addXmlElement(warnings, "CheckForSignificantDifference", inputCfg.shared.warningSignificantDifference);
+ addXmlElement(warnings, "CheckForSignificantDifference", inputCfg.warnings.warningSignificantDifference);
//check free disk space
- addXmlElement(warnings, "CheckForFreeDiskSpace", inputCfg.shared.warningNotEnoughDiskSpace);
+ addXmlElement(warnings, "CheckForFreeDiskSpace", inputCfg.warnings.warningNotEnoughDiskSpace);
//check for unresolved conflicts
- addXmlElement(warnings, "CheckForUnresolvedConflicts", inputCfg.shared.warningUnresolvedConflicts);
+ addXmlElement(warnings, "CheckForUnresolvedConflicts", inputCfg.warnings.warningUnresolvedConflicts);
+
+ //check for very old dates or dates in the future
+ addXmlElement(warnings, "CheckForInvalidFileDate", inputCfg.warnings.warningInvalidDate);
- //small reminder that synchronization will be starting immediately
- addXmlElement(warnings, "SynchronizationStarting", inputCfg.shared.warningSynchronizationStarting);
+ //check for changed files with same modification date
+ addXmlElement(warnings, "SameDateDifferentFileSize", inputCfg.warnings.warningSameDateDiffSize);
+
+ //check for files that have a difference in file modification date below 1 hour when DST check is active
+ addXmlElement(warnings, "FileChangeWithinHour", inputCfg.warnings.warningDSTChangeWithinHour);
//###################################################################
@@ -948,8 +958,10 @@ 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, "PopupOnConfigChange" , inputCfg.gui.popupOnConfigChange);
+ addXmlElement(mainWindow, "ShowFileIconsLeft", inputCfg.gui.showFileIconsLeft);
+ addXmlElement(mainWindow, "ShowFileIconsRight", inputCfg.gui.showFileIconsRight);
+ addXmlElement(mainWindow, "PopupOnConfigChange", inputCfg.gui.popupOnConfigChange);
+ addXmlElement(mainWindow, "SummaryBeforeSync", inputCfg.gui.showSummaryBeforeSync);
//write column attributes
TiXmlElement* leftColumn = new TiXmlElement("LeftColumns");
@@ -996,6 +1008,7 @@ bool XmlConfigOutput::writeXmlGlobalSettings(const xmlAccess::XmlGlobalSettings&
historyRight->SetAttribute("MaximumSize", globalFunctions::numberToString(inputCfg.gui.folderHistRightMax));
addXmlElementTable(historyRight, "Folder", inputCfg.gui.folderHistoryRight);
+ addXmlElement(mainWindow, "SelectedTabBottomLeft", inputCfg.gui.selectedTabBottomLeft);
//commandline for file manager integration
addXmlElement(gui, "FileManager", std::string((inputCfg.gui.commandLineFileManager).ToUTF8()));
@@ -1056,6 +1069,10 @@ int xmlAccess::retrieveSystemLanguage()
case wxLANGUAGE_CHINESE_TAIWAN:
return wxLANGUAGE_CHINESE_SIMPLIFIED;
+ //variants of wxLANGUAGE_RUSSIAN
+ case wxLANGUAGE_RUSSIAN_UKRAINE:
+ return wxLANGUAGE_RUSSIAN;
+
//variants of wxLANGUAGE_SPANISH
case wxLANGUAGE_SPANISH_ARGENTINA:
case wxLANGUAGE_SPANISH_BOLIVIA:
@@ -1110,11 +1127,13 @@ bool xmlAccess::supportForSymbolicLinks()
}
-void xmlAccess::XmlGlobalSettings::_Shared::resetWarnings()
+void xmlAccess::WarningMessages::resetWarnings()
{
warningDependentFolders = true;
warningSignificantDifference = true;
warningNotEnoughDiskSpace = true;
warningUnresolvedConflicts = true;
- warningSynchronizationStarting = true;
+ warningInvalidDate = true;
+ warningSameDateDiffSize = true;
+ warningDSTChangeWithinHour = true;
}
diff --git a/library/processXml.h b/library/processXml.h
index 313fbfae..15972289 100644
--- a/library/processXml.h
+++ b/library/processXml.h
@@ -6,9 +6,6 @@
namespace xmlAccess
{
- extern const wxString LAST_CONFIG_FILE;
- extern const wxString GLOBAL_CONFIG_FILE;
-
enum OnError
{
ON_ERROR_POPUP,
@@ -93,38 +90,45 @@ namespace xmlAccess
bool supportForSymbolicLinks();
+ struct WarningMessages
+ {
+ WarningMessages()
+ {
+ resetWarnings();
+ }
+
+ void resetWarnings();
+
+ bool warningDependentFolders;
+ bool warningSignificantDifference;
+ bool warningNotEnoughDiskSpace;
+ bool warningUnresolvedConflicts;
+ bool warningInvalidDate;
+ bool warningSameDateDiffSize;
+ bool warningDSTChangeWithinHour;
+ };
+
+
struct XmlGlobalSettings
{
//---------------------------------------------------------------------
- struct _Shared
- {
- _Shared() :
- programLanguage(retrieveSystemLanguage()),
- fileTimeTolerance(2), //default 2s: FAT vs NTFS
- ignoreOneHourDiff(true),
- traverseDirectorySymlinks(false),
- copyFileSymlinks(supportForSymbolicLinks()),
- lastUpdateCheck(0)
- {
- resetWarnings();
- }
-
- int programLanguage;
- unsigned int fileTimeTolerance; //max. allowed file time deviation
- bool ignoreOneHourDiff; //ignore +/- 1 hour due to DST change
- bool traverseDirectorySymlinks;
- bool copyFileSymlinks; //copy symbolic link instead of target file
- long lastUpdateCheck; //time of last update check
-
- //warnings
- void resetWarnings();
-
- bool warningDependentFolders;
- bool warningSignificantDifference;
- bool warningNotEnoughDiskSpace;
- bool warningUnresolvedConflicts;
- bool warningSynchronizationStarting;
- } shared;
+ //Shared (GUI/BATCH) settings
+ XmlGlobalSettings() :
+ programLanguage(retrieveSystemLanguage()),
+ fileTimeTolerance(2), //default 2s: FAT vs NTFS
+ ignoreOneHourDiff(true),
+ traverseDirectorySymlinks(false),
+ copyFileSymlinks(supportForSymbolicLinks()),
+ lastUpdateCheck(0) {}
+
+ int programLanguage;
+ unsigned int fileTimeTolerance; //max. allowed file time deviation
+ bool ignoreOneHourDiff; //ignore +/- 1 hour due to DST change
+ bool traverseDirectorySymlinks;
+ bool copyFileSymlinks; //copy symbolic link instead of target file
+ long lastUpdateCheck; //time of last update check
+
+ WarningMessages warnings;
//---------------------------------------------------------------------
struct _Gui
@@ -143,10 +147,13 @@ namespace xmlAccess
cfgHistoryMax(10),
folderHistLeftMax(12),
folderHistRightMax(12),
+ selectedTabBottomLeft(0),
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),
- popupOnConfigChange(true) {}
+ showFileIconsLeft(true),
+ showFileIconsRight(true),
+ popupOnConfigChange(true),
+ showSummaryBeforeSync(true) {}
int widthNotMaximized;
int heightNotMaximized;
@@ -168,10 +175,14 @@ namespace xmlAccess
std::vector<wxString> folderHistoryRight;
unsigned int folderHistRightMax;
+ int selectedTabBottomLeft;
+
bool deleteOnBothSides;
bool useRecyclerForManualDeletion;
- bool showFileIcons;
+ bool showFileIconsLeft;
+ bool showFileIconsRight;
bool popupOnConfigChange;
+ bool showSummaryBeforeSync;
} gui;
//---------------------------------------------------------------------
diff --git a/library/resources.cpp b/library/resources.cpp
index 9e43a78a..38f2a051 100644
--- a/library/resources.cpp
+++ b/library/resources.cpp
@@ -5,20 +5,15 @@
#include <wx/icon.h>
#include <wx/mstream.h>
#include "globalFunctions.h"
+#include "../structures.h"
-#ifdef FFS_WIN
-const wxChar GlobalResources::FILE_NAME_SEPARATOR = '\\';
-#elif defined FFS_LINUX
-const wxChar GlobalResources::FILE_NAME_SEPARATOR = '/';
-#else
-assert(false);
-#endif
-//these two global variables are language-dependent => cannot be set statically! See CustomLocale
-const wxChar* GlobalResources::DECIMAL_POINT = wxEmptyString;
-const wxChar* GlobalResources::THOUSANDS_SEPARATOR = wxEmptyString;
+const GlobalResources& GlobalResources::getInstance()
+{
+ static GlobalResources instance;
+ return instance;
+}
-GlobalResources globalResource; //init resources on program startup
GlobalResources::GlobalResources()
{
@@ -42,6 +37,7 @@ GlobalResources::GlobalResources()
bitmapResource[wxT("sync.png")] = (bitmapSync = new wxBitmap(wxNullBitmap));
bitmapResource[wxT("sync disabled.png")] = (bitmapSyncDisabled = new wxBitmap(wxNullBitmap));
bitmapResource[wxT("swap.png")] = (bitmapSwap = new wxBitmap(wxNullBitmap));
+ bitmapResource[wxT("swapSmall.png")] = (bitmapSwapSmall = new wxBitmap(wxNullBitmap));
bitmapResource[wxT("help.png")] = (bitmapHelp = new wxBitmap(wxNullBitmap));
bitmapResource[wxT("leftOnly.png")] = (bitmapLeftOnly = new wxBitmap(wxNullBitmap));
bitmapResource[wxT("leftOnlyAct.png")] = (bitmapLeftOnlyAct = new wxBitmap(wxNullBitmap));
@@ -67,6 +63,7 @@ GlobalResources::GlobalResources()
bitmapResource[wxT("exclude.png")] = (bitmapExclude = new wxBitmap(wxNullBitmap));
bitmapResource[wxT("filter active.png")] = (bitmapFilterOn = new wxBitmap(wxNullBitmap));
bitmapResource[wxT("filter not active.png")] = (bitmapFilterOff = new wxBitmap(wxNullBitmap));
+ bitmapResource[wxT("filter_small.png")] = (bitmapFilterSmall = new wxBitmap(wxNullBitmap));
bitmapResource[wxT("warning.png")] = (bitmapWarning = new wxBitmap(wxNullBitmap));
bitmapResource[wxT("warningSmall.png")] = (bitmapWarningSmall = new wxBitmap(wxNullBitmap));
bitmapResource[wxT("error.png")] = (bitmapError = new wxBitmap(wxNullBitmap));
@@ -124,6 +121,7 @@ GlobalResources::GlobalResources()
bitmapResource[wxT("brazil.png")] = (bitmapBrazil = new wxBitmap(wxNullBitmap));
bitmapResource[wxT("slovakia.png")] = (bitmapSlovakia = new wxBitmap(wxNullBitmap));
bitmapResource[wxT("spain.png")] = (bitmapSpain = new wxBitmap(wxNullBitmap));
+ bitmapResource[wxT("russia.png")] = (bitmapRussia = new wxBitmap(wxNullBitmap));
bitmapResource[wxT("syncDirLeftAct.png")] = (bitmapSyncDirLeftAct = new wxBitmap(wxNullBitmap));
bitmapResource[wxT("syncDirLeftDeact.png")] = (bitmapSyncDirLeftDeact = new wxBitmap(wxNullBitmap));
bitmapResource[wxT("syncDirRightAct.png")] = (bitmapSyncDirRightAct = new wxBitmap(wxNullBitmap));
@@ -133,6 +131,10 @@ GlobalResources::GlobalResources()
bitmapResource[wxT("syncDirLeftSmall.png")] = (bitmapSyncDirLeftSmall = new wxBitmap(wxNullBitmap));
bitmapResource[wxT("syncDirRightSmall.png")] = (bitmapSyncDirRightSmall = new wxBitmap(wxNullBitmap));
bitmapResource[wxT("syncDirNoneSmall.png")] = (bitmapSyncDirNoneSmall = new wxBitmap(wxNullBitmap));
+ bitmapResource[wxT("createLeftSmall.png")] = (bitmapCreateLeftSmall = new wxBitmap(wxNullBitmap));
+ bitmapResource[wxT("createRightSmall.png")] = (bitmapCreateRightSmall = new wxBitmap(wxNullBitmap));
+ bitmapResource[wxT("deleteLeftSmall.png")] = (bitmapDeleteLeftSmall = new wxBitmap(wxNullBitmap));
+ bitmapResource[wxT("deleteRightSmall.png")] = (bitmapDeleteRightSmall = new wxBitmap(wxNullBitmap));
bitmapResource[wxT("leftOnlySmall.png")] = (bitmapLeftOnlySmall = new wxBitmap(wxNullBitmap));
bitmapResource[wxT("rightOnlySmall.png")] = (bitmapRightOnlySmall = new wxBitmap(wxNullBitmap));
bitmapResource[wxT("leftNewerSmall.png")] = (bitmapLeftNewerSmall = new wxBitmap(wxNullBitmap));
@@ -144,8 +146,8 @@ GlobalResources::GlobalResources()
bitmapResource[wxT("update.png")] = (bitmapUpdate = new wxBitmap(wxNullBitmap));
bitmapResource[wxT("delete.png")] = (bitmapDelete = new wxBitmap(wxNullBitmap));
bitmapResource[wxT("data.png")] = (bitmapData = new wxBitmap(wxNullBitmap));
- bitmapResource[wxT("cmpView.png")] = (bitmapCmpView = new wxBitmap(wxNullBitmap));
- bitmapResource[wxT("syncView.png")] = (bitmapSyncView = new wxBitmap(wxNullBitmap));
+ bitmapResource[wxT("cmpViewSmall.png")] = (bitmapCmpViewSmall = new wxBitmap(wxNullBitmap));
+ bitmapResource[wxT("syncViewSmall.png")] = (bitmapSyncViewSmall = new wxBitmap(wxNullBitmap));
bitmapResource[wxT("toggleViewSmall.png")] = (bitmapSwitchViewSmall = new wxBitmap(wxNullBitmap));
@@ -187,9 +189,9 @@ void loadAnimFromZip(wxZipInputStream& zipInput, wxAnimation* animation)
}
-void GlobalResources::load()
+void GlobalResources::load() const
{
- wxFileInputStream input(wxT("Resources.dat"));
+ wxFileInputStream input(FreeFileSync::getInstallationDir() + FreeFileSync::FILE_NAME_SEPARATOR + wxT("Resources.dat"));
if (input.IsOk()) //if not... we don't want to react too harsh here
{
//activate support for .png files
@@ -214,7 +216,7 @@ void GlobalResources::load()
}
#ifdef FFS_WIN
- *programIcon = wxIcon(wxT("ffsIcon1"));
+ *programIcon = wxIcon(wxT("A_PROGRAM_ICON"));
#else
#include "FreeFileSync.xpm"
*programIcon = wxIcon(FreeFileSync_xpm);
diff --git a/library/resources.h b/library/resources.h
index 60b4ab48..1ce7d20e 100644
--- a/library/resources.h
+++ b/library/resources.h
@@ -10,16 +10,7 @@
class GlobalResources
{
public:
- GlobalResources();
- ~GlobalResources();
-
- void load();
-
- static const wxChar FILE_NAME_SEPARATOR;
-
- //language dependent global variables: need to be initialized by CustomLocale on program startup and language switch
- static const wxChar* DECIMAL_POINT;
- static const wxChar* THOUSANDS_SEPARATOR;
+ static const GlobalResources& getInstance();
//image resource objects
wxBitmap* bitmapArrowLeft;
@@ -41,6 +32,7 @@ public:
wxBitmap* bitmapSync;
wxBitmap* bitmapSyncDisabled;
wxBitmap* bitmapSwap;
+ wxBitmap* bitmapSwapSmall;
wxBitmap* bitmapHelp;
wxBitmap* bitmapLeftOnly;
wxBitmap* bitmapLeftOnlyAct;
@@ -66,6 +58,7 @@ public:
wxBitmap* bitmapExclude;
wxBitmap* bitmapFilterOn;
wxBitmap* bitmapFilterOff;
+ wxBitmap* bitmapFilterSmall;
wxBitmap* bitmapWarning;
wxBitmap* bitmapWarningSmall;
wxBitmap* bitmapError;
@@ -123,6 +116,7 @@ public:
wxBitmap* bitmapBrazil;
wxBitmap* bitmapSlovakia;
wxBitmap* bitmapSpain;
+ wxBitmap* bitmapRussia;
wxBitmap* bitmapSyncDirLeftAct;
wxBitmap* bitmapSyncDirLeftDeact;
wxBitmap* bitmapSyncDirRightAct;
@@ -132,6 +126,10 @@ public:
wxBitmap* bitmapSyncDirLeftSmall;
wxBitmap* bitmapSyncDirRightSmall;
wxBitmap* bitmapSyncDirNoneSmall;
+ wxBitmap* bitmapCreateLeftSmall;
+ wxBitmap* bitmapCreateRightSmall;
+ wxBitmap* bitmapDeleteLeftSmall;
+ wxBitmap* bitmapDeleteRightSmall;
wxBitmap* bitmapLeftOnlySmall;
wxBitmap* bitmapRightOnlySmall;
wxBitmap* bitmapLeftNewerSmall;
@@ -143,8 +141,8 @@ public:
wxBitmap* bitmapUpdate;
wxBitmap* bitmapDelete;
wxBitmap* bitmapData;
- wxBitmap* bitmapCmpView;
- wxBitmap* bitmapSyncView;
+ wxBitmap* bitmapCmpViewSmall;
+ wxBitmap* bitmapSyncViewSmall;
wxBitmap* bitmapSwitchViewSmall;
wxAnimation* animationMoney;
@@ -152,12 +150,14 @@ public:
wxIcon* programIcon;
+ void load() const; //loads bitmap resources on program startup: logical const!
+
private:
+ GlobalResources();
+ ~GlobalResources();
+
//resource mapping
- std::map<wxString, wxBitmap*> bitmapResource;
+ mutable std::map<wxString, wxBitmap*> bitmapResource;
};
-
-extern GlobalResources globalResource; //loads bitmap resources on program startup
-
#endif // RESOURCES_H_INCLUDED
diff --git a/library/shadow.cpp b/library/shadow.cpp
new file mode 100644
index 00000000..c353bcb2
--- /dev/null
+++ b/library/shadow.cpp
@@ -0,0 +1,148 @@
+#include "shadow.h"
+#include <wx/msw/wrapwin.h> //includes "windows.h"
+#include <wx/intl.h>
+#include "../structures.h"
+
+using FreeFileSync::ShadowCopy;
+
+
+class ShadowlDllHandler //dynamically load windows API functions
+{
+ typedef bool (*CreateShadowCopyFct)( //volumeName must end with "\", while shadowVolName does not end with "\"
+ const wchar_t* volumeName,
+ wchar_t* shadowVolName,
+ unsigned int shadowBufferLen,
+ void** backupHandle,
+ wchar_t* errorMessage,
+ unsigned int errorBufferLen);
+
+ typedef void (*ReleaseShadowCopyFct)(void* backupHandle);
+
+public:
+ static const ShadowlDllHandler& getInstance()
+ {
+ static ShadowlDllHandler instance;
+ return instance;
+ }
+
+ CreateShadowCopyFct createShadowCopy;
+ ReleaseShadowCopyFct releaseShadowCopy;
+
+private:
+ ShadowlDllHandler() :
+ createShadowCopy(NULL),
+ releaseShadowCopy(NULL),
+ hShadow(NULL)
+ {
+ //get a handle to the DLL module containing the required functionality
+ hShadow = ::LoadLibrary(L"Shadow.dll");
+ if (hShadow)
+ {
+ createShadowCopy = reinterpret_cast<CreateShadowCopyFct>(::GetProcAddress(hShadow, "createShadowCopy"));
+ releaseShadowCopy = reinterpret_cast<ReleaseShadowCopyFct>(::GetProcAddress(hShadow, "releaseShadowCopy"));
+ }
+ }
+
+ ~ShadowlDllHandler()
+ {
+ if (hShadow) ::FreeLibrary(hShadow);
+ }
+
+ HINSTANCE hShadow;
+};
+
+
+ShadowCopy::ShadowCopy() :
+ backupHandle(NULL) {}
+
+
+ShadowCopy::~ShadowCopy()
+{
+ if (backupHandle != NULL)
+ ShadowlDllHandler::getInstance().releaseShadowCopy(backupHandle);
+}
+
+
+bool ShadowCopy::isOkay()
+{
+ //check that all functions could be loaded
+ return ShadowlDllHandler::getInstance().createShadowCopy != NULL &&
+ ShadowlDllHandler::getInstance().releaseShadowCopy != NULL;
+}
+
+
+Zstring ShadowCopy::makeShadowCopy(const Zstring& inputFile) throw(FreeFileSync::FileError)
+{
+ //check if shadow copy dll was loaded correctly
+ if (!isOkay())
+ throw FileError(Zstring(_("Error copying locked file %x!")).Replace(wxT("%x"), Zstring(wxT("\"")) + inputFile + wxT("\"")) + wxT("\n\n") +
+ _("Error starting Volume Shadow Copy Service!") + wxT("\n") +
+ _("Please copy the appropriate \"Shadow.dll\" (located in \"Shadow.zip\" archive) into the FreeFileSync installation directory to enable this feature."));
+
+
+ wchar_t volumeNameRaw[1000];
+
+ if (!GetVolumePathName(inputFile.c_str(), //__in LPCTSTR lpszFileName,
+ volumeNameRaw, //__out LPTSTR lpszVolumePathName,
+ 1000)) //__in DWORD cchBufferLength
+ throw FileError(Zstring(_("Error copying locked file %x!")).Replace(wxT("%x"), Zstring(wxT("\"")) + inputFile + wxT("\"")) + wxT("\n\n") +
+ _("Could not determine volume name for file:") + wxT("\n\"") + inputFile + wxT("\""));
+
+ Zstring volumeNameFormatted = volumeNameRaw;
+ if (!volumeNameFormatted.EndsWith(FreeFileSync::FILE_NAME_SEPARATOR))
+ volumeNameFormatted += FreeFileSync::FILE_NAME_SEPARATOR;
+
+ if (volumeNameFormatted != realVolumeLast)
+ {
+ //release old shadow copy
+ if (backupHandle != NULL)
+ {
+ ShadowlDllHandler::getInstance().releaseShadowCopy(backupHandle);
+ backupHandle = NULL;
+ }
+ realVolumeLast.clear(); //...if next call fails...
+ shadowVolumeLast.clear(); //...if next call fails...
+
+ //start shadow volume copy service:
+ wchar_t shadowVolName[1000];
+ void* backupHandleTmp = NULL;
+ wchar_t errorMessage[1000];
+
+ if (!ShadowlDllHandler::getInstance().createShadowCopy(
+ volumeNameFormatted.c_str(),
+ shadowVolName,
+ 1000,
+ &backupHandleTmp,
+ errorMessage,
+ 1000))
+ throw FileError(Zstring(_("Error copying locked file %x!")).Replace(wxT("%x"), Zstring(wxT("\"")) + inputFile + wxT("\"")) + wxT("\n\n") +
+ _("Error starting Volume Shadow Copy Service!") + wxT("\n") +
+ wxT("(") + errorMessage + wxT(")"));
+
+ realVolumeLast = volumeNameFormatted;
+ shadowVolumeLast = Zstring(shadowVolName) + FreeFileSync::FILE_NAME_SEPARATOR; //shadowVolName NEVER has a trailing backslash
+ backupHandle = backupHandleTmp;
+ }
+
+ const size_t pos = inputFile.find(volumeNameFormatted);
+ if (pos == Zstring::npos)
+ {
+ Zstring msg = _("Volume name %x not part of filename %y!");
+ msg.Replace(wxT("%x"), Zstring(wxT("\"")) + volumeNameFormatted + wxT("\""), false);
+ msg.Replace(wxT("%y"), Zstring(wxT("\"")) + inputFile + wxT("\""), false);
+ throw FileError(Zstring(_("Error copying locked file %x!")).Replace(wxT("%x"), Zstring(wxT("\"")) + inputFile + wxT("\"")) + wxT("\n\n") +
+ msg);
+ }
+
+ //return filename alias on shadow copy volume
+ return shadowVolumeLast + Zstring(inputFile.c_str() + pos + volumeNameFormatted.length());
+}
+
+
+
+
+
+
+
+
+
diff --git a/library/shadow.h b/library/shadow.h
new file mode 100644
index 00000000..ded9d746
--- /dev/null
+++ b/library/shadow.h
@@ -0,0 +1,31 @@
+#ifndef SHADOW_H_INCLUDED
+#define SHADOW_H_INCLUDED
+
+#ifndef FFS_WIN
+#warning //this header should be used in the windows build only!
+#endif
+
+#include "zstring.h"
+#include "fileError.h"
+
+
+namespace FreeFileSync
+{
+ class ShadowCopy //buffer access to Windows Volume Shadow Copy Service
+ {
+ public:
+ ShadowCopy();
+ ~ShadowCopy();
+
+ Zstring makeShadowCopy(const Zstring& inputFile) throw(FileError); //returns filename on shadow copy
+
+ private:
+ bool isOkay();
+
+ Zstring realVolumeLast; //buffer last volume name
+ Zstring shadowVolumeLast; //buffer last created shadow volume
+ void* backupHandle;
+ };
+}
+
+#endif // SHADOW_H_INCLUDED
diff --git a/library/statistics.cpp b/library/statistics.cpp
index ec03c59e..f2506da8 100644
--- a/library/statistics.cpp
+++ b/library/statistics.cpp
@@ -5,6 +5,11 @@
#include "statusHandler.h"
#include "../algorithm.h"
#include <limits>
+#include <wx/stopwatch.h>
+
+
+RetrieveStatistics::RetrieveStatistics() :
+ timer(new wxStopWatch) {}
RetrieveStatistics::~RetrieveStatistics()
@@ -30,7 +35,7 @@ void RetrieveStatistics::writeEntry(const double value, const int objects)
statEntry newEntry;
newEntry.value = value;
newEntry.objects = objects;
- newEntry.time = timer.Time();
+ newEntry.time = timer->Time();
data.push_back(newEntry);
}
@@ -102,8 +107,8 @@ Statistics::Statistics(const int totalObjectCount,
windowSizeRemTime(windowSizeRemainingTime),
windowSizeBPS(windowSizeBytesPerSecond),
windowMax(std::max(windowSizeRemainingTime, windowSizeBytesPerSecond)),
- remainingTimeLast(256*256*256*100) //something "big"
-{}
+ remainingTimeLast(256*256*256*100), //something "big"
+ timer(new wxStopWatch) {}
void Statistics::addMeasurement(const int objectsCurrent, const double dataCurrent)
@@ -111,7 +116,7 @@ void Statistics::addMeasurement(const int objectsCurrent, const double dataCurre
record newEntry;
newEntry.objects = objectsCurrent;
newEntry.data = dataCurrent;
- newEntry.time = timer.Time();
+ newEntry.time = timer->Time();
//insert new record
measurements.push_back(newEntry);
@@ -176,13 +181,13 @@ wxString Statistics::getBytesPerSecond() const
void Statistics::pauseTimer()
{
- timer.Pause();
+ timer->Pause();
}
void Statistics::resumeTimer()
{
- timer.Resume();
+ timer->Resume();
}
/*
diff --git a/library/statistics.h b/library/statistics.h
index 3d4179db..f0eafad8 100644
--- a/library/statistics.h
+++ b/library/statistics.h
@@ -2,13 +2,18 @@
#define STATISTICS_H_INCLUDED
#include <vector>
-#include <wx/stopwatch.h>
#include <list>
+#include <memory>
+#include <wx/defs.h>
+
+class wxStopWatch;
+class wxString;
+
class RetrieveStatistics
{
public:
- wxDEPRECATED(RetrieveStatistics() {}) //generate compiler warnings as a reminder to remove code after measurements
+ wxDEPRECATED( RetrieveStatistics() ); //generate compiler warnings as a reminder to remove code after measurements
~RetrieveStatistics();
void writeEntry(const double value, const int objects);
@@ -22,7 +27,7 @@ private:
};
std::vector<statEntry> data;
- wxStopWatch timer;
+ std::auto_ptr<wxStopWatch> timer;
};
@@ -61,7 +66,7 @@ private:
};
std::list<record> measurements;
- wxStopWatch timer;
+ std::auto_ptr<wxStopWatch> timer;
};
#endif // STATISTICS_H_INCLUDED
diff --git a/library/zstring.cpp b/library/zstring.cpp
index ddf1fc73..b26ee451 100644
--- a/library/zstring.cpp
+++ b/library/zstring.cpp
@@ -19,7 +19,7 @@ AllocationCount::~AllocationCount()
}
-AllocationCount& AllocationCount::getGlobal()
+AllocationCount& AllocationCount::getInstance()
{
static AllocationCount global;
return global;
@@ -260,7 +260,7 @@ Zstring& Zstring::replace(size_t pos1, size_t n1, const DefaultChar* str, size_t
}
const size_t newLen = oldLen - n1 + n2;
- if (n1 < n2 || descr->refCount > 1)
+ if (newLen > oldLen || descr->refCount > 1)
{ //allocate a new string
StringDescriptor* newDescr;
DefaultChar* newData;
@@ -279,7 +279,7 @@ Zstring& Zstring::replace(size_t pos1, size_t n1, const DefaultChar* str, size_t
else //overwrite current string: case "n2 == 0" is handled implicitly
{
memcpy(data + pos1, str, n2 * sizeof(DefaultChar));
- if (n1 > n2)
+ if (newLen < oldLen)
{
memmove(data + pos1 + n2, data + pos1 + n1, (oldLen - pos1 - n1) * sizeof(DefaultChar));
data[newLen] = 0;
diff --git a/library/zstring.h b/library/zstring.h
index 007922d8..bca50862 100644
--- a/library/zstring.h
+++ b/library/zstring.h
@@ -11,7 +11,6 @@
#include <cctype>
#include <assert.h>
#include <new>
-#include <cstdlib>
namespace FreeFileSync
@@ -23,18 +22,10 @@ namespace FreeFileSync
}
-#ifdef FFS_WIN
-#define ZSTRING_WIDE_CHAR //use wide character strings
-
-#elif defined FFS_LINUX
-#define ZSTRING_CHAR //use char strings
-#endif
-
-
#ifdef ZSTRING_CHAR
-typedef char DefaultChar;
+typedef char DefaultChar; //use char strings
#elif defined ZSTRING_WIDE_CHAR
-typedef wchar_t DefaultChar;
+typedef wchar_t DefaultChar; //use wide character strings
#endif
class Zsubstr;
@@ -54,6 +45,7 @@ public:
bool StartsWith(const DefaultChar* begin) const;
bool StartsWith(const Zstring& begin) const;
bool EndsWith(const DefaultChar* end) const;
+ bool EndsWith(const DefaultChar end) const;
bool EndsWith(const Zstring& end) const;
#ifdef FFS_WIN
int CmpNoCase(const DefaultChar* other) const;
@@ -76,6 +68,7 @@ public:
Zstring substr(size_t pos = 0, size_t len = npos) const; //allocate new string
Zsubstr zsubstr(size_t pos = 0) const; //use ref-counting!
bool empty() const;
+ void clear();
int compare(const DefaultChar* other) const;
int compare(const Zstring& other) const;
int compare(const size_t pos1, const size_t n1, const DefaultChar* other) const;
@@ -119,7 +112,7 @@ private:
size_t length;
size_t capacity; //allocated length without null-termination
};
- static void allocate(const size_t newLength, Zstring::StringDescriptor*& newDescr, DefaultChar*& newData);
+ static void allocate(const size_t newLength, StringDescriptor*& newDescr, DefaultChar*& newData);
StringDescriptor* descr;
DefaultChar* data;
@@ -211,13 +204,13 @@ int defaultCompare(const wchar_t* str1, const wchar_t* str2, const size_t count)
}
inline
-wchar_t* defaultStrFind(const wchar_t* str1, const wchar_t* str2)
+const wchar_t* defaultStrFind(const wchar_t* str1, const wchar_t* str2)
{
return wcsstr(str1, str2);
}
inline
-wchar_t* defaultStrFind(const wchar_t* str1, int ch)
+const wchar_t* defaultStrFind(const wchar_t* str1, int ch)
{
return wcschr(str1, ch);
}
@@ -251,7 +244,7 @@ public:
--count;
}
- static AllocationCount& getGlobal();
+ static AllocationCount& getInstance();
private:
AllocationCount() : count(0) {}
@@ -277,17 +270,15 @@ void Zstring::allocate(const size_t newLength,
const size_t newCapacity = getCapacityToAllocate(newLength);
assert(newCapacity);
- newDescr = (StringDescriptor*) malloc( sizeof(StringDescriptor) + (newCapacity + 1) * sizeof(DefaultChar));
- if (newDescr == NULL)
- throw std::bad_alloc();
- newData = (DefaultChar*)(newDescr + 1);
+ newDescr = static_cast<StringDescriptor*>(operator new [] (sizeof(StringDescriptor) + (newCapacity + 1) * sizeof(DefaultChar)));
+ newData = reinterpret_cast<DefaultChar*>(newDescr + 1);
newDescr->refCount = 1;
newDescr->length = newLength;
newDescr->capacity = newCapacity;
#ifdef __WXDEBUG__
- AllocationCount::getGlobal().inc(); //test Zstring for memory leaks
+ AllocationCount::getInstance().inc(); //test Zstring for memory leaks
#endif
}
@@ -361,10 +352,10 @@ void Zstring::decRef()
assert(descr && descr->refCount >= 1); //descr points to the begin of the allocated memory block
if (--descr->refCount == 0)
{
- free(descr); //this must NEVER be changed!! E.g. Trim() relies on descr being start of allocated memory block
+ operator delete [] (descr); //this must NEVER be changed!! E.g. Trim() relies on descr being start of allocated memory block
descr = NULL;
#ifdef __WXDEBUG__
- AllocationCount::getGlobal().dec(); //test Zstring for memory leaks
+ AllocationCount::getInstance().dec(); //test Zstring for memory leaks
#endif
}
}
@@ -474,6 +465,16 @@ bool Zstring::EndsWith(const DefaultChar* end) const
inline
+bool Zstring::EndsWith(const DefaultChar end) const
+{
+ const size_t thisLength = length();
+ if (thisLength < 1)
+ return false;
+ return *(c_str() + thisLength - 1) == end;
+}
+
+
+inline
bool Zstring::EndsWith(const Zstring& end) const
{
const size_t thisLength = length();
@@ -611,6 +612,13 @@ bool Zstring::empty() const
inline
+void Zstring::clear()
+{
+ *this = Zstring();
+}
+
+
+inline
DefaultChar Zstring::operator[](const size_t pos) const
{
assert(pos < length());
@@ -690,16 +698,12 @@ bool Zsubstr::StartsWith(const Zstring& begin) const
inline
size_t Zsubstr::findFromEnd(const DefaultChar ch) const
{
- if (length() == 0)
- return Zstring::npos;
-
- size_t pos = length() - 1;
- do //pos points to last char of the string
+ size_t pos = length();
+ while (--pos != static_cast<size_t>(-1))
{
if (m_data[pos] == ch)
return pos;
}
- while (--pos != static_cast<size_t>(-1));
return Zstring::npos;
}
bgstack15