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;
}
// Feature
/// @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)
ExportFilter::ExportFilter(wxString const& name, wxString const& description, int priority)
: AssExportFilter(name, description, priority)
, config_dialog(0)
{
AssExportFilterChain::Register(this);
}
/// @brief DOCME
///
FeatureFilter::~FeatureFilter()
ExportFilter::~ExportFilter()
{
delete config_dialog;
AssExportFilterChain::Unregister(this);
}
/// @brief DOCME
/// @return
///
wxString FeatureFilter::GetScriptSettingsIdentifier()
wxString ExportFilter::GetScriptSettingsIdentifier()
{
return inline_string_encode(wxString::Format("Automation Settings %s", AssExportFilter::GetName()));
return inline_string_encode(wxString::Format("Automation Settings %s", GetName()));
}
/// @brief DOCME
/// @param parent
/// @return
///
wxWindow* FeatureFilter::GetConfigDialogWindow(wxWindow *parent) {
wxWindow* ExportFilter::GetConfigDialogWindow(wxWindow *parent, agi::Context *c) {
if (config_dialog) {
delete config_dialog;
config_dialog = 0;
}
if ((config_dialog = GenerateConfigDialog(parent)) != NULL) {
wxString val = AssFile::top->GetScriptInfo(GetScriptSettingsIdentifier());
if (!val.IsEmpty()) {
if (config_dialog = GenerateConfigDialog(parent, c)) {
wxString val = c->ass->GetScriptInfo(GetScriptSettingsIdentifier());
if (!val.empty())
config_dialog->Unserialise(val);
}
return config_dialog->GetWindow(parent);
} else {
return 0;
}
return 0;
}
/// @brief DOCME
/// @param IsDefault
///
void FeatureFilter::LoadSettings(bool IsDefault) {
void ExportFilter::LoadSettings(bool is_default, agi::Context *c) {
if (config_dialog) {
config_dialog->ReadBack();
wxString val = config_dialog->Serialise();
if (!val.IsEmpty()) {
AssFile::top->SetScriptInfo(GetScriptSettingsIdentifier(), val);
}
if (!val.empty())
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
@ -587,16 +425,12 @@ namespace Automation4 {
/// @brief DOCME
/// @return
///
const std::vector<FeatureMacro*>& ScriptManager::GetMacros()
const std::vector<cmd::Command*>& ScriptManager::GetMacros()
{
macros.clear();
for (std::vector<Script*>::iterator i = scripts.begin(); i != scripts.end(); ++i) {
std::vector<Feature*> &sfs = (*i)->GetFeatures();
for (std::vector<Feature*>::iterator j = sfs.begin(); j != sfs.end(); ++j) {
FeatureMacro *m = dynamic_cast<FeatureMacro*>(*j);
if (!m) continue;
macros.push_back(m);
}
std::vector<cmd::Command*> sfs = (*i)->GetMacros();
copy(sfs.begin(), sfs.end(), back_inserter(macros));
}
return macros;
}
@ -641,12 +475,12 @@ namespace Automation4 {
bool more = dir.GetFirst(&fn, wxEmptyString, wxDIR_FILES);
while (more) {
script_path.SetName(fn);
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++;
}
more = dir.GetNext(&fn);
}
}

View File

@ -57,8 +57,6 @@
#include <libaegisub/signal.h>
#include "ass_export_filter.h"
#include "subtitle_format.h"
class AssFile;
class AssStyle;
@ -70,12 +68,11 @@ class wxStopWatch;
class wxPathList;
namespace agi { struct Context; }
namespace cmd { class Command; }
DECLARE_EVENT_TYPE(wxEVT_AUTOMATION_SCRIPT_COMPLETED, -1)
/// DOCME
namespace Automation4 {
DEFINE_BASE_EXCEPTION_NOINNER(AutomationError, agi::Exception)
@ -85,159 +82,28 @@ namespace Automation4 {
// Calculate the extents of a text string given a style
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;
/// DOCME
/// @class FeatureFilter
/// @brief DOCME
///
/// DOCME
class FeatureFilter : public virtual Feature, public AssExportFilter {
private:
/// DOCME
class ExportFilter : public AssExportFilter {
ScriptConfigDialog *config_dialog;
/// subclasses should implement this, producing a new ScriptConfigDialog
virtual ScriptConfigDialog* GenerateConfigDialog(wxWindow *parent, agi::Context *c) = 0;
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();
public:
virtual ~FeatureFilter();
ExportFilter(wxString const& name, wxString const& description, int priority);
virtual ~ExportFilter();
wxWindow* GetConfigDialogWindow(wxWindow *parent);
void LoadSettings(bool IsDefault);
wxWindow* GetConfigDialogWindow(wxWindow *parent, agi::Context *c);
void LoadSettings(bool is_default, agi::Context *c);
// 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
/// @class ScriptConfigDialog
/// @brief DOCME
@ -343,8 +209,12 @@ namespace Automation4 {
/// Did the script load correctly?
virtual bool GetLoadedState() const=0;
/// Get a list of features provided by this script
virtual std::vector<Feature*> GetFeatures() const=0;
/// Get a list of commands provided by this script
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
@ -360,7 +230,7 @@ namespace Automation4 {
/// DOCME
std::vector<FeatureMacro*> macros;
std::vector<cmd::Command*> macros;
public:
ScriptManager();
@ -372,7 +242,7 @@ namespace Automation4 {
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.
// They automatically register themselves in the relevant places.
};
@ -464,6 +334,8 @@ namespace Automation4 {
wxString GetVersion() const { return ""; }
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_file.h"
#include "ass_override.h"
#include "ass_style.h"
#include "auto4_lua_factory.h"
#include "auto4_lua_scriptreader.h"
#include "include/aegisub/context.h"
#include "main.h"
#include "selection_controller.h"
#include "standard_paths.h"
#include "text_file_reader.h"
#include "utils.h"
#include "subtitle_format.h"
#include "video_context.h"
#include "utils.h"
// This must be below the headers above.
#ifdef __WINDOWS__
@ -78,36 +79,49 @@
#endif
namespace {
void push_value(lua_State *L, lua_CFunction fn) {
inline void push_value(lua_State *L, lua_CFunction 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);
}
void push_value(lua_State *L, double n) {
inline void push_value(lua_State *L, double n)
{
lua_pushnumber(L, n);
}
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);
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);
wxString ret;
if (lua_isstring(L, -1))
ret = wxString(lua_tostring(L, -1), wxConvUTF8);
ret = get_wxstring(L, -1);
lua_pop(L, 1);
return ret;
}
}
namespace Automation4 {
// LuaStackcheck
#if 0
struct LuaStackcheck {
@ -138,35 +152,18 @@ namespace Automation4 {
}
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); }
};
#else
/// DOCME
struct LuaStackcheck {
/// @brief DOCME
/// @param additional
///
void check_stack(int additional) { }
/// @brief DOCME
///
void dump() { }
/// @brief DOCME
/// @param L
///
LuaStackcheck(lua_State *L) { }
/// @brief DOCME
///
~LuaStackcheck() { }
LuaStackcheck(lua_State*) { }
};
#endif
namespace Automation4 {
// LuaScript
LuaScript::LuaScript(wxString const& filename)
: Script(filename)
@ -235,18 +232,14 @@ namespace Automation4 {
// reference to the script object
lua_pushlightuserdata(L, this);
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);
// make "aegisub" table
lua_pushstring(L, "aegisub");
lua_newtable(L);
set_field(L, "register_macro", LuaFeatureMacro::LuaRegister);
set_field(L, "register_filter", LuaFeatureFilter::LuaRegister);
set_field(L, "register_macro", LuaCommand::LuaRegister);
set_field(L, "register_filter", LuaExportFilter::LuaRegister);
set_field(L, "text_extents", LuaTextExtents);
set_field(L, "frame_from_ms", LuaFrameFromMs);
set_field(L, "ms_from_frame", LuaMsFromFrame);
@ -260,8 +253,7 @@ namespace Automation4 {
// load user script
LuaScriptReader script_reader(GetFilename());
if (lua_load(L, script_reader.reader_func, &script_reader, GetPrettyFilename().utf8_str())) {
wxString err(lua_tostring(L, -1), wxConvUTF8);
err.Prepend("Error loading Lua script \"" + GetPrettyFilename() + "\":\n\n");
wxString err = wxString::Format("Error loading Lua script \"%s\":\n\n%s", GetPrettyFilename(), get_wxstring(L, -1));
throw ScriptLoadError(STD_STR(err));
}
_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
if (lua_pcall(L, 0, 0, 0)) {
// error occurred, assumed to be on top of Lua stack
wxString err(lua_tostring(L, -1), wxConvUTF8);
err.Prepend("Error initialising Lua script \"" + GetPrettyFilename() + "\":\n\n");
wxString err = wxString::Format("Error initialising Lua script \"%s\":\n\n%s", GetPrettyFilename(), get_wxstring(L, -1));
throw ScriptLoadError(STD_STR(err));
}
_stackcheck.check_stack(0);
@ -308,7 +299,12 @@ namespace Automation4 {
// Assume the script object is clean if there's no Lua state
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);
L = 0;
@ -319,6 +315,18 @@ namespace Automation4 {
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)
{
lua_getfield(L, LUA_REGISTRYINDEX, "aegisub");
@ -327,18 +335,10 @@ namespace Automation4 {
return (LuaScript*)ptr;
}
int LuaScript::RegisterFeature(Feature *feature) {
features.push_back(feature);
return features.size() - 1;
}
int LuaScript::LuaTextExtents(lua_State *L)
{
if (!lua_istable(L, 1))
return luaL_error(L, "First argument to text_extents must be a table");
if (!lua_isstring(L, 2))
return luaL_error(L, "Second argument to text_extents must be a string");
luaL_argcheck(L, lua_istable(L, 1), 1, "");
luaL_argcheck(L, lua_isstring(L, 2), 2, "");
lua_pushvalue(L, 1);
agi::scoped_ptr<AssEntry> et(LuaAssFile::LuaToAssEntry(L));
@ -347,10 +347,8 @@ namespace Automation4 {
if (!st)
return luaL_error(L, "Not a style entry");
wxString text(lua_tostring(L, 2), wxConvUTF8);
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");
lua_pushnumber(L, width);
@ -366,13 +364,13 @@ namespace Automation4 {
int LuaScript::LuaModuleLoader(lua_State *L)
{
int pretop = lua_gettop(L);
wxString module(lua_tostring(L, -1), wxConvUTF8);
wxString module(get_wxstring(L, -1));
module.Replace(".", LUA_DIRSEP);
lua_getglobal(L, "package");
lua_pushstring(L, "path");
lua_gettable(L, -2);
wxString package_paths(lua_tostring(L, -1), wxConvUTF8);
wxString package_paths(get_wxstring(L, -1));
lua_pop(L, 2);
wxStringTokenizer toker(package_paths, ";", wxTOKEN_STRTOK);
@ -393,10 +391,7 @@ namespace Automation4 {
{
LuaScript *s = GetScriptObject(L);
if (!lua_isstring(L, 1))
return luaL_error(L, "Argument to include must be a string");
wxString fnames(lua_tostring(L, 1), wxConvUTF8);
wxString fnames(check_wxstring(L, 1));
wxFileName fname(fnames);
if (fname.GetDirCount() == 0) {
@ -441,7 +436,6 @@ namespace Automation4 {
lua_pushnumber(L, VideoContext::Get()->TimeAtFrame(frame, agi::vfr::START));
else
lua_pushnil(L);
return 1;
}
@ -487,283 +481,235 @@ namespace Automation4 {
}
}
// LuaFeature
/// @brief DOCME
/// @param _L
/// @param _featureclass
/// @param _name
///
LuaFeature::LuaFeature(lua_State *_L, ScriptFeatureClass _featureclass, const wxString &_name)
: Feature(_featureclass, _name)
, L(_L)
LuaFeature::LuaFeature(lua_State *L)
: L(L)
{
}
/// @brief DOCME
///
void LuaFeature::RegisterFeature()
{
// get the LuaScript objects
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);
myid = luaL_ref(L, LUA_REGISTRYINDEX);
}
/// @brief DOCME
/// @param functionid
///
void LuaFeature::GetFeatureFunction(int functionid)
void LuaFeature::UnregisterFeature()
{
luaL_unref(L, LUA_REGISTRYINDEX, myid);
}
void LuaFeature::GetFeatureFunction(const char *function)
{
// get feature table
lua_getfield(L, LUA_REGISTRYINDEX, "features");
// get this feature's function pointers
lua_rawgeti(L, -1, myid);
lua_rawgeti(L, LUA_REGISTRYINDEX, myid);
// get pointer for validation function
lua_rawgeti(L, -1, functionid);
lua_remove(L, -2);
lua_pushstring(L, function);
lua_rawget(L, -2);
// remove the function table
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
/// @brief DOCME
/// @param L
/// @return
///
int LuaFeatureMacro::LuaRegister(lua_State *L)
int LuaCommand::LuaRegister(lua_State *L)
{
wxString _name(lua_tostring(L, 1), wxConvUTF8);
wxString _description(lua_tostring(L, 2), wxConvUTF8);
LuaFeatureMacro *macro = new LuaFeatureMacro(_name, _description, L);
(void)macro;
cmd::reg(new LuaCommand(L));
return 0;
}
/// @brief DOCME
/// @param _name
/// @param _description
/// @param _L
///
LuaFeatureMacro::LuaFeatureMacro(const wxString &_name, const wxString &_description, lua_State *_L)
: Feature(SCRIPTFEATURE_MACRO, _name)
, FeatureMacro(_name, _description)
, LuaFeature(_L, SCRIPTFEATURE_MACRO, _name)
LuaCommand::LuaCommand(lua_State *L)
: LuaFeature(L)
, cmd_name(std::string("automation/cmd/") + lua_tostring(L, 1))
, display(check_wxstring(L, 1))
, help(get_wxstring(L, 2))
, cmd_type(cmd::COMMAND_NORMAL)
{
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
lua_newtable(L);
// store processing function
if (!lua_isfunction(L, 3)) {
lua_pushstring(L, "The macro processing function must be a function");
lua_error(L);
}
lua_pushstring(L, "run");
lua_pushvalue(L, 3);
lua_rawseti(L, -2, 1);
// and validation function
lua_rawset(L, -3);
// store validation function
lua_pushstring(L, "validate");
lua_pushvalue(L, 4);
no_validate = !lua_isfunction(L, -1);
lua_rawseti(L, -2, 2);
// make the feature known
lua_rawset(L, -3);
// store active function
lua_pushstring(L, "isactive");
lua_pushvalue(L, 5);
lua_rawset(L, -3);
// store the table in the registry
RegisterFeature();
// and remove the feature function table again
lua_pop(L, 1);
LuaScript::GetScriptObject(L)->RegisterCommand(this);
}
/// @brief DOCME
/// @param subs
/// @param selected
/// @param active
/// @return
///
bool LuaFeatureMacro::Validate(AssFile *subs, const std::vector<int> &selected, int active)
LuaCommand::~LuaCommand()
{
if (no_validate)
return true;
UnregisterFeature();
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
LuaAssFile *subsobj = new LuaAssFile(L, subs, false, false);
(void) subsobj;
CreateIntegerArray(selected); // selected items
lua_pushinteger(L, -1); // active line
lua_newtable(L);
int active_idx = -1;
// do call
int err = lua_pcall(L, 3, 1, 0);
bool result;
if (err) {
wxString errmsg(lua_tostring(L, -1), wxConvUTF8);
wxLogWarning("Runtime error in Lua macro validation function:\n%s", errmsg);
result = false;
} else {
result = !!lua_toboolean(L, -1);
int row = 1;
int idx = 1;
for (entryIter it = c->ass->Line.begin(); it != c->ass->Line.end(); ++it, ++row) {
AssDialogue *diag = dynamic_cast<AssDialogue*>(*it);
if (!diag) continue;
if (diag == active_line) active_idx = row;
if (sel.count(diag)) {
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)
lua_pop(L, 1);
return result;
}
/// @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)
void LuaCommand::operator()(agi::Context *c)
{
GetFeatureFunction(1); // 1 = processing function
LuaAssFile *subsobj = new LuaAssFile(L, subs, true, true);
CreateIntegerArray(selected); // selected items
lua_pushinteger(L, -1); // active line
GetFeatureFunction("run");
LuaAssFile *subsobj = new LuaAssFile(L, c->ass, true, true);
lua_pushinteger(L, transform_selection(L, c));
// do call
// 3 args: subtitles, selected lines, active line
// 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
if (lua_istable(L, -1)) {
selected.clear();
selected.reserve(lua_objlen(L, -1));
std::set<AssDialogue*> sel;
entryIter it = c->ass->Line.begin();
int last_idx = 1;
lua_pushnil(L);
while (lua_next(L, -2)) {
if (lua_isnumber(L, -1)) {
// Lua uses one-based indexing but we want zero-based, so subtract one
selected.push_back(lua_tointeger(L, -1) - 1);
int cur = lua_tointeger(L, -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);
}
std::sort(selected.begin(), selected.end());
c->selectionController->SetSelectedSet(sel);
}
// either way, there will be something on the stack
lua_pop(L, 1);
}
bool LuaCommand::IsActive(const agi::Context *c)
{
return false;
}
// LuaFeatureFilter
/// @brief DOCME
/// @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)
LuaExportFilter::LuaExportFilter(lua_State *L)
: ExportFilter(check_wxstring(L, 1), get_wxstring(L, 2), lua_tointeger(L, 3))
, LuaFeature(L)
{
// 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);
if (!lua_isfunction(L, 4)) {
lua_pushstring(L, "The filter processing function must be a function");
lua_error(L);
}
// store processing function
lua_pushstring(L, "run");
lua_pushvalue(L, 4);
lua_rawseti(L, -2, 1);
lua_rawset(L, -3);
// store config function
lua_pushstring(L, "config");
lua_pushvalue(L, 5);
has_config = lua_isfunction(L, -1);
lua_rawseti(L, -2, 2);
lua_rawset(L, -3);
// store the table in the registry
RegisterFeature();
lua_pop(L, 1);
LuaScript::GetScriptObject(L)->RegisterFilter(this);
}
/// @brief DOCME
///
void LuaFeatureFilter::Init()
int LuaExportFilter::LuaRegister(lua_State *L)
{
// Don't think there's anything to do here... (empty in auto3)
}
/// @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;
(void)new LuaExportFilter(L);
return 0;
}
/// @brief DOCME
/// @param subs
/// @param export_dialog
///
void LuaFeatureFilter::ProcessSubs(AssFile *subs, wxWindow *export_dialog)
void LuaExportFilter::ProcessSubs(AssFile *subs, wxWindow *export_dialog)
{
LuaStackcheck stackcheck(L);
GetFeatureFunction(1); // 1 = processing function
assert(lua_isfunction(L, -1));
GetFeatureFunction("run");
stackcheck.check_stack(1);
// prepare function call
// subtitles (undo doesn't make sense in exported subs, in fact it'll totally break the undo system)
LuaAssFile *subsobj = new LuaAssFile(L, subs, true/*allow modifications*/, false/*disallow undo*/);
// The entire point of an export filter is to modify the file, but
// setting undo points makes no sense
LuaAssFile *subsobj = new LuaAssFile(L, subs, true);
assert(lua_isuserdata(L, -1));
stackcheck.check_stack(2);
// config
if (has_config && config_dialog) {
int results_produced = config_dialog->LuaReadBack(L);
@ -777,43 +723,38 @@ namespace Automation4 {
assert(lua_istable(L, -1));
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);
subsobj->ProcessingComplete();
}
/// @brief DOCME
/// @param parent
/// @return
///
ScriptConfigDialog* LuaFeatureFilter::GenerateConfigDialog(wxWindow *parent)
ScriptConfigDialog* LuaExportFilter::GenerateConfigDialog(wxWindow *parent, agi::Context *c)
{
if (!has_config)
return 0;
GetFeatureFunction(2); // 2 = config dialog function
GetFeatureFunction("config"); // 2 = config dialog function
// prepare function call
// subtitles (don't allow any modifications during dialog creation, ideally the subs aren't even accessed)
LuaAssFile *subsobj = new LuaAssFile(L, AssFile::top, false/*allow modifications*/, false/*disallow undo*/);
(void) subsobj;
LuaAssFile *subsobj = new LuaAssFile(L, c->ass);
// stored options
lua_newtable(L); // TODO, nothing for now
// do call
int err = lua_pcall(L, 2, 1, 0);
subsobj->ProcessingComplete();
if (err) {
wxString errmsg(lua_tostring(L, -1), wxConvUTF8);
wxLogWarning("Runtime error in Lua macro validation function:\n%s", errmsg);
wxLogWarning("Runtime error in Lua config dialog function:\n%s", get_wxstring(L, -1));
lua_pop(L, 1); // remove error message
return config_dialog = 0;
} else {
// 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()
@ -832,6 +773,6 @@ namespace Automation4 {
return 0;
}
}
};
}
#endif // WITH_AUTO4_LUA

View File

@ -42,14 +42,11 @@
#include "compat.h"
#include "auto4_base.h"
#ifdef __WINDOWS__
#include "../../contrib/lua51/src/lua.h"
#else
#include <lua.hpp>
#endif
#include "command/command.h"
class AssEntry;
class wxWindow;
struct lua_State;
namespace agi { namespace vfr { class Framerate; } }
namespace Automation4 {
@ -246,83 +243,58 @@ namespace Automation4 {
void ReadBack(); // from auto4 base
};
/// DOCME
/// @class LuaFeature
/// @brief DOCME
///
/// DOCME
class LuaFeature : public virtual Feature {
class LuaFeature {
int myid;
protected:
/// DOCME
lua_State *L;
/// DOCME
int myid;
LuaFeature(lua_State *_L, ScriptFeatureClass _featureclass, const wxString &_name);
void RegisterFeature();
void UnregisterFeature();
void GetFeatureFunction(int functionid);
void CreateIntegerArray(const std::vector<int> &ints);
void GetFeatureFunction(const char *function);
void ThrowError();
LuaFeature(lua_State *L);
};
void LuaThreadedCall(lua_State *L, int nargs, int nresults, wxString const& title, wxWindow *parent, bool can_open_config);
/// DOCME
/// @class LuaFeatureMacro
/// @brief DOCME
///
/// DOCME
class LuaFeatureMacro : public FeatureMacro, LuaFeature {
private:
class LuaCommand : public cmd::Command, private LuaFeature {
std::string cmd_name;
wxString display;
wxString help;
int cmd_type;
/// DOCME
bool no_validate;
protected:
LuaFeatureMacro(const wxString &_name, const wxString &_description, lua_State *_L);
LuaCommand(lua_State *L);
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);
/// @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 LuaFeatureFilter
/// @brief DOCME
///
/// DOCME
class LuaFeatureFilter : public FeatureFilter, LuaFeature {
private:
/// DOCME
class LuaExportFilter : public ExportFilter, private LuaFeature {
bool has_config;
/// DOCME
LuaConfigDialog *config_dialog;
protected:
LuaFeatureFilter(const wxString &_name, const wxString &_description, int merit, lua_State *_L);
LuaExportFilter(lua_State *L);
ScriptConfigDialog* GenerateConfigDialog(wxWindow *parent);
void Init();
ScriptConfigDialog* GenerateConfigDialog(wxWindow *parent, agi::Context *c);
public:
static int LuaRegister(lua_State *L);
/// @brief DOCME
///
virtual ~LuaFeatureFilter() { }
virtual ~LuaExportFilter() { }
void ProcessSubs(AssFile *subs, wxWindow *export_dialog);
};
@ -335,7 +307,8 @@ namespace Automation4 {
wxString author;
wxString version;
std::vector<Feature*> features;
std::vector<cmd::Command*> macros;
std::vector<ExportFilter*> filters;
/// load script and create internal structures etc.
void Create();
@ -353,9 +326,11 @@ namespace Automation4 {
LuaScript(const wxString &filename);
~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
void Reload();
@ -366,6 +341,8 @@ namespace Automation4 {
wxString GetVersion() const { return version; }
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->GetLoadedState() ? _("Correctly loaded") : _("Failed to load"));
for (std::vector<Automation4::Feature*>::iterator f = ei->script->GetFeatures().begin(); f != ei->script->GetFeatures().end(); ++f) {
switch ((*f)->GetClass()) {
case Automation4::SCRIPTFEATURE_MACRO:
info += _(" Macro: "); break;
case Automation4::SCRIPTFEATURE_FILTER:
info += _(" Export filter: "); break;
case Automation4::SCRIPTFEATURE_SUBFORMAT:
info += _(" Subtitle format handler: "); break;
default:
info += " Unknown class: "; break;
}
info += (*f)->GetName() + "\n";
}
std::vector<cmd::Command*> macros = ei->script->GetMacros();
for (std::vector<cmd::Command*>::const_iterator f = macros.begin(); f != macros.end(); ++f)
info += _(" Macro: ") + (*f)->StrDisplay(context) + "\n";
std::vector<Automation4::ExportFilter*> filters = ei->script->GetFilters();
for (std::vector<Automation4::ExportFilter*>::const_iterator f = filters.begin(); f != filters.end(); ++f)
info += _(" Export filter: ") + (*f)->GetName() + "\n";
std::vector<SubtitleFormat*> formats = ei->script->GetFormats();
for (std::vector<SubtitleFormat*>::const_iterator f = formats.begin(); f != formats.end(); ++f)
info += _(" Subtitle format handler: ") + (*f)->GetName() + "\n";
}
wxMessageBox(info, _("Automation Script Info"));