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