diff options
Diffstat (limited to 'ui/batch_status_handler.cpp')
-rw-r--r-- | ui/batch_status_handler.cpp | 477 |
1 files changed, 0 insertions, 477 deletions
diff --git a/ui/batch_status_handler.cpp b/ui/batch_status_handler.cpp deleted file mode 100644 index bca6b484..00000000 --- a/ui/batch_status_handler.cpp +++ /dev/null @@ -1,477 +0,0 @@ -// ************************************************************************** -// * This file is part of the FreeFileSync project. It is distributed under * -// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * -// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * -// ************************************************************************** - -#include "batch_status_handler.h" -#include <zen/file_handling.h> -#include <zen/file_traverser.h> -//#include <zen/format_unit.h> -#include <zen/shell_execute.h> -#include <wx+/popup_dlg.h> -#include <wx/app.h> -#include "exec_finished_box.h" -#include "../lib/ffs_paths.h" -#include "../lib/resolve_path.h" -#include "../lib/status_handler_impl.h" -#include "../lib/generate_logfile.h" - -using namespace zen; - - -namespace -{ -class FindLogfiles : public TraverseCallback -{ -public: - FindLogfiles(const Zstring& prefix, std::vector<Zstring>& logfiles) : prefix_(prefix), logfiles_(logfiles) {} - -private: - virtual void onFile(const Zchar* shortName, const Zstring& fullName, const FileInfo& details) - { - const Zstring fileName(shortName); - if (startsWith(fileName, prefix_) && endsWith(fileName, Zstr(".log"))) - logfiles_.push_back(fullName); - } - - virtual TraverseCallback* onDir (const Zchar* shortName, const Zstring& fullName) { return nullptr; } //DON'T traverse into subdirs - virtual HandleLink onSymlink(const Zchar* shortName, const Zstring& fullName, const SymlinkInfo& details) { return LINK_SKIP; } - virtual HandleError reportDirError (const std::wstring& msg, size_t retryNumber) { assert(false); return ON_ERROR_IGNORE; } //errors are not really critical in this context - virtual HandleError reportItemError(const std::wstring& msg, size_t retryNumber, const Zchar* shortName) { assert(false); return ON_ERROR_IGNORE; } // - - const Zstring prefix_; - std::vector<Zstring>& logfiles_; -}; - - -void limitLogfileCount(const Zstring& logdir, const std::wstring& jobname, size_t maxCount) //throw() -{ - std::vector<Zstring> logFiles; - FindLogfiles traverseCallback(utfCvrtTo<Zstring>(jobname), logFiles); //throw()! - - traverseFolder(logdir, - traverseCallback); - - if (logFiles.size() <= maxCount) - return; - - //delete oldest logfiles: take advantage of logfile naming convention to find them - std::nth_element(logFiles.begin(), logFiles.end() - maxCount, logFiles.end(), LessFilename()); - - std::for_each(logFiles.begin(), logFiles.end() - maxCount, - [](const Zstring& filename) { try { removeFile(filename); } catch (FileError&) {} }); -} - - -std::unique_ptr<FileOutput> prepareNewLogfile(const Zstring& logfileDirectory, //throw FileError - const std::wstring& jobName, - const TimeComp& timeStamp) //return value always bound! -{ - //create logfile directory if required - Zstring logfileDir = logfileDirectory.empty() ? - getConfigDir() + Zstr("Logs") : - getFormattedDirectoryName(logfileDirectory); - - makeDirectory(logfileDir); //throw FileError - - //assemble logfile name - const Zstring body = appendSeparator(logfileDir) + utfCvrtTo<Zstring>(jobName) + Zstr(" ") + formatTime<Zstring>(Zstr("%Y-%m-%d %H%M%S"), timeStamp); - - //ensure uniqueness - for (int i = 0;; ++i) - try - { - const Zstring& filename = i == 0 ? - body + Zstr(".log") : - body + Zstr('_') + numberTo<Zstring>(i) + Zstr(".log"); - - return make_unique<FileOutput>(filename, FileOutput::ACC_CREATE_NEW); //throw FileError, ErrorTargetExisting - //*no* file system race-condition! - } - catch (const ErrorTargetExisting&) {} -} -} - -//############################################################################################################################## - -BatchStatusHandler::BatchStatusHandler(bool showProgress, - const std::wstring& jobName, - const TimeComp& timeStamp, - const Zstring& logfileDirectory, //may be empty - int logfilesCountLimit, - size_t lastSyncsLogFileSizeMax, - const xmlAccess::OnError handleError, - size_t automaticRetryCount, - size_t automaticRetryDelay, - const SwitchToGui& switchBatchToGui, //functionality to change from batch mode to GUI mode - FfsReturnCode& returnCode, - const std::wstring& execWhenFinished, - std::vector<std::wstring>& execFinishedHistory) : - switchBatchToGui_(switchBatchToGui), - showFinalResults(showProgress), //=> exit immediately or wait when finished - switchToGuiRequested(false), - logfilesCountLimit_(logfilesCountLimit), - lastSyncsLogFileSizeMax_(lastSyncsLogFileSizeMax), - handleError_(handleError), - returnCode_(returnCode), - automaticRetryCount_(automaticRetryCount), - automaticRetryDelay_(automaticRetryDelay), - progressDlg(createProgressDialog(*this, [this] { this->onProgressDialogTerminate(); }, *this, nullptr, showProgress, jobName, execWhenFinished, execFinishedHistory)), - jobName_(jobName) -{ - if (logfilesCountLimit != 0) - { - zen::Opt<std::wstring> errMsg = tryReportingError([&] { logFile = prepareNewLogfile(logfileDirectory, jobName, timeStamp); }, //throw FileError; return value always bound! - *this); - if (errMsg) - { - raiseReturnCode(returnCode_, FFS_RC_ABORTED); - throw BatchAbortProcess(); - } - } - - totalTime.Start(); //measure total time - - //if (logFile) - // ::wxSetEnv(L"logfile", utfCvrtTo<wxString>(logFile->getFilename())); -} - - -BatchStatusHandler::~BatchStatusHandler() -{ - //------------ "on completion" command conceptually is part of the sync, not cleanup -------------------------------------- - - //decide whether to stay on status screen or exit immediately... - if (switchToGuiRequested) //-> avoid recursive yield() calls, thous switch not before ending batch mode - { - try - { - switchBatchToGui_.execute(); //open FreeFileSync GUI - } - catch (...) {} - showFinalResults = false; - } - else if (progressDlg) - { - if (progressDlg->getWindowIfVisible()) - showFinalResults = true; - - //execute "on completion" command (even in case of ignored errors) - if (!abortIsRequested()) //if aborted (manually), we don't execute the command - { - const std::wstring finalCommand = progressDlg->getExecWhenFinishedCommand(); //final value (after possible user modification) - if (!finalCommand.empty()) - { - if (isCloseProgressDlgCommand(finalCommand)) - showFinalResults = false; //take precedence over current visibility status - else - try - { - tryReportingError([&] { shellExecute2(expandMacros(utfCvrtTo<Zstring>(finalCommand)), EXEC_TYPE_SYNC); }, //throw FileError, throw X? - *this); - } - catch (...) {} - } - } - } - //------------ end of sync: begin of cleanup -------------------------------------- - - const int totalErrors = errorLog.getItemCount(TYPE_ERROR | TYPE_FATAL_ERROR); //evaluate before finalizing log - const int totalWarnings = errorLog.getItemCount(TYPE_WARNING); - - //finalize error log - std::wstring finalStatus; - if (abortIsRequested()) - { - raiseReturnCode(returnCode_, FFS_RC_ABORTED); - finalStatus = _("Synchronization stopped"); - errorLog.logMsg(finalStatus, TYPE_ERROR); - } - else if (totalErrors > 0) - { - raiseReturnCode(returnCode_, FFS_RC_FINISHED_WITH_ERRORS); - finalStatus = _("Synchronization completed with errors"); - errorLog.logMsg(finalStatus, TYPE_ERROR); - } - else if (totalWarnings > 0) - { - raiseReturnCode(returnCode_, FFS_RC_FINISHED_WITH_WARNINGS); - finalStatus = _("Synchronization completed with warnings"); - errorLog.logMsg(finalStatus, TYPE_WARNING); - } - else - { - if (getObjectsTotal(PHASE_SYNCHRONIZING) == 0 && //we're past "initNewPhase(PHASE_SYNCHRONIZING)" at this point! - getDataTotal (PHASE_SYNCHRONIZING) == 0) - finalStatus = _("Nothing to synchronize"); //even if "ignored conflicts" occurred! - else - finalStatus = _("Synchronization completed successfully"); - errorLog.logMsg(finalStatus, TYPE_INFO); - } - - const SummaryInfo summary = - { - jobName_, - finalStatus, - getObjectsCurrent(PHASE_SYNCHRONIZING), getDataCurrent(PHASE_SYNCHRONIZING), - getObjectsTotal (PHASE_SYNCHRONIZING), getDataTotal (PHASE_SYNCHRONIZING), - totalTime.Time() / 1000 - }; - - //print the results list: logfile - if (logFile.get()) - { - try - { - //saving log file below may take a *long* time, so report (without logging) - reportStatus(replaceCpy(_("Saving log file %x..."), L"%x", fmtFileName(logFile->getFilename()))); //throw? - forceUiRefresh(); // - } - catch (...) {} - - if (logfilesCountLimit_ > 0) - limitLogfileCount(beforeLast(logFile->getFilename(), FILE_NAME_SEPARATOR), jobName_, logfilesCountLimit_); //throw() - - try - { - saveLogToFile(summary, errorLog, *logFile); //throw FileError - } - catch (FileError&) {} - } - try - { - saveToLastSyncsLog(summary, errorLog, lastSyncsLogFileSizeMax_); //throw FileError - } - catch (FileError&) {} - - if (progressDlg) - { - if (showFinalResults) //warning: wxWindow::Show() is called within processHasFinished()! - { - //notify about (logical) application main window => program won't quit, but stay on this dialog - //setMainWindow(progressDlg->getAsWindow()); -> not required anymore since we block waiting until dialog is closed below - - //notify to progressDlg that current process has ended - if (abortIsRequested()) - progressDlg->processHasFinished(SyncProgressDialog::RESULT_ABORTED, errorLog); //enable okay and close events - else if (totalErrors > 0) - progressDlg->processHasFinished(SyncProgressDialog::RESULT_FINISHED_WITH_ERROR, errorLog); - else if (totalWarnings > 0) - progressDlg->processHasFinished(SyncProgressDialog::RESULT_FINISHED_WITH_WARNINGS, errorLog); - else - progressDlg->processHasFinished(SyncProgressDialog::RESULT_FINISHED_WITH_SUCCESS, errorLog); - } - else - progressDlg->closeWindowDirectly(); //progressDlg is main window => program will quit directly - - //wait until progress dialog notified shutdown via onProgressDialogTerminate() - //-> required since it has our "this" pointer captured in lambda "notifyWindowTerminate"! - //-> nicely manages dialog lifetime - while (progressDlg) - { - wxTheApp->Yield(); //*first* refresh GUI (removing flicker) before sleeping! - boost::this_thread::sleep(boost::posix_time::milliseconds(UI_UPDATE_INTERVAL)); - } - } -} - - -void BatchStatusHandler::initNewPhase(int objectsTotal, Int64 dataTotal, ProcessCallback::Phase phaseID) -{ - StatusHandler::initNewPhase(objectsTotal, dataTotal, phaseID); - if (progressDlg) - progressDlg->initNewPhase(); //call after "StatusHandler::initNewPhase" -} - - -void BatchStatusHandler::updateProcessedData(int objectsDelta, Int64 dataDelta) -{ - StatusHandler::updateProcessedData(objectsDelta, dataDelta); - - if (progressDlg) - progressDlg->notifyProgressChange(); //noexcept - //note: this method should NOT throw in order to properly allow undoing setting of statistics! -} - - -void BatchStatusHandler::reportInfo(const std::wstring& text) -{ - StatusHandler::reportInfo(text); - errorLog.logMsg(text, TYPE_INFO); -} - - -void BatchStatusHandler::reportWarning(const std::wstring& warningMessage, bool& warningActive) -{ - errorLog.logMsg(warningMessage, TYPE_WARNING); - - if (!warningActive) - return; - - switch (handleError_) - { - case xmlAccess::ON_ERROR_POPUP: - { - if (!progressDlg) abortThisProcess(); - PauseTimers dummy(*progressDlg); - forceUiRefresh(); - - bool dontWarnAgain = false; - switch (showConfirmationDialog3(progressDlg->getWindowIfVisible(), DialogInfoType::WARNING, PopupDialogCfg3(). - setDetailInstructions(warningMessage + L"\n\n" + _("You can switch to FreeFileSync's main window to resolve this issue.")). - setCheckBox(dontWarnAgain, _("&Don't show this warning again"), ConfirmationButton3::DONT_DO_IT), - _("&Ignore"), _("&Switch"))) - { - case ConfirmationButton3::DO_IT: //ignore - warningActive = !dontWarnAgain; - break; - - case ConfirmationButton3::DONT_DO_IT: //switch - errorLog.logMsg(_("Switching to FreeFileSync's main window"), TYPE_INFO); - switchToGuiRequested = true; - abortThisProcess(); - break; - - case ConfirmationButton3::CANCEL: - abortThisProcess(); - break; - } - } - break; //keep it! last switch might not find match - - case xmlAccess::ON_ERROR_STOP: - abortThisProcess(); - break; - - case xmlAccess::ON_ERROR_IGNORE: - break; - } -} - - -ProcessCallback::Response BatchStatusHandler::reportError(const std::wstring& errorMessage, size_t retryNumber) -{ - //auto-retry - if (retryNumber < automaticRetryCount_) - { - errorLog.logMsg(errorMessage + L"\n=> " + - _P("Automatic retry in 1 second...", "Automatic retry in %x seconds...", automaticRetryDelay_), TYPE_INFO); - //delay - const int iterations = static_cast<int>(1000 * automaticRetryDelay_ / UI_UPDATE_INTERVAL); //always round down: don't allow for negative remaining time below - for (int i = 0; i < iterations; ++i) - { - reportStatus(_("Error") + L": " + _P("Automatic retry in 1 second...", "Automatic retry in %x seconds...", - (1000 * automaticRetryDelay_ - i * UI_UPDATE_INTERVAL + 999) / 1000)); //integer round up - boost::this_thread::sleep(boost::posix_time::milliseconds(UI_UPDATE_INTERVAL)); - } - return ProcessCallback::RETRY; - } - - - //always, except for "retry": - zen::ScopeGuard guardWriteLog = zen::makeGuard([&] { errorLog.logMsg(errorMessage, TYPE_ERROR); }); - - switch (handleError_) - { - case xmlAccess::ON_ERROR_POPUP: - { - if (!progressDlg) abortThisProcess(); - PauseTimers dummy(*progressDlg); - forceUiRefresh(); - - bool ignoreNextErrors = false; - switch (showConfirmationDialog3(progressDlg->getWindowIfVisible(), DialogInfoType::ERROR2, PopupDialogCfg3(). - setDetailInstructions(errorMessage). - setCheckBox(ignoreNextErrors, _("&Ignore subsequent errors"), ConfirmationButton3::DONT_DO_IT), - _("&Ignore"), _("&Retry"))) - { - case ConfirmationButton3::DO_IT: //ignore - if (ignoreNextErrors) //falsify only - handleError_ = xmlAccess::ON_ERROR_IGNORE; - return ProcessCallback::IGNORE_ERROR; - - case ConfirmationButton3::DONT_DO_IT: //retry - guardWriteLog.dismiss(); - errorLog.logMsg(errorMessage + L"\n=> " + _("Retrying operation..."), TYPE_INFO); - return ProcessCallback::RETRY; - - case ConfirmationButton3::CANCEL: - abortThisProcess(); - break; - } - } - break; //used if last switch didn't find a match - - case xmlAccess::ON_ERROR_STOP: - abortThisProcess(); - break; - - case xmlAccess::ON_ERROR_IGNORE: - return ProcessCallback::IGNORE_ERROR; - } - - assert(false); - return ProcessCallback::IGNORE_ERROR; //dummy value -} - - -void BatchStatusHandler::reportFatalError(const std::wstring& errorMessage) -{ - errorLog.logMsg(errorMessage, TYPE_FATAL_ERROR); - - switch (handleError_) - { - case xmlAccess::ON_ERROR_POPUP: - { - if (!progressDlg) abortThisProcess(); - PauseTimers dummy(*progressDlg); - forceUiRefresh(); - - bool ignoreNextErrors = false; - switch (showConfirmationDialog(progressDlg->getWindowIfVisible(), DialogInfoType::ERROR2, - PopupDialogCfg().setTitle(_("Serious Error")). - setDetailInstructions(errorMessage). - setCheckBox(ignoreNextErrors, _("&Ignore subsequent errors")), - _("&Ignore"))) - { - case ConfirmationButton::DO_IT: - if (ignoreNextErrors) //falsify only - handleError_ = xmlAccess::ON_ERROR_IGNORE; - break; - case ConfirmationButton::CANCEL: - abortThisProcess(); - break; - } - } - break; - - case xmlAccess::ON_ERROR_STOP: - abortThisProcess(); - break; - - case xmlAccess::ON_ERROR_IGNORE: - break; - } -} - - -void BatchStatusHandler::forceUiRefresh() -{ - if (progressDlg) - progressDlg->updateGui(); -} - - -void BatchStatusHandler::abortThisProcess() -{ - requestAbortion(); //just make sure... - throw BatchAbortProcess(); //abort can be triggered by progressDlg -} - - -void BatchStatusHandler::onProgressDialogTerminate() -{ - //it's responsibility of "progressDlg" to call requestAbortion() when closing dialog - progressDlg = nullptr; -} |