summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDavid Robillard <d@drobilla.net>2012-03-16 22:27:16 +0000
committerDavid Robillard <d@drobilla.net>2012-03-16 22:27:16 +0000
commitbc3afd8380d59c750c8f8e9bf1ed1b8d4a6826e9 (patch)
treeb42f56620ce85f6207568eadfb901360436c6f74
parent7126f005be3e49818dafe0d2666b6745e09f8aff (diff)
downloadingen-bc3afd8380d59c750c8f8e9bf1ed1b8d4a6826e9.tar.gz
ingen-bc3afd8380d59c750c8f8e9bf1ed1b8d4a6826e9.tar.bz2
ingen-bc3afd8380d59c750c8f8e9bf1ed1b8d4a6826e9.zip
Preliminary work towards native LV2 UI.
git-svn-id: http://svn.drobilla.net/lad/trunk/ingen@4074 a436a847-0d15-0410-975c-d299462d15a1
-rw-r--r--ingen.lv2/manifest.ttl17
-rw-r--r--ingen/shared/AtomSink.hpp (renamed from src/shared/AtomSink.hpp)6
-rw-r--r--ingen/shared/AtomWriter.hpp (renamed from src/shared/AtomWriter.hpp)8
-rw-r--r--ingen/shared/URIs.hpp2
-rw-r--r--src/client/ClientStore.cpp2
-rw-r--r--src/client/PluginUI.cpp3
-rw-r--r--src/gui/App.cpp7
-rw-r--r--src/gui/App.hpp2
-rw-r--r--src/gui/ConnectWindow.cpp2
-rw-r--r--src/gui/PatchBox.cpp710
-rw-r--r--src/gui/PatchBox.hpp170
-rw-r--r--src/gui/PatchView.hpp2
-rw-r--r--src/gui/PatchWindow.cpp647
-rw-r--r--src/gui/PatchWindow.hpp133
-rw-r--r--src/gui/Port.cpp26
-rw-r--r--src/gui/Port.hpp4
-rw-r--r--src/gui/WindowFactory.cpp18
-rw-r--r--src/gui/WindowFactory.hpp9
-rw-r--r--src/gui/ingen_gui_lv2.cpp179
-rw-r--r--src/gui/wscript11
-rw-r--r--src/server/ServerInterfaceImpl.hpp2
-rw-r--r--src/server/events/Get.cpp1
-rw-r--r--src/shared/AtomWriter.cpp48
-rw-r--r--src/shared/LV2Atom.cpp4
-rw-r--r--src/shared/URIs.cpp2
25 files changed, 1226 insertions, 789 deletions
diff --git a/ingen.lv2/manifest.ttl b/ingen.lv2/manifest.ttl
index 1789c078..da4adeac 100644
--- a/ingen.lv2/manifest.ttl
+++ b/ingen.lv2/manifest.ttl
@@ -1,28 +1,39 @@
@prefix ingen: <http://drobilla.net/ns/ingen#> .
-@prefix lv2: <http://lv2plug.in/ns/lv2core#> .
-@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
-@prefix templates: <http://drobilla.net/software/ingen/templates/> .
+@prefix lv2: <http://lv2plug.in/ns/lv2core#> .
+@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
+@prefix ui: <http://lv2plug.in/ns/extensions/ui#> .
+@prefix urid: <http://lv2plug.in/ns/ext/urid#> .
+
+<http://drobilla.net/ns/ingen#ui>
+ a ui:GtkUI ;
+ ui:binary <libingen_gui_lv2.so> ;
+ lv2:requiredFeature urid:map ,
+ urid:unmap .
<mono_synth.ttl>
a lv2:Plugin ,
ingen:Patch ;
rdfs:seeAlso <mono_synth.ttl> ;
+ ui:ui <http://drobilla.net/ns/ingen#ui> ;
lv2:binary <libingen_lv2.so> .
<mono_effect.ttl>
a lv2:Plugin ,
ingen:Patch ;
rdfs:seeAlso <mono_effect.ttl> ;
+ ui:ui <http://drobilla.net/ns/ingen#ui> ;
lv2:binary <libingen_lv2.so> .
<stereo_synth.ttl>
a lv2:Plugin ,
ingen:Patch ;
rdfs:seeAlso <stereo_synth.ttl> ;
+ ui:ui <http://drobilla.net/ns/ingen#ui> ;
lv2:binary <libingen_lv2.so> .
<stereo_effect.ttl>
a lv2:Plugin ,
ingen:Patch ;
rdfs:seeAlso <stereo_effect.ttl> ;
+ ui:ui <http://drobilla.net/ns/ingen#ui> ;
lv2:binary <libingen_lv2.so> .
diff --git a/src/shared/AtomSink.hpp b/ingen/shared/AtomSink.hpp
index 5bc5aef6..e8192567 100644
--- a/src/shared/AtomSink.hpp
+++ b/ingen/shared/AtomSink.hpp
@@ -18,12 +18,14 @@
#ifndef INGEN_ATOMSINK_HPP
#define INGEN_ATOMSINK_HPP
+#include "lv2/lv2plug.in/ns/ext/atom/atom.h"
+
namespace Ingen {
namespace Shared {
-class AtomSink
-{
+class AtomSink {
public:
+ virtual ~AtomSink() {}
virtual void write(const LV2_Atom* msg) = 0;
};
diff --git a/src/shared/AtomWriter.hpp b/ingen/shared/AtomWriter.hpp
index 2983848c..3ba59583 100644
--- a/src/shared/AtomWriter.hpp
+++ b/ingen/shared/AtomWriter.hpp
@@ -22,15 +22,18 @@
#include "ingen/shared/LV2URIMap.hpp"
#include "ingen/shared/URIs.hpp"
#include "lv2/lv2plug.in/ns/ext/atom/forge.h"
+#include "serd/serd.h"
namespace Ingen {
namespace Shared {
+class AtomSink;
+
/** An Interface that writes LV2 atoms. */
class AtomWriter : public Interface
{
public:
- AtomWriter(LV2URIMap& map, URIs& uris);
+ AtomWriter(LV2URIMap& map, URIs& uris, AtomSink& sink);
~AtomWriter() {}
Raul::URI uri() const { return "http://drobilla.net/ns/ingen#AtomWriter"; }
@@ -74,10 +77,13 @@ public:
void error(const std::string& msg);
private:
+ void finish_msg();
int32_t next_id();
LV2URIMap& _map;
URIs& _uris;
+ AtomSink& _sink;
+ SerdChunk _out;
LV2_Atom_Forge _forge;
int32_t _id;
};
diff --git a/ingen/shared/URIs.hpp b/ingen/shared/URIs.hpp
index 22c46f15..4f95c6c7 100644
--- a/ingen/shared/URIs.hpp
+++ b/ingen/shared/URIs.hpp
@@ -44,7 +44,7 @@ public:
const Quark atom_Bool;
const Quark atom_Float;
- const Quark atom_Int32;
+ const Quark atom_Int;
const Quark atom_MessagePort;
const Quark atom_String;
const Quark atom_ValuePort;
diff --git a/src/client/ClientStore.cpp b/src/client/ClientStore.cpp
index 83815187..ecf03dad 100644
--- a/src/client/ClientStore.cpp
+++ b/src/client/ClientStore.cpp
@@ -31,7 +31,7 @@
#define LOG(s) s << "[ClientStore] "
-//#define INGEN_CLIENT_STORE_DUMP 1
+// #define INGEN_CLIENT_STORE_DUMP 1
using namespace std;
using namespace Raul;
diff --git a/src/client/PluginUI.cpp b/src/client/PluginUI.cpp
index a3a2dabb..c20d41b1 100644
--- a/src/client/PluginUI.cpp
+++ b/src/client/PluginUI.cpp
@@ -94,10 +94,13 @@ lv2_ui_write(SuilController controller,
}
} else if (format == uris.atom_eventTransfer.id) {
+ std::cerr << "FIXME: atom event transfer" << std::endl;
+ #if 0
LV2_Atom* buf = (LV2_Atom*)buffer;
Raul::Atom val;
Shared::LV2Atom::to_atom(uris, buf, val);
ui->world()->engine()->set_property(port->path(), uris.ingen_value, val);
+ #endif
} else {
warn << "Unknown value format " << format
diff --git a/src/gui/App.cpp b/src/gui/App.cpp
index 620aceb4..ad70a7f9 100644
--- a/src/gui/App.cpp
+++ b/src/gui/App.cpp
@@ -317,14 +317,17 @@ App::show_about()
* @return true iff the application quit.
*/
bool
-App::quit(Gtk::Window& dialog_parent)
+App::quit(Gtk::Window* dialog_parent)
{
bool quit = true;
if (_world->local_engine()) {
- Gtk::MessageDialog d(dialog_parent,
+ Gtk::MessageDialog d(
"The engine is running in this process. Quitting will terminate Ingen."
"\n\n" "Are you sure you want to quit?",
true, Gtk::MESSAGE_WARNING, Gtk::BUTTONS_NONE, true);
+ if (dialog_parent) {
+ d.set_transient_for(*dialog_parent);
+ }
d.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
d.add_button(Gtk::Stock::QUIT, Gtk::RESPONSE_CLOSE);
quit = (d.run() == Gtk::RESPONSE_CLOSE);
diff --git a/src/gui/App.hpp b/src/gui/App.hpp
index 6f2cd592..7b45a6e8 100644
--- a/src/gui/App.hpp
+++ b/src/gui/App.hpp
@@ -81,7 +81,7 @@ public:
bool gtk_main_iteration();
void show_about();
- bool quit(Gtk::Window& dialog_parent);
+ bool quit(Gtk::Window* dialog_parent);
void port_activity(Port* port);
void activity_port_destroyed(Port* port);
diff --git a/src/gui/ConnectWindow.cpp b/src/gui/ConnectWindow.cpp
index dfb285a4..8e4b7cc3 100644
--- a/src/gui/ConnectWindow.cpp
+++ b/src/gui/ConnectWindow.cpp
@@ -321,7 +321,7 @@ ConnectWindow::on_hide()
void
ConnectWindow::quit_clicked()
{
- if (_app->quit(*this))
+ if (_app->quit(this))
_quit_flag = true;
}
diff --git a/src/gui/PatchBox.cpp b/src/gui/PatchBox.cpp
new file mode 100644
index 00000000..127da746
--- /dev/null
+++ b/src/gui/PatchBox.cpp
@@ -0,0 +1,710 @@
+/* This file is part of Ingen.
+ * Copyright 2007-2012 David Robillard <http://drobilla.net>
+ *
+ * Ingen 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.
+ *
+ * Ingen 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 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 St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <cassert>
+#include <sstream>
+
+#include <boost/format.hpp>
+#include <glib/gstdio.h>
+#include <glibmm/fileutils.h>
+
+#include "raul/AtomRDF.hpp"
+
+#include "ingen/Interface.hpp"
+#include "ingen/client/ClientStore.hpp"
+#include "ingen/client/PatchModel.hpp"
+#include "ingen/shared/LV2URIMap.hpp"
+
+#include "App.hpp"
+#include "BreadCrumbs.hpp"
+#include "Configuration.hpp"
+#include "ConnectWindow.hpp"
+#include "LoadPatchWindow.hpp"
+#include "LoadPluginWindow.hpp"
+#include "MessagesWindow.hpp"
+#include "NewSubpatchWindow.hpp"
+#include "NodeControlWindow.hpp"
+#include "PatchCanvas.hpp"
+#include "PatchTreeWindow.hpp"
+#include "PatchView.hpp"
+#include "PatchWindow.hpp"
+#include "ThreadedLoader.hpp"
+#include "WindowFactory.hpp"
+#include "ingen_config.h"
+
+#ifdef HAVE_WEBKIT
+#include <webkit/webkit.h>
+#endif
+
+using namespace Raul;
+
+namespace Ingen {
+namespace GUI {
+
+static const int STATUS_CONTEXT_ENGINE = 0;
+static const int STATUS_CONTEXT_PATCH = 1;
+static const int STATUS_CONTEXT_HOVER = 2;
+
+PatchBox::PatchBox(BaseObjectType* cobject,
+ const Glib::RefPtr<Gtk::Builder>& xml)
+ : Gtk::VBox(cobject)
+ , _app(NULL)
+ , _window(NULL)
+ , _breadcrumbs(NULL)
+ , _has_shown_documentation(false)
+ , _enable_signal(true)
+{
+ property_visible() = false;
+
+ xml->get_widget("patch_win_alignment", _alignment);
+ xml->get_widget("patch_win_status_bar", _status_bar);
+ //xml->get_widget("patch_win_status_bar", _status_bar);
+ //xml->get_widget("patch_open_menuitem", _menu_open);
+ xml->get_widget("patch_import_menuitem", _menu_import);
+ //xml->get_widget("patch_open_into_menuitem", _menu_open_into);
+ xml->get_widget("patch_save_menuitem", _menu_save);
+ xml->get_widget("patch_save_as_menuitem", _menu_save_as);
+ xml->get_widget("patch_draw_menuitem", _menu_draw);
+ xml->get_widget("patch_edit_controls_menuitem", _menu_edit_controls);
+ xml->get_widget("patch_cut_menuitem", _menu_cut);
+ xml->get_widget("patch_copy_menuitem", _menu_copy);
+ xml->get_widget("patch_paste_menuitem", _menu_paste);
+ xml->get_widget("patch_delete_menuitem", _menu_delete);
+ xml->get_widget("patch_select_all_menuitem", _menu_select_all);
+ xml->get_widget("patch_close_menuitem", _menu_close);
+ xml->get_widget("patch_quit_menuitem", _menu_quit);
+ xml->get_widget("patch_view_control_window_menuitem", _menu_view_control_window);
+ xml->get_widget("patch_view_engine_window_menuitem", _menu_view_engine_window);
+ xml->get_widget("patch_properties_menuitem", _menu_view_patch_properties);
+ xml->get_widget("patch_fullscreen_menuitem", _menu_fullscreen);
+ xml->get_widget("patch_human_names_menuitem", _menu_human_names);
+ xml->get_widget("patch_show_port_names_menuitem", _menu_show_port_names);
+ xml->get_widget("patch_zoom_in_menuitem", _menu_zoom_in);
+ xml->get_widget("patch_zoom_out_menuitem", _menu_zoom_out);
+ xml->get_widget("patch_zoom_normal_menuitem", _menu_zoom_normal);
+ xml->get_widget("patch_status_bar_menuitem", _menu_show_status_bar);
+ xml->get_widget("patch_arrange_menuitem", _menu_arrange);
+ xml->get_widget("patch_view_messages_window_menuitem", _menu_view_messages_window);
+ xml->get_widget("patch_view_patch_tree_window_menuitem", _menu_view_patch_tree_window);
+ xml->get_widget("patch_help_about_menuitem", _menu_help_about);
+ xml->get_widget("patch_documentation_paned", _doc_paned);
+ xml->get_widget("patch_documentation_scrolledwindow", _doc_scrolledwindow);
+
+ _menu_view_control_window->property_sensitive() = false;
+ _menu_import->signal_activate().connect(
+ sigc::mem_fun(this, &PatchBox::event_import));
+ _menu_save->signal_activate().connect(
+ sigc::mem_fun(this, &PatchBox::event_save));
+ _menu_save_as->signal_activate().connect(
+ sigc::mem_fun(this, &PatchBox::event_save_as));
+ _menu_draw->signal_activate().connect(
+ sigc::mem_fun(this, &PatchBox::event_draw));
+ _menu_edit_controls->signal_activate().connect(
+ sigc::mem_fun(this, &PatchBox::event_edit_controls));
+ _menu_copy->signal_activate().connect(
+ sigc::mem_fun(this, &PatchBox::event_copy));
+ _menu_paste->signal_activate().connect(
+ sigc::mem_fun(this, &PatchBox::event_paste));
+ _menu_delete->signal_activate().connect(
+ sigc::mem_fun(this, &PatchBox::event_delete));
+ _menu_select_all->signal_activate().connect(
+ sigc::mem_fun(this, &PatchBox::event_select_all));
+ _menu_close->signal_activate().connect(
+ sigc::mem_fun(this, &PatchBox::event_close));
+ _menu_quit->signal_activate().connect(
+ sigc::mem_fun(this, &PatchBox::event_quit));
+ _menu_fullscreen->signal_activate().connect(
+ sigc::mem_fun(this, &PatchBox::event_fullscreen_toggled));
+ _menu_human_names->signal_activate().connect(
+ sigc::mem_fun(this, &PatchBox::event_human_names_toggled));
+ _menu_show_status_bar->signal_activate().connect(
+ sigc::mem_fun(this, &PatchBox::event_status_bar_toggled));
+ _menu_show_port_names->signal_activate().connect(
+ sigc::mem_fun(this, &PatchBox::event_port_names_toggled));
+ _menu_arrange->signal_activate().connect(
+ sigc::mem_fun(this, &PatchBox::event_arrange));
+ _menu_quit->signal_activate().connect(
+ sigc::mem_fun(this, &PatchBox::event_quit));
+ _menu_zoom_in->signal_activate().connect(
+ sigc::mem_fun(this, &PatchBox::event_zoom_in));
+ _menu_zoom_out->signal_activate().connect(
+ sigc::mem_fun(this, &PatchBox::event_zoom_out));
+ _menu_zoom_normal->signal_activate().connect(
+ sigc::mem_fun(this, &PatchBox::event_zoom_normal));
+ _menu_view_engine_window->signal_activate().connect(
+ sigc::mem_fun(this, &PatchBox::event_show_engine));
+ _menu_view_control_window->signal_activate().connect(
+ sigc::mem_fun(this, &PatchBox::event_show_controls));
+ _menu_view_patch_properties->signal_activate().connect(
+ sigc::mem_fun(this, &PatchBox::event_show_properties));
+
+ Glib::RefPtr<Gtk::Clipboard> clipboard = Gtk::Clipboard::get();
+ clipboard->signal_owner_change().connect(
+ sigc::mem_fun(this, &PatchBox::event_clipboard_changed));
+}
+
+PatchBox::~PatchBox()
+{
+ delete _breadcrumbs;
+}
+
+void
+PatchBox::init_box(App& app)
+{
+ _app = &app;
+
+ string engine_name = _app->engine()->uri().str();
+ if (engine_name == "http://drobilla.net/ns/ingen#internal") {
+ engine_name = "internal engine";
+ }
+ _status_bar->push(string("Connected to ") + engine_name, STATUS_CONTEXT_ENGINE);
+
+ _menu_view_messages_window->signal_activate().connect(
+ sigc::mem_fun<void>(_app->messages_dialog(), &MessagesWindow::present));
+ _menu_view_patch_tree_window->signal_activate().connect(
+ sigc::mem_fun<void>(_app->patch_tree(), &PatchTreeWindow::present));
+
+ _menu_help_about->signal_activate().connect(sigc::hide_return(
+ sigc::mem_fun(_app, &App::show_about)));
+
+ _breadcrumbs = new BreadCrumbs(*_app);
+ _breadcrumbs->signal_patch_selected.connect(
+ sigc::mem_fun(this, &PatchBox::set_patch_from_path));
+}
+
+void
+PatchBox::set_patch_from_path(const Raul::Path& path, SharedPtr<PatchView> view)
+{
+ std::cerr << "FIXME: Set patch from path" << std::endl;
+}
+
+/** Sets the patch for this box and initializes everything.
+ *
+ * If @a view is NULL, a new view will be created.
+ */
+void
+PatchBox::set_patch(SharedPtr<const PatchModel> patch,
+ SharedPtr<PatchView> view)
+{
+ if (!patch || patch == _patch)
+ return;
+
+ _enable_signal = false;
+
+ new_port_connection.disconnect();
+ removed_port_connection.disconnect();
+ edit_mode_connection.disconnect();
+ _entered_connection.disconnect();
+ _left_connection.disconnect();
+
+ _status_bar->pop(STATUS_CONTEXT_PATCH);
+
+ _patch = patch;
+ _view = view;
+
+ if (!_view)
+ _view = _breadcrumbs->view(patch->path());
+
+ if (!_view)
+ _view = PatchView::create(*_app, patch);
+
+ assert(_view);
+
+ // Add view to our alignment
+ if (_view->get_parent())
+ _view->get_parent()->remove(*_view.get());
+
+ _alignment->remove();
+ _alignment->add(*_view.get());
+
+ if (_breadcrumbs->get_parent())
+ _breadcrumbs->get_parent()->remove(*_breadcrumbs);
+
+ _view->breadcrumb_container()->remove();
+ _view->breadcrumb_container()->add(*_breadcrumbs);
+ _view->breadcrumb_container()->show();
+
+ _breadcrumbs->build(patch->path(), _view);
+ _breadcrumbs->show();
+
+ _menu_view_control_window->property_sensitive() = false;
+
+ for (NodeModel::Ports::const_iterator p = patch->ports().begin();
+ p != patch->ports().end(); ++p) {
+ if (_app->can_control(p->get())) {
+ _menu_view_control_window->property_sensitive() = true;
+ break;
+ }
+ }
+
+ new_port_connection = patch->signal_new_port().connect(
+ sigc::mem_fun(this, &PatchBox::patch_port_added));
+ removed_port_connection = patch->signal_removed_port().connect(
+ sigc::mem_fun(this, &PatchBox::patch_port_removed));
+ removed_port_connection = patch->signal_editable().connect(
+ sigc::mem_fun(this, &PatchBox::editable_changed));
+
+ show();
+ _alignment->show_all();
+
+ _view->signal_object_entered.connect(
+ sigc::mem_fun(this, &PatchBox::object_entered));
+ _view->signal_object_left.connect(
+ sigc::mem_fun(this, &PatchBox::object_left));
+
+ _enable_signal = true;
+}
+
+void
+PatchBox::patch_port_added(SharedPtr<const PortModel> port)
+{
+ if (port->is_input() && _app->can_control(port.get())) {
+ _menu_view_control_window->property_sensitive() = true;
+ }
+}
+
+void
+PatchBox::patch_port_removed(SharedPtr<const PortModel> port)
+{
+ if (!(port->is_input() && _app->can_control(port.get())))
+ return;
+
+ for (NodeModel::Ports::const_iterator i = _patch->ports().begin();
+ i != _patch->ports().end(); ++i) {
+ if ((*i)->is_input() && _app->can_control(i->get())) {
+ _menu_view_control_window->property_sensitive() = true;
+ return;
+ }
+ }
+
+ _menu_view_control_window->property_sensitive() = false;
+}
+
+void
+PatchBox::show_documentation(const std::string& doc, bool html)
+{
+#ifdef HAVE_WEBKIT
+ WebKitWebView* view = WEBKIT_WEB_VIEW(webkit_web_view_new());
+ webkit_web_view_load_html_string(view, doc.c_str(), "");
+ _doc_scrolledwindow->add(*Gtk::manage(Glib::wrap(GTK_WIDGET(view))));
+ _doc_scrolledwindow->show_all();
+#else
+ Gtk::TextView* view = Gtk::manage(new Gtk::TextView());
+ view->get_buffer()->set_text(doc);
+ _doc_scrolledwindow->add(*view);
+ _doc_scrolledwindow->show_all();
+#endif
+ if (!_has_shown_documentation) {
+ const Gtk::Allocation allocation = get_allocation();
+ _doc_paned->set_position(allocation.get_width() / 1.61803399);
+ }
+ _has_shown_documentation = true;
+}
+
+void
+PatchBox::hide_documentation()
+{
+ _doc_scrolledwindow->remove();
+ _doc_scrolledwindow->hide();
+}
+
+void
+PatchBox::show_status(const ObjectModel* model)
+{
+ std::stringstream msg;
+ msg << model->path().chop_scheme();
+
+ const PortModel* port = 0;
+ const NodeModel* node = 0;
+
+ if ((port = dynamic_cast<const PortModel*>(model))) {
+ show_port_status(port, port->value());
+
+ } else if ((node = dynamic_cast<const NodeModel*>(model))) {
+ const PluginModel* plugin = dynamic_cast<const PluginModel*>(node->plugin());
+ if (plugin)
+ msg << ((boost::format(" (%1%)") % plugin->human_name()).str());
+ _status_bar->push(msg.str(), STATUS_CONTEXT_HOVER);
+ }
+}
+
+void
+PatchBox::show_port_status(const PortModel* port, const Raul::Atom& value)
+{
+ std::stringstream msg;
+ msg << port->path().chop_scheme();
+
+ const NodeModel* parent = dynamic_cast<const NodeModel*>(port->parent().get());
+ if (parent) {
+ const PluginModel* plugin = dynamic_cast<const PluginModel*>(parent->plugin());
+ if (plugin) {
+ const string& human_name = plugin->port_human_name(port->index());
+ if (!human_name.empty())
+ msg << " (" << human_name << ")";
+ }
+ }
+
+ if (value.is_valid()) {
+ msg << " = " << value;
+ }
+
+ _status_bar->pop(STATUS_CONTEXT_HOVER);
+ _status_bar->push(msg.str(), STATUS_CONTEXT_HOVER);
+}
+
+void
+PatchBox::object_entered(const ObjectModel* model)
+{
+ show_status(model);
+}
+
+void
+PatchBox::object_left(const ObjectModel* model)
+{
+ _status_bar->pop(STATUS_CONTEXT_PATCH);
+ _status_bar->pop(STATUS_CONTEXT_HOVER);
+}
+
+void
+PatchBox::editable_changed(bool editable)
+{
+ _menu_edit_controls->set_active(editable);
+}
+
+void
+PatchBox::event_show_engine()
+{
+ if (_patch)
+ _app->connect_window()->show();
+}
+
+void
+PatchBox::event_clipboard_changed(GdkEventOwnerChange* ev)
+{
+ Glib::RefPtr<Gtk::Clipboard> clipboard = Gtk::Clipboard::get();
+ _menu_paste->set_sensitive(clipboard->wait_is_text_available());
+}
+
+void
+PatchBox::event_show_controls()
+{
+ _app->window_factory()->present_controls(_patch);
+}
+
+void
+PatchBox::event_show_properties()
+{
+ _app->window_factory()->present_properties(_patch);
+}
+
+void
+PatchBox::event_import()
+{
+ _app->window_factory()->present_load_patch(_patch);
+}
+
+void
+PatchBox::event_save()
+{
+ const Raul::Atom& document = _patch->get_property(_app->uris().ingen_document);
+ if (!document.is_valid() || document.type() != Raul::Atom::URI) {
+ event_save_as();
+ } else {
+ _app->loader()->save_patch(_patch, document.get_uri());
+ _status_bar->push(
+ (boost::format("Saved %1% to %2%") % _patch->path().chop_scheme()
+ % document.get_uri()).str(),
+ STATUS_CONTEXT_PATCH);
+ }
+}
+
+int
+PatchBox::message_dialog(const Glib::ustring& message,
+ const Glib::ustring& secondary_text,
+ Gtk::MessageType type,
+ Gtk::ButtonsType buttons)
+{
+ Gtk::MessageDialog dialog(message, true, type, buttons, true);
+ dialog.set_secondary_text(secondary_text);
+ if (_window) {
+ dialog.set_transient_for(*_window);
+ }
+ return dialog.run();
+}
+
+void
+PatchBox::event_save_as()
+{
+ const Shared::URIs& uris = _app->uris();
+ while (true) {
+ Gtk::FileChooserDialog dialog("Save Patch", Gtk::FILE_CHOOSER_ACTION_SAVE);
+ if (_window) {
+ dialog.set_transient_for(*_window);
+ }
+
+ dialog.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
+ Gtk::Button* save_button = dialog.add_button(Gtk::Stock::SAVE, Gtk::RESPONSE_OK);
+ save_button->property_has_default() = true;
+
+ Gtk::FileFilter filt;
+ filt.add_pattern("*.ingen");
+ filt.set_name("Ingen bundles");
+ dialog.set_filter(filt);
+
+ // Set current folder to most sensible default
+ const Raul::Atom& document = _patch->get_property(uris.ingen_document);
+ if (document.type() == Raul::Atom::URI)
+ dialog.set_uri(document.get_uri());
+ else if (_app->configuration()->patch_folder().length() > 0)
+ dialog.set_current_folder(_app->configuration()->patch_folder());
+
+ if (dialog.run() != Gtk::RESPONSE_OK)
+ break;
+
+ std::string filename = dialog.get_filename();
+ std::string basename = Glib::path_get_basename(filename);
+
+ if (basename.find('.') == string::npos) {
+ filename += ".ingen";
+ basename += ".ingen";
+ } else if (filename.substr(filename.length() - 10) != ".ingen") {
+ message_dialog(
+ "<b>Ingen patches must be saved to Ingen bundles (*.ingen).</b>",
+ "", Gtk::MESSAGE_ERROR, Gtk::BUTTONS_OK);
+ continue;
+ }
+
+ const std::string symbol(basename.substr(0, basename.find('.')));
+
+ if (!Symbol::is_valid(symbol)) {
+ message_dialog(
+ "<b>Ingen bundle names must be valid symbols.</b>",
+ "All characters must be _, a-z, A-Z, or 0-9, but the first may not be 0-9.",
+ Gtk::MESSAGE_ERROR, Gtk::BUTTONS_OK);
+ continue;
+ }
+
+ //_patch->set_property(uris.lv2_symbol, Atom(symbol.c_str()));
+
+ bool confirm = true;
+ if (Glib::file_test(filename, Glib::FILE_TEST_IS_DIR)) {
+ if (Glib::file_test(Glib::build_filename(filename, "manifest.ttl"),
+ Glib::FILE_TEST_EXISTS)) {
+ int ret = message_dialog(
+ (boost::format("<b>A bundle named \"%1%\" already exists."
+ " Replace it?</b>") % basename).str(),
+ "", Gtk::MESSAGE_WARNING, Gtk::BUTTONS_YES_NO);
+ confirm = (ret == Gtk::RESPONSE_YES);
+ } else {
+ int ret = message_dialog(
+ (boost::format("<b>A directory named \"%1%\" already exists,"
+ "but is not an Ingen bundle. "
+ "Save into it anyway?</b>") % basename).str(),
+ "This will create at least 2 .ttl files in this directory,"
+ "and possibly several more files and/or directories, recursively. "
+ "Existing files will be overwritten.",
+ Gtk::MESSAGE_WARNING, Gtk::BUTTONS_YES_NO);
+ confirm = (ret == Gtk::RESPONSE_YES);
+ }
+ } else if (Glib::file_test(filename, Glib::FILE_TEST_EXISTS)) {
+ int ret = message_dialog(
+ (boost::format("<b>A file named \"%1%\" already exists. "
+ "Replace it with an Ingen bundle?</b>")
+ % basename).str(),
+ "", Gtk::MESSAGE_WARNING, Gtk::BUTTONS_YES_NO);
+ confirm = (ret == Gtk::RESPONSE_YES);
+ if (confirm)
+ ::g_remove(filename.c_str());
+ }
+
+ if (confirm) {
+ const Glib::ustring uri = Glib::filename_to_uri(filename);
+ _app->loader()->save_patch(_patch, uri);
+ const_cast<PatchModel*>(_patch.get())->set_property(
+ uris.ingen_document,
+ _app->forge().alloc(Atom::URI, uri.c_str()),
+ Resource::EXTERNAL);
+ _status_bar->push(
+ (boost::format("Saved %1% to %2%") % _patch->path().chop_scheme()
+ % filename).str(),
+ STATUS_CONTEXT_PATCH);
+ }
+
+ _app->configuration()->set_patch_folder(dialog.get_current_folder());
+ break;
+ }
+}
+
+void
+PatchBox::event_draw()
+{
+ Gtk::FileChooserDialog dialog("Draw to DOT", Gtk::FILE_CHOOSER_ACTION_SAVE);
+ if (_window) {
+ dialog.set_transient_for(*_window);
+ }
+
+ dialog.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
+ Gtk::Button* save_button = dialog.add_button(Gtk::Stock::SAVE, Gtk::RESPONSE_OK);
+ save_button->property_has_default() = true;
+
+ int result = dialog.run();
+
+ if (result == Gtk::RESPONSE_OK) {
+ string filename = dialog.get_filename();
+ if (filename.find(".") == string::npos)
+ filename += ".dot";
+
+ bool confirm = true;
+ if (Glib::file_test(filename, Glib::FILE_TEST_EXISTS)) {
+ int ret = message_dialog(
+ (boost::format("File exists! Overwrite %1%?") % filename).str(),
+ "", Gtk::MESSAGE_WARNING, Gtk::BUTTONS_YES_NO);
+ confirm = (ret == Gtk::RESPONSE_YES);
+ }
+
+ if (confirm) {
+ _view->canvas()->render_to_dot(filename);
+ _status_bar->push(
+ (boost::format("Rendered %1% to %2%") % _patch->path() % filename).str(),
+ STATUS_CONTEXT_PATCH);
+ }
+ }
+}
+
+void
+PatchBox::event_edit_controls()
+{
+ if (_view)
+ _view->set_editable(_menu_edit_controls->get_active());
+}
+
+void
+PatchBox::event_copy()
+{
+ if (_view)
+ _view->canvas()->copy_selection();
+}
+
+void
+PatchBox::event_paste()
+{
+ if (_view)
+ _view->canvas()->paste();
+}
+
+void
+PatchBox::event_delete()
+{
+ if (_view)
+ _view->canvas()->destroy_selection();
+}
+
+void
+PatchBox::event_select_all()
+{
+ if (_view)
+ _view->canvas()->select_all();
+}
+
+void
+PatchBox::event_close()
+{
+ if (_window) {
+ _app->window_factory()->remove_patch_window(_window);
+ }
+}
+
+void
+PatchBox::event_quit()
+{
+ _app->quit(_window);
+}
+
+void
+PatchBox::event_zoom_in()
+{
+ _view->canvas()->set_font_size(_view->canvas()->get_font_size() + 1.0);
+}
+
+void
+PatchBox::event_zoom_out()
+{
+ _view->canvas()->set_font_size(_view->canvas()->get_font_size() - 1.0);
+}
+
+void
+PatchBox::event_zoom_normal()
+{
+ _view->canvas()->set_zoom_and_font_size(1.0, _view->canvas()->get_default_font_size());
+}
+
+void
+PatchBox::event_arrange()
+{
+ _view->canvas()->arrange(false);
+}
+
+void
+PatchBox::event_fullscreen_toggled()
+{
+ // FIXME: ugh, use GTK signals to track state and know for sure
+ static bool is_fullscreen = false;
+
+ if (_window) {
+ if (!is_fullscreen) {
+ _window->fullscreen();
+ is_fullscreen = true;
+ } else {
+ _window->unfullscreen();
+ is_fullscreen = false;
+ }
+ }
+}
+
+void
+PatchBox::event_status_bar_toggled()
+{
+ if (_menu_show_status_bar->get_active())
+ _status_bar->show();
+ else
+ _status_bar->hide();
+}
+
+void
+PatchBox::event_human_names_toggled()
+{
+ _view->canvas()->show_human_names(_menu_human_names->get_active());
+ if (_menu_human_names->get_active())
+ _app->configuration()->set_name_style(Configuration::HUMAN);
+ else
+ _app->configuration()->set_name_style(Configuration::PATH);
+}
+
+void
+PatchBox::event_port_names_toggled()
+{
+ if (_menu_show_port_names->get_active()) {
+ _view->canvas()->set_direction(GANV_DIRECTION_RIGHT);
+ _view->canvas()->show_port_names(true);
+ } else {
+ _view->canvas()->set_direction(GANV_DIRECTION_DOWN);
+ _view->canvas()->show_port_names(false);
+ }
+}
+
+} // namespace GUI
+} // namespace Ingen
diff --git a/src/gui/PatchBox.hpp b/src/gui/PatchBox.hpp
new file mode 100644
index 00000000..bd275cbb
--- /dev/null
+++ b/src/gui/PatchBox.hpp
@@ -0,0 +1,170 @@
+/* This file is part of Ingen.
+ * Copyright 2007-2012 David Robillard <http://drobilla.net>
+ *
+ * Ingen 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.
+ *
+ * Ingen 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 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 St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef INGEN_GUI_PATCH_BOX_HPP
+#define INGEN_GUI_PATCH_BOX_HPP
+
+#include <gtkmm.h>
+
+#include "raul/SharedPtr.hpp"
+
+#include "Window.hpp"
+
+namespace Raul { class Atom; class Path; }
+
+namespace Ingen {
+
+namespace Client {
+ class PatchModel;
+ class PortModel;
+ class ObjectModel;
+}
+using namespace Ingen::Client;
+
+namespace GUI {
+
+class BreadCrumbs;
+class LoadPatchBox;
+class LoadPluginWindow;
+class NewSubpatchWindow;
+class NodeControlWindow;
+class PatchDescriptionWindow;
+class PatchView;
+class PatchWindow;
+class SubpatchModule;
+
+/** A window for a patch.
+ *
+ * \ingroup GUI
+ */
+class PatchBox : public Gtk::VBox
+{
+public:
+ PatchBox(BaseObjectType* cobject,
+ const Glib::RefPtr<Gtk::Builder>& xml);
+ ~PatchBox();
+
+ void init_box(App& app);
+ void set_patch(SharedPtr<const PatchModel> pc, SharedPtr<PatchView> view);
+ void set_window(PatchWindow* win) { _window = win; }
+
+ void show_documentation(const std::string& doc, bool html);
+ void hide_documentation();
+
+ SharedPtr<const PatchModel> patch() const { return _patch; }
+ SharedPtr<PatchView> view() const { return _view; }
+
+ void show_port_status(const PortModel* model, const Raul::Atom& value);
+
+ void set_patch_from_path(const Raul::Path& path, SharedPtr<PatchView> view);
+
+ void object_entered(const ObjectModel* model);
+ void object_left(const ObjectModel* model);
+
+private:
+ void patch_port_added(SharedPtr<const PortModel> port);
+ void patch_port_removed(SharedPtr<const PortModel> port);
+ void show_status(const ObjectModel* model);
+ void editable_changed(bool editable);
+
+ int message_dialog(const Glib::ustring& message,
+ const Glib::ustring& secondary_text,
+ Gtk::MessageType type,
+ Gtk::ButtonsType buttons);
+
+ void event_import();
+ void event_save();
+ void event_save_as();
+ void event_draw();
+ void event_edit_controls();
+ void event_copy();
+ void event_paste();
+ void event_delete();
+ void event_select_all();
+ void event_close();
+ void event_quit();
+ void event_fullscreen_toggled();
+ void event_status_bar_toggled();
+ void event_human_names_toggled();
+ void event_port_names_toggled();
+ void event_zoom_in();
+ void event_zoom_out();
+ void event_zoom_normal();
+ void event_arrange();
+ void event_show_properties();
+ void event_show_controls();
+ void event_show_engine();
+ void event_clipboard_changed(GdkEventOwnerChange* ev);
+
+ App* _app;
+ SharedPtr<const PatchModel> _patch;
+ SharedPtr<PatchView> _view;
+ PatchWindow* _window;
+
+ sigc::connection new_port_connection;
+ sigc::connection removed_port_connection;
+ sigc::connection edit_mode_connection;
+
+ Gtk::MenuItem* _menu_import;
+ Gtk::MenuItem* _menu_save;
+ Gtk::MenuItem* _menu_save_as;
+ Gtk::MenuItem* _menu_draw;
+ Gtk::CheckMenuItem* _menu_edit_controls;
+ Gtk::MenuItem* _menu_cut;
+ Gtk::MenuItem* _menu_copy;
+ Gtk::MenuItem* _menu_paste;
+ Gtk::MenuItem* _menu_delete;
+ Gtk::MenuItem* _menu_select_all;
+ Gtk::MenuItem* _menu_close;
+ Gtk::MenuItem* _menu_quit;
+ Gtk::CheckMenuItem* _menu_human_names;
+ Gtk::CheckMenuItem* _menu_show_port_names;
+ Gtk::CheckMenuItem* _menu_show_status_bar;
+ Gtk::MenuItem* _menu_zoom_in;
+ Gtk::MenuItem* _menu_zoom_out;
+ Gtk::MenuItem* _menu_zoom_normal;
+ Gtk::MenuItem* _menu_fullscreen;
+ Gtk::MenuItem* _menu_arrange;
+ Gtk::MenuItem* _menu_view_engine_window;
+ Gtk::MenuItem* _menu_view_control_window;
+ Gtk::MenuItem* _menu_view_patch_properties;
+ Gtk::MenuItem* _menu_view_messages_window;
+ Gtk::MenuItem* _menu_view_patch_tree_window;
+ Gtk::MenuItem* _menu_help_about;
+
+ Gtk::VBox* _vbox;
+ Gtk::Alignment* _alignment;
+ BreadCrumbs* _breadcrumbs;
+ Gtk::Statusbar* _status_bar;
+
+ Gtk::HPaned* _doc_paned;
+ Gtk::ScrolledWindow* _doc_scrolledwindow;
+
+ sigc::connection _entered_connection;
+ sigc::connection _left_connection;
+
+ /** Invisible bin used to store breadcrumbs when not shown by a view */
+ Gtk::Alignment _breadcrumb_bin;
+
+ bool _has_shown_documentation;
+ bool _enable_signal;
+};
+
+} // namespace GUI
+} // namespace Ingen
+
+#endif // INGEN_GUI_PATCH_BOX_HPP
diff --git a/src/gui/PatchView.hpp b/src/gui/PatchView.hpp
index 8d7d78a7..f6306a1a 100644
--- a/src/gui/PatchView.hpp
+++ b/src/gui/PatchView.hpp
@@ -23,6 +23,7 @@
#include <gtkmm.h>
#include "raul/SharedPtr.hpp"
+#include "raul/URI.hpp"
namespace Raul { class Atom; }
namespace Ganv { class Port; class Item; }
@@ -39,6 +40,7 @@ using namespace Ingen::Client;
namespace GUI {
+class App;
class LoadPluginWindow;
class NewSubpatchWindow;
class NodeControlWindow;
diff --git a/src/gui/PatchWindow.cpp b/src/gui/PatchWindow.cpp
index c97ae89a..f555ff37 100644
--- a/src/gui/PatchWindow.cpp
+++ b/src/gui/PatchWindow.cpp
@@ -1,5 +1,5 @@
/* This file is part of Ingen.
- * Copyright 2007-2011 David Robillard <http://drobilla.net>
+ * Copyright 2007-2012 David Robillard <http://drobilla.net>
*
* Ingen 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
@@ -15,183 +15,48 @@
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
-#include <cassert>
-#include <sstream>
-
-#include <boost/format.hpp>
-#include <glib/gstdio.h>
-#include <glibmm/fileutils.h>
-
-#include "raul/AtomRDF.hpp"
-
-#include "ingen/Interface.hpp"
#include "ingen/client/ClientStore.hpp"
#include "ingen/client/PatchModel.hpp"
-#include "ingen/shared/LV2URIMap.hpp"
#include "App.hpp"
-#include "BreadCrumbs.hpp"
-#include "Configuration.hpp"
-#include "ConnectWindow.hpp"
-#include "LoadPatchWindow.hpp"
-#include "LoadPluginWindow.hpp"
-#include "MessagesWindow.hpp"
-#include "NewSubpatchWindow.hpp"
-#include "NodeControlWindow.hpp"
#include "PatchCanvas.hpp"
-#include "PatchTreeWindow.hpp"
#include "PatchView.hpp"
#include "PatchWindow.hpp"
-#include "ThreadedLoader.hpp"
#include "WindowFactory.hpp"
-#include "ingen_config.h"
-
-#ifdef HAVE_WEBKIT
-#include <webkit/webkit.h>
-#endif
-
-using namespace Raul;
namespace Ingen {
namespace GUI {
-static const int STATUS_CONTEXT_ENGINE = 0;
-static const int STATUS_CONTEXT_PATCH = 1;
-static const int STATUS_CONTEXT_HOVER = 2;
-
PatchWindow::PatchWindow(BaseObjectType* cobject,
const Glib::RefPtr<Gtk::Builder>& xml)
: Window(cobject)
- , _enable_signal(true)
+ , _box(NULL)
, _position_stored(false)
, _x(0)
, _y(0)
- , _breadcrumbs(NULL)
- , _has_shown_documentation(false)
{
property_visible() = false;
- xml->get_widget("patch_win_vbox", _vbox);
- xml->get_widget("patch_win_alignment", _alignment);
- xml->get_widget("patch_win_status_bar", _status_bar);
- //xml->get_widget("patch_win_status_bar", _status_bar);
- //xml->get_widget("patch_open_menuitem", _menu_open);
- xml->get_widget("patch_import_menuitem", _menu_import);
- //xml->get_widget("patch_open_into_menuitem", _menu_open_into);
- xml->get_widget("patch_save_menuitem", _menu_save);
- xml->get_widget("patch_save_as_menuitem", _menu_save_as);
- xml->get_widget("patch_draw_menuitem", _menu_draw);
- xml->get_widget("patch_edit_controls_menuitem", _menu_edit_controls);
- xml->get_widget("patch_cut_menuitem", _menu_cut);
- xml->get_widget("patch_copy_menuitem", _menu_copy);
- xml->get_widget("patch_paste_menuitem", _menu_paste);
- xml->get_widget("patch_delete_menuitem", _menu_delete);
- xml->get_widget("patch_select_all_menuitem", _menu_select_all);
- xml->get_widget("patch_close_menuitem", _menu_close);
- xml->get_widget("patch_quit_menuitem", _menu_quit);
- xml->get_widget("patch_view_control_window_menuitem", _menu_view_control_window);
- xml->get_widget("patch_view_engine_window_menuitem", _menu_view_engine_window);
- xml->get_widget("patch_properties_menuitem", _menu_view_patch_properties);
- xml->get_widget("patch_fullscreen_menuitem", _menu_fullscreen);
- xml->get_widget("patch_human_names_menuitem", _menu_human_names);
- xml->get_widget("patch_show_port_names_menuitem", _menu_show_port_names);
- xml->get_widget("patch_zoom_in_menuitem", _menu_zoom_in);
- xml->get_widget("patch_zoom_out_menuitem", _menu_zoom_out);
- xml->get_widget("patch_zoom_normal_menuitem", _menu_zoom_normal);
- xml->get_widget("patch_status_bar_menuitem", _menu_show_status_bar);
- xml->get_widget("patch_arrange_menuitem", _menu_arrange);
- xml->get_widget("patch_view_messages_window_menuitem", _menu_view_messages_window);
- xml->get_widget("patch_view_patch_tree_window_menuitem", _menu_view_patch_tree_window);
- xml->get_widget("patch_help_about_menuitem", _menu_help_about);
- xml->get_widget("patch_documentation_paned", _doc_paned);
- xml->get_widget("patch_documentation_scrolledwindow", _doc_scrolledwindow);
-
- _menu_view_control_window->property_sensitive() = false;
- _menu_import->signal_activate().connect(
- sigc::mem_fun(this, &PatchWindow::event_import));
- _menu_save->signal_activate().connect(
- sigc::mem_fun(this, &PatchWindow::event_save));
- _menu_save_as->signal_activate().connect(
- sigc::mem_fun(this, &PatchWindow::event_save_as));
- _menu_draw->signal_activate().connect(
- sigc::mem_fun(this, &PatchWindow::event_draw));
- _menu_edit_controls->signal_activate().connect(
- sigc::mem_fun(this, &PatchWindow::event_edit_controls));
- _menu_copy->signal_activate().connect(
- sigc::mem_fun(this, &PatchWindow::event_copy));
- _menu_paste->signal_activate().connect(
- sigc::mem_fun(this, &PatchWindow::event_paste));
- _menu_delete->signal_activate().connect(
- sigc::mem_fun(this, &PatchWindow::event_delete));
- _menu_select_all->signal_activate().connect(
- sigc::mem_fun(this, &PatchWindow::event_select_all));
- _menu_close->signal_activate().connect(
- sigc::mem_fun(this, &PatchWindow::event_close));
- _menu_quit->signal_activate().connect(
- sigc::mem_fun(this, &PatchWindow::event_quit));
- _menu_fullscreen->signal_activate().connect(
- sigc::mem_fun(this, &PatchWindow::event_fullscreen_toggled));
- _menu_human_names->signal_activate().connect(
- sigc::mem_fun(this, &PatchWindow::event_human_names_toggled));
- _menu_show_status_bar->signal_activate().connect(
- sigc::mem_fun(this, &PatchWindow::event_status_bar_toggled));
- _menu_show_port_names->signal_activate().connect(
- sigc::mem_fun(this, &PatchWindow::event_port_names_toggled));
- _menu_arrange->signal_activate().connect(
- sigc::mem_fun(this, &PatchWindow::event_arrange));
- _menu_quit->signal_activate().connect(
- sigc::mem_fun(this, &PatchWindow::event_quit));
- _menu_zoom_in->signal_activate().connect(
- sigc::mem_fun(this, &PatchWindow::event_zoom_in));
- _menu_zoom_out->signal_activate().connect(
- sigc::mem_fun(this, &PatchWindow::event_zoom_out));
- _menu_zoom_normal->signal_activate().connect(
- sigc::mem_fun(this, &PatchWindow::event_zoom_normal));
- _menu_view_engine_window->signal_activate().connect(
- sigc::mem_fun(this, &PatchWindow::event_show_engine));
- _menu_view_control_window->signal_activate().connect(
- sigc::mem_fun(this, &PatchWindow::event_show_controls));
- _menu_view_patch_properties->signal_activate().connect(
- sigc::mem_fun(this, &PatchWindow::event_show_properties));
+ xml->get_widget_derived("patch_win_vbox", _box);
- Glib::RefPtr<Gtk::Clipboard> clipboard = Gtk::Clipboard::get();
- clipboard->signal_owner_change().connect(
- sigc::mem_fun(this, &PatchWindow::event_clipboard_changed));
+ //set_title(_patch->path().chop_scheme() + " - Ingen");
+ set_title("Ingen");
}
PatchWindow::~PatchWindow()
{
- delete _breadcrumbs;
+ delete _box;
}
void
PatchWindow::init_window(App& app)
{
Window::init_window(app);
-
- string engine_name = _app->engine()->uri().str();
- if (engine_name == "http://drobilla.net/ns/ingen#internal") {
- engine_name = "internal engine";
- }
- _status_bar->push(string("Connected to ") + engine_name, STATUS_CONTEXT_ENGINE);
-
- _menu_view_messages_window->signal_activate().connect(
- sigc::mem_fun<void>(_app->messages_dialog(), &MessagesWindow::present));
- _menu_view_patch_tree_window->signal_activate().connect(
- sigc::mem_fun<void>(_app->patch_tree(), &PatchTreeWindow::present));
-
- _menu_help_about->signal_activate().connect(sigc::hide_return(
- sigc::mem_fun(_app, &App::show_about)));
-
- _breadcrumbs = new BreadCrumbs(*_app);
- _breadcrumbs->signal_patch_selected.connect(
- sigc::mem_fun(this, &PatchWindow::set_patch_from_path));
+ _box->init_box(app);
}
-/** Set the patch controller from a Path (for use by eg. BreadCrumbs)
- */
void
-PatchWindow::set_patch_from_path(const Path& path, SharedPtr<PatchView> view)
+PatchWindow::set_patch_from_path(const Raul::Path& path, SharedPtr<PatchView> view)
{
if (view) {
assert(view->patch()->path() == path);
@@ -204,418 +69,6 @@ PatchWindow::set_patch_from_path(const Path& path, SharedPtr<PatchView> view)
}
}
-/** Sets the patch controller for this window and initializes everything.
- *
- * If @a view is NULL, a new view will be created.
- */
-void
-PatchWindow::set_patch(SharedPtr<const PatchModel> patch,
- SharedPtr<PatchView> view)
-{
- if (!patch || patch == _patch)
- return;
-
- _enable_signal = false;
-
- new_port_connection.disconnect();
- removed_port_connection.disconnect();
- edit_mode_connection.disconnect();
- _entered_connection.disconnect();
- _left_connection.disconnect();
-
- _status_bar->pop(STATUS_CONTEXT_PATCH);
-
- _patch = patch;
- _view = view;
-
- if (!_view)
- _view = _breadcrumbs->view(patch->path());
-
- if (!_view)
- _view = PatchView::create(*_app, patch);
-
- assert(_view);
-
- // Add view to our alignment
- if (_view->get_parent())
- _view->get_parent()->remove(*_view.get());
-
- _alignment->remove();
- _alignment->add(*_view.get());
-
- if (_breadcrumbs->get_parent())
- _breadcrumbs->get_parent()->remove(*_breadcrumbs);
-
- _view->breadcrumb_container()->remove();
- _view->breadcrumb_container()->add(*_breadcrumbs);
- _view->breadcrumb_container()->show();
-
- _breadcrumbs->build(patch->path(), _view);
- _breadcrumbs->show();
-
- _menu_view_control_window->property_sensitive() = false;
-
- for (NodeModel::Ports::const_iterator p = patch->ports().begin();
- p != patch->ports().end(); ++p) {
- if (_app->can_control(p->get())) {
- _menu_view_control_window->property_sensitive() = true;
- break;
- }
- }
-
- set_title(_patch->path().chop_scheme() + " - Ingen");
-
- new_port_connection = patch->signal_new_port().connect(
- sigc::mem_fun(this, &PatchWindow::patch_port_added));
- removed_port_connection = patch->signal_removed_port().connect(
- sigc::mem_fun(this, &PatchWindow::patch_port_removed));
- removed_port_connection = patch->signal_editable().connect(
- sigc::mem_fun(this, &PatchWindow::editable_changed));
-
- show();
- _alignment->show_all();
-
- _view->signal_object_entered.connect(
- sigc::mem_fun(this, &PatchWindow::object_entered));
- _view->signal_object_left.connect(
- sigc::mem_fun(this, &PatchWindow::object_left));
-
- _enable_signal = true;
-}
-
-void
-PatchWindow::patch_port_added(SharedPtr<const PortModel> port)
-{
- if (port->is_input() && _app->can_control(port.get())) {
- _menu_view_control_window->property_sensitive() = true;
- }
-}
-
-void
-PatchWindow::patch_port_removed(SharedPtr<const PortModel> port)
-{
- if (!(port->is_input() && _app->can_control(port.get())))
- return;
-
- for (NodeModel::Ports::const_iterator i = _patch->ports().begin();
- i != _patch->ports().end(); ++i) {
- if ((*i)->is_input() && _app->can_control(i->get())) {
- _menu_view_control_window->property_sensitive() = true;
- return;
- }
- }
-
- _menu_view_control_window->property_sensitive() = false;
-}
-
-void
-PatchWindow::show_documentation(const std::string& doc, bool html)
-{
-#ifdef HAVE_WEBKIT
- WebKitWebView* view = WEBKIT_WEB_VIEW(webkit_web_view_new());
- webkit_web_view_load_html_string(view, doc.c_str(), "");
- _doc_scrolledwindow->add(*Gtk::manage(Glib::wrap(GTK_WIDGET(view))));
- _doc_scrolledwindow->show_all();
-#else
- Gtk::TextView* view = Gtk::manage(new Gtk::TextView());
- view->get_buffer()->set_text(doc);
- _doc_scrolledwindow->add(*view);
- _doc_scrolledwindow->show_all();
-#endif
- if (!_has_shown_documentation) {
- int width, height;
- get_size(width, height);
- _doc_paned->set_position(width / 1.61803399);
- }
- _has_shown_documentation = true;
-}
-
-void
-PatchWindow::hide_documentation()
-{
- _doc_scrolledwindow->remove();
- _doc_scrolledwindow->hide();
-}
-
-void
-PatchWindow::show_status(const ObjectModel* model)
-{
- std::stringstream msg;
- msg << model->path().chop_scheme();
-
- const PortModel* port = 0;
- const NodeModel* node = 0;
-
- if ((port = dynamic_cast<const PortModel*>(model))) {
- show_port_status(port, port->value());
-
- } else if ((node = dynamic_cast<const NodeModel*>(model))) {
- const PluginModel* plugin = dynamic_cast<const PluginModel*>(node->plugin());
- if (plugin)
- msg << ((boost::format(" (%1%)") % plugin->human_name()).str());
- _status_bar->push(msg.str(), STATUS_CONTEXT_HOVER);
- }
-}
-
-void
-PatchWindow::show_port_status(const PortModel* port, const Raul::Atom& value)
-{
- std::stringstream msg;
- msg << port->path().chop_scheme();
-
- const NodeModel* parent = dynamic_cast<const NodeModel*>(port->parent().get());
- if (parent) {
- const PluginModel* plugin = dynamic_cast<const PluginModel*>(parent->plugin());
- if (plugin) {
- const string& human_name = plugin->port_human_name(port->index());
- if (!human_name.empty())
- msg << " (" << human_name << ")";
- }
- }
-
- if (value.is_valid()) {
- msg << " = " << value;
- }
-
- _status_bar->pop(STATUS_CONTEXT_HOVER);
- _status_bar->push(msg.str(), STATUS_CONTEXT_HOVER);
-}
-
-void
-PatchWindow::object_entered(const ObjectModel* model)
-{
- show_status(model);
-}
-
-void
-PatchWindow::object_left(const ObjectModel* model)
-{
- _status_bar->pop(STATUS_CONTEXT_PATCH);
- _status_bar->pop(STATUS_CONTEXT_HOVER);
-}
-
-void
-PatchWindow::editable_changed(bool editable)
-{
- _menu_edit_controls->set_active(editable);
-}
-
-void
-PatchWindow::event_show_engine()
-{
- if (_patch)
- _app->connect_window()->show();
-}
-
-void
-PatchWindow::event_clipboard_changed(GdkEventOwnerChange* ev)
-{
- Glib::RefPtr<Gtk::Clipboard> clipboard = Gtk::Clipboard::get();
- _menu_paste->set_sensitive(clipboard->wait_is_text_available());
-}
-
-void
-PatchWindow::event_show_controls()
-{
- _app->window_factory()->present_controls(_patch);
-}
-
-void
-PatchWindow::event_show_properties()
-{
- _app->window_factory()->present_properties(_patch);
-}
-
-void
-PatchWindow::event_import()
-{
- _app->window_factory()->present_load_patch(_patch);
-}
-
-void
-PatchWindow::event_save()
-{
- const Raul::Atom& document = _patch->get_property(_app->uris().ingen_document);
- if (!document.is_valid() || document.type() != Raul::Atom::URI) {
- event_save_as();
- } else {
- _app->loader()->save_patch(_patch, document.get_uri());
- _status_bar->push(
- (boost::format("Saved %1% to %2%") % _patch->path().chop_scheme()
- % document.get_uri()).str(),
- STATUS_CONTEXT_PATCH);
- }
-}
-
-void
-PatchWindow::event_save_as()
-{
- const Shared::URIs& uris = _app->uris();
- while (true) {
- Gtk::FileChooserDialog dialog(*this, "Save Patch", Gtk::FILE_CHOOSER_ACTION_SAVE);
-
- dialog.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
- Gtk::Button* save_button = dialog.add_button(Gtk::Stock::SAVE, Gtk::RESPONSE_OK);
- save_button->property_has_default() = true;
-
- Gtk::FileFilter filt;
- filt.add_pattern("*.ingen");
- filt.set_name("Ingen bundles");
- dialog.set_filter(filt);
-
- // Set current folder to most sensible default
- const Raul::Atom& document = _patch->get_property(uris.ingen_document);
- if (document.type() == Raul::Atom::URI)
- dialog.set_uri(document.get_uri());
- else if (_app->configuration()->patch_folder().length() > 0)
- dialog.set_current_folder(_app->configuration()->patch_folder());
-
- if (dialog.run() != Gtk::RESPONSE_OK)
- break;
-
- std::string filename = dialog.get_filename();
- std::string basename = Glib::path_get_basename(filename);
-
- if (basename.find('.') == string::npos) {
- filename += ".ingen";
- basename += ".ingen";
- } else if (filename.substr(filename.length() - 10) != ".ingen") {
- Gtk::MessageDialog error_dialog(*this,
-"<b>" "Ingen patches must be saved to Ingen bundles (*.ingen)." "</b>",
- true, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_OK, true);
- error_dialog.run();
- continue;
- }
-
- const std::string symbol(basename.substr(0, basename.find('.')));
-
- if (!Symbol::is_valid(symbol)) {
- Gtk::MessageDialog error_dialog(*this,
- "<b>" "Ingen bundle names must be valid symbols." "</b>",
- true, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_OK, true);
- error_dialog.set_secondary_text(
-"All characters must be _, a-z, A-Z, or 0-9, but the first may not be 0-9.");
- error_dialog.run();
- continue;
- }
-
- //_patch->set_property(uris.lv2_symbol, Atom(symbol.c_str()));
-
- bool confirm = true;
- if (Glib::file_test(filename, Glib::FILE_TEST_IS_DIR)) {
- if (Glib::file_test(Glib::build_filename(filename, "manifest.ttl"),
- Glib::FILE_TEST_EXISTS)) {
- Gtk::MessageDialog confirm_dialog(*this, (boost::format("<b>"
- "A bundle named \"%1%\" already exists. Replace it?"
- "</b>") % basename).str(),
- true, Gtk::MESSAGE_WARNING, Gtk::BUTTONS_YES_NO, true);
- confirm = (confirm_dialog.run() == Gtk::RESPONSE_YES);
- } else {
- Gtk::MessageDialog confirm_dialog(*this, (boost::format("<b>"
-"A directory named \"%1%\" already exists, but is not an Ingen bundle. \
-Save into it anyway?" "</b>") % basename).str(),
- true, Gtk::MESSAGE_WARNING, Gtk::BUTTONS_YES_NO, true);
- confirm_dialog.set_secondary_text(
-"This will create at least 2 .ttl files in this directory, and possibly several \
-more files and/or directories, recursively. Existing files will be overwritten.");
- confirm = (confirm_dialog.run() == Gtk::RESPONSE_YES);
- }
- } else if (Glib::file_test(filename, Glib::FILE_TEST_EXISTS)) {
- Gtk::MessageDialog confirm_dialog(*this, (boost::format("<b>"
-"A file named \"%1%\" already exists. Replace it with an Ingen bundle?" "</b>") % basename).str(),
- true, Gtk::MESSAGE_WARNING, Gtk::BUTTONS_YES_NO, true);
- confirm = (confirm_dialog.run() == Gtk::RESPONSE_YES);
- if (confirm)
- ::g_remove(filename.c_str());
- }
-
- if (confirm) {
- const Glib::ustring uri = Glib::filename_to_uri(filename);
- _app->loader()->save_patch(_patch, uri);
- const_cast<PatchModel*>(_patch.get())->set_property(
- uris.ingen_document,
- _app->forge().alloc(Atom::URI, uri.c_str()),
- Resource::EXTERNAL);
- _status_bar->push(
- (boost::format("Saved %1% to %2%") % _patch->path().chop_scheme()
- % filename).str(),
- STATUS_CONTEXT_PATCH);
- }
-
- _app->configuration()->set_patch_folder(dialog.get_current_folder());
- break;
- }
-}
-
-void
-PatchWindow::event_draw()
-{
- Gtk::FileChooserDialog dialog(*this, "Draw to DOT", Gtk::FILE_CHOOSER_ACTION_SAVE);
-
- dialog.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
- Gtk::Button* save_button = dialog.add_button(Gtk::Stock::SAVE, Gtk::RESPONSE_OK);
- save_button->property_has_default() = true;
-
- int result = dialog.run();
-
- if (result == Gtk::RESPONSE_OK) {
- string filename = dialog.get_filename();
- if (filename.find(".") == string::npos)
- filename += ".dot";
-
- bool confirm = true;
- if (Glib::file_test(filename, Glib::FILE_TEST_EXISTS)) {
- const string msg = string("File exists!\n")
- + "Are you sure you want to overwrite " + filename + "?";
- Gtk::MessageDialog confirm_dialog(*this,
- msg, false, Gtk::MESSAGE_WARNING, Gtk::BUTTONS_YES_NO, true);
- confirm = (confirm_dialog.run() == Gtk::RESPONSE_YES);
- }
-
- if (confirm) {
- _view->canvas()->render_to_dot(filename);
- _status_bar->push(
- (boost::format("Rendered %1% to %2%") % _patch->path() % filename).str(),
- STATUS_CONTEXT_PATCH);
- }
- }
-}
-
-void
-PatchWindow::event_edit_controls()
-{
- if (_view)
- _view->set_editable(_menu_edit_controls->get_active());
-}
-
-void
-PatchWindow::event_copy()
-{
- if (_view)
- _view->canvas()->copy_selection();
-}
-
-void
-PatchWindow::event_paste()
-{
- if (_view)
- _view->canvas()->paste();
-}
-
-void
-PatchWindow::event_delete()
-{
- if (_view)
- _view->canvas()->destroy_selection();
-}
-
-void
-PatchWindow::event_select_all()
-{
- if (_view)
- _view->canvas()->select_all();
-}
-
void
PatchWindow::on_show()
{
@@ -637,94 +90,12 @@ bool
PatchWindow::on_event(GdkEvent* event)
{
if ((event->type == GDK_KEY_PRESS || event->type == GDK_KEY_RELEASE)
- && _view->canvas()->on_event(event)) {
+ && box()->view()->canvas()->on_event(event)) {
return true;
} else {
return Gtk::Window::on_event(event);
}
}
-void
-PatchWindow::event_close()
-{
- _app->window_factory()->remove_patch_window(this);
-}
-
-void
-PatchWindow::event_quit()
-{
- _app->quit(*this);
-}
-
-void
-PatchWindow::event_zoom_in()
-{
- _view->canvas()->set_font_size(_view->canvas()->get_font_size() + 1.0);
-}
-
-void
-PatchWindow::event_zoom_out()
-{
- _view->canvas()->set_font_size(_view->canvas()->get_font_size() - 1.0);
-}
-
-void
-PatchWindow::event_zoom_normal()
-{
- _view->canvas()->set_zoom_and_font_size(1.0, _view->canvas()->get_default_font_size());
-}
-
-void
-PatchWindow::event_arrange()
-{
- _view->canvas()->arrange(false);
-}
-
-void
-PatchWindow::event_fullscreen_toggled()
-{
- // FIXME: ugh, use GTK signals to track state and know for sure
- static bool is_fullscreen = false;
-
- if (!is_fullscreen) {
- fullscreen();
- is_fullscreen = true;
- } else {
- unfullscreen();
- is_fullscreen = false;
- }
-}
-
-void
-PatchWindow::event_status_bar_toggled()
-{
- if (_menu_show_status_bar->get_active())
- _status_bar->show();
- else
- _status_bar->hide();
-}
-
-void
-PatchWindow::event_human_names_toggled()
-{
- _view->canvas()->show_human_names(_menu_human_names->get_active());
- if (_menu_human_names->get_active())
- _app->configuration()->set_name_style(Configuration::HUMAN);
- else
- _app->configuration()->set_name_style(Configuration::PATH);
-}
-
-void
-PatchWindow::event_port_names_toggled()
-{
- if (_menu_show_port_names->get_active()) {
- _view->canvas()->set_direction(GANV_DIRECTION_RIGHT);
- _view->canvas()->show_port_names(true);
- } else {
- _view->canvas()->set_direction(GANV_DIRECTION_DOWN);
- _view->canvas()->show_port_names(false);
- }
-}
-
} // namespace GUI
} // namespace Ingen
diff --git a/src/gui/PatchWindow.hpp b/src/gui/PatchWindow.hpp
index fcb0e3f7..b1407f1e 100644
--- a/src/gui/PatchWindow.hpp
+++ b/src/gui/PatchWindow.hpp
@@ -15,37 +15,25 @@
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
-#ifndef INGEN_GUI_PATCHWINDOW_HPP
-#define INGEN_GUI_PATCHWINDOW_HPP
+#ifndef INGEN_GUI_PATCH_WINDOW_HPP
+#define INGEN_GUI_PATCH_WINDOW_HPP
#include <gtkmm.h>
#include "raul/SharedPtr.hpp"
+#include "PatchBox.hpp"
#include "Window.hpp"
-namespace Raul { class Atom; class Path; }
-
namespace Ingen {
namespace Client {
class PatchModel;
- class PortModel;
- class ObjectModel;
}
using namespace Ingen::Client;
namespace GUI {
-class BreadCrumbs;
-class LoadPatchWindow;
-class LoadPluginWindow;
-class NewSubpatchWindow;
-class NodeControlWindow;
-class PatchDescriptionWindow;
-class PatchView;
-class SubpatchModule;
-
/** A window for a patch.
*
* \ingroup GUI
@@ -55,116 +43,41 @@ class PatchWindow : public Window
public:
PatchWindow(BaseObjectType* cobject,
const Glib::RefPtr<Gtk::Builder>& xml);
+
~PatchWindow();
void init_window(App& app);
- void set_patch_from_path(const Raul::Path& path, SharedPtr<PatchView> view);
- void set_patch(SharedPtr<const PatchModel> pc, SharedPtr<PatchView> view);
- void show_documentation(const std::string& doc, bool html);
- void hide_documentation();
+ SharedPtr<const PatchModel> patch() const { return _box->patch(); }
+ PatchBox* box() const { return _box; }
- SharedPtr<const PatchModel> patch() const { return _patch; }
+ void set_patch_from_path(const Raul::Path& path, SharedPtr<PatchView> view);
- Gtk::MenuItem* menu_view_control_window() { return _menu_view_control_window; }
+ void show_documentation(const std::string& doc, bool html) {
+ _box->show_documentation(doc, html);
+ }
- void show_port_status(const PortModel* model, const Raul::Atom& value);
+ void hide_documentation() {
+ _box->hide_documentation();
+ }
- void object_entered(const ObjectModel* model);
- void object_left(const ObjectModel* model);
+ void show_port_status(const PortModel* model, const Raul::Atom& value) {
+ _box->show_port_status(model, value);
+ }
protected:
- void on_show();
- void on_hide();
bool on_event(GdkEvent* event);
+ void on_hide();
+ void on_show();
private:
- void patch_port_added(SharedPtr<const PortModel> port);
- void patch_port_removed(SharedPtr<const PortModel> port);
- void show_status(const ObjectModel* model);
- void editable_changed(bool editable);
-
- void event_import();
- void event_save();
- void event_save_as();
- void event_draw();
- void event_edit_controls();
- void event_copy();
- void event_paste();
- void event_delete();
- void event_select_all();
- void event_close();
- void event_quit();
- void event_fullscreen_toggled();
- void event_status_bar_toggled();
- void event_human_names_toggled();
- void event_port_names_toggled();
- void event_zoom_in();
- void event_zoom_out();
- void event_zoom_normal();
- void event_arrange();
- void event_show_properties();
- void event_show_controls();
- void event_show_engine();
- void event_clipboard_changed(GdkEventOwnerChange* ev);
-
- SharedPtr<const PatchModel> _patch;
- SharedPtr<PatchView> _view;
-
- sigc::connection new_port_connection;
- sigc::connection removed_port_connection;
- sigc::connection edit_mode_connection;
-
- bool _enable_signal;
- bool _position_stored;
- int _x;
- int _y;
-
- Gtk::MenuItem* _menu_import;
- Gtk::MenuItem* _menu_save;
- Gtk::MenuItem* _menu_save_as;
- Gtk::MenuItem* _menu_draw;
- Gtk::CheckMenuItem* _menu_edit_controls;
- Gtk::MenuItem* _menu_cut;
- Gtk::MenuItem* _menu_copy;
- Gtk::MenuItem* _menu_paste;
- Gtk::MenuItem* _menu_delete;
- Gtk::MenuItem* _menu_select_all;
- Gtk::MenuItem* _menu_close;
- Gtk::MenuItem* _menu_quit;
- Gtk::CheckMenuItem* _menu_human_names;
- Gtk::CheckMenuItem* _menu_show_port_names;
- Gtk::CheckMenuItem* _menu_show_status_bar;
- Gtk::MenuItem* _menu_zoom_in;
- Gtk::MenuItem* _menu_zoom_out;
- Gtk::MenuItem* _menu_zoom_normal;
- Gtk::MenuItem* _menu_fullscreen;
- Gtk::MenuItem* _menu_arrange;
- Gtk::MenuItem* _menu_view_engine_window;
- Gtk::MenuItem* _menu_view_control_window;
- Gtk::MenuItem* _menu_view_patch_properties;
- Gtk::MenuItem* _menu_view_messages_window;
- Gtk::MenuItem* _menu_view_patch_tree_window;
- Gtk::MenuItem* _menu_help_about;
-
- Gtk::VBox* _vbox;
- Gtk::Alignment* _alignment;
- BreadCrumbs* _breadcrumbs;
- Gtk::Statusbar* _status_bar;
-
- Gtk::HPaned* _doc_paned;
- Gtk::ScrolledWindow* _doc_scrolledwindow;
-
- sigc::connection _entered_connection;
- sigc::connection _left_connection;
-
- /** Invisible bin used to store breadcrumbs when not shown by a view */
- Gtk::Alignment _breadcrumb_bin;
-
- bool _has_shown_documentation;
+ PatchBox* _box;
+ bool _position_stored;
+ int _x;
+ int _y;
};
} // namespace GUI
} // namespace Ingen
-#endif // INGEN_GUI_PATCHWINDOW_HPP
+#endif // INGEN_GUI_PATCH_WINDOW_HPP
diff --git a/src/gui/Port.cpp b/src/gui/Port.cpp
index 7bf3412d..aa3786e3 100644
--- a/src/gui/Port.cpp
+++ b/src/gui/Port.cpp
@@ -154,9 +154,9 @@ Port::on_value_changed(const Glib::VariantBase& value)
world->uris()->ingen_value,
_app.forge().make(fval));
- PatchWindow* pw = get_patch_window();
- if (pw) {
- pw->show_port_status(model().get(), _app.forge().make(fval));
+ PatchBox* box = get_patch_box();
+ if (box) {
+ box->show_port_status(model().get(), _app.forge().make(fval));
}
}
@@ -171,18 +171,18 @@ Port::value_changed(const Atom& value)
bool
Port::on_event(GdkEvent* ev)
{
- PatchWindow* win = NULL;
+ PatchBox* box = NULL;
switch (ev->type) {
case GDK_ENTER_NOTIFY:
- win = get_patch_window();
- if (win) {
- win->object_entered(model().get());
+ box = get_patch_box();
+ if (box) {
+ box->object_entered(model().get());
}
break;
case GDK_LEAVE_NOTIFY:
- win = get_patch_window();
- if (win) {
- win->object_left(model().get());
+ box = get_patch_box();
+ if (box) {
+ box->object_left(model().get());
}
break;
case GDK_BUTTON_PRESS:
@@ -263,15 +263,15 @@ Port::activity(const Raul::Atom& value)
}
}
-PatchWindow*
-Port::get_patch_window() const
+PatchBox*
+Port::get_patch_box() const
{
SharedPtr<const PatchModel> patch = PtrCast<const PatchModel>(model()->parent());
if (!patch) {
patch = PtrCast<const PatchModel>(model()->parent()->parent());
}
- return _app.window_factory()->patch_window(patch);
+ return _app.window_factory()->patch_box(patch);
}
void
diff --git a/src/gui/Port.hpp b/src/gui/Port.hpp
index 7eef8c56..aaa0ffc8 100644
--- a/src/gui/Port.hpp
+++ b/src/gui/Port.hpp
@@ -34,7 +34,7 @@ using Ingen::Client::PortModel;
namespace GUI {
class App;
-class PatchWindow;
+class PatchBox;
/** A Port on an Module.
*
@@ -71,7 +71,7 @@ private:
const std::string& name,
bool flip = false);
- PatchWindow* get_patch_window() const;
+ PatchBox* get_patch_box() const;
void property_changed(const Raul::URI& key, const Raul::Atom& value);
void moved();
diff --git a/src/gui/WindowFactory.cpp b/src/gui/WindowFactory.cpp
index c60d493f..811e697e 100644
--- a/src/gui/WindowFactory.cpp
+++ b/src/gui/WindowFactory.cpp
@@ -37,6 +37,7 @@ namespace GUI {
WindowFactory::WindowFactory(App& app)
: _app(app)
+ , _main_box(NULL)
, _load_plugin_win(NULL)
, _load_patch_win(NULL)
, _new_subpatch_win(NULL)
@@ -98,6 +99,17 @@ WindowFactory::num_open_patch_windows()
return ret;
}
+PatchBox*
+WindowFactory::patch_box(SharedPtr<const PatchModel> patch)
+{
+ PatchWindow* window = patch_window(patch);
+ if (window) {
+ return window->box();
+ } else {
+ return _main_box;
+ }
+}
+
PatchWindow*
WindowFactory::patch_window(SharedPtr<const PatchModel> patch)
{
@@ -147,7 +159,7 @@ WindowFactory::present_patch(SharedPtr<const PatchModel> patch,
w = _patch_windows.find(preferred->patch()->path());
assert((*w).second == preferred);
- preferred->set_patch(patch, view);
+ preferred->box()->set_patch(patch, view);
_patch_windows.erase(w);
_patch_windows[patch->path()] = preferred;
preferred->present();
@@ -168,7 +180,7 @@ WindowFactory::new_patch_window(SharedPtr<const PatchModel> patch,
WidgetFactory::get_widget_derived("patch_win", win);
win->init_window(_app);
- win->set_patch(patch, view);
+ win->box()->set_patch(patch, view);
_patch_windows[patch->path()] = win;
win->signal_delete_event().connect(sigc::bind<0>(
@@ -181,7 +193,7 @@ bool
WindowFactory::remove_patch_window(PatchWindow* win, GdkEventAny* ignored)
{
if (_patch_windows.size() <= 1)
- return !_app.quit(*win);
+ return !_app.quit(win);
PatchWindowMap::iterator w = _patch_windows.find(win->patch()->path());
diff --git a/src/gui/WindowFactory.hpp b/src/gui/WindowFactory.hpp
index d520740f..7bee1fd4 100644
--- a/src/gui/WindowFactory.hpp
+++ b/src/gui/WindowFactory.hpp
@@ -44,6 +44,7 @@ class LoadPluginWindow;
class NewSubpatchWindow;
class NodeControlWindow;
class PropertiesWindow;
+class PatchBox;
class PatchView;
class PatchWindow;
class RenameWindow;
@@ -62,13 +63,14 @@ public:
size_t num_open_patch_windows();
+ PatchBox* patch_box(SharedPtr<const PatchModel> patch);
PatchWindow* patch_window(SharedPtr<const PatchModel> patch);
PatchWindow* parent_patch_window(SharedPtr<const NodeModel> node);
NodeControlWindow* control_window(SharedPtr<const NodeModel> node);
void present_patch(SharedPtr<const PatchModel> model,
- PatchWindow* preferred = NULL,
- SharedPtr<PatchView> view = SharedPtr<PatchView>());
+ PatchWindow* preferred = NULL,
+ SharedPtr<PatchView> view = SharedPtr<PatchView>());
void present_controls(SharedPtr<const NodeModel> node);
@@ -83,6 +85,8 @@ public:
bool remove_patch_window(PatchWindow* win, GdkEventAny* ignored = NULL);
+ void set_main_box(PatchBox* box) { _main_box = box; }
+
void clear();
private:
@@ -97,6 +101,7 @@ private:
GdkEventAny* ignored);
App& _app;
+ PatchBox* _main_box;
PatchWindowMap _patch_windows;
ControlWindowMap _control_windows;
LoadPluginWindow* _load_plugin_win;
diff --git a/src/gui/ingen_gui_lv2.cpp b/src/gui/ingen_gui_lv2.cpp
new file mode 100644
index 00000000..9898005e
--- /dev/null
+++ b/src/gui/ingen_gui_lv2.cpp
@@ -0,0 +1,179 @@
+/* This file is part of Ingen.
+ * Copyright 2012 David Robillard <http://drobilla.net>
+ *
+ * Ingen 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.
+ *
+ * Ingen 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 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 St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "ingen/client/ClientStore.hpp"
+#include "ingen/client/PatchModel.hpp"
+#include "ingen/client/SigClientInterface.hpp"
+#include "ingen/shared/AtomSink.hpp"
+#include "ingen/shared/AtomWriter.hpp"
+#include "ingen/shared/Configuration.hpp"
+#include "ingen/shared/World.hpp"
+#include "ingen/shared/runtime_paths.hpp"
+#include "lv2/lv2plug.in/ns/extensions/ui/ui.h"
+
+#include "App.hpp"
+#include "PatchView.hpp"
+
+#define INGEN_LV2_UI_URI "http://drobilla.net/ns/ingen#ui"
+
+/** A sink that writes atoms to a port via the UI extension. */
+struct IngenLV2AtomSink : public Ingen::Shared::AtomSink {
+ IngenLV2AtomSink(Ingen::Shared::URIs& uris,
+ LV2UI_Write_Function ui_write,
+ LV2UI_Controller ui_controller)
+ : _uris(uris)
+ , _ui_write(ui_write)
+ , _ui_controller(ui_controller)
+ {}
+
+ void write(const LV2_Atom* atom) {
+ _ui_write(_ui_controller,
+ 0,
+ lv2_atom_total_size(atom),
+ _uris.atom_eventTransfer,
+ atom);
+ }
+
+ Ingen::Shared::URIs& _uris;
+ LV2UI_Write_Function _ui_write;
+ LV2UI_Controller _ui_controller;
+};
+
+struct IngenLV2UI {
+ IngenLV2UI()
+ : conf(&forge)
+ , sink(NULL)
+ {
+ }
+
+ int argc;
+ char** argv;
+ Raul::Forge forge;
+ Ingen::Shared::Configuration conf;
+ Ingen::Shared::World* world;
+ IngenLV2AtomSink* sink;
+ SharedPtr<Ingen::GUI::App> app;
+ SharedPtr<Ingen::GUI::PatchView> view;
+ SharedPtr<Ingen::Interface> engine;
+ SharedPtr<Ingen::Client::SigClientInterface> client;
+};
+
+static LV2UI_Handle
+instantiate(const LV2UI_Descriptor* descriptor,
+ const char* plugin_uri,
+ const char* bundle_path,
+ LV2UI_Write_Function write_function,
+ LV2UI_Controller controller,
+ LV2UI_Widget* widget,
+ const LV2_Feature* const* features)
+{
+ Ingen::Shared::set_bundle_path(bundle_path);
+
+ IngenLV2UI* ui = new IngenLV2UI();
+
+ LV2_URID_Map* map = NULL;
+ LV2_URID_Unmap* unmap = NULL;
+ for (int i = 0; features[i]; ++i) {
+ if (!strcmp(features[i]->URI, LV2_URID_URI "#map")) {
+ map = (LV2_URID_Map*)features[i]->data;
+ } else if (!strcmp(features[i]->URI, LV2_URID_URI "#unmap")) {
+ unmap = (LV2_URID_Unmap*)features[i]->data;
+ }
+ }
+
+ ui->world = new Ingen::Shared::World(
+ &ui->conf, ui->argc, ui->argv, map, unmap);
+
+ if (!ui->world->load_module("client")) {
+ delete ui;
+ return NULL;
+ }
+
+ ui->sink = new IngenLV2AtomSink(
+ *ui->world->uris().get(), write_function, controller);
+
+ // Set up an engine interface that writes LV2 atoms
+ ui->engine = SharedPtr<Ingen::Interface>(
+ new Ingen::Shared::AtomWriter(*ui->world->lv2_uri_map().get(),
+ *ui->world->uris().get(),
+ *ui->sink));
+
+ ui->world->set_engine(ui->engine);
+
+ // Create App and client
+ ui->app = Ingen::GUI::App::create(ui->world);
+ ui->client = SharedPtr<Ingen::Client::SigClientInterface>(
+ new Ingen::Client::SigClientInterface());
+ ui->app->attach(ui->client);
+
+ // Create empty root patch model
+ Ingen::Resource::Properties props;
+ props.insert(std::make_pair(ui->app->uris().rdf_type,
+ ui->app->uris().ingen_Patch));
+ ui->app->store()->put("path:/", props);
+
+ // Create a PatchView for the root and set as the UI widget
+ SharedPtr<const Ingen::Client::PatchModel> root = PtrCast<const Ingen::Client::PatchModel>(
+ ui->app->store()->object("path:/"));
+ ui->view = Ingen::GUI::PatchView::create(*ui->app, root);
+ ui->view->unparent();
+ *widget = ui->view->gobj();
+
+ return ui;
+}
+
+static void
+cleanup(LV2UI_Handle handle)
+{
+ IngenLV2UI* ui = (IngenLV2UI*)handle;
+ delete ui;
+}
+
+static void
+port_event(LV2UI_Handle handle,
+ uint32_t port_index,
+ uint32_t buffer_size,
+ uint32_t format,
+ const void* buffer)
+{
+}
+
+const void*
+extension_data(const char* uri)
+{
+ return NULL;
+}
+
+static const LV2UI_Descriptor descriptor = {
+ INGEN_LV2_UI_URI,
+ instantiate,
+ cleanup,
+ port_event,
+ extension_data
+};
+
+LV2_SYMBOL_EXPORT
+const LV2UI_Descriptor*
+lv2ui_descriptor(uint32_t index)
+{
+ switch (index) {
+ case 0:
+ return &descriptor;
+ default:
+ return NULL;
+ }
+}
diff --git a/src/gui/wscript b/src/gui/wscript
index ec6e451a..8fc5058e 100644
--- a/src/gui/wscript
+++ b/src/gui/wscript
@@ -42,6 +42,7 @@ def build(bld):
NodeMenu.cpp
NodeModule.cpp
ObjectMenu.cpp
+ PatchBox.cpp
PatchCanvas.cpp
PatchPortModule.cpp
PatchTreeWindow.cpp
@@ -67,3 +68,13 @@ def build(bld):
install_path = '${DATADIR}/ingen',
chmod = Utils.O755,
INGEN_VERSION = bld.env['INGEN_VERSION'])
+
+ # LV2 UI
+ obj = bld(features = 'cxx cxxshlib',
+ source = 'ingen_gui_lv2.cpp',
+ includes = ['.', '../..'],
+ name = 'ingen_gui_lv2',
+ target = 'ingen_gui_lv2',
+ install_path = '${LV2DIR}/ingen.lv2/',
+ use = 'libingen_gui libingen_shared')
+ autowaf.use_lib(bld, obj, 'LV2_UI')
diff --git a/src/server/ServerInterfaceImpl.hpp b/src/server/ServerInterfaceImpl.hpp
index ef9042bf..0ba3a1b9 100644
--- a/src/server/ServerInterfaceImpl.hpp
+++ b/src/server/ServerInterfaceImpl.hpp
@@ -83,8 +83,6 @@ public:
virtual void disconnect_all(const Raul::Path& parent_patch_path,
const Raul::Path& path);
- virtual void ping();
-
virtual void get(const Raul::URI& uri);
virtual void response(int32_t id, Status status) {} ///< N/A
diff --git a/src/server/events/Get.cpp b/src/server/events/Get.cpp
index 404cd981..06e30a47 100644
--- a/src/server/events/Get.cpp
+++ b/src/server/events/Get.cpp
@@ -69,6 +69,7 @@ Get::post_process()
_engine.broadcaster()->send_plugins_to(_request_client, _plugins);
}
} else if (_uri == "ingen:engine") {
+ respond(SUCCESS);
// TODO: Keep a proper RDF model of the engine
if (_request_client) {
Shared::URIs& uris = *_engine.world()->uris().get();
diff --git a/src/shared/AtomWriter.cpp b/src/shared/AtomWriter.cpp
index a5317bfb..c86ac96f 100644
--- a/src/shared/AtomWriter.cpp
+++ b/src/shared/AtomWriter.cpp
@@ -15,19 +15,50 @@
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
+#include "ingen/shared/AtomSink.hpp"
+#include "ingen/shared/AtomWriter.hpp"
#include "raul/Path.hpp"
-
-#include "AtomWriter.hpp"
+#include "serd/serd.h"
namespace Ingen {
namespace Shared {
-AtomWriter::AtomWriter(LV2URIMap& map, URIs& uris)
+static LV2_Atom_Forge_Ref
+forge_sink(LV2_Atom_Forge_Sink_Handle handle,
+ const void* buf,
+ uint32_t size)
+{
+ SerdChunk* chunk = (SerdChunk*)handle;
+ const LV2_Atom_Forge_Ref ref = chunk->len + 1;
+ serd_chunk_sink(buf, size, chunk);
+ return ref;
+}
+
+static LV2_Atom*
+forge_deref(LV2_Atom_Forge_Sink_Handle handle, LV2_Atom_Forge_Ref ref)
+{
+ SerdChunk* chunk = (SerdChunk*)handle;
+ return (LV2_Atom*)(chunk->buf + ref - 1);
+}
+
+
+AtomWriter::AtomWriter(LV2URIMap& map, URIs& uris, AtomSink& sink)
: _map(map)
, _uris(uris)
- , _id(-1)
+ , _sink(sink)
+ , _id(1)
{
+ _out.buf = NULL;
+ _out.len = 0;
lv2_atom_forge_init(&_forge, &map.urid_map_feature()->urid_map);
+ lv2_atom_forge_set_sink(&_forge, forge_sink, forge_deref, &_out);
+}
+
+void
+AtomWriter::finish_msg()
+{
+ _sink.write((LV2_Atom*)_out.buf);
+ _out.len = 0;
}
int32_t
@@ -55,6 +86,11 @@ AtomWriter::put(const Raul::URI& uri,
const Resource::Properties& properties,
Resource::Graph ctx)
{
+ LV2_Atom_Forge_Frame msg;
+ lv2_atom_forge_blank(&_forge, &msg, next_id(), _uris.patch_Put);
+ // ...
+ lv2_atom_forge_pop(&_forge, &msg);
+ finish_msg();
}
void
@@ -92,6 +128,8 @@ AtomWriter::connect(const Raul::Path& src,
lv2_atom_forge_pop(&_forge, &body);
lv2_atom_forge_pop(&_forge, &msg);
+
+ finish_msg();
}
void
@@ -126,6 +164,7 @@ AtomWriter::get(const Raul::URI& uri)
lv2_atom_forge_property_head(&_forge, _uris.patch_subject, 0);
lv2_atom_forge_uri(&_forge, uri.c_str(), uri.length());
lv2_atom_forge_pop(&_forge, &msg);
+ finish_msg();
}
void
@@ -136,6 +175,7 @@ AtomWriter::response(int32_t id, Status status)
lv2_atom_forge_property_head(&_forge, _uris.patch_request, 0);
lv2_atom_forge_int32(&_forge, id);
lv2_atom_forge_pop(&_forge, &msg);
+ finish_msg();
}
void
diff --git a/src/shared/LV2Atom.cpp b/src/shared/LV2Atom.cpp
index 63686bf5..ccd5f352 100644
--- a/src/shared/LV2Atom.cpp
+++ b/src/shared/LV2Atom.cpp
@@ -43,7 +43,7 @@ to_atom(Raul::Forge* forge,
} else if (object->type == uris.atom_Bool.id) {
atom = forge->make((bool)(int32_t*)(object + 1));
return true;
- } else if (object->type == uris.atom_Int32.id) {
+ } else if (object->type == uris.atom_Int.id) {
atom = forge->make((int32_t*)(object + 1));
return true;
} else if (object->type == uris.atom_Float.id) {
@@ -67,7 +67,7 @@ from_atom(const Shared::URIs& uris, const Raul::Atom& atom, LV2_Atom* object)
*(float*)(object + 1) = atom.get_float();
break;
case Raul::Atom::INT:
- object->type = uris.atom_Int32.id;
+ object->type = uris.atom_Int.id;
object->size = sizeof(int32_t);
*(int32_t*)(object + 1) = atom.get_int32();
break;
diff --git a/src/shared/URIs.cpp b/src/shared/URIs.cpp
index 3ca8a69d..73718fab 100644
--- a/src/shared/URIs.cpp
+++ b/src/shared/URIs.cpp
@@ -52,7 +52,7 @@ URIs::URIs(Raul::Forge& f, LV2URIMap* map)
: forge(f)
, atom_Bool (map, LV2_ATOM__Bool)
, atom_Float (map, LV2_ATOM__Float)
- , atom_Int32 (map, LV2_ATOM__Int32)
+ , atom_Int (map, LV2_ATOM__Int)
, atom_MessagePort (map, LV2_ATOM__MessagePort)
, atom_String (map, LV2_ATOM__String)
, atom_ValuePort (map, LV2_ATOM__ValuePort)