// ***************************************************************************** // * This file is part of the FreeFileSync project. It is distributed under * // * GNU General Public License: https://www.gnu.org/licenses/gpl-3.0 * // * Copyright (C) Zenju (zenju AT freefilesync DOT org) - All Rights Reserved * // ***************************************************************************** #ifndef STATUS_HANDLER_H_81704805908341534 #define STATUS_HANDLER_H_81704805908341534 #include #include "base/process_callback.h" #include "return_codes.h" namespace fff { bool uiUpdateDue(bool force = false); //test if a specific amount of time is over /* Updating GUI is fast! time per call to ProcessCallback::forceUiRefresh() - Comparison 0.025 ms - Synchronization 0.74 ms (despite complex graph control!) */ //Exception class used to abort the "compare" and "sync" process class CancelProcess {}; enum class CancelReason { user, firstError, }; //GUI may want to abort process struct CancelCallback { virtual ~CancelCallback() {} virtual void userRequestCancel() = 0; }; struct ProgressStats { int items = 0; int64_t bytes = 0; bool operator==(const ProgressStats&) const = default; }; //common statistics "everybody" needs struct Statistics { virtual ~Statistics() {} virtual ProcessPhase currentPhase() const = 0; virtual ProgressStats getCurrentStats() const = 0; virtual ProgressStats getTotalStats () const = 0; struct ErrorStats { int errorCount; int warningCount; }; virtual ErrorStats getErrorStats() const = 0; virtual std::optional taskCancelled() const = 0; virtual const std::wstring& currentStatusText() const = 0; }; struct ProcessSummary { std::chrono::system_clock::time_point startTime; TaskResult result = TaskResult::cancelled; std::vector jobNames; //may be empty ProgressStats statsProcessed; ProgressStats statsTotal; std::chrono::milliseconds totalTime{}; }; //partial callback implementation with common functionality for "batch", "GUI/Compare" and "GUI/Sync" class StatusHandler : public ProcessCallback, public CancelCallback, public Statistics { public: //StatusHandler() {} //implement parts of ProcessCallback void initNewPhase(int itemsTotal, int64_t bytesTotal, ProcessPhase phase) override //(throw X) { assert((itemsTotal < 0) == (bytesTotal < 0)); currentPhase_ = phase; statsCurrent_ = {}; statsTotal_ = {itemsTotal, bytesTotal}; } void updateDataProcessed(int itemsDelta, int64_t bytesDelta) override { updateData(statsCurrent_, itemsDelta, bytesDelta); } //note: these methods MUST NOT throw in order void updateDataTotal (int itemsDelta, int64_t bytesDelta) override { updateData(statsTotal_, itemsDelta, bytesDelta); } //to allow usage within destructors! void requestUiUpdate(bool force) final //throw CancelProcess { if (uiUpdateDue(force)) { const bool abortRequestedBefore = static_cast(cancelRequested_); forceUiUpdateNoThrow(); //triggered by userRequestCancel() // => sufficient to evaluate occasionally when uiUpdateDue()! // => refresh *before* throwing: support requestUiUpdate() during destruction if (cancelRequested_) { if (!abortRequestedBefore) forceUiUpdateNoThrow(); //immediately show the "Stop requested..." status after user clicked cancel throw CancelProcess(); } } } virtual void forceUiUpdateNoThrow() = 0; //noexcept void updateStatus(std::wstring&& msg) final //throw CancelProcess { //assert(!msg.empty()); -> possible, e.g. start of parallel scan statusText_ = std::move(msg); //update *before* running operations that can throw requestUiUpdate(false /*force*/); //throw CancelProcess } [[noreturn]] void cancelProcessNow(CancelReason reason) { if (!cancelRequested_ || reason == CancelReason::user) //CancelReason::user overwrites CancelReason::firstError cancelRequested_ = reason; forceUiUpdateNoThrow(); //flush GUI to show new cancelled state throw CancelProcess(); } //implement CancelCallback void userRequestCancel() final { cancelRequested_ = CancelReason::user; //may overwrite CancelReason::firstError } //called from GUI code: this does NOT call cancelProcessNow() immediately, but later when we're out of the C GUI call stack //=> don't call forceUiUpdateNoThrow() here! //implement Statistics ProcessPhase currentPhase() const final { return currentPhase_; } ProgressStats getCurrentStats() const override { return statsCurrent_; } ProgressStats getTotalStats () const override { return statsTotal_; } const std::wstring& currentStatusText() const override { return statusText_; } std::optional taskCancelled() const override { return cancelRequested_; } private: void updateData(ProgressStats& stats, int itemsDelta, int64_t bytesDelta) { assert(stats.items >= 0); assert(stats.bytes >= 0); stats.items += itemsDelta; stats.bytes += bytesDelta; } ProcessPhase currentPhase_ = ProcessPhase::none; ProgressStats statsCurrent_; ProgressStats statsTotal_{-1, -1}; std::wstring statusText_; std::optional cancelRequested_; }; void delayAndCountDown(std::chrono::nanoseconds delay, const std::function& notifyStatus); } #endif //STATUS_HANDLER_H_81704805908341534