Make automation macros standard commands and remove the Automation4::Feature base class as it's no longer a useful abstraction.

Originally committed to SVN as r5641.
This commit is contained in:
Thomas Goyne 2011-09-28 19:48:47 +00:00
parent 492a0d3046
commit eec3d64221
5 changed files with 311 additions and 689 deletions

View File

@ -191,212 +191,50 @@ namespace Automation4 {
return true; return true;
} }
ExportFilter::ExportFilter(wxString const& name, wxString const& description, int priority)
// Feature : AssExportFilter(name, description, priority)
, config_dialog(0)
/// @brief DOCME
/// @param _featureclass
/// @param _name
///
Feature::Feature(ScriptFeatureClass _featureclass, const wxString &_name)
: featureclass(_featureclass)
, name(_name)
{
// nothing to do
}
/// @brief DOCME
/// @return
///
ScriptFeatureClass Feature::GetClass() const
{
return featureclass;
}
/// @brief DOCME
/// @return
///
FeatureMacro* Feature::AsMacro()
{
if (featureclass == SCRIPTFEATURE_MACRO)
// For VS, remember to enable building with RTTI, otherwise dynamic_cast<> won't work
return dynamic_cast<FeatureMacro*>(this);
return 0;
}
/// @brief DOCME
/// @return
///
FeatureFilter* Feature::AsFilter()
{
if (featureclass == SCRIPTFEATURE_FILTER)
return dynamic_cast<FeatureFilter*>(this);
return 0;
}
/// @brief DOCME
/// @return
///
FeatureSubtitleFormat* Feature::AsSubFormat()
{
if (featureclass == SCRIPTFEATURE_SUBFORMAT)
return dynamic_cast<FeatureSubtitleFormat*>(this);
return 0;
}
/// @brief DOCME
/// @return
///
const wxString& Feature::GetName() const
{
return name;
}
// FeatureMacro
/// @brief DOCME
/// @param _name
/// @param _description
///
FeatureMacro::FeatureMacro(const wxString &_name, const wxString &_description)
: Feature(SCRIPTFEATURE_MACRO, _name)
, description(_description)
{
// nothing to do
}
/// @brief DOCME
/// @return
///
const wxString& FeatureMacro::GetDescription() const
{
return description;
}
// FeatureFilter
/// @brief DOCME
/// @param _name
/// @param _description
/// @param _priority
///
FeatureFilter::FeatureFilter(const wxString &_name, const wxString &_description, int _priority)
: Feature(SCRIPTFEATURE_FILTER, _name)
, AssExportFilter(_name, _description, _priority)
, config_dialog(0)
{ {
AssExportFilterChain::Register(this); AssExportFilterChain::Register(this);
} }
ExportFilter::~ExportFilter()
/// @brief DOCME
///
FeatureFilter::~FeatureFilter()
{ {
delete config_dialog;
AssExportFilterChain::Unregister(this); AssExportFilterChain::Unregister(this);
} }
wxString ExportFilter::GetScriptSettingsIdentifier()
/// @brief DOCME
/// @return
///
wxString FeatureFilter::GetScriptSettingsIdentifier()
{ {
return inline_string_encode(wxString::Format("Automation Settings %s", AssExportFilter::GetName())); return inline_string_encode(wxString::Format("Automation Settings %s", GetName()));
} }
wxWindow* ExportFilter::GetConfigDialogWindow(wxWindow *parent, agi::Context *c) {
/// @brief DOCME
/// @param parent
/// @return
///
wxWindow* FeatureFilter::GetConfigDialogWindow(wxWindow *parent) {
if (config_dialog) { if (config_dialog) {
delete config_dialog; delete config_dialog;
config_dialog = 0; config_dialog = 0;
} }
if ((config_dialog = GenerateConfigDialog(parent)) != NULL) {
wxString val = AssFile::top->GetScriptInfo(GetScriptSettingsIdentifier()); if (config_dialog = GenerateConfigDialog(parent, c)) {
if (!val.IsEmpty()) { wxString val = c->ass->GetScriptInfo(GetScriptSettingsIdentifier());
if (!val.empty())
config_dialog->Unserialise(val); config_dialog->Unserialise(val);
}
return config_dialog->GetWindow(parent); return config_dialog->GetWindow(parent);
} else {
return 0;
} }
return 0;
} }
void ExportFilter::LoadSettings(bool is_default, agi::Context *c) {
/// @brief DOCME
/// @param IsDefault
///
void FeatureFilter::LoadSettings(bool IsDefault) {
if (config_dialog) { if (config_dialog) {
config_dialog->ReadBack(); config_dialog->ReadBack();
wxString val = config_dialog->Serialise(); wxString val = config_dialog->Serialise();
if (!val.IsEmpty()) { if (!val.empty())
AssFile::top->SetScriptInfo(GetScriptSettingsIdentifier(), val); c->ass->SetScriptInfo(GetScriptSettingsIdentifier(), val);
}
} }
} }
// FeatureSubtitleFormat
/// @brief DOCME
/// @param _name
/// @param _extension
///
FeatureSubtitleFormat::FeatureSubtitleFormat(const wxString &_name, const wxString &_extension)
: Feature(SCRIPTFEATURE_SUBFORMAT, _name)
, SubtitleFormat(_name)
, extension(_extension)
{
// nothing to do
}
/// @brief DOCME
/// @return
///
const wxString& FeatureSubtitleFormat::GetExtension() const
{
return extension;
}
/// @brief DOCME
/// @param filename
/// @return
///
bool FeatureSubtitleFormat::CanWriteFile(wxString filename)
{
return !filename.Right(extension.Length()).CmpNoCase(extension);
}
/// @brief DOCME
/// @param filename
/// @return
///
bool FeatureSubtitleFormat::CanReadFile(wxString filename)
{
return !filename.Right(extension.Length()).CmpNoCase(extension);
}
// ScriptConfigDialog // ScriptConfigDialog
@ -587,16 +425,12 @@ namespace Automation4 {
/// @brief DOCME /// @brief DOCME
/// @return /// @return
/// ///
const std::vector<FeatureMacro*>& ScriptManager::GetMacros() const std::vector<cmd::Command*>& ScriptManager::GetMacros()
{ {
macros.clear(); macros.clear();
for (std::vector<Script*>::iterator i = scripts.begin(); i != scripts.end(); ++i) { for (std::vector<Script*>::iterator i = scripts.begin(); i != scripts.end(); ++i) {
std::vector<Feature*> &sfs = (*i)->GetFeatures(); std::vector<cmd::Command*> sfs = (*i)->GetMacros();
for (std::vector<Feature*>::iterator j = sfs.begin(); j != sfs.end(); ++j) { copy(sfs.begin(), sfs.end(), back_inserter(macros));
FeatureMacro *m = dynamic_cast<FeatureMacro*>(*j);
if (!m) continue;
macros.push_back(m);
}
} }
return macros; return macros;
} }
@ -641,12 +475,12 @@ namespace Automation4 {
bool more = dir.GetFirst(&fn, wxEmptyString, wxDIR_FILES); bool more = dir.GetFirst(&fn, wxEmptyString, wxDIR_FILES);
while (more) { while (more) {
script_path.SetName(fn); script_path.SetName(fn);
wxString fullpath = script_path.GetFullPath(); wxString fullpath = script_path.GetFullPath();
if (ScriptFactory::CanHandleScriptFormat(fullpath)) { if (ScriptFactory::CanHandleScriptFormat(fullpath)) {
Script *s = ScriptFactory::CreateFromFile(fullpath, true); Script *s = ScriptFactory::CreateFromFile(fullpath, true);
Add(s); Add(s);
if (!s->GetLoadedState()) error_count++; if (!s->GetLoadedState()) error_count++;
} }
more = dir.GetNext(&fn); more = dir.GetNext(&fn);
} }
} }

View File

@ -57,8 +57,6 @@
#include <libaegisub/signal.h> #include <libaegisub/signal.h>
#include "ass_export_filter.h" #include "ass_export_filter.h"
#include "subtitle_format.h"
class AssFile; class AssFile;
class AssStyle; class AssStyle;
@ -70,12 +68,11 @@ class wxStopWatch;
class wxPathList; class wxPathList;
namespace agi { struct Context; } namespace agi { struct Context; }
namespace cmd { class Command; }
DECLARE_EVENT_TYPE(wxEVT_AUTOMATION_SCRIPT_COMPLETED, -1) DECLARE_EVENT_TYPE(wxEVT_AUTOMATION_SCRIPT_COMPLETED, -1)
/// DOCME /// DOCME
namespace Automation4 { namespace Automation4 {
DEFINE_BASE_EXCEPTION_NOINNER(AutomationError, agi::Exception) DEFINE_BASE_EXCEPTION_NOINNER(AutomationError, agi::Exception)
@ -85,159 +82,28 @@ namespace Automation4 {
// Calculate the extents of a text string given a style // Calculate the extents of a text string given a style
bool CalculateTextExtents(AssStyle *style, wxString &text, double &width, double &height, double &descent, double &extlead); bool CalculateTextExtents(AssStyle *style, wxString &text, double &width, double &height, double &descent, double &extlead);
/// DOCME
enum ScriptFeatureClass {
/// DOCME
SCRIPTFEATURE_MACRO = 0,
/// DOCME
SCRIPTFEATURE_FILTER,
/// DOCME
SCRIPTFEATURE_SUBFORMAT,
/// DOCME
SCRIPTFEATURE_MAX // must be last
};
// A Feature describes a function provided by a Script.
// There are several distinct classes of features.
class FeatureMacro;
class FeatureFilter;
class FeatureSubtitleFormat;
/// DOCME
/// @class Feature
/// @brief DOCME
///
/// DOCME
class Feature {
private:
/// DOCME
ScriptFeatureClass featureclass;
/// DOCME
wxString name;
protected:
Feature(ScriptFeatureClass _featureclass, const wxString &_name);
public:
/// @brief DOCME
///
virtual ~Feature() { }
ScriptFeatureClass GetClass() const;
FeatureMacro* AsMacro();
FeatureFilter* AsFilter();
FeatureSubtitleFormat* AsSubFormat();
virtual const wxString& GetName() const;
};
/// DOCME
/// @class FeatureMacro
/// @brief DOCME
///
/// DOCME
class FeatureMacro : public virtual Feature {
private:
/// DOCME
wxString description;
protected:
FeatureMacro(const wxString &_name, const wxString &_description);
public:
/// @brief DOCME
///
virtual ~FeatureMacro() { }
const wxString& GetDescription() const;
virtual bool Validate(AssFile *subs, const std::vector<int> &selected, int active) = 0;
virtual void Process(AssFile *subs, std::vector<int> &selected, int active, wxWindow * const progress_parent) = 0;
};
class ScriptConfigDialog; class ScriptConfigDialog;
/// DOCME class ExportFilter : public AssExportFilter {
/// @class FeatureFilter
/// @brief DOCME
///
/// DOCME
class FeatureFilter : public virtual Feature, public AssExportFilter {
private:
/// DOCME
ScriptConfigDialog *config_dialog; ScriptConfigDialog *config_dialog;
/// subclasses should implement this, producing a new ScriptConfigDialog
virtual ScriptConfigDialog* GenerateConfigDialog(wxWindow *parent, agi::Context *c) = 0;
protected: protected:
FeatureFilter(const wxString &_name, const wxString &_description, int _priority);
// Subclasses should probably implement AssExportFilter::Init
virtual ScriptConfigDialog* GenerateConfigDialog(wxWindow *parent) = 0; // subclasses should implement this, producing a new ScriptConfigDialog
wxString GetScriptSettingsIdentifier(); wxString GetScriptSettingsIdentifier();
public: public:
virtual ~FeatureFilter(); ExportFilter(wxString const& name, wxString const& description, int priority);
virtual ~ExportFilter();
wxWindow* GetConfigDialogWindow(wxWindow *parent); wxWindow* GetConfigDialogWindow(wxWindow *parent, agi::Context *c);
void LoadSettings(bool IsDefault); void LoadSettings(bool is_default, agi::Context *c);
// Subclasses must implement ProcessSubs from AssExportFilter // Subclasses must implement ProcessSubs from AssExportFilter
}; };
/// DOCME
/// @class FeatureSubtitleFormat
/// @brief DOCME
///
/// DOCME
class FeatureSubtitleFormat : public virtual Feature, public SubtitleFormat {
private:
/// DOCME
wxString extension;
protected:
FeatureSubtitleFormat(const wxString &_name, const wxString &_extension);
public:
/// @brief DOCME
/// @return
///
virtual ~FeatureSubtitleFormat() { }
const wxString& GetExtension() const;
// Default implementations of these are provided, that just checks extension,
// but subclasses can provide more elaborate implementations, or go back to
// the "return false" implementation, in case of reader-only or writer-only.
virtual bool CanWriteFile(wxString filename);
virtual bool CanReadFile(wxString filename);
// Subclasses should implement ReadFile and/or WriteFile here
};
/// DOCME /// DOCME
/// @class ScriptConfigDialog /// @class ScriptConfigDialog
/// @brief DOCME /// @brief DOCME
@ -343,8 +209,12 @@ namespace Automation4 {
/// Did the script load correctly? /// Did the script load correctly?
virtual bool GetLoadedState() const=0; virtual bool GetLoadedState() const=0;
/// Get a list of features provided by this script /// Get a list of commands provided by this script
virtual std::vector<Feature*> GetFeatures() const=0; virtual std::vector<cmd::Command*> GetMacros() const=0;
/// Get a list of export filters provided by this script
virtual std::vector<ExportFilter*> GetFilters() const=0;
/// Get a list of subtitle formats provided by this script
virtual std::vector<SubtitleFormat*> GetFormats() const=0;
}; };
/// DOCME /// DOCME
@ -360,7 +230,7 @@ namespace Automation4 {
/// DOCME /// DOCME
std::vector<FeatureMacro*> macros; std::vector<cmd::Command*> macros;
public: public:
ScriptManager(); ScriptManager();
@ -372,7 +242,7 @@ namespace Automation4 {
const std::vector<Script*>& GetScripts() const; const std::vector<Script*>& GetScripts() const;
const std::vector<FeatureMacro*>& GetMacros(); const std::vector<cmd::Command*>& GetMacros();
// No need to have getters for the other kinds of features, I think. // No need to have getters for the other kinds of features, I think.
// They automatically register themselves in the relevant places. // They automatically register themselves in the relevant places.
}; };
@ -464,6 +334,8 @@ namespace Automation4 {
wxString GetVersion() const { return ""; } wxString GetVersion() const { return ""; }
bool GetLoadedState() const { return false; } bool GetLoadedState() const { return false; }
std::vector<Feature*> GetFeatures() const { return std::vector<Feature*>(); } std::vector<cmd::Command*> GetMacros() const { return std::vector<cmd::Command*>(); }
std::vector<ExportFilter*> GetFilters() const { return std::vector<ExportFilter*>(); }
std::vector<SubtitleFormat*> GetFormats() const { return std::vector<SubtitleFormat*>(); }
}; };
}; };

View File

@ -58,15 +58,16 @@
#include "ass_dialogue.h" #include "ass_dialogue.h"
#include "ass_file.h" #include "ass_file.h"
#include "ass_override.h"
#include "ass_style.h" #include "ass_style.h"
#include "auto4_lua_factory.h" #include "auto4_lua_factory.h"
#include "auto4_lua_scriptreader.h" #include "auto4_lua_scriptreader.h"
#include "include/aegisub/context.h"
#include "main.h" #include "main.h"
#include "selection_controller.h"
#include "standard_paths.h" #include "standard_paths.h"
#include "text_file_reader.h" #include "subtitle_format.h"
#include "utils.h"
#include "video_context.h" #include "video_context.h"
#include "utils.h"
// This must be below the headers above. // This must be below the headers above.
#ifdef __WINDOWS__ #ifdef __WINDOWS__
@ -78,36 +79,49 @@
#endif #endif
namespace { namespace {
void push_value(lua_State *L, lua_CFunction fn) { inline void push_value(lua_State *L, lua_CFunction fn)
{
lua_pushcfunction(L, fn); lua_pushcfunction(L, fn);
} }
void push_value(lua_State *L, int n) { inline void push_value(lua_State *L, int n)
{
lua_pushinteger(L, n); lua_pushinteger(L, n);
} }
void push_value(lua_State *L, double n) { inline void push_value(lua_State *L, double n)
{
lua_pushnumber(L, n); lua_pushnumber(L, n);
} }
template<class T> template<class T>
void set_field(lua_State *L, const char *name, T value) { inline void set_field(lua_State *L, const char *name, T value)
{
push_value(L, value); push_value(L, value);
lua_setfield(L, -2, name); lua_setfield(L, -2, name);
} }
wxString get_global_string(lua_State *L, const char *name) { inline wxString get_wxstring(lua_State *L, int idx)
{
return wxString(lua_tostring(L, idx), wxConvUTF8);
}
inline wxString check_wxstring(lua_State *L, int idx)
{
return wxString(luaL_checkstring(L, idx), wxConvUTF8);
}
wxString get_global_string(lua_State *L, const char *name)
{
lua_getglobal(L, name); lua_getglobal(L, name);
wxString ret; wxString ret;
if (lua_isstring(L, -1)) if (lua_isstring(L, -1))
ret = wxString(lua_tostring(L, -1), wxConvUTF8); ret = get_wxstring(L, -1);
lua_pop(L, 1); lua_pop(L, 1);
return ret; return ret;
} }
} }
namespace Automation4 {
// LuaStackcheck // LuaStackcheck
#if 0 #if 0
struct LuaStackcheck { struct LuaStackcheck {
@ -138,35 +152,18 @@ namespace Automation4 {
} }
LOG_D("automation/lua") << "--- end dump"; LOG_D("automation/lua") << "--- end dump";
} }
LuaStackcheck(lua_State *_L) : L(_L) { startstack = lua_gettop(L); } LuaStackcheck(lua_State *L) : L(L) { startstack = lua_gettop(L); }
~LuaStackcheck() { check_stack(0); } ~LuaStackcheck() { check_stack(0); }
}; };
#else #else
/// DOCME
struct LuaStackcheck { struct LuaStackcheck {
/// @brief DOCME
/// @param additional
///
void check_stack(int additional) { } void check_stack(int additional) { }
/// @brief DOCME
///
void dump() { } void dump() { }
LuaStackcheck(lua_State*) { }
/// @brief DOCME
/// @param L
///
LuaStackcheck(lua_State *L) { }
/// @brief DOCME
///
~LuaStackcheck() { }
}; };
#endif #endif
namespace Automation4 {
// LuaScript // LuaScript
LuaScript::LuaScript(wxString const& filename) LuaScript::LuaScript(wxString const& filename)
: Script(filename) : Script(filename)
@ -235,18 +232,14 @@ namespace Automation4 {
// reference to the script object // reference to the script object
lua_pushlightuserdata(L, this); lua_pushlightuserdata(L, this);
lua_setfield(L, LUA_REGISTRYINDEX, "aegisub"); lua_setfield(L, LUA_REGISTRYINDEX, "aegisub");
// the "feature" table
// integer indexed, using same indexes as "features" vector in the base Script class
lua_newtable(L);
lua_setfield(L, LUA_REGISTRYINDEX, "features");
_stackcheck.check_stack(0); _stackcheck.check_stack(0);
// make "aegisub" table // make "aegisub" table
lua_pushstring(L, "aegisub"); lua_pushstring(L, "aegisub");
lua_newtable(L); lua_newtable(L);
set_field(L, "register_macro", LuaFeatureMacro::LuaRegister); set_field(L, "register_macro", LuaCommand::LuaRegister);
set_field(L, "register_filter", LuaFeatureFilter::LuaRegister); set_field(L, "register_filter", LuaExportFilter::LuaRegister);
set_field(L, "text_extents", LuaTextExtents); set_field(L, "text_extents", LuaTextExtents);
set_field(L, "frame_from_ms", LuaFrameFromMs); set_field(L, "frame_from_ms", LuaFrameFromMs);
set_field(L, "ms_from_frame", LuaMsFromFrame); set_field(L, "ms_from_frame", LuaMsFromFrame);
@ -260,8 +253,7 @@ namespace Automation4 {
// load user script // load user script
LuaScriptReader script_reader(GetFilename()); LuaScriptReader script_reader(GetFilename());
if (lua_load(L, script_reader.reader_func, &script_reader, GetPrettyFilename().utf8_str())) { if (lua_load(L, script_reader.reader_func, &script_reader, GetPrettyFilename().utf8_str())) {
wxString err(lua_tostring(L, -1), wxConvUTF8); wxString err = wxString::Format("Error loading Lua script \"%s\":\n\n%s", GetPrettyFilename(), get_wxstring(L, -1));
err.Prepend("Error loading Lua script \"" + GetPrettyFilename() + "\":\n\n");
throw ScriptLoadError(STD_STR(err)); throw ScriptLoadError(STD_STR(err));
} }
_stackcheck.check_stack(1); _stackcheck.check_stack(1);
@ -271,8 +263,7 @@ namespace Automation4 {
// don't thread this, as there's no point in it and it seems to break on wx 2.8.3, for some reason // don't thread this, as there's no point in it and it seems to break on wx 2.8.3, for some reason
if (lua_pcall(L, 0, 0, 0)) { if (lua_pcall(L, 0, 0, 0)) {
// error occurred, assumed to be on top of Lua stack // error occurred, assumed to be on top of Lua stack
wxString err(lua_tostring(L, -1), wxConvUTF8); wxString err = wxString::Format("Error initialising Lua script \"%s\":\n\n%s", GetPrettyFilename(), get_wxstring(L, -1));
err.Prepend("Error initialising Lua script \"" + GetPrettyFilename() + "\":\n\n");
throw ScriptLoadError(STD_STR(err)); throw ScriptLoadError(STD_STR(err));
} }
_stackcheck.check_stack(0); _stackcheck.check_stack(0);
@ -308,7 +299,12 @@ namespace Automation4 {
// Assume the script object is clean if there's no Lua state // Assume the script object is clean if there's no Lua state
if (!L) return; if (!L) return;
delete_clear(features); // loops backwards because commands remove themselves from macros when
// they're unregistered
for (int i = macros.size() - 1; i >= 0; --i)
cmd::unreg(macros[i]->name());
delete_clear(filters);
lua_close(L); lua_close(L);
L = 0; L = 0;
@ -319,6 +315,18 @@ namespace Automation4 {
Create(); Create();
} }
void LuaScript::RegisterCommand(LuaCommand *command) {
macros.push_back(command);
}
void LuaScript::UnregisterCommand(LuaCommand *command) {
macros.erase(remove(macros.begin(), macros.end(), command), macros.end());
}
void LuaScript::RegisterFilter(LuaExportFilter *filter) {
filters.push_back(filter);
}
LuaScript* LuaScript::GetScriptObject(lua_State *L) LuaScript* LuaScript::GetScriptObject(lua_State *L)
{ {
lua_getfield(L, LUA_REGISTRYINDEX, "aegisub"); lua_getfield(L, LUA_REGISTRYINDEX, "aegisub");
@ -327,18 +335,10 @@ namespace Automation4 {
return (LuaScript*)ptr; return (LuaScript*)ptr;
} }
int LuaScript::RegisterFeature(Feature *feature) {
features.push_back(feature);
return features.size() - 1;
}
int LuaScript::LuaTextExtents(lua_State *L) int LuaScript::LuaTextExtents(lua_State *L)
{ {
if (!lua_istable(L, 1)) luaL_argcheck(L, lua_istable(L, 1), 1, "");
return luaL_error(L, "First argument to text_extents must be a table"); luaL_argcheck(L, lua_isstring(L, 2), 2, "");
if (!lua_isstring(L, 2))
return luaL_error(L, "Second argument to text_extents must be a string");
lua_pushvalue(L, 1); lua_pushvalue(L, 1);
agi::scoped_ptr<AssEntry> et(LuaAssFile::LuaToAssEntry(L)); agi::scoped_ptr<AssEntry> et(LuaAssFile::LuaToAssEntry(L));
@ -347,10 +347,8 @@ namespace Automation4 {
if (!st) if (!st)
return luaL_error(L, "Not a style entry"); return luaL_error(L, "Not a style entry");
wxString text(lua_tostring(L, 2), wxConvUTF8);
double width, height, descent, extlead; double width, height, descent, extlead;
if (!CalculateTextExtents(st, text, width, height, descent, extlead)) if (!CalculateTextExtents(st, check_wxstring(L, 2), width, height, descent, extlead))
return luaL_error(L, "Some internal error occurred calculating text_extents"); return luaL_error(L, "Some internal error occurred calculating text_extents");
lua_pushnumber(L, width); lua_pushnumber(L, width);
@ -366,13 +364,13 @@ namespace Automation4 {
int LuaScript::LuaModuleLoader(lua_State *L) int LuaScript::LuaModuleLoader(lua_State *L)
{ {
int pretop = lua_gettop(L); int pretop = lua_gettop(L);
wxString module(lua_tostring(L, -1), wxConvUTF8); wxString module(get_wxstring(L, -1));
module.Replace(".", LUA_DIRSEP); module.Replace(".", LUA_DIRSEP);
lua_getglobal(L, "package"); lua_getglobal(L, "package");
lua_pushstring(L, "path"); lua_pushstring(L, "path");
lua_gettable(L, -2); lua_gettable(L, -2);
wxString package_paths(lua_tostring(L, -1), wxConvUTF8); wxString package_paths(get_wxstring(L, -1));
lua_pop(L, 2); lua_pop(L, 2);
wxStringTokenizer toker(package_paths, ";", wxTOKEN_STRTOK); wxStringTokenizer toker(package_paths, ";", wxTOKEN_STRTOK);
@ -393,10 +391,7 @@ namespace Automation4 {
{ {
LuaScript *s = GetScriptObject(L); LuaScript *s = GetScriptObject(L);
if (!lua_isstring(L, 1)) wxString fnames(check_wxstring(L, 1));
return luaL_error(L, "Argument to include must be a string");
wxString fnames(lua_tostring(L, 1), wxConvUTF8);
wxFileName fname(fnames); wxFileName fname(fnames);
if (fname.GetDirCount() == 0) { if (fname.GetDirCount() == 0) {
@ -441,7 +436,6 @@ namespace Automation4 {
lua_pushnumber(L, VideoContext::Get()->TimeAtFrame(frame, agi::vfr::START)); lua_pushnumber(L, VideoContext::Get()->TimeAtFrame(frame, agi::vfr::START));
else else
lua_pushnil(L); lua_pushnil(L);
return 1; return 1;
} }
@ -487,283 +481,235 @@ namespace Automation4 {
} }
} }
// LuaFeature // LuaFeature
LuaFeature::LuaFeature(lua_State *L)
: L(L)
/// @brief DOCME
/// @param _L
/// @param _featureclass
/// @param _name
///
LuaFeature::LuaFeature(lua_State *_L, ScriptFeatureClass _featureclass, const wxString &_name)
: Feature(_featureclass, _name)
, L(_L)
{ {
} }
/// @brief DOCME
///
void LuaFeature::RegisterFeature() void LuaFeature::RegisterFeature()
{ {
// get the LuaScript objects myid = luaL_ref(L, LUA_REGISTRYINDEX);
lua_getfield(L, LUA_REGISTRYINDEX, "aegisub");
LuaScript *s = (LuaScript*)lua_touserdata(L, -1);
lua_pop(L, 1);
// add the Feature object
myid = s->RegisterFeature(this);
// create table with the functions
// get features table
lua_getfield(L, LUA_REGISTRYINDEX, "features");
lua_pushvalue(L, -2);
lua_rawseti(L, -2, myid);
lua_pop(L, 1);
} }
void LuaFeature::UnregisterFeature()
/// @brief DOCME {
/// @param functionid luaL_unref(L, LUA_REGISTRYINDEX, myid);
/// }
void LuaFeature::GetFeatureFunction(int functionid)
void LuaFeature::GetFeatureFunction(const char *function)
{ {
// get feature table
lua_getfield(L, LUA_REGISTRYINDEX, "features");
// get this feature's function pointers // get this feature's function pointers
lua_rawgeti(L, -1, myid); lua_rawgeti(L, LUA_REGISTRYINDEX, myid);
// get pointer for validation function // get pointer for validation function
lua_rawgeti(L, -1, functionid); lua_pushstring(L, function);
lua_remove(L, -2); lua_rawget(L, -2);
// remove the function table
lua_remove(L, -2); lua_remove(L, -2);
assert(lua_isfunction(L, -1));
} }
/// @brief DOCME
/// @param ints
///
void LuaFeature::CreateIntegerArray(const std::vector<int> &ints)
{
// create an array-style table with an integer vector in it
// leave the new table on top of the stack
lua_newtable(L);
for (size_t i = 0; i != ints.size(); ++i) {
// We use zero-based indexing but Lua wants one-based, so add one
lua_pushinteger(L, ints[i] + 1);
lua_rawseti(L, -2, (int)i+1);
}
}
/// @brief DOCME
///
void LuaFeature::ThrowError()
{
wxString err(lua_tostring(L, -1), wxConvUTF8);
lua_pop(L, 1);
wxLogError(err);
}
// LuaFeatureMacro // LuaFeatureMacro
int LuaCommand::LuaRegister(lua_State *L)
/// @brief DOCME
/// @param L
/// @return
///
int LuaFeatureMacro::LuaRegister(lua_State *L)
{ {
wxString _name(lua_tostring(L, 1), wxConvUTF8); cmd::reg(new LuaCommand(L));
wxString _description(lua_tostring(L, 2), wxConvUTF8);
LuaFeatureMacro *macro = new LuaFeatureMacro(_name, _description, L);
(void)macro;
return 0; return 0;
} }
LuaCommand::LuaCommand(lua_State *L)
/// @brief DOCME : LuaFeature(L)
/// @param _name , cmd_name(std::string("automation/cmd/") + lua_tostring(L, 1))
/// @param _description , display(check_wxstring(L, 1))
/// @param _L , help(get_wxstring(L, 2))
/// , cmd_type(cmd::COMMAND_NORMAL)
LuaFeatureMacro::LuaFeatureMacro(const wxString &_name, const wxString &_description, lua_State *_L)
: Feature(SCRIPTFEATURE_MACRO, _name)
, FeatureMacro(_name, _description)
, LuaFeature(_L, SCRIPTFEATURE_MACRO, _name)
{ {
if (!lua_isfunction(L, 3))
luaL_error(L, "The macro processing function must be a function");
if (lua_isfunction(L, 4))
cmd_type |= cmd::COMMAND_VALIDATE;
if (lua_isfunction(L, 5))
cmd_type |= cmd::COMMAND_TOGGLE;
// new table for containing the functions for this feature // new table for containing the functions for this feature
lua_newtable(L); lua_newtable(L);
// store processing function // store processing function
if (!lua_isfunction(L, 3)) { lua_pushstring(L, "run");
lua_pushstring(L, "The macro processing function must be a function");
lua_error(L);
}
lua_pushvalue(L, 3); lua_pushvalue(L, 3);
lua_rawseti(L, -2, 1); lua_rawset(L, -3);
// and validation function
// store validation function
lua_pushstring(L, "validate");
lua_pushvalue(L, 4); lua_pushvalue(L, 4);
no_validate = !lua_isfunction(L, -1); lua_rawset(L, -3);
lua_rawseti(L, -2, 2);
// make the feature known // store active function
lua_pushstring(L, "isactive");
lua_pushvalue(L, 5);
lua_rawset(L, -3);
// store the table in the registry
RegisterFeature(); RegisterFeature();
// and remove the feature function table again
lua_pop(L, 1); LuaScript::GetScriptObject(L)->RegisterCommand(this);
} }
LuaCommand::~LuaCommand()
/// @brief DOCME
/// @param subs
/// @param selected
/// @param active
/// @return
///
bool LuaFeatureMacro::Validate(AssFile *subs, const std::vector<int> &selected, int active)
{ {
if (no_validate) UnregisterFeature();
return true; LuaScript::GetScriptObject(L)->UnregisterCommand(this);
}
GetFeatureFunction(2); // 2 = validation function static int transform_selection(lua_State *L, const agi::Context *c)
{
std::set<AssDialogue*> sel = c->selectionController->GetSelectedSet();
AssDialogue *active_line = c->selectionController->GetActiveLine();
// prepare function call lua_newtable(L);
LuaAssFile *subsobj = new LuaAssFile(L, subs, false, false); int active_idx = -1;
(void) subsobj;
CreateIntegerArray(selected); // selected items
lua_pushinteger(L, -1); // active line
// do call int row = 1;
int err = lua_pcall(L, 3, 1, 0); int idx = 1;
bool result; for (entryIter it = c->ass->Line.begin(); it != c->ass->Line.end(); ++it, ++row) {
if (err) { AssDialogue *diag = dynamic_cast<AssDialogue*>(*it);
wxString errmsg(lua_tostring(L, -1), wxConvUTF8); if (!diag) continue;
wxLogWarning("Runtime error in Lua macro validation function:\n%s", errmsg);
result = false; if (diag == active_line) active_idx = row;
} else { if (sel.count(diag)) {
result = !!lua_toboolean(L, -1); lua_pushinteger(L, row);
lua_rawseti(L, -2, idx++);
}
} }
return active_idx;
}
bool LuaCommand::Validate(const agi::Context *c)
{
if (!(cmd_type & cmd::COMMAND_VALIDATE)) return true;
GetFeatureFunction("validate");
LuaAssFile *subsobj = new LuaAssFile(L, c->ass);
lua_pushinteger(L, transform_selection(L, c));
int err = lua_pcall(L, 3, 1, 0);
subsobj->ProcessingComplete();
bool result = false;
if (err)
wxLogWarning("Runtime error in Lua macro validation function:\n%s", get_wxstring(L, -1));
else
result = !!lua_toboolean(L, -1);
// clean up stack (result or error message) // clean up stack (result or error message)
lua_pop(L, 1); lua_pop(L, 1);
return result; return result;
} }
void LuaCommand::operator()(agi::Context *c)
/// @brief DOCME
/// @param subs
/// @param selected
/// @param active
/// @param progress_parent
/// @return
///
void LuaFeatureMacro::Process(AssFile *subs, std::vector<int> &selected, int active, wxWindow * const progress_parent)
{ {
GetFeatureFunction(1); // 1 = processing function GetFeatureFunction("run");
LuaAssFile *subsobj = new LuaAssFile(L, subs, true, true); LuaAssFile *subsobj = new LuaAssFile(L, c->ass, true, true);
CreateIntegerArray(selected); // selected items lua_pushinteger(L, transform_selection(L, c));
lua_pushinteger(L, -1); // active line
// do call // do call
// 3 args: subtitles, selected lines, active line // 3 args: subtitles, selected lines, active line
// 1 result: new selected lines // 1 result: new selected lines
LuaThreadedCall(L, 3, 1, GetName(), progress_parent, true); LuaThreadedCall(L, 3, 1, StrDisplay(c), c->parent, true);
subsobj->ProcessingComplete(GetName()); subsobj->ProcessingComplete(StrDisplay(c));
// top of stack will be selected lines array, if any was returned // top of stack will be selected lines array, if any was returned
if (lua_istable(L, -1)) { if (lua_istable(L, -1)) {
selected.clear(); std::set<AssDialogue*> sel;
selected.reserve(lua_objlen(L, -1)); entryIter it = c->ass->Line.begin();
int last_idx = 1;
lua_pushnil(L); lua_pushnil(L);
while (lua_next(L, -2)) { while (lua_next(L, -2)) {
if (lua_isnumber(L, -1)) { if (lua_isnumber(L, -1)) {
// Lua uses one-based indexing but we want zero-based, so subtract one int cur = lua_tointeger(L, -1);
selected.push_back(lua_tointeger(L, -1) - 1); if (cur < 1 || cur > (int)c->ass->Line.size()) {
wxLogError("Selected row %d is out of bounds (must be 1-%u)", cur, c->ass->Line.size());
break;
}
advance(it, cur - last_idx);
AssDialogue *diag = dynamic_cast<AssDialogue*>(*it);
if (!diag) {
wxLogError("Selected row %d is not a dialogue line", cur);
break;
}
sel.insert(diag);
last_idx = cur;
} }
lua_pop(L, 1); lua_pop(L, 1);
} }
std::sort(selected.begin(), selected.end());
c->selectionController->SetSelectedSet(sel);
} }
// either way, there will be something on the stack // either way, there will be something on the stack
lua_pop(L, 1); lua_pop(L, 1);
} }
bool LuaCommand::IsActive(const agi::Context *c)
{
return false;
}
// LuaFeatureFilter // LuaFeatureFilter
LuaExportFilter::LuaExportFilter(lua_State *L)
: ExportFilter(check_wxstring(L, 1), get_wxstring(L, 2), lua_tointeger(L, 3))
/// @brief DOCME , LuaFeature(L)
/// @param _name
/// @param _description
/// @param merit
/// @param _L
///
LuaFeatureFilter::LuaFeatureFilter(const wxString &_name, const wxString &_description, int merit, lua_State *_L)
: Feature(SCRIPTFEATURE_FILTER, _name)
, FeatureFilter(_name, _description, merit)
, LuaFeature(_L, SCRIPTFEATURE_FILTER, _name)
{ {
// Works the same as in LuaFeatureMacro if (!lua_isfunction(L, 4))
luaL_error(L, "The filter processing function must be a function");
// new table for containing the functions for this feature
lua_newtable(L); lua_newtable(L);
if (!lua_isfunction(L, 4)) {
lua_pushstring(L, "The filter processing function must be a function"); // store processing function
lua_error(L); lua_pushstring(L, "run");
}
lua_pushvalue(L, 4); lua_pushvalue(L, 4);
lua_rawseti(L, -2, 1); lua_rawset(L, -3);
// store config function
lua_pushstring(L, "config");
lua_pushvalue(L, 5); lua_pushvalue(L, 5);
has_config = lua_isfunction(L, -1); has_config = lua_isfunction(L, -1);
lua_rawseti(L, -2, 2); lua_rawset(L, -3);
// store the table in the registry
RegisterFeature(); RegisterFeature();
lua_pop(L, 1);
LuaScript::GetScriptObject(L)->RegisterFilter(this);
} }
int LuaExportFilter::LuaRegister(lua_State *L)
/// @brief DOCME
///
void LuaFeatureFilter::Init()
{ {
// Don't think there's anything to do here... (empty in auto3) (void)new LuaExportFilter(L);
}
/// @brief DOCME
/// @param L
/// @return
///
int LuaFeatureFilter::LuaRegister(lua_State *L)
{
wxString _name(lua_tostring(L, 1), wxConvUTF8);
wxString _description(lua_tostring(L, 2), wxConvUTF8);
int _merit = lua_tointeger(L, 3);
LuaFeatureFilter *filter = new LuaFeatureFilter(_name, _description, _merit, L);
(void) filter;
return 0; return 0;
} }
void LuaExportFilter::ProcessSubs(AssFile *subs, wxWindow *export_dialog)
/// @brief DOCME
/// @param subs
/// @param export_dialog
///
void LuaFeatureFilter::ProcessSubs(AssFile *subs, wxWindow *export_dialog)
{ {
LuaStackcheck stackcheck(L); LuaStackcheck stackcheck(L);
GetFeatureFunction(1); // 1 = processing function GetFeatureFunction("run");
assert(lua_isfunction(L, -1));
stackcheck.check_stack(1); stackcheck.check_stack(1);
// prepare function call // The entire point of an export filter is to modify the file, but
// subtitles (undo doesn't make sense in exported subs, in fact it'll totally break the undo system) // setting undo points makes no sense
LuaAssFile *subsobj = new LuaAssFile(L, subs, true/*allow modifications*/, false/*disallow undo*/); LuaAssFile *subsobj = new LuaAssFile(L, subs, true);
assert(lua_isuserdata(L, -1)); assert(lua_isuserdata(L, -1));
stackcheck.check_stack(2); stackcheck.check_stack(2);
// config // config
if (has_config && config_dialog) { if (has_config && config_dialog) {
int results_produced = config_dialog->LuaReadBack(L); int results_produced = config_dialog->LuaReadBack(L);
@ -777,43 +723,38 @@ namespace Automation4 {
assert(lua_istable(L, -1)); assert(lua_istable(L, -1));
stackcheck.check_stack(3); stackcheck.check_stack(3);
LuaThreadedCall(L, 2, 0, AssExportFilter::GetName(), export_dialog, false); LuaThreadedCall(L, 2, 0, GetName(), export_dialog, false);
stackcheck.check_stack(0); stackcheck.check_stack(0);
subsobj->ProcessingComplete(); subsobj->ProcessingComplete();
} }
ScriptConfigDialog* LuaExportFilter::GenerateConfigDialog(wxWindow *parent, agi::Context *c)
/// @brief DOCME
/// @param parent
/// @return
///
ScriptConfigDialog* LuaFeatureFilter::GenerateConfigDialog(wxWindow *parent)
{ {
if (!has_config) if (!has_config)
return 0; return 0;
GetFeatureFunction(2); // 2 = config dialog function GetFeatureFunction("config"); // 2 = config dialog function
// prepare function call // prepare function call
// subtitles (don't allow any modifications during dialog creation, ideally the subs aren't even accessed) LuaAssFile *subsobj = new LuaAssFile(L, c->ass);
LuaAssFile *subsobj = new LuaAssFile(L, AssFile::top, false/*allow modifications*/, false/*disallow undo*/);
(void) subsobj;
// stored options // stored options
lua_newtable(L); // TODO, nothing for now lua_newtable(L); // TODO, nothing for now
// do call // do call
int err = lua_pcall(L, 2, 1, 0); int err = lua_pcall(L, 2, 1, 0);
subsobj->ProcessingComplete();
if (err) { if (err) {
wxString errmsg(lua_tostring(L, -1), wxConvUTF8); wxLogWarning("Runtime error in Lua config dialog function:\n%s", get_wxstring(L, -1));
wxLogWarning("Runtime error in Lua macro validation function:\n%s", errmsg);
lua_pop(L, 1); // remove error message lua_pop(L, 1); // remove error message
return config_dialog = 0;
} else { } else {
// Create config dialogue from table on top of stack // Create config dialogue from table on top of stack
return config_dialog = new LuaConfigDialog(L, false); config_dialog = new LuaConfigDialog(L, false);
} }
return config_dialog;
} }
LuaScriptFactory::LuaScriptFactory() LuaScriptFactory::LuaScriptFactory()
@ -832,6 +773,6 @@ namespace Automation4 {
return 0; return 0;
} }
} }
}; }
#endif // WITH_AUTO4_LUA #endif // WITH_AUTO4_LUA

View File

@ -42,14 +42,11 @@
#include "compat.h" #include "compat.h"
#include "auto4_base.h" #include "auto4_base.h"
#ifdef __WINDOWS__ #include "command/command.h"
#include "../../contrib/lua51/src/lua.h"
#else
#include <lua.hpp>
#endif
class AssEntry; class AssEntry;
class wxWindow; class wxWindow;
struct lua_State;
namespace agi { namespace vfr { class Framerate; } } namespace agi { namespace vfr { class Framerate; } }
namespace Automation4 { namespace Automation4 {
@ -246,83 +243,58 @@ namespace Automation4 {
void ReadBack(); // from auto4 base void ReadBack(); // from auto4 base
}; };
class LuaFeature {
int myid;
/// DOCME
/// @class LuaFeature
/// @brief DOCME
///
/// DOCME
class LuaFeature : public virtual Feature {
protected: protected:
/// DOCME
lua_State *L; lua_State *L;
/// DOCME
int myid;
LuaFeature(lua_State *_L, ScriptFeatureClass _featureclass, const wxString &_name);
void RegisterFeature(); void RegisterFeature();
void UnregisterFeature();
void GetFeatureFunction(int functionid); void GetFeatureFunction(const char *function);
void CreateIntegerArray(const std::vector<int> &ints);
void ThrowError(); void ThrowError();
LuaFeature(lua_State *L);
}; };
void LuaThreadedCall(lua_State *L, int nargs, int nresults, wxString const& title, wxWindow *parent, bool can_open_config); void LuaThreadedCall(lua_State *L, int nargs, int nresults, wxString const& title, wxWindow *parent, bool can_open_config);
/// DOCME class LuaCommand : public cmd::Command, private LuaFeature {
/// @class LuaFeatureMacro std::string cmd_name;
/// @brief DOCME wxString display;
/// wxString help;
/// DOCME int cmd_type;
class LuaFeatureMacro : public FeatureMacro, LuaFeature {
private:
/// DOCME LuaCommand(lua_State *L);
bool no_validate;
protected:
LuaFeatureMacro(const wxString &_name, const wxString &_description, lua_State *_L);
public: public:
~LuaCommand();
const char* name() { return cmd_name.c_str(); }
wxString StrMenu(const agi::Context *) const { return display; }
wxString StrDisplay(const agi::Context *) const { return display; }
wxString StrHelp() const { return help; }
int Type() const { return cmd_type; }
void operator()(agi::Context *c);
bool Validate(const agi::Context *c);
virtual bool IsActive(const agi::Context *c);
static int LuaRegister(lua_State *L); static int LuaRegister(lua_State *L);
/// @brief DOCME
///
virtual ~LuaFeatureMacro() { }
virtual bool Validate(AssFile *subs, const std::vector<int> &selected, int active);
virtual void Process(AssFile *subs, std::vector<int> &selected, int active, wxWindow * const progress_parent);
}; };
/// DOCME class LuaExportFilter : public ExportFilter, private LuaFeature {
/// @class LuaFeatureFilter
/// @brief DOCME
///
/// DOCME
class LuaFeatureFilter : public FeatureFilter, LuaFeature {
private:
/// DOCME
bool has_config; bool has_config;
/// DOCME
LuaConfigDialog *config_dialog; LuaConfigDialog *config_dialog;
protected: protected:
LuaFeatureFilter(const wxString &_name, const wxString &_description, int merit, lua_State *_L); LuaExportFilter(lua_State *L);
ScriptConfigDialog* GenerateConfigDialog(wxWindow *parent); ScriptConfigDialog* GenerateConfigDialog(wxWindow *parent, agi::Context *c);
void Init();
public: public:
static int LuaRegister(lua_State *L); static int LuaRegister(lua_State *L);
virtual ~LuaExportFilter() { }
/// @brief DOCME
///
virtual ~LuaFeatureFilter() { }
void ProcessSubs(AssFile *subs, wxWindow *export_dialog); void ProcessSubs(AssFile *subs, wxWindow *export_dialog);
}; };
@ -335,7 +307,8 @@ namespace Automation4 {
wxString author; wxString author;
wxString version; wxString version;
std::vector<Feature*> features; std::vector<cmd::Command*> macros;
std::vector<ExportFilter*> filters;
/// load script and create internal structures etc. /// load script and create internal structures etc.
void Create(); void Create();
@ -353,9 +326,11 @@ namespace Automation4 {
LuaScript(const wxString &filename); LuaScript(const wxString &filename);
~LuaScript(); ~LuaScript();
static LuaScript* GetScriptObject(lua_State *L); void RegisterCommand(LuaCommand *command);
void UnregisterCommand(LuaCommand *command);
void RegisterFilter(LuaExportFilter *filter);
int RegisterFeature(Feature *feature); static LuaScript* GetScriptObject(lua_State *L);
// Script implementation // Script implementation
void Reload(); void Reload();
@ -366,6 +341,8 @@ namespace Automation4 {
wxString GetVersion() const { return version; } wxString GetVersion() const { return version; }
bool GetLoadedState() const { return L != 0; } bool GetLoadedState() const { return L != 0; }
std::vector<Feature*> GetFeatures() const { return features; } std::vector<cmd::Command*> GetMacros() const { return macros; }
std::vector<ExportFilter*> GetFilters() const { return filters; }
std::vector<SubtitleFormat*> GetFormats() const { return std::vector<SubtitleFormat*>(); }
}; };
}; };

View File

@ -253,19 +253,17 @@ void DialogAutomation::OnInfo(wxCommandEvent &)
ei->script->GetFilename(), ei->script->GetFilename(),
ei->script->GetLoadedState() ? _("Correctly loaded") : _("Failed to load")); ei->script->GetLoadedState() ? _("Correctly loaded") : _("Failed to load"));
for (std::vector<Automation4::Feature*>::iterator f = ei->script->GetFeatures().begin(); f != ei->script->GetFeatures().end(); ++f) { std::vector<cmd::Command*> macros = ei->script->GetMacros();
switch ((*f)->GetClass()) { for (std::vector<cmd::Command*>::const_iterator f = macros.begin(); f != macros.end(); ++f)
case Automation4::SCRIPTFEATURE_MACRO: info += _(" Macro: ") + (*f)->StrDisplay(context) + "\n";
info += _(" Macro: "); break;
case Automation4::SCRIPTFEATURE_FILTER: std::vector<Automation4::ExportFilter*> filters = ei->script->GetFilters();
info += _(" Export filter: "); break; for (std::vector<Automation4::ExportFilter*>::const_iterator f = filters.begin(); f != filters.end(); ++f)
case Automation4::SCRIPTFEATURE_SUBFORMAT: info += _(" Export filter: ") + (*f)->GetName() + "\n";
info += _(" Subtitle format handler: "); break;
default: std::vector<SubtitleFormat*> formats = ei->script->GetFormats();
info += " Unknown class: "; break; for (std::vector<SubtitleFormat*>::const_iterator f = formats.begin(); f != formats.end(); ++f)
} info += _(" Subtitle format handler: ") + (*f)->GetName() + "\n";
info += (*f)->GetName() + "\n";
}
} }
wxMessageBox(info, _("Automation Script Info")); wxMessageBox(info, _("Automation Script Info"));