Aegisub/aegisub/src/dialog_progress.cpp

212 lines
6.4 KiB
C++
Raw Normal View History

// Copyright (c) 2013, Thomas Goyne <plorkyeran@aegisub.org>
2006-01-16 22:02:54 +01:00
//
// Permission to use, copy, modify, and distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
2006-01-16 22:02:54 +01:00
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
/// @file dialog_progress.cpp
/// @brief Progress-bar dialog box for displaying during long operations
/// @ingroup utility
///
2006-01-16 22:02:54 +01:00
#include "config.h"
#include "dialog_progress.h"
#include "compat.h"
#include "utils.h"
#include <libaegisub/dispatch.h>
#include <libaegisub/exception.h>
#include <libaegisub/util_osx.h>
#include <atomic>
#include <wx/button.h>
#include <wx/gauge.h>
#include <wx/sizer.h>
#include <wx/stattext.h>
#include <wx/textctrl.h>
using agi::dispatch::Main;
class DialogProgressSink : public agi::ProgressSink {
DialogProgress *dialog;
std::atomic<bool> cancelled;
2006-01-16 22:02:54 +01:00
public:
DialogProgressSink(DialogProgress *dialog)
: dialog(dialog)
, cancelled(false)
{
}
void SetTitle(std::string const& title) {
Main().Async([=]{ dialog->title->SetLabelText(to_wx(title)); });
}
void SetMessage(std::string const& msg) {
Main().Async([=]{ dialog->text->SetLabelText(to_wx(msg)); });
}
2006-01-16 22:02:54 +01:00
void SetProgress(int64_t cur, int64_t max) {
2013-09-27 00:54:59 +02:00
Main().Async([=]{ dialog->SetProgress(mid<int>(0, double(cur) / max * 300, 300)); });
}
2006-01-16 22:02:54 +01:00
void Log(std::string const& str) {
Main().Async([=]{ dialog->pending_log += to_wx(str); });
}
2006-01-16 22:02:54 +01:00
bool IsCancelled() {
return cancelled;
}
void Cancel() {
cancelled = true;
}
void SetIndeterminate() {
Main().Async([=]{ dialog->pulse_timer.Start(1000); });
}
};
DialogProgress::DialogProgress(wxWindow *parent, wxString const& title_text, wxString const& message)
: wxDialog(parent, -1, title_text, wxDefaultPosition, wxDefaultSize, wxBORDER_RAISED)
, pulse_timer(GetEventHandler())
2013-09-27 00:54:59 +02:00
, progress_current(0)
, progress_target(0)
{
title = new wxStaticText(this, -1, title_text, wxDefaultPosition, wxDefaultSize, wxALIGN_CENTRE | wxST_NO_AUTORESIZE);
2013-09-27 00:54:59 +02:00
gauge = new wxGauge(this, -1, 300, wxDefaultPosition, wxSize(300,20));
text = new wxStaticText(this, -1, message, wxDefaultPosition, wxDefaultSize, wxALIGN_CENTRE | wxST_NO_AUTORESIZE);
cancel_button = new wxButton(this, wxID_CANCEL);
log_output = new wxTextCtrl(this, -1, "", wxDefaultPosition, wxSize(600, 240), wxTE_MULTILINE | wxTE_READONLY);
// make the title a slightly larger font
wxFont title_font = title->GetFont();
int fontsize = title_font.GetPointSize();
title_font.SetPointSize(fontsize * 1.375);
title_font.SetWeight(wxFONTWEIGHT_BOLD);
title->SetFont(title_font);
wxSizer *sizer = new wxBoxSizer(wxVERTICAL);
sizer->Add(title, wxSizerFlags().Expand().Center());
sizer->Add(gauge, wxSizerFlags(1).Expand().Border());
sizer->Add(text, wxSizerFlags().Expand().Center());
sizer->Add(cancel_button, wxSizerFlags().Center().Border());
sizer->Add(log_output, wxSizerFlags().Expand().Border(wxALL & ~wxTOP));
sizer->Hide(log_output);
SetSizerAndFit(sizer);
CenterOnParent();
2006-01-16 22:02:54 +01:00
Bind(wxEVT_SHOW, &DialogProgress::OnShow, this);
Bind(wxEVT_TIMER, [=](wxTimerEvent&) { gauge->Pulse(); });
Bind(wxEVT_IDLE, &DialogProgress::OnIdle, this);
2006-01-16 22:02:54 +01:00
}
2012-09-25 01:35:27 +02:00
void DialogProgress::Run(std::function<void(agi::ProgressSink*)> task, int priority) {
DialogProgressSink ps(this);
this->ps = &ps;
auto current_title = from_wx(title->GetLabelText());
agi::dispatch::Background().Async([=]{
agi::osx::AppNapDisabler app_nap_disabler(current_title);
try {
task(this->ps);
}
catch (agi::Exception const& e) {
this->ps->Log(e.GetChainedMessage());
}
Main().Async([this]{
pulse_timer.Stop();
// Unbind the cancel handler so that the default behavior happens (i.e. the
// dialog is closed) as there's no longer a task to cancel
Unbind(wxEVT_COMMAND_BUTTON_CLICKED, &DialogProgress::OnCancel, this, wxID_CANCEL);
// If it ran to completion and there is debug output, leave the window open
// so the user can read the debug output and switch the cancel button to a
// close button
bool cancelled = this->ps->IsCancelled();
if (cancelled || (log_output->IsEmpty() && !pending_log))
EndModal(!cancelled);
else
cancel_button->SetLabelText(_("Close"));
});
});
if (!ShowModal())
throw agi::UserCancelException("Cancelled by user");
2006-01-16 22:02:54 +01:00
}
void DialogProgress::OnShow(wxShowEvent&) {
// Restore the cancel button in case it was previously switched to a close
// button
Bind(wxEVT_COMMAND_BUTTON_CLICKED, &DialogProgress::OnCancel, this, wxID_CANCEL);
cancel_button->SetLabelText(_("Cancel"));
cancel_button->Enable();
wxSizer *sizer = GetSizer();
if (sizer->IsShown(log_output)) {
sizer->Hide(log_output);
Layout();
sizer->Fit(this);
log_output->Clear();
}
2006-01-16 22:02:54 +01:00
}
void DialogProgress::OnIdle(wxIdleEvent&) {
2013-09-27 00:54:59 +02:00
if (progress_current != progress_target) {
using namespace std::chrono;
auto now = steady_clock::now();
int ms = mid<int>(0, duration_cast<milliseconds>(now - progress_anim_start_time).count(), progress_anim_duration);
int dist = (progress_target - progress_anim_start_value) * ms / progress_anim_duration;
if (dist) {
progress_current = progress_anim_start_value + dist;
gauge->SetValue(progress_current);
}
}
if (!pending_log) return;
if (log_output->IsEmpty()) {
wxSizer *sizer = GetSizer();
sizer->Show(log_output);
Layout();
sizer->Fit(this);
CenterOnParent();
}
*log_output << pending_log;
log_output->SetInsertionPointEnd();
pending_log.clear();
}
void DialogProgress::OnCancel(wxCommandEvent &) {
ps->Cancel();
cancel_button->Enable(false);
cancel_button->SetLabelText(_("Cancelling..."));
2006-01-16 22:02:54 +01:00
}
2013-09-27 00:54:59 +02:00
void DialogProgress::SetProgress(int target) {
using namespace std::chrono;
progress_anim_start_value = progress_current;
auto now = steady_clock::now();
if (progress_target == 0)
progress_anim_duration = 1000;
else
progress_anim_duration = std::max<int>(100, duration_cast<milliseconds>(now - progress_anim_start_time).count() * 11 / 10);
progress_anim_start_time = now;
progress_target = target;
}