diff options
author | Daniel Wilhelm <daniel@wili.li> | 2014-04-18 17:15:16 +0200 |
---|---|---|
committer | Daniel Wilhelm <daniel@wili.li> | 2014-04-18 17:15:16 +0200 |
commit | bd6336c629841c6db3a6ca53a936d629d34db53b (patch) | |
tree | 3721ef997864108df175ce677a8a7d4342a6f1d2 /library | |
parent | 4.0 (diff) | |
download | FreeFileSync-bd6336c629841c6db3a6ca53a936d629d34db53b.tar.gz FreeFileSync-bd6336c629841c6db3a6ca53a936d629d34db53b.tar.bz2 FreeFileSync-bd6336c629841c6db3a6ca53a936d629d34db53b.zip |
4.1
Diffstat (limited to 'library')
34 files changed, 0 insertions, 9237 deletions
diff --git a/library/Batch.ico b/library/Batch.ico Binary files differdeleted file mode 100644 index 7b33067a..00000000 --- a/library/Batch.ico +++ /dev/null diff --git a/library/FreeFileSync.ico b/library/FreeFileSync.ico Binary files differdeleted file mode 100644 index b87789a7..00000000 --- a/library/FreeFileSync.ico +++ /dev/null diff --git a/library/SyncDB.ico b/library/SyncDB.ico Binary files differdeleted file mode 100644 index eee91c14..00000000 --- a/library/SyncDB.ico +++ /dev/null diff --git a/library/binary.cpp b/library/binary.cpp deleted file mode 100644 index 1e043d33..00000000 --- a/library/binary.cpp +++ /dev/null @@ -1,125 +0,0 @@ -// ************************************************************************** -// * This file is part of the FreeFileSync project. It is distributed under * -// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * -// * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * -// ************************************************************************** - -#include "binary.h" -#include "../shared/file_io.h" -#include <vector> -#include <wx/stopwatch.h> -#include "../shared/int64.h" -#include <boost/thread/tss.hpp> - -inline -void setMinSize(std::vector<char>& buffer, size_t minSize) -{ - if (buffer.size() < minSize) //this is similar to reserve(), but we need a "properly initialized" array here - buffer.resize(minSize); -} - - -namespace -{ -class BufferSize -{ -public: - BufferSize() : bufSize(BUFFER_SIZE_START) {} - - void inc() - { - if (bufSize < BUFFER_SIZE_MAX) - bufSize *= 2; - } - - void dec() - { - if (bufSize > BUFFER_SIZE_MIN) - bufSize /= 2; - } - - operator size_t() const { return bufSize; } - -private: - static const size_t BUFFER_SIZE_MIN = 128 * 1024; - static const size_t BUFFER_SIZE_START = 512 * 1024; //512 kb seems to be a reasonable initial buffer size - static const size_t BUFFER_SIZE_MAX = 16 * 1024 * 1024; - - /*Tests on Win7 x64 show that buffer size does NOT matter if files are located on different physical disks! - Impact of buffer size when files are on same disk: - - buffer MB/s - ------------ - 64 10 - 128 19 - 512 40 - 1024 48 - 2048 56 - 4096 56 - 8192 56 - */ - - size_t bufSize; -}; -} - - -bool zen::filesHaveSameContent(const Zstring& filename1, const Zstring& filename2, CompareCallback& callback) -{ - FileInput file1(filename1); //throw FileError - FileInput file2(filename2); //throw FileError - - static boost::thread_specific_ptr<std::vector<char>> cpyBuf1; - static boost::thread_specific_ptr<std::vector<char>> cpyBuf2; - if (!cpyBuf1.get()) - cpyBuf1.reset(new std::vector<char>()); - if (!cpyBuf2.get()) - cpyBuf2.reset(new std::vector<char>()); - - std::vector<char>& memory1 = *cpyBuf1; - std::vector<char>& memory2 = *cpyBuf2; - - BufferSize bufferSize; - zen::UInt64 bytesCompared; - - wxLongLong lastDelayViolation = wxGetLocalTimeMillis(); - - do - { - setMinSize(memory1, bufferSize); - setMinSize(memory2, bufferSize); - - const wxLongLong startTime = wxGetLocalTimeMillis(); - - const size_t length1 = file1.read(&memory1[0], bufferSize); //returns actual number of bytes read; throw FileError() - const size_t length2 = file2.read(&memory2[0], bufferSize); // - - const wxLongLong stopTime = wxGetLocalTimeMillis(); - - //-------- dynamically set buffer size to keep callback interval between 200 - 500ms --------------------- - const wxLongLong loopTime = stopTime - startTime; - if (loopTime < 200 && stopTime - lastDelayViolation > 2000) //avoid "flipping back": e.g. DVD-Roms read 32MB at once, so first read may be > 300 ms, but second one will be 0ms! - { - lastDelayViolation = stopTime; - bufferSize.inc(); //practically no costs! - } - else if (loopTime > 500) - { - lastDelayViolation = stopTime; - bufferSize.dec(); // - } - //------------------------------------------------------------------------------------------------ - - if (length1 != length2 || ::memcmp(&memory1[0], &memory2[0], length1) != 0) - return false; - - bytesCompared += length1 * 2; - callback.updateCompareStatus(bytesCompared); //send progress updates - } - while (!file1.eof()); - - if (!file2.eof()) //highly unlikely, but theoretically possible! (but then again, not in this context where both files have same size...) - return false; - - return true; -} diff --git a/library/binary.h b/library/binary.h deleted file mode 100644 index 4dbbcd45..00000000 --- a/library/binary.h +++ /dev/null @@ -1,28 +0,0 @@ -// ************************************************************************** -// * This file is part of the FreeFileSync project. It is distributed under * -// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * -// * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * -// ************************************************************************** - -#ifndef BINARY_H_INCLUDED -#define BINARY_H_INCLUDED - -#include "../shared/zstring.h" -#include "../shared/file_error.h" -#include "../shared/int64.h" - -namespace zen -{ - -//callback functionality for status updates while comparing -class CompareCallback -{ -public: - virtual ~CompareCallback() {} - virtual void updateCompareStatus(zen::UInt64 totalBytesTransferred) = 0; -}; - -bool filesHaveSameContent(const Zstring& filename1, const Zstring& filename2, CompareCallback& callback); //throw FileError -} - -#endif // BINARY_H_INCLUDED diff --git a/library/cmp_filetime.h b/library/cmp_filetime.h deleted file mode 100644 index 24b331c6..00000000 --- a/library/cmp_filetime.h +++ /dev/null @@ -1,64 +0,0 @@ -#ifndef CMP_FILETIME_H_INCLUDED -#define CMP_FILETIME_H_INCLUDED - -#include <wx/stopwatch.h> -#include "../shared/int64.h" - -namespace zen -{ -//--------------------------------------------------------------------------------------------------------------- -inline -bool sameFileTime(const Int64& a, const Int64& b, size_t tolerance) -{ - if (a < b) - return b <= a + static_cast<int>(tolerance); - else - return a <= b + static_cast<int>(tolerance); -} -//--------------------------------------------------------------------------------------------------------------- - -class CmpFileTime -{ -public: - CmpFileTime(size_t tolerance) : tolerance_(tolerance) {} - - enum Result - { - TIME_EQUAL, - TIME_LEFT_NEWER, - TIME_RIGHT_NEWER, - TIME_LEFT_INVALID, - TIME_RIGHT_INVALID - }; - - Result getResult(const Int64& lhs, const Int64& rhs) const - { - if (lhs == rhs) - return TIME_EQUAL; - - //number of seconds since Jan 1st 1970 + 1 year (needn't be too precise) - static const long oneYearFromNow = wxGetUTCTime() + 365 * 24 * 3600; //static in header: not a big deal in this case! - - //check for erroneous dates (but only if dates are not (EXACTLY) the same) - if (lhs < 0 || lhs > oneYearFromNow) //earlier than Jan 1st 1970 or more than one year in future - return TIME_LEFT_INVALID; - - if (rhs < 0 || rhs > oneYearFromNow) - return TIME_RIGHT_INVALID; - - if (sameFileTime(lhs, rhs, tolerance_)) //last write time may differ by up to 2 seconds (NTFS vs FAT32) - return TIME_EQUAL; - - //regular time comparison - if (lhs < rhs) - return TIME_RIGHT_NEWER; - else - return TIME_LEFT_NEWER; - } - -private: - const size_t tolerance_; -}; -} - -#endif // CMP_FILETIME_H_INCLUDED diff --git a/library/custom_grid.cpp b/library/custom_grid.cpp deleted file mode 100644 index b4d7af41..00000000 --- a/library/custom_grid.cpp +++ /dev/null @@ -1,2404 +0,0 @@ -// ************************************************************************** -// * This file is part of the FreeFileSync project. It is distributed under * -// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * -// * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * -// ************************************************************************** - -#include "custom_grid.h" -#include "resources.h" -#include <wx/dc.h> -#include "../shared/util.h" -#include "../shared/string_conv.h" -#include "resources.h" -#include <typeinfo> -#include "../ui/grid_view.h" -#include "../synchronization.h" -#include "../shared/custom_tooltip.h" -#include "../shared/i18n.h" -#include <wx/dcclient.h> -#include <wx/icon.h> -#include <wx/tooltip.h> -#include <wx/settings.h> -#include "../shared/i18n.h" - -#ifdef FFS_WIN -#include <wx/timer.h> -#include "status_handler.h" -#include <cmath> - -#elif defined FFS_LINUX -#include <gtk/gtk.h> -#endif - -using namespace zen; - - -const size_t MIN_ROW_COUNT = 15; - -//class containing pure grid data: basically the same as wxGridStringTable, but adds cell formatting - -/* -class hierarchy: - CustomGridTable - /|\ - ________________|________________ - | | - CustomGridTableRim | - /|\ | - __________|__________ | - | | | -CustomGridTableLeft CustomGridTableRight CustomGridTableMiddle -*/ - -class CustomGridTable : public wxGridTableBase -{ -public: - CustomGridTable(int initialRows = 0, int initialCols = 0) : //note: initialRows/initialCols MUST match with GetNumberRows()/GetNumberCols() at initialization!!! - wxGridTableBase(), - gridDataView(NULL), - lastNrRows(initialRows), - lastNrCols(initialCols) {} - - - virtual ~CustomGridTable() {} - - - void setGridDataTable(const GridView* view) - { - this->gridDataView = view; - } - - - //########################################################################### - //grid standard input output methods, redirected directly to gridData to improve performance - - virtual int GetNumberRows() - { - if (gridDataView) - return static_cast<int>(std::max(gridDataView->rowsOnView(), MIN_ROW_COUNT)); - else - return 0; //grid is initialized with zero number of rows - } - - - virtual bool IsEmptyCell(int row, int col) - { - return false; //avoid overlapping cells - - //return (GetValue(row, col) == wxEmptyString); - } - - - virtual void SetValue(int row, int col, const wxString& value) - { - assert (false); //should not be used, since values are retrieved directly from gridDataView - } - - //update dimensions of grid: no need for InsertRows(), AppendRows(), DeleteRows() anymore!!! - void updateGridSizes() - { - const int currentNrRows = GetNumberRows(); - - if (lastNrRows < currentNrRows) - { - if (GetView()) - { - wxGridTableMessage msg(this, - wxGRIDTABLE_NOTIFY_ROWS_APPENDED, - currentNrRows - lastNrRows); - - GetView()->ProcessTableMessage( msg ); - } - } - else if (lastNrRows > currentNrRows) - { - if (GetView()) - { - wxGridTableMessage msg(this, - wxGRIDTABLE_NOTIFY_ROWS_DELETED, - 0, - lastNrRows - currentNrRows); - - GetView()->ProcessTableMessage( msg ); - } - } - lastNrRows = currentNrRows; - - const int currentNrCols = GetNumberCols(); - - if (lastNrCols < currentNrCols) - { - if (GetView()) - { - wxGridTableMessage msg(this, - wxGRIDTABLE_NOTIFY_COLS_APPENDED, - currentNrCols - lastNrCols); - - GetView()->ProcessTableMessage( msg ); - } - } - else if (lastNrCols > currentNrCols) - { - if (GetView()) - { - wxGridTableMessage msg(this, - wxGRIDTABLE_NOTIFY_COLS_DELETED, - 0, - lastNrCols - currentNrCols); - - GetView()->ProcessTableMessage( msg ); - } - } - lastNrCols = currentNrCols; - } - //########################################################################### - - - virtual wxGridCellAttr* GetAttr(int row, int col, wxGridCellAttr::wxAttrKind kind) - { - const std::pair<wxColour, wxColour> color = getRowColor(row); - - //add color to some rows - wxGridCellAttr* result = wxGridTableBase::GetAttr(row, col, kind); - if (result) - { - if (result->GetTextColour() == color.first && - result->GetBackgroundColour() == color.second) - { - return result; - } - else //grid attribute might be referenced by other elements, so clone it! - { - wxGridCellAttr* attr = result->Clone(); //attr has ref-count 1 - result->DecRef(); - result = attr; - } - } - else - result = new wxGridCellAttr; //created with ref-count 1 - - result->SetTextColour (color.first); - result->SetBackgroundColour(color.second); - - return result; - } - - - const FileSystemObject* getRawData(size_t row) const - { - if (gridDataView) - return gridDataView->getObject(row); //returns NULL if request is not valid or not data found - - return NULL; - } - -protected: - static const wxColour COLOR_BLUE; - static const wxColour COLOR_GREY; - static const wxColour COLOR_ORANGE; - static const wxColour COLOR_CMP_RED; - static const wxColour COLOR_CMP_BLUE; - static const wxColour COLOR_CMP_GREEN; - static const wxColour COLOR_SYNC_BLUE; - static const wxColour COLOR_SYNC_BLUE_LIGHT; - static const wxColour COLOR_SYNC_GREEN; - static const wxColour COLOR_SYNC_GREEN_LIGHT; - static const wxColour COLOR_YELLOW; - static const wxColour COLOR_YELLOW_LIGHT; - - const GridView* gridDataView; //(very fast) access to underlying grid data :) - -private: - virtual const std::pair<wxColour, wxColour> getRowColor(int row) = 0; //rows that are filtered out are shown in different color: <foreground, background> - - int lastNrRows; - int lastNrCols; -}; - -//see http://www.latiumsoftware.com/en/articles/00015.php#12 for "safe" colors -const wxColour CustomGridTable::COLOR_ORANGE( 238, 201, 0); -const wxColour CustomGridTable::COLOR_BLUE( 80, 110, 255); -const wxColour CustomGridTable::COLOR_GREY( 212, 208, 200); -const wxColour CustomGridTable::COLOR_CMP_RED( 249, 163, 165); -const wxColour CustomGridTable::COLOR_CMP_BLUE( 144, 232, 246); -const wxColour CustomGridTable::COLOR_CMP_GREEN( 147, 253, 159); -const wxColour CustomGridTable::COLOR_SYNC_BLUE( 201, 203, 247); -const wxColour CustomGridTable::COLOR_SYNC_BLUE_LIGHT(201, 225, 247); -const wxColour CustomGridTable::COLOR_SYNC_GREEN(197, 248, 190); -const wxColour CustomGridTable::COLOR_SYNC_GREEN_LIGHT(226, 248, 190); -const wxColour CustomGridTable::COLOR_YELLOW( 247, 252, 62); -const wxColour CustomGridTable::COLOR_YELLOW_LIGHT(253, 252, 169); - - -class CustomGridTableRim : public CustomGridTable -{ -public: - virtual ~CustomGridTableRim() {} - - virtual int GetNumberCols() - { - return static_cast<int>(columnPositions.size()); - } - - virtual wxString GetColLabelValue( int col ) - { - return CustomGridRim::getTypeName(getTypeAtPos(col)); - } - - - void setupColumns(const std::vector<xmlAccess::ColumnTypes>& positions) - { - columnPositions = positions; - updateGridSizes(); //add or remove columns - } - - - xmlAccess::ColumnTypes getTypeAtPos(size_t pos) const - { - if (pos < columnPositions.size()) - return columnPositions[pos]; - else - return xmlAccess::DIRECTORY; - } - - //get filename in order to retrieve the icon from it - virtual Zstring getIconFile(size_t row) const = 0; //return "folder" if row points to a folder - -protected: - template <SelectedSide side> - wxString GetValueSub(int row, int col) - { - const FileSystemObject* fsObj = getRawData(row); - if (fsObj) - { - struct GetValue : public FSObjectVisitor - { - GetValue(xmlAccess::ColumnTypes colType, const FileSystemObject& fso) : colType_(colType), fsObj_(fso) {} - virtual void visit(const FileMapping& fileObj) - { - switch (colType_) - { - case xmlAccess::FULL_PATH: - value = toWx(fileObj.getFullName<side>().BeforeLast(FILE_NAME_SEPARATOR)); - break; - case xmlAccess::FILENAME: //filename - value = toWx(fileObj.getShortName<side>()); - break; - case xmlAccess::REL_PATH: //relative path - value = toWx(fileObj.getObjRelativeName().BeforeLast(FILE_NAME_SEPARATOR)); //returns empty string if ch not found - break; - case xmlAccess::DIRECTORY: - value = toWx(fileObj.getBaseDirPf<side>()); - break; - case xmlAccess::SIZE: //file size - if (!fsObj_.isEmpty<side>()) - value = zen::toStringSep(fileObj.getFileSize<side>()); - break; - case xmlAccess::DATE: //date - if (!fsObj_.isEmpty<side>()) - value = zen::utcTimeToLocalString(fileObj.getLastWriteTime<side>()); - break; - case xmlAccess::EXTENSION: //file extension - value = toWx(fileObj.getExtension<side>()); - break; - } - } - - virtual void visit(const SymLinkMapping& linkObj) - { - switch (colType_) - { - case xmlAccess::FULL_PATH: - value = toWx(linkObj.getFullName<side>().BeforeLast(FILE_NAME_SEPARATOR)); - break; - case xmlAccess::FILENAME: //filename - value = toWx(linkObj.getShortName<side>()); - break; - case xmlAccess::REL_PATH: //relative path - value = toWx(linkObj.getObjRelativeName().BeforeLast(FILE_NAME_SEPARATOR)); //returns empty string if ch not found - break; - case xmlAccess::DIRECTORY: - value = toWx(linkObj.getBaseDirPf<side>()); - break; - case xmlAccess::SIZE: //file size - if (!fsObj_.isEmpty<side>()) - value = _("<Symlink>"); - break; - case xmlAccess::DATE: //date - if (!fsObj_.isEmpty<side>()) - value = zen::utcTimeToLocalString(linkObj.getLastWriteTime<side>()); - break; - case xmlAccess::EXTENSION: //file extension - value = wxEmptyString; - break; - } - } - - virtual void visit(const DirMapping& dirObj) - { - switch (colType_) - { - case xmlAccess::FULL_PATH: - value = toWx(dirObj.getFullName<side>()); - break; - case xmlAccess::FILENAME: - value = toWx(dirObj.getShortName<side>()); - break; - case xmlAccess::REL_PATH: - value = toWx(dirObj.getObjRelativeName().BeforeLast(FILE_NAME_SEPARATOR)); //returns empty string if ch not found - break; - case xmlAccess::DIRECTORY: - value = toWx(dirObj.getBaseDirPf<side>()); - break; - case xmlAccess::SIZE: //file size - if (!fsObj_.isEmpty<side>()) - value = _("<Directory>"); - break; - case xmlAccess::DATE: //date - if (!fsObj_.isEmpty<side>()) - value = wxEmptyString; - break; - case xmlAccess::EXTENSION: //file extension - value = wxEmptyString; - break; - } - } - xmlAccess::ColumnTypes colType_; - wxString value; - - const FileSystemObject& fsObj_; - } getVal(getTypeAtPos(col), *fsObj); - fsObj->accept(getVal); - return getVal.value; - } - //if data is not found: - return wxEmptyString; - } - - template <SelectedSide side> - Zstring getIconFileImpl(size_t row) const //return "folder" if row points to a folder - { - const FileSystemObject* fsObj = getRawData(row); - if (fsObj && !fsObj->isEmpty<side>()) - { - struct GetIcon : public FSObjectVisitor - { - virtual void visit(const FileMapping& fileObj) - { - //Optimization: if filename exists on both sides, always use left side's file - //if (!fileObj.isEmpty<LEFT_SIDE>() && !fileObj.isEmpty<RIGHT_SIDE>()) - // iconName = fileObj.getFullName<LEFT_SIDE>(); - //else -> now with thumbnails this isn't viable anymore - iconName = fileObj.getFullName<side>(); - } - virtual void visit(const SymLinkMapping& linkObj) - { - iconName = linkObj.getLinkType<side>() == LinkDescriptor::TYPE_DIR ? - Zstr("folder") : - linkObj.getFullName<side>(); - } - virtual void visit(const DirMapping& dirObj) - { - iconName = Zstr("folder"); - } - - Zstring iconName; - } getIcon; - fsObj->accept(getIcon); - return getIcon.iconName; - } - - return Zstring(); - } - - -private: - virtual const std::pair<wxColour, wxColour> getRowColor(int row) //rows that are filtered out are shown in different color: <foreground, background> - { - std::pair<wxColour, wxColour> result(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT), - wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW)); - - const FileSystemObject* fsObj = getRawData(row); - if (fsObj) - { - //mark filtered rows - if (!fsObj->isActive()) - { - result.first = *wxBLACK; - result.second = COLOR_BLUE; - } - else - { - //mark directories and symlinks - struct GetRowColor : public FSObjectVisitor - { - GetRowColor(wxColour& foreground, wxColour& background) : foreground_(foreground), background_(background) {} - - virtual void visit(const FileMapping& fileObj) {} - virtual void visit(const SymLinkMapping& linkObj) - { - foreground_ = *wxBLACK; - background_ = COLOR_ORANGE; - } - virtual void visit(const DirMapping& dirObj) - { - foreground_ = *wxBLACK; - background_ = COLOR_GREY; - } - - private: - wxColour& foreground_; - wxColour& background_; - } getCol(result.first, result.second); - fsObj->accept(getCol); - } - } - - return result; - } - - std::vector<xmlAccess::ColumnTypes> columnPositions; -}; - - -class CustomGridTableLeft : public CustomGridTableRim -{ -public: - - virtual wxString GetValue(int row, int col) - { - return CustomGridTableRim::GetValueSub<LEFT_SIDE>(row, col); - } - - virtual Zstring getIconFile(size_t row) const //return "folder" if row points to a folder - { - return getIconFileImpl<LEFT_SIDE>(row); - } -}; - - -class CustomGridTableRight : public CustomGridTableRim -{ -public: - virtual wxString GetValue(int row, int col) - { - return CustomGridTableRim::GetValueSub<RIGHT_SIDE>(row, col); - } - - virtual Zstring getIconFile(size_t row) const //return "folder" if row points to a folder - { - return getIconFileImpl<RIGHT_SIDE>(row); - } -}; - - -class CustomGridTableMiddle : public CustomGridTable -{ -public: - //middle grid is created (first wxWidgets internal call to GetNumberCols()) with one column - CustomGridTableMiddle() : - CustomGridTable(0, GetNumberCols()), //attention: static binding to virtual GetNumberCols() in a Constructor! - syncPreviewActive(false) {} - - virtual int GetNumberCols() - { - return 1; - } - - virtual wxString GetColLabelValue( int col ) - { - return wxEmptyString; - } - - 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(fsObj->getSyncOperation()); - else - return getSymbol(fsObj->getCategory()); - } - return wxEmptyString; - } - - void enableSyncPreview(bool value) - { - syncPreviewActive = value; - } - - bool syncPreviewIsActive() const - { - return syncPreviewActive; - } - -private: - virtual const std::pair<wxColour, wxColour> getRowColor(int row) //rows that are filtered out are shown in different color: <foreground, background> - { - std::pair<wxColour, wxColour> result(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT), - wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW)); - - const FileSystemObject* fsObj = getRawData(row); - if (fsObj) - { - //mark filtered rows - if (!fsObj->isActive()) - { - result.first = *wxBLACK;; - result.second = COLOR_BLUE; - } - else - { - if (syncPreviewActive) //synchronization preview - { - switch (fsObj->getSyncOperation()) //evaluate comparison result and sync direction - { - case SO_DO_NOTHING: - case SO_EQUAL: - break;//usually white - case SO_CREATE_NEW_LEFT: - case SO_OVERWRITE_LEFT: - case SO_DELETE_LEFT: - result.first = *wxBLACK; - result.second = COLOR_SYNC_BLUE; - break; - case SO_COPY_METADATA_TO_LEFT: - result.first = *wxBLACK; - result.second = COLOR_SYNC_BLUE_LIGHT; - break; - case SO_CREATE_NEW_RIGHT: - case SO_OVERWRITE_RIGHT: - case SO_DELETE_RIGHT: - result.first = *wxBLACK; - result.second = COLOR_SYNC_GREEN; - break; - case SO_COPY_METADATA_TO_RIGHT: - result.first = *wxBLACK; - result.second = COLOR_SYNC_GREEN_LIGHT; - break; - case SO_UNRESOLVED_CONFLICT: - result.first = *wxBLACK; - result.second = COLOR_YELLOW; - break; - } - } - else //comparison results view - { - switch (fsObj->getCategory()) - { - case FILE_LEFT_SIDE_ONLY: - case FILE_LEFT_NEWER: - result.first = *wxBLACK; - result.second = COLOR_SYNC_BLUE; //COLOR_CMP_BLUE; - break; - - case FILE_RIGHT_SIDE_ONLY: - case FILE_RIGHT_NEWER: - result.first = *wxBLACK; - result.second = COLOR_SYNC_GREEN; //COLOR_CMP_GREEN; - break; - case FILE_DIFFERENT: - result.first = *wxBLACK; - result.second = COLOR_CMP_RED; - break; - case FILE_EQUAL: - break;//usually white - case FILE_CONFLICT: - result.first = *wxBLACK; - result.second = COLOR_YELLOW; - break; - case FILE_DIFFERENT_METADATA: - result.first = *wxBLACK; - result.second = COLOR_YELLOW_LIGHT; - break; - } - } - } - } - - return result; - } - - bool syncPreviewActive; //determines wheter grid shall show compare result or sync preview -}; - -//######################################################################################################## - - -CustomGrid::CustomGrid(wxWindow* parent, - wxWindowID id, - const wxPoint& pos, - const wxSize& size, - long style, - const wxString& name) : - wxGrid(parent, id, pos, size, style, name), - m_gridLeft(NULL), - m_gridMiddle(NULL), - m_gridRight(NULL), - isLeading(false), - m_marker(-1, ASCENDING) -{ - //wxColour darkBlue(40, 35, 140); -> user default colors instead! - //SetSelectionBackground(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT)); - //SetSelectionForeground(*wxWHITE); -} - - -void CustomGrid::initSettings(CustomGridLeft* gridLeft, - CustomGridMiddle* gridMiddle, - CustomGridRight* gridRight, - const GridView* gridDataView) -{ - assert(this == gridLeft || this == gridRight || this == gridMiddle); - - //these grids will scroll together - m_gridLeft = gridLeft; - m_gridRight = gridRight; - m_gridMiddle = gridMiddle; - - //enhance grid functionality; identify leading grid by keyboard input or scroll action - Connect(wxEVT_KEY_DOWN, wxEventHandler(CustomGrid::onGridAccess), NULL, this); - Connect(wxEVT_SCROLLWIN_TOP, wxEventHandler(CustomGrid::onGridAccess), NULL, this); - Connect(wxEVT_SCROLLWIN_BOTTOM, wxEventHandler(CustomGrid::onGridAccess), NULL, this); - Connect(wxEVT_SCROLLWIN_LINEUP, wxEventHandler(CustomGrid::onGridAccess), NULL, this); - Connect(wxEVT_SCROLLWIN_LINEDOWN, wxEventHandler(CustomGrid::onGridAccess), NULL, this); - Connect(wxEVT_SCROLLWIN_PAGEUP, wxEventHandler(CustomGrid::onGridAccess), NULL, this); - Connect(wxEVT_SCROLLWIN_PAGEDOWN, wxEventHandler(CustomGrid::onGridAccess), NULL, this); - Connect(wxEVT_SCROLLWIN_THUMBTRACK, wxEventHandler(CustomGrid::onGridAccess), NULL, this); - Connect(wxEVT_SCROLLWIN_THUMBRELEASE, wxEventHandler(CustomGrid::onGridAccess), NULL, this); - Connect(wxEVT_GRID_LABEL_LEFT_CLICK, wxEventHandler(CustomGrid::onGridAccess), NULL, this); - Connect(wxEVT_SET_FOCUS, wxEventHandler(CustomGrid::onGridAccess), NULL, this); //used by grid text-search - GetGridWindow()->Connect(wxEVT_LEFT_DOWN, wxEventHandler(CustomGrid::onGridAccess), NULL, this); - GetGridWindow()->Connect(wxEVT_RIGHT_DOWN, wxEventHandler(CustomGrid::onGridAccess), NULL, this); - - GetGridWindow()->Connect(wxEVT_ENTER_WINDOW, wxEventHandler(CustomGrid::adjustGridHeights), NULL, this); - - //parallel grid scrolling: do NOT use DoPrepareDC() to align grids! GDI resource leak! Use regular paint event instead: - GetGridWindow()->Connect(wxEVT_PAINT, wxEventHandler(CustomGrid::OnPaintGrid), NULL, this); -} - - -void CustomGrid::release() //release connection to zen::GridView -{ - assert(getGridDataTable()); - getGridDataTable()->setGridDataTable(NULL); //kind of "disable" griddatatable; don't delete it with SetTable(NULL)! May be used by wxGridCellStringRenderer -} - - -bool CustomGrid::isLeadGrid() const -{ - return isLeading; -} - - -void CustomGrid::setIconManager(const std::shared_ptr<IconBuffer>& iconBuffer) -{ - if (iconBuffer.get()) - SetDefaultRowSize(iconBuffer->getSize() + 1, true); //+ 1 for line between rows - else - SetDefaultRowSize(IconBuffer(IconBuffer::SIZE_SMALL).getSize() + 1, true); //currently iconBuffer is always bound, but we may use it as a "no icon" status at some time... - - enableFileIcons(iconBuffer); - Refresh(); -} - - -void CustomGrid::RefreshCell(int row, int col) -{ - wxRect rectScrolled(CellToRect(row, col)); - //use: wxRect rect = CellToRect( row, col ); ? - CalcScrolledPosition(rectScrolled.x, rectScrolled.y, &rectScrolled.x, &rectScrolled.y); - - GetGridWindow()->RefreshRect(rectScrolled); //note: CellToRect() and YToRow work on m_gridWindow NOT on the whole grid! -} - - -void CustomGrid::OnPaintGrid(wxEvent& event) -{ - if (isLeadGrid()) //avoid back coupling - alignOtherGrids(m_gridLeft, m_gridMiddle, m_gridRight); //scroll other grids - event.Skip(); -} - - -void moveCursorWhileSelecting(int anchor, int oldPos, int newPos, wxGrid* grid) -{ - //note: all positions are valid in this context! - - grid->SetGridCursor( newPos, grid->GetGridCursorCol()); - grid->MakeCellVisible(newPos, grid->GetGridCursorCol()); - - if (oldPos < newPos) - { - for (int i = oldPos; i < std::min(anchor, newPos); ++i) - grid->DeselectRow(i); //remove selection - - for (int i = std::max(oldPos, anchor); i <= newPos; ++i) - grid->SelectRow(i, true); //add to selection - } - else - { - for (int i = std::max(newPos, anchor) + 1; i <= oldPos; ++i) - grid->DeselectRow(i); //remove selection - - for (int i = newPos; i <= std::min(oldPos, anchor); ++i) - grid->SelectRow(i, true); //add to selection - } -} - - -void execGridCommands(wxEvent& event, wxGrid* grid) -{ - static int anchorRow = 0; - if (grid->GetNumberRows() == 0 || - grid->GetNumberCols() == 0) - return; - - const wxKeyEvent* keyEvent = dynamic_cast<const wxKeyEvent*> (&event); - if (keyEvent) - { - //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); - - const bool shiftPressed = keyEvent->ShiftDown(); - const bool ctrlPressed = keyEvent->ControlDown(); - const int keyCode = keyEvent->GetKeyCode(); - - //SHIFT + X - if (shiftPressed) - switch (keyCode) - { - case WXK_UP: - case WXK_NUMPAD_UP: - { - const int cursorNewPos = std::max(cursorOldPos - 1, 0); - moveCursorWhileSelecting(anchorRow, cursorOldPos, cursorNewPos, grid); - return; //no event.Skip() - } - case WXK_DOWN: - case WXK_NUMPAD_DOWN: - { - const int cursorNewPos = std::min(cursorOldPos + 1, grid->GetNumberRows() - 1); - moveCursorWhileSelecting(anchorRow, cursorOldPos, cursorNewPos, grid); - return; //no event.Skip() - } - case WXK_LEFT: - case WXK_NUMPAD_LEFT: - { - const int cursorColumn = std::max(cursorOldColumn - 1, 0); - grid->SetGridCursor(cursorOldPos, cursorColumn); - grid->MakeCellVisible(cursorOldPos, cursorColumn); - return; //no event.Skip() - } - case WXK_RIGHT: - case WXK_NUMPAD_RIGHT: - { - 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); - moveCursorWhileSelecting(anchorRow, cursorOldPos, cursorNewPos, grid); - 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); - moveCursorWhileSelecting(anchorRow, cursorOldPos, cursorNewPos, grid); - return; //no event.Skip() - } - case WXK_HOME: - case WXK_NUMPAD_HOME: - { - const int cursorNewPos = 0; - moveCursorWhileSelecting(anchorRow, cursorOldPos, cursorNewPos, grid); - return; //no event.Skip() - } - case WXK_END: - case WXK_NUMPAD_END: - { - const int cursorNewPos = grid->GetNumberRows() - 1; - moveCursorWhileSelecting(anchorRow, cursorOldPos, cursorNewPos, grid); - return; //no event.Skip() - } - } - - //CTRL + X - if (ctrlPressed) - switch (keyCode) - { - case WXK_UP: - case WXK_NUMPAD_UP: - { - grid->SetGridCursor(0, grid->GetGridCursorCol()); - grid->MakeCellVisible(0, grid->GetGridCursorCol()); - return; //no event.Skip() - } - case WXK_DOWN: - case WXK_NUMPAD_DOWN: - { - grid->SetGridCursor(grid->GetNumberRows() - 1, grid->GetGridCursorCol()); - grid->MakeCellVisible(grid->GetNumberRows() - 1, grid->GetGridCursorCol()); - return; //no event.Skip() - } - case WXK_LEFT: - case WXK_NUMPAD_LEFT: - { - grid->SetGridCursor(grid->GetGridCursorRow(), 0); - grid->MakeCellVisible(grid->GetGridCursorRow(), 0); - return; //no event.Skip() - } - case WXK_RIGHT: - case WXK_NUMPAD_RIGHT: - { - grid->SetGridCursor(grid->GetGridCursorRow(), grid->GetNumberCols() - 1); - grid->MakeCellVisible(grid->GetGridCursorRow(), grid->GetNumberCols() - 1); - return; //no event.Skip() - } - } - - //button with or without control keys pressed - switch (keyCode) - { - case WXK_HOME: - case WXK_NUMPAD_HOME: - { - grid->SetGridCursor(0, grid->GetGridCursorCol()); - grid->MakeCellVisible(0, grid->GetGridCursorCol()); - return; //no event.Skip() - } - case WXK_END: - case WXK_NUMPAD_END: - { - grid->SetGridCursor(grid->GetNumberRows() - 1, grid->GetGridCursorCol()); - grid->MakeCellVisible(grid->GetNumberRows() - 1, grid->GetGridCursorCol()); - 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() - } - } - - //button without additonal control keys pressed - if (keyEvent->GetModifiers() == wxMOD_NONE) - switch (keyCode) - { - case WXK_UP: - case WXK_NUMPAD_UP: - { - 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: - case WXK_NUMPAD_DOWN: - { - 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: - case WXK_NUMPAD_LEFT: - { - const int cursorColumn = std::max(cursorOldColumn - 1, 0); - grid->SetGridCursor(cursorOldPos, cursorColumn); - grid->MakeCellVisible(cursorOldPos, cursorColumn); - return; //no event.Skip() - } - case WXK_RIGHT: - case WXK_NUMPAD_RIGHT: - { - const int cursorColumn = std::min(cursorOldColumn + 1, grid->GetNumberCols() - 1); - grid->SetGridCursor(cursorOldPos, cursorColumn); - grid->MakeCellVisible(cursorOldPos, cursorColumn); - return; //no event.Skip() - } - } - } - - anchorRow = grid->GetGridCursorRow(); - event.Skip(); //let event delegate! -} - - -inline -bool gridsShouldBeCleared(const wxEvent& event) -{ - const wxMouseEvent* mouseEvent = dynamic_cast<const wxMouseEvent*>(&event); - if (mouseEvent) - { - if (mouseEvent->ControlDown() || mouseEvent->ShiftDown()) - return false; - - if (mouseEvent->ButtonDown(wxMOUSE_BTN_LEFT)) - return true; - } - else - { - const wxKeyEvent* keyEvent = dynamic_cast<const wxKeyEvent*>(&event); - if (keyEvent) - { - if (keyEvent->ControlDown() || keyEvent->AltDown() || keyEvent->ShiftDown()) - return false; - - switch (keyEvent->GetKeyCode()) - { - //default navigation keys - case WXK_UP: - case WXK_DOWN: - case WXK_LEFT: - case WXK_RIGHT: - case WXK_PAGEUP: - case WXK_PAGEDOWN: - case WXK_HOME: - case WXK_END: - case WXK_NUMPAD_UP: - case WXK_NUMPAD_DOWN: - case WXK_NUMPAD_LEFT: - case WXK_NUMPAD_RIGHT: - case WXK_NUMPAD_PAGEUP: - case WXK_NUMPAD_PAGEDOWN: - case WXK_NUMPAD_HOME: - case WXK_NUMPAD_END: - //other keys - case WXK_TAB: - case WXK_RETURN: - case WXK_NUMPAD_ENTER: - case WXK_ESCAPE: - return true; - } - } - } - - return false; -} - - -void CustomGrid::onGridAccess(wxEvent& event) -{ - if (!isLeading) - { - //notify other grids of new user focus - m_gridLeft ->isLeading = m_gridLeft == this; - m_gridMiddle->isLeading = m_gridMiddle == this; - m_gridRight ->isLeading = m_gridRight == this; - - wxGrid::SetFocus(); - } - - //clear grids - if (gridsShouldBeCleared(event)) - { - m_gridLeft ->ClearSelection(); - m_gridMiddle->ClearSelection(); - m_gridRight ->ClearSelection(); - } - - //update row labels NOW (needed when scrolling if buttons keep being pressed) - m_gridLeft ->GetGridRowLabelWindow()->Update(); - m_gridRight->GetGridRowLabelWindow()->Update(); - - //support for custom short-cuts (overwriting wxWidgets functionality!) - execGridCommands(event, this); //event.Skip is handled here! -} - - -//workaround: ensure that all grids are properly aligned: add some extra window space to grids that have no horizontal scrollbar -void CustomGrid::adjustGridHeights(wxEvent& event) -{ - //m_gridLeft, m_gridRight, m_gridMiddle not NULL because called after initSettings() - - int y1 = 0; - int y2 = 0; - int y3 = 0; - int dummy = 0; - - m_gridLeft ->GetViewStart(&dummy, &y1); - m_gridRight ->GetViewStart(&dummy, &y2); - m_gridMiddle->GetViewStart(&dummy, &y3); - - if (y1 != y2 || y2 != y3) - { - int yMax = std::max(y1, std::max(y2, y3)); - - if (m_gridLeft->isLeadGrid()) //do not handle case (y1 == yMax) here!!! Avoid back coupling! - m_gridLeft->SetMargins(0, 0); - else if (y1 < yMax) - m_gridLeft->SetMargins(0, 30); - - if (m_gridRight->isLeadGrid()) - m_gridRight->SetMargins(0, 0); - else if (y2 < yMax) - m_gridRight->SetMargins(0, 30); - - if (m_gridMiddle->isLeadGrid()) - m_gridMiddle->SetMargins(0, 0); - else if (y3 < yMax) - m_gridMiddle->SetMargins(0, 30); - - m_gridLeft ->ForceRefresh(); - m_gridRight ->ForceRefresh(); - m_gridMiddle->ForceRefresh(); - } -} - - -void CustomGrid::updateGridSizes() -{ - if (getGridDataTable()) - getGridDataTable()->updateGridSizes(); -} - - -void CustomGridRim::updateGridSizes() -{ - CustomGrid::updateGridSizes(); - - //set row label size - - //SetRowLabelSize(wxGRID_AUTOSIZE); -> we can do better - wxClientDC dc(GetGridRowLabelWindow()); - dc.SetFont(GetLabelFont()); - - wxArrayString lines; - lines.push_back(GetRowLabelValue(GetNumberRows())); - - long width = 0; - long dummy = 0; - GetTextBoxSize(dc, lines, &width, &dummy); - - width += 8; - SetRowLabelSize(width); -} - - -void CustomGrid::setSortMarker(SortMarker marker) -{ - m_marker = marker; -} - - -void CustomGrid::DrawColLabel(wxDC& dc, int col) -{ - wxGrid::DrawColLabel(dc, col); - - if (col == m_marker.first) - { - if (m_marker.second == ASCENDING) - dc.DrawBitmap(GlobalResources::getImage(wxT("smallUp")), GetColRight(col) - 16 - 2, 2, true); //respect 2-pixel border - else - dc.DrawBitmap(GlobalResources::getImage(wxT("smallDown")), GetColRight(col) - 16 - 2, 2, true); //respect 2-pixel border - } -} - - -std::pair<int, int> CustomGrid::mousePosToCell(wxPoint pos) -{ - int x = -1; - int y = -1; - CalcUnscrolledPosition(pos.x, pos.y, &x, &y); - - std::pair<int, int> output(-1, -1); - if (x >= 0 && y >= 0) - { - output.first = YToRow(y); - output.second = XToCol(x); - } - return output; -} - - -std::set<size_t> CustomGrid::getAllSelectedRows() const -{ - std::set<size_t> output; - - const wxArrayInt selectedRows = this->GetSelectedRows(); - if (!selectedRows.IsEmpty()) - { - for (size_t i = 0; i < selectedRows.GetCount(); ++i) - output.insert(selectedRows[i]); - } - - if (!this->GetSelectedCols().IsEmpty()) //if a column is selected this is means all rows are marked for deletion - { - for (int k = 0; k < const_cast<CustomGrid*>(this)->GetNumberRows(); ++k) //messy wxGrid implementation... - output.insert(k); - } - - const wxGridCellCoordsArray singlySelected = this->GetSelectedCells(); - if (!singlySelected.IsEmpty()) - { - for (size_t k = 0; k < singlySelected.GetCount(); ++k) - output.insert(singlySelected[k].GetRow()); - } - - const wxGridCellCoordsArray tmpArrayTop = this->GetSelectionBlockTopLeft(); - if (!tmpArrayTop.IsEmpty()) - { - wxGridCellCoordsArray tmpArrayBottom = this->GetSelectionBlockBottomRight(); - - size_t arrayCount = tmpArrayTop.GetCount(); - - if (arrayCount == tmpArrayBottom.GetCount()) - { - for (size_t i = 0; i < arrayCount; ++i) - { - const int rowTop = tmpArrayTop[i].GetRow(); - const int rowBottom = tmpArrayBottom[i].GetRow(); - - for (int k = rowTop; k <= rowBottom; ++k) - output.insert(k); - } - } - } - - //some exception: also add current cursor row to selection if there are no others... hopefully improving usability - if (output.empty() && this->isLeadGrid()) - output.insert(const_cast<CustomGrid*>(this)->GetCursorRow()); //messy wxGrid implementation... - - return output; -} - - -//############################################################################################ -//CustomGrid specializations - -class GridCellRenderer : public wxGridCellStringRenderer -{ -public: - GridCellRenderer(CustomGridRim::FailedIconLoad& failedLoads, - const CustomGridTableRim* gridDataTable, - const std::shared_ptr<zen::IconBuffer>& iconBuffer) : - failedLoads_(failedLoads), - m_gridDataTable(gridDataTable), - iconBuffer_(iconBuffer) {} - - - virtual void Draw(wxGrid& grid, - wxGridCellAttr& attr, - wxDC& dc, - const wxRect& rect, //unscrolled rect - int row, int col, - bool isSelected) - { - //############## show windows explorer file icons ###################### - - if (iconBuffer_.get() && - m_gridDataTable->getTypeAtPos(col) == xmlAccess::FILENAME) - { - const int iconSize = iconBuffer_->getSize(); - if (rect.GetWidth() >= iconSize) - { - // Partitioning: - // ____________________________ - // | 2 pix border | icon | rest | - // ---------------------------- - { - //clear area where icon will be placed (including border) - wxRect rectShrinked(rect); - rectShrinked.SetWidth(LEFT_BORDER + iconSize); //add 2 pixel border - wxGridCellRenderer::Draw(grid, attr, dc, rectShrinked, row, col, isSelected); - } - - { - //draw rest - wxRect rest(rect); //unscrolled - rest.x += LEFT_BORDER + iconSize; - rest.width -= LEFT_BORDER + iconSize; - wxGridCellStringRenderer::Draw(grid, attr, dc, rest, row, col, isSelected); - } - - wxRect rectIcon(rect); - rectIcon.SetWidth(iconSize); //set to icon area only - rectIcon.x += LEFT_BORDER; // - - //try to draw icon - //retrieve grid data - const Zstring fileName = m_gridDataTable->getIconFile(row); - if (!fileName.empty()) - { - wxIcon icon; - - //first check if it is a directory icon: - if (fileName == Zstr("folder")) - icon = iconBuffer_->genericDirIcon(); - else //retrieve file icon - { - if (!iconBuffer_->requestFileIcon(fileName, &icon)) //returns false if icon is not in buffer - { - icon = iconBuffer_->genericFileIcon(); //better than nothing - - failedLoads_.insert(row); //save status of failed icon load -> used for async. icon loading - //falsify only! we want to avoid writing incorrect success values when only partially updating the DC, e.g. when scrolling, - //see repaint behavior of ::ScrollWindow() function! - } - } - - if (icon.IsOk()) - { - int posX = rectIcon.GetX(); - int posY = rectIcon.GetY(); - //center icon if it is too small - if (rectIcon.GetWidth() > icon.GetWidth()) - posX += (rectIcon.GetWidth() - icon.GetWidth()) / 2; - if (rectIcon.GetHeight() > icon.GetHeight()) - posY += (rectIcon.GetHeight() - icon.GetHeight()) / 2; - - dc.DrawIcon(icon, posX, posY); - } - } - return; - } - } - - //default - wxGridCellStringRenderer::Draw(grid, attr, dc, rect, row, col, isSelected); - } - - - virtual wxSize GetBestSize(wxGrid& grid, //adapt reported width if file icons are shown - wxGridCellAttr& attr, - wxDC& dc, - int row, int col) - { - if (iconBuffer_.get() && //evaluate at compile time - m_gridDataTable->getTypeAtPos(col) == xmlAccess::FILENAME) - { - wxSize rv = wxGridCellStringRenderer::GetBestSize(grid, attr, dc, row, col); - rv.SetWidth(rv.GetWidth() + LEFT_BORDER + iconBuffer_->getSize()); - return rv; - } - - //default - return wxGridCellStringRenderer::GetBestSize(grid, attr, dc, row, col); - } - - -private: - CustomGridRim::FailedIconLoad& failedLoads_; - const CustomGridTableRim* const m_gridDataTable; - std::shared_ptr<zen::IconBuffer> iconBuffer_; - - static const int LEFT_BORDER = 2; -}; - -//---------------------------------------------------------------------------------------- - -CustomGridRim::CustomGridRim(wxWindow* parent, - wxWindowID id, - const wxPoint& pos, - const wxSize& size, - long style, - const wxString& name) : - CustomGrid(parent, id, pos, size, style, name), otherGrid(NULL) -{ - Connect(wxEVT_GRID_COL_SIZE, wxGridSizeEventHandler(CustomGridRim::OnResizeColumn), NULL, this); //row-based tooltip -} - - -void CustomGridRim::setOtherGrid(CustomGridRim* other) //call during initialization! -{ - otherGrid = other; -} - - -void CustomGridRim::OnResizeColumn(wxGridSizeEvent& event) -{ - //Resize columns on both sides in parallel - const int thisCol = event.GetRowOrCol(); - - if (!otherGrid || thisCol < 0 || thisCol >= GetNumberCols()) return; - - const xmlAccess::ColumnTypes thisColType = getTypeAtPos(thisCol); - - for (int i = 0; i < otherGrid->GetNumberCols(); ++i) - if (otherGrid->getTypeAtPos(i) == thisColType) - { - otherGrid->SetColSize(i, GetColSize(thisCol)); - otherGrid->ForceRefresh(); - break; - } -} - - -//this method is called when grid view changes: useful for parallel updating of multiple grids -void CustomGridRim::alignOtherGrids(CustomGrid* gridLeft, CustomGrid* gridMiddle, CustomGrid* gridRight) -{ - if (!otherGrid) return; - - int x = 0; - int y = 0; - GetViewStart(&x, &y); - gridMiddle->Scroll(-1, y); - otherGrid->Scroll(x, y); -} - - -template <SelectedSide side> -void CustomGridRim::setTooltip(const wxMouseEvent& event) -{ - const int hoveredRow = mousePosToCell(event.GetPosition()).first; - - wxString toolTip; - if (hoveredRow >= 0 && getGridDataTable() != NULL) - { - const FileSystemObject* const fsObj = getGridDataTable()->getRawData(hoveredRow); - if (fsObj && !fsObj->isEmpty<side>()) - { - struct AssembleTooltip : public FSObjectVisitor - { - AssembleTooltip(wxString& tipMsg) : tipMsg_(tipMsg) {} - - virtual void visit(const FileMapping& fileObj) - { - tipMsg_ = toWx(fileObj.getRelativeName<side>()) + "\n" + - _("Size") + ": " + zen::formatFilesizeToShortString(fileObj.getFileSize<side>()) + "\n" + - _("Date") + ": " + zen::utcTimeToLocalString(fileObj.getLastWriteTime<side>()); - } - - virtual void visit(const SymLinkMapping& linkObj) - { - tipMsg_ = toWx(linkObj.getRelativeName<side>()) + "\n" + - _("Date") + ": " + zen::utcTimeToLocalString(linkObj.getLastWriteTime<side>()); - } - - virtual void visit(const DirMapping& dirObj) - { - tipMsg_ = toWx(dirObj.getRelativeName<side>()); - } - - wxString& tipMsg_; - } assembler(toolTip); - fsObj->accept(assembler); - } - } - - - wxToolTip* tt = GetGridWindow()->GetToolTip(); - - const wxString currentTip = tt ? tt->GetTip() : wxString(); - if (toolTip != currentTip) - { - if (toolTip.IsEmpty()) - GetGridWindow()->SetToolTip(NULL); //wxGTK doesn't allow wxToolTip with empty text! - else - { - //wxWidgets bug: tooltip multiline property is defined by first tooltip text containing newlines or not (same is true for maximum width) - if (!tt) - GetGridWindow()->SetToolTip(new wxToolTip(wxT("a b\n\ - a b"))); //ugly, but is working (on Windows) - tt = GetGridWindow()->GetToolTip(); //should be bound by now - if (tt) - tt->SetTip(toolTip); - } - } -} - - -xmlAccess::ColumnAttributes CustomGridRim::getDefaultColumnAttributes() -{ - xmlAccess::ColumnAttributes defaultColumnSettings; - - xmlAccess::ColumnAttrib newEntry; - newEntry.type = xmlAccess::FULL_PATH; - newEntry.visible = false; - newEntry.position = 0; - newEntry.width = 150; - defaultColumnSettings.push_back(newEntry); - - newEntry.type = xmlAccess::DIRECTORY; - newEntry.position = 1; - newEntry.width = 140; - defaultColumnSettings.push_back(newEntry); - - newEntry.type = xmlAccess::REL_PATH; - newEntry.visible = true; - newEntry.position = 2; - newEntry.width = 118; - defaultColumnSettings.push_back(newEntry); - - newEntry.type = xmlAccess::FILENAME; - newEntry.position = 3; - newEntry.width = 138; - defaultColumnSettings.push_back(newEntry); - - newEntry.type = xmlAccess::SIZE; - newEntry.position = 4; - newEntry.width = 70; - defaultColumnSettings.push_back(newEntry); - - newEntry.type = xmlAccess::DATE; - newEntry.position = 5; - newEntry.width = 113; - defaultColumnSettings.push_back(newEntry); - - newEntry.type = xmlAccess::EXTENSION; - newEntry.visible = false; - newEntry.position = 6; - newEntry.width = 60; - defaultColumnSettings.push_back(newEntry); - - return defaultColumnSettings; -} - - -xmlAccess::ColumnAttributes CustomGridRim::getColumnAttributes() -{ - std::sort(columnSettings.begin(), columnSettings.end(), xmlAccess::sortByPositionAndVisibility); - - xmlAccess::ColumnAttributes output; - xmlAccess::ColumnAttrib newEntry; - for (size_t i = 0; i < columnSettings.size(); ++i) - { - newEntry = columnSettings[i]; - if (newEntry.visible) - newEntry.width = GetColSize(static_cast<int>(i)); //hidden columns are sorted to the end of vector! - output.push_back(newEntry); - } - - return output; -} - - -void CustomGridRim::setColumnAttributes(const xmlAccess::ColumnAttributes& attr) -{ - //remove special alignment for column "size" - if (GetLayoutDirection() != wxLayout_RightToLeft) //don't change for RTL languages - for (int i = 0; i < GetNumberCols(); ++i) - if (getTypeAtPos(i) == xmlAccess::SIZE) - { - wxGridCellAttr* cellAttributes = GetOrCreateCellAttr(0, i); - cellAttributes->SetAlignment(wxALIGN_LEFT, wxALIGN_CENTRE); - SetColAttr(i, cellAttributes); - break; - } - //---------------------------------------------------------------------------------- - - columnSettings.clear(); - if (attr.size() == 0) - { - //default settings: - columnSettings = getDefaultColumnAttributes(); - } - else - { - for (size_t i = 0; i < xmlAccess::COLUMN_TYPE_COUNT; ++i) - { - xmlAccess::ColumnAttrib newEntry; - - if (i < attr.size()) - newEntry = attr[i]; - else //fix corrupted data: - { - newEntry.type = static_cast<xmlAccess::ColumnTypes>(xmlAccess::COLUMN_TYPE_COUNT); //sort additional rows to the end - newEntry.visible = false; - newEntry.position = i; - newEntry.width = 100; - } - columnSettings.push_back(newEntry); - } - - std::sort(columnSettings.begin(), columnSettings.end(), xmlAccess::sortByType); - for (size_t i = 0; i < xmlAccess::COLUMN_TYPE_COUNT; ++i) //just be sure that each type exists only once - columnSettings[i].type = static_cast<xmlAccess::ColumnTypes>(i); - - std::sort(columnSettings.begin(), columnSettings.end(), xmlAccess::sortByPositionOnly); - for (size_t i = 0; i < xmlAccess::COLUMN_TYPE_COUNT; ++i) //just be sure that positions are numbered correctly - columnSettings[i].position = i; - } - - std::sort(columnSettings.begin(), columnSettings.end(), xmlAccess::sortByPositionAndVisibility); - std::vector<xmlAccess::ColumnTypes> newPositions; - for (size_t i = 0; i < columnSettings.size() && columnSettings[i].visible; ++i) //hidden columns are sorted to the end of vector! - newPositions.push_back(columnSettings[i].type); - - //set column positions - if (getGridDataTableRim()) - getGridDataTableRim()->setupColumns(newPositions); - - //set column width (set them after setupColumns!) - for (size_t i = 0; i < newPositions.size(); ++i) - SetColSize(static_cast<int>(i), columnSettings[i].width); - - //-------------------------------------------------------------------------------------------------------- - //set special alignment for column "size" - if (GetLayoutDirection() != wxLayout_RightToLeft) //don't change for RTL languages - for (int i = 0; i < GetNumberCols(); ++i) - if (getTypeAtPos(i) == xmlAccess::SIZE) - { - wxGridCellAttr* cellAttributes = GetOrCreateCellAttr(0, i); - cellAttributes->SetAlignment(wxALIGN_RIGHT, wxALIGN_CENTRE); - SetColAttr(i, cellAttributes); //make filesize right justified on grids - break; - } - - ClearSelection(); - ForceRefresh(); -} - - -xmlAccess::ColumnTypes CustomGridRim::getTypeAtPos(size_t pos) const -{ - if (getGridDataTableRim()) - return getGridDataTableRim()->getTypeAtPos(pos); - else - return xmlAccess::DIRECTORY; -} - - -wxString CustomGridRim::getTypeName(xmlAccess::ColumnTypes colType) -{ - switch (colType) - { - case xmlAccess::FULL_PATH: - return _("Full path"); - case xmlAccess::FILENAME: - return _("Filename"); - case xmlAccess::REL_PATH: - return _("Relative path"); - case xmlAccess::DIRECTORY: - return _("Directory"); - case xmlAccess::SIZE: - return _("Size"); - case xmlAccess::DATE: - return _("Date"); - case xmlAccess::EXTENSION: - return _("Extension"); - } - - return wxEmptyString; //dummy -} - - -void CustomGridRim::autoSizeColumns() //performance optimized column resizer (analog to wxGrid::AutoSizeColumns() -{ - for (int col = 0; col < GetNumberCols(); ++col) - { - if (col < 0) - return; - - int rowMax = -1; - size_t lenMax = 0; - for (int row = 0; row < GetNumberRows(); ++row) - if (GetCellValue(row, col).size() > lenMax) - { - lenMax = GetCellValue(row, col).size(); - rowMax = row; - } - - wxCoord extentMax = 0; - - //calculate width of (most likely) widest cell - wxClientDC dc(GetGridWindow()); - if (rowMax > -1) - { - wxGridCellAttr* attr = GetCellAttr(rowMax, col); - if (attr) - { - wxGridCellRenderer* renderer = attr->GetRenderer(this, rowMax, col); - if (renderer) - { - const wxSize size = renderer->GetBestSize(*this, *attr, dc, rowMax, col); - extentMax = std::max(extentMax, size.x); - renderer->DecRef(); - } - attr->DecRef(); - } - } - - //consider column label - dc.SetFont(GetLabelFont()); - wxCoord w = 0; - wxCoord h = 0; - dc.GetMultiLineTextExtent(GetColLabelValue(col), &w, &h ); - if (GetColLabelTextOrientation() == wxVERTICAL) - w = h; - extentMax = std::max(extentMax, w); - - extentMax += 15; //leave some space around text - - SetColSize(col, extentMax); - - } - Refresh(); -} - - -void CustomGridRim::enableFileIcons(const std::shared_ptr<IconBuffer>& iconBuffer) -{ - iconBuffer_ = iconBuffer; - SetDefaultRenderer(new GridCellRenderer(failedLoads, getGridDataTableRim(), iconBuffer)); //SetDefaultRenderer takes ownership! -} - - -std::pair<CustomGridRim::RowBegin, CustomGridRim::RowEnd> CustomGridRim::getVisibleRows() -{ - int dummy = -1; - int height = -1; - GetGridWindow()->GetClientSize(&dummy, &height); - - if (height >= 0) - { - const int rowTop = mousePosToCell(wxPoint(0, 0)).first; - int rowEnd = mousePosToCell(wxPoint(0, height)).first; - if (rowEnd == -1) //when scrolling to the very end, there are a few border pixels that do not belong to any row - rowEnd = GetNumberRows(); - else - ++rowEnd; - - if (0 <= rowTop && rowTop <= rowEnd) - return std::make_pair(rowTop, rowEnd); //"top" means here top of the screen => smaller value - } - return std::make_pair(0, 0); -} - - -inline -CustomGridTableRim* CustomGridRim::getGridDataTableRim() const -{ - return dynamic_cast<CustomGridTableRim*>(getGridDataTable()); //I'm tempted to use a static cast here... -} - - -void CustomGridRim::getIconsToBeLoaded(std::vector<Zstring>& newLoad) //loads all (not yet) drawn icons -{ - //don't check too often! give worker thread some time to fetch data - - newLoad.clear(); - - if (iconBuffer_.get()) - { - const CustomGridTableRim* gridDataTable = getGridDataTableRim(); - if (!gridDataTable) return; - - const int totalCols = const_cast<CustomGridTableRim*>(gridDataTable)->GetNumberCols(); - const int totalRows = const_cast<CustomGridTableRim*>(gridDataTable)->GetNumberRows(); - - //determine column - const int colFilename = [&]() -> int - { - for (int k = 0; k < totalCols; ++k) - if (gridDataTable->getTypeAtPos(k) == xmlAccess::FILENAME) - return k; - return -1; - }(); - if (colFilename < 0) - return; - - const auto rowsOnScreen = getVisibleRows(); - - //loop over all visible rows - const int firstRow = static_cast<int>(rowsOnScreen.first); - const int rowNo = std::min(static_cast<int>(rowsOnScreen.second), totalRows) - firstRow; - - for (int i = 0; i < rowNo; ++i) - { - //alternate when adding rows: first, last, first + 1, last - 1 ... -> Icon buffer will then load reversely, i.e. from inside out - const int currentRow = firstRow + (i % 2 == 0 ? - i / 2 : - rowNo - 1 - (i - 1) / 2); - - if (failedLoads.find(currentRow) != failedLoads.end()) //find failed attempts to load icon - { - const Zstring fileName = gridDataTable->getIconFile(currentRow); - if (!fileName.empty()) - { - //test if they are already loaded in buffer: - if (iconBuffer_->requestFileIcon(fileName)) - { - //exists in buffer: refresh Row - RefreshCell(currentRow, colFilename); //do a *full* refresh for *every* failed load to update partial DC updates while scrolling - failedLoads.erase(currentRow); // - } - else //not yet in buffer: mark for async. loading - { - newLoad.push_back(fileName); - } - } - } - } - } -} - -//---------------------------------------------------------------------------------------- - - -//update file icons periodically: use SINGLE instance to coordinate left and right grid at once -IconUpdater::IconUpdater(CustomGridLeft* leftGrid, CustomGridRight* rightGrid) : - m_leftGrid(leftGrid), - m_rightGrid(rightGrid), - m_timer(new wxTimer) //connect timer event for async. icon loading -{ - m_timer->Connect(wxEVT_TIMER, wxEventHandler(IconUpdater::loadIconsAsynchronously), NULL, this); - m_timer->Start(50); //timer interval in ms -} - - -IconUpdater::~IconUpdater() {} - - -void IconUpdater::loadIconsAsynchronously(wxEvent& event) //loads all (not yet) drawn icons -{ - std::vector<Zstring> iconsLeft; - m_leftGrid->getIconsToBeLoaded(iconsLeft); - - std::vector<Zstring> newLoad; - m_rightGrid->getIconsToBeLoaded(newLoad); - - //merge vectors - newLoad.insert(newLoad.end(), iconsLeft.begin(), iconsLeft.end()); - - if (m_leftGrid->iconBuffer_.get()) - m_leftGrid->iconBuffer_->setWorkload(newLoad); - - //event.Skip(); -} - -//---------------------------------------------------------------------------------------- - - -CustomGridLeft::CustomGridLeft(wxWindow* parent, - wxWindowID id, - const wxPoint& pos, - const wxSize& size, - long style, - const wxString& name) : - CustomGridRim(parent, id, pos, size, style, name) -{ - GetGridWindow()->Connect(wxEVT_MOTION, wxMouseEventHandler(CustomGridLeft::OnMouseMovement), NULL, this); //row-based tooltip -} - - -bool CustomGridLeft::CreateGrid(int numRows, int numCols, wxGrid::wxGridSelectionModes selmode) -{ - //use custom wxGridTableBase class for management of large sets of formatted data. - //This is done in CreateGrid instead of SetTable method since source code is generated and wxFormbuilder invokes CreatedGrid by default. - SetTable(new CustomGridTableLeft, true, wxGrid::wxGridSelectRows); //give ownership to wxGrid: gridDataTable is deleted automatically in wxGrid destructor - return true; -} - - -void CustomGridLeft::initSettings(CustomGridLeft* gridLeft, //create connection with zen::GridView - CustomGridMiddle* gridMiddle, - CustomGridRight* gridRight, - const zen::GridView* gridDataView) -{ - //set underlying grid data - assert(getGridDataTable()); - getGridDataTable()->setGridDataTable(gridDataView); - - CustomGridRim::setOtherGrid(gridRight); - - CustomGridRim::initSettings(gridLeft, gridMiddle, gridRight, gridDataView); -} - - -void CustomGridLeft::OnMouseMovement(wxMouseEvent& event) -{ - CustomGridRim::setTooltip<LEFT_SIDE>(event); - event.Skip(); -} - - -CustomGridTable* CustomGridLeft::getGridDataTable() const -{ - return static_cast<CustomGridTable*>(GetTable()); //one of the few cases where no dynamic_cast is required! -} - - -//---------------------------------------------------------------------------------------- -CustomGridRight::CustomGridRight(wxWindow* parent, - wxWindowID id, - const wxPoint& pos, - const wxSize& size, - long style, - const wxString& name) : - CustomGridRim(parent, id, pos, size, style, name) -{ - GetGridWindow()->Connect(wxEVT_MOTION, wxMouseEventHandler(CustomGridRight::OnMouseMovement), NULL, this); //row-based tooltip -} - - -bool CustomGridRight::CreateGrid(int numRows, int numCols, wxGrid::wxGridSelectionModes selmode) -{ - SetTable(new CustomGridTableRight, true, wxGrid::wxGridSelectRows); //give ownership to wxGrid: gridDataTable is deleted automatically in wxGrid destructor - return true; -} - - -void CustomGridRight::initSettings(CustomGridLeft* gridLeft, //create connection with zen::GridView - CustomGridMiddle* gridMiddle, - CustomGridRight* gridRight, - const zen::GridView* gridDataView) -{ - //set underlying grid data - assert(getGridDataTable()); - getGridDataTable()->setGridDataTable(gridDataView); - - CustomGridRim::setOtherGrid(gridLeft); - - CustomGridRim::initSettings(gridLeft, gridMiddle, gridRight, gridDataView); -} - - -void CustomGridRight::OnMouseMovement(wxMouseEvent& event) -{ - CustomGridRim::setTooltip<RIGHT_SIDE>(event); - event.Skip(); -} - - -CustomGridTable* CustomGridRight::getGridDataTable() const -{ - return static_cast<CustomGridTable*>(GetTable()); //one of the few cases where no dynamic_cast is required! -} - - -//---------------------------------------------------------------------------------------- -class GridCellRendererMiddle : public wxGridCellStringRenderer -{ -public: - GridCellRendererMiddle(const CustomGridMiddle& middleGrid) : m_gridMiddle(middleGrid) {}; - - virtual void Draw(wxGrid& grid, - wxGridCellAttr& attr, - wxDC& dc, - const wxRect& rect, - int row, int col, - bool isSelected); - -private: - const CustomGridMiddle& m_gridMiddle; -}; - - -//define new event types -const wxEventType FFS_CHECK_ROWS_EVENT = wxNewEventType(); //attention! do NOT place in header to keep (generated) id unique! -const wxEventType FFS_SYNC_DIRECTION_EVENT = wxNewEventType(); - -const int CHECK_BOX_IMAGE = 11; //width of checkbox image -const int CHECK_BOX_WIDTH = CHECK_BOX_IMAGE + 3; //width of first block - -// cell: -// ---------------------------------- -// | checkbox | left | middle | right| -// ---------------------------------- - - -CustomGridMiddle::CustomGridMiddle(wxWindow* parent, - wxWindowID id, - const wxPoint& pos, - const wxSize& size, - long style, - const wxString& name) : - CustomGrid(parent, id, pos, size, style, name), - selectionRowBegin(-1), - selectionPos(BLOCKPOS_CHECK_BOX), - highlightedRow(-1), - highlightedPos(BLOCKPOS_CHECK_BOX), - toolTip(new CustomTooltip) -{ - SetLayoutDirection(wxLayout_LeftToRight); // - GetGridWindow()->SetLayoutDirection(wxLayout_LeftToRight); //avoid mirroring this dialog in RTL languages like Hebrew or Arabic - GetGridColLabelWindow()->SetLayoutDirection(wxLayout_LeftToRight); // - - //connect events for dynamic selection of sync direction - GetGridWindow()->Connect(wxEVT_MOTION, wxMouseEventHandler(CustomGridMiddle::OnMouseMovement), NULL, this); - GetGridWindow()->Connect(wxEVT_LEAVE_WINDOW, wxMouseEventHandler(CustomGridMiddle::OnLeaveWindow), NULL, this); - GetGridWindow()->Connect(wxEVT_LEFT_UP, wxMouseEventHandler(CustomGridMiddle::OnLeftMouseUp), NULL, this); - GetGridWindow()->Connect(wxEVT_LEFT_DOWN, wxMouseEventHandler(CustomGridMiddle::OnLeftMouseDown), NULL, this); -} - - -CustomGridMiddle::~CustomGridMiddle() {} - - -bool CustomGridMiddle::CreateGrid(int numRows, int numCols, wxGrid::wxGridSelectionModes selmode) -{ - SetTable(new CustomGridTableMiddle, true, wxGrid::wxGridSelectRows); //give ownership to wxGrid: gridDataTable is deleted automatically in wxGrid destructor - - //display checkboxes (representing bool values) if row is enabled for synchronization - SetDefaultRenderer(new GridCellRendererMiddle(*this)); //SetDefaultRenderer takes ownership! - - return true; -} - - -void CustomGridMiddle::initSettings(CustomGridLeft* gridLeft, //create connection with zen::GridView - CustomGridMiddle* gridMiddle, - CustomGridRight* gridRight, - const zen::GridView* gridDataView) -{ - //set underlying grid data - assert(getGridDataTable()); - getGridDataTable()->setGridDataTable(gridDataView); - -#ifdef FFS_LINUX //get rid of scrollbars; Linux: change policy for GtkScrolledWindow - GtkWidget* gridWidget = wxWindow::m_widget; - GtkScrolledWindow* scrolledWindow = GTK_SCROLLED_WINDOW(gridWidget); - gtk_scrolled_window_set_policy(scrolledWindow, GTK_POLICY_NEVER, GTK_POLICY_NEVER); -#endif - - CustomGrid::initSettings(gridLeft, gridMiddle, gridRight, gridDataView); -} - - -CustomGridTable* CustomGridMiddle::getGridDataTable() const -{ - return static_cast<CustomGridTable*>(GetTable()); //one of the few cases where no dynamic_cast is required! -} - - -inline -CustomGridTableMiddle* CustomGridMiddle::getGridDataTableMiddle() const -{ - return static_cast<CustomGridTableMiddle*>(getGridDataTable()); -} - - -#ifdef FFS_WIN //get rid of scrollbars; Windows: overwrite virtual method -void CustomGridMiddle::SetScrollbar(int orientation, int position, int thumbSize, int range, bool refresh) -{ - wxWindow::SetScrollbar(orientation, 0, 0, 0, refresh); -} -#endif - - -void CustomGridMiddle::OnMouseMovement(wxMouseEvent& event) -{ - const int rowOld = highlightedRow; - const BlockPosition posOld = highlightedPos; - - - if (selectionRowBegin == -1) //change highlightning only if currently not dragging mouse - { - highlightedRow = mousePosToRow(event.GetPosition(), &highlightedPos); - - if (rowOld != highlightedRow) - { - RefreshCell(highlightedRow, 0); - RefreshCell(rowOld, 0); - } - else if (posOld != highlightedPos) - RefreshCell(highlightedRow, 0); - - //handle tooltip - showToolTip(highlightedRow, GetGridWindow()->ClientToScreen(event.GetPosition())); - } - - event.Skip(); -} - - -void CustomGridMiddle::OnLeaveWindow(wxMouseEvent& event) -{ - highlightedRow = -1; - highlightedPos = BLOCKPOS_CHECK_BOX; - Refresh(); - - //handle tooltip - toolTip->hide(); -} - - -void CustomGridMiddle::showToolTip(int rowNumber, wxPoint pos) -{ - if (!getGridDataTableMiddle()) - return; - - const FileSystemObject* const fsObj = getGridDataTableMiddle()->getRawData(rowNumber); - if (fsObj == NULL) //if invalid row... - { - toolTip->hide(); - return; - } - - if (getGridDataTableMiddle()->syncPreviewIsActive()) //synchronization preview - { - const SyncOperation syncOp = fsObj->getSyncOperation(); - switch (syncOp) - { - case SO_CREATE_NEW_LEFT: - toolTip->show(getDescription(syncOp), pos, &GlobalResources::getImage(wxT("createLeft"))); - break; - case SO_CREATE_NEW_RIGHT: - toolTip->show(getDescription(syncOp), pos, &GlobalResources::getImage(wxT("createRight"))); - break; - case SO_DELETE_LEFT: - toolTip->show(getDescription(syncOp), pos, &GlobalResources::getImage(wxT("deleteLeft"))); - break; - case SO_DELETE_RIGHT: - toolTip->show(getDescription(syncOp), pos, &GlobalResources::getImage(wxT("deleteRight"))); - break; - case SO_OVERWRITE_LEFT: - case SO_COPY_METADATA_TO_LEFT: - toolTip->show(getDescription(syncOp), pos, &GlobalResources::getImage(wxT("updateLeft"))); - break; - case SO_OVERWRITE_RIGHT: - case SO_COPY_METADATA_TO_RIGHT: - toolTip->show(getDescription(syncOp), pos, &GlobalResources::getImage(wxT("updateRight"))); - break; - case SO_DO_NOTHING: - toolTip->show(getDescription(syncOp), pos, &GlobalResources::getImage(wxT("none"))); - break; - case SO_EQUAL: - toolTip->show(getDescription(syncOp), pos, &GlobalResources::getImage(wxT("equal"))); - break; - case SO_UNRESOLVED_CONFLICT: - toolTip->show(fsObj->getSyncOpConflict(), pos, &GlobalResources::getImage(wxT("conflict"))); - break; - }; - } - else - { - const CompareFilesResult cmpRes = fsObj->getCategory(); - switch (cmpRes) - { - case FILE_LEFT_SIDE_ONLY: - toolTip->show(getDescription(cmpRes), pos, &GlobalResources::getImage(wxT("leftOnly"))); - break; - case FILE_RIGHT_SIDE_ONLY: - toolTip->show(getDescription(cmpRes), pos, &GlobalResources::getImage(wxT("rightOnly"))); - break; - case FILE_LEFT_NEWER: - toolTip->show(getDescription(cmpRes), pos, &GlobalResources::getImage(wxT("leftNewer"))); - break; - case FILE_RIGHT_NEWER: - toolTip->show(getDescription(cmpRes), pos, &GlobalResources::getImage(wxT("rightNewer"))); - break; - case FILE_DIFFERENT: - toolTip->show(getDescription(cmpRes), pos, &GlobalResources::getImage(wxT("different"))); - break; - case FILE_EQUAL: - toolTip->show(getDescription(cmpRes), pos, &GlobalResources::getImage(wxT("equal"))); - break; - case FILE_DIFFERENT_METADATA: - toolTip->show(getDescription(cmpRes), pos, &GlobalResources::getImage(wxT("conflict"))); - break; - case FILE_CONFLICT: - toolTip->show(fsObj->getCatConflict(), pos, &GlobalResources::getImage(wxT("conflict"))); - break; - } - } -} - - -void CustomGridMiddle::OnLeftMouseDown(wxMouseEvent& event) -{ - selectionRowBegin = mousePosToRow(event.GetPosition(), &selectionPos); - event.Skip(); -} - - -void CustomGridMiddle::OnLeftMouseUp(wxMouseEvent& event) -{ - //int selRowEnd = mousePosToCell(event.GetPosition()).first; - //-> use visibly marked rows instead! with wxWidgets 2.8.12 there is no other way than IsInSelection() - int selRowEnd = -1; - if (0 <= selectionRowBegin && selectionRowBegin < GetNumberRows()) - { - for (int i = selectionRowBegin; i < GetNumberRows() && IsInSelection(i, 0); ++i) - selRowEnd = i; - - if (selRowEnd == selectionRowBegin) - for (int i = selectionRowBegin; i >= 0 && IsInSelection(i, 0); --i) - selRowEnd = i; - } - - if (0 <= selectionRowBegin && 0 <= selRowEnd) - { - switch (selectionPos) - { - case BLOCKPOS_CHECK_BOX: - { - //create a custom event - FFSCheckRowsEvent evt(selectionRowBegin, selRowEnd); - AddPendingEvent(evt); - } - break; - case BLOCKPOS_LEFT: - { - //create a custom event - FFSSyncDirectionEvent evt(selectionRowBegin, selRowEnd, SYNC_DIR_LEFT); - AddPendingEvent(evt); - } - break; - case BLOCKPOS_MIDDLE: - { - //create a custom event - FFSSyncDirectionEvent evt(selectionRowBegin, selRowEnd, SYNC_DIR_NONE); - AddPendingEvent(evt); - } - break; - case BLOCKPOS_RIGHT: - { - //create a custom event - FFSSyncDirectionEvent evt(selectionRowBegin, selRowEnd, SYNC_DIR_RIGHT); - AddPendingEvent(evt); - } - break; - } - } - selectionRowBegin = -1; - selectionPos = BLOCKPOS_CHECK_BOX; - - ClearSelection(); - event.Skip(); -} - - -int CustomGridMiddle::mousePosToRow(wxPoint pos, BlockPosition* block) -{ - if (!getGridDataTableMiddle()) - return 0; - - int row = -1; - int x = -1; - int y = -1; - CalcUnscrolledPosition( pos.x, pos.y, &x, &y ); - if (x >= 0 && y >= 0) - { - row = YToRow(y); - - //determine blockposition within cell (optional) - if (block) - { - *block = BLOCKPOS_CHECK_BOX; //default - - if (row >= 0) - { - const FileSystemObject* const fsObj = getGridDataTableMiddle()->getRawData(row); - if (fsObj != NULL && //if valid row... - getGridDataTableMiddle()->syncPreviewIsActive() && - fsObj->getSyncOperation() != SO_EQUAL) //in sync-preview equal files shall be treated as in cmp result, i.e. as full checkbox - { - // cell: - // ---------------------------------- - // | checkbox | left | middle | right| - // ---------------------------------- - - const wxRect rect = CellToRect(row, 0); - if (rect.GetWidth() > CHECK_BOX_WIDTH) - { - const double blockWidth = (rect.GetWidth() - CHECK_BOX_WIDTH) / 3.0; - if (rect.GetX() + CHECK_BOX_WIDTH <= x && x < rect.GetX() + rect.GetWidth()) - { - if (x - (rect.GetX() + CHECK_BOX_WIDTH) < blockWidth) - *block = BLOCKPOS_LEFT; - else if (x - (rect.GetX() + CHECK_BOX_WIDTH) < 2 * blockWidth) - *block = BLOCKPOS_MIDDLE; - else - *block = BLOCKPOS_RIGHT; - } - } - } - } - } - } - return row; -} - - -void CustomGridMiddle::enableSyncPreview(bool value) -{ - assert(getGridDataTableMiddle()); - getGridDataTableMiddle()->enableSyncPreview(value); - - if (value) - GetGridColLabelWindow()->SetToolTip(_("Synchronization Preview")); - else - GetGridColLabelWindow()->SetToolTip(_("Comparison Result")); -} - - -void GridCellRendererMiddle::Draw(wxGrid& grid, - wxGridCellAttr& attr, - wxDC& dc, - const wxRect& rect, - int row, int col, - bool isSelected) -{ - //retrieve grid data - const FileSystemObject* const fsObj = m_gridMiddle.getGridDataTableMiddle() ? m_gridMiddle.getGridDataTableMiddle()->getRawData(row) : NULL; - if (fsObj != NULL) //if valid row... - { - if (rect.GetWidth() > CHECK_BOX_WIDTH) - { - const bool rowIsHighlighted = row == m_gridMiddle.highlightedRow; - - wxRect rectShrinked(rect); - - //clean first block of rect that will receive image of checkbox - rectShrinked.SetWidth(CHECK_BOX_WIDTH); - wxGridCellRenderer::Draw(grid, attr, dc, rectShrinked, row, col, isSelected); - - //print checkbox into first block - rectShrinked.SetX(rect.GetX() + 1); - - //HIGHLIGHTNING (checkbox): - if (rowIsHighlighted && - m_gridMiddle.highlightedPos == CustomGridMiddle::BLOCKPOS_CHECK_BOX) - { - dc.DrawLabel(wxEmptyString, GlobalResources::getImage(fsObj->isActive() ? - wxT("checkboxTrueFocus") : - wxT("checkboxFalseFocus")), rectShrinked, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL); - } - else //default - dc.DrawLabel(wxEmptyString, GlobalResources::getImage(fsObj->isActive() ? - wxT("checkboxTrue") : - wxT("checkboxFalse")), rectShrinked, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL); - - //clean remaining block of rect that will receive image of checkbox/directions - rectShrinked.SetWidth(rect.GetWidth() - CHECK_BOX_WIDTH); - rectShrinked.SetX(rect.GetX() + CHECK_BOX_WIDTH); - wxGridCellRenderer::Draw(grid, attr, dc, rectShrinked, row, col, isSelected); - - //print remaining block - if (m_gridMiddle.getGridDataTableMiddle()->syncPreviewIsActive()) //synchronization preview - { - //print sync direction into second block - - //HIGHLIGHTNING (sync direction): - if (rowIsHighlighted && - m_gridMiddle.highlightedPos != CustomGridMiddle::BLOCKPOS_CHECK_BOX) //don't allow changing direction for "=="-files - switch (m_gridMiddle.highlightedPos) - { - case CustomGridMiddle::BLOCKPOS_CHECK_BOX: - break; - case CustomGridMiddle::BLOCKPOS_LEFT: - dc.DrawLabel(wxEmptyString, getSyncOpImage(fsObj->testSyncOperation(SYNC_DIR_LEFT)), rectShrinked, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL); - break; - case CustomGridMiddle::BLOCKPOS_MIDDLE: - dc.DrawLabel(wxEmptyString, getSyncOpImage(fsObj->testSyncOperation(SYNC_DIR_NONE)), rectShrinked, wxALIGN_CENTER | wxALIGN_CENTER_VERTICAL); - break; - case CustomGridMiddle::BLOCKPOS_RIGHT: - dc.DrawLabel(wxEmptyString, getSyncOpImage(fsObj->testSyncOperation(SYNC_DIR_RIGHT)), rectShrinked, wxALIGN_RIGHT | wxALIGN_CENTER_VERTICAL); - break; - } - else //default - { - const wxBitmap& syncOpIcon = getSyncOpImage(fsObj->getSyncOperation()); - dc.DrawLabel(wxEmptyString, syncOpIcon, rectShrinked, wxALIGN_CENTER | wxALIGN_CENTER_VERTICAL); - } - } - else //comparison results view - { - switch (fsObj->getCategory()) - { - case FILE_LEFT_SIDE_ONLY: - dc.DrawLabel(wxEmptyString, GlobalResources::getImage(wxT("leftOnlySmall")), rectShrinked, wxALIGN_CENTER | wxALIGN_CENTER_VERTICAL); - break; - case FILE_RIGHT_SIDE_ONLY: - dc.DrawLabel(wxEmptyString, GlobalResources::getImage(wxT("rightOnlySmall")), rectShrinked, wxALIGN_CENTER | wxALIGN_CENTER_VERTICAL); - break; - case FILE_LEFT_NEWER: - dc.DrawLabel(wxEmptyString, GlobalResources::getImage(wxT("leftNewerSmall")), rectShrinked, wxALIGN_CENTER | wxALIGN_CENTER_VERTICAL); - break; - case FILE_RIGHT_NEWER: - dc.DrawLabel(wxEmptyString, GlobalResources::getImage(wxT("rightNewerSmall")), rectShrinked, wxALIGN_CENTER | wxALIGN_CENTER_VERTICAL); - break; - case FILE_DIFFERENT: - dc.DrawLabel(wxEmptyString, GlobalResources::getImage(wxT("differentSmall")), rectShrinked, wxALIGN_CENTER | wxALIGN_CENTER_VERTICAL); - break; - case FILE_EQUAL: - dc.DrawLabel(wxEmptyString, GlobalResources::getImage(wxT("equalSmall")), rectShrinked, wxALIGN_CENTER | wxALIGN_CENTER_VERTICAL); - break; - case FILE_CONFLICT: - case FILE_DIFFERENT_METADATA: - dc.DrawLabel(wxEmptyString, GlobalResources::getImage(wxT("conflictSmall")), rectShrinked, wxALIGN_CENTER | wxALIGN_CENTER_VERTICAL); - break; - } - } - - return; - } - } - - //fallback - wxGridCellStringRenderer::Draw(grid, attr, dc, rect, row, col, isSelected); -} - - -//this method is called when grid view changes: useful for parallel updating of multiple grids -void CustomGridMiddle::alignOtherGrids(CustomGrid* gridLeft, CustomGrid* gridMiddle, CustomGrid* gridRight) -{ - int x = 0; - int y = 0; - GetViewStart(&x, &y); - gridLeft->Scroll(-1, y); - gridRight->Scroll(-1, y); -} - - -void CustomGridMiddle::DrawColLabel(wxDC& dc, int col) -{ - CustomGrid::DrawColLabel(dc, col); - - if (!getGridDataTableMiddle()) - return; - - const wxRect rect(GetColLeft(col), 0, GetColWidth(col), GetColLabelSize()); - - if (getGridDataTableMiddle()->syncPreviewIsActive()) - dc.DrawLabel(wxEmptyString, GlobalResources::getImage(wxT("syncViewSmall")), rect, wxALIGN_CENTER_HORIZONTAL | wxALIGN_CENTER_VERTICAL); - else - dc.DrawLabel(wxEmptyString, GlobalResources::getImage(wxT("cmpViewSmall")), rect, wxALIGN_CENTER_HORIZONTAL | wxALIGN_CENTER_VERTICAL); -} - - -const wxBitmap& zen::getSyncOpImage(SyncOperation syncOp) -{ - switch (syncOp) //evaluate comparison result and sync direction - { - case SO_CREATE_NEW_LEFT: - return GlobalResources::getImage(wxT("createLeftSmall")); - case SO_CREATE_NEW_RIGHT: - return GlobalResources::getImage(wxT("createRightSmall")); - case SO_DELETE_LEFT: - return GlobalResources::getImage(wxT("deleteLeftSmall")); - case SO_DELETE_RIGHT: - return GlobalResources::getImage(wxT("deleteRightSmall")); - case SO_OVERWRITE_RIGHT: - case SO_COPY_METADATA_TO_RIGHT: - return GlobalResources::getImage(wxT("updateRightSmall")); - case SO_OVERWRITE_LEFT: - case SO_COPY_METADATA_TO_LEFT: - return GlobalResources::getImage(wxT("updateLeftSmall")); - case SO_DO_NOTHING: - return GlobalResources::getImage(wxT("noneSmall")); - case SO_EQUAL: - return GlobalResources::getImage(wxT("equalSmall")); - case SO_UNRESOLVED_CONFLICT: - return GlobalResources::getImage(wxT("conflictSmall")); - } - - return wxNullBitmap; //dummy -} - diff --git a/library/custom_grid.h b/library/custom_grid.h deleted file mode 100644 index a1bce692..00000000 --- a/library/custom_grid.h +++ /dev/null @@ -1,370 +0,0 @@ -// ************************************************************************** -// * This file is part of the FreeFileSync project. It is distributed under * -// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * -// * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * -// ************************************************************************** - -#ifndef CUSTOMGRID_H_INCLUDED -#define CUSTOMGRID_H_INCLUDED - -#include <vector> -#include <wx/grid.h> -#include "process_xml.h" -#include <memory> -#include <set> -#include "../file_hierarchy.h" -#include "icon_buffer.h" - - -class CustomGridTable; -class CustomGridTableRim; -class CustomGridTableLeft; -class CustomGridTableRight; -class CustomGridTableMiddle; -class GridCellRendererMiddle; -class wxTimer; -class CustomTooltip; -class CustomGridRim; -class CustomGridLeft; -class CustomGridMiddle; -class CustomGridRight; - - -namespace zen -{ -class GridView; - -const wxBitmap& getSyncOpImage(SyncOperation syncOp); -} -//################################################################################## - -/* -class hierarchy: - CustomGrid - /|\ - ____________|____________ - | | - CustomGridRim | - /|\ | - ________|_______ | - | | | -CustomGridLeft CustomGridRight CustomGridMiddle -*/ - -class CustomGrid : public wxGrid -{ -public: - CustomGrid(wxWindow* parent, - wxWindowID id, - const wxPoint& pos = wxDefaultPosition, - const wxSize& size = wxDefaultSize, - long style = wxWANTS_CHARS, - const wxString& name = wxGridNameStr); - - virtual ~CustomGrid() {} - - virtual void initSettings(CustomGridLeft* gridLeft, //create connection with zen::GridView - CustomGridMiddle* gridMiddle, - CustomGridRight* gridRight, - const zen::GridView* gridDataView); - - void release(); //release connection to zen::GridView - - std::set<size_t> getAllSelectedRows() const; - - //set sort direction indicator on UI - typedef int SortColumn; - - //notify wxGrid that underlying table size has changed - virtual void updateGridSizes(); - - enum SortDirection - { - ASCENDING, - DESCENDING - }; - typedef std::pair<SortColumn, SortDirection> SortMarker; - void setSortMarker(SortMarker marker); - - bool isLeadGrid() const; - - void setIconManager(const std::shared_ptr<zen::IconBuffer>& iconBuffer); - -protected: - void RefreshCell(int row, int col); - virtual void DrawColLabel(wxDC& dc, int col); - std::pair<int, int> mousePosToCell(wxPoint pos); //returns (row/column) pair - - virtual CustomGridTable* getGridDataTable() const = 0; - -private: - void onGridAccess(wxEvent& event); - - //this method is called when grid view changes: useful for parallel updating of multiple grids - void OnPaintGrid(wxEvent& event); - - virtual void alignOtherGrids(CustomGrid* gridLeft, CustomGrid* gridMiddle, CustomGrid* gridRight) = 0; - - void adjustGridHeights(wxEvent& event); - virtual void enableFileIcons(const std::shared_ptr<zen::IconBuffer>& iconBuffer) = 0; - - CustomGrid* m_gridLeft; - CustomGrid* m_gridMiddle; - CustomGrid* m_gridRight; - - bool isLeading; //identify grid that has user focus - - SortMarker m_marker; -}; - - -class GridCellRenderer; - - -//----------------------------------------------------------- -class IconUpdater : private wxEvtHandler //update file icons periodically: use SINGLE instance to coordinate left and right grid at once -{ -public: - IconUpdater(CustomGridLeft* leftGrid, CustomGridRight* rightGrid); - ~IconUpdater(); - -private: - void loadIconsAsynchronously(wxEvent& event); //loads all (not yet) drawn icons - - CustomGridRim* m_leftGrid; - CustomGridRim* m_rightGrid; - - std::unique_ptr<wxTimer> m_timer; //user timer event to periodically update icons: better than idle event because also active when scrolling! :) -}; - - -//############## SPECIALIZATIONS ################### -class CustomGridRim : public CustomGrid -{ - friend class IconUpdater; - friend class GridCellRenderer; - -public: - CustomGridRim(wxWindow* parent, - wxWindowID id, - const wxPoint& pos, - const wxSize& size, - long style, - const wxString& name); - - //set visibility, position and width of columns - static xmlAccess::ColumnAttributes getDefaultColumnAttributes(); - xmlAccess::ColumnAttributes getColumnAttributes(); - void setColumnAttributes(const xmlAccess::ColumnAttributes& attr); - - xmlAccess::ColumnTypes getTypeAtPos(size_t pos) const; - static wxString getTypeName(xmlAccess::ColumnTypes colType); - - void autoSizeColumns(); //performance optimized column resizer - - virtual void updateGridSizes(); - -protected: - template <zen::SelectedSide side> - void setTooltip(const wxMouseEvent& event); - - void setOtherGrid(CustomGridRim* other); //call during initialization! - -private: - CustomGridTableRim* getGridDataTableRim() const; - virtual void enableFileIcons(const std::shared_ptr<zen::IconBuffer>& iconBuffer); - - void OnResizeColumn(wxGridSizeEvent& event); - - //this method is called when grid view changes: useful for parallel updating of multiple grids - virtual void alignOtherGrids(CustomGrid* gridLeft, CustomGrid* gridMiddle, CustomGrid* gridRight); - - //asynchronous icon loading - void getIconsToBeLoaded(std::vector<Zstring>& newLoad); //loads all (not yet) drawn icons - - typedef size_t RowBegin; - typedef size_t RowEnd; - std::pair<RowBegin, RowEnd> getVisibleRows(); //return [first, last) number pair - - typedef size_t RowNumber; - typedef std::set<RowNumber> FailedIconLoad; - FailedIconLoad failedLoads; //save status of last icon load when drawing on GUI - - std::shared_ptr<zen::IconBuffer> iconBuffer_; - - xmlAccess::ColumnAttributes columnSettings; //set visibility, position and width of columns - CustomGridRim* otherGrid; //sibling grid on other side -}; - - -class CustomGridLeft : public CustomGridRim -{ -public: - CustomGridLeft(wxWindow* parent, - wxWindowID id, - const wxPoint& pos = wxDefaultPosition, - const wxSize& size = wxDefaultSize, - long style = wxWANTS_CHARS, - const wxString& name = wxGridNameStr); - - virtual bool CreateGrid(int numRows, int numCols, wxGrid::wxGridSelectionModes selmode = wxGrid::wxGridSelectCells); - - virtual void initSettings(CustomGridLeft* gridLeft, //create connection with zen::GridView - CustomGridMiddle* gridMiddle, - CustomGridRight* gridRight, - const zen::GridView* gridDataView); - -private: - void OnMouseMovement(wxMouseEvent& event); - virtual CustomGridTable* getGridDataTable() const; -}; - - -class CustomGridRight : public CustomGridRim -{ -public: - CustomGridRight(wxWindow* parent, - wxWindowID id, - const wxPoint& pos = wxDefaultPosition, - const wxSize& size = wxDefaultSize, - long style = wxWANTS_CHARS, - const wxString& name = wxGridNameStr); - - virtual bool CreateGrid(int numRows, int numCols, wxGrid::wxGridSelectionModes selmode = wxGrid::wxGridSelectCells); - - virtual void initSettings(CustomGridLeft* gridLeft, //create connection with zen::GridView - CustomGridMiddle* gridMiddle, - CustomGridRight* gridRight, - const zen::GridView* gridDataView); - -private: - void OnMouseMovement(wxMouseEvent& event); - virtual CustomGridTable* getGridDataTable() const; -}; - - -class CustomGridMiddle : public CustomGrid -{ - friend class GridCellRendererMiddle; - -public: - CustomGridMiddle(wxWindow* parent, - wxWindowID id, - const wxPoint& pos = wxDefaultPosition, - const wxSize& size = wxDefaultSize, - long style = wxWANTS_CHARS, - const wxString& name = wxGridNameStr); - - ~CustomGridMiddle(); - - virtual bool CreateGrid(int numRows, int numCols, wxGrid::wxGridSelectionModes selmode = wxGrid::wxGridSelectCells); - - virtual void initSettings(CustomGridLeft* gridLeft, //create connection with zen::GridView - CustomGridMiddle* gridMiddle, - CustomGridRight* gridRight, - const zen::GridView* gridDataView); - - void enableSyncPreview(bool value); - -private: - virtual CustomGridTable* getGridDataTable() const; - CustomGridTableMiddle* getGridDataTableMiddle() const; - - virtual void enableFileIcons(const std::shared_ptr<zen::IconBuffer>& iconBuffer) {}; -#ifdef FFS_WIN //get rid of scrollbars; Windows: overwrite virtual method - virtual void SetScrollbar(int orientation, int position, int thumbSize, int range, bool refresh = true); -#endif - - //this method is called when grid view changes: useful for parallel updating of multiple grids - virtual void alignOtherGrids(CustomGrid* gridLeft, CustomGrid* gridMiddle, CustomGrid* gridRight); - - virtual void DrawColLabel(wxDC& dc, int col); - - void OnMouseMovement(wxMouseEvent& event); - void OnLeaveWindow(wxMouseEvent& event); - void OnLeftMouseDown(wxMouseEvent& event); - void OnLeftMouseUp(wxMouseEvent& event); - - void showToolTip(int rowNumber, wxPoint pos); - - //small helper methods - enum BlockPosition //each cell can be divided into four blocks concerning mouse selections - { - BLOCKPOS_CHECK_BOX, - BLOCKPOS_LEFT, - BLOCKPOS_MIDDLE, - BLOCKPOS_RIGHT - }; - int mousePosToRow(const wxPoint pos, BlockPosition* block = NULL); - - //variables for selecting sync direction - int selectionRowBegin; - BlockPosition selectionPos; - - //variables for highlightning on mouse-over - int highlightedRow; - BlockPosition highlightedPos; - - std::unique_ptr<CustomTooltip> toolTip; -}; - -//custom events for middle grid: - -//-------------------------------------------------------------------------------------------- -//(UN-)CHECKING ROWS FROM SYNCHRONIZATION - -extern const wxEventType FFS_CHECK_ROWS_EVENT; //define new event type - -class FFSCheckRowsEvent : public wxCommandEvent -{ -public: - FFSCheckRowsEvent(const int from, const int to) : - wxCommandEvent(FFS_CHECK_ROWS_EVENT), - rowFrom(from), - rowTo(to) {} - - virtual wxEvent* Clone() const - { - return new FFSCheckRowsEvent(rowFrom, rowTo); - } - - const int rowFrom; - const int rowTo; -}; - -typedef void (wxEvtHandler::*FFSCheckRowsEventFunction)(FFSCheckRowsEvent&); - -#define FFSCheckRowsEventHandler(func) \ - (wxObjectEventFunction)(wxEventFunction)wxStaticCastEvent(FFSCheckRowsEventFunction, &func) - -//-------------------------------------------------------------------------------------------- -//SELECTING SYNC DIRECTION - -extern const wxEventType FFS_SYNC_DIRECTION_EVENT; //define new event type - -class FFSSyncDirectionEvent : public wxCommandEvent -{ -public: - FFSSyncDirectionEvent(const int from, const int to, const zen::SyncDirection dir) : - wxCommandEvent(FFS_SYNC_DIRECTION_EVENT), - rowFrom(from), - rowTo(to), - direction(dir) {} - - virtual wxEvent* Clone() const - { - return new FFSSyncDirectionEvent(rowFrom, rowTo, direction); - } - - const int rowFrom; - const int rowTo; - const zen::SyncDirection direction; -}; - -typedef void (wxEvtHandler::*FFSSyncDirectionEventFunction)(FFSSyncDirectionEvent&); - -#define FFSSyncDirectionEventHandler(func) \ - (wxObjectEventFunction)(wxEventFunction)wxStaticCastEvent(FFSSyncDirectionEventFunction, &func) - - -#endif // CUSTOMGRID_H_INCLUDED diff --git a/library/db_file.cpp b/library/db_file.cpp deleted file mode 100644 index 268e411e..00000000 --- a/library/db_file.cpp +++ /dev/null @@ -1,598 +0,0 @@ -// ************************************************************************** -// * This file is part of the FreeFileSync project. It is distributed under * -// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * -// * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * -// ************************************************************************** - -#include "db_file.h" -#include <wx/wfstream.h> -#include <wx/zstream.h> -#include <wx/mstream.h> -#include "../shared/global_func.h" -#include "../shared/file_error.h" -#include "../shared/string_conv.h" -#include "../shared/file_handling.h" -#include "../shared/serialize.h" -#include "../shared/file_io.h" -#include "../shared/loki/ScopeGuard.h" -#include "../shared/i18n.h" -#include <boost/bind.hpp> - -#ifdef FFS_WIN -#include <wx/msw/wrapwin.h> //includes "windows.h" -#include "../shared/long_path_prefix.h" -#endif - -using namespace zen; - - -namespace -{ -//------------------------------------------------------------------------------------------------------------------------------- -const char FILE_FORMAT_DESCR[] = "FreeFileSync"; -const int FILE_FORMAT_VER = 7; -//------------------------------------------------------------------------------------------------------------------------------- - - -template <SelectedSide side> inline -Zstring getDBFilename(const BaseDirMapping& baseMap, bool tempfile = false) -{ - //Linux and Windows builds are binary incompatible: char/wchar_t case, sensitive/insensitive - //32 and 64 bit db files ARE designed to be binary compatible! - //Give db files different names. - //make sure they end with ".ffs_db". These files will not be included into comparison when located in base sync directories -#ifdef FFS_WIN - Zstring dbname = Zstring(Zstr("sync")) + (tempfile ? Zstr(".tmp") : Zstr("")) + SYNC_DB_FILE_ENDING; -#elif defined FFS_LINUX - //files beginning with dots are hidden e.g. in Nautilus - Zstring dbname = Zstring(Zstr(".sync")) + (tempfile ? Zstr(".tmp") : Zstr("")) + SYNC_DB_FILE_ENDING; -#endif - - return baseMap.getBaseDirPf<side>() + dbname; -} - - - -class FileInputStreamDB : public FileInputStream -{ -public: - FileInputStreamDB(const Zstring& filename) : //throw FileError - FileInputStream(filename) - { - //read FreeFileSync file identifier - char formatDescr[sizeof(FILE_FORMAT_DESCR)] = {}; - Read(formatDescr, sizeof(formatDescr)); //throw FileError - - if (!std::equal(FILE_FORMAT_DESCR, FILE_FORMAT_DESCR + sizeof(FILE_FORMAT_DESCR), formatDescr)) - throw FileError(_("Incompatible synchronization database format:") + " \n" + "\"" + filename + "\""); - } - -private: -}; - - -class FileOutputStreamDB : public FileOutputStream -{ -public: - FileOutputStreamDB(const Zstring& filename) : //throw FileError - FileOutputStream(filename) - { - //write FreeFileSync file identifier - Write(FILE_FORMAT_DESCR, sizeof(FILE_FORMAT_DESCR)); //throw FileError - } - -private: -}; -} -//####################################################################################################################################### - - -class ReadDirInfo : public zen::ReadInputStream -{ -public: - ReadDirInfo(wxInputStream& stream, const wxString& errorObjName, DirInformation& dirInfo) : ReadInputStream(stream, errorObjName) - { - //|------------------------------------------------------------------------------------- - //| ensure 32/64 bit portability: use fixed size data types only e.g. boost::uint32_t | - //|------------------------------------------------------------------------------------- - - //read filter settings -> currently not required, but persisting it doesn't hurt - dirInfo.filter = HardFilter::loadFilter(getStream()); - check(); - - //start recursion - execute(dirInfo.baseDirContainer); - } - -private: - void execute(DirContainer& dirCont) const - { - while (readNumberC<bool>()) - readSubFile(dirCont); - - while (readNumberC<bool>()) - readSubLink(dirCont); - - while (readNumberC<bool>()) - readSubDirectory(dirCont); - } - - void readSubFile(DirContainer& dirCont) const - { - //attention: order of function argument evaluation is undefined! So do it one after the other... - const Zstring shortName = readStringC<Zstring>(); //file name - - const boost::int64_t modTime = readNumberC<boost::int64_t>(); - const boost::uint64_t fileSize = readNumberC<boost::uint64_t>(); - - //const util::FileID fileIdentifier(stream_); - //check(); - - dirCont.addSubFile(shortName, - FileDescriptor(modTime, fileSize)); - } - - - void readSubLink(DirContainer& dirCont) const - { - //attention: order of function argument evaluation is undefined! So do it one after the other... - const Zstring shortName = readStringC<Zstring>(); //file name - const boost::int64_t modTime = readNumberC<boost::int64_t>(); - const Zstring targetPath = readStringC<Zstring>(); //file name - const LinkDescriptor::LinkType linkType = static_cast<LinkDescriptor::LinkType>(readNumberC<boost::int32_t>()); - - dirCont.addSubLink(shortName, - LinkDescriptor(modTime, targetPath, linkType)); - } - - - void readSubDirectory(DirContainer& dirCont) const - { - const Zstring shortName = readStringC<Zstring>(); //directory name - DirContainer& subDir = dirCont.addSubDir(shortName); - execute(subDir); //recurse - } -}; - -namespace -{ -typedef std::string UniqueId; -typedef std::shared_ptr<std::vector<char> > MemoryStreamPtr; //byte stream representing DirInformation -typedef std::map<UniqueId, MemoryStreamPtr> StreamMapping; //list of streams ordered by session UUID -} - -class ReadFileStream : public zen::ReadInputStream -{ -public: - ReadFileStream(wxInputStream& stream, const wxString& filename, StreamMapping& streamList) : ReadInputStream(stream, filename) - { - //|------------------------------------------------------------------------------------- - //| ensure 32/64 bit portability: used fixed size data types only e.g. boost::uint32_t | - //|------------------------------------------------------------------------------------- - - boost::int32_t version = readNumberC<boost::int32_t>(); - - if (version != FILE_FORMAT_VER) //read file format version - throw FileError(_("Incompatible synchronization database format:") + " \n" + "\"" + filename.c_str() + "\""); - - streamList.clear(); - - boost::uint32_t dbCount = readNumberC<boost::uint32_t>(); //number of databases: one for each sync-pair - while (dbCount-- != 0) - { - //DB id of partner databases - const CharArray tmp2 = readArrayC(); - const std::string sessionID(tmp2->begin(), tmp2->end()); - - CharArray buffer = readArrayC(); //read db-entry stream (containing DirInformation) - - streamList.insert(std::make_pair(sessionID, buffer)); - } - } -}; - -namespace -{ -StreamMapping loadStreams(const Zstring& filename) //throw FileError -{ - if (!zen::fileExists(filename)) - throw FileErrorDatabaseNotExisting(_("Initial synchronization:") + " \n\n" + - _("One of the FreeFileSync database files is not yet existing:") + " \n" + - "\"" + filename + "\""); - - try - { - //read format description (uncompressed) - FileInputStreamDB uncompressed(filename); //throw FileError - - wxZlibInputStream input(uncompressed, wxZLIB_ZLIB); - - StreamMapping streamList; - ReadFileStream(input, toWx(filename), streamList); - return streamList; - } - catch (const std::bad_alloc&) //this is most likely caused by a corrupted database file - { - throw FileError(_("Error reading from synchronization database:") + " (bad_alloc)"); - } -} - - -DirInfoPtr parseStream(const std::vector<char>& stream, const Zstring& fileName) //throw FileError -> return value always bound! -{ - try - { - //read streams into DirInfo - auto dirInfo = std::make_shared<DirInformation>(); - wxMemoryInputStream buffer(&stream[0], stream.size()); //convert char-array to inputstream: no copying, ownership not transferred - ReadDirInfo(buffer, toWx(fileName), *dirInfo); //throw FileError - return dirInfo; - } - catch (const std::bad_alloc&) //this is most likely caused by a corrupted database file - { - throw FileError(_("Error reading from synchronization database:") + " (bad_alloc)"); - } -} -} - - -std::pair<DirInfoPtr, DirInfoPtr> zen::loadFromDisk(const BaseDirMapping& baseMapping) //throw FileError -{ - const Zstring fileNameLeft = getDBFilename<LEFT_SIDE>(baseMapping); - const Zstring fileNameRight = getDBFilename<RIGHT_SIDE>(baseMapping); - - //read file data: list of session ID + DirInfo-stream - const StreamMapping streamListLeft = ::loadStreams(fileNameLeft); //throw FileError - const StreamMapping streamListRight = ::loadStreams(fileNameRight); //throw FileError - - //find associated session: there can be at most one session within intersection of left and right ids - StreamMapping::const_iterator streamLeft = streamListLeft .end(); - StreamMapping::const_iterator streamRight = streamListRight.end(); - for (auto iterLeft = streamListLeft.begin(); iterLeft != streamListLeft.end(); ++iterLeft) - { - auto iterRight = streamListRight.find(iterLeft->first); - if (iterRight != streamListRight.end()) - { - streamLeft = iterLeft; - streamRight = iterRight; - break; - } - } - - if (streamLeft == streamListLeft .end() || - streamRight == streamListRight.end() || - !streamLeft ->second.get() || - !streamRight->second.get()) - throw FileErrorDatabaseNotExisting(_("Initial synchronization:") + " \n\n" + - _("Database files do not share a common synchronization session:") + " \n" + - "\"" + fileNameLeft + "\"\n" + - "\"" + fileNameRight + "\""); - //read streams into DirInfo - DirInfoPtr dirInfoLeft = parseStream(*streamLeft ->second, fileNameLeft); //throw FileError - DirInfoPtr dirInfoRight = parseStream(*streamRight->second, fileNameRight); //throw FileError - - return std::make_pair(dirInfoLeft, dirInfoRight); -} - - -//------------------------------------------------------------------------------------------------------------------------- -template <SelectedSide side> -class SaveDirInfo : public WriteOutputStream -{ -public: - SaveDirInfo(const BaseDirMapping& baseMapping, const DirContainer* oldDirInfo, const wxString& errorObjName, wxOutputStream& stream) : WriteOutputStream(errorObjName, stream) - { - //save filter settings - baseMapping.getFilter()->saveFilter(getStream()); - check(); - - //start recursion - execute(baseMapping, oldDirInfo); - } - -private: - void execute(const HierarchyObject& hierObj, const DirContainer* oldDirInfo) - { - std::for_each(hierObj.refSubFiles().begin(), hierObj.refSubFiles().end(), boost::bind(&SaveDirInfo::processFile, this, _1, oldDirInfo)); - writeNumberC<bool>(false); //mark last entry - std::for_each(hierObj.refSubLinks().begin(), hierObj.refSubLinks().end(), boost::bind(&SaveDirInfo::processLink, this, _1, oldDirInfo)); - writeNumberC<bool>(false); //mark last entry - std::for_each(hierObj.refSubDirs ().begin(), hierObj.refSubDirs ().end(), boost::bind(&SaveDirInfo::processDir, this, _1, oldDirInfo)); - writeNumberC<bool>(false); //mark last entry - } - - void processFile(const FileMapping& fileMap, const DirContainer* oldParentDir) - { - if (fileMap.getCategory() == FILE_EQUAL) //data in sync: write current state - { - if (!fileMap.isEmpty<side>()) - { - writeNumberC<bool>(true); //mark beginning of entry - writeStringC(fileMap.getShortName<side>()); //save respecting case! (Windows) - writeNumberC<boost::int64_t >(to<boost::int64_t>(fileMap.getLastWriteTime<side>())); //last modification time - writeNumberC<boost::uint64_t>(to<boost::uint64_t>(fileMap.getFileSize<side>())); //filesize - } - } - else //not in sync: reuse last synchronous state - { - if (oldParentDir) //no data is also a "synchronous state"! - { - auto iter = oldParentDir->files.find(fileMap.getObjShortName()); - if (iter != oldParentDir->files.end()) - { - writeNumberC<bool>(true); //mark beginning of entry - writeStringC(iter->first); //save respecting case! (Windows) - writeNumberC<boost::int64_t >(to<boost::int64_t>(iter->second.lastWriteTimeRaw)); //last modification time - writeNumberC<boost::uint64_t>(to<boost::uint64_t>(iter->second.fileSize)); //filesize - } - } - } - } - - void processLink(const SymLinkMapping& linkObj, const DirContainer* oldParentDir) - { - if (linkObj.getLinkCategory() == SYMLINK_EQUAL) //data in sync: write current state - { - if (!linkObj.isEmpty<side>()) - { - writeNumberC<bool>(true); //mark beginning of entry - writeStringC(linkObj.getShortName<side>()); //save respecting case! (Windows) - writeNumberC<boost::int64_t>(to<boost::int64_t>(linkObj.getLastWriteTime<side>())); //last modification time - writeStringC(linkObj.getTargetPath<side>()); - writeNumberC<boost::int32_t>(linkObj.getLinkType<side>()); - } - } - else //not in sync: reuse last synchronous state - { - if (oldParentDir) //no data is also a "synchronous state"! - { - auto iter = oldParentDir->links.find(linkObj.getObjShortName()); - if (iter != oldParentDir->links.end()) - { - writeNumberC<bool>(true); //mark beginning of entry - writeStringC(iter->first); //save respecting case! (Windows) - writeNumberC<boost::int64_t>(to<boost::int64_t>(iter->second.lastWriteTimeRaw)); //last modification time - writeStringC(iter->second.targetPath); - writeNumberC<boost::int32_t>(iter->second.type); - } - } - } - } - - void processDir(const DirMapping& dirMap, const DirContainer* oldParentDir) - { - const DirContainer* oldDir = NULL; - const Zstring* oldDirName = NULL; - if (oldParentDir) //no data is also a "synchronous state"! - { - auto iter = oldParentDir->dirs.find(dirMap.getObjShortName()); - if (iter != oldParentDir->dirs.end()) - { - oldDirName = &iter->first; - oldDir = &iter->second; - } - } - - CompareDirResult cat = dirMap.getDirCategory(); - - if (cat == DIR_EQUAL) //data in sync: write current state - { - if (!dirMap.isEmpty<side>()) - { - writeNumberC<bool>(true); //mark beginning of entry - writeStringC(dirMap.getShortName<side>()); //save respecting case! (Windows) - execute(dirMap, oldDir); //recurse - } - } - else //not in sync: reuse last synchronous state - { - if (oldDir) - { - writeNumberC<bool>(true); //mark beginning of entry - writeStringC(*oldDirName); //save respecting case! (Windows) - execute(dirMap, oldDir); //recurse - return; - } - //no data is also a "synchronous state"! - - //else: not in sync AND no "last synchronous state" - //we cannot simply skip the whole directory, since sub-items might be in sync - //Example: directories on left and right differ in case while sub-files are equal - switch (cat) - { - case DIR_LEFT_SIDE_ONLY: //sub-items cannot be in sync - break; - case DIR_RIGHT_SIDE_ONLY: //sub-items cannot be in sync - break; - case DIR_EQUAL: - assert(false); - break; - case DIR_DIFFERENT_METADATA: - writeNumberC<bool>(true); - writeStringC(dirMap.getShortName<side>()); - //ATTENTION: strictly this is a violation of the principle of reporting last synchronous state! - //however in this case this will result in "last sync unsuccessful" for this directory within <automatic> algorithm, which is fine - execute(dirMap, oldDir); //recurse and save sub-items which are in sync - break; - } - } - } -}; - - -class WriteFileStream : public WriteOutputStream -{ -public: - WriteFileStream(const StreamMapping& streamList, const wxString& filename, wxOutputStream& stream) : WriteOutputStream(filename, stream) - { - //save file format version - writeNumberC<boost::int32_t>(FILE_FORMAT_VER); - - writeNumberC<boost::uint32_t>(static_cast<boost::uint32_t>(streamList.size())); //number of database records: one for each sync-pair - - for (StreamMapping::const_iterator i = streamList.begin(); i != streamList.end(); ++i) - { - //sync session id - writeArrayC(std::vector<char>(i->first.begin(), i->first.end())); - - //write DirInformation stream - writeArrayC(*(i->second)); - } - } -}; - - -//save/load DirContainer -void saveFile(const StreamMapping& streamList, const Zstring& filename) //throw FileError -{ - { - //write format description (uncompressed) - FileOutputStreamDB uncompressed(filename); //throw FileError - - wxZlibOutputStream output(uncompressed, 4, wxZLIB_ZLIB); - /* 4 - best compromise between speed and compression: (scanning 200.000 objects) - 0 (uncompressed) 8,95 MB - 422 ms - 2 2,07 MB - 470 ms - 4 1,87 MB - 500 ms - 6 1,77 MB - 613 ms - 9 (maximal compression) 1,74 MB - 3330 ms */ - - WriteFileStream(streamList, toWx(filename), output); - } - //(try to) hide database file -#ifdef FFS_WIN - ::SetFileAttributes(zen::applyLongPathPrefix(filename).c_str(), FILE_ATTRIBUTE_HIDDEN); -#endif -} - - -bool equalEntry(const MemoryStreamPtr& lhs, const MemoryStreamPtr& rhs) -{ - if (!lhs.get() || !rhs.get()) - return lhs.get() == rhs.get(); - - return *lhs == *rhs; -} - - -void zen::saveToDisk(const BaseDirMapping& baseMapping) //throw FileError -{ - //transactional behaviour! write to tmp files first - const Zstring dbNameLeftTmp = getDBFilename<LEFT_SIDE >(baseMapping, true); - const Zstring dbNameRightTmp = getDBFilename<RIGHT_SIDE>(baseMapping, true); - - const Zstring dbNameLeft = getDBFilename<LEFT_SIDE >(baseMapping); - const Zstring dbNameRight = getDBFilename<RIGHT_SIDE>(baseMapping); - - //delete old tmp file, if necessary -> throws if deletion fails! - removeFile(dbNameLeftTmp); // - removeFile(dbNameRightTmp); //throw FileError - - //(try to) load old database files... - StreamMapping streamListLeft; - StreamMapping streamListRight; - - try //read file data: list of session ID + DirInfo-stream - { - streamListLeft = ::loadStreams(dbNameLeft); - } - catch (FileError&) {} //if error occurs: just overwrite old file! User is already informed about issues right after comparing! - try - { - streamListRight = ::loadStreams(dbNameRight); - } - catch (FileError&) {} - - //find associated session: there can be at most one session within intersection of left and right ids - StreamMapping::iterator streamLeft = streamListLeft .end(); - StreamMapping::iterator streamRight = streamListRight.end(); - for (auto iterLeft = streamListLeft.begin(); iterLeft != streamListLeft.end(); ++iterLeft) - { - auto iterRight = streamListRight.find(iterLeft->first); - if (iterRight != streamListRight.end()) - { - streamLeft = iterLeft; - streamRight = iterRight; - break; - } - } - - //(try to) read old DirInfo - DirInfoPtr oldDirInfoLeft; - DirInfoPtr oldDirInfoRight; - try - { - if (streamLeft != streamListLeft .end() && - streamRight != streamListRight.end() && - streamLeft ->second.get() && - streamRight->second.get()) - { - oldDirInfoLeft = parseStream(*streamLeft ->second, dbNameLeft); //throw FileError - oldDirInfoRight = parseStream(*streamRight->second, dbNameRight); //throw FileError - } - } - catch (FileError&) - { - //if error occurs: just overwrite old file! User is already informed about issues right after comparing! - oldDirInfoLeft .reset(); //read both or none! - oldDirInfoRight.reset(); // - } - - //create new database entries - MemoryStreamPtr newStreamLeft = std::make_shared<std::vector<char>>(); - { - wxMemoryOutputStream buffer; - const DirContainer* oldDir = oldDirInfoLeft.get() ? &oldDirInfoLeft->baseDirContainer : NULL; - SaveDirInfo<LEFT_SIDE>(baseMapping, oldDir, toWx(dbNameLeft), buffer); - newStreamLeft->resize(buffer.GetSize()); //convert output stream to char-array - buffer.CopyTo(&(*newStreamLeft)[0], buffer.GetSize()); // - } - - MemoryStreamPtr newStreamRight = std::make_shared<std::vector<char>>(); - { - wxMemoryOutputStream buffer; - const DirContainer* oldDir = oldDirInfoRight.get() ? &oldDirInfoRight->baseDirContainer : NULL; - SaveDirInfo<RIGHT_SIDE>(baseMapping, oldDir, toWx(dbNameRight), buffer); - newStreamRight->resize(buffer.GetSize()); //convert output stream to char-array - buffer.CopyTo(&(*newStreamRight)[0], buffer.GetSize()); // - } - - //check if there is some work to do at all - { - const bool updateRequiredLeft = streamLeft == streamListLeft .end() || !equalEntry(newStreamLeft, streamLeft ->second); - const bool updateRequiredRight = streamRight == streamListRight.end() || !equalEntry(newStreamRight, streamRight->second); - //some users monitor the *.ffs_db file with RTS => don't touch the file if it isnt't strictly needed - if (!updateRequiredLeft && !updateRequiredRight) - return; - } - - //create/update DirInfo-streams - std::string sessionID = util::generateGUID(); - - //erase old session data - if (streamLeft != streamListLeft.end()) - streamListLeft.erase(streamLeft); - if (streamRight != streamListRight.end()) - streamListRight.erase(streamRight); - - //fill in new - streamListLeft .insert(std::make_pair(sessionID, newStreamLeft)); - streamListRight.insert(std::make_pair(sessionID, newStreamRight)); - - //write (temp-) files... - Loki::ScopeGuard guardTempFileLeft = Loki::MakeGuard(&zen::removeFile, dbNameLeftTmp); - saveFile(streamListLeft, dbNameLeftTmp); //throw FileError - - Loki::ScopeGuard guardTempFileRight = Loki::MakeGuard(&zen::removeFile, dbNameRightTmp); - saveFile(streamListRight, dbNameRightTmp); //throw FileError - - //operation finished: rename temp files -> this should work transactionally: - //if there were no write access, creation of temp files would have failed - removeFile(dbNameLeft); - removeFile(dbNameRight); - renameFile(dbNameLeftTmp, dbNameLeft); //throw FileError; - renameFile(dbNameRightTmp, dbNameRight); //throw FileError; - - guardTempFileLeft. Dismiss(); //no need to delete temp file anymore - guardTempFileRight.Dismiss(); // -} diff --git a/library/db_file.h b/library/db_file.h deleted file mode 100644 index 188d7d92..00000000 --- a/library/db_file.h +++ /dev/null @@ -1,31 +0,0 @@ -// ************************************************************************** -// * This file is part of the FreeFileSync project. It is distributed under * -// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * -// * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * -// ************************************************************************** - -#ifndef DBFILE_H_INCLUDED -#define DBFILE_H_INCLUDED - -#include "../shared/file_error.h" -#include "../file_hierarchy.h" - -namespace zen -{ -const Zstring SYNC_DB_FILE_ENDING = Zstr(".ffs_db"); - -void saveToDisk(const BaseDirMapping& baseMapping); //throw FileError - -struct DirInformation -{ - HardFilter::FilterRef filter; //filter settings (used when retrieving directory data) - DirContainer baseDirContainer; //hierarchical directory information -}; -typedef std::shared_ptr<const DirInformation> DirInfoPtr; - -DEFINE_NEW_FILE_ERROR(FileErrorDatabaseNotExisting); - -std::pair<DirInfoPtr, DirInfoPtr> loadFromDisk(const BaseDirMapping& baseMapping); //throw FileError, FileErrorDatabaseNotExisting -> return value always bound! -} - -#endif // DBFILE_H_INCLUDED diff --git a/library/detect_renaming.cpp b/library/detect_renaming.cpp deleted file mode 100644 index 39e7eb4b..00000000 --- a/library/detect_renaming.cpp +++ /dev/null @@ -1,285 +0,0 @@ -// ************************************************************************** -// * This file is part of the FreeFileSync project. It is distributed under * -// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * -// * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * -// ************************************************************************** - -#include "detect_renaming.h" -#include <map> -#include <vector> -#include <boost/bind.hpp> - -using namespace FreeFileSync; - -/*detect renamed files: -Example: - X -> |_| Create right -|_| -> Y Delete right - -is detected as: - -Rename Y to X on right - -Algorithm: ----------- -DB-file left ---filename, Metadata(=:MD)---> DB-file right - /|\ | - | fileID, MD - fileID, MD | - | \|/ - X Y - -*/ - - -class FindDBAssoc -{ - /* - load and associate db-files by filename and metadata(size, date) - fileID, MD |-> fileID - */ -public: - struct AssocKey - { - AssocKey(const Utility::FileID& fileId, - const wxLongLong& lastWriteTimeRaw, - const wxULongLong& fileSize); - - bool operator<(const AssocKey& other) const; - - Utility::FileID fileId_; - wxLongLong lastWriteTimeRaw_; - wxULongLong fileSize_; - }; - - FindDBAssoc(const FreeFileSync::BaseDirMapping& baseMapping, - std::map<AssocKey, Utility::FileID>& assocDBLeftToRight); - -private: - void recurse(const DirContainer& leftSide, const DirContainer& rightSide); - - std::map<AssocKey, Utility::FileID>& assocDBLeftToRight_; //--> -}; - - -inline -FindDBAssoc::AssocKey::AssocKey(const Utility::FileID& fileId, - const wxLongLong& lastWriteTimeRaw, - const wxULongLong& fileSize) : - fileId_(fileId), - lastWriteTimeRaw_(lastWriteTimeRaw), - fileSize_(fileSize) {} - - -inline -bool FindDBAssoc::AssocKey::operator<(const AssocKey& other) const -{ - if (fileId_ != other.fileId_) - return fileId_ < other.fileId_; - - if (lastWriteTimeRaw_ != other.lastWriteTimeRaw_) - return lastWriteTimeRaw_ < other.lastWriteTimeRaw_; - - return fileSize_ < other.fileSize_; -} - - -FindDBAssoc::FindDBAssoc(const FreeFileSync::BaseDirMapping& baseMapping, - std::map<AssocKey, Utility::FileID>& assocDBLeftToRight) : assocDBLeftToRight_(assocDBLeftToRight) -{ - try - { - std::pair<FreeFileSync::DirInfoPtr, FreeFileSync::DirInfoPtr> dbInfo = - FreeFileSync::loadFromDisk(baseMapping); //throw (FileError) - - recurse(dbInfo.first->baseDirContainer, - dbInfo.second->baseDirContainer); - } - catch (...) {} //swallow... -} - - -void FindDBAssoc::recurse(const DirContainer& leftSide, const DirContainer& rightSide) -{ - for (DirContainer::SubFileList::const_iterator i = leftSide.getSubFiles().begin(); i != leftSide.getSubFiles().end(); ++i) - { - const FileDescriptor& fileDescrI = i->second.getData(); - if (!fileDescrI.fileIdentifier.isNull()) //fileIdentifier may be NULL - { - const DirContainer::SubFileList::const_iterator j = rightSide.getSubFiles().find(i->first); - - //find files that exist on left and right - if (j != rightSide.getSubFiles().end()) - { - const FileDescriptor& fileDescrJ = j->second.getData(); - if (!fileDescrJ.fileIdentifier.isNull()) //fileIdentifier may be NULL - { - if ( fileDescrI.lastWriteTimeRaw == fileDescrJ.lastWriteTimeRaw && - fileDescrI.fileSize == fileDescrJ.fileSize) - { - assocDBLeftToRight_[AssocKey(fileDescrI.fileIdentifier, - fileDescrI.lastWriteTimeRaw, - fileDescrI.fileSize)] = fileDescrJ.fileIdentifier; - } - } - } - } - } - - //----------------------------------------------------------------------------------------------- - for (DirContainer::SubDirList::const_iterator i = leftSide.getSubDirs().begin(); i != leftSide.getSubDirs().end(); ++i) - { - const DirContainer::SubDirList::const_iterator j = rightSide.getSubDirs().find(i->first); - - //directories that exist on both sides - if (j != rightSide.getSubDirs().end()) - { - recurse(i->second, j->second); //recurse into subdirectories - } - } -} - - - -class FindRenameCandidates -{ -public: - FindRenameCandidates(FreeFileSync::BaseDirMapping& baseMapping) - { - FindDBAssoc(baseMapping, - assocDBLeftToRight); - - if (!assocDBLeftToRight.empty()) - recurse(baseMapping); - } - - void getRenameCandidates(std::vector<std::pair<FileMapping*, FileMapping*> >& renameOnLeft, - std::vector<std::pair<FileMapping*, FileMapping*> >& renameOnRight); - -private: - void recurse(HierarchyObject& hierObj) - { - //files - std::for_each(hierObj.subFiles.begin(), hierObj.subFiles.end(), - boost::bind(&FindRenameCandidates::processFile, this, _1)); - - //directories - std::for_each(hierObj.subDirs.begin(), hierObj.subDirs.end(), - boost::bind(&FindRenameCandidates::recurse, this, _1));//recursion - } - - void processFile(FileMapping& fileObj) - { - switch (fileObj.getSyncOperation()) //evaluate comparison result and sync direction - { - case SO_CREATE_NEW_LEFT: - if (!fileObj.getFileID<RIGHT_SIDE>().isNull()) //fileIdentifier may be NULL - createLeft[FindDBAssoc::AssocKey(fileObj.getFileID<RIGHT_SIDE>(), - fileObj.getLastWriteTime<RIGHT_SIDE>(), - fileObj.getFileSize<RIGHT_SIDE>())] = &fileObj; - break; - - case SO_CREATE_NEW_RIGHT: - if (!fileObj.getFileID<LEFT_SIDE>().isNull()) //fileIdentifier may be NULL - createRight.push_back(&fileObj); - break; - - case SO_DELETE_LEFT: - if (!fileObj.getFileID<LEFT_SIDE>().isNull()) //fileIdentifier may be NULL - deleteLeft.push_back(&fileObj); - break; - - case SO_DELETE_RIGHT: - if (!fileObj.getFileID<RIGHT_SIDE>().isNull()) //fileIdentifier may be NULL - deleteRight[FindDBAssoc::AssocKey(fileObj.getFileID<RIGHT_SIDE>(), - fileObj.getLastWriteTime<RIGHT_SIDE>(), - fileObj.getFileSize<RIGHT_SIDE>())] = &fileObj; - break; - - case SO_OVERWRITE_RIGHT: - case SO_OVERWRITE_LEFT: - case SO_DO_NOTHING: - case SO_UNRESOLVED_CONFLICT: - break; - } - - } - - - std::vector<FileMapping*> createRight; //pointer always bound! - std::vector<FileMapping*> deleteLeft; // - // | - // \|/ - std::map<FindDBAssoc::AssocKey, Utility::FileID> assocDBLeftToRight; - // | - // \|/ - std::map<FindDBAssoc::AssocKey, FileMapping*> deleteRight; //pointer always bound! - std::map<FindDBAssoc::AssocKey, FileMapping*> createLeft; // - -}; - - - -void FindRenameCandidates::getRenameCandidates( - std::vector<std::pair<FileMapping*, FileMapping*> >& renameOnLeft, - std::vector<std::pair<FileMapping*, FileMapping*> >& renameOnRight) -{ - for (std::vector<FileMapping*>::const_iterator crRightIter = createRight.begin(); - crRightIter != createRight.end(); - ++crRightIter) - { - const FindDBAssoc::AssocKey assocDbKey((*crRightIter)->getFileID<LEFT_SIDE>(), - (*crRightIter)->getLastWriteTime<LEFT_SIDE>(), - (*crRightIter)->getFileSize<LEFT_SIDE>()); - - const std::map<FindDBAssoc::AssocKey, Utility::FileID>::const_iterator assocDBIter = - assocDBLeftToRight.find(assocDbKey); - - if (assocDBIter != assocDBLeftToRight.end()) - { - std::map<FindDBAssoc::AssocKey, FileMapping*>::const_iterator delRightIter = - deleteRight.find(FindDBAssoc::AssocKey(assocDBIter->second, //FileID of right side - assocDbKey.lastWriteTimeRaw_, - assocDbKey.fileSize_)); - - if (delRightIter != deleteRight.end()) - { - renameOnRight.push_back(std::make_pair(*crRightIter, delRightIter->second)); - } - } - } - //------------------------------------------------------------------------------------------------ - for (std::vector<FileMapping*>::const_iterator delLeftIter = deleteLeft.begin(); - delLeftIter != deleteLeft.end(); - ++delLeftIter) - { - const FindDBAssoc::AssocKey assocDbKey((*delLeftIter)->getFileID<LEFT_SIDE>(), - (*delLeftIter)->getLastWriteTime<LEFT_SIDE>(), - (*delLeftIter)->getFileSize<LEFT_SIDE>()); - - const std::map<FindDBAssoc::AssocKey, Utility::FileID>::const_iterator assocDBIter = - assocDBLeftToRight.find(assocDbKey); - - if (assocDBIter != assocDBLeftToRight.end()) - { - std::map<FindDBAssoc::AssocKey, FileMapping*>::const_iterator createLeftIter = - createLeft.find(FindDBAssoc::AssocKey(assocDBIter->second, //FileID of right side - assocDbKey.lastWriteTimeRaw_, - assocDbKey.fileSize_)); - - if (createLeftIter != createLeft.end()) - { - renameOnLeft.push_back(std::make_pair(createLeftIter->second, *delLeftIter)); - } - } - } -} - - -void FreeFileSync::getRenameCandidates(FreeFileSync::BaseDirMapping& baseMapping, //in - std::vector<std::pair<CreateOnLeft, DeleteOnLeft> >& renameOnLeft, //out - std::vector<std::pair<CreateOnRight, DeleteOnRight> >& renameOnRight) //out throw()! -{ - FindRenameCandidates(baseMapping).getRenameCandidates(renameOnLeft, renameOnRight); -} - diff --git a/library/detect_renaming.h b/library/detect_renaming.h deleted file mode 100644 index e94927c0..00000000 --- a/library/detect_renaming.h +++ /dev/null @@ -1,26 +0,0 @@ -// ************************************************************************** -// * This file is part of the FreeFileSync project. It is distributed under * -// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * -// * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * -// ************************************************************************** - -#ifndef DETECTRENAMING_H_INCLUDED -#define DETECTRENAMING_H_INCLUDED - -#include "../file_hierarchy.h" - - -//identify a file "create and delete"-operation as a file renaming! - -namespace zen -{ -typedef FileMapping* CreateOnLeft; -typedef FileMapping* DeleteOnLeft; -typedef FileMapping* CreateOnRight; -typedef FileMapping* DeleteOnRight; -void getRenameCandidates(zen::BaseDirMapping& baseMapping, //in - std::vector<std::pair<CreateOnLeft, DeleteOnLeft> >& renameOnLeft, //out - std::vector<std::pair<CreateOnRight, DeleteOnRight> >& renameOnRight); //out throw()! -} - -#endif // DETECTRENAMING_H_INCLUDED diff --git a/library/dir_exist_async.h b/library/dir_exist_async.h deleted file mode 100644 index cab82aa2..00000000 --- a/library/dir_exist_async.h +++ /dev/null @@ -1,35 +0,0 @@ -// ************************************************************************** -// * This file is part of the FreeFileSync project. It is distributed under * -// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * -// * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * -// ************************************************************************** - -#ifndef DIR_EXIST_HEADER_08173281673432158067342132467183267 -#define DIR_EXIST_HEADER_08173281673432158067342132467183267 - -#include "../shared/check_exist.h" -#include "status_handler.h" -#include "../shared/file_error.h" -#include "../shared/i18n.h" - -//dir existence checking may hang for non-existent network drives => run asynchronously and update UI! -namespace -{ -using namespace zen; //restricted to unnamed namespace! - -bool dirExistsUpdating(const Zstring& dirname, ProcessCallback& procCallback) -{ - using namespace util; - - std::wstring statusText = _("Searching for directory %x..."); - replace(statusText, L"%x", std::wstring(L"\"") + dirname + L"\"", false); - procCallback.reportStatus(statusText); - - auto ft = dirExistsAsync(dirname); - while (!ft.timed_wait(boost::posix_time::milliseconds(UI_UPDATE_INTERVAL))) - procCallback.requestUiRefresh(); //may throw! - return ft.get(); -} -} - -#endif //DIR_EXIST_HEADER_08173281673432158067342132467183267 diff --git a/library/dir_lock.cpp b/library/dir_lock.cpp deleted file mode 100644 index 7feb51ff..00000000 --- a/library/dir_lock.cpp +++ /dev/null @@ -1,599 +0,0 @@ -#include "dir_lock.h" -#include <utility> -#include <wx/utils.h> -#include <wx/timer.h> -#include <wx/log.h> -#include <wx/msgdlg.h> -#include <memory> -#include <boost/weak_ptr.hpp> -#include "../shared/string_conv.h" -#include "../shared/i18n.h" -#include "../shared/last_error.h" -#include "../shared/boost_thread_wrap.h" //include <boost/thread.hpp> -#include "../shared/loki/ScopeGuard.h" -#include "../shared/guid.h" -#include "../shared/file_io.h" -#include "../shared/assert_static.h" -#include "../shared/serialize.h" -#include "../shared/build_info.h" -#include "../shared/int64.h" -#include "../shared/file_handling.h" - -#ifdef FFS_WIN -#include <tlhelp32.h> -#include <wx/msw/wrapwin.h> //includes "windows.h" -#include "../shared/long_path_prefix.h" - -#elif defined FFS_LINUX -#include <sys/stat.h> -#include <cerrno> -#include <unistd.h> -#endif - -using namespace zen; -using namespace std::rel_ops; - - -namespace -{ -const size_t EMIT_LIFE_SIGN_INTERVAL = 5000; //show life sign; unit [ms] -const size_t POLL_LIFE_SIGN_INTERVAL = 6000; //poll for life sign; unit [ms] -const size_t DETECT_EXITUS_INTERVAL = 30000; //assume abandoned lock; unit [ms] - -const char LOCK_FORMAT_DESCR[] = "FreeFileSync"; -const int LOCK_FORMAT_VER = 1; //lock file format version -} - -//worker thread -class LifeSigns -{ -public: - LifeSigns(const Zstring& lockfilename) : //throw()!!! siehe SharedDirLock() - lockfilename_(lockfilename) {} //thread safety: make deep copy! - - void operator()() const //thread entry - { - try - { - while (true) - { - boost::thread::sleep(boost::get_system_time() + boost::posix_time::milliseconds(EMIT_LIFE_SIGN_INTERVAL)); //interruption point! - - //actual work - emitLifeSign(); //throw () - } - } - catch (const std::exception& e) //exceptions must be catched per thread - { - wxSafeShowMessage(wxString(_("An exception occurred!")) + wxT("(Dirlock)"), wxString::FromAscii(e.what())); //simple wxMessageBox won't do for threads - } - } - - void emitLifeSign() const //try to append one byte...; throw() - { - const char buffer[1] = {' '}; - -#ifdef FFS_WIN - //ATTENTION: setting file pointer IS required! => use CreateFile/FILE_GENERIC_WRITE + SetFilePointerEx! - //although CreateFile/FILE_APPEND_DATA without SetFilePointerEx works locally, it MAY NOT work on some network shares creating a 4 gig file!!! - - const HANDLE fileHandle = ::CreateFile(applyLongPathPrefix(lockfilename_.c_str()).c_str(), - GENERIC_READ | GENERIC_WRITE, //use both when writing over network, see comment in file_io.cpp - FILE_SHARE_READ, - 0, - OPEN_EXISTING, - FILE_ATTRIBUTE_NORMAL, - NULL); - if (fileHandle == INVALID_HANDLE_VALUE) - return; - LOKI_ON_BLOCK_EXIT2(::CloseHandle(fileHandle)); - - const LARGE_INTEGER moveDist = {}; - if (!::SetFilePointerEx(fileHandle, //__in HANDLE hFile, - moveDist, //__in LARGE_INTEGER liDistanceToMove, - NULL, //__out_opt PLARGE_INTEGER lpNewFilePointer, - FILE_END)) //__in DWORD dwMoveMethod - return; - - DWORD bytesWritten = 0; - ::WriteFile(fileHandle, //__in HANDLE hFile, - buffer, //__out LPVOID lpBuffer, - 1, //__in DWORD nNumberOfBytesToRead, - &bytesWritten, //__out_opt LPDWORD lpNumberOfBytesWritten, - NULL); //__inout_opt LPOVERLAPPED lpOverlapped - -#elif defined FFS_LINUX - const int fileHandle = ::open(lockfilename_.c_str(), O_WRONLY | O_APPEND); //O_EXCL contains a race condition on NFS file systems: http://linux.die.net/man/2/open - if (fileHandle == -1) - return; - LOKI_ON_BLOCK_EXIT2(::close(fileHandle)); - - const ssize_t bytesWritten = ::write(fileHandle, buffer, 1); - (void)bytesWritten; -#endif - } - -private: - const Zstring lockfilename_; //thread local! atomic ref-count => binary value-type semantics! -}; - - -namespace -{ -UInt64 getLockFileSize(const Zstring& filename) //throw FileError, ErrorNotExisting -{ -#ifdef FFS_WIN - WIN32_FIND_DATA fileInfo = {}; - const HANDLE searchHandle = ::FindFirstFile(applyLongPathPrefix(filename).c_str(), &fileInfo); - if (searchHandle == INVALID_HANDLE_VALUE) - { - const DWORD lastError = ::GetLastError(); - - std::wstring errorMessage = _("Error reading file attributes:") + "\n\"" + filename + "\"" + "\n\n" + getLastErrorFormatted(lastError); - - if (lastError == ERROR_FILE_NOT_FOUND || - lastError == ERROR_PATH_NOT_FOUND) - throw ErrorNotExisting(errorMessage); - else - throw FileError(errorMessage); - } - - ::FindClose(searchHandle); - - return zen::UInt64(fileInfo.nFileSizeLow, fileInfo.nFileSizeHigh); - -#elif defined FFS_LINUX - struct stat fileInfo = {}; - if (::stat(filename.c_str(), &fileInfo) != 0) //follow symbolic links - { - const int lastError = errno; - - std::wstring errorMessage = _("Error reading file attributes:") + "\n\"" + filename + "\"" + "\n\n" + getLastErrorFormatted(lastError); - - if (lastError == ENOENT) - throw ErrorNotExisting(errorMessage); - else - throw FileError(errorMessage); - } - - return zen::UInt64(fileInfo.st_size); -#endif -} - - -Zstring deleteAbandonedLockName(const Zstring& lockfilename) //make sure to NOT change file ending! -{ - const size_t pos = lockfilename.rfind(FILE_NAME_SEPARATOR); //search from end - return pos == Zstring::npos ? Zstr("Del.") + lockfilename : - Zstring(lockfilename.c_str(), pos + 1) + //include path separator - Zstr("Del.") + - lockfilename.AfterLast(FILE_NAME_SEPARATOR); //returns the whole string if ch not found -} - - -namespace -{ -//read string from file stream -inline -std::string readString(wxInputStream& stream) //throw std::exception -{ - const auto strLength = readPOD<boost::uint32_t>(stream); - std::string output; - if (strLength > 0) - { - output.resize(strLength); //throw std::bad_alloc - stream.Read(&output[0], strLength); - } - return output; -} - - -inline -void writeString(wxOutputStream& stream, const std::string& str) //write string to filestream -{ - writePOD(stream, static_cast<boost::uint32_t>(str.length())); - stream.Write(str.c_str(), str.length()); -} - - -std::string getComputerId() //returns empty string on error -{ - const wxString fhn = ::wxGetFullHostName(); - if (fhn.empty()) return std::string(); -#ifdef FFS_WIN - return "Windows " + std::string(fhn.ToUTF8()); -#elif defined FFS_LINUX - return "Linux " + std::string(fhn.ToUTF8()); -#endif -} - - -struct LockInformation -{ - LockInformation() - { - lockId = util::generateGUID(); -#ifdef FFS_WIN - procDescr.processId = ::GetCurrentProcessId(); -#elif defined FFS_LINUX - procDescr.processId = ::getpid(); -#endif - procDescr.computerId = getComputerId(); - } - - LockInformation(wxInputStream& stream) //read - { - char formatDescr[sizeof(LOCK_FORMAT_DESCR)] = {}; - stream.Read(formatDescr, sizeof(LOCK_FORMAT_DESCR)); //file format header - const int lockFileVersion = readPOD<boost::int32_t>(stream); // - (void)lockFileVersion; - - //some format checking here? - - lockId = readString(stream); - procDescr.processId = static_cast<ProcessId>(readPOD<boost::uint64_t>(stream)); //possible loss of precision (32/64 bit process) covered by buildId - procDescr.computerId = readString(stream); - } - - void toStream(wxOutputStream& stream) const //write - { - assert_static(sizeof(ProcessId) <= sizeof(boost::uint64_t)); //ensure portability - - stream.Write(LOCK_FORMAT_DESCR, sizeof(LOCK_FORMAT_DESCR)); - writePOD<boost::int32_t>(stream, LOCK_FORMAT_VER); - - writeString(stream, lockId); - writePOD<boost::uint64_t>(stream, procDescr.processId); - writeString(stream, procDescr.computerId); - } - -#ifdef FFS_WIN - typedef DWORD ProcessId; //same size on 32 and 64 bit windows! -#elif defined FFS_LINUX - typedef pid_t ProcessId; -#endif - - std::string lockId; //16 byte UUID - - struct ProcessDescription - { - ProcessId processId; - std::string computerId; - } procDescr; -}; - - -//true: process not available, false: cannot say anything -enum ProcessStatus -{ - PROC_STATUS_NOT_RUNNING, - PROC_STATUS_RUNNING, - PROC_STATUS_ITS_US, - PROC_STATUS_NO_IDEA -}; -ProcessStatus getProcessStatus(const LockInformation::ProcessDescription& procDescr) -{ - if (procDescr.computerId != getComputerId() || - procDescr.computerId.empty()) //both names are empty - return PROC_STATUS_NO_IDEA; //lock owned by different computer - -#ifdef FFS_WIN - if (procDescr.processId == ::GetCurrentProcessId()) //may seem obscure, but it's possible: a lock file is "stolen" and put back while the program is running - return PROC_STATUS_ITS_US; - - //note: ::OpenProcess() is no option as it may successfully return for crashed processes! - HANDLE snapshot = ::CreateToolhelp32Snapshot( - TH32CS_SNAPPROCESS, //__in DWORD dwFlags, - 0); //__in DWORD th32ProcessID - if (snapshot == INVALID_HANDLE_VALUE) - return PROC_STATUS_NO_IDEA; - LOKI_ON_BLOCK_EXIT2(::CloseHandle(snapshot)); - - PROCESSENTRY32 processEntry = {}; - processEntry.dwSize = sizeof(processEntry); - - if (!::Process32First(snapshot, //__in HANDLE hSnapshot, - &processEntry)) //__inout LPPROCESSENTRY32 lppe - return PROC_STATUS_NO_IDEA; - do - { - if (processEntry.th32ProcessID == procDescr.processId) - return PROC_STATUS_RUNNING; //process still running - } - while (::Process32Next(snapshot, &processEntry)); - - return PROC_STATUS_NOT_RUNNING; - -#elif defined FFS_LINUX - if (procDescr.processId == ::getpid()) //may seem obscure, but it's possible: a lock file is "stolen" and put back while the program is running - return PROC_STATUS_ITS_US; - - if (procDescr.processId <= 0 || procDescr.processId >= 65536) - return PROC_STATUS_NO_IDEA; //invalid process id - - return zen::dirExists(Zstr("/proc/") + Zstring::fromNumber(procDescr.processId)) ? PROC_STATUS_RUNNING : PROC_STATUS_NOT_RUNNING; -#endif -} - - -void writeLockInfo(const Zstring& lockfilename) //throw FileError -{ - //write GUID at the beginning of the file: this ID is a universal identifier for this lock (no matter what the path is, considering symlinks, distributed network, etc.) - FileOutputStream lockFile(lockfilename); //throw FileError - LockInformation().toStream(lockFile); -} - - -LockInformation retrieveLockInfo(const Zstring& lockfilename) //throw FileError, ErrorNotExisting -{ - //read GUID from beginning of file - FileInputStream lockFile(lockfilename); //throw FileError, ErrorNotExisting - return LockInformation(lockFile); -} - - -std::string retrieveLockId(const Zstring& lockfilename) //throw FileError, ErrorNotExisting -{ - return retrieveLockInfo(lockfilename).lockId; -} -} - - -void waitOnDirLock(const Zstring& lockfilename, DirLockCallback* callback) //throw FileError -{ - std::wstring infoMsg = _("Waiting while directory is locked (%x)..."); - replace(infoMsg, L"%x", std::wstring(L"\"") + lockfilename + "\""); - if (callback) - callback->reportInfo(infoMsg); - //--------------------------------------------------------------- - try - { - const LockInformation lockInfo = retrieveLockInfo(lockfilename); //throw FileError, ErrorNotExisting - - bool lockOwnderDead = false; //convenience optimization: if we know the owning process crashed, we needn't wait DETECT_EXITUS_INTERVAL sec - switch (getProcessStatus(lockInfo.procDescr)) - { - case PROC_STATUS_ITS_US: //since we've already passed LockAdmin, the lock file seems abandoned ("stolen"?) although it's from this process - case PROC_STATUS_NOT_RUNNING: - lockOwnderDead = true; - break; - case PROC_STATUS_RUNNING: - case PROC_STATUS_NO_IDEA: - break; - } - - zen::UInt64 fileSizeOld; - wxLongLong lockSilentStart = wxGetLocalTimeMillis(); - - while (true) - { - const zen::UInt64 fileSizeNew = ::getLockFileSize(lockfilename); //throw FileError, ErrorNotExisting - wxLongLong currentTime = wxGetLocalTimeMillis(); - - if (fileSizeNew != fileSizeOld) - { - //received life sign from lock - fileSizeOld = fileSizeNew; - lockSilentStart = currentTime; - } - - if (lockOwnderDead || //no need to wait any longer... - currentTime - lockSilentStart > DETECT_EXITUS_INTERVAL) - { - DirLock dummy(deleteAbandonedLockName(lockfilename), callback); //throw FileError - - //now that the lock is in place check existence again: meanwhile another process may have deleted and created a new lock! - - if (retrieveLockId(lockfilename) != lockInfo.lockId) //throw FileError, ErrorNotExisting - return; //another process has placed a new lock, leave scope: the wait for the old lock is technically over... - - if (getLockFileSize(lockfilename) != fileSizeOld) //throw FileError, ErrorNotExisting - continue; //belated lifesign - - removeFile(lockfilename); //throw FileError - return; - } - - //wait some time... - const size_t GUI_CALLBACK_INTERVAL = 100; - for (size_t i = 0; i < POLL_LIFE_SIGN_INTERVAL / GUI_CALLBACK_INTERVAL; ++i) - { - if (callback) callback->requestUiRefresh(); - wxMilliSleep(GUI_CALLBACK_INTERVAL); - - //show some countdown on abandoned locks - if (callback) - { - if (currentTime - lockSilentStart > EMIT_LIFE_SIGN_INTERVAL) //one signal missed: it's likely this is an abandoned lock: - { - long remainingSeconds = ((DETECT_EXITUS_INTERVAL - (wxGetLocalTimeMillis() - lockSilentStart)) / 1000).ToLong(); - remainingSeconds = std::max(0L, remainingSeconds); - - std::wstring remSecMsg = _P("1 sec", "%x sec", remainingSeconds); - replace(remSecMsg, L"%x", toString<std::wstring>(remainingSeconds)); - callback->reportInfo(infoMsg + " " + remSecMsg); - } - else - callback->reportInfo(infoMsg); //emit a message in any case (might clear other one) - } - } - } - } - catch (const ErrorNotExisting&) - { - return; //what we are waiting for... - } -} - - -void releaseLock(const Zstring& lockfilename) //throw () -{ - try - { - removeFile(lockfilename); - } - catch (...) {} -} - - -bool tryLock(const Zstring& lockfilename) //throw FileError -{ -#ifdef FFS_WIN - const HANDLE fileHandle = ::CreateFile(applyLongPathPrefix(lockfilename).c_str(), - GENERIC_READ | GENERIC_WRITE, //use both when writing over network, see comment in file_io.cpp - FILE_SHARE_READ, - 0, - CREATE_NEW, - FILE_ATTRIBUTE_NORMAL, - NULL); - if (fileHandle == INVALID_HANDLE_VALUE) - { -#ifndef _MSC_VER -#warning fix this problem! - //read-only FTP may return: ERROR_FILE_EXISTS (NetDrive @ GNU) -#endif - if (::GetLastError() == ERROR_FILE_EXISTS) - return false; - else - throw FileError(_("Error setting directory lock:") + "\n\"" + lockfilename + "\"" + "\n\n" + getLastErrorFormatted()); - } - ::CloseHandle(fileHandle); - -#elif defined FFS_LINUX - //O_EXCL contains a race condition on NFS file systems: http://linux.die.net/man/2/open - ::umask(0); //important! - const int fileHandle = ::open(lockfilename.c_str(), O_CREAT | O_WRONLY | O_EXCL, S_IRWXU | S_IRWXG | S_IRWXO); - if (fileHandle == -1) - { - if (errno == EEXIST) - return false; - else - throw FileError(_("Error setting directory lock:") + "\n\"" + lockfilename + "\"" + "\n\n" + getLastErrorFormatted()); - } - ::close(fileHandle); -#endif - - Loki::ScopeGuard guardLockFile = Loki::MakeGuard(zen::removeFile, lockfilename); - - //write UUID at the beginning of the file: this ID is a universal identifier for this lock (no matter what the path is, considering symlinks, etc.) - writeLockInfo(lockfilename); //throw FileError - - guardLockFile.Dismiss(); //lockfile created successfully - return true; -} -} - - -class DirLock::SharedDirLock -{ -public: - SharedDirLock(const Zstring& lockfilename, DirLockCallback* callback = NULL) : //throw FileError - lockfilename_(lockfilename) - { - while (!::tryLock(lockfilename)) //throw FileError - ::waitOnDirLock(lockfilename, callback); // - - threadObj = boost::thread(LifeSigns(lockfilename)); - } - - ~SharedDirLock() - { - threadObj.interrupt(); //thread lifetime is subset of this instances's life - threadObj.join(); - - ::releaseLock(lockfilename_); //throw () - } - -private: - SharedDirLock(const DirLock&); - SharedDirLock& operator=(const DirLock&); - - const Zstring lockfilename_; - - boost::thread threadObj; -}; - - -class DirLock::LockAdmin //administrate all locks held by this process to avoid deadlock by recursion -{ -public: - static LockAdmin& instance() - { - static LockAdmin inst; - return inst; - } - - //create or retrieve a SharedDirLock - std::shared_ptr<SharedDirLock> retrieve(const Zstring& lockfilename, DirLockCallback* callback) //throw FileError - { - //optimization: check if there is an active(!) lock for "lockfilename" - FileToUuidMap::const_iterator iterUuid = fileToUuid.find(lockfilename); - if (iterUuid != fileToUuid.end()) - { - const std::shared_ptr<SharedDirLock>& activeLock = findActive(iterUuid->second); //returns null-lock if not found - if (activeLock) - return activeLock; //SharedDirLock is still active -> enlarge circle of shared ownership - } - - try //actual check based on lock UUID, deadlock prevention: "lockfilename" may be an alternative name for an already active lock - { - const std::string lockId = retrieveLockId(lockfilename); //throw FileError, ErrorNotExisting - - const std::shared_ptr<SharedDirLock>& activeLock = findActive(lockId); //returns null-lock if not found - if (activeLock) - { - fileToUuid[lockfilename] = lockId; //perf-optimization: update relation - return activeLock; - } - } - catch (...) {} //catch everything, let SharedDirLock constructor deal with errors, e.g. 0-sized/corrupted lock file - - //not yet in buffer, so create a new directory lock - std::shared_ptr<SharedDirLock> newLock(new SharedDirLock(lockfilename, callback)); //throw FileError - const std::string newLockId = retrieveLockId(lockfilename); //throw FileError, ErrorNotExisting - - //update registry - fileToUuid[lockfilename] = newLockId; //throw() - uuidToLock[newLockId] = newLock; // - - return newLock; - } - -private: - LockAdmin() {} - - std::shared_ptr<SharedDirLock> findActive(const std::string& lockId) //returns null-lock if not found - { - UuidToLockMap::const_iterator iterLock = uuidToLock.find(lockId); - return iterLock != uuidToLock.end() ? - iterLock->second.lock() : //try to get shared_ptr; throw() - std::shared_ptr<SharedDirLock>(); - } - - typedef std::weak_ptr<SharedDirLock> SharedLock; - - typedef std::string UniqueId; - typedef std::map<Zstring, UniqueId, LessFilename> FileToUuidMap; //n:1 handle uppper/lower case correctly - typedef std::map<UniqueId, SharedLock> UuidToLockMap; //1:1 - - FileToUuidMap fileToUuid; //lockname |-> UUID; locks can be referenced by a lockfilename or alternatively a UUID - UuidToLockMap uuidToLock; //UUID |-> "shared lock ownership" -}; - - -DirLock::DirLock(const Zstring& lockfilename, DirLockCallback* callback) //throw FileError -{ -#ifdef FFS_WIN - std::vector<wchar_t> volName(std::max(lockfilename.size(), static_cast<size_t>(10000))); - if (::GetVolumePathName(lockfilename.c_str(), //__in LPCTSTR lpszFileName, - &volName[0], //__out LPTSTR lpszVolumePathName, - static_cast<DWORD>(volName.size()))) //__in DWORD cchBufferLength - { - DWORD dt = ::GetDriveType(&volName[0]); - if (dt == DRIVE_CDROM) - return; //we don't need a lock for a CD ROM - } -#endif - - sharedLock = LockAdmin::instance().retrieve(lockfilename, callback); -} diff --git a/library/dir_lock.h b/library/dir_lock.h deleted file mode 100644 index 8cec9d69..00000000 --- a/library/dir_lock.h +++ /dev/null @@ -1,35 +0,0 @@ -#ifndef DIR_LOCK_H_INCLUDED -#define DIR_LOCK_H_INCLUDED - -#include "../shared/file_error.h" -#include <memory> - - -struct DirLockCallback //while waiting for the lock -{ - virtual ~DirLockCallback() {} - virtual void requestUiRefresh() = 0; //allowed to throw exceptions - virtual void reportInfo(const std::wstring& text) = 0; -}; - -/* -RAII structure to place a directory lock against other FFS processes: - - recursive locking supported, even with alternate lockfile names, e.g. via symlinks, network mounts etc. - - ownership shared between all object instances refering to a specific lock location(= UUID) - - can be copied safely and efficiently! (ref-counting) - - detects and resolves abandoned locks (instantly if lock is associated with local pc, else after 30 seconds) - - temporary locks created during abandoned lock resolution keep "lockfilename"'s extension - - race-free (Windows, almost on Linux(NFS)) -*/ -class DirLock -{ -public: - DirLock(const Zstring& lockfilename, DirLockCallback* callback = NULL); //throw FileError, callback only used during construction - -private: - class LockAdmin; - class SharedDirLock; - std::shared_ptr<SharedDirLock> sharedLock; -}; - -#endif // DIR_LOCK_H_INCLUDED diff --git a/library/error_log.cpp b/library/error_log.cpp deleted file mode 100644 index 28819f40..00000000 --- a/library/error_log.cpp +++ /dev/null @@ -1,96 +0,0 @@ -// ************************************************************************** -// * This file is part of the FreeFileSync project. It is distributed under * -// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * -// * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * -// ************************************************************************** - -#include "error_log.h" -#include <wx/datetime.h> -#include "../shared/i18n.h" - - -using zen::ErrorLogging; - - -void ErrorLogging::logMsg(const wxString& message, zen::MessageType type) -{ - Entry newEntry; - newEntry.type = type; - newEntry.time = wxDateTime::GetTimeNow(); - newEntry.message = message; - - messages.push_back(newEntry); - - ++statistics[type]; -} - - -int ErrorLogging::typeCount(int types) const -{ - int count = 0; - - if (types & TYPE_INFO) - count += statistics[TYPE_INFO]; - if (types & TYPE_WARNING) - count += statistics[TYPE_WARNING]; - if (types & TYPE_ERROR) - count += statistics[TYPE_ERROR]; - if (types & TYPE_FATAL_ERROR) - count += statistics[TYPE_FATAL_ERROR]; - - return count; -} - - -std::vector<wxString> ErrorLogging::getFormattedMessages(int types) const -{ - std::vector<wxString> output; - - for (std::vector<Entry>::const_iterator i = messages.begin(); i != messages.end(); ++i) - if (i->type & types) - output.push_back(formatMessage(*i)); - - return output; -} - - -wxString ErrorLogging::formatMessage(const Entry& msg) -{ - wxString typeName; - switch (msg.type) - { - case TYPE_INFO: - typeName = _("Info"); - break; - case TYPE_WARNING: - typeName = _("Warning"); - break; - case TYPE_ERROR: - typeName = _("Error"); - break; - case TYPE_FATAL_ERROR: - typeName = _("Fatal Error"); - break; - } - - const wxString prefix = wxString(L"[") + wxDateTime(msg.time).FormatTime() + L"] " + typeName + L": "; - - wxString formattedText = prefix; - for (auto i = msg.message.begin(); i != msg.message.end(); ++i) - if (*i == wxChar('\n')) - { - formattedText += L'\n'; - - wxString blanks; - blanks.resize(prefix.size(), L' '); - formattedText += blanks; - - while (*++i == L'\n') //remove duplicate newlines - ; - --i; - } - else - formattedText += *i; - - return formattedText; -} diff --git a/library/error_log.h b/library/error_log.h deleted file mode 100644 index f8a0c909..00000000 --- a/library/error_log.h +++ /dev/null @@ -1,50 +0,0 @@ -// ************************************************************************** -// * This file is part of the FreeFileSync project. It is distributed under * -// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * -// * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * -// ************************************************************************** - -#ifndef ERRORLOGGING_H_INCLUDED -#define ERRORLOGGING_H_INCLUDED - -#include <wx/string.h> -#include <vector> -#include <map> - - -namespace zen -{ -enum MessageType -{ - TYPE_INFO = 1, - TYPE_WARNING = 2, - TYPE_ERROR = 4, - TYPE_FATAL_ERROR = 8, -}; - -class ErrorLogging -{ -public: - void logMsg(const wxString& message, MessageType type); - - int typeCount(int types = TYPE_INFO | TYPE_WARNING | TYPE_ERROR | TYPE_FATAL_ERROR) const; - - std::vector<wxString> getFormattedMessages(int types = TYPE_INFO | TYPE_WARNING | TYPE_ERROR | TYPE_FATAL_ERROR) const; - -private: - struct Entry - { - MessageType type; - time_t time; - wxString message; - }; - - static wxString formatMessage(const Entry& msg); - - std::vector<Entry> messages; //list of non-resolved errors and warnings - - mutable std::map<MessageType, int> statistics; -}; -} - -#endif // ERRORLOGGING_H_INCLUDED diff --git a/library/hard_filter.cpp b/library/hard_filter.cpp deleted file mode 100644 index 56fb091b..00000000 --- a/library/hard_filter.cpp +++ /dev/null @@ -1,368 +0,0 @@ -// ************************************************************************** -// * This file is part of the FreeFileSync project. It is distributed under * -// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * -// * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * -// ************************************************************************** - -#include "hard_filter.h" -#include "../shared/zstring.h" -#include <wx/string.h> -#include <set> -#include <stdexcept> -#include <vector> -#include "../structures.h" -#include <boost/bind.hpp> -#include "../shared/loki/LokiTypeInfo.h" -#include "../shared/serialize.h" - -using namespace zen; - - -//-------------------------------------------------------------------------------------------------- -bool zen::operator<(const HardFilter& lhs, const HardFilter& rhs) -{ - if (Loki::TypeInfo(typeid(lhs)) != typeid(rhs)) - return Loki::TypeInfo(typeid(lhs)) < typeid(rhs); - - //this and other are same type: - return lhs.cmpLessSameType(rhs); -} - - -void HardFilter::saveFilter(wxOutputStream& stream) const //serialize derived object -{ - //save type information - writeString(stream, uniqueClassIdentifier()); - - //save actual object - save(stream); -} - - -HardFilter::FilterRef HardFilter::loadFilter(wxInputStream& stream) -{ - //read type information - const Zstring uniqueClassId = readString<Zstring>(stream); - - //read actual object - if (uniqueClassId == Zstr("NullFilter")) - return NullFilter::load(stream); - else if (uniqueClassId == Zstr("NameFilter")) - return NameFilter::load(stream); - else if (uniqueClassId == Zstr("CombinedFilter")) - return CombinedFilter::load(stream); - else - throw std::logic_error("Programming Error: Unknown filter!"); -} - - -//-------------------------------------------------------------------------------------------------- -void addFilterEntry(const Zstring& filtername, std::set<Zstring>& fileFilter, std::set<Zstring>& directoryFilter) -{ - Zstring filterFormatted = filtername; - -#ifdef FFS_WIN - //Windows does NOT distinguish between upper/lower-case - makeUpper(filterFormatted); -#elif defined FFS_LINUX - //Linux DOES distinguish between upper/lower-case: nothing to do here -#endif - - const Zstring sepAsterisk = Zstring(FILE_NAME_SEPARATOR) + Zchar('*'); - const Zstring sepQuestionMark = Zstring(FILE_NAME_SEPARATOR) + Zchar('?'); - const Zstring asteriskSep = Zstring(Zstr('*')) + FILE_NAME_SEPARATOR; - const Zstring questionMarkSep = Zstring(Zstr('?')) + FILE_NAME_SEPARATOR; - - //-------------------------------------------------------------------------------------------------- - //add some syntactic sugar: handle beginning of filtername - if (filterFormatted.StartsWith(FILE_NAME_SEPARATOR)) - { - //remove leading separators (keep BEFORE test for Zstring::empty()!) - filterFormatted = filterFormatted.AfterFirst(FILE_NAME_SEPARATOR); - } - else if (filterFormatted.StartsWith(asteriskSep) || // *\abc - filterFormatted.StartsWith(questionMarkSep)) // ?\abc - { - addFilterEntry(Zstring(filterFormatted.c_str() + 1), fileFilter, directoryFilter); //prevent further recursion by prefix - } - - //-------------------------------------------------------------------------------------------------- - //even more syntactic sugar: handle end of filtername - if (filterFormatted.EndsWith(FILE_NAME_SEPARATOR)) - { - const Zstring candidate = filterFormatted.BeforeLast(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(FILE_NAME_SEPARATOR); - if (!candidate.empty()) - directoryFilter.insert(candidate); //only relevant for directory filtering - } - else if (!filterFormatted.empty()) - { - fileFilter. insert(filterFormatted); - directoryFilter.insert(filterFormatted); - } -} - - -namespace -{ -template <class T> -inline -const T* cStringFind(const T* str1, T ch) //strchr() -{ - while (*str1 != ch) //ch is allowed to be 0 by contract! must return end of string in this case - { - if (*str1 == 0) - return NULL; - ++str1; - } - return str1; -} - -bool matchesMask(const Zchar* str, const Zchar* mask) -{ - for (Zchar ch; (ch = *mask) != 0; ++mask, ++str) - { - switch (ch) - { - case Zstr('?'): - if (*str == 0) - return false; - break; - - case Zstr('*'): - //advance to next non-*/? char - do - { - ++mask; - ch = *mask; - } - while (ch == Zstr('*') || ch == Zstr('?')); - //if mask ends with '*': - if (ch == 0) - return true; - - ++mask; - while ((str = cStringFind(str, ch)) != NULL) - { - ++str; - if (matchesMask(str, mask)) - return true; - } - return false; - - default: - if (*str != ch) - return false; - } - } - return *str == 0; -} - -//returns true if string matches at least the beginning of mask -inline -bool matchesMaskBegin(const Zchar* str, const Zchar* mask) -{ - for (Zchar ch; (ch = *mask) != 0; ++mask, ++str) - { - if (*str == 0) - return true; - - switch (ch) - { - case Zstr('?'): - break; - - case Zstr('*'): - return true; - - default: - if (*str != ch) - return false; - } - } - return *str == 0; -} -} - - -class MatchFound : public std::unary_function<Zstring, bool> -{ -public: - MatchFound(const Zstring& name) : name_(name) {} - - bool operator()(const Zstring& mask) const - { - return matchesMask(name_.c_str(), mask.c_str()); - } -private: - const Zstring& name_; -}; - - -inline -bool matchesFilter(const Zstring& nameFormatted, const std::set<Zstring>& filter) -{ - return std::find_if(filter.begin(), filter.end(), MatchFound(nameFormatted)) != filter.end(); -} - - -inline -bool matchesFilterBegin(const Zstring& nameFormatted, const std::set<Zstring>& filter) -{ - for (std::set<Zstring>::const_iterator i = filter.begin(); i != filter.end(); ++i) - if (matchesMaskBegin(nameFormatted.c_str(), i->c_str())) - return true; - return false; - - // return std::find_if(filter.begin(), filter.end(), - // boost::bind(matchesMaskBegin, nameFormatted.c_str(), _1)) != filter.end(); -} - - -std::vector<Zstring> compoundStringToFilter(const Zstring& filterString) -{ - //delimiters may be ';' or '\n' - std::vector<Zstring> output; - - const std::vector<Zstring> blocks = split(filterString, Zchar(';')); - std::for_each(blocks.begin(), blocks.end(), - [&](const Zstring& item) - { - const std::vector<Zstring> blocks2 = split(item, Zchar('\n')); - - std::for_each(blocks2.begin(), blocks2.end(), - [&](Zstring entry) - { - trim(entry); - if (!entry.empty()) - output.push_back(entry); - }); - }); - - return output; -} - -//################################################################################################# -NameFilter::NameFilter(const Zstring& includeFilter, const Zstring& excludeFilter) : - includeFilterTmp(includeFilter), //save constructor arguments for serialization - excludeFilterTmp(excludeFilter) -{ - //no need for regular expressions! In tests wxRegex was by factor of 10 slower than wxString::Matches()!! - - //load filter into vectors of strings - //delimiters may be ';' or '\n' - const std::vector<Zstring>& includeList = compoundStringToFilter(includeFilter); - const std::vector<Zstring>& excludeList = compoundStringToFilter(excludeFilter); - - //setup include/exclude filters for files and directories - std::for_each(includeList.begin(), includeList.end(), [&](const Zstring& entry) { addFilterEntry(entry, filterFileIn, filterFolderIn); }); - std::for_each(excludeList.begin(), excludeList.end(), [&](const Zstring& entry) { addFilterEntry(entry, filterFileEx, filterFolderEx); }); -} - - -bool NameFilter::passFileFilter(const Zstring& relFilename) const -{ -#ifdef FFS_WIN //Windows does NOT distinguish between upper/lower-case - Zstring nameFormatted = relFilename; - makeUpper(nameFormatted); -#elif defined FFS_LINUX //Linux DOES distinguish between upper/lower-case - const Zstring& nameFormatted = relFilename; //nothing to do here -#endif - - return matchesFilter(nameFormatted, filterFileIn) && //process include filters - !matchesFilter(nameFormatted, filterFileEx); //process exclude filters -} - - -bool NameFilter::passDirFilter(const Zstring& relDirname, bool* subObjMightMatch) const -{ - assert(subObjMightMatch == NULL || *subObjMightMatch == true); //check correct usage - -#ifdef FFS_WIN //Windows does NOT distinguish between upper/lower-case - Zstring nameFormatted = relDirname; - makeUpper(nameFormatted); -#elif defined FFS_LINUX //Linux DOES distinguish between upper/lower-case - const Zstring& nameFormatted = relDirname; //nothing to do here -#endif - - if (matchesFilter(nameFormatted, filterFolderEx)) //process exclude filters - { - if (subObjMightMatch) - *subObjMightMatch = false; //exclude subfolders/subfiles as well - return false; - } - - if (!matchesFilter(nameFormatted, filterFolderIn)) //process include filters - { - if (subObjMightMatch) - { - const Zstring& subNameBegin = nameFormatted + FILE_NAME_SEPARATOR; //const-ref optimization - - *subObjMightMatch = matchesFilterBegin(subNameBegin, filterFileIn) || //might match a file in subdirectory - matchesFilterBegin(subNameBegin, filterFolderIn); //or another subdirectory - } - return false; - } - - return true; -} - - -bool NameFilter::isNull() const -{ - static NameFilter output(Zstr("*"), Zstring()); - return *this == output; -} - - -bool NameFilter::cmpLessSameType(const HardFilter& other) const -{ - assert(typeid(*this) == typeid(other)); //always given in this context! - - const NameFilter& otherNameFilt = static_cast<const NameFilter&>(other); - - if (filterFileIn != otherNameFilt.filterFileIn) - return filterFileIn < otherNameFilt.filterFileIn; - - if (filterFolderIn != otherNameFilt.filterFolderIn) - return filterFolderIn < otherNameFilt.filterFolderIn; - - if (filterFileEx != otherNameFilt.filterFileEx) - return filterFileEx < otherNameFilt.filterFileEx; - - if (filterFolderEx != otherNameFilt.filterFolderEx) - return filterFolderEx < otherNameFilt.filterFolderEx; - - return false; //vectors equal -} - - -Zstring NameFilter::uniqueClassIdentifier() const -{ - return Zstr("NameFilter"); -} - - -void NameFilter::save(wxOutputStream& stream) const -{ - writeString(stream, includeFilterTmp); - writeString(stream, excludeFilterTmp); -} - - -HardFilter::FilterRef NameFilter::load(wxInputStream& stream) //"constructor" -{ - const Zstring include = readString<Zstring>(stream); - const Zstring exclude = readString<Zstring>(stream); - - return FilterRef(new NameFilter(include, exclude)); -} diff --git a/library/hard_filter.h b/library/hard_filter.h deleted file mode 100644 index bb0b6d54..00000000 --- a/library/hard_filter.h +++ /dev/null @@ -1,279 +0,0 @@ -// ************************************************************************** -// * This file is part of the FreeFileSync project. It is distributed under * -// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * -// * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * -// ************************************************************************** - -#ifndef FFS_FILTER_H_INCLUDED -#define FFS_FILTER_H_INCLUDED - -#include "../shared/zstring.h" -#include <set> -#include <memory> -#include <wx/stream.h> - -namespace zen -{ -//------------------------------------------------------------------ -/* -Semantics of HardFilter: -1. using it creates a NEW folder hierarchy! -> must be considered by <Automatic>-mode! (fortunately it turns out, doing nothing already has perfect semantics :) -2. it applies equally to both sides => it always matches either both sides or none! => can be used while traversing a single folder! - - class hierarchy: - - HardFilter (interface) - /|\ - _________|_____________ - | | | -NullFilter NameFilter CombinedFilter -*/ - -class HardFilter //interface for filtering -{ -public: - virtual ~HardFilter() {} - - //filtering - virtual bool passFileFilter(const Zstring& relFilename) const = 0; - virtual bool passDirFilter (const Zstring& relDirname, bool* subObjMightMatch) const = 0; - //subObjMightMatch: file/dir in subdirectories could(!) match - //note: variable is only set if passDirFilter returns false! - - virtual bool isNull() const = 0; //filter is equivalent to NullFilter, but may be technically slower - - typedef std::shared_ptr<const HardFilter> FilterRef; //always bound by design! - - //serialization - void saveFilter(wxOutputStream& stream) const; //serialize derived object - static FilterRef loadFilter(wxInputStream& stream); //CAVEAT!!! adapt this method for each new derivation!!! - -private: - friend bool operator< (const HardFilter& lhs, const HardFilter& rhs); - - virtual void save(wxOutputStream& stream) const = 0; //serialization - virtual Zstring uniqueClassIdentifier() const = 0; //get identifier, used for serialization - virtual bool cmpLessSameType(const HardFilter& other) const = 0; //typeid(*this) == typeid(other) in this context! -}; - -inline bool operator==(const HardFilter& lhs, const HardFilter& rhs) { return !(lhs < rhs) && !(rhs < lhs); } -inline bool operator!=(const HardFilter& lhs, const HardFilter& rhs) { return !(lhs == rhs); } - - -//small helper method: merge two hard filters (thereby remove Null-filters) -HardFilter::FilterRef combineFilters(const HardFilter::FilterRef& first, - const HardFilter::FilterRef& second); - - -class NullFilter : public HardFilter //no filtering at all -{ -public: - virtual bool passFileFilter(const Zstring& relFilename) const; - virtual bool passDirFilter(const Zstring& relDirname, bool* subObjMightMatch) const; - virtual bool isNull() const; - -private: - friend class HardFilter; - virtual void save(wxOutputStream& stream) const {} - virtual Zstring uniqueClassIdentifier() const; - static FilterRef load(wxInputStream& stream); //"serial constructor" - virtual bool cmpLessSameType(const HardFilter& other) const; -}; - - -class NameFilter : public HardFilter //standard filter by filename -{ -public: - NameFilter(const Zstring& includeFilter, const Zstring& excludeFilter); - - virtual bool passFileFilter(const Zstring& relFilename) const; - virtual bool passDirFilter(const Zstring& relDirname, bool* subObjMightMatch) const; - virtual bool isNull() const; - -private: - friend class HardFilter; - virtual void save(wxOutputStream& stream) const; - virtual Zstring uniqueClassIdentifier() const; - static FilterRef load(wxInputStream& stream); //"serial constructor" - virtual bool cmpLessSameType(const HardFilter& other) const; - - std::set<Zstring> filterFileIn; //upper case (windows) - std::set<Zstring> filterFolderIn; // - std::set<Zstring> filterFileEx; // - std::set<Zstring> filterFolderEx; // - - const Zstring includeFilterTmp; //save constructor arguments for serialization - const Zstring excludeFilterTmp; // -}; - - -class CombinedFilter : public HardFilter //combine two filters to match if and only if both match -{ -public: - CombinedFilter(const FilterRef& first, const FilterRef& second) : first_(first), second_(second) {} - - virtual bool passFileFilter(const Zstring& relFilename) const; - virtual bool passDirFilter(const Zstring& relDirname, bool* subObjMightMatch) const; - virtual bool isNull() const; - -private: - friend class HardFilter; - virtual void save(wxOutputStream& stream) const; - virtual Zstring uniqueClassIdentifier() const; - static FilterRef load(wxInputStream& stream); //"serial constructor" - virtual bool cmpLessSameType(const HardFilter& other) const; - - const FilterRef first_; - const FilterRef second_; -}; - - - - - - - - - - - - - - - - - - -//---------------Inline Implementation--------------------------------------------------- -inline -HardFilter::FilterRef NullFilter::load(wxInputStream& stream) //"serial constructor" -{ - return FilterRef(new NullFilter); -} - - -inline -bool NullFilter::passFileFilter(const Zstring& relFilename) const -{ - return true; -} - - -inline -bool NullFilter::passDirFilter(const Zstring& relDirname, bool* subObjMightMatch) const -{ - assert(subObjMightMatch == NULL || *subObjMightMatch == true); //check correct usage - return true; -} - - -inline -bool NullFilter::isNull() const -{ - return true; -} - - -inline -bool NullFilter::cmpLessSameType(const HardFilter& other) const -{ - assert(typeid(*this) == typeid(other)); //always given in this context! - return false; -} - - -inline -Zstring NullFilter::uniqueClassIdentifier() const -{ - return Zstr("NullFilter"); -} - - -inline -bool CombinedFilter::passFileFilter(const Zstring& relFilename) const -{ - return first_->passFileFilter(relFilename) && //short-circuit behavior - second_->passFileFilter(relFilename); -} - - -inline -bool CombinedFilter::passDirFilter(const Zstring& relDirname, bool* subObjMightMatch) const -{ - return first_->passDirFilter(relDirname, subObjMightMatch) && //short-circuit behavior: subObjMightMatch handled correctly! - second_->passDirFilter(relDirname, subObjMightMatch); -} - - -inline -bool CombinedFilter::isNull() const -{ - return first_->isNull() && second_->isNull(); -} - - -inline -bool CombinedFilter::cmpLessSameType(const HardFilter& other) const -{ - assert(typeid(*this) == typeid(other)); //always given in this context! - - const CombinedFilter& otherCombFilt = static_cast<const CombinedFilter&>(other); - - if (*first_ != *otherCombFilt.first_) - return *first_ < *otherCombFilt.first_; - - return *second_ < *otherCombFilt.second_; -} - - -inline -Zstring CombinedFilter::uniqueClassIdentifier() const -{ - return Zstr("CombinedFilter"); -} - - -inline -void CombinedFilter::save(wxOutputStream& stream) const -{ - first_->saveFilter(stream); - second_->saveFilter(stream); -} - - -inline -HardFilter::FilterRef CombinedFilter::load(wxInputStream& stream) //"constructor" -{ - FilterRef first = loadFilter(stream); - FilterRef second = loadFilter(stream); - - return combineFilters(first, second); -} - - -inline -HardFilter::FilterRef combineFilters(const HardFilter::FilterRef& first, - const HardFilter::FilterRef& second) -{ - if (first->isNull()) - { - if (second->isNull()) - return HardFilter::FilterRef(new NullFilter); - else - return second; - } - else - { - if (second->isNull()) - return first; - else - return HardFilter::FilterRef(new CombinedFilter(first, second)); - } -} - - -} - - -#endif // FFS_FILTER_H_INCLUDED - diff --git a/library/icon_buffer.cpp b/library/icon_buffer.cpp deleted file mode 100644 index b8ee66cd..00000000 --- a/library/icon_buffer.cpp +++ /dev/null @@ -1,613 +0,0 @@ -// ************************************************************************** -// * This file is part of the FreeFileSync project. It is distributed under * -// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * -// * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * -// ************************************************************************** - -#include "icon_buffer.h" -#include <queue> -#include <set> -#include "../shared/boost_thread_wrap.h" //include <boost/thread.hpp> -#include "../shared/loki/ScopeGuard.h" -#include <boost/thread/once.hpp> - -#ifdef FFS_WIN -#include <wx/msw/wrapwin.h> //includes "windows.h" -#include "../shared/dll_loader.h" -#include "../shared/Thumbnail/thumbnail.h" - -#elif defined FFS_LINUX -#include <giomm/file.h> -#include <gtkmm/icontheme.h> -#include <gtkmm/main.h> -#endif - -using namespace zen; - - -const size_t BUFFER_SIZE_MAX = 800; //maximum number of icons to buffer - - -int zen::IconBuffer::cvrtSize(IconSize sz) //get size in pixel -{ - switch (sz) - { - case SIZE_SMALL: -#ifdef FFS_WIN - return 16; -#elif defined FFS_LINUX - return 24; -#endif - case SIZE_MEDIUM: - return 48; - case SIZE_LARGE: - return 128; - } - assert(false); - return 0; -} - - -class IconHolder //handle HICON/GdkPixbuf ownership WITHOUT ref-counting to allow thread-safe usage (in contrast to wxIcon) -{ -public: -#ifdef FFS_WIN - typedef HICON HandleType; -#elif defined FFS_LINUX - typedef GdkPixbuf* HandleType; -#endif - - IconHolder(HandleType handle = 0) : handle_(handle) {} //take ownership! - - //icon holder has value semantics! - IconHolder(const IconHolder& other) : handle_(other.handle_ == NULL ? NULL : -#ifdef FFS_WIN - ::CopyIcon(other.handle_) -#elif defined FFS_LINUX - ::gdk_pixbuf_copy(other.handle_) //create new Pix buf with reference count 1 or return 0 on error -#endif - ) {} - - IconHolder& operator=(const IconHolder& other) - { - IconHolder(other).swap(*this); - return *this; - } - - ~IconHolder() - { - if (handle_ != NULL) -#ifdef FFS_WIN - ::DestroyIcon(handle_); -#elif defined FFS_LINUX - ::g_object_unref(handle_); -#endif - } - - void swap(IconHolder& other) { std::swap(handle_, other.handle_); } //throw() - - wxIcon toWxIcon(int expectedSize) const //copy HandleType, caller needs to take ownership! - { - if (handle_ == NULL) - return wxNullIcon; - - IconHolder clone(*this); - - wxIcon newIcon; //attention: wxIcon uses reference counting! -#ifdef FFS_WIN - newIcon.SetHICON(clone.handle_); - - { //this block costs ~0.04 ms - ICONINFO icoInfo = {}; - if (::GetIconInfo(clone.handle_, &icoInfo)) - { - ::DeleteObject(icoInfo.hbmMask); //nice potential for a GDI leak! - LOKI_ON_BLOCK_EXIT2(::DeleteObject(icoInfo.hbmColor)); // - - BITMAP bmpInfo = {}; - if (::GetObject(icoInfo.hbmColor, //__in HGDIOBJ hgdiobj, - sizeof(BITMAP), //__in int cbBuffer, - &bmpInfo) != 0) // __out LPVOID lpvObject - { - const int maxExtent = std::max(bmpInfo.bmWidth, bmpInfo.bmHeight); - if (maxExtent > expectedSize) - { - bmpInfo.bmWidth = bmpInfo.bmWidth * expectedSize / maxExtent; //scale those Vista jumbo 256x256 icons down! - bmpInfo.bmHeight = bmpInfo.bmHeight * expectedSize / maxExtent; // - } - newIcon.SetSize(bmpInfo.bmWidth, bmpInfo.bmHeight); //wxIcon is stretched to this size - } - } - } - //no stretching for now - //newIcon.SetSize(defaultSize, defaultSize); //icon is stretched to this size if referenced HICON differs - -#elif defined FFS_LINUX // - newIcon.SetPixbuf(clone.handle_); // transfer ownership!! -#endif // - clone.handle_ = NULL; // - return newIcon; - } - -private: - HandleType handle_; - struct ConversionToBool { int dummy; }; - -public: - //use member pointer as implicit conversion to bool (C++ Templates - Vandevoorde/Josuttis; chapter 20) - operator int ConversionToBool::* () const { return handle_ != NULL ? &ConversionToBool::dummy : NULL; } -}; - - -#ifdef FFS_WIN -namespace -{ -Zstring getFileExtension(const Zstring& filename) -{ - const Zstring shortName = afterLast(filename, Zchar('\\')); //warning: using windows file name separator! - - return shortName.find(Zchar('.')) != Zstring::npos ? - filename.AfterLast(Zchar('.')) : - Zstring(); -} - -std::set<Zstring, LessFilename> priceyExtensions; //thread-safe! -boost::once_flag initExtensionsOnce = BOOST_ONCE_INIT; // - - -//test for extension for non-thumbnail icons that physically have to be retrieved from disc -bool isCheapExtension(const Zstring& extension) -{ - boost::call_once(initExtensionsOnce, []() - { - priceyExtensions.insert(L"exe"); - priceyExtensions.insert(L"lnk"); - priceyExtensions.insert(L"ico"); - priceyExtensions.insert(L"ani"); - priceyExtensions.insert(L"cur"); - priceyExtensions.insert(L"url"); - priceyExtensions.insert(L"msc"); - priceyExtensions.insert(L"scr"); - }); - return priceyExtensions.find(extension) == priceyExtensions.end(); -} - - -bool vistaOrLater() -{ - OSVERSIONINFO osvi = {}; - osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); - - //IFileOperation is supported with Vista and later - if (::GetVersionEx(&osvi)) - return osvi.dwMajorVersion > 5; - //XP has majorVersion == 5, minorVersion == 1 - //Vista has majorVersion == 6, minorVersion == 0 - //version overview: http://msdn.microsoft.com/en-us/library/ms724834(VS.85).aspx - return false; -} - - -bool wereVistaOrLater = false; -boost::once_flag initVistaFlagOnce = BOOST_ONCE_INIT; - - -int getShilIconType(IconBuffer::IconSize sz) -{ - boost::call_once(initVistaFlagOnce, []() - { - wereVistaOrLater = vistaOrLater(); - }); - - switch (sz) - { - case IconBuffer::SIZE_SMALL: - return SHIL_SMALL; //16x16, but the size can be customized by the user. - case IconBuffer::SIZE_MEDIUM: - return SHIL_EXTRALARGE; //typically 48x48, but the size can be customized by the user. - case IconBuffer::SIZE_LARGE: - return wereVistaOrLater ? SHIL_JUMBO //normally 256x256 pixels -> will be scaled down by IconHolder - : SHIL_EXTRALARGE; //XP doesn't have jumbo icons - } - return SHIL_SMALL; -} - - -util::DllFun<thumb::GetIconByIndexFct> getIconByIndex; -boost::once_flag initGetIconByIndexOnce = BOOST_ONCE_INIT; - - -IconHolder getIconByAttribute(LPCWSTR pszPath, DWORD dwFileAttributes, IconBuffer::IconSize sz) -{ - //NOTE: CoInitializeEx()/CoUninitialize() needs to be called for THIS thread! - SHFILEINFO fileInfo = {}; //initialize hIcon - DWORD_PTR imgList = ::SHGetFileInfo(pszPath, //Windows 7 doesn't like this parameter to be an empty string - dwFileAttributes, - &fileInfo, - sizeof(fileInfo), - SHGFI_SYSICONINDEX | SHGFI_USEFILEATTRIBUTES); - //no need to IUnknown::Release() imgList! - if (!imgList) - return NULL; - - boost::call_once(initGetIconByIndexOnce, []() //thread-safe init - { - getIconByIndex = util::DllFun<thumb::GetIconByIndexFct>(thumb::getDllName(), thumb::getIconByIndexFctName); - }); - return getIconByIndex ? static_cast<HICON>(getIconByIndex(fileInfo.iIcon, getShilIconType(sz))) : NULL; -} - - -IconHolder getAssociatedIconByExt(const Zstring& extension, IconBuffer::IconSize sz) -{ - //no read-access to disk! determine icon by extension - return getIconByAttribute((Zstr("dummy.") + extension).c_str(), FILE_ATTRIBUTE_NORMAL, sz); -} - - -util::DllFun<thumb::GetThumbnailFct> getThumbnailIcon; -boost::once_flag initThumbnailOnce = BOOST_ONCE_INIT; -} -#endif -//################################################################################################################################################ - - -IconHolder getThumbnail(const Zstring& filename, int requestedSize) //return 0 on failure -{ -#ifdef FFS_WIN - using namespace thumb; - - boost::call_once(initThumbnailOnce, []() //note: "getThumbnail" function itself is already thread-safe - { - getThumbnailIcon = util::DllFun<GetThumbnailFct>(getDllName(), getThumbnailFctName); - }); - return getThumbnailIcon ? static_cast< ::HICON>(getThumbnailIcon(filename.c_str(), requestedSize)) : NULL; - -#elif defined FFS_LINUX - //call Gtk::Main::init_gtkmm_internals() on application startup!! - try - { - Glib::RefPtr<Gdk::Pixbuf> iconPixbuf = Gdk::Pixbuf::create_from_file(filename.c_str(), requestedSize, requestedSize); - if (iconPixbuf) - return IconHolder(iconPixbuf->gobj_copy()); //copy and pass icon ownership (may be 0) - } - catch (const Glib::Error&) {} - - return IconHolder(); -#endif -} - - -IconHolder getAssociatedIcon(const Zstring& filename, IconBuffer::IconSize sz) -{ - //1. try to load thumbnails - switch (sz) - { - case IconBuffer::SIZE_SMALL: - break; - case IconBuffer::SIZE_MEDIUM: - case IconBuffer::SIZE_LARGE: - { - IconHolder ico = getThumbnail(filename, IconBuffer::cvrtSize(sz)); - if (ico) - return ico; - //else: fallback to non-thumbnail icon - } - break; - } - - //2. retrieve file icons -#ifdef FFS_WIN - //perf: optimize fallback case for SIZE_MEDIUM and SIZE_LARGE: - const Zstring& extension = getFileExtension(filename); - if (isCheapExtension(extension)) //"pricey" extensions are stored with fullnames and are read from disk, while cheap ones require just the extension - return getAssociatedIconByExt(extension, sz); - //result will not be buffered under extension name, but full filename; this is okay, since we're in SIZE_MEDIUM or SIZE_LARGE context, - //which means the access to get thumbnail failed: thumbnail failure is not dependent from extension in general! - - SHFILEINFO fileInfo = {}; - DWORD_PTR imgList = ::SHGetFileInfo(filename.c_str(), //zen::removeLongPathPrefix(fileName), //::SHGetFileInfo() can't handle \\?\-prefix! - 0, - &fileInfo, - sizeof(fileInfo), - SHGFI_SYSICONINDEX); - //Quote: "The IImageList pointer type, such as that returned in the ppv parameter, can be cast as an HIMAGELIST as - // needed; for example, for use in a list view. Conversely, an HIMAGELIST can be cast as a pointer to an IImageList." - //http://msdn.microsoft.com/en-us/library/windows/desktop/bb762185(v=vs.85).aspx - - if (!imgList) - return NULL; - //imgList->Release(); //empiric study: crash on XP if we release this! Seems we do not own it... -> also no GDI leak on Win7 -> okay - //another comment on http://msdn.microsoft.com/en-us/library/bb762179(v=VS.85).aspx describes exact same behavior on Win7/XP - - boost::call_once(initGetIconByIndexOnce, []() //thread-safe init - { - getIconByIndex = util::DllFun<thumb::GetIconByIndexFct>(thumb::getDllName(), thumb::getIconByIndexFctName); - }); - return getIconByIndex ? static_cast<HICON>(getIconByIndex(fileInfo.iIcon, getShilIconType(sz))) : NULL; - -#elif defined FFS_LINUX - const int requestedSize = IconBuffer::cvrtSize(sz); - //call Gtk::Main::init_gtkmm_internals() on application startup!! - try - { - Glib::RefPtr<Gio::File> fileObj = Gio::File::create_for_path(filename.c_str()); //never fails - Glib::RefPtr<Gio::FileInfo> fileInfo = fileObj->query_info(G_FILE_ATTRIBUTE_STANDARD_ICON); - if (fileInfo) - { - Glib::RefPtr<Gio::Icon> gicon = fileInfo->get_icon(); - if (gicon) - { - Glib::RefPtr<Gtk::IconTheme> iconTheme = Gtk::IconTheme::get_default(); - if (iconTheme) - { - Gtk::IconInfo iconInfo = iconTheme->lookup_icon(gicon, requestedSize, Gtk::ICON_LOOKUP_USE_BUILTIN); //this may fail if icon is not installed on system - if (iconInfo) - { - Glib::RefPtr<Gdk::Pixbuf> iconPixbuf = iconInfo.load_icon(); //render icon into Pixbuf - if (iconPixbuf) - return IconHolder(iconPixbuf->gobj_copy()); //copy and pass icon ownership (may be 0) - } - } - } - } - } - catch (const Glib::Error&) {} - - try //fallback: icon lookup may fail because some icons are currently not present on system - { - Glib::RefPtr<Gtk::IconTheme> iconTheme = Gtk::IconTheme::get_default(); - if (iconTheme) - { - Glib::RefPtr<Gdk::Pixbuf> iconPixbuf = iconTheme->load_icon("misc", requestedSize, Gtk::ICON_LOOKUP_USE_BUILTIN); - if (!iconPixbuf) - iconPixbuf = iconTheme->load_icon("text-x-generic", requestedSize, Gtk::ICON_LOOKUP_USE_BUILTIN); - if (iconPixbuf) - return IconHolder(iconPixbuf->gobj_copy()); //copy and pass icon ownership (may be 0) - } - } - catch (const Glib::Error&) {} - - //fallback fallback - return IconHolder(); -#endif -} - - -IconHolder getDirectoryIcon(IconBuffer::IconSize sz) -{ -#ifdef FFS_WIN - return getIconByAttribute(L"dummy", //Windows 7 doesn't like this parameter to be an empty string! - FILE_ATTRIBUTE_DIRECTORY, sz); -#elif defined FFS_LINUX - return ::getAssociatedIcon(Zstr("/usr/"), sz); //all directories will look like "/usr/" -#endif -} - - -IconHolder getFileIcon(IconBuffer::IconSize sz) -{ -#ifdef FFS_WIN - return getIconByAttribute(L"dummy", FILE_ATTRIBUTE_NORMAL, sz); -#elif defined FFS_LINUX - const int requestedSize = IconBuffer::cvrtSize(sz); - try - { - Glib::RefPtr<Gtk::IconTheme> iconTheme = Gtk::IconTheme::get_default(); - if (iconTheme) - { - Glib::RefPtr<Gdk::Pixbuf> iconPixbuf = iconTheme->load_icon("misc", requestedSize, Gtk::ICON_LOOKUP_USE_BUILTIN); - if (!iconPixbuf) - iconPixbuf = iconTheme->load_icon("text-x-generic", requestedSize, Gtk::ICON_LOOKUP_USE_BUILTIN); - if (iconPixbuf) - return IconHolder(iconPixbuf->gobj_copy()); // transfer ownership!! - } - } - catch (const Glib::Error&) {} - - return IconHolder(); -#endif -} -//################################################################################################################################################ - - -//---------------------- Shared Data ------------------------- -struct WorkLoad -{ -public: - Zstring extractNextFile() //context of worker thread, blocking - { - boost::unique_lock<boost::mutex> dummy(lockFiles); - - while (filesToLoad.empty()) - conditionNewFiles.timed_wait(dummy, boost::get_system_time() + boost::posix_time::milliseconds(50)); //interruption point! - - Zstring fileName = filesToLoad.back(); - filesToLoad.pop_back(); - return fileName; - } - - void setWorkload(const std::vector<Zstring>& newLoad) //context of main thread - { - { - boost::unique_lock<boost::mutex> dummy(lockFiles); - filesToLoad = newLoad; - } - conditionNewFiles.notify_one(); - //condition handling, see: http://www.boost.org/doc/libs/1_43_0/doc/html/thread/synchronization.html#thread.synchronization.condvar_ref - } - -private: - std::vector<Zstring> filesToLoad; //processes last elements of vector first! - boost::mutex lockFiles; - boost::condition_variable conditionNewFiles; //signal event: data for processing available -}; - - -typedef std::map<Zstring, IconHolder, LessFilename> NameIconMap; //entryName/icon -> note: Zstring is thread-safe -typedef std::queue<Zstring> IconDbSequence; //entryName - -class Buffer -{ -public: - bool requestFileIcon(const Zstring& fileName, IconHolder* icon = NULL) - { - boost::lock_guard<boost::mutex> dummy(lockBuffer); - - auto iter = iconMappping.find(fileName); - if (iter != iconMappping.end()) - { - if (icon != NULL) - *icon = iter->second; - return true; - } - return false; - } - - void insertIntoBuffer(const Zstring& entryName, const IconHolder& icon) //called by worker thread - { - boost::lock_guard<boost::mutex> dummy(lockBuffer); - - //thread saftey: icon uses ref-counting! But is NOT shared with main thread! - auto rc = iconMappping.insert(std::make_pair(entryName, icon)); - if (rc.second) //if insertion took place - iconSequence.push(entryName); //note: sharing Zstring with IconDB!!! - - assert(iconMappping.size() == iconSequence.size()); - - //remove elements if buffer becomes too big: - if (iconMappping.size() > BUFFER_SIZE_MAX) //limit buffer size: critical because GDI resources are limited (e.g. 10000 on XP per process) - { - //remove oldest element - iconMappping.erase(iconSequence.front()); - iconSequence.pop(); - } - } - -private: - boost::mutex lockBuffer; - NameIconMap iconMappping; //use synchronisation when accessing this! - IconDbSequence iconSequence; //save sequence of buffer entry to delete oldest elements -}; -//------------------------------------------------------------- -//################################################################################################################################################ - -class WorkerThread //lifetime is part of icon buffer -{ -public: - WorkerThread(const std::shared_ptr<WorkLoad>& workload, - const std::shared_ptr<Buffer>& buffer, - IconBuffer::IconSize sz) : - workload_(workload), - buffer_(buffer), - icoSize(sz) {} - - void operator()(); //thread entry - -private: - std::shared_ptr<WorkLoad> workload_; //main/worker thread may access different shared_ptr instances safely (even though they have the same target!) - std::shared_ptr<Buffer> buffer_; //http://www.boost.org/doc/libs/1_43_0/libs/smart_ptr/shared_ptr.htm?sess=8153b05b34d890e02d48730db1ff7ddc#ThreadSafety - const IconBuffer::IconSize icoSize; -}; - - -void WorkerThread::operator()() //thread entry -{ - //failure to initialize COM for each thread is a source of hard to reproduce bugs: https://sourceforge.net/tracker/?func=detail&aid=3160472&group_id=234430&atid=1093080 -#ifdef FFS_WIN - //Prerequisites, see thumbnail.h - - //1. Initialize COM - ::CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE); - LOKI_ON_BLOCK_EXIT2(::CoUninitialize()); - - //2. Initialize system image list - typedef BOOL (WINAPI *FileIconInitFun)(BOOL fRestoreCache); - const util::DllFun<FileIconInitFun> fileIconInit(L"Shell32.dll", reinterpret_cast<LPCSTR>(660)); - assert(fileIconInit); - if (fileIconInit) - fileIconInit(false); //TRUE to restore the system image cache from disk; FALSE otherwise. -#endif - - while (true) - { - boost::this_thread::interruption_point(); - - const Zstring fileName = workload_->extractNextFile(); //start work: get next icon to load - - if (buffer_->requestFileIcon(fileName)) - continue; //icon already in buffer: skip - - buffer_->insertIntoBuffer(fileName, getAssociatedIcon(fileName, icoSize)); - } -} -//######################### redirect to impl ##################################################### - -struct IconBuffer::Pimpl -{ - Pimpl() : - workload(std::make_shared<WorkLoad>()), - buffer(std::make_shared<Buffer>()) {} - - std::shared_ptr<WorkLoad> workload; - std::shared_ptr<Buffer> buffer; - - boost::thread worker; -}; - - -IconBuffer::IconBuffer(IconSize sz) : - pimpl(new Pimpl), - icoSize(sz), - genDirIcon(::getDirectoryIcon(sz).toWxIcon(cvrtSize(icoSize))), - genFileIcon(::getFileIcon(sz).toWxIcon(cvrtSize(icoSize))) -{ - pimpl->worker = boost::thread(WorkerThread(pimpl->workload, pimpl->buffer, sz)); -} - - -IconBuffer::~IconBuffer() -{ - setWorkload(std::vector<Zstring>()); //make sure interruption point is always reached! - pimpl->worker.interrupt(); - pimpl->worker.join(); -} - - -bool IconBuffer::requestFileIcon(const Zstring& filename, wxIcon* icon) -{ - auto getIcon = [&](const Zstring& entryName) -> bool - { - if (!icon) - return pimpl->buffer->requestFileIcon(entryName); - - IconHolder heldIcon; - if (!pimpl->buffer->requestFileIcon(entryName, &heldIcon)) - return false; - *icon = heldIcon.toWxIcon(cvrtSize(icoSize)); - return true; - }; - -#ifdef FFS_WIN - //perf: let's read icons which don't need file access right away! No async delay justified! - if (icoSize == IconBuffer::SIZE_SMALL) //non-thumbnail view, we need file type icons only! - { - const Zstring& extension = getFileExtension(filename); - if (isCheapExtension(extension)) //"pricey" extensions are stored with fullnames and are read from disk, while cheap ones require just the extension - { - if (!getIcon(extension)) - { - IconHolder heldIcon = getAssociatedIconByExt(extension, icoSize); //fast! - pimpl->buffer->insertIntoBuffer(extension, heldIcon); - if (icon) - *icon = heldIcon.toWxIcon(cvrtSize(icoSize)); - } - return true; - } - } -#endif - - return getIcon(filename); -} - -void IconBuffer::setWorkload(const std::vector<Zstring>& load) { pimpl->workload->setWorkload(load); } diff --git a/library/icon_buffer.h b/library/icon_buffer.h deleted file mode 100644 index cbd582f2..00000000 --- a/library/icon_buffer.h +++ /dev/null @@ -1,50 +0,0 @@ -// ************************************************************************** -// * This file is part of the FreeFileSync project. It is distributed under * -// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * -// * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * -// ************************************************************************** - -#ifndef ICONBUFFER_H_INCLUDED -#define ICONBUFFER_H_INCLUDED - -#include "../shared/zstring.h" -#include <memory> -#include <wx/icon.h> - - -namespace zen -{ -class IconBuffer -{ -public: - enum IconSize - { - SIZE_SMALL, - SIZE_MEDIUM, - SIZE_LARGE - }; - - IconBuffer(IconSize sz); - ~IconBuffer(); - - int getSize() const { return cvrtSize(icoSize); } //*maximum* icon size in pixel - - const wxIcon& genericDirIcon () { return genDirIcon; } - const wxIcon& genericFileIcon() { return genFileIcon; } - - bool requestFileIcon(const Zstring& filename, wxIcon* icon = NULL); //returns false if icon is not in buffer - void setWorkload(const std::vector<Zstring>& load); //(re-)set new workload of icons to be retrieved; - - static int cvrtSize(IconSize sz); - -private: - struct Pimpl; - std::unique_ptr<Pimpl> pimpl; - - const IconSize icoSize; - const wxIcon genDirIcon; - const wxIcon genFileIcon; -}; -} - -#endif // ICONBUFFER_H_INCLUDED diff --git a/library/lock_holder.h b/library/lock_holder.h deleted file mode 100644 index b72b12c1..00000000 --- a/library/lock_holder.h +++ /dev/null @@ -1,58 +0,0 @@ -#ifndef LOCK_HOLDER_H_INCLUDED -#define LOCK_HOLDER_H_INCLUDED - -#include <map> -#include "../shared/zstring.h" -#include "dir_lock.h" -#include "status_handler.h" -#include "dir_exist_async.h" - -namespace zen -{ -const Zstring LOCK_FILE_ENDING = Zstr(".ffs_lock"); //intermediate locks created by DirLock use this extension, too! - -//convenience class for creating and holding locks for a number of directories -class LockHolder -{ -public: - void addDir(const Zstring& dirnameFmt, ProcessCallback& procCallback) //resolved dirname ending with path separator - { - if (dirnameFmt.empty()) - return; - - if (!dirExistsUpdating(dirnameFmt, procCallback)) - return; - - if (lockHolder.find(dirnameFmt) != lockHolder.end()) return; - assert(dirnameFmt.EndsWith(FILE_NAME_SEPARATOR)); //this is really the contract, formatting does other things as well, e.g. macro substitution - - class WaitOnLockHandler : public DirLockCallback - { - public: - WaitOnLockHandler(ProcessCallback& pc) : pc_(pc) {} - virtual void requestUiRefresh() { pc_.requestUiRefresh(); } //allowed to throw exceptions - virtual void reportInfo(const std::wstring& text) { pc_.reportStatus(text); } - private: - ProcessCallback& pc_; - } callback(procCallback); - - try - { - lockHolder.insert(std::make_pair(dirnameFmt, DirLock(dirnameFmt + Zstr("sync") + LOCK_FILE_ENDING, &callback))); - } - catch (const FileError& e) - { - bool dummy = false; //this warning shall not be shown but logged only - procCallback.reportWarning(e.msg(), dummy); //may throw! - } - } - -private: - typedef std::map<Zstring, DirLock, LessFilename> DirnameLockMap; - DirnameLockMap lockHolder; -}; - -} - - -#endif // LOCK_HOLDER_H_INCLUDED diff --git a/library/norm_filter.h b/library/norm_filter.h deleted file mode 100644 index 609c81db..00000000 --- a/library/norm_filter.h +++ /dev/null @@ -1,98 +0,0 @@ -// ************************************************************************** -// * This file is part of the FreeFileSync project. It is distributed under * -// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * -// * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * -// ************************************************************************** - -#ifndef NORM_FILTER_H_INCLUDED -#define NORM_FILTER_H_INCLUDED - -#include "hard_filter.h" -#include "soft_filter.h" - -namespace zen -{ - -struct NormalizedFilter //grade-a filter: global/local filter settings combined, units resolved, ready for use -{ - NormalizedFilter(const HardFilter::FilterRef& hf, const SoftFilter& sf) : nameFilter(hf), timeSizeFilter(sf) {} - - //"hard" filter: relevant during comparison, physically skips files - HardFilter::FilterRef nameFilter; - //"soft" filter: relevant after comparison; equivalent to user selection - SoftFilter timeSizeFilter; -}; -namespace -{ -//combine global and local filters via "logical and" -NormalizedFilter normalizeFilters(const FilterConfig& global, const FilterConfig& local); - -inline -bool isNullFilter(const FilterConfig& filterCfg) -{ - return NameFilter(filterCfg.includeFilter, filterCfg.excludeFilter).isNull() && - SoftFilter(filterCfg.timeSpan, filterCfg.unitTimeSpan, - filterCfg.sizeMin, filterCfg.unitSizeMin, - filterCfg.sizeMax, filterCfg.unitSizeMax).isNull(); -} - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -// ----------------------- implementation ----------------------- -NormalizedFilter normalizeFilters(const FilterConfig& global, const FilterConfig& local) -{ - HardFilter::FilterRef globalName(new NameFilter(global.includeFilter, global.excludeFilter)); - HardFilter::FilterRef localName (new NameFilter(local .includeFilter, local .excludeFilter)); - - SoftFilter globalTimeSize(global.timeSpan, global.unitTimeSpan, - global.sizeMin, global.unitSizeMin, - global.sizeMax, global.unitSizeMax); - - SoftFilter localTimeSize(local.timeSpan, local.unitTimeSpan, - local.sizeMin, local.unitSizeMin, - local.sizeMax, local.unitSizeMax); - - return NormalizedFilter(combineFilters(globalName, localName), - combineFilters(globalTimeSize, localTimeSize)); -} -} -} - -#endif //NORM_FILTER_H_INCLUDED diff --git a/library/parallel_scan.cpp b/library/parallel_scan.cpp deleted file mode 100644 index 2c24600f..00000000 --- a/library/parallel_scan.cpp +++ /dev/null @@ -1,611 +0,0 @@ -// ************************************************************************** -// * This file is part of the FreeFileSync project. It is distributed under * -// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * -// * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * -// ************************************************************************** - -#include "parallel_scan.h" -#include <boost/detail/atomic_count.hpp> -#include "db_file.h" -#include "lock_holder.h" -#include "../shared/i18n.h" -#include "../shared/file_traverser.h" -#include "../shared/file_error.h" -#include "../shared/string_conv.h" -#include "../shared/boost_thread_wrap.h" //include <boost/thread.hpp> -#include "loki/ScopeGuard.h" -//#include "../shared/file_id.h" - -/* -#ifdef FFS_WIN -#include <wx/msw/wrapwin.h> //includes "windows.h" -#include "WinIoCtl.h" - -#elif defined FFS_LINUX -#endif -*/ -using namespace zen; - - -#ifndef BOOST_HAS_THREADS -#error just some paranoia check... -#endif - - -namespace -{ -/* -#ifdef FFS_WIN - -struct DiskInfo -{ - DiskInfo() : - driveType(DRIVE_UNKNOWN), - diskID(-1) {} - - UINT driveType; - int diskID; // -1 if id could not be determined, this one is filled if driveType == DRIVE_FIXED or DRIVE_REMOVABLE; -}; - -inline -bool operator<(const DiskInfo& lhs, const DiskInfo& rhs) -{ - if (lhs.driveType != rhs.driveType) - return lhs.driveType < rhs.driveType; - - if (lhs.diskID < 0 || rhs.diskID < 0) - return false; - //consider "same", reason: one volume may be uniquely associated with one disk, while the other volume is associated to the same disk AND another one! - //volume <-> disk is 0..N:1..N - - return lhs.diskID < rhs.diskID ; -} - - -DiskInfo retrieveDiskInfo(const Zstring& pathName) -{ - std::vector<wchar_t> volName(std::max(pathName.size(), static_cast<size_t>(10000))); - - DiskInfo output; - - //full pathName need not yet exist! - if (!::GetVolumePathName(pathName.c_str(), //__in LPCTSTR lpszFileName, - &volName[0], //__out LPTSTR lpszVolumePathName, - static_cast<DWORD>(volName.size()))) //__in DWORD cchBufferLength - return output; - - const Zstring rootPathName = &volName[0]; - - output.driveType = ::GetDriveType(rootPathName.c_str()); - - if (output.driveType == DRIVE_NO_ROOT_DIR) //these two should be the same error category - output.driveType = DRIVE_UNKNOWN; - - if (output.driveType != DRIVE_FIXED && output.driveType != DRIVE_REMOVABLE) - return output; //no reason to get disk ID - - //go and find disk id: - - //format into form: "\\.\C:" - Zstring volnameFmt = rootPathName; - if (endsWith(volnameFmt, FILE_NAME_SEPARATOR)) - volnameFmt.resize(volnameFmt.size() - 1); - volnameFmt = L"\\\\.\\" + volnameFmt; - - HANDLE hVolume = ::CreateFile(volnameFmt.c_str(), - 0, - FILE_SHARE_READ | FILE_SHARE_WRITE, - 0, - OPEN_EXISTING, - 0, - NULL); - if (hVolume == INVALID_HANDLE_VALUE) - return output; - LOKI_ON_BLOCK_EXIT2(::CloseHandle(hVolume)); - - std::vector<char> buffer(sizeof(VOLUME_DISK_EXTENTS) + sizeof(DISK_EXTENT)); //reserve buffer for at most one disk! call below will then fail if volume spans multiple disks! - - DWORD bytesReturned = 0; - if (!::DeviceIoControl(hVolume, // handle to device - IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS, // dwIoControlCode - NULL, // lpInBuffer - 0, // nInBufferSize - &buffer[0], // output buffer - static_cast<DWORD>(buffer.size()), // size of output buffer - &bytesReturned, // number of bytes returned - NULL)) // OVERLAPPED structure - return output; - - const VOLUME_DISK_EXTENTS& volDisks = *reinterpret_cast<VOLUME_DISK_EXTENTS*>(&buffer[0]); - - if (volDisks.NumberOfDiskExtents != 1) - return output; - - output.diskID = volDisks.Extents[0].DiskNumber; - - return output; -} - -#elif defined FFS_LINUX -#endif -*/ - -/* -PERF NOTE - --------------------------------------------- -|Testcase: Reading from two different disks| --------------------------------------------- -Windows 7: - 1st(unbuffered) |2nd (OS buffered) - ---------------------------------- -1 Thread: 57s | 8s -2 Threads: 39s | 7s - --------------------------------------------------- -|Testcase: Reading two directories from same disk| --------------------------------------------------- -Windows 7: Windows XP: - 1st(unbuffered) |2nd (OS buffered) 1st(unbuffered) |2nd (OS buffered) - ---------------------------------- ---------------------------------- -1 Thread: 41s | 13s 1 Thread: 45s | 13s -2 Threads: 42s | 11s 2 Threads: 38s | 8s - -=> Traversing does not take any advantage of file locality so that even multiple threads operating on the same disk impose no performance overhead. -*/ - - -std::vector<std::set<DirectoryKey>> separateByDistinctDisk(const std::set<DirectoryKey>& dirkeys) -{ - //see perf note: use one thread per dirkey: - typedef std::map<int, std::set<DirectoryKey>> DiskKeyMapping; - DiskKeyMapping tmp; - int index = 0; - std::for_each(dirkeys.begin(), dirkeys.end(), - [&](const DirectoryKey& key) { tmp[++index].insert(key); }); - - /* - //use one thread per physical disk: - typedef std::map<DiskInfo, std::set<DirectoryKey>> DiskKeyMapping; - DiskKeyMapping tmp; - std::for_each(dirkeys.begin(), dirkeys.end(), - [&](const DirectoryKey& key) { tmp[retrieveDiskInfo(key.dirnameFull_)].insert(key); }); - */ - std::vector<std::set<DirectoryKey>> buckets; - std::transform(tmp.begin(), tmp.end(), std::back_inserter(buckets), - [&](const DiskKeyMapping::value_type& diskToKey) { return diskToKey.second; }); - return buckets; -} - -//------------------------------------------------------------------------------------------ -typedef Zbase<wchar_t, StorageRefCountThreadSafe> BasicWString; //thread safe string class for UI texts - - -class AsyncCallback -{ -public: - AsyncCallback() : - notifyingThreadID(-1), - textScanning(_("Scanning:")), - itemsScanned(0), - activeWorker(0) {} - - FillBufferCallback::HandleError reportError(const std::wstring& msg) //blocking call: context of worker thread - { - boost::unique_lock<boost::mutex> dummy(lockErrorMsg); - while (!errorMsg.empty() || errorResponse.get()) - conditionCanReportError.timed_wait(dummy, boost::posix_time::milliseconds(50)); //interruption point! - - errorMsg = BasicWString(msg); - - while (!errorResponse.get()) - conditionGotResponse.timed_wait(dummy, boost::posix_time::milliseconds(50)); //interruption point! - - FillBufferCallback::HandleError rv = *errorResponse; - - errorMsg.clear(); - errorResponse.reset(); - - //dummy.unlock(); - conditionCanReportError.notify_one(); - - return rv; - } - - void processErrors(FillBufferCallback& callback) //context of main thread, call repreatedly - { - boost::lock_guard<boost::mutex> dummy(lockErrorMsg); - if (!errorMsg.empty() && !errorResponse.get()) - { - FillBufferCallback::HandleError rv = callback.reportError(cvrtString<std::wstring>(errorMsg)); //throw! - errorResponse.reset(new FillBufferCallback::HandleError(rv)); - - //dummy.unlock(); - conditionGotResponse.notify_one(); - } - } - - void setNotifyingThread(int threadID) { notifyingThreadID = threadID; } //context of main thread - - void reportCurrentFile(const Zstring& filename, int threadID) //context of worker thread - { - if (threadID != notifyingThreadID) return; //only one thread may report status - - boost::lock_guard<boost::mutex> dummy(lockCurrentStatus); - currentFile = filename; - currentStatus.clear(); - } - - void reportCurrentStatus(const std::wstring& status, int threadID) //context of worker thread - { - if (threadID != notifyingThreadID) return; //only one thread may report status - - boost::lock_guard<boost::mutex> dummy(lockCurrentStatus); - currentFile.clear(); - currentStatus = BasicWString(status); //we cannot assume std::wstring to be thread safe (yet)! - } - - std::wstring getCurrentStatus() //context of main thread, call repreatedly - { - std::wstring filename; - std::wstring statusMsg; - { - boost::lock_guard<boost::mutex> dummy(lockCurrentStatus); - if (!currentFile.empty()) - filename = utf8CvrtTo<std::wstring>(currentFile); - else if (!currentStatus.empty()) - statusMsg = cvrtString<std::wstring>(currentStatus); - } - - if (!filename.empty()) - { - std::wstring statusText = cvrtString<std::wstring>(textScanning); - const long activeCount = activeWorker; - if (activeCount >= 2) - { - statusText += L" " + _P("[1 Thread]", "[%x Threads]", activeCount); - replace(statusText, L"%x", toString<std::wstring>(activeCount)); - } - statusText += std::wstring(L" \n") + L'\"' + filename + L'\"'; - return statusText; - } - else - return statusMsg; - } - - void incItemsScanned() { ++itemsScanned; } - long getItemsScanned() const { return itemsScanned; } - - void incActiveWorker() { ++activeWorker; } - void decActiveWorker() { --activeWorker; } - long getActiveWorker() const { return activeWorker; } - -private: - //---- error handling ---- - boost::mutex lockErrorMsg; - boost::condition_variable conditionCanReportError; - boost::condition_variable conditionGotResponse; - BasicWString errorMsg; - std::unique_ptr<FillBufferCallback::HandleError> errorResponse; - - //---- status updates ---- - volatile int notifyingThreadID; //theoretically racy, but there is nothing that could go wrong... - //CAVEAT: do NOT use boost::thread::id as long as this showstopper exists: https://svn.boost.org/trac/boost/ticket/5754 - boost::mutex lockCurrentStatus; //use a different lock for current file: continue traversing while some thread may process an error - Zstring currentFile; //only one of these two is filled at a time! - BasicWString currentStatus; // - - const BasicWString textScanning; //this one is (currently) not shared and could be made a std::wstring, but we stay consistent and use thread-safe variables in this class only! - - //---- status updates II (lock free) ---- - boost::detail::atomic_count itemsScanned; - boost::detail::atomic_count activeWorker; -}; -//------------------------------------------------------------------------------------------------- - -class DirCallback; - -struct TraverserShared -{ -public: - TraverserShared(int threadID, - SymLinkHandling handleSymlinks, - const HardFilter::FilterRef& filter, - std::set<Zstring>& failedReads, - AsyncCallback& acb) : - handleSymlinks_(handleSymlinks), - filterInstance(filter), - failedReads_(failedReads), - acb_(acb), - threadID_(threadID) {} - - typedef std::shared_ptr<DirCallback> CallbackPointer; - std::vector<CallbackPointer> callBackBox; //collection of callback pointers to handle ownership - - const SymLinkHandling handleSymlinks_; - const HardFilter::FilterRef filterInstance; //always bound! - - std::set<Zstring>& failedReads_; //relative postfixed names of directories that could not be read (empty for root) - - AsyncCallback& acb_; - int threadID_; -}; - - -class DirCallback : public zen::TraverseCallback -{ -public: - DirCallback(TraverserShared& config, - const Zstring& relNameParentPf, //postfixed with FILE_NAME_SEPARATOR! - DirContainer& output) : - cfg(config), - relNameParentPf_(relNameParentPf), - output_(output) {} - - virtual void onFile (const Zchar* shortName, const Zstring& fullName, const FileInfo& details); - virtual void onSymlink(const Zchar* shortName, const Zstring& fullName, const SymlinkInfo& details); - virtual ReturnValDir onDir (const Zchar* shortName, const Zstring& fullName); - virtual HandleError onError (const std::wstring& errorText); - -private: - TraverserShared& cfg; - const Zstring relNameParentPf_; - DirContainer& output_; -}; - - -void DirCallback::onFile(const Zchar* shortName, const Zstring& fullName, const FileInfo& details) -{ - boost::this_thread::interruption_point(); - - const Zstring fileNameShort = shortName; - - //do not list the database file(s) sync.ffs_db, sync.x64.ffs_db, etc. or lock files - if (endsWith(fileNameShort, SYNC_DB_FILE_ENDING) || - endsWith(fileNameShort, LOCK_FILE_ENDING)) - return; - - //update status information no matter whether object is excluded or not! - cfg.acb_.reportCurrentFile(fullName, cfg.threadID_); - - //------------------------------------------------------------------------------------ - //apply filter before processing (use relative name!) - if (!cfg.filterInstance->passFileFilter(relNameParentPf_ + fileNameShort)) - return; - - // std::string fileId = details.fileSize >= 1024 * 1024U ? - // util::retrieveFileID(fullName) : - // std::string(); - /* - Perf test Windows 7, SSD, 350k files, 50k dirs, files > 1MB: 7000 - regular: 6.9s - ID per file: 43.9s - ID per file > 1MB: 7.2s - ID per dir: 8.4s - - Linux: retrieveFileID takes about 50% longer in VM! (avoidable because of redundant stat() call!) - */ - - output_.addSubFile(fileNameShort, FileDescriptor(details.lastWriteTimeRaw, details.fileSize)); - - cfg.acb_.incItemsScanned(); //add 1 element to the progress indicator -} - - -void DirCallback::onSymlink(const Zchar* shortName, const Zstring& fullName, const SymlinkInfo& details) -{ - boost::this_thread::interruption_point(); - - if (cfg.handleSymlinks_ == SYMLINK_IGNORE) - return; - - //update status information no matter whether object is excluded or not! - cfg.acb_.reportCurrentFile(fullName, cfg.threadID_); - - //------------------------------------------------------------------------------------ - const Zstring& relName = relNameParentPf_ + shortName; - - //apply filter before processing (use relative name!) - if (!cfg.filterInstance->passFileFilter(relName)) //always use file filter: Link type may not be "stable" on Linux! - return; - - output_.addSubLink(shortName, LinkDescriptor(details.lastWriteTimeRaw, details.targetPath, details.dirLink ? LinkDescriptor::TYPE_DIR : LinkDescriptor::TYPE_FILE)); - - cfg.acb_.incItemsScanned(); //add 1 element to the progress indicator -} - - -TraverseCallback::ReturnValDir DirCallback::onDir(const Zchar* shortName, const Zstring& fullName) -{ - boost::this_thread::interruption_point(); - - //update status information no matter whether object is excluded or not! - cfg.acb_.reportCurrentFile(fullName, cfg.threadID_); - - //------------------------------------------------------------------------------------ - const Zstring& relName = relNameParentPf_ + shortName; - - //apply filter before processing (use relative name!) - bool subObjMightMatch = true; - const bool passFilter = cfg.filterInstance->passDirFilter(relName, &subObjMightMatch); - if (!passFilter && !subObjMightMatch) - return Loki::Int2Type<ReturnValDir::TRAVERSING_DIR_IGNORE>(); //do NOT traverse subdirs - //else: attention! ensure directory filtering is applied later to exclude actually filtered directories - - DirContainer& subDir = output_.addSubDir(shortName); - if (passFilter) - cfg.acb_.incItemsScanned(); //add 1 element to the progress indicator - - TraverserShared::CallbackPointer subDirCallback = std::make_shared<DirCallback>(cfg, relName + FILE_NAME_SEPARATOR, subDir); - cfg.callBackBox.push_back(subDirCallback); //handle lifetime - return ReturnValDir(Loki::Int2Type<ReturnValDir::TRAVERSING_DIR_CONTINUE>(), *subDirCallback); -} - - -DirCallback::HandleError DirCallback::onError(const std::wstring& errorText) -{ - switch (cfg.acb_.reportError(errorText)) - { - case FillBufferCallback::TRAV_ERROR_IGNORE: - cfg.failedReads_.insert(relNameParentPf_); - return TRAV_ERROR_IGNORE; - - case FillBufferCallback::TRAV_ERROR_RETRY: - return TRAV_ERROR_RETRY; - } - - assert(false); - return TRAV_ERROR_IGNORE; -} - - -#ifdef FFS_WIN -class DstHackCallbackImpl : public DstHackCallback -{ -public: - DstHackCallbackImpl(AsyncCallback& acb, int threadID) : - acb_(acb), - threadID_(threadID), - textApplyingDstHack(toZ(_("Encoding extended time information: %x")).Replace(Zstr("%x"), Zstr("\n\"%x\""))) {} - -private: - virtual void requestUiRefresh(const Zstring& filename) //applying DST hack imposes significant one-time performance drawback => callback to inform user - { - Zstring statusText = textApplyingDstHack; - replace(statusText, Zstr("%x"), filename); - acb_.reportCurrentStatus(utf8CvrtTo<std::wstring>(statusText), threadID_); - } - - AsyncCallback& acb_; - int threadID_; - const Zstring textApplyingDstHack; -}; -#endif -//------------------------------------------------------------------------------------------ - - -class WorkerThread -{ -public: - WorkerThread(int threadID, - const std::shared_ptr<AsyncCallback>& acb, - const std::vector<std::pair<DirectoryKey, DirectoryValue*>>& workload) : - threadID_(threadID), - acb_(acb), - workload_(workload) {} - - void operator()() //thread entry - { - acb_->incActiveWorker(); - LOKI_ON_BLOCK_EXIT2(acb_->decActiveWorker();); - - std::for_each(workload_.begin(), workload_.end(), - [&](std::pair<DirectoryKey, DirectoryValue*>& item) - { - const Zstring& directoryName = item.first.dirnameFull_; - DirectoryValue& dirVal = *item.second; - - acb_->reportCurrentFile(directoryName, threadID_); //just in case first directory access is blocking - - TraverserShared travCfg(threadID_, - item.first.handleSymlinks_, //shared by all(!) instances of DirCallback while traversing a folder hierarchy - item.first.filter_, - dirVal.failedReads, - *acb_); - - DirCallback traverser(travCfg, - Zstring(), - dirVal.dirCont); - - bool followSymlinks = false; - switch (item.first.handleSymlinks_) - { - case SYMLINK_IGNORE: - followSymlinks = false; //=> symlinks will be reported via onSymlink() where they are excluded - break; - case SYMLINK_USE_DIRECTLY: - followSymlinks = false; - break; - case SYMLINK_FOLLOW_LINK: - followSymlinks = true; - break; - } - - DstHackCallback* dstCallbackPtr = NULL; -#ifdef FFS_WIN - DstHackCallbackImpl dstCallback(*acb_, threadID_); - dstCallbackPtr = &dstCallback; -#endif - - //get all files and folders from directoryPostfixed (and subdirectories) - traverseFolder(directoryName, followSymlinks, traverser, dstCallbackPtr); //exceptions may be thrown! - }); - } - -private: - int threadID_; - std::shared_ptr<AsyncCallback> acb_; - std::vector<std::pair<DirectoryKey, DirectoryValue*>> workload_; -}; -} - - -void zen::fillBuffer(const std::set<DirectoryKey>& keysToRead, //in - std::map<DirectoryKey, DirectoryValue>& buf, //out - FillBufferCallback& callback, - size_t statusInterval) -{ - buf.clear(); - - std::vector<std::set<DirectoryKey>> buckets = separateByDistinctDisk(keysToRead); //one bucket per physical device - - std::vector<boost::thread> worker; //note: GCC doesn't allow to construct an array of empty threads since they would be initialized by const boost::thread& - worker.reserve(buckets.size()); - - Loki::ScopeGuard guardWorker = Loki::MakeGuard([&]() - { - std::for_each(worker.begin(), worker.end(), std::mem_fun_ref(&boost::thread::interrupt)); //interrupt all at once, then join - std::for_each(worker.begin(), worker.end(), std::mem_fun_ref(&boost::thread::join)); - }); - - std::shared_ptr<AsyncCallback> acb = std::make_shared<AsyncCallback>(); - - //init worker threads - for (auto iter = buckets.begin(); iter != buckets.end(); ++iter) - { - int threadID = iter - buckets.begin(); - const std::set<DirectoryKey>& bucket = *iter; - - std::vector<std::pair<DirectoryKey, DirectoryValue*>> workload; - std::for_each(bucket.begin(), bucket.end(), - [&](const DirectoryKey& key) - { - auto rv = buf.insert(std::make_pair(key, DirectoryValue())); - assert(rv.second); - workload.push_back(std::make_pair(key, &rv.first->second)); - }); - - worker.push_back(boost::thread(WorkerThread(threadID, acb, workload))); - } - - //wait until done - for (auto iter = worker.begin(); iter != worker.end(); ++iter) - { - boost::thread& wt = *iter; - int threadID = iter - worker.begin(); - - acb->setNotifyingThread(threadID); //process info messages of first (active) thread only - - do - { - //update status - callback.reportStatus(acb->getCurrentStatus(), acb->getItemsScanned()); //throw! - - //process errors - acb->processErrors(callback); - } - while (!wt.timed_join(boost::posix_time::milliseconds(statusInterval))); - } - - guardWorker.Dismiss(); -} diff --git a/library/parallel_scan.h b/library/parallel_scan.h deleted file mode 100644 index 5c998969..00000000 --- a/library/parallel_scan.h +++ /dev/null @@ -1,74 +0,0 @@ -// ************************************************************************** -// * This file is part of the FreeFileSync project. It is distributed under * -// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * -// * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * -// ************************************************************************** - -#ifndef PARALLEL_SCAN_H_INCLUDED -#define PARALLEL_SCAN_H_INCLUDED - -#include <map> -#include <set> -#include "hard_filter.h" -#include "../structures.h" -#include "../file_hierarchy.h" - -namespace zen -{ -struct DirectoryKey -{ - DirectoryKey(const Zstring& dirnameFull, - const HardFilter::FilterRef& filter, - SymLinkHandling handleSymlinks) : - dirnameFull_(dirnameFull), - filter_(filter), - handleSymlinks_(handleSymlinks) {} - - Zstring dirnameFull_; - HardFilter::FilterRef filter_; //filter interface: always bound by design! - SymLinkHandling handleSymlinks_; -}; - -inline -bool operator<(const DirectoryKey& lhs, const DirectoryKey& rhs) -{ - if (lhs.handleSymlinks_ != rhs.handleSymlinks_) - return lhs.handleSymlinks_ < rhs.handleSymlinks_; - - if (!EqualFilename()(lhs.dirnameFull_, rhs.dirnameFull_)) - return LessFilename()(lhs.dirnameFull_, rhs.dirnameFull_); - - return *lhs.filter_ < *rhs.filter_; -} - - -struct DirectoryValue -{ - DirContainer dirCont; - std::set<Zstring> failedReads; //relative postfixed names of directories that could not be read (empty string for root), e.g. access denied, or temporal network drop -}; - - -class FillBufferCallback -{ -public: - virtual ~FillBufferCallback() {} - - enum HandleError - { - TRAV_ERROR_RETRY, - TRAV_ERROR_IGNORE - }; - virtual HandleError reportError (const std::wstring& errorText) = 0; //may throw! - virtual void reportStatus(const std::wstring& statusMsg, int itemTotal) = 0; // -}; - -//attention: ensure directory filtering is applied later to exclude filtered directories which have been kept as parent folders - -void fillBuffer(const std::set<DirectoryKey>& keysToRead, //in - std::map<DirectoryKey, DirectoryValue>& buf, //out - FillBufferCallback& callback, - size_t statusInterval); //unit: [ms] -} - -#endif // PARALLEL_SCAN_H_INCLUDED diff --git a/library/process_xml.cpp b/library/process_xml.cpp deleted file mode 100644 index 8362e073..00000000 --- a/library/process_xml.cpp +++ /dev/null @@ -1,1172 +0,0 @@ -// ************************************************************************** -// * This file is part of the FreeFileSync project. It is distributed under * -// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * -// * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * -// ************************************************************************** - -#include "process_xml.h" -#include <zenXml/zenxml.h> -#include "../shared/i18n.h" -#include "../shared/global_func.h" -#include "../shared/standard_paths.h" -#include "../shared/string_conv.h" -#include "../shared/file_handling.h" -#include "../shared/file_io.h" -#include "../shared/xml_base.h" - -using namespace zen; -using namespace xmlAccess; //functionally needed for correct overload resolution!!! - - -XmlType getXmlType(const zen::XmlDoc& doc) //throw() -{ - if (doc.root().getNameAs<std::string>() == "FreeFileSync") - { - std::string type; - if (doc.root().getAttribute("XmlType", type)) - { - if (type == "GUI") - return XML_TYPE_GUI; - else if (type == "BATCH") - return XML_TYPE_BATCH; - else if (type == "GLOBAL") - return XML_TYPE_GLOBAL; - } - } - return XML_TYPE_OTHER; -} - - -XmlType xmlAccess::getXmlType(const wxString& filename) //throw() -{ - XmlDoc doc; - try - { - //do NOT use zen::loadStream as it will superfluously load even huge files! - loadXmlDocument(filename, doc); //throw FfsXmlError, quick exit if file is not an FFS XML - } - catch (const FfsXmlError&) - { - return XML_TYPE_OTHER; - } - return ::getXmlType(doc); -} - - -void setXmlType(XmlDoc& doc, XmlType type) //throw() -{ - switch (type) - { - case XML_TYPE_GUI: - doc.root().setAttribute("XmlType", "GUI"); - break; - case XML_TYPE_BATCH: - doc.root().setAttribute("XmlType", "BATCH"); - break; - case XML_TYPE_GLOBAL: - doc.root().setAttribute("XmlType", "GLOBAL"); - break; - case XML_TYPE_OTHER: - assert(false); - break; - } -} -//################################################################################################################ - - -wxString xmlAccess::getGlobalConfigFile() -{ - return zen::getConfigDir() + wxT("GlobalSettings.xml"); -} - - -void xmlAccess::OptionalDialogs::resetDialogs() -{ - warningDependentFolders = true; - warningMultiFolderWriteAccess = true; - warningSignificantDifference = true; - warningNotEnoughDiskSpace = true; - warningUnresolvedConflicts = true; - warningSyncDatabase = true; - warningRecyclerMissing = true; - popupOnConfigChange = true; - showSummaryBeforeSync = true; -} - - -xmlAccess::XmlGuiConfig xmlAccess::convertBatchToGui(const xmlAccess::XmlBatchConfig& batchCfg) -{ - XmlGuiConfig output; - output.mainCfg = batchCfg.mainCfg; - - switch (batchCfg.handleError) - { - case ON_ERROR_EXIT: - case ON_ERROR_POPUP: - output.handleError = ON_GUIERROR_POPUP; - break; - case ON_ERROR_IGNORE: - output.handleError = ON_GUIERROR_IGNORE; - break; - } - return output; -} - - -xmlAccess::XmlBatchConfig xmlAccess::convertGuiToBatch(const xmlAccess::XmlGuiConfig& guiCfg, const wxString& referenceFile) -{ - //try to take over batch-specific settings from reference - if (!referenceFile.empty() && getXmlType(referenceFile) == XML_TYPE_BATCH) - try - { - XmlBatchConfig output; - - std::vector<wxString> filenames; - filenames.push_back(referenceFile); - convertConfig(filenames, output); //throw xmlAccess::FfsXmlError - - output.mainCfg = guiCfg.mainCfg; - return output; - } - catch (xmlAccess::FfsXmlError&) {} - - XmlBatchConfig output; //use default batch-settings - output.mainCfg = guiCfg.mainCfg; - - switch (guiCfg.handleError) - { - case ON_GUIERROR_POPUP: - output.handleError = ON_ERROR_POPUP; - break; - case ON_GUIERROR_IGNORE: - output.handleError = ON_ERROR_IGNORE; - break; - } - - return output; -} - - -xmlAccess::MergeType xmlAccess::getMergeType(const std::vector<wxString>& filenames) //throw () -{ - bool guiCfgExists = false; - bool batchCfgExists = false; - - for (std::vector<wxString>::const_iterator i = filenames.begin(); i != filenames.end(); ++i) - { - switch (xmlAccess::getXmlType(*i)) //throw() - { - case XML_TYPE_GUI: - guiCfgExists = true; - break; - - case XML_TYPE_BATCH: - batchCfgExists = true; - break; - - case XML_TYPE_GLOBAL: - case XML_TYPE_OTHER: - return MERGE_OTHER; - } - } - - if (guiCfgExists && batchCfgExists) - return MERGE_GUI_BATCH; - else if (guiCfgExists && !batchCfgExists) - return MERGE_GUI; - else if (!guiCfgExists && batchCfgExists) - return MERGE_BATCH; - else - return MERGE_OTHER; -} - - -namespace -{ -template <class XmlCfg> -XmlCfg loadCfgImpl(const wxString& filename, std::unique_ptr<xmlAccess::FfsXmlError>& exeption) //throw xmlAccess::FfsXmlError -{ - XmlCfg cfg; - try - { - xmlAccess::readConfig(filename, cfg); //throw xmlAccess::FfsXmlError - } - catch (const xmlAccess::FfsXmlError& e) - { - if (e.getSeverity() == xmlAccess::FfsXmlError::FATAL) - throw; - else - exeption.reset(new xmlAccess::FfsXmlError(e)); - } - return cfg; -} - - -template <class XmlCfg> -void mergeConfigFilesImpl(const std::vector<wxString>& filenames, XmlCfg& config) //throw xmlAccess::FfsXmlError -{ - using namespace xmlAccess; - - assert(!filenames.empty()); - if (filenames.empty()) - return; - - std::vector<zen::MainConfiguration> mainCfgs; - std::unique_ptr<FfsXmlError> savedException; - - for (std::vector<wxString>::const_iterator i = filenames.begin(); i != filenames.end(); ++i) - { - switch (getXmlType(*i)) - { - case XML_TYPE_GUI: - mainCfgs.push_back(loadCfgImpl<XmlGuiConfig>(*i, savedException).mainCfg); //throw xmlAccess::FfsXmlError - break; - - case XML_TYPE_BATCH: - mainCfgs.push_back(loadCfgImpl<XmlBatchConfig>(*i, savedException).mainCfg); //throw xmlAccess::FfsXmlError - break; - - case XML_TYPE_GLOBAL: - case XML_TYPE_OTHER: - break; - } - } - - if (mainCfgs.empty()) - throw FfsXmlError(_("Invalid FreeFileSync config file!")); - - try //...to init all non-"mainCfg" settings with first config file - { - xmlAccess::readConfig(filenames[0], config); //throw xmlAccess::FfsXmlError - } - catch (xmlAccess::FfsXmlError&) {} - - config.mainCfg = merge(mainCfgs); - - if (savedException.get()) //"re-throw" exception - throw* savedException; -} -} - - -void xmlAccess::convertConfig(const std::vector<wxString>& filenames, XmlGuiConfig& config) //throw (xmlAccess::FfsXmlError) -{ - mergeConfigFilesImpl(filenames, config); //throw (xmlAccess::FfsXmlError) -} - - -void xmlAccess::convertConfig(const std::vector<wxString>& filenames, XmlBatchConfig& config) //throw (xmlAccess::FfsXmlError); -{ - mergeConfigFilesImpl(filenames, config); //throw (xmlAccess::FfsXmlError) -} - - -namespace zen -{ -template <> inline -void writeText(const CompareVariant& value, std::string& output) -{ - switch (value) - { - case zen::CMP_BY_TIME_SIZE: - output = "ByTimeAndSize"; - break; - case zen::CMP_BY_CONTENT: - output = "ByContent"; - break; - } -} - -template <> inline -bool readText(const std::string& input, CompareVariant& value) -{ - std::string tmp = input; - zen::trim(tmp); - if (tmp == "ByTimeAndSize") - value = zen::CMP_BY_TIME_SIZE; - else if (tmp == "ByContent") - value = zen::CMP_BY_CONTENT; - else - return false; - return true; -} - - -template <> inline -void writeText(const SyncDirection& value, std::string& output) -{ - switch (value) - { - case SYNC_DIR_LEFT: - output = "left"; - break; - case SYNC_DIR_RIGHT: - output = "right"; - break; - case SYNC_DIR_NONE: - output = "none"; - break; - } -} - -template <> inline -bool readText(const std::string& input, SyncDirection& value) -{ - std::string tmp = input; - zen::trim(tmp); - if (tmp == "left") - value = SYNC_DIR_LEFT; - else if (tmp == "right") - value = SYNC_DIR_RIGHT; - else if (tmp == "none") - value = SYNC_DIR_NONE; - else - return false; - return true; -} - - -template <> inline -void writeText(const OnError& value, std::string& output) -{ - switch (value) - { - case ON_ERROR_IGNORE: - output = "Ignore"; - break; - case ON_ERROR_EXIT: - output = "Exit"; - break; - case ON_ERROR_POPUP: - output = "Popup"; - break; - } -} - -template <> inline -bool readText(const std::string& input, OnError& value) -{ - std::string tmp = input; - zen::trim(tmp); - if (tmp == "Ignore") - value = ON_ERROR_IGNORE; - else if (tmp == "Exit") - value = ON_ERROR_EXIT; - else if (tmp == "Popup") - value = ON_ERROR_POPUP; - else - return false; - return true; -} - - -template <> inline -void writeText(const OnGuiError& value, std::string& output) -{ - switch (value) - { - case ON_GUIERROR_IGNORE: - output = "Ignore"; - break; - case ON_GUIERROR_POPUP: - output = "Popup"; - break; - } -} - -template <> inline -bool readText(const std::string& input, OnGuiError& value) -{ - std::string tmp = input; - zen::trim(tmp); - if (tmp == "Ignore") - value = ON_GUIERROR_IGNORE; - else if (tmp == "Popup") - value = ON_GUIERROR_POPUP; - else - return false; - return true; -} - - -template <> inline -void writeText(const FileIconSize& value, std::string& output) -{ - switch (value) - { - case ICON_SIZE_SMALL: - output = "Small"; - break; - case ICON_SIZE_MEDIUM: - output = "Medium"; - break; - case ICON_SIZE_LARGE: - output = "Large"; - break; - } -} - - -template <> inline -bool readText(const std::string& input, FileIconSize& value) -{ - std::string tmp = input; - zen::trim(tmp); - if (tmp == "Small") - value = ICON_SIZE_SMALL; - else if (tmp == "Medium") - value = ICON_SIZE_MEDIUM; - else if (tmp == "Large") - value = ICON_SIZE_LARGE; - else - return false; - return true; -} - - -template <> inline -void writeText(const DeletionPolicy& value, std::string& output) -{ - switch (value) - { - case DELETE_PERMANENTLY: - output = "DeletePermanently"; - break; - case MOVE_TO_RECYCLE_BIN: - output = "MoveToRecycleBin"; - break; - case MOVE_TO_CUSTOM_DIRECTORY: - output = "MoveToCustomDirectory"; - break; - } -} - -template <> inline -bool readText(const std::string& input, DeletionPolicy& value) -{ - std::string tmp = input; - zen::trim(tmp); - if (tmp == "DeletePermanently") - value = DELETE_PERMANENTLY; - else if (tmp == "MoveToRecycleBin") - value = MOVE_TO_RECYCLE_BIN; - else if (tmp == "MoveToCustomDirectory") - value = MOVE_TO_CUSTOM_DIRECTORY; - else - return false; - return true; -} - - -template <> inline -void writeText(const SymLinkHandling& value, std::string& output) -{ - switch (value) - { - case SYMLINK_IGNORE: - output = "Ignore"; - break; - case SYMLINK_USE_DIRECTLY: - output = "UseDirectly"; - break; - case SYMLINK_FOLLOW_LINK: - output = "FollowLink"; - break; - } -} - -template <> inline -bool readText(const std::string& input, SymLinkHandling& value) -{ - std::string tmp = input; - zen::trim(tmp); - if (tmp == "Ignore") - value = SYMLINK_IGNORE; - else if (tmp == "UseDirectly") - value = SYMLINK_USE_DIRECTLY; - else if (tmp == "FollowLink") - value = SYMLINK_FOLLOW_LINK; - else - return false; - return true; -} - - -template <> inline -void writeText(const UnitTime& value, std::string& output) -{ - switch (value) - { - case UTIME_NONE: - output = "Inactive"; - break; - // case UTIME_LAST_X_HOURS: - // output = "x-hours"; - // break; - case UTIME_TODAY: - output = "Today"; - break; - case UTIME_THIS_WEEK: - output = "Week"; - break; - case UTIME_THIS_MONTH: - output = "Month"; - break; - case UTIME_THIS_YEAR: - output = "Year"; - break; - } -} - -template <> inline -bool readText(const std::string& input, UnitTime& value) -{ - std::string tmp = input; - zen::trim(tmp); - if (tmp == "Inactive") - value = UTIME_NONE; - // else if (tmp == "x-hours") - // value = UTIME_LAST_X_HOURS; - else if (tmp == "Today") - value = UTIME_TODAY; - else if (tmp == "Week") - value = UTIME_THIS_WEEK; - else if (tmp == "Month") - value = UTIME_THIS_MONTH; - else if (tmp == "Year") - value = UTIME_THIS_YEAR; - else - return false; - return true; -} - - -template <> inline -void writeText(const ColumnTypes& value, std::string& output) -{ - output = toString<std::string>(value); -} - -template <> inline -bool readText(const std::string& input, ColumnTypes& value) -{ - value = static_cast<ColumnTypes>(toNumber<int>(input)); - return true; -} - - -template <> inline -void writeText(const UnitSize& value, std::string& output) -{ - switch (value) - { - case USIZE_NONE: - output = "Inactive"; - break; - case USIZE_BYTE: - output = "Byte"; - break; - case USIZE_KB: - output = "KB"; - break; - case USIZE_MB: - output = "MB"; - break; - } -} - -template <> inline -bool readText(const std::string& input, UnitSize& value) -{ - std::string tmp = input; - zen::trim(tmp); - if (tmp == "Inactive") - value = USIZE_NONE; - else if (tmp == "Byte") - value = USIZE_BYTE; - else if (tmp == "KB") - value = USIZE_KB; - else if (tmp == "MB") - value = USIZE_MB; - else - return false; - return true; -} - - -template <> inline -void writeText(const DirectionConfig::Variant& value, std::string& output) -{ - switch (value) - { - case DirectionConfig::AUTOMATIC: - output = "Automatic"; - break; - case DirectionConfig::MIRROR: - output = "Mirror"; - break; - case DirectionConfig::UPDATE: - output = "Update"; - break; - case DirectionConfig::CUSTOM: - output = "Custom"; - break; - } -} - -template <> inline -bool readText(const std::string& input, DirectionConfig::Variant& value) -{ - std::string tmp = input; - zen::trim(tmp); - if (tmp == "Automatic") - value = DirectionConfig::AUTOMATIC; - else if (tmp == "Mirror") - value = DirectionConfig::MIRROR; - else if (tmp == "Update") - value = DirectionConfig::UPDATE; - else if (tmp == "Custom") - value = DirectionConfig::CUSTOM; - else - return false; - return true; -} - - -template <> inline -bool readValue(const XmlElement& input, ColumnAttrib& value) -{ - XmlIn in(input); - bool rv1 = in.attribute("Type", value.type); - bool rv2 = in.attribute("Visible", value.visible); - bool rv3 = in.attribute("Width", value.width); - value.position = 0; - return rv1 && rv2 && rv3; -} - -template <> inline -void writeValue(const ColumnAttrib& value, XmlElement& output) -{ - XmlOut out(output); - out.attribute("Type", value.type); - out.attribute("Visible", value.visible); - out.attribute("Width", value.width); -} -} - - -namespace -{ -void readConfig(const XmlIn& in, CompConfig& cmpConfig) -{ - in["Variant" ](cmpConfig.compareVar); - in["HandleSymlinks"](cmpConfig.handleSymlinks); -} - - -void readConfig(const XmlIn& in, DirectionConfig& directCfg) -{ - in["Variant"](directCfg.var); - - XmlIn inCustDir = in["CustomDirections"]; - inCustDir["LeftOnly" ](directCfg.custom.exLeftSideOnly); - inCustDir["RightOnly" ](directCfg.custom.exRightSideOnly); - inCustDir["LeftNewer" ](directCfg.custom.leftNewer); - inCustDir["RightNewer"](directCfg.custom.rightNewer); - inCustDir["Different" ](directCfg.custom.different); - inCustDir["Conflict" ](directCfg.custom.conflict); -} - - -void readConfig(const XmlIn& in, SyncConfig& syncCfg) -{ - readConfig(in, syncCfg.directionCfg); - - in["DeletionPolicy" ](syncCfg.handleDeletion); - in["CustomDeletionFolder"](syncCfg.customDeletionDirectory); -} - - -void readConfig(const XmlIn& in, FilterConfig& filter) -{ - in["Include"](filter.includeFilter); - in["Exclude"](filter.excludeFilter); - - in["TimeSpan" ](filter.timeSpan); - in["UnitTimeSpan"](filter.unitTimeSpan); - - in["SizeMin" ](filter.sizeMin); - in["UnitSizeMin"](filter.unitSizeMin); - - in["SizeMax" ](filter.sizeMax); - in["UnitSizeMax"](filter.unitSizeMax); -} - - -void readConfig(const XmlIn& in, FolderPairEnh& enhPair) -{ - //read folder pairs - in["Left" ](enhPair.leftDirectory); - in["Right"](enhPair.rightDirectory); - - //########################################################### - //alternate comp configuration (optional) - XmlIn inAltCmp = in["AlternateCompareConfig"]; - if (inAltCmp) - { - CompConfig altCmpCfg; - readConfig(inAltCmp, altCmpCfg); - - enhPair.altCmpConfig = std::make_shared<CompConfig>(altCmpCfg);; - } - //########################################################### - //alternate sync configuration (optional) - XmlIn inAltSync = in["SyncConfig"]; - if (inAltSync) - { - SyncConfig altSyncCfg; - readConfig(inAltSync, altSyncCfg); - - enhPair.altSyncConfig = std::make_shared<SyncConfig>(altSyncCfg); - } - - //########################################################### - //alternate filter configuration - readConfig(in["LocalFilter"], enhPair.localFilter); -} - - -void readConfig(const XmlIn& in, MainConfiguration& mainCfg) -{ - //read compare settings - XmlIn inCmp = in["MainConfig"]["Comparison"]; - - readConfig(inCmp, mainCfg.cmpConfig); - //########################################################### - - XmlIn inSync = in["MainConfig"]["SyncConfig"]; - - //read sync configuration - readConfig(inSync, mainCfg.syncCfg); - //########################################################### - - XmlIn inFilter = in["MainConfig"]["GlobalFilter"]; - //read filter settings - readConfig(inFilter, mainCfg.globalFilter); - - //########################################################### - //read all folder pairs - mainCfg.additionalPairs.clear(); - - bool firstIter = true; - for (XmlIn inPair = in["MainConfig"]["FolderPairs"]["Pair"]; inPair; inPair.next()) - { - FolderPairEnh newPair; - readConfig(inPair, newPair); - - if (firstIter) - { - firstIter = false; - mainCfg.firstPair = newPair; //set first folder pair - } - else - mainCfg.additionalPairs.push_back(newPair); //set additional folder pairs - } -} - - -void readConfig(const XmlIn& in, xmlAccess::XmlGuiConfig& config) -{ - ::readConfig(in, config.mainCfg); //read main config - - //read GUI specific config data - XmlIn inGuiCfg = in["GuiConfig"]; - - inGuiCfg["HideFiltered" ](config.hideFilteredElements); - inGuiCfg["HandleError" ](config.handleError); - inGuiCfg["SyncPreviewActive"](config.syncPreviewEnabled); -} - - -void readConfig(const XmlIn& in, xmlAccess::XmlBatchConfig& config) -{ - ::readConfig(in, config.mainCfg); //read main config - - //read GUI specific config data - XmlIn inBatchCfg = in["BatchConfig"]; - - inBatchCfg["Silent" ](config.silent); - inBatchCfg["LogfileDirectory"](config.logFileDirectory); - inBatchCfg["LogfileCountMax" ](config.logFileCountMax); - inBatchCfg["HandleError" ](config.handleError); -} - - -void readConfig(const XmlIn& in, XmlGlobalSettings& config) -{ - XmlIn inShared = in["Shared"]; - - //try to read program language setting - inShared["Language"](config.programLanguage); - - inShared["CopyLockedFiles" ](config.copyLockedFiles); - inShared["CopyFilePermissions" ](config.copyFilePermissions); - inShared["TransactionalFileCopy"](config.transactionalFileCopy); - inShared["VerifyCopiedFiles" ](config.verifyFileCopy); - - //max. allowed file time deviation - inShared["FileTimeTolerance"](config.fileTimeTolerance); - - XmlIn inOpt = inShared["ShowOptionalDialogs"]; - inOpt["CheckForDependentFolders" ](config.optDialogs.warningDependentFolders); - inOpt["CheckForMultipleWriteAccess" ](config.optDialogs.warningMultiFolderWriteAccess); - inOpt["CheckForSignificantDifference"](config.optDialogs.warningSignificantDifference); - inOpt["CheckForFreeDiskSpace"](config.optDialogs.warningNotEnoughDiskSpace); - inOpt["CheckForUnresolvedConflicts"](config.optDialogs.warningUnresolvedConflicts); - inOpt["NotifyDatabaseError"](config.optDialogs.warningSyncDatabase); - inOpt["CheckMissingRecycleBin"](config.optDialogs.warningRecyclerMissing); - inOpt["PopupOnConfigChange"](config.optDialogs.popupOnConfigChange); - inOpt["SummaryBeforeSync" ](config.optDialogs.showSummaryBeforeSync); - - //gui specific global settings (optional) - XmlIn inGui = in["Gui"]; - XmlIn inWnd = inGui["Windows"]["Main"]; - - //read application window size and position - inWnd["Width" ](config.gui.dlgSize.x); - inWnd["Height" ](config.gui.dlgSize.y); - inWnd["PosX" ](config.gui.dlgPos.x); - inWnd["PosY" ](config.gui.dlgPos.y); - inWnd["Maximized"](config.gui.isMaximized); - - inWnd["MaxFolderPairsVisible"](config.gui.maxFolderPairsVisible); - - inWnd["ManualDeletionOnBothSides"](config.gui.deleteOnBothSides); - inWnd["ManualDeletionUseRecycler"](config.gui.useRecyclerForManualDeletion); - inWnd["RespectCaseOnSearch" ](config.gui.textSearchRespectCase); - - inWnd["IconSize"](config.gui.iconSize); - - //########################################################### - //read column attributes - XmlIn inColLeft = inWnd["LeftColumns"]; - inColLeft.attribute("AutoAdjust", config.gui.autoAdjustColumnsLeft); - - inColLeft(config.gui.columnAttribLeft); - for (size_t i = 0; i < config.gui.columnAttribLeft.size(); ++i) - config.gui.columnAttribLeft[i].position = i; - - //########################################################### - XmlIn inColRight = inWnd["RightColumns"]; - inColRight.attribute("AutoAdjust", config.gui.autoAdjustColumnsRight); - - inColRight(config.gui.columnAttribRight); - for (size_t i = 0; i < config.gui.columnAttribRight.size(); ++i) - config.gui.columnAttribRight[i].position = i; - - inWnd["FolderHistoryLeft" ](config.gui.folderHistoryLeft); - inWnd["FolderHistoryRight"](config.gui.folderHistoryRight); - inWnd["MaximumHistorySize"](config.gui.folderHistMax); - inWnd["Perspective" ](config.gui.guiPerspectiveLast); - - //external applications - inGui["ExternalApplications"](config.gui.externelApplications); - - //load config file history - inGui["LastConfigActive"](config.gui.lastUsedConfigFiles); - inGui["ConfigHistory"](config.gui.cfgFileHistory); - - //last update check - inGui["LastUpdateCheck"](config.gui.lastUpdateCheck); - - //batch specific global settings - //XmlIn inBatch = in["Batch"]; -} - - -template <class ConfigType> -void readConfig(const wxString& filename, XmlType type, ConfigType& config) -{ - if (!fileExists(toZ(filename))) - throw FfsXmlError(wxString(_("File does not exist:")) + wxT("\n\"") + filename + wxT("\"")); - - XmlDoc doc; - loadXmlDocument(filename, doc); //throw (FfsXmlError) - - if (getXmlType(doc) != type) //throw() - throw FfsXmlError(wxString(_("Error parsing configuration file:")) + wxT("\n\"") + filename + wxT("\"")); - - XmlIn in(doc); - ::readConfig(in, config); - - if (in.errorsOccured()) - throw FfsXmlError(wxString(_("Error parsing configuration file:")) + wxT("\n\"") + filename + wxT("\"\n\n") + - getErrorMessageFormatted(in), FfsXmlError::WARNING); -} -} - - -void xmlAccess::readConfig(const wxString& filename, xmlAccess::XmlGuiConfig& config) -{ - ::readConfig(filename, XML_TYPE_GUI, config); -} - - -void xmlAccess::readConfig(const wxString& filename, xmlAccess::XmlBatchConfig& config) -{ - ::readConfig(filename, XML_TYPE_BATCH, config); -} - - -void xmlAccess::readConfig(xmlAccess::XmlGlobalSettings& config) -{ - ::readConfig(getGlobalConfigFile(), XML_TYPE_GLOBAL, config); -} - - -//################################################################################################ -namespace -{ -void writeConfig(const CompConfig& cmpConfig, XmlOut& out) -{ - out["Variant" ](cmpConfig.compareVar); - out["HandleSymlinks"](cmpConfig.handleSymlinks); -} - - -void writeConfig(const DirectionConfig& directCfg, XmlOut& out) -{ - out["Variant"](directCfg.var); - - XmlOut outCustDir = out["CustomDirections"]; - outCustDir["LeftOnly" ](directCfg.custom.exLeftSideOnly); - outCustDir["RightOnly" ](directCfg.custom.exRightSideOnly); - outCustDir["LeftNewer" ](directCfg.custom.leftNewer); - outCustDir["RightNewer"](directCfg.custom.rightNewer); - outCustDir["Different" ](directCfg.custom.different); - outCustDir["Conflict" ](directCfg.custom.conflict); -} - - -void writeConfig(const SyncConfig& syncCfg, XmlOut& out) -{ - writeConfig(syncCfg.directionCfg, out); - - out["DeletionPolicy" ](syncCfg.handleDeletion); - out["CustomDeletionFolder"](syncCfg.customDeletionDirectory); -} - - -void writeConfig(const FilterConfig& filter, XmlOut& out) -{ - out["Include"](filter.includeFilter); - out["Exclude"](filter.excludeFilter); - - out["TimeSpan" ](filter.timeSpan); - out["UnitTimeSpan"](filter.unitTimeSpan); - - out["SizeMin" ](filter.sizeMin); - out["UnitSizeMin"](filter.unitSizeMin); - - out["SizeMax" ](filter.sizeMax); - out["UnitSizeMax"](filter.unitSizeMax); -} - - -void writeConfigFolderPair(const FolderPairEnh& enhPair, XmlOut& out) -{ - XmlOut outPair = out.ref().addChild("Pair"); - - //read folder pairs - outPair["Left" ](enhPair.leftDirectory); - outPair["Right"](enhPair.rightDirectory); - - //########################################################### - //alternate comp configuration (optional) - if (enhPair.altCmpConfig.get()) - { - XmlOut outAlt = outPair["AlternateCompareConfig"]; - - writeConfig(*enhPair.altCmpConfig, outAlt); - } - //########################################################### - //alternate sync configuration (optional) - if (enhPair.altSyncConfig.get()) - { - XmlOut outAltSync = outPair["SyncConfig"]; - - writeConfig(*enhPair.altSyncConfig, outAltSync); - } - - //########################################################### - //alternate filter configuration - XmlOut outFilter = outPair["LocalFilter"]; - writeConfig(enhPair.localFilter, outFilter); -} - - -void writeConfig(const MainConfiguration& mainCfg, XmlOut& out) -{ - XmlOut outCmp = out["MainConfig"]["Comparison"]; - - writeConfig(mainCfg.cmpConfig, outCmp); - //########################################################### - - XmlOut outSync = out["MainConfig"]["SyncConfig"]; - - writeConfig(mainCfg.syncCfg, outSync); - //########################################################### - - XmlOut outFilter = out["MainConfig"]["GlobalFilter"]; - //write filter settings - writeConfig(mainCfg.globalFilter, outFilter); - - //########################################################### - //write all folder pairs - - XmlOut outFp = out["MainConfig"]["FolderPairs"]; - - //write first folder pair - writeConfigFolderPair(mainCfg.firstPair, outFp); - - //write additional folder pairs - std::for_each(mainCfg.additionalPairs.begin(), mainCfg.additionalPairs.end(), - [&](const FolderPairEnh& fp) { writeConfigFolderPair(fp, outFp); }); -} - - -void writeConfig(const XmlGuiConfig& config, XmlOut& out) -{ - writeConfig(config.mainCfg, out); //write main config - - //write GUI specific config data - XmlOut outGuiCfg = out["GuiConfig"]; - - outGuiCfg["HideFiltered" ](config.hideFilteredElements); - outGuiCfg["HandleError" ](config.handleError); - outGuiCfg["SyncPreviewActive"](config.syncPreviewEnabled); -} - -void writeConfig(const XmlBatchConfig& config, XmlOut& out) -{ - - writeConfig(config.mainCfg, out); //write main config - - //write GUI specific config data - XmlOut outBatchCfg = out["BatchConfig"]; - - outBatchCfg["Silent" ](config.silent); - outBatchCfg["LogfileDirectory"](config.logFileDirectory); - outBatchCfg["LogfileCountMax" ](config.logFileCountMax); - outBatchCfg["HandleError" ](config.handleError); -} - - -void writeConfig(const XmlGlobalSettings& config, XmlOut& out) -{ - XmlOut outShared = out["Shared"]; - - //write program language setting - outShared["Language"](config.programLanguage); - - outShared["CopyLockedFiles" ](config.copyLockedFiles); - outShared["CopyFilePermissions" ](config.copyFilePermissions); - outShared["TransactionalFileCopy"](config.transactionalFileCopy); - outShared["VerifyCopiedFiles" ](config.verifyFileCopy); - - //max. allowed file time deviation - outShared["FileTimeTolerance"](config.fileTimeTolerance); - - XmlOut outOpt = outShared["ShowOptionalDialogs"]; - outOpt["CheckForDependentFolders" ](config.optDialogs.warningDependentFolders); - outOpt["CheckForMultipleWriteAccess" ](config.optDialogs.warningMultiFolderWriteAccess); - outOpt["CheckForSignificantDifference"](config.optDialogs.warningSignificantDifference); - outOpt["CheckForFreeDiskSpace"](config.optDialogs.warningNotEnoughDiskSpace); - outOpt["CheckForUnresolvedConflicts"](config.optDialogs.warningUnresolvedConflicts); - outOpt["NotifyDatabaseError"](config.optDialogs.warningSyncDatabase); - outOpt["CheckMissingRecycleBin"](config.optDialogs.warningRecyclerMissing); - outOpt["PopupOnConfigChange"](config.optDialogs.popupOnConfigChange); - outOpt["SummaryBeforeSync" ](config.optDialogs.showSummaryBeforeSync); - - //gui specific global settings (optional) - XmlOut outGui = out["Gui"]; - XmlOut outWnd = outGui["Windows"]["Main"]; - - //write application window size and position - outWnd["Width" ](config.gui.dlgSize.x); - outWnd["Height" ](config.gui.dlgSize.y); - outWnd["PosX" ](config.gui.dlgPos.x); - outWnd["PosY" ](config.gui.dlgPos.y); - outWnd["Maximized"](config.gui.isMaximized); - - outWnd["MaxFolderPairsVisible"](config.gui.maxFolderPairsVisible); - - outWnd["ManualDeletionOnBothSides"](config.gui.deleteOnBothSides); - outWnd["ManualDeletionUseRecycler"](config.gui.useRecyclerForManualDeletion); - outWnd["RespectCaseOnSearch" ](config.gui.textSearchRespectCase); - - outWnd["IconSize"](config.gui.iconSize); - - //########################################################### - - //write column attributes - XmlOut outColLeft = outWnd["LeftColumns"]; - outColLeft.attribute("AutoAdjust", config.gui.autoAdjustColumnsLeft); - - outColLeft(config.gui.columnAttribLeft); - - //########################################################### - XmlOut outColRight = outWnd["RightColumns"]; - outColRight.attribute("AutoAdjust", config.gui.autoAdjustColumnsRight); - - outColRight(config.gui.columnAttribRight); - - outWnd["FolderHistoryLeft" ](config.gui.folderHistoryLeft); - outWnd["FolderHistoryRight"](config.gui.folderHistoryRight); - outWnd["MaximumHistorySize"](config.gui.folderHistMax); - outWnd["Perspective" ](config.gui.guiPerspectiveLast); - - //external applications - outGui["ExternalApplications"](config.gui.externelApplications); - - //load config file history - outGui["LastConfigActive"](config.gui.lastUsedConfigFiles); - outGui["ConfigHistory"](config.gui.cfgFileHistory); - - //last update check - outGui["LastUpdateCheck"](config.gui.lastUpdateCheck); - - //batch specific global settings - //XmlOut outBatch = out["Batch"]; -} - - -template <class ConfigType> -void writeConfig(const ConfigType& config, XmlType type, const wxString& filename) -{ - XmlDoc doc("FreeFileSync"); - setXmlType(doc, type); //throw() - - XmlOut out(doc); - writeConfig(config, out); - - saveXmlDocument(doc, filename); //throw (FfsXmlError) -} -} - -void xmlAccess::writeConfig(const XmlGuiConfig& config, const wxString& filename) -{ - ::writeConfig(config, XML_TYPE_GUI, filename); //throw (FfsXmlError) -} - - -void xmlAccess::writeConfig(const XmlBatchConfig& config, const wxString& filename) -{ - ::writeConfig(config, XML_TYPE_BATCH, filename); //throw (FfsXmlError) -} - - -void xmlAccess::writeConfig(const XmlGlobalSettings& config) -{ - ::writeConfig(config, XML_TYPE_GLOBAL, getGlobalConfigFile()); //throw (FfsXmlError) -} diff --git a/library/process_xml.h b/library/process_xml.h deleted file mode 100644 index 851b0d9c..00000000 --- a/library/process_xml.h +++ /dev/null @@ -1,283 +0,0 @@ -// ************************************************************************** -// * This file is part of the FreeFileSync project. It is distributed under * -// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * -// * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * -// ************************************************************************** - -#ifndef PROCESSXML_H_INCLUDED -#define PROCESSXML_H_INCLUDED - -#include <wx/gdicmn.h> -#include "../structures.h" -#include "../shared/xml_error.h" -#include "../shared/localization.h" - -namespace xmlAccess -{ -enum XmlType -{ - XML_TYPE_GUI, - XML_TYPE_BATCH, - XML_TYPE_GLOBAL, - XML_TYPE_OTHER -}; - -XmlType getXmlType(const wxString& filename); //throw() - - -enum OnError -{ - ON_ERROR_POPUP, - ON_ERROR_IGNORE, - ON_ERROR_EXIT -}; - -enum OnGuiError -{ - ON_GUIERROR_POPUP, - ON_GUIERROR_IGNORE -}; - -enum ColumnTypes -{ - DIRECTORY, //this needs to begin with 0 and be continuous (some code relies on it) - FULL_PATH, - REL_PATH, - FILENAME, - SIZE, - DATE, - EXTENSION -}; -const size_t COLUMN_TYPE_COUNT = 7; - -struct ColumnAttrib -{ - ColumnTypes type; - bool visible; - size_t position; - int width; -}; -typedef std::vector<ColumnAttrib> ColumnAttributes; - - -typedef wxString Description; -typedef wxString Commandline; -typedef std::vector<std::pair<Description, Commandline> > ExternalApps; - -//--------------------------------------------------------------------- -struct XmlGuiConfig -{ - XmlGuiConfig() : - hideFilteredElements(false), - handleError(ON_GUIERROR_POPUP), - syncPreviewEnabled(true) {} //initialize values - - zen::MainConfiguration mainCfg; - - bool hideFilteredElements; - OnGuiError handleError; //reaction on error situation during synchronization - bool syncPreviewEnabled; - - bool operator==(const XmlGuiConfig& other) const - { - return mainCfg == other.mainCfg && - hideFilteredElements == other.hideFilteredElements && - handleError == other.handleError && - syncPreviewEnabled == other.syncPreviewEnabled; - } - - bool operator!=(const XmlGuiConfig& other) const { return !(*this == other); } -}; - - -struct XmlBatchConfig -{ - XmlBatchConfig() : - silent(false), - logFileCountMax(200), - handleError(ON_ERROR_POPUP) {} - - zen::MainConfiguration mainCfg; - - bool silent; - wxString logFileDirectory; - size_t logFileCountMax; - OnError handleError; //reaction on error situation during synchronization -}; - - -struct OptionalDialogs -{ - OptionalDialogs() { resetDialogs();} - - void resetDialogs(); - - bool warningDependentFolders; - bool warningMultiFolderWriteAccess; - bool warningSignificantDifference; - bool warningNotEnoughDiskSpace; - bool warningUnresolvedConflicts; - bool warningSyncDatabase; - bool warningRecyclerMissing; - bool popupOnConfigChange; - bool showSummaryBeforeSync; -}; - - -enum FileIconSize -{ - ICON_SIZE_SMALL, - ICON_SIZE_MEDIUM, - ICON_SIZE_LARGE -}; - - -wxString getGlobalConfigFile(); - -struct XmlGlobalSettings -{ - //--------------------------------------------------------------------- - //Shared (GUI/BATCH) settings - XmlGlobalSettings() : - programLanguage(zen::retrieveSystemLanguage()), - copyLockedFiles(true), - copyFilePermissions(false), - fileTimeTolerance(2), //default 2s: FAT vs NTFS - verifyFileCopy(false), - transactionalFileCopy(true) {} - - int programLanguage; - bool copyLockedFiles; //VSS usage - bool copyFilePermissions; - - size_t fileTimeTolerance; //max. allowed file time deviation - bool verifyFileCopy; //verify copied files - bool transactionalFileCopy; - - OptionalDialogs optDialogs; - - //--------------------------------------------------------------------- - struct _Gui - { - _Gui() : - dlgPos(wxDefaultCoord, wxDefaultCoord), - dlgSize(wxDefaultCoord, wxDefaultCoord), - isMaximized(false), - maxFolderPairsVisible(6), - autoAdjustColumnsLeft(false), - autoAdjustColumnsRight(false), - folderHistMax(15), - deleteOnBothSides(false), - useRecyclerForManualDeletion(true), //enable if OS supports it; else user will have to activate first and then get an error message -#ifdef FFS_WIN - textSearchRespectCase(false), -#elif defined FFS_LINUX - textSearchRespectCase(true), -#endif - iconSize(ICON_SIZE_MEDIUM), - lastUpdateCheck(0) - { - //default external apps will be translated "on the fly"!!! -#ifdef FFS_WIN - externelApplications.push_back(std::make_pair(wxT("Show in Explorer"), //mark for extraction: _("Show in Explorer") - wxT("explorer /select, \"%name\""))); - externelApplications.push_back(std::make_pair(wxT("Open with default application"), //mark for extraction: _("Open with default application") - wxT("\"%name\""))); -#elif defined FFS_LINUX - externelApplications.push_back(std::make_pair(wxT("Browse directory"), //mark for extraction: _("Browse directory") - wxT("xdg-open \"%dir\""))); - externelApplications.push_back(std::make_pair(wxT("Open with default application"), //mark for extraction: _("Open with default application") - wxT("xdg-open \"%name\""))); -#endif - } - - wxPoint dlgPos; - wxSize dlgSize; - bool isMaximized; - - int maxFolderPairsVisible; - - ColumnAttributes columnAttribLeft; - ColumnAttributes columnAttribRight; - - bool autoAdjustColumnsLeft; - bool autoAdjustColumnsRight; - - ExternalApps externelApplications; - - std::vector<wxString> cfgFileHistory; - std::vector<wxString> lastUsedConfigFiles; - - std::vector<Zstring> folderHistoryLeft; - std::vector<Zstring> folderHistoryRight; - unsigned int folderHistMax; - - bool deleteOnBothSides; - bool useRecyclerForManualDeletion; - bool textSearchRespectCase; - - FileIconSize iconSize; - - long lastUpdateCheck; //time of last update check - - wxString guiPerspectiveLast; //used by wxAuiManager - } gui; - - //--------------------------------------------------------------------- - //struct _Batch -}; - - -inline -bool sortByType(const ColumnAttrib& a, const ColumnAttrib& b) -{ - return a.type < b.type; -} - - -inline -bool sortByPositionOnly(const ColumnAttrib& a, const ColumnAttrib& b) -{ - return a.position < b.position; -} - - -inline -bool sortByPositionAndVisibility(const ColumnAttrib& a, const ColumnAttrib& b) -{ - if (a.visible == false) //hidden elements shall appear at end of vector - return false; - if (b.visible == false) - return true; - return a.position < b.position; -} - -void readConfig(const wxString& filename, XmlGuiConfig& config); //throw xmlAccess::FfsXmlError -void readConfig(const wxString& filename, XmlBatchConfig& config); //throw xmlAccess::FfsXmlError -void readConfig( XmlGlobalSettings& config); //throw xmlAccess::FfsXmlError - -void writeConfig(const XmlGuiConfig& config, const wxString& filename); //throw xmlAccess::FfsXmlError -void writeConfig(const XmlBatchConfig& config, const wxString& filename); //throw xmlAccess::FfsXmlError -void writeConfig(const XmlGlobalSettings& config); //throw xmlAccess::FfsXmlError - -//config conversion utilities -XmlGuiConfig convertBatchToGui(const XmlBatchConfig& batchCfg); -XmlBatchConfig convertGuiToBatch(const XmlGuiConfig& guiCfg, const wxString& referenceFile); - - -//convert (multiple) *.ffs_gui, *.ffs_batch files or combinations of both into target config structure: -enum MergeType -{ - MERGE_GUI, //pure gui config files - MERGE_BATCH, // " batch " - MERGE_GUI_BATCH, //gui and batch files - MERGE_OTHER -}; -MergeType getMergeType(const std::vector<wxString>& filenames); //throw () - -void convertConfig(const std::vector<wxString>& filenames, XmlGuiConfig& config); //throw xmlAccess::FfsXmlError -void convertConfig(const std::vector<wxString>& filenames, XmlBatchConfig& config); //throw xmlAccess::FfsXmlError -} - - -#endif // PROCESSXML_H_INCLUDED diff --git a/library/resources.cpp b/library/resources.cpp deleted file mode 100644 index d8dacc06..00000000 --- a/library/resources.cpp +++ /dev/null @@ -1,122 +0,0 @@ -// ************************************************************************** -// * This file is part of the FreeFileSync project. It is distributed under * -// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * -// * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * -// ************************************************************************** - -#include "resources.h" -#include <wx/wfstream.h> -#include <wx/zipstrm.h> -#include <wx/image.h> -#include <wx/icon.h> -#include <wx/mstream.h> -#include "../shared/string_conv.h" -#include <memory> -#include "../shared/standard_paths.h" - -using namespace zen; - - -const GlobalResources& GlobalResources::instance() -{ - static GlobalResources inst; - return inst; -} - - -namespace -{ -void loadAnimFromZip(wxZipInputStream& zipInput, wxAnimation* animation) -{ - //Workaround for wxWidgets: - //construct seekable input stream (zip-input stream is non-seekable) for wxAnimation::Load() - //luckily this method call is very fast: below measurement precision! - std::vector<char> data; - data.reserve(10000); - - int newValue = 0; - while ((newValue = zipInput.GetC()) != wxEOF) - data.push_back(newValue); - - wxMemoryInputStream seekAbleStream(&data.front(), data.size()); //stream does not take ownership of data - - animation->Load(seekAbleStream, wxANIMATION_TYPE_GIF); -} -} - - -GlobalResources::GlobalResources() -{ - //init all the other resource files - animationMoney = new wxAnimation(wxNullAnimation); - animationSync = new wxAnimation(wxNullAnimation); - programIcon = new wxIcon(wxNullIcon); - - wxFFileInputStream input(zen::getResourceDir() + wxT("Resources.zip")); - if (input.IsOk()) //if not... we don't want to react too harsh here - { - //activate support for .png files - wxImage::AddHandler(new wxPNGHandler); //ownership passed - - wxZipInputStream resourceFile(input); - - while (true) - { - std::unique_ptr<wxZipEntry> entry(resourceFile.GetNextEntry()); - if (entry.get() == NULL) - break; - - const wxString name = entry->GetName(); - - //generic image loading - if (name.EndsWith(wxT(".png"))) - { - if (bitmapResource.find(name) == bitmapResource.end()) //avoid duplicate entry: prevent memory leak! - bitmapResource[name] = new wxBitmap(wxImage(resourceFile, wxBITMAP_TYPE_PNG)); - } - else if (name == wxT("money.gif")) - loadAnimFromZip(resourceFile, animationMoney); - else if (name == wxT("working.gif")) - loadAnimFromZip(resourceFile, animationSync); - } - } - -#ifdef FFS_WIN - //for compatibility it seems we need to stick with a "real" icon - *programIcon = wxIcon(wxT("A_PROGRAM_ICON")); -#else - //use big logo bitmap for better quality - programIcon->CopyFromBitmap(getImageInt(wxT("FreeFileSync.png"))); - //attention: this is the reason we need a member getImage -> it must not implicitly create static object instance!!! - //erroneously calling static object constructor twice will deadlock on Linux!! -#endif -} - - -GlobalResources::~GlobalResources() -{ - //free bitmap resources - for (std::map<wxString, wxBitmap*>::iterator i = bitmapResource.begin(); i != bitmapResource.end(); ++i) - delete i->second; - - //free other resources - delete animationMoney; - delete animationSync; - delete programIcon; -} - - -const wxBitmap& GlobalResources::getImageInt(const wxString& imageName) const -{ - const std::map<wxString, wxBitmap*>::const_iterator bmp = bitmapResource.find( - imageName.Find(L'.') == wxNOT_FOUND ? //assume .png ending if nothing else specified - imageName + wxT(".png") : - imageName); - if (bmp != bitmapResource.end()) - return *bmp->second; - else - { - assert(false); - return wxNullBitmap; - } -} diff --git a/library/resources.h b/library/resources.h deleted file mode 100644 index e985d9bf..00000000 --- a/library/resources.h +++ /dev/null @@ -1,45 +0,0 @@ -// ************************************************************************** -// * This file is part of the FreeFileSync project. It is distributed under * -// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * -// * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * -// ************************************************************************** - -#ifndef RESOURCES_H_INCLUDED -#define RESOURCES_H_INCLUDED - -#include <wx/bitmap.h> -#include <wx/animate.h> -#include <wx/string.h> -#include <map> - - -class GlobalResources -{ -public: - static const wxBitmap& getImage(const wxString& name) - { - const GlobalResources& inst = instance(); - return inst.getImageInt(name); - } - - static const GlobalResources& instance(); - - //global image resource objects - wxAnimation* animationMoney; - wxAnimation* animationSync; - wxIcon* programIcon; - -private: - GlobalResources(); - ~GlobalResources(); - GlobalResources(const GlobalResources&); - GlobalResources& operator=(const GlobalResources&); - - const wxBitmap& getImageInt(const wxString& name) const; - - - //resource mapping - std::map<wxString, wxBitmap*> bitmapResource; -}; - -#endif // RESOURCES_H_INCLUDED diff --git a/library/soft_filter.h b/library/soft_filter.h deleted file mode 100644 index f3c1bd41..00000000 --- a/library/soft_filter.h +++ /dev/null @@ -1,121 +0,0 @@ -// ************************************************************************** -// * This file is part of the FreeFileSync project. It is distributed under * -// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * -// * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * -// ************************************************************************** - -#ifndef SOFT_FILTER_H_INCLUDED -#define SOFT_FILTER_H_INCLUDED - -#include <algorithm> -#include <limits> -#include "../structures.h" -#include <wx/stopwatch.h> - -namespace zen -{ -/* -Semantics of SoftFilter: -1. It potentially may match only one side => it MUST NOT be applied while traversing a single folder to avoid mismatches -2. => it is applied after traversing and just marks rows, (NO deletions after comparison are allowed) -3. => equivalent to a user temporarily (de-)selecting rows -> not relevant for <Automatic>-mode! ;) -*/ - -class SoftFilter -{ -public: - SoftFilter(size_t timeSpan, UnitTime unitTimeSpan, - size_t sizeMin, UnitSize unitSizeMin, - size_t sizeMax, UnitSize unitSizeMax); - - bool matchTime(Int64 writeTime) const { return timeFrom_ <= writeTime; } - bool matchSize(UInt64 fileSize) const { return sizeMin_ <= fileSize && fileSize <= sizeMax_; } - bool matchFolder() const { return timeFrom_ == std::numeric_limits<Int64>::min(); } - //if date filter is active we deactivate all folders: effectively gets rid of empty folders! - - bool isNull() const; //filter is equivalent to NullFilter, but may be technically slower - - //small helper method: merge two soft filters - friend SoftFilter combineFilters(const SoftFilter& first, const SoftFilter& second); - -private: - SoftFilter(const Int64& timeFrom, - const UInt64& sizeMin, - const UInt64& sizeMax); - - Int64 timeFrom_; //unit: UTC, seconds - UInt64 sizeMin_; //unit: bytes - UInt64 sizeMax_; //unit: bytes -}; -} - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -// ----------------------- implementation ----------------------- -namespace zen -{ -inline -SoftFilter::SoftFilter(size_t timeSpan, UnitTime unitTimeSpan, - size_t sizeMin, UnitSize unitSizeMin, - size_t sizeMax, UnitSize unitSizeMax) -{ - resolveUnits(timeSpan, unitTimeSpan, - sizeMin, unitSizeMin, - sizeMax, unitSizeMax, - timeFrom_, - sizeMin_, - sizeMax_); -} - -inline -SoftFilter::SoftFilter(const Int64& timeFrom, - const UInt64& sizeMin, - const UInt64& sizeMax) : - timeFrom_(timeFrom), - sizeMin_ (sizeMin), - sizeMax_ (sizeMax) {} - -inline -SoftFilter combineFilters(const SoftFilter& first, const SoftFilter& second) -{ - return SoftFilter(std::max(first.timeFrom_, second.timeFrom_), - std::max(first.sizeMin_, second.sizeMin_), - std::min(first.sizeMax_, second.sizeMax_)); -} - -inline -bool SoftFilter::isNull() const //filter is equivalent to NullFilter, but may be technically slower -{ - return timeFrom_ == std::numeric_limits<Int64>::min() && - sizeMin_ == 0U && - sizeMax_ == std::numeric_limits<UInt64>::max(); -} -} - -#endif // SOFT_FILTER_H_INCLUDED diff --git a/library/statistics.cpp b/library/statistics.cpp deleted file mode 100644 index f6ec01ad..00000000 --- a/library/statistics.cpp +++ /dev/null @@ -1,378 +0,0 @@ -// ************************************************************************** -// * This file is part of the FreeFileSync project. It is distributed under * -// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * -// * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * -// ************************************************************************** - -#include "statistics.h" - -#include <wx/ffile.h> -#include "../shared/global_func.h" -#include "status_handler.h" -#include "../shared/util.h" -#include "../shared/i18n.h" -#include <limits> -#include <wx/stopwatch.h> -#include "../shared/assert_static.h" - - -using namespace zen; - -RetrieveStatistics::RetrieveStatistics() : - timer(new wxStopWatch) {} - - -RetrieveStatistics::~RetrieveStatistics() -{ - //write statistics to a file - wxFFile outputFile(wxT("statistics.dat"), wxT("w")); - - outputFile.Write(wxT("Time(ms);Objects;Data\n")); - - for (std::vector<StatEntry>::const_iterator i = data.begin(); i != data.end(); ++i) - { - outputFile.Write(toString<wxString>(i->time)); - outputFile.Write(wxT(";")); - outputFile.Write(toString<wxString>(i->objects)); - outputFile.Write(wxT(";")); - outputFile.Write(toString<wxString>(i->value)); - outputFile.Write(wxT("\n")); - } -} - - -void RetrieveStatistics::writeEntry(const double value, const int objects) -{ - StatEntry newEntry; - newEntry.value = value; - newEntry.objects = objects; - newEntry.time = timer->Time(); - data.push_back(newEntry); -} - - -//######################################################################################## -namespace -{ -template <class T> -inline -bool isNull(T number) -{ - return common::abs(number) <= std::numeric_limits<T>::epsilon(); //epsilon == 0 for integer types, therefore less-equal(!) -} -} - - -enum UnitRemTime -{ - URT_SEC, - URT_MIN, - URT_HOUR, - URT_DAY -}; - - -inline -wxString Statistics::formatRemainingTime(double timeInMs) const -{ - double remainingTime = timeInMs / 1000; - - //determine preferred unit - UnitRemTime unit = URT_SEC; - if (remainingTime > 55) - { - unit = URT_MIN; - remainingTime /= 60; - if (remainingTime > 59) - { - unit = URT_HOUR; - remainingTime /= 60; - if (remainingTime > 23) - { - unit = URT_DAY; - remainingTime /= 24; - } - } - } - - int formattedTime = common::round(remainingTime); - - //reduce precision to 5 seconds - if (unit == URT_SEC && formattedTime % 5 != 0) - formattedTime += 5 - formattedTime % 5; //"ceiling" - - - //avoid "jumping back and forth" when fluctuating around .5 - if (remainingTimeLast < formattedTime) - { - if (unit == URT_SEC) - { - formattedTime = common::round(remainingTime); - formattedTime -= formattedTime % 5; //"floor" - } - else - formattedTime = static_cast<int>(remainingTime); //"floor" - } - remainingTimeLast = formattedTime; - - //generate output message - wxString output; - switch (unit) - { - case URT_SEC: - output = _P("1 sec", "%x sec", formattedTime); - break; - case URT_MIN: - output = _P("1 min", "%x min", formattedTime); - break; - case URT_HOUR: - output = _P("1 hour", "%x hours", formattedTime); - break; - case URT_DAY: - output = _P("1 day", "%x days", formattedTime); - break; - } - output.Replace(wxT("%x"), zen::toStringSep(formattedTime)); - return output; -} - - -Statistics::Statistics(int totalObjectCount, - double totalDataAmount, - unsigned windowSizeRemainingTime, - unsigned windowSizeBytesPerSecond) : - objectsTotal(totalObjectCount), - dataTotal(totalDataAmount), - windowSizeRemTime(windowSizeRemainingTime), - windowSizeBPS(windowSizeBytesPerSecond), - windowMax(std::max(windowSizeRemainingTime, windowSizeBytesPerSecond)), - remainingTimeLast(std::numeric_limits<int>::max()), //something "big" - timer(new wxStopWatch) {} - -Statistics::~Statistics() -{ - delete timer; -} - -void Statistics::addMeasurement(int objectsCurrent, double dataCurrent) -{ - Record newRecord; - newRecord.objects = objectsCurrent; - newRecord.data = dataCurrent; - - const long currentTime = timer->Time(); - - const TimeRecordMap::value_type newEntry(currentTime, newRecord); - - //insert new record - if (!measurements.empty()) - { - //assert(dataCurrent >= (--measurements.end())->second.data); - measurements.insert(--measurements.end(), newEntry); //use fact that time is monotonously ascending - } - else - measurements.insert(newEntry); - - //remove all records earlier than "currentTime - windowSize" - const long newBegin = currentTime - windowMax; - TimeRecordMap::iterator windowBegin = measurements.upper_bound(newBegin); - if (windowBegin != measurements.begin()) - measurements.erase(measurements.begin(), --windowBegin); //retain one point before newBegin in order to handle "measurement holes" -} - - -wxString Statistics::getRemainingTime() const -{ - if (!measurements.empty()) - { - const TimeRecordMap::value_type& backRecord = *measurements.rbegin(); - //find start of records "window" - const long frontTime = backRecord.first - windowSizeRemTime; - TimeRecordMap::const_iterator windowBegin = measurements.upper_bound(frontTime); - if (windowBegin != measurements.begin()) - --windowBegin; //one point before window begin in order to handle "measurement holes" - - const TimeRecordMap::value_type& frontRecord = *windowBegin; - //----------------------------------------------------------------------------------------------- - const double timeDelta = backRecord.first - frontRecord.first; - const double dataDelta = backRecord.second.data - frontRecord.second.data; - - const double dataRemaining = dataTotal - backRecord.second.data; - - if (!isNull(dataDelta)) - return formatRemainingTime(dataRemaining * timeDelta / dataDelta); - } - - return wxT("-"); //fallback -} - - -wxString Statistics::getBytesPerSecond() const -{ - if (!measurements.empty()) - { - const TimeRecordMap::value_type& backRecord = *measurements.rbegin(); - //find start of records "window" - const long frontTime = backRecord.first - windowSizeBPS; - TimeRecordMap::const_iterator windowBegin = measurements.upper_bound(frontTime); - if (windowBegin != measurements.begin()) - --windowBegin; //one point before window begin in order to handle "measurement holes" - - const TimeRecordMap::value_type& frontRecord = *windowBegin; - //----------------------------------------------------------------------------------------------- - const double timeDelta = backRecord.first - frontRecord.first; - const double dataDelta = backRecord.second.data - frontRecord.second.data; - - if (!isNull(timeDelta)) - if (dataDelta > 0) //may be negative if user cancels copying - return zen::formatFilesizeToShortString(zen::UInt64(dataDelta * 1000 / timeDelta)) + _("/sec"); - } - - return wxT("-"); //fallback -} - - -void Statistics::pauseTimer() -{ - timer->Pause(); -} - - -void Statistics::resumeTimer() -{ - timer->Resume(); -} - -/* -class for calculation of remaining time: ----------------------------------------- -"filesize |-> time" is an affine linear function f(x) = z_1 + z_2 x - -For given n measurements, sizes x_0, ..., x_n and times f_0, ..., f_n, the function f (as a polynom of degree 1) can be lineary approximated by - -z_1 = (r - s * q / p) / ((n + 1) - s * s / p) -z_2 = (q - s * z_1) / p = (r - (n + 1) z_1) / s - -with -p := x_0^2 + ... + x_n^2 -q := f_0 x_0 + ... + f_n x_n -r := f_0 + ... + f_n -s := x_0 + ... + x_n - -=> the time to process N files with amount of data D is: N * z_1 + D * z_2 - -Problem: --------- -Times f_0, ..., f_n can be very small so that precision of the PC clock is poor. -=> Times have to be accumulated to enhance precision: -Copying of m files with sizes x_i and times f_i (i = 1, ..., m) takes sum_i f(x_i) := m * z_1 + z_2 * sum x_i = sum f_i -With X defined as the accumulated sizes and F the accumulated times this gives: (in theory...) -m * z_1 + z_2 * X = F <=> -z_1 + z_2 * X / m = F / m - -=> we obtain a new (artificial) measurement with size X / m and time F / m to be used in the linear approximation above - - -Statistics::Statistics(const int totalObjectCount, const double totalDataAmount, const unsigned recordCount) : - objectsTotal(totalObjectCount), - dataTotal(totalDataAmount), - recordsMax(recordCount), - objectsLast(0), - dataLast(0), - timeLast(wxGetLocalTimeMillis()), - z1_current(0), - z2_current(0), - dummyRecordPresent(false) {} - - -wxString Statistics::getRemainingTime(const int objectsCurrent, const double dataCurrent) -{ - //add new measurement point - const int m = objectsCurrent - objectsLast; - if (m != 0) - { - objectsLast = objectsCurrent; - - const double X = dataCurrent - dataLast; - dataLast = dataCurrent; - - const zen::Int64 timeCurrent = wxGetLocalTimeMillis(); - const double F = (timeCurrent - timeLast).ToDouble(); - timeLast = timeCurrent; - - record newEntry; - newEntry.x_i = X / m; - newEntry.f_i = F / m; - - //remove dummy record - if (dummyRecordPresent) - { - measurements.pop_back(); - dummyRecordPresent = false; - } - - //insert new record - measurements.push_back(newEntry); - if (measurements.size() > recordsMax) - measurements.pop_front(); - } - else //dataCurrent increased without processing new objects: - { //modify last measurement until m != 0 - const double X = dataCurrent - dataLast; //do not set dataLast, timeLast variables here, but write dummy record instead - if (!isNull(X)) - { - const zen::Int64 timeCurrent = wxGetLocalTimeMillis(); - const double F = (timeCurrent - timeLast).ToDouble(); - - record modifyEntry; - modifyEntry.x_i = X; - modifyEntry.f_i = F; - - //insert dummy record - if (!dummyRecordPresent) - { - measurements.push_back(modifyEntry); - if (measurements.size() > recordsMax) - measurements.pop_front(); - dummyRecordPresent = true; - } - else //modify dummy record - measurements.back() = modifyEntry; - } - } - - //calculate remaining time based on stored measurement points - double p = 0; - double q = 0; - double r = 0; - double s = 0; - for (std::list<record>::const_iterator i = measurements.begin(); i != measurements.end(); ++i) - { - const double x_i = i->x_i; - const double f_i = i->f_i; - p += x_i * x_i; - q += f_i * x_i; - r += f_i; - s += x_i; - } - - if (!isNull(p)) - { - const double n = measurements.size(); - const double tmp = (n - s * s / p); - - if (!isNull(tmp) && !isNull(s)) - { - const double z1 = (r - s * q / p) / tmp; - const double z2 = (r - n * z1) / s; //not (n + 1) here, since n already is the number of measurements - - //refresh current values for z1, z2 - z1_current = z1; - z2_current = z2; - } - } - - return formatRemainingTime((objectsTotal - objectsCurrent) * z1_current + (dataTotal - dataCurrent) * z2_current); -} - -*/ diff --git a/library/statistics.h b/library/statistics.h deleted file mode 100644 index cfa3e07c..00000000 --- a/library/statistics.h +++ /dev/null @@ -1,83 +0,0 @@ -// ************************************************************************** -// * This file is part of the FreeFileSync project. It is distributed under * -// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * -// * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * -// ************************************************************************** - -#ifndef STATISTICS_H_INCLUDED -#define STATISTICS_H_INCLUDED - -#include <vector> -#include <map> -#include <memory> -#include <wx/defs.h> - - -#include <wx/string.h> - -class wxStopWatch; - - -class RetrieveStatistics -{ -public: - wxDEPRECATED( RetrieveStatistics() ); //generate compiler warnings as a reminder to remove code after measurements - ~RetrieveStatistics(); - - void writeEntry(const double value, const int objects); - -private: - struct StatEntry - { - long time; - int objects; - double value; - }; - - std::vector<StatEntry> data; - std::unique_ptr<wxStopWatch> timer; -}; - - -class Statistics -{ -public: - Statistics(int totalObjectCount, - double totalDataAmount, - unsigned windowSizeRemainingTime, //time in ms - unsigned windowSizeBytesPerSecond); //time in ms - - ~Statistics(); - - void addMeasurement(int objectsCurrent, double dataCurrent); - wxString getRemainingTime() const; //returns the remaining time in milliseconds - wxString getBytesPerSecond() const; - - void pauseTimer(); - void resumeTimer(); - -private: - wxString formatRemainingTime(double timeInMs) const; - - const int objectsTotal; - const double dataTotal; - - const unsigned windowSizeRemTime; //"window width" of statistics used for calculation of remaining time in ms - const unsigned windowSizeBPS; // - const unsigned windowMax; - - mutable int remainingTimeLast; //used for "smoothening" remaining time - - struct Record - { - int objects; //object count - double data; //unit: bytes - }; - - typedef std::multimap<long, Record> TimeRecordMap; //time, unit: milliseconds - TimeRecordMap measurements; // - - wxStopWatch* timer; -}; - -#endif // STATISTICS_H_INCLUDED diff --git a/library/status_handler.cpp b/library/status_handler.cpp deleted file mode 100644 index 55f82c64..00000000 --- a/library/status_handler.cpp +++ /dev/null @@ -1,34 +0,0 @@ -// ************************************************************************** -// * This file is part of the FreeFileSync project. It is distributed under * -// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * -// * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * -// ************************************************************************** - -#include "status_handler.h" -#include <wx/app.h> -#include <ctime> - -void updateUiNow() -{ - //process UI events and prevent application from "not responding" -> NO performance issue! - wxTheApp->Yield(); - - // while (wxTheApp->Pending()) - // wxTheApp->Dispatch(); -} - - -bool updateUiIsAllowed() -{ - const std::clock_t CLOCK_UPDATE_INTERVAL = UI_UPDATE_INTERVAL * CLOCKS_PER_SEC / 1000; - - static std::clock_t lastExec = 0; - const std::clock_t now = std::clock(); //this is quite fast: 2 * 10^-5 - - if (now - lastExec >= CLOCK_UPDATE_INTERVAL) //perform ui updates not more often than necessary - { - lastExec = now; - return true; - } - return false; -} diff --git a/library/status_handler.h b/library/status_handler.h deleted file mode 100644 index 1282f9f1..00000000 --- a/library/status_handler.h +++ /dev/null @@ -1,102 +0,0 @@ -// ************************************************************************** -// * This file is part of the FreeFileSync project. It is distributed under * -// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * -// * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * -// ************************************************************************** - -#ifndef STATUSHANDLER_H_INCLUDED -#define STATUSHANDLER_H_INCLUDED - -#include <wx/string.h> -#include <string> -#include "../shared/int64.h" - -const int UI_UPDATE_INTERVAL = 100; //unit: [ms]; perform ui updates not more often than necessary, 100 seems to be a good value with only a minimal performance loss - -bool updateUiIsAllowed(); //test if a specific amount of time is over -void updateUiNow(); //do the updating - - -//interfaces for status updates (can be implemented by GUI or Batch mode) - - -//report status during comparison and synchronization -struct ProcessCallback -{ - virtual ~ProcessCallback() {} - - //identifiers of different phases - enum Process - { - PROCESS_NONE = 10, - PROCESS_SCANNING, - PROCESS_COMPARING_CONTENT, - PROCESS_SYNCHRONIZING - }; - - //these methods have to be implemented in the derived classes to handle error and status information - virtual void initNewProcess(int objectsTotal, zen::Int64 dataTotal, Process processID) = 0; //informs about the total amount of data that will be processed from now on - - //note: this one must NOT throw in order to properly allow undoing setting of statistics! - //it is in general paired with a call to requestUiRefresh() to compensate! - virtual void updateProcessedData(int objectsProcessed, zen::Int64 dataProcessed) = 0; //throw() - - //opportunity to abort must be implemented in a frequently executed method like requestUiRefresh() - virtual void requestUiRefresh() = 0; //throw ? - - //this method is triggered repeatedly by requestUiRefresh() and can be used to refresh the ui by dispatching pending events - virtual void forceUiRefresh() = 0; - - //called periodically after data was processed: expected(!) to request GUI update - virtual void reportStatus(const wxString& text) = 0; //status info only, should not be logged! - - //called periodically after data was processed: expected(!) to request GUI update - virtual void reportInfo(const wxString& text) = 0; - - virtual void reportWarning(const wxString& warningMessage, bool& warningActive) = 0; - - //error handling: - enum Response - { - IGNORE_ERROR = 10, - RETRY - }; - virtual Response reportError (const wxString& errorMessage) = 0; //recoverable error situation - virtual void reportFatalError(const wxString& errorMessage) = 0; //non-recoverable error situation -}; - - -//gui may want to abort process -struct AbortCallback -{ - virtual ~AbortCallback() {} - virtual void requestAbortion() = 0; -}; - - -//actual callback implementation will have to satisfy "process" and "gui" -class StatusHandler : public ProcessCallback, public AbortCallback -{ -public: - StatusHandler() : abortRequested(false) {} - - virtual void requestUiRefresh() - { - if (updateUiIsAllowed()) //test if specific time span between ui updates is over - forceUiRefresh(); - - if (abortRequested) - abortThisProcess(); //abort can be triggered by requestAbortion() - } - - virtual void abortThisProcess() = 0; - virtual void requestAbortion() { abortRequested = true; } //this does NOT call abortThisProcess immediately, but when appropriate (e.g. async. processes finished) - bool abortIsRequested() { return abortRequested; } - -private: - bool abortRequested; -}; - - - -#endif // STATUSHANDLER_H_INCLUDED |