diff options
32 files changed, 1884 insertions, 1003 deletions
diff --git a/Application.cpp b/Application.cpp index 2c08f988..3c6465f1 100644 --- a/Application.cpp +++ b/Application.cpp @@ -30,8 +30,8 @@ bool Application::ProcessIdle() return wxApp::ProcessIdle(); } -//Note: initialization is done in the FIRST idle event instead in OnInit. Reason: Commandline mode requires the wxApp eventhandler to be established -//for UI update events. This is not the case in OnInit. +//Note: initialization is done in the FIRST idle event instead of OnInit. Reason: Commandline mode requires the wxApp eventhandler to be established +//for UI update events. This is not the case at the time of OnInit. bool Application::OnInit() { @@ -103,8 +103,8 @@ int Application::OnRun() int Application::OnExit() { - delete programLanguage; GlobalResources::unloadResourceFiles(); + delete programLanguage; return 0; } @@ -117,19 +117,19 @@ SyncDirection convertCmdlineCfg(const wxString& cfg, const int i) switch (cfg[i]) { case 'L': - return SyncDirLeft; + return syncDirLeft; break; case 'R': - return SyncDirRight; + return syncDirRight; break; case 'N': - return SyncDirNone; + return syncDirNone; break; default: assert(false); } //dummy return value to suppress compiler warning - return SyncDirNone; + return syncDirNone; } @@ -337,14 +337,14 @@ void Application::parseCommandline() //until here all options and parameters are consistent //-------------------------------------------------------------------- - CompareVariant cmpVar = CompareByMD5; //dummy value to suppress compiler warning + CompareVariant cmpVar = compareByMD5; //dummy value to suppress compiler warning SyncConfiguration syncConfiguration; FileCompareResult currentGridData; if (cmp == "SIZEDATE") - cmpVar = CompareByTimeAndSize; + cmpVar = compareByTimeAndSize; else if (cmp == "CONTENT") - cmpVar = CompareByMD5; + cmpVar = compareByMD5; else assert (false); @@ -362,17 +362,17 @@ void Application::parseCommandline() { //COMPARE DIRECTORIES //unsigned int startTime = GetTickCount(); - FreeFileSync::getModelDiff(currentGridData, - FreeFileSync::getFormattedDirectoryName(leftDir), - FreeFileSync::getFormattedDirectoryName(rightDir), - cmpVar, - &statusUpdater); + FreeFileSync::startCompareProcess(currentGridData, + FreeFileSync::getFormattedDirectoryName(leftDir), + FreeFileSync::getFormattedDirectoryName(rightDir), + cmpVar, + &statusUpdater); //wxMessageBox(wxString::Format(wxT("%i"), unsigned(GetTickCount()) - startTime)); //check if folders are already in sync bool nothingToSync = true; for (FileCompareResult::const_iterator i = currentGridData.begin(); i != currentGridData.end(); ++i) - if (i->cmpResult != FilesEqual) + if (i->cmpResult != filesEqual) { nothingToSync = false; break; @@ -395,9 +395,6 @@ void Application::parseCommandline() FreeFileSync::filterCurrentGridData(currentGridData, included, excluded); //START SYNCHRONIZATION - //adjust progress indicator - statusUpdater.switchToSyncProcess(FreeFileSync::calcTotalBytesToTransfer(currentGridData, syncConfiguration).get_d()); - //unsigned int startTime = GetTickCount(); FreeFileSync::startSynchronizationProcess(currentGridData, syncConfiguration, &statusUpdater, false); //default: do not use recycle bin since it's not sure if its possible //wxMessageBox(wxString::Format(wxT("%i"), unsigned(GetTickCount()) - startTime)); @@ -429,7 +426,7 @@ CommandLineStatusUpdater::CommandLineStatusUpdater(Application* application, boo app(application), skipErrors(skipErr), silentMode(silent), - switchedToSynchronisation(false) //used for changing mode of gauge + currentProcess(-1) { if (!silentMode) { @@ -449,41 +446,64 @@ CommandLineStatusUpdater::~CommandLineStatusUpdater() //print the results list unsigned int failedItems = unhandledErrors.GetCount(); - wxString result; + wxString errorMessages; if (failedItems) { - result = wxString(_("Warning: Synchronization failed for ")) + GlobalFunctions::numberToWxString(failedItems) + _(" item(s):\n\n"); + errorMessages = wxString(_("Warning: Synchronization failed for ")) + globalFunctions::numberToWxString(failedItems) + _(" item(s):\n\n"); for (unsigned int j = 0; j < failedItems; ++j) - result+= unhandledErrors[j] + "\n"; + errorMessages+= unhandledErrors[j] + "\n"; + + syncStatusFrame->setStatusText_NoUpdate(errorMessages); } - syncStatusFrame->setStatusText_NoUpdate(result); syncStatusFrame->updateStatusDialogNow(); } } inline -void CommandLineStatusUpdater::updateStatus(const wxString& text) +void CommandLineStatusUpdater::updateStatusText(const wxString& text) +{ + if (!silentMode) + syncStatusFrame->setStatusText_NoUpdate(text); +} + + +void CommandLineStatusUpdater::initNewProcess(int objectsTotal, double dataTotal, int processID) { + currentProcess = processID; if (!silentMode) { - if (switchedToSynchronisation) - syncStatusFrame->setStatusText_NoUpdate(text); - else - syncStatusFrame->setStatusText_NoUpdate(_("Scanning... ") + text); + if (currentProcess == FreeFileSync::scanningFilesProcess) + syncStatusFrame->m_staticTextStatus->SetLabel(_("Scanning...")); + + else if (currentProcess == FreeFileSync::calcMD5Process) + { + syncStatusFrame->resetGauge(objectsTotal, dataTotal); + syncStatusFrame->m_staticTextStatus->SetLabel(_("Comparing...")); + } + + else if (currentProcess == FreeFileSync::synchronizeFilesProcess) + { + syncStatusFrame->resetGauge(objectsTotal, dataTotal); + syncStatusFrame->m_staticTextStatus->SetLabel(_("Synchronizing...")); + } + else assert(false); } } inline -void CommandLineStatusUpdater::updateProgressIndicator(double number) +void CommandLineStatusUpdater::updateProcessedData(int objectsProcessed, double dataProcessed) { if (!silentMode) { - if (switchedToSynchronisation) - syncStatusFrame->incProgressIndicator_NoUpdate(number); - else + if (currentProcess == FreeFileSync::scanningFilesProcess) syncStatusFrame->m_gauge1->Pulse(); + else if (currentProcess == FreeFileSync::calcMD5Process) + syncStatusFrame->incProgressIndicator_NoUpdate(objectsProcessed, dataProcessed); + else if (currentProcess == FreeFileSync::synchronizeFilesProcess) + syncStatusFrame->incProgressIndicator_NoUpdate(objectsProcessed, dataProcessed); + else assert(false); } } @@ -495,7 +515,7 @@ int CommandLineStatusUpdater::reportError(const wxString& text) app->writeLog(text, _("Error")); if (skipErrors) // <- /|\ before return, the logfile is written!!! - return StatusUpdater::Continue; + return StatusUpdater::continueNext; else { abortionRequested = true; @@ -508,7 +528,7 @@ int CommandLineStatusUpdater::reportError(const wxString& text) if (skipErrors) //this option can be set from commandline or by the user in the error dialog on UI { unhandledErrors.Add(text); - return StatusUpdater::Continue; + return StatusUpdater::continueNext; } wxString errorMessage = text + _("\n\nContinue with next object, retry or abort synchronization?"); @@ -520,12 +540,12 @@ int CommandLineStatusUpdater::reportError(const wxString& text) switch (rv) { - case ErrorDlg::ContinueButtonPressed: + case ErrorDlg::continueButtonPressed: unhandledErrors.Add(text); - return StatusUpdater::Continue; - case ErrorDlg::RetryButtonPressed: - return StatusUpdater::Retry; - case ErrorDlg::AbortButtonPressed: + return StatusUpdater::continueNext; + case ErrorDlg::retryButtonPressed: + return StatusUpdater::retry; + case ErrorDlg::abortButtonPressed: { unhandledErrors.Add(text); abortionRequested = true; @@ -536,7 +556,7 @@ int CommandLineStatusUpdater::reportError(const wxString& text) } } - return StatusUpdater::Continue; //dummy return value + return StatusUpdater::continueNext; //dummy return value } @@ -555,21 +575,10 @@ inline void CommandLineStatusUpdater::updateFinalStatus(const wxString& text) //set by parsedCommandline() to communicate e.g. the final "success message" { if (!silentMode) - syncStatusFrame->m_textCtrlInfo->SetValue(text); + syncStatusFrame->setStatusText_NoUpdate(text); } -inline -void CommandLineStatusUpdater::switchToSyncProcess(double number) -{ - if (!silentMode) - { - syncStatusFrame->resetGauge(number); //usually this number is set in SyncStatus constructor, but in case of commandline - //the total number is known only ofter "compare" so it has to be set later - switchedToSynchronisation = true; - } -} - //###################################################################################################### diff --git a/Application.h b/Application.h index bcf49d00..8c9283ed 100644 --- a/Application.h +++ b/Application.h @@ -89,13 +89,12 @@ public: CommandLineStatusUpdater(Application* application, bool skipErr, bool silent); ~CommandLineStatusUpdater(); - void updateStatus(const wxString& text); - void updateProgressIndicator(double number); + void updateStatusText(const wxString& text); + void initNewProcess(int objectsTotal, double dataTotal, int processID); + void updateProcessedData(int objectsProcessed, double dataProcessed); int reportError(const wxString& text); - void triggerUI_Refresh(); - void switchToSyncProcess(double number); void updateFinalStatus(const wxString& text); private: @@ -104,8 +103,8 @@ private: bool skipErrors; bool silentMode; - bool switchedToSynchronisation; wxArrayString unhandledErrors; //list of non-resolved errors + int currentProcess; }; diff --git a/COMPILE.cmd b/COMPILE.cmd index 7df1cd02..e34c2dce 100644 --- a/COMPILE.cmd +++ b/COMPILE.cmd @@ -1,16 +1,22 @@ set widgets=C:\Programme\CodeBlocks\wxWidgets set sources=C:\Programme\CodeBlocks\Projects\FreeFileSync -mingw32-g++.exe -Wall -pipe -mthreads -D__GNUWIN32__ -D__WXMSW__ -DFFS_WIN -O2 -DNDEBUG -I%widgets%\include -I%widgets%\contrib\include -I%widgets%\lib\gcc_lib\msw -c %sources%\Application.cpp -o obj\Release\Application.o +md obj +md obj\Release +md obj\Release\library +md obj\Release\ui + +mingw32-g++.exe -Wall -pipe -mthreads -D__GNUWIN32__ -D__WXMSW__ -DFFS_WIN -O2 -DNDEBUG -I%widgets%\include -I%widgets%\contrib\include -I%widgets%\lib\gcc_lib\msw -c %sources%\application.cpp -o obj\Release\Application.o mingw32-g++.exe -Wall -pipe -mthreads -D__GNUWIN32__ -D__WXMSW__ -DFFS_WIN -O2 -DNDEBUG -I%widgets%\include -I%widgets%\contrib\include -I%widgets%\lib\gcc_lib\msw -c %sources%\FreeFileSync.cpp -o obj\Release\FreeFileSync.o -mingw32-g++.exe -Wall -pipe -mthreads -D__GNUWIN32__ -D__WXMSW__ -DFFS_WIN -O2 -DNDEBUG -I%widgets%\include -I%widgets%\contrib\include -I%widgets%\lib\gcc_lib\msw -c %sources%\Library\globalFunctions.cpp -o obj\Release\Library\globalFunctions.o -mingw32-g++.exe -Wall -pipe -mthreads -D__GNUWIN32__ -D__WXMSW__ -DFFS_WIN -O2 -DNDEBUG -I%widgets%\include -I%widgets%\contrib\include -I%widgets%\lib\gcc_lib\msw -c %sources%\UI\GUI_Generated.cpp -o obj\Release\UI\GUI_Generated.o -mingw32-g++.exe -Wall -pipe -mthreads -D__GNUWIN32__ -D__WXMSW__ -DFFS_WIN -O2 -DNDEBUG -I%widgets%\include -I%widgets%\contrib\include -I%widgets%\lib\gcc_lib\msw -c %sources%\UI\MainDialog.cpp -o obj\Release\UI\MainDialog.o -mingw32-g++.exe -Wall -pipe -mthreads -D__GNUWIN32__ -D__WXMSW__ -DFFS_WIN -O2 -DNDEBUG -I%widgets%\include -I%widgets%\contrib\include -I%widgets%\lib\gcc_lib\msw -c %sources%\UI\SyncDialog.cpp -o obj\Release\UI\SyncDialog.o -mingw32-g++.exe -Wall -pipe -mthreads -D__GNUWIN32__ -D__WXMSW__ -DFFS_WIN -O2 -DNDEBUG -I%widgets%\include -I%widgets%\contrib\include -I%widgets%\lib\gcc_lib\msw -c %sources%\library\CustomGrid.cpp -o obj\Release\library\CustomGrid.o -mingw32-g++.exe -Wall -pipe -mthreads -D__GNUWIN32__ -D__WXMSW__ -DFFS_WIN -O2 -DNDEBUG -I%widgets%\include -I%widgets%\contrib\include -I%widgets%\lib\gcc_lib\msw -c %sources%\ui\Resources.cpp -o obj\Release\ui\Resources.o -mingw32-g++.exe -Wall -pipe -mthreads -D__GNUWIN32__ -D__WXMSW__ -DFFS_WIN -O2 -DNDEBUG -I%widgets%\include -I%widgets%\contrib\include -I%widgets%\lib\gcc_lib\msw -c %sources%\ui\SmallDialogs.cpp -o obj\Release\ui\SmallDialogs.o -mingw32-gcc.exe -Wall -pipe -mthreads -D__GNUWIN32__ -D__WXMSW__ -DFFS_WIN -O2 -DNDEBUG -I%widgets%\wxWidgets\include -I%widgets%\contrib\include -I%widgets%\lib\gcc_lib\msw -c %sources%\Library\md5.c -o obj\Release\Library\md5.o +mingw32-g++.exe -Wall -pipe -mthreads -D__GNUWIN32__ -D__WXMSW__ -DFFS_WIN -O2 -DNDEBUG -I%widgets%\include -I%widgets%\contrib\include -I%widgets%\lib\gcc_lib\msw -c %sources%\library\globalFunctions.cpp -o obj\Release\library\globalFunctions.o +mingw32-g++.exe -Wall -pipe -mthreads -D__GNUWIN32__ -D__WXMSW__ -DFFS_WIN -O2 -DNDEBUG -I%widgets%\include -I%widgets%\contrib\include -I%widgets%\lib\gcc_lib\msw -c %sources%\library\multithreading.cpp -o obj\Release\library\multithreading.cpp.o +mingw32-g++.exe -Wall -pipe -mthreads -D__GNUWIN32__ -D__WXMSW__ -DFFS_WIN -O2 -DNDEBUG -I%widgets%\include -I%widgets%\contrib\include -I%widgets%\lib\gcc_lib\msw -c %sources%\ui\guiGenerated.cpp -o obj\Release\UI\GUI_Generated.o +mingw32-g++.exe -Wall -pipe -mthreads -D__GNUWIN32__ -D__WXMSW__ -DFFS_WIN -O2 -DNDEBUG -I%widgets%\include -I%widgets%\contrib\include -I%widgets%\lib\gcc_lib\msw -c %sources%\ui\mainDialog.cpp -o obj\Release\ui\MainDialog.o +mingw32-g++.exe -Wall -pipe -mthreads -D__GNUWIN32__ -D__WXMSW__ -DFFS_WIN -O2 -DNDEBUG -I%widgets%\include -I%widgets%\contrib\include -I%widgets%\lib\gcc_lib\msw -c %sources%\ui\syncDialog.cpp -o obj\Release\ui\SyncDialog.o +mingw32-g++.exe -Wall -pipe -mthreads -D__GNUWIN32__ -D__WXMSW__ -DFFS_WIN -O2 -DNDEBUG -I%widgets%\include -I%widgets%\contrib\include -I%widgets%\lib\gcc_lib\msw -c %sources%\library\customGrid.cpp -o obj\Release\library\CustomGrid.o +mingw32-g++.exe -Wall -pipe -mthreads -D__GNUWIN32__ -D__WXMSW__ -DFFS_WIN -O2 -DNDEBUG -I%widgets%\include -I%widgets%\contrib\include -I%widgets%\lib\gcc_lib\msw -c %sources%\ui\resources.cpp -o obj\Release\ui\Resources.o +mingw32-g++.exe -Wall -pipe -mthreads -D__GNUWIN32__ -D__WXMSW__ -DFFS_WIN -O2 -DNDEBUG -I%widgets%\include -I%widgets%\contrib\include -I%widgets%\lib\gcc_lib\msw -c %sources%\ui\smallDialogs.cpp -o obj\Release\ui\SmallDialogs.o +mingw32-gcc.exe -Wall -pipe -mthreads -D__GNUWIN32__ -D__WXMSW__ -DFFS_WIN -O2 -DNDEBUG -I%widgets%\wxWidgets\include -I%widgets%\contrib\include -I%widgets%\lib\gcc_lib\msw -c %sources%\library\md5.c -o obj\Release\library\md5.o windres.exe -i %sources%\resource.rc -J rc -o obj\Release\resource.res -O coff -I%widgets%\include -I%widgets%\lib\gcc_lib\msw -mingw32-g++.exe -L%widgets%\lib\gcc_lib -o FreeFileSync.exe obj\Release\Application.o obj\Release\FreeFileSync.o obj\Release\Library\globalFunctions.o obj\Release\Library\md5.o obj\Release\UI\GUI_Generated.o obj\Release\UI\MainDialog.o obj\Release\UI\SyncDialog.o obj\Release\library\CustomGrid.o obj\Release\ui\Resources.o obj\Release\ui\SmallDialogs.o obj\Release\resource.res -s -mthreads -lwxmsw28_adv -lwxmsw28_core -lwxbase28 -lwxpng -lwxzlib -lkernel32 -luser32 -lgdi32 -lwinspool -lcomdlg32 -ladvapi32 -lshell32 -lole32 -loleaut32 -luuid -lcomctl32 -lwsock32 -lodbc32 Library\GMP\lib\libgmpxx.a Library\GMP\lib\libgmp.a -mwindows +mingw32-g++.exe -L%widgets%\lib\gcc_lib -o FreeFileSync.exe obj\Release\Application.o obj\Release\FreeFileSync.o obj\Release\library\globalFunctions.o obj\Release\library\multithreading.cpp.o obj\Release\library\md5.o obj\Release\ui\GUI_Generated.o obj\Release\ui\MainDialog.o obj\Release\ui\SyncDialog.o obj\Release\library\CustomGrid.o obj\Release\ui\Resources.o obj\Release\ui\SmallDialogs.o obj\Release\resource.res -s -mthreads -lwxmsw28_adv -lwxmsw28_core -lwxbase28 -lwxpng -lwxzlib -lkernel32 -luser32 -lgdi32 -lwinspool -lcomdlg32 -ladvapi32 -lshell32 -lole32 -loleaut32 -luuid -lcomctl32 -lwsock32 -lodbc32 library\gmp\lib\libgmpxx.a library\gmp\lib\libgmp.a -mwindows pause
\ No newline at end of file diff --git a/Changelog.txt b/Changelog.txt new file mode 100644 index 00000000..ff9821aa --- /dev/null +++ b/Changelog.txt @@ -0,0 +1,63 @@ +FreeFileSync +------------ + +Changelog v1.4 +-------------- +Implemented generic multithreading class to keep "compare by content" and "file synchronisation" responsive +Added status bar when comparing files (with additional status information for "compare by content") +Some further speed optimizations +Added option to skip error messages and have them listed after synchronization +Restructured loading of configuration files +The result grid after synchronization now always consists of items that have not been synchronized (even if abort was pressed) +Added "remaining files" as sync-progress information +Updated German translation + +Changelog v1.3 +-------------- +Maintain and load different configurations by drag&drop, load-button or commandline +New function to delete files (or move them to recycle bin) manually on the UI (without having to re-compare): + Deleting folders results in deletion of all dependent files, subfolders on UI grid (also no re-compare needed) + while catching arrow situations and allowing to resolve them +Improved manual filtering of rows: If folders are marked all dependent subfolders and files are marked as well +(keeping sort sequence when "hide filtered elements" is marked) +Comprehensive performance optimization of the two features above (manual filtering, deletion) for large grids (> 200.000 rows) +Improved usability: resizable borders, keyboard shortcuts, default buttons, dialog standard focus +Main window will remember restored position even if maximized +Updated sources to become more Linux and Unicode friendly +Updated German translation + +Changelog v1.2 +-------------- +New progress indicator and status information when synchronizing: + ->available for commandline mode and UI mode: Status update and final error report +New progress information when comparing directories +Multithreading for copying of files to keep program responsive +Optimized all status dialogs and progress indicators for high performance: practically NO performance loss +Possibility to abort all performance critical operations (comparison, synchronization) at any time +New options in case of an error: "Continue, retry, abort" for UI and commandline +New commandline option "-skiperrors" to continue synchronization despite errors +Enhanced logfile (-silent mode) to include all errors during compare and synchronization +Do not synchronize folders that have been deleted externally (but show an error message) +Manually filter out ranges from synchronization instead of just single rows +Some UI improvements +New option to use Recycle Bin when deleting or overwriting files +New synchronization sequence: first delete files, then copy files to avoid disc space shortages +Added different return values when used in commandline mode to report success or failure +Updated German translation + +Changelog v1.1 +-------------- +Some further speed optimizations (sorting) +Written custom wxGrid class to avoid mapping of data to UI: huge performance increase (especially with formatted grids > 100000 items) +Filter files to include/exclude them from synchronization +Minor UI and text adaptions +Allow direct keyboard input for directory names +Updated German translation +Added possibility to continue on error +Added indicator for sort direction +Simplified code concerning loading of UI resources +Prepared code to support unicode in some future version + +Changelog v1.0 +-------------- +Initial release
\ No newline at end of file diff --git a/FreeFileSync.cbp b/FreeFileSync.cbp index 780e4977..c8d9be57 100644 --- a/FreeFileSync.cbp +++ b/FreeFileSync.cbp @@ -100,6 +100,8 @@ <Option compilerVar="CC" /> </Unit> <Unit filename="library\md5.h" /> + <Unit filename="library\multithreading.cpp" /> + <Unit filename="library\multithreading.h" /> <Unit filename="library\wxWidgets.h" /> <Unit filename="resource.rc"> <Option compilerVar="WINDRES" /> diff --git a/FreeFileSync.cpp b/FreeFileSync.cpp index 01031f9b..09e8efb8 100644 --- a/FreeFileSync.cpp +++ b/FreeFileSync.cpp @@ -8,8 +8,9 @@ #include "library/gmp/include\gmp.h" #include <wx/filename.h> #include <fstream> +#include "ui\resources.h" -using namespace GlobalFunctions; +using namespace globalFunctions; const wxString FreeFileSync::FFS_ConfigFileID = "FFS_CONFIG"; const wxString FreeFileSync::FFS_LastConfigFile = "LastRun.FFS"; @@ -39,14 +40,6 @@ wxString formatTime(unsigned int number) } -bool filetimeCmpSmallerThan(const FILETIME a, const FILETIME b) -{ - if (a.dwHighDateTime != b.dwHighDateTime) - return (a.dwHighDateTime < b.dwHighDateTime); - else - return (a.dwLowDateTime < b.dwLowDateTime); -} - bool filetimeCmpEqual(const FILETIME a,const FILETIME b) { if (a.dwHighDateTime == b.dwHighDateTime && a.dwLowDateTime == b.dwLowDateTime) @@ -57,6 +50,28 @@ bool filetimeCmpEqual(const FILETIME a,const FILETIME b) void FreeFileSync::getFileInformation(FileInfo& output, const wxString& filename) { +// +// wxFileName file(filename); +// +// wxDateTime lastWriteTime; +// wxULongLong filesize; +// +// +// +// if (file.FileExists()) +// { +// if (!file.GetTimes(NULL, &lastWriteTime, NULL) || ((filesize = file.GetSize()) == wxInvalidSize)) +// throw FileError(wxString(_("Could not retrieve file info for: ")) + "\"" + filename + "\""); +// output.lastWriteTime = lastWriteTime.FormatISODate(); +// +// output.lastWriteTimeUTC.dwHighDateTime = 0; +// output.lastWriteTimeUTC.dwLowDateTime = 0; +// } +// +// output.fileSize = filesize.ToString(); +// + + WIN32_FIND_DATA winFileInfo; FILETIME localFileTime; SYSTEMTIME time; @@ -67,26 +82,6 @@ void FreeFileSync::getFileInformation(FileInfo& output, const wxString& filename FindClose(fileHandle); - /* if (FileTimeToLocalFileTime( - &winFileInfo.ftCreationTime, // pointer to UTC file time to convert - &localFileTime // pointer to converted file time - ) == 0) - throw std::runtime_error("Error converting FILETIME to local FILETIME"); - - if (FileTimeToSystemTime( - &localFileTime, // pointer to file time to convert - &time // pointer to structure to receive system time - ) == 0) - throw std::runtime_error("Error converting FILETIME to SYSTEMTIME"); - - output.creationTime = numberToString(time.wYear) + "." + - formatTime(time.wMonth) + "." + - formatTime(time.wDay) + " " + - formatTime(time.wHour) + ":" + - formatTime(time.wMinute) + ":" + - formatTime(time.wSecond);*/ - -//***************************************************************************** if (FileTimeToLocalFileTime( &winFileInfo.ftLastWriteTime, // pointer to UTC file time to convert &localFileTime // pointer to converted file time @@ -108,21 +103,15 @@ void FreeFileSync::getFileInformation(FileInfo& output, const wxString& filename formatTime(time.wSecond); //UTC times - output.lastWriteTimeUTC = winFileInfo.ftLastWriteTime; - - mpz_t largeInt; - mpz_init_set_ui(largeInt, winFileInfo.nFileSizeHigh); - mpz_mul_ui(largeInt, largeInt, 65536); - mpz_mul_ui(largeInt, largeInt, 65536); - mpz_add_ui(largeInt, largeInt, winFileInfo.nFileSizeLow); - output.fileSize = mpz_get_str(0, 10, largeInt); - mpz_clear(largeInt); + output.lastWriteTimeUTC = wxULongLong(winFileInfo.ftLastWriteTime.dwHighDateTime, winFileInfo.ftLastWriteTime.dwLowDateTime); + + output.fileSize = wxULongLong(winFileInfo.nFileSizeHigh, winFileInfo.nFileSizeLow); } -wxString FreeFileSync::calculateMD5Hash(const wxString& filename) +string FreeFileSync::calculateMD5Hash(const wxString& filename) { - const unsigned int bufferSize = 4096; + const unsigned int bufferSize = 8192; char md5_output[33]; unsigned char signature[16]; @@ -163,31 +152,102 @@ void FreeFileSync::generateFileAndFolderDescriptions(DirectoryDescrType& output, } -void FreeFileSync::getModelDiff(FileCompareResult& output, const wxString& dirLeft, const wxString& dirRight, CompareVariant cmpVar, StatusUpdater* updateClass) +//handle execution of a method while updating the UI +class UpdateWhileCalculatingMD5 : public UpdateWhileExecuting { - assert (updateClass); +public: + UpdateWhileCalculatingMD5() {} + ~UpdateWhileCalculatingMD5() {} + + wxString file; + bool success; + wxString errorMessage; + string result; + +private: + void longRunner() //virtual method implementation + { + try + { + result = FreeFileSync::calculateMD5Hash(file); + success = true; + } + catch (FileError& error) + { + success = false; + errorMessage = error.show(); + } + } +}; + + +string FreeFileSync::calculateMD5HashMultithreaded(const wxString& filename, StatusUpdater* updateClass) +{ + static UpdateWhileCalculatingMD5 calcAndUpdate; //single instantiation: after each execution thread enters wait phase + + calcAndUpdate.waitUntilReady(); + + //longRunner is called from thread, but no mutex needed here, since thread is in waiting state! + calcAndUpdate.file = filename; + + calcAndUpdate.execAndUpdate(updateClass); + + //no mutex needed here since longRunner is finished + if (!calcAndUpdate.success) + throw FileError(calcAndUpdate.errorMessage); + + return calcAndUpdate.result; +} + + +void calcTotalDataForMD5(int& objectsTotal, double& dataTotal, const FileCompareResult& grid, const set<int>& rowsForMD5) +{ + dataTotal = 0; + + for (set<int>::iterator i = rowsForMD5.begin(); i != rowsForMD5.end(); ++i) + { + const FileCompareLine& gridline = grid[*i]; + + dataTotal+= gridline.fileDescrLeft.fileSize.ToDouble(); + dataTotal+= gridline.fileDescrRight.fileSize.ToDouble(); + } + + objectsTotal = rowsForMD5.size() * 2; +} + + +void FreeFileSync::startCompareProcess(FileCompareResult& output, const wxString& dirLeft, const wxString& dirRight, CompareVariant cmpVar, StatusUpdater* statusUpdater) +{ + assert (statusUpdater); + +//################################################################################################################################################ + + //inform about the total amount of data that will be processed from now on + statusUpdater->initNewProcess(-1, 0, FreeFileSync::scanningFilesProcess); //it's not known how many files will be scanned => -1 objects + + FileCompareResult output_tmp; //write to output not before END of process! try { //retrieve sets of files (with description data) DirectoryDescrType directoryLeft; DirectoryDescrType directoryRight; - generateFileAndFolderDescriptions(directoryLeft, dirLeft, updateClass); - generateFileAndFolderDescriptions(directoryRight, dirRight, updateClass); + generateFileAndFolderDescriptions(directoryLeft, dirLeft, statusUpdater); + generateFileAndFolderDescriptions(directoryRight, dirRight, statusUpdater); - FileCompareLine gridLine; + FileCompareLine newline; - output.clear(); + set<int> delayedMD5calculation; //md5 calculation happens AFTER compare in order to separate into two processe (needed by progress indicators) //find files/folders that exist in left file model but not in right model for (DirectoryDescrType::iterator i = directoryLeft.begin(); i != directoryLeft.end(); i++) if (directoryRight.find(*i) == directoryRight.end()) { - gridLine.fileDescrLeft = *i; - gridLine.fileDescrRight = FileDescrLine(); - gridLine.fileDescrRight.directory = dirRight; - gridLine.cmpResult = FileOnLeftSideOnly; - output.push_back(gridLine); + newline.fileDescrLeft = *i; + newline.fileDescrRight = FileDescrLine(); + newline.fileDescrRight.directory = dirRight; + newline.cmpResult = fileOnLeftSideOnly; + output_tmp.push_back(newline); } for (DirectoryDescrType::iterator j = directoryRight.begin(); j != directoryRight.end(); j++) @@ -197,109 +257,167 @@ void FreeFileSync::getModelDiff(FileCompareResult& output, const wxString& dirLe //find files/folders that exist in right file model but not in left model if ((i = directoryLeft.find(*j)) == directoryLeft.end()) { - gridLine.fileDescrLeft = FileDescrLine(); - gridLine.fileDescrLeft.directory = dirLeft; - gridLine.fileDescrRight = *j; - gridLine.cmpResult = FileOnRightSideOnly; - output.push_back(gridLine); + newline.fileDescrLeft = FileDescrLine(); + newline.fileDescrLeft.directory = dirLeft; + newline.fileDescrRight = *j; + newline.cmpResult = fileOnRightSideOnly; + output_tmp.push_back(newline); } //find files that exist in left and right file model else - { //objType != isNothing - if (i->objType == IsDirectory && j->objType == IsDirectory) + { //objType != isNothing + if (i->objType == isDirectory && j->objType == isDirectory) { - gridLine.fileDescrLeft = *i; - gridLine.fileDescrRight = *j; - gridLine.cmpResult = FilesEqual; - output.push_back(gridLine); + newline.fileDescrLeft = *i; + newline.fileDescrRight = *j; + newline.cmpResult = filesEqual; + output_tmp.push_back(newline); } //if we have a nameclash between a file and a directory: split into separate rows else if (i->objType != j->objType) { - gridLine.fileDescrLeft = *i; - gridLine.fileDescrRight = FileDescrLine(); - gridLine.fileDescrRight.directory = dirRight; - gridLine.cmpResult = FileOnLeftSideOnly; - output.push_back(gridLine); - - gridLine.fileDescrLeft = FileDescrLine(); - gridLine.fileDescrLeft.directory = dirLeft; - gridLine.fileDescrRight = *j; - gridLine.cmpResult = FileOnRightSideOnly; - output.push_back(gridLine); + newline.fileDescrLeft = *i; + newline.fileDescrRight = FileDescrLine(); + newline.fileDescrRight.directory = dirRight; + newline.cmpResult = fileOnLeftSideOnly; + output_tmp.push_back(newline); + + newline.fileDescrLeft = FileDescrLine(); + newline.fileDescrLeft.directory = dirLeft; + newline.fileDescrRight = *j; + newline.cmpResult = fileOnRightSideOnly; + output_tmp.push_back(newline); } - else if (cmpVar == CompareByTimeAndSize) - { - //check files that exist in left and right model but have different properties - if (!filetimeCmpEqual(i->lastWriteTimeUTC, j->lastWriteTimeUTC) || + else if (cmpVar == compareByTimeAndSize) + { //check files that exist in left and right model but have different properties + if (i->lastWriteTimeUTC != j->lastWriteTimeUTC || i->fileSize != j->fileSize) { - gridLine.fileDescrLeft = *i; - gridLine.fileDescrRight = *j; + newline.fileDescrLeft = *i; + newline.fileDescrRight = *j; - if (filetimeCmpEqual(i->lastWriteTimeUTC, j->lastWriteTimeUTC)) - gridLine.cmpResult = FilesDifferent; - else if (filetimeCmpSmallerThan(i->lastWriteTimeUTC, j->lastWriteTimeUTC)) - gridLine.cmpResult = RightFileNewer; + if (i->lastWriteTimeUTC == j->lastWriteTimeUTC) + newline.cmpResult = filesDifferent; + else if (i->lastWriteTimeUTC < j->lastWriteTimeUTC) + newline.cmpResult = rightFileNewer; else - gridLine.cmpResult = LeftFileNewer; - output.push_back(gridLine); + newline.cmpResult = leftFileNewer; + output_tmp.push_back(newline); } else { - gridLine.fileDescrLeft = *i; - gridLine.fileDescrRight = *j; - gridLine.cmpResult = FilesEqual; - output.push_back(gridLine); + newline.fileDescrLeft = *i; + newline.fileDescrRight = *j; + newline.cmpResult = filesEqual; + output_tmp.push_back(newline); } } - else if (cmpVar == CompareByMD5) + else if (cmpVar == compareByMD5) + { //check files that exist in left and right model but have different content + + //check filesize first! + if (i->fileSize == j->fileSize) + { + newline.fileDescrLeft = *i; + newline.fileDescrRight = *j; + //newline.cmpResult = ...; //not yet determined + output_tmp.push_back(newline); + + //md5 needed only if filesizes are the same + delayedMD5calculation.insert(output_tmp.size() - 1); //save index of row, to calculate cmpResult later + } + else + { + newline.fileDescrLeft = *i; + newline.fileDescrRight = *j; + newline.cmpResult = filesDifferent; + output_tmp.push_back(newline); + } + } + else assert (false); + } + } + +//################################################################################################################################################ + //calculate MD5 checksums and set value "cmpResult" + if (cmpVar == compareByMD5) + { + int objectsTotal = 0; + double dataTotal = 0; + calcTotalDataForMD5(objectsTotal, dataTotal, output_tmp, delayedMD5calculation); + + statusUpdater->initNewProcess(objectsTotal, dataTotal, FreeFileSync::calcMD5Process); + + set<int> rowsToDelete; //if errors occur during file access and user skips, these rows need to be deleted from result + + for (set<int>::iterator i = delayedMD5calculation.begin(); i != delayedMD5calculation.end(); ++i) + { + FileCompareLine& gridline = output_tmp[*i]; + + //check files that exist in left and right model but have different checksums + string leftFileHash; + string rightFileHash; + + while (true) { - while (true) + //trigger display refresh + statusUpdater->triggerUI_Refresh(); + + try { - try + if (leftFileHash.empty()) { - //check files that exist in left and right model but have different checksums - if (j->fileSize != i->fileSize || calculateMD5Hash(i->filename) != calculateMD5Hash(j->filename)) - { - gridLine.fileDescrLeft = *i; - gridLine.fileDescrRight = *j; + statusUpdater->updateStatusText(wxString(_("Reading content of ") + gridline.fileDescrLeft.filename)); + leftFileHash = calculateMD5HashMultithreaded(gridline.fileDescrLeft.filename, statusUpdater); + statusUpdater->updateProcessedData(1, gridline.fileDescrLeft.fileSize.ToDouble()); + } - gridLine.cmpResult = FilesDifferent; - output.push_back(gridLine); - } - else - { - gridLine.fileDescrLeft = *i; - gridLine.fileDescrRight = *j; - gridLine.cmpResult = FilesEqual; - output.push_back(gridLine); - } - break; + if (rightFileHash.empty()) + { + statusUpdater->updateStatusText(wxString(_("Reading content of ") + gridline.fileDescrRight.filename)); + rightFileHash = calculateMD5HashMultithreaded(gridline.fileDescrRight.filename, statusUpdater); + statusUpdater->updateProcessedData(1, gridline.fileDescrRight.fileSize.ToDouble()); } - catch (FileError& error) + + if (leftFileHash == rightFileHash) + gridline.cmpResult = filesEqual; + else + gridline.cmpResult = filesDifferent; + + break; + } + catch (FileError& error) + { + //if (updateClass) -> is mandatory + int rv = statusUpdater->reportError(error.show()); + if ( rv == StatusUpdater::continueNext) { - //if (updateClass) -> is mandatory - int rv = updateClass->reportError(error.show()); - if ( rv == StatusUpdater::Continue) - break; - else if (rv == StatusUpdater::Retry) - ; //continue with loop - else - assert (false); + rowsToDelete.insert(*i); + break; } + else if (rv == StatusUpdater::retry) + ; //continue with loop + else + assert (false); } } - else assert (false); } + + //delete invalid rows that have no valid cmpResult + if (rowsToDelete.size() > 0) + removeRowsFromVector(output_tmp, rowsToDelete); } - updateClass->triggerUI_Refresh(); + + statusUpdater->triggerUI_Refresh(); } catch (std::runtime_error& theException) { wxMessageBox(_(theException.what()), _("An exception occured!"), wxOK | wxICON_ERROR); return; } + + //only if everything was processed correctly output is written to! + output_tmp.swap(output); } @@ -310,14 +428,14 @@ void FreeFileSync::swapGrids(FileCompareResult& grid) for (FileCompareResult::iterator i = grid.begin(); i != grid.end(); ++i) { //swap compare result - if (i->cmpResult == FileOnLeftSideOnly) - i->cmpResult = FileOnRightSideOnly; - else if (i->cmpResult == FileOnRightSideOnly) - i->cmpResult = FileOnLeftSideOnly; - else if (i->cmpResult == RightFileNewer) - i->cmpResult = LeftFileNewer; - else if (i->cmpResult == LeftFileNewer) - i->cmpResult = RightFileNewer; + if (i->cmpResult == fileOnLeftSideOnly) + i->cmpResult = fileOnRightSideOnly; + else if (i->cmpResult == fileOnRightSideOnly) + i->cmpResult = fileOnLeftSideOnly; + else if (i->cmpResult == rightFileNewer) + i->cmpResult = leftFileNewer; + else if (i->cmpResult == leftFileNewer) + i->cmpResult = rightFileNewer; //swap file descriptors tmp = i->fileDescrLeft; @@ -351,9 +469,9 @@ wxDirTraverseResult GetAllFilesFull::OnFile(const wxString& filename) { //if (updateClass) -> is mandatory int rv = statusUpdater->reportError(error.show()); - if ( rv == StatusUpdater::Continue) + if ( rv == StatusUpdater::continueNext) return wxDIR_CONTINUE; - else if (rv == StatusUpdater::Retry) + else if (rv == StatusUpdater::retry) ; //continue with loop else assert (false); @@ -363,13 +481,13 @@ wxDirTraverseResult GetAllFilesFull::OnFile(const wxString& filename) fileDescr.lastWriteTime = currentFileInfo.lastWriteTime; fileDescr.lastWriteTimeUTC = currentFileInfo.lastWriteTimeUTC; fileDescr.fileSize = currentFileInfo.fileSize; - fileDescr.objType = IsFile; + fileDescr.objType = isFile; m_output.insert(fileDescr); //update UI/commandline status information - statusUpdater->updateStatus(filename); // NO performance issue at all + statusUpdater->updateStatusText(wxString(_("Scanning ")) + filename); // NO performance issue at all //add 1 element to the progress indicator - statusUpdater->updateProgressIndicator(1); // NO performance issue at all + statusUpdater->updateProcessedData(1, 0); // NO performance issue at all //trigger display refresh statusUpdater->triggerUI_Refresh(); @@ -398,9 +516,9 @@ wxDirTraverseResult GetAllFilesFull::OnDir(const wxString& dirname) { //if (updateClass) -> is mandatory int rv = statusUpdater->reportError(error.show()); - if ( rv == StatusUpdater::Continue) + if ( rv == StatusUpdater::continueNext) return wxDIR_IGNORE; - else if (rv == StatusUpdater::Retry) + else if (rv == StatusUpdater::retry) ; //continue with loop else assert (false); @@ -408,14 +526,14 @@ wxDirTraverseResult GetAllFilesFull::OnDir(const wxString& dirname) } fileDescr.lastWriteTime = currentFileInfo.lastWriteTime; fileDescr.lastWriteTimeUTC = currentFileInfo.lastWriteTimeUTC; - fileDescr.fileSize = "0"; //currentFileInfo.fileSize should be "0" as well, but just to be sure... <- isn't needed anyway... - fileDescr.objType = IsDirectory; + fileDescr.fileSize = wxULongLong(0); //currentFileInfo.fileSize should be "0" as well, but just to be sure... currently used by getBytesToTransfer + fileDescr.objType = isDirectory; m_output.insert(fileDescr); //update UI/commandline status information - statusUpdater->updateStatus(dirname); // NO performance issue at all + statusUpdater->updateStatusText(wxString(_("Scanning ")) + dirname); // NO performance issue at all //add 1 element to the progress indicator - statusUpdater->updateProgressIndicator(1); // NO performance issue at all + statusUpdater->updateProcessedData(1, 0); // NO performance issue at all //trigger display refresh statusUpdater->triggerUI_Refresh(); @@ -497,11 +615,12 @@ void FreeFileSync::removeFile(const wxString& filename) return; } +#ifdef FFS_WIN if (!SetFileAttributes( filename.c_str(), // address of filename FILE_ATTRIBUTE_NORMAL // address of attributes to set )) throw FileError(wxString(_("Error deleting file ")) + "\"" + filename + "\""); - +#endif // FFS_WIN if (!wxRemoveFile(filename)) throw FileError(wxString(_("Error deleting file ")) + "\"" + filename + "\""); @@ -575,7 +694,7 @@ void FreeFileSync::createDirectory(const wxString& directory, int level) return; //if not successfull try to create containing folders first - wxString createFirstDir = wxDir(directory).GetName().BeforeLast(FileNameSeparator); + wxString createFirstDir = wxDir(directory).GetName().BeforeLast(GlobalResources::fileNameSeparator); //call function recursively if (createFirstDir.IsEmpty()) return; @@ -601,83 +720,48 @@ void FreeFileSync::copyCreatingDirs(const wxString& source, const wxString& targ //########################################################################################### -class CopyThread : public wxThread + +//handle execution of a method while updating the UI +class UpdateWhileCopying : public UpdateWhileExecuting { public: - CopyThread(const wxString& sourceFile, const wxString& targetFile) : - source(sourceFile), - target(targetFile) - {} - - ~CopyThread() {} - - ExitCode Entry() + UpdateWhileCopying() + { //prevent wxWidgets logging + noWxLogs = new wxLogNull; + } + ~UpdateWhileCopying() { - bool success = (CopyFile( - source.c_str(), // pointer to name of an existing file - target.c_str(), // pointer to filename to copy to - TRUE // break if file exists - )); - - copyFileCritSec.Enter(); - - //report status to main thread - threadIsFinished = true; - threadWasSuccessful = success; - - copyFileCritSec.Leave(); - - return 0; + delete noWxLogs; } - - static wxCriticalSection copyFileCritSec; - - //shared thread data -> protect with critical section! - static bool threadIsFinished; - static bool threadWasSuccessful; - // + wxString source; + wxString target; + bool success; private: - const wxString& source; - const wxString& target; -}; - -wxCriticalSection CopyThread::copyFileCritSec; -bool CopyThread::threadIsFinished = true; -bool CopyThread::threadWasSuccessful = true; + void longRunner() //virtual method implementation + { + success = wxCopyFile(source, target, false); //abort if file exists + } + wxLogNull* noWxLogs; +}; void FreeFileSync::copyfileMultithreaded(const wxString& source, const wxString& target, StatusUpdater* updateClass) { - //at this point threadIsRunning is not shared between threads - CopyThread::threadIsFinished = false; - - //create thread for copying of (large) files - CopyThread* copyFileThread = new CopyThread(source, target); //quite faster than joinable threads + static UpdateWhileCopying copyAndUpdate; //single instantiation: after each execution thread enters wait phase - copyFileThread->Create(); - copyFileThread->SetPriority(80); - copyFileThread->Run(); + copyAndUpdate.waitUntilReady(); - bool processCompleted; - while (true) - { - CopyThread::copyFileCritSec.Enter(); - processCompleted = CopyThread::threadIsFinished; //always put shared data into mutextes/critical sections - CopyThread::copyFileCritSec.Leave(); + //longRunner is called from thread, but no mutex needed here, since thread is in waiting state! + copyAndUpdate.source = source; + copyAndUpdate.target = target; - if (processCompleted) //if completed the data is not shared anymore - { - if (!CopyThread::threadWasSuccessful) - { - throw FileError(wxString(_("Error copying file ")) + "\"" + source + "\"" + _(" to ") + "\"" + target + "\""); - } - else return; - } + copyAndUpdate.execAndUpdate(updateClass); - updateClass->triggerUI_Refresh(); - } + //no mutex needed here since longRunner is finished + if (!copyAndUpdate.success) + throw FileError(wxString(_("Error copying file ")) + "\"" + source + "\"" + _(" to ") + "\"" + target + "\""); } @@ -700,7 +784,7 @@ FreeFileSync::FreeFileSync() recycleBinAvailable = true; } - //prevent logging of wxWidgets + //prevent wxWidgets logging noWxLogs = new wxLogNull; } @@ -736,106 +820,99 @@ SyncDirection getSyncDirection(const CompareFilesResult cmpResult, const SyncCon { switch (cmpResult) { - case FileOnLeftSideOnly: + case fileOnLeftSideOnly: return config.exLeftSideOnly; break; - case FileOnRightSideOnly: + case fileOnRightSideOnly: return config.exRightSideOnly; break; - case RightFileNewer: + case rightFileNewer: return config.rightNewer; break; - case LeftFileNewer: + case leftFileNewer: return config.leftNewer; break; - case FilesDifferent: + case filesDifferent: return config.different; break; default: assert (false); } - return SyncDirNone; + return syncDirNone; } -void FreeFileSync::getBytesToTransfer(mpz_t& result, const FileCompareLine& fileCmpLine, const SyncConfiguration& config) +void FreeFileSync::wxULongLongToMpz(mpz_t& output, const wxULongLong& input) { - mpz_set_ui(result, 0); //always initialize variables + mpz_set_ui(output, input.GetHi()); + mpz_mul_ui(output, output, 65536); + mpz_mul_ui(output, output, 65536); + mpz_add_ui(output, output, input.GetLo()); +} + + +bool getBytesToTransfer(double& result, const FileCompareLine& fileCmpLine, const SyncConfiguration& config) +{ //false if nothing has to be done + + result = 0; //always initialize variables //do not add filtered entries if (!fileCmpLine.selectedForSynchronization) - return; - - int returnValue = 0; + return false; switch (fileCmpLine.cmpResult) { - case FileOnLeftSideOnly: - if (config.exLeftSideOnly == SyncDirRight) - //copy files to right - returnValue = mpz_set_str(result, fileCmpLine.fileDescrLeft.fileSize.c_str(), 10); - break; - - case FileOnRightSideOnly: - if (config.exRightSideOnly == SyncDirLeft) - //copy files to left - returnValue = mpz_set_str(result, fileCmpLine.fileDescrRight.fileSize.c_str(), 10); - break; - - case LeftFileNewer: - case RightFileNewer: - case FilesDifferent: + case fileOnLeftSideOnly: + case fileOnRightSideOnly: + case leftFileNewer: + case rightFileNewer: + case filesDifferent: switch (getSyncDirection(fileCmpLine.cmpResult, config)) { - case SyncDirLeft: //copy from right to left - returnValue = mpz_set_str(result, fileCmpLine.fileDescrRight.fileSize.c_str(), 10); - break; - case SyncDirRight: //copy from left to right - returnValue = mpz_set_str(result, fileCmpLine.fileDescrLeft.fileSize.c_str(), 10); - break; - case SyncDirNone: - break; + case syncDirLeft: //copy from right to left + result = fileCmpLine.fileDescrRight.fileSize.ToDouble(); + return true; + case syncDirRight: //copy from left to right + result = fileCmpLine.fileDescrLeft.fileSize.ToDouble(); + return true; + case syncDirNone: + return false; } break; - case FilesEqual: - break; + case filesEqual: + return false; }; - assert (returnValue == 0); + + return true; } -mpz_class FreeFileSync::calcTotalBytesToTransfer(const FileCompareResult& fileCmpResult, const SyncConfiguration& config) +void FreeFileSync::calcTotalBytesToSync(int& objectsTotal, double& dataTotal, const FileCompareResult& fileCmpResult, const SyncConfiguration& config) { - mpz_t largeTmpInt, result_c; - mpz_init(largeTmpInt); - mpz_init(result_c); + objectsTotal = 0; + dataTotal = 0; + + double tmp = 0; for (FileCompareResult::const_iterator i = fileCmpResult.begin(); i != fileCmpResult.end(); ++i) - { //only sum up sizes of files, not directories - if (i->fileDescrLeft.objType == IsFile || i->fileDescrRight.objType == IsFile) + { //only sum up sizes of files AND directories + if (getBytesToTransfer(tmp, *i, config)) { - getBytesToTransfer(largeTmpInt, *i, config); - mpz_add(result_c, result_c, largeTmpInt); //much faster than converting this to mpz_class for each iteration + dataTotal+= tmp; + objectsTotal++; } } - - mpz_class result(result_c); - - mpz_clear(largeTmpInt); - mpz_clear(result_c); - - return result; } bool FreeFileSync::synchronizeFile(const FileCompareLine& filename, const SyncConfiguration& config, StatusUpdater* statusUpdater) -{ +{ //false if nothing was to be done assert (statusUpdater); if (!filename.selectedForSynchronization) return false; @@ -845,80 +922,76 @@ bool FreeFileSync::synchronizeFile(const FileCompareLine& filename, const SyncCo //synchronize file: switch (filename.cmpResult) { - case FileOnLeftSideOnly: + case fileOnLeftSideOnly: switch (config.exLeftSideOnly) { - case SyncDirLeft: //delete files on left - statusUpdater->updateStatus(wxString(_("Deleting file ") + filename.fileDescrLeft.filename)); + case syncDirLeft: //delete files on left + statusUpdater->updateStatusText(wxString(_("Deleting file ") + filename.fileDescrLeft.filename)); removeFile(filename.fileDescrLeft.filename); break; - case SyncDirRight: //copy files to right + case syncDirRight: //copy files to right target = filename.fileDescrRight.directory + filename.fileDescrLeft.relFilename.c_str(); - statusUpdater->updateStatus(wxString(_("Copying file ")) + filename.fileDescrLeft.filename + - _(" to ") + target); + statusUpdater->updateStatusText(wxString(_("Copying file ")) + filename.fileDescrLeft.filename + + _(" to ") + target); copyfileMultithreaded(filename.fileDescrLeft.filename, target, statusUpdater); break; - case SyncDirNone: + case syncDirNone: return false; - break; default: assert (false); } break; - case FileOnRightSideOnly: + case fileOnRightSideOnly: switch (config.exRightSideOnly) { - case SyncDirLeft: //copy files to left + case syncDirLeft: //copy files to left target = filename.fileDescrLeft.directory + filename.fileDescrRight.relFilename; - statusUpdater->updateStatus(wxString(_("Copying file ")) + filename.fileDescrRight.filename + - _(" to ") + target); + statusUpdater->updateStatusText(wxString(_("Copying file ")) + filename.fileDescrRight.filename + + _(" to ") + target); copyfileMultithreaded(filename.fileDescrRight.filename, target, statusUpdater); break; - case SyncDirRight: //delete files on right - statusUpdater->updateStatus(wxString(_("Deleting file ") + filename.fileDescrRight.filename)); + case syncDirRight: //delete files on right + statusUpdater->updateStatusText(wxString(_("Deleting file ") + filename.fileDescrRight.filename)); removeFile(filename.fileDescrRight.filename); break; - case SyncDirNone: + case syncDirNone: return false; - break; default: assert (false); } break; - case LeftFileNewer: - case RightFileNewer: - case FilesDifferent: + case leftFileNewer: + case rightFileNewer: + case filesDifferent: switch (getSyncDirection(filename.cmpResult, config)) { - case SyncDirLeft: //copy from right to left - statusUpdater->updateStatus(wxString(_("Copying file ")) + filename.fileDescrRight.filename + - _(" overwriting ") + filename.fileDescrLeft.filename); + case syncDirLeft: //copy from right to left + statusUpdater->updateStatusText(wxString(_("Copying file ")) + filename.fileDescrRight.filename + + _(" overwriting ") + filename.fileDescrLeft.filename); removeFile(filename.fileDescrLeft.filename); //only used if switch activated by user, else file is simply deleted copyfileMultithreaded(filename.fileDescrRight.filename, filename.fileDescrLeft.filename, statusUpdater); break; - case SyncDirRight: //copy from left to right - statusUpdater->updateStatus(wxString(_("Copying file ")) + filename.fileDescrLeft.filename + - _(" overwriting ") + filename.fileDescrRight.filename); + case syncDirRight: //copy from left to right + statusUpdater->updateStatusText(wxString(_("Copying file ")) + filename.fileDescrLeft.filename + + _(" overwriting ") + filename.fileDescrRight.filename); removeFile(filename.fileDescrRight.filename); //only used if switch activated by user, else file is simply deleted copyfileMultithreaded(filename.fileDescrLeft.filename, filename.fileDescrRight.filename, statusUpdater); break; - case SyncDirNone: + case syncDirNone: return false; - break; default: assert (false); } break; - case FilesEqual: + case filesEqual: return false; - break; default: assert (false); @@ -928,7 +1001,7 @@ bool FreeFileSync::synchronizeFile(const FileCompareLine& filename, const SyncCo bool FreeFileSync::synchronizeFolder(const FileCompareLine& filename, const SyncConfiguration& config, StatusUpdater* statusUpdater) -{ +{ //false if nothing was to be done assert (statusUpdater); if (!filename.selectedForSynchronization) return false; @@ -938,60 +1011,57 @@ bool FreeFileSync::synchronizeFolder(const FileCompareLine& filename, const Sync //synchronize folders: switch (filename.cmpResult) { - case FileOnLeftSideOnly: + case fileOnLeftSideOnly: switch (config.exLeftSideOnly) { - case SyncDirLeft: //delete folders on left - statusUpdater->updateStatus(wxString(_("Deleting folder ") + filename.fileDescrLeft.filename)); + case syncDirLeft: //delete folders on left + statusUpdater->updateStatusText(wxString(_("Deleting folder ") + filename.fileDescrLeft.filename)); removeDirectory(filename.fileDescrLeft.filename); break; - case SyncDirRight: //create folders on right + case syncDirRight: //create folders on right target = filename.fileDescrRight.directory + filename.fileDescrLeft.relFilename; - statusUpdater->updateStatus(wxString(_("Creating folder ") + target)); + statusUpdater->updateStatusText(wxString(_("Creating folder ") + target)); //some check to catch the error that directory on source has been deleted externally after the "compare"... if (!wxDirExists(filename.fileDescrLeft.filename)) throw FileError(wxString(_("Error: Source directory does not exist anymore: ")) + "\"" + filename.fileDescrLeft.filename + "\""); createDirectory(target); break; - case SyncDirNone: + case syncDirNone: return false; - break; default: assert (false); } break; - case FileOnRightSideOnly: + case fileOnRightSideOnly: switch (config.exRightSideOnly) { - case SyncDirLeft: //create folders on left + case syncDirLeft: //create folders on left target = filename.fileDescrLeft.directory + filename.fileDescrRight.relFilename; - statusUpdater->updateStatus(wxString(_("Creating folder ") + target)); + statusUpdater->updateStatusText(wxString(_("Creating folder ") + target)); //some check to catch the error that directory on source has been deleted externally after the "compare"... if (!wxDirExists(filename.fileDescrRight.filename)) throw FileError(wxString(_("Error: Source directory does not exist anymore: ")) + filename.fileDescrRight.filename); createDirectory(target); break; - case SyncDirRight: //delete folders on right - statusUpdater->updateStatus(wxString(_("Deleting folder ") + filename.fileDescrRight.filename)); + case syncDirRight: //delete folders on right + statusUpdater->updateStatusText(wxString(_("Deleting folder ") + filename.fileDescrRight.filename)); removeDirectory(filename.fileDescrRight.filename); break; - case SyncDirNone: + case syncDirNone: return false; - break; default: assert (false); } break; - case FilesEqual: + case filesEqual: return false; - break; - case RightFileNewer: - case LeftFileNewer: - case FilesDifferent: + case rightFileNewer: + case leftFileNewer: + case filesDifferent: default: assert (false); } @@ -1049,11 +1119,11 @@ wxString FreeFileSync::formatFilesizeToShortString(const mpz_class& filesize) switch (exponent) { case 0: - temp = wxString("0") + FloatingPointSeparator + temp.substr(0, 2); //shorten mantisse as a 0 will be inserted + temp = wxString("0") + GlobalResources::floatingPointSeparator + temp.substr(0, 2); //shorten mantisse as a 0 will be inserted break; case 1: case 2: - temp.insert(exponent, FloatingPointSeparator); + temp.insert(exponent, GlobalResources::floatingPointSeparator); break; case 3: break; @@ -1103,7 +1173,7 @@ void FreeFileSync::filterCurrentGridData(FileCompareResult& currentGridData, con for (FileCompareResult::iterator i = currentGridData.begin(); i != currentGridData.end(); i++) { //process include filters - if (i->fileDescrLeft.objType != IsNothing) + if (i->fileDescrLeft.objType != isNothing) { bool includedLeft = false; for (vector<wxString>::const_iterator j = includeList.begin(); j != includeList.end(); ++j) @@ -1120,7 +1190,7 @@ void FreeFileSync::filterCurrentGridData(FileCompareResult& currentGridData, con } } - if (i->fileDescrRight.objType != IsNothing) + if (i->fileDescrRight.objType != isNothing) { bool includedRight = false; for (vector<wxString>::const_iterator j = includeList.begin(); j != includeList.end(); ++j) @@ -1172,7 +1242,7 @@ wxString FreeFileSync::getFormattedDirectoryName(const wxString& dirname) //let wxWidgets do the directory formatting, e.g. replace '/' with '\' for Windows wxString result = wxDir(dirname).GetName(); - result.Append(FileNameSeparator); + result.Append(GlobalResources::fileNameSeparator); return result; } @@ -1180,25 +1250,55 @@ wxString FreeFileSync::getFormattedDirectoryName(const wxString& dirname) inline bool deletionImminent(const FileCompareLine& line, const SyncConfiguration& config) { //test if current sync-line will result in deletion of files -> used to avoid disc space bottlenecks - if ((line.cmpResult == FileOnLeftSideOnly && config.exLeftSideOnly == SyncDirLeft) || - (line.cmpResult == FileOnRightSideOnly && config.exRightSideOnly == SyncDirRight)) + if ((line.cmpResult == fileOnLeftSideOnly && config.exLeftSideOnly == syncDirLeft) || + (line.cmpResult == fileOnRightSideOnly && config.exRightSideOnly == syncDirRight)) return true; else return false; } +class AlwaysWriteResult //this class ensures, that the result of the method below is ALWAYS written on exit, even if exceptions were thrown! +{ +public: + AlwaysWriteResult(FileCompareResult& grid) : + gridToWrite(grid) + {} + + ~AlwaysWriteResult() + { + removeRowsFromVector(gridToWrite, rowsProcessed); + } + + void rowProcessedSuccessfully(int nr) + { + rowsProcessed.insert(nr); + } + +private: + FileCompareResult& gridToWrite; + set<int> rowsProcessed; +}; + + +//synchronizes while processing rows in grid and returns all rows that have not been synced void FreeFileSync::startSynchronizationProcess(FileCompareResult& grid, const SyncConfiguration& config, StatusUpdater* statusUpdater, bool useRecycleBin) { assert (statusUpdater); + AlwaysWriteResult writeOutput(grid); //ensure that grid is always written to, even if method is exitted via exceptions + + //inform about the total amount of data that will be processed from now on + int objectsTotal = 0; + double dataTotal = 0; + FreeFileSync::calcTotalBytesToSync(objectsTotal, dataTotal, grid, config); + statusUpdater->initNewProcess(objectsTotal, dataTotal, FreeFileSync::synchronizeFilesProcess); + try { FreeFileSync fileSyncObject; //currently only needed for recycle bin fileSyncObject.setRecycleBinUsage(useRecycleBin); - FileCompareResult resultsGrid; - // it should never happen, that a directory on left side has same name as file on right side. GetModelDiff should take care of this // and split into two "exists on one side only" cases // Note: this case is not handled by this tool as this is considered to be a bug and must be solved by the user @@ -1206,10 +1306,10 @@ void FreeFileSync::startSynchronizationProcess(FileCompareResult& grid, const Sy //synchronize folders: for (FileCompareResult::const_iterator i = grid.begin(); i != grid.end(); ++i) { - if (i->fileDescrLeft.objType == IsDirectory || i->fileDescrRight.objType == IsDirectory) + if (i->fileDescrLeft.objType == isDirectory || i->fileDescrRight.objType == isDirectory) { while (true) - { //trigger display refresh + { //trigger display refresh statusUpdater->triggerUI_Refresh(); try @@ -1217,7 +1317,9 @@ void FreeFileSync::startSynchronizationProcess(FileCompareResult& grid, const Sy if (fileSyncObject.synchronizeFolder(*i, config, statusUpdater)) //progress indicator update //indicator is updated only if directory is synched correctly (and if some sync was done)! - statusUpdater->updateProgressIndicator(0); //each call represents one processed file/directory + statusUpdater->updateProcessedData(1, 0); //each call represents one processed file/directory + + writeOutput.rowProcessedSuccessfully(i - grid.begin()); break; } catch (FileError& error) @@ -1225,12 +1327,9 @@ void FreeFileSync::startSynchronizationProcess(FileCompareResult& grid, const Sy //if (updateClass) -> is mandatory int rv = statusUpdater->reportError(error.show()); - if ( rv == StatusUpdater::Continue) - { - resultsGrid.push_back(*i); //append folders that have not been synced successfully + if ( rv == StatusUpdater::continueNext) break; - } - else if (rv == StatusUpdater::Retry) + else if (rv == StatusUpdater::retry) ; //continue with loop else assert (false); @@ -1246,7 +1345,7 @@ void FreeFileSync::startSynchronizationProcess(FileCompareResult& grid, const Sy for (FileCompareResult::const_iterator i = grid.begin(); i != grid.end(); ++i) { - if (i->fileDescrLeft.objType == IsFile || i->fileDescrRight.objType == IsFile) + if (i->fileDescrLeft.objType == isFile || i->fileDescrRight.objType == isFile) { if (deleteLoop && deletionImminent(*i, config) || !deleteLoop && !deletionImminent(*i, config)) @@ -1261,12 +1360,12 @@ void FreeFileSync::startSynchronizationProcess(FileCompareResult& grid, const Sy { //progress indicator update //indicator is updated only if file is synched correctly (and if some sync was done)! - mpz_t largeTmpInt; - mpz_init(largeTmpInt); - FreeFileSync::getBytesToTransfer(largeTmpInt, *i, config); - statusUpdater->updateProgressIndicator(mpz_get_d(largeTmpInt)); - mpz_clear(largeTmpInt); + double processedData = 0; + if (getBytesToTransfer(processedData, *i, config)) //update status if some work was done (answer is always "yes" in this context) + statusUpdater->updateProcessedData(1, processedData); } + + writeOutput.rowProcessedSuccessfully(i - grid.begin()); break; } catch (FileError& error) @@ -1274,12 +1373,9 @@ void FreeFileSync::startSynchronizationProcess(FileCompareResult& grid, const Sy //if (updateClass) -> is mandatory int rv = statusUpdater->reportError(error.show()); - if ( rv == StatusUpdater::Continue) - { - resultsGrid.push_back(*i); //append files that have not been synced successfully + if ( rv == StatusUpdater::continueNext) break; - } - else if (rv == StatusUpdater::Retry) + else if (rv == StatusUpdater::retry) ; //continue with loop else assert (false); @@ -1289,9 +1385,6 @@ void FreeFileSync::startSynchronizationProcess(FileCompareResult& grid, const Sy } } } - - //return files that couldn't be processed - grid = resultsGrid; } catch (std::runtime_error& theException) { @@ -1321,10 +1414,10 @@ void FreeFileSync::addSubElements(set<int>& subElements, const FileCompareResult { wxString relevantDirectory; - if (relevantRow.fileDescrLeft.objType == IsDirectory) + if (relevantRow.fileDescrLeft.objType == isDirectory) relevantDirectory = relevantRow.fileDescrLeft.relFilename; - else if (relevantRow.fileDescrRight.objType == IsDirectory) + else if (relevantRow.fileDescrRight.objType == isDirectory) relevantDirectory = relevantRow.fileDescrRight.relFilename; else @@ -1353,14 +1446,14 @@ void FreeFileSync::deleteOnGridAndHD(FileCompareResult& grid, const set<int>& ro { try { - if (currentCmpLine.fileDescrLeft.objType == IsFile) + if (currentCmpLine.fileDescrLeft.objType == isFile) fileSyncObject.removeFile(currentCmpLine.fileDescrLeft.filename); - else if (currentCmpLine.fileDescrLeft.objType == IsDirectory) + else if (currentCmpLine.fileDescrLeft.objType == isDirectory) fileSyncObject.removeDirectory(currentCmpLine.fileDescrLeft.filename); - if (currentCmpLine.fileDescrRight.objType == IsFile) + if (currentCmpLine.fileDescrRight.objType == isFile) fileSyncObject.removeFile(currentCmpLine.fileDescrRight.filename); - else if (currentCmpLine.fileDescrRight.objType == IsDirectory) + else if (currentCmpLine.fileDescrRight.objType == isDirectory) fileSyncObject.removeDirectory(currentCmpLine.fileDescrRight.filename); rowsToDeleteInGrid.insert(*i); @@ -1373,10 +1466,10 @@ void FreeFileSync::deleteOnGridAndHD(FileCompareResult& grid, const set<int>& ro //if (updateClass) -> is mandatory int rv = statusUpdater->reportError(error.show()); - if (rv == StatusUpdater::Continue) + if (rv == StatusUpdater::continueNext) break; - else if (rv == StatusUpdater::Retry) + else if (rv == StatusUpdater::retry) ; //continue with loop else assert (false); @@ -1394,34 +1487,6 @@ void FreeFileSync::deleteOnGridAndHD(FileCompareResult& grid, const set<int>& ro //remove deleted rows from grid - - //for (set<int>::reverse_iterator i = rowsToDeleteInGrid.rbegin(); i != rowsToDeleteInGrid.rend(); ++i) - // grid.erase(grid.begin() + *i); - - //Note: the following lines are a performance optimization for deleting elements from a vector. It is incredibly faster to create a new - //vector and leave specific elements out than to delete row by row and force recopying of most elements for each single deletion (linear vs quadratic runtime) - - FileCompareResult temp; - int rowNr = 0; - int rowToSkip = -1; - - set<int>::iterator rowToSkipIndex = rowsToDeleteInGrid.begin(); - - if (rowToSkipIndex != rowsToDeleteInGrid.end()) - rowToSkip = *rowToSkipIndex; - - for (FileCompareResult::iterator i = grid.begin(); i != grid.end(); ++i, ++rowNr) - { - if (rowNr != rowToSkip) - temp.push_back(*i); - else - { - rowToSkipIndex++; - if (rowToSkipIndex != rowsToDeleteInGrid.end()) - rowToSkip = *rowToSkipIndex; - } - } - grid.swap(temp); + removeRowsFromVector(grid, rowsToDeleteInGrid); } - diff --git a/FreeFileSync.h b/FreeFileSync.h index 9285ba7c..c0e4c367 100644 --- a/FreeFileSync.h +++ b/FreeFileSync.h @@ -8,14 +8,15 @@ #include <windows.h> #include "library/gmp/include/gmpxx.h" #include <wx/log.h> +#include "library/multithreading.h" using namespace std; enum SyncDirection { - SyncDirLeft, - SyncDirRight, - SyncDirNone + syncDirLeft, + syncDirRight, + syncDirNone }; struct SyncConfiguration @@ -27,30 +28,31 @@ struct SyncConfiguration SyncDirection different; }; -typedef struct + +struct FileInfo { - wxString fileSize; + wxULongLong fileSize; wxString lastWriteTime; - FILETIME lastWriteTimeUTC; -} FileInfo; + wxULongLong lastWriteTimeUTC; +}; enum ObjectType { - IsNothing, - IsDirectory, - IsFile + isNothing, + isDirectory, + isFile }; struct FileDescrLine { - FileDescrLine() : objType(IsNothing) {}; + FileDescrLine() : objType(isNothing) {}; wxString filename; // == directory + relFilename wxString directory; //directory to be synced wxString relFilename; //filename without directory that is being synchronized wxString lastWriteTime; - FILETIME lastWriteTimeUTC; - wxString fileSize; + wxULongLong lastWriteTimeUTC; + wxULongLong fileSize; ObjectType objType; //is it a file or directory or initial? //the following operators are needed by template class "set" @@ -93,12 +95,12 @@ typedef set<FileDescrLine> DirectoryDescrType; enum CompareFilesResult { - FileOnLeftSideOnly, - FileOnRightSideOnly, - RightFileNewer, - LeftFileNewer, - FilesDifferent, - FilesEqual + fileOnLeftSideOnly, + fileOnRightSideOnly, + rightFileNewer, + leftFileNewer, + filesDifferent, + filesEqual }; struct FileCompareLine @@ -116,35 +118,8 @@ typedef vector<FileCompareLine> FileCompareResult; enum CompareVariant { - CompareByMD5, - CompareByTimeAndSize -}; - -//interface for status updates (can be implemented by UI or commandline) -//overwrite virtual methods for respective functionality -class StatusUpdater -{ -public: - StatusUpdater() : abortionRequested(false) {} - virtual ~StatusUpdater() {} - - //these three methods have to be implemented in the derived classes to handle error and status information - virtual void updateStatus(const wxString& text) = 0; - virtual void updateProgressIndicator(double number) = 0; - virtual int reportError(const wxString& text) = 0; - - //this method is triggered repeatedly and can be used to refresh the ui by dispatching pending events - virtual void triggerUI_Refresh() {} - - void requestAbortion() //opportunity to abort must be implemented in the three virtual status and error methods (for example in triggerUI_Refresh()) - { //currently used by the UI status information screen, when button "Abort is pressed" - abortionRequested = true; - } - static const int Continue = -1; - static const int Retry = -2; - -protected: - bool abortionRequested; + compareByMD5, + compareByTimeAndSize }; @@ -168,8 +143,36 @@ private: StatusUpdater* statusUpdater; }; -typedef WINSHELLAPI int (*DLLFUNC)(LPSHFILEOPSTRUCT lpFileOp); +//Note: the following lines are a performance optimization for deleting elements from a vector. It is incredibly faster to create a new +//vector and leave specific elements out than to delete row by row and force recopying of most elements for each single deletion (linear vs quadratic runtime) +template <class T> +void removeRowsFromVector(vector<T>& grid, const set<int>& rowsToRemove) +{ + vector<T> temp; + int rowToSkip = -1; //keep it an INT! + + set<int>::iterator rowToSkipIndex = rowsToRemove.begin(); + + if (rowToSkipIndex != rowsToRemove.end()) + rowToSkip = *rowToSkipIndex; + + for (int i = 0; i < int(grid.size()); ++i) + { + if (i != rowToSkip) + temp.push_back(grid[i]); + else + { + rowToSkipIndex++; + if (rowToSkipIndex != rowsToRemove.end()) + rowToSkip = *rowToSkipIndex; + } + } + grid.swap(temp); +} + + +typedef WINSHELLAPI int (*DLLFUNC)(LPSHFILEOPSTRUCT lpFileOp); class FreeFileSync { @@ -181,8 +184,13 @@ public: friend class GetAllFilesFull; friend class CopyThread; + //identifiers of different processed + static const int scanningFilesProcess = 1; + static const int calcMD5Process = 2; + static const int synchronizeFilesProcess = 3; + //main function for compare - static void getModelDiff(FileCompareResult& output, const wxString& dirLeft, const wxString& dirRight, CompareVariant cmpVar, StatusUpdater* updateClass); + static void startCompareProcess(FileCompareResult& output, const wxString& dirLeft, const wxString& dirRight, CompareVariant cmpVar, StatusUpdater* statusUpdater); //main function for synchronization static void startSynchronizationProcess(FileCompareResult& grid, const SyncConfiguration& config, StatusUpdater* statusUpdater, bool useRecycleBin); @@ -191,7 +199,7 @@ public: bool setRecycleBinUsage(bool activate); //enables/disables Recycle Bin usage (but only if usage is possible at all): RV: Setting was successful or not static void deleteOnGridAndHD(FileCompareResult& grid, const set<int>& rowsToDelete, StatusUpdater* statusUpdater, bool useRecycleBin); -static void addSubElements(set<int>& subElements, const FileCompareResult& grid, const FileCompareLine& relevantRow); + static void addSubElements(set<int>& subElements, const FileCompareResult& grid, const FileCompareLine& relevantRow); static void filterCurrentGridData(FileCompareResult& currentGridData, const wxString& includeFilter, const wxString& excludeFilter); static void removeFilterOnCurrentGridData(FileCompareResult& currentGridData); @@ -199,9 +207,15 @@ static void addSubElements(set<int>& subElements, const FileCompareResult& grid, static wxString formatFilesizeToShortString(const mpz_class& filesize); static wxString getFormattedDirectoryName(const wxString& dirname); - static mpz_class calcTotalBytesToTransfer(const FileCompareResult& fileCmpResult, const SyncConfiguration& config); + static void calcTotalBytesToSync(int& objectsTotal, double& dataTotal, const FileCompareResult& fileCmpResult, const SyncConfiguration& config); + static void swapGrids(FileCompareResult& grid); + static void wxULongLongToMpz(mpz_t& output, const wxULongLong& input); + + static string calculateMD5Hash(const wxString& filename); + static string calculateMD5HashMultithreaded(const wxString& filename, StatusUpdater* updateClass); + static bool isFFS_ConfigFile(const wxString& filename); static const wxString FFS_ConfigFileID; @@ -220,17 +234,12 @@ private: void createDirectory(const wxString& directory, int level = 0); //level is used internally only //some special file functions void moveToRecycleBin(const wxString& filename); - void copyfileMultithreaded(const wxString& source, const wxString& target, StatusUpdater* updateClass); - - - static void getBytesToTransfer(mpz_t& result, const FileCompareLine& fileCmpLine, const SyncConfiguration& config); + static void copyfileMultithreaded(const wxString& source, const wxString& target, StatusUpdater* updateClass); static void generateFileAndFolderDescriptions(DirectoryDescrType& output, const wxString& directory, StatusUpdater* updateClass = 0); static void getFileInformation(FileInfo& output, const wxString& filename); - static wxString calculateMD5Hash(const wxString& filename); - bool recycleBinAvailable; wxLogNull* noWxLogs; HINSTANCE hinstShell; @@ -1,4 +1,4 @@ -FreeFileSync v1.3 +FreeFileSync v1.4 ----------------- Usage diff --git a/Resources.a02 b/Resources.a02 Binary files differnew file mode 100644 index 00000000..23b7d209 --- /dev/null +++ b/Resources.a02 diff --git a/Resources.dat b/Resources.dat Binary files differindex 6f9befc5..ecdd870b 100644 --- a/Resources.dat +++ b/Resources.dat diff --git a/language.dat b/language.dat index 1fb4ea26..55000014 100644 --- a/language.dat +++ b/language.dat @@ -1,5 +1,7 @@ Time: Uhrzeit: + already exists. Overwrite? + existiert bereits. Überschreiben? does not exist. Aborting! existiert nicht. Abbruch! does not exist. \n\nSynchronization aborted! @@ -28,12 +30,24 @@ gesamt) != files are different\n != Dateien sind verschieden\n +&Abort +&Abbruch +&Back +&Zurück +&Continue +&Fortfahren +&Default +&Standard +&Retry +&Wiederholen (-) filtered out from sync-process\n (-) nicht synchronisieren\n (Build: (Build: , . +--,- MB +--,- MB ------------------------------------------------- ----------------------------------------------------- ---------\n @@ -50,10 +64,6 @@ 1 Element rechts, 1 row in view ( 1 Zeile zur Ansicht ( -10,0 MB -10,0 MB -1000000 -1000000 << left file is newer\n << linke Datei ist neuer\n <Directory> @@ -76,6 +86,8 @@ About Info Action Aktion +All items have been synchronized! +Alle Elemente wurden synchronisiert! An error occured Fehler An exception occured @@ -88,6 +100,8 @@ Begin Synchronization Starte Synchronisation Benchmark: Benchmark: +Cancel +Abbruch Choose to hide filtered files/directories from list Gefilterte Dateien ein-/ausblenden Compare both sides @@ -95,17 +109,25 @@ Beide Seiten vergleichen Compare by Vergleichen nach Compare by \"File size and date\"\n----------------------------------------\nThis compare variant evaluates two equally named files as being equal when they have the same file size AND the same last write date and time. For the latter the system's UTC time (coordinated word time) is used internally, although the local file time is displayed on the result list. So there are no problems concerning different time zones or daylight saving time.\n\nWhen \"Compare\" is triggered with this option set the following decision tree is processed:\n\n -----------------\n |Decision tree|\n -----------------\n ________|___________\n | |\n file exists on both sides on one side only\n _____|______ __|___\n | | | |\nequal different left right\n _________|_____________\n | | |\n left newer right newer different (but same date)\n\nAs a result 6 different status can be returned to categorize all files:\n\n- exists left only\n- exists right only\n- left newer\n- right newer\n- different (but same date)\n- equal\n\n\nCompare by \"File content\"\n----------------------------------------\nAs the name suggests, two files which share the same name are marked as equal if and only if they have the same content. This option is useful for consistency checks rather than backup operations. Therefore the file times are not taken into account at all.\n\nWith this option enabled the decision tree is smaller:\n\n -----------------\n |Decision tree|\n -----------------\n ________|___________\n | |\n file exists on both sides on one side only\n _____|______ __|___\n | | | |\nequal different left right\n\nAs a result the files are separated into the following categories:\n\n- exists left only\n- exists right only\n- different\n- equal -Vergleichen nach \"Dateigröße und -datum\"\n---------------------------------------------------\nDiese Variante identifiziert zwei gleichnamige Dateien als gleich, wenn sie die gleiche Dateigröße haben UND der Zeitpunkt des letzten Zugriffs derselbe ist. Für Letzteres wird intern die UTC-Zeit (koordinierte Weltzeit) verwendet, auch wenn die lokale Zeit in der Ergebnisliste angezeigt wird. Dadurch werden Probleme vermieden, die durch verschiedene Zeitzonen oder Sommer-/Winterzeit entstehen.\n\nNachdem \"Compare\" mit dieser Einstellung gestartet wurde, wird der folgende Entscheidungsbaum abgearbeitet:\n\n -------------------------\n |Entscheidungsbaum|\n -------------------------\n __________|___________\n | |\n Datei ex. auf beiden Seiten nur auf einer Seite\n ________|____ __|___\n | | | |\n gleich verschieden links rechts\n __________|___________\n | | |\n links neuer rechts neuer verschieden (bei gleichem Datum)\n\nAls Ergebnis werden 6 verschiedene Status zurückgegeben, um Dateien zu kategorisieren:\n\n- existiert nur links\n- existiert nur rechts\n- links neuer\n- rechts neuer\n- verschieden (bei gleichem Datum)\n- gleich\n\n\nVergleichen nach \"Dateiinhalt\"\n------------------------------------\nWie der Name bereits sagt, werden zwei Dateien mit gleichem Namen genau dann als gleich angesehen, wenn sie den gleichen Dateiinhalt haben. Diese Einstellung ist eher für Konsistenzprüfungen geeignet als für Backup-Operationen. Aus diesem Grund wird der Zeitpunkt der letzten Änderung der Dateien nicht berücksichtigt.\n\nDer Entscheidungsbaum ist in diesem Fall kleiner:\n\n -------------------------\n |Entscheidungsbaum|\n -------------------------\n __________|___________\n | |\n Datei ex. auf beiden Seiten nur auf einer Seite\n ________|____ __|___\n | | | |\n gleich verschieden links rechts\n\nDas Ergebnis ist eine Aufteilung in folgende Kategorien:\n\n- existiert nur links\n- existiert nur rechts\n- verschieden\n- gleich +Vergleichen nach \"Dateigröße und -datum\"\n---------------------------------------------------\nDiese Variante identifiziert zwei gleichnamige Dateien als gleich, wenn sie die gleiche Dateigröße haben UND der Zeitpunkt der letzten Änderung derselbe ist. Für Letzteres wird intern die UTC-Zeit (koordinierte Weltzeit) verwendet, auch wenn die lokale Zeit in der Ergebnisliste angezeigt wird. Dadurch werden Probleme vermieden, die durch verschiedene Zeitzonen oder Sommer-/Winterzeit entstehen.\n\nNachdem \"Compare\" mit dieser Einstellung gestartet wurde, wird der folgende Entscheidungsbaum abgearbeitet:\n\n -------------------------\n |Entscheidungsbaum|\n -------------------------\n __________|___________\n | |\n Datei ex. auf beiden Seiten nur auf einer Seite\n ________|____ __|___\n | | | |\n gleich verschieden links rechts\n __________|___________\n | | |\n links neuer rechts neuer verschieden (bei gleichem Datum)\n\nAls Ergebnis werden 6 verschiedene Status zurückgegeben, um Dateien zu kategorisieren:\n\n- existiert nur links\n- existiert nur rechts\n- links neuer\n- rechts neuer\n- verschieden (bei gleichem Datum)\n- gleich\n\n\nVergleichen nach \"Dateiinhalt\"\n------------------------------------\nWie der Name bereits sagt, werden zwei Dateien mit gleichem Namen genau dann als gleich angesehen, wenn sie den gleichen Dateiinhalt haben. Diese Einstellung ist eher für Konsistenzprüfungen geeignet als für Backup-Operationen. Aus diesem Grund wird der Zeitpunkt der letzten Änderung der Dateien nicht berücksichtigt.\n\nDer Entscheidungsbaum ist in diesem Fall kleiner:\n\n -------------------------\n |Entscheidungsbaum|\n -------------------------\n __________|___________\n | |\n Datei ex. auf beiden Seiten nur auf einer Seite\n ________|____ __|___\n | | | |\n gleich verschieden links rechts\n\nDas Ergebnis ist eine Aufteilung in folgende Kategorien:\n\n- existiert nur links\n- existiert nur rechts\n- verschieden\n- gleich +Comparing... +Vergleiche... Completed Fertig Configuration Konfiguration +Configuration loaded! +Konfiguration geladen! +Configuration saved! +Konfiguration gespeichert! Configure Filter... Konfiguriere Filter... Configure filter settings Konfiguriere Filter Configure your own synchronization rules. Eigene Synchronisationsregeln definieren. +Confirm +Bestätigen Continue Fortfahren Copy from left to right @@ -126,10 +148,14 @@ Could not open ""config.dat"" for write access! Fehler beim Schreiben von ""config.dat""! Could not open file: Fehler beim Öffnen der Datei: +Could not read configuration file +Fehler beim Lesen der Konfigurationsdatei Could not retrieve file info for: Fehler beim Lesen der Dateiattribute von: Could not set working directory to directory containing executable file! Arbeitsverzeichnisses konnte nicht auf Pfad der .exe-Datei gelegt werden! +Could not write to +Fehler beim Schreiben von Creating folder Erstelle Verzeichnis Current operation: @@ -138,8 +164,14 @@ Custom Eigene Data copied: Kopierte Daten: +Data remaining: +Verbliebene Daten: Data to be transferred: Zu übertragende Daten: +Data to compare: +Zu vergleichende Daten: +Data to transfer: +Zu übertragende Daten: Date Datum Default @@ -162,10 +194,16 @@ Displays help on the command line parameters\n Hilfe zu den Kommandozeilenparametern\n Do nothing Nichts tun +Do you really want to delete the following objects(s)? +Sollen die folgenden Elemente wirklich gelöscht werden? +Do you really want to move the following objects(s) to the recycle bin? +Sollen die folgenden Elemente wirklich in den Papierkorb verschoben werden? Donate with Paypal Spenden mit Paypal Drag && drop Drag && Drop +Dummy text +Dummy text Email: Email: Equality condition @@ -207,7 +245,11 @@ Dateiname Files are found equal if\n - file content\nis the same. Dateien gelten als gleich, wenn\n - der Inhalt\ngleich ist. Files are found equal if\n - filesize\n - last write time (UTC) and date\nare the same. -Dateien gelten als gleich, wenn\n - die Größe\n - Datum und Uhrzeit(UTC) des letzten Zugriffs\ngleich sind. +Dateien gelten als gleich, wenn\n - die Größe\n - Datum und Uhrzeit(UTC) der letzten Änderung\ngleich sind. +Files remaining: +Verbliebene Dateien: +Files scanned: +Eingelesene Dateien: Files that are different Unterschiedliche Dateien Files that are equal @@ -226,6 +268,8 @@ Files that exist on left view only Dateien, die nur links existieren Files that exist on right view only Dateien, die nur rechts existieren +Files to compare: +Zu vergleichende Dateien: Filter Filter Filter active: Press again to deactivate @@ -246,12 +290,16 @@ FreeFileSync at Sourceforge FreeFileSync bei Sourceforge Help Hilfe +Hide error messages +Unterdrücke Fehlermeldungen Hide filtered items Gefilterte Elemente ausblenden Hide further error messages Weitere Fehlermeldungen unterdrücken Hide further messages during current process Weitere Meldungen während dieser Operation ausblenden +Hides error messages during synchronization:\nThey are collected and shown as a list at the end of the process +Verhindert das Anzeigen von Fehlermeldungen während der Synchronisation:\nSie werden jedoch gesammelt und nach dem Vorgang als Liste angezeigt Homepage: Homepage: If errors occur during folder comparison or synchronization they are ignored and the process continues.\n @@ -268,26 +316,42 @@ Items completed: Elemente komplett: Legend\n Legende\n +Load configuration from file:\n - use this choice box\n - drag & drop config file to this window\n - specify config file as first commandline parameter +Konfiguration aus Datei laden:\n - diese Auswahlliste verwenden\n - Config-Datei per Drag & Drop auf dieses Fenster ziehen\n - Config-Datei als ersten Kommandozeilenparameter mitgeben +Load configuration... +Konfiguration laden... Log-messages: Lognachrichten: Mirror backup of left folder: Right folder will be overwritten and exactly match left folder after synchronization. Backup des linken Ordners erstellen: Der rechte Ordner wird dabei überschrieben und nach der Synchronisation dem linken exakt gleichen. +Not all items have been synchronized! You may try to synchronize the remaining items again (WITHOUT having to re-compare)! +Nicht alle Objekte wurden synchronisiert! Die Synchronisation kann erneut gestartet werden (OHNE dass ein erneuter Vergleich notwendig ist)! +Not all items were synchronized! Have a look at the list. +Nicht alle Objekte wurden synchronisiert! Siehe die verbliebenen Elemente im Hauptfenster. Nothing to synchronize. Both directories seem to contain the same data! Nichts zu synchronisieren. Beide Verzeichnisse scheinen dieselben Daten zu enthalten! OK OK +Objects to process: +Zu übertragende Elemente: One way -> Nach rechts -> Only files/directories that pass filtering will be relevant for synchronization.\n\n1. Enter full file or directory names separated by ';'.\n2. Wildcard characters '*' and '?' are supported.\n3. Case sensitive expressions!\n\nExample: *.tmp;C:\\data\\dummy.log;*filename*;*directory* Nur Dateien/Ordner, die zu den Filtereinstellungen passen, werden bei der\nSynchronisation berücksichtigt.\n\n1. Komplette Datei- oder Verzeichnisnamen getrennt durch ';' eingeben.\n2. Die Platzhalter '*' und '?' werden unterstützt.\n3. Groß-/Kleinschreibung wird unterschieden!\n\nBeispiel: *.tmp;C:\\data\\dummy.log;*filename*;*directory* Open synchronization dialog Zeige Synchronisationseinstellungen +Operation aborted! +Aktion abgebrochen! +Operation: +Vorgang: Please select directories for both sides! Bitte für beide Seiten ein Verzeichnis wählen! Press button to activate filter Auswählen, um Filter zu aktivieren Quit Beenden +Reading content of +Lese Inhalt von Relative path Relativer Pfad Result @@ -295,9 +359,19 @@ Ergebnis Retry Wiederholen Running... -Synchronisiere... +Aktiv... +Save current configuration to file +Aktuelle Konfiguration in Datei sichern +Saved aborted! +Speichern abgebrochen! +Scanning +Lese Datei Scanning files/folders: Scanne Dateien/Ordner: +Scanning... +Suche Dateien... +Scanning... +Scanne... Select a folder Verzeichnis wählen Select variant: @@ -316,10 +390,14 @@ Specify names to be included separated by ';'. Wildcards '*' and '?' are support Einzuschließende Namen, getrennt durch ';' angeben. Platzhalter '*' und '?' werden unterstützt. Standard: \"*\"\n Specify the sync-direction used for each type of file by a string of five chars:\n\n\t\tChar 1: Folders/files that exist on left side only\n\t\tChar 2: Folders/files that exist on right side only\n\t\tChar 3: Files that exist on both sides, left one is newer\n\t\tChar 4: Files that exist on both sides, right one is newer\n\t\tChar 5: Files that exist on both sides and are different\n\n\t\tSync-direction: L: left, R: right, N: none\n Synchronisationseinstellungen durch Zeichenkette der Länge 5 spezifizieren:\n\n\t\t1. Zeichen: Ordner/Dateien, die nur links existieren\n\t\t2. Zeichen: Ordner/Dateien, die nur rechts existieren\n\t\t3. Zeichen: Dateien, die auf beiden Seiten existieren; linke Datei ist neuer\n\t\t4. Zeichen: Dateien, die auf beiden Seiten existieren; rechte Datei ist neuer\n\t\t5. Zeichen: Dateien, die auf beiden Seiten existieren und verschieden sind\n\n\t\tSynchronisationsrichtung: L: links, R: rechts, N: nichts tun\n +Start synchronization +Starte die Synchronisation Start synchronizing files Beginne Synchronisation der Daten Swap sides Vertausche Seiten +Synchronization aborted: You may try to synchronize remaining items again (WITHOUT having to re-compare)! +Synchronisation abgebrochen: Die Synchronisation kann erneut gestartet werden (OHNE dass ein erneuter Vergleich notwendig ist)! Synchronization completed! Synchronisation abgeschlossen! Synchronization settings @@ -328,6 +406,12 @@ Synchronization status Synchronisation: Status Synchronize both sides simultaneously: Copy new or updated files in both directions. Beide Seiten gleichzeitig synchronisieren: Neue oder aktualisierte Dateien auf die jeweils andere Seite kopieren. +Synchronizing... +Synchronisiere... +The selected file does not contain a valid configuration! +Die ausgewählte Datei enthält keine gültige Konfiguration! +The selected file does not exist anymore! +Die gewählte Datei existiert nicht mehr! Two way <-> Beidseitig <-> Unable to create logfile! @@ -346,6 +430,8 @@ Warnung: Synchronisation fehlgeschlagen für \n\nMit nächstem Objekt fortsetzen, wiederholen oder abbrechen? \n\nContinue with next object, retry or abort synchronization? \n\nMit nächstem Objekt fortsetzen, wiederholen oder abbrechen? +\n\nInformation: If you skip the error and continue or abort a re-compare will be necessary! +\n\nInformation: Wenn der Fehler nicht behoben wird, muss der Vergleich erneut gestartet werden! \n\nSkip this directory and continue synchronization? \n\nDieses Verzeichnis überspringen und mit Synchronisation fortfahren? \n\nSkip this file and continue synchronization? diff --git a/library/CustomGrid.cpp b/library/CustomGrid.cpp index 876ad8f9..eea2a0d4 100644 --- a/library/CustomGrid.cpp +++ b/library/CustomGrid.cpp @@ -166,7 +166,7 @@ public: { if (currentUI_ViewPtr->size() > unsigned(row)) { - if ((*currentUI_ViewPtr)[row].cmpResult == ConstFilteredOut) + if ((*currentUI_ViewPtr)[row].cmpResult == constFilteredOut) return true; } } diff --git a/library/GMP/include/gmp.h b/library/GMP/include/gmp.h index 1268c74d..fa86f2dc 100644 --- a/library/GMP/include/gmp.h +++ b/library/GMP/include/gmp.h @@ -421,7 +421,7 @@ typedef __mpq_struct *mpq_ptr; GCC 4.3 and above with -std=c99 or -std=gnu99 implements ISO C99 inline semantics, unless -fgnu89-inline is used. */ #ifdef __GNUC__ -#ifdef __GNUC_STDC_INLINE__ +#if (defined __GNUC_STDC_INLINE__) || (__GNUC__ == 4 && __GNUC_MINOR__ == 2) #define __GMP_EXTERN_INLINE extern __inline__ __attribute__ ((__gnu_inline__)) #else #define __GMP_EXTERN_INLINE extern __inline__ @@ -518,6 +518,7 @@ typedef __mpq_struct *mpq_ptr; #if defined (__cplusplus) extern "C" { +#include <cstdio> #ifdef _GMP_H_HAVE_FILE using std::FILE; #endif @@ -2173,10 +2174,14 @@ enum GMP_ERROR_INVALID_ARGUMENT = 8 }; +/* Define CC and CFLAGS which were used to build this version of GMP */ +#define __GMP_CC "gcc" +#define __GMP_CFLAGS "-m32 -O2 -fomit-frame-pointer -mtune=k8 -march=k8 -mno-cygwin" + /* Major version number is the value of __GNU_MP__ too, above and in mp.h. */ #define __GNU_MP_VERSION 4 #define __GNU_MP_VERSION_MINOR 2 -#define __GNU_MP_VERSION_PATCHLEVEL 2 +#define __GNU_MP_VERSION_PATCHLEVEL 3 #define __GMP_H__ #endif /* __GMP_H__ */ diff --git a/library/GMP/include/gmpxx.h b/library/GMP/include/gmpxx.h index 76a72678..6472707a 100644 --- a/library/GMP/include/gmpxx.h +++ b/library/GMP/include/gmpxx.h @@ -1,6 +1,6 @@ /* gmpxx.h -- C++ class wrapper for GMP types. -*- C++ -*- -Copyright 2001, 2002, 2003, 2006 Free Software Foundation, Inc. +Copyright 2001, 2002, 2003, 2006, 2008 Free Software Foundation, Inc. This file is part of the GNU MP Library. @@ -37,6 +37,7 @@ along with the GNU MP Library. If not, see http://www.gnu.org/licenses/. */ #include <cstring> /* for strlen */ #include <string> #include <stdexcept> +#include <cfloat> #include <gmp.h> @@ -655,22 +656,83 @@ struct __gmp_binary_modulus } }; +// Max allocations for plain types when converted to mpz_t +// FIXME: how do we get the proper max "double" exponent? +#define __GMP_DBL_LIMBS (2 + DBL_MAX_EXP / GMP_NUMB_BITS) +#define __GMP_ULI_LIMBS (1 + (8 * sizeof (long) - 1) / GMP_NUMB_BITS) + +#define __GMPXX_TMP_UI \ + mpz_t temp; \ + mp_limb_t limbs[__GMP_ULI_LIMBS]; \ + temp->_mp_d = limbs; \ + temp->_mp_alloc = __GMP_ULI_LIMBS; \ + mpz_set_ui (temp, l) +#define __GMPXX_TMP_SI \ + mpz_t temp; \ + mp_limb_t limbs[__GMP_ULI_LIMBS]; \ + temp->_mp_d = limbs; \ + temp->_mp_alloc = __GMP_ULI_LIMBS; \ + mpz_set_si (temp, l) +#define __GMPXX_TMP_D \ + mpz_t temp; \ + mp_limb_t limbs[__GMP_DBL_LIMBS]; \ + temp->_mp_d = limbs; \ + temp->_mp_alloc = __GMP_DBL_LIMBS; \ + mpz_set_d (temp, d) + struct __gmp_binary_and { static void eval(mpz_ptr z, mpz_srcptr w, mpz_srcptr v) { mpz_and(z, w, v); } + + static void eval(mpz_ptr z, mpz_srcptr w, unsigned long int l) + { __GMPXX_TMP_UI; mpz_and (z, w, temp); } + static void eval(mpz_ptr z, unsigned long int l, mpz_srcptr w) + { __GMPXX_TMP_UI; mpz_and (z, w, temp); } + static void eval(mpz_ptr z, mpz_srcptr w, signed long int l) + { __GMPXX_TMP_SI; mpz_and (z, w, temp); } + static void eval(mpz_ptr z, signed long int l, mpz_srcptr w) + { __GMPXX_TMP_SI; mpz_and (z, w, temp); } + static void eval(mpz_ptr z, mpz_srcptr w, double d) + { __GMPXX_TMP_D; mpz_and (z, w, temp); } + static void eval(mpz_ptr z, double d, mpz_srcptr w) + { __GMPXX_TMP_D; mpz_and (z, w, temp); } }; struct __gmp_binary_ior { static void eval(mpz_ptr z, mpz_srcptr w, mpz_srcptr v) { mpz_ior(z, w, v); } + static void eval(mpz_ptr z, mpz_srcptr w, unsigned long int l) + { __GMPXX_TMP_UI; mpz_ior (z, w, temp); } + static void eval(mpz_ptr z, unsigned long int l, mpz_srcptr w) + { __GMPXX_TMP_UI; mpz_ior (z, w, temp); } + static void eval(mpz_ptr z, mpz_srcptr w, signed long int l) + { __GMPXX_TMP_SI; mpz_ior (z, w, temp); } + static void eval(mpz_ptr z, signed long int l, mpz_srcptr w) + { __GMPXX_TMP_SI; mpz_ior (z, w, temp); } + static void eval(mpz_ptr z, mpz_srcptr w, double d) + { __GMPXX_TMP_D; mpz_ior (z, w, temp); } + static void eval(mpz_ptr z, double d, mpz_srcptr w) + { __GMPXX_TMP_D; mpz_ior (z, w, temp); } }; struct __gmp_binary_xor { static void eval(mpz_ptr z, mpz_srcptr w, mpz_srcptr v) { mpz_xor(z, w, v); } + static void eval(mpz_ptr z, mpz_srcptr w, unsigned long int l) + { __GMPXX_TMP_UI; mpz_xor (z, w, temp); } + static void eval(mpz_ptr z, unsigned long int l, mpz_srcptr w) + { __GMPXX_TMP_UI; mpz_xor (z, w, temp); } + static void eval(mpz_ptr z, mpz_srcptr w, signed long int l) + { __GMPXX_TMP_SI; mpz_xor (z, w, temp); } + static void eval(mpz_ptr z, signed long int l, mpz_srcptr w) + { __GMPXX_TMP_SI; mpz_xor (z, w, temp); } + static void eval(mpz_ptr z, mpz_srcptr w, double d) + { __GMPXX_TMP_D; mpz_xor (z, w, temp); } + static void eval(mpz_ptr z, double d, mpz_srcptr w) + { __GMPXX_TMP_D; mpz_xor (z, w, temp); } }; struct __gmp_binary_lshift @@ -686,7 +748,7 @@ struct __gmp_binary_lshift struct __gmp_binary_rshift { static void eval(mpz_ptr z, mpz_srcptr w, unsigned long int l) - { mpz_tdiv_q_2exp(z, w, l); } + { mpz_fdiv_q_2exp(z, w, l); } static void eval(mpq_ptr q, mpq_srcptr r, unsigned long int l) { mpq_div_2exp(q, r, l); } static void eval(mpf_ptr f, mpf_srcptr g, unsigned long int l) @@ -1291,13 +1353,17 @@ struct __gmp_rand_function /* this is much the same as gmp_allocated_string in gmp-impl.h since gmp-impl.h is not publicly available, I redefine it here I use a different name to avoid possible clashes */ + +extern "C" { + typedef void (*__gmp_freefunc_t) (void *, size_t); +} struct __gmp_alloc_cstring { char *str; __gmp_alloc_cstring(char *s) { str = s; } ~__gmp_alloc_cstring() { - void (*freefunc) (void *, size_t); + __gmp_freefunc_t freefunc; mp_get_memory_functions (NULL, NULL, &freefunc); (*freefunc) (str, std::strlen(str)+1); } @@ -1616,9 +1682,9 @@ public: __GMP_DECLARE_COMPOUND_OPERATOR(operator/=) __GMP_DECLARE_COMPOUND_OPERATOR(operator%=) - __GMPP_DECLARE_COMPOUND_OPERATOR(operator&=) - __GMPP_DECLARE_COMPOUND_OPERATOR(operator|=) - __GMPP_DECLARE_COMPOUND_OPERATOR(operator^=) + __GMP_DECLARE_COMPOUND_OPERATOR(operator&=) + __GMP_DECLARE_COMPOUND_OPERATOR(operator|=) + __GMP_DECLARE_COMPOUND_OPERATOR(operator^=) __GMP_DECLARE_COMPOUND_OPERATOR_UI(operator<<=) __GMP_DECLARE_COMPOUND_OPERATOR_UI(operator>>=) @@ -3006,9 +3072,6 @@ __GMPN_DEFINE_COMPOUND_OPERATOR(type, fun, eval_fun) #define __GMPZ_DEFINE_COMPOUND_OPERATOR(fun, eval_fun) \ __GMP_DEFINE_COMPOUND_OPERATOR(mpz, fun, eval_fun) -#define __GMPZZ_DEFINE_COMPOUND_OPERATOR(fun, eval_fun) \ -__GMPP_DEFINE_COMPOUND_OPERATOR(mpz, fun, eval_fun) - #define __GMPQ_DEFINE_COMPOUND_OPERATOR(fun, eval_fun) \ __GMP_DEFINE_COMPOUND_OPERATOR(mpq, fun, eval_fun) @@ -3109,9 +3172,9 @@ __GMPZ_DEFINE_COMPOUND_OPERATOR(operator*=, __gmp_binary_multiplies) __GMPZ_DEFINE_COMPOUND_OPERATOR(operator/=, __gmp_binary_divides) __GMPZ_DEFINE_COMPOUND_OPERATOR(operator%=, __gmp_binary_modulus) -__GMPZZ_DEFINE_COMPOUND_OPERATOR(operator&=, __gmp_binary_and) -__GMPZZ_DEFINE_COMPOUND_OPERATOR(operator|=, __gmp_binary_ior) -__GMPZZ_DEFINE_COMPOUND_OPERATOR(operator^=, __gmp_binary_xor) +__GMPZ_DEFINE_COMPOUND_OPERATOR(operator&=, __gmp_binary_and) +__GMPZ_DEFINE_COMPOUND_OPERATOR(operator|=, __gmp_binary_ior) +__GMPZ_DEFINE_COMPOUND_OPERATOR(operator^=, __gmp_binary_xor) __GMPZ_DEFINE_COMPOUND_OPERATOR_UI(operator<<=, __gmp_binary_lshift) __GMPZ_DEFINE_COMPOUND_OPERATOR_UI(operator>>=, __gmp_binary_rshift) @@ -3269,7 +3332,6 @@ public: #undef __GMP_DECLARE_INCREMENT_OPERATOR #undef __GMPZQ_DEFINE_EXPR -#undef __GMP_DEFINE_TERNARY_EXPR #undef __GMP_DEFINE_UNARY_FUNCTION #undef __GMP_DEFINE_UNARY_TYPE_FUNCTION @@ -3294,15 +3356,7 @@ public: #undef __GMPN_DEFINE_BINARY_TYPE_FUNCTION #undef __GMP_DEFINE_BINARY_TYPE_FUNCTION -#undef __GMPP_DECLARE_COMPOUND_OPERATOR -#undef __GMPN_DECLARE_COMPOUND_OPERATOR -#undef __GMP_DECLARE_COMPOUND_OPERATOR - -#undef __GMP_DECLARE_COMPOUND_OPERATOR_UI -#undef __GMP_DECLARE_INCREMENT_OPERATOR - #undef __GMPZ_DEFINE_COMPOUND_OPERATOR -#undef __GMPZZ_DEFINE_COMPOUND_OPERATOR #undef __GMPZN_DEFINE_COMPOUND_OPERATOR #undef __GMPZNN_DEFINE_COMPOUND_OPERATOR #undef __GMPZNS_DEFINE_COMPOUND_OPERATOR @@ -3319,8 +3373,6 @@ public: #undef __GMPN_DEFINE_COMPOUND_OPERATOR #undef __GMP_DEFINE_COMPOUND_OPERATOR -#undef __GMPZ_DEFINE_COMPOUND_OPERATOR -#undef __GMPZZ_DEFINE_COMPOUND_OPERATOR #undef __GMPQ_DEFINE_COMPOUND_OPERATOR #undef __GMPF_DEFINE_COMPOUND_OPERATOR diff --git a/library/GMP/lib/libgmp.a b/library/GMP/lib/libgmp.a Binary files differindex 041ee6c7..e53850ec 100644 --- a/library/GMP/lib/libgmp.a +++ b/library/GMP/lib/libgmp.a diff --git a/library/GMP/lib/libgmp.la b/library/GMP/lib/libgmp.la index ee6e9a74..a0e34d40 100644 --- a/library/GMP/lib/libgmp.la +++ b/library/GMP/lib/libgmp.la @@ -19,7 +19,7 @@ dependency_libs='' # Version information for libgmp. current=7 age=4 -revision=2 +revision=3 # Is this an already installed library? installed=yes diff --git a/library/GMP/lib/libgmpxx.a b/library/GMP/lib/libgmpxx.a Binary files differindex 90ca1d40..3c4452d3 100644 --- a/library/GMP/lib/libgmpxx.a +++ b/library/GMP/lib/libgmpxx.a diff --git a/library/GMP/lib/libgmpxx.la b/library/GMP/lib/libgmpxx.la index e608eba7..dbb77dd0 100644 --- a/library/GMP/lib/libgmpxx.la +++ b/library/GMP/lib/libgmpxx.la @@ -19,7 +19,7 @@ dependency_libs=' /c/gmp/lib/libgmp.la' # Version information for libgmpxx. current=4 age=0 -revision=2 +revision=3 # Is this an already installed library? installed=yes diff --git a/library/globalFunctions.cpp b/library/globalFunctions.cpp index 70a5abaf..d0aad7a4 100644 --- a/library/globalFunctions.cpp +++ b/library/globalFunctions.cpp @@ -1,98 +1,86 @@ #include "globalFunctions.h" -#include <wx/intl.h> - -#ifdef FFS_WIN -const wxChar FileNameSeparator = '\\'; -#endif // FFS_WIN - -#ifdef FFS_LINUX -const wxChar FileNameSeparator = '/'; -#endif // FFS_LINUX - -const wxChar* FloatingPointSeparator = _("."); -const wxChar* NumberSeparator = _(","); - +#include "../ui/resources.h" inline -int GlobalFunctions::round(const double d) +int globalFunctions::round(const double d) { return static_cast<int>(d<0?d-.5:d+.5); } inline -int GlobalFunctions::abs(const int d) +int globalFunctions::abs(const int d) { return(d<0?-d:d); } inline -unsigned int GlobalFunctions::abs(const unsigned int d) +unsigned int globalFunctions::abs(const unsigned int d) { return(d<0?-d:d); } inline -float GlobalFunctions::abs(const float d) +float globalFunctions::abs(const float d) { return(d<0?-d:d); }; inline -double GlobalFunctions::abs(const double d) +double globalFunctions::abs(const double d) { return(d<0?-d:d); } -string GlobalFunctions::numberToString(const unsigned int number) +string globalFunctions::numberToString(const unsigned int number) { char result[100]; sprintf( result, "%u", number); return string(result); } -string GlobalFunctions::numberToString(const int number) +string globalFunctions::numberToString(const int number) { char result[100]; sprintf( result, "%d", number); return string(result); } -string GlobalFunctions::numberToString(const float number) +string globalFunctions::numberToString(const float number) { char result[100]; sprintf( result, "%f", number); return string(result); } -wxString GlobalFunctions::numberToWxString(const unsigned int number) +wxString globalFunctions::numberToWxString(const unsigned int number) { return wxString::Format(wxT("%u"), number); } -wxString GlobalFunctions::numberToWxString(const int number) +wxString globalFunctions::numberToWxString(const int number) { return wxString::Format(wxT("%i"), number); } -wxString GlobalFunctions::numberToWxString(const float number) +wxString globalFunctions::numberToWxString(const float number) { return wxString::Format(wxT("%f"), number); } inline -int GlobalFunctions::stringToInt(const string& number) +int globalFunctions::stringToInt(const string& number) { return atoi(number.c_str()); } inline -double GlobalFunctions::stringToDouble(const string& number) +double globalFunctions::stringToDouble(const string& number) { return atof(number.c_str()); } inline -int GlobalFunctions::wxStringToInt(const wxString& number) +int globalFunctions::wxStringToInt(const wxString& number) { long result = 0; if (number.ToLong(&result)) @@ -102,7 +90,7 @@ int GlobalFunctions::wxStringToInt(const wxString& number) } inline -double GlobalFunctions::wxStringToDouble(const wxString& number) +double globalFunctions::wxStringToDouble(const wxString& number) { double result = 0; if (number.ToDouble(&result)) @@ -111,9 +99,9 @@ double GlobalFunctions::wxStringToDouble(const wxString& number) throw std::runtime_error("Error when converting number to double"); } -wxString& GlobalFunctions::includeNumberSeparator(wxString& number) +wxString& globalFunctions::includeNumberSeparator(wxString& number) { for (int i = number.size() - 3; i > 0; i-= 3) - number.insert(i, NumberSeparator); + number.insert(i, GlobalResources::numberSeparator); return number; } diff --git a/library/globalFunctions.h b/library/globalFunctions.h index b766a097..5fe576e1 100644 --- a/library/globalFunctions.h +++ b/library/globalFunctions.h @@ -8,11 +8,7 @@ using namespace std; -extern const wxChar FileNameSeparator; -extern const wxChar* FloatingPointSeparator; -extern const wxChar* NumberSeparator; - -namespace GlobalFunctions +namespace globalFunctions { int round(double d); //little rounding function diff --git a/library/multithreading.cpp b/library/multithreading.cpp new file mode 100644 index 00000000..dd96eaf4 --- /dev/null +++ b/library/multithreading.cpp @@ -0,0 +1,148 @@ +#include "multithreading.h" +#include <wx/utils.h> +//#include "windows.h" +//MessageBox(0, "hi", "", 0); + +/*choreography: + + ------------- --------------- + |main thread| |worker thread| + ------------- --------------- + +1. Instantiation (Constructor) +------------------------------- + create thread + enter waiting state +2. waitUntilReady +------------------------------- + wait until thread is ready + +3. Call execAndUpdate +------------------------------- + send signal to start + start processing + update UI while thread works + finish processing + wait until main thread is ready to receive signal + receive signal + enter waiting state +4. Termination (Destructor) +------------------------------- + wait until thread is in wait state + set exit flag + signal thread to continue (and exit) +*/ +class WorkerThread : public wxThread +{ +public: + WorkerThread(UpdateWhileExecuting* handler) : + readyToBeginProcessing(), + beginProcessing(readyToBeginProcessing), + threadIsInitialized(false), + threadExitIsRequested(false), + threadHandler(handler) + { } + + ~WorkerThread() {} + + ExitCode Entry() + { + readyToBeginProcessing.Lock(); //this lock needs to be called IN the thread => calling it from constructor(Main thread) would be useless + sharedData.Enter(); + threadIsInitialized = true; + sharedData.Leave(); + + while (true) + { + beginProcessing.Wait(); + + //no mutex needed in this context + if (threadExitIsRequested) + return 0; + + //actual (long running) work is done in this method + threadHandler->longRunner(); + + threadHandler->readyToReceiveResult.Lock(); + threadHandler->receivingResult.Signal(); + threadHandler->readyToReceiveResult.Unlock(); + } + + return 0; + } + + + wxMutex readyToBeginProcessing; + wxCondition beginProcessing; + + //shared data + wxCriticalSection sharedData; + bool threadIsInitialized; + bool threadExitIsRequested; + + +private: + UpdateWhileExecuting* threadHandler; +}; + + +UpdateWhileExecuting::UpdateWhileExecuting() : + readyToReceiveResult(), + receivingResult(readyToReceiveResult) +{ + //mutex needs to be initially locked for condition receivingResult to work properly + readyToReceiveResult.Lock(); + + + theWorkerThread = new WorkerThread(this); + + theWorkerThread->Create(); + theWorkerThread->Run(); + + //wait until the thread has locked readyToBeginProcessing + bool threadInitialized = false; + while (!threadInitialized) + { + theWorkerThread->sharedData.Enter(); + threadInitialized = theWorkerThread->threadIsInitialized; + theWorkerThread->sharedData.Leave(); + wxMilliSleep(5); + } //-> it's not nice, but works and is no issue +} + + +UpdateWhileExecuting::~UpdateWhileExecuting() +{ + //wait until thread is ready, then start and exit immediately + readyToReceiveResult.Unlock(); //avoid possible deadlock, when thread might be waiting to send the signal + + theWorkerThread->readyToBeginProcessing.Lock(); + //set flag to exit thread + theWorkerThread->threadExitIsRequested = true; + theWorkerThread->beginProcessing.Signal(); + + theWorkerThread->readyToBeginProcessing.Unlock(); + + //theWorkerThread deletes itself! +} + + +void UpdateWhileExecuting::waitUntilReady() +{ + readyToReceiveResult.Unlock(); //avoid possible deadlock, when thread might be waiting to send the signal (if abort was pressed) + + theWorkerThread->readyToBeginProcessing.Lock(); +} +// /|\ \|/ must be called directly after each other + +void UpdateWhileExecuting::execAndUpdate(StatusUpdater* statusUpdater) +{ + readyToReceiveResult.Lock(); + + theWorkerThread->beginProcessing.Signal(); + theWorkerThread->readyToBeginProcessing.Unlock(); + + while (receivingResult.WaitTimeout(uiUpdateInterval) == wxCOND_TIMEOUT) + statusUpdater->triggerUI_Refresh(); //ATTENTION: Exception "AbortThisProcess" may be thrown here!!! +} + diff --git a/library/multithreading.h b/library/multithreading.h new file mode 100644 index 00000000..8466bb01 --- /dev/null +++ b/library/multithreading.h @@ -0,0 +1,64 @@ +#ifndef MULTITHREADING_H_INCLUDED +#define MULTITHREADING_H_INCLUDED + +#include <wx/string.h> +#include <wx/thread.h> + +//interface for status updates (can be implemented by UI or commandline) +//overwrite virtual methods for respective functionality +class StatusUpdater +{ +public: + StatusUpdater() : abortionRequested(false) {} + virtual ~StatusUpdater() {} + + //these four methods have to be implemented in the derived classes to handle error and status information + virtual void updateStatusText(const wxString& text) = 0; + virtual void initNewProcess(int objectsTotal, double dataTotal, int processID) = 0; //informs about the total amount of data that will be processed from now on + virtual void updateProcessedData(int objectsProcessed, double dataProcessed) = 0; //called periodically after data was processed + virtual int reportError(const wxString& text) = 0; + + //this method is triggered repeatedly and can be used to refresh the ui by dispatching pending events + virtual void triggerUI_Refresh() {} + + void requestAbortion() //opportunity to abort must be implemented in the three virtual status and error methods (for example in triggerUI_Refresh()) + { //currently used by the UI status information screen, when button "Abort is pressed" + abortionRequested = true; + } + static const int continueNext = -1; + static const int retry = -2; + +protected: + bool abortionRequested; +}; + + +const int uiUpdateInterval = 100; //perform ui updates not more often than necessary, 100 seems to be a good value with only a minimal performance loss + +class WorkerThread; + + +//class handling execution of a method while updating the UI +class UpdateWhileExecuting +{ + friend class WorkerThread; + +public: + UpdateWhileExecuting(); + virtual ~UpdateWhileExecuting(); + + void waitUntilReady(); + void execAndUpdate(StatusUpdater* statusUpdater); + + +private: + //implement a longrunning method without dependencies (e.g. copy file function) returning "true" on success + virtual void longRunner() = 0; + + WorkerThread* theWorkerThread; + + wxMutex readyToReceiveResult; + wxCondition receivingResult; +}; + +#endif // MULTITHREADING_H_INCLUDED diff --git a/mingwm10.dll b/mingwm10.dll Binary files differnew file mode 100644 index 00000000..1c50d073 --- /dev/null +++ b/mingwm10.dll diff --git a/ui/MainDialog.cpp b/ui/MainDialog.cpp index f063dfcb..1528f0c8 100644 --- a/ui/MainDialog.cpp +++ b/ui/MainDialog.cpp @@ -17,7 +17,7 @@ #include <cmath> #include <wx/msgdlg.h> -using namespace GlobalFunctions; +using namespace globalFunctions; int leadingPanel = 0; @@ -35,6 +35,14 @@ MainDialog::MainDialog(wxFrame* frame, const wxString& cfgFileName) : //initialize sync configuration readConfigurationFromHD(cfgFileName, true); + leftOnlyFilesActive = true; + leftNewerFilesActive = true; + differentFilesActive = true; + rightNewerFilesActive = true; //do not put these bool values into config.dat! + rightOnlyFilesActive = true; //it's more convenient to have them defaulted at startup + equalFilesActive = false; + updateViewFilterButtons(); + //set icons for this dialog m_bpButton11->SetBitmapLabel(*GlobalResources::bitmapAbout); m_bpButton10->SetBitmapLabel(*GlobalResources::bitmapExit); @@ -112,8 +120,6 @@ MainDialog::MainDialog(wxFrame* frame, const wxString& cfgFileName) : m_grid2->setGridDataTable(¤tUI_View); m_grid3->setGridDataTable(¤tUI_View); - updateViewFilterButtons(); - //disable sync button as long as "compare" hasn't been triggered. m_bpButtonSync->Disable(); @@ -129,11 +135,6 @@ MainDialog::MainDialog(wxFrame* frame, const wxString& cfgFileName) : //as the name says: disable them m_grid3->deactivateScrollbars(); - //set status of filter button - updateFilterButton(); - //set status of "hide filtered items" checkbox - m_checkBoxHideFilt->SetValue(hideFiltered); - //mainly to update row label sizes... writeGrid(currentGridData); @@ -216,14 +217,7 @@ MainDialog::~MainDialog() } delete cfgFileHistory; - try - { - writeConfigurationToHD(FreeFileSync::FFS_LastConfigFile); //don't trow exceptions in destructors - } - catch (std::runtime_error& theException) - { - wxMessageBox(_(theException.what()), _("An exception occured!"), wxOK | wxICON_ERROR); - } + writeConfigurationToHD(FreeFileSync::FFS_LastConfigFile); //don't trow exceptions in destructors } @@ -265,15 +259,11 @@ void MainDialog::onGrid3access(wxEvent& event) } -void MainDialog::filterRangeManual(int begin, int end, int leadingRow) +void MainDialog::filterRangeManual(const set<int>& rowsToFilterOnUI_View, int leadingRow) { - int currentUI_Size = currentUI_View.size(); - - int topRow = max(begin, 0); - int bottomRow = min(end, currentUI_Size - 1); - - if (topRow <= bottomRow) // bottomRow might be -1 ? + if (rowsToFilterOnUI_View.size() > 0) { + int currentUI_Size = currentUI_View.size(); bool newSelection = false; //default: deselect range @@ -287,12 +277,17 @@ void MainDialog::filterRangeManual(int begin, int end, int leadingRow) //get all lines that need to be filtered (e.g. if a folder is marked, then its subelements should be marked as well) set<int> rowsToFilterOnGridData; //rows to filter in backend - for (int i = topRow; i <= bottomRow; ++i) + + + for (set<int>::iterator i = rowsToFilterOnUI_View.begin(); i != rowsToFilterOnUI_View.end(); ++i) { - unsigned int gridDataLine = currentUI_View[i].linkToCurrentGridData; + if (0 <= *i && *i < currentUI_Size) + { + unsigned int gridIndex = currentUI_View[*i].linkToCurrentGridData; - rowsToFilterOnGridData.insert(gridDataLine); - FreeFileSync::addSubElements(rowsToFilterOnGridData, currentGridData, currentGridData[gridDataLine]); + rowsToFilterOnGridData.insert(gridIndex); + FreeFileSync::addSubElements(rowsToFilterOnGridData, currentGridData, currentGridData[gridIndex]); + } } @@ -327,37 +322,7 @@ void MainDialog::filterRangeManual(int begin, int end, int leadingRow) wxMilliSleep(400); //delete rows, that are filtered out: - - //for (set<int>::reverse_iterator i = filteredOutRowsOnUI.rbegin(); i != filteredOutRowsOnUI.rend(); ++i) - // currentUI_View.erase(currentUI_View.begin() + *i); - - //Note: the following lines are a performance optimization for deleting elements from a vector. It is incredibly faster to create a new - //vector and leave specific elements out than to delete row by row and force recopying of most elements for each single deletion (linear vs quadratic runtime) - - //Note: This is the most time consuming part in this whole method! - - UI_Grid temp; - int rowNr = 0; - int rowToSkip = -1; - - set<int>::iterator rowToSkipIndex = filteredOutRowsOnUI.begin(); - - if (rowToSkipIndex != filteredOutRowsOnUI.end()) - rowToSkip = *rowToSkipIndex; - - for (UI_Grid::iterator i = currentUI_View.begin(); i != currentUI_View.end(); ++i, ++rowNr) - { - if (rowNr != rowToSkip) - temp.push_back(*i); - else - { - rowToSkipIndex++; - if (rowToSkipIndex != filteredOutRowsOnUI.end()) - rowToSkip = *rowToSkipIndex; - } - } - currentUI_View.swap(temp); - + removeRowsFromVector(currentUI_View, filteredOutRowsOnUI); //redraw grid necessary to update new dimensions writeGrid(currentGridData, true); //use UI buffer, no mapping from currentGridData to UI model again, just a re-dimensioning of grids @@ -429,7 +394,11 @@ void MainDialog::OnIdleEvent(wxEvent& event) { //a mouse up event, but no mouse down! (e.g. when window is maximized and cursor is on grid3) filteringInitialized = false; - filterRangeManual(selectedRange3Begin, selectedRange3End, selectionLead); + set<int> filteredRows; + for (int i = selectedRange3Begin; i <= selectedRange3End; ++i) + filteredRows.insert(i); + + filterRangeManual(filteredRows, selectionLead); } } @@ -608,7 +577,7 @@ public: if (suppressUI_Errormessages) { unsolvedErrors = true; - return StatusUpdater::Continue; + return StatusUpdater::continueNext; } wxString errorMessage = text + _("\n\nInformation: If you skip the error and continue or abort a re-compare will be necessary!"); @@ -620,12 +589,12 @@ public: switch (rv) { - case ErrorDlg::ContinueButtonPressed: + case ErrorDlg::continueButtonPressed: unsolvedErrors = true; - return StatusUpdater::Continue; - case ErrorDlg::RetryButtonPressed: - return StatusUpdater::Retry; - case ErrorDlg::AbortButtonPressed: + return StatusUpdater::continueNext; + case ErrorDlg::retryButtonPressed: + return StatusUpdater::retry; + case ErrorDlg::abortButtonPressed: { unsolvedErrors = true; throw AbortThisProcess(); @@ -634,10 +603,11 @@ public: assert (false); } - return StatusUpdater::Continue; //dummy return value + return StatusUpdater::continueNext; //dummy return value } - void updateStatus(const wxString& text) {} - void updateProgressIndicator(double number) {} + void updateStatusText(const wxString& text) {} + void initNewProcess(int objectsTotal, double dataTotal, int processID) {} + void updateProcessedData(int objectsProcessed, double dataProcessed) {} private: @@ -675,10 +645,10 @@ void MainDialog::deleteFilesOnGrid(wxGrid* grid) { const FileCompareLine& currentCmpLine = currentGridData[*i]; - if (currentCmpLine.fileDescrLeft.objType != IsNothing) + if (currentCmpLine.fileDescrLeft.objType != isNothing) filesToDelete+= currentCmpLine.fileDescrLeft.filename + "\n"; - if (currentCmpLine.fileDescrRight.objType != IsNothing) + if (currentCmpLine.fileDescrRight.objType != isNothing) filesToDelete+= currentCmpLine.fileDescrRight.filename + "\n"; filesToDelete+= "\n"; @@ -688,7 +658,7 @@ void MainDialog::deleteFilesOnGrid(wxGrid* grid) switch (confirmDeletion->ShowModal()) { - case DeleteDialog::OkayButtonPressed: + case DeleteDialog::okayButtonPressed: { bool unsolvedErrorOccured = false; //if an error is skipped a re-compare will be necessary! @@ -709,10 +679,12 @@ void MainDialog::deleteFilesOnGrid(wxGrid* grid) //redraw grid neccessary to update new dimensions and for UI-Backend data linkage writeGrid(currentGridData); //do NOT use UI buffer here + + grid->ClearSelection(); //clear selection on grid } break; - case DeleteDialog::CancelButtonPressed: + case DeleteDialog::cancelButtonPressed: default: break; @@ -837,27 +809,6 @@ void MainDialog::OnDirChangedPanel2(wxFileDirPickerEvent& event) } -void onFilesDropped(const wxString& elementName, wxTextCtrl* txtCtrl, wxDirPickerCtrl* dirPicker) -{ - wxString fileName = elementName; - - if (wxDirExists(fileName)) - { - txtCtrl->SetValue(fileName); - dirPicker->SetPath(fileName); - } - else - { - fileName = wxFileName(fileName).GetPath(); - if (wxDirExists(fileName)) - { - txtCtrl->SetValue(fileName); - dirPicker->SetPath(fileName); - } - } -} - - void MainDialog::clearStatusBar() { stackObjects = 0; //prevent old stack objects from popping up @@ -884,10 +835,10 @@ bool sameFileSpecified(const wxString& file1, const wxString& file2) wxString file2Full = file2; if (wxFileName(file1).GetPath() == wxEmptyString) - file1Full = wxFileName::GetCwd() + FileNameSeparator + file1; + file1Full = wxFileName::GetCwd() + GlobalResources::fileNameSeparator + file1; if (wxFileName(file2).GetPath() == wxEmptyString) - file2Full = wxFileName::GetCwd() + FileNameSeparator + file2; + file2Full = wxFileName::GetCwd() + GlobalResources::fileNameSeparator + file2; return (file1Full == file2Full); } @@ -932,6 +883,27 @@ void MainDialog::addCfgFileToHistory(const wxString& filename) } +void onFilesDropped(const wxString& elementName, wxTextCtrl* txtCtrl, wxDirPickerCtrl* dirPicker) +{ + wxString fileName = elementName; + + if (wxDirExists(fileName)) + { + txtCtrl->SetValue(fileName); + dirPicker->SetPath(fileName); + } + else + { + fileName = wxFileName(fileName).GetPath(); + if (wxDirExists(fileName)) + { + txtCtrl->SetValue(fileName); + dirPicker->SetPath(fileName); + } + } +} + + bool FileDropEvent::OnDropFiles(wxCoord x, wxCoord y, const wxArrayString& filenames) { if (filenames.IsEmpty()) @@ -1033,14 +1005,16 @@ void MainDialog::OnLoadConfiguration(wxCommandEvent& event) if (!newCfgFile.IsEmpty()) { - if (wxFileExists(newCfgFile) && FreeFileSync::isFFS_ConfigFile(newCfgFile)) + if (!wxFileExists(newCfgFile)) + wxMessageBox(_("The selected file does not exist anymore!"), _("Warning"), wxOK); + else if (!FreeFileSync::isFFS_ConfigFile(newCfgFile)) + wxMessageBox(_("The selected file does not contain a valid configuration!"), _("Warning"), wxOK); + else { readConfigurationFromHD(newCfgFile); pushStatusInformation(_("Configuration loaded!")); } - else - wxMessageBox(_("The selected file does not contain a valid configuration!"), _("Warning"), wxOK); } } event.Skip(); @@ -1087,47 +1061,67 @@ void MainDialog::OnQuit(wxCommandEvent &event) } -void MainDialog::readConfigurationFromHD(const wxString& filename, bool programStartup) +void MainDialog::loadDefaultConfiguration() { //default values - syncConfiguration.exLeftSideOnly = SyncDirRight; - syncConfiguration.exRightSideOnly = SyncDirRight; - syncConfiguration.leftNewer = SyncDirRight; - syncConfiguration.rightNewer = SyncDirRight; - syncConfiguration.different = SyncDirRight; + syncConfiguration.exLeftSideOnly = syncDirRight; + syncConfiguration.exRightSideOnly = syncDirRight; + syncConfiguration.leftNewer = syncDirRight; + syncConfiguration.rightNewer = syncDirRight; + syncConfiguration.different = syncDirRight; - leftOnlyFilesActive = true; - leftNewerFilesActive = true; - differentFilesActive = true; - rightNewerFilesActive = true; //do not put these bool values into config.dat! - rightOnlyFilesActive = true; //it's more convenient to have them defaulted for each execution - equalFilesActive = false; + m_radioBtnSizeDate->SetValue(true); //compare algorithm includeFilter = "*"; //include all files/folders excludeFilter = ""; //exlude nothing - hideFiltered = false; //show filtered items + + //set status of filter button filterIsActive = false; //do not filter by default + updateFilterButton(); + + //set status of "hide filtered items" checkbox + hideFiltered = false; //show filtered items + m_checkBoxHideFilt->SetValue(hideFiltered); useRecycleBin = false; //do not use: in case OS doesn't support this, user will have to activate first and then get the error message + hideErrorMessages = false; widthNotMaximized = wxDefaultCoord; heightNotMaximized = wxDefaultCoord; posXNotMaximized = wxDefaultCoord; posYNotMaximized = wxDefaultCoord; -//##################################################### +} + + +void MainDialog::readConfigurationFromHD(const wxString& filename, bool programStartup) +{ + char bigBuffer[10000]; ifstream config(filename.c_str()); if (!config) + { + if (programStartup) + loadDefaultConfiguration(); + else + wxMessageBox(wxString(_("Could not read configuration file ")) + "\"" + filename + "\"", _("An exception occured!"), wxOK | wxICON_ERROR); + + return; + } + + //read FFS identifier + config.get(bigBuffer, FreeFileSync::FFS_ConfigFileID.Len() + 1); + + if (wxString(bigBuffer) != FreeFileSync::FFS_ConfigFileID) + { + wxMessageBox(_("The selected file does not contain a valid configuration!"), _("Warning"), wxOK); + config.close(); return; + } + //put filename on list of last used config files addCfgFileToHistory(filename); - char bigBuffer[10000]; - - - //read FFS identifier - config.get(bigBuffer, FreeFileSync::FFS_ConfigFileID.Len() + 1); //read sync configuration syncConfiguration.exLeftSideOnly = SyncDirection(config.get()); @@ -1136,13 +1130,13 @@ void MainDialog::readConfigurationFromHD(const wxString& filename, bool programS syncConfiguration.rightNewer = SyncDirection(config.get()); syncConfiguration.different = SyncDirection(config.get()); - //read find method + //read compare algorithm switch (int(config.get())) { - case CompareByTimeAndSize: + case compareByTimeAndSize: m_radioBtnSizeDate->SetValue(true); break; - case CompareByMD5: + case compareByMD5: m_radioBtnContent->SetValue(true); break; default: @@ -1200,7 +1194,10 @@ void MainDialog::readConfigurationFromHD(const wxString& filename, bool programS //read filter settings: hideFiltered = bool(config.get()); + m_checkBoxHideFilt->SetValue(hideFiltered); + filterIsActive = bool(config.get()); + updateFilterButton(); //include config.getline(bigBuffer, 10000, char(0)); @@ -1212,14 +1209,20 @@ void MainDialog::readConfigurationFromHD(const wxString& filename, bool programS useRecycleBin = bool(config.get()); + hideErrorMessages = bool(config.get()); + config.close(); } + void MainDialog::writeConfigurationToHD(const wxString& filename) { ofstream config(filename.c_str()); if (!config) - throw runtime_error(string(_("Could not write to ")) + filename.c_str()); + { + wxMessageBox(wxString(_("Could not write to ")) + "\"" + filename + "\"", _("An exception occured!"), wxOK | wxICON_ERROR); + return; + } //put filename on list of last used config files addCfgFileToHistory(filename); @@ -1236,9 +1239,9 @@ void MainDialog::writeConfigurationToHD(const wxString& filename) //write find method if (m_radioBtnSizeDate->GetValue()) - config<<char(CompareByTimeAndSize); + config<<char(compareByTimeAndSize); else if (m_radioBtnContent->GetValue()) - config<<char(CompareByMD5); + config<<char(compareByMD5); else assert (false); @@ -1272,34 +1275,39 @@ void MainDialog::writeConfigurationToHD(const wxString& filename) config<<char(useRecycleBin); + config<<char(hideErrorMessages); + config.close(); } + void MainDialog::OnAbout(wxCommandEvent &event) { AboutDlg* aboutDlg = new AboutDlg(this); aboutDlg->ShowModal(); } + void MainDialog::OnShowHelpDialog(wxCommandEvent &event) { HelpDlg* helpDlg = new HelpDlg(this); helpDlg->ShowModal(); } + void MainDialog::OnFilterButton(wxCommandEvent &event) { //toggle filter on/off filterIsActive = !filterIsActive; + //make sure, button-appearance and "filterIsActive" are in sync. + updateFilterButton(); + if (filterIsActive) FreeFileSync::filterCurrentGridData(currentGridData, includeFilter, excludeFilter); else FreeFileSync::removeFilterOnCurrentGridData(currentGridData); writeGrid(currentGridData); - - //make sure, button-appearance and "filterIsActive" are in sync. - updateFilterButton(); } @@ -1316,11 +1324,18 @@ void MainDialog::OnHideFilteredButton(wxCommandEvent &event) void MainDialog::OnConfigureFilter(wxHyperlinkEvent &event) { + wxString beforeImage = includeFilter + wxChar(0) + excludeFilter; + FilterDlg* filterDlg = new FilterDlg(this); - if (filterDlg->ShowModal() == FilterDlg::OkayButtonPressed) + if (filterDlg->ShowModal() == FilterDlg::okayButtonPressed) { - if (filterIsActive) + wxString afterImage = includeFilter + wxChar(0) + excludeFilter; + + if (beforeImage != afterImage) //if filter settings are changed: set filtering to "on" { + filterIsActive = true; + updateFilterButton(); + FreeFileSync::filterCurrentGridData(currentGridData, includeFilter, excludeFilter); writeGrid(currentGridData); @@ -1444,25 +1459,25 @@ void MainDialog::OnCompare(wxCommandEvent &event) wxBeginBusyCursor(); - CompareVariant cmpVar = CompareByTimeAndSize; //assign a value to suppress compiler warning + CompareVariant cmpVar = compareByTimeAndSize; //assign a value to suppress compiler warning if (m_radioBtnSizeDate->GetValue()) - cmpVar = CompareByTimeAndSize; + cmpVar = compareByTimeAndSize; else if (m_radioBtnContent->GetValue()) - cmpVar = CompareByMD5; + cmpVar = compareByMD5; else assert (false); try { //class handling status display and error messages - CompareStatusUpdater statusUpdater(this, m_statusBar1); + CompareStatusUpdater statusUpdater(this); cmpStatusUpdaterTmp = &statusUpdater; stackObjects = 0; //unsigned int startTime3 = GetTickCount(); - FreeFileSync::getModelDiff(currentGridData, - FreeFileSync::getFormattedDirectoryName(m_directoryPanel1->GetValue()), - FreeFileSync::getFormattedDirectoryName(m_directoryPanel2->GetValue()), - cmpVar, - &statusUpdater); + FreeFileSync::startCompareProcess(currentGridData, + FreeFileSync::getFormattedDirectoryName(m_directoryPanel1->GetValue()), + FreeFileSync::getFormattedDirectoryName(m_directoryPanel2->GetValue()), + cmpVar, + &statusUpdater); //wxMessageBox(numberToWxString(unsigned(GetTickCount()) - startTime3)); @@ -1470,12 +1485,12 @@ void MainDialog::OnCompare(wxCommandEvent &event) if (filterIsActive) FreeFileSync::filterCurrentGridData(currentGridData, includeFilter, excludeFilter); - writeGrid(currentGridData); - //once compare is finished enable the sync button - m_bpButtonSync->Enable(true); + m_bpButtonSync->Enable(); m_bpButtonSync->SetFocus(); + writeGrid(currentGridData); //keep it in try/catch to not overwrite status information if compare is abortet + cmpStatusUpdaterTmp = 0; } catch (AbortThisProcess& theException) @@ -1501,38 +1516,38 @@ wxString MainDialog::evaluateCmpResult(const CompareFilesResult result, const bo if (selectedForSynchronization) switch (result) { - case FileOnLeftSideOnly: + case fileOnLeftSideOnly: return "<|"; break; - case FileOnRightSideOnly: + case fileOnRightSideOnly: return "|>"; break; - case RightFileNewer: + case rightFileNewer: return ">>"; break; - case LeftFileNewer: + case leftFileNewer: return "<<"; break; - case FilesDifferent: + case filesDifferent: return "!="; break; - case FilesEqual: + case filesEqual: return "=="; break; default: assert (false); } - else return ConstFilteredOut; + else return constFilteredOut; } void MainDialog::writeGrid(const FileCompareResult& gridData, bool useUI_GridCache) { - //unsigned int startTime = GetTickCount(); - if (!useUI_GridCache) { + //unsigned int startTime = GetTickCount(); mapFileModelToUI(currentUI_View, gridData); //update currentUI_View + //wxMessageBox(wxString("Benchmark: ") + numberToWxString(unsigned(GetTickCount()) - startTime) + " ms"); updateStatusInformation(currentUI_View); //write status information for currentUI_View } @@ -1563,8 +1578,6 @@ void MainDialog::writeGrid(const FileCompareResult& gridData, bool useUI_GridCac m_grid1->EndBatch(); m_grid2->EndBatch(); m_grid3->EndBatch(); - - //wxMessageBox(wxString("Benchmark: ") + numberToWxString(unsigned(GetTickCount()) - startTime) + " ms"); } @@ -1573,7 +1586,7 @@ void MainDialog::OnSync( wxCommandEvent& event ) //check if there are files/folders that can be synced bool nothingToSync = true; for (FileCompareResult::const_iterator i = currentGridData.begin(); i != currentGridData.end(); ++i) - if (i->cmpResult != FilesEqual) + if (i->cmpResult != filesEqual) { nothingToSync = false; break; @@ -1592,9 +1605,33 @@ void MainDialog::OnSync( wxCommandEvent& event ) clearStatusBar(); - //unsigned int startTime = GetTickCount(); - synchronizeFolders(currentGridData, syncConfiguration); - //wxMessageBox(numberToWxString(unsigned(GetTickCount()) - startTime)); + try + { + //class handling status updates and error messages + SyncStatusUpdater statusUpdater(this, hideErrorMessages); + + //start synchronization and return elements that were not sync'ed in currentGridData + + //unsigned int startTime3 = GetTickCount(); + FreeFileSync::startSynchronizationProcess(currentGridData, syncConfiguration, &statusUpdater, useRecycleBin); + //wxMessageBox(numberToWxString(unsigned(GetTickCount()) - startTime3)); + } + catch (AbortThisProcess& theException) + { //do NOT disable the sync button: user might want to try to sync the REMAINING rows + } //m_bpButtonSync->Disable(); + + + //display files that were not processed + writeGrid(currentGridData); + + if (currentGridData.size() > 0) + pushStatusInformation(_("Not all items were synchronized! Have a look at the list.")); + else + { + pushStatusInformation(_("All items have been synchronized!")); + m_bpButtonSync->Disable(); + } + wxEndBusyCursor(); } @@ -1649,26 +1686,12 @@ bool cmpString(const wxString& a, const wxString& b) } inline -bool cmpLargeInt(const wxString& a, const wxString& b) +bool cmpLargeInt(const wxULongLong& a, const wxULongLong& b) { - //if a and b not empty: - bool result = true; - wxString tmpString; - mpz_t largeTempIntegerA; - mpz_t largeTempIntegerB; - mpz_init(largeTempIntegerA); - mpz_init(largeTempIntegerB); - mpz_set_str(largeTempIntegerA, a.c_str(), 10); - //return value should be checked: if function fails, largeTempIntegerA is not changed: no issue here - mpz_set_str(largeTempIntegerB, b.c_str(), 10); - //return value should be checked: if function fails, largeTempIntegerA is not changed: no issue here if (sortAscending) - result = (mpz_cmp(largeTempIntegerA, largeTempIntegerB) < 0); // true if A < B - else - result = (mpz_cmp(largeTempIntegerA, largeTempIntegerB) > 0); // true if A > B - mpz_clear(largeTempIntegerA); - mpz_clear(largeTempIntegerB); - return result; + return (a < b); + + return (a > b); } @@ -1687,13 +1710,13 @@ bool sortGridLeft(const UI_GridLine& a, const UI_GridLine& b) ObjectType typeB = (*currentGridDataPtr)[b.linkToCurrentGridData].fileDescrLeft.objType; //presort types: first files, then directories then empty rows - if (typeA == IsNothing) + if (typeA == isNothing) return false; //empty rows always last - else if (typeB == IsNothing) + else if (typeB == isNothing) return true; //empty rows always last - else if (typeA == IsDirectory) + else if (typeA == isDirectory) return false; - else if (typeB == IsDirectory) + else if (typeB == isDirectory) return true; else //use unformatted filesizes and sort by size return cmpLargeInt((*currentGridDataPtr)[a.linkToCurrentGridData].fileDescrLeft.fileSize, (*currentGridDataPtr)[b.linkToCurrentGridData].fileDescrLeft.fileSize); @@ -1723,13 +1746,13 @@ bool sortGridRight(const UI_GridLine& a, const UI_GridLine& b) ObjectType typeB = (*currentGridDataPtr)[b.linkToCurrentGridData].fileDescrRight.objType; //presort types: first files, then directories then empty rows - if (typeA == IsNothing) + if (typeA == isNothing) return false; //empty rows always last - else if (typeB == IsNothing) + else if (typeB == isNothing) return true; //empty rows always last - else if (typeA == IsDirectory) + else if (typeA == isDirectory) return false; - else if (typeB == IsDirectory) + else if (typeB == isDirectory) return true; else //use unformatted filesizes and sort by size return cmpLargeInt((*currentGridDataPtr)[a.linkToCurrentGridData].fileDescrRight.fileSize, (*currentGridDataPtr)[b.linkToCurrentGridData].fileDescrRight.fileSize); @@ -1802,29 +1825,6 @@ void MainDialog::OnSwapDirs( wxCommandEvent& event ) } -void MainDialog::synchronizeFolders(FileCompareResult& grid, const SyncConfiguration config) -{ - try - { //class handling status updates and error messages - SyncStatusUpdater statusUpdater(this, FreeFileSync::calcTotalBytesToTransfer(grid, config).get_d()); - - //start synchronization and return elements that were errorneous in "grid" - - //unsigned int startTime3 = GetTickCount(); - FreeFileSync::startSynchronizationProcess(grid, config, &statusUpdater, useRecycleBin); - //wxMessageBox(numberToWxString(unsigned(GetTickCount()) - startTime3)); - - - //display files that couldn't be processed - writeGrid(grid); - } - catch (AbortThisProcess& theException) - { - //disable the sync button - m_bpButtonSync->Enable(false); - } -} - //this sorting method is currently NOT used bool cmpGridSmallerThan(const UI_GridLine& a, const UI_GridLine& b) { @@ -1868,7 +1868,8 @@ bool cmpGridSmallerThan(const UI_GridLine& a, const UI_GridLine& b) void MainDialog::updateStatusInformation(const UI_Grid& visibleGrid) { - stackObjects = 0; + clearStatusBar(); + unsigned int objectsOnLeftView = 0; unsigned int objectsOnRightView = 0; mpz_t filesizeLeftView, filesizeRightView, tmpInt; @@ -1876,29 +1877,24 @@ void MainDialog::updateStatusInformation(const UI_Grid& visibleGrid) mpz_init(filesizeRightView); mpz_init(tmpInt); - int returnValue = 0; - for (UI_Grid::const_iterator i = visibleGrid.begin(); i != visibleGrid.end(); i++) { const FileCompareLine& refLine = currentGridData[i->linkToCurrentGridData]; //calculate total number of bytes for each sied - if (refLine.fileDescrLeft.objType != IsNothing) + if (refLine.fileDescrLeft.objType != isNothing) { - mpz_set_ui(tmpInt, 0); - returnValue = mpz_set_str(tmpInt, refLine.fileDescrLeft.fileSize.c_str(), 10); + FreeFileSync::wxULongLongToMpz(tmpInt, refLine.fileDescrLeft.fileSize); mpz_add(filesizeLeftView, filesizeLeftView, tmpInt); - assert (returnValue == 0); objectsOnLeftView++; } - if (refLine.fileDescrRight.objType != IsNothing) + if (refLine.fileDescrRight.objType != isNothing) { - mpz_set_ui(tmpInt, 0); - returnValue = mpz_set_str(tmpInt, refLine.fileDescrRight.fileSize.c_str(), 10); + + FreeFileSync::wxULongLongToMpz(tmpInt, refLine.fileDescrRight.fileSize); mpz_add(filesizeRightView, filesizeRightView, tmpInt); - assert (returnValue == 0); objectsOnRightView++; } @@ -1906,7 +1902,7 @@ void MainDialog::updateStatusInformation(const UI_Grid& visibleGrid) //show status information on "root" level. This cannot be accomplished in writeGrid since filesizes are already formatted for display there wxString objectsViewLeft = numberToWxString(objectsOnLeftView); - GlobalFunctions::includeNumberSeparator(objectsViewLeft); + globalFunctions::includeNumberSeparator(objectsViewLeft); if (objectsOnLeftView == 1) m_statusBar1->SetStatusText(wxString(_("1 item on left, ")) + FreeFileSync::formatFilesizeToShortString(mpz_class(filesizeLeftView)), 0); else @@ -1914,9 +1910,9 @@ void MainDialog::updateStatusInformation(const UI_Grid& visibleGrid) wxString objectsTotal = numberToWxString(currentGridData.size()); - GlobalFunctions::includeNumberSeparator(objectsTotal); + globalFunctions::includeNumberSeparator(objectsTotal); wxString objectsView = numberToWxString(visibleGrid.size()); - GlobalFunctions::includeNumberSeparator(objectsView); + globalFunctions::includeNumberSeparator(objectsView); if (currentGridData.size() == 1) m_statusBar1->SetStatusText(objectsView + _(" of ") + objectsTotal + _(" row in view"), 1); else @@ -1924,7 +1920,7 @@ void MainDialog::updateStatusInformation(const UI_Grid& visibleGrid) wxString objectsViewRight = numberToWxString(objectsOnRightView); - GlobalFunctions::includeNumberSeparator(objectsViewRight); + globalFunctions::includeNumberSeparator(objectsViewRight); if (objectsOnRightView == 1) m_statusBar1->SetStatusText(wxString(_("1 item on right, ")) + FreeFileSync::formatFilesizeToShortString(mpz_class(filesizeRightView)), 2); else @@ -1949,22 +1945,22 @@ void MainDialog::mapFileModelToUI(UI_Grid& output, const FileCompareResult& file //process UI filter settings switch (i->cmpResult) { - case FileOnLeftSideOnly: + case fileOnLeftSideOnly: if (!leftOnlyFilesActive) continue; break; - case FileOnRightSideOnly: + case fileOnRightSideOnly: if (!rightOnlyFilesActive) continue; break; - case RightFileNewer: + case rightFileNewer: if (!rightNewerFilesActive) continue; break; - case LeftFileNewer: + case leftFileNewer: if (!leftNewerFilesActive) continue; break; - case FilesDifferent: + case filesDifferent: if (!differentFilesActive) continue; break; - case FilesEqual: + case filesEqual: if (!equalFilesActive) continue; break; default: @@ -1975,7 +1971,7 @@ void MainDialog::mapFileModelToUI(UI_Grid& output, const FileCompareResult& file if (hideFiltered && !i->selectedForSynchronization) continue; - if (i->fileDescrLeft.objType == IsDirectory) + if (i->fileDescrLeft.objType == isDirectory) { gridline.leftFilename = wxEmptyString; gridline.leftRelativePath = i->fileDescrLeft.relFilename; @@ -1983,16 +1979,19 @@ void MainDialog::mapFileModelToUI(UI_Grid& output, const FileCompareResult& file } else { - gridline.leftFilename = i->fileDescrLeft.relFilename.AfterLast(FileNameSeparator); - gridline.leftRelativePath = i->fileDescrLeft.relFilename.BeforeLast(FileNameSeparator); - gridline.leftSize = GlobalFunctions::includeNumberSeparator(fileSize = i->fileDescrLeft.fileSize); + gridline.leftFilename = i->fileDescrLeft.relFilename.AfterLast(GlobalResources::fileNameSeparator); + gridline.leftRelativePath = i->fileDescrLeft.relFilename.BeforeLast(GlobalResources::fileNameSeparator); + if (i->fileDescrLeft.fileSize != 0) + gridline.leftSize = globalFunctions::includeNumberSeparator(fileSize = i->fileDescrLeft.fileSize.ToString()); + else + gridline.leftSize = ""; } - gridline.leftDate = i->fileDescrLeft.lastWriteTime; + gridline.leftDate = i->fileDescrLeft.lastWriteTime; - gridline.cmpResult = evaluateCmpResult(i->cmpResult, i->selectedForSynchronization); + gridline.cmpResult = evaluateCmpResult(i->cmpResult, i->selectedForSynchronization); gridline.linkToCurrentGridData = currentRow; - if (i->fileDescrRight.objType == IsDirectory) + if (i->fileDescrRight.objType == isDirectory) { gridline.rightFilename = wxEmptyString; gridline.rightRelativePath = i->fileDescrRight.relFilename; @@ -2000,11 +1999,14 @@ void MainDialog::mapFileModelToUI(UI_Grid& output, const FileCompareResult& file } else { - gridline.rightFilename = i->fileDescrRight.relFilename.AfterLast(FileNameSeparator); - gridline.rightRelativePath = i->fileDescrRight.relFilename.BeforeLast(FileNameSeparator); - gridline.rightSize = GlobalFunctions::includeNumberSeparator(fileSize = i->fileDescrRight.fileSize); + gridline.rightFilename = i->fileDescrRight.relFilename.AfterLast(GlobalResources::fileNameSeparator); + gridline.rightRelativePath = i->fileDescrRight.relFilename.BeforeLast(GlobalResources::fileNameSeparator); + if (i->fileDescrRight.fileSize != 0) + gridline.rightSize = globalFunctions::includeNumberSeparator(fileSize = i->fileDescrRight.fileSize.ToString()); + else + gridline.rightSize = ""; } - gridline.rightDate = i->fileDescrRight.lastWriteTime; + gridline.rightDate = i->fileDescrRight.lastWriteTime; output.push_back(gridline); } @@ -2020,7 +2022,6 @@ void updateUI_Now() //process UI events and prevent application from "not responding" -> NO performance issue! wxTheApp->Yield(); - // while (wxTheApp->Pending()) // wxTheApp->Dispatch(); } @@ -2032,7 +2033,7 @@ bool updateUI_IsAllowed() wxLongLong newExec = wxGetLocalTimeMillis(); - if (newExec - lastExec >= 100) //perform ui updates not more often than necessary, 100 seems to be a good value with only a minimal performance loss + if (newExec - lastExec >= uiUpdateInterval) //perform ui updates not more often than necessary { lastExec = newExec; return true; @@ -2043,11 +2044,10 @@ bool updateUI_IsAllowed() //######################################################################################################## -CompareStatusUpdater::CompareStatusUpdater(MainDialog* dlg, wxStatusBar* mainWindowBar) : +CompareStatusUpdater::CompareStatusUpdater(MainDialog* dlg) : mainDialog(dlg), - statusBar(mainWindowBar), suppressUI_Errormessages(false), - numberOfScannedObjects(0) + currentProcess(-1) { //prevent user input during "compare", do not disable maindialog since abort-button would also be disabled //it's not nice, but works - even has the advantage that certain actions are still possible: exit, about.. @@ -2071,13 +2071,19 @@ CompareStatusUpdater::CompareStatusUpdater(MainDialog* dlg, wxStatusBar* mainWin mainDialog->m_panel3->Disable(); mainDialog->m_bpButton201->Disable(); mainDialog->m_choiceLoad->Disable(); + mainDialog->m_bpButton10->Disable(); //show abort button mainDialog->m_buttonAbort->Show(); mainDialog->m_bpButtonCompare->Hide(); + mainDialog->m_buttonAbort->SetFocus(); + + //display status panel during compare + statusPanel = new CompareStatus(mainDialog); + mainDialog->bSizer1->Insert(1, statusPanel, 0, wxEXPAND | wxALL, 5 ); + updateUI_Now(); mainDialog->Layout(); mainDialog->Refresh(); - mainDialog->m_buttonAbort->SetFocus(); } CompareStatusUpdater::~CompareStatusUpdater() @@ -2103,31 +2109,56 @@ CompareStatusUpdater::~CompareStatusUpdater() mainDialog->m_panel3->Enable(); mainDialog->m_bpButton201->Enable(); mainDialog->m_choiceLoad->Enable(); + mainDialog->m_bpButton10->Enable(); if (abortionRequested) mainDialog->pushStatusInformation(_("Operation aborted!")); mainDialog->m_buttonAbort->Hide(); mainDialog->m_bpButtonCompare->Show(); + + //remove status panel from main window + mainDialog->bSizer1->Detach(statusPanel); + statusPanel->Destroy(); + updateUI_Now(); mainDialog->Layout(); mainDialog->Refresh(); } -void CompareStatusUpdater::updateStatus(const wxString& text) +inline +void CompareStatusUpdater::updateStatusText(const wxString& text) +{ + statusPanel->setStatusText_NoUpdate(text); +} + + +void CompareStatusUpdater::initNewProcess(int objectsTotal, double dataTotal, int processID) { - //not relevant for "compare"; it's sufficient to display the number of scanned files/folders + currentProcess = processID; + + if (currentProcess == FreeFileSync::scanningFilesProcess) + ; + else if (currentProcess == FreeFileSync::calcMD5Process) + statusPanel->resetMD5Gauge(objectsTotal, dataTotal); + else assert(false); } + inline -void CompareStatusUpdater::updateProgressIndicator(double number) +void CompareStatusUpdater::updateProcessedData(int objectsProcessed, double dataProcessed) { - numberOfScannedObjects+= int(number); //conversion is harmless, since number == 1 in case of "compare" + if (currentProcess == FreeFileSync::scanningFilesProcess) + statusPanel->incScannedFiles_NoUpdate(objectsProcessed); + else if (currentProcess == FreeFileSync::calcMD5Process) + statusPanel->incProcessedMD5Data_NoUpdate(objectsProcessed, dataProcessed); + else assert(false); } + int CompareStatusUpdater::reportError(const wxString& text) { if (suppressUI_Errormessages) - return StatusUpdater::Continue; + return StatusUpdater::continueNext; wxString errorMessage = text + _("\n\nContinue with next object, retry or abort comparison?"); @@ -2138,11 +2169,11 @@ int CompareStatusUpdater::reportError(const wxString& text) switch (rv) { - case ErrorDlg::ContinueButtonPressed: - return StatusUpdater::Continue; - case ErrorDlg::RetryButtonPressed: - return StatusUpdater::Retry; - case ErrorDlg::AbortButtonPressed: + case ErrorDlg::continueButtonPressed: + return StatusUpdater::continueNext; + case ErrorDlg::retryButtonPressed: + return StatusUpdater::retry; + case ErrorDlg::abortButtonPressed: { abortionRequested = true; throw AbortThisProcess(); @@ -2151,7 +2182,7 @@ int CompareStatusUpdater::reportError(const wxString& text) assert (false); } - return StatusUpdater::Continue; //dummy return value + return StatusUpdater::continueNext; //dummy return value } @@ -2161,22 +2192,20 @@ void CompareStatusUpdater::triggerUI_Refresh() if (abortionRequested) throw AbortThisProcess(); //abort can be triggered by syncStatusFrame if (updateUI_IsAllowed()) //test if specific time span between ui updates is over - { - statusBar->SetStatusText(wxString(_("Scanning files/folders: ")) + numberToWxString(numberOfScannedObjects), 1); - updateUI_Now(); - } + statusPanel->updateStatusPanelNow(); } //######################################################################################################## -SyncStatusUpdater::SyncStatusUpdater(wxWindow* dlg, double gaugeTotalElements) : - suppressUI_Errormessages(false) +SyncStatusUpdater::SyncStatusUpdater(wxWindow* dlg, bool hideErrorMessages) : + suppressUI_Errormessages(hideErrorMessages) { - syncStatusFrame = new SyncStatus(this, gaugeTotalElements, dlg); + syncStatusFrame = new SyncStatus(this, dlg); syncStatusFrame->Show(); updateUI_Now(); } + SyncStatusUpdater::~SyncStatusUpdater() { if (abortionRequested) @@ -2192,23 +2221,38 @@ SyncStatusUpdater::~SyncStatusUpdater() result = wxString(_("Warning: Synchronization failed for ")) + numberToWxString(failedItems) + _(" item(s):\n\n"); for (unsigned int j = 0; j < failedItems; ++j) result+= unhandledErrors[j] + "\n"; + result+= "\n"; } + + if (failedItems) + result+= _("Not all items have been synchronized! You may try to synchronize the remaining items again (WITHOUT having to re-compare)!"); + else if (abortionRequested) + result+= _("Synchronization aborted: You may try to synchronize remaining items again (WITHOUT having to re-compare)!"); + syncStatusFrame->setStatusText_NoUpdate(result); syncStatusFrame->updateStatusDialogNow(); } inline -void SyncStatusUpdater::updateStatus(const wxString& text) +void SyncStatusUpdater::updateStatusText(const wxString& text) { syncStatusFrame->setStatusText_NoUpdate(text); } +void SyncStatusUpdater::initNewProcess(int objectsTotal, double dataTotal, int processID) +{ + assert (processID == FreeFileSync::synchronizeFilesProcess); + + syncStatusFrame->resetGauge(objectsTotal, dataTotal); +} + + inline -void SyncStatusUpdater::updateProgressIndicator(double number) +void SyncStatusUpdater::updateProcessedData(int objectsProcessed, double dataProcessed) { - syncStatusFrame->incProgressIndicator_NoUpdate(number); + syncStatusFrame->incProgressIndicator_NoUpdate(objectsProcessed, dataProcessed); } @@ -2217,7 +2261,7 @@ int SyncStatusUpdater::reportError(const wxString& text) if (suppressUI_Errormessages) { unhandledErrors.Add(text); - return StatusUpdater::Continue; + return StatusUpdater::continueNext; } wxString errorMessage = text + _("\n\nContinue with next object, retry or abort synchronization?"); @@ -2229,12 +2273,12 @@ int SyncStatusUpdater::reportError(const wxString& text) switch (rv) { - case ErrorDlg::ContinueButtonPressed: + case ErrorDlg::continueButtonPressed: unhandledErrors.Add(text); - return StatusUpdater::Continue; - case ErrorDlg::RetryButtonPressed: - return StatusUpdater::Retry; - case ErrorDlg::AbortButtonPressed: + return StatusUpdater::continueNext; + case ErrorDlg::retryButtonPressed: + return StatusUpdater::retry; + case ErrorDlg::abortButtonPressed: { unhandledErrors.Add(text); abortionRequested = true; @@ -2244,7 +2288,7 @@ int SyncStatusUpdater::reportError(const wxString& text) assert (false); } - return StatusUpdater::Continue; //dummy return value + return StatusUpdater::continueNext; //dummy return value } @@ -2254,9 +2298,8 @@ void SyncStatusUpdater::triggerUI_Refresh() throw AbortThisProcess(); //abort can be triggered by syncStatusFrame if (updateUI_IsAllowed()) //test if specific time span between ui updates is over - { syncStatusFrame->updateStatusDialogNow(); - } } + diff --git a/ui/MainDialog.h b/ui/MainDialog.h index 7803cfd6..1f014502 100644 --- a/ui/MainDialog.h +++ b/ui/MainDialog.h @@ -22,7 +22,7 @@ using namespace std; -const wxString ConstFilteredOut = "(-)"; +const wxString constFilteredOut = "(-)"; struct UI_GridLine { @@ -42,9 +42,8 @@ struct UI_GridLine }; typedef vector<UI_GridLine> UI_Grid; -bool updateUI_IsAllowed(); //test if a specific amount of time is over -void updateUI_Now(); //do the updating - +bool updateUI_IsAllowed(); //test if a specific amount of time is over +void updateUI_Now(); //do the updating extern int leadingPanel; @@ -66,17 +65,13 @@ private: void readConfigurationFromHD(const wxString& filename, bool programStartup = false); void writeConfigurationToHD(const wxString& filename); - - void loadResourceFiles(); - void unloadResourceFiles(); + void loadDefaultConfiguration(); void updateViewFilterButtons(); void updateFilterButton(); void addCfgFileToHistory(const wxString& filename); - void synchronizeFolders(FileCompareResult& grid, const SyncConfiguration config); - static wxString evaluateCmpResult(const CompareFilesResult result, const bool selectedForSynchronization); //main method for putting gridData on UI: maps data respecting current view settings @@ -84,7 +79,8 @@ private: void mapFileModelToUI(UI_Grid& output, const FileCompareResult& fileCmpResult); void updateStatusInformation(const UI_Grid& output); - void filterRangeManual(int begin, int end, int leadingRow); + void filterRangeManual(const set<int>& rowsToFilterOnUI_View, int leadingRow); + void deleteFilesOnGrid(wxGrid* grid); //work to be done in idle time @@ -131,7 +127,7 @@ private: void OnLoadConfiguration( wxCommandEvent& event); void OnChoiceKeyEvent( wxKeyEvent& event ); -void onResizeMainWindow(wxEvent& event); + void onResizeMainWindow(wxEvent& event); void OnAbortCompare( wxCommandEvent& event); void OnFilterButton( wxCommandEvent& event); void OnHideFilteredButton( wxCommandEvent& event); @@ -178,7 +174,8 @@ void onResizeMainWindow(wxEvent& event); int posYNotMaximized; //other options - bool useRecycleBin; //use Recycle bin when deleting or overwriting files while synchronizing + bool useRecycleBin; //use Recycle bin when deleting or overwriting files while synchronizing + bool hideErrorMessages; //hides error messages during synchronization //*********************************************** @@ -229,37 +226,39 @@ private: //###################################################################################### //classes handling sync and compare error as well as status information +class CompareStatus; +class SyncStatus; class CompareStatusUpdater : public StatusUpdater { public: - CompareStatusUpdater(MainDialog* dlg, wxStatusBar* mainWindowBar); + CompareStatusUpdater(MainDialog* dlg); ~CompareStatusUpdater(); - void updateStatus(const wxString& text); - void updateProgressIndicator(double number); + void updateStatusText(const wxString& text); + void initNewProcess(int objectsTotal, double dataTotal, int processID); + void updateProcessedData(int objectsProcessed, double dataProcessed); int reportError(const wxString& text); void triggerUI_Refresh(); private: MainDialog* mainDialog; - wxStatusBar* statusBar; bool suppressUI_Errormessages; - - unsigned int numberOfScannedObjects; + CompareStatus* statusPanel; + int currentProcess; }; -class SyncStatus; class SyncStatusUpdater : public StatusUpdater { public: - SyncStatusUpdater(wxWindow* dlg, double gaugeTotalElements); + SyncStatusUpdater(wxWindow* dlg, bool hideErrorMessages); ~SyncStatusUpdater(); - void updateStatus(const wxString& text); - void updateProgressIndicator(double number); + void updateStatusText(const wxString& text); + void initNewProcess(int objectsTotal, double dataTotal, int processID); + void updateProcessedData(int objectsProcessed, double dataProcessed); int reportError(const wxString& text); void triggerUI_Refresh(); diff --git a/ui/Resources.cpp b/ui/Resources.cpp index a92c73f7..3bbd1523 100644 --- a/ui/Resources.cpp +++ b/ui/Resources.cpp @@ -4,6 +4,18 @@ #include <wx/image.h> #include <stdexcept> //for std::runtime_error +#ifdef FFS_WIN +wxChar GlobalResources::fileNameSeparator = '\\'; +#endif // FFS_WIN + +#ifdef FFS_LINUX +wxChar GlobalResources::fileNameSeparator = '/'; +#endif // FFS_LINUX + +const wxChar* GlobalResources::floatingPointSeparator = ""; +const wxChar* GlobalResources::numberSeparator = ""; + + map<wxString, wxBitmap*> GlobalResources::bitmapResource; wxBitmap* GlobalResources::bitmapLeftArrow = 0; @@ -38,13 +50,21 @@ wxBitmap* GlobalResources::bitmapFilterOff = 0; wxBitmap* GlobalResources::bitmapWarning = 0; wxBitmap* GlobalResources::bitmapSmallUp = 0; wxBitmap* GlobalResources::bitmapSmallDown = 0; -wxBitmap* GlobalResources::bitmapSave = 0; +wxBitmap* GlobalResources::bitmapSave = 0; +wxBitmap* GlobalResources::bitmapFFS = 0; +wxBitmap* GlobalResources::bitmapDeleteFile = 0; wxAnimation* GlobalResources::animationMoney = 0; +wxAnimation* GlobalResources::animationSync = 0; void GlobalResources::loadResourceFiles() { + + floatingPointSeparator = _("."); + numberSeparator = _(","); + + //map, allocate and initialize pictures bitmapResource["left arrow.png"] = (bitmapLeftArrow = new wxBitmap(wxNullBitmap)); bitmapResource["start sync.png"] = (bitmapStartSync = new wxBitmap(wxNullBitmap)); @@ -79,8 +99,11 @@ void GlobalResources::loadResourceFiles() bitmapResource["small arrow up.png"] = (bitmapSmallUp = new wxBitmap(wxNullBitmap)); bitmapResource["small arrow down.png"] = (bitmapSmallDown = new wxBitmap(wxNullBitmap)); bitmapResource["save.png"] = (bitmapSave = new wxBitmap(wxNullBitmap)); + bitmapResource["FFS.png"] = (bitmapFFS = new wxBitmap(wxNullBitmap)); + bitmapResource["deleteFile.png"] = (bitmapDeleteFile = new wxBitmap(wxNullBitmap)); animationMoney = new wxAnimation(wxNullAnimation); + animationSync = new wxAnimation(wxNullAnimation); wxFileInputStream input("Resources.dat"); if (!input.IsOk()) throw runtime_error(_("Unable to load Resources.dat!")); @@ -99,6 +122,7 @@ void GlobalResources::loadResourceFiles() } animationMoney->LoadFile("Resources.a01"); + animationSync->LoadFile("Resources.a02"); } @@ -110,4 +134,5 @@ void GlobalResources::unloadResourceFiles() //free other resources delete animationMoney; + delete animationSync; } diff --git a/ui/Resources.h b/ui/Resources.h index fa7eaf29..e2ae1303 100644 --- a/ui/Resources.h +++ b/ui/Resources.h @@ -11,10 +11,15 @@ using namespace std; class GlobalResources { public: - static void loadResourceFiles(); static void unloadResourceFiles(); + //language dependent global variables: need to be initialized by CustomLocale on program startup + static wxChar fileNameSeparator; + static const wxChar* floatingPointSeparator; + static const wxChar* numberSeparator; + + //image resource objects static wxBitmap* bitmapLeftArrow; static wxBitmap* bitmapStartSync; static wxBitmap* bitmapRightArrow; @@ -48,8 +53,11 @@ public: static wxBitmap* bitmapSmallUp; static wxBitmap* bitmapSmallDown; static wxBitmap* bitmapSave; + static wxBitmap* bitmapFFS; + static wxBitmap* bitmapDeleteFile; static wxAnimation* animationMoney; + static wxAnimation* animationSync; private: //resource mapping diff --git a/ui/SmallDialogs.cpp b/ui/SmallDialogs.cpp index cf21bc22..77fc8a59 100644 --- a/ui/SmallDialogs.cpp +++ b/ui/SmallDialogs.cpp @@ -1,10 +1,13 @@ #include "smallDialogs.h" #include "../library/globalFunctions.h" +using namespace globalFunctions; + AboutDlg::AboutDlg(MainDialog* window) : AboutDlgGenerated(window) { m_bitmap9->SetBitmap(*GlobalResources::bitmapWebsite); m_bitmap10->SetBitmap(*GlobalResources::bitmapEmail); + m_bitmap11->SetBitmap(*GlobalResources::bitmapFFS); m_animationControl1->SetAnimation(*GlobalResources::animationMoney); m_animationControl1->Play(); @@ -79,7 +82,7 @@ void FilterDlg::OnOK(wxCommandEvent& event) mainDialog->excludeFilter = m_textCtrlExclude->GetValue(); //when leaving dialog: filter and redraw grid, if filter is active - EndModal(OkayButtonPressed); + EndModal(okayButtonPressed); } @@ -104,6 +107,7 @@ DeleteDialog::DeleteDialog(const wxString& headerText, const wxString& messageTe { m_staticTextHeader->SetLabel(headerText); m_textCtrlMessage->SetValue(messageText); + m_bitmap12->SetBitmap(*GlobalResources::bitmapDeleteFile); m_buttonOK->SetFocus(); } @@ -113,17 +117,17 @@ DeleteDialog::~DeleteDialog() {} void DeleteDialog::OnOK(wxCommandEvent& event) { - EndModal(OkayButtonPressed); + EndModal(okayButtonPressed); } void DeleteDialog::OnCancel(wxCommandEvent& event) { - EndModal(CancelButtonPressed); + EndModal(cancelButtonPressed); } void DeleteDialog::OnClose(wxCloseEvent& event) { - EndModal(CancelButtonPressed); + EndModal(cancelButtonPressed); } //######################################################################################## @@ -140,49 +144,58 @@ ErrorDlg::ErrorDlg(const wxString messageText, bool& suppressErrormessages) : ErrorDlg::~ErrorDlg() {} + void ErrorDlg::OnClose(wxCloseEvent& event) { //suppressErrors = m_checkBoxSuppress->GetValue(); -> not needed here - EndModal(AbortButtonPressed); + EndModal(abortButtonPressed); } + void ErrorDlg::OnContinue(wxCommandEvent& event) { suppressErrors = m_checkBoxSuppress->GetValue(); - EndModal(ContinueButtonPressed); + EndModal(continueButtonPressed); } + void ErrorDlg::OnRetry(wxCommandEvent& event) { //suppressErrors = m_checkBoxSuppress->GetValue(); -> not needed here - EndModal(RetryButtonPressed); + EndModal(retryButtonPressed); } + void ErrorDlg::OnAbort(wxCommandEvent& event) { //suppressErrors = m_checkBoxSuppress->GetValue(); -> not needed here - EndModal(AbortButtonPressed); + EndModal(abortButtonPressed); } //######################################################################################## -SyncStatus::SyncStatus(StatusUpdater* updater, double gaugeTotalElements, wxWindow* parentWindow) : +SyncStatus::SyncStatus(StatusUpdater* updater, wxWindow* parentWindow) : SyncStatusGenerated(parentWindow), currentStatusUpdater(updater), windowToDis(parentWindow), currentProcessIsRunning(true), - numberOfProcessedObjects(0) + totalData(0), + currentData(0), + scalingFactor(0), + currentObjects(0), + totalObjects(0) { + m_animationControl1->SetAnimation(*GlobalResources::animationSync); + m_animationControl1->Play(); + //initialize gauge m_gauge1->SetRange(50000); m_gauge1->SetValue(0); - resetGauge(gaugeTotalElements); - m_buttonAbort->SetFocus(); if (windowToDis) //disable (main) window while this status dialog is shown - windowToDis->Enable(false); + windowToDis->Disable(); } @@ -190,28 +203,31 @@ SyncStatus::~SyncStatus() { if (windowToDis) { - windowToDis->Enable(true); + windowToDis->Enable(); windowToDis->Raise(); } } -void SyncStatus::resetGauge(double totalNrOfElements) +void SyncStatus::resetGauge(int totalObjectsToProcess, double totalDataToProcess) { - currentElements = 0; - totalElements = totalNrOfElements; + currentData = 0; + totalData = totalDataToProcess; + + currentObjects = 0; + totalObjects = totalObjectsToProcess; - if (totalElements != 0) - scalingFactor = 50000 / totalElements; //let's normalize to 50000 + if (totalData != 0) + scalingFactor = 50000 / totalData; //let's normalize to 50000 else scalingFactor = 0; } -void SyncStatus::incProgressIndicator_NoUpdate(double number) +void SyncStatus::incProgressIndicator_NoUpdate(int objectsProcessed, double dataProcessed) { - currentElements+= number; - numberOfProcessedObjects++; + currentData+= dataProcessed; + currentObjects+= objectsProcessed; } @@ -224,19 +240,17 @@ void SyncStatus::setStatusText_NoUpdate(const wxString& text) void SyncStatus::updateStatusDialogNow() { //progress indicator - m_gauge1->SetValue(int(currentElements * scalingFactor)); + m_gauge1->SetValue(int(currentData * scalingFactor)); //status text m_textCtrlInfo->SetValue(currentStatusText); - //processed objects - m_staticTextProcessedObj->SetLabel(GlobalFunctions::numberToWxString(numberOfProcessedObjects)); + //remaining objects + m_staticTextRemainingObj->SetLabel(numberToWxString(totalObjects - currentObjects)); //remaining bytes left for copy - const wxString remainingBytes = - FreeFileSync::formatFilesizeToShortString(mpz_class(currentElements)) + "/" + - FreeFileSync::formatFilesizeToShortString(mpz_class(totalElements)); - m_staticTextBytesCopied->SetLabel(remainingBytes); + const wxString remainingBytes = FreeFileSync::formatFilesizeToShortString(mpz_class(totalData - currentData)); + m_staticTextDataRemaining->SetLabel(remainingBytes); //do the ui update updateUI_Now(); @@ -252,6 +266,8 @@ void SyncStatus::processHasFinished(const wxString& finalStatusText) //essential m_buttonOK->Show(); m_buttonOK->SetFocus(); + m_animationControl1->Stop(); + updateStatusDialogNow(); //keep this sequence to avoid display distortion, if e.g. only 1 item is sync'ed Layout(); // } @@ -271,6 +287,82 @@ void SyncStatus::OnAbort(wxCommandEvent& event) void SyncStatus::OnClose(wxCloseEvent& event) { - if (!currentProcessIsRunning) Destroy(); + if (currentProcessIsRunning) currentStatusUpdater->requestAbortion(); + else + Destroy(); } +//######################################################################################## + + +CompareStatus::CompareStatus(wxWindow* parentWindow) : + CompareStatusGenerated(parentWindow), + scannedFiles(0), + totalMD5Data(0), + currentMD5Data(0), + scalingFactorMD5(0), + currentMD5Objects(0), + totalMD5Objects(0) +{ + //initialize gauge + m_gauge2->SetRange(50000); + m_gauge2->SetValue(0); +} + +CompareStatus::~CompareStatus() {} + + +void CompareStatus::resetMD5Gauge(int totalMD5ObjectsToProcess, double totalMD5DataToProcess) +{ + currentMD5Data = 0; + totalMD5Data = totalMD5DataToProcess; + + currentMD5Objects = 0; + totalMD5Objects = totalMD5ObjectsToProcess; + + if (totalMD5Data != 0) + scalingFactorMD5 = 50000 / totalMD5Data; //let's normalize to 50000 + else + scalingFactorMD5 = 0; +} + + +void CompareStatus::incScannedFiles_NoUpdate(int number) +{ + scannedFiles+= number; +} + + +void CompareStatus::incProcessedMD5Data_NoUpdate(int objectsProcessed, double dataProcessed) +{ + currentMD5Data+= dataProcessed; + currentMD5Objects+= objectsProcessed; +} + + +void CompareStatus::setStatusText_NoUpdate(const wxString& text) +{ + currentStatusText = text; +} + + +void CompareStatus::updateStatusPanelNow() +{ + //status texts + m_textCtrlFilename->SetValue(currentStatusText); + + m_staticTextScanned->SetLabel(numberToWxString(scannedFiles)); + + //progress indicator for MD5 + m_gauge2->SetValue(int(currentMD5Data * scalingFactorMD5)); + + //remaining MD5 objects + m_staticTextFilesToCompare->SetLabel(numberToWxString(totalMD5Objects - currentMD5Objects)); + + //remaining bytes left for MD5 calculation + const wxString remainingBytes = FreeFileSync::formatFilesizeToShortString(mpz_class(totalMD5Data - currentMD5Data)); + m_staticTextDataToCompare->SetLabel(remainingBytes); + + //do the ui update + updateUI_Now(); +} diff --git a/ui/SmallDialogs.h b/ui/SmallDialogs.h index c41ca2b8..9ae3ae88 100644 --- a/ui/SmallDialogs.h +++ b/ui/SmallDialogs.h @@ -35,7 +35,7 @@ public: FilterDlg(MainDialog* window); ~FilterDlg(); - static const int OkayButtonPressed = 25; + static const int okayButtonPressed = 25; private: void OnDefault(wxCommandEvent& event); @@ -53,8 +53,8 @@ public: DeleteDialog(const wxString& headerText, const wxString& messageText, wxWindow* main); ~DeleteDialog(); - static const int OkayButtonPressed = 35; - static const int CancelButtonPressed = 45; + static const int okayButtonPressed = 35; + static const int cancelButtonPressed = 45; private: void OnOK(wxCommandEvent& event); @@ -69,9 +69,9 @@ public: ErrorDlg(const wxString messageText, bool& suppressErrormessages); ~ErrorDlg(); - static const int ContinueButtonPressed = 35; - static const int RetryButtonPressed = 45; - static const int AbortButtonPressed = 55; + static const int continueButtonPressed = 35; + static const int retryButtonPressed = 45; + static const int abortButtonPressed = 55; private: void OnClose(wxCloseEvent& event); @@ -86,11 +86,11 @@ private: class SyncStatus : public SyncStatusGenerated { public: - SyncStatus(StatusUpdater* updater, double gaugeTotalElements, wxWindow* parentWindow = 0); + SyncStatus(StatusUpdater* updater, wxWindow* parentWindow = 0); ~SyncStatus(); - void resetGauge(double totalNrOfElements); - void incProgressIndicator_NoUpdate(double number); + void resetGauge(int totalObjectsToProcess, double totalDataToProcess); + void incProgressIndicator_NoUpdate(int objectsProcessed, double dataProcessed); void setStatusText_NoUpdate(const wxString& text); void updateStatusDialogNow(); @@ -106,12 +106,39 @@ private: bool currentProcessIsRunning; //gauge variables - double totalElements; //each element represents one byte for proper progress indicator scaling - double currentElements; - double scalingFactor; //nr of elements has to be normalized to smaller nr. because of range of int limitation + double totalData; //each data element represents one byte for proper progress indicator scaling + double currentData; + double scalingFactor; //nr of elements has to be normalized to smaller nr. because of range of int limitation + int currentObjects; //each object represents a file or directory processed + int totalObjects; wxString currentStatusText; - unsigned int numberOfProcessedObjects; +}; + + +class CompareStatus : public CompareStatusGenerated +{ +public: + CompareStatus(wxWindow* parentWindow); + ~CompareStatus(); + + void resetMD5Gauge(int totalMD5ObjectsToProcess, double totalMD5DataToProcess); + void incScannedFiles_NoUpdate(int number); + void incProcessedMD5Data_NoUpdate(int objectsProcessed, double dataProcessed); + void setStatusText_NoUpdate(const wxString& text); + void updateStatusPanelNow(); + +private: + //status variables + unsigned int scannedFiles; + wxString currentStatusText; + + //gauge variables + double totalMD5Data; //each data element represents one byte for proper progress indicator scaling + double currentMD5Data; + double scalingFactorMD5; //nr of elements has to be normalized to smaller nr. because of range of int limitation + int currentMD5Objects; //each object represents a file or directory processed + int totalMD5Objects; }; diff --git a/ui/SyncDialog.cpp b/ui/SyncDialog.cpp index 1f2804ae..a40da108 100644 --- a/ui/SyncDialog.cpp +++ b/ui/SyncDialog.cpp @@ -8,6 +8,7 @@ SyncDialog::SyncDialog(MainDialog* window) localSyncConfiguration = mainDialog->syncConfiguration; m_checkBoxUseRecycler->SetValue(mainDialog->useRecycleBin); + m_checkBoxHideErrors->SetValue(mainDialog->hideErrorMessages); //set icons for this dialog m_bpButton18->SetBitmapLabel(*GlobalResources::bitmapStartSync); @@ -21,18 +22,18 @@ SyncDialog::SyncDialog(MainDialog* window) updateConfigIcons(); //set radiobutton - if (localSyncConfiguration.exLeftSideOnly == SyncDirRight && - localSyncConfiguration.exRightSideOnly == SyncDirRight && - localSyncConfiguration.leftNewer == SyncDirRight && - localSyncConfiguration.rightNewer == SyncDirRight && - localSyncConfiguration.different == SyncDirRight) + if (localSyncConfiguration.exLeftSideOnly == syncDirRight && + localSyncConfiguration.exRightSideOnly == syncDirRight && + localSyncConfiguration.leftNewer == syncDirRight && + localSyncConfiguration.rightNewer == syncDirRight && + localSyncConfiguration.different == syncDirRight) m_radioBtn1->SetValue(true); //one way -> - else if (localSyncConfiguration.exLeftSideOnly == SyncDirRight && - localSyncConfiguration.exRightSideOnly == SyncDirLeft && - localSyncConfiguration.leftNewer == SyncDirRight && - localSyncConfiguration.rightNewer == SyncDirLeft && - localSyncConfiguration.different == SyncDirNone) + else if (localSyncConfiguration.exLeftSideOnly == syncDirRight && + localSyncConfiguration.exRightSideOnly == syncDirLeft && + localSyncConfiguration.leftNewer == syncDirRight && + localSyncConfiguration.rightNewer == syncDirLeft && + localSyncConfiguration.different == syncDirNone) m_radioBtn2->SetValue(true); //two way <-> else @@ -46,88 +47,97 @@ SyncDialog::~SyncDialog() {} void SyncDialog::updateConfigIcons() { - if (localSyncConfiguration.exLeftSideOnly == SyncDirRight) + if (localSyncConfiguration.exLeftSideOnly == syncDirRight) { m_bpButton5->SetBitmapLabel(*GlobalResources::bitmapRightArrow); m_bpButton5->SetToolTip(_("Copy from left to right")); } - else if (localSyncConfiguration.exLeftSideOnly == SyncDirLeft) + else if (localSyncConfiguration.exLeftSideOnly == syncDirLeft) { m_bpButton5->SetBitmapLabel(*GlobalResources::bitmapDelete); m_bpButton5->SetToolTip(_("Delete files existing on left side only")); } - else if (localSyncConfiguration.exLeftSideOnly == SyncDirNone) + else if (localSyncConfiguration.exLeftSideOnly == syncDirNone) { m_bpButton5->SetBitmapLabel(wxNullBitmap); m_bpButton5->SetToolTip(_("Do nothing")); } - if (localSyncConfiguration.exRightSideOnly == SyncDirRight) + if (localSyncConfiguration.exRightSideOnly == syncDirRight) { m_bpButton6->SetBitmapLabel(*GlobalResources::bitmapDelete); m_bpButton6->SetToolTip(_("Delete files existing on right side only")); } - else if (localSyncConfiguration.exRightSideOnly == SyncDirLeft) + else if (localSyncConfiguration.exRightSideOnly == syncDirLeft) { m_bpButton6->SetBitmapLabel(*GlobalResources::bitmapLeftArrow); m_bpButton6->SetToolTip(_("Copy from right to left")); } - else if (localSyncConfiguration.exRightSideOnly == SyncDirNone) + else if (localSyncConfiguration.exRightSideOnly == syncDirNone) { m_bpButton6->SetBitmapLabel(wxNullBitmap); m_bpButton6->SetToolTip(_("Do nothing")); } - if (localSyncConfiguration.leftNewer == SyncDirRight) + if (localSyncConfiguration.leftNewer == syncDirRight) { m_bpButton7->SetBitmapLabel(*GlobalResources::bitmapRightArrow); m_bpButton7->SetToolTip(_("Copy from left to right overwriting")); } - else if (localSyncConfiguration.leftNewer == SyncDirLeft) + else if (localSyncConfiguration.leftNewer == syncDirLeft) { m_bpButton7->SetBitmapLabel(*GlobalResources::bitmapLeftArrow); m_bpButton7->SetToolTip(_("Copy from right to left overwriting")); } - else if (localSyncConfiguration.leftNewer == SyncDirNone) + else if (localSyncConfiguration.leftNewer == syncDirNone) { m_bpButton7->SetBitmapLabel(wxNullBitmap); m_bpButton7->SetToolTip(_("Do nothing")); } - if (localSyncConfiguration.rightNewer == SyncDirRight) + if (localSyncConfiguration.rightNewer == syncDirRight) { m_bpButton8->SetBitmapLabel(*GlobalResources::bitmapRightArrow); m_bpButton8->SetToolTip(_("Copy from left to right overwriting")); } - else if (localSyncConfiguration.rightNewer == SyncDirLeft) + else if (localSyncConfiguration.rightNewer == syncDirLeft) { m_bpButton8->SetBitmapLabel(*GlobalResources::bitmapLeftArrow); m_bpButton8->SetToolTip(_("Copy from right to left overwriting")); } - else if (localSyncConfiguration.rightNewer == SyncDirNone) + else if (localSyncConfiguration.rightNewer == syncDirNone) { m_bpButton8->SetBitmapLabel(wxNullBitmap); m_bpButton8->SetToolTip(_("Do nothing")); } - if (localSyncConfiguration.different == SyncDirRight) + if (localSyncConfiguration.different == syncDirRight) { m_bpButton9->SetBitmapLabel(*GlobalResources::bitmapRightArrow); m_bpButton9->SetToolTip(_("Copy from left to right overwriting")); } - else if (localSyncConfiguration.different == SyncDirLeft) + else if (localSyncConfiguration.different == syncDirLeft) { m_bpButton9->SetBitmapLabel(*GlobalResources::bitmapLeftArrow); m_bpButton9->SetToolTip(_("Copy from right to left overwriting")); } - else if (localSyncConfiguration.different == SyncDirNone) + else if (localSyncConfiguration.different == syncDirNone) { m_bpButton9->SetBitmapLabel(wxNullBitmap); m_bpButton9->SetToolTip(_("Do nothing")); } //update preview of bytes to be transferred: - m_textCtrl5->SetValue(FreeFileSync::formatFilesizeToShortString(FreeFileSync::calcTotalBytesToTransfer(mainDialog->currentGridData, localSyncConfiguration))); + int objectsTotal = 0; + double dataTotal = 0; + FreeFileSync::calcTotalBytesToSync(objectsTotal, dataTotal, mainDialog->currentGridData, localSyncConfiguration); + + wxString objects = globalFunctions::numberToWxString(objectsTotal); + globalFunctions::includeNumberSeparator(objects); + wxString data = FreeFileSync::formatFilesizeToShortString(dataTotal); + + m_textCtrl12->SetValue(objects); + m_textCtrl5->SetValue(data); } @@ -148,6 +158,7 @@ void SyncDialog::OnBack(wxCommandEvent& event) //write configuration to main dialog mainDialog->syncConfiguration = localSyncConfiguration; mainDialog->useRecycleBin = m_checkBoxUseRecycler->GetValue(); + mainDialog->hideErrorMessages = m_checkBoxHideErrors->GetValue(); EndModal(0); } @@ -157,6 +168,7 @@ void SyncDialog::OnStartSync(wxCommandEvent& event) //write configuration to main dialog mainDialog->syncConfiguration = localSyncConfiguration; mainDialog->useRecycleBin = m_checkBoxUseRecycler->GetValue(); + mainDialog->hideErrorMessages = m_checkBoxHideErrors->GetValue(); EndModal(StartSynchronizationProcess); } @@ -177,11 +189,11 @@ void SyncDialog::OnSelectRecycleBin(wxCommandEvent& event) void SyncDialog::OnSyncLeftToRight( wxCommandEvent& event ) { - localSyncConfiguration.exLeftSideOnly = SyncDirRight; - localSyncConfiguration.exRightSideOnly = SyncDirRight; - localSyncConfiguration.leftNewer = SyncDirRight; - localSyncConfiguration.rightNewer = SyncDirRight; - localSyncConfiguration.different = SyncDirRight; + localSyncConfiguration.exLeftSideOnly = syncDirRight; + localSyncConfiguration.exRightSideOnly = syncDirRight; + localSyncConfiguration.leftNewer = syncDirRight; + localSyncConfiguration.rightNewer = syncDirRight; + localSyncConfiguration.different = syncDirRight; updateConfigIcons(); @@ -192,11 +204,11 @@ void SyncDialog::OnSyncLeftToRight( wxCommandEvent& event ) void SyncDialog::OnSyncBothSides( wxCommandEvent& event ) { - localSyncConfiguration.exLeftSideOnly = SyncDirRight; - localSyncConfiguration.exRightSideOnly = SyncDirLeft; - localSyncConfiguration.leftNewer = SyncDirRight; - localSyncConfiguration.rightNewer = SyncDirLeft; - localSyncConfiguration.different = SyncDirNone; + localSyncConfiguration.exLeftSideOnly = syncDirRight; + localSyncConfiguration.exRightSideOnly = syncDirLeft; + localSyncConfiguration.leftNewer = syncDirRight; + localSyncConfiguration.rightNewer = syncDirLeft; + localSyncConfiguration.different = syncDirNone; updateConfigIcons(); //if event is triggered by button @@ -206,12 +218,12 @@ void SyncDialog::OnSyncBothSides( wxCommandEvent& event ) void toggleSyncDirection(SyncDirection& current) { - if (current == SyncDirRight) - current = SyncDirLeft; - else if (current == SyncDirLeft) - current = SyncDirNone; - else if (current== SyncDirNone) - current = SyncDirRight; + if (current == syncDirRight) + current = syncDirLeft; + else if (current == syncDirLeft) + current = syncDirNone; + else if (current== syncDirNone) + current = syncDirRight; else assert (false); } diff --git a/ui/guiGenerated.cpp b/ui/guiGenerated.cpp index 4308804e..1d51a28f 100644 --- a/ui/guiGenerated.cpp +++ b/ui/guiGenerated.cpp @@ -111,7 +111,7 @@ GuiGenerated::GuiGenerated( wxWindow* parent, wxWindowID id, const wxString& tit bSizer6->Add( 40, 0, 0, wxEXPAND, 5 ); - bSizer1->Add( bSizer6, 0, wxEXPAND|wxALIGN_CENTER_HORIZONTAL, 5 ); + bSizer1->Add( bSizer6, 0, wxEXPAND, 5 ); wxBoxSizer* bSizer2; bSizer2 = new wxBoxSizer( wxHORIZONTAL ); @@ -354,7 +354,7 @@ GuiGenerated::GuiGenerated( wxWindow* parent, wxWindowID id, const wxString& tit bSizer3->Add( 0, 0, 1, wxEXPAND, 5 ); - bSizer3->Add( 190, 0, 0, wxEXPAND, 5 ); + bSizer3->Add( 190, 0, 0, wxALL, 5 ); bSizer3->Add( 0, 0, 1, wxEXPAND, 5 ); @@ -451,6 +451,101 @@ GuiGenerated::~GuiGenerated() m_bpButton10->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( GuiGenerated::OnQuit ), NULL, this ); } +CompareStatusGenerated::CompareStatusGenerated( wxWindow* parent, wxWindowID id, const wxPoint& pos, const wxSize& size, long style ) : wxPanel( parent, id, pos, size, style ) +{ + wxBoxSizer* bSizer40; + bSizer40 = new wxBoxSizer( wxVERTICAL ); + + wxBoxSizer* bSizer42; + bSizer42 = new wxBoxSizer( wxHORIZONTAL ); + + wxStaticBoxSizer* sbSizer10; + sbSizer10 = new wxStaticBoxSizer( new wxStaticBox( this, wxID_ANY, wxEmptyString ), wxHORIZONTAL ); + + m_staticText321 = new wxStaticText( this, wxID_ANY, _("Files scanned:"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticText321->Wrap( -1 ); + m_staticText321->SetFont( wxFont( 10, 74, 90, 90, false, wxT("Tahoma") ) ); + + sbSizer10->Add( m_staticText321, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL|wxRIGHT, 5 ); + + m_staticTextScanned = new wxStaticText( this, wxID_ANY, _("123456"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticTextScanned->Wrap( -1 ); + m_staticTextScanned->SetFont( wxFont( 10, 74, 90, 92, false, wxT("Tahoma") ) ); + + sbSizer10->Add( m_staticTextScanned, 0, wxALIGN_CENTER_VERTICAL, 5 ); + + bSizer42->Add( sbSizer10, 0, wxEXPAND|wxALIGN_CENTER_VERTICAL, 5 ); + + + bSizer42->Add( 0, 0, 1, wxEXPAND, 5 ); + + wxStaticBoxSizer* sbSizer13; + sbSizer13 = new wxStaticBoxSizer( new wxStaticBox( this, wxID_ANY, wxEmptyString ), wxHORIZONTAL ); + + m_staticText46 = new wxStaticText( this, wxID_ANY, _("Files to compare:"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticText46->Wrap( -1 ); + m_staticText46->SetFont( wxFont( 10, 74, 90, 90, false, wxT("Tahoma") ) ); + + sbSizer13->Add( m_staticText46, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT, 5 ); + + m_staticTextFilesToCompare = new wxStaticText( this, wxID_ANY, _("123456"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticTextFilesToCompare->Wrap( -1 ); + m_staticTextFilesToCompare->SetFont( wxFont( 10, 74, 90, 92, false, wxT("Tahoma") ) ); + + sbSizer13->Add( m_staticTextFilesToCompare, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); + + bSizer42->Add( sbSizer13, 0, wxEXPAND|wxALIGN_CENTER_VERTICAL, 5 ); + + + bSizer42->Add( 0, 0, 1, wxEXPAND, 5 ); + + wxStaticBoxSizer* sbSizer11; + sbSizer11 = new wxStaticBoxSizer( new wxStaticBox( this, wxID_ANY, wxEmptyString ), wxHORIZONTAL ); + + m_staticText32 = new wxStaticText( this, wxID_ANY, _("Data to compare:"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticText32->Wrap( -1 ); + m_staticText32->SetFont( wxFont( 10, 74, 90, 90, false, wxT("Tahoma") ) ); + + sbSizer11->Add( m_staticText32, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL|wxRIGHT, 5 ); + + m_staticTextDataToCompare = new wxStaticText( this, wxID_ANY, _("123456"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticTextDataToCompare->Wrap( -1 ); + m_staticTextDataToCompare->SetFont( wxFont( 10, 74, 90, 92, false, wxT("Tahoma") ) ); + + sbSizer11->Add( m_staticTextDataToCompare, 0, wxALIGN_CENTER_VERTICAL, 5 ); + + bSizer42->Add( sbSizer11, 0, wxEXPAND|wxALIGN_CENTER_VERTICAL, 5 ); + + bSizer40->Add( bSizer42, 0, wxALIGN_CENTER_HORIZONTAL|wxEXPAND|wxALL, 5 ); + + wxBoxSizer* bSizer48; + bSizer48 = new wxBoxSizer( wxHORIZONTAL ); + + m_staticText30 = new wxStaticText( this, wxID_ANY, _("Operation:"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticText30->Wrap( -1 ); + m_staticText30->SetFont( wxFont( 10, 74, 90, 92, false, wxT("Tahoma") ) ); + + bSizer48->Add( m_staticText30, 0, wxRIGHT|wxALIGN_CENTER_VERTICAL, 5 ); + + m_textCtrlFilename = new wxTextCtrl( this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxTE_READONLY ); + m_textCtrlFilename->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_ACTIVEBORDER ) ); + + bSizer48->Add( m_textCtrlFilename, 1, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); + + bSizer40->Add( bSizer48, 0, wxEXPAND|wxALIGN_CENTER_HORIZONTAL|wxBOTTOM|wxRIGHT|wxLEFT, 5 ); + + m_gauge2 = new wxGauge( this, wxID_ANY, 100, wxDefaultPosition, wxSize( -1,20 ), wxGA_HORIZONTAL ); + bSizer40->Add( m_gauge2, 0, wxALL|wxEXPAND, 5 ); + + this->SetSizer( bSizer40 ); + this->Layout(); + bSizer40->Fit( this ); +} + +CompareStatusGenerated::~CompareStatusGenerated() +{ +} + SyncDialogGenerated::SyncDialogGenerated( wxWindow* parent, wxWindowID id, const wxString& title, const wxPoint& pos, const wxSize& size, long style ) : wxDialog( parent, id, title, pos, size, style ) { this->SetSizeHints( wxDefaultSize, wxDefaultSize ); @@ -475,27 +570,42 @@ SyncDialogGenerated::SyncDialogGenerated( wxWindow* parent, wxWindowID id, const m_bpButton18 = new wxBitmapButton( this, wxID_OK, wxNullBitmap, wxDefaultPosition, wxSize( 140,58 ), wxBU_AUTODRAW ); m_bpButton18->SetDefault(); - m_bpButton18->SetToolTip( _("Start synchronizing files") ); + m_bpButton18->SetToolTip( _("Start synchronization") ); + + m_bpButton18->SetToolTip( _("Start synchronization") ); + + bSizer201->Add( m_bpButton18, 0, wxRIGHT|wxALIGN_CENTER_VERTICAL, 5 ); + + + bSizer201->Add( 10, 0, 0, wxEXPAND, 5 ); - m_bpButton18->SetToolTip( _("Start synchronizing files") ); + wxGridSizer* gSizer1; + gSizer1 = new wxGridSizer( 2, 2, 0, 0 ); - bSizer201->Add( m_bpButton18, 0, wxRIGHT, 5 ); + m_staticText37 = new wxStaticText( this, wxID_ANY, _("Objects to process:"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticText37->Wrap( -1 ); + m_staticText37->SetFont( wxFont( 10, 74, 93, 90, false, wxT("Tahoma") ) ); - wxBoxSizer* bSizer211; - bSizer211 = new wxBoxSizer( wxHORIZONTAL ); + gSizer1->Add( m_staticText37, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5 ); - m_staticText14 = new wxStaticText( this, wxID_ANY, _("Data to be transferred:"), wxDefaultPosition, wxDefaultSize, 0 ); + m_textCtrl12 = new wxTextCtrl( this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0 ); + m_textCtrl12->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 90, false, wxEmptyString ) ); + m_textCtrl12->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_ACTIVEBORDER ) ); + + gSizer1->Add( m_textCtrl12, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5 ); + + m_staticText14 = new wxStaticText( this, wxID_ANY, _("Data to transfer:"), wxDefaultPosition, wxDefaultSize, 0 ); m_staticText14->Wrap( -1 ); m_staticText14->SetFont( wxFont( 10, 74, 93, 90, false, wxT("Tahoma") ) ); - bSizer211->Add( m_staticText14, 0, wxALIGN_CENTER_HORIZONTAL|wxALL|wxALIGN_CENTER_VERTICAL, 5 ); + gSizer1->Add( m_staticText14, 0, wxALIGN_CENTER_VERTICAL|wxBOTTOM|wxRIGHT|wxLEFT, 5 ); m_textCtrl5 = new wxTextCtrl( this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize( 55,-1 ), wxTE_READONLY ); m_textCtrl5->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_ACTIVEBORDER ) ); - bSizer211->Add( m_textCtrl5, 0, wxALL|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); + gSizer1->Add( m_textCtrl5, 0, wxALIGN_CENTER_VERTICAL|wxBOTTOM|wxRIGHT|wxLEFT, 5 ); - bSizer201->Add( bSizer211, 0, wxALIGN_CENTER_VERTICAL|wxLEFT, 5 ); + bSizer201->Add( gSizer1, 0, wxALIGN_CENTER_VERTICAL, 5 ); bSizer29->Add( bSizer201, 0, wxEXPAND, 5 ); @@ -583,19 +693,30 @@ SyncDialogGenerated::SyncDialogGenerated( wxWindow* parent, wxWindowID id, const bSizer291->Add( m_button6, 0, wxALIGN_CENTER_VERTICAL|wxBOTTOM|wxLEFT, 5 ); - m_button16 = new wxButton( this, wxID_CANCEL, _("dummyButton"), wxDefaultPosition, wxSize( 0,-1 ), 0 ); - bSizer291->Add( m_button16, 0, wxALL, 5 ); + m_button16 = new wxButton( this, wxID_CANCEL, _("dummy"), wxDefaultPosition, wxSize( 0,-1 ), 0 ); + bSizer291->Add( m_button16, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5 ); bSizer291->Add( 82, 0, 0, wxEXPAND|wxALIGN_CENTER_VERTICAL, 5 ); + wxBoxSizer* bSizer38; + bSizer38 = new wxBoxSizer( wxVERTICAL ); + m_checkBoxUseRecycler = new wxCheckBox( this, wxID_ANY, _("Use Recycle Bin"), wxDefaultPosition, wxDefaultSize, 0 ); m_checkBoxUseRecycler->SetToolTip( _("Use Recycle Bin when deleting or overwriting files during synchronization") ); - bSizer291->Add( m_checkBoxUseRecycler, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5 ); + bSizer38->Add( m_checkBoxUseRecycler, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5 ); + + m_checkBoxHideErrors = new wxCheckBox( this, wxID_ANY, _("Hide error messages"), wxDefaultPosition, wxDefaultSize, 0 ); + + m_checkBoxHideErrors->SetToolTip( _("Hides error messages during synchronization:\nThey are collected and shown as a list at the end of the process") ); - bSizer29->Add( bSizer291, 1, wxEXPAND|wxALIGN_CENTER_HORIZONTAL, 5 ); + bSizer38->Add( m_checkBoxHideErrors, 0, wxALL, 5 ); + + bSizer291->Add( bSizer38, 0, wxALIGN_BOTTOM, 5 ); + + bSizer29->Add( bSizer291, 0, wxALIGN_CENTER_HORIZONTAL|wxEXPAND, 5 ); bSizer181->Add( bSizer29, 1, wxEXPAND, 5 ); @@ -809,11 +930,22 @@ AboutDlgGenerated::AboutDlgGenerated( wxWindow* parent, wxWindowID id, const wxS bSizer31->Add( 0, 10, 0, wxEXPAND, 5 ); - m_staticText14 = new wxStaticText( this, wxID_ANY, _("FreeFileSync v1.3"), wxDefaultPosition, wxDefaultSize, 0 ); + wxBoxSizer* bSizer36; + bSizer36 = new wxBoxSizer( wxHORIZONTAL ); + + m_bitmap11 = new wxStaticBitmap( this, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 40,40 ), 0 ); + bSizer36->Add( m_bitmap11, 0, wxALIGN_CENTER_VERTICAL, 5 ); + + m_staticText14 = new wxStaticText( this, wxID_ANY, _("FreeFileSync v1.4"), wxDefaultPosition, wxDefaultSize, 0 ); m_staticText14->Wrap( -1 ); m_staticText14->SetFont( wxFont( 16, 74, 90, 92, false, wxT("Tahoma") ) ); - bSizer31->Add( m_staticText14, 0, wxALL|wxALIGN_CENTER_HORIZONTAL, 5 ); + bSizer36->Add( m_staticText14, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL|wxRIGHT|wxLEFT, 5 ); + + + bSizer36->Add( 40, 0, 0, wxALIGN_CENTER_VERTICAL, 5 ); + + bSizer31->Add( bSizer36, 0, wxALIGN_CENTER_HORIZONTAL, 5 ); m_staticText15 = new wxStaticText( this, wxID_ANY, _("-Open-Source file synchronization-"), wxDefaultPosition, wxDefaultSize, 0 ); m_staticText15->Wrap( -1 ); @@ -996,11 +1128,25 @@ DeleteDialogGenerated::DeleteDialogGenerated( wxWindow* parent, wxWindowID id, c bSizer24->Add( 0, 10, 0, wxEXPAND, 5 ); + wxBoxSizer* bSizer41; + bSizer41 = new wxBoxSizer( wxHORIZONTAL ); + + + bSizer41->Add( 0, 0, 1, wxEXPAND, 5 ); + + m_bitmap12 = new wxStaticBitmap( this, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 40,40 ), 0 ); + bSizer41->Add( m_bitmap12, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5 ); + m_staticTextHeader = new wxStaticText( this, wxID_ANY, _("Dummy text"), wxDefaultPosition, wxDefaultSize, 0 ); m_staticTextHeader->Wrap( -1 ); m_staticTextHeader->SetFont( wxFont( 10, 74, 90, 90, false, wxT("Tahoma") ) ); - bSizer24->Add( m_staticTextHeader, 0, wxALL|wxALIGN_CENTER_HORIZONTAL, 5 ); + bSizer41->Add( m_staticTextHeader, 0, wxALL|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); + + + bSizer41->Add( 0, 0, 1, wxEXPAND, 5 ); + + bSizer24->Add( bSizer41, 0, wxEXPAND, 5 ); m_textCtrlMessage = new wxTextCtrl( this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize( -1,-1 ), wxTE_MULTILINE|wxTE_READONLY ); m_textCtrlMessage->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_ACTIVEBORDER ) ); @@ -1123,7 +1269,7 @@ FilterDlgGenerated::FilterDlgGenerated( wxWindow* parent, wxWindowID id, const w bSizer22->Add( 0, 0, 1, wxEXPAND, 5 ); - m_button17 = new wxButton( this, wxID_CANCEL, _("dummyButton"), wxDefaultPosition, wxSize( 0,-1 ), 0 ); + m_button17 = new wxButton( this, wxID_CANCEL, _("dummy"), wxDefaultPosition, wxSize( 0,-1 ), 0 ); bSizer22->Add( m_button17, 0, wxALL, 5 ); m_button10 = new wxButton( this, wxID_OK, _("OK"), wxDefaultPosition, wxSize( 80,30 ), 0 ); @@ -1163,13 +1309,21 @@ SyncStatusGenerated::SyncStatusGenerated( wxWindow* parent, wxWindowID id, const bSizer27->Add( 0, 15, 0, wxEXPAND, 5 ); + wxBoxSizer* bSizer37; + bSizer37 = new wxBoxSizer( wxHORIZONTAL ); + + m_animationControl1 = new wxAnimationCtrl(this, wxID_ANY, wxNullAnimation, wxDefaultPosition, wxSize( 45,45 )); + bSizer37->Add( m_animationControl1, 0, wxALL, 5 ); + m_staticText20 = new wxStaticText( this, wxID_ANY, _("Synchronization status"), wxDefaultPosition, wxDefaultSize, 0 ); m_staticText20->Wrap( -1 ); m_staticText20->SetFont( wxFont( 16, 74, 90, 92, false, wxT("Tahoma") ) ); - bSizer27->Add( m_staticText20, 0, wxALL|wxALIGN_CENTER_HORIZONTAL, 5 ); + bSizer37->Add( m_staticText20, 0, wxALL|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); + + bSizer27->Add( bSizer37, 0, wxALIGN_CENTER_HORIZONTAL, 5 ); - m_staticTextStatus = new wxStaticText( this, wxID_ANY, _("Running..."), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticTextStatus = new wxStaticText( this, wxID_ANY, _("Synchronizing..."), wxDefaultPosition, wxDefaultSize, 0 ); m_staticTextStatus->Wrap( -1 ); m_staticTextStatus->SetFont( wxFont( 14, 74, 93, 90, false, wxT("Tahoma") ) ); @@ -1187,18 +1341,6 @@ SyncStatusGenerated::SyncStatusGenerated( wxWindow* parent, wxWindowID id, const bSizer31->Add( 0, 0, 1, wxEXPAND, 5 ); - m_staticText25 = new wxStaticText( this, wxID_ANY, _("Items completed:"), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticText25->Wrap( -1 ); - m_staticText25->SetFont( wxFont( 10, 74, 93, 90, false, wxT("Tahoma") ) ); - - bSizer31->Add( m_staticText25, 0, wxTOP|wxBOTTOM|wxLEFT|wxALIGN_CENTER_VERTICAL, 5 ); - - m_staticTextProcessedObj = new wxStaticText( this, wxID_ANY, _("1000000"), wxDefaultPosition, wxSize( -1,-1 ), 0 ); - m_staticTextProcessedObj->Wrap( -1 ); - m_staticTextProcessedObj->SetFont( wxFont( 10, 74, 90, 92, false, wxT("Tahoma") ) ); - - bSizer31->Add( m_staticTextProcessedObj, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5 ); - bSizer27->Add( bSizer31, 0, wxEXPAND, 5 ); m_textCtrlInfo = new wxTextCtrl( this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxTE_MULTILINE|wxTE_READONLY ); @@ -1215,19 +1357,19 @@ SyncStatusGenerated::SyncStatusGenerated( wxWindow* parent, wxWindowID id, const wxBoxSizer* bSizer32; bSizer32 = new wxBoxSizer( wxHORIZONTAL ); - m_staticText26 = new wxStaticText( this, wxID_ANY, _("Data copied:"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticText26 = new wxStaticText( this, wxID_ANY, _("Data remaining:"), wxDefaultPosition, wxDefaultSize, 0 ); m_staticText26->Wrap( -1 ); m_staticText26->SetFont( wxFont( 10, 74, 93, 90, false, wxT("Tahoma") ) ); bSizer32->Add( m_staticText26, 0, wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxLEFT, 5 ); - m_staticTextBytesCopied = new wxStaticText( this, wxID_ANY, _("--,- MB"), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticTextBytesCopied->Wrap( -1 ); - m_staticTextBytesCopied->SetFont( wxFont( 10, 74, 90, 92, false, wxT("Tahoma") ) ); + m_staticTextDataRemaining = new wxStaticText( this, wxID_ANY, _("--,- MB"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticTextDataRemaining->Wrap( -1 ); + m_staticTextDataRemaining->SetFont( wxFont( 10, 74, 90, 92, false, wxT("Tahoma") ) ); - bSizer32->Add( m_staticTextBytesCopied, 0, wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxLEFT, 5 ); + bSizer32->Add( m_staticTextDataRemaining, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5 ); - bSizer28->Add( bSizer32, 1, wxALIGN_CENTER_VERTICAL, 5 ); + bSizer28->Add( bSizer32, 0, wxALIGN_CENTER_VERTICAL, 5 ); bSizer28->Add( 0, 0, 1, wxEXPAND, 5 ); @@ -1248,15 +1390,21 @@ SyncStatusGenerated::SyncStatusGenerated( wxWindow* parent, wxWindowID id, const bSizer28->Add( 0, 0, 1, wxEXPAND, 5 ); wxBoxSizer* bSizer33; - bSizer33 = new wxBoxSizer( wxVERTICAL ); + bSizer33 = new wxBoxSizer( wxHORIZONTAL ); - m_staticText32 = new wxStaticText( this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0 ); - m_staticText32->Wrap( -1 ); - m_staticText32->SetFont( wxFont( 10, 74, 90, 90, false, wxT("Tahoma") ) ); + m_staticText25 = new wxStaticText( this, wxID_ANY, _("Files remaining:"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticText25->Wrap( -1 ); + m_staticText25->SetFont( wxFont( 10, 74, 93, 90, false, wxT("Tahoma") ) ); + + bSizer33->Add( m_staticText25, 0, wxTOP|wxBOTTOM|wxLEFT|wxALIGN_CENTER_VERTICAL, 5 ); + + m_staticTextRemainingObj = new wxStaticText( this, wxID_ANY, _("1000000"), wxDefaultPosition, wxSize( -1,-1 ), 0 ); + m_staticTextRemainingObj->Wrap( -1 ); + m_staticTextRemainingObj->SetFont( wxFont( 10, 74, 90, 92, false, wxT("Tahoma") ) ); - bSizer33->Add( m_staticText32, 0, wxALL, 5 ); + bSizer33->Add( m_staticTextRemainingObj, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5 ); - bSizer28->Add( bSizer33, 1, wxEXPAND, 5 ); + bSizer28->Add( bSizer33, 0, wxALIGN_CENTER_VERTICAL, 5 ); bSizer27->Add( bSizer28, 0, wxALIGN_CENTER_HORIZONTAL|wxEXPAND, 5 ); diff --git a/ui/guiGenerated.h b/ui/guiGenerated.h index fe685d9c..2b6f5b6d 100644 --- a/ui/guiGenerated.h +++ b/ui/guiGenerated.h @@ -35,11 +35,11 @@ class CustomGrid; #include <wx/statusbr.h> #include <wx/frame.h> #include <wx/stattext.h> +#include <wx/gauge.h> #include <wx/statline.h> #include <wx/statbmp.h> #include <wx/dialog.h> #include <wx/animate.h> -#include <wx/gauge.h> /////////////////////////////////////////////////////////////////////////// @@ -129,12 +129,38 @@ class GuiGenerated : public wxFrame public: - GuiGenerated( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = _("FreeFileSync - Folder Synchronization"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize( 930,603 ), long style = wxDEFAULT_FRAME_STYLE|wxTAB_TRAVERSAL ); + GuiGenerated( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = _("FreeFileSync - Folder Synchronization"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize( 933,612 ), long style = wxDEFAULT_FRAME_STYLE|wxTAB_TRAVERSAL ); ~GuiGenerated(); }; /////////////////////////////////////////////////////////////////////////////// +/// Class CompareStatusGenerated +/////////////////////////////////////////////////////////////////////////////// +class CompareStatusGenerated : public wxPanel +{ + private: + + protected: + wxStaticText* m_staticText321; + wxStaticText* m_staticTextScanned; + + wxStaticText* m_staticText46; + wxStaticText* m_staticTextFilesToCompare; + + wxStaticText* m_staticText32; + wxStaticText* m_staticTextDataToCompare; + wxStaticText* m_staticText30; + wxTextCtrl* m_textCtrlFilename; + wxGauge* m_gauge2; + + public: + CompareStatusGenerated( wxWindow* parent, wxWindowID id = wxID_ANY, const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize( -1,-1 ), long style = wxTAB_TRAVERSAL ); + ~CompareStatusGenerated(); + +}; + +/////////////////////////////////////////////////////////////////////////////// /// Class SyncDialogGenerated /////////////////////////////////////////////////////////////////////////////// class SyncDialogGenerated : public wxDialog @@ -145,6 +171,9 @@ class SyncDialogGenerated : public wxDialog wxBitmapButton* m_bpButton18; + + wxStaticText* m_staticText37; + wxTextCtrl* m_textCtrl12; wxStaticText* m_staticText14; wxTextCtrl* m_textCtrl5; @@ -164,6 +193,7 @@ class SyncDialogGenerated : public wxDialog wxButton* m_button16; wxCheckBox* m_checkBoxUseRecycler; + wxCheckBox* m_checkBoxHideErrors; wxStaticText* m_staticText2; @@ -204,7 +234,7 @@ class SyncDialogGenerated : public wxDialog public: - SyncDialogGenerated( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = _("Synchronization settings"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize( 533,349 ), long style = wxDEFAULT_DIALOG_STYLE ); + SyncDialogGenerated( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = _("Synchronization settings"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize( 525,356 ), long style = wxDEFAULT_DIALOG_STYLE ); ~SyncDialogGenerated(); }; @@ -243,7 +273,9 @@ class AboutDlgGenerated : public wxDialog protected: + wxStaticBitmap* m_bitmap11; wxStaticText* m_staticText14; + wxStaticText* m_staticText15; wxStaticText* m_build; @@ -312,7 +344,10 @@ class DeleteDialogGenerated : public wxDialog protected: + + wxStaticBitmap* m_bitmap12; wxStaticText* m_staticTextHeader; + wxTextCtrl* m_textCtrlMessage; wxButton* m_buttonOK; wxButton* m_buttonCancel; @@ -376,16 +411,16 @@ class SyncStatusGenerated : public wxDialog protected: + wxAnimationCtrl* m_animationControl1; wxStaticText* m_staticText20; wxStaticText* m_staticText21; - wxStaticText* m_staticText25; - wxStaticText* m_staticTextProcessedObj; wxStaticText* m_staticText26; - wxStaticText* m_staticTextBytesCopied; + wxStaticText* m_staticTextDataRemaining; - wxStaticText* m_staticText32; + wxStaticText* m_staticText25; + wxStaticText* m_staticTextRemainingObj; // Virtual event handlers, overide them in your derived class |