2007-01-15 23:19:50 +01:00
|
|
|
// Copyright (c) 2006, 2007, Niels Martin Hansen
|
2006-12-28 22:18:35 +01:00
|
|
|
// All rights reserved.
|
|
|
|
//
|
|
|
|
// Redistribution and use in source and binary forms, with or without
|
|
|
|
// modification, are permitted provided that the following conditions are met:
|
|
|
|
//
|
|
|
|
// * Redistributions of source code must retain the above copyright notice,
|
|
|
|
// this list of conditions and the following disclaimer.
|
|
|
|
// * Redistributions in binary form must reproduce the above copyright notice,
|
|
|
|
// this list of conditions and the following disclaimer in the documentation
|
|
|
|
// and/or other materials provided with the distribution.
|
|
|
|
// * Neither the name of the Aegisub Group nor the names of its contributors
|
|
|
|
// may be used to endorse or promote products derived from this software
|
|
|
|
// without specific prior written permission.
|
|
|
|
//
|
|
|
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
|
|
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
|
|
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
|
|
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
|
|
|
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
|
|
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
|
|
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
|
|
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
|
|
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
|
|
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
|
|
// POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
//
|
2009-07-29 07:43:02 +02:00
|
|
|
// Aegisub Project http://www.aegisub.org/
|
|
|
|
|
|
|
|
/// @file auto4_lua.cpp
|
|
|
|
/// @brief Lua 5.1-based scripting engine
|
|
|
|
/// @ingroup scripting
|
|
|
|
///
|
2006-12-28 22:18:35 +01:00
|
|
|
|
2011-09-28 21:48:37 +02:00
|
|
|
#include "auto4_lua.h"
|
|
|
|
|
2006-12-28 22:18:35 +01:00
|
|
|
#include "ass_dialogue.h"
|
|
|
|
#include "ass_file.h"
|
2014-05-23 00:40:16 +02:00
|
|
|
#include "ass_info.h"
|
2009-09-10 15:06:40 +02:00
|
|
|
#include "ass_style.h"
|
2014-05-22 01:23:28 +02:00
|
|
|
#include "async_video_provider.h"
|
2009-09-10 15:06:40 +02:00
|
|
|
#include "auto4_lua_factory.h"
|
2017-05-28 17:43:17 +02:00
|
|
|
#include "audio_controller.h"
|
|
|
|
#include "audio_timing.h"
|
2014-03-27 03:24:37 +01:00
|
|
|
#include "command/command.h"
|
2012-01-18 21:08:42 +01:00
|
|
|
#include "compat.h"
|
2018-03-25 13:50:05 +02:00
|
|
|
#include "frame_main.h"
|
2011-09-28 21:48:47 +02:00
|
|
|
#include "include/aegisub/context.h"
|
2013-01-30 04:35:37 +01:00
|
|
|
#include "options.h"
|
2014-05-22 01:23:28 +02:00
|
|
|
#include "project.h"
|
2011-09-28 21:48:47 +02:00
|
|
|
#include "selection_controller.h"
|
2013-01-26 02:57:46 +01:00
|
|
|
#include "subs_controller.h"
|
2014-05-22 01:23:28 +02:00
|
|
|
#include "video_controller.h"
|
2011-09-28 21:48:47 +02:00
|
|
|
#include "utils.h"
|
2008-03-05 05:10:20 +01:00
|
|
|
|
2018-03-25 13:50:05 +02:00
|
|
|
#include <libaegisub/dispatch.h>
|
2014-05-29 00:19:05 +02:00
|
|
|
#include <libaegisub/format.h>
|
2014-07-28 23:19:57 +02:00
|
|
|
#include <libaegisub/lua/ffi.h>
|
2014-03-27 03:24:37 +01:00
|
|
|
#include <libaegisub/lua/modules.h>
|
|
|
|
#include <libaegisub/lua/script_reader.h>
|
|
|
|
#include <libaegisub/lua/utils.h>
|
2014-04-23 22:53:24 +02:00
|
|
|
#include <libaegisub/make_unique.h>
|
2014-05-29 00:19:05 +02:00
|
|
|
#include <libaegisub/path.h>
|
2013-01-04 16:01:50 +01:00
|
|
|
|
|
|
|
#include <algorithm>
|
2014-04-23 21:50:26 +02:00
|
|
|
#include <boost/algorithm/string/case_conv.hpp>
|
2013-01-04 16:01:50 +01:00
|
|
|
#include <boost/algorithm/string/classification.hpp>
|
2014-04-29 18:33:22 +02:00
|
|
|
#include <boost/algorithm/string/predicate.hpp>
|
2014-04-28 22:36:56 +02:00
|
|
|
#include <boost/scope_exit.hpp>
|
2013-01-04 16:01:50 +01:00
|
|
|
#include <cassert>
|
2013-09-22 00:59:28 +02:00
|
|
|
#include <mutex>
|
2013-01-04 16:01:50 +01:00
|
|
|
#include <wx/clipbrd.h>
|
|
|
|
#include <wx/log.h>
|
|
|
|
#include <wx/msgdlg.h>
|
|
|
|
|
2014-03-27 03:24:37 +01:00
|
|
|
using namespace agi::lua;
|
|
|
|
using namespace Automation4;
|
2014-04-25 18:27:27 +02:00
|
|
|
|
2011-09-28 21:48:37 +02:00
|
|
|
namespace {
|
2014-03-27 03:24:37 +01:00
|
|
|
wxString get_wxstring(lua_State *L, int idx)
|
|
|
|
{
|
|
|
|
return wxString::FromUTF8(lua_tostring(L, idx));
|
|
|
|
}
|
|
|
|
|
|
|
|
wxString check_wxstring(lua_State *L, int idx)
|
|
|
|
{
|
2014-04-27 16:20:11 +02:00
|
|
|
return to_wx(check_string(L, idx));
|
2014-03-27 03:24:37 +01:00
|
|
|
}
|
|
|
|
|
2011-09-28 21:49:47 +02:00
|
|
|
void set_context(lua_State *L, const agi::Context *c)
|
|
|
|
{
|
|
|
|
// Explicit cast is needed to discard the const
|
2013-01-04 16:01:50 +01:00
|
|
|
push_value(L, (void *)c);
|
2011-09-28 21:49:47 +02:00
|
|
|
lua_setfield(L, LUA_REGISTRYINDEX, "project_context");
|
|
|
|
}
|
|
|
|
|
|
|
|
const agi::Context *get_context(lua_State *L)
|
|
|
|
{
|
|
|
|
lua_getfield(L, LUA_REGISTRYINDEX, "project_context");
|
2012-01-09 21:31:19 +01:00
|
|
|
if (!lua_islightuserdata(L, -1)) {
|
|
|
|
lua_pop(L, 1);
|
2013-11-21 18:13:36 +01:00
|
|
|
return nullptr;
|
2012-01-09 21:31:19 +01:00
|
|
|
}
|
2011-09-28 21:49:47 +02:00
|
|
|
const agi::Context * c = static_cast<const agi::Context *>(lua_touserdata(L, -1));
|
|
|
|
lua_pop(L, 1);
|
|
|
|
return c;
|
|
|
|
}
|
2012-02-15 22:23:42 +01:00
|
|
|
|
2012-02-22 00:32:58 +01:00
|
|
|
int get_file_name(lua_State *L)
|
|
|
|
{
|
|
|
|
const agi::Context *c = get_context(L);
|
2013-01-26 02:57:46 +01:00
|
|
|
if (c && !c->subsController->Filename().empty())
|
|
|
|
push_value(L, c->subsController->Filename().filename());
|
2012-02-22 00:32:58 +01:00
|
|
|
else
|
|
|
|
lua_pushnil(L);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2012-03-13 00:35:29 +01:00
|
|
|
int get_translation(lua_State *L)
|
|
|
|
{
|
|
|
|
wxString str(check_wxstring(L, 1));
|
2014-03-27 03:24:37 +01:00
|
|
|
push_value(L, _(str).utf8_str());
|
2012-03-13 00:35:29 +01:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2014-07-28 23:19:57 +02:00
|
|
|
const char *clipboard_get()
|
2012-02-22 21:47:34 +01:00
|
|
|
{
|
2013-01-04 16:01:50 +01:00
|
|
|
std::string data = GetClipboard();
|
|
|
|
if (data.empty())
|
2014-07-28 23:19:57 +02:00
|
|
|
return nullptr;
|
2014-12-28 05:03:39 +01:00
|
|
|
return strndup(data);
|
2012-02-22 21:47:34 +01:00
|
|
|
}
|
|
|
|
|
2014-07-28 23:19:57 +02:00
|
|
|
bool clipboard_set(const char *str)
|
2012-02-22 21:47:34 +01:00
|
|
|
{
|
|
|
|
bool succeeded = false;
|
|
|
|
|
|
|
|
#if wxUSE_OLE
|
|
|
|
// OLE needs to be initialized on each thread that wants to write to
|
|
|
|
// the clipboard, which wx does not handle automatically
|
|
|
|
wxClipboard cb;
|
|
|
|
#else
|
2013-06-13 00:48:35 +02:00
|
|
|
wxClipboard &cb = *wxTheClipboard;
|
2012-02-22 21:47:34 +01:00
|
|
|
#endif
|
2013-06-13 00:48:35 +02:00
|
|
|
if (cb.Open()) {
|
2014-07-28 23:19:57 +02:00
|
|
|
succeeded = cb.SetData(new wxTextDataObject(wxString::FromUTF8(str)));
|
2013-06-13 00:48:35 +02:00
|
|
|
cb.Close();
|
|
|
|
cb.Flush();
|
2012-02-22 21:47:34 +01:00
|
|
|
}
|
|
|
|
|
2014-07-28 23:19:57 +02:00
|
|
|
return succeeded;
|
2012-02-22 21:47:34 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
int clipboard_init(lua_State *L)
|
|
|
|
{
|
2014-07-28 23:19:57 +02:00
|
|
|
agi::lua::register_lib_table(L, {}, "get", clipboard_get, "set", clipboard_set);
|
2012-02-22 21:47:34 +01:00
|
|
|
return 1;
|
|
|
|
}
|
2013-05-02 03:26:05 +02:00
|
|
|
|
2013-04-30 15:16:59 +02:00
|
|
|
int frame_from_ms(lua_State *L)
|
|
|
|
{
|
|
|
|
const agi::Context *c = get_context(L);
|
|
|
|
int ms = lua_tointeger(L, -1);
|
|
|
|
lua_pop(L, 1);
|
2014-05-22 01:23:28 +02:00
|
|
|
if (c && c->project->Timecodes().IsLoaded())
|
2013-04-30 15:16:59 +02:00
|
|
|
push_value(L, c->videoController->FrameAtTime(ms, agi::vfr::START));
|
|
|
|
else
|
|
|
|
lua_pushnil(L);
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
int ms_from_frame(lua_State *L)
|
|
|
|
{
|
|
|
|
const agi::Context *c = get_context(L);
|
|
|
|
int frame = lua_tointeger(L, -1);
|
|
|
|
lua_pop(L, 1);
|
2014-05-22 01:23:28 +02:00
|
|
|
if (c && c->project->Timecodes().IsLoaded())
|
2013-04-30 15:16:59 +02:00
|
|
|
push_value(L, c->videoController->TimeAtFrame(frame, agi::vfr::START));
|
|
|
|
else
|
|
|
|
lua_pushnil(L);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
int video_size(lua_State *L)
|
|
|
|
{
|
|
|
|
const agi::Context *c = get_context(L);
|
2014-05-22 01:23:28 +02:00
|
|
|
if (c && c->project->VideoProvider()) {
|
|
|
|
auto provider = c->project->VideoProvider();
|
|
|
|
push_value(L, provider->GetWidth());
|
|
|
|
push_value(L, provider->GetHeight());
|
2013-04-30 15:16:59 +02:00
|
|
|
push_value(L, c->videoController->GetAspectRatioValue());
|
|
|
|
push_value(L, (int)c->videoController->GetAspectRatioType());
|
|
|
|
return 4;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
lua_pushnil(L);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int get_keyframes(lua_State *L)
|
|
|
|
{
|
2015-08-22 03:17:48 +02:00
|
|
|
if (const agi::Context *c = get_context(L))
|
2014-05-22 01:23:28 +02:00
|
|
|
push_value(L, c->project->Keyframes());
|
2014-04-27 15:47:00 +02:00
|
|
|
else
|
2013-04-30 15:16:59 +02:00
|
|
|
lua_pushnil(L);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
int decode_path(lua_State *L)
|
|
|
|
{
|
2014-04-27 16:20:11 +02:00
|
|
|
std::string path = check_string(L, 1);
|
2013-04-30 15:16:59 +02:00
|
|
|
lua_pop(L, 1);
|
2015-08-22 03:17:48 +02:00
|
|
|
if (const agi::Context *c = get_context(L))
|
|
|
|
push_value(L, c->path->Decode(path));
|
|
|
|
else
|
|
|
|
push_value(L, config::path->Decode(path));
|
2013-04-30 15:16:59 +02:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
int cancel_script(lua_State *L)
|
|
|
|
{
|
|
|
|
lua_pushnil(L);
|
2014-04-27 16:20:11 +02:00
|
|
|
throw error_tag();
|
2013-04-30 15:16:59 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
int lua_text_textents(lua_State *L)
|
|
|
|
{
|
2014-04-27 16:20:11 +02:00
|
|
|
argcheck(L, !!lua_istable(L, 1), 1, "");
|
|
|
|
argcheck(L, !!lua_isstring(L, 2), 2, "");
|
2013-04-30 15:16:59 +02:00
|
|
|
|
2014-04-23 21:50:26 +02:00
|
|
|
// have to check that it looks like a style table before actually converting
|
|
|
|
// if it's a dialogue table then an active AssFile object is required
|
|
|
|
{
|
|
|
|
lua_getfield(L, 1, "class");
|
|
|
|
std::string actual_class{lua_tostring(L, -1)};
|
|
|
|
boost::to_lower(actual_class);
|
|
|
|
if (actual_class != "style")
|
2014-04-27 16:20:11 +02:00
|
|
|
return error(L, "Not a style entry");
|
2014-04-23 21:50:26 +02:00
|
|
|
lua_pop(L, 1);
|
|
|
|
}
|
|
|
|
|
2013-04-30 15:16:59 +02:00
|
|
|
lua_pushvalue(L, 1);
|
2013-06-08 06:19:40 +02:00
|
|
|
std::unique_ptr<AssEntry> et(Automation4::LuaAssFile::LuaToAssEntry(L));
|
2013-04-30 15:16:59 +02:00
|
|
|
lua_pop(L, 1);
|
2014-12-26 17:51:14 +01:00
|
|
|
if (typeid(*et) != typeid(AssStyle))
|
2014-04-27 16:20:11 +02:00
|
|
|
return error(L, "Not a style entry");
|
2013-04-30 15:16:59 +02:00
|
|
|
|
|
|
|
double width, height, descent, extlead;
|
2014-12-26 17:51:14 +01:00
|
|
|
if (!Automation4::CalculateTextExtents(static_cast<AssStyle*>(et.get()),
|
|
|
|
check_string(L, 2), width, height, descent, extlead))
|
2014-04-27 16:20:11 +02:00
|
|
|
return error(L, "Some internal error occurred calculating text_extents");
|
2013-04-30 15:16:59 +02:00
|
|
|
|
|
|
|
push_value(L, width);
|
|
|
|
push_value(L, height);
|
|
|
|
push_value(L, descent);
|
|
|
|
push_value(L, extlead);
|
|
|
|
return 4;
|
|
|
|
}
|
Note: This was done using a script! it's far from perfect but 95% of the work has been done already formatting-wise.
Document all functions, class, struct, union, enum, macro, variable, typedefs. This isn't the actual document in itself but empty documentation using any old documentation if it was there.
This was done using exuberant ctags to get tag info, then a TCL script to parse/remove old comments and convert them into Doxygen-style.
Some notes:
* Anything labeled 'DOCME' needs to be documented, @param and @return have been left blank as it would be annoying to delete the 'DOCME' from every one of those.
* Some multiline comments may have been munged into single line comments
* Leave the /// comments above global variables with a space, if they're harder to read then we'll be less likey to use them.
* Enum comments can go after the enumeration itself '[value] /// comment'
* include/aegisub/*.h haven't been converted yet, this will be done in a later commit
* Some documentation blocks are in the wrong place, in the .h when it should be in the .cpp, or vice versa.
See http://devel.aegisub.org/wiki/Doxygen for some details on Doxygen and a 'style guide'.
Originally committed to SVN as r3312.
2009-07-30 00:59:22 +02:00
|
|
|
|
2017-05-28 17:43:17 +02:00
|
|
|
int lua_get_audio_selection(lua_State *L)
|
|
|
|
{
|
|
|
|
const agi::Context *c = get_context(L);
|
|
|
|
if (!c || !c->audioController || !c->audioController->GetTimingController()) {
|
|
|
|
lua_pushnil(L);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
const TimeRange range = c->audioController->GetTimingController()->GetActiveLineRange();
|
|
|
|
push_value(L, range.begin());
|
|
|
|
push_value(L, range.end());
|
|
|
|
return 2;
|
|
|
|
}
|
|
|
|
|
2018-03-25 13:50:05 +02:00
|
|
|
int lua_set_status_text(lua_State *L)
|
|
|
|
{
|
|
|
|
const agi::Context *c = get_context(L);
|
|
|
|
if (!c || !c->frame) {
|
|
|
|
lua_pushnil(L);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
std::string text = check_string(L, 1);
|
|
|
|
lua_pop(L, 1);
|
|
|
|
agi::dispatch::Main().Async([=] { c->frame->StatusTimeout(to_wx(text)); });
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-05-22 16:08:22 +02:00
|
|
|
int project_properties(lua_State *L)
|
|
|
|
{
|
|
|
|
const agi::Context *c = get_context(L);
|
|
|
|
if (!c)
|
|
|
|
lua_pushnil(L);
|
|
|
|
else {
|
|
|
|
lua_createtable(L, 0, 14);
|
|
|
|
#define PUSH_FIELD(name) set_field(L, #name, c->ass->Properties.name)
|
|
|
|
PUSH_FIELD(automation_scripts);
|
|
|
|
PUSH_FIELD(export_filters);
|
|
|
|
PUSH_FIELD(export_encoding);
|
|
|
|
PUSH_FIELD(style_storage);
|
|
|
|
PUSH_FIELD(video_zoom);
|
|
|
|
PUSH_FIELD(ar_value);
|
|
|
|
PUSH_FIELD(scroll_position);
|
|
|
|
PUSH_FIELD(active_row);
|
|
|
|
PUSH_FIELD(ar_mode);
|
|
|
|
PUSH_FIELD(video_position);
|
|
|
|
#undef PUSH_FIELD
|
2015-08-22 03:17:48 +02:00
|
|
|
set_field(L, "audio_file", c->path->MakeAbsolute(c->ass->Properties.audio_file, "?script"));
|
|
|
|
set_field(L, "video_file", c->path->MakeAbsolute(c->ass->Properties.video_file, "?script"));
|
|
|
|
set_field(L, "timecodes_file", c->path->MakeAbsolute(c->ass->Properties.timecodes_file, "?script"));
|
|
|
|
set_field(L, "keyframes_file", c->path->MakeAbsolute(c->ass->Properties.keyframes_file, "?script"));
|
2014-05-22 16:08:22 +02:00
|
|
|
}
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2014-04-25 18:27:27 +02:00
|
|
|
class LuaFeature {
|
2014-04-26 00:40:43 +02:00
|
|
|
int myid = 0;
|
2014-04-25 18:27:27 +02:00
|
|
|
protected:
|
|
|
|
lua_State *L;
|
2013-04-30 05:37:47 +02:00
|
|
|
|
2014-04-25 18:27:27 +02:00
|
|
|
void RegisterFeature();
|
|
|
|
void UnregisterFeature();
|
2014-04-24 22:57:23 +02:00
|
|
|
|
2014-04-25 18:27:27 +02:00
|
|
|
void GetFeatureFunction(const char *function) const;
|
|
|
|
|
2014-04-26 00:40:43 +02:00
|
|
|
LuaFeature(lua_State *L) : L(L) { }
|
2014-04-25 18:27:27 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
/// Run a lua function on a background thread
|
|
|
|
/// @param L Lua state
|
|
|
|
/// @param nargs Number of arguments the function takes
|
|
|
|
/// @param nresults Number of values the function returns
|
|
|
|
/// @param title Title to use for the progress dialog
|
|
|
|
/// @param parent Parent window for the progress dialog
|
|
|
|
/// @param can_open_config Can the function open its own dialogs?
|
|
|
|
/// @throws agi::UserCancelException if the function fails to run to completion (either due to cancelling or errors)
|
|
|
|
void LuaThreadedCall(lua_State *L, int nargs, int nresults, std::string const& title, wxWindow *parent, bool can_open_config);
|
|
|
|
|
|
|
|
class LuaCommand final : public cmd::Command, private LuaFeature {
|
|
|
|
std::string cmd_name;
|
|
|
|
wxString display;
|
|
|
|
wxString help;
|
|
|
|
int cmd_type;
|
|
|
|
|
|
|
|
public:
|
|
|
|
LuaCommand(lua_State *L);
|
|
|
|
~LuaCommand();
|
2013-04-10 05:46:59 +02:00
|
|
|
|
2014-04-25 18:27:27 +02:00
|
|
|
const char* name() const override { return cmd_name.c_str(); }
|
|
|
|
wxString StrMenu(const agi::Context *) const override { return display; }
|
|
|
|
wxString StrDisplay(const agi::Context *) const override { return display; }
|
|
|
|
wxString StrHelp() const override { return help; }
|
|
|
|
|
|
|
|
int Type() const override { return cmd_type; }
|
|
|
|
|
|
|
|
void operator()(agi::Context *c) override;
|
|
|
|
bool Validate(const agi::Context *c) override;
|
|
|
|
virtual bool IsActive(const agi::Context *c) override;
|
|
|
|
|
|
|
|
static int LuaRegister(lua_State *L);
|
|
|
|
};
|
|
|
|
|
|
|
|
class LuaExportFilter final : public ExportFilter, private LuaFeature {
|
|
|
|
bool has_config;
|
|
|
|
LuaDialog *config_dialog;
|
|
|
|
|
|
|
|
protected:
|
|
|
|
std::unique_ptr<ScriptDialog> GenerateConfigDialog(wxWindow *parent, agi::Context *c) override;
|
|
|
|
|
|
|
|
public:
|
|
|
|
LuaExportFilter(lua_State *L);
|
|
|
|
static int LuaRegister(lua_State *L);
|
|
|
|
|
|
|
|
void ProcessSubs(AssFile *subs, wxWindow *export_dialog) override;
|
|
|
|
};
|
2014-03-13 02:39:07 +01:00
|
|
|
class LuaScript final : public Script {
|
2014-04-26 00:40:43 +02:00
|
|
|
lua_State *L = nullptr;
|
2013-04-30 15:16:59 +02:00
|
|
|
|
|
|
|
std::string name;
|
|
|
|
std::string description;
|
|
|
|
std::string author;
|
|
|
|
std::string version;
|
|
|
|
|
|
|
|
std::vector<cmd::Command*> macros;
|
2013-10-27 15:15:39 +01:00
|
|
|
std::vector<std::unique_ptr<ExportFilter>> filters;
|
2013-04-30 15:16:59 +02:00
|
|
|
|
|
|
|
/// load script and create internal structures etc.
|
|
|
|
void Create();
|
|
|
|
/// destroy internal structures, unreg features and delete environment
|
|
|
|
void Destroy();
|
|
|
|
|
|
|
|
static int LuaInclude(lua_State *L);
|
|
|
|
|
|
|
|
public:
|
|
|
|
LuaScript(agi::fs::path const& filename);
|
|
|
|
~LuaScript() { Destroy(); }
|
|
|
|
|
|
|
|
void RegisterCommand(LuaCommand *command);
|
|
|
|
void UnregisterCommand(LuaCommand *command);
|
|
|
|
void RegisterFilter(LuaExportFilter *filter);
|
|
|
|
|
|
|
|
static LuaScript* GetScriptObject(lua_State *L);
|
|
|
|
|
|
|
|
// Script implementation
|
2013-11-21 18:13:36 +01:00
|
|
|
void Reload() override { Create(); }
|
2013-04-30 15:16:59 +02:00
|
|
|
|
2013-11-21 18:13:36 +01:00
|
|
|
std::string GetName() const override { return name; }
|
|
|
|
std::string GetDescription() const override { return description; }
|
|
|
|
std::string GetAuthor() const override { return author; }
|
|
|
|
std::string GetVersion() const override { return version; }
|
|
|
|
bool GetLoadedState() const override { return L != nullptr; }
|
2013-04-30 15:16:59 +02:00
|
|
|
|
2013-11-21 18:13:36 +01:00
|
|
|
std::vector<cmd::Command*> GetMacros() const override { return macros; }
|
|
|
|
std::vector<ExportFilter*> GetFilters() const override;
|
2013-04-30 15:16:59 +02:00
|
|
|
};
|
|
|
|
|
2013-01-04 16:01:50 +01:00
|
|
|
LuaScript::LuaScript(agi::fs::path const& filename)
|
2011-09-28 21:48:37 +02:00
|
|
|
: Script(filename)
|
2006-12-28 22:18:35 +01:00
|
|
|
{
|
2007-02-14 01:43:01 +01:00
|
|
|
Create();
|
2006-12-28 22:18:35 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void LuaScript::Create()
|
|
|
|
{
|
|
|
|
Destroy();
|
|
|
|
|
2014-04-28 22:36:56 +02:00
|
|
|
name = GetPrettyFilename().string();
|
2006-12-28 22:18:35 +01:00
|
|
|
|
2014-04-28 22:36:56 +02:00
|
|
|
// create lua environment
|
2016-02-28 03:59:03 +01:00
|
|
|
L = luaL_newstate();
|
2015-11-23 03:27:23 +01:00
|
|
|
if (!L) {
|
|
|
|
description = "Could not initialize Lua state";
|
|
|
|
return;
|
|
|
|
}
|
2011-09-28 21:52:20 +02:00
|
|
|
|
2014-04-28 22:36:56 +02:00
|
|
|
bool loaded = false;
|
|
|
|
BOOST_SCOPE_EXIT_ALL(&) { if (!loaded) Destroy(); };
|
|
|
|
LuaStackcheck stackcheck(L);
|
2011-09-28 21:48:37 +02:00
|
|
|
|
2014-04-28 22:36:56 +02:00
|
|
|
// register standard libs
|
|
|
|
preload_modules(L);
|
|
|
|
stackcheck.check_stack(0);
|
|
|
|
|
|
|
|
// dofile and loadfile are replaced with include
|
|
|
|
lua_pushnil(L);
|
|
|
|
lua_setglobal(L, "dofile");
|
|
|
|
lua_pushnil(L);
|
|
|
|
lua_setglobal(L, "loadfile");
|
|
|
|
push_value(L, exception_wrapper<LuaInclude>);
|
|
|
|
lua_setglobal(L, "include");
|
|
|
|
|
|
|
|
// Replace the default lua module loader with our unicode compatible
|
|
|
|
// one and set the module search path
|
2014-04-28 23:19:16 +02:00
|
|
|
if (!Install(L, include_path)) {
|
|
|
|
description = get_string_or_default(L, 1);
|
|
|
|
lua_pop(L, 1);
|
|
|
|
return;
|
|
|
|
}
|
2014-04-28 22:36:56 +02:00
|
|
|
stackcheck.check_stack(0);
|
|
|
|
|
|
|
|
// prepare stuff in the registry
|
|
|
|
|
|
|
|
// store the script's filename
|
|
|
|
push_value(L, GetFilename().stem());
|
|
|
|
lua_setfield(L, LUA_REGISTRYINDEX, "filename");
|
|
|
|
stackcheck.check_stack(0);
|
|
|
|
|
|
|
|
// reference to the script object
|
|
|
|
push_value(L, this);
|
|
|
|
lua_setfield(L, LUA_REGISTRYINDEX, "aegisub");
|
|
|
|
stackcheck.check_stack(0);
|
|
|
|
|
|
|
|
// make "aegisub" table
|
|
|
|
lua_pushstring(L, "aegisub");
|
2014-05-22 16:08:22 +02:00
|
|
|
lua_createtable(L, 0, 13);
|
2014-04-28 22:36:56 +02:00
|
|
|
|
|
|
|
set_field<LuaCommand::LuaRegister>(L, "register_macro");
|
|
|
|
set_field<LuaExportFilter::LuaRegister>(L, "register_filter");
|
|
|
|
set_field<lua_text_textents>(L, "text_extents");
|
|
|
|
set_field<frame_from_ms>(L, "frame_from_ms");
|
|
|
|
set_field<ms_from_frame>(L, "ms_from_frame");
|
|
|
|
set_field<video_size>(L, "video_size");
|
|
|
|
set_field<get_keyframes>(L, "keyframes");
|
|
|
|
set_field<decode_path>(L, "decode_path");
|
|
|
|
set_field<cancel_script>(L, "cancel");
|
|
|
|
set_field(L, "lua_automation_version", 4);
|
|
|
|
set_field<clipboard_init>(L, "__init_clipboard");
|
|
|
|
set_field<get_file_name>(L, "file_name");
|
|
|
|
set_field<get_translation>(L, "gettext");
|
2014-05-22 16:08:22 +02:00
|
|
|
set_field<project_properties>(L, "project_properties");
|
2017-05-28 17:43:17 +02:00
|
|
|
set_field<lua_get_audio_selection>(L, "get_audio_selection");
|
2018-03-25 13:50:05 +02:00
|
|
|
set_field<lua_set_status_text>(L, "set_status_text");
|
2014-04-28 22:36:56 +02:00
|
|
|
|
|
|
|
// store aegisub table to globals
|
|
|
|
lua_settable(L, LUA_GLOBALSINDEX);
|
|
|
|
stackcheck.check_stack(0);
|
|
|
|
|
|
|
|
// load user script
|
|
|
|
if (!LoadFile(L, GetFilename())) {
|
|
|
|
description = get_string_or_default(L, 1);
|
|
|
|
lua_pop(L, 1);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
stackcheck.check_stack(1);
|
|
|
|
|
2015-02-15 17:10:26 +01:00
|
|
|
// Insert our error handler under the user's script
|
|
|
|
lua_pushcclosure(L, add_stack_trace, 0);
|
|
|
|
lua_insert(L, -2);
|
|
|
|
|
2014-04-28 22:36:56 +02:00
|
|
|
// and execute it
|
|
|
|
// this is where features are registered
|
2015-02-15 17:10:26 +01:00
|
|
|
if (lua_pcall(L, 0, 0, -2)) {
|
2014-04-28 22:36:56 +02:00
|
|
|
// error occurred, assumed to be on top of Lua stack
|
2014-05-29 00:19:05 +02:00
|
|
|
description = agi::format("Error initialising Lua script \"%s\":\n\n%s", GetPrettyFilename().string(), get_string_or_default(L, -1));
|
2015-02-15 17:10:26 +01:00
|
|
|
lua_pop(L, 2); // error + error handler
|
2014-04-28 22:36:56 +02:00
|
|
|
return;
|
|
|
|
}
|
2015-02-15 17:10:26 +01:00
|
|
|
lua_pop(L, 1); // error handler
|
2014-04-28 22:36:56 +02:00
|
|
|
stackcheck.check_stack(0);
|
2006-12-28 22:18:35 +01:00
|
|
|
|
2014-04-28 22:36:56 +02:00
|
|
|
lua_getglobal(L, "version");
|
|
|
|
if (lua_isnumber(L, -1) && lua_tointeger(L, -1) == 3) {
|
|
|
|
lua_pop(L, 1); // just to avoid tripping the stackcheck in debug
|
|
|
|
description = "Attempted to load an Automation 3 script as an Automation 4 Lua script. Automation 3 is no longer supported.";
|
|
|
|
return;
|
2006-12-28 22:18:35 +01:00
|
|
|
}
|
2014-04-28 22:36:56 +02:00
|
|
|
|
|
|
|
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())
|
2013-01-04 16:01:50 +01:00
|
|
|
name = GetPrettyFilename().string();
|
2014-04-28 22:36:56 +02:00
|
|
|
|
|
|
|
lua_pop(L, 1);
|
|
|
|
// if we got this far, the script should be ready
|
|
|
|
loaded = true;
|
2011-09-28 21:48:37 +02:00
|
|
|
}
|
Note: This was done using a script! it's far from perfect but 95% of the work has been done already formatting-wise.
Document all functions, class, struct, union, enum, macro, variable, typedefs. This isn't the actual document in itself but empty documentation using any old documentation if it was there.
This was done using exuberant ctags to get tag info, then a TCL script to parse/remove old comments and convert them into Doxygen-style.
Some notes:
* Anything labeled 'DOCME' needs to be documented, @param and @return have been left blank as it would be annoying to delete the 'DOCME' from every one of those.
* Some multiline comments may have been munged into single line comments
* Leave the /// comments above global variables with a space, if they're harder to read then we'll be less likey to use them.
* Enum comments can go after the enumeration itself '[value] /// comment'
* include/aegisub/*.h haven't been converted yet, this will be done in a later commit
* Some documentation blocks are in the wrong place, in the .h when it should be in the .cpp, or vice versa.
See http://devel.aegisub.org/wiki/Doxygen for some details on Doxygen and a 'style guide'.
Originally committed to SVN as r3312.
2009-07-30 00:59:22 +02:00
|
|
|
|
2006-12-28 22:18:35 +01:00
|
|
|
void LuaScript::Destroy()
|
|
|
|
{
|
|
|
|
// Assume the script object is clean if there's no Lua state
|
|
|
|
if (!L) return;
|
|
|
|
|
2011-09-28 21:48:47 +02:00
|
|
|
// 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());
|
|
|
|
|
2013-10-27 15:15:39 +01:00
|
|
|
filters.clear();
|
2006-12-28 22:18:35 +01:00
|
|
|
|
|
|
|
lua_close(L);
|
2013-04-30 15:16:59 +02:00
|
|
|
L = nullptr;
|
2006-12-28 22:18:35 +01:00
|
|
|
}
|
|
|
|
|
2013-10-27 15:15:39 +01:00
|
|
|
std::vector<ExportFilter*> LuaScript::GetFilters() const
|
|
|
|
{
|
|
|
|
std::vector<ExportFilter *> ret;
|
|
|
|
ret.reserve(filters.size());
|
|
|
|
for (auto& filter : filters) ret.push_back(filter.get());
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2011-09-28 21:52:28 +02:00
|
|
|
void LuaScript::RegisterCommand(LuaCommand *command)
|
|
|
|
{
|
2012-11-04 04:53:03 +01:00
|
|
|
for (auto macro : macros) {
|
|
|
|
if (macro->name() == command->name()) {
|
2014-04-27 16:20:11 +02:00
|
|
|
error(L, "A macro named '%s' is already defined in script '%s'",
|
2013-11-21 18:13:36 +01:00
|
|
|
command->StrDisplay(nullptr).utf8_str().data(), name.c_str());
|
2011-09-28 21:52:28 +02:00
|
|
|
}
|
|
|
|
}
|
2011-09-28 21:48:47 +02:00
|
|
|
macros.push_back(command);
|
|
|
|
}
|
|
|
|
|
2011-09-28 21:52:28 +02:00
|
|
|
void LuaScript::UnregisterCommand(LuaCommand *command)
|
|
|
|
{
|
2011-09-28 21:48:47 +02:00
|
|
|
macros.erase(remove(macros.begin(), macros.end(), command), macros.end());
|
|
|
|
}
|
|
|
|
|
2011-09-28 21:52:28 +02:00
|
|
|
void LuaScript::RegisterFilter(LuaExportFilter *filter)
|
|
|
|
{
|
2013-10-27 15:15:39 +01:00
|
|
|
filters.emplace_back(filter);
|
2011-09-28 21:48:47 +02:00
|
|
|
}
|
|
|
|
|
2006-12-28 22:18:35 +01:00
|
|
|
LuaScript* LuaScript::GetScriptObject(lua_State *L)
|
|
|
|
{
|
|
|
|
lua_getfield(L, LUA_REGISTRYINDEX, "aegisub");
|
|
|
|
void *ptr = lua_touserdata(L, -1);
|
|
|
|
lua_pop(L, 1);
|
|
|
|
return (LuaScript*)ptr;
|
|
|
|
}
|
|
|
|
|
2010-01-28 02:13:13 +01:00
|
|
|
|
2006-12-28 22:18:35 +01:00
|
|
|
int LuaScript::LuaInclude(lua_State *L)
|
|
|
|
{
|
2013-01-04 16:01:50 +01:00
|
|
|
const LuaScript *s = GetScriptObject(L);
|
|
|
|
|
2014-04-27 16:20:11 +02:00
|
|
|
const std::string filename(check_string(L, 1));
|
2013-01-04 16:01:50 +01:00
|
|
|
agi::fs::path filepath;
|
|
|
|
|
|
|
|
// Relative or absolute path
|
|
|
|
if (!boost::all(filename, !boost::is_any_of("/\\")))
|
|
|
|
filepath = s->GetFilename().parent_path()/filename;
|
|
|
|
else { // Plain filename
|
|
|
|
for (auto const& dir : s->include_path) {
|
|
|
|
filepath = dir/filename;
|
|
|
|
if (agi::fs::FileExists(filepath))
|
|
|
|
break;
|
|
|
|
}
|
2006-12-28 22:18:35 +01:00
|
|
|
}
|
|
|
|
|
2013-01-04 16:01:50 +01:00
|
|
|
if (!agi::fs::FileExists(filepath))
|
2014-04-27 16:20:11 +02:00
|
|
|
return error(L, "Lua include not found: %s", filename.c_str());
|
2013-01-04 16:01:50 +01:00
|
|
|
|
2013-04-30 15:16:59 +02:00
|
|
|
if (!LoadFile(L, filepath))
|
2014-04-27 16:20:11 +02:00
|
|
|
return error(L, "Error loading Lua include \"%s\":\n%s", filename.c_str(), check_string(L, 1).c_str());
|
2011-09-28 21:48:37 +02:00
|
|
|
|
2006-12-28 22:18:35 +01:00
|
|
|
int pretop = lua_gettop(L) - 1; // don't count the function value itself
|
|
|
|
lua_call(L, 0, LUA_MULTRET);
|
|
|
|
return lua_gettop(L) - pretop;
|
|
|
|
}
|
|
|
|
|
2013-01-04 16:01:50 +01:00
|
|
|
void LuaThreadedCall(lua_State *L, int nargs, int nresults, std::string const& title, wxWindow *parent, bool can_open_config)
|
2011-09-28 21:47:40 +02:00
|
|
|
{
|
2011-10-25 03:15:03 +02:00
|
|
|
bool failed = false;
|
2011-09-28 21:47:40 +02:00
|
|
|
BackgroundScriptRunner bsr(parent, title);
|
2012-11-26 06:28:13 +01:00
|
|
|
bsr.Run([&](ProgressSink *ps) {
|
|
|
|
LuaProgressSink lps(L, ps, can_open_config);
|
|
|
|
|
2013-09-21 18:44:44 +02:00
|
|
|
// Insert our error handler under the function to call
|
2013-09-21 20:22:58 +02:00
|
|
|
lua_pushcclosure(L, add_stack_trace, 0);
|
2013-09-21 18:44:44 +02:00
|
|
|
lua_insert(L, -nargs - 2);
|
|
|
|
|
|
|
|
if (lua_pcall(L, nargs, nresults, -nargs - 2)) {
|
2012-11-26 06:28:13 +01:00
|
|
|
if (!lua_isnil(L, -1)) {
|
|
|
|
// if the call failed, log the error here
|
|
|
|
ps->Log("\n\nLua reported a runtime error:\n");
|
2013-03-18 15:01:54 +01:00
|
|
|
ps->Log(get_string_or_default(L, -1));
|
2012-11-26 06:28:13 +01:00
|
|
|
}
|
2013-09-24 16:45:41 +02:00
|
|
|
lua_pop(L, 2);
|
2012-11-26 06:28:13 +01:00
|
|
|
failed = true;
|
|
|
|
}
|
2013-09-24 16:45:41 +02:00
|
|
|
else
|
|
|
|
lua_remove(L, -nresults - 1);
|
2012-11-26 06:28:13 +01:00
|
|
|
|
|
|
|
lua_gc(L, LUA_GCCOLLECT, 0);
|
|
|
|
});
|
2011-10-25 03:15:03 +02:00
|
|
|
if (failed)
|
|
|
|
throw agi::UserCancelException("Script threw an error");
|
2006-12-28 22:18:35 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// LuaFeature
|
|
|
|
void LuaFeature::RegisterFeature()
|
|
|
|
{
|
2011-09-28 21:48:47 +02:00
|
|
|
myid = luaL_ref(L, LUA_REGISTRYINDEX);
|
2006-12-28 22:18:35 +01:00
|
|
|
}
|
|
|
|
|
2011-09-28 21:48:47 +02:00
|
|
|
void LuaFeature::UnregisterFeature()
|
|
|
|
{
|
|
|
|
luaL_unref(L, LUA_REGISTRYINDEX, myid);
|
|
|
|
}
|
Note: This was done using a script! it's far from perfect but 95% of the work has been done already formatting-wise.
Document all functions, class, struct, union, enum, macro, variable, typedefs. This isn't the actual document in itself but empty documentation using any old documentation if it was there.
This was done using exuberant ctags to get tag info, then a TCL script to parse/remove old comments and convert them into Doxygen-style.
Some notes:
* Anything labeled 'DOCME' needs to be documented, @param and @return have been left blank as it would be annoying to delete the 'DOCME' from every one of those.
* Some multiline comments may have been munged into single line comments
* Leave the /// comments above global variables with a space, if they're harder to read then we'll be less likey to use them.
* Enum comments can go after the enumeration itself '[value] /// comment'
* include/aegisub/*.h haven't been converted yet, this will be done in a later commit
* Some documentation blocks are in the wrong place, in the .h when it should be in the .cpp, or vice versa.
See http://devel.aegisub.org/wiki/Doxygen for some details on Doxygen and a 'style guide'.
Originally committed to SVN as r3312.
2009-07-30 00:59:22 +02:00
|
|
|
|
2012-01-20 22:33:39 +01:00
|
|
|
void LuaFeature::GetFeatureFunction(const char *function) const
|
2006-12-28 22:18:35 +01:00
|
|
|
{
|
|
|
|
// get this feature's function pointers
|
2011-09-28 21:48:47 +02:00
|
|
|
lua_rawgeti(L, LUA_REGISTRYINDEX, myid);
|
2006-12-28 22:18:35 +01:00
|
|
|
// get pointer for validation function
|
2013-01-04 16:01:50 +01:00
|
|
|
push_value(L, function);
|
2011-09-28 21:48:47 +02:00
|
|
|
lua_rawget(L, -2);
|
|
|
|
// remove the function table
|
2006-12-28 22:18:35 +01:00
|
|
|
lua_remove(L, -2);
|
2011-09-28 21:48:47 +02:00
|
|
|
assert(lua_isfunction(L, -1));
|
2006-12-28 22:18:35 +01:00
|
|
|
}
|
|
|
|
|
2011-09-28 21:48:47 +02:00
|
|
|
// LuaFeatureMacro
|
|
|
|
int LuaCommand::LuaRegister(lua_State *L)
|
2006-12-28 22:18:35 +01:00
|
|
|
{
|
2013-09-22 00:59:28 +02:00
|
|
|
static std::mutex mutex;
|
2014-04-23 22:53:24 +02:00
|
|
|
auto command = agi::make_unique<LuaCommand>(L);
|
2013-09-22 00:59:28 +02:00
|
|
|
{
|
|
|
|
std::lock_guard<std::mutex> lock(mutex);
|
|
|
|
cmd::reg(std::move(command));
|
|
|
|
}
|
2011-09-28 21:48:47 +02:00
|
|
|
return 0;
|
2006-12-28 22:18:35 +01:00
|
|
|
}
|
|
|
|
|
2011-09-28 21:48:47 +02:00
|
|
|
LuaCommand::LuaCommand(lua_State *L)
|
|
|
|
: LuaFeature(L)
|
|
|
|
, display(check_wxstring(L, 1))
|
2012-01-25 01:21:37 +01:00
|
|
|
, help(get_wxstring(L, 2))
|
2011-09-28 21:48:47 +02:00
|
|
|
, cmd_type(cmd::COMMAND_NORMAL)
|
2006-12-28 22:18:35 +01:00
|
|
|
{
|
2011-09-28 21:52:20 +02:00
|
|
|
lua_getfield(L, LUA_REGISTRYINDEX, "filename");
|
2014-05-29 00:19:05 +02:00
|
|
|
cmd_name = agi::format("automation/lua/%s/%s", check_string(L, -1), check_string(L, 1));
|
2011-09-28 21:52:20 +02:00
|
|
|
|
2011-09-28 21:48:47 +02:00
|
|
|
if (!lua_isfunction(L, 3))
|
2014-04-27 16:20:11 +02:00
|
|
|
error(L, "The macro processing function must be a function");
|
2006-12-28 22:18:35 +01:00
|
|
|
|
2011-09-28 21:48:47 +02:00
|
|
|
if (lua_isfunction(L, 4))
|
|
|
|
cmd_type |= cmd::COMMAND_VALIDATE;
|
2006-12-28 22:18:35 +01:00
|
|
|
|
2011-09-28 21:48:47 +02:00
|
|
|
if (lua_isfunction(L, 5))
|
|
|
|
cmd_type |= cmd::COMMAND_TOGGLE;
|
2006-12-28 22:18:35 +01:00
|
|
|
|
2011-09-28 21:48:47 +02:00
|
|
|
// new table for containing the functions for this feature
|
2014-04-27 15:41:28 +02:00
|
|
|
lua_createtable(L, 0, 3);
|
Note: This was done using a script! it's far from perfect but 95% of the work has been done already formatting-wise.
Document all functions, class, struct, union, enum, macro, variable, typedefs. This isn't the actual document in itself but empty documentation using any old documentation if it was there.
This was done using exuberant ctags to get tag info, then a TCL script to parse/remove old comments and convert them into Doxygen-style.
Some notes:
* Anything labeled 'DOCME' needs to be documented, @param and @return have been left blank as it would be annoying to delete the 'DOCME' from every one of those.
* Some multiline comments may have been munged into single line comments
* Leave the /// comments above global variables with a space, if they're harder to read then we'll be less likey to use them.
* Enum comments can go after the enumeration itself '[value] /// comment'
* include/aegisub/*.h haven't been converted yet, this will be done in a later commit
* Some documentation blocks are in the wrong place, in the .h when it should be in the .cpp, or vice versa.
See http://devel.aegisub.org/wiki/Doxygen for some details on Doxygen and a 'style guide'.
Originally committed to SVN as r3312.
2009-07-30 00:59:22 +02:00
|
|
|
|
2011-09-28 21:48:47 +02:00
|
|
|
// store processing function
|
2013-01-04 16:01:50 +01:00
|
|
|
push_value(L, "run");
|
2011-09-28 21:48:47 +02:00
|
|
|
lua_pushvalue(L, 3);
|
|
|
|
lua_rawset(L, -3);
|
|
|
|
|
|
|
|
// store validation function
|
2013-01-04 16:01:50 +01:00
|
|
|
push_value(L, "validate");
|
2011-09-28 21:48:47 +02:00
|
|
|
lua_pushvalue(L, 4);
|
|
|
|
lua_rawset(L, -3);
|
2006-12-28 22:18:35 +01:00
|
|
|
|
2011-09-28 21:48:47 +02:00
|
|
|
// store active function
|
2013-01-04 16:01:50 +01:00
|
|
|
push_value(L, "isactive");
|
2011-09-28 21:48:47 +02:00
|
|
|
lua_pushvalue(L, 5);
|
|
|
|
lua_rawset(L, -3);
|
2006-12-28 22:18:35 +01:00
|
|
|
|
2011-09-28 21:48:47 +02:00
|
|
|
// store the table in the registry
|
|
|
|
RegisterFeature();
|
|
|
|
|
|
|
|
LuaScript::GetScriptObject(L)->RegisterCommand(this);
|
2006-12-28 22:18:35 +01:00
|
|
|
}
|
|
|
|
|
2011-09-28 21:48:47 +02:00
|
|
|
LuaCommand::~LuaCommand()
|
|
|
|
{
|
|
|
|
UnregisterFeature();
|
|
|
|
LuaScript::GetScriptObject(L)->UnregisterCommand(this);
|
|
|
|
}
|
Note: This was done using a script! it's far from perfect but 95% of the work has been done already formatting-wise.
Document all functions, class, struct, union, enum, macro, variable, typedefs. This isn't the actual document in itself but empty documentation using any old documentation if it was there.
This was done using exuberant ctags to get tag info, then a TCL script to parse/remove old comments and convert them into Doxygen-style.
Some notes:
* Anything labeled 'DOCME' needs to be documented, @param and @return have been left blank as it would be annoying to delete the 'DOCME' from every one of those.
* Some multiline comments may have been munged into single line comments
* Leave the /// comments above global variables with a space, if they're harder to read then we'll be less likey to use them.
* Enum comments can go after the enumeration itself '[value] /// comment'
* include/aegisub/*.h haven't been converted yet, this will be done in a later commit
* Some documentation blocks are in the wrong place, in the .h when it should be in the .cpp, or vice versa.
See http://devel.aegisub.org/wiki/Doxygen for some details on Doxygen and a 'style guide'.
Originally committed to SVN as r3312.
2009-07-30 00:59:22 +02:00
|
|
|
|
2014-03-31 17:04:57 +02:00
|
|
|
static std::vector<int> selected_rows(const agi::Context *c)
|
2006-12-28 22:18:35 +01:00
|
|
|
{
|
2014-03-25 01:15:14 +01:00
|
|
|
auto const& sel = c->selectionController->GetSelectedSet();
|
2014-03-31 17:04:57 +02:00
|
|
|
int offset = c->ass->Info.size() + c->ass->Styles.size();
|
|
|
|
std::vector<int> rows;
|
|
|
|
rows.reserve(sel.size());
|
|
|
|
for (auto line : sel)
|
|
|
|
rows.push_back(line->Row + offset + 1);
|
|
|
|
sort(begin(rows), end(rows));
|
|
|
|
return rows;
|
|
|
|
}
|
2011-09-28 21:48:47 +02:00
|
|
|
|
|
|
|
bool LuaCommand::Validate(const agi::Context *c)
|
2006-12-28 22:18:35 +01:00
|
|
|
{
|
2011-09-28 21:48:47 +02:00
|
|
|
if (!(cmd_type & cmd::COMMAND_VALIDATE)) return true;
|
2006-12-28 22:18:35 +01:00
|
|
|
|
2011-09-28 21:49:47 +02:00
|
|
|
set_context(L, c);
|
|
|
|
|
2015-03-14 21:58:32 +01:00
|
|
|
// Error handler goes under the function to call
|
|
|
|
lua_pushcclosure(L, add_stack_trace, 0);
|
|
|
|
|
2011-09-28 21:48:47 +02:00
|
|
|
GetFeatureFunction("validate");
|
2014-03-25 22:49:26 +01:00
|
|
|
auto subsobj = new LuaAssFile(L, c->ass.get());
|
2014-03-31 17:04:57 +02:00
|
|
|
|
2014-04-27 15:47:00 +02:00
|
|
|
push_value(L, selected_rows(c));
|
2014-03-31 17:04:57 +02:00
|
|
|
if (auto active_line = c->selectionController->GetActiveLine())
|
|
|
|
push_value(L, active_line->Row + c->ass->Info.size() + c->ass->Styles.size() + 1);
|
2015-03-14 21:58:32 +01:00
|
|
|
else
|
|
|
|
lua_pushnil(L);
|
2006-12-28 22:18:35 +01:00
|
|
|
|
2015-03-14 21:58:32 +01:00
|
|
|
int err = lua_pcall(L, 3, 2, -5 /* three args, function, error handler */);
|
2011-09-28 21:48:47 +02:00
|
|
|
subsobj->ProcessingComplete();
|
|
|
|
|
2012-01-25 01:21:37 +01:00
|
|
|
if (err) {
|
2011-09-28 21:48:47 +02:00
|
|
|
wxLogWarning("Runtime error in Lua macro validation function:\n%s", get_wxstring(L, -1));
|
2015-03-14 21:58:32 +01:00
|
|
|
lua_pop(L, 2);
|
2012-01-25 01:21:37 +01:00
|
|
|
return false;
|
|
|
|
}
|
2006-12-28 22:18:35 +01:00
|
|
|
|
2012-01-25 01:21:37 +01:00
|
|
|
bool result = !!lua_toboolean(L, -2);
|
|
|
|
|
|
|
|
wxString new_help_string(get_wxstring(L, -1));
|
|
|
|
if (new_help_string.size()) {
|
|
|
|
help = new_help_string;
|
|
|
|
cmd_type |= cmd::COMMAND_DYNAMIC_HELP;
|
|
|
|
}
|
|
|
|
|
2015-03-14 21:58:32 +01:00
|
|
|
lua_pop(L, 3); // two return values and error handler
|
2006-12-28 22:18:35 +01:00
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2011-09-28 21:48:47 +02:00
|
|
|
void LuaCommand::operator()(agi::Context *c)
|
2006-12-28 22:18:35 +01:00
|
|
|
{
|
2012-02-10 02:41:22 +01:00
|
|
|
LuaStackcheck stackcheck(L);
|
2011-09-28 21:49:47 +02:00
|
|
|
set_context(L, c);
|
2012-02-10 02:41:22 +01:00
|
|
|
stackcheck.check_stack(0);
|
2011-09-28 21:49:47 +02:00
|
|
|
|
2011-09-28 21:48:47 +02:00
|
|
|
GetFeatureFunction("run");
|
2014-03-25 22:49:26 +01:00
|
|
|
auto subsobj = new LuaAssFile(L, c->ass.get(), true, true);
|
2014-03-31 17:04:57 +02:00
|
|
|
|
|
|
|
int original_offset = c->ass->Info.size() + c->ass->Styles.size() + 1;
|
|
|
|
auto original_sel = selected_rows(c);
|
|
|
|
int original_active = 0;
|
|
|
|
if (auto active_line = c->selectionController->GetActiveLine())
|
|
|
|
original_active = active_line->Row + original_offset;
|
|
|
|
|
2014-04-27 15:47:00 +02:00
|
|
|
push_value(L, original_sel);
|
2014-03-31 17:04:57 +02:00
|
|
|
push_value(L, original_active);
|
2006-12-28 22:18:35 +01:00
|
|
|
|
2011-09-28 21:50:14 +02:00
|
|
|
try {
|
2013-01-04 16:01:50 +01:00
|
|
|
LuaThreadedCall(L, 3, 2, from_wx(StrDisplay(c)), c->parent, true);
|
2015-03-11 21:58:23 +01:00
|
|
|
}
|
|
|
|
catch (agi::UserCancelException const&) {
|
|
|
|
subsobj->Cancel();
|
|
|
|
stackcheck.check_stack(0);
|
|
|
|
return;
|
|
|
|
}
|
2011-09-28 21:50:14 +02:00
|
|
|
|
2015-03-11 21:58:23 +01:00
|
|
|
auto lines = subsobj->ProcessingComplete(StrDisplay(c));
|
2011-09-28 21:50:14 +02:00
|
|
|
|
2015-03-11 21:58:23 +01:00
|
|
|
AssDialogue *active_line = nullptr;
|
|
|
|
int active_idx = original_active;
|
2012-01-31 01:44:16 +01:00
|
|
|
|
2015-03-11 21:58:23 +01:00
|
|
|
// Check for a new active row
|
|
|
|
if (lua_isnumber(L, -1)) {
|
|
|
|
active_idx = lua_tointeger(L, -1);
|
|
|
|
if (active_idx < 1 || active_idx > (int)lines.size()) {
|
|
|
|
wxLogError("Active row %d is out of bounds (must be 1-%u)", active_idx, lines.size());
|
|
|
|
active_idx = original_active;
|
2012-01-31 01:44:16 +01:00
|
|
|
}
|
2015-03-11 21:58:23 +01:00
|
|
|
}
|
2012-02-10 02:41:22 +01:00
|
|
|
|
2015-03-11 21:58:23 +01:00
|
|
|
stackcheck.check_stack(2);
|
|
|
|
lua_pop(L, 1);
|
2012-01-31 01:44:16 +01:00
|
|
|
|
2015-03-11 21:58:23 +01:00
|
|
|
// top of stack will be selected lines array, if any was returned
|
|
|
|
if (lua_istable(L, -1)) {
|
|
|
|
std::set<AssDialogue*> sel;
|
|
|
|
lua_for_each(L, [&] {
|
|
|
|
if (!lua_isnumber(L, -1))
|
|
|
|
return;
|
|
|
|
int cur = lua_tointeger(L, -1);
|
|
|
|
if (cur < 1 || cur > (int)lines.size()) {
|
|
|
|
wxLogError("Selected row %d is out of bounds (must be 1-%u)", cur, lines.size());
|
|
|
|
throw LuaForEachBreak();
|
2014-03-31 17:04:57 +02:00
|
|
|
}
|
|
|
|
|
2015-03-11 21:58:23 +01:00
|
|
|
if (typeid(*lines[cur - 1]) != typeid(AssDialogue)) {
|
|
|
|
wxLogError("Selected row %d is not a dialogue line", cur);
|
|
|
|
throw LuaForEachBreak();
|
|
|
|
}
|
2014-03-31 17:04:57 +02:00
|
|
|
|
2015-03-11 21:58:23 +01:00
|
|
|
auto diag = static_cast<AssDialogue*>(lines[cur - 1]);
|
|
|
|
sel.insert(diag);
|
|
|
|
if (!active_line || active_idx == cur)
|
|
|
|
active_line = diag;
|
|
|
|
});
|
|
|
|
|
|
|
|
AssDialogue *new_active = c->selectionController->GetActiveLine();
|
|
|
|
if (active_line && (active_idx > 0 || !sel.count(new_active)))
|
|
|
|
new_active = active_line;
|
|
|
|
if (sel.empty())
|
|
|
|
sel.insert(new_active);
|
|
|
|
c->selectionController->SetSelectionAndActive(std::move(sel), new_active);
|
2011-09-28 21:50:14 +02:00
|
|
|
}
|
2015-03-11 21:58:23 +01:00
|
|
|
else {
|
|
|
|
lua_pop(L, 1);
|
|
|
|
|
|
|
|
Selection new_sel;
|
|
|
|
AssDialogue *new_active = nullptr;
|
|
|
|
|
|
|
|
int prev = original_offset;
|
|
|
|
auto it = c->ass->Events.begin();
|
|
|
|
for (int row : original_sel) {
|
|
|
|
while (row > prev && it != c->ass->Events.end()) {
|
|
|
|
++prev;
|
|
|
|
++it;
|
|
|
|
}
|
|
|
|
if (it == c->ass->Events.end()) break;
|
|
|
|
new_sel.insert(&*it);
|
|
|
|
if (row == original_active)
|
|
|
|
new_active = &*it;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (new_sel.empty() && !c->ass->Events.empty())
|
|
|
|
new_sel.insert(&c->ass->Events.front());
|
|
|
|
if (!new_sel.count(new_active))
|
|
|
|
new_active = *new_sel.begin();
|
|
|
|
c->selectionController->SetSelectionAndActive(std::move(new_sel), new_active);
|
2007-07-06 16:26:04 +02:00
|
|
|
}
|
2015-03-11 21:58:23 +01:00
|
|
|
|
2012-02-10 03:16:49 +01:00
|
|
|
stackcheck.check_stack(0);
|
2006-12-28 22:18:35 +01:00
|
|
|
}
|
|
|
|
|
2011-09-28 21:48:47 +02:00
|
|
|
bool LuaCommand::IsActive(const agi::Context *c)
|
|
|
|
{
|
2011-09-28 21:49:18 +02:00
|
|
|
if (!(cmd_type & cmd::COMMAND_TOGGLE)) return false;
|
|
|
|
|
2012-02-10 03:16:49 +01:00
|
|
|
LuaStackcheck stackcheck(L);
|
|
|
|
|
2011-09-28 21:49:47 +02:00
|
|
|
set_context(L, c);
|
2012-02-10 03:16:49 +01:00
|
|
|
stackcheck.check_stack(0);
|
2011-09-28 21:49:47 +02:00
|
|
|
|
2011-09-28 21:49:18 +02:00
|
|
|
GetFeatureFunction("isactive");
|
2014-03-25 22:49:26 +01:00
|
|
|
auto subsobj = new LuaAssFile(L, c->ass.get());
|
2014-04-27 15:47:00 +02:00
|
|
|
push_value(L, selected_rows(c));
|
2014-03-31 17:04:57 +02:00
|
|
|
if (auto active_line = c->selectionController->GetActiveLine())
|
|
|
|
push_value(L, active_line->Row + c->ass->Info.size() + c->ass->Styles.size() + 1);
|
2011-09-28 21:49:18 +02:00
|
|
|
|
|
|
|
int err = lua_pcall(L, 3, 1, 0);
|
|
|
|
subsobj->ProcessingComplete();
|
|
|
|
|
|
|
|
bool result = false;
|
|
|
|
if (err)
|
|
|
|
wxLogWarning("Runtime error in Lua macro IsActive function:\n%s", get_wxstring(L, -1));
|
|
|
|
else
|
|
|
|
result = !!lua_toboolean(L, -1);
|
|
|
|
|
|
|
|
// clean up stack (result or error message)
|
2012-02-10 03:16:49 +01:00
|
|
|
stackcheck.check_stack(1);
|
2011-09-28 21:49:18 +02:00
|
|
|
lua_pop(L, 1);
|
|
|
|
|
|
|
|
return result;
|
2011-09-28 21:48:47 +02:00
|
|
|
}
|
2006-12-28 22:18:35 +01:00
|
|
|
|
|
|
|
// LuaFeatureFilter
|
2011-09-28 21:48:47 +02:00
|
|
|
LuaExportFilter::LuaExportFilter(lua_State *L)
|
2014-04-27 16:20:11 +02:00
|
|
|
: ExportFilter(check_string(L, 1), lua_tostring(L, 2), lua_tointeger(L, 3))
|
2011-09-28 21:48:47 +02:00
|
|
|
, LuaFeature(L)
|
2006-12-28 22:18:35 +01:00
|
|
|
{
|
2011-09-28 21:48:47 +02:00
|
|
|
if (!lua_isfunction(L, 4))
|
2014-04-27 16:20:11 +02:00
|
|
|
error(L, "The filter processing function must be a function");
|
2011-09-28 21:48:47 +02:00
|
|
|
|
|
|
|
// new table for containing the functions for this feature
|
2014-04-27 15:41:28 +02:00
|
|
|
lua_createtable(L, 0, 2);
|
2011-09-28 21:48:47 +02:00
|
|
|
|
|
|
|
// store processing function
|
2013-01-04 16:01:50 +01:00
|
|
|
push_value(L, "run");
|
2006-12-28 22:18:35 +01:00
|
|
|
lua_pushvalue(L, 4);
|
2011-09-28 21:48:47 +02:00
|
|
|
lua_rawset(L, -3);
|
|
|
|
|
|
|
|
// store config function
|
2013-01-04 16:01:50 +01:00
|
|
|
push_value(L, "config");
|
2006-12-28 22:18:35 +01:00
|
|
|
lua_pushvalue(L, 5);
|
|
|
|
has_config = lua_isfunction(L, -1);
|
2011-09-28 21:48:47 +02:00
|
|
|
lua_rawset(L, -3);
|
2006-12-28 22:18:35 +01:00
|
|
|
|
2011-09-28 21:48:47 +02:00
|
|
|
// store the table in the registry
|
|
|
|
RegisterFeature();
|
Note: This was done using a script! it's far from perfect but 95% of the work has been done already formatting-wise.
Document all functions, class, struct, union, enum, macro, variable, typedefs. This isn't the actual document in itself but empty documentation using any old documentation if it was there.
This was done using exuberant ctags to get tag info, then a TCL script to parse/remove old comments and convert them into Doxygen-style.
Some notes:
* Anything labeled 'DOCME' needs to be documented, @param and @return have been left blank as it would be annoying to delete the 'DOCME' from every one of those.
* Some multiline comments may have been munged into single line comments
* Leave the /// comments above global variables with a space, if they're harder to read then we'll be less likey to use them.
* Enum comments can go after the enumeration itself '[value] /// comment'
* include/aegisub/*.h haven't been converted yet, this will be done in a later commit
* Some documentation blocks are in the wrong place, in the .h when it should be in the .cpp, or vice versa.
See http://devel.aegisub.org/wiki/Doxygen for some details on Doxygen and a 'style guide'.
Originally committed to SVN as r3312.
2009-07-30 00:59:22 +02:00
|
|
|
|
2011-09-28 21:48:47 +02:00
|
|
|
LuaScript::GetScriptObject(L)->RegisterFilter(this);
|
2006-12-28 22:18:35 +01:00
|
|
|
}
|
|
|
|
|
2011-09-28 21:48:47 +02:00
|
|
|
int LuaExportFilter::LuaRegister(lua_State *L)
|
2006-12-28 22:18:35 +01:00
|
|
|
{
|
2013-09-22 00:59:28 +02:00
|
|
|
static std::mutex mutex;
|
2014-04-23 22:53:24 +02:00
|
|
|
auto filter = agi::make_unique<LuaExportFilter>(L);
|
2013-09-22 00:59:28 +02:00
|
|
|
{
|
|
|
|
std::lock_guard<std::mutex> lock(mutex);
|
|
|
|
AssExportFilterChain::Register(std::move(filter));
|
|
|
|
}
|
2006-12-28 22:18:35 +01:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2011-09-28 21:48:47 +02:00
|
|
|
void LuaExportFilter::ProcessSubs(AssFile *subs, wxWindow *export_dialog)
|
2006-12-28 22:18:35 +01:00
|
|
|
{
|
2008-01-24 00:02:26 +01:00
|
|
|
LuaStackcheck stackcheck(L);
|
|
|
|
|
2011-09-28 21:48:47 +02:00
|
|
|
GetFeatureFunction("run");
|
2008-03-09 22:09:51 +01:00
|
|
|
stackcheck.check_stack(1);
|
2006-12-28 22:18:35 +01:00
|
|
|
|
2011-09-28 21:48:47 +02:00
|
|
|
// The entire point of an export filter is to modify the file, but
|
|
|
|
// setting undo points makes no sense
|
2013-11-21 18:13:36 +01:00
|
|
|
auto subsobj = new LuaAssFile(L, subs, true);
|
2008-01-24 00:02:26 +01:00
|
|
|
assert(lua_isuserdata(L, -1));
|
2008-03-09 22:09:51 +01:00
|
|
|
stackcheck.check_stack(2);
|
2011-09-28 21:48:47 +02:00
|
|
|
|
2006-12-28 22:18:35 +01:00
|
|
|
// config
|
|
|
|
if (has_config && config_dialog) {
|
2008-01-24 00:02:26 +01:00
|
|
|
int results_produced = config_dialog->LuaReadBack(L);
|
|
|
|
assert(results_produced == 1);
|
2008-03-13 20:12:55 +01:00
|
|
|
(void) results_produced; // avoid warning on release builds
|
2006-12-28 22:18:35 +01:00
|
|
|
// TODO, write back stored options here
|
2007-02-20 03:50:40 +01:00
|
|
|
} else {
|
|
|
|
// no config so put an empty table instead
|
|
|
|
lua_newtable(L);
|
2006-12-28 22:18:35 +01:00
|
|
|
}
|
2008-01-24 00:02:26 +01:00
|
|
|
assert(lua_istable(L, -1));
|
2008-03-09 22:09:51 +01:00
|
|
|
stackcheck.check_stack(3);
|
2006-12-28 22:18:35 +01:00
|
|
|
|
2011-09-28 21:50:14 +02:00
|
|
|
try {
|
|
|
|
LuaThreadedCall(L, 2, 0, GetName(), export_dialog, false);
|
|
|
|
stackcheck.check_stack(0);
|
|
|
|
subsobj->ProcessingComplete();
|
|
|
|
}
|
|
|
|
catch (agi::UserCancelException const&) {
|
|
|
|
subsobj->Cancel();
|
|
|
|
throw;
|
|
|
|
}
|
2006-12-28 22:18:35 +01:00
|
|
|
}
|
|
|
|
|
2014-04-23 01:34:18 +02:00
|
|
|
std::unique_ptr<ScriptDialog> LuaExportFilter::GenerateConfigDialog(wxWindow *parent, agi::Context *c)
|
2006-12-28 22:18:35 +01:00
|
|
|
{
|
|
|
|
if (!has_config)
|
2013-11-21 18:13:36 +01:00
|
|
|
return nullptr;
|
2006-12-28 22:18:35 +01:00
|
|
|
|
2011-09-28 21:49:47 +02:00
|
|
|
set_context(L, c);
|
|
|
|
|
2011-11-30 00:17:20 +01:00
|
|
|
GetFeatureFunction("config");
|
2006-12-28 22:18:35 +01:00
|
|
|
|
|
|
|
// prepare function call
|
2014-03-25 22:49:26 +01:00
|
|
|
auto subsobj = new LuaAssFile(L, c->ass.get());
|
2006-12-28 22:18:35 +01:00
|
|
|
// stored options
|
|
|
|
lua_newtable(L); // TODO, nothing for now
|
|
|
|
|
|
|
|
// do call
|
2009-07-28 17:39:52 +02:00
|
|
|
int err = lua_pcall(L, 2, 1, 0);
|
2011-09-28 21:48:47 +02:00
|
|
|
subsobj->ProcessingComplete();
|
|
|
|
|
2009-07-28 17:39:52 +02:00
|
|
|
if (err) {
|
2011-09-28 21:48:47 +02:00
|
|
|
wxLogWarning("Runtime error in Lua config dialog function:\n%s", get_wxstring(L, -1));
|
2009-07-28 17:39:52 +02:00
|
|
|
lua_pop(L, 1); // remove error message
|
|
|
|
} else {
|
|
|
|
// Create config dialogue from table on top of stack
|
2011-09-28 21:49:27 +02:00
|
|
|
config_dialog = new LuaDialog(L, false);
|
2007-02-20 03:50:40 +01:00
|
|
|
}
|
2011-09-28 21:48:47 +02:00
|
|
|
|
2014-04-23 01:34:18 +02:00
|
|
|
return std::unique_ptr<ScriptDialog>{config_dialog};
|
2006-12-28 22:18:35 +01:00
|
|
|
}
|
2014-04-25 18:27:27 +02:00
|
|
|
}
|
2006-12-28 22:18:35 +01:00
|
|
|
|
2014-04-25 18:27:27 +02:00
|
|
|
namespace Automation4 {
|
2011-09-28 21:46:05 +02:00
|
|
|
LuaScriptFactory::LuaScriptFactory()
|
2013-04-30 15:16:59 +02:00
|
|
|
: ScriptFactory("Lua", "*.lua,*.moon")
|
2008-03-09 22:49:46 +01:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2013-06-08 06:19:40 +02:00
|
|
|
std::unique_ptr<Script> LuaScriptFactory::Produce(agi::fs::path const& filename) const
|
2008-03-09 22:49:46 +01:00
|
|
|
{
|
2013-04-30 15:16:59 +02:00
|
|
|
if (agi::fs::HasExtension(filename, "lua") || agi::fs::HasExtension(filename, "moon"))
|
2014-04-23 22:53:24 +02:00
|
|
|
return agi::make_unique<LuaScript>(filename);
|
2013-04-30 15:16:59 +02:00
|
|
|
return nullptr;
|
2008-03-09 22:49:46 +01:00
|
|
|
}
|
2011-09-28 21:48:47 +02:00
|
|
|
}
|