177 lines
5.7 KiB
C++
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
|