diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index b31d224ab..a25a7d8b1 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt @@ -41,6 +41,8 @@ add_library(common null_audio_stream.cpp null_audio_stream.h rectangle.h + progress_callback.cpp + progress_callback.h state_wrapper.cpp state_wrapper.h string.cpp diff --git a/src/common/common.vcxproj b/src/common/common.vcxproj index 8a993f302..9e5559329 100644 --- a/src/common/common.vcxproj +++ b/src/common/common.vcxproj @@ -60,6 +60,7 @@ + @@ -94,6 +95,7 @@ + diff --git a/src/common/common.vcxproj.filters b/src/common/common.vcxproj.filters index 84877fa9d..450591a77 100644 --- a/src/common/common.vcxproj.filters +++ b/src/common/common.vcxproj.filters @@ -51,6 +51,7 @@ d3d11 + @@ -98,6 +99,7 @@ d3d11 + diff --git a/src/common/progress_callback.cpp b/src/common/progress_callback.cpp new file mode 100644 index 000000000..55f334321 --- /dev/null +++ b/src/common/progress_callback.cpp @@ -0,0 +1,397 @@ +#include "progress_callback.h" +#include "assert.h" +#include "byte_stream.h" +#include "log.h" +#include +#include +#include +Log_SetChannel(ProgressCallback); + +ProgressCallback::~ProgressCallback() {} + +void ProgressCallback::SetFormattedStatusText(const char* Format, ...) +{ + SmallString str; + va_list ap; + + va_start(ap, Format); + str.FormatVA(Format, ap); + va_end(ap); + + SetStatusText(str); +} + +void ProgressCallback::DisplayFormattedError(const char* format, ...) +{ + SmallString str; + va_list ap; + + va_start(ap, format); + str.FormatVA(format, ap); + va_end(ap); + + DisplayError(str); +} + +void ProgressCallback::DisplayFormattedWarning(const char* format, ...) +{ + SmallString str; + va_list ap; + + va_start(ap, format); + str.FormatVA(format, ap); + va_end(ap); + + DisplayWarning(str); +} + +void ProgressCallback::DisplayFormattedInformation(const char* format, ...) +{ + SmallString str; + va_list ap; + + va_start(ap, format); + str.FormatVA(format, ap); + va_end(ap); + + DisplayInformation(str); +} + +void ProgressCallback::DisplayFormattedDebugMessage(const char* format, ...) +{ + SmallString str; + va_list ap; + + va_start(ap, format); + str.FormatVA(format, ap); + va_end(ap); + + DisplayDebugMessage(str); +} + +void ProgressCallback::DisplayFormattedModalError(const char* format, ...) +{ + SmallString str; + va_list ap; + + va_start(ap, format); + str.FormatVA(format, ap); + va_end(ap); + + ModalError(str); +} + +bool ProgressCallback::DisplayFormattedModalConfirmation(const char* format, ...) +{ + SmallString str; + va_list ap; + + va_start(ap, format); + str.FormatVA(format, ap); + va_end(ap); + + return ModalConfirmation(str); +} + +void ProgressCallback::UpdateProgressFromStream(ByteStream* pStream) +{ + u32 streamSize = (u32)pStream->GetSize(); + u32 streamPosition = (u32)pStream->GetPosition(); + + SetProgressRange(streamSize); + SetProgressValue(streamPosition); +} + +class NullProgressCallbacks final : public ProgressCallback +{ +public: + void PushState() override {} + void PopState() override {} + + bool IsCancelled() const override { return false; } + bool IsCancellable() const override { return false; } + + void SetCancellable(bool cancellable) override {} + void SetStatusText(const char* statusText) override {} + void SetProgressRange(u32 range) override {} + void SetProgressValue(u32 value) override {} + void IncrementProgressValue() override {} + + void DisplayError(const char* message) override { Log_ErrorPrint(message); } + void DisplayWarning(const char* message) override { Log_WarningPrint(message); } + void DisplayInformation(const char* message) override { Log_InfoPrint(message); } + void DisplayDebugMessage(const char* message) override { Log_DevPrint(message); } + + void ModalError(const char* message) override { Log_ErrorPrint(message); } + bool ModalConfirmation(const char* message) override + { + Log_InfoPrint(message); + return false; + } + u32 ModalPrompt(const char* message, u32 nOptions, ...) override + { + DebugAssert(nOptions > 0); + Log_InfoPrint(message); + return 0; + } +}; + +static NullProgressCallbacks s_nullProgressCallbacks; +ProgressCallback* ProgressCallback::NullProgressCallback = &s_nullProgressCallbacks; + +BaseProgressCallback::BaseProgressCallback() + : m_cancellable(false), m_cancelled(false), m_progress_range(1), m_progress_value(0), m_base_progress_value(0), + m_saved_state(NULL) +{ +} + +BaseProgressCallback::~BaseProgressCallback() +{ + State* pNextState = m_saved_state; + while (pNextState != NULL) + { + State* pCurrentState = pNextState; + pNextState = pCurrentState->next_saved_state; + delete pCurrentState; + } +} + +void BaseProgressCallback::PushState() +{ + State* pNewState = new State; + pNewState->cancellable = m_cancellable; + pNewState->status_text = m_status_text; + pNewState->progress_range = m_progress_range; + pNewState->progress_value = m_progress_value; + pNewState->base_progress_value = m_base_progress_value; + pNewState->next_saved_state = m_saved_state; + m_saved_state = pNewState; +} + +void BaseProgressCallback::PopState() +{ + DebugAssert(m_saved_state); + State* state = m_saved_state; + m_saved_state = nullptr; + + // impose the current position into the previous range + const u32 new_progress_value = + (m_progress_range != 0) ? + static_cast(((float)m_progress_value / (float)m_progress_range) * (float)state->progress_range) : + state->progress_value; + + SetCancellable(state->cancellable); + SetStatusText(state->status_text); + SetProgressRange(state->progress_range); + SetProgressValue(new_progress_value); + + m_base_progress_value = state->base_progress_value; + m_saved_state = state->next_saved_state; + delete state; +} + +bool BaseProgressCallback::IsCancelled() const +{ + return m_cancelled; +} + +bool BaseProgressCallback::IsCancellable() const +{ + return m_cancellable; +} + +void BaseProgressCallback::SetCancellable(bool cancellable) +{ + m_cancellable = cancellable; +} + +void BaseProgressCallback::SetStatusText(const char* text) +{ + m_status_text = text; +} + +void BaseProgressCallback::SetProgressRange(u32 range) +{ + if (m_saved_state) + { + // impose the previous range on this range + m_progress_range = m_saved_state->progress_range * range; + m_base_progress_value = m_progress_value = m_saved_state->progress_value * range; + } + else + { + m_progress_range = range; + m_progress_value = 0; + m_base_progress_value = 0; + } +} + +void BaseProgressCallback::SetProgressValue(u32 value) +{ + m_progress_value = m_base_progress_value + value; +} + +void BaseProgressCallback::IncrementProgressValue() +{ + SetProgressValue((m_progress_value - m_base_progress_value) + 1); +} + +ConsoleProgressCallback::ConsoleProgressCallback() + : BaseProgressCallback(), m_last_percent_complete(std::numeric_limits::infinity()), + m_last_bar_length(0xFFFFFFFF) +{ +} + +ConsoleProgressCallback::~ConsoleProgressCallback() +{ + Clear(); +} + +void ConsoleProgressCallback::PushState() +{ + BaseProgressCallback::PushState(); +} + +void ConsoleProgressCallback::PopState() +{ + BaseProgressCallback::PopState(); + Redraw(false); +} + +void ConsoleProgressCallback::SetCancellable(bool cancellable) +{ + BaseProgressCallback::SetCancellable(cancellable); + Redraw(false); +} + +void ConsoleProgressCallback::SetStatusText(const char* text) +{ + BaseProgressCallback::SetStatusText(text); + Redraw(false); +} + +void ConsoleProgressCallback::SetProgressRange(u32 range) +{ + u32 last_range = m_progress_range; + + BaseProgressCallback::SetProgressRange(range); + + if (m_progress_range != last_range) + Redraw(false); +} + +void ConsoleProgressCallback::SetProgressValue(u32 value) +{ + u32 lastValue = m_progress_value; + + BaseProgressCallback::SetProgressValue(value); + + if (m_progress_value != lastValue) + Redraw(true); +} + +void ConsoleProgressCallback::Clear() +{ + SmallString message; + for (u32 i = 0; i < COLUMNS; i++) + message.AppendCharacter(' '); + message.AppendCharacter('\r'); + + std::fwrite(message.GetCharArray(), message.GetLength(), 1, stderr); + std::fflush(stderr); +} + +void ConsoleProgressCallback::Redraw(bool update_value_only) +{ + float percent_complete = (m_progress_range > 0) ? ((float)m_progress_value / (float)m_progress_range) * 100.0f : 0.0f; + if (percent_complete > 100.0f) + percent_complete = 100.0f; + + const u32 current_length = m_status_text.GetLength() + 14; + const u32 max_bar_length = (current_length < COLUMNS) ? COLUMNS - current_length : 0; + const u32 current_bar_length = + (max_bar_length > 0) ? (static_cast(percent_complete / 100.0f * (float)max_bar_length)) : 0; + + if (update_value_only && (current_bar_length == m_last_bar_length) && + std::abs(percent_complete - m_last_percent_complete) < 0.01f) + { + return; + } + + m_last_bar_length = current_bar_length; + m_last_percent_complete = percent_complete; + + SmallString message; + message.AppendString(m_status_text); + message.AppendFormattedString(" [%.2f%%]", percent_complete); + + if (max_bar_length > 0) + { + message.AppendString(" |"); + + u32 i; + for (i = 0; i < current_bar_length; i++) + message.AppendCharacter('='); + for (; i < max_bar_length; i++) + message.AppendCharacter(' '); + + message.AppendString("|"); + } + + message.AppendCharacter('\r'); + + std::fwrite(message.GetCharArray(), message.GetLength(), 1, stderr); + std::fflush(stderr); +} + +void ConsoleProgressCallback::DisplayError(const char* message) +{ + Clear(); + Log_ErrorPrint(message); + Redraw(false); +} + +void ConsoleProgressCallback::DisplayWarning(const char* message) +{ + Clear(); + Log_WarningPrint(message); + Redraw(false); +} + +void ConsoleProgressCallback::DisplayInformation(const char* message) +{ + Clear(); + Log_InfoPrint(message); + Redraw(false); +} + +void ConsoleProgressCallback::DisplayDebugMessage(const char* message) +{ + Clear(); + Log_DevPrint(message); + Redraw(false); +} + +void ConsoleProgressCallback::ModalError(const char* message) +{ + Clear(); + Log_ErrorPrint(message); + Redraw(false); +} + +bool ConsoleProgressCallback::ModalConfirmation(const char* message) +{ + Clear(); + Log_InfoPrint(message); + Redraw(false); + return false; +} + +u32 ConsoleProgressCallback::ModalPrompt(const char* message, u32 num_options, ...) +{ + Clear(); + DebugAssert(num_options > 0); + Log_InfoPrint(message); + Redraw(false); + return 0; +} diff --git a/src/common/progress_callback.h b/src/common/progress_callback.h new file mode 100644 index 000000000..a6436e47d --- /dev/null +++ b/src/common/progress_callback.h @@ -0,0 +1,121 @@ +#pragma once +#include "string.h" +#include "types.h" + +class ByteStream; + +class ProgressCallback +{ +public: + virtual ~ProgressCallback(); + + virtual void PushState() = 0; + virtual void PopState() = 0; + + virtual bool IsCancelled() const = 0; + virtual bool IsCancellable() const = 0; + + virtual void SetCancellable(bool cancellable) = 0; + + virtual void SetStatusText(const char* text) = 0; + virtual void SetProgressRange(u32 range) = 0; + virtual void SetProgressValue(u32 value) = 0; + virtual void IncrementProgressValue() = 0; + + void SetFormattedStatusText(const char* Format, ...); + + virtual void DisplayError(const char* message) = 0; + virtual void DisplayWarning(const char* message) = 0; + virtual void DisplayInformation(const char* message) = 0; + virtual void DisplayDebugMessage(const char* message) = 0; + + virtual void ModalError(const char* message) = 0; + virtual bool ModalConfirmation(const char* message) = 0; + virtual u32 ModalPrompt(const char* message, u32 num_options, ...) = 0; + + void DisplayFormattedError(const char* format, ...); + void DisplayFormattedWarning(const char* format, ...); + void DisplayFormattedInformation(const char* format, ...); + void DisplayFormattedDebugMessage(const char* format, ...); + void DisplayFormattedModalError(const char* format, ...); + bool DisplayFormattedModalConfirmation(const char* format, ...); + + void UpdateProgressFromStream(ByteStream* stream); + +public: + static ProgressCallback* NullProgressCallback; +}; + +class BaseProgressCallback : public ProgressCallback +{ +public: + BaseProgressCallback(); + virtual ~BaseProgressCallback(); + + virtual void PushState() override; + virtual void PopState() override; + + virtual bool IsCancelled() const override; + virtual bool IsCancellable() const override; + + virtual void SetCancellable(bool cancellable) override; + virtual void SetStatusText(const char* text) override; + virtual void SetProgressRange(u32 range) override; + virtual void SetProgressValue(u32 value) override; + virtual void IncrementProgressValue() override; + +protected: + struct State + { + State* next_saved_state; + String status_text; + u32 progress_range; + u32 progress_value; + u32 base_progress_value; + bool cancellable; + }; + + bool m_cancellable; + bool m_cancelled; + String m_status_text; + u32 m_progress_range; + u32 m_progress_value; + + u32 m_base_progress_value; + + State* m_saved_state; +}; + +class ConsoleProgressCallback : public BaseProgressCallback +{ +public: + static const u32 COLUMNS = 78; + +public: + ConsoleProgressCallback(); + ~ConsoleProgressCallback(); + + virtual void PushState() override; + virtual void PopState() override; + + virtual void SetCancellable(bool cancellable) override; + virtual void SetStatusText(const char* text) override; + virtual void SetProgressRange(u32 range) override; + virtual void SetProgressValue(u32 value) override; + + virtual void DisplayError(const char* message) override; + virtual void DisplayWarning(const char* message) override; + virtual void DisplayInformation(const char* message) override; + virtual void DisplayDebugMessage(const char* message) override; + + virtual void ModalError(const char* message) override; + virtual bool ModalConfirmation(const char* message) override; + virtual u32 ModalPrompt(const char* message, u32 num_options, ...) override; + +private: + void Clear(); + void Redraw(bool update_value_only); + + float m_last_percent_complete; + u32 m_last_bar_length; +};