Robin Gareus
4050ca5633
Copyright-holder and year information is extracted from git log. git history begins in 2005. So (C) from 1998..2005 is lost. Also some (C) assignment of commits where the committer didn't use --author.
762 lines
21 KiB
C++
762 lines
21 KiB
C++
/*
|
|
* Copyright (C) 2016-2017 Robin Gareus <robin@gareus.org>
|
|
* Copyright (C) 2016-2018 Paul Davis <paul@linuxaudiosystems.com>
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License along
|
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
*/
|
|
|
|
#ifdef PLATFORM_WINDOWS
|
|
#define random() rand()
|
|
#endif
|
|
|
|
#ifdef WAF_BUILD
|
|
#include "gtk2ardour-config.h"
|
|
#endif
|
|
|
|
#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/utils.h"
|
|
#include "gtkmm2ext/window_title.h"
|
|
|
|
#include "widgets/pane.h"
|
|
#include "widgets/tooltips.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 "utils.h"
|
|
#include "ui_config.h"
|
|
#include "utils_videotl.h"
|
|
|
|
#include "pbd/i18n.h"
|
|
|
|
using namespace ARDOUR;
|
|
using namespace PBD;
|
|
using namespace Gtk;
|
|
using namespace Glib;
|
|
using namespace Gtkmm2ext;
|
|
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::instance ()
|
|
{
|
|
if (!_instance) {
|
|
_instance = new LuaWindow;
|
|
}
|
|
|
|
return _instance;
|
|
}
|
|
|
|
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);
|
|
|
|
#ifdef __APPLE__
|
|
set_type_hint (Gdk::WINDOW_TYPE_HINT_DIALOG);
|
|
#else
|
|
if (UIConfiguration::instance().get_all_floating_windows_are_dialogs()) {
|
|
set_type_hint (Gdk::WINDOW_TYPE_HINT_DIALOG);
|
|
} else {
|
|
set_type_hint (Gdk::WINDOW_TYPE_HINT_UTILITY);
|
|
}
|
|
#endif
|
|
|
|
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));
|
|
|
|
_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);
|
|
|
|
// 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 (_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 (*scrollin, true, true, 0);
|
|
vbox->pack_start (*hbox, false, false, 2);
|
|
|
|
ArdourWidgets::VPane *vpane = manage (new ArdourWidgets::VPane ());
|
|
vpane->add (*vbox);
|
|
vpane->add (scrollout);
|
|
vpane->set_divider (0, 0.75);
|
|
|
|
vpane->show_all ();
|
|
add (*vpane);
|
|
set_size_request (640, 480); // XXX
|
|
ArdourWidgets::set_tooltip (script_select, _("Select Editor Buffer"));
|
|
|
|
setup_buffers ();
|
|
LuaScripting::instance().scripts_changed.connect (*this, invalidator (*this), boost::bind (&LuaWindow::refresh_scriptlist, this), gui_context());
|
|
|
|
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
|
|
LuaWindow::show_window ()
|
|
{
|
|
present();
|
|
_visible = true;
|
|
}
|
|
|
|
bool
|
|
LuaWindow::hide_window (GdkEventAny *ev)
|
|
{
|
|
if (!_visible) return 0;
|
|
_visible = false;
|
|
return ARDOUR_UI_UTILS::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->sandbox (false);
|
|
|
|
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);
|
|
if (!_session) {
|
|
return;
|
|
}
|
|
|
|
update_title ();
|
|
_session->DirtyChanged.connect (_session_connections, invalidator (*this), boost::bind (&LuaWindow::update_title, this), gui_context());
|
|
|
|
lua_State* L = lua->getState();
|
|
LuaBindings::set_session (L, _session);
|
|
}
|
|
|
|
void
|
|
LuaWindow::session_going_away ()
|
|
{
|
|
ENSURE_GUI_THREAD (*this, &LuaWindow::session_going_away);
|
|
reinit_lua (); // drop state (all variables, session references)
|
|
|
|
SessionHandlePtr::session_going_away ();
|
|
_session = 0;
|
|
update_title ();
|
|
|
|
lua_State* L = lua->getState();
|
|
LuaBindings::set_session (L, _session);
|
|
}
|
|
|
|
void
|
|
LuaWindow::update_title ()
|
|
{
|
|
if (_session) {
|
|
string n;
|
|
|
|
if (_session->snap_name() != _session->name()) {
|
|
n = _session->snap_name ();
|
|
} else {
|
|
n = _session->name ();
|
|
}
|
|
|
|
if (_session->dirty ()) {
|
|
n = "*" + n;
|
|
}
|
|
|
|
WindowTitle title (n);
|
|
title += S_("Window|Lua");
|
|
title += Glib::get_application_name ();
|
|
set_title (title.get_string());
|
|
|
|
} else {
|
|
WindowTitle title (S_("Window|Lua"));
|
|
title += Glib::get_application_name ();
|
|
set_title (title.get_string());
|
|
}
|
|
}
|
|
|
|
void
|
|
LuaWindow::scroll_to_bottom ()
|
|
{
|
|
Gtk::Adjustment *adj;
|
|
adj = scrollout.get_vadjustment();
|
|
adj->set_value (MAX(0,(adj->get_upper() - adj->get_page_size())));
|
|
}
|
|
|
|
void
|
|
LuaWindow::run_script ()
|
|
{
|
|
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()));
|
|
} catch (Glib::Exception const& e) {
|
|
append_text (string_compose (_("Glib Exception: %1"), e.what()));
|
|
} catch (std::exception const& e) {
|
|
append_text (string_compose (_("C++ Exception: %1"), e.what()));
|
|
} catch (...) {
|
|
append_text (string_compose (_("C++ Exception: %1"), "..."));
|
|
}
|
|
} 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()));
|
|
} catch (Glib::Exception const& e) {
|
|
append_text (string_compose (_("Glib Exception: %1"), e.what()));
|
|
} catch (std::exception const& e) {
|
|
append_text (string_compose (_("C++ Exception: %1"), e.what()));
|
|
} catch (...) {
|
|
append_text (string_compose (_("C++ Exception: %1"), "..."));
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
LuaWindow::append_text (std::string s)
|
|
{
|
|
Glib::RefPtr<Gtk::TextBuffer> tb (outtext.get_buffer());
|
|
tb->insert (tb->end(), s + "\n");
|
|
scroll_to_bottom ();
|
|
Gtkmm2ext::UI::instance()->flush_pending (0.05);
|
|
}
|
|
|
|
void
|
|
LuaWindow::clear_output ()
|
|
{
|
|
Glib::RefPtr<Gtk::TextBuffer> tb (outtext.get_buffer());
|
|
tb->set_text ("");
|
|
}
|
|
|
|
void
|
|
LuaWindow::edit_script (const std::string& name, const std::string& script)
|
|
{
|
|
ScriptBuffer* sb = new LuaWindow::ScriptBuffer (name);
|
|
sb->script = script;
|
|
script_buffers.push_back (ScriptBufferPtr (sb));
|
|
script_selection_changed (script_buffers.back ());
|
|
refresh_scriptlist ();
|
|
show_window ();
|
|
}
|
|
|
|
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. true);
|
|
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 const& 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 tme[80];
|
|
char buf[80];
|
|
time_t t = time(0);
|
|
struct tm * timeinfo = localtime (&t);
|
|
strftime (tme, sizeof(tme), "%s", timeinfo);
|
|
snprintf (buf, sizeof(buf), "%s%ld", tme, random ());
|
|
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 const& 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 const& e) {
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|