2 * Copyright (C) 2016-2018 Robin Gareus <robin@gareus.org>
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License along
15 * with this program; if not, write to the Free Software Foundation, Inc.,
16 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 #include <gtkmm/box.h>
20 #include <gtkmm/frame.h>
21 #include <gtkmm/messagedialog.h>
23 #include "gtkmm2ext/gui_thread.h"
24 #include "gtkmm2ext/utils.h"
26 #include "ardour/session.h"
28 #include "LuaBridge/LuaBridge.h"
30 #include "ardour_ui.h"
31 #include "lua_script_manager.h"
32 #include "luawindow.h"
33 #include "script_selector.h"
38 using namespace ARDOUR;
40 LuaScriptManager::LuaScriptManager ()
41 : ArdourWindow (_("Script Manager"))
42 , _a_set_button (_("Add/Set"))
43 , _a_del_button (_("Remove"))
44 , _a_edit_button (_("Edit"))
45 , _a_call_button (_("Call"))
46 , _c_add_button (_("New Hook"))
47 , _c_del_button (_("Remove"))
48 , _s_add_button (_("Load"))
49 , _s_del_button (_("Remove"))
51 /* action script page */
52 _a_store = ListStore::create (_a_model);
53 _a_view.set_model (_a_store);
54 _a_view.append_column (_("Action"), _a_model.action);
55 _a_view.append_column (_("Name"), _a_model.name);
56 _a_view.get_column(0)->set_resizable (true);
57 _a_view.get_column(0)->set_expand (true);
58 _a_view.get_column(1)->set_resizable (true);
59 _a_view.get_column(1)->set_expand (true);
64 Gtk::HBox* edit_box = manage (new Gtk::HBox);
65 edit_box->set_spacing(3);
67 edit_box->pack_start (_a_set_button, true, true);
68 edit_box->pack_start (_a_del_button, true, true);
69 edit_box->pack_start (_a_edit_button, true, true);
70 edit_box->pack_start (_a_call_button, true, true);
72 _a_set_button.signal_clicked().connect (sigc::mem_fun(*this, &LuaScriptManager::set_action_btn_clicked));
73 _a_del_button.signal_clicked().connect (sigc::mem_fun(*this, &LuaScriptManager::del_action_btn_clicked));
74 _a_edit_button.signal_clicked().connect (sigc::mem_fun(*this, &LuaScriptManager::edit_action_btn_clicked));
75 _a_call_button.signal_clicked().connect (sigc::mem_fun(*this, &LuaScriptManager::call_action_btn_clicked));
76 _a_view.get_selection()->signal_changed().connect (sigc::mem_fun (*this, &LuaScriptManager::action_selection_changed));
78 LuaInstance::instance()->ActionChanged.connect (sigc::mem_fun (*this, &LuaScriptManager::set_action_script_name));
79 LuaInstance::instance()->SlotChanged.connect (sigc::mem_fun (*this, &LuaScriptManager::set_callback_script_name));
81 f = manage (new Frame (_("Description")));
82 doc = manage (new Label (
83 _("Action Scripts are user initiated actions (menu, shortcuts, toolbar-button) for batch processing or customized tasks.")
85 doc->set_padding (5, 5);
89 Gtk::ScrolledWindow *scroller = manage (new Gtk::ScrolledWindow());
90 scroller->set_policy (Gtk::POLICY_NEVER, Gtk::POLICY_AUTOMATIC);
91 scroller->add (_a_view);
92 Gtk::VBox *vbox = manage (new VBox());
93 vbox->pack_start (*scroller, true, true);
94 vbox->pack_end (*edit_box, false, false);
95 vbox->pack_end (*f, false, false);
98 pages.pages ().push_back (Notebook_Helpers::TabElem (*vbox, "Action Scripts"));
100 /* action hooks page */
102 _c_store = ListStore::create (_c_model);
103 _c_view.set_model (_c_store);
104 _c_view.append_column (_("Name"), _c_model.name);
105 _c_view.append_column (_("Signal(s)"), _c_model.signals);
106 _c_view.get_column(0)->set_resizable (true);
107 _c_view.get_column(0)->set_expand (true);
108 _c_view.get_column(1)->set_resizable (true);
109 _c_view.get_column(1)->set_expand (true);
110 Gtk::CellRendererText* r = dynamic_cast<Gtk::CellRendererText*>(_c_view.get_column_cell_renderer (1));
111 r->property_ellipsize () = Pango::ELLIPSIZE_MIDDLE;
113 edit_box = manage (new Gtk::HBox);
114 edit_box->set_spacing(3);
115 edit_box->pack_start (_c_add_button, true, true);
116 edit_box->pack_start (_c_del_button, true, true);
118 _c_add_button.signal_clicked().connect (sigc::mem_fun(*this, &LuaScriptManager::add_callback_btn_clicked));
119 _c_del_button.signal_clicked().connect (sigc::mem_fun(*this, &LuaScriptManager::del_callback_btn_clicked));
120 _c_view.get_selection()->signal_changed().connect (sigc::mem_fun (*this, &LuaScriptManager::callback_selection_changed));
122 f = manage (new Frame (_("Description")));
123 doc = manage (new Label (
124 _("Lua action hooks are event-triggered callbacks for the Editor/Mixer GUI. Once a script is registered it is automatically triggered by events to perform some task.")
126 doc->set_padding (5, 5);
127 doc->set_line_wrap();
130 scroller = manage (new Gtk::ScrolledWindow());
131 scroller->set_policy (Gtk::POLICY_NEVER, Gtk::POLICY_AUTOMATIC);
132 scroller->add (_c_view);
133 vbox = manage (new VBox());
134 vbox->pack_start (*scroller, true, true);
135 vbox->pack_end (*edit_box, false, false);
136 vbox->pack_end (*f, false, false);
139 pages.pages ().push_back (Notebook_Helpers::TabElem (*vbox, "Action Hooks"));
141 /* session script page */
143 _s_store = ListStore::create (_s_model);
144 _s_view.set_model (_s_store);
145 _s_view.append_column (_("Name"), _s_model.name);
146 _s_view.get_column(0)->set_resizable (true);
147 _s_view.get_column(0)->set_expand (true);
149 edit_box = manage (new Gtk::HBox);
150 edit_box->set_spacing(3);
151 edit_box->pack_start (_s_add_button, true, true);
152 edit_box->pack_start (_s_del_button, true, true);
154 _s_add_button.signal_clicked().connect (sigc::mem_fun(*this, &LuaScriptManager::add_sess_btn_clicked));
155 _s_del_button.signal_clicked().connect (sigc::mem_fun(*this, &LuaScriptManager::del_sess_btn_clicked));
156 _s_view.get_selection()->signal_changed().connect (sigc::mem_fun (*this, &LuaScriptManager::session_script_selection_changed));
158 f = manage (new Frame (_("Description")));
159 doc = manage (new Label (
160 _("Lua session scripts are loaded into processing engine and run in realtime. They are called periodically at the start of every audio cycle in the realtime process context before any processing takes place.")
162 doc->set_padding (5, 5);
163 doc->set_line_wrap();
166 scroller = manage (new Gtk::ScrolledWindow());
167 scroller->set_policy (Gtk::POLICY_NEVER, Gtk::POLICY_AUTOMATIC);
168 scroller->add (_s_view);
169 vbox = manage (new VBox());
170 vbox->pack_start (*scroller, true, true);
171 vbox->pack_end (*edit_box, false, false);
172 vbox->pack_end (*f, false, false);
175 pages.pages ().push_back (Notebook_Helpers::TabElem (*vbox, "Session Scripts"));
184 setup_session_scripts ();
186 action_selection_changed ();
187 callback_selection_changed ();
188 session_script_selection_changed ();
192 LuaScriptManager::set_session (ARDOUR::Session *s)
194 ArdourWindow::set_session (s);
195 setup_session_scripts ();
200 _session->LuaScriptsChanged.connect (_session_script_connection, invalidator (*this), boost::bind (&LuaScriptManager::setup_session_scripts, this), gui_context());
201 setup_session_scripts ();
205 LuaScriptManager::session_going_away ()
207 ArdourWindow::session_going_away ();
208 _session_script_connection.disconnect ();
213 LuaScriptManager::setup_actions ()
215 LuaInstance *li = LuaInstance::instance();
216 for (int i = 0; i < MAX_LUA_ACTION_SCRIPTS; ++i) {
218 TreeModel::Row r = *_a_store->append ();
220 r[_a_model.action] = string_compose (_("Action %1"), i + 1);
221 if (li->lua_action_name (i, name)) {
222 r[_a_model.name] = name;
223 r[_a_model.enabled] = true;
225 r[_a_model.name] = _("Unset");
226 r[_a_model.enabled] = false;
232 LuaScriptManager::action_selection_changed ()
234 TreeModel::Row row = *(_a_view.get_selection()->get_selected());
236 _a_set_button.set_sensitive (true);
239 _a_set_button.set_sensitive (false);
242 if (row && row[_a_model.enabled]) {
243 _a_del_button.set_sensitive (true);
244 _a_edit_button.set_sensitive (true);
245 _a_call_button.set_sensitive (true);
247 _a_del_button.set_sensitive (false);
248 _a_edit_button.set_sensitive (false);
249 _a_call_button.set_sensitive (false);
254 LuaScriptManager::set_action_btn_clicked ()
256 TreeModel::Row row = *(_a_view.get_selection()->get_selected());
258 LuaInstance *li = LuaInstance::instance();
259 li->interactive_add (LuaScriptInfo::EditorAction, row[_a_model.id]);
263 LuaScriptManager::del_action_btn_clicked ()
265 TreeModel::Row row = *(_a_view.get_selection()->get_selected());
267 LuaInstance *li = LuaInstance::instance();
268 if (!li->remove_lua_action (row[_a_model.id])) {
274 LuaScriptManager::call_action_btn_clicked ()
276 TreeModel::Row row = *(_a_view.get_selection()->get_selected());
277 assert (row && row[_a_model.enabled]);
278 LuaInstance *li = LuaInstance::instance();
279 li->call_action (row[_a_model.id]);
283 LuaScriptManager::edit_action_btn_clicked ()
285 TreeModel::Row row = *(_a_view.get_selection()->get_selected());
287 int id = row[_a_model.id];
288 LuaInstance *li = LuaInstance::instance();
289 std::string name, script;
290 LuaScriptParamList args;
291 if (!li->lua_action (id, name, script, args)) {
294 LuaWindow::instance()->edit_script (name, script);
298 LuaScriptManager::set_action_script_name (int i, const std::string& name)
300 typedef Gtk::TreeModel::Children type_children;
301 type_children children = _a_store->children();
302 for(type_children::iterator iter = children.begin(); iter != children.end(); ++iter) {
303 Gtk::TreeModel::Row row = *iter;
304 if (row[_a_model.id] == i) {
306 row[_a_model.enabled] = false;
307 row[_a_model.name] = _("Unset");
309 row[_a_model.enabled] = true;
310 row[_a_model.name] = name;
315 action_selection_changed ();
320 LuaScriptManager::setup_callbacks ()
322 LuaInstance *li = LuaInstance::instance();
323 std::vector<PBD::ID> ids = li->lua_slots();
324 for (std::vector<PBD::ID>::const_iterator i = ids.begin(); i != ids.end(); ++i) {
328 LuaScriptParamList lsp;
329 if (li->lua_slot (*i, name, script, ah, lsp)) {
330 set_callback_script_name (*i, name, ah);
336 LuaScriptManager::callback_selection_changed ()
338 TreeModel::Row row = *(_c_view.get_selection()->get_selected());
340 _c_del_button.set_sensitive (true);
342 _c_del_button.set_sensitive (false);
347 LuaScriptManager::add_callback_btn_clicked ()
349 LuaInstance *li = LuaInstance::instance();
350 li->interactive_add (LuaScriptInfo::EditorHook, -1);
354 LuaScriptManager::del_callback_btn_clicked ()
356 TreeModel::Row row = *(_c_view.get_selection()->get_selected());
358 LuaInstance *li = LuaInstance::instance();
359 if (!li->unregister_lua_slot (row[_c_model.id])) {
365 LuaScriptManager::set_callback_script_name (PBD::ID id, const std::string& name, const ActionHook& ah)
368 typedef Gtk::TreeModel::Children type_children;
369 type_children children = _c_store->children();
370 for(type_children::iterator iter = children.begin(); iter != children.end(); ++iter) {
371 Gtk::TreeModel::Row row = *iter;
372 PBD::ID i = row[_c_model.id];
374 _c_store->erase (iter);
379 TreeModel::Row r = *_c_store->append ();
381 r[_c_model.name] = name;
383 for (uint32_t i = 0; i < LuaSignal::LAST_SIGNAL; ++i) {
385 if (!sig.empty()) sig += ", ";
386 sig += enum2str (LuaSignal::LuaSignal (i));
389 r[_c_model.signals] = sig;
391 callback_selection_changed ();
396 LuaScriptManager::setup_session_scripts ()
402 std::vector<std::string> reg = _session->registered_lua_functions ();
403 for (std::vector<string>::const_iterator i = reg.begin(); i != reg.end(); ++i) {
404 TreeModel::Row r = *_s_store->append ();
405 r[_s_model.name] = *i;
407 session_script_selection_changed ();
411 LuaScriptManager::session_script_selection_changed ()
414 _s_del_button.set_sensitive (false);
415 _s_add_button.set_sensitive (false);
418 TreeModel::Row row = *(_s_view.get_selection()->get_selected());
420 _s_del_button.set_sensitive (true);
422 _s_del_button.set_sensitive (false);
424 _s_add_button.set_sensitive (true);
428 LuaScriptManager::add_sess_btn_clicked ()
433 LuaInstance *li = LuaInstance::instance();
434 li->interactive_add (LuaScriptInfo::Session, -1);
438 LuaScriptManager::del_sess_btn_clicked ()
441 TreeModel::Row row = *(_s_view.get_selection()->get_selected());
442 const std::string& name = row[_s_model.name];
444 _session->unregister_lua_function (name);
445 } catch (luabridge::LuaException const& e) {
446 string msg = string_compose (_("Session script '%1' removal failed: %2"), name, e.what ());
447 MessageDialog am (msg);