Lua GUI Instance update: prepare for template scripts
[ardour.git] / gtk2_ardour / luainstance.cc
index b8dbb4accf9eaf95adcafd3bb3c87b2832f8a4c1..9db09e38dcc00d655124743745087b146032b674 100644 (file)
@@ -27,6 +27,7 @@
 #include "ardour/plugin_manager.h"
 #include "ardour/route.h"
 #include "ardour/session.h"
+#include "ardour/system_exec.h"
 
 #include "LuaBridge/LuaBridge.h"
 
@@ -34,6 +35,7 @@
 #include "ardour_ui.h"
 #include "public_editor.h"
 #include "region_selection.h"
+#include "luadialog.h"
 #include "luainstance.h"
 #include "luasignal.h"
 #include "marker.h"
@@ -375,6 +377,7 @@ namespace LuaMixer {
 using namespace ARDOUR;
 
 PBD::Signal0<void> LuaInstance::LuaTimerDS;
+PBD::Signal0<void> LuaInstance::SetSession;
 
 void
 LuaInstance::register_hooks (lua_State* L)
@@ -396,6 +399,10 @@ LuaInstance::register_hooks (lua_State* L)
                .beginStdBitSet <LuaSignal::LAST_SIGNAL> ("Set")
                .endClass()
                .endNamespace ();
+
+#if 0 // Dump size -> libs/ardour/luabindings.cc
+       printf ("LuaInstance: registered %d signals\n", LuaSignal::LAST_SIGNAL);
+#endif
 }
 
 void
@@ -548,6 +555,51 @@ LuaInstance::bind_cairo (lua_State* L)
 
 }
 
+void
+LuaInstance::bind_dialog (lua_State* L)
+{
+       luabridge::getGlobalNamespace (L)
+               .beginNamespace ("LuaDialog")
+
+               .beginClass <LuaDialog::Message> ("Message")
+               .addConstructor <void (*) (std::string const&, std::string const&, LuaDialog::Message::MessageType, LuaDialog::Message::ButtonType)> ()
+               .addFunction ("run", &LuaDialog::Message::run)
+               .endClass ()
+
+               .beginClass <LuaDialog::Dialog> ("Dialog")
+               .addConstructor <void (*) (std::string const&, luabridge::LuaRef)> ()
+               .addCFunction ("run", &LuaDialog::Dialog::run)
+               .endClass ()
+
+               /* enums */
+               .beginNamespace ("MessageType")
+               .addConst ("Info", LuaDialog::Message::Info)
+               .addConst ("Warning", LuaDialog::Message::Warning)
+               .addConst ("Question", LuaDialog::Message::Question)
+               .addConst ("Error", LuaDialog::Message::Error)
+               .endNamespace ()
+
+               .beginNamespace ("ButtonType")
+               .addConst ("OK", LuaDialog::Message::OK)
+               .addConst ("Close", LuaDialog::Message::Close)
+               .addConst ("Cancel", LuaDialog::Message::Cancel)
+               .addConst ("Yes_No", LuaDialog::Message::Yes_No)
+               .addConst ("OK_Cancel", LuaDialog::Message::OK_Cancel)
+               .endNamespace ()
+
+               .beginNamespace ("Response")
+               .addConst ("OK", 0)
+               .addConst ("Cancel", 1)
+               .addConst ("Close", 2)
+               .addConst ("Yes", 3)
+               .addConst ("No", 4)
+               .addConst ("None", -1)
+               .endNamespace ()
+
+               .endNamespace ();
+
+}
+
 void
 LuaInstance::register_classes (lua_State* L)
 {
@@ -557,7 +609,7 @@ LuaInstance::register_classes (lua_State* L)
        LuaBindings::osc (L);
 
        bind_cairo (L);
-       register_hooks (L);
+       bind_dialog (L);
 
        luabridge::getGlobalNamespace (L)
                .beginNamespace ("ArdourUI")
@@ -581,6 +633,9 @@ LuaInstance::register_classes (lua_State* L)
                .deriveClass <TimeAxisView, AxisView> ("TimeAxisView")
                .endClass ()
 
+               .deriveClass <StripableTimeAxisView, TimeAxisView> ("StripableTimeAxisView")
+               .endClass ()
+
                .beginClass <Selectable> ("Selectable")
                .endClass ()
 
@@ -594,7 +649,8 @@ LuaInstance::register_classes (lua_State* L)
                .endClass ()
 
                .deriveClass <RouteTimeAxisView, RouteUI> ("RouteTimeAxisView")
-               .addCast<TimeAxisView> ("to_timeaxisview")
+               .addCast<StripableTimeAxisView> ("to_stripabletimeaxisview")
+               .addCast<TimeAxisView> ("to_timeaxisview") // deprecated
                .endClass ()
 
                // std::list<Selectable*>
@@ -717,6 +773,8 @@ LuaInstance::register_classes (lua_State* L)
 #endif
                .addFunction ("hide_track_in_display", &PublicEditor::hide_track_in_display)
                .addFunction ("show_track_in_display", &PublicEditor::show_track_in_display)
+               .addFunction ("set_visible_track_count", &PublicEditor::set_visible_track_count)
+               .addFunction ("fit_selection", &PublicEditor::fit_selection)
 
                .addFunction ("regionview_from_region", &PublicEditor::regionview_from_region)
                .addFunction ("set_stationary_playhead", &PublicEditor::set_stationary_playhead)
@@ -764,7 +822,7 @@ LuaInstance::register_classes (lua_State* L)
                .addFunction ("drags", &PublicEditor::drags)
 #endif
 
-               .addFunction ("get_route_view_by_route_id", &PublicEditor::get_route_view_by_route_id)
+               .addFunction ("get_stripable_time_axis_by_id", &PublicEditor::get_stripable_time_axis_by_id)
                .addFunction ("get_track_views", &PublicEditor::get_track_views)
                .addFunction ("rtav_from_route", &PublicEditor::rtav_from_route)
                .addFunction ("axis_views_from_routes", &PublicEditor::axis_views_from_routes)
@@ -793,6 +851,7 @@ LuaInstance::register_classes (lua_State* L)
 #endif
 
                .addFunction ("access_action", &PublicEditor::access_action)
+               .addFunction ("set_toggleaction", &PublicEditor::set_toggleaction)
                .endClass ()
 
                /* ArdourUI enums */
@@ -817,7 +876,14 @@ LuaInstance::register_classes (lua_State* L)
                .addConst ("Add", Selection::Operation(Selection::Add))
                .endNamespace ()
 
-               .endNamespace (); // end ArdourUI
+               .endNamespace () // end ArdourUI
+
+               .beginNamespace ("ARDOUR")
+               .beginClass <ARDOUR::SystemExec> ("SystemExec")
+               .addConstructor <void (*) (std::string, std::string)> ()
+               .addFunction ("start", &ARDOUR::SystemExec::start)
+               .endClass ()
+               .endNamespace (); // end ARDOUR
 
        // Editing Symbols
 
@@ -854,7 +920,9 @@ using namespace ARDOUR_UI_UTILS;
 using namespace PBD;
 using namespace std;
 
-static void _lua_print (std::string s) {
+void
+LuaInstance::_lua_print (std::string s)
+{
 #ifndef NDEBUG
        std::cout << "LuaInstance: " << s << "\n";
 #endif
@@ -905,6 +973,7 @@ LuaInstance::~LuaInstance ()
 void
 LuaInstance::init ()
 {
+       lua.sandbox (false);
        lua.do_command (
                        "function ScriptManager ()"
                        "  local self = { scripts = {}, instances = {}, icons = {} }"
@@ -922,7 +991,7 @@ LuaInstance::init ()
                        "   assert(type(f) == 'function', 'Factory is a not a function')"
                        "   assert(type(a) == 'table' or type(a) == 'nil', 'Given argument is invalid')"
                        "   self.scripts[i] = { ['n'] = n, ['s'] = s, ['f'] = f, ['a'] = a, ['c'] = c }"
-                       "   local env = _ENV;  env.f = nil env.debug = nil os.exit = nil require = nil dofile = nil loadfile = nil package = nil"
+                       "   local env = _ENV; env.f = nil env.io = nil"
                        "   self.instances[i] = load (string.dump(f, true), nil, nil, env)(a)"
                        "   if type(c) == 'function' then"
                        "     self.icons[i] = load (string.dump(c, true), nil, nil, env)(a)"
@@ -977,7 +1046,6 @@ LuaInstance::init ()
                        ""
                        "  local function serialize (name, value)"
                        "   local rv = name .. ' = '"
-                       "   collectgarbage()"
                        "   if type(value) == \"number\" or type(value) == \"string\" or type(value) == \"nil\" then"
                        "    return rv .. basic_serialize(value) .. ' '"
                        "   elseif type(value) == \"table\" then"
@@ -985,7 +1053,6 @@ LuaInstance::init ()
                        "    for k,v in pairs(value) do"
                        "     local fieldname = string.format(\"%s[%s]\", name, basic_serialize(k))"
                        "     rv = rv .. serialize(fieldname, v) .. ' '"
-                       "     collectgarbage()" // string concatenation allocates a new string
                        "    end"
                        "    return rv;"
                        "   elseif type(value) == \"function\" then"
@@ -1025,7 +1092,6 @@ LuaInstance::init ()
                        " manager = ScriptManager ()"
                        " ScriptManager = nil"
                        );
-
        lua_State* L = lua.getState();
 
        try {
@@ -1050,6 +1116,7 @@ LuaInstance::init ()
        }
 
        register_classes (L);
+       register_hooks (L);
 
        luabridge::push <PublicEditor *> (L, &PublicEditor::instance());
        lua_setglobal (L, "Editor");
@@ -1069,6 +1136,7 @@ void LuaInstance::set_session (Session* s)
                i->second->set_session (s);
        }
        point_one_second_connection = Timers::rapid_connect (sigc::mem_fun(*this, & LuaInstance::every_point_one_seconds));
+       SetSession (); /* EMIT SIGNAL */
 }
 
 void
@@ -1098,7 +1166,6 @@ LuaInstance::every_point_one_seconds ()
 int
 LuaInstance::set_state (const XMLNode& node)
 {
-       LocaleGuard lg;
        XMLNode* child;
 
        if ((child = find_named_node (node, "ActionScript"))) {
@@ -1190,13 +1257,19 @@ LuaInstance::interactive_add (LuaScriptInfo::ScriptType type, int id)
        LuaScriptParamList lsp = LuaScriptParams::script_params (spi, param_function);
 
        ScriptParameterDialog spd (_("Set Script Parameters"), spi, reg, lsp);
-       switch (spd.run ()) {
-               case Gtk::RESPONSE_ACCEPT:
-                       break;
-               default:
-                       return false;
+
+       if (!spd.need_interation ()) {
+               switch (spd.run ()) {
+                       case Gtk::RESPONSE_ACCEPT:
+                               break;
+                       default:
+                               return false;
+               }
        }
 
+       LuaScriptParamPtr lspp (new LuaScriptParam("x-script-origin", "", spi->path, false));
+       lsp.push_back (lspp);
+
        switch (type) {
                case LuaScriptInfo::EditorAction:
                        return set_lua_action (id, spd.name(), script, lsp);
@@ -1225,7 +1298,6 @@ LuaInstance::interactive_add (LuaScriptInfo::ScriptType type, int id)
 XMLNode&
 LuaInstance::get_action_state ()
 {
-       LocaleGuard lg;
        std::string saved;
        {
                luabridge::LuaRef savedstate ((*_lua_save)());
@@ -1238,7 +1310,7 @@ LuaInstance::get_action_state ()
        g_free (b64);
 
        XMLNode* script_node = new XMLNode (X_("ActionScript"));
-       script_node->add_property (X_("lua"), LUA_VERSION);
+       script_node->set_property (X_("lua"), LUA_VERSION);
        script_node->add_content (b64s);
 
        return *script_node;
@@ -1418,6 +1490,7 @@ LuaInstance::register_lua_slot (const std::string& name, const std::string& scri
        try {
                LuaState l;
                l.Print.connect (&_lua_print);
+               l.sandbox (true);
                lua_State* L = l.getState();
                register_hooks (L);
                l.do_command ("function ardour () end");
@@ -1597,17 +1670,25 @@ LuaCallback::get_state (void)
                luabridge::LuaRef savedstate ((*_lua_save)());
                saved = savedstate.cast<std::string>();
        }
-       lua.collect_garbage ();
+
+       lua.collect_garbage (); // this may be expensive:
+       /* Editor::instant_save() calls Editor::get_state() which
+        * calls LuaInstance::get_hook_state() which in turn calls
+        * this LuaCallback::get_state() for every registered hook.
+        *
+        * serialize in _lua_save() allocates many small strings
+        * on the lua-stack, collecting them all may take a ms.
+        */
 
        gchar* b64 = g_base64_encode ((const guchar*)saved.c_str (), saved.size ());
        std::string b64s (b64);
        g_free (b64);
 
        XMLNode* script_node = new XMLNode (X_("LuaCallback"));
-       script_node->add_property (X_("lua"), LUA_VERSION);
-       script_node->add_property (X_("id"), _id.to_s ());
-       script_node->add_property (X_("name"), _name);
-       script_node->add_property (X_("signals"), _signals.to_string ());
+       script_node->set_property (X_("lua"), LUA_VERSION);
+       script_node->set_property (X_("id"), _id.to_s ());
+       script_node->set_property (X_("name"), _name);
+       script_node->set_property (X_("signals"), _signals.to_string ());
        script_node->add_content (b64s);
        return *script_node;
 }
@@ -1615,7 +1696,8 @@ LuaCallback::get_state (void)
 void
 LuaCallback::init (void)
 {
-       lua.Print.connect (&_lua_print);
+       lua.Print.connect (&LuaInstance::_lua_print);
+       lua.sandbox (false);
 
        lua.do_command (
                        "function ScriptManager ()"
@@ -1627,7 +1709,7 @@ LuaCallback::init (void)
                        "   assert(type(f) == 'function', 'Factory is a not a function')"
                        "   assert(type(a) == 'table' or type(a) == 'nil', 'Given argument is invalid')"
                        "   self.script = { ['n'] = n, ['s'] = s, ['f'] = f, ['a'] = a }"
-                       "   local env = _ENV;  env.f = nil env.debug = nil os.exit = nil require = nil dofile = nil loadfile = nil package = nil"
+                       "   local env = _ENV; env.f = nil env.io = nil"
                        "   self.instance = load (string.dump(f, true), nil, nil, env)(a)"
                        "  end"
                        ""
@@ -1673,7 +1755,6 @@ LuaCallback::init (void)
                        ""
                        "  local function serialize (name, value)"
                        "   local rv = name .. ' = '"
-                       "   collectgarbage()"
                        "   if type(value) == \"number\" or type(value) == \"string\" or type(value) == \"nil\" then"
                        "    return rv .. basic_serialize(value) .. ' '"
                        "   elseif type(value) == \"table\" then"
@@ -1681,7 +1762,6 @@ LuaCallback::init (void)
                        "    for k,v in pairs(value) do"
                        "     local fieldname = string.format(\"%s[%s]\", name, basic_serialize(k))"
                        "     rv = rv .. serialize(fieldname, v) .. ' '"
-                       "     collectgarbage()" // string concatenation allocates a new string
                        "    end"
                        "    return rv;"
                        "   elseif type(value) == \"function\" then"
@@ -1734,6 +1814,7 @@ LuaCallback::init (void)
        }
 
        LuaInstance::register_classes (L);
+       LuaInstance::register_hooks (L);
 
        luabridge::push <PublicEditor *> (L, &PublicEditor::instance());
        lua_setglobal (L, "Editor");