1// Aseprite
2// Copyright (C) 2019-2022 Igara Studio S.A.
3// Copyright (C) 2018 David Capello
4//
5// This program is distributed under the terms of
6// the End-User License Agreement for Aseprite.
7
8#ifdef HAVE_CONFIG_H
9#include "config.h"
10#endif
11
12#include "app/app.h"
13#include "app/commands/command.h"
14#include "app/commands/commands.h"
15#include "app/commands/new_params.h"
16#include "app/commands/params.h"
17#include "app/context.h"
18#include "app/script/luacpp.h"
19
20namespace app {
21namespace script {
22
23namespace {
24
25struct AppCommand { };
26
27int Command_call(lua_State* L)
28{
29 app::Context* ctx = App::instance()->context();
30 if (!ctx)
31 return 0;
32
33 auto command = get_ptr<Command>(L, 1);
34 Params params;
35
36 if (auto commandLua = dynamic_cast<CommandWithNewParamsBase*>(command)) {
37 commandLua->loadParamsFromLuaTable(L, 2);
38 }
39 else {
40 if (lua_istable(L, 2)) {
41 lua_pushnil(L);
42 while (lua_next(L, 2) != 0) {
43 if (const char* k = lua_tostring(L, -2)) {
44 const char* v = luaL_tolstring(L, -1, nullptr);
45 if (v)
46 params.set(k, v);
47 lua_pop(L, 1); // pop the value generated by luaL_tolstring()
48 }
49 lua_pop(L, 1); // pop the value lua_next(), leave the key in the stack
50 }
51 }
52 }
53
54 ctx->executeCommand(command, params);
55
56 if (ctx->commandResult().type() == CommandResult::kOk) {
57 lua_pushboolean(L, true);
58 }
59 else {
60 // TODO rollback/cancel the whole current transaction?
61 // or just throw an luaL_error()?
62 lua_pushboolean(L, false);
63 }
64 return 1;
65}
66
67int AppCommand_index(lua_State* L)
68{
69 const char* id = lua_tostring(L, 2);
70 if (!id)
71 return luaL_error(L, "id in app.command.id() must be a string");
72
73 Command* cmd = Commands::instance()->byId(id);
74 if (!cmd)
75 return luaL_error(L, "command '%s' not found", id);
76
77 push_ptr(L, cmd);
78 return 1;
79}
80
81const luaL_Reg Command_methods[] = {
82 { "__call", Command_call },
83 { nullptr, nullptr }
84};
85
86const luaL_Reg AppCommand_methods[] = {
87 { "__index", AppCommand_index },
88 { nullptr, nullptr }
89};
90
91} // anonymous namespace
92
93DEF_MTNAME(Command);
94DEF_MTNAME(AppCommand);
95
96void register_app_command_object(lua_State* L)
97{
98 REG_CLASS(L, Command);
99 REG_CLASS(L, AppCommand);
100
101 lua_getglobal(L, "app");
102 lua_pushstring(L, "command");
103 push_new<AppCommand>(L);
104 lua_rawset(L, -3);
105 lua_pop(L, 1);
106}
107
108} // namespace script
109} // namespace app
110