From 222024f07e505617aec93dc4837be2be27d18856 Mon Sep 17 00:00:00 2001 From: Daniel Wilhelm Date: Fri, 18 Apr 2014 16:55:14 +0200 Subject: 1.13 --- Application.cpp | 233 +- Application.h | 24 +- Changelog.txt | 11 + FreeFileSync - Unicode.cbp | 15 +- FreeFileSync.cpp | 1923 ---------------- FreeFileSync.h | 335 ++- Makefile | 17 +- Makefile_Win_Unicode.cmd | 9 +- Readme.txt | 5 +- Resources.dat | Bin 180418 -> 143266 bytes algorithm.cpp | 682 ++++++ algorithm.h | 36 + comparison.cpp | 739 +++++++ comparison.h | 32 + dutch.lng | 98 +- french.lng | 100 +- german.lng | 108 +- japanese.lng | 106 +- library/CustomGrid.cpp | 80 +- library/CustomGrid.h | 11 +- library/fileHandling.cpp | 218 ++ library/fileHandling.h | 37 + library/globalFunctions.cpp | 4 +- library/globalFunctions.h | 30 + library/misc.cpp | 4 +- library/processXml.cpp | 40 +- library/processXml.h | 14 +- library/resources.cpp | 15 +- library/resources.h | 13 +- library/sorting.h | 30 +- library/statusHandler.h | 14 +- synchronization.cpp | 563 +++++ synchronization.h | 34 + ui/MainDialog.cpp | 576 +++-- ui/MainDialog.h | 30 +- ui/SmallDialogs.cpp | 160 +- ui/SmallDialogs.h | 38 +- ui/SyncDialog.cpp | 51 +- ui/SyncDialog.h | 9 +- ui/guiGenerated.cpp | 5135 ++++++++++++++++++++++--------------------- ui/guiGenerated.h | 1485 ++++++------- 41 files changed, 6986 insertions(+), 6078 deletions(-) delete mode 100644 FreeFileSync.cpp create mode 100644 algorithm.cpp create mode 100644 algorithm.h create mode 100644 comparison.cpp create mode 100644 comparison.h create mode 100644 library/fileHandling.cpp create mode 100644 library/fileHandling.h create mode 100644 synchronization.cpp create mode 100644 synchronization.h diff --git a/Application.cpp b/Application.cpp index c7a0beda..1ff7c029 100644 --- a/Application.cpp +++ b/Application.cpp @@ -16,6 +16,9 @@ #include #include "library/processXml.h" #include +#include "comparison.h" +#include "synchronization.h" +#include "algorithm.h" using namespace xmlAccess; @@ -54,7 +57,7 @@ bool Application::OnInit() } catch (const FileError& error) { - if (wxFileExists(FreeFileSync::FfsGlobalSettingsFile)) + if (wxFileExists(FreeFileSync::GLOBAL_CONFIG_FILE)) { //show messagebox and quit program immediately wxMessageBox(error.show(), _("Error"), wxOK | wxICON_ERROR); return false; @@ -102,7 +105,7 @@ void Application::initialize() } else //start in GUI mode (standard) { - MainDialog* frame = new MainDialog(NULL, FreeFileSync::FfsLastConfigFile, &programLanguage, globalSettings); + MainDialog* frame = new MainDialog(NULL, FreeFileSync::LAST_CONFIG_FILE, &programLanguage, globalSettings); frame->SetIcon(*globalResource.programIcon); //set application icon frame->Show(); } @@ -167,13 +170,15 @@ public: readyToWrite = logFile.IsOpened(); if (readyToWrite) { - logFile.Write(wxString(_("FreeFileSync (Date: ")) + wxDateTime::Now().FormatDate() + _(" Time: ") + wxDateTime::Now().FormatTime() + wxT(")") + wxChar('\n')); - logFile.Write(wxString(_("-------------------------------------------------")) + wxChar('\n')); - logFile.Write(wxChar('\n')); - logFile.Write(_("Log-messages:\n-------------")); - logFile.Write(wxChar('\n')); - write(_("Start")); - logFile.Write(wxChar('\n')); + wxString headerLine = wxString(wxT("FreeFileSync (")) + _("Date: ") + wxDateTime::Now().FormatDate() + wxT(" ") + _("Time: ") + wxDateTime::Now().FormatTime() + wxT(")"); + logFile.Write(headerLine + wxChar('\n')); + logFile.Write(wxString().Pad(headerLine.Len(), wxChar('-')) + wxChar('\n') + wxChar('\n')); + + wxString caption = _("Log-messages:"); + logFile.Write(caption + wxChar('\n')); + logFile.Write(wxString().Pad(caption.Len(), wxChar('-')) + wxChar('\n')); + + write(wxString(_("Start")) + wxChar('\n')); totalTime.Start(); //measure total time } @@ -250,7 +255,7 @@ void Application::runBatchMode(const wxString& filename, xmlAccess::XmlGlobalSet } //all settings have been read successfully... - applicationRunsInBatchWithoutWindows = batchCfg.silent; //this value is needed for the application to decide whether to wait for windows to close or not + applicationRunsInBatchWithoutWindows = batchCfg.silent; //this value is needed for the application to decide whether to wait for all windows to close or not //format directory names for (vector::iterator i = batchCfg.directoryPairs.begin(); i != batchCfg.directoryPairs.end(); ++i) @@ -272,53 +277,120 @@ void Application::runBatchMode(const wxString& filename, xmlAccess::XmlGlobalSet } - //check if directories exist - wxString errorMessage; - if (!FreeFileSync::foldersAreValidForComparison(batchCfg.directoryPairs, errorMessage)) + //begin of synchronization process (all in one try-catch block) + try { - if (batchCfg.silent) + FileCompareResult currentGridData; + //class handling status updates and error messages + BatchStatusUpdater statusUpdater(batchCfg.mainCfg.ignoreErrors, batchCfg.silent, log); + + //test existence of Recycle Bin + if (batchCfg.mainCfg.useRecycleBin) { - log->write(errorMessage, _("Warning")); - log->close(_("Synchronization aborted!")); + if (!FreeFileSync::recycleBinExists()) + { + statusUpdater.setFinalStatus(_("Unable to initialize Recycle Bin!"), SyncStatus::ABORTED); + returnValue = -2; + return; + } } - else wxMessageBox(errorMessage + wxT("\n\n") + _("Synchronization aborted!"), _("Warning"), wxICON_WARNING); - returnValue = -2; - return; - } + //check if directories are valid + wxString errorMessage; + if (!FreeFileSync::foldersAreValidForComparison(batchCfg.directoryPairs, errorMessage)) + { + statusUpdater.setFinalStatus(errorMessage, SyncStatus::ABORTED); + returnValue = -2; + return; + } - //test existence of Recycle Bin - if (batchCfg.mainCfg.useRecycleBin) - { - if (!FreeFileSync::recycleBinExists()) + //check if folders have dependencies + if (globalSettings.global.folderDependCheckActive) { - wxString errorMessage = wxString(_("Unable to initialize Recycle Bin!")); - wxString statusMessage = wxString(_("Synchronization aborted!")); - if (batchCfg.silent) + wxString warningMessage; + if (FreeFileSync::foldersHaveDependencies(batchCfg.directoryPairs, warningMessage)) { - log->write(errorMessage, _("Error")); - log->close(statusMessage); + //abort if in silent mode + if (batchCfg.silent) + { + statusUpdater.setFinalStatus(warningMessage, SyncStatus::ABORTED); + returnValue = -2; + return; + } + + //non silent mode: offer possibility to ignore issue + bool hideThisDialog = false; + wxString messageText = warningMessage + wxT("\n\n") + + _("Consider this when setting up synchronization rules: You might want to avoid write access to these directories so that synchronization of both does not interfere."); + + //show popup and ask user how to handle warning + WarningDlg* warningDlg = new WarningDlg(statusUpdater.getWindow(), WarningDlg::BUTTON_IGNORE | WarningDlg::BUTTON_ABORT, messageText, hideThisDialog); + if (warningDlg->ShowModal() == WarningDlg::BUTTON_ABORT) + { + statusUpdater.setFinalStatus(warningMessage, SyncStatus::ABORTED); + returnValue = -2; + return; + } + else + globalSettings.global.folderDependCheckActive = !hideThisDialog; } - else wxMessageBox(errorMessage + wxT("\n\n") + statusMessage, _("Error"), wxICON_WARNING); - - returnValue = -2; - return; } - } - //begin of synchronization process (all in one try-catch block) - try - { - FileCompareResult currentGridData; - //class handling status updates and error messages - BatchStatusUpdater statusUpdater(batchCfg.mainCfg.continueOnError, batchCfg.silent, log); //COMPARE DIRECTORIES - FreeFileSync::startCompareProcess(batchCfg.directoryPairs, - batchCfg.mainCfg.compareVar, - currentGridData, - &statusUpdater); + bool lineBreakOnMessages = !batchCfg.silent; + FreeFileSync::CompareProcess comparison(lineBreakOnMessages, &statusUpdater); + comparison.startCompareProcess(batchCfg.directoryPairs, + batchCfg.mainCfg.compareVar, + currentGridData); + +#ifdef FFS_WIN + //check if DST time correction needs to be applied + if (globalSettings.global.dstCheckActive) + { + int timeShift = 0; + wxString driveName; + FreeFileSync::checkForDSTChange(currentGridData, batchCfg.directoryPairs, timeShift, driveName); + if (timeShift) + { + //abort if in silent mode + if (batchCfg.silent) + { + statusUpdater.setFinalStatus(_("Daylight saving time change detected for FAT/FAT32 drive."), SyncStatus::ABORTED); + returnValue = -2; + return; + } + + //non silent mode: offer possibility to resolve issue + bool hideThisDialog = false; + wxString errorMessage = wxString(_("A file time shift due to a daylight saving time change was detected for a FAT/FAT32 drive.")) + wxT("\n") + + _("You can adjust the file times accordingly to resolve the issue:"); + errorMessage+= wxString(wxT("\n\n")) + _("Drive:") + wxT(" ") + driveName + wxT("\n") + + _("Time shift:") + wxT(" ") + globalFunctions::numberToWxString(timeShift); + + //show popup and ask user how to handle the DST change + WarningDlg* warningDlg = new WarningDlg(statusUpdater.getWindow(), WarningDlg::BUTTON_RESOLVE | WarningDlg::BUTTON_IGNORE, errorMessage, hideThisDialog); + warningDlg->m_bitmap10->SetBitmap(*globalResource.bitmapClock); + + if (warningDlg->ShowModal() == WarningDlg::BUTTON_RESOLVE) + { + ModifyFilesDlg* modifyDlg = new ModifyFilesDlg(NULL, driveName, timeShift); + modifyDlg->ShowModal(); + delete modifyDlg; + + //exit batch processing + statusUpdater.setFinalStatus(wxString(_("Daylight saving time change detected for FAT/FAT32 drive.")) + wxT("\n\n") + + _("Please restart synchronization!"), SyncStatus::ABORTED); + returnValue = -2; + return; + } + else + globalSettings.global.dstCheckActive = !hideThisDialog; + } + } +#endif //FFS_WIN + //APPLY FILTERS if (batchCfg.mainCfg.filterIsActive) @@ -337,14 +409,14 @@ void Application::runBatchMode(const wxString& filename, xmlAccess::XmlGlobalSet batchCfg.mainCfg.syncConfiguration); if (objectsToCreate + objectsToOverwrite + objectsToDelete == 0) { - statusUpdater.noSynchronizationNeeded(); //inform about this special case - - returnValue = -3; + statusUpdater.setFinalStatus(_("Nothing to synchronize. Both directories adhere to the sync-configuration!"), SyncStatus::FINISHED_WITH_SUCCESS); //inform about this special case + returnValue = 0; return; } //START SYNCHRONIZATION - FreeFileSync::startSynchronizationProcess(currentGridData, batchCfg.mainCfg.syncConfiguration, &statusUpdater, batchCfg.mainCfg.useRecycleBin); + FreeFileSync::SyncProcess synchronization(batchCfg.mainCfg.useRecycleBin, lineBreakOnMessages, &statusUpdater); + synchronization.startSynchronizationProcess(currentGridData, batchCfg.mainCfg.syncConfiguration); } catch (AbortThisProcess& theException) //exit used by statusUpdater { @@ -357,12 +429,11 @@ void Application::runBatchMode(const wxString& filename, xmlAccess::XmlGlobalSet //###################################################################################################### -BatchStatusUpdater::BatchStatusUpdater(bool continueOnError, bool silent, LogFile* log) : +BatchStatusUpdater::BatchStatusUpdater(bool ignoreAllErrors, bool silent, LogFile* log) : m_log(log), - continueErrors(continueOnError), + ignoreErrors(ignoreAllErrors), silentMode(silent), - currentProcess(StatusHandler::PROCESS_NONE), - synchronizationNeeded(true) + currentProcess(StatusHandler::PROCESS_NONE) { if (!silentMode) { @@ -379,16 +450,14 @@ BatchStatusUpdater::~BatchStatusUpdater() //output the result if (silentMode) { - if (abortionRequested) + if (!customStatusMessage.IsEmpty()) + m_log->close(customStatusMessage); + else if (abortRequested) m_log->close(_("Synchronization aborted!")); else if (failedItems) m_log->close(_("Synchronization completed with errors!")); else - { - if (!synchronizationNeeded) - m_log->write(_("Nothing to synchronize. Both directories adhere to the sync-configuration!"), _("Info")); m_log->close(_("Synchronization completed successfully.")); - } } else { @@ -402,7 +471,13 @@ BatchStatusUpdater::~BatchStatusUpdater() } //notify to syncStatusFrame that current process has ended - if (abortionRequested) + if (!customStatusMessage.IsEmpty()) //custom status message written by service consumer + { + finalMessage+= customStatusMessage; + syncStatusFrame->setStatusText_NoUpdate(finalMessage); + syncStatusFrame->processHasFinished(customStatusId); + } + else if (abortRequested) { finalMessage+= _("Synchronization aborted!"); syncStatusFrame->setStatusText_NoUpdate(finalMessage); @@ -416,11 +491,7 @@ BatchStatusUpdater::~BatchStatusUpdater() } else { - if (synchronizationNeeded) - finalMessage+= _("Synchronization completed successfully."); - else - finalMessage+= _("Nothing to synchronize. Both directories adhere to the sync-configuration!"); - + finalMessage+= _("Synchronization completed successfully."); syncStatusFrame->setStatusText_NoUpdate(finalMessage); syncStatusFrame->processHasFinished(SyncStatus::FINISHED_WITH_SUCCESS); } @@ -489,32 +560,33 @@ ErrorHandler::Response BatchStatusUpdater::reportError(const wxString& text) unhandledErrors.Add(text); m_log->write(text, _("Error")); - if (continueErrors) // /|\ before return, the logfile is written!!! + if (ignoreErrors) // /|\ before return, the logfile is written!!! return ErrorHandler::CONTINUE_NEXT; else { - abortionRequested = true; + abortRequested = true; throw AbortThisProcess(); } } else //show dialog to user who can decide how to continue { - if (continueErrors) //this option can be set from commandline or by the user in the error dialog on UI + if (ignoreErrors) //this option can be set from commandline or by the user in the error dialog on UI { unhandledErrors.Add(text); return ErrorHandler::CONTINUE_NEXT; } - wxString errorMessage = text + _("\n\nContinue with next object, retry or abort synchronization?"); - ErrorDlg* errorDlg = new ErrorDlg(errorMessage, continueErrors); - syncStatusFrame->updateStatusDialogNow(); - int rv = errorDlg->ShowModal(); - errorDlg->Destroy(); + bool ignoreNextErrors = false; + wxString errorMessage = text + wxT("\n\n") + _("Ignore this error, retry or abort synchronization?"); + ErrorDlg* errorDlg = new ErrorDlg(syncStatusFrame, errorMessage, ignoreNextErrors, 90); + + int rv = errorDlg->ShowModal(); switch (rv) { - case ErrorDlg::BUTTON_CONTINUE: + case ErrorDlg::BUTTON_IGNORE: + ignoreErrors = ignoreNextErrors; unhandledErrors.Add(text); return ErrorHandler::CONTINUE_NEXT; case ErrorDlg::BUTTON_RETRY: @@ -522,7 +594,7 @@ ErrorHandler::Response BatchStatusUpdater::reportError(const wxString& text) case ErrorDlg::BUTTON_ABORT: { unhandledErrors.Add(text); - abortionRequested = true; + abortRequested = true; throw AbortThisProcess(); } default: @@ -547,7 +619,18 @@ void BatchStatusUpdater::abortThisProcess() } -void BatchStatusUpdater::noSynchronizationNeeded() +void BatchStatusUpdater::setFinalStatus(const wxString& message, SyncStatus::SyncStatusID id) { - synchronizationNeeded = false;; + customStatusMessage = message; + customStatusId = id; +} + + +wxWindow* BatchStatusUpdater::getWindow() +{ + assert(!silentMode); + if (!silentMode) + return syncStatusFrame; + else + return NULL; } diff --git a/Application.h b/Application.h index 220c81a1..42c425a7 100644 --- a/Application.h +++ b/Application.h @@ -12,7 +12,6 @@ #include #include -#include "FreeFileSync.h" #include "ui/smallDialogs.h" #include "library/misc.h" #include "library/processXml.h" @@ -43,28 +42,31 @@ class LogFile; class BatchStatusUpdater : public StatusHandler { public: - BatchStatusUpdater(bool continueOnError, bool silent, LogFile* log); + BatchStatusUpdater(bool ignoreAllErrors, bool silent, LogFile* log); ~BatchStatusUpdater(); - void updateStatusText(const wxString& text); - void initNewProcess(int objectsTotal, double dataTotal, Process processID); - void updateProcessedData(int objectsProcessed, double dataProcessed); - ErrorHandler::Response reportError(const wxString& text); - void forceUiRefresh(); + virtual void updateStatusText(const wxString& text); + virtual void initNewProcess(int objectsTotal, double dataTotal, Process processID); + virtual void updateProcessedData(int objectsProcessed, double dataProcessed); + virtual ErrorHandler::Response reportError(const wxString& text); + virtual void forceUiRefresh(); - void noSynchronizationNeeded(); + wxWindow* getWindow(); + void setFinalStatus(const wxString& message, SyncStatus::SyncStatusID id); //overwrite final status message text private: - void abortThisProcess(); + virtual void abortThisProcess(); LogFile* m_log; SyncStatus* syncStatusFrame; - bool continueErrors; + bool ignoreErrors; bool silentMode; wxArrayString unhandledErrors; //list of non-resolved errors Process currentProcess; - bool synchronizationNeeded; + + wxString customStatusMessage; //final status message written by service consumer + SyncStatus::SyncStatusID customStatusId; }; diff --git a/Changelog.txt b/Changelog.txt index 0e64fbac..ce24ee35 100644 --- a/Changelog.txt +++ b/Changelog.txt @@ -1,6 +1,17 @@ FreeFileSync ------------ +Changelog v1.13 +--------------- +Automatically detect daylight saving time (DST) change for FAT/FAT32 drives +Added directory dependency check when synchronizing multiple folder pairs +New synchronization option: "update" +Reduced status screen flicker when comparing and synchronizing +Fixed bug when sorting by filename +Further GUI improvements +Updated translation files + + Changelog v1.12 --------------- Significantly improved speed of all sorting algorithms diff --git a/FreeFileSync - Unicode.cbp b/FreeFileSync - Unicode.cbp index 640c289f..7b47c0e2 100644 --- a/FreeFileSync - Unicode.cbp +++ b/FreeFileSync - Unicode.cbp @@ -107,7 +107,6 @@ - + + + + + + + + + +