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