diff --git a/aegisub/build/libaegisub_vs2008/libaegisub_vs2008.vcproj b/aegisub/build/libaegisub_vs2008/libaegisub_vs2008.vcproj
index b1f8df819..77b194fbf 100644
--- a/aegisub/build/libaegisub_vs2008/libaegisub_vs2008.vcproj
+++ b/aegisub/build/libaegisub_vs2008/libaegisub_vs2008.vcproj
@@ -397,6 +397,10 @@
RelativePath="..\..\libaegisub\include\libaegisub\access.h"
>
+
+
diff --git a/aegisub/libaegisub/include/libaegisub/background_runner.h b/aegisub/libaegisub/include/libaegisub/background_runner.h
new file mode 100644
index 000000000..09f374f55
--- /dev/null
+++ b/aegisub/libaegisub/include/libaegisub/background_runner.h
@@ -0,0 +1,87 @@
+// Copyright (c) 2011, Thomas Goyne
+//
+// 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.
+//
+// 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.
+//
+// $Id$
+
+/// @file background_runner.h
+/// @brief Background runner and progress sink interfaces
+/// @ingroup libaegisub
+
+#pragma once
+
+#ifndef LAGI_PRE
+#include
+#ifdef _WIN32
+#include
+#else
+#include
+#endif
+#endif
+
+namespace agi {
+ /// @class ProgressSink
+ /// @brief A receiver for progress updates sent from a worker function
+ ///
+ /// Note that ProgressSinks are not required to be thread-safe. The only
+ /// guarantee provided is that they can be used on the thread they are
+ /// spawned on (which may or may not be the GUI thread).
+ class ProgressSink {
+ public:
+ /// Virtual destructor so that things can safely inherit from this
+ virtual ~ProgressSink() { }
+
+ /// Set the progress to indeterminate
+ virtual void SetIndeterminate()=0;
+
+ /// Set the title of the running task
+ virtual void SetTitle(std::string const& title)=0;
+ /// Set an additional message associated with the task
+ virtual void SetMessage(std::string const& msg)=0;
+ /// Set the current task progress
+ virtual void SetProgress(int cur, int max)=0;
+
+ /// @brief Log a message
+ ///
+ /// If any messages are logged then the dialog will not automatically close
+ /// when the task finishes so that the user has the chance to read them.
+ virtual void Log(std::string const& str)=0;
+
+ /// Has the user asked the task to cancel?
+ virtual bool IsCancelled()=0;
+ };
+
+ /// @class BackgroundRunner
+ /// @brief A class which runs a function, providing it with a progress sink
+ ///
+ /// Normally implementations of this interface will spawn a new thread to
+ /// run the task on, but there are sensible implementations that may not.
+ /// For example, an implementation which has no UI and simply writes the
+ /// log output to a file would simply run on the main thread.
+ class BackgroundRunner {
+ public:
+ /// Virtual destructor so that things can safely inherit from this
+ virtual ~BackgroundRunner() { }
+
+ /// @brief Run a function on a background thread
+ /// @param task Function to run
+ /// @param priority Thread priority or -1 for default
+ /// @throws agi::UserCancelException on cancel
+ ///
+ /// Blocks the calling thread until the task completes or is canceled.
+ /// Progress updates sent to the progress sink passed to the task should
+ /// be displayed to the user in some way, along with some way for the
+ /// user to cancel the task.
+ virtual void Run(std::tr1::function task, int priority=-1)=0;
+ };
+}
diff --git a/aegisub/src/audio_provider_hd.cpp b/aegisub/src/audio_provider_hd.cpp
index 566a149f1..209afdee6 100644
--- a/aegisub/src/audio_provider_hd.cpp
+++ b/aegisub/src/audio_provider_hd.cpp
@@ -49,9 +49,9 @@
#include "standard_paths.h"
#include "utils.h"
-/// @brief Constructor
-/// @param source
-///
+
+
+
HDAudioProvider::HDAudioProvider(AudioProvider *src) {
std::auto_ptr source(src);
// Copy parameters
@@ -76,44 +76,34 @@ HDAudioProvider::HDAudioProvider(AudioProvider *src) {
file_cache.Open(diskCacheFilename,wxFile::read_write);
if (!file_cache.IsOpened()) throw AudioOpenError("Unable to write to audio disk cache.");
- // Start progress
- volatile bool canceled = false;
- DialogProgress *progress = new DialogProgress(AegisubApp::Get()->frame,"Load audio",&canceled,"Reading to Hard Disk cache",0,num_samples);
- progress->Show();
-
- // Write to disk
- int block = 4096;
- data = new char[block * channels * bytes_per_sample];
- for (int64_t i=0;i num_samples) block = num_samples - i;
- source->GetAudio(data,i,block);
- file_cache.Write(data,block * channels * bytes_per_sample);
- progress->SetProgress(i,num_samples);
- }
- file_cache.Seek(0);
-
- // Finish
- if (canceled) {
- file_cache.Close();
- delete[] data;
- throw agi::UserCancelException("Audio loading cancelled by user");
- }
- progress->Destroy();
+ DialogProgress progress(AegisubApp::Get()->frame, "Load audio", "Reading to Hard Disk cache");
+ progress.Run(bind(&HDAudioProvider::FillCache, this, src, std::tr1::placeholders::_1));
}
-/// @brief Destructor
-///
HDAudioProvider::~HDAudioProvider() {
file_cache.Close();
wxRemoveFile(diskCacheFilename);
delete[] data;
}
-/// @brief Get audio
-/// @param buf
-/// @param start
-/// @param count
-///
+void HDAudioProvider::FillCache(AudioProvider *src, agi::ProgressSink *ps) {
+ int64_t block = 4096;
+ data = new char[block * channels * bytes_per_sample];
+ for (int64_t i = 0; i < num_samples; i += block) {
+ block = std::min(block, num_samples - i);
+ src->GetAudio(data, i, block);
+ file_cache.Write(data, block * channels * bytes_per_sample);
+ ps->SetProgress(i, num_samples);
+
+ if (ps->IsCancelled()) {
+ file_cache.Close();
+ wxRemoveFile(diskCacheFilename);
+ delete[] data;
+ }
+ }
+ file_cache.Seek(0);
+}
+
void HDAudioProvider::GetAudio(void *buf, int64_t start, int64_t count) const {
// Requested beyond the length of audio
if (start+count > num_samples) {
diff --git a/aegisub/src/audio_provider_hd.h b/aegisub/src/audio_provider_hd.h
index f62af2396..e2a22d252 100644
--- a/aegisub/src/audio_provider_hd.h
+++ b/aegisub/src/audio_provider_hd.h
@@ -41,6 +41,8 @@
#include "include/aegisub/audio_provider.h"
+namespace agi { class ProgressSink; }
+
/// DOCME
/// @class HDAudioProvider
/// @brief DOCME
@@ -65,6 +67,8 @@ class HDAudioProvider : public AudioProvider {
static wxString DiskCachePath();
static wxString DiskCacheName();
+ void FillCache(AudioProvider *src, agi::ProgressSink *ps);
+
public:
HDAudioProvider(AudioProvider *source);
~HDAudioProvider();
diff --git a/aegisub/src/audio_provider_ram.cpp b/aegisub/src/audio_provider_ram.cpp
index dc2986850..f17ad2705 100644
--- a/aegisub/src/audio_provider_ram.cpp
+++ b/aegisub/src/audio_provider_ram.cpp
@@ -42,38 +42,28 @@
#include "main.h"
#include "utils.h"
-
-/// DOCME
#define CacheBits ((22))
-/// DOCME
#define CacheBlockSize ((1 << CacheBits))
-/// @brief Constructor
-/// @param source
-///
RAMAudioProvider::RAMAudioProvider(AudioProvider *src) {
std::auto_ptr source(src);
- // Init
- blockcache = NULL;
- blockcount = 0;
+
samples_native_endian = source->AreSamplesNativeEndian();
// Allocate cache
int64_t ssize = source->GetNumSamples() * source->GetBytesPerSample();
blockcount = (ssize + CacheBlockSize - 1) >> CacheBits;
blockcache = new char*[blockcount];
- for (int i = 0; i < blockcount; i++) {
- blockcache[i] = NULL;
- }
+ memset(blockcache, blockcount * sizeof(char*), 0);
// Allocate cache blocks
try {
for (int i = 0; i < blockcount; i++) {
- blockcache[i] = new char[std::min(CacheBlockSize,ssize-i*CacheBlockSize)];
+ blockcache[i] = new char[std::min(CacheBlockSize, ssize - i * CacheBlockSize)];
}
}
- catch (...) {
+ catch (std::bad_alloc const&) {
Clear();
throw AudioOpenError("Couldn't open audio, not enough ram available.");
}
@@ -85,35 +75,27 @@ RAMAudioProvider::RAMAudioProvider(AudioProvider *src) {
sample_rate = source->GetSampleRate();
filename = source->GetFilename();
- // Start progress
- volatile bool canceled = false;
- DialogProgress *progress = new DialogProgress(AegisubApp::Get()->frame,_("Load audio"),&canceled,_("Reading into RAM"),0,source->GetNumSamples());
- progress->Show();
- progress->SetProgress(0,1);
-
- // Read cache
- int readsize = CacheBlockSize / source->GetBytesPerSample();
- for (int i=0;iGetAudio((char*)blockcache[i],i*readsize, i == blockcount-1 ? (source->GetNumSamples() - i*readsize) : readsize);
- progress->SetProgress(i,blockcount-1);
- }
-
- // Clean up progress
- if (canceled) {
- Clear();
- throw agi::UserCancelException("Audio loading cancelled by user");
- }
- progress->Destroy();
+ DialogProgress progress(AegisubApp::Get()->frame, _("Load audio"), _("Reading into RAM"));
+ progress.Run(std::tr1::bind(&RAMAudioProvider::FillCache, this, src, std::tr1::placeholders::_1));
}
-/// @brief Destructor
-///
RAMAudioProvider::~RAMAudioProvider() {
Clear();
}
-/// @brief Clear
-///
+void RAMAudioProvider::FillCache(AudioProvider *source, agi::ProgressSink *ps) {
+ int64_t readsize = CacheBlockSize / source->GetBytesPerSample();
+ for (int i = 0; i < blockcount; i++) {
+ source->GetAudio((char*)blockcache[i], i * readsize, std::min(readsize, num_samples - i * readsize));
+
+ ps->SetProgress(i, (blockcount - 1));
+ if (ps->IsCancelled()) {
+ Clear();
+ return;
+ }
+ }
+}
+
void RAMAudioProvider::Clear() {
// Free ram cache
if (blockcache) {
@@ -122,15 +104,8 @@ void RAMAudioProvider::Clear() {
}
delete [] blockcache;
}
- blockcache = NULL;
- blockcount = 0;
}
-/// @brief Get audio
-/// @param buf
-/// @param start
-/// @param count
-///
void RAMAudioProvider::GetAudio(void *buf, int64_t start, int64_t count) const {
// Requested beyond the length of audio
if (start+count > num_samples) {
diff --git a/aegisub/src/audio_provider_ram.h b/aegisub/src/audio_provider_ram.h
index efba843ae..0e6c82024 100644
--- a/aegisub/src/audio_provider_ram.h
+++ b/aegisub/src/audio_provider_ram.h
@@ -36,6 +36,8 @@
#include "include/aegisub/audio_provider.h"
+namespace agi { class ProgressSink; }
+
/// DOCME
/// @class RAMAudioProvider
/// @brief DOCME
@@ -52,6 +54,7 @@ class RAMAudioProvider : public AudioProvider {
bool samples_native_endian;
void Clear();
+ void FillCache(AudioProvider *source, agi::ProgressSink *ps);
public:
RAMAudioProvider(AudioProvider *source);
diff --git a/aegisub/src/auto4_base.cpp b/aegisub/src/auto4_base.cpp
index 304de6baa..4b5c85c1e 100644
--- a/aegisub/src/auto4_base.cpp
+++ b/aegisub/src/auto4_base.cpp
@@ -62,6 +62,7 @@
#include "ass_style.h"
#include "auto4_base.h"
#include "compat.h"
+#include "dialog_progress.h"
#include "include/aegisub/context.h"
#include "main.h"
#include "standard_paths.h"
@@ -292,7 +293,7 @@ namespace Automation4 {
FeatureFilter::FeatureFilter(const wxString &_name, const wxString &_description, int _priority)
: Feature(SCRIPTFEATURE_FILTER, _name)
, AssExportFilter(_name, _description, _priority)
- , config_dialog(0)
+ , config_dialog(0)
{
AssExportFilterChain::Register(this);
}
@@ -311,7 +312,7 @@ namespace Automation4 {
///
wxString FeatureFilter::GetScriptSettingsIdentifier()
{
- return inline_string_encode(wxString::Format("Automation Settings %s", GetName()));
+ return inline_string_encode(wxString::Format("Automation Settings %s", AssExportFilter::GetName()));
}
@@ -395,14 +396,6 @@ namespace Automation4 {
return !filename.Right(extension.Length()).CmpNoCase(extension);
}
-
- // ShowConfigDialogEvent
-
-
- /// DOCME
- const wxEventType EVT_SHOW_CONFIG_DIALOG_t = wxNewEventType();
-
-
// ScriptConfigDialog
@@ -436,220 +429,72 @@ namespace Automation4 {
// ProgressSink
+ wxDEFINE_EVENT(EVT_SHOW_CONFIG_DIALOG, wxThreadEvent);
-
- /// @brief DOCME
- /// @param parent
- ///
- ProgressSink::ProgressSink(wxWindow *parent)
- : wxDialog(parent, -1, "Automation", wxDefaultPosition, wxDefaultSize, wxBORDER_RAISED)
- , debug_visible(false)
- , data_updated(false)
- , cancelled(false)
- , has_inited(false)
- , script_finished(false)
+ ProgressSink::ProgressSink(agi::ProgressSink *impl, BackgroundScriptRunner *bsr)
+ : impl(impl)
+ , bsr(bsr)
+ , trace_level(OPT_GET("Automation/Trace Level")->GetInt())
{
- // make the controls
- progress_display = new wxGauge(this, -1, 1000, wxDefaultPosition, wxSize(300, 20));
- title_display = new wxStaticText(this, -1, "", wxDefaultPosition, wxDefaultSize, wxALIGN_CENTRE|wxST_NO_AUTORESIZE);
- task_display = new wxStaticText(this, -1, "", wxDefaultPosition, wxDefaultSize, wxALIGN_CENTRE|wxST_NO_AUTORESIZE);
- cancel_button = new wxButton(this, wxID_CANCEL);
- debug_output = new wxTextCtrl(this, -1, "", wxDefaultPosition, wxSize(300, 120), wxTE_MULTILINE|wxTE_READONLY);
-
- // put it in a sizer
- sizer = new wxBoxSizer(wxVERTICAL);
- sizer->Add(title_display, 0, wxEXPAND | wxALL, 5);
- sizer->Add(progress_display, 0, wxALL&~wxTOP, 5);
- sizer->Add(task_display, 0, wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM, 5);
- sizer->Add(cancel_button, 0, wxALIGN_CENTER | wxLEFT | wxRIGHT | wxBOTTOM, 5);
- sizer->Add(debug_output, 0, wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM, 5);
- sizer->Show(debug_output, false);
-
- // make the title a slightly larger font
- wxFont title_font = title_display->GetFont();
- int fontsize = title_font.GetPointSize();
- title_font.SetPointSize(fontsize + fontsize/4 + fontsize/8);
- title_font.SetWeight(wxFONTWEIGHT_BOLD);
- title_display->SetFont(title_font);
-
- // Set up a timer to regularly update the status
- // It doesn't need an event handler attached, as just a the timer in itself
- // will ensure that the idle event is fired
- update_timer = new wxTimer();
- update_timer->Start(50, false);
-
- sizer->SetSizeHints(this);
- SetSizer(sizer);
- Center();
-
- // Init trace level
- trace_level = OPT_GET("Automation/Trace Level")->GetInt();
}
-
- /// @brief DOCME
- ///
- ProgressSink::~ProgressSink()
+ void ProgressSink::ShowConfigDialog(ScriptConfigDialog *config_dialog)
{
- delete update_timer;
+ wxSemaphore sema(0, 1);
+ wxThreadEvent *evt = new wxThreadEvent(EVT_SHOW_CONFIG_DIALOG);
+ evt->SetPayload(std::make_pair(config_dialog, &sema));
+ bsr->QueueEvent(evt);
+ sema.Wait();
}
-
- /// @brief DOCME
- /// @param evt
- ///
- void ProgressSink::OnIdle(wxIdleEvent &evt)
+ BackgroundScriptRunner::BackgroundScriptRunner(wxWindow *parent, wxString const& title)
+ : impl(new DialogProgress(parent, title))
{
- // The big glossy "update display" event
- DoUpdateDisplay();
-
- if (script_finished) {
- if (!debug_visible) {
- EndModal(0);
- } else {
- cancel_button->Enable(true);
- cancel_button->SetLabel(_("Close"));
- SetProgress(100.0);
- SetTask(_("Script completed"));
- }
- }
+ impl->Bind(EVT_SHOW_CONFIG_DIALOG, &BackgroundScriptRunner::OnConfigDialog, this);
}
-
- /// @brief DOCME
- /// @return
- ///
- void ProgressSink::DoUpdateDisplay()
+ BackgroundScriptRunner::~BackgroundScriptRunner()
{
- // If debug output isn't handled before the test for script_finished later,
- // there might actually be some debug output but the debug_visible flag won't
- // be set before the dialog closes itself.
- wxMutexLocker lock(data_mutex);
- if (!data_updated) return;
- if (!pending_debug_output.IsEmpty()) {
- if (!debug_visible) {
- sizer->Show(debug_output, true);
- Layout();
- sizer->Fit(this);
-
- debug_visible = true;
- }
-
- *debug_output << pending_debug_output;
- debug_output->SetInsertionPointEnd();
-
- pending_debug_output = "";
- }
-
- progress_display->SetValue((int)(progress*10));
- task_display->SetLabel(task);
- title_display->SetLabel(title);
- data_updated = false;
}
-
- /// @brief DOCME
- /// @param _progress
- ///
- void ProgressSink::SetProgress(float _progress)
+ void BackgroundScriptRunner::OnConfigDialog(wxThreadEvent &evt)
{
- wxMutexLocker lock(data_mutex);
- progress = _progress;
- data_updated = true;
+ std::pair payload = evt.GetPayload >();
+
+ wxDialog w(impl.get(), -1, impl->GetTitle()); // container dialog box
+ wxBoxSizer *s = new wxBoxSizer(wxHORIZONTAL); // sizer for putting contents in
+ wxWindow *ww = payload.first->GetWindow(&w); // get/generate actual dialog contents
+ s->Add(ww, 0, wxALL, 5); // add contents to dialog
+ w.SetSizerAndFit(s);
+ w.CenterOnParent();
+ w.ShowModal();
+ payload.first->ReadBack();
+ payload.first->DeleteWindow();
+
+ payload.second->Post();
}
-
- /// @brief DOCME
- /// @param _task
- ///
- void ProgressSink::SetTask(const wxString &_task)
- {
- wxMutexLocker lock(data_mutex);
- task = _task;
- data_updated = true;
+ void BackgroundScriptRunner::QueueEvent(wxEvent *evt) {
+ wxQueueEvent(impl.get(), evt);
}
-
- /// @brief DOCME
- /// @param _title
- ///
- void ProgressSink::SetTitle(const wxString &_title)
+ // Convert a function taking an Automation4::ProgressSink to one taking an
+ // agi::ProgressSink so that we can pass it to an agi::BackgroundWorker
+ static void progress_sink_wrapper(std::tr1::function task, agi::ProgressSink *ps, BackgroundScriptRunner *bsr)
{
- wxMutexLocker lock(data_mutex);
- title = _title;
- data_updated = true;
+ ProgressSink aps(ps, bsr);
+ task(&aps);
}
-
- /// @brief DOCME
- /// @param msg
- ///
- void ProgressSink::AddDebugOutput(const wxString &msg)
+ void BackgroundScriptRunner::Run(std::tr1::function task)
{
- wxMutexLocker lock(data_mutex);
- pending_debug_output << msg;
- data_updated = true;
- }
+ int prio = OPT_GET("Automation/Thread Priority")->GetInt();
+ if (prio == 0) prio = 50; // normal
+ else if (prio == 1) prio = 30; // below normal
+ else if (prio == 2) prio = 10; // lowest
+ else prio = 50; // fallback normal
- BEGIN_EVENT_TABLE(ProgressSink, wxWindow)
- EVT_INIT_DIALOG(ProgressSink::OnInit)
- EVT_BUTTON(wxID_CANCEL, ProgressSink::OnCancel)
- EVT_IDLE(ProgressSink::OnIdle)
- EVT_SHOW_CONFIG_DIALOG(ProgressSink::OnConfigDialog)
- END_EVENT_TABLE()
-
-
- /// @brief DOCME
- /// @param evt
- ///
- void ProgressSink::OnInit(wxInitDialogEvent &evt)
- {
- has_inited = true;
- }
-
-
- /// @brief DOCME
- /// @param evt
- ///
- void ProgressSink::OnCancel(wxCommandEvent &evt)
- {
- if (!script_finished) {
- cancelled = true;
- cancel_button->Enable(false);
- } else {
- EndModal(0);
- }
- }
-
-
- /// @brief DOCME
- /// @param evt
- ///
- void ProgressSink::OnConfigDialog(ShowConfigDialogEvent &evt)
- {
- // assume we're in the GUI thread here
-
- DoUpdateDisplay();
-
- if (evt.config_dialog) {
- wxDialog *w = new wxDialog(this, -1, title); // container dialog box
- wxBoxSizer *s = new wxBoxSizer(wxHORIZONTAL); // sizer for putting contents in
- wxWindow *ww = evt.config_dialog->GetWindow(w); // get/generate actual dialog contents
- s->Add(ww, 0, wxALL, 5); // add contents to dialog
- w->SetSizerAndFit(s);
- w->CenterOnParent();
- w->ShowModal();
- evt.config_dialog->ReadBack();
- evt.config_dialog->DeleteWindow();
- delete w;
- } else {
- wxMessageBox("Uh... no config dialog?");
- }
-
- // See note in auto4_base.h
- if (evt.sync_sema) {
- evt.sync_sema->Post();
- }
+ impl->Run(bind(progress_sink_wrapper, task, std::tr1::placeholders::_1, this), prio);
}
@@ -891,12 +736,12 @@ namespace Automation4 {
while (more) {
script_path.SetName(fn);
try {
- wxString fullpath = script_path.GetFullPath();
- if (ScriptFactory::CanHandleScriptFormat(fullpath)) {
- Script *s = ScriptFactory::CreateFromFile(fullpath, true);
- Add(s);
- if (!s->GetLoadedState()) error_count++;
- }
+ wxString fullpath = script_path.GetFullPath();
+ if (ScriptFactory::CanHandleScriptFormat(fullpath)) {
+ Script *s = ScriptFactory::CreateFromFile(fullpath, true);
+ Add(s);
+ if (!s->GetLoadedState()) error_count++;
+ }
}
catch (const char *e) {
error_count++;
@@ -1094,7 +939,7 @@ namespace Automation4 {
/// @param filename
///
UnknownScript::UnknownScript(const wxString &filename)
- : Script(filename)
+ : Script(filename)
{
wxFileName fn(filename);
name = fn.GetName();
diff --git a/aegisub/src/auto4_base.h b/aegisub/src/auto4_base.h
index ed4c96a1a..98e89f96f 100644
--- a/aegisub/src/auto4_base.h
+++ b/aegisub/src/auto4_base.h
@@ -50,6 +50,9 @@
#include
#endif
+#include
+#include
+#include
#include
#include "ass_export_filter.h"
@@ -58,6 +61,8 @@
class AssFile;
class AssStyle;
+class DialogProgress;
+class SubtitleFormat;
class wxWindow;
class wxDialog;
class wxStopWatch;
@@ -264,137 +269,44 @@ namespace Automation4 {
virtual void Unserialise(const wxString &serialised) { }
};
+ class ProgressSink;
- // Config dialog event class and related stuff (wx 3)
- extern const wxEventType EVT_SHOW_CONFIG_DIALOG_t;
+ class BackgroundScriptRunner {
+ agi::scoped_ptr impl;
-
- /// DOCME
- /// @class ShowConfigDialogEvent
- /// @brief DOCME
- ///
- /// DOCME
- class ShowConfigDialogEvent : public wxCommandEvent {
+ void OnConfigDialog(wxThreadEvent &evt);
public:
- /// @brief DOCME
- /// @param event
- /// @return
- ///
- ShowConfigDialogEvent(const wxEventType &event = EVT_SHOW_CONFIG_DIALOG_t)
- : wxCommandEvent(event)
- , config_dialog(0)
- , sync_sema(0) { };
+ void QueueEvent(wxEvent *evt);
+ void Run(std::tr1::function task);
- /// @brief DOCME
- /// @return
- ///
- virtual wxEvent *Clone() const { return new ShowConfigDialogEvent(*this); }
-
-
- /// DOCME
- ScriptConfigDialog *config_dialog;
-
- /// DOCME
- wxSemaphore *sync_sema;
+ BackgroundScriptRunner(wxWindow *parent, wxString const& title);
+ ~BackgroundScriptRunner();
};
-
- /// DOCME
- typedef void (wxEvtHandler::*ShowConfigDialogEventFunction)(ShowConfigDialogEvent&);
-
-
-/// DOCME
-#define EVT_SHOW_CONFIG_DIALOG(fn) DECLARE_EVENT_TABLE_ENTRY( EVT_SHOW_CONFIG_DIALOG_t, -1, -1, (wxObjectEventFunction)(wxEventFunction)(ShowConfigDialogEventFunction)&fn, (wxObject*)0 ),
-
-
-
- /// DOCME
- /// @class ProgressSink
- /// @brief DOCME
- ///
- /// DOCME
- class ProgressSink : public wxDialog {
- private:
-
- /// DOCME
- wxBoxSizer *sizer;
-
- /// DOCME
- wxGauge *progress_display;
-
- /// DOCME
- wxButton *cancel_button;
-
- /// DOCME
- wxStaticText *title_display;
-
- /// DOCME
- wxStaticText *task_display;
-
- /// DOCME
- wxTextCtrl *debug_output;
-
-
- /// DOCME
- volatile bool debug_visible;
-
- /// DOCME
- volatile bool data_updated;
-
-
- /// DOCME
- float progress;
-
- /// DOCME
- wxString task;
-
- /// DOCME
- wxString title;
-
- /// DOCME
- wxString pending_debug_output;
-
- /// DOCME
- wxMutex data_mutex;
-
-
- /// DOCME
- wxTimer *update_timer;
-
- void OnCancel(wxCommandEvent &evt);
- void OnInit(wxInitDialogEvent &evt);
- void OnIdle(wxIdleEvent &evt);
- void OnConfigDialog(ShowConfigDialogEvent &evt);
-
- void DoUpdateDisplay();
-
- protected:
-
- /// DOCME
- volatile bool cancelled;
-
- /// DOCME
+ /// A wrapper around agi::ProgressSink which adds the ability to open
+ /// dialogs on the GUI thread
+ class ProgressSink : public agi::ProgressSink {
+ agi::ProgressSink *impl;
+ BackgroundScriptRunner *bsr;
int trace_level;
-
- ProgressSink(wxWindow *parent);
- virtual ~ProgressSink();
-
public:
- void SetProgress(float _progress);
- void SetTask(const wxString &_task);
- void SetTitle(const wxString &_title);
- void AddDebugOutput(const wxString &msg);
+ void SetIndeterminate() { impl->SetIndeterminate(); }
+ void SetTitle(std::string const& title) { impl->SetTitle(title); }
+ void SetMessage(std::string const& msg) { impl->SetMessage(msg); }
+ void SetProgress(int cur, int max) { impl->SetProgress(cur, max); }
+ void Log(std::string const& str) { impl->Log(str); }
+ bool IsCancelled() { return impl->IsCancelled(); }
+ /// Show the passed dialog on the GUI thread, blocking the calling
+ /// thread until it closes
+ void ShowConfigDialog(ScriptConfigDialog *config_dialog);
- /// DOCME
- volatile bool has_inited;
+ /// Get the current automation trace level
+ int GetTraceLevel() const { return trace_level; }
- /// DOCME
- volatile bool script_finished;
-
- DECLARE_EVENT_TABLE()
+ ProgressSink(agi::ProgressSink *impl, BackgroundScriptRunner *bsr);
};
@@ -511,6 +423,8 @@ namespace Automation4 {
void Reload();
};
+ /// Both a base class for script factories and a manager of registered
+ /// script factories
class ScriptFactory {
/// Vector of loaded script engines
static std::vector *factories;
@@ -556,12 +470,8 @@ namespace Automation4 {
static const std::vector& GetFactories();
};
-
- /// DOCME
- /// @class UnknownScript
- /// @brief DOCME
- ///
- /// DOCME
+ /// A script which represents a file not recognized by any registered
+ /// automation engines
class UnknownScript : public Script {
public:
UnknownScript(const wxString &filename);
diff --git a/aegisub/src/auto4_lua.cpp b/aegisub/src/auto4_lua.cpp
index 3e687d60e..f838b76aa 100644
--- a/aegisub/src/auto4_lua.cpp
+++ b/aegisub/src/auto4_lua.cpp
@@ -324,7 +324,7 @@ namespace Automation4 {
name = GetPrettyFilename();
description = "Unknown error initialising Lua script";
}
- }
+ }
/// @brief DOCME
@@ -503,7 +503,7 @@ namespace Automation4 {
lua_pushnil(L);
return 1;
}
- }
+ }
/// @brief DOCME
@@ -521,7 +521,7 @@ namespace Automation4 {
lua_pushnil(L);
return 1;
}
- }
+ }
/// @brief DOCME
@@ -543,62 +543,31 @@ namespace Automation4 {
}
}
-
- // LuaThreadedCall
-
-
- /// @brief DOCME
- /// @param _L
- /// @param _nargs
- /// @param _nresults
- ///
- LuaThreadedCall::LuaThreadedCall(lua_State *_L, int _nargs, int _nresults)
- : wxThread(wxTHREAD_JOINABLE)
- , L(_L)
- , nargs(_nargs)
- , nresults(_nresults)
+ static void lua_threaded_call(ProgressSink *ps, lua_State *L, int nargs, int nresults, bool can_open_config)
{
- int prio = OPT_GET("Automation/Lua/Thread Priority")->GetInt();
- if (prio == 0) prio = 50; // normal
- else if (prio == 1) prio = 30; // below normal
- else if (prio == 2) prio = 10; // lowest
- else prio = 50; // fallback normal
- Create();
- SetPriority(prio);
- Run();
- }
+ LuaProgressSink lps(L, ps, can_open_config);
-
- /// @brief DOCME
- /// @return
- ///
- wxThread::ExitCode LuaThreadedCall::Entry()
- {
- int result = lua_pcall(L, nargs, nresults, 0);
-
- // see if there's a progress sink window to close
- lua_getfield(L, LUA_REGISTRYINDEX, "progress_sink");
- if (lua_isuserdata(L, -1)) {
- LuaProgressSink *ps = LuaProgressSink::GetObjPointer(L, -1);
-
- if (result) {
+ if (lua_pcall(L, nargs, nresults, 0)) {
// if the call failed, log the error here
- wxString errmsg(lua_tostring(L, -2), wxConvUTF8);
- ps->AddDebugOutput("\n\nLua reported a runtime error:\n");
- ps->AddDebugOutput(errmsg);
+ ps->Log("\n\nLua reported a runtime error:\n");
+ ps->Log(lua_tostring(L, -1));
lua_pop(L, 1);
}
- // don't bother protecting this with a mutex, it should be safe enough like this
- ps->script_finished = true;
- // tell wx to run its idle-events now, just to make the progress window notice earlier that we're done
- wxWakeUpIdle();
- }
- lua_pop(L, 1);
-
lua_gc(L, LUA_GCCOLLECT, 0);
- if (result) return (wxThread::ExitCode) 1;
- else return 0;
+ }
+
+
+ // LuaThreadedCall
+ void LuaThreadedCall(lua_State *L, int nargs, int nresults, wxString const& title, wxWindow *parent, bool can_open_config)
+ {
+ BackgroundScriptRunner bsr(parent, title);
+ try {
+ bsr.Run(bind(lua_threaded_call, std::tr1::placeholders::_1, L, nargs, nresults, can_open_config));
+ }
+ catch (agi::UserCancelException const&) {
+ /// @todo perhaps this needs to continue up for exporting?
+ }
}
@@ -779,25 +748,16 @@ namespace Automation4 {
void LuaFeatureMacro::Process(AssFile *subs, std::vector &selected, int active, wxWindow * const progress_parent)
{
GetFeatureFunction(1); // 1 = processing function
-
- // prepare function call
LuaAssFile *subsobj = new LuaAssFile(L, subs, true, true);
- (void) subsobj;
CreateIntegerArray(selected); // selected items
lua_pushinteger(L, -1); // active line
- LuaProgressSink *ps = new LuaProgressSink(L, progress_parent);
- ps->SetTitle(GetName());
-
// do call
// 3 args: subtitles, selected lines, active line
// 1 result: new selected lines
- LuaThreadedCall call(L, 3, 1);
+ LuaThreadedCall(L, 3, 1, GetName(), progress_parent, true);
- ps->ShowModal();
- wxThread::ExitCode code = call.Wait();
- (void) code; // ignore
- //if (code) ThrowError();
+ subsobj->ProcessingComplete(GetName());
// top of stack will be selected lines array, if any was returned
if (lua_istable(L, -1)) {
@@ -815,8 +775,6 @@ namespace Automation4 {
}
// either way, there will be something on the stack
lua_pop(L, 1);
-
- delete ps;
}
@@ -905,24 +863,11 @@ namespace Automation4 {
assert(lua_istable(L, -1));
stackcheck.check_stack(3);
- LuaProgressSink *ps = new LuaProgressSink(L, export_dialog, false);
- ps->SetTitle(GetName());
- stackcheck.check_stack(3);
-
- // do call
- LuaThreadedCall call(L, 2, 0);
-
- ps->ShowModal();
- wxThread::ExitCode code = call.Wait();
- (void) code;
- //if (code) ThrowError();
+ LuaThreadedCall(L, 2, 0, AssExportFilter::GetName(), export_dialog, false);
stackcheck.check_stack(0);
- // Just ensure that subsobj survives until here
- (void) subsobj;
-
- delete ps;
+ subsobj->ProcessingComplete();
}
diff --git a/aegisub/src/auto4_lua.h b/aegisub/src/auto4_lua.h
index 25d70f9a0..c78c72ebd 100644
--- a/aegisub/src/auto4_lua.h
+++ b/aegisub/src/auto4_lua.h
@@ -122,17 +122,7 @@ namespace Automation4 {
LuaAssFile(lua_State *L, AssFile *ass, bool can_modify = false, bool can_set_undo = false);
};
-
-
- /// DOCME
- /// @class LuaProgressSink
- /// @brief DOCME
- ///
- /// DOCME
- class LuaProgressSink : public ProgressSink {
- private:
-
- /// DOCME
+ class LuaProgressSink {
lua_State *L;
static int LuaSetProgress(lua_State *L);
@@ -143,10 +133,10 @@ namespace Automation4 {
static int LuaDisplayDialog(lua_State *L);
public:
- LuaProgressSink(lua_State *_L, wxWindow *parent, bool allow_config_dialog = true);
- virtual ~LuaProgressSink();
+ LuaProgressSink(lua_State *L, ProgressSink *ps, bool allow_config_dialog = true);
+ ~LuaProgressSink();
- static LuaProgressSink* GetObjPointer(lua_State *L, int idx);
+ static ProgressSink* GetObjPointer(lua_State *L, int idx);
};
@@ -317,26 +307,7 @@ namespace Automation4 {
- /// DOCME
- /// @class LuaThreadedCall
- /// @brief DOCME
- ///
- /// DOCME
- class LuaThreadedCall : public wxThread {
- private:
-
- /// DOCME
- lua_State *L;
-
- /// DOCME
- int nargs;
-
- /// DOCME
- int nresults;
- public:
- LuaThreadedCall(lua_State *_L, int _nargs, int _nresults);
- virtual ExitCode Entry();
- };
+ void LuaThreadedCall(lua_State *L, int nargs, int nresults, wxString const& title, wxWindow *parent, bool can_open_config);
diff --git a/aegisub/src/auto4_lua_assfile.cpp b/aegisub/src/auto4_lua_assfile.cpp
index 49d244165..ffe9cb731 100644
--- a/aegisub/src/auto4_lua_assfile.cpp
+++ b/aegisub/src/auto4_lua_assfile.cpp
@@ -107,7 +107,7 @@ namespace {
lua_setfield(L, -2, name);
}
- DEFINE_SIMPLE_EXCEPTION_NOINNER(BadField, Automation4::MacroRunError, "automation/macro/bad_field")
+ DEFINE_SIMPLE_EXCEPTION_NOINNER(BadField, agi::Exception, "automation/macro/bad_field")
BadField bad_field(const char *expected_type, const char *name, const char *line_clasee)
{
return BadField(std::string("Invalid ") + expected_type + " '" + name + "' field in '" + line_clasee + "' class subtitle line");
diff --git a/aegisub/src/auto4_lua_progresssink.cpp b/aegisub/src/auto4_lua_progresssink.cpp
index c40afc4e5..b95cc34c4 100644
--- a/aegisub/src/auto4_lua_progresssink.cpp
+++ b/aegisub/src/auto4_lua_progresssink.cpp
@@ -44,45 +44,50 @@
#include
#endif
-static void push_closure(lua_State *L, const char *name, lua_CFunction fn) {
- lua_pushvalue(L, -3);
- lua_pushcclosure(L, fn, 1);
- lua_setfield(L, -2, name);
+namespace {
+ void set_field_to_closure(lua_State *L, const char *name, lua_CFunction fn, int ps_idx = -3)
+ {
+ lua_pushvalue(L, ps_idx);
+ lua_pushcclosure(L, fn, 1);
+ lua_setfield(L, -2, name);
+ }
+
+ void set_field_to_nil(lua_State *L, int idx, const char *name)
+ {
+ lua_pushnil(L);
+ lua_setfield(L, idx, name);
+ }
}
namespace Automation4 {
- LuaProgressSink::LuaProgressSink(lua_State *L, wxWindow *parent, bool allow_config_dialog)
- : ProgressSink(parent)
- , L(L)
+ LuaProgressSink::LuaProgressSink(lua_State *L, ProgressSink *ps, bool allow_config_dialog)
+ : L(L)
{
- LuaProgressSink **ud = (LuaProgressSink**)lua_newuserdata(L, sizeof(LuaProgressSink*));
- *ud = this;
+ ProgressSink **ud = (ProgressSink**)lua_newuserdata(L, sizeof(ProgressSink*));
+ *ud = ps;
// register progress reporting stuff
lua_getglobal(L, "aegisub");
+
+ // Create aegisub.progress table
lua_newtable(L);
-
- push_closure(L, "set", LuaSetProgress);
- push_closure(L, "task", LuaSetTask);
- push_closure(L, "title", LuaSetTitle);
- push_closure(L, "is_cancelled", LuaGetCancelled);
-
+ set_field_to_closure(L, "set", LuaSetProgress);
+ set_field_to_closure(L, "task", LuaSetTask);
+ set_field_to_closure(L, "title", LuaSetTitle);
+ set_field_to_closure(L, "is_cancelled", LuaGetCancelled);
lua_setfield(L, -2, "progress");
+ // Create aegisub.debug table
lua_newtable(L);
- lua_pushvalue(L, -3);
- lua_pushcclosure(L, LuaDebugOut, 1);
- lua_setfield(L, -2, "out");
+ set_field_to_closure(L, "out", LuaDebugOut);
lua_setfield(L, -2, "debug");
- lua_pushvalue(L, -2);
- lua_pushcclosure(L, LuaDebugOut, 1);
- lua_setfield(L, -2, "log");
+
+ // Set aegisub.log
+ set_field_to_closure(L, "log", LuaDebugOut, -2);
if (allow_config_dialog) {
lua_newtable(L);
- lua_pushvalue(L, -3);
- lua_pushcclosure(L, LuaDisplayDialog, 1);
- lua_setfield(L, -2, "display");
+ set_field_to_closure(L, "display", LuaDisplayDialog);
lua_setfield(L, -2, "dialog");
}
@@ -97,54 +102,50 @@ namespace Automation4 {
{
// remove progress reporting stuff
lua_getglobal(L, "aegisub");
- lua_pushnil(L);
- lua_setfield(L, -2, "progress");
- lua_pushnil(L);
- lua_setfield(L, -2, "debug");
+ set_field_to_nil(L, -2, "progress");
+ set_field_to_nil(L, -2, "debug");
lua_pop(L, 1);
- lua_pushnil(L);
- lua_setfield(L, LUA_REGISTRYINDEX, "progress_sink");
+
+ set_field_to_nil(L, LUA_REGISTRYINDEX, "progress_sink");
}
- LuaProgressSink* LuaProgressSink::GetObjPointer(lua_State *L, int idx)
+ ProgressSink* LuaProgressSink::GetObjPointer(lua_State *L, int idx)
{
assert(lua_type(L, idx) == LUA_TUSERDATA);
- void *ud = lua_touserdata(L, idx);
- return *((LuaProgressSink**)ud);
+ return *((ProgressSink**)lua_touserdata(L, idx));
}
int LuaProgressSink::LuaSetProgress(lua_State *L)
{
- GetObjPointer(L, lua_upvalueindex(1))->SetProgress(lua_tonumber(L, 1));
+ GetObjPointer(L, lua_upvalueindex(1))->SetProgress(lua_tonumber(L, 1), 100);
return 0;
}
int LuaProgressSink::LuaSetTask(lua_State *L)
{
- GetObjPointer(L, lua_upvalueindex(1))->SetTask(wxString(lua_tostring(L, 1), wxConvUTF8));
+ GetObjPointer(L, lua_upvalueindex(1))->SetMessage(lua_tostring(L, 1));
return 0;
}
int LuaProgressSink::LuaSetTitle(lua_State *L)
{
- GetObjPointer(L, lua_upvalueindex(1))->SetTitle(wxString(lua_tostring(L, 1), wxConvUTF8));
+ GetObjPointer(L, lua_upvalueindex(1))->SetTitle(lua_tostring(L, 1));
return 0;
}
int LuaProgressSink::LuaGetCancelled(lua_State *L)
{
- lua_pushboolean(L, GetObjPointer(L, lua_upvalueindex(1))->cancelled);
+ lua_pushboolean(L, GetObjPointer(L, lua_upvalueindex(1))->IsCancelled());
return 1;
}
int LuaProgressSink::LuaDebugOut(lua_State *L)
{
- LuaProgressSink *ps = GetObjPointer(L, lua_upvalueindex(1));
+ ProgressSink *ps = GetObjPointer(L, lua_upvalueindex(1));
// Check trace level
if (lua_isnumber(L, 1)) {
- int level = lua_tointeger(L, 1);
- if (level > ps->trace_level)
+ if (lua_tointeger(L, 1) > ps->GetTraceLevel())
return 0;
// remove trace level
lua_remove(L, 1);
@@ -166,14 +167,13 @@ namespace Automation4 {
}
// Top of stack is now a string to output
- wxString msg(lua_tostring(L, 1), wxConvUTF8);
- ps->AddDebugOutput(msg);
+ ps->Log(lua_tostring(L, 1));
return 0;
}
int LuaProgressSink::LuaDisplayDialog(lua_State *L)
{
- LuaProgressSink *ps = GetObjPointer(L, lua_upvalueindex(1));
+ ProgressSink *ps = GetObjPointer(L, lua_upvalueindex(1));
// Check that two arguments were actually given
// If only one, add another empty table for buttons
@@ -185,19 +185,8 @@ namespace Automation4 {
lua_settop(L, 2);
}
- // Send the "show dialog" event
- // See comments in auto4_base.h for more info on this synchronisation
- ShowConfigDialogEvent evt;
-
LuaConfigDialog dlg(L, true); // magically creates the config dialog structure etc
- evt.config_dialog = &dlg;
-
- wxSemaphore sema(0, 1);
- evt.sync_sema = &sema;
-
- ps->AddPendingEvent(evt);
-
- sema.Wait();
+ ps->ShowConfigDialog(&dlg);
// more magic: puts two values on stack: button pushed and table with control results
return dlg.LuaReadBack(L);
diff --git a/aegisub/src/dialog_progress.cpp b/aegisub/src/dialog_progress.cpp
index 02ca179d9..8cb9e7031 100644
--- a/aegisub/src/dialog_progress.cpp
+++ b/aegisub/src/dialog_progress.cpp
@@ -1,216 +1,236 @@
-// Copyright (c) 2005, Rodrigo Braz Monteiro
-// All rights reserved.
+// Copyright (c) 2011, Thomas Goyne
//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
+// 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.
//
-// * Redistributions of source code must retain the above copyright notice,
-// this list of conditions and the following disclaimer.
-// * Redistributions in binary form must reproduce the above copyright notice,
-// this list of conditions and the following disclaimer in the documentation
-// and/or other materials provided with the distribution.
-// * Neither the name of the Aegisub Group nor the names of its contributors
-// may be used to endorse or promote products derived from this software
-// without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
-// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-// POSSIBILITY OF SUCH DAMAGE.
-//
-// Aegisub Project http://www.aegisub.org/
+// 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.
//
// $Id$
/// @file dialog_progress.cpp
-/// @brief Progress-bar dialogue box for displaying during long operations
+/// @brief Progress-bar dialog box for displaying during long operations
/// @ingroup utility
///
-
-///////////
-// Headers
#include "config.h"
+#include "dialog_progress.h"
+
+#include
+
+#include "compat.h"
+#include "utils.h"
+
#ifndef AGI_PRE
#include
+#include
#include
+#include
+#include
#endif
-#include "dialog_progress.h"
-#include "utils.h"
+wxDEFINE_EVENT(EVT_TITLE, wxThreadEvent);
+wxDEFINE_EVENT(EVT_MESSAGE, wxThreadEvent);
+wxDEFINE_EVENT(EVT_PROGRESS, wxThreadEvent);
+wxDEFINE_EVENT(EVT_INDETERMINATE, wxThreadEvent);
+wxDEFINE_EVENT(EVT_LOG, wxThreadEvent);
+wxDEFINE_EVENT(EVT_COMPLETE, wxThreadEvent);
+class DialogProgressSink : public agi::ProgressSink {
+ DialogProgress *dialog;
+ bool cancelled;
+ wxMutex cancelled_mutex;
-DEFINE_EVENT_TYPE(wxEVT_PROGRESS_UPDATE)
-
-
-/// @brief Constructor
-/// @param parent
-/// @param title
-/// @param cancel
-/// @param message
-/// @param cur
-/// @param max
-///
-DialogProgress::DialogProgress(wxWindow *parent,wxString title,volatile bool *cancel,wxString message,int cur,int max)
-: wxDialog(parent,-1,title,wxDefaultPosition,wxDefaultSize,wxBORDER_RAISED/* | wxSTAY_ON_TOP*/)
-{
- // Variables
- canceled = cancel;
- if (cancel) *canceled = false;
- virtualMax = max;
-
- // Gauge
- gauge = new wxGauge(this, -1, 100, wxDefaultPosition, wxSize(300,20), wxGA_HORIZONTAL);
- wxButton *cancelButton = NULL;
- if (cancel) cancelButton = new wxButton(this,wxID_CANCEL);
- text = new wxStaticText(this, -1, message, wxDefaultPosition, wxDefaultSize, wxALIGN_CENTRE | wxST_NO_AUTORESIZE);
-
- // Main sizer
- wxSizer *MainSizer = new wxBoxSizer(wxVERTICAL);
- MainSizer->Add(gauge,1,wxEXPAND | wxALL,5);
- MainSizer->Add(text,0,wxEXPAND | wxALIGN_CENTER | wxBOTTOM,5);
- if (cancel) MainSizer->Add(cancelButton,0,wxALIGN_CENTER | wxLEFT | wxRIGHT | wxBOTTOM,5);
- MainSizer->SetSizeHints(this);
- SetSizer(MainSizer);
- CenterOnParent();
- Connect(0,wxEVT_PROGRESS_UPDATE,wxCommandEventHandler(DialogProgress::OnUpdateProgress));
-}
-
-
-
-/// @brief Set progress
-/// @param cur
-/// @param max
-/// @return
-///
-void DialogProgress::SetProgress(int cur,int max) {
- // Return if there's nothing to do
- int value = cur*100/virtualMax;
- if (gauge->GetValue() == value && virtualMax == max) return;
- virtualMax = max;
-
- // Check if it's the main thread, if so, just process it now
- if (wxIsMainThread()) {
- gauge->SetValue(mid(0,value,100));
- wxYield();
- return;
+ template
+ void SafeQueue(wxEventType type, T const& value) {
+ wxThreadEvent *evt = new wxThreadEvent(type);
+ evt->SetPayload(value);
+ wxQueueEvent(dialog, evt);
}
- // Otherwise, go on
+public:
+ DialogProgressSink(DialogProgress *dialog)
+ : dialog(dialog)
+ , cancelled(false)
{
- wxMutexLocker locker(mutex);
- if (count >= 2) return;
- else count++;
}
- wxCommandEvent evt(wxEVT_PROGRESS_UPDATE,0);
- evt.SetInt(value);
- AddPendingEvent(evt);
-}
+ void SetTitle(std::string const& title) {
+ SafeQueue(EVT_TITLE, lagi_wxString(title));
+ }
+ void SetMessage(std::string const& msg) {
+ SafeQueue(EVT_MESSAGE, lagi_wxString(msg));
+ }
+ void SetProgress(int cur, int max) {
+ SafeQueue(EVT_PROGRESS, int(double(cur) / max * 100));
+ }
-/// @brief Update progress
-/// @param event
-///
-void DialogProgress::OnUpdateProgress(wxCommandEvent &event)
+ void Log(std::string const& str) {
+ SafeQueue(EVT_LOG, lagi_wxString(str));
+ }
+
+ bool IsCancelled() {
+ wxMutexLocker l(cancelled_mutex);
+ return cancelled;
+ }
+
+ void Cancel() {
+ wxMutexLocker l(cancelled_mutex);
+ cancelled = true;
+ }
+
+ void SetIndeterminate() {
+ wxQueueEvent(dialog, new wxThreadEvent(EVT_INDETERMINATE));
+ }
+};
+
+class TaskRunner : public wxThread {
+ std::tr1::function task;
+ agi::ProgressSink *ps;
+ wxDialog *dialog;
+
+public:
+ TaskRunner(std::tr1::function task, agi::ProgressSink *ps, wxDialog *dialog, int priority)
+ : task(task)
+ , ps(ps)
+ , dialog(dialog)
+ {
+ Create();
+ if (priority != -1)
+ SetPriority(priority);
+ Run();
+ }
+
+ wxThread::ExitCode Entry() {
+ task(ps);
+ wxQueueEvent(dialog, new wxThreadEvent(EVT_COMPLETE));
+ return 0;
+ }
+};
+
+DialogProgress::DialogProgress(wxWindow *parent, wxString const& title_text, wxString const& message)
+: wxDialog(parent, -1, title_text, wxDefaultPosition, wxDefaultSize, wxBORDER_RAISED)
+, pulse_timer(GetEventHandler())
{
- int value = event.GetInt();
- if (gauge->GetValue() != value) gauge->SetValue(mid(0,value,100));
- wxMutexLocker locker(mutex);
- count--;
+ title = new wxStaticText(this, -1, title_text, wxDefaultPosition, wxDefaultSize, wxALIGN_CENTRE | wxST_NO_AUTORESIZE);
+ gauge = new wxGauge(this, -1, 100, 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(300, 120), 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();
+
+ Bind(wxEVT_SHOW, &DialogProgress::OnShow, this);
+ Bind(wxEVT_TIMER, &DialogProgress::OnPulseTimer, this);
+
+ Bind(EVT_TITLE, &DialogProgress::OnSetTitle, this);
+ Bind(EVT_MESSAGE, &DialogProgress::OnSetMessage, this);
+ Bind(EVT_PROGRESS, &DialogProgress::OnSetProgress, this);
+ Bind(EVT_INDETERMINATE, &DialogProgress::OnSetIndeterminate, this);
+ Bind(EVT_COMPLETE, &DialogProgress::OnComplete, this);
+ Bind(EVT_LOG, &DialogProgress::OnLog, this);
}
-
-
-/// @brief Set progress
-/// @param setto
-///
-void DialogProgress::SetText(wxString setto) {
- // Lock
- bool isMain = wxIsMainThread();
- if (!isMain) wxMutexGuiEnter();
-
- // Update
- text->SetLabel(setto);
- wxYield();
-
- // Unlock
- if (!isMain) wxMutexGuiLeave();
+void DialogProgress::Run(std::tr1::function task, int priority) {
+ DialogProgressSink ps(this);
+ this->ps = &ps;
+ new TaskRunner(task, &ps, this, priority);
+ if (ShowModal())
+ throw agi::UserCancelException("Cancelled by user");
}
+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();
-///////////////
-// Event table
-BEGIN_EVENT_TABLE(DialogProgress,wxDialog)
- EVT_BUTTON(wxID_CANCEL,DialogProgress::OnCancel)
-END_EVENT_TABLE()
-
-
-
-/// @brief Cancel
-/// @param event
-///
-void DialogProgress::OnCancel(wxCommandEvent &event) {
- if (canceled) *canceled = true;
- bool isMain = wxIsMainThread();
- if (!isMain) wxMutexGuiEnter();
- Destroy();
- if (!isMain) wxMutexGuiLeave();
+ wxSizer *sizer = GetSizer();
+ if (sizer->IsShown(log_output)) {
+ sizer->Hide(log_output);
+ Layout();
+ sizer->Fit(this);
+ log_output->Clear();
+ }
}
-
-/// @brief Thread constructor
-/// @param parent
-/// @param title
-/// @param canceled
-/// @param message
-/// @param cur
-/// @param max
-///
-DialogProgressThread::DialogProgressThread(wxWindow *parent,wxString title,volatile bool *canceled,wxString message,int cur,int max)
-: wxThread(wxTHREAD_DETACHED)
-{
- dialog = new DialogProgress(parent,title,canceled,message,cur,max);
+void DialogProgress::OnSetTitle(wxThreadEvent &evt) {
+ title->SetLabelText(evt.GetPayload());
}
-
-
-/// @brief Thread destructor
-///
-DialogProgressThread::~DialogProgressThread() {
+void DialogProgress::OnSetMessage(wxThreadEvent &evt) {
+ text->SetLabelText(evt.GetPayload());
}
-
-
-/// @brief Thread entry point
-/// @return
-///
-wxThread::ExitCode DialogProgressThread::Entry() {
- dialog->ShowModal();
- dialog = NULL;
- Delete();
- return 0;
+void DialogProgress::OnSetProgress(wxThreadEvent &evt) {
+ gauge->SetValue(mid(0, evt.GetPayload(), 100));
}
-
-
-/// @brief Close
-///
-void DialogProgressThread::Close() {
- wxCommandEvent event(wxEVT_COMMAND_BUTTON_CLICKED,wxID_CANCEL);
- dialog->canceled = NULL;
- dialog->GetEventHandler()->ProcessEvent(event);
+void DialogProgress::OnSetIndeterminate(wxThreadEvent &evt) {
+ pulse_timer.Start(1000);
}
-void DialogProgress::Pulse() {
+void DialogProgress::OnComplete(wxThreadEvent &evt) {
+ 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 = ps->IsCancelled();
+ if (cancelled || log_output->IsEmpty())
+ EndModal(cancelled);
+ else
+ cancel_button->SetLabelText(_("Close"));
+}
+
+void DialogProgress::OnLog(wxThreadEvent &evt) {
+ if (log_output->IsEmpty()) {
+ wxSizer *sizer = GetSizer();
+ sizer->Show(log_output);
+ Layout();
+ sizer->Fit(this);
+ }
+
+ *log_output << evt.GetPayload();
+ log_output->SetInsertionPointEnd();
+}
+
+void DialogProgress::OnCancel(wxCommandEvent &evt) {
+ ps->Cancel();
+ cancel_button->Enable(false);
+ cancel_button->SetLabelText(_("Cancelling..."));
+}
+
+void DialogProgress::OnPulseTimer(wxTimerEvent&) {
gauge->Pulse();
}
diff --git a/aegisub/src/dialog_progress.h b/aegisub/src/dialog_progress.h
index d9d878192..837298071 100644
--- a/aegisub/src/dialog_progress.h
+++ b/aegisub/src/dialog_progress.h
@@ -1,31 +1,16 @@
-// Copyright (c) 2005, Rodrigo Braz Monteiro
-// All rights reserved.
+// Copyright (c) 2011, Thomas Goyne
//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
+// 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.
//
-// * Redistributions of source code must retain the above copyright notice,
-// this list of conditions and the following disclaimer.
-// * Redistributions in binary form must reproduce the above copyright notice,
-// this list of conditions and the following disclaimer in the documentation
-// and/or other materials provided with the distribution.
-// * Neither the name of the Aegisub Group nor the names of its contributors
-// may be used to endorse or promote products derived from this software
-// without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
-// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-// POSSIBILITY OF SUCH DAMAGE.
-//
-// Aegisub Project http://www.aegisub.org/
+// 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.
//
// $Id$
@@ -34,74 +19,52 @@
/// @ingroup utility
///
-
-
-
-///////////
-// Headers
#ifndef AGI_PRE
#include
-#include
-#include
+#include
#endif
+#include
+#include
+
+class DialogProgressSink;
+class wxButton;
+class wxGauge;
+class wxStaticText;
+class wxTextCtrl;
/// DOCME
/// @class DialogProgress
-/// @brief DOCME
-///
-/// DOCME
-class DialogProgress : public wxDialog {
-private:
+/// @brief Progress-bar dialog box for displaying during long operations
+class DialogProgress : public wxDialog, public agi::BackgroundRunner {
+ DialogProgressSink *ps;
- /// DOCME
- volatile int count;
-
- /// DOCME
- int virtualMax;
-
- /// DOCME
- wxMutex mutex;
-
-
- /// DOCME
- wxGauge *gauge;
-
- /// DOCME
+ wxStaticText *title;
wxStaticText *text;
- void OnCancel(wxCommandEvent &event);
- void OnUpdateProgress(wxCommandEvent &event);
+ wxGauge *gauge;
+ wxButton *cancel_button;
+ wxTextCtrl *log_output;
+
+ wxTimer pulse_timer;
+
+ void OnSetTitle(wxThreadEvent &evt);
+ void OnSetMessage(wxThreadEvent &evt);
+ void OnSetProgress(wxThreadEvent &evt);
+ void OnSetIndeterminate(wxThreadEvent &evt);
+ void OnLog(wxThreadEvent &evt);
+ void OnComplete(wxThreadEvent &evt);
+
+ void OnShow(wxShowEvent&);
+ void OnCancel(wxCommandEvent &);
+ void OnPulseTimer(wxTimerEvent&);
public:
+ /// Constructor
+ /// @param parent Parent window of the dialog
+ /// @param title Initial title of the dialog
+ /// @param message Initial message of the dialog
+ DialogProgress(wxWindow *parent, wxString const& title="", wxString const& message="");
- /// DOCME
- volatile bool *canceled;
-
- DialogProgress(wxWindow *parent,wxString title,volatile bool *cancel,wxString message,int cur,int max);
- void SetProgress(int cur,int max);
- void SetText(wxString text);
- void Run();
- void Pulse();
-
- DECLARE_EVENT_TABLE()
-};
-
-
-
-/// DOCME
-/// @class DialogProgressThread
-/// @brief DOCME
-///
-/// DOCME
-class DialogProgressThread : public wxThread {
- DialogProgressThread(wxWindow *parent,wxString title,volatile bool *canceled,wxString message,int cur,int max);
-
-public:
-
- /// DOCME
- DialogProgress *dialog;
-
- ~DialogProgressThread();
- wxThread::ExitCode Entry();
- void Close();
+ /// BackgroundWorker implementation
+ void Run(std::tr1::function task, int priority=-1);
};
diff --git a/aegisub/src/ffmpegsource_common.cpp b/aegisub/src/ffmpegsource_common.cpp
index 73246b2a7..4077c0cd6 100644
--- a/aegisub/src/ffmpegsource_common.cpp
+++ b/aegisub/src/ffmpegsource_common.cpp
@@ -49,6 +49,7 @@
#include
#include "compat.h"
+#include "dialog_progress.h"
#include "ffmpegsource_common.h"
#include "frame_main.h"
#include "main.h"
@@ -58,26 +59,23 @@
wxMutex FFmpegSourceProvider::CleaningInProgress;
-
/// @brief Callback function that updates the indexing progress dialog
/// @param Current The current file positition in bytes
/// @param Total The total file size in bytes
-/// @param Private A pointer to the progress dialog box to update
+/// @param Private A pointer to the progress sink to update
/// @return Returns non-0 if indexing is cancelled, 0 otherwise.
///
-int FFMS_CC FFmpegSourceProvider::UpdateIndexingProgress(int64_t Current, int64_t Total, void *Private) {
- IndexingProgressDialog *Progress = (IndexingProgressDialog *)Private;
-
- if (Progress->IndexingCanceled)
- return 1;
-
- // no one cares about a little bit of a rounding error here anyway
- Progress->ProgressDialog->SetProgress(((int64_t)1000*Current)/Total, 1000);
-
- return 0;
+static int FFMS_CC UpdateIndexingProgress(int64_t Current, int64_t Total, void *Private) {
+ agi::ProgressSink *ps = static_cast(Private);
+ ps->SetProgress(Current, Total);
+ return ps->IsCancelled();
}
-
+/// A wrapper around FFMS_DoIndexing to make the signature void -> void
+static void DoIndexingWrapper(FFMS_Index **Ret, FFMS_Indexer *Indexer, int IndexMask, int ErrorHandling, void *ICPrivate, FFMS_ErrorInfo *ErrorInfo) {
+ *Ret = FFMS_DoIndexing(Indexer, IndexMask, FFMS_TRACKMASK_NONE, NULL, NULL, ErrorHandling,
+ UpdateIndexingProgress, ICPrivate, ErrorInfo);
+}
/// @brief Does indexing of a source file
/// @param Indexer A pointer to the indexer object representing the file to be indexed
@@ -96,21 +94,12 @@ FFMS_Index *FFmpegSourceProvider::DoIndexing(FFMS_Indexer *Indexer, const wxStri
wxString MsgString;
// set up progress dialog callback
- IndexingProgressDialog Progress;
- Progress.IndexingCanceled = false;
- Progress.ProgressDialog = new DialogProgress(AegisubApp::Get()->frame,
- _("Indexing"), &Progress.IndexingCanceled,
- _("Reading timecodes and frame/sample data"), 0, 1);
- Progress.ProgressDialog->Show();
- Progress.ProgressDialog->SetProgress(0,1);
+ DialogProgress Progress(AegisubApp::Get()->frame, _("Indexing"), _("Reading timecodes and frame/sample data"));
// index all audio tracks
- FFMS_Index *Index = FFMS_DoIndexing(Indexer, Trackmask, FFMS_TRACKMASK_NONE, NULL, NULL, IndexEH,
- FFmpegSourceProvider::UpdateIndexingProgress, &Progress, &ErrInfo);
- Progress.ProgressDialog->Destroy();
- if (Progress.IndexingCanceled) {
- throw agi::UserCancelException("indexing cancelled by user");
- }
+ FFMS_Index *Index;
+ Progress.Run(bind(DoIndexingWrapper, &Index, Indexer, Trackmask, IndexEH, std::tr1::placeholders::_1, &ErrInfo));
+
if (Index == NULL) {
MsgString.Append("Failed to index: ").Append(wxString(ErrInfo.Buffer, wxConvUTF8));
throw MsgString;
diff --git a/aegisub/src/ffmpegsource_common.h b/aegisub/src/ffmpegsource_common.h
index 6f63712d8..61e72e23c 100644
--- a/aegisub/src/ffmpegsource_common.h
+++ b/aegisub/src/ffmpegsource_common.h
@@ -45,8 +45,6 @@
#include
-#include "dialog_progress.h"
-
/// Index all tracks
#define FFMS_TRACKMASK_ALL -1
/// Index no tracks
@@ -70,18 +68,10 @@ public:
FFMS_LOG_DEBUG = 48,
};
- /// Indexing progress report dialog
- struct IndexingProgressDialog {
- volatile bool IndexingCanceled;
- DialogProgress *ProgressDialog;
- };
-
/// Mutex preventing two cache cleaner threads from running at the same time
static wxMutex CleaningInProgress;
bool CleanCache();
- static int FFMS_CC UpdateIndexingProgress(int64_t Current, int64_t Total, void *Private);
-
FFMS_Index *DoIndexing(FFMS_Indexer *Indexer, const wxString& Cachename, int Trackmask, FFMS_IndexErrorHandling IndexEH);
std::map GetTracksOfType(FFMS_Indexer *Indexer, FFMS_TrackType Type);
int AskForTrackSelection(const std::map& TrackList, FFMS_TrackType Type);
diff --git a/aegisub/src/libresrc/default_config.json b/aegisub/src/libresrc/default_config.json
index d259ed59b..b59b74963 100644
--- a/aegisub/src/libresrc/default_config.json
+++ b/aegisub/src/libresrc/default_config.json
@@ -86,9 +86,7 @@
"Automation" : {
"Autoreload Mode" : 1,
- "Lua" : {
- "Thread Priority" : 1
- },
+ "Thread Priority" : 1,
"Trace Level" : 3
},
diff --git a/aegisub/src/mkv_wrap.cpp b/aegisub/src/mkv_wrap.cpp
index 0c017906b..9f3ff3501 100644
--- a/aegisub/src/mkv_wrap.cpp
+++ b/aegisub/src/mkv_wrap.cpp
@@ -68,6 +68,69 @@ public:
#define CACHESIZE 65536
+static void read_subtitles(agi::ProgressSink *ps, MatroskaFile *file, MkvStdIO *input, bool srt, bool ssa, double totalTime, AssFile *target) {
+ std::map subList;
+ char *readBuf = 0;
+ size_t readBufSize = 0;
+
+ // Load blocks
+ ulonglong startTime, endTime, filePos;
+ unsigned int rt, frameSize, frameFlags;
+
+ while (mkv_ReadFrame(file,0,&rt,&startTime,&endTime,&filePos,&frameSize,&frameFlags) == 0) {
+ if (ps->IsCancelled()) {
+ delete readBuf;
+ return;
+ }
+
+ // Read to temp
+ if (frameSize > readBufSize) {
+ delete readBuf;
+ readBufSize = frameSize * 2;
+ readBuf = new char[readBufSize];
+ }
+
+ fseek(input->fp, filePos, SEEK_SET);
+ fread(readBuf, 1, frameSize, input->fp);
+ wxString blockString(readBuf, wxConvUTF8, frameSize);
+
+ // Get start and end times
+ longlong timecodeScaleLow = 1000000;
+ AssTime subStart,subEnd;
+ subStart.SetMS(startTime / timecodeScaleLow);
+ subEnd.SetMS(endTime / timecodeScaleLow);
+
+ // Process SSA/ASS
+ if (!srt) {
+ long order = 0, layer = 0;
+ blockString.BeforeFirst(',', &blockString).ToLong(&order);
+ blockString.BeforeFirst(',', &blockString).ToLong(&layer);
+
+ subList[order] = wxString::Format("Dialogue: %d,%s,%s,%s", layer, subStart.GetASSFormated(), subEnd.GetASSFormated(), blockString);
+ }
+ // Process SRT
+ else {
+ blockString = wxString::Format("Dialogue: 0,%s,%s,%s", subStart.GetASSFormated(), subEnd.GetASSFormated(), blockString);
+ blockString.Replace("\r\n","\\N");
+ blockString.Replace("\r","\\N");
+ blockString.Replace("\n","\\N");
+
+ subList[subList.size()] = blockString;
+ }
+
+ ps->SetProgress(startTime, totalTime);
+ }
+
+ delete readBuf;
+
+ // Insert into file
+ wxString group = "[Events]";
+ int version = ssa;
+ for (std::map::iterator it = subList.begin(); it != subList.end(); ++it) {
+ target->AddLine(it->second, group, version, &group);
+ }
+}
+
void MatroskaWrapper::GetSubtitles(wxString const& filename, AssFile *target) {
MkvStdIO input(filename);
char err[2048];
@@ -82,10 +145,6 @@ void MatroskaWrapper::GetSubtitles(wxString const& filename, AssFile *target) {
wxArrayString tracksNames;
unsigned trackToRead;
- // Haali's library variables
- ulonglong startTime, endTime, filePos;
- unsigned int rt, frameSize, frameFlags;
-
// Find tracks
for (unsigned track = 0; track < tracks; track++) {
trackInfo = mkv_GetTrackInfo(file,track);
@@ -122,14 +181,14 @@ void MatroskaWrapper::GetSubtitles(wxString const& filename, AssFile *target) {
}
// Picked track
- // Get codec type (0 = ASS/SSA, 1 = SRT)
+ mkv_SetTrackMask(file, ~(1 << trackToRead));
trackInfo = mkv_GetTrackInfo(file,trackToRead);
wxString CodecID = wxString(trackInfo->CodecID,*wxConvCurrent);
- int codecType = 0;
- if (CodecID == "S_TEXT/UTF8") codecType = 1;
+ bool srt = CodecID == "S_TEXT/UTF8";
+ bool ssa = CodecID == "S_TEXT/SSA";
// Read private data if it's ASS/SSA
- if (codecType == 0) {
+ if (!srt) {
// Read raw data
trackInfo = mkv_GetTrackInfo(file,trackToRead);
wxString privString((const char *)trackInfo->CodecPrivate, wxConvUTF8, trackInfo->CodecPrivateSize);
@@ -155,73 +214,9 @@ void MatroskaWrapper::GetSubtitles(wxString const& filename, AssFile *target) {
longlong timecodeScale = mkv_TruncFloat(trackInfo->TimecodeScale) * segInfo->TimecodeScale;
// Progress bar
- int totalTime = int(double(segInfo->Duration) / timecodeScale);
- volatile bool canceled = false;
- DialogProgress *progress = new DialogProgress(NULL,_("Parsing Matroska"),&canceled,_("Reading subtitles from Matroska file."),0,totalTime);
- progress->Show();
- progress->SetProgress(0,1);
-
- std::map subList;
- char *readBuf = 0;
- size_t readBufSize = 0;
-
- // Load blocks
- mkv_SetTrackMask(file, ~(1 << trackToRead));
- while (mkv_ReadFrame(file,0,&rt,&startTime,&endTime,&filePos,&frameSize,&frameFlags) == 0) {
- if (canceled) {
- delete readBuf;
- throw agi::UserCancelException("cancelled");
- }
-
- // Read to temp
- if (frameSize > readBufSize) {
- delete readBuf;
- readBufSize = frameSize * 2;
- readBuf = new char[readBufSize];
- }
-
- fseek(input.fp, filePos, SEEK_SET);
- fread(readBuf, 1, frameSize, input.fp);
- wxString blockString(readBuf, wxConvUTF8, frameSize);
-
- // Get start and end times
- longlong timecodeScaleLow = 1000000;
- AssTime subStart,subEnd;
- subStart.SetMS(startTime / timecodeScaleLow);
- subEnd.SetMS(endTime / timecodeScaleLow);
-
- // Process SSA/ASS
- if (codecType == 0) {
- long order = 0, layer = 0;
- blockString.BeforeFirst(',', &blockString).ToLong(&order);
- blockString.BeforeFirst(',', &blockString).ToLong(&layer);
-
- subList[order] = wxString::Format("Dialogue: %d,%s,%s,%s", layer, subStart.GetASSFormated(), subEnd.GetASSFormated(), blockString);
- }
- // Process SRT
- else {
- blockString = wxString::Format("Dialogue: 0,%s,%s,%s", subStart.GetASSFormated(), subEnd.GetASSFormated(), blockString);
- blockString.Replace("\r\n","\\N");
- blockString.Replace("\r","\\N");
- blockString.Replace("\n","\\N");
-
- subList[subList.size()] = blockString;
- }
-
- progress->SetProgress(int(double(startTime) / 1000000.0),totalTime);
- }
-
- delete readBuf;
-
- // Insert into file
- wxString group = "[Events]";
- int version = (CodecID == "S_TEXT/SSA");
- for (std::map::iterator it = subList.begin(); it != subList.end(); ++it) {
- target->AddLine(it->second,group,version,&group);
- }
-
- // Close progress bar
- if (!canceled) progress->Destroy();
+ double totalTime = double(segInfo->Duration) / timecodeScale * 1000000.0;
+ DialogProgress progress(NULL, _("Parsing Matroska"), _("Reading subtitles from Matroska file."));
+ progress.Run(bind(read_subtitles, std::tr1::placeholders::_1, file, &input, srt, ssa, totalTime, target));
}
catch (...) {
mkv_Close(file);
diff --git a/aegisub/src/preferences.cpp b/aegisub/src/preferences.cpp
index 6069df78e..0da40a75a 100644
--- a/aegisub/src/preferences.cpp
+++ b/aegisub/src/preferences.cpp
@@ -267,7 +267,7 @@ Automation::Automation(wxTreebook *book, Preferences *parent): OptionPage(book,
const wxString tp_arr[3] = { _("Normal"), _("Below Normal (recommended)"), _("Lowest") };
wxArrayString tp_choice(3, tp_arr);
- OptionChoice(general, _("Thread priority"), tp_choice, "Automation/Lua/Thread Priority");
+ OptionChoice(general, _("Thread priority"), tp_choice, "Automation/Thread Priority");
const wxString ar_arr[4] = { _("No scripts"), _("Subtitle-local scripts"), _("Global autoload scripts"), _("All scripts") };
wxArrayString ar_choice(4, ar_arr);
diff --git a/aegisub/src/subtitles_provider_libass.cpp b/aegisub/src/subtitles_provider_libass.cpp
index 0de56f454..c3667b8ef 100644
--- a/aegisub/src/subtitles_provider_libass.cpp
+++ b/aegisub/src/subtitles_provider_libass.cpp
@@ -121,19 +121,17 @@ public:
}
};
+static void do_wait(agi::ProgressSink *ps, FontConfigCacheThread const * const * const cache_worker) {
+ ps->SetIndeterminate();
+ while (*cache_worker && !ps->IsCancelled())
+ wxMilliSleep(100);
+}
+
static void wait_for_cache_thread(FontConfigCacheThread const * const * const cache_worker) {
if (!*cache_worker) return;
- bool canceled;
- DialogProgress *progress = new DialogProgress(AegisubApp::Get()->frame, "", &canceled, "Caching fonts", 0, 1);
- progress->Show();
- while (*cache_worker) {
- if (canceled) throw agi::UserCancelException("Font caching cancelled");
- progress->Pulse();
- wxYield();
- wxMilliSleep(100);
- }
- progress->Destroy();
+ DialogProgress progress(AegisubApp::Get()->frame, "Updating font index", "This may take several minutes");
+ progress.Run(bind(do_wait, std::tr1::placeholders::_1, cache_worker));
}
/// @brief Constructor