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