NO-OP; clean up script spacing and remove goto
[ardour.git] / gtk2_ardour / lua_script_manager.cc
1 /*
2  * Copyright (C) 2016 Robin Gareus <robin@gareus.org>
3  *
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.
8  *
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.
13  *
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.
17  */
18
19 #include "gtkmm2ext/gui_thread.h"
20 #include "gtkmm2ext/utils.h"
21
22 #include "ardour/session.h"
23
24 #include "LuaBridge/LuaBridge.h"
25
26 #include "lua_script_manager.h"
27 #include "luawindow.h"
28 #include "script_selector.h"
29 #include "pbd/i18n.h"
30
31 using namespace std;
32 using namespace Gtk;
33 using namespace ARDOUR;
34
35 LuaScriptManager::LuaScriptManager ()
36         : ArdourWindow (_("Script Manager"))
37         , _a_set_button (_("Add/Set"))
38         , _a_del_button (_("Remove"))
39         , _a_edit_button (_("Edit"))
40         , _a_call_button (_("Call"))
41         , _c_add_button (_("New Hook"))
42         , _c_del_button (_("Remove"))
43         , _s_add_button (_("Load"))
44         , _s_del_button (_("Remove"))
45 {
46         /* action script page */
47         _a_store = ListStore::create (_a_model);
48         _a_view.set_model (_a_store);
49         _a_view.append_column (_("Action"), _a_model.action);
50         _a_view.append_column (_("Name"), _a_model.name);
51         _a_view.get_column(0)->set_resizable (true);
52         _a_view.get_column(0)->set_expand (true);
53         _a_view.get_column(1)->set_resizable (true);
54         _a_view.get_column(1)->set_expand (true);
55
56         Frame* f;
57         Gtk::Label* doc;
58
59         Gtk::HBox* edit_box = manage (new Gtk::HBox);
60         edit_box->set_spacing(3);
61
62         edit_box->pack_start (_a_set_button, true, true);
63         edit_box->pack_start (_a_del_button, true, true);
64         edit_box->pack_start (_a_edit_button, true, true);
65         edit_box->pack_start (_a_call_button, true, true);
66
67         _a_set_button.signal_clicked().connect (sigc::mem_fun(*this, &LuaScriptManager::set_action_btn_clicked));
68         _a_del_button.signal_clicked().connect (sigc::mem_fun(*this, &LuaScriptManager::del_action_btn_clicked));
69         _a_edit_button.signal_clicked().connect (sigc::mem_fun(*this, &LuaScriptManager::edit_action_btn_clicked));
70         _a_call_button.signal_clicked().connect (sigc::mem_fun(*this, &LuaScriptManager::call_action_btn_clicked));
71         _a_view.get_selection()->signal_changed().connect (sigc::mem_fun (*this, &LuaScriptManager::action_selection_changed));
72
73         LuaInstance::instance()->ActionChanged.connect (sigc::mem_fun (*this, &LuaScriptManager::set_action_script_name));
74         LuaInstance::instance()->SlotChanged.connect (sigc::mem_fun (*this, &LuaScriptManager::set_callback_script_name));
75
76         f = manage (new Frame (_("Description")));
77         doc = manage (new Label (
78                                 _("Action Scripts are user initiated actions (menu, shortcuts, toolbar-button) for batch processing or customized tasks.")
79                                 ));
80         doc->set_padding (5, 5);
81         doc->set_line_wrap();
82         f->add (*doc);
83
84         Gtk::VBox *vbox = manage (new VBox());
85         vbox->pack_start (_a_view, false, false);
86         vbox->pack_end (*edit_box, false, false);
87         vbox->pack_end (*f, false, false);
88         vbox->show_all ();
89
90         pages.pages ().push_back (Notebook_Helpers::TabElem (*vbox, "Action Scripts"));
91
92         /* action hooks page */
93
94         _c_store = ListStore::create (_c_model);
95         _c_view.set_model (_c_store);
96         _c_view.append_column (_("Name"), _c_model.name);
97         _c_view.append_column (_("Signal(s)"), _c_model.signals);
98         _c_view.get_column(0)->set_resizable (true);
99         _c_view.get_column(0)->set_expand (true);
100         _c_view.get_column(1)->set_resizable (true);
101         _c_view.get_column(1)->set_expand (true);
102         Gtk::CellRendererText* r = dynamic_cast<Gtk::CellRendererText*>(_c_view.get_column_cell_renderer (1));
103         r->property_ellipsize () = Pango::ELLIPSIZE_MIDDLE;
104
105         edit_box = manage (new Gtk::HBox);
106         edit_box->set_spacing(3);
107         edit_box->pack_start (_c_add_button, true, true);
108         edit_box->pack_start (_c_del_button, true, true);
109
110         _c_add_button.signal_clicked().connect (sigc::mem_fun(*this, &LuaScriptManager::add_callback_btn_clicked));
111         _c_del_button.signal_clicked().connect (sigc::mem_fun(*this, &LuaScriptManager::del_callback_btn_clicked));
112         _c_view.get_selection()->signal_changed().connect (sigc::mem_fun (*this, &LuaScriptManager::callback_selection_changed));
113
114         f = manage (new Frame (_("Description")));
115         doc = manage (new Label (
116                                 _("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                                 ));
118         doc->set_padding (5, 5);
119         doc->set_line_wrap();
120         f->add (*doc);
121
122         vbox = manage (new VBox());
123         vbox->pack_start (_c_view, false, false);
124         vbox->pack_end (*edit_box, false, false);
125         vbox->pack_end (*f, false, false);
126         vbox->show_all ();
127
128         pages.pages ().push_back (Notebook_Helpers::TabElem (*vbox, "Action Hooks"));
129
130         /* session script page */
131
132         _s_store = ListStore::create (_s_model);
133         _s_view.set_model (_s_store);
134         _s_view.append_column (_("Name"), _s_model.name);
135         _s_view.get_column(0)->set_resizable (true);
136         _s_view.get_column(0)->set_expand (true);
137
138         edit_box = manage (new Gtk::HBox);
139         edit_box->set_spacing(3);
140         edit_box->pack_start (_s_add_button, true, true);
141         edit_box->pack_start (_s_del_button, true, true);
142
143         _s_add_button.signal_clicked().connect (sigc::mem_fun(*this, &LuaScriptManager::add_sess_btn_clicked));
144         _s_del_button.signal_clicked().connect (sigc::mem_fun(*this, &LuaScriptManager::del_sess_btn_clicked));
145         _s_view.get_selection()->signal_changed().connect (sigc::mem_fun (*this, &LuaScriptManager::session_script_selection_changed));
146
147         f = manage (new Frame (_("Description")));
148         doc = manage (new Label (
149                                 _("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                                 ));
151         doc->set_padding (5, 5);
152         doc->set_line_wrap();
153         f->add (*doc);
154
155         vbox = manage (new VBox());
156         vbox->pack_start (_s_view, false, false);
157         vbox->pack_end (*edit_box, false, false);
158         vbox->pack_end (*f, false, false);
159         vbox->show_all ();
160
161         pages.pages ().push_back (Notebook_Helpers::TabElem (*vbox, "Session Scripts"));
162
163         /* global layout */
164
165         add (pages);
166         pages.show();
167
168         setup_actions ();
169         setup_callbacks ();
170         setup_session_scripts ();
171
172         action_selection_changed ();
173         callback_selection_changed ();
174         session_script_selection_changed ();
175 }
176
177 void
178 LuaScriptManager::set_session (ARDOUR::Session *s)
179 {
180         ArdourWindow::set_session (s);
181         setup_session_scripts ();
182         if (!_session) {
183                 return;
184         }
185
186         _session->LuaScriptsChanged.connect (_session_script_connection,  invalidator (*this), boost::bind (&LuaScriptManager::setup_session_scripts, this), gui_context());
187         setup_session_scripts ();
188 }
189
190 void
191 LuaScriptManager::session_going_away ()
192 {
193         ArdourWindow::session_going_away ();
194         _session_script_connection.disconnect ();
195         hide_all();
196 }
197
198 void
199 LuaScriptManager::setup_actions ()
200 {
201         LuaInstance *li = LuaInstance::instance();
202         for (int i = 0; i < 9; ++i) {
203                 std::string name;
204                 TreeModel::Row r = *_a_store->append ();
205                 r[_a_model.id] = i;
206                 r[_a_model.action] = string_compose (_("Action %1"), i + 1);
207                 if (li->lua_action_name (i, name)) {
208                         r[_a_model.name] = name;
209                         r[_a_model.enabled] = true;
210                 } else {
211                         r[_a_model.name] = _("Unset");
212                         r[_a_model.enabled] = false;
213                 }
214         }
215 }
216
217 void
218 LuaScriptManager::action_selection_changed ()
219 {
220         TreeModel::Row row = *(_a_view.get_selection()->get_selected());
221         if (row) {
222                 _a_set_button.set_sensitive (true);
223         }
224         else {
225                 _a_set_button.set_sensitive (false);
226         }
227
228         if (row && row[_a_model.enabled]) {
229                 _a_del_button.set_sensitive (true);
230                 _a_edit_button.set_sensitive (true);
231                 _a_call_button.set_sensitive (true);
232         } else {
233                 _a_del_button.set_sensitive (false);
234                 _a_edit_button.set_sensitive (false);
235                 _a_call_button.set_sensitive (false);
236         }
237 }
238
239 void
240 LuaScriptManager::set_action_btn_clicked ()
241 {
242         TreeModel::Row row = *(_a_view.get_selection()->get_selected());
243         assert (row);
244         LuaInstance *li = LuaInstance::instance();
245         li->interactive_add (LuaScriptInfo::EditorAction, row[_a_model.id]);
246 }
247
248 void
249 LuaScriptManager::del_action_btn_clicked ()
250 {
251         TreeModel::Row row = *(_a_view.get_selection()->get_selected());
252         assert (row);
253         LuaInstance *li = LuaInstance::instance();
254         if (!li->remove_lua_action (row[_a_model.id])) {
255                 // error
256         }
257 }
258
259 void
260 LuaScriptManager::call_action_btn_clicked ()
261 {
262         TreeModel::Row row = *(_a_view.get_selection()->get_selected());
263         assert (row && row[_a_model.enabled]);
264         LuaInstance *li = LuaInstance::instance();
265         li->call_action (row[_a_model.id]);
266 }
267
268 void
269 LuaScriptManager::edit_action_btn_clicked ()
270 {
271         TreeModel::Row row = *(_a_view.get_selection()->get_selected());
272         assert (row);
273         int id = row[_a_model.id];
274         LuaInstance *li = LuaInstance::instance();
275         std::string name, script;
276         LuaScriptParamList args;
277         if (!li->lua_action (id, name, script, args)) {
278                 return;
279         }
280         LuaWindow::instance()->edit_script (name, script);
281 }
282
283 void
284 LuaScriptManager::set_action_script_name (int i, const std::string& name)
285 {
286         typedef Gtk::TreeModel::Children type_children;
287         type_children children = _a_store->children();
288         for(type_children::iterator iter = children.begin(); iter != children.end(); ++iter) {
289                 Gtk::TreeModel::Row row = *iter;
290                 if (row[_a_model.id] == i) {
291                         if (name.empty()) {
292                                 row[_a_model.enabled] = false;
293                                 row[_a_model.name] = _("Unset");
294                         } else {
295                                 row[_a_model.enabled] = true;
296                                 row[_a_model.name] = name;
297                         }
298                         break;
299                 }
300         }
301         action_selection_changed ();
302 }
303
304
305 void
306 LuaScriptManager::setup_callbacks ()
307 {
308         LuaInstance *li = LuaInstance::instance();
309         std::vector<PBD::ID> ids = li->lua_slots();
310         for (std::vector<PBD::ID>::const_iterator i = ids.begin(); i != ids.end(); ++i) {
311                 std::string name;
312                 std::string script;
313                 ActionHook ah;
314                 LuaScriptParamList lsp;
315                 if (li->lua_slot (*i, name, script, ah, lsp)) {
316                         set_callback_script_name (*i, name, ah);
317                 }
318         }
319 }
320
321 void
322 LuaScriptManager::callback_selection_changed ()
323 {
324         TreeModel::Row row = *(_c_view.get_selection()->get_selected());
325         if (row) {
326                 _c_del_button.set_sensitive (true);
327         } else {
328                 _c_del_button.set_sensitive (false);
329         }
330 }
331
332 void
333 LuaScriptManager::add_callback_btn_clicked ()
334 {
335         LuaInstance *li = LuaInstance::instance();
336         li->interactive_add (LuaScriptInfo::EditorHook, -1);
337 }
338
339 void
340 LuaScriptManager::del_callback_btn_clicked ()
341 {
342         TreeModel::Row row = *(_c_view.get_selection()->get_selected());
343         assert (row);
344         LuaInstance *li = LuaInstance::instance();
345         if (!li->unregister_lua_slot (row[_c_model.id])) {
346                 // error
347         }
348 }
349
350 void
351 LuaScriptManager::set_callback_script_name (PBD::ID id, const std::string& name, const ActionHook& ah)
352 {
353         if (name.empty()) {
354                 typedef Gtk::TreeModel::Children type_children;
355                 type_children children = _c_store->children();
356                 for(type_children::iterator iter = children.begin(); iter != children.end(); ++iter) {
357                         Gtk::TreeModel::Row row = *iter;
358                         PBD::ID i = row[_c_model.id];
359                         if (i == id) {
360                                 _c_store->erase (iter);
361                                 break;
362                         }
363                 }
364         } else {
365                 TreeModel::Row r = *_c_store->append ();
366                 r[_c_model.id] = id;
367                 r[_c_model.name] = name;
368                 string sig;
369                 for (uint32_t i = 0; i < LuaSignal::LAST_SIGNAL; ++i) {
370                         if (ah[i]) {
371                                 if (!sig.empty()) sig += ", ";
372                                 sig += enum2str (LuaSignal::LuaSignal (i));
373                         }
374                 }
375                 r[_c_model.signals] = sig;
376         }
377         callback_selection_changed ();
378 }
379
380
381 void
382 LuaScriptManager::setup_session_scripts ()
383 {
384         _s_store->clear ();
385         if (!_session) {
386                 return;
387         }
388         std::vector<std::string> reg = _session->registered_lua_functions ();
389         for (std::vector<string>::const_iterator i = reg.begin(); i != reg.end(); ++i) {
390                 TreeModel::Row r = *_s_store->append ();
391                 r[_s_model.name] = *i;
392         }
393         session_script_selection_changed ();
394 }
395
396 void
397 LuaScriptManager::session_script_selection_changed ()
398 {
399         if (!_session) {
400                 _s_del_button.set_sensitive (false);
401                 _s_add_button.set_sensitive (false);
402                 return;
403         }
404         TreeModel::Row row = *(_s_view.get_selection()->get_selected());
405         if (row) {
406                 _s_del_button.set_sensitive (true);
407         } else {
408                 _s_del_button.set_sensitive (false);
409         }
410         _s_add_button.set_sensitive (true);
411 }
412
413 void
414 LuaScriptManager::add_sess_btn_clicked ()
415 {
416         if (!_session) {
417                 return;
418         }
419         LuaInstance *li = LuaInstance::instance();
420         li->interactive_add (LuaScriptInfo::Session, -1);
421 }
422
423 void
424 LuaScriptManager::del_sess_btn_clicked ()
425 {
426         assert (_session);
427         TreeModel::Row row = *(_s_view.get_selection()->get_selected());
428         const std::string& name = row[_s_model.name];
429         try {
430                 _session->unregister_lua_function (name);
431         } catch (luabridge::LuaException const& e) {
432                 string msg = string_compose (_("Session script '%1' removal failed: %2"), name, e.what ());
433                 MessageDialog am (msg);
434                 am.run ();
435         }
436 }