diff options
Diffstat (limited to 'ui/batch_status_handler.cpp')
-rw-r--r-- | ui/batch_status_handler.cpp | 288 |
1 files changed, 163 insertions, 125 deletions
diff --git a/ui/batch_status_handler.cpp b/ui/batch_status_handler.cpp index ba70de39..9d57b4e3 100644 --- a/ui/batch_status_handler.cpp +++ b/ui/batch_status_handler.cpp @@ -15,16 +15,56 @@ #include "../shared/global_func.h" #include "../shared/app_main.h" #include "../shared/util.h" +#include "../shared/file_traverser.h" using namespace ffs3; +namespace +{ +class FindLogfiles : public ffs3::TraverseCallback +{ +public: + FindLogfiles(const Zstring& prefix, std::vector<Zstring>& logfiles) : prefix_(prefix), logfiles_(logfiles) {} + + virtual void onFile(const Zchar* shortName, const Zstring& fullName, const FileInfo& details) + { + const Zstring fileName(shortName); + if (fileName.StartsWith(prefix_) && fileName.EndsWith(Zstr(".log"))) + logfiles_.push_back(fullName); + } + + virtual void onSymlink(const Zchar* shortName, const Zstring& fullName, const SymlinkInfo& details) {} + + virtual ReturnValDir onDir(const Zchar* shortName, const Zstring& fullName) + { + return Loki::Int2Type<ReturnValDir::TRAVERSING_DIR_IGNORE>(); //DON'T traverse into subdirs + } + + virtual void onError(const wxString& errorText) {} //errors are not really critical in this context + +private: + const Zstring prefix_; + std::vector<Zstring>& logfiles_; +}; + +void removeFileNoThrow(const Zstring& filename) +{ + try + { + ffs3::removeFile(filename); + } + catch(...) {} +} +} + + class LogFile { public: - LogFile(const wxString& logfileDirectory, const wxString& jobName) //throw (FileError&) + LogFile(const wxString& logfileDirectory, const wxString& jobName) : jobName_(jobName) //throw (FileError&) { - const wxString logfileName = findUniqueLogname(logfileDirectory, jobName); + logfileName = findUniqueLogname(logfileDirectory, jobName); logFile.Open(logfileName, wxT("w")); if (!logFile.IsOpened()) @@ -50,8 +90,8 @@ public: void writeLog(const ErrorLogging& log) { //write actual logfile - const ErrorLogging::MessageEntry& messages = log.getFormattedMessages(); - for (ErrorLogging::MessageEntry::const_iterator i = messages.begin(); i != messages.end(); ++i) + const std::vector<wxString>& messages = log.getFormattedMessages(); + for (std::vector<wxString>::const_iterator i = messages.begin(); i != messages.end(); ++i) logFile.Write(*i + wxChar('\n')); } @@ -65,6 +105,23 @@ public: logFile.Write(wxString(_("Stop")) + wxT(" (") + _("Total time:") + wxT(" ") + (wxTimeSpan::Milliseconds(time)).Format() + wxT(")")); } + void limitLogfileCount(size_t maxCount) const + { + std::vector<Zstring> logFiles; + FindLogfiles traverseCallback(wxToZ(jobName_), logFiles); + + traverseFolder(wxToZ(logfileName).BeforeLast(common::FILE_NAME_SEPARATOR), //throw(); + false, //follow Symlinks + traverseCallback); + + if (logFiles.size() <= maxCount) + return; + + //delete oldest logfiles + std::sort(logFiles.begin(), logFiles.end()); //take advantage of logfile naming convention to sort by age + std::for_each(logFiles.begin(), logFiles.end() - maxCount, ::removeFileNoThrow); + } + private: static wxString findUniqueLogname(const wxString& logfileDirectory, const wxString& jobName) { @@ -101,6 +158,8 @@ private: return output; } + const wxString jobName_; + wxString logfileName; wxFFile logFile; wxStopWatch totalTime; }; @@ -110,6 +169,7 @@ private: BatchStatusHandler::BatchStatusHandler(bool runSilent, const wxString& jobName, const wxString* logfileDirectory, + size_t logFileMaxCount, const xmlAccess::OnError handleError, const SwitchToGui& switchBatchToGui, //functionality to change from batch mode to GUI mode int& returnVal) : @@ -121,11 +181,12 @@ BatchStatusHandler::BatchStatusHandler(bool runSilent, returnValue(returnVal), syncStatusFrame(*this, NULL, runSilent, jobName) { - if (logfileDirectory) + if (logfileDirectory && logFileMaxCount > 0) { try { logFile.reset(new LogFile(*logfileDirectory, jobName)); + logFile->limitLogfileCount(logFileMaxCount); } catch (ffs3::FileError& error) { @@ -141,21 +202,21 @@ BatchStatusHandler::BatchStatusHandler(bool runSilent, BatchStatusHandler::~BatchStatusHandler() { - const int totalErrors = errorLog.errorsTotal(); //evaluate before finalizing log + const int totalErrors = errorLog.typeCount(TYPE_ERROR | TYPE_FATAL_ERROR); //evaluate before finalizing log //finalize error log if (abortIsRequested()) { returnValue = -4; - errorLog.logError(_("Synchronization aborted!")); + errorLog.logMsg(_("Synchronization aborted!"), TYPE_ERROR); } - else if (totalErrors) + else if (totalErrors > 0) { returnValue = -5; - errorLog.logWarning(_("Synchronization completed with errors!")); + errorLog.logMsg(_("Synchronization completed with errors!"), TYPE_WARNING); } else - errorLog.logInfo(_("Synchronization completed successfully!")); + errorLog.logMsg(_("Synchronization completed successfully!"), TYPE_INFO); //print the results list: logfile @@ -170,32 +231,16 @@ BatchStatusHandler::~BatchStatusHandler() } else if (!exitWhenFinished || syncStatusFrame.getAsWindow()->IsShown()) //warning: wxWindow::Show() is called within processHasFinished()! { - //print the results list: GUI - wxString finalMessage; - if (totalErrors > 0) - { - wxString header(_("Warning: Synchronization failed for %x item(s):")); - header.Replace(wxT("%x"), ffs3::numberToStringSep(totalErrors), false); - finalMessage += header + wxT("\n\n"); - } - - const ErrorLogging::MessageEntry& messages = errorLog.getFormattedMessages(); - for (ErrorLogging::MessageEntry::const_iterator i = messages.begin(); i != messages.end(); ++i) - { - finalMessage += *i; - finalMessage += wxT("\n\n"); - } - //notify about (logical) application main window => program won't quit, but stay on this dialog ffs3::AppMainWindow::setMainWindow(syncStatusFrame.getAsWindow()); //notify to syncStatusFrame that current process has ended if (abortIsRequested()) - syncStatusFrame.processHasFinished(SyncStatus::ABORTED, finalMessage); //enable okay and close events - else if (totalErrors) - syncStatusFrame.processHasFinished(SyncStatus::FINISHED_WITH_ERROR, finalMessage); + syncStatusFrame.processHasFinished(SyncStatus::ABORTED, errorLog); //enable okay and close events + else if (totalErrors > 0) + syncStatusFrame.processHasFinished(SyncStatus::FINISHED_WITH_ERROR, errorLog); else - syncStatusFrame.processHasFinished(SyncStatus::FINISHED_WITH_SUCCESS, finalMessage); + syncStatusFrame.processHasFinished(SyncStatus::FINISHED_WITH_SUCCESS, errorLog); } else syncStatusFrame.closeWindowDirectly(); //syncStatusFrame is main window => program will quit directly @@ -205,8 +250,8 @@ BatchStatusHandler::~BatchStatusHandler() inline void BatchStatusHandler::reportInfo(const Zstring& text) { - if (currentProcess == StatusHandler::PROCESS_SYNCHRONIZING && logFile.get()) //write file transfer information to log - errorLog.logInfo(zToWx(text)); //avoid spamming with file copy info: visually identifying warning messages has priority! however when saving to a log file wee need this info + if (currentProcess == StatusHandler::PROCESS_SYNCHRONIZING) //write file transfer information to log + errorLog.logMsg(zToWx(text), TYPE_INFO); //avoid spamming with file copy info: visually identifying warning messages has priority! however when saving to a log file wee need this info syncStatusFrame.setStatusText_NoUpdate(text); } @@ -218,21 +263,21 @@ void BatchStatusHandler::initNewProcess(int objectsTotal, wxLongLong dataTotal, switch (currentProcess) { - case StatusHandler::PROCESS_SCANNING: - syncStatusFrame.resetGauge(0, 0); //initialize some gui elements (remaining time, speed) - syncStatusFrame.setCurrentStatus(SyncStatus::SCANNING); - break; - case StatusHandler::PROCESS_COMPARING_CONTENT: - syncStatusFrame.resetGauge(objectsTotal, dataTotal); - syncStatusFrame.setCurrentStatus(SyncStatus::COMPARING_CONTENT); - break; - case StatusHandler::PROCESS_SYNCHRONIZING: - syncStatusFrame.resetGauge(objectsTotal, dataTotal); - syncStatusFrame.setCurrentStatus(SyncStatus::SYNCHRONIZING); - break; - case StatusHandler::PROCESS_NONE: - assert(false); - break; + case StatusHandler::PROCESS_SCANNING: + syncStatusFrame.resetGauge(0, 0); //initialize some gui elements (remaining time, speed) + syncStatusFrame.setCurrentStatus(SyncStatus::SCANNING); + break; + case StatusHandler::PROCESS_COMPARING_CONTENT: + syncStatusFrame.resetGauge(objectsTotal, dataTotal); + syncStatusFrame.setCurrentStatus(SyncStatus::COMPARING_CONTENT); + break; + case StatusHandler::PROCESS_SYNCHRONIZING: + syncStatusFrame.resetGauge(objectsTotal, dataTotal); + syncStatusFrame.setCurrentStatus(SyncStatus::SYNCHRONIZING); + break; + case StatusHandler::PROCESS_NONE: + assert(false); + break; } } @@ -242,70 +287,64 @@ void BatchStatusHandler::updateProcessedData(int objectsProcessed, wxLongLong da { switch (currentProcess) { - case StatusHandler::PROCESS_SCANNING: - syncStatusFrame.incScannedObjects_NoUpdate(objectsProcessed); - break; - case StatusHandler::PROCESS_COMPARING_CONTENT: - case StatusHandler::PROCESS_SYNCHRONIZING: - syncStatusFrame.incProgressIndicator_NoUpdate(objectsProcessed, dataProcessed); - break; - case StatusHandler::PROCESS_NONE: - assert(false); - break; + case StatusHandler::PROCESS_SCANNING: + syncStatusFrame.incScannedObjects_NoUpdate(objectsProcessed); + break; + case StatusHandler::PROCESS_COMPARING_CONTENT: + case StatusHandler::PROCESS_SYNCHRONIZING: + syncStatusFrame.incProgressIndicator_NoUpdate(objectsProcessed, dataProcessed); + break; + case StatusHandler::PROCESS_NONE: + assert(false); + break; } } -void BatchStatusHandler::logInfo(const wxString& infoMessage) -{ - errorLog.logInfo(infoMessage); -} - - void BatchStatusHandler::reportWarning(const wxString& warningMessage, bool& warningActive) { - errorLog.logWarning(warningMessage); + errorLog.logMsg(warningMessage, TYPE_WARNING); if (!warningActive) return; switch (handleError_) { - case xmlAccess::ON_ERROR_POPUP: - { - //show popup and ask user how to handle warning - bool dontWarnAgain = false; - WarningDlg warningDlg(NULL, - WarningDlg::BUTTON_IGNORE | WarningDlg::BUTTON_SWITCH | WarningDlg::BUTTON_ABORT, - warningMessage + wxT("\n\n") + _("Press \"Switch\" to open FreeFileSync GUI mode."), - dontWarnAgain); - warningDlg.Raise(); - const WarningDlg::Response rv = static_cast<WarningDlg::Response>(warningDlg.ShowModal()); - switch (rv) + case xmlAccess::ON_ERROR_POPUP: { - case WarningDlg::BUTTON_ABORT: - abortThisProcess(); - break; + //show popup and ask user how to handle warning + bool dontWarnAgain = false; + WarningDlg warningDlg(NULL, + WarningDlg::BUTTON_IGNORE | WarningDlg::BUTTON_SWITCH | WarningDlg::BUTTON_ABORT, + warningMessage + wxT("\n\n") + _("Press \"Switch\" to open FreeFileSync GUI mode."), + dontWarnAgain); + warningDlg.Raise(); + const WarningDlg::Response rv = static_cast<WarningDlg::Response>(warningDlg.ShowModal()); + switch (rv) + { + case WarningDlg::BUTTON_ABORT: + abortThisProcess(); + break; + + case WarningDlg::BUTTON_SWITCH: + errorLog.logMsg(_("Switching to FreeFileSync GUI mode..."), TYPE_WARNING); + switchToGuiRequested = true; + abortThisProcess(); + break; + + case WarningDlg::BUTTON_IGNORE: //no unhandled error situation! + warningActive = !dontWarnAgain; + break; + } + } + break; //keep it! last switch might not find match - case WarningDlg::BUTTON_SWITCH: - errorLog.logWarning(_("Switching to FreeFileSync GUI mode...")); - switchToGuiRequested = true; + case xmlAccess::ON_ERROR_EXIT: //abort abortThisProcess(); break; - case WarningDlg::BUTTON_IGNORE: //no unhandled error situation! - warningActive = !dontWarnAgain; + case xmlAccess::ON_ERROR_IGNORE: //no unhandled error situation! break; - } - } - break; //keep it! last switch might not find match - - case xmlAccess::ON_ERROR_EXIT: //abort - abortThisProcess(); - break; - - case xmlAccess::ON_ERROR_IGNORE: //no unhandled error situation! - break; } } @@ -314,40 +353,40 @@ ErrorHandler::Response BatchStatusHandler::reportError(const wxString& errorMess { switch (handleError_) { - case xmlAccess::ON_ERROR_POPUP: - { - bool ignoreNextErrors = false; - ErrorDlg errorDlg(NULL, - ErrorDlg::BUTTON_IGNORE | ErrorDlg::BUTTON_RETRY | ErrorDlg::BUTTON_ABORT, - errorMessage, - ignoreNextErrors); - errorDlg.Raise(); - const ErrorDlg::ReturnCodes rv = static_cast<ErrorDlg::ReturnCodes>(errorDlg.ShowModal()); - switch (rv) + case xmlAccess::ON_ERROR_POPUP: { - case ErrorDlg::BUTTON_IGNORE: - if (ignoreNextErrors) //falsify only - handleError_ = xmlAccess::ON_ERROR_IGNORE; - errorLog.logError(errorMessage); - return ErrorHandler::IGNORE_ERROR; - - case ErrorDlg::BUTTON_RETRY: - return ErrorHandler::RETRY; - - case ErrorDlg::BUTTON_ABORT: - errorLog.logError(errorMessage); - abortThisProcess(); + bool ignoreNextErrors = false; + ErrorDlg errorDlg(NULL, + ErrorDlg::BUTTON_IGNORE | ErrorDlg::BUTTON_RETRY | ErrorDlg::BUTTON_ABORT, + errorMessage, + ignoreNextErrors); + errorDlg.Raise(); + const ErrorDlg::ReturnCodes rv = static_cast<ErrorDlg::ReturnCodes>(errorDlg.ShowModal()); + switch (rv) + { + case ErrorDlg::BUTTON_IGNORE: + if (ignoreNextErrors) //falsify only + handleError_ = xmlAccess::ON_ERROR_IGNORE; + errorLog.logMsg(errorMessage, TYPE_ERROR); + return ErrorHandler::IGNORE_ERROR; + + case ErrorDlg::BUTTON_RETRY: + return ErrorHandler::RETRY; + + case ErrorDlg::BUTTON_ABORT: + errorLog.logMsg(errorMessage, TYPE_ERROR); + abortThisProcess(); + } } - } - break; //used if last switch didn't find a match + break; //used if last switch didn't find a match - case xmlAccess::ON_ERROR_EXIT: //abort - errorLog.logError(errorMessage); - abortThisProcess(); + case xmlAccess::ON_ERROR_EXIT: //abort + errorLog.logMsg(errorMessage, TYPE_ERROR); + abortThisProcess(); - case xmlAccess::ON_ERROR_IGNORE: - errorLog.logError(errorMessage); - return ErrorHandler::IGNORE_ERROR; + case xmlAccess::ON_ERROR_IGNORE: + errorLog.logMsg(errorMessage, TYPE_ERROR); + return ErrorHandler::IGNORE_ERROR; } assert(false); @@ -361,8 +400,7 @@ void BatchStatusHandler::reportFatalError(const wxString& errorMessage) if (handleError_ == xmlAccess::ON_ERROR_POPUP) exitWhenFinished = false; //log fatal error and show it on status dialog - errorLog.logFatalError(errorMessage); - abortThisProcess(); + errorLog.logMsg(errorMessage, TYPE_FATAL_ERROR); } |