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