*/
+#ifdef PLATFORM_WINDOWS
+#define random() rand()
+#endif
+
#ifdef WAF_BUILD
#include "gtk2ardour-config.h"
#endif
-#include <gtkmm2ext/gtk_ui.h>
-#include <gtkmm2ext/utils.h>
-#include <gtkmm2ext/window_title.h>
+#include "pbd/gstdio_compat.h"
+#include <glibmm/fileutils.h>
+#include <gtkmm/messagedialog.h>
+
+#include "pbd/basename.h"
+#include "pbd/file_utils.h"
+#include "pbd/md5.h"
+
+#include "gtkmm2ext/gtk_ui.h"
+#include "gtkmm2ext/pane.h"
+#include "gtkmm2ext/utils.h"
+#include "gtkmm2ext/window_title.h"
+
+#include "ardour/filesystem_paths.h"
+#include "ardour/luabindings.h"
+#include "LuaBridge/LuaBridge.h"
#include "ardour_ui.h"
#include "gui_thread.h"
#include "luainstance.h"
#include "luawindow.h"
#include "public_editor.h"
+#include "tooltips.h"
#include "utils.h"
+#include "utils_videotl.h"
-#include "ardour/luabindings.h"
-#include "LuaBridge/LuaBridge.h"
-
-#include "i18n.h"
+#include "pbd/i18n.h"
using namespace ARDOUR;
using namespace ARDOUR_UI_UTILS;
using namespace std;
+inline LuaWindow::BufferFlags operator| (const LuaWindow::BufferFlags& a, const LuaWindow::BufferFlags& b) {
+ return static_cast<LuaWindow::BufferFlags> (static_cast <int>(a) | static_cast<int> (b));
+}
+
+inline LuaWindow::BufferFlags operator|= (LuaWindow::BufferFlags& a, const LuaWindow::BufferFlags& b) {
+ return a = static_cast<LuaWindow::BufferFlags> (static_cast <int>(a) | static_cast<int> (b));
+}
+
+inline LuaWindow::BufferFlags operator&= (LuaWindow::BufferFlags& a, const LuaWindow::BufferFlags& b) {
+ return a = static_cast<LuaWindow::BufferFlags> (static_cast <int>(a) & static_cast<int> (b));
+}
+
LuaWindow* LuaWindow::_instance = 0;
LuaWindow*
LuaWindow::LuaWindow ()
: Window (Gtk::WINDOW_TOPLEVEL)
, VisibilityTracker (*((Gtk::Window*) this))
+ , lua (0)
, _visible (false)
+ , _menu_scratch (0)
+ , _menu_snippet (0)
+ , _menu_actions (0)
+ , _btn_run (_("Run"))
+ , _btn_clear (_("Clear Output"))
+ , _btn_open (_("Import"))
+ , _btn_save (_("Save"))
+ , _btn_delete (_("Delete"))
+ , _btn_revert (_("Revert"))
+ , _current_buffer ()
{
set_name ("Lua");
+ reinit_lua ();
update_title ();
set_wmclass (X_("ardour_mixer"), PROGRAM_NAME);
+ script_select.disable_scrolling ();
+
set_border_width (0);
outtext.set_editable (false);
outtext.set_wrap_mode (Gtk::WRAP_WORD);
+ outtext.set_cursor_visible (false);
signal_delete_event().connect (sigc::mem_fun (*this, &LuaWindow::hide_window));
signal_configure_event().connect (sigc::mem_fun (*ARDOUR_UI::instance(), &ARDOUR_UI::configure_handler));
- scrollwin.set_policy (Gtk::POLICY_NEVER, Gtk::POLICY_ALWAYS);
- scrollwin.add (outtext);
+ _btn_run.signal_clicked.connect (sigc::mem_fun(*this, &LuaWindow::run_script));
+ _btn_clear.signal_clicked.connect (sigc::mem_fun(*this, &LuaWindow::clear_output));
+ _btn_open.signal_clicked.connect (sigc::mem_fun(*this, &LuaWindow::import_script));
+ _btn_save.signal_clicked.connect (sigc::mem_fun(*this, &LuaWindow::save_script));
+ _btn_delete.signal_clicked.connect (sigc::mem_fun(*this, &LuaWindow::delete_script));
+ _btn_revert.signal_clicked.connect (sigc::mem_fun(*this, &LuaWindow::revert_script));
+
+ _btn_open.set_sensitive (false); // TODO
+ _btn_save.set_sensitive (false);
+ _btn_delete.set_sensitive (false);
+ _btn_revert.set_sensitive (false);
- Gtk::Button *btn_clr = manage (new Button ("Clear"));
- btn_clr->signal_clicked().connect (sigc::mem_fun(*this, &LuaWindow::clear_output));
+ // layout
+
+ Gtk::ScrolledWindow *scrollin = manage (new Gtk::ScrolledWindow);
+ scrollin->set_policy (Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
+ scrollin->add (entry);
+ scrollout.set_policy (Gtk::POLICY_NEVER, Gtk::POLICY_ALWAYS);
+ scrollout.add (outtext);
+
+ entry.set_name ("ArdourLuaEntry");
+ outtext.set_name ("ArdourLuaEntry");
Gtk::HBox *hbox = manage (new HBox());
- hbox->pack_start (entry, true, true, 2);
- hbox->pack_start (*btn_clr, false, false, 0);
+ hbox->pack_start (_btn_run, false, false, 2);
+ hbox->pack_start (_btn_clear, false, false, 2);
+ hbox->pack_start (_btn_open, false, false, 2);
+ hbox->pack_start (_btn_save, false, false, 2);
+ hbox->pack_start (_btn_delete, false, false, 2);
+ hbox->pack_start (_btn_revert, false, false, 2);
+ hbox->pack_start (script_select, false, false, 2);
Gtk::VBox *vbox = manage (new VBox());
- vbox->pack_start (scrollwin, true, true, 0);
+ vbox->pack_start (*scrollin, true, true, 0);
vbox->pack_start (*hbox, false, false, 2);
- entry.signal_activate().connect (sigc::mem_fun (*this, &LuaWindow::entry_activated));
-
- lua.Print.connect (sigc::mem_fun (*this, &LuaWindow::append_text));
+ Gtkmm2ext::VPane *vpane = manage (new Gtkmm2ext::VPane ());
+ vpane->add (*vbox);
+ vpane->add (scrollout);
- vbox->show_all ();
- add (*vbox);
+ vpane->show_all ();
+ add (*vpane);
set_size_request (640, 480); // XXX
+ ARDOUR_UI_UTILS::set_tooltip (script_select, _("Select Editor Buffer"));
- LuaInstance::register_classes (lua.getState());
- // TODO register some callback functions.
+ setup_buffers ();
+ LuaScripting::instance().scripts_changed.connect (*this, invalidator (*this), boost::bind (&LuaWindow::refresh_scriptlist, this), gui_context());
- lua_State* L = lua.getState();
- luabridge::push <PublicEditor *> (L, &PublicEditor::instance());
- lua_setglobal (L, "Editor");
- // TODO
- // - allow to load files
- // - allow to run files directly
- // - history buffer
- // - multi-line input ??
+ Glib::RefPtr<Gtk::TextBuffer> tb (entry.get_buffer());
+ _script_changed_connection = tb->signal_changed().connect (sigc::mem_fun(*this, &LuaWindow::script_changed));
}
LuaWindow::~LuaWindow ()
{
+ delete lua;
}
void
return just_hide_it (ev, static_cast<Gtk::Window *>(this));
}
+void LuaWindow::reinit_lua ()
+{
+ ENSURE_GUI_THREAD (*this, &LuaWindow::session_going_away);
+ delete lua;
+ lua = new LuaState();
+ lua->Print.connect (sigc::mem_fun (*this, &LuaWindow::append_text));
+
+ lua_State* L = lua->getState();
+ LuaInstance::register_classes (L);
+ luabridge::push <PublicEditor *> (L, &PublicEditor::instance());
+ lua_setglobal (L, "Editor");
+}
+
void LuaWindow::set_session (Session* s)
{
SessionHandlePtr::set_session (s);
update_title ();
_session->DirtyChanged.connect (_session_connections, invalidator (*this), boost::bind (&LuaWindow::update_title, this), gui_context());
- // expose "Session" point directly
- lua_State* L = lua.getState();
+ lua_State* L = lua->getState();
LuaBindings::set_session (L, _session);
}
LuaWindow::session_going_away ()
{
ENSURE_GUI_THREAD (*this, &LuaWindow::session_going_away);
- lua.do_command ("collectgarbage();");
- //TODO: re-init lua-engine (drop all references) ??
+ reinit_lua (); // drop state (all variables, session references)
SessionHandlePtr::session_going_away ();
_session = 0;
update_title ();
- lua_State* L = lua.getState();
+ lua_State* L = lua->getState();
LuaBindings::set_session (L, _session);
}
LuaWindow::scroll_to_bottom ()
{
Gtk::Adjustment *adj;
- adj = scrollwin.get_vadjustment();
+ adj = scrollout.get_vadjustment();
adj->set_value (MAX(0,(adj->get_upper() - adj->get_page_size())));
}
void
-LuaWindow::entry_activated ()
+LuaWindow::run_script ()
{
- std::string cmd = entry.get_text();
- append_text ("> " + cmd);
-
- if (0 == lua.do_command (cmd)) {
- entry.set_text("");
+ Glib::RefPtr<Gtk::TextBuffer> tb (entry.get_buffer());
+ std::string script = tb->get_text();
+ const std::string& bytecode = LuaScripting::get_factory_bytecode (script);
+ if (bytecode.empty()) {
+ // plain script or faulty script -- run directly
+ try {
+ lua->do_command ("function ardour () end");
+ if (0 == lua->do_command (script)) {
+ append_text ("> OK");
+ }
+ } catch (luabridge::LuaException const& e) {
+ append_text (string_compose (_("LuaException: %1"), e.what()));
+ }
+ } else {
+ // script with factory method
+ try {
+ lua_State* L = lua->getState();
+ lua->do_command ("function ardour () end");
+
+ LuaScriptParamList args = LuaScriptParams::script_params (script, "action_param", false);
+ luabridge::LuaRef tbl_arg (luabridge::newTable(L));
+ LuaScriptParams::params_to_ref (&tbl_arg, args);
+ lua->do_command (script); // register "factory"
+ luabridge::LuaRef lua_factory = luabridge::getGlobal (L, "factory");
+ if (lua_factory.isFunction()) {
+ lua_factory(tbl_arg)();
+ }
+ lua->do_command ("factory = nil;");
+ } catch (luabridge::LuaException const& e) {
+ append_text (string_compose (_("LuaException: %1"), e.what()));
+ }
}
}
Glib::RefPtr<Gtk::TextBuffer> tb (outtext.get_buffer());
tb->set_text ("");
}
+
+void
+LuaWindow::new_script ()
+{
+ char buf[32];
+ snprintf (buf, sizeof (buf), "#%d", count_scratch_buffers () + 1);
+ script_buffers.push_back (ScriptBufferPtr (new LuaWindow::ScriptBuffer (buf)));
+ script_selection_changed (script_buffers.back ());
+ refresh_scriptlist ();
+}
+
+void
+LuaWindow::delete_script ()
+{
+ assert ((_current_buffer->flags & Buffer_Scratch) || !(_current_buffer->flags & Buffer_ReadOnly));
+ bool refresh = false;
+ bool neednew = true;
+ if (_current_buffer->flags & Buffer_HasFile) {
+ if (0 == ::g_unlink (_current_buffer->path.c_str())) {
+ append_text (X_("> ") + string_compose (_("Deleted %1"), _current_buffer->path));
+ refresh = true;
+ } else {
+ append_text (X_("> ") + string_compose (_("Failed to delete %1"), _current_buffer->path));
+ }
+ }
+ for (ScriptBufferList::iterator i = script_buffers.begin (); i != script_buffers.end (); ++i) {
+ if ((*i) == _current_buffer) {
+ script_buffers.erase (i);
+ break;
+ }
+ }
+
+ for (ScriptBufferList::const_iterator i = script_buffers.begin (); i != script_buffers.end (); ++i) {
+ if ((*i)->flags & Buffer_Scratch) {
+ script_selection_changed (*i);
+ neednew = false;
+ }
+ }
+ if (neednew) {
+ new_script ();
+ }
+ if (refresh) {
+ LuaScripting::instance ().refresh (true);
+ }
+}
+
+void
+LuaWindow::revert_script ()
+{
+ _current_buffer->flags &= BufferFlags(~Buffer_Valid);
+ script_selection_changed (_current_buffer, true);
+}
+
+void
+LuaWindow::import_script ()
+{
+ // TODO: dialog to select file or enter URL
+ // TODO convert a few URL (eg. pastebin) to raw.
+#if 0
+ char *url = "http://pastebin.com/raw/3UMkZ6nV";
+ char *rv = ArdourCurl::http_get (url, 0);
+ if (rv) {
+ new_script ();
+ Glib::RefPtr<Gtk::TextBuffer> tb (entry.get_buffer());
+ tb->set_text (rv);
+ _current_buffer->flags &= BufferFlags(~Buffer_Dirty);
+ update_gui_state ();
+ }
+ free (rv);
+#endif
+}
+
+void
+LuaWindow::save_script ()
+{
+ Glib::RefPtr<Gtk::TextBuffer> tb (entry.get_buffer());
+ std::string script = tb->get_text();
+ std::string msg = "Unknown error";
+
+ std::string path;
+ LuaScriptInfoPtr lsi = LuaScripting::script_info (script);
+ ScriptBuffer & sb (*_current_buffer);
+
+ assert (sb.flags & Buffer_Dirty);
+
+ // 1) check if it has a valid header and factory
+ const std::string& bytecode = LuaScripting::get_factory_bytecode (script);
+ if (bytecode.empty()) {
+ msg = _("Missing script header.\nThe script requires an '{ardour}' info table and a 'factory' function.");
+ goto errorout;
+ }
+
+ if (!LuaScripting::try_compile (script, LuaScriptParams::script_params (script, "action_param", false))) {
+ msg = _("Script fails to compile.");
+ goto errorout;
+ }
+
+ // 2) check script name & type
+ lsi = LuaScripting::script_info (script);
+ if (!lsi) {
+ msg = _("Invalid or missing script-name or script-type.");
+ goto errorout;
+ }
+
+ if (lsi->type != LuaScriptInfo::Snippet && lsi->type != LuaScriptInfo::EditorAction) {
+ msg = _("Invalid script-type.\nValid types are 'EditorAction' and 'Snippet'.");
+ goto errorout;
+ }
+
+ // 3) if there's already a writable file,...
+ if ((sb.flags & Buffer_HasFile) && !(sb.flags & Buffer_ReadOnly)) {
+ try {
+ Glib::file_set_contents (sb.path, script);
+ sb.name = lsi->name;
+ sb.flags &= BufferFlags(~Buffer_Dirty);
+ update_gui_state (); // XXX here?
+ append_text (X_("> ") + string_compose (_("Saved as %1"), sb.path));
+ return; // OK
+ } catch (Glib::FileError e) {
+ msg = string_compose (_("Error saving file: %1"), e.what());
+ goto errorout;
+ }
+ }
+
+ // 4) check if the name is unique for the given type; locally at least
+ if (true /*sb.flags & Buffer_HasFile*/) {
+ LuaScriptList& lsl (LuaScripting::instance ().scripts (lsi->type));
+ for (LuaScriptList::const_iterator s = lsl.begin(); s != lsl.end(); ++s) {
+ if ((*s)->name == lsi->name) {
+ msg = string_compose (_("Script with given name '%1' already exists.\nUse a different name in the descriptor."), lsi->name);
+ goto errorout;
+ }
+ }
+ }
+
+ // 5) construct filename -- TODO ask user for name, ask to replace file.
+ do {
+ char buf[80];
+ time_t t = time(0);
+ struct tm * timeinfo = localtime (&t);
+ strftime (buf, sizeof(buf), "%s%d", timeinfo);
+ sprintf (buf, "%s%ld", buf, random ()); // is this valid?
+ MD5 md5;
+ std::string fn = md5.digestString (buf);
+
+ switch (lsi->type) {
+ case LuaScriptInfo::EditorAction:
+ fn = "a_" + fn;
+ break;
+ case LuaScriptInfo::Snippet:
+ fn = "s_" + fn;
+ break;
+ default:
+ break;
+ }
+ path = Glib::build_filename (LuaScripting::user_script_dir (), fn.substr(0, 11) + ".lua");
+ } while (Glib::file_test (path, Glib::FILE_TEST_EXISTS));
+
+ try {
+ Glib::file_set_contents (path, script);
+ sb.path = path;
+ sb.name = lsi->name;
+ sb.flags |= Buffer_HasFile;
+ sb.flags &= BufferFlags(~Buffer_Dirty);
+ sb.flags &= BufferFlags(~Buffer_ReadOnly);
+ update_gui_state (); // XXX here? .refresh (true) may trigger this, too
+ LuaScripting::instance().refresh (true);
+ append_text (X_("> ") + string_compose (_("Saved as %1"), path));
+ return; // OK
+ } catch (Glib::FileError e) {
+ msg = string_compose (_("Error saving file: %1"), e.what());
+ goto errorout;
+ }
+
+errorout:
+ MessageDialog am (msg);
+ am.run ();
+}
+
+void
+LuaWindow::setup_buffers ()
+{
+ if (script_buffers.size() > 0) {
+ return;
+ }
+ script_buffers.push_back (ScriptBufferPtr (new LuaWindow::ScriptBuffer("#1")));
+ _current_buffer = script_buffers.front();
+
+ Glib::RefPtr<Gtk::TextBuffer> tb (entry.get_buffer());
+ tb->set_text (_current_buffer->script);
+
+ refresh_scriptlist ();
+ update_gui_state ();
+}
+
+uint32_t
+LuaWindow::count_scratch_buffers () const
+{
+ uint32_t n = 0;
+ for (ScriptBufferList::const_iterator i = script_buffers.begin (); i != script_buffers.end (); ++i) {
+ if ((*i)->flags & Buffer_Scratch) {
+ ++n;
+ }
+ }
+ return n;
+}
+
+void
+LuaWindow::refresh_scriptlist ()
+{
+ for (ScriptBufferList::iterator i = script_buffers.begin (); i != script_buffers.end ();) {
+ if ((*i)->flags & Buffer_Scratch) {
+ ++i;
+ continue;
+ }
+ i = script_buffers.erase (i);
+ }
+ LuaScriptList& lsa (LuaScripting::instance ().scripts (LuaScriptInfo::EditorAction));
+ for (LuaScriptList::const_iterator s = lsa.begin(); s != lsa.end(); ++s) {
+ script_buffers.push_back (ScriptBufferPtr (new LuaWindow::ScriptBuffer(*s)));
+ }
+
+ LuaScriptList& lss (LuaScripting::instance ().scripts (LuaScriptInfo::Snippet));
+ for (LuaScriptList::const_iterator s = lss.begin(); s != lss.end(); ++s) {
+ script_buffers.push_back (ScriptBufferPtr (new LuaWindow::ScriptBuffer(*s)));
+ }
+ rebuild_menu ();
+}
+
+void
+LuaWindow::rebuild_menu ()
+{
+ using namespace Menu_Helpers;
+
+ _menu_scratch = manage (new Menu);
+ _menu_snippet = manage (new Menu);
+ _menu_actions = manage (new Menu);
+
+ MenuList& items_scratch (_menu_scratch->items());
+ MenuList& items_snippet (_menu_snippet->items());
+ MenuList& items_actions (_menu_actions->items());
+
+ {
+ Menu_Helpers::MenuElem elem = Gtk::Menu_Helpers::MenuElem(_("New"),
+ sigc::mem_fun(*this, &LuaWindow::new_script));
+ items_scratch.push_back(elem);
+ }
+
+ items_scratch.push_back(SeparatorElem());
+
+ for (ScriptBufferList::const_iterator i = script_buffers.begin (); i != script_buffers.end (); ++i) {
+ std::string name;
+ if ((*i)->flags & Buffer_ReadOnly) {
+ name = "[R] " + (*i)->name;
+ } else {
+ name = (*i)->name;
+ }
+ Menu_Helpers::MenuElem elem = Gtk::Menu_Helpers::MenuElem(name,
+ sigc::bind(sigc::mem_fun(*this, &LuaWindow::script_selection_changed), (*i), false));
+
+ if ((*i)->flags & Buffer_Scratch) {
+ items_scratch.push_back(elem);
+ }
+ else if ((*i)->type == LuaScriptInfo::EditorAction) {
+ items_actions.push_back(elem);
+ }
+ else if ((*i)->type == LuaScriptInfo::Snippet) {
+ items_snippet.push_back(elem);
+ }
+ }
+
+ script_select.clear_items ();
+ script_select.AddMenuElem (Menu_Helpers::MenuElem ("Scratch", *_menu_scratch));
+ script_select.AddMenuElem (Menu_Helpers::MenuElem ("Snippets", *_menu_snippet));
+ script_select.AddMenuElem (Menu_Helpers::MenuElem ("Actions", *_menu_actions));
+}
+
+void
+LuaWindow::script_selection_changed (ScriptBufferPtr n, bool force)
+{
+ if (n == _current_buffer && !force) {
+ return;
+ }
+
+ Glib::RefPtr<Gtk::TextBuffer> tb (entry.get_buffer());
+
+ if (_current_buffer->flags & Buffer_Valid) {
+ _current_buffer->script = tb->get_text();
+ }
+
+ if (!(n->flags & Buffer_Valid)) {
+ if (!n->load()) {
+ append_text ("! Failed to load buffer.");
+ }
+ }
+
+ if (n->flags & Buffer_Valid) {
+ _current_buffer = n;
+ _script_changed_connection.block ();
+ tb->set_text (n->script);
+ _script_changed_connection.unblock ();
+ } else {
+ append_text ("! Failed to switch buffer.");
+ }
+ update_gui_state ();
+}
+
+void
+LuaWindow::update_gui_state ()
+{
+ const ScriptBuffer & sb (*_current_buffer);
+ std::string name;
+ if (sb.flags & Buffer_Scratch) {
+ name = string_compose (_("Scratch Buffer %1"), sb.name);
+ } else if (sb.type == LuaScriptInfo::EditorAction) {
+ name = string_compose (_("Action: '%1'"), sb.name);
+ } else if (sb.type == LuaScriptInfo::Snippet) {
+ name = string_compose (_("Snippet: %1"), sb.name);
+ } else {
+ cerr << "Invalid Script type\n";
+ assert (0);
+ return;
+ }
+ if (sb.flags & Buffer_Dirty) {
+ name += " *";
+ }
+ script_select.set_text(name);
+
+ if (sb.flags & Buffer_ReadOnly) {
+ _btn_save.set_text (_("Save as"));
+ } else {
+ _btn_save.set_text (_("Save"));
+ }
+ _btn_save.set_sensitive (sb.flags & Buffer_Dirty);
+ _btn_delete.set_sensitive (sb.flags & Buffer_Scratch || ((sb.flags & (Buffer_ReadOnly | Buffer_HasFile)) == Buffer_HasFile));
+ _btn_revert.set_sensitive ((sb.flags & Buffer_Dirty) && (sb.flags & Buffer_HasFile));
+}
+
+void
+LuaWindow::script_changed () {
+ if (_current_buffer->flags & Buffer_Dirty) {
+ return;
+ }
+ _current_buffer->flags |= Buffer_Dirty;
+ update_gui_state ();
+}
+
+LuaWindow::ScriptBuffer::ScriptBuffer (const std::string& n)
+ : name (n)
+ , flags (Buffer_Scratch | Buffer_Valid)
+{
+ script =
+ "---- this header is (only) required to save the script\n"
+ "-- ardour { [\"type\"] = \"Snippet\", name = \"\" }\n"
+ "-- function factory () return function () -- -- end end\n";
+}
+
+LuaWindow::ScriptBuffer::ScriptBuffer (LuaScriptInfoPtr p)
+ : name (p->name)
+ , path (p->path)
+ , flags (Buffer_HasFile)
+ , type (p->type)
+{
+ if (!PBD::exists_and_writable (path)) {
+ flags |= Buffer_ReadOnly;
+ }
+ if (path.find (user_config_directory ()) != 0) {
+ // mark non-user scripts as read-only
+ flags |= Buffer_ReadOnly;
+ }
+}
+
+#if 0
+LuaWindow::ScriptBuffer::ScriptBuffer (const ScriptBuffer& other)
+ : script (other.script)
+ , name (other.name)
+ , path (other.path)
+ , flags (other.flags)
+ , type (other.type)
+{
+}
+#endif
+
+LuaWindow::ScriptBuffer::~ScriptBuffer ()
+{
+}
+
+bool
+LuaWindow::ScriptBuffer::load ()
+{
+ assert (!(flags & Buffer_Valid));
+ if (!(flags & Buffer_HasFile)) return false;
+ try {
+ script = Glib::file_get_contents (path);
+ flags |= Buffer_Valid;
+ flags &= BufferFlags(~Buffer_Dirty);
+ } catch (Glib::FileError e) {
+ return false;
+ }
+ return true;
+}