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