Files
2025-12-10 14:38:26 -08:00

177 lines
5.7 KiB
C++

// *****************************************************************************
// * 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 <functional>
#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<CancelReason> 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<std::wstring> 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<bool>(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<CancelReason> 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<CancelReason> cancelRequested_;
};
void delayAndCountDown(std::chrono::nanoseconds delay, const std::function<void(const std::wstring& timeRemMsg)>& notifyStatus);
}
#endif //STATUS_HANDLER_H_81704805908341534