diff --git a/aegisub/src/auto4_base.cpp b/aegisub/src/auto4_base.cpp index 8717960b1..3a6d6176e 100644 --- a/aegisub/src/auto4_base.cpp +++ b/aegisub/src/auto4_base.cpp @@ -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(this); - return 0; - } - - - /// @brief DOCME - /// @return - /// - FeatureFilter* Feature::AsFilter() - { - if (featureclass == SCRIPTFEATURE_FILTER) - return dynamic_cast(this); - return 0; - } - - - /// @brief DOCME - /// @return - /// - FeatureSubtitleFormat* Feature::AsSubFormat() - { - if (featureclass == SCRIPTFEATURE_SUBFORMAT) - return dynamic_cast(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& ScriptManager::GetMacros() + const std::vector& ScriptManager::GetMacros() { macros.clear(); for (std::vector::iterator i = scripts.begin(); i != scripts.end(); ++i) { - std::vector &sfs = (*i)->GetFeatures(); - for (std::vector::iterator j = sfs.begin(); j != sfs.end(); ++j) { - FeatureMacro *m = dynamic_cast(*j); - if (!m) continue; - macros.push_back(m); - } + std::vector 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); } } diff --git a/aegisub/src/auto4_base.h b/aegisub/src/auto4_base.h index e80e2d930..beb416aec 100644 --- a/aegisub/src/auto4_base.h +++ b/aegisub/src/auto4_base.h @@ -57,8 +57,6 @@ #include #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 &selected, int active) = 0; - virtual void Process(AssFile *subs, std::vector &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 GetFeatures() const=0; + /// Get a list of commands provided by this script + virtual std::vector GetMacros() const=0; + /// Get a list of export filters provided by this script + virtual std::vector GetFilters() const=0; + /// Get a list of subtitle formats provided by this script + virtual std::vector GetFormats() const=0; }; /// DOCME @@ -360,7 +230,7 @@ namespace Automation4 { /// DOCME - std::vector macros; + std::vector macros; public: ScriptManager(); @@ -372,7 +242,7 @@ namespace Automation4 { const std::vector& GetScripts() const; - const std::vector& GetMacros(); + const std::vector& 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 GetFeatures() const { return std::vector(); } + std::vector GetMacros() const { return std::vector(); } + std::vector GetFilters() const { return std::vector(); } + std::vector GetFormats() const { return std::vector(); } }; }; diff --git a/aegisub/src/auto4_lua.cpp b/aegisub/src/auto4_lua.cpp index d35107b91..f77fb28b4 100644 --- a/aegisub/src/auto4_lua.cpp +++ b/aegisub/src/auto4_lua.cpp @@ -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 - 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 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 &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 &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 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(*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 &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 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(*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 diff --git a/aegisub/src/auto4_lua.h b/aegisub/src/auto4_lua.h index 7aaba7437..18239fdc0 100644 --- a/aegisub/src/auto4_lua.h +++ b/aegisub/src/auto4_lua.h @@ -42,14 +42,11 @@ #include "compat.h" #include "auto4_base.h" -#ifdef __WINDOWS__ -#include "../../contrib/lua51/src/lua.h" -#else -#include -#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 &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 &selected, int active); - virtual void Process(AssFile *subs, std::vector &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 features; + std::vector macros; + std::vector 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 GetFeatures() const { return features; } + std::vector GetMacros() const { return macros; } + std::vector GetFilters() const { return filters; } + std::vector GetFormats() const { return std::vector(); } }; }; diff --git a/aegisub/src/dialog_automation.cpp b/aegisub/src/dialog_automation.cpp index 7c3e04fb1..b178ede2a 100644 --- a/aegisub/src/dialog_automation.cpp +++ b/aegisub/src/dialog_automation.cpp @@ -253,19 +253,17 @@ void DialogAutomation::OnInfo(wxCommandEvent &) ei->script->GetFilename(), ei->script->GetLoadedState() ? _("Correctly loaded") : _("Failed to load")); - for (std::vector::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 macros = ei->script->GetMacros(); + for (std::vector::const_iterator f = macros.begin(); f != macros.end(); ++f) + info += _(" Macro: ") + (*f)->StrDisplay(context) + "\n"; + + std::vector filters = ei->script->GetFilters(); + for (std::vector::const_iterator f = filters.begin(); f != filters.end(); ++f) + info += _(" Export filter: ") + (*f)->GetName() + "\n"; + + std::vector formats = ei->script->GetFormats(); + for (std::vector::const_iterator f = formats.begin(); f != formats.end(); ++f) + info += _(" Subtitle format handler: ") + (*f)->GetName() + "\n"; } wxMessageBox(info, _("Automation Script Info"));