Fix crash when closing session, recursive mon-section removal
[ardour.git] / gtk2_ardour / luawindow.cc
1 /*
2     Copyright (C) 2016 Robin Gareus <robin@gareus.org>
3
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.
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., 675 Mass Ave, Cambridge, MA 02139, USA.
17
18 */
19
20 #ifdef PLATFORM_WINDOWS
21 #define random() rand()
22 #endif
23
24 #ifdef WAF_BUILD
25 #include "gtk2ardour-config.h"
26 #endif
27
28 #include "pbd/gstdio_compat.h"
29 #include <glibmm/fileutils.h>
30 #include <gtkmm/messagedialog.h>
31
32 #include "pbd/basename.h"
33 #include "pbd/file_utils.h"
34 #include "pbd/md5.h"
35
36 #include "gtkmm2ext/gtk_ui.h"
37 #include "gtkmm2ext/utils.h"
38 #include "gtkmm2ext/window_title.h"
39
40 #include "widgets/pane.h"
41 #include "widgets/tooltips.h"
42
43 #include "ardour/filesystem_paths.h"
44 #include "ardour/luabindings.h"
45 #include "LuaBridge/LuaBridge.h"
46
47 #include "ardour_ui.h"
48 #include "gui_thread.h"
49 #include "luainstance.h"
50 #include "luawindow.h"
51 #include "public_editor.h"
52 #include "utils.h"
53 #include "ui_config.h"
54 #include "utils_videotl.h"
55
56 #include "pbd/i18n.h"
57
58 using namespace ARDOUR;
59 using namespace PBD;
60 using namespace Gtk;
61 using namespace Glib;
62 using namespace Gtkmm2ext;
63 using namespace std;
64
65
66 inline LuaWindow::BufferFlags operator| (const LuaWindow::BufferFlags& a, const LuaWindow::BufferFlags& b) {
67         return static_cast<LuaWindow::BufferFlags> (static_cast <int>(a) | static_cast<int> (b));
68 }
69
70 inline LuaWindow::BufferFlags operator|= (LuaWindow::BufferFlags& a, const LuaWindow::BufferFlags& b) {
71         return a = static_cast<LuaWindow::BufferFlags> (static_cast <int>(a) | static_cast<int> (b));
72 }
73
74 inline LuaWindow::BufferFlags operator&= (LuaWindow::BufferFlags& a, const LuaWindow::BufferFlags& b) {
75         return a = static_cast<LuaWindow::BufferFlags> (static_cast <int>(a) & static_cast<int> (b));
76 }
77
78 LuaWindow* LuaWindow::_instance = 0;
79
80 LuaWindow*
81 LuaWindow::instance ()
82 {
83         if (!_instance) {
84                 _instance  = new LuaWindow;
85         }
86
87         return _instance;
88 }
89
90 LuaWindow::LuaWindow ()
91         : Window (Gtk::WINDOW_TOPLEVEL)
92         , VisibilityTracker (*((Gtk::Window*) this))
93         , lua (0)
94         , _visible (false)
95         , _menu_scratch (0)
96         , _menu_snippet (0)
97         , _menu_actions (0)
98         , _btn_run (_("Run"))
99         , _btn_clear (_("Clear Output"))
100         , _btn_open (_("Import"))
101         , _btn_save (_("Save"))
102         , _btn_delete (_("Delete"))
103         , _btn_revert (_("Revert"))
104         , _current_buffer ()
105 {
106         set_name ("Lua");
107
108         reinit_lua ();
109         update_title ();
110         set_wmclass (X_("ardour_mixer"), PROGRAM_NAME);
111
112 #ifdef __APPLE__
113         set_type_hint (Gdk::WINDOW_TYPE_HINT_DIALOG);
114 #else
115         if (UIConfiguration::instance().get_all_floating_windows_are_dialogs()) {
116                 set_type_hint (Gdk::WINDOW_TYPE_HINT_DIALOG);
117         } else {
118                 set_type_hint (Gdk::WINDOW_TYPE_HINT_UTILITY);
119         }
120 #endif
121
122         script_select.disable_scrolling ();
123
124         set_border_width (0);
125
126         outtext.set_editable (false);
127         outtext.set_wrap_mode (Gtk::WRAP_WORD);
128         outtext.set_cursor_visible (false);
129
130         signal_delete_event().connect (sigc::mem_fun (*this, &LuaWindow::hide_window));
131         signal_configure_event().connect (sigc::mem_fun (*ARDOUR_UI::instance(), &ARDOUR_UI::configure_handler));
132
133         _btn_run.signal_clicked.connect (sigc::mem_fun(*this, &LuaWindow::run_script));
134         _btn_clear.signal_clicked.connect (sigc::mem_fun(*this, &LuaWindow::clear_output));
135         _btn_open.signal_clicked.connect (sigc::mem_fun(*this, &LuaWindow::import_script));
136         _btn_save.signal_clicked.connect (sigc::mem_fun(*this, &LuaWindow::save_script));
137         _btn_delete.signal_clicked.connect (sigc::mem_fun(*this, &LuaWindow::delete_script));
138         _btn_revert.signal_clicked.connect (sigc::mem_fun(*this, &LuaWindow::revert_script));
139
140         _btn_open.set_sensitive (false); // TODO
141         _btn_save.set_sensitive (false);
142         _btn_delete.set_sensitive (false);
143         _btn_revert.set_sensitive (false);
144
145         // layout
146
147         Gtk::ScrolledWindow *scrollin = manage (new Gtk::ScrolledWindow);
148         scrollin->set_policy (Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
149         scrollin->add (entry);
150         scrollout.set_policy (Gtk::POLICY_NEVER, Gtk::POLICY_ALWAYS);
151         scrollout.add (outtext);
152
153         entry.set_name ("ArdourLuaEntry");
154         outtext.set_name ("ArdourLuaEntry");
155
156         Gtk::HBox *hbox = manage (new HBox());
157
158         hbox->pack_start (_btn_run, false, false, 2);
159         hbox->pack_start (_btn_clear, false, false, 2);
160         hbox->pack_start (_btn_open, false, false, 2);
161         hbox->pack_start (_btn_save, false, false, 2);
162         hbox->pack_start (_btn_delete, false, false, 2);
163         hbox->pack_start (_btn_revert, false, false, 2);
164         hbox->pack_start (script_select, false, false, 2);
165
166         Gtk::VBox *vbox = manage (new VBox());
167         vbox->pack_start (*scrollin, true, true, 0);
168         vbox->pack_start (*hbox, false, false, 2);
169
170         ArdourWidgets::VPane *vpane = manage (new ArdourWidgets::VPane ());
171         vpane->add (*vbox);
172         vpane->add (scrollout);
173         vpane->set_divider (0, 0.75);
174
175         vpane->show_all ();
176         add (*vpane);
177         set_size_request (640, 480); // XXX
178         ArdourWidgets::set_tooltip (script_select, _("Select Editor Buffer"));
179
180         setup_buffers ();
181         LuaScripting::instance().scripts_changed.connect (*this, invalidator (*this), boost::bind (&LuaWindow::refresh_scriptlist, this), gui_context());
182
183         Glib::RefPtr<Gtk::TextBuffer> tb (entry.get_buffer());
184         _script_changed_connection = tb->signal_changed().connect (sigc::mem_fun(*this, &LuaWindow::script_changed));
185 }
186
187 LuaWindow::~LuaWindow ()
188 {
189         delete lua;
190 }
191
192 void
193 LuaWindow::show_window ()
194 {
195         present();
196         _visible = true;
197 }
198
199 bool
200 LuaWindow::hide_window (GdkEventAny *ev)
201 {
202         if (!_visible) return 0;
203         _visible = false;
204         return ARDOUR_UI_UTILS::just_hide_it (ev, static_cast<Gtk::Window *>(this));
205 }
206
207 void LuaWindow::reinit_lua ()
208 {
209         ENSURE_GUI_THREAD (*this, &LuaWindow::session_going_away);
210         delete lua;
211         lua = new LuaState();
212         lua->Print.connect (sigc::mem_fun (*this, &LuaWindow::append_text));
213         lua->sandbox (false);
214
215         lua_State* L = lua->getState();
216         LuaInstance::register_classes (L);
217         luabridge::push <PublicEditor *> (L, &PublicEditor::instance());
218         lua_setglobal (L, "Editor");
219 }
220
221 void LuaWindow::set_session (Session* s)
222 {
223         SessionHandlePtr::set_session (s);
224         if (!_session) {
225                 return;
226         }
227
228         update_title ();
229         _session->DirtyChanged.connect (_session_connections, invalidator (*this), boost::bind (&LuaWindow::update_title, this), gui_context());
230
231         lua_State* L = lua->getState();
232         LuaBindings::set_session (L, _session);
233 }
234
235 void
236 LuaWindow::session_going_away ()
237 {
238         ENSURE_GUI_THREAD (*this, &LuaWindow::session_going_away);
239         reinit_lua (); // drop state (all variables, session references)
240
241         SessionHandlePtr::session_going_away ();
242         _session = 0;
243         update_title ();
244
245         lua_State* L = lua->getState();
246         LuaBindings::set_session (L, _session);
247 }
248
249 void
250 LuaWindow::update_title ()
251 {
252         if (_session) {
253                 string n;
254
255                 if (_session->snap_name() != _session->name()) {
256                         n = _session->snap_name ();
257                 } else {
258                         n = _session->name ();
259                 }
260
261                 if (_session->dirty ()) {
262                         n = "*" + n;
263                 }
264
265                 WindowTitle title (n);
266                 title += S_("Window|Lua");
267                 title += Glib::get_application_name ();
268                 set_title (title.get_string());
269
270         } else {
271                 WindowTitle title (S_("Window|Lua"));
272                 title += Glib::get_application_name ();
273                 set_title (title.get_string());
274         }
275 }
276
277 void
278 LuaWindow::scroll_to_bottom ()
279 {
280         Gtk::Adjustment *adj;
281         adj = scrollout.get_vadjustment();
282         adj->set_value (MAX(0,(adj->get_upper() - adj->get_page_size())));
283 }
284
285 void
286 LuaWindow::run_script ()
287 {
288         Glib::RefPtr<Gtk::TextBuffer> tb (entry.get_buffer());
289         std::string script = tb->get_text();
290         const std::string& bytecode = LuaScripting::get_factory_bytecode (script);
291         if (bytecode.empty()) {
292                 // plain script or faulty script -- run directly
293                 try {
294                         lua->do_command ("function ardour () end");
295                         if (0 == lua->do_command (script)) {
296                                 append_text ("> OK");
297                         }
298                 } catch (luabridge::LuaException const& e) {
299                         append_text (string_compose (_("LuaException: %1"), e.what()));
300                 } catch (Glib::Exception const& e) {
301                         append_text (string_compose (_("Glib Exception: %1"), e.what()));
302                 } catch (std::exception const& e) {
303                         append_text (string_compose (_("C++ Exception: %1"), e.what()));
304                 } catch (...) {
305                         append_text (string_compose (_("C++ Exception: %1"), "..."));
306                 }
307         } else {
308                 // script with factory method
309                 try {
310                         lua_State* L = lua->getState();
311                         lua->do_command ("function ardour () end");
312
313                         LuaScriptParamList args = LuaScriptParams::script_params (script, "action_param", false);
314                         luabridge::LuaRef tbl_arg (luabridge::newTable(L));
315                         LuaScriptParams::params_to_ref (&tbl_arg, args);
316                         lua->do_command (script); // register "factory"
317                         luabridge::LuaRef lua_factory = luabridge::getGlobal (L, "factory");
318                         if (lua_factory.isFunction()) {
319                                 lua_factory(tbl_arg)();
320                         }
321                         lua->do_command ("factory = nil;");
322                 } catch (luabridge::LuaException const& e) {
323                         append_text (string_compose (_("LuaException: %1"), e.what()));
324                 } catch (Glib::Exception const& e) {
325                         append_text (string_compose (_("Glib Exception: %1"), e.what()));
326                 } catch (std::exception const& e) {
327                         append_text (string_compose (_("C++ Exception: %1"), e.what()));
328                 } catch (...) {
329                         append_text (string_compose (_("C++ Exception: %1"), "..."));
330                 }
331         }
332 }
333
334 void
335 LuaWindow::append_text (std::string s)
336 {
337         Glib::RefPtr<Gtk::TextBuffer> tb (outtext.get_buffer());
338         tb->insert (tb->end(), s + "\n");
339         scroll_to_bottom ();
340         Gtkmm2ext::UI::instance()->flush_pending (0.05);
341 }
342
343 void
344 LuaWindow::clear_output ()
345 {
346         Glib::RefPtr<Gtk::TextBuffer> tb (outtext.get_buffer());
347         tb->set_text ("");
348 }
349
350 void
351 LuaWindow::edit_script (const std::string& name, const std::string& script)
352 {
353         ScriptBuffer* sb = new LuaWindow::ScriptBuffer (name);
354         sb->script = script;
355         script_buffers.push_back (ScriptBufferPtr (sb));
356         script_selection_changed (script_buffers.back ());
357         refresh_scriptlist ();
358         show_window ();
359 }
360
361 void
362 LuaWindow::new_script ()
363 {
364         char buf[32];
365         snprintf (buf, sizeof (buf), "#%d", count_scratch_buffers () + 1);
366         script_buffers.push_back (ScriptBufferPtr (new LuaWindow::ScriptBuffer (buf)));
367         script_selection_changed (script_buffers.back ());
368         refresh_scriptlist ();
369 }
370
371 void
372 LuaWindow::delete_script ()
373 {
374         assert ((_current_buffer->flags & Buffer_Scratch) || !(_current_buffer->flags & Buffer_ReadOnly));
375         bool refresh = false;
376         bool neednew = true;
377         if (_current_buffer->flags & Buffer_HasFile) {
378                 if (0 == ::g_unlink (_current_buffer->path.c_str())) {
379                         append_text (X_("> ") + string_compose (_("Deleted %1"), _current_buffer->path));
380                         refresh = true;
381                 } else {
382                         append_text (X_("> ") + string_compose (_("Failed to delete %1"), _current_buffer->path));
383                 }
384         }
385         for (ScriptBufferList::iterator i = script_buffers.begin (); i != script_buffers.end (); ++i) {
386                 if ((*i) == _current_buffer) {
387                         script_buffers.erase (i);
388                         break;
389                 }
390         }
391
392         for (ScriptBufferList::const_iterator i = script_buffers.begin (); i != script_buffers.end (); ++i) {
393                 if ((*i)->flags & Buffer_Scratch) {
394                         script_selection_changed (*i);
395                         neednew = false;
396                 }
397         }
398         if (neednew) {
399                 new_script ();
400         }
401         if (refresh) {
402                 LuaScripting::instance ().refresh (true);
403         }
404 }
405
406 void
407 LuaWindow::revert_script ()
408 {
409         _current_buffer->flags &= BufferFlags(~Buffer_Valid);
410         script_selection_changed (_current_buffer, true);
411 }
412
413 void
414 LuaWindow::import_script ()
415 {
416         // TODO: dialog to select file or enter URL
417         // TODO convert a few URL (eg. pastebin) to raw.
418 #if 0
419         char *url = "http://pastebin.com/raw/3UMkZ6nV";
420         char *rv = ArdourCurl::http_get (url, 0. true);
421         if (rv) {
422                 new_script ();
423                 Glib::RefPtr<Gtk::TextBuffer> tb (entry.get_buffer());
424                 tb->set_text (rv);
425                 _current_buffer->flags &= BufferFlags(~Buffer_Dirty);
426                 update_gui_state ();
427         }
428         free (rv);
429 #endif
430 }
431
432 void
433 LuaWindow::save_script ()
434 {
435         Glib::RefPtr<Gtk::TextBuffer> tb (entry.get_buffer());
436         std::string script = tb->get_text();
437         std::string msg = "Unknown error";
438
439         std::string path;
440         LuaScriptInfoPtr lsi = LuaScripting::script_info (script);
441         ScriptBuffer & sb (*_current_buffer);
442
443         assert (sb.flags & Buffer_Dirty);
444
445         // 1) check if it has a valid header and factory
446         const std::string& bytecode = LuaScripting::get_factory_bytecode (script);
447         if (bytecode.empty()) {
448                 msg = _("Missing script header.\nThe script requires an '{ardour}' info table and a 'factory' function.");
449                 goto errorout;
450         }
451
452         if (!LuaScripting::try_compile (script, LuaScriptParams::script_params (script, "action_param", false))) {
453                 msg = _("Script fails to compile.");
454                 goto errorout;
455         }
456
457         // 2) check script name & type
458         lsi = LuaScripting::script_info (script);
459         if (!lsi) {
460                 msg = _("Invalid or missing script-name or script-type.");
461                 goto errorout;
462         }
463
464         if (lsi->type != LuaScriptInfo::Snippet && lsi->type != LuaScriptInfo::EditorAction) {
465                 msg = _("Invalid script-type.\nValid types are 'EditorAction' and 'Snippet'.");
466                 goto errorout;
467         }
468
469         // 3) if there's already a writable file,...
470         if ((sb.flags & Buffer_HasFile) && !(sb.flags & Buffer_ReadOnly)) {
471                 try {
472                         Glib::file_set_contents (sb.path, script);
473                         sb.name = lsi->name;
474                         sb.flags &= BufferFlags(~Buffer_Dirty);
475                         update_gui_state (); // XXX here?
476                         append_text (X_("> ") + string_compose (_("Saved as %1"), sb.path));
477                         return; // OK
478                 } catch (Glib::FileError const& e) {
479                         msg = string_compose (_("Error saving file: %1"), e.what());
480                         goto errorout;
481                 }
482         }
483
484         // 4) check if the name is unique for the given type; locally at least
485         if (true /*sb.flags & Buffer_HasFile*/) {
486                 LuaScriptList& lsl (LuaScripting::instance ().scripts (lsi->type));
487                 for (LuaScriptList::const_iterator s = lsl.begin(); s != lsl.end(); ++s) {
488                         if ((*s)->name == lsi->name) {
489                                 msg = string_compose (_("Script with given name '%1' already exists.\nUse a different name in the descriptor."), lsi->name);
490                                 goto errorout;
491                         }
492                 }
493         }
494
495         // 5) construct filename -- TODO ask user for name, ask to replace file.
496         do {
497                 char tme[80];
498                 char buf[80];
499                 time_t t = time(0);
500                 struct tm * timeinfo = localtime (&t);
501                 strftime (tme, sizeof(tme), "%s", timeinfo);
502                 snprintf (buf, sizeof(buf), "%s%ld", tme, random ());
503                 MD5 md5;
504                 std::string fn = md5.digestString (buf);
505
506                 switch (lsi->type) {
507                         case LuaScriptInfo::EditorAction:
508                                 fn = "a_" + fn;
509                                 break;
510                         case LuaScriptInfo::Snippet:
511                                 fn = "s_" + fn;
512                                 break;
513                         default:
514                                 break;
515                 }
516                 path = Glib::build_filename (LuaScripting::user_script_dir (), fn.substr(0, 11) + ".lua");
517         } while (Glib::file_test (path, Glib::FILE_TEST_EXISTS));
518
519         try {
520                 Glib::file_set_contents (path, script);
521                 sb.path = path;
522                 sb.name = lsi->name;
523                 sb.flags |= Buffer_HasFile;
524                 sb.flags &= BufferFlags(~Buffer_Dirty);
525                 sb.flags &= BufferFlags(~Buffer_ReadOnly);
526                 update_gui_state (); // XXX here? .refresh (true) may trigger this, too
527                 LuaScripting::instance().refresh (true);
528                 append_text (X_("> ") + string_compose (_("Saved as %1"), path));
529                 return; // OK
530         } catch (Glib::FileError const& e) {
531                 msg = string_compose (_("Error saving file: %1"), e.what());
532                 goto errorout;
533         }
534
535 errorout:
536                 MessageDialog am (msg);
537                 am.run ();
538 }
539
540 void
541 LuaWindow::setup_buffers ()
542 {
543         if (script_buffers.size() > 0) {
544                 return;
545         }
546         script_buffers.push_back (ScriptBufferPtr (new LuaWindow::ScriptBuffer("#1")));
547         _current_buffer = script_buffers.front();
548
549         Glib::RefPtr<Gtk::TextBuffer> tb (entry.get_buffer());
550         tb->set_text (_current_buffer->script);
551
552         refresh_scriptlist ();
553         update_gui_state ();
554 }
555
556 uint32_t
557 LuaWindow::count_scratch_buffers () const
558 {
559         uint32_t n = 0;
560         for (ScriptBufferList::const_iterator i = script_buffers.begin (); i != script_buffers.end (); ++i) {
561                 if ((*i)->flags & Buffer_Scratch) {
562                         ++n;
563                 }
564         }
565         return n;
566 }
567
568 void
569 LuaWindow::refresh_scriptlist ()
570 {
571         for (ScriptBufferList::iterator i = script_buffers.begin (); i != script_buffers.end ();) {
572                 if ((*i)->flags & Buffer_Scratch) {
573                         ++i;
574                         continue;
575                 }
576                 i = script_buffers.erase (i);
577         }
578         LuaScriptList& lsa (LuaScripting::instance ().scripts (LuaScriptInfo::EditorAction));
579         for (LuaScriptList::const_iterator s = lsa.begin(); s != lsa.end(); ++s) {
580                 script_buffers.push_back (ScriptBufferPtr (new LuaWindow::ScriptBuffer(*s)));
581         }
582
583         LuaScriptList& lss (LuaScripting::instance ().scripts (LuaScriptInfo::Snippet));
584         for (LuaScriptList::const_iterator s = lss.begin(); s != lss.end(); ++s) {
585                 script_buffers.push_back (ScriptBufferPtr (new LuaWindow::ScriptBuffer(*s)));
586         }
587         rebuild_menu ();
588 }
589
590 void
591 LuaWindow::rebuild_menu ()
592 {
593         using namespace Menu_Helpers;
594
595         _menu_scratch = manage (new Menu);
596         _menu_snippet = manage (new Menu);
597         _menu_actions = manage (new Menu);
598
599         MenuList& items_scratch (_menu_scratch->items());
600         MenuList& items_snippet (_menu_snippet->items());
601         MenuList& items_actions (_menu_actions->items());
602
603         {
604                 Menu_Helpers::MenuElem elem = Gtk::Menu_Helpers::MenuElem(_("New"),
605                                 sigc::mem_fun(*this, &LuaWindow::new_script));
606                 items_scratch.push_back(elem);
607         }
608
609         items_scratch.push_back(SeparatorElem());
610
611         for (ScriptBufferList::const_iterator i = script_buffers.begin (); i != script_buffers.end (); ++i) {
612                 std::string name;
613                 if ((*i)->flags & Buffer_ReadOnly) {
614                         name = "[R] " + (*i)->name;
615                 } else {
616                         name = (*i)->name;
617                 }
618                 Menu_Helpers::MenuElem elem = Gtk::Menu_Helpers::MenuElem(name,
619                                 sigc::bind(sigc::mem_fun(*this, &LuaWindow::script_selection_changed), (*i), false));
620
621                 if ((*i)->flags & Buffer_Scratch) {
622                         items_scratch.push_back(elem);
623                 }
624                 else if ((*i)->type == LuaScriptInfo::EditorAction) {
625                                 items_actions.push_back(elem);
626                 }
627                 else if ((*i)->type == LuaScriptInfo::Snippet) {
628                                 items_snippet.push_back(elem);
629                 }
630         }
631
632         script_select.clear_items ();
633         script_select.AddMenuElem (Menu_Helpers::MenuElem ("Scratch", *_menu_scratch));
634         script_select.AddMenuElem (Menu_Helpers::MenuElem ("Snippets", *_menu_snippet));
635         script_select.AddMenuElem (Menu_Helpers::MenuElem ("Actions", *_menu_actions));
636 }
637
638 void
639 LuaWindow::script_selection_changed (ScriptBufferPtr n, bool force)
640 {
641         if (n == _current_buffer && !force) {
642                 return;
643         }
644
645         Glib::RefPtr<Gtk::TextBuffer> tb (entry.get_buffer());
646
647         if (_current_buffer->flags & Buffer_Valid) {
648                 _current_buffer->script = tb->get_text();
649         }
650
651         if (!(n->flags & Buffer_Valid)) {
652                 if (!n->load()) {
653                         append_text ("! Failed to load buffer.");
654                 }
655         }
656
657         if (n->flags & Buffer_Valid) {
658                 _current_buffer = n;
659                 _script_changed_connection.block ();
660                 tb->set_text (n->script);
661                 _script_changed_connection.unblock ();
662         } else {
663                 append_text ("! Failed to switch buffer.");
664         }
665         update_gui_state ();
666 }
667
668 void
669 LuaWindow::update_gui_state ()
670 {
671         const ScriptBuffer & sb (*_current_buffer);
672         std::string name;
673         if (sb.flags & Buffer_Scratch) {
674                 name = string_compose (_("Scratch Buffer %1"), sb.name);
675         } else if (sb.type == LuaScriptInfo::EditorAction) {
676                 name = string_compose (_("Action: '%1'"), sb.name);
677         } else if (sb.type == LuaScriptInfo::Snippet) {
678                 name = string_compose (_("Snippet: %1"), sb.name);
679         } else {
680                 cerr << "Invalid Script type\n";
681                 assert (0);
682                 return;
683         }
684         if (sb.flags & Buffer_Dirty) {
685                 name += " *";
686         }
687         script_select.set_text(name);
688
689         if (sb.flags & Buffer_ReadOnly) {
690                 _btn_save.set_text (_("Save as"));
691         } else {
692                 _btn_save.set_text (_("Save"));
693         }
694         _btn_save.set_sensitive (sb.flags & Buffer_Dirty);
695         _btn_delete.set_sensitive (sb.flags & Buffer_Scratch || ((sb.flags & (Buffer_ReadOnly | Buffer_HasFile)) == Buffer_HasFile));
696         _btn_revert.set_sensitive ((sb.flags & Buffer_Dirty) && (sb.flags & Buffer_HasFile));
697 }
698
699 void
700 LuaWindow::script_changed () {
701         if (_current_buffer->flags & Buffer_Dirty) {
702                 return;
703         }
704         _current_buffer->flags |= Buffer_Dirty;
705         update_gui_state ();
706 }
707
708 LuaWindow::ScriptBuffer::ScriptBuffer (const std::string& n)
709         : name (n)
710         , flags (Buffer_Scratch | Buffer_Valid)
711 {
712         script =
713                 "---- this header is (only) required to save the script\n"
714                 "-- ardour { [\"type\"] = \"Snippet\", name = \"\" }\n"
715                 "-- function factory () return function () -- -- end end\n";
716 }
717
718 LuaWindow::ScriptBuffer::ScriptBuffer (LuaScriptInfoPtr p)
719         : name (p->name)
720         , path (p->path)
721         , flags (Buffer_HasFile)
722         , type (p->type)
723 {
724         if (!PBD::exists_and_writable (path)) {
725                 flags |= Buffer_ReadOnly;
726         }
727         if (path.find (user_config_directory ()) != 0) {
728                 // mark non-user scripts as read-only
729                 flags |= Buffer_ReadOnly;
730         }
731 }
732
733 #if 0
734 LuaWindow::ScriptBuffer::ScriptBuffer (const ScriptBuffer& other)
735         : script (other.script)
736         , name (other.name)
737         , path (other.path)
738         , flags (other.flags)
739         , type (other.type)
740 {
741 }
742 #endif
743
744 LuaWindow::ScriptBuffer::~ScriptBuffer ()
745 {
746 }
747
748 bool
749 LuaWindow::ScriptBuffer::load ()
750 {
751         assert (!(flags & Buffer_Valid));
752         if (!(flags & Buffer_HasFile)) return false;
753         try {
754                 script = Glib::file_get_contents (path);
755                 flags |= Buffer_Valid;
756                 flags &= BufferFlags(~Buffer_Dirty);
757         } catch (Glib::FileError const& e) {
758                 return false;
759         }
760         return true;
761 }