From 492a0d3046f16ac1718e1ca7dbbe64a56eeb1f41 Mon Sep 17 00:00:00 2001 From: Thomas Goyne Date: Wed, 28 Sep 2011 19:48:37 +0000 Subject: [PATCH] Make Automation4::Script mostly pure abstract and clean up the implementation of LuaScript Originally committed to SVN as r5640. --- aegisub/src/auto4_base.cpp | 116 +--------------- aegisub/src/auto4_base.h | 82 +++++------ aegisub/src/auto4_lua.cpp | 271 ++++++++++++++----------------------- aegisub/src/auto4_lua.h | 81 +++++------ 4 files changed, 181 insertions(+), 369 deletions(-) diff --git a/aegisub/src/auto4_base.cpp b/aegisub/src/auto4_base.cpp index 4bed9b270..8717960b1 100644 --- a/aegisub/src/auto4_base.cpp +++ b/aegisub/src/auto4_base.cpp @@ -36,6 +36,8 @@ #include "config.h" +#include "auto4_base.h" + #ifndef AGI_PRE #ifdef __WINDOWS__ #include @@ -60,7 +62,6 @@ #include "ass_file.h" #include "ass_style.h" -#include "auto4_base.h" #include "compat.h" #include "dialog_progress.h" #include "include/aegisub/context.h" @@ -497,20 +498,9 @@ namespace Automation4 { impl->Run(bind(progress_sink_wrapper, task, std::tr1::placeholders::_1, this), prio); } - // Script - - - /// @brief DOCME - /// @param _filename - /// - Script::Script(const wxString &_filename) - : filename(_filename) - , name("") - , description("") - , author("") - , version("") - , loaded(false) + Script::Script(wxString const& filename) + : filename(filename) { // copied from auto3 include_path.clear(); @@ -526,90 +516,6 @@ namespace Automation4 { } } - - /// @brief DOCME - /// - Script::~Script() - { - for (std::vector::iterator f = features.begin(); f != features.end(); ++f) { - delete *f; - } - } - - - /// @brief DOCME - /// @return - /// - wxString Script::GetPrettyFilename() const - { - wxFileName fn(filename); - return fn.GetFullName(); - } - - - /// @brief DOCME - /// @return - /// - const wxString& Script::GetFilename() const - { - return filename; - } - - - /// @brief DOCME - /// @return - /// - const wxString& Script::GetName() const - { - return name; - } - - - /// @brief DOCME - /// @return - /// - const wxString& Script::GetDescription() const - { - return description; - } - - - /// @brief DOCME - /// @return - /// - const wxString& Script::GetAuthor() const - { - return author; - } - - - /// @brief DOCME - /// @return - /// - const wxString& Script::GetVersion() const - { - return version; - } - - - /// @brief DOCME - /// @return - /// - bool Script::GetLoadedState() const - { - return loaded; - } - - - /// @brief DOCME - /// @return - /// - std::vector& Script::GetFeatures() - { - return features; - } - - // ScriptManager @@ -923,18 +829,8 @@ namespace Automation4 { // UnknownScript - - - /// @brief DOCME - /// @param filename - /// - UnknownScript::UnknownScript(const wxString &filename) - : Script(filename) + UnknownScript::UnknownScript(wxString const& filename) + : Script(filename) { - wxFileName fn(filename); - name = fn.GetName(); - description = _("File was not recognized as a script"); - loaded = false; } - }; diff --git a/aegisub/src/auto4_base.h b/aegisub/src/auto4_base.h index 12f581b67..e80e2d930 100644 --- a/aegisub/src/auto4_base.h +++ b/aegisub/src/auto4_base.h @@ -42,6 +42,7 @@ #include #include +#include #include #include #include @@ -312,64 +313,40 @@ namespace Automation4 { ProgressSink(agi::ProgressSink *impl, BackgroundScriptRunner *bsr); }; - - - /// DOCME - /// @class Script - /// @brief DOCME - /// - /// DOCME class Script { - private: - - /// DOCME wxString filename; protected: - - /// DOCME - wxString name; - - /// DOCME - wxString description; - - /// DOCME - wxString author; - - /// DOCME - wxString version; - - /// DOCME - bool loaded; // is the script properly loaded? - - - /// DOCME + /// The automation include path, consisting of the user-specified paths + /// along with the script's path wxPathList include_path; - - - /// DOCME - std::vector features; - - Script(const wxString &_filename); + Script(wxString const& filename); public: - virtual ~Script(); + virtual ~Script() { } + /// Reload this script virtual void Reload() = 0; - const wxString& GetFilename() const; - wxString GetPrettyFilename() const; - const wxString& GetName() const; - const wxString& GetDescription() const; - const wxString& GetAuthor() const; - const wxString& GetVersion() const; - bool GetLoadedState() const; + /// The script's file name with path + wxString GetFilename() const { return filename; } + /// The script's file name without path + wxString GetPrettyFilename() const { return wxFileName(filename).GetFullName(); } + /// The script's name. Not required to be unique. + virtual wxString GetName() const=0; + /// A short description of the script + virtual wxString GetDescription() const=0; + /// The author of the script + virtual wxString GetAuthor() const=0; + /// A version string that should not be used for anything but display + virtual wxString GetVersion() const=0; + /// Did the script load correctly? + virtual bool GetLoadedState() const=0; - std::vector& GetFeatures(); + /// Get a list of features provided by this script + virtual std::vector GetFeatures() const=0; }; - - /// DOCME /// @class ScriptManager /// @brief DOCME @@ -477,11 +454,16 @@ namespace Automation4 { /// automation engines class UnknownScript : public Script { public: - UnknownScript(const wxString &filename); + UnknownScript(wxString const& filename); - /// @brief DOCME - /// - void Reload() { }; + void Reload() { } + + wxString GetName() const { return wxFileName(GetFilename()).GetName(); } + wxString GetDescription() const { return _("File was not recognized as a script"); } + wxString GetAuthor() const { return ""; } + wxString GetVersion() const { return ""; } + bool GetLoadedState() const { return false; } + + std::vector GetFeatures() const { return std::vector(); } }; - }; diff --git a/aegisub/src/auto4_lua.cpp b/aegisub/src/auto4_lua.cpp index 8e7fbc786..d35107b91 100644 --- a/aegisub/src/auto4_lua.cpp +++ b/aegisub/src/auto4_lua.cpp @@ -38,8 +38,10 @@ #ifdef WITH_AUTO4_LUA +#include "auto4_lua.h" + #ifndef AGI_PRE -#include +#include #include @@ -52,17 +54,18 @@ #endif #include +#include #include "ass_dialogue.h" #include "ass_file.h" #include "ass_override.h" #include "ass_style.h" -#include "auto4_lua.h" #include "auto4_lua_factory.h" #include "auto4_lua_scriptreader.h" #include "main.h" #include "standard_paths.h" #include "text_file_reader.h" +#include "utils.h" #include "video_context.h" // This must be below the headers above. @@ -74,13 +77,38 @@ #include #endif +namespace { + void push_value(lua_State *L, lua_CFunction fn) { + lua_pushcfunction(L, fn); + } + + void push_value(lua_State *L, int n) { + lua_pushinteger(L, n); + } + + void push_value(lua_State *L, double n) { + lua_pushnumber(L, n); + } + + template + 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) { + lua_getglobal(L, name); + wxString ret; + if (lua_isstring(L, -1)) + ret = wxString(lua_tostring(L, -1), wxConvUTF8); + lua_pop(L, 1); + return ret; + } +} -/// DOCME namespace Automation4 { // LuaStackcheck - -//#ifdef _DEBUG #if 0 struct LuaStackcheck { lua_State *L; @@ -140,35 +168,22 @@ namespace Automation4 { // LuaScript - - - /// @brief DOCME - /// @param filename - /// - LuaScript::LuaScript(const wxString &filename) - : Script(filename) - , L(0) + LuaScript::LuaScript(wxString const& filename) + : Script(filename) + , L(0) { Create(); } - - /// @brief DOCME - /// LuaScript::~LuaScript() { - if (L) Destroy(); + Destroy(); } - - /// @brief DOCME - /// void LuaScript::Create() { Destroy(); - loaded = true; - try { // create lua environment L = lua_open(); @@ -209,7 +224,7 @@ namespace Automation4 { lua_settable(L, -3); - // Replace the default lua module loader with our utf-8 compatible one + // Replace the default lua module loader with our unicode compatible one lua_getfield(L, -1, "loaders"); lua_pushcfunction(L, LuaModuleLoader); lua_rawseti(L, -2, 2); @@ -229,37 +244,28 @@ namespace Automation4 { // make "aegisub" table lua_pushstring(L, "aegisub"); lua_newtable(L); - // aegisub.register_macro - lua_pushcfunction(L, LuaFeatureMacro::LuaRegister); - lua_setfield(L, -2, "register_macro"); - // aegisub.register_filter - lua_pushcfunction(L, LuaFeatureFilter::LuaRegister); - lua_setfield(L, -2, "register_filter"); - // aegisub.text_extents - lua_pushcfunction(L, LuaTextExtents); - lua_setfield(L, -2, "text_extents"); - // VFR handling - lua_pushcfunction(L, LuaFrameFromMs); - lua_setfield(L, -2, "frame_from_ms"); - lua_pushcfunction(L, LuaMsFromFrame); - lua_setfield(L, -2, "ms_from_frame"); - lua_pushcfunction(L, LuaVideoSize); - lua_setfield(L, -2, "video_size"); - // aegisub.lua_automation_version - lua_pushinteger(L, 4); - lua_setfield(L, -2, "lua_automation_version"); + + set_field(L, "register_macro", LuaFeatureMacro::LuaRegister); + set_field(L, "register_filter", LuaFeatureFilter::LuaRegister); + set_field(L, "text_extents", LuaTextExtents); + set_field(L, "frame_from_ms", LuaFrameFromMs); + set_field(L, "ms_from_frame", LuaMsFromFrame); + set_field(L, "video_size", LuaVideoSize); + set_field(L, "lua_automation_version", 4); + // store aegisub table to globals lua_settable(L, LUA_GLOBALSINDEX); _stackcheck.check_stack(0); // load user script LuaScriptReader script_reader(GetFilename()); - if (lua_load(L, script_reader.reader_func, &script_reader, GetPrettyFilename().mb_str(wxConvUTF8))) { + 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"); throw ScriptLoadError(STD_STR(err)); } _stackcheck.check_stack(1); + // and execute it // this is where features are registered // don't thread this, as there's no point in it and it seems to break on wx 2.8.3, for some reason @@ -270,80 +276,49 @@ namespace Automation4 { throw ScriptLoadError(STD_STR(err)); } _stackcheck.check_stack(0); + lua_getglobal(L, "version"); - if (lua_isnumber(L, -1)) { - if (lua_tointeger(L, -1) == 3) { - lua_pop(L, 1); // just to avoid tripping the stackcheck in debug + if (lua_isnumber(L, -1) && lua_tointeger(L, -1) == 3) { + lua_pop(L, 1); // just to avoid tripping the stackcheck in debug throw ScriptLoadError("Attempted to load an Automation 3 script as an Automation 4 Lua script. Automation 3 is no longer supported."); - } } - lua_getglobal(L, "script_name"); - if (lua_isstring(L, -1)) { - name = wxString(lua_tostring(L, -1), wxConvUTF8); - } else { + + name = get_global_string(L, "script_name"); + description = get_global_string(L, "script_description"); + author = get_global_string(L, "script_author"); + version = get_global_string(L, "script_version"); + + if (name.empty()) name = GetPrettyFilename(); - } - lua_getglobal(L, "script_description"); - if (lua_isstring(L, -1)) { - description = wxString(lua_tostring(L, -1), wxConvUTF8); - } - lua_getglobal(L, "script_author"); - if (lua_isstring(L, -1)) { - author = wxString(lua_tostring(L, -1), wxConvUTF8); - } - lua_getglobal(L, "script_version"); - if (lua_isstring(L, -1)) { - version = wxString(lua_tostring(L, -1), wxConvUTF8); - } - lua_pop(L, 5); + + lua_pop(L, 1); // if we got this far, the script should be ready _stackcheck.check_stack(0); } catch (agi::Exception const& e) { Destroy(); - loaded = false; name = GetPrettyFilename(); description = e.GetChainedMessage(); } - } + } - /// @brief DOCME - /// @return - /// void LuaScript::Destroy() { // Assume the script object is clean if there's no Lua state if (!L) return; - // remove features - for (int i = 0; i < (int)features.size(); i++) { - Feature *f = features[i]; - delete f; - } - features.clear(); + delete_clear(features); - // delete environment lua_close(L); L = 0; - - loaded = false; } - - /// @brief DOCME - /// void LuaScript::Reload() { - Destroy(); Create(); } - - /// @brief DOCME - /// @param L - /// @return - /// LuaScript* LuaScript::GetScriptObject(lua_State *L) { lua_getfield(L, LUA_REGISTRYINDEX, "aegisub"); @@ -352,41 +327,31 @@ namespace Automation4 { return (LuaScript*)ptr; } + int LuaScript::RegisterFeature(Feature *feature) { + features.push_back(feature); + return features.size() - 1; + } - /// @brief DOCME - /// @param L - /// @return - /// int LuaScript::LuaTextExtents(lua_State *L) { - if (!lua_istable(L, 1)) { - lua_pushstring(L, "First argument to text_extents must be a table"); - lua_error(L); - } - if (!lua_isstring(L, 2)) { - lua_pushstring(L, "Second argument to text_extents must be a string"); - lua_error(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"); lua_pushvalue(L, 1); - AssEntry *et = LuaAssFile::LuaToAssEntry(L); - AssStyle *st = dynamic_cast(et); + agi::scoped_ptr et(LuaAssFile::LuaToAssEntry(L)); + AssStyle *st = dynamic_cast(et.get()); lua_pop(L, 1); - if (!st) { - delete et; // Make sure to delete the "live" pointer - lua_pushstring(L, "Not a style entry"); - lua_error(L); - } + 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)) { - delete st; - lua_pushstring(L, "Some internal error occurred calculating text_extents"); - lua_error(L); - } - delete st; + if (!CalculateTextExtents(st, text, width, height, descent, extlead)) + return luaL_error(L, "Some internal error occurred calculating text_extents"); lua_pushnumber(L, width); lua_pushnumber(L, height); @@ -417,28 +382,20 @@ namespace Automation4 { if (wxFileName::FileExists(filename)) { LuaScriptReader script_reader(filename); if (lua_load(L, script_reader.reader_func, &script_reader, filename.utf8_str())) { - lua_pushfstring(L, "Error loading Lua module \"%s\":\n\n%s", filename.utf8_str().data(), lua_tostring(L, -1)); - lua_error(L); - return lua_gettop(L) - pretop; + return luaL_error(L, "Error loading Lua module \"%s\":\n\n%s", filename.utf8_str().data(), lua_tostring(L, -1)); } } } return lua_gettop(L) - pretop; } - /// @brief DOCME - /// @param L - /// @return - /// int LuaScript::LuaInclude(lua_State *L) { LuaScript *s = GetScriptObject(L); - if (!lua_isstring(L, 1)) { - lua_pushstring(L, "Argument to include must be a string"); - lua_error(L); - return 0; - } + if (!lua_isstring(L, 1)) + return luaL_error(L, "Argument to include must be a string"); + wxString fnames(lua_tostring(L, 1), wxConvUTF8); wxFileName fname(fnames); @@ -452,63 +409,42 @@ namespace Automation4 { } else { // absolute path, do nothing } - if (!fname.IsOk() || !fname.FileExists()) { - lua_pushfstring(L, "Lua include not found: %s", fnames.mb_str(wxConvUTF8).data()); - lua_error(L); - } + if (!fname.IsOk() || !fname.FileExists()) + return luaL_error(L, "Lua include not found: %s", fnames.utf8_str().data()); LuaScriptReader script_reader(fname.GetFullPath()); - if (lua_load(L, script_reader.reader_func, &script_reader, fname.GetFullName().mb_str(wxConvUTF8))) { - lua_pushfstring(L, "Error loading Lua include \"%s\":\n\n%s", fname.GetFullPath().mb_str(wxConvUTF8).data(), lua_tostring(L, -1)); - lua_error(L); - return 0; - } + if (lua_load(L, script_reader.reader_func, &script_reader, fname.GetFullName().utf8_str())) + return luaL_error(L, "Error loading Lua include \"%s\":\n\n%s", fname.GetFullPath().utf8_str().data(), lua_tostring(L, -1)); + int pretop = lua_gettop(L) - 1; // don't count the function value itself lua_call(L, 0, LUA_MULTRET); return lua_gettop(L) - pretop; } - - /// @brief DOCME - /// @param L - /// @return - /// int LuaScript::LuaFrameFromMs(lua_State *L) { - int ms = (int)lua_tonumber(L, -1); + int ms = lua_tointeger(L, -1); lua_pop(L, 1); - if (VideoContext::Get()->TimecodesLoaded()) { + if (VideoContext::Get()->TimecodesLoaded()) lua_pushnumber(L, VideoContext::Get()->FrameAtTime(ms, agi::vfr::START)); - return 1; - } else { + else lua_pushnil(L); - return 1; - } - } + return 1; + } - /// @brief DOCME - /// @param L - /// @return - /// int LuaScript::LuaMsFromFrame(lua_State *L) { - int frame = (int)lua_tonumber(L, -1); + int frame = lua_tointeger(L, -1); lua_pop(L, 1); - if (VideoContext::Get()->TimecodesLoaded()) { + if (VideoContext::Get()->TimecodesLoaded()) lua_pushnumber(L, VideoContext::Get()->TimeAtFrame(frame, agi::vfr::START)); - return 1; - } else { + else lua_pushnil(L); - return 1; - } - } + return 1; + } - /// @brief DOCME - /// @param L - /// @return - /// int LuaScript::LuaVideoSize(lua_State *L) { VideoContext *ctx = VideoContext::Get(); @@ -529,14 +465,14 @@ namespace Automation4 { LuaProgressSink lps(L, ps, can_open_config); if (lua_pcall(L, nargs, nresults, 0)) { - // if the call failed, log the error here + // if the call failed, log the error here ps->Log("\n\nLua reported a runtime error:\n"); ps->Log(lua_tostring(L, -1)); - lua_pop(L, 1); - } + lua_pop(L, 1); + } lua_gc(L, LUA_GCCOLLECT, 0); - } + } // LuaThreadedCall @@ -577,10 +513,7 @@ namespace Automation4 { lua_pop(L, 1); // add the Feature object - s->features.push_back(this); - - // get the index+1 it was pushed into - myid = (int)s->features.size()-1; + myid = s->RegisterFeature(this); // create table with the functions // get features table diff --git a/aegisub/src/auto4_lua.h b/aegisub/src/auto4_lua.h index c78c72ebd..7aaba7437 100644 --- a/aegisub/src/auto4_lua.h +++ b/aegisub/src/auto4_lua.h @@ -271,46 +271,8 @@ namespace Automation4 { void ThrowError(); }; - - - /// DOCME - /// @class LuaScript - /// @brief DOCME - /// - /// DOCME - class LuaScript : public Script { - friend class LuaFeature; - - private: - - /// DOCME - lua_State *L; - - void Create(); // load script and create internal structures etc. - void Destroy(); // destroy internal structures, unreg features and delete environment - - static LuaScript* GetScriptObject(lua_State *L); - - static int LuaTextExtents(lua_State *L); - static int LuaInclude(lua_State *L); - static int LuaModuleLoader(lua_State *L); - static int LuaFrameFromMs(lua_State *L); - static int LuaMsFromFrame(lua_State *L); - static int LuaVideoSize(lua_State *L); - - public: - LuaScript(const wxString &filename); - virtual ~LuaScript(); - - virtual void Reload(); - }; - - - void LuaThreadedCall(lua_State *L, int nargs, int nresults, wxString const& title, wxWindow *parent, bool can_open_config); - - /// DOCME /// @class LuaFeatureMacro /// @brief DOCME @@ -334,8 +296,6 @@ namespace Automation4 { virtual void Process(AssFile *subs, std::vector &selected, int active, wxWindow * const progress_parent); }; - - /// DOCME /// @class LuaFeatureFilter /// @brief DOCME @@ -367,4 +327,45 @@ namespace Automation4 { void ProcessSubs(AssFile *subs, wxWindow *export_dialog); }; + class LuaScript : public Script { + lua_State *L; + + wxString name; + wxString description; + wxString author; + wxString version; + + std::vector features; + + /// load script and create internal structures etc. + void Create(); + /// destroy internal structures, unreg features and delete environment + void Destroy(); + + static int LuaTextExtents(lua_State *L); + static int LuaInclude(lua_State *L); + static int LuaModuleLoader(lua_State *L); + static int LuaFrameFromMs(lua_State *L); + static int LuaMsFromFrame(lua_State *L); + static int LuaVideoSize(lua_State *L); + + public: + LuaScript(const wxString &filename); + ~LuaScript(); + + static LuaScript* GetScriptObject(lua_State *L); + + int RegisterFeature(Feature *feature); + + // Script implementation + void Reload(); + + wxString GetName() const { return name; } + wxString GetDescription() const { return description; } + wxString GetAuthor() const { return author; } + wxString GetVersion() const { return version; } + bool GetLoadedState() const { return L != 0; } + + std::vector GetFeatures() const { return features; } + }; };