Remove LocaleGuards from LuaInstance state methods
[ardour.git] / gtk2_ardour / luainstance.cc
index 32ce2341ee91c79f89d520ab75acac060691e64f..d08967a581e090ffadaf5df7c94132661095a7d8 100644 (file)
@@ -21,7 +21,6 @@
 #include <pango/pangocairo.h>
 
 #include "gtkmm2ext/gui_thread.h"
-#include "canvas/colors.h"
 
 #include "ardour/audioengine.h"
 #include "ardour/diskstream.h"
 #include "ardour_ui.h"
 #include "public_editor.h"
 #include "region_selection.h"
+#include "luadialog.h"
 #include "luainstance.h"
 #include "luasignal.h"
 #include "marker.h"
+#include "region_view.h"
 #include "processor_box.h"
 #include "time_axis_view.h"
+#include "time_axis_view_item.h"
 #include "selection.h"
 #include "script_selector.h"
 #include "timers.h"
@@ -309,34 +311,15 @@ class PangoLayout {
                        pango_cairo_show_layout (c->cobj (), _layout->gobj());
                }
 
+               void layout_cairo_path (Cairo::Context* c) {
+                       pango_cairo_update_layout (c->cobj (), _layout->gobj());
+                       pango_cairo_layout_path (c->cobj (), _layout->gobj());
+               }
+
        private:
                Glib::RefPtr<Pango::Layout> _layout;
 };
 
-/** expand RGBA color to parameters
- *
- * convert a Canvas::Color (uint32_t 0xRRGGBBAA) into
- * double RGBA values which can be passed as parameters to
- * Cairo::Context::set_source_rgba
- *
- * @returns r, g, b, a
- */
-static int color_to_rgba (lua_State *L)
-{
-       int top = lua_gettop (L);
-       if (top < 1) {
-               return luaL_argerror (L, 1, "invalid number of arguments, color_to_rgba (uint32_t)");
-       }
-       uint32_t color = luabridge::Stack<uint32_t>::get (L, 1);
-       double r, g, b, a;
-       ArdourCanvas::color_to_rgba (color, r, g, b, a);
-       luabridge::Stack <double>::push (L, r);
-       luabridge::Stack <double>::push (L, g);
-       luabridge::Stack <double>::push (L, b);
-       luabridge::Stack <double>::push (L, a);
-       return 4;
-}
-
 }; // namespace
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -516,6 +499,7 @@ LuaInstance::bind_cairo (lua_State* L)
                .addFunction ("get_text", &LuaCairo::PangoLayout::get_text)
                .addFunction ("set_text", &LuaCairo::PangoLayout::set_text)
                .addFunction ("show_in_cairo_context", &LuaCairo::PangoLayout::show_in_cairo_context)
+               .addFunction ("layout_cairo_path", &LuaCairo::PangoLayout::layout_cairo_path)
                .addFunction ("set_markup", &LuaCairo::PangoLayout::set_markup)
                .addFunction ("set_width", &LuaCairo::PangoLayout::set_width)
                .addFunction ("set_ellipsize", &LuaCairo::PangoLayout::set_ellipsize)
@@ -540,11 +524,6 @@ LuaInstance::bind_cairo (lua_State* L)
                .addConst ("WordChar", Pango::WRAP_WORD_CHAR)
                .endNamespace ()
 
-
-               .endNamespace ()
-
-               .beginNamespace ("LuaCairo")
-               .addCFunction ("color_to_rgba", &LuaCairo::color_to_rgba)
                .endNamespace ();
 
 /* Lua/cairo bindings operate on Cairo::Context, there is no Cairo::RefPtr wrapper [yet].
@@ -570,6 +549,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)
 {
@@ -579,6 +603,7 @@ LuaInstance::register_classes (lua_State* L)
        LuaBindings::osc (L);
 
        bind_cairo (L);
+       bind_dialog (L);
        register_hooks (L);
 
        luabridge::getGlobalNamespace (L)
@@ -597,17 +622,38 @@ LuaInstance::register_classes (lua_State* L)
                .addFunction ("_type", &ArdourMarker::type)
                .endClass ()
 
-#if 0
                .beginClass <AxisView> ("AxisView")
                .endClass ()
+
                .deriveClass <TimeAxisView, AxisView> ("TimeAxisView")
                .endClass ()
-               .deriveClass <RouteTimeAxisView, TimeAxisView> ("RouteTimeAxisView")
+
+               .beginClass <Selectable> ("Selectable")
+               .endClass ()
+
+               .deriveClass <TimeAxisViewItem, Selectable> ("TimeAxisViewItem")
+               .endClass ()
+
+               .deriveClass <RegionView, TimeAxisViewItem> ("RegionView")
+               .endClass ()
+
+               .deriveClass <RouteUI, Selectable> ("RouteUI")
                .endClass ()
-#endif
+
+               .deriveClass <RouteTimeAxisView, RouteUI> ("RouteTimeAxisView")
+               .addCast<TimeAxisView> ("to_timeaxisview")
+               .endClass ()
+
+               // std::list<Selectable*>
+               .beginStdCPtrList <Selectable> ("SelectionList")
+               .endClass ()
+
+               // std::list<TimeAxisView*>
+               .beginStdCPtrList <TimeAxisView> ("TrackViewStdList")
+               .endClass ()
+
 
                .beginClass <RegionSelection> ("RegionSelection")
-               .addFunction ("clear_all", &RegionSelection::clear_all)
                .addFunction ("start", &RegionSelection::start)
                .addFunction ("end_frame", &RegionSelection::end_frame)
                .addFunction ("n_midi_regions", &RegionSelection::n_midi_regions)
@@ -623,8 +669,9 @@ LuaInstance::register_classes (lua_State* L)
                .deriveClass <MarkerSelection, std::list<ArdourMarker*> > ("MarkerSelection")
                .endClass ()
 
-               .beginClass <TrackViewList> ("TrackViewList")
-               .addFunction ("routelist", &TrackViewList::routelist) // XXX check windows binding (libardour)
+               .deriveClass <TrackViewList, std::list<TimeAxisView*> > ("TrackViewList")
+               .addFunction ("contains", &TrackViewList::contains)
+               .addFunction ("routelist", &TrackViewList::routelist)
                .endClass ()
 
                .deriveClass <TrackSelection, TrackViewList> ("TrackSelection")
@@ -670,6 +717,8 @@ LuaInstance::register_classes (lua_State* L)
                .addFunction ("get_cut_buffer", &PublicEditor::get_cut_buffer)
                .addRefFunction ("get_selection_extents", &PublicEditor::get_selection_extents)
 
+               .addFunction ("set_selection", &PublicEditor::set_selection)
+
                .addFunction ("play_selection", &PublicEditor::play_selection)
                .addFunction ("play_with_preroll", &PublicEditor::play_with_preroll)
                .addFunction ("maybe_locate_with_edit_preroll", &PublicEditor::maybe_locate_with_edit_preroll)
@@ -700,21 +749,23 @@ LuaInstance::register_classes (lua_State* L)
                .addFunction ("get_current_zoom", &PublicEditor::get_current_zoom)
                .addFunction ("reset_zoom", &PublicEditor::reset_zoom)
 
-#if 0 // These need TimeAxisView* which isn't exposed, yet
-               .addFunction ("playlist_selector", &PublicEditor::playlist_selector)
                .addFunction ("clear_playlist", &PublicEditor::clear_playlist)
                .addFunction ("new_playlists", &PublicEditor::new_playlists)
                .addFunction ("copy_playlists", &PublicEditor::copy_playlists)
                .addFunction ("clear_playlists", &PublicEditor::clear_playlists)
-#endif
 
                .addFunction ("select_all_tracks", &PublicEditor::select_all_tracks)
                .addFunction ("deselect_all", &PublicEditor::deselect_all)
-#if 0
+
+#if 0 // TimeAxisView&  can't be bound (pure virtual fn)
                .addFunction ("set_selected_track", &PublicEditor::set_selected_track)
                .addFunction ("set_selected_mixer_strip", &PublicEditor::set_selected_mixer_strip)
-               .addFunction ("hide_track_in_display", &PublicEditor::hide_track_in_display)
+               .addFunction ("ensure_time_axis_view_is_visible", &PublicEditor::ensure_time_axis_view_is_visible)
 #endif
+               .addFunction ("hide_track_in_display", &PublicEditor::hide_track_in_display)
+               .addFunction ("show_track_in_display", &PublicEditor::show_track_in_display)
+
+               .addFunction ("regionview_from_region", &PublicEditor::regionview_from_region)
                .addFunction ("set_stationary_playhead", &PublicEditor::set_stationary_playhead)
                .addFunction ("stationary_playhead", &PublicEditor::stationary_playhead)
                .addFunction ("set_follow_playhead", &PublicEditor::set_follow_playhead)
@@ -725,7 +776,6 @@ LuaInstance::register_classes (lua_State* L)
                .addFunction ("current_page_samples", &PublicEditor::current_page_samples)
                .addFunction ("visible_canvas_height", &PublicEditor::visible_canvas_height)
                .addFunction ("temporal_zoom_step", &PublicEditor::temporal_zoom_step)
-               //.addFunction ("ensure_time_axis_view_is_visible", &PublicEditor::ensure_time_axis_view_is_visible)
                .addFunction ("override_visible_track_count", &PublicEditor::override_visible_track_count)
 
                .addFunction ("scroll_tracks_down_line", &PublicEditor::scroll_tracks_down_line)
@@ -757,15 +807,15 @@ LuaInstance::register_classes (lua_State* L)
                .addFunction ("set_video_timeline_height", &PublicEditor::set_video_timeline_height)
 
 #if 0
-               .addFunction ("get_route_view_by_route_id", &PublicEditor::get_route_view_by_route_id)
                .addFunction ("get_equivalent_regions", &PublicEditor::get_equivalent_regions)
-
-               .addFunction ("axis_view_from_route", &PublicEditor::axis_view_from_route)
-               .addFunction ("axis_views_from_routes", &PublicEditor::axis_views_from_routes)
-               .addFunction ("get_track_views", &PublicEditor::get_track_views)
                .addFunction ("drags", &PublicEditor::drags)
 #endif
 
+               .addFunction ("get_route_view_by_route_id", &PublicEditor::get_route_view_by_route_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)
+
                .addFunction ("center_screen", &PublicEditor::center_screen)
 
                .addFunction ("get_smart_mode", &PublicEditor::get_smart_mode)
@@ -807,6 +857,13 @@ LuaInstance::register_classes (lua_State* L)
                .addConst ("PunchOut", ArdourMarker::Type(ArdourMarker::PunchOut))
                .endNamespace ()
 
+               .beginNamespace ("SelectionOp")
+               .addConst ("Toggle", Selection::Operation(Selection::Toggle))
+               .addConst ("Set", Selection::Operation(Selection::Set))
+               .addConst ("Extend", Selection::Operation(Selection::Extend))
+               .addConst ("Add", Selection::Operation(Selection::Add))
+               .endNamespace ()
+
                .endNamespace (); // end ArdourUI
 
        // Editing Symbols
@@ -967,7 +1024,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"
@@ -975,7 +1031,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"
@@ -1088,7 +1143,6 @@ LuaInstance::every_point_one_seconds ()
 int
 LuaInstance::set_state (const XMLNode& node)
 {
-       LocaleGuard lg;
        XMLNode* child;
 
        if ((child = find_named_node (node, "ActionScript"))) {
@@ -1131,16 +1185,25 @@ bool
 LuaInstance::interactive_add (LuaScriptInfo::ScriptType type, int id)
 {
        std::string title;
+       std::string param_function = "action_params";
        std::vector<std::string> reg;
 
        switch (type) {
                case LuaScriptInfo::EditorAction:
                        reg = lua_action_names ();
-                       title = "Add Lua Action";
+                       title = _("Add Lua Action");
                        break;
                case LuaScriptInfo::EditorHook:
                        reg = lua_slot_names ();
-                       title = "Add Lua Callback Hook";
+                       title = _("Add Lua Callback Hook");
+                       break;
+               case LuaScriptInfo::Session:
+                       if (!_session) {
+                               return false;
+                       }
+                       reg = _session->registered_lua_functions ();
+                       title = _("Add Lua Session Script");
+                       param_function = "sess_params";
                        break;
                default:
                        return false;
@@ -1168,16 +1231,22 @@ LuaInstance::interactive_add (LuaScriptInfo::ScriptType type, int id)
                return false;
        }
 
-       LuaScriptParamList lsp = LuaScriptParams::script_params (spi, "action_params");
+       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);
@@ -1185,6 +1254,18 @@ LuaInstance::interactive_add (LuaScriptInfo::ScriptType type, int id)
                case LuaScriptInfo::EditorHook:
                        return register_lua_slot (spd.name(), script, lsp);
                        break;
+               case LuaScriptInfo::Session:
+                       try {
+                               _session->register_lua_function (spd.name(), script, lsp);
+                       } catch (luabridge::LuaException const& e) {
+                               string msg = string_compose (_("Session script '%1' instantiation failed: %2"), spd.name(), e.what ());
+                               Gtk::MessageDialog am (msg);
+                               am.run ();
+                       } catch (SessionException e) {
+                               string msg = string_compose (_("Loading Session script '%1' failed: %2"), spd.name(), e.what ());
+                               Gtk::MessageDialog am (msg);
+                               am.run ();
+                       }
                default:
                        break;
        }
@@ -1194,7 +1275,6 @@ LuaInstance::interactive_add (LuaScriptInfo::ScriptType type, int id)
 XMLNode&
 LuaInstance::get_action_state ()
 {
-       LocaleGuard lg;
        std::string saved;
        {
                luabridge::LuaRef savedstate ((*_lua_save)());
@@ -1207,7 +1287,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;
@@ -1566,17 +1646,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;
 }
@@ -1642,7 +1730,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"
@@ -1650,7 +1737,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"