2 * Copyright (C) 2016 Robin Gareus <robin@gareus.org>
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version 2
7 * of the License, or (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
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 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 "lua_script_manager.h"
31 #include "luawindow.h"
32 #include "script_selector.h"
37 using namespace ARDOUR;
39 LuaScriptManager::LuaScriptManager ()
40 : ArdourWindow (_("Script Manager"))
41 , _a_set_button (_("Add/Set"))
42 , _a_del_button (_("Remove"))
43 , _a_edit_button (_("Edit"))
44 , _a_call_button (_("Call"))
45 , _c_add_button (_("New Hook"))
46 , _c_del_button (_("Remove"))
47 , _s_add_button (_("Load"))
48 , _s_del_button (_("Remove"))
50 /* action script page */
51 _a_store = ListStore::create (_a_model);
52 _a_view.set_model (_a_store);
53 _a_view.append_column (_("Action"), _a_model.action);
54 _a_view.append_column (_("Name"), _a_model.name);
55 _a_view.get_column(0)->set_resizable (true);
56 _a_view.get_column(0)->set_expand (true);
57 _a_view.get_column(1)->set_resizable (true);
58 _a_view.get_column(1)->set_expand (true);
63 Gtk::HBox* edit_box = manage (new Gtk::HBox);
64 edit_box->set_spacing(3);
66 edit_box->pack_start (_a_set_button, true, true);
67 edit_box->pack_start (_a_del_button, true, true);
68 edit_box->pack_start (_a_edit_button, true, true);
69 edit_box->pack_start (_a_call_button, true, true);
71 _a_set_button.signal_clicked().connect (sigc::mem_fun(*this, &LuaScriptManager::set_action_btn_clicked));
72 _a_del_button.signal_clicked().connect (sigc::mem_fun(*this, &LuaScriptManager::del_action_btn_clicked));
73 _a_edit_button.signal_clicked().connect (sigc::mem_fun(*this, &LuaScriptManager::edit_action_btn_clicked));
74 _a_call_button.signal_clicked().connect (sigc::mem_fun(*this, &LuaScriptManager::call_action_btn_clicked));
75 _a_view.get_selection()->signal_changed().connect (sigc::mem_fun (*this, &LuaScriptManager::action_selection_changed));
77 LuaInstance::instance()->ActionChanged.connect (sigc::mem_fun (*this, &LuaScriptManager::set_action_script_name));
78 LuaInstance::instance()->SlotChanged.connect (sigc::mem_fun (*this, &LuaScriptManager::set_callback_script_name));
80 f = manage (new Frame (_("Description")));
81 doc = manage (new Label (
82 _("Action Scripts are user initiated actions (menu, shortcuts, toolbar-button) for batch processing or customized tasks.")
84 doc->set_padding (5, 5);
88 Gtk::VBox *vbox = manage (new VBox());
89 vbox->pack_start (_a_view, false, false);
90 vbox->pack_end (*edit_box, false, false);
91 vbox->pack_end (*f, false, false);
94 pages.pages ().push_back (Notebook_Helpers::TabElem (*vbox, "Action Scripts"));
96 /* action hooks page */
98 _c_store = ListStore::create (_c_model);
99 _c_view.set_model (_c_store);
100 _c_view.append_column (_("Name"), _c_model.name);
101 _c_view.append_column (_("Signal(s)"), _c_model.signals);
102 _c_view.get_column(0)->set_resizable (true);
103 _c_view.get_column(0)->set_expand (true);
104 _c_view.get_column(1)->set_resizable (true);
105 _c_view.get_column(1)->set_expand (true);
106 Gtk::CellRendererText* r = dynamic_cast<Gtk::CellRendererText*>(_c_view.get_column_cell_renderer (1));
107 r->property_ellipsize () = Pango::ELLIPSIZE_MIDDLE;
109 edit_box = manage (new Gtk::HBox);
110 edit_box->set_spacing(3);
111 edit_box->pack_start (_c_add_button, true, true);
112 edit_box->pack_start (_c_del_button, true, true);
114 _c_add_button.signal_clicked().connect (sigc::mem_fun(*this, &LuaScriptManager::add_callback_btn_clicked));
115 _c_del_button.signal_clicked().connect (sigc::mem_fun(*this, &LuaScriptManager::del_callback_btn_clicked));
116 _c_view.get_selection()->signal_changed().connect (sigc::mem_fun (*this, &LuaScriptManager::callback_selection_changed));
118 f = manage (new Frame (_("Description")));
119 doc = manage (new Label (
120 _("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.")
122 doc->set_padding (5, 5);
123 doc->set_line_wrap();
126 vbox = manage (new VBox());
127 vbox->pack_start (_c_view, false, false);
128 vbox->pack_end (*edit_box, false, false);
129 vbox->pack_end (*f, false, false);
132 pages.pages ().push_back (Notebook_Helpers::TabElem (*vbox, "Action Hooks"));
134 /* session script page */
136 _s_store = ListStore::create (_s_model);
137 _s_view.set_model (_s_store);
138 _s_view.append_column (_("Name"), _s_model.name);
139 _s_view.get_column(0)->set_resizable (true);
140 _s_view.get_column(0)->set_expand (true);
142 edit_box = manage (new Gtk::HBox);
143 edit_box->set_spacing(3);
144 edit_box->pack_start (_s_add_button, true, true);
145 edit_box->pack_start (_s_del_button, true, true);
147 _s_add_button.signal_clicked().connect (sigc::mem_fun(*this, &LuaScriptManager::add_sess_btn_clicked));
148 _s_del_button.signal_clicked().connect (sigc::mem_fun(*this, &LuaScriptManager::del_sess_btn_clicked));
149 _s_view.get_selection()->signal_changed().connect (sigc::mem_fun (*this, &LuaScriptManager::session_script_selection_changed));
151 f = manage (new Frame (_("Description")));
152 doc = manage (new Label (
153 _("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.")
155 doc->set_padding (5, 5);
156 doc->set_line_wrap();
159 vbox = manage (new VBox());
160 vbox->pack_start (_s_view, false, false);
161 vbox->pack_end (*edit_box, false, false);
162 vbox->pack_end (*f, false, false);
165 pages.pages ().push_back (Notebook_Helpers::TabElem (*vbox, "Session Scripts"));
174 setup_session_scripts ();
176 action_selection_changed ();
177 callback_selection_changed ();
178 session_script_selection_changed ();
182 LuaScriptManager::set_session (ARDOUR::Session *s)
184 ArdourWindow::set_session (s);
185 setup_session_scripts ();
190 _session->LuaScriptsChanged.connect (_session_script_connection, invalidator (*this), boost::bind (&LuaScriptManager::setup_session_scripts, this), gui_context());
191 setup_session_scripts ();
195 LuaScriptManager::session_going_away ()
197 ArdourWindow::session_going_away ();
198 _session_script_connection.disconnect ();
203 LuaScriptManager::setup_actions ()
205 LuaInstance *li = LuaInstance::instance();
206 for (int i = 0; i < 9; ++i) {
208 TreeModel::Row r = *_a_store->append ();
210 r[_a_model.action] = string_compose (_("Action %1"), i + 1);
211 if (li->lua_action_name (i, name)) {
212 r[_a_model.name] = name;
213 r[_a_model.enabled] = true;
215 r[_a_model.name] = _("Unset");
216 r[_a_model.enabled] = false;
222 LuaScriptManager::action_selection_changed ()
224 TreeModel::Row row = *(_a_view.get_selection()->get_selected());
226 _a_set_button.set_sensitive (true);
229 _a_set_button.set_sensitive (false);
232 if (row && row[_a_model.enabled]) {
233 _a_del_button.set_sensitive (true);
234 _a_edit_button.set_sensitive (true);
235 _a_call_button.set_sensitive (true);
237 _a_del_button.set_sensitive (false);
238 _a_edit_button.set_sensitive (false);
239 _a_call_button.set_sensitive (false);
244 LuaScriptManager::set_action_btn_clicked ()
246 TreeModel::Row row = *(_a_view.get_selection()->get_selected());
248 LuaInstance *li = LuaInstance::instance();
249 li->interactive_add (LuaScriptInfo::EditorAction, row[_a_model.id]);
253 LuaScriptManager::del_action_btn_clicked ()
255 TreeModel::Row row = *(_a_view.get_selection()->get_selected());
257 LuaInstance *li = LuaInstance::instance();
258 if (!li->remove_lua_action (row[_a_model.id])) {
264 LuaScriptManager::call_action_btn_clicked ()
266 TreeModel::Row row = *(_a_view.get_selection()->get_selected());
267 assert (row && row[_a_model.enabled]);
268 LuaInstance *li = LuaInstance::instance();
269 li->call_action (row[_a_model.id]);
273 LuaScriptManager::edit_action_btn_clicked ()
275 TreeModel::Row row = *(_a_view.get_selection()->get_selected());
277 int id = row[_a_model.id];
278 LuaInstance *li = LuaInstance::instance();
279 std::string name, script;
280 LuaScriptParamList args;
281 if (!li->lua_action (id, name, script, args)) {
284 LuaWindow::instance()->edit_script (name, script);
288 LuaScriptManager::set_action_script_name (int i, const std::string& name)
290 typedef Gtk::TreeModel::Children type_children;
291 type_children children = _a_store->children();
292 for(type_children::iterator iter = children.begin(); iter != children.end(); ++iter) {
293 Gtk::TreeModel::Row row = *iter;
294 if (row[_a_model.id] == i) {
296 row[_a_model.enabled] = false;
297 row[_a_model.name] = _("Unset");
299 row[_a_model.enabled] = true;
300 row[_a_model.name] = name;
305 action_selection_changed ();
310 LuaScriptManager::setup_callbacks ()
312 LuaInstance *li = LuaInstance::instance();
313 std::vector<PBD::ID> ids = li->lua_slots();
314 for (std::vector<PBD::ID>::const_iterator i = ids.begin(); i != ids.end(); ++i) {
318 LuaScriptParamList lsp;
319 if (li->lua_slot (*i, name, script, ah, lsp)) {
320 set_callback_script_name (*i, name, ah);
326 LuaScriptManager::callback_selection_changed ()
328 TreeModel::Row row = *(_c_view.get_selection()->get_selected());
330 _c_del_button.set_sensitive (true);
332 _c_del_button.set_sensitive (false);
337 LuaScriptManager::add_callback_btn_clicked ()
339 LuaInstance *li = LuaInstance::instance();
340 li->interactive_add (LuaScriptInfo::EditorHook, -1);
344 LuaScriptManager::del_callback_btn_clicked ()
346 TreeModel::Row row = *(_c_view.get_selection()->get_selected());
348 LuaInstance *li = LuaInstance::instance();
349 if (!li->unregister_lua_slot (row[_c_model.id])) {
355 LuaScriptManager::set_callback_script_name (PBD::ID id, const std::string& name, const ActionHook& ah)
358 typedef Gtk::TreeModel::Children type_children;
359 type_children children = _c_store->children();
360 for(type_children::iterator iter = children.begin(); iter != children.end(); ++iter) {
361 Gtk::TreeModel::Row row = *iter;
362 PBD::ID i = row[_c_model.id];
364 _c_store->erase (iter);
369 TreeModel::Row r = *_c_store->append ();
371 r[_c_model.name] = name;
373 for (uint32_t i = 0; i < LuaSignal::LAST_SIGNAL; ++i) {
375 if (!sig.empty()) sig += ", ";
376 sig += enum2str (LuaSignal::LuaSignal (i));
379 r[_c_model.signals] = sig;
381 callback_selection_changed ();
386 LuaScriptManager::setup_session_scripts ()
392 std::vector<std::string> reg = _session->registered_lua_functions ();
393 for (std::vector<string>::const_iterator i = reg.begin(); i != reg.end(); ++i) {
394 TreeModel::Row r = *_s_store->append ();
395 r[_s_model.name] = *i;
397 session_script_selection_changed ();
401 LuaScriptManager::session_script_selection_changed ()
404 _s_del_button.set_sensitive (false);
405 _s_add_button.set_sensitive (false);
408 TreeModel::Row row = *(_s_view.get_selection()->get_selected());
410 _s_del_button.set_sensitive (true);
412 _s_del_button.set_sensitive (false);
414 _s_add_button.set_sensitive (true);
418 LuaScriptManager::add_sess_btn_clicked ()
423 LuaInstance *li = LuaInstance::instance();
424 li->interactive_add (LuaScriptInfo::Session, -1);
428 LuaScriptManager::del_sess_btn_clicked ()
431 TreeModel::Row row = *(_s_view.get_selection()->get_selected());
432 const std::string& name = row[_s_model.name];
434 _session->unregister_lua_function (name);
435 } catch (luabridge::LuaException const& e) {
436 string msg = string_compose (_("Session script '%1' removal failed: %2"), name, e.what ());
437 MessageDialog am (msg);