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 "gtkmm2ext/gui_thread.h"
20 #include "gtkmm2ext/utils.h"
22 #include "ardour/session.h"
24 #include "LuaBridge/LuaBridge.h"
26 #include "lua_script_manager.h"
27 #include "script_selector.h"
32 using namespace ARDOUR;
34 LuaScriptManager::LuaScriptManager ()
35 : ArdourWindow (_("Script Manager"))
36 , _a_set_button (_("Add/Set"))
37 , _a_del_button (_("Remove"))
38 , _a_edit_button (_("Edit"))
39 , _a_call_button (_("Call"))
40 , _c_add_button (_("New Hook"))
41 , _c_del_button (_("Remove"))
42 , _s_add_button (_("Load"))
43 , _s_del_button (_("Remove"))
45 /* action script page */
46 _a_store = ListStore::create (_a_model);
47 _a_view.set_model (_a_store);
48 _a_view.append_column (_("Action"), _a_model.action);
49 _a_view.append_column (_("Name"), _a_model.name);
50 _a_view.get_column(0)->set_resizable (true);
51 _a_view.get_column(0)->set_expand (true);
52 _a_view.get_column(1)->set_resizable (true);
53 _a_view.get_column(1)->set_expand (true);
58 Gtk::HBox* edit_box = manage (new Gtk::HBox);
59 edit_box->set_spacing(3);
61 edit_box->pack_start (_a_set_button, true, true);
62 edit_box->pack_start (_a_del_button, true, true);
63 edit_box->pack_start (_a_edit_button, true, true);
64 edit_box->pack_start (_a_call_button, true, true);
66 _a_set_button.signal_clicked().connect (sigc::mem_fun(*this, &LuaScriptManager::set_action_btn_clicked));
67 _a_del_button.signal_clicked().connect (sigc::mem_fun(*this, &LuaScriptManager::del_action_btn_clicked));
68 _a_edit_button.signal_clicked().connect (sigc::mem_fun(*this, &LuaScriptManager::edit_action_btn_clicked));
69 _a_call_button.signal_clicked().connect (sigc::mem_fun(*this, &LuaScriptManager::call_action_btn_clicked));
70 _a_view.get_selection()->signal_changed().connect (sigc::mem_fun (*this, &LuaScriptManager::action_selection_changed));
72 LuaInstance::instance()->ActionChanged.connect (sigc::mem_fun (*this, &LuaScriptManager::set_action_script_name));
73 LuaInstance::instance()->SlotChanged.connect (sigc::mem_fun (*this, &LuaScriptManager::set_callback_script_name));
75 f = manage (new Frame (_("Description")));
76 doc = manage (new Label (
77 _("Action Scripts are user initiated actions (menu, shortcuts, toolbar-button) for batch processing or customized tasks.")
79 doc->set_padding (5, 5);
83 Gtk::VBox *vbox = manage (new VBox());
84 vbox->pack_start (_a_view, false, false);
85 vbox->pack_end (*edit_box, false, false);
86 vbox->pack_end (*f, false, false);
89 pages.pages ().push_back (Notebook_Helpers::TabElem (*vbox, "Action Scripts"));
91 /* action hooks page */
93 _c_store = ListStore::create (_c_model);
94 _c_view.set_model (_c_store);
95 _c_view.append_column (_("Name"), _c_model.name);
96 _c_view.append_column (_("Signal(s)"), _c_model.signals);
97 _c_view.get_column(0)->set_resizable (true);
98 _c_view.get_column(0)->set_expand (true);
99 _c_view.get_column(1)->set_resizable (true);
100 _c_view.get_column(1)->set_expand (true);
101 Gtk::CellRendererText* r = dynamic_cast<Gtk::CellRendererText*>(_c_view.get_column_cell_renderer (1));
102 r->property_ellipsize () = Pango::ELLIPSIZE_MIDDLE;
104 edit_box = manage (new Gtk::HBox);
105 edit_box->set_spacing(3);
106 edit_box->pack_start (_c_add_button, true, true);
107 edit_box->pack_start (_c_del_button, true, true);
109 _c_add_button.signal_clicked().connect (sigc::mem_fun(*this, &LuaScriptManager::add_callback_btn_clicked));
110 _c_del_button.signal_clicked().connect (sigc::mem_fun(*this, &LuaScriptManager::del_callback_btn_clicked));
111 _c_view.get_selection()->signal_changed().connect (sigc::mem_fun (*this, &LuaScriptManager::callback_selection_changed));
113 f = manage (new Frame (_("Description")));
114 doc = manage (new Label (
115 _("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.")
117 doc->set_padding (5, 5);
118 doc->set_line_wrap();
121 vbox = manage (new VBox());
122 vbox->pack_start (_c_view, false, false);
123 vbox->pack_end (*edit_box, false, false);
124 vbox->pack_end (*f, false, false);
127 pages.pages ().push_back (Notebook_Helpers::TabElem (*vbox, "Action Hooks"));
129 /* session script page */
131 _s_store = ListStore::create (_s_model);
132 _s_view.set_model (_s_store);
133 _s_view.append_column (_("Name"), _s_model.name);
134 _s_view.get_column(0)->set_resizable (true);
135 _s_view.get_column(0)->set_expand (true);
137 edit_box = manage (new Gtk::HBox);
138 edit_box->set_spacing(3);
139 edit_box->pack_start (_s_add_button, true, true);
140 edit_box->pack_start (_s_del_button, true, true);
142 _s_add_button.signal_clicked().connect (sigc::mem_fun(*this, &LuaScriptManager::add_sess_btn_clicked));
143 _s_del_button.signal_clicked().connect (sigc::mem_fun(*this, &LuaScriptManager::del_sess_btn_clicked));
144 _s_view.get_selection()->signal_changed().connect (sigc::mem_fun (*this, &LuaScriptManager::session_script_selection_changed));
146 f = manage (new Frame (_("Description")));
147 doc = manage (new Label (
148 _("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.")
150 doc->set_padding (5, 5);
151 doc->set_line_wrap();
154 vbox = manage (new VBox());
155 vbox->pack_start (_s_view, false, false);
156 vbox->pack_end (*edit_box, false, false);
157 vbox->pack_end (*f, false, false);
160 pages.pages ().push_back (Notebook_Helpers::TabElem (*vbox, "Session Scripts"));
169 setup_session_scripts ();
171 action_selection_changed ();
172 callback_selection_changed ();
173 session_script_selection_changed ();
177 LuaScriptManager::set_session (ARDOUR::Session *s)
179 ArdourWindow::set_session (s);
180 setup_session_scripts ();
185 _session->LuaScriptsChanged.connect (_session_script_connection, invalidator (*this), boost::bind (&LuaScriptManager::setup_session_scripts, this), gui_context());
186 setup_session_scripts ();
190 LuaScriptManager::session_going_away ()
192 ArdourWindow::session_going_away ();
193 _session_script_connection.disconnect ();
198 LuaScriptManager::setup_actions ()
200 LuaInstance *li = LuaInstance::instance();
201 for (int i = 0; i < 9; ++i) {
203 TreeModel::Row r = *_a_store->append ();
205 r[_a_model.action] = string_compose (_("Action %1"), i + 1);
206 if (li->lua_action_name (i, name)) {
207 r[_a_model.name] = name;
208 r[_a_model.enabled] = true;
210 r[_a_model.name] = _("Unset");
211 r[_a_model.enabled] = false;
217 LuaScriptManager::action_selection_changed ()
219 TreeModel::Row row = *(_a_view.get_selection()->get_selected());
221 _a_set_button.set_sensitive (true);
224 _a_set_button.set_sensitive (false);
227 if (row && row[_a_model.enabled]) {
228 _a_del_button.set_sensitive (true);
229 _a_edit_button.set_sensitive (false); // TODO
230 _a_call_button.set_sensitive (true);
232 _a_del_button.set_sensitive (false);
233 _a_edit_button.set_sensitive (false);
234 _a_call_button.set_sensitive (false);
239 LuaScriptManager::set_action_btn_clicked ()
241 TreeModel::Row row = *(_a_view.get_selection()->get_selected());
243 LuaInstance *li = LuaInstance::instance();
244 li->interactive_add (LuaScriptInfo::EditorAction, row[_a_model.id]);
248 LuaScriptManager::del_action_btn_clicked ()
250 TreeModel::Row row = *(_a_view.get_selection()->get_selected());
252 LuaInstance *li = LuaInstance::instance();
253 if (!li->remove_lua_action (row[_a_model.id])) {
259 LuaScriptManager::call_action_btn_clicked ()
261 TreeModel::Row row = *(_a_view.get_selection()->get_selected());
262 assert (row && row[_a_model.enabled]);
263 LuaInstance *li = LuaInstance::instance();
264 li->call_action (row[_a_model.id]);
268 LuaScriptManager::edit_action_btn_clicked ()
270 TreeModel::Row row = *(_a_view.get_selection()->get_selected());
272 int id = row[_a_model.id];
273 LuaInstance *li = LuaInstance::instance();
274 std::string name, script;
275 LuaScriptParamList args;
276 if (!li->lua_action (id, name, script, args)) {
280 // TODO text-editor window, update script directly
282 if (!LuaScripting::try_compile (script, args)) {
283 // compilation failed, keep editing
287 if (li->set_lua_action (id, name, script, args)) {
290 // load failed, keep editing..
292 action_selection_changed ();
296 LuaScriptManager::set_action_script_name (int i, const std::string& name)
298 typedef Gtk::TreeModel::Children type_children;
299 type_children children = _a_store->children();
300 for(type_children::iterator iter = children.begin(); iter != children.end(); ++iter) {
301 Gtk::TreeModel::Row row = *iter;
302 if (row[_a_model.id] == i) {
304 row[_a_model.enabled] = false;
305 row[_a_model.name] = _("Unset");
307 row[_a_model.enabled] = true;
308 row[_a_model.name] = name;
313 action_selection_changed ();
318 LuaScriptManager::setup_callbacks ()
320 LuaInstance *li = LuaInstance::instance();
321 std::vector<PBD::ID> ids = li->lua_slots();
322 for (std::vector<PBD::ID>::const_iterator i = ids.begin(); i != ids.end(); ++i) {
326 LuaScriptParamList lsp;
327 if (li->lua_slot (*i, name, script, ah, lsp)) {
328 set_callback_script_name (*i, name, ah);
334 LuaScriptManager::callback_selection_changed ()
336 TreeModel::Row row = *(_c_view.get_selection()->get_selected());
338 _c_del_button.set_sensitive (true);
340 _c_del_button.set_sensitive (false);
345 LuaScriptManager::add_callback_btn_clicked ()
347 LuaInstance *li = LuaInstance::instance();
348 li->interactive_add (LuaScriptInfo::EditorHook, -1);
352 LuaScriptManager::del_callback_btn_clicked ()
354 TreeModel::Row row = *(_c_view.get_selection()->get_selected());
356 LuaInstance *li = LuaInstance::instance();
357 if (!li->unregister_lua_slot (row[_c_model.id])) {
363 LuaScriptManager::set_callback_script_name (PBD::ID id, const std::string& name, const ActionHook& ah)
366 typedef Gtk::TreeModel::Children type_children;
367 type_children children = _c_store->children();
368 for(type_children::iterator iter = children.begin(); iter != children.end(); ++iter) {
369 Gtk::TreeModel::Row row = *iter;
370 PBD::ID i = row[_c_model.id];
372 _c_store->erase (iter);
377 TreeModel::Row r = *_c_store->append ();
379 r[_c_model.name] = name;
381 for (uint32_t i = 0; i < LuaSignal::LAST_SIGNAL; ++i) {
383 if (!sig.empty()) sig += ", ";
384 sig += enum2str (LuaSignal::LuaSignal (i));
387 r[_c_model.signals] = sig;
389 callback_selection_changed ();
394 LuaScriptManager::setup_session_scripts ()
400 std::vector<std::string> reg = _session->registered_lua_functions ();
401 for (std::vector<string>::const_iterator i = reg.begin(); i != reg.end(); ++i) {
402 TreeModel::Row r = *_s_store->append ();
403 r[_s_model.name] = *i;
405 session_script_selection_changed ();
409 LuaScriptManager::session_script_selection_changed ()
412 _s_del_button.set_sensitive (false);
413 _s_add_button.set_sensitive (false);
416 TreeModel::Row row = *(_s_view.get_selection()->get_selected());
418 _s_del_button.set_sensitive (true);
420 _s_del_button.set_sensitive (false);
422 _s_add_button.set_sensitive (true);
426 LuaScriptManager::add_sess_btn_clicked ()
431 LuaInstance *li = LuaInstance::instance();
432 li->interactive_add (LuaScriptInfo::Session, -1);
436 LuaScriptManager::del_sess_btn_clicked ()
439 TreeModel::Row row = *(_s_view.get_selection()->get_selected());
440 const std::string& name = row[_s_model.name];
442 _session->unregister_lua_function (name);
443 } catch (luabridge::LuaException const& e) {
444 string msg = string_compose (_("Session script '%1' removal failed: %2"), name, e.what ());
445 MessageDialog am (msg);