summaryrefslogtreecommitdiffstats
path: root/src/libs/gui
diff options
context:
space:
mode:
authorDavid Robillard <d@drobilla.net>2007-05-04 03:59:38 +0000
committerDavid Robillard <d@drobilla.net>2007-05-04 03:59:38 +0000
commita96166710faf2447ed10194d1829db5564b0dff9 (patch)
tree4eb792fcceee17f188566fc6e6bee11e5a8c4336 /src/libs/gui
parentcf14d321e8f084f742b03e09c086d5ef30297492 (diff)
downloadingen-a96166710faf2447ed10194d1829db5564b0dff9.tar.gz
ingen-a96166710faf2447ed10194d1829db5564b0dff9.tar.bz2
ingen-a96166710faf2447ed10194d1829db5564b0dff9.zip
Made engine, serialisation, client library, and GUI all dynamically loaded modules.
Combined all executables into a single "ingen" program which can do everything. git-svn-id: http://svn.drobilla.net/lad/ingen@493 a436a847-0d15-0410-975c-d299462d15a1
Diffstat (limited to 'src/libs/gui')
-rw-r--r--src/libs/gui/App.cpp260
-rw-r--r--src/libs/gui/App.h140
-rw-r--r--src/libs/gui/BreadCrumb.h81
-rw-r--r--src/libs/gui/BreadCrumbBox.cpp206
-rw-r--r--src/libs/gui/BreadCrumbBox.h70
-rw-r--r--src/libs/gui/ConfigWindow.cpp88
-rw-r--r--src/libs/gui/ConfigWindow.h65
-rw-r--r--src/libs/gui/Configuration.cpp187
-rw-r--r--src/libs/gui/Configuration.h79
-rw-r--r--src/libs/gui/ConnectWindow.cpp426
-rw-r--r--src/libs/gui/ConnectWindow.h92
-rw-r--r--src/libs/gui/Connection.h60
-rw-r--r--src/libs/gui/ControlGroups.cpp430
-rw-r--r--src/libs/gui/ControlGroups.h162
-rw-r--r--src/libs/gui/ControlPanel.cpp259
-rw-r--r--src/libs/gui/ControlPanel.h94
-rw-r--r--src/libs/gui/DSSIController.cpp279
-rw-r--r--src/libs/gui/DSSIController.h77
-rw-r--r--src/libs/gui/DSSIModule.cpp43
-rw-r--r--src/libs/gui/DSSIModule.h46
-rw-r--r--src/libs/gui/GladeFactory.cpp72
-rw-r--r--src/libs/gui/GladeFactory.h51
-rw-r--r--src/libs/gui/LashController.cpp170
-rw-r--r--src/libs/gui/LashController.h56
-rw-r--r--src/libs/gui/LoadPatchWindow.cpp149
-rw-r--r--src/libs/gui/LoadPatchWindow.h81
-rw-r--r--src/libs/gui/LoadPluginWindow.cpp458
-rw-r--r--src/libs/gui/LoadPluginWindow.h151
-rw-r--r--src/libs/gui/LoadRemotePatchWindow.cpp163
-rw-r--r--src/libs/gui/LoadRemotePatchWindow.h93
-rw-r--r--src/libs/gui/LoadSubpatchWindow.cpp175
-rw-r--r--src/libs/gui/LoadSubpatchWindow.h79
-rw-r--r--src/libs/gui/Makefile.am104
-rw-r--r--src/libs/gui/MessagesWindow.cpp67
-rw-r--r--src/libs/gui/MessagesWindow.h58
-rw-r--r--src/libs/gui/NewSubpatchWindow.cpp111
-rw-r--r--src/libs/gui/NewSubpatchWindow.h67
-rw-r--r--src/libs/gui/NodeControlWindow.cpp136
-rw-r--r--src/libs/gui/NodeControlWindow.h76
-rw-r--r--src/libs/gui/NodeMenu.cpp262
-rw-r--r--src/libs/gui/NodeMenu.h78
-rw-r--r--src/libs/gui/NodeModule.cpp153
-rw-r--r--src/libs/gui/NodeModule.h90
-rw-r--r--src/libs/gui/NodePropertiesWindow.cpp67
-rw-r--r--src/libs/gui/NodePropertiesWindow.h58
-rw-r--r--src/libs/gui/PatchCanvas.cpp525
-rw-r--r--src/libs/gui/PatchCanvas.h129
-rw-r--r--src/libs/gui/PatchPortModule.cpp113
-rw-r--r--src/libs/gui/PatchPortModule.h80
-rw-r--r--src/libs/gui/PatchPropertiesWindow.cpp89
-rw-r--r--src/libs/gui/PatchPropertiesWindow.h64
-rw-r--r--src/libs/gui/PatchTreeWindow.cpp270
-rw-r--r--src/libs/gui/PatchTreeWindow.h112
-rw-r--r--src/libs/gui/PatchView.cpp162
-rw-r--r--src/libs/gui/PatchView.h102
-rw-r--r--src/libs/gui/PatchWindow.cpp467
-rw-r--r--src/libs/gui/PatchWindow.h143
-rw-r--r--src/libs/gui/Port.cpp61
-rw-r--r--src/libs/gui/Port.h57
-rw-r--r--src/libs/gui/PortPropertiesWindow.cpp175
-rw-r--r--src/libs/gui/PortPropertiesWindow.h70
-rw-r--r--src/libs/gui/RenameWindow.cpp117
-rw-r--r--src/libs/gui/RenameWindow.h60
-rw-r--r--src/libs/gui/SubpatchModule.cpp95
-rw-r--r--src/libs/gui/SubpatchModule.h71
-rw-r--r--src/libs/gui/ThreadedLoader.cpp141
-rw-r--r--src/libs/gui/ThreadedLoader.h99
-rw-r--r--src/libs/gui/UploadPatchWindow.cpp283
-rw-r--r--src/libs/gui/UploadPatchWindow.h105
-rw-r--r--src/libs/gui/WindowFactory.cpp357
-rw-r--r--src/libs/gui/WindowFactory.h105
-rw-r--r--src/libs/gui/cmdline.h86
-rw-r--r--src/libs/gui/ingen-icon.svg54
-rw-r--r--src/libs/gui/ingen.svg54
-rw-r--r--src/libs/gui/ingen_gui.glade3044
-rw-r--r--src/libs/gui/ingen_gui.gladep9
76 files changed, 13468 insertions, 0 deletions
diff --git a/src/libs/gui/App.cpp b/src/libs/gui/App.cpp
new file mode 100644
index 00000000..928e5f5e
--- /dev/null
+++ b/src/libs/gui/App.cpp
@@ -0,0 +1,260 @@
+/* This file is part of Ingen.
+ * Copyright (C) 2007 Dave 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 "config.h"
+#include "App.h"
+#include <cassert>
+#include <string>
+#include <fstream>
+#include <libgnomecanvasmm.h>
+#include <time.h>
+#include <sys/time.h>
+#include <raul/Path.h>
+#include "interface/EngineInterface.h"
+#include "client/ObjectModel.h"
+#include "client/PatchModel.h"
+#include "client/Store.h"
+#include "NodeModule.h"
+#include "ControlPanel.h"
+#include "SubpatchModule.h"
+#include "LoadPluginWindow.h"
+#include "PatchWindow.h"
+#include "MessagesWindow.h"
+#include "ConfigWindow.h"
+#include "GladeFactory.h"
+#include "PatchTreeWindow.h"
+#include "Configuration.h"
+#include "ConnectWindow.h"
+#include "ThreadedLoader.h"
+#include "WindowFactory.h"
+#ifdef HAVE_LASH
+#include "LashController.h"
+#endif
+using std::cerr; using std::cout; using std::endl;
+using std::string;
+namespace Ingen { namespace Client { class PluginModel; } }
+using namespace Ingen::Client;
+
+namespace Ingen {
+namespace GUI {
+
+class Port;
+
+
+/// Singleton instance
+App* App::_instance = 0;
+
+
+App::App()
+: _configuration(new Configuration()),
+ _about_dialog(NULL),
+ _window_factory(new WindowFactory()),
+ _enable_signal(true)
+{
+ Glib::RefPtr<Gnome::Glade::Xml> glade_xml = GladeFactory::new_glade_reference();
+
+ glade_xml->get_widget_derived("connect_win", _connect_window);
+ glade_xml->get_widget_derived("messages_win", _messages_window);
+ glade_xml->get_widget_derived("patch_tree_win", _patch_tree_window);
+ glade_xml->get_widget_derived("config_win", _config_window);
+ glade_xml->get_widget("about_win", _about_dialog);
+
+ _rdf_world.add_prefix("xsd", "http://www.w3.org/2001/XMLSchema#");
+ _rdf_world.add_prefix("ingen", "http://drobilla.net/ns/ingen#");
+ _rdf_world.add_prefix("ingenuity", "http://drobilla.net/ns/ingenuity#");
+ _rdf_world.add_prefix("lv2", "http://lv2plug.in/ontology#");
+ _rdf_world.add_prefix("rdfs", "http://www.w3.org/2000/01/rdf-schema#");
+ _rdf_world.add_prefix("doap", "http://usefulinc.com/ns/doap#");
+
+ _config_window->configuration(_configuration);
+
+#ifdef HAVE_SLV2
+ SLV2World slv2_world = slv2_world_new_using_rdf_world(_rdf_world.world());
+ PluginModel::set_slv2_world(slv2_world);
+#endif
+}
+
+
+App::~App()
+{
+}
+
+
+void
+App::run(int argc, char** argv)
+{
+ Gnome::Canvas::init();
+ Gtk::Main main(argc, argv);
+
+ if (!_instance)
+ _instance = new App();
+
+ /* Load settings */
+ _instance->configuration()->load_settings();
+ _instance->configuration()->apply_settings();
+
+ Gtk::Window::set_default_icon_from_file(PKGDATADIR "/ingen.svg");
+ App::instance().connect_window()->start();
+
+ main.run();
+}
+
+
+void
+App::attach(const SharedPtr<EngineInterface>& engine, const SharedPtr<SigClientInterface>& client)
+{
+ assert( ! _engine);
+ assert( ! _client);
+ assert( ! _store);
+ assert( ! _loader);
+
+ _engine = engine;
+ _client = client;
+ _store = SharedPtr<Store>(new Store(engine, client));
+ _loader = SharedPtr<ThreadedLoader>(new ThreadedLoader(engine));
+
+ _patch_tree_window->init(*_store);
+}
+
+
+void
+App::detach()
+{
+ if (_engine) {
+ _window_factory->clear();
+ _store->clear();
+
+ _loader.reset();
+ _store.reset();
+ _client.reset();
+ _engine.reset();
+ }
+}
+
+
+void
+App::error_message(const string& str)
+{
+ _messages_window->post(str);
+ _messages_window->show();
+ _messages_window->raise();
+}
+
+
+/*
+bool
+App::idle_callback()
+{
+ _client_hooks->process_events();
+
+#ifdef HAVE_LASH
+ //if (lash_controller->enabled())
+ // lash_controller->process_events();
+#endif
+
+ return true;
+}
+*/
+
+
+/******** Event Handlers ************/
+
+
+#if 0
+App::event_load_session()
+{
+ Gtk::FileChooserDialog* dialog
+ = new Gtk::FileChooserDialog(*_main_window, "Load Session", Gtk::FILE_CHOOSER_ACTION_OPEN);
+
+ dialog->add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
+ dialog->add_button(Gtk::Stock::OPEN, Gtk::RESPONSE_OK);
+ int result = dialog->run();
+ string filename = dialog->get_filename();
+ delete dialog;
+
+ cout << result << endl;
+
+ assert(result == Gtk::RESPONSE_OK || result == Gtk::RESPONSE_CANCEL || result == Gtk::RESPONSE_NONE);
+
+ if (result == Gtk::RESPONSE_OK)
+ //configuration->load_session(filename);
+ _controller->load_session(filename);
+}
+
+
+void
+App::event_save_session_as()
+{
+ Gtk::FileChooserDialog dialog(*_main_window, "Save Session", Gtk::FILE_CHOOSER_ACTION_SAVE);
+
+ /*
+ Gtk::VBox* box = dialog.get_vbox();
+ Gtk::Label warning("Warning: Recursively saving will overwrite any subpatch files \
+ without confirmation.");
+ box->pack_start(warning, false, false, 2);
+ Gtk::CheckButton recursive_checkbutton("Recursively save all subpatches");
+ box->pack_start(recursive_checkbutton, false, false, 0);
+ recursive_checkbutton.show();
+ */
+ dialog.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
+ dialog.add_button(Gtk::Stock::SAVE, Gtk::RESPONSE_OK);
+
+ int result = dialog.run();
+ //bool recursive = recursive_checkbutton.get_active();
+
+ assert(result == Gtk::RESPONSE_OK || result == Gtk::RESPONSE_CANCEL || result == Gtk::RESPONSE_NONE);
+
+ if (result == Gtk::RESPONSE_OK) {
+ string filename = dialog.get_filename();
+ if (filename.length() < 11 || filename.substr(filename.length()-10) != ".omsession")
+ filename += ".omsession";
+
+ bool confirm = false;
+ std::fstream fin;
+ fin.open(filename.c_str(), std::ios::in);
+ if (fin.is_open()) { // File exists
+ string msg = "File already exists! Are you sure you want to overwrite ";
+ msg += filename + "?";
+ Gtk::MessageDialog confir_dialog(*_main_window,
+ msg, false, Gtk::MESSAGE_WARNING, Gtk::BUTTONS_YES_NO, true);
+ if (confir_dialog.run() == Gtk::RESPONSE_YES)
+ confirm = true;
+ else
+ confirm = false;
+ } else { // File doesn't exist
+ confirm = true;
+ }
+ fin.close();
+
+ if (confirm) {
+ _controller->save_session(filename);
+ }
+ }
+}
+#endif
+
+
+void
+App::quit()
+{
+ Gtk::Main::quit();
+}
+
+
+} // namespace GUI
+} // namespace Ingen
+
diff --git a/src/libs/gui/App.h b/src/libs/gui/App.h
new file mode 100644
index 00000000..504c6920
--- /dev/null
+++ b/src/libs/gui/App.h
@@ -0,0 +1,140 @@
+/* This file is part of Ingen.
+ * Copyright (C) 2007 Dave 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 APP_H
+#define APP_H
+
+#include <cassert>
+#include <string>
+#include <map>
+#include <list>
+#include <iostream>
+#include <libgnomecanvasmm.h>
+#include <gtkmm.h>
+#include <libglademm.h>
+#include <raul/RDFWorld.h>
+#include <raul/SharedPtr.h>
+using std::string; using std::map; using std::list;
+using std::cerr; using std::endl;
+
+namespace Ingen {
+ namespace Shared {
+ class EngineInterface;
+ }
+ namespace Client {
+ class PatchModel;
+ class PluginModel;
+ class Store;
+ class SigClientInterface;
+ }
+}
+using namespace Ingen::Client;
+using Ingen::Shared::EngineInterface;
+
+/** \defgroup GUI GTK GUI
+ */
+
+namespace Ingen {
+namespace GUI {
+
+class MessagesWindow;
+class ConfigWindow;
+class PatchCanvas;
+class PatchTreeView;
+class PatchTreeWindow;
+class ConnectWindow;
+class Configuration;
+class ThreadedLoader;
+class WindowFactory;
+
+
+/** Singleton master class most everything is contained within.
+ *
+ * This is a horrible god-object, but it's shrinking in size as things are
+ * moved out. Hopefully it will go away entirely some day..
+ *
+ * \ingroup GUI
+ */
+class App
+{
+public:
+ ~App();
+
+ void error_message(const string& msg);
+
+ void attach(const SharedPtr<EngineInterface>& engine,
+ const SharedPtr<SigClientInterface>& client);
+
+ void detach();
+
+ void quit();
+
+ ConnectWindow* connect_window() const { return _connect_window; }
+ Gtk::Dialog* about_dialog() const { return _about_dialog; }
+ ConfigWindow* configuration_dialog() const { return _config_window; }
+ MessagesWindow* messages_dialog() const { return _messages_window; }
+ PatchTreeWindow* patch_tree() const { return _patch_tree_window; }
+ Configuration* configuration() const { return _configuration; }
+ WindowFactory* window_factory() const { return _window_factory; }
+
+ Raul::RDF::World* rdf_world() { return &_rdf_world; }
+
+ const SharedPtr<EngineInterface>& engine() const { return _engine; }
+ const SharedPtr<SigClientInterface>& client() const { return _client; }
+ const SharedPtr<Store>& store() const { return _store; }
+ const SharedPtr<ThreadedLoader>& loader() const { return _loader; }
+
+ static inline App& instance() { assert(_instance); return *_instance; }
+
+ static void run(int argc, char** argv);
+
+protected:
+ App();
+ static App* _instance;
+
+ static void instantiate(int argc, char** argv);
+
+ SharedPtr<EngineInterface> _engine;
+ SharedPtr<SigClientInterface> _client;
+ SharedPtr<Store> _store;
+ SharedPtr<ThreadedLoader> _loader;
+
+ Configuration* _configuration;
+
+ ConnectWindow* _connect_window;
+ MessagesWindow* _messages_window;
+ PatchTreeWindow* _patch_tree_window;
+ ConfigWindow* _config_window;
+ Gtk::Dialog* _about_dialog;
+ WindowFactory* _window_factory;
+
+ Raul::RDF::World _rdf_world;
+
+ /** Used to avoid feedback loops with (eg) process checkbutton
+ * FIXME: Maybe this should be globally implemented at the Controller level,
+ * disable all command sending while handling events to avoid feedback
+ * issues with widget event callbacks? This same pattern is duplicated
+ * too much... */
+ bool _enable_signal;
+};
+
+
+} // namespace GUI
+} // namespace Ingen
+
+#endif // APP_H
+
diff --git a/src/libs/gui/BreadCrumb.h b/src/libs/gui/BreadCrumb.h
new file mode 100644
index 00000000..96464b50
--- /dev/null
+++ b/src/libs/gui/BreadCrumb.h
@@ -0,0 +1,81 @@
+/* This file is part of Ingen.
+ * Copyright (C) 2007 Dave 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 BREADCRUMB_H
+#define BREADCRUMB_H
+
+#include <gtkmm.h>
+#include <raul/Path.h>
+#include <raul/SharedPtr.h>
+#include "PatchView.h"
+
+namespace Ingen {
+namespace GUI {
+
+
+/** Breadcrumb button in a PatchWindow.
+ *
+ * Each Breadcrumb stores a reference to a PatchView for quick switching.
+ * So, the amount of allocated PatchViews at a given time is equal to the
+ * number of visible breadcrumbs (which is the perfect cache for GUI
+ * responsiveness balanced with mem consumption).
+ *
+ * \ingroup GUI
+ */
+class BreadCrumb : public Gtk::ToggleButton
+{
+public:
+ BreadCrumb(const Path& path, SharedPtr<PatchView> view = SharedPtr<PatchView>())
+ : _path(path)
+ , _view(view)
+ {
+ assert( !view || view->patch()->path() == path);
+ set_border_width(0);
+ set_path(path);
+ show_all();
+ }
+
+ void set_view(SharedPtr<PatchView> view) {
+ assert( !view || view->patch()->path() == _path);
+ _view = view;
+ }
+
+ const Path& path() const { return _path; }
+ SharedPtr<PatchView> view() const { return _view; }
+
+ void set_path(const Path& path)
+ {
+ remove();
+ const string text = (path == "/") ? "/" : path.name();
+ Gtk::Label* lab = manage(new Gtk::Label(text));
+ lab->set_padding(0, 0);
+ lab->show();
+ add(*lab);
+
+ if (_view && _view->patch()->path() != path)
+ _view.reset();
+ }
+
+private:
+ Path _path;
+ SharedPtr<PatchView> _view;
+};
+
+} // namespace GUI
+} // namespace Ingen
+
+#endif // BREADCRUMB_H
diff --git a/src/libs/gui/BreadCrumbBox.cpp b/src/libs/gui/BreadCrumbBox.cpp
new file mode 100644
index 00000000..79a0bf69
--- /dev/null
+++ b/src/libs/gui/BreadCrumbBox.cpp
@@ -0,0 +1,206 @@
+/* This file is part of Ingen.
+ * Copyright (C) 2007 Dave 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 "BreadCrumbBox.h"
+#include "BreadCrumb.h"
+namespace Ingen {
+namespace GUI {
+
+
+BreadCrumbBox::BreadCrumbBox()
+: Gtk::HBox()
+, _active_path("/")
+, _full_path("/")
+, _enable_signal(true)
+{
+}
+
+
+SharedPtr<PatchView>
+BreadCrumbBox::view(const Path& path)
+{
+ for (std::list<BreadCrumb*>::const_iterator i = _breadcrumbs.begin(); i != _breadcrumbs.end(); ++i)
+ if ((*i)->path() == path)
+ return (*i)->view();
+
+ return SharedPtr<PatchView>();
+}
+
+
+/** Sets up the crumbs to display @a path.
+ *
+ * If @a path is already part of the shown path, it will be selected and the
+ * children preserved.
+ */
+void
+BreadCrumbBox::build(Path path, SharedPtr<PatchView> view)
+{
+ bool old_enable_signal = _enable_signal;
+ _enable_signal = false;
+
+ // Moving to a path we already contain, just switch the active button
+ if (_breadcrumbs.size() > 0 && (path.is_parent_of(_full_path) || path == _full_path)) {
+
+ for (std::list<BreadCrumb*>::iterator i = _breadcrumbs.begin(); i != _breadcrumbs.end(); ++i) {
+ if ((*i)->path() == path) {
+ (*i)->set_active(true);
+ if (!(*i)->view())
+ (*i)->set_view(view);
+
+ // views are expensive, having two around for the same patch is a bug
+ assert((*i)->view() == view);
+
+ } else {
+ (*i)->set_active(false);
+ }
+ }
+
+ _active_path = path;
+ _enable_signal = old_enable_signal;
+
+
+ // Moving to a child of the full path, just append crumbs (preserve view cache)
+ } else if (_breadcrumbs.size() > 0 && (path.is_child_of(_full_path))) {
+
+ string suffix = path.substr(_full_path.length());
+ while (suffix.length() > 0) {
+ if (suffix[0] == '/')
+ suffix = suffix.substr(1);
+ const string name = suffix.substr(0, suffix.find("/"));
+ _full_path = _full_path.base() + name;
+ BreadCrumb* but = create_crumb(_full_path, view);
+ pack_start(*but, false, false, 1);
+ _breadcrumbs.push_back(but);
+ but->show();
+ if (suffix.find("/") == string::npos)
+ break;
+ else
+ suffix = suffix.substr(suffix.find("/")+1);
+ }
+
+ for (std::list<BreadCrumb*>::iterator i = _breadcrumbs.begin(); i != _breadcrumbs.end(); ++i)
+ (*i)->set_active(false);
+ _breadcrumbs.back()->set_active(true);
+
+
+ // Rebuild from scratch
+ // Getting here is bad unless absolutely necessary, since the PatchView cache is lost
+ } else {
+
+ _full_path = path;
+ _active_path = path;
+
+ // Empty existing breadcrumbs
+ for (std::list<BreadCrumb*>::iterator i = _breadcrumbs.begin(); i != _breadcrumbs.end(); ++i)
+ remove(**i);
+ _breadcrumbs.clear();
+
+ // Add root
+ BreadCrumb* root_but = create_crumb("/", view);
+ pack_start(*root_but, false, false, 1);
+ _breadcrumbs.push_front(root_but);
+ root_but->set_active(root_but->path() == _active_path);
+
+ Path working_path = "/";
+ string suffix = path.substr(1);
+ while (suffix.length() > 0) {
+ if (suffix[0] == '/')
+ suffix = suffix.substr(1);
+ const string name = suffix.substr(0, suffix.find("/"));
+ working_path = working_path.base() + name;
+ BreadCrumb* but = create_crumb(working_path, view);
+ pack_start(*but, false, false, 1);
+ _breadcrumbs.push_back(but);
+ but->set_active(working_path == _active_path);
+ but->show();
+ if (suffix.find("/") == string::npos)
+ break;
+ else
+ suffix = suffix.substr(suffix.find("/")+1);
+ }
+ }
+
+ _enable_signal = old_enable_signal;
+}
+
+
+/** Create a new crumb, assigning it a reference to @a view if their paths
+ * match, otherwise ignoring @a view.
+ */
+BreadCrumb*
+BreadCrumbBox::create_crumb(const Path& path,
+ SharedPtr<PatchView> view)
+{
+ BreadCrumb* but = manage(new BreadCrumb(path,
+ (view && path == view->patch()->path()) ? view : SharedPtr<PatchView>()));
+
+ but->signal_toggled().connect(sigc::bind(sigc::mem_fun(
+ this, &BreadCrumbBox::breadcrumb_clicked), but));
+
+ return but;
+}
+
+
+void
+BreadCrumbBox::breadcrumb_clicked(BreadCrumb* crumb)
+{
+ if (_enable_signal) {
+ _enable_signal = false;
+
+ if (!crumb->get_active()) {
+ // Tried to turn off the current active button, bad user, no cookie
+ crumb->set_active(true);
+ } else {
+ signal_patch_selected.emit(crumb->path(), crumb->view());
+ if (crumb->path() != _active_path)
+ crumb->set_active(false);
+ }
+ _enable_signal = true;
+ }
+}
+
+
+void
+BreadCrumbBox::object_removed(const Path& path)
+{
+ for (std::list<BreadCrumb*>::iterator i = _breadcrumbs.begin(); i != _breadcrumbs.end(); ++i) {
+ if ((*i)->path() == path) {
+ // Remove all crumbs after the removed one (inclusive)
+ for (std::list<BreadCrumb*>::iterator j = i; j != _breadcrumbs.end(); ) {
+ BreadCrumb* bc = *j;
+ j = _breadcrumbs.erase(j);
+ remove(*bc);
+ }
+ break;
+ }
+ }
+}
+
+
+void
+BreadCrumbBox::object_renamed(const Path& old_path, const Path& new_path)
+{
+ for (std::list<BreadCrumb*>::iterator i = _breadcrumbs.begin(); i != _breadcrumbs.end(); ++i) {
+ if ((*i)->path() == old_path)
+ (*i)->set_path(new_path);
+ }
+}
+
+
+} // namespace GUI
+} // namespace Ingen
+
diff --git a/src/libs/gui/BreadCrumbBox.h b/src/libs/gui/BreadCrumbBox.h
new file mode 100644
index 00000000..8b806f5a
--- /dev/null
+++ b/src/libs/gui/BreadCrumbBox.h
@@ -0,0 +1,70 @@
+/* This file is part of Ingen.
+ * Copyright (C) 2007 Dave 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 BREADCRUMBBOX_H
+#define BREADCRUMBBOX_H
+
+#include <list>
+#include <gtkmm.h>
+#include <libglademm/xml.h>
+#include <libglademm.h>
+#include <raul/Path.h>
+#include <raul/SharedPtr.h>
+#include "PatchView.h"
+
+namespace Ingen {
+namespace GUI {
+
+class BreadCrumb;
+
+
+/** Collection of breadcrumb buttons forming a path.
+ *
+ * This doubles as a cache for PatchViews.
+ *
+ * \ingroup GUI
+ */
+class BreadCrumbBox : public Gtk::HBox
+{
+public:
+ BreadCrumbBox();
+
+ SharedPtr<PatchView> view(const Path& path);
+
+ void build(Path path, SharedPtr<PatchView> view);
+
+ sigc::signal<void, const Path&, SharedPtr<PatchView> > signal_patch_selected;
+
+private:
+ BreadCrumb* create_crumb(const Path& path,
+ SharedPtr<PatchView> view = SharedPtr<PatchView>());
+
+ void breadcrumb_clicked(BreadCrumb* crumb);
+
+ void object_removed(const Path& path);
+ void object_renamed(const Path& old_path, const Path& new_path);
+
+ Path _active_path;
+ Path _full_path;
+ bool _enable_signal;
+ std::list<BreadCrumb*> _breadcrumbs;
+};
+
+} // namespace GUI
+} // namespace Ingen
+
+#endif // BREADCRUMBBOX_H
diff --git a/src/libs/gui/ConfigWindow.cpp b/src/libs/gui/ConfigWindow.cpp
new file mode 100644
index 00000000..c8b29f1a
--- /dev/null
+++ b/src/libs/gui/ConfigWindow.cpp
@@ -0,0 +1,88 @@
+/* This file is part of Ingen.
+ * Copyright (C) 2007 Dave 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 <iostream>
+#include <cassert>
+#include <algorithm>
+#include <cctype>
+#include "client/NodeModel.h"
+#include "ConfigWindow.h"
+
+using namespace std;
+
+namespace Ingen {
+namespace GUI {
+
+
+ConfigWindow::ConfigWindow(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& xml)
+: Gtk::Window(cobject),
+ _configuration(NULL)
+{
+ xml->get_widget("config_path_entry", _path_entry);
+ xml->get_widget("config_save_button", _save_button);
+ xml->get_widget("config_cancel_button", _cancel_button);
+ xml->get_widget("config_ok_button", _ok_button);
+
+ _save_button->signal_clicked().connect( sigc::mem_fun(this, &ConfigWindow::save_clicked));
+ _cancel_button->signal_clicked().connect(sigc::mem_fun(this, &ConfigWindow::cancel_clicked));
+ _ok_button->signal_clicked().connect( sigc::mem_fun(this, &ConfigWindow::ok_clicked));
+}
+
+
+/** Sets the state manager for this window and initializes everything.
+ *
+ * This function MUST be called before using the window in any way!
+ */
+void
+ConfigWindow::configuration(Configuration* sm)
+{
+ _configuration = sm;
+ _path_entry->set_text(sm->patch_path());
+}
+
+
+
+///// Event Handlers //////
+
+
+void
+ConfigWindow::save_clicked()
+{
+ _configuration->patch_path(_path_entry->get_text());
+ _configuration->apply_settings();
+ _configuration->save_settings();
+}
+
+
+void
+ConfigWindow::cancel_clicked()
+{
+ hide();
+}
+
+
+void
+ConfigWindow::ok_clicked()
+{
+ _configuration->patch_path(_path_entry->get_text());
+ _configuration->apply_settings();
+ hide();
+}
+
+
+} // namespace GUI
+} // namespace Ingen
diff --git a/src/libs/gui/ConfigWindow.h b/src/libs/gui/ConfigWindow.h
new file mode 100644
index 00000000..d1a2cff6
--- /dev/null
+++ b/src/libs/gui/ConfigWindow.h
@@ -0,0 +1,65 @@
+/* This file is part of Ingen.
+ * Copyright (C) 2007 Dave 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 CONFIGWINDOW_H
+#define CONFIGWINDOW_H
+
+#include <list>
+#include <libglademm/xml.h>
+#include <libglademm.h>
+#include <gtkmm.h>
+#include "client/PluginModel.h"
+#include "Configuration.h"
+
+using std::list;
+using Ingen::Client::PluginModel;
+
+namespace Ingen {
+namespace GUI {
+
+
+/** 'Configuration' window.
+ *
+ * Loaded by glade as a derived object.
+ *
+ * \ingroup GUI
+ */
+class ConfigWindow : public Gtk::Window
+{
+public:
+ ConfigWindow(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& xml);
+
+ void configuration(Configuration* sm);
+
+private:
+ void save_clicked();
+ void cancel_clicked();
+ void ok_clicked();
+
+ Configuration* _configuration;
+
+ Gtk::Entry* _path_entry;
+ Gtk::Button* _save_button;
+ Gtk::Button* _cancel_button;
+ Gtk::Button* _ok_button;
+};
+
+
+} // namespace GUI
+} // namespace Ingen
+
+#endif // CONFIGWINDOW_H
diff --git a/src/libs/gui/Configuration.cpp b/src/libs/gui/Configuration.cpp
new file mode 100644
index 00000000..d641ea98
--- /dev/null
+++ b/src/libs/gui/Configuration.cpp
@@ -0,0 +1,187 @@
+/* This file is part of Ingen.
+ * Copyright (C) 2007 Dave 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 "Configuration.h"
+#include <cstdlib>
+#include <cassert>
+#include <iostream>
+#include <fstream>
+#include <map>
+#include "client/PortModel.h"
+#include "client/PluginModel.h"
+#include "client/PatchModel.h"
+#include "serialisation/Loader.h"
+#include "App.h"
+
+using std::cerr; using std::cout; using std::endl;
+using std::map; using std::string;
+using Ingen::Client::PatchModel;
+
+namespace Ingen {
+namespace GUI {
+
+using namespace Ingen::Client;
+
+
+Configuration::Configuration()
+: _patch_path("/usr/share/ingen/patches:/usr/local/share/ingen/patches"),
+ _audio_port_color( 0x394f66B0),
+ _control_port_color(0x396639B0),
+ _midi_port_color( 0x663939B0)
+{
+}
+
+
+Configuration::~Configuration()
+{
+}
+
+
+/** Loads settings from the rc file. Passing no parameter will load from
+ * the default location.
+ */
+void
+Configuration::load_settings(string filename)
+{
+ if (filename == "")
+ filename = string(getenv("HOME")).append("/.omgtkrc");
+
+ std::ifstream is;
+ is.open(filename.c_str(), std::ios::in);
+
+ if ( ! is.good()) {
+ cout << "[Configuration] Unable to open settings file " << filename << endl;
+ return;
+ } else {
+ cout << "[Configuration] Loading settings from " << filename << endl;
+ }
+
+ string s;
+
+ is >> s;
+ if (s != "file_version") {
+ cerr << "[Configuration] Corrupt settings file, load aborted." << endl;
+ is.close();
+ return;
+ }
+
+ is >> s;
+ if (s != "1") {
+ cerr << "[Configuration] Unknown settings file version number, load aborted." << endl;
+ is.close();
+ return;
+ }
+
+ is >> s;
+ if (s != "patch_path") {
+ cerr << "[Configuration] Corrupt settings file, load aborted." << endl;
+ is.close();
+ return;
+ }
+
+ is >> s;
+ _patch_path = s;
+
+ is.close();
+}
+
+
+/** Saves settings to rc file. Passing no parameter will save to the
+ * default location.
+ */
+void
+Configuration::save_settings(string filename)
+{
+ if (filename == "")
+ filename = string(getenv("HOME")).append("/.omgtkrc");
+
+ std::ofstream os;
+ os.open(filename.c_str(), std::ios::out);
+
+ if ( ! os.good()) {
+ cout << "[Configuration] Unable to write to setting file " << filename << endl;
+ return;
+ } else {
+ cout << "[Configuration] Saving settings to " << filename << endl;
+ }
+
+ os << "file_version 1" << endl;
+ os << "patch_path " << _patch_path << endl;
+
+ os.close();
+}
+
+
+/** Applies the current loaded settings to whichever parts of the app
+ * need updating.
+ */
+void
+Configuration::apply_settings()
+{
+ cerr << "FIXME: patch path" << endl;
+ //App::instance().loader()->set_patch_path(_patch_path);
+}
+
+
+int
+Configuration::get_port_color(const PortModel* pi)
+{
+ assert(pi != NULL);
+
+ if (pi->is_control()) {
+ return _control_port_color;
+ } else if (pi->is_audio()) {
+ return _audio_port_color;
+ } else if (pi->is_midi()) {
+ return _midi_port_color;
+ }
+
+ cerr << "[Configuration] Unknown port type! Port will be bright red, this is an error." << endl;
+ return 0xFF0000B0;
+}
+
+/*
+Coord
+Configuration::get_window_location(const string& id)
+{
+ return _window_locations[id];
+}
+
+
+void
+Configuration::set_window_location(const string& id, Coord loc)
+{
+ _window_locations[id] = loc;
+}
+
+
+Coord
+Configuration::get_window_size(const string& id)
+{
+ return _window_sizes[id];
+}
+
+
+void
+Configuration::set_window_size(const string& id, Coord size)
+{
+ _window_sizes[id] = size;
+}*/
+
+
+} // namespace GUI
+} // namespace Ingen
diff --git a/src/libs/gui/Configuration.h b/src/libs/gui/Configuration.h
new file mode 100644
index 00000000..b1a861cb
--- /dev/null
+++ b/src/libs/gui/Configuration.h
@@ -0,0 +1,79 @@
+/* This file is part of Ingen.
+ * Copyright (C) 2007 Dave 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 CONFIG_H
+#define CONFIG_H
+
+#include <string>
+
+namespace Ingen { namespace Client { class PortModel; } }
+using Ingen::Client::PortModel;
+using std::string;
+
+struct Coord { double x; double y; };
+
+namespace Ingen {
+namespace GUI {
+
+class Controller;
+
+
+/** Singleton state manager for the entire app.
+ *
+ * Stores settings like color preferences, search paths, etc.
+ * (ie any user-defined preferences to be stoed in the rc file).
+ *
+ * \ingroup GUI
+ */
+class Configuration
+{
+public:
+ Configuration();
+ ~Configuration();
+
+ void load_settings(string filename = "");
+ void save_settings(string filename = "");
+
+ void apply_settings();
+
+ string patch_path() { return _patch_path; }
+ void patch_path(const string& path) { _patch_path = path; }
+
+ const string& patch_folder() { return _patch_folder; }
+ void set_patch_folder(const string& f) { _patch_folder = f; }
+
+ int get_port_color(const PortModel* pi);
+
+private:
+ /** Search path for patch files. Colon delimited, as usual. */
+ string _patch_path;
+
+ /** Most recent patch folder shown in open dialog */
+ string _patch_folder;
+
+ int _audio_port_color;
+ int _control_port_color;
+ int _midi_port_color;
+};
+
+
+} // namespace GUI
+} // namespace Ingen
+
+#endif // CONFIG_H
+
+
diff --git a/src/libs/gui/ConnectWindow.cpp b/src/libs/gui/ConnectWindow.cpp
new file mode 100644
index 00000000..367a38fc
--- /dev/null
+++ b/src/libs/gui/ConnectWindow.cpp
@@ -0,0 +1,426 @@
+/* This file is part of Ingen.
+ * Copyright (C) 2007 Dave 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 <string>
+#include <time.h>
+#include <sys/time.h>
+#include <stdlib.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <raul/Process.h>
+#include "config.h"
+#include "interface/ClientKey.h"
+#include "interface/EngineInterface.h"
+#include "engine/tuning.h"
+#include "engine/Engine.h"
+#include "engine/DirectResponder.h"
+#include "engine/QueuedEngineInterface.h"
+#include "client/OSCClientReceiver.h"
+#include "client/OSCEngineSender.h"
+#include "client/ThreadedSigClientInterface.h"
+#include "client/Store.h"
+#include "client/PatchModel.h"
+#include "module/Module.h"
+#include "App.h"
+#include "WindowFactory.h"
+#include "ConnectWindow.h"
+using Ingen::QueuedEngineInterface;
+using Ingen::Client::ThreadedSigClientInterface;
+
+namespace Ingen {
+namespace GUI {
+
+
+// Paste together some interfaces to get the combination we want
+
+
+struct OSCSigEmitter : public OSCClientReceiver, public ThreadedSigClientInterface {
+ OSCSigEmitter(size_t queue_size, int listen_port)
+ : Ingen::Shared::ClientInterface()
+ , OSCClientReceiver(listen_port)
+ , ThreadedSigClientInterface(queue_size)
+ {
+ }
+};
+
+
+// ConnectWindow
+
+
+ConnectWindow::ConnectWindow(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& xml)
+ : Gtk::Dialog(cobject)
+ , _mode(CONNECT_REMOTE)
+ , _ping_id(-1)
+ , _attached(false)
+ , _connect_stage(0)
+ , _new_engine(NULL)
+{
+ xml->get_widget("connect_icon", _icon);
+ xml->get_widget("connect_progress_bar", _progress_bar);
+ xml->get_widget("connect_progress_label", _progress_label);
+ xml->get_widget("connect_server_radiobutton", _server_radio);
+ xml->get_widget("connect_url_entry", _url_entry);
+ xml->get_widget("connect_launch_radiobutton", _launch_radio);
+ xml->get_widget("connect_port_spinbutton", _port_spinbutton);
+ xml->get_widget("connect_internal_radiobutton", _internal_radio);
+ xml->get_widget("connect_disconnect_button", _disconnect_button);
+ xml->get_widget("connect_connect_button", _connect_button);
+ xml->get_widget("connect_quit_button", _quit_button);
+
+ _server_radio->signal_toggled().connect(sigc::mem_fun(this, &ConnectWindow::server_toggled));
+ _launch_radio->signal_toggled().connect(sigc::mem_fun(this, &ConnectWindow::launch_toggled));
+ _internal_radio->signal_clicked().connect(sigc::mem_fun(this, &ConnectWindow::internal_toggled));
+ _disconnect_button->signal_clicked().connect(sigc::mem_fun(this, &ConnectWindow::disconnect));
+ _connect_button->signal_clicked().connect(sigc::mem_fun(this, &ConnectWindow::connect));
+ _quit_button->signal_clicked().connect(sigc::mem_fun(this, &ConnectWindow::quit));
+
+ _engine_module = Ingen::Shared::load_module("ingen_engine");
+
+ if (!_engine_module) {
+ cerr << "Unable to load ingen_engine module, internal engine unavailable." << endl;
+ cerr << "If you are running from the source tree, run ingenuity_dev." << endl;
+ }
+
+ bool found = _engine_module->get_symbol("new_engine", (void*&)_new_engine);
+
+ if (!found) {
+ cerr << "Unable to find module entry point, internal engine unavailable." << endl;
+ _engine_module.reset();
+ }
+}
+
+
+void
+ConnectWindow::start()
+{
+ resize(100, 100);
+ init();
+ show();
+ connect();
+}
+
+
+void
+ConnectWindow::init()
+{
+ _icon->set(Gtk::Stock::DISCONNECT, Gtk::ICON_SIZE_LARGE_TOOLBAR);
+ _progress_bar->set_fraction(0.0);
+ _url_entry->set_sensitive(true);
+ _connect_button->set_sensitive(true);
+ _disconnect_button->set_sensitive(false);
+ _port_spinbutton->set_sensitive(false);
+ _launch_radio->set_sensitive(true);
+ if (_new_engine)
+ _internal_radio->set_sensitive(true);
+ else
+ _internal_radio->set_sensitive(false);
+ server_toggled();
+
+ _progress_label->set_text(string("Disconnected"));
+}
+
+
+/** Launch (if applicable) and connect to the Engine.
+ *
+ * This will create the EngineInterface and ClientInterface and initialize
+ * the App with them.
+ */
+void
+ConnectWindow::connect()
+{
+ assert(!_attached);
+ assert(!App::instance().engine());
+ assert(!App::instance().client());
+
+ _connect_button->set_sensitive(false);
+ _disconnect_button->set_label("gtk-cancel");
+ _disconnect_button->set_sensitive(true);
+
+ _connect_stage = 0;
+
+ if (_mode == CONNECT_REMOTE) {
+ SharedPtr<EngineInterface> engine(
+ new OSCEngineSender(_url_entry->get_text()));
+
+ OSCSigEmitter* ose = new OSCSigEmitter(1024, 16181); // FIXME: args
+ SharedPtr<SigClientInterface> client(ose);
+ App::instance().attach(engine, client);
+
+ Glib::signal_timeout().connect(
+ sigc::mem_fun(this, &ConnectWindow::gtk_callback), 100);
+
+ Glib::signal_timeout().connect(
+ sigc::mem_fun(ose, &ThreadedSigClientInterface::emit_signals), 2, G_PRIORITY_HIGH_IDLE);
+
+ } else if (_mode == LAUNCH_REMOTE) {
+
+ int port = _port_spinbutton->get_value_as_int();
+ char port_str[6];
+ snprintf(port_str, 6, "%u", port);
+ const string cmd = string("ingen --port=").append(port_str);
+
+ if (Raul::Process::launch(cmd)) {
+ SharedPtr<EngineInterface> engine(
+ new OSCEngineSender(string("osc.udp://localhost:").append(port_str)));
+
+ OSCSigEmitter* ose = new OSCSigEmitter(1024, 16181); // FIXME: args
+ SharedPtr<SigClientInterface> client(ose);
+ App::instance().attach(engine, client);
+
+ Glib::signal_timeout().connect(
+ sigc::mem_fun(this, &ConnectWindow::gtk_callback), 100);
+
+ Glib::signal_timeout().connect(
+ sigc::mem_fun(ose, &ThreadedSigClientInterface::emit_signals),
+ 2, G_PRIORITY_HIGH_IDLE);
+ } else {
+ cerr << "Failed to launch ingen process." << endl;
+ }
+
+ } else if (_mode == INTERNAL) {
+ assert(_new_engine);
+ SharedPtr<Ingen::Engine> engine(_new_engine());
+
+ engine->start_jack_driver();
+
+ SharedPtr<Ingen::EngineInterface> engine_interface = engine->new_queued_interface();
+
+ ThreadedSigClientInterface* tsci = new ThreadedSigClientInterface(Ingen::event_queue_size);
+ SharedPtr<SigClientInterface> client(tsci);
+
+ App::instance().attach(engine_interface, client);
+
+ engine_interface->set_responder(SharedPtr<Ingen::Shared::Responder>(new Ingen::DirectResponder(client, 1)));
+
+ //engine->set_event_source(engine_interface);
+
+ engine->activate();
+
+ Glib::signal_timeout().connect(
+ sigc::mem_fun(engine.get(), &Ingen::Engine::main_iteration), 1000);
+
+ Glib::signal_timeout().connect(
+ sigc::mem_fun(this, &ConnectWindow::gtk_callback), 100);
+
+ Glib::signal_timeout().connect(
+ sigc::mem_fun(tsci, &ThreadedSigClientInterface::emit_signals), 2, G_PRIORITY_HIGH_IDLE);
+ }
+}
+
+
+void
+ConnectWindow::disconnect()
+{
+ _connect_stage = -1;
+ _attached = false;
+
+ _progress_bar->set_fraction(0.0);
+ _connect_button->set_sensitive(false);
+ _disconnect_button->set_sensitive(false);
+
+ App::instance().detach();
+
+ init();
+
+ _connect_button->set_sensitive(true);
+ _disconnect_button->set_sensitive(false);
+}
+
+
+void
+ConnectWindow::quit()
+{
+ if (_attached) {
+ Gtk::MessageDialog d(*this, "This will exit the GUI, but the engine will "
+ "remain running (if it is remote).\n\nAre you sure you want to quit?",
+ true, Gtk::MESSAGE_WARNING, Gtk::BUTTONS_NONE, true);
+ d.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
+ d.add_button(Gtk::Stock::QUIT, Gtk::RESPONSE_CLOSE);
+ int ret = d.run();
+ if (ret == Gtk::RESPONSE_CLOSE)
+ Gtk::Main::quit();
+ } else {
+ Gtk::Main::quit();
+ }
+}
+
+
+void
+ConnectWindow::server_toggled()
+{
+ _url_entry->set_sensitive(true);
+ _port_spinbutton->set_sensitive(false);
+ _mode = CONNECT_REMOTE;
+}
+
+
+void
+ConnectWindow::launch_toggled()
+{
+ _url_entry->set_sensitive(false);
+ _port_spinbutton->set_sensitive(true);
+ _mode = LAUNCH_REMOTE;
+}
+
+
+void
+ConnectWindow::internal_toggled()
+{
+ _url_entry->set_sensitive(false);
+ _port_spinbutton->set_sensitive(false);
+ _mode = INTERNAL;
+}
+
+
+bool
+ConnectWindow::gtk_callback()
+{
+ /* This isn't very nice (isn't threaded), but better than no dialog at
+ * all like before :)
+ */
+
+ // Timing stuff for repeated attach attempts
+ timeval now;
+ gettimeofday(&now, NULL);
+ static timeval last = now;
+
+ /* Connecting to engine */
+ if (_connect_stage == 0) {
+
+ assert(!_attached);
+ assert(App::instance().engine());
+ assert(App::instance().client());
+
+ // FIXME
+ //assert(!App::instance().engine()->is_attached());
+ _progress_label->set_text("Connecting to engine...");
+ present();
+
+ App::instance().client()->response_sig.connect(sigc::mem_fun(this, &ConnectWindow::response_received));
+
+ _ping_id = rand();
+ while (_ping_id == -1)
+ _ping_id = rand();
+
+ App::instance().engine()->set_next_response_id(_ping_id);
+ App::instance().engine()->ping();
+ ++_connect_stage;
+
+
+ } else if (_connect_stage == 1) {
+ if (_attached) {
+ App::instance().engine()->activate();
+ ++_connect_stage;
+ } else {
+ const float ms_since_last = (now.tv_sec - last.tv_sec) * 1000.0f +
+ (now.tv_usec - last.tv_usec) * 0.001f;
+ if (ms_since_last > 1000) {
+ App::instance().engine()->set_next_response_id(_ping_id);
+ App::instance().engine()->ping();
+ last = now;
+ }
+ }
+ } else if (_connect_stage == 2) {
+ _progress_label->set_text(string("Registering as client..."));
+ //App::instance().engine()->register_client(App::instance().engine()->client_hooks());
+ // FIXME
+ //auto_ptr<ClientInterface> client(new ThreadedSigClientInterface();
+ App::instance().engine()->register_client(ClientKey(), App::instance().client());
+ App::instance().engine()->load_plugins();
+ ++_connect_stage;
+ } else if (_connect_stage == 3) {
+ // Register idle callback to process events and whatnot
+ // (Gtk refreshes at priority G_PRIORITY_HIGH_IDLE+20)
+ /*Glib::signal_timeout().connect(
+ sigc::mem_fun(this, &App::idle_callback), 100, G_PRIORITY_HIGH_IDLE);*/
+ //Glib::signal_idle().connect(sigc::mem_fun(this, &App::idle_callback));
+ /* Glib::signal_timeout().connect(
+ sigc::mem_fun((ThreadedSigClientInterface*)_client, &ThreadedSigClientInterface::emit_signals),
+ 5, G_PRIORITY_DEFAULT_IDLE);*/
+
+ _progress_label->set_text(string("Requesting plugins..."));
+ App::instance().engine()->request_plugins();
+ ++_connect_stage;
+ } else if (_connect_stage == 4) {
+ // Wait for first plugins message
+ if (App::instance().store()->plugins().size() > 0) {
+ _progress_label->set_text(string("Receiving plugins..."));
+ ++_connect_stage;
+ }
+ } else if (_connect_stage == 5) {
+ // FIXME
+ /*if (App::instance().store().plugins().size() < _client->num_plugins()) {
+ static char buf[32];
+ snprintf(buf, 32, "%zu/%zu", App::instance().store().plugins().size(),
+ ThreadedSigClientInterface::instance()->num_plugins());
+ _progress_bar->set_text(Glib::ustring(buf));
+ _progress_bar->set_fraction(
+ App::instance().store().plugins().size() / (double)_client->num_plugins());
+ } else {*/
+ _progress_bar->set_text("");
+ ++_connect_stage;
+ //}
+ } else if (_connect_stage == 6) {
+ _progress_label->set_text(string("Waiting for root patch..."));
+ App::instance().engine()->request_all_objects();
+ ++_connect_stage;
+ } else if (_connect_stage == 7) {
+ if (App::instance().store()->objects().size() > 0) {
+ SharedPtr<PatchModel> root = PtrCast<PatchModel>(App::instance().store()->object("/"));
+ assert(root);
+ App::instance().window_factory()->present_patch(root);
+ ++_connect_stage;
+ }
+ } else if (_connect_stage == 8) {
+ _progress_label->set_text("Connected to engine");
+ ++_connect_stage;
+ } else if (_connect_stage == 9) {
+ ++_connect_stage;
+ hide();
+ } else if (_connect_stage == 10) {
+ _icon->set(Gtk::Stock::CONNECT, Gtk::ICON_SIZE_LARGE_TOOLBAR);
+ _progress_bar->set_fraction(1.0);
+ _url_entry->set_sensitive(false);
+ _connect_button->set_sensitive(false);
+ _disconnect_button->set_label("gtk-disconnect");
+ _disconnect_button->set_sensitive(true);
+ _port_spinbutton->set_sensitive(false);
+ _launch_radio->set_sensitive(false);
+ _internal_radio->set_sensitive(false);
+ _connect_stage = 0; // set ourselves up for next time (if there is one)
+ return false; // deregister this callback
+ }
+
+ if (_connect_stage != 5) // yeah, ew
+ _progress_bar->pulse();
+
+ if (_connect_stage == -1) { // we were cancelled
+ _icon->set(Gtk::Stock::DISCONNECT, Gtk::ICON_SIZE_LARGE_TOOLBAR);
+ _progress_bar->set_fraction(0.0);
+ _connect_button->set_sensitive(true);
+ _disconnect_button->set_sensitive(false);
+ _disconnect_button->set_label("gtk-disconnect");
+ _progress_label->set_text(string("Disconnected"));
+ return false;
+ } else {
+ return true;
+ }
+}
+
+
+} // namespace GUI
+} // namespace Ingen
diff --git a/src/libs/gui/ConnectWindow.h b/src/libs/gui/ConnectWindow.h
new file mode 100644
index 00000000..1a4a654e
--- /dev/null
+++ b/src/libs/gui/ConnectWindow.h
@@ -0,0 +1,92 @@
+/* This file is part of Ingen.
+ * Copyright (C) 2007 Dave 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 CONNECT_WINDOW_H
+#define CONNECT_WINDOW_H
+
+#include <gtkmm.h>
+#include <libglademm/xml.h>
+#include <libglademm.h>
+#include <raul/SharedPtr.h>
+#include "client/ThreadedSigClientInterface.h"
+using Ingen::Client::SigClientInterface;
+
+namespace Ingen { class Engine; class QueuedEngineInterface; }
+
+namespace Ingen {
+namespace GUI {
+
+class App;
+class Controller;
+
+
+/** The initially visible "Connect to engine" window.
+ *
+ * This handles actually connecting to the engine and making sure everything
+ * is ready before really launching the app (eg wait for the root patch).
+ *
+ * \ingroup GUI
+ */
+class ConnectWindow : public Gtk::Dialog
+{
+public:
+ ConnectWindow(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& xml);
+
+ void start();
+ void response_received(int32_t id, bool, string) { if ((id) == _ping_id) _attached = true; }
+
+private:
+ enum Mode { CONNECT_REMOTE, LAUNCH_REMOTE, INTERNAL };
+
+ void server_toggled();
+ void launch_toggled();
+ void internal_toggled();
+
+ void init();
+ void disconnect();
+ void connect();
+ void quit();
+
+ bool gtk_callback();
+
+ Mode _mode;
+ int32_t _ping_id;
+ bool _attached;
+
+ int _connect_stage;
+
+ SharedPtr<Glib::Module> _engine_module;
+ Ingen::Engine* (*_new_engine)();
+
+ Gtk::Image* _icon;
+ Gtk::ProgressBar* _progress_bar;
+ Gtk::Label* _progress_label;
+ Gtk::Entry* _url_entry;
+ Gtk::RadioButton* _server_radio;
+ Gtk::SpinButton* _port_spinbutton;
+ Gtk::RadioButton* _launch_radio;
+ Gtk::RadioButton* _internal_radio;
+ Gtk::Button* _disconnect_button;
+ Gtk::Button* _connect_button;
+ Gtk::Button* _quit_button;
+};
+
+
+} // namespace GUI
+} // namespace Ingen
+
+#endif // CONNECT_WINDOW_H
diff --git a/src/libs/gui/Connection.h b/src/libs/gui/Connection.h
new file mode 100644
index 00000000..d4d5c10c
--- /dev/null
+++ b/src/libs/gui/Connection.h
@@ -0,0 +1,60 @@
+/* This file is part of Ingen.
+ * Copyright (C) 2007 Dave 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 CONNECTION_H
+#define CONNECTION_H
+
+#include <cassert>
+#include <string>
+#include <flowcanvas/Connection.h>
+#include <raul/SharedPtr.h>
+#include "client/ConnectionModel.h"
+using Ingen::Client::ConnectionModel;
+
+namespace Ingen {
+namespace GUI {
+
+
+/** A Connection in a Patch.
+ *
+ * \ingroup GUI
+ */
+class Connection : public LibFlowCanvas::Connection
+{
+public:
+ Connection(boost::shared_ptr<LibFlowCanvas::FlowCanvas> canvas,
+ boost::shared_ptr<ConnectionModel> model,
+ boost::shared_ptr<LibFlowCanvas::Connectable> src,
+ boost::shared_ptr<LibFlowCanvas::Connectable> dst,
+ uint32_t color)
+ : LibFlowCanvas::Connection(canvas, src, dst, color)
+ , _connection_model(model)
+ {}
+
+ virtual ~Connection() {}
+
+ SharedPtr<ConnectionModel> model() const { return _connection_model; }
+
+private:
+ SharedPtr<ConnectionModel> _connection_model;
+};
+
+
+} // namespace GUI
+} // namespace Ingen
+
+#endif // CONNECTION_H
diff --git a/src/libs/gui/ControlGroups.cpp b/src/libs/gui/ControlGroups.cpp
new file mode 100644
index 00000000..7af9011b
--- /dev/null
+++ b/src/libs/gui/ControlGroups.cpp
@@ -0,0 +1,430 @@
+/* This file is part of Ingen.
+ * Copyright (C) 2007 Dave 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 <cmath>
+#include <iostream>
+#include "interface/EngineInterface.h"
+#include "client/PluginModel.h"
+#include "client/NodeModel.h"
+#include "client/PortModel.h"
+#include "ControlGroups.h"
+#include "ControlPanel.h"
+#include "PortPropertiesWindow.h"
+#include "GladeFactory.h"
+#include "App.h"
+
+using std::cerr; using std::cout; using std::endl;
+
+using namespace Ingen::Client;
+
+namespace Ingen {
+namespace GUI {
+
+
+// ////////////////////// ControlGroup ///////////////////////////////// //
+
+ControlGroup::ControlGroup(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& glade_xml)
+: Gtk::VBox(cobject),
+ _control_panel(NULL),
+ _enable_signal(false)
+{
+}
+
+
+void
+ControlGroup::init(ControlPanel* panel, SharedPtr<PortModel> pm)
+{
+ _control_panel = panel;
+ _port_model = pm,
+
+ assert(_port_model);
+ assert(panel);
+
+ pm->control_change_sig.connect(sigc::mem_fun(this, &ControlGroup::set_value));
+}
+
+
+// ////////////////// SliderControlGroup ////////////////////// //
+
+
+SliderControlGroup::SliderControlGroup(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& xml)
+: ControlGroup(cobject, xml),
+ _enabled(true)
+{
+ xml->get_widget("control_strip_name_label", _name_label);
+ xml->get_widget("control_strip_slider", _slider);
+ xml->get_widget("control_strip_spinner", _value_spinner);
+
+ Glib::RefPtr<Gnome::Glade::Xml> menu_xml = GladeFactory::new_glade_reference("port_control_menu");
+ menu_xml->get_widget("port_control_menu", _menu);
+ menu_xml->get_widget("port_control_menu_properties", _menu_properties);
+
+ _menu_properties->signal_activate().connect(
+ sigc::mem_fun(this, &SliderControlGroup::menu_properties));
+}
+
+
+void
+SliderControlGroup::init(ControlPanel* panel, SharedPtr<PortModel> pm)
+{
+ _enable_signal = false;
+ _enabled = true;
+
+ ControlGroup::init(panel, pm);
+
+ assert(_name_label);
+ assert(_slider);
+
+ set_name(pm->path().name());
+
+ _slider->set_draw_value(false);
+
+ _name_label->signal_button_press_event().connect(sigc::mem_fun(*this, &SliderControlGroup::clicked));
+ _slider->signal_button_press_event().connect(sigc::mem_fun(*this, &SliderControlGroup::clicked));
+
+ _slider->signal_event().connect(
+ sigc::mem_fun(*this, &SliderControlGroup::slider_pressed));
+
+ _slider->signal_value_changed().connect(
+ sigc::mem_fun(*this, &SliderControlGroup::update_value_from_slider));
+
+ _value_spinner->signal_value_changed().connect(
+ sigc::mem_fun(*this, &SliderControlGroup::update_value_from_spinner));
+
+ // FIXME: code duplication w/ PortPropertiesWindow.cpp
+ float min = 0.0f;
+ float max = 1.0f;
+
+ const Atom& min_atom = pm->get_metadata("ingen:minimum");
+ const Atom& max_atom = pm->get_metadata("ingen:maximum");
+ if (min_atom.type() == Atom::FLOAT && max_atom.type() == Atom::FLOAT) {
+ min = min_atom.get_float();
+ max = max_atom.get_float();
+ }
+
+ const SharedPtr<NodeModel> parent = PtrCast<NodeModel>(pm->parent());
+
+ if (parent && parent->plugin() && parent->plugin()->type() == PluginModel::LV2) {
+ min = slv2_port_get_minimum_value(
+ parent->plugin()->slv2_plugin(),
+ slv2_plugin_get_port_by_symbol(parent->plugin()->slv2_plugin(),
+ pm->path().name().c_str()));
+ max = slv2_port_get_maximum_value(
+ parent->plugin()->slv2_plugin(),
+ slv2_plugin_get_port_by_symbol(parent->plugin()->slv2_plugin(),
+ pm->path().name().c_str()));
+ }
+
+ if (max <= min)
+ max = min + 1.0f;
+
+ _slider->set_increments(0, 0);
+ _slider->set_range(min, max);
+ _value_spinner->set_range(min, max);
+
+ set_value(pm->value());
+
+ _enable_signal = true;
+
+ show_all();
+}
+
+
+bool
+SliderControlGroup::clicked(GdkEventButton* ev)
+{
+ if (ev->button == 3) {
+ _menu->popup(ev->button, ev->time);
+ return true;
+ } else {
+ return false;
+ }
+}
+
+
+void
+SliderControlGroup::menu_properties()
+{
+ Glib::RefPtr<Gnome::Glade::Xml> xml = GladeFactory::new_glade_reference();
+
+ PortPropertiesWindow* dialog;
+ xml->get_widget_derived("port_properties_win", dialog);
+ dialog->init(this, _port_model);
+ dialog->run();
+}
+
+
+void
+SliderControlGroup::set_value(float val)
+{
+ _enable_signal = false;
+ if (_enabled) {
+ if (_slider->get_value() != val)
+ _slider->set_value(val);
+ if (_value_spinner->get_value() != val)
+ _value_spinner->set_value(val);
+ }
+ _enable_signal = true;
+}
+
+
+void
+SliderControlGroup::set_range(float min, float max)
+{
+ _slider->set_range(min, max);
+ _value_spinner->set_range(min, max);
+}
+
+
+void
+SliderControlGroup::set_name(const string& name)
+{
+ string name_label = "<span weight=\"bold\">";
+ name_label += name + "</span>";
+ _name_label->set_markup(name_label);
+}
+
+
+void
+SliderControlGroup::enable()
+{
+ _slider->property_sensitive() = true;
+ //_min_spinner->property_sensitive() = true;
+ //_max_spinner->property_sensitive() = true;
+ _value_spinner->property_sensitive() = true;
+ _name_label->property_sensitive() = true;
+}
+
+
+void
+SliderControlGroup::disable()
+{
+ _slider->property_sensitive() = false;
+ //_min_spinner->property_sensitive() = false;
+ //_max_spinner->property_sensitive() = false;
+ _value_spinner->property_sensitive() = false;
+ _name_label->property_sensitive() = false;
+}
+
+
+void
+SliderControlGroup::update_value_from_slider()
+{
+ if (_enable_signal) {
+ const float value = _slider->get_value();
+ _enable_signal = false;
+
+ _value_spinner->set_value(value);
+ _control_panel->value_changed(_port_model, value);
+
+ _enable_signal = true;
+ }
+}
+
+
+void
+SliderControlGroup::update_value_from_spinner()
+{
+ if (_enable_signal) {
+ _enable_signal = false;
+ const float value = _value_spinner->get_value();
+
+ /*if (value < _min_spinner->get_value()) {
+ _min_spinner->set_value(value);
+ _slider->set_range(_min_spinner->get_value(), _max_spinner->get_value());
+ }
+ if (value > _max_spinner->get_value()) {
+ _max_spinner->set_value(value);
+ _slider->set_range(_min_spinner->get_value(), _max_spinner->get_value());
+ }*/
+
+ _slider->set_value(value);
+
+ _control_panel->value_changed(_port_model, value);
+
+ //m_port_model->value(value);
+ _enable_signal = true;
+ }
+}
+
+
+/** Callback for when slider is grabbed so we can ignore set_control
+ * events for this port (and avoid nasty feedback issues).
+ */
+bool
+SliderControlGroup::slider_pressed(GdkEvent* ev)
+{
+ //cerr << "Pressed: " << ev->type << endl;
+ if (ev->type == GDK_BUTTON_PRESS) {
+ _enabled = false;
+ //GtkClientInterface::instance()->set_ignore_port(_port_model->path());
+ } else if (ev->type == GDK_BUTTON_RELEASE) {
+ _enabled = true;
+ //GtkClientInterface::instance()->clear_ignore_port();
+ }
+
+ return false;
+}
+
+
+// ///////////// IntegerControlGroup ////////////// //
+
+#if 0
+IntegerControlGroup::IntegerControlGroup(ControlPanel* panel, SharedPtr<PortModel> pm)
+: ControlGroup(panel, pm),
+ _enable_signal(false),
+ _alignment(0.5, 0.5, 0.0, 0.0),
+ _name_label(pm->path().name()),
+ _spinner(1.0, 0)
+{
+ set_name(pm->path().name());
+
+ _spinner.set_range(-99999, 99999);
+ _spinner.set_value(_port_model->value());
+ _spinner.signal_value_changed().connect(
+ sigc::mem_fun(*this, &IntegerControlGroup::update_value));
+ _spinner.set_increments(1, 10);
+
+ _alignment.add(_spinner);
+ pack_start(_name_label);
+ pack_start(_alignment);
+
+ _enable_signal = true;
+
+ show_all();
+}
+
+
+void
+IntegerControlGroup::set_name(const string& name)
+{
+ string name_label = "<span weight=\"bold\">";
+ name_label += name + "</span>";
+ _name_label->set_markup(name_label);
+}
+
+
+void
+IntegerControlGroup::set_value(float val)
+{
+ //cerr << "[IntegerControlGroup] Setting value to " << val << endl;
+ _enable_signal = false;
+ _spinner.set_value(val);
+ _enable_signal = true;
+}
+
+
+void
+IntegerControlGroup::enable()
+{
+ _spinner.property_sensitive() = true;
+ _name_label->property_sensitive() = true;
+}
+
+
+void
+IntegerControlGroup::disable()
+{
+ _spinner.property_sensitive() = false;
+ _name_label->property_sensitive() = false;
+}
+
+
+void
+IntegerControlGroup::update_value()
+{
+ if (_enable_signal) {
+ float value = _spinner.get_value();
+ _control_panel->value_changed(_port_model, value);
+ //m_port_model->value(value);
+ }
+}
+
+
+// ///////////// ToggleControlGroup ////////////// //
+
+
+ToggleControlGroup::ToggleControlGroup(ControlPanel* panel, SharedPtr<PortModel> pm)
+: ControlGroup(panel, pm),
+ _enable_signal(false),
+ _alignment(0.5, 0.5, 0.0, 0.0),
+ _name_label(pm->path().name())
+{
+ set_name(pm->path().name());
+
+ set_value(_port_model->value());
+ _checkbutton.signal_toggled().connect(
+ sigc::mem_fun(*this, &ToggleControlGroup::update_value));
+
+ _alignment.add(_checkbutton);
+ pack_start(_name_label);
+ pack_start(_alignment);
+
+ _enable_signal = true;
+
+ show_all();
+}
+
+
+void
+ToggleControlGroup::set_name(const string& name)
+{
+ string name_label = "<span weight=\"bold\">";
+ name_label += name + "</span>";
+ _name_label->set_markup(name_label);
+}
+
+
+void
+ToggleControlGroup::set_value(float val)
+{
+ //cerr << "[ToggleControlGroup] Setting value to " << val << endl;
+ _enable_signal = false;
+ _checkbutton.set_active( (val > 0.0f) );
+ _enable_signal = true;
+}
+
+
+void
+ToggleControlGroup::enable()
+{
+ _checkbutton.property_sensitive() = true;
+ _name_label->property_sensitive() = true;
+}
+
+
+void
+ToggleControlGroup::disable()
+{
+ _checkbutton.property_sensitive() = false;
+ _name_label->property_sensitive() = false;
+}
+
+
+void
+ToggleControlGroup::update_value()
+{
+ if (_enable_signal) {
+ float value = _checkbutton.get_active() ? 1.0f : 0.0f;
+ _control_panel->value_changed(_port_model, value);
+ //m_port_model->value(value);
+ }
+}
+#endif
+
+} // namespace GUI
+} // namespace Ingen
diff --git a/src/libs/gui/ControlGroups.h b/src/libs/gui/ControlGroups.h
new file mode 100644
index 00000000..4a1f758e
--- /dev/null
+++ b/src/libs/gui/ControlGroups.h
@@ -0,0 +1,162 @@
+/* This file is part of Ingen.
+ * Copyright (C) 2007 Dave 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 CONTROLGROUPS_H
+#define CONTROLGROUPS_H
+
+#include <cassert>
+#include <gtkmm.h>
+#include <libglademm/xml.h>
+#include <libglademm.h>
+#include "client/PortModel.h"
+#include <raul/SharedPtr.h>
+
+namespace Ingen { namespace Client { class PortModel; } }
+using namespace Ingen::Client;
+
+namespace Ingen {
+namespace GUI {
+
+class ControlPanel;
+class PortPropertiesWindow;
+
+
+/** A group of controls (for a single Port) in a NodeControlWindow.
+ *
+ * \ingroup GUI
+ */
+class ControlGroup : public Gtk::VBox
+{
+public:
+ ControlGroup(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& glade_xml);
+ virtual ~ControlGroup() { }
+
+ void init(ControlPanel* panel, SharedPtr<PortModel> pm);
+
+ inline const SharedPtr<PortModel> port_model() const { return _port_model; }
+
+protected:
+ friend class PortPropertiesWindow;
+
+ virtual void set_value(float value) = 0;
+ virtual void set_range(float min, float max) {}
+
+ ControlPanel* _control_panel;
+ SharedPtr<PortModel> _port_model;
+ bool _enable_signal;
+};
+
+
+/** A slider for a continuous control.
+ *
+ * \ingroup GUI
+ */
+class SliderControlGroup : public ControlGroup
+{
+public:
+ SliderControlGroup(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& glade_xml);
+ void init(ControlPanel* panel, SharedPtr<PortModel> pm);
+
+ void enable();
+ void disable();
+
+ void set_min(float val);
+ void set_max(float val);
+
+private:
+ void set_name(const string& name);
+
+ bool clicked(GdkEventButton* ev);
+
+ void set_value(float value);
+ void set_range(float min, float max);
+
+ void update_range();
+ void update_value_from_slider();
+ void update_value_from_spinner();
+
+ void menu_properties();
+
+ bool slider_pressed(GdkEvent* ev);
+
+ bool _enabled;
+
+ Gtk::Label* _name_label;
+ Gtk::SpinButton* _value_spinner;
+ Gtk::HScale* _slider;
+
+ Gtk::Menu* _menu;
+ Gtk::MenuItem* _menu_properties;
+};
+
+
+#if 0
+
+/** A spinbutton for integer controls.
+ *
+ * \ingroup GUI
+ */
+class IntegerControlGroup : public ControlGroup
+{
+public:
+ IntegerControlGroup(ControlPanel* panel, SharedPtr<PortModel> pm);
+
+ void enable();
+ void disable();
+
+private:
+ void set_name(const string& name);
+ void set_value(float val);
+
+ void update_value();
+
+ bool _enable_signal;
+ Gtk::Alignment _alignment;
+ Gtk::Label _name_label;
+ Gtk::SpinButton _spinner;
+};
+
+
+/** A radio button for toggle controls.
+ *
+ * \ingroup GUI
+ */
+class ToggleControlGroup : public ControlGroup
+{
+public:
+ ToggleControlGroup(ControlPanel* panel, SharedPtr<PortModel> pm);
+
+ void enable();
+ void disable();
+
+private:
+ void set_name(const string& name);
+ void set_value(float val);
+
+ void update_value();
+
+ bool _enable_signal;
+ Gtk::Alignment _alignment;
+ Gtk::Label _name_label;
+ Gtk::CheckButton _checkbutton;
+};
+#endif
+
+} // namespace GUI
+} // namespace Ingen
+
+#endif // CONTROLGROUPS_H
diff --git a/src/libs/gui/ControlPanel.cpp b/src/libs/gui/ControlPanel.cpp
new file mode 100644
index 00000000..6f3638a8
--- /dev/null
+++ b/src/libs/gui/ControlPanel.cpp
@@ -0,0 +1,259 @@
+/* This file is part of Ingen.
+ * Copyright (C) 2007 Dave 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 alongCont
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "interface/EngineInterface.h"
+#include "client/PatchModel.h"
+#include "client/NodeModel.h"
+#include "client/PortModel.h"
+#include "client/PluginModel.h"
+#include "App.h"
+#include "ControlPanel.h"
+#include "ControlGroups.h"
+#include "GladeFactory.h"
+
+namespace Ingen {
+namespace GUI {
+
+
+ControlPanel::ControlPanel(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& xml)
+: Gtk::HBox(cobject),
+ _callback_enabled(true)
+{
+ xml->get_widget("control_panel_controls_box", _control_box);
+ xml->get_widget("control_panel_voice_controls_box", _voice_control_box);
+ xml->get_widget("control_panel_all_voices_radio", _all_voices_radio);
+ xml->get_widget("control_panel_specific_voice_radio", _specific_voice_radio);
+ xml->get_widget("control_panel_voice_spinbutton", _voice_spinbutton);
+
+ _all_voices_radio->signal_toggled().connect(sigc::mem_fun(this, &ControlPanel::all_voices_selected));
+ _specific_voice_radio->signal_toggled().connect(sigc::mem_fun(this, &ControlPanel::specific_voice_selected));
+ _voice_spinbutton->signal_value_changed().connect(sigc::mem_fun(this, &ControlPanel::voice_selected));
+
+ show_all();
+}
+
+
+ControlPanel::~ControlPanel()
+{
+ for (vector<ControlGroup*>::iterator i = _controls.begin(); i != _controls.end(); ++i)
+ delete (*i);
+}
+
+
+void
+ControlPanel::init(SharedPtr<NodeModel> node, size_t poly)
+{
+ assert(node != NULL);
+ assert(poly > 0);
+
+ if (poly > 1) {
+ _voice_spinbutton->set_range(0, poly - 1);
+ } else {
+ remove(*_voice_control_box);
+ }
+
+ for (PortModelList::const_iterator i = node->ports().begin(); i != node->ports().end(); ++i) {
+ add_port(*i);
+ }
+
+ _callback_enabled = true;
+}
+
+
+ControlGroup*
+ControlPanel::find_port(const Path& path) const
+{
+ for (vector<ControlGroup*>::const_iterator cg = _controls.begin(); cg != _controls.end(); ++cg)
+ if ((*cg)->port_model()->path() == path)
+ return (*cg);
+
+ return NULL;
+}
+
+
+/** Add a control to the panel for the given port.
+ */
+void
+ControlPanel::add_port(SharedPtr<PortModel> pm)
+{
+ assert(pm);
+
+ // Already have port, don't add another
+ if (find_port(pm->path()) != NULL)
+ return;
+
+ // Add port
+ if (pm->is_control() && pm->is_input()) {
+ SliderControlGroup* cg = NULL;
+ if (pm->is_integer())
+ cerr << "FIXME: integer\n";
+ //cg = new IntegerControlGroup(this, pm);
+ else if (pm->is_toggle())
+ cerr << "FIXME: toggle\n";
+ //cg = new ToggleControlGroup(this, pm);
+ else {
+ Glib::RefPtr<Gnome::Glade::Xml> xml = GladeFactory::new_glade_reference("control_strip");
+ xml->get_widget_derived("control_strip", cg);
+ cg->init(this, pm);
+ }
+
+ if (_controls.size() > 0)
+ _control_box->pack_start(*Gtk::manage(new Gtk::HSeparator()), false, false, 4);
+
+ _controls.push_back(cg);
+ _control_box->pack_start(*cg, false, false, 0);
+
+ cg->enable();
+ /*if (pm->connected())
+ cg->disable();
+ else
+ cg->enable();*/
+
+ cg->show();
+
+ cerr << "FIXME: control panel connected port tracking\n";
+ // pm->connection_sig.connect(bind(sigc::mem_fun(this, &ControlPanel::connection), pm))
+ // pm->disconnection_sig.connect(bind(sigc::mem_fun(this, &ControlPanel::disconnection), pm))
+ }
+
+ Gtk::Requisition controls_size;
+ _control_box->size_request(controls_size);
+ _ideal_size.first = controls_size.width;
+ _ideal_size.second = controls_size.height;
+
+ Gtk::Requisition voice_size;
+ _voice_control_box->size_request(voice_size);
+ _ideal_size.first += voice_size.width;
+ _ideal_size.second += voice_size.height;
+
+ //cerr << "Setting ideal size to " << _ideal_size.first
+ // << " x " << _ideal_size.second << endl;
+}
+
+
+/** Remove the control for the given port.
+ */
+void
+ControlPanel::remove_port(const Path& path)
+{
+ bool was_first = false;
+ for (vector<ControlGroup*>::iterator cg = _controls.begin(); cg != _controls.end(); ++cg) {
+ if ((*cg)->port_model()->path() == path) {
+ _control_box->remove(**cg);
+ if (cg == _controls.begin())
+ was_first = true;
+ _controls.erase(cg);
+ break;
+ }
+ }
+}
+
+
+/** Rename the control for the given port.
+ */
+/*
+void
+ControlPanel::rename_port(const Path& old_path, const Path& new_path)
+{
+ for (vector<ControlGroup*>::iterator cg = _controls.begin(); cg != _controls.end(); ++cg) {
+ if ((*cg)->port_model()->path() == old_path) {
+ (*cg)->set_name(new_path.name());
+ return;
+ }
+ }
+}
+*/
+
+#if 0
+/** Enable the control for the given port.
+ *
+ * Used when all connections to port are un-made.
+ */
+void
+ControlPanel::enable_port(const Path& path)
+{
+ for (vector<ControlGroup*>::iterator i = _controls.begin(); i != _controls.end(); ++i) {
+ if ((*i)->port_model()->path() == path) {
+ (*i)->enable();
+ return;
+ }
+ }
+}
+
+
+/** Disable the control for the given port.
+ *
+ * Used when port is connected.
+ */
+void
+ControlPanel::disable_port(const Path& path)
+{
+ for (vector<ControlGroup*>::iterator i = _controls.begin(); i != _controls.end(); ++i) {
+ if ((*i)->port_model()->path() == path) {
+ (*i)->disable();
+ return;
+ }
+ }
+}
+#endif
+
+/** Callback for ControlGroups to notify this of a change.
+ */
+void
+ControlPanel::value_changed(SharedPtr<PortModel> port, float val)
+{
+ if (_callback_enabled) {
+ App::instance().engine()->disable_responses();
+
+ /* Send the message, but set the client-side model's value to the new
+ * setting right away (so the value doesn't need to be echoed back) */
+
+ if (_all_voices_radio->get_active()) {
+ App::instance().engine()->set_port_value(port->path(), val);
+ port->value(val);
+ } else {
+ int voice = _voice_spinbutton->get_value_as_int();
+ App::instance().engine()->set_port_value(port->path(), voice, val);
+ port->value(val);
+ }
+
+ App::instance().engine()->set_next_response_id(rand()); // FIXME: inefficient, probably not good
+ }
+}
+
+void
+ControlPanel::all_voices_selected()
+{
+ _voice_spinbutton->property_sensitive() = false;
+}
+
+
+void
+ControlPanel::specific_voice_selected()
+{
+ _voice_spinbutton->property_sensitive() = true;
+}
+
+
+void
+ControlPanel::voice_selected()
+{
+}
+
+
+} // namespace GUI
+} // namespace Ingen
diff --git a/src/libs/gui/ControlPanel.h b/src/libs/gui/ControlPanel.h
new file mode 100644
index 00000000..230b9b65
--- /dev/null
+++ b/src/libs/gui/ControlPanel.h
@@ -0,0 +1,94 @@
+/* This file is part of Ingen.
+ * Copyright (C) 2007 Dave 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 CONTROLPANEL_H
+#define CONTROLPANEL_H
+
+#include <vector>
+#include <string>
+#include <iostream>
+#include <utility> // for pair<>
+#include <sigc++/sigc++.h>
+#include <gtkmm.h>
+#include <libglademm/xml.h>
+#include <libglademm.h>
+#include <raul/Path.h>
+#include "ControlGroups.h"
+
+
+using std::vector; using std::string; using std::pair;
+using std::cerr; using std::cout; using std::endl;
+
+namespace Ingen { namespace Client {
+ class PortModel;
+ class NodeModel;
+} }
+using namespace Ingen::Client;
+
+namespace Ingen {
+namespace GUI {
+
+
+/** A group of controls for a node (or patch).
+ *
+ * Used by both NodeControlWindow and the main window (for patch controls).
+ *
+ * \ingroup GUI
+ */
+class ControlPanel : public Gtk::HBox {
+public:
+ ControlPanel(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& glade_xml);
+ virtual ~ControlPanel();
+
+ void init(SharedPtr<NodeModel> node, size_t poly);
+
+ ControlGroup* find_port(const Path& path) const;
+
+ void add_port(SharedPtr<PortModel> port);
+ void remove_port(const Path& path);
+
+ void enable_port(const Path& path);
+ void disable_port(const Path& path);
+
+ size_t num_controls() const { return _controls.size(); }
+ pair<int,int> ideal_size() const { return _ideal_size; }
+
+ // Callback for ControlGroup
+ void value_changed(SharedPtr<PortModel> port_path, float val);
+
+private:
+ void all_voices_selected();
+ void specific_voice_selected();
+ void voice_selected();
+
+ bool _callback_enabled;
+
+ pair<int,int> _ideal_size;
+
+ vector<ControlGroup*> _controls;
+ Gtk::VBox* _control_box;
+ Gtk::Box* _voice_control_box;
+ Gtk::RadioButton* _all_voices_radio;
+ Gtk::RadioButton* _specific_voice_radio;
+ Gtk::SpinButton* _voice_spinbutton;
+};
+
+
+} // namespace GUI
+} // namespace Ingen
+
+#endif // CONTROLPANEL_H
diff --git a/src/libs/gui/DSSIController.cpp b/src/libs/gui/DSSIController.cpp
new file mode 100644
index 00000000..f245d61f
--- /dev/null
+++ b/src/libs/gui/DSSIController.cpp
@@ -0,0 +1,279 @@
+/* This file is part of Ingen.
+ * Copyright (C) 2007 Dave 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 "DSSIController.h"
+#include <iomanip>
+#include <sstream>
+#include <dirent.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include "interface/EngineInterface.h"
+#include "client/NodeModel.h"
+#include "App.h"
+#include "DSSIModule.h"
+
+namespace Ingen {
+namespace GUI {
+
+
+DSSIController::DSSIController(SharedPtr<NodeModel> model)
+: _banks_dirty(true)
+{
+#if 0
+ assert(model->plugin()->type() == PluginModel::DSSI);
+ Gtk::Menu::MenuList& items = _menu.items();
+ items[0].property_sensitive() = true; // "Show Control Window" item
+
+ Gtk::Menu_Helpers::MenuElem program_elem("Select Program", _program_menu);
+ items.push_front(program_elem);
+ _program_menu_item = program_elem.get_child();
+ _program_menu_item->set_sensitive(false);
+
+ items.push_front(Gtk::Menu_Helpers::MenuElem("Show Plugin GUI",
+ sigc::mem_fun(this, &DSSIController::show_gui)));
+#endif
+}
+
+void
+DSSIController::program_add(int bank, int program, const string& name)
+{
+ cerr << "FIXME: DSSI add program\n";
+ //node_model()->add_program(bank, program, name);
+ //m_banks_dirty = true;
+}
+
+
+void
+DSSIController::program_remove(int bank, int program)
+{
+ cerr << "FIXME: DSSI add program\n";
+ //node_model()->remove_program(bank, program);
+ //m_banks_dirty = true;
+}
+
+/** Trivial wrapper of attempt_to_show_gui for libsigc
+ */
+void
+DSSIController::show_gui()
+{
+ attempt_to_show_gui();
+}
+
+
+void
+DSSIController::update_program_menu()
+{
+ cerr << "FIXME: Program menu\n";
+#if 0
+ _program_menu.items().clear();
+
+ const map<int, map<int, string> >& banks = node_model()->get_programs();
+ std::ostringstream oss;
+
+ map<int, map<int, string> >::const_iterator i;
+ for (i = banks.begin(); i != banks.end(); ++i) {
+ Gtk::Menu* bank_menu;
+ if (banks.size() > 1)
+ bank_menu = manage(new Gtk::Menu());
+ else
+ bank_menu = &_program_menu;
+ map<int, string>::const_iterator j;
+ for (j = i->second.begin(); j != i->second.end(); ++j) {
+ oss.str("");
+ oss << std::setw(3) << std::setfill('0')
+ << j->first << ' ' << j->second;
+ sigc::slot<void> slt = bind(
+ bind(sigc::mem_fun(*this, &DSSIController::send_program_change),
+ j->first), i->first);
+ bank_menu->items().push_back(
+ Gtk::Menu_Helpers::MenuElem(oss.str(), slt));
+ }
+ if (banks.size() > 1) {
+ oss.str("");
+ oss << "Bank " << i->first;
+ _program_menu.items().push_back(
+ Gtk::Menu_Helpers::MenuElem(oss.str(), *bank_menu));
+ }
+ }
+
+ // Disable the program menu if there are no programs
+ if (banks.size() == 0)
+ _program_menu_item->set_sensitive(false);
+ else
+ _program_menu_item->set_sensitive(true);
+
+ _banks_dirty = false;
+#endif
+}
+
+
+void
+DSSIController::send_program_change(int bank, int program)
+{
+ //App::instance().engine()->set_program(node_model()->path(), bank, program);
+}
+
+
+/** Attempt to show the DSSI GUI for this plugin.
+ *
+ * Returns whether or not GUI was successfully loaded/shown.
+ */
+bool
+DSSIController::attempt_to_show_gui()
+{
+ cerr << "FIXME: DSSI GUI" << endl;
+#if 0
+ // Shamelessley "inspired by" jack-dssi-host
+ // Copyright 2004 Chris Cannam, Steve Harris and Sean Bolton.
+
+ const bool verbose = false;
+
+ string engine_url = App::instance().engine()->engine_url();
+ // Hack off last character if it's a /
+ if (engine_url[engine_url.length()-1] == '/')
+ engine_url = engine_url.substr(0, engine_url.length()-1);
+
+ const char* dllName = node_model()->plugin()->lib_name().c_str();
+ const char* label = node_model()->plugin()->plug_label().c_str();
+ const char* myName = "ingenuity";
+ const string& oscUrl = engine_url + "/dssi" + node_model()->path();
+
+ struct dirent* entry = NULL;
+ char* dllBase = strdup(dllName);
+ char* subpath = NULL;
+ DIR* subdir = NULL;
+ char* filename = NULL;
+ struct stat buf;
+ int fuzzy = 0;
+
+ char* env_dssi_path = getenv("DSSI_PATH");
+ string dssi_path;
+ if (!env_dssi_path) {
+ cerr << "DSSI_PATH is empty. Assuming /usr/lib/dssi:/usr/local/lib/dssi." << endl;
+ dssi_path = "/usr/lib/dssi:/usr/local/lib/dssi";
+ } else {
+ dssi_path = env_dssi_path;
+ }
+
+ if (strlen(dllBase) > 3 && !strcasecmp(dllBase + strlen(dllBase) - 3, ".so")) {
+ dllBase[strlen(dllBase) - 3] = '\0';
+ }
+
+
+ // This is pretty nasty, it loops through the path, even if the dllBase is absolute
+ while (dssi_path != "") {
+ string directory = dssi_path.substr(0, dssi_path.find(':'));
+ if (dssi_path.find(':') != string::npos)
+ dssi_path = dssi_path.substr(dssi_path.find(':')+1);
+ else
+ dssi_path = "";
+
+ if (*dllBase == '/') {
+ subpath = strdup(dllBase);
+ } else {
+ subpath = (char*)malloc(strlen(directory.c_str()) + strlen(dllBase) + 2);
+ sprintf(subpath, "%s/%s", directory.c_str(), dllBase);
+ }
+
+ for (fuzzy = 0; fuzzy <= 1; ++fuzzy) {
+
+ if (!(subdir = opendir(subpath))) {
+ if (verbose) {
+ fprintf(stderr, "%s: can't open plugin GUI directory \"%s\"\n", myName, subpath);
+ }
+ break;
+ }
+
+ while ((entry = readdir(subdir))) {
+
+ if (entry->d_name[0] == '.')
+ continue;
+ if (!strchr(entry->d_name, '_'))
+ continue;
+
+ if (fuzzy) {
+ if (verbose) {
+ fprintf(stderr, "checking %s against %s\n", entry->d_name, dllBase);
+ }
+ if (strncmp(entry->d_name, dllBase, strlen(dllBase)))
+ continue;
+ } else {
+ if (verbose) {
+ fprintf(stderr, "checking %s against %s\n", entry->d_name, label);
+ }
+ if (strncmp(entry->d_name, label, strlen(label)))
+ continue;
+ }
+
+ filename = (char*)malloc(strlen(subpath) + strlen(entry->d_name) + 2);
+ sprintf(filename, "%s/%s", subpath, entry->d_name);
+
+ if (stat(filename, &buf)) {
+ perror("stat failed");
+ free(filename);
+ continue;
+ }
+
+ if ((S_ISREG(buf.st_mode) || S_ISLNK(buf.st_mode)) &&
+ (buf.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))) {
+
+ if (verbose) {
+ fprintf(stderr, "%s: trying to execute GUI at \"%s\"\n",
+ myName, filename);
+ }
+
+ if (fork() == 0) {
+ execlp(filename, filename, oscUrl.c_str(), dllName, label,
+ node_model()->name().c_str(), 0);
+ perror("exec failed");
+ exit(1);
+ }
+
+ free(filename);
+ free(subpath);
+ free(dllBase);
+ return true;
+ }
+
+ free(filename);
+ }
+ }
+ }
+
+ cerr << "Unable to launch DSSI GUI for " << node_model()->path() << endl;
+
+ free(subpath);
+ free(dllBase);
+#endif
+ return false;
+}
+
+
+void
+DSSIController::show_menu(GdkEventButton* event)
+{
+#if 0
+ if (_banks_dirty)
+ update_program_menu();
+ NodeController::show_menu(event);
+#endif
+}
+
+
+} // namespace GUI
+} // namespace Ingen
+
diff --git a/src/libs/gui/DSSIController.h b/src/libs/gui/DSSIController.h
new file mode 100644
index 00000000..fdb0c1c0
--- /dev/null
+++ b/src/libs/gui/DSSIController.h
@@ -0,0 +1,77 @@
+/* This file is part of Ingen.
+ * Copyright (C) 2007 Dave 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 DSSICONTROLLER_H
+#define DSSICONTROLLER_H
+
+#include <string>
+#include <gtkmm.h>
+#include <raul/Path.h>
+#include "client/NodeModel.h"
+
+using std::string;
+using namespace Ingen::Client;
+
+namespace Ingen { namespace Client {
+ class MetadataModel;
+ class NodeModel;
+ class PortModel;
+} }
+
+namespace Ingen {
+namespace GUI {
+
+class NodeControlWindow;
+class NodePropertiesWindow;
+
+/* Controller for a DSSI node.
+ *
+ * FIXME: legacy cruft. move this code to the appropriate places and nuke
+ * this class.
+ *
+ * \ingroup GUI
+ */
+class DSSIController
+{
+public:
+ DSSIController(SharedPtr<NodeModel> model);
+
+ virtual ~DSSIController() {}
+
+ void show_gui();
+ bool attempt_to_show_gui();
+ void program_add(int bank, int program, const string& name);
+ void program_remove(int bank, int program);
+
+ void send_program_change(int bank, int program);
+
+ void show_menu(GdkEventButton* event);
+
+private:
+ void update_program_menu();
+
+ Gtk::Menu _program_menu;
+ Glib::RefPtr<Gtk::MenuItem> _program_menu_item;
+
+ bool _banks_dirty;
+};
+
+
+} // namespace GUI
+} // namespace Ingen
+
+#endif // DSSICONTROLLER_H
diff --git a/src/libs/gui/DSSIModule.cpp b/src/libs/gui/DSSIModule.cpp
new file mode 100644
index 00000000..57cbd03f
--- /dev/null
+++ b/src/libs/gui/DSSIModule.cpp
@@ -0,0 +1,43 @@
+/* This file is part of Ingen.
+ * Copyright (C) 2007 Dave 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 "DSSIModule.h"
+#include "DSSIController.h"
+
+namespace Ingen {
+namespace GUI {
+
+
+DSSIModule::DSSIModule(boost::shared_ptr<PatchCanvas> canvas, SharedPtr<NodeModel> node)
+: NodeModule(canvas, node)
+{
+}
+
+
+void
+DSSIModule::on_double_click(GdkEventButton* ev)
+{
+ /*
+ DSSIController* dc = dynamic_cast<DSSIController*>(_node);
+ if (!dc || ! dc->attempt_to_show_gui())
+ show_control_window();
+ */
+}
+
+
+} // namespace GUI
+} // namespace Ingen
diff --git a/src/libs/gui/DSSIModule.h b/src/libs/gui/DSSIModule.h
new file mode 100644
index 00000000..1c041900
--- /dev/null
+++ b/src/libs/gui/DSSIModule.h
@@ -0,0 +1,46 @@
+/* This file is part of Ingen.
+ * Copyright (C) 2007 Dave 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 DSSIMODULE_H
+#define DSSIMODULE_H
+
+#include "NodeModule.h"
+
+namespace Ingen {
+namespace GUI {
+
+class DSSIController;
+
+/* Module for a DSSI node.
+ *
+ * \ingroup GUI
+ */
+class DSSIModule : public NodeModule
+{
+public:
+ DSSIModule(boost::shared_ptr<PatchCanvas> canvas, SharedPtr<NodeModel> node);
+ virtual ~DSSIModule() {}
+
+ void on_double_click(GdkEventButton* ev);
+};
+
+
+} // namespace GUI
+} // namespace Ingen
+
+#endif // DSSIMODULE_H
+
diff --git a/src/libs/gui/GladeFactory.cpp b/src/libs/gui/GladeFactory.cpp
new file mode 100644
index 00000000..ac796d9f
--- /dev/null
+++ b/src/libs/gui/GladeFactory.cpp
@@ -0,0 +1,72 @@
+/* This file is part of Ingen.
+ * Copyright (C) 2007 Dave 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 "GladeFactory.h"
+#include <iostream>
+#include <fstream>
+using std::cout; using std::cerr; using std::endl;
+using std::ifstream;
+
+namespace Ingen {
+namespace GUI {
+
+
+Glib::ustring GladeFactory::glade_filename = "";
+
+
+void
+GladeFactory::find_glade_file()
+{
+ // Check for the .glade file in current directory
+ glade_filename = "./ingenuity.glade";
+ ifstream fs(glade_filename.c_str());
+ if (fs.fail()) { // didn't find it, check PKGDATADIR
+ fs.clear();
+ glade_filename = PKGDATADIR;
+ glade_filename += "/ingenuity.glade";
+
+ fs.open(glade_filename.c_str());
+ if (fs.fail()) {
+ cerr << "[GladeFactory] Unable to find ingenuity.glade in current directory or " << PKGDATADIR << "." << endl;
+ throw;
+ }
+ fs.close();
+ }
+ cerr << "[GladeFactory] Loading widgets from " << glade_filename.c_str() << endl;
+}
+
+
+Glib::RefPtr<Gnome::Glade::Xml>
+GladeFactory::new_glade_reference(const string& toplevel_widget)
+{
+ if (glade_filename == "")
+ find_glade_file();
+
+ try {
+ if (toplevel_widget == "")
+ return Gnome::Glade::Xml::create(glade_filename);
+ else
+ return Gnome::Glade::Xml::create(glade_filename, toplevel_widget.c_str());
+ } catch (const Gnome::Glade::XmlError& ex) {
+ cerr << ex.what() << endl;
+ throw ex;
+ }
+}
+
+
+} // namespace GUI
+} // namespace Ingen
diff --git a/src/libs/gui/GladeFactory.h b/src/libs/gui/GladeFactory.h
new file mode 100644
index 00000000..0a2e88fc
--- /dev/null
+++ b/src/libs/gui/GladeFactory.h
@@ -0,0 +1,51 @@
+/* This file is part of Ingen.
+ * Copyright (C) 2007 Dave 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 GLADEFACTORY_H
+#define GLADEFACTORY_H
+
+#include <string>
+#include <libglademm/xml.h>
+
+using std::string;
+
+namespace Ingen {
+namespace GUI {
+
+
+/** Creates glade references, so various objects can create widgets.
+ * Purely static.
+ *
+ * \ingroup GUI
+ */
+class GladeFactory {
+public:
+ static Glib::RefPtr<Gnome::Glade::Xml>
+ new_glade_reference(const string& toplevel_widget = "");
+
+private:
+ GladeFactory() {}
+
+ static void find_glade_file();
+ static Glib::ustring glade_filename;
+};
+
+
+} // namespace GUI
+} // namespace Ingen
+
+#endif // GLADEFACTORY_H
diff --git a/src/libs/gui/LashController.cpp b/src/libs/gui/LashController.cpp
new file mode 100644
index 00000000..b62ec818
--- /dev/null
+++ b/src/libs/gui/LashController.cpp
@@ -0,0 +1,170 @@
+/* This file is part of Ingen.
+ * Copyright (C) 2007 Dave 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 "LashController.h"
+#include "config.h"
+#include <iostream>
+#include <string>
+#include <cassert>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include "App.h"
+#include "PatchModel.h"
+
+using std::cerr; using std::cout; using std::endl;
+using std::string;
+
+namespace Ingen {
+namespace GUI {
+
+
+LashController::LashController(lash_args_t* args)
+: _client(NULL)
+{
+ _client = lash_init(args, PACKAGE_NAME,
+ /*LASH_Config_Data_Set|*/LASH_Config_File, LASH_PROTOCOL(2, 0));
+ if (_client == NULL) {
+ cerr << "[LashController] Failed to connect to LASH. Session management will not function." << endl;
+ } else {
+ cout << "[LashController] Lash initialised" << endl;
+ }
+
+ lash_event_t* event = lash_event_new_with_type(LASH_Client_Name);
+ lash_event_set_string(event, "Ingen");
+ lash_send_event(_client, event);
+}
+
+
+LashController::~LashController()
+{
+ if (_client != NULL) {
+ lash_event_t* quit_event = lash_event_new_with_type(LASH_Quit);
+ lash_send_event(_client, quit_event);
+ }
+}
+
+
+void
+LashController::process_events()
+{
+ assert(_client != NULL);
+
+ lash_event_t* ev = NULL;
+ lash_config_t* conf = NULL;
+
+ // Process events
+ while ((ev = lash_get_event(_client)) != NULL) {
+ handle_event(ev);
+ lash_event_destroy(ev);
+ }
+
+ // Process configs
+ while ((conf = lash_get_config(_client)) != NULL) {
+ handle_config(conf);
+ lash_config_destroy(conf);
+ }
+}
+
+
+void
+LashController::handle_event(lash_event_t* ev)
+{
+ assert(ev != NULL);
+
+ LASH_Event_Type type = lash_event_get_type(ev);
+ const char* c_str = lash_event_get_string(ev);
+ string str = (c_str == NULL) ? "" : c_str;
+
+ if (type == LASH_Save_File) {
+ cout << "[LashController] LASH Save File - " << str << endl;
+ save(str);
+ lash_send_event(_client, lash_event_new_with_type(LASH_Save_File));
+ } else if (type == LASH_Restore_File) {
+ cout << "[LashController] LASH Restore File - " << str << endl;
+ cerr << "LASH RESTORE NOT YET (RE)IMPLEMENTED." << endl;
+ /*_controller->load_session_blocking(str + "/session");
+ _controller->lash_restore_finished();
+ lash_send_event(_client, lash_event_new_with_type(LASH_Restore_File));
+ */
+ /*} else if (type == LASH_Save_Data_Set) {
+ //cout << "[LashController] LASH Save Data Set - " << endl;
+
+ // Tell LASH we're done
+ lash_send_event(_client, lash_event_new_with_type(LASH_Save_Data_Set));
+ */
+ } else if (type == LASH_Quit) {
+ cout << "[LashController] LASH Quit" << endl;
+ _client = NULL;
+ App::instance().quit();
+ } else {
+ cerr << "[LashController] Unhandled LASH event, type: " << static_cast<int>(type) << endl;
+ }
+}
+
+
+void
+LashController::handle_config(lash_config_t* conf)
+{
+ assert(conf != NULL);
+
+ const char* key = NULL;
+ const void* val = NULL;
+ size_t val_size = 0;
+
+ cout << "[LashController] LASH Config. Key = " << key << endl;
+
+ key = lash_config_get_key(conf);
+ val = lash_config_get_value(conf);
+ val_size = lash_config_get_value_size(conf);
+}
+
+void
+LashController::save(const string& dir)
+{
+ cerr << "LASH SAVING NOT YET (RE)IMPLEMENTED\n";
+ /*
+ PatchController* pc = NULL;
+
+ // Save every patch under dir with it's path as a filename
+ // (so the dir structure will resemble the patch heirarchy)
+ for (map<string,PatchController*>::iterator i = _app->patches().begin();
+ i != _app->patches().end(); ++i) {
+ pc = (*i).second;
+ pc->model()->filename(dir + pc->model()->path() + ".om");
+ }
+
+ // Create directories
+ for (map<string,PatchController*>::iterator i = _app->patches().begin();
+ i != _app->patches().end(); ++i) {
+ pc = (*i).second;
+ if (pc->model()->parent() != NULL) {
+ string path = Path::parent(pc->model()->path()).substr(1) + "/";
+ while (path.find("/") != string::npos) {
+ mkdir(string(dir +"/"+ path.substr(0, path.find("/"))).c_str(), 0744);
+ path = path.substr(path.find("/")+1);
+ }
+ }
+ _controller->save_patch_blocking(pc->model(), pc->model()->filename(), false);
+ }
+
+ //m_app->state_manager()->save(str + "/omgtkrc");
+ _controller->save_session_blocking(dir + "/session");
+ */
+}
+
+} // namespace GUI
+} // namespace Ingen
diff --git a/src/libs/gui/LashController.h b/src/libs/gui/LashController.h
new file mode 100644
index 00000000..f47eb5e9
--- /dev/null
+++ b/src/libs/gui/LashController.h
@@ -0,0 +1,56 @@
+/* This file is part of IngenGtk.
+ * Copyright (C) 2007 Dave 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 LASHCONTROLLER_H
+#define LASHCONTROLLER_H
+
+#include <string>
+#include <lash/lash.h>
+using std::string;
+
+namespace Ingen {
+namespace GUI {
+
+class App;
+
+/* Old and unused LASH controller.
+ *
+ * \ingroup GUI
+ */
+class LashController
+{
+public:
+ LashController(lash_args_t* args);
+ ~LashController();
+
+ bool enabled() { return lash_enabled(_client); }
+ void process_events();
+
+private:
+ void save(const string& dir);
+
+ lash_client_t* _client;
+
+ void handle_event(lash_event_t* conf);
+ void handle_config(lash_config_t* conf);
+};
+
+
+} // namespace GUI
+} // namespace Ingen
+
+#endif // LASHCONTROLLER_H
diff --git a/src/libs/gui/LoadPatchWindow.cpp b/src/libs/gui/LoadPatchWindow.cpp
new file mode 100644
index 00000000..faaa519a
--- /dev/null
+++ b/src/libs/gui/LoadPatchWindow.cpp
@@ -0,0 +1,149 @@
+/* This file is part of Ingen.
+ * Copyright (C) 2007 Dave 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 "LoadPatchWindow.h"
+#include <sys/types.h>
+#include <dirent.h>
+#include <boost/optional/optional.hpp>
+#include "interface/EngineInterface.h"
+#include "client/PatchModel.h"
+#include "App.h"
+#include "Configuration.h"
+#include "ThreadedLoader.h"
+
+using namespace Ingen::Serialisation;
+using boost::optional;
+
+namespace Ingen {
+namespace GUI {
+
+
+LoadPatchWindow::LoadPatchWindow(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& xml)
+: Gtk::FileChooserDialog(cobject),
+ _replace(true)
+{
+ xml->get_widget("load_patch_poly_from_current_radio", _poly_from_current_radio);
+ xml->get_widget("load_patch_poly_from_file_radio", _poly_from_file_radio);
+ xml->get_widget("load_patch_poly_from_user_radio", _poly_from_user_radio);
+ xml->get_widget("load_patch_poly_spinbutton", _poly_spinbutton);
+ xml->get_widget("load_patch_ok_button", _ok_button);
+ xml->get_widget("load_patch_cancel_button", _cancel_button);
+
+ _poly_from_current_radio->signal_toggled().connect(sigc::mem_fun(this, &LoadPatchWindow::poly_from_file_selected));
+ _poly_from_file_radio->signal_toggled().connect(sigc::mem_fun(this, &LoadPatchWindow::poly_from_file_selected));
+ _poly_from_user_radio->signal_toggled().connect(sigc::mem_fun(this, &LoadPatchWindow::poly_from_user_selected));
+ _ok_button->signal_clicked().connect(sigc::mem_fun(this, &LoadPatchWindow::ok_clicked));
+ _cancel_button->signal_clicked().connect(sigc::mem_fun(this, &LoadPatchWindow::cancel_clicked));
+
+ _poly_from_current_radio->set_active(true);
+
+ Gtk::FileFilter filt;
+ filt.add_pattern("*.om");
+ filt.set_name("Om patch files (XML, DEPRECATED) (*.om)");
+ filt.add_pattern("*.ingen.ttl");
+ filt.set_name("Ingen patch files (RDF, *.ingen.ttl)");
+ set_filter(filt);
+
+ // Add global examples directory to "shortcut folders" (bookmarks)
+ string examples_dir = PKGDATADIR;
+ examples_dir.append("/patches");
+ DIR* d = opendir(examples_dir.c_str());
+ if (d != NULL)
+ add_shortcut_folder(examples_dir);
+}
+
+
+void
+LoadPatchWindow::present(SharedPtr<PatchModel> patch, MetadataMap data)
+{
+ set_patch(patch);
+ _initial_data = data;
+ Gtk::Window::present();
+}
+
+
+/** Sets the patch controller for this window and initializes everything.
+ *
+ * This function MUST be called before using the window in any way!
+ */
+void
+LoadPatchWindow::set_patch(SharedPtr<PatchModel> patch)
+{
+ _patch = patch;
+}
+
+
+void
+LoadPatchWindow::on_show()
+{
+ if (App::instance().configuration()->patch_folder().length() > 0)
+ set_current_folder(App::instance().configuration()->patch_folder());
+ Gtk::FileChooserDialog::on_show();
+}
+
+
+///// Event Handlers //////
+
+
+void
+LoadPatchWindow::poly_from_file_selected()
+{
+ _poly_spinbutton->property_sensitive() = false;
+}
+
+
+void
+LoadPatchWindow::poly_from_user_selected()
+{
+ _poly_spinbutton->property_sensitive() = true;
+}
+
+
+void
+LoadPatchWindow::ok_clicked()
+{
+ // If unset load_patch will load values
+ optional<const string&> name;
+ optional<size_t> poly;
+
+ optional<Path> parent;
+
+ if (_poly_from_user_radio->get_active())
+ poly = _poly_spinbutton->get_value_as_int();
+
+ if (_replace)
+ App::instance().engine()->clear_patch(_patch->path());
+
+ if (_patch->path() != "/")
+ parent = _patch->path().parent();
+
+ App::instance().loader()->load_patch(true, get_uri(), "/",
+ _initial_data, parent, name, poly);
+
+ hide();
+}
+
+
+void
+LoadPatchWindow::cancel_clicked()
+{
+ hide();
+}
+
+
+} // namespace GUI
+} // namespace Ingen
diff --git a/src/libs/gui/LoadPatchWindow.h b/src/libs/gui/LoadPatchWindow.h
new file mode 100644
index 00000000..47f0bc8f
--- /dev/null
+++ b/src/libs/gui/LoadPatchWindow.h
@@ -0,0 +1,81 @@
+/* This file is part of Ingen.
+ * Copyright (C) 2007 Dave 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 LOADPATCHWINDOW_H
+#define LOADPATCHWINDOW_H
+
+#include <libglademm/xml.h>
+#include <gtkmm.h>
+#include <raul/SharedPtr.h>
+#include "client/PluginModel.h"
+#include "client/PatchModel.h"
+using Ingen::Client::PatchModel;
+using Ingen::Client::MetadataMap;
+
+namespace Ingen {
+namespace GUI {
+
+
+/** 'Load Patch' window.
+ *
+ * Loaded by glade as a derived object. Used for both "Import" and "Load"
+ * (e.g. Replace, clear-then-import) operations (the radio button state
+ * should be changed with the provided methods before presenting).
+ *
+ * This is not for loading subpatches. See @a LoadSubpatchWindow for that.
+ *
+ * \ingroup GUI
+ */
+class LoadPatchWindow : public Gtk::FileChooserDialog
+{
+public:
+ LoadPatchWindow(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& xml);
+
+ void set_patch(SharedPtr<PatchModel> patch);
+
+ void set_replace() { _replace = true; }
+ void set_merge() { _replace = false; }
+
+ void present(SharedPtr<PatchModel> patch, MetadataMap data);
+
+protected:
+ void on_show();
+
+private:
+ void poly_from_file_selected();
+ void poly_from_user_selected();
+ void ok_clicked();
+ void cancel_clicked();
+
+ MetadataMap _initial_data;
+
+ SharedPtr<PatchModel> _patch;
+ bool _replace;
+
+ Gtk::RadioButton* _poly_from_current_radio;
+ Gtk::RadioButton* _poly_from_file_radio;
+ Gtk::RadioButton* _poly_from_user_radio;
+ Gtk::SpinButton* _poly_spinbutton;
+ Gtk::Button* _ok_button;
+ Gtk::Button* _cancel_button;
+};
+
+
+} // namespace GUI
+} // namespace Ingen
+
+#endif // LOADPATCHWINDOW_H
diff --git a/src/libs/gui/LoadPluginWindow.cpp b/src/libs/gui/LoadPluginWindow.cpp
new file mode 100644
index 00000000..a4b24e5f
--- /dev/null
+++ b/src/libs/gui/LoadPluginWindow.cpp
@@ -0,0 +1,458 @@
+/* This file is part of Ingen.
+ * Copyright (C) 2007 Dave 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 <iostream>
+#include <cassert>
+#include <algorithm>
+#include <cctype>
+#include "interface/EngineInterface.h"
+#include "client/NodeModel.h"
+#include "client/PatchModel.h"
+#include "client/Store.h"
+#include "App.h"
+#include "LoadPluginWindow.h"
+#include "PatchWindow.h"
+#include "PatchView.h"
+#include "PatchCanvas.h"
+using std::cout; using std::cerr; using std::endl;
+
+
+namespace Ingen {
+namespace GUI {
+
+LoadPluginWindow::LoadPluginWindow(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& xml)
+: Gtk::Window(cobject),
+ _has_shown(false),
+ _plugin_name_offset(0)
+{
+ xml->get_widget("load_plugin_plugins_treeview", _plugins_treeview);
+ xml->get_widget("load_plugin_polyphonic_checkbutton", _polyphonic_checkbutton);
+ xml->get_widget("load_plugin_name_entry", _node_name_entry);
+ xml->get_widget("load_plugin_clear_button", _clear_button);
+ xml->get_widget("load_plugin_add_button", _add_button);
+ //xml->get_widget("load_plugin_close_button", _close_button);
+ //xml->get_widget("load_plugin_ok_button", _add_button);
+
+ xml->get_widget("load_plugin_filter_combo", _filter_combo);
+ xml->get_widget("load_plugin_search_entry", _search_entry);
+
+ // Set up the plugins list
+ _plugins_liststore = Gtk::ListStore::create(_plugins_columns);
+ _plugins_treeview->set_model(_plugins_liststore);
+ _plugins_treeview->append_column("Name", _plugins_columns._col_name);
+ _plugins_treeview->append_column("Type", _plugins_columns._col_type);
+ _plugins_treeview->append_column("URI", _plugins_columns._col_uri);
+ //m_plugins_treeview->append_column("Library", _plugins_columns._col_library);
+ //m_plugins_treeview->append_column("Label", _plugins_columns._col_label);
+
+ // This could be nicer.. store the TreeViewColumns locally maybe?
+ _plugins_treeview->get_column(0)->set_sort_column(_plugins_columns._col_name);
+ _plugins_treeview->get_column(1)->set_sort_column(_plugins_columns._col_type);
+ _plugins_treeview->get_column(2)->set_sort_column(_plugins_columns._col_uri);
+ //m_plugins_treeview->get_column(3)->set_sort_column(_plugins_columns._col_library);
+ //m_plugins_treeview->get_column(4)->set_sort_column(_plugins_columns._col_label);
+ for (int i=0; i < 3; ++i)
+ _plugins_treeview->get_column(i)->set_resizable(true);
+
+ _plugins_liststore->set_default_sort_func(sigc::mem_fun(this, &LoadPluginWindow::plugin_compare));
+
+ // Set up the search criteria combobox
+ _criteria_liststore = Gtk::ListStore::create(_criteria_columns);
+ _filter_combo->set_model(_criteria_liststore);
+ Gtk::TreeModel::iterator iter = _criteria_liststore->append();
+ Gtk::TreeModel::Row row = *iter;
+ row[_criteria_columns._col_label] = "Name contains";
+ row[_criteria_columns._col_criteria] = CriteriaColumns::NAME;
+ _filter_combo->set_active(iter);
+ iter = _criteria_liststore->append(); row = *iter;
+ row[_criteria_columns._col_label] = "Type contains";
+ row[_criteria_columns._col_criteria] = CriteriaColumns::TYPE;
+ iter = _criteria_liststore->append(); row = *iter;
+ row[_criteria_columns._col_label] = "URI contains";
+ row[_criteria_columns._col_criteria] = CriteriaColumns::URI;
+ /*iter = _criteria_liststore->append(); row = *iter;
+ row[_criteria_columns._col_label] = "Library contains: ";
+ row[_criteria_columns._col_criteria] = CriteriaColumns::LIBRARY;
+ iter = _criteria_liststore->append(); row = *iter;
+ row[_criteria_columns._col_label] = "Label contains: ";
+ row[_criteria_columns._col_criteria] = CriteriaColumns::LABEL;*/
+
+ _clear_button->signal_clicked().connect( sigc::mem_fun(this, &LoadPluginWindow::clear_clicked));
+ _add_button->signal_clicked().connect( sigc::mem_fun(this, &LoadPluginWindow::add_clicked));
+ //m_close_button->signal_clicked().connect( sigc::mem_fun(this, &LoadPluginWindow::close_clicked));
+ //m_add_button->signal_clicked().connect( sigc::mem_fun(this, &LoadPluginWindow::ok_clicked));
+ _plugins_treeview->signal_row_activated().connect(sigc::mem_fun(this, &LoadPluginWindow::plugin_activated));
+ _search_entry->signal_activate().connect( sigc::mem_fun(this, &LoadPluginWindow::add_clicked));
+ _search_entry->signal_changed().connect( sigc::mem_fun(this, &LoadPluginWindow::filter_changed));
+ _node_name_entry->signal_changed().connect( sigc::mem_fun(this, &LoadPluginWindow::name_changed));
+
+ _selection = _plugins_treeview->get_selection();
+ _selection->signal_changed().connect(sigc::mem_fun(this, &LoadPluginWindow::plugin_selection_changed));
+
+ //m_add_button->grab_default();
+}
+
+
+void
+LoadPluginWindow::present(SharedPtr<PatchModel> patch, MetadataMap data)
+{
+ set_patch(patch);
+ _initial_data = data;
+ Gtk::Window::present();
+}
+
+
+/** Called every time the user types into the name input box.
+ * Used to display warning messages, and enable/disable the OK button.
+ */
+void
+LoadPluginWindow::name_changed()
+{
+ string name = _node_name_entry->get_text();
+ if (!Path::is_valid_name(name)) {
+ //m_message_label->set_text("Name contains invalid characters.");
+ _add_button->property_sensitive() = false;
+ } else if (_patch->get_node(name)) {
+ //m_message_label->set_text("An object already exists with that name.");
+ _add_button->property_sensitive() = false;
+ } else if (name.length() == 0) {
+ //m_message_label->set_text("");
+ _add_button->property_sensitive() = false;
+ } else {
+ //m_message_label->set_text("");
+ _add_button->property_sensitive() = true;
+ }
+}
+
+
+/** Sets the patch controller for this window and initializes everything.
+ *
+ * This function MUST be called before using the window in any way!
+ */
+void
+LoadPluginWindow::set_patch(SharedPtr<PatchModel> patch)
+{
+ _patch = patch;
+
+ if (patch->poly() <= 1)
+ _polyphonic_checkbutton->property_sensitive() = false;
+ else
+ _polyphonic_checkbutton->property_sensitive() = true;
+
+}
+
+
+/** Populates the plugin list on the first show.
+ *
+ * This is done here instead of construction time as the list population is
+ * really expensive and bogs down creation of a patch. This is especially
+ * important when many patch notifications are sent at one time from the
+ * engine.
+ */
+void
+LoadPluginWindow::on_show()
+{
+ if (!_has_shown) {
+ set_plugin_list(App::instance().store()->plugins());
+
+ // Center on patch window
+ /*int _w, _h;
+ get_size(_w, _h);
+
+ int parent_x, parent_y, parent_w, parent_h;
+ _patch_controller->window()->get_position(parent_x, parent_y);
+ _patch_controller->window()->get_size(parent_w, parent_h);
+
+ move(parent_x + parent_w/2 - _w/2, parent_y + parent_h/2 - _h/2);
+ */
+ _has_shown = true;
+ }
+ Gtk::Window::on_show();
+}
+
+
+int
+LoadPluginWindow::plugin_compare(const Gtk::TreeModel::iterator& a_i,
+ const Gtk::TreeModel::iterator& b_i)
+{
+ SharedPtr<PluginModel> a = a_i->get_value(_plugins_columns._col_plugin_model);
+ SharedPtr<PluginModel> b = b_i->get_value(_plugins_columns._col_plugin_model);
+
+ // FIXME: haaack
+ if (!a && !b)
+ return 0;
+ else if (!a)
+ return 1;
+ else if (!b)
+ return -1;
+
+ if (a->type() == b->type())
+ return strcmp(a->name().c_str(), b->name().c_str());
+ else
+ return ((int)a->type() < (int)b->type()) ? -1 : 1;
+}
+
+
+void
+LoadPluginWindow::set_plugin_list(const std::map<string, SharedPtr<PluginModel> >& m)
+{
+ _plugins_liststore->clear();
+
+ for (std::map<string, SharedPtr<PluginModel> >::const_iterator i = m.begin(); i != m.end(); ++i) {
+ SharedPtr<PluginModel> plugin = (*i).second;
+
+ Gtk::TreeModel::iterator iter = _plugins_liststore->append();
+ Gtk::TreeModel::Row row = *iter;
+
+ row[_plugins_columns._col_name] = plugin->name();
+ //row[_plugins_columns._col_label] = plugin->plug_label();
+ if (plugin->type_uri() == "ingen:Internal")
+ row[_plugins_columns._col_type] = "Internal";
+ else if (plugin->type_uri() == "ingen:LV2")
+ row[_plugins_columns._col_type] = "LV2";
+ else if (plugin->type_uri() == "ingen:DSSI")
+ row[_plugins_columns._col_type] = "DSSI";
+ else if (plugin->type_uri() == "ingen:LADSPA")
+ row[_plugins_columns._col_type] = "LADSPA";
+ else
+ row[_plugins_columns._col_type] = plugin->type_uri();
+ row[_plugins_columns._col_uri] = plugin->uri();
+ row[_plugins_columns._col_label] = plugin->name();
+ //row[_plugins_columns._col_library] = plugin->lib_name();
+ row[_plugins_columns._col_plugin_model] = plugin;
+ }
+
+ _plugins_liststore->set_sort_column(Gtk::TreeSortable::DEFAULT_SORT_COLUMN_ID, Gtk::SORT_ASCENDING);
+
+ _plugins_treeview->columns_autosize();
+}
+
+
+void
+LoadPluginWindow::add_plugin(SharedPtr<PluginModel> plugin)
+{
+ Gtk::TreeModel::iterator iter = _plugins_liststore->append();
+ Gtk::TreeModel::Row row = *iter;
+
+ row[_plugins_columns._col_name] = plugin->name();
+ //row[_plugins_columns._col_label] = plugin->plug_label();
+ row[_plugins_columns._col_type] = plugin->type_uri();
+ row[_plugins_columns._col_uri] = plugin->uri();
+ row[_plugins_columns._col_label] = plugin->name();
+ //row[_plugins_columns._col_library] = plugin->lib_name();
+ row[_plugins_columns._col_plugin_model] = plugin;
+}
+
+
+
+///// Event Handlers //////
+
+
+void
+LoadPluginWindow::plugin_activated(const Gtk::TreeModel::Path& path, Gtk::TreeViewColumn* col)
+{
+ add_clicked();
+}
+
+
+void
+LoadPluginWindow::plugin_selection_changed()
+{
+ _plugin_name_offset = 0;
+
+ _node_name_entry->set_text(generate_module_name());
+
+ //Gtk::TreeModel::iterator iter = _selection->get_selected();
+ //Gtk::TreeModel::Row row = *iter;
+ //const PluginModel* plugin = row.get_value(_plugins_columns._col_plugin_model);
+}
+
+
+/** Generate an automatic name for this Node.
+ *
+ * Offset is an offset of the number that will be appended to the plugin's
+ * label, needed if the user adds multiple plugins faster than the engine
+ * sends the notification back.
+ */
+string
+LoadPluginWindow::generate_module_name(int offset)
+{
+ string name = "";
+
+ Gtk::TreeModel::iterator iter = _selection->get_selected();
+
+ if (iter) {
+ Gtk::TreeModel::Row row = *iter;
+ SharedPtr<PluginModel> plugin = row.get_value(_plugins_columns._col_plugin_model);
+ return plugin->default_node_name(_patch);
+ }
+ /*char num_buf[3];
+ for (uint i=0; i < 99; ++i) {
+ name = plugin->default_node_name();
+ if (name == "")
+ name = plugin->name().substr(0, plugin->name().find(' '));
+ if (i+offset != 0) {
+ snprintf(num_buf, 3, "%d", i+offset+1);
+ name += "_";
+ name += num_buf;
+ }
+ if (!_patch->get_node(name))
+ break;
+ else
+ name = "";
+ }
+ }*/
+
+ return name;
+}
+
+
+void
+LoadPluginWindow::add_clicked()
+{
+ Gtk::TreeModel::iterator iter = _selection->get_selected();
+ bool polyphonic = _polyphonic_checkbutton->get_active();
+
+ if (iter) { // If anything is selected
+ Gtk::TreeModel::Row row = *iter;
+ SharedPtr<PluginModel> plugin = row.get_value(_plugins_columns._col_plugin_model);
+ string name = _node_name_entry->get_text();
+ if (name == "") {
+ name = generate_module_name();
+ }
+ if (name == "") {
+ Gtk::MessageDialog dialog(*this,
+ "Unable to chose a default name for this node. Please enter a name.",
+ false, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_OK, true);
+
+ dialog.run();
+ } else {
+ Path path = _patch->path().base() + Path::nameify(name);
+ App::instance().engine()->create_node(path, plugin->uri(), polyphonic);
+ for (MetadataMap::const_iterator i = _initial_data.begin(); i != _initial_data.end(); ++i)
+ App::instance().engine()->set_metadata(path, i->first, i->second);
+ ++_plugin_name_offset;
+ _node_name_entry->set_text(generate_module_name(_plugin_name_offset));
+
+ // Set the next module location 20 over, for a cascade effect
+ cerr << "FIXME: cascade\n";
+ //m_new_module_x += 20;
+ //m_new_module_y += 20;
+ }
+ }
+}
+
+
+/*
+void
+LoadPluginWindow::close_clicked()
+{
+ hide();
+}
+
+
+void
+LoadPluginWindow::ok_clicked()
+{
+ add_clicked();
+ close_clicked();
+}
+*/
+
+void
+LoadPluginWindow::filter_changed()
+{
+ _plugins_liststore->clear();
+
+ string search = _search_entry->get_text();
+ transform(search.begin(), search.end(), search.begin(), toupper);
+
+ // Get selected criteria
+ const Gtk::TreeModel::Row row = *(_filter_combo->get_active());
+ CriteriaColumns::Criteria criteria = row[_criteria_columns._col_criteria];
+
+ string field;
+
+ Gtk::TreeModel::Row model_row;
+ Gtk::TreeModel::iterator model_iter;
+ size_t num_visible = 0;
+
+
+ for (std::map<string, SharedPtr<PluginModel> >::const_iterator i = App::instance().store()->plugins().begin();
+ i != App::instance().store()->plugins().end(); ++i) {
+
+ const SharedPtr<PluginModel> plugin = (*i).second;
+
+ switch (criteria) {
+ case CriteriaColumns::NAME:
+ field = plugin->name(); break;
+ case CriteriaColumns::TYPE:
+ field = plugin->type_uri(); break;
+ case CriteriaColumns::URI:
+ field = plugin->uri(); break;
+ /*case CriteriaColumns::LIBRARY:
+ field = plugin->lib_name(); break;
+ case CriteriaColumns::LABEL:
+ field = plugin->plug_label(); break;*/
+ default:
+ throw;
+ }
+
+ transform(field.begin(), field.end(), field.begin(), toupper);
+
+ if (field.find(search) != string::npos) {
+ model_iter = _plugins_liststore->append();
+ model_row = *model_iter;
+
+ model_row[_plugins_columns._col_name] = plugin->name();
+ //model_row[_plugins_columns._col_label] = plugin->plug_label();
+ model_row[_plugins_columns._col_type] = plugin->type_uri();
+ model_row[_plugins_columns._col_uri] = plugin->uri();
+ model_row[_plugins_columns._col_plugin_model] = plugin;
+
+ ++num_visible;
+ }
+ }
+
+ if (num_visible == 1) {
+ _selection->unselect_all();
+ _selection->select(model_iter);
+ }
+}
+
+
+void
+LoadPluginWindow::clear_clicked()
+{
+ _search_entry->set_text("");
+ set_plugin_list(App::instance().store()->plugins());
+}
+
+bool
+LoadPluginWindow::on_key_press_event(GdkEventKey* event)
+{
+ if (event->keyval == GDK_w && event->state & GDK_CONTROL_MASK) {
+ hide();
+ return true;
+ } else {
+ return Gtk::Window::on_key_press_event(event);
+ }
+}
+
+
+} // namespace GUI
+} // namespace Ingen
diff --git a/src/libs/gui/LoadPluginWindow.h b/src/libs/gui/LoadPluginWindow.h
new file mode 100644
index 00000000..1b39d314
--- /dev/null
+++ b/src/libs/gui/LoadPluginWindow.h
@@ -0,0 +1,151 @@
+/* This file is part of Ingen.
+ * Copyright (C) 2007 Dave 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 LOADPLUGINWINDOW_H
+#define LOADPLUGINWINDOW_H
+
+#include <map>
+#include <libglademm/xml.h>
+#include <libglademm.h>
+#include <gtkmm.h>
+#include <raul/SharedPtr.h>
+#include "client/PatchModel.h"
+#include "client/PluginModel.h"
+using Ingen::Client::PluginModel;
+using Ingen::Client::PatchModel;
+using Ingen::Client::MetadataMap;
+
+namespace Ingen {
+namespace GUI {
+
+
+// Gtkmm _really_ needs to add some helper to abstract away this stupid nonsense
+
+/** Columns for the plugin list in the load plugin window.
+ *
+ * \ingroup GUI
+ */
+class ModelColumns : public Gtk::TreeModel::ColumnRecord
+{
+public:
+ ModelColumns() {
+ add(_col_name);
+ add(_col_type);
+ add(_col_uri);
+ add(_col_label);
+ //add(_col_library);
+ //add(_col_label);
+ add(_col_plugin_model);
+ }
+
+ Gtk::TreeModelColumn<Glib::ustring> _col_name;
+ Gtk::TreeModelColumn<Glib::ustring> _col_type;
+ Gtk::TreeModelColumn<Glib::ustring> _col_uri;
+
+ // Not displayed:
+ Gtk::TreeModelColumn<Glib::ustring> _col_label;
+ Gtk::TreeModelColumn<SharedPtr<PluginModel> > _col_plugin_model;
+};
+
+
+/** Column for the criteria combo box in the load plugin window.
+ *
+ * \ingroup GUI
+ */
+class CriteriaColumns : public Gtk::TreeModel::ColumnRecord
+{
+public:
+ enum Criteria { NAME, TYPE, URI, };
+
+ CriteriaColumns() { add(_col_label); add(_col_criteria); }
+
+ Gtk::TreeModelColumn<Glib::ustring> _col_label;
+ Gtk::TreeModelColumn<Criteria> _col_criteria;
+};
+
+
+/** 'Load Plugin' window.
+ *
+ * Loaded by glade as a derived object.
+ *
+ * \ingroup GUI
+ */
+class LoadPluginWindow : public Gtk::Window
+{
+public:
+ LoadPluginWindow(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& xml);
+
+ void set_patch(SharedPtr<PatchModel> patch);
+ void set_plugin_list(const std::map<string, SharedPtr<PluginModel> >& m);
+
+ void add_plugin(SharedPtr<PluginModel> plugin);
+ bool has_shown() const { return _has_shown; }
+
+ void present(SharedPtr<PatchModel> patch, MetadataMap data);
+
+protected:
+ void on_show();
+ bool on_key_press_event(GdkEventKey* event);
+
+private:
+ void add_clicked();
+ //void close_clicked();
+ //void ok_clicked();
+ void filter_changed();
+ void clear_clicked();
+ void name_changed();
+
+ int plugin_compare(const Gtk::TreeModel::iterator& a,
+ const Gtk::TreeModel::iterator& b);
+
+ void plugin_activated(const Gtk::TreeModel::Path& path, Gtk::TreeViewColumn* col);
+ void plugin_selection_changed();
+ string generate_module_name(int offset = 0);
+
+ MetadataMap _initial_data;
+
+ SharedPtr<PatchModel> _patch;
+
+ bool _has_shown; // plugin list only populated on show to speed patch window creation
+
+ Glib::RefPtr<Gtk::ListStore> _plugins_liststore;
+ ModelColumns _plugins_columns;
+
+ Glib::RefPtr<Gtk::ListStore> _criteria_liststore;
+ CriteriaColumns _criteria_columns;
+
+ Glib::RefPtr<Gtk::TreeSelection> _selection;
+
+ int _plugin_name_offset; // see comments for generate_plugin_name
+
+ Gtk::TreeView* _plugins_treeview;
+ Gtk::CheckButton* _polyphonic_checkbutton;
+ Gtk::Entry* _node_name_entry;
+ Gtk::Button* _clear_button;
+ Gtk::Button* _add_button;
+ //Gtk::Button* _close_button;
+ //Gtk::Button* _ok_button;
+ Gtk::ComboBox* _filter_combo;
+ Gtk::Entry* _search_entry;
+};
+
+
+} // namespace GUI
+} // namespace Ingen
+
+#endif // LOADPLUGINWINDOW_H
diff --git a/src/libs/gui/LoadRemotePatchWindow.cpp b/src/libs/gui/LoadRemotePatchWindow.cpp
new file mode 100644
index 00000000..534a6949
--- /dev/null
+++ b/src/libs/gui/LoadRemotePatchWindow.cpp
@@ -0,0 +1,163 @@
+/* This file is part of Ingen.
+ * Copyright (C) 2007 Dave 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 "LoadRemotePatchWindow.h"
+#include <sys/types.h>
+#include <dirent.h>
+#include <boost/optional/optional.hpp>
+#include <raul/RDFQuery.h>
+#include "interface/EngineInterface.h"
+#include "client/PatchModel.h"
+#include "App.h"
+#include "Configuration.h"
+#include "ThreadedLoader.h"
+
+using boost::optional;
+using namespace Raul;
+
+namespace Ingen {
+namespace GUI {
+
+
+LoadRemotePatchWindow::LoadRemotePatchWindow(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& xml)
+: Gtk::Dialog(cobject),
+ _replace(true)
+{
+ xml->get_widget("load_remote_patch_treeview", _treeview);
+ xml->get_widget("load_remote_patch_uri_entry", _uri_entry);
+ xml->get_widget("load_remote_patch_cancel_button", _cancel_button);
+ xml->get_widget("load_remote_patch_open_button", _open_button);
+
+ _liststore = Gtk::ListStore::create(_columns);
+ _treeview->set_model(_liststore);
+ _treeview->append_column("Name", _columns._col_name);
+ _treeview->append_column("URI", _columns._col_uri);
+
+ _selection = _treeview->get_selection();
+ _selection->signal_changed().connect(sigc::mem_fun(this, &LoadRemotePatchWindow::patch_selected));
+ _treeview->signal_row_activated().connect(sigc::mem_fun(this, &LoadRemotePatchWindow::patch_activated));
+
+ _open_button->signal_clicked().connect(sigc::mem_fun(this, &LoadRemotePatchWindow::open_clicked));
+ _cancel_button->signal_clicked().connect(sigc::mem_fun(this, &LoadRemotePatchWindow::cancel_clicked));
+ _uri_entry->signal_changed().connect(sigc::mem_fun(this, &LoadRemotePatchWindow::uri_changed));
+}
+
+
+void
+LoadRemotePatchWindow::present(SharedPtr<PatchModel> patch, MetadataMap data)
+{
+ _liststore->clear();
+
+ set_patch(patch);
+ _initial_data = data;
+
+ RDF::Model model(*App::instance().rdf_world(),
+ "http://rdf.drobilla.net/ingen_patches/index.ttl",
+ "http://rdf.drobilla.net/ingen_patches/");
+
+ RDF::Query query(*App::instance().rdf_world(), Glib::ustring(
+ "SELECT DISTINCT ?name ?uri WHERE {"
+ " ?uri a ingen:Patch ;"
+ " doap:name ?name ."
+ "}"));
+
+ RDF::Query::Results results = query.run(*App::instance().rdf_world(), model);
+
+ for (RDF::Query::Results::iterator i = results.begin(); i != results.end(); ++i) {
+ Gtk::TreeModel::iterator iter = _liststore->append();
+ (*iter)[_columns._col_name] = (*i)["name"].to_string();
+ (*iter)[_columns._col_uri] = (*i)["uri"].to_string();
+ }
+
+ _treeview->columns_autosize();
+
+ Gtk::Window::present();
+}
+
+
+/** Sets the patch controller for this window and initializes everything.
+ *
+ * This function MUST be called before using the window in any way!
+ */
+void
+LoadRemotePatchWindow::set_patch(SharedPtr<PatchModel> patch)
+{
+ _patch = patch;
+}
+
+
+void
+LoadRemotePatchWindow::uri_changed()
+{
+ _open_button->property_sensitive() = (_uri_entry->get_text().length() > 0);
+}
+
+
+void
+LoadRemotePatchWindow::patch_activated(const Gtk::TreeModel::Path& path, Gtk::TreeViewColumn* col)
+{
+ open_clicked();
+}
+
+
+void
+LoadRemotePatchWindow::patch_selected()
+{
+ Gtk::TreeModel::iterator selected_i = _selection->get_selected();
+
+ if (selected_i) { // If anything is selected
+ const Glib::ustring uri = selected_i->get_value(_columns._col_uri);
+ _uri_entry->set_text(uri);
+ }
+}
+
+
+void
+LoadRemotePatchWindow::open_clicked()
+{
+ Glib::ustring uri = _uri_entry->get_text();
+
+ cerr << "OPEN URI: " << uri << endl;
+
+ // If unset load_patch will load values
+ optional<const string&> name;
+ optional<size_t> poly;
+
+ optional<Path> parent;
+
+ if (_replace)
+ App::instance().engine()->clear_patch(_patch->path());
+
+ if (_patch->path() != "/")
+ parent = _patch->path().parent();
+
+ App::instance().loader()->load_patch(true, uri, "/",
+ _initial_data, parent, name, poly);
+
+ hide();
+}
+
+
+void
+LoadRemotePatchWindow::cancel_clicked()
+{
+ hide();
+}
+
+
+} // namespace GUI
+} // namespace Ingen
diff --git a/src/libs/gui/LoadRemotePatchWindow.h b/src/libs/gui/LoadRemotePatchWindow.h
new file mode 100644
index 00000000..bbb3d93e
--- /dev/null
+++ b/src/libs/gui/LoadRemotePatchWindow.h
@@ -0,0 +1,93 @@
+/* This file is part of Ingen.
+ * Copyright (C) 2007 Dave 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 LOADREMOTEPATCHWINDOW_H
+#define LOADREMOTEPATCHWINDOW_H
+
+#include <libglademm/xml.h>
+#include <gtkmm.h>
+#include <raul/SharedPtr.h>
+#include "client/PatchModel.h"
+#include "client/PluginModel.h"
+using Ingen::Client::PatchModel;
+using Ingen::Client::MetadataMap;
+
+namespace Ingen {
+namespace GUI {
+
+
+/** Columns for the remote patch list.
+ *
+ * \ingroup GUI
+ */
+class PatchColumns : public Gtk::TreeModel::ColumnRecord
+{
+public:
+ PatchColumns() {
+ add(_col_name);
+ add(_col_uri);
+ }
+
+ Gtk::TreeModelColumn<Glib::ustring> _col_name;
+ Gtk::TreeModelColumn<Glib::ustring> _col_uri;
+};
+
+
+
+/* Load remote patch ("import location") dialog.
+ *
+ * \ingroup GUI
+ */
+class LoadRemotePatchWindow : public Gtk::Dialog
+{
+public:
+ LoadRemotePatchWindow(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& xml);
+
+ void set_patch(SharedPtr<PatchModel> patch);
+
+ void set_replace() { _replace = true; }
+ void set_merge() { _replace = false; }
+
+ void present(SharedPtr<PatchModel> patch, MetadataMap data);
+
+private:
+ void patch_activated(const Gtk::TreeModel::Path& path, Gtk::TreeViewColumn* col);
+ void patch_selected();
+ void uri_changed();
+ void open_clicked();
+ void cancel_clicked();
+
+ MetadataMap _initial_data;
+
+ SharedPtr<PatchModel> _patch;
+ bool _replace;
+
+ Glib::RefPtr<Gtk::TreeSelection> _selection;
+ Glib::RefPtr<Gtk::ListStore> _liststore;
+ PatchColumns _columns;
+
+ Gtk::TreeView* _treeview;
+ Gtk::Entry* _uri_entry;
+ Gtk::Button* _open_button;
+ Gtk::Button* _cancel_button;
+};
+
+
+} // namespace GUI
+} // namespace Ingen
+
+#endif // LOADREMOTEPATCHWINDOW_H
diff --git a/src/libs/gui/LoadSubpatchWindow.cpp b/src/libs/gui/LoadSubpatchWindow.cpp
new file mode 100644
index 00000000..57fa9adc
--- /dev/null
+++ b/src/libs/gui/LoadSubpatchWindow.cpp
@@ -0,0 +1,175 @@
+/* This file is part of Ingen.
+ * Copyright (C) 2007 Dave 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 <sys/types.h>
+#include <dirent.h>
+#include <cassert>
+#include <boost/optional.hpp>
+#include "interface/EngineInterface.h"
+#include "client/NodeModel.h"
+#include "client/PatchModel.h"
+#include "App.h"
+#include "LoadSubpatchWindow.h"
+#include "PatchView.h"
+#include "Configuration.h"
+#include "ThreadedLoader.h"
+using boost::optional;
+
+namespace Ingen {
+namespace GUI {
+
+
+LoadSubpatchWindow::LoadSubpatchWindow(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& xml)
+: Gtk::FileChooserDialog(cobject)
+{
+ xml->get_widget("load_subpatch_name_from_file_radio", _name_from_file_radio);
+ xml->get_widget("load_subpatch_name_from_user_radio", _name_from_user_radio);
+ xml->get_widget("load_subpatch_name_entry", _name_entry);
+ xml->get_widget("load_subpatch_poly_from_file_radio", _poly_from_file_radio);
+ xml->get_widget("load_subpatch_poly_from_parent_radio", _poly_from_parent_radio);
+ xml->get_widget("load_subpatch_poly_from_user_radio", _poly_from_user_radio);
+ xml->get_widget("load_subpatch_poly_spinbutton", _poly_spinbutton);
+ xml->get_widget("load_subpatch_ok_button", _ok_button);
+ xml->get_widget("load_subpatch_cancel_button", _cancel_button);
+
+ _name_from_file_radio->signal_toggled().connect(sigc::mem_fun(this, &LoadSubpatchWindow::disable_name_entry));
+ _name_from_user_radio->signal_toggled().connect(sigc::mem_fun(this, &LoadSubpatchWindow::enable_name_entry));
+ _poly_from_file_radio->signal_toggled().connect(sigc::mem_fun(this, &LoadSubpatchWindow::disable_poly_spinner));
+ _poly_from_parent_radio->signal_toggled().connect(sigc::mem_fun(this, &LoadSubpatchWindow::disable_poly_spinner));
+ _poly_from_user_radio->signal_toggled().connect(sigc::mem_fun(this, &LoadSubpatchWindow::enable_poly_spinner));
+ _ok_button->signal_clicked().connect(sigc::mem_fun(this, &LoadSubpatchWindow::ok_clicked));
+ _cancel_button->signal_clicked().connect(sigc::mem_fun(this, &LoadSubpatchWindow::cancel_clicked));
+
+ Gtk::FileFilter filt;
+ filt.add_pattern("*.om");
+ filt.set_name("Om patch files (XML, DEPRECATED) (*.om)");
+ filt.add_pattern("*.ingen.ttl");
+ filt.set_name("Ingen patch files (RDF, *.ingen.ttl)");
+ set_filter(filt);
+
+ // Add global examples directory to "shortcut folders" (bookmarks)
+ string examples_dir = PKGDATADIR;
+ examples_dir.append("/patches");
+ DIR* d = opendir(examples_dir.c_str());
+ if (d != NULL)
+ add_shortcut_folder(examples_dir);
+}
+
+
+void
+LoadSubpatchWindow::present(SharedPtr<PatchModel> patch, MetadataMap data)
+{
+ set_patch(patch);
+ _initial_data = data;
+ Gtk::Window::present();
+}
+
+
+/** Sets the patch controller for this window and initializes everything.
+ *
+ * This function MUST be called before using the window in any way!
+ */
+void
+LoadSubpatchWindow::set_patch(SharedPtr<PatchModel> patch)
+{
+ _patch = patch;
+
+ char temp_buf[4];
+ snprintf(temp_buf, 4, "%zd", patch->poly());
+ Glib::ustring txt = "Same as parent (";
+ txt.append(temp_buf).append(")");
+ _poly_from_parent_radio->set_label(txt);
+}
+
+
+void
+LoadSubpatchWindow::on_show()
+{
+ if (App::instance().configuration()->patch_folder().length() > 0)
+ set_current_folder(App::instance().configuration()->patch_folder());
+ Gtk::FileChooserDialog::on_show();
+}
+
+
+///// Event Handlers //////
+
+
+
+void
+LoadSubpatchWindow::disable_name_entry()
+{
+ _name_entry->property_sensitive() = false;
+}
+
+
+void
+LoadSubpatchWindow::enable_name_entry()
+{
+ _name_entry->property_sensitive() = true;
+}
+
+
+void
+LoadSubpatchWindow::disable_poly_spinner()
+{
+ _poly_spinbutton->property_sensitive() = false;
+}
+
+
+void
+LoadSubpatchWindow::enable_poly_spinner()
+{
+ _poly_spinbutton->property_sensitive() = true;
+}
+
+
+void
+LoadSubpatchWindow::ok_clicked()
+{
+ assert(_patch);
+
+ // If unset load_patch will load values
+ optional<const string&> name;
+ optional<size_t> poly;
+ string name_str = "";
+
+ if (_name_from_user_radio->get_active()) {
+ name_str = _name_entry->get_text();
+ name = name_str;
+ }
+
+ if (_poly_from_user_radio->get_active())
+ poly = _poly_spinbutton->get_value_as_int();
+ else if (_poly_from_parent_radio->get_active())
+ poly = _patch->poly();
+
+ App::instance().loader()->load_patch(false, get_uri(), "/",
+ _initial_data, _patch->path(), name, poly);
+
+ hide();
+}
+
+
+void
+LoadSubpatchWindow::cancel_clicked()
+{
+ hide();
+}
+
+
+} // namespace GUI
+} // namespace Ingen
diff --git a/src/libs/gui/LoadSubpatchWindow.h b/src/libs/gui/LoadSubpatchWindow.h
new file mode 100644
index 00000000..08a6f7db
--- /dev/null
+++ b/src/libs/gui/LoadSubpatchWindow.h
@@ -0,0 +1,79 @@
+/* This file is part of Ingen.
+ * Copyright (C) 2007 Dave 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 LOADSUBPATCHWINDOW_H
+#define LOADSUBPATCHWINDOW_H
+
+#include <libglademm/xml.h>
+#include <gtkmm.h>
+#include <raul/SharedPtr.h>
+#include "client/PatchModel.h"
+#include "client/PluginModel.h"
+using Ingen::Client::PatchModel;
+using Ingen::Client::MetadataMap;
+
+namespace Ingen {
+namespace GUI {
+
+
+/** 'Add Subpatch' window.
+ *
+ * Loaded by glade as a derived object.
+ *
+ * \ingroup GUI
+ */
+class LoadSubpatchWindow : public Gtk::FileChooserDialog
+{
+public:
+ LoadSubpatchWindow(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& xml);
+
+ void set_patch(SharedPtr<PatchModel> patch);
+
+ void present(SharedPtr<PatchModel> patch, MetadataMap data);
+
+protected:
+ void on_show();
+
+private:
+ void disable_name_entry();
+ void enable_name_entry();
+ void disable_poly_spinner();
+ void enable_poly_spinner();
+
+ void ok_clicked();
+ void cancel_clicked();
+
+ MetadataMap _initial_data;
+
+ SharedPtr<PatchModel> _patch;
+
+ Gtk::RadioButton* _name_from_file_radio;
+ Gtk::RadioButton* _name_from_user_radio;
+ Gtk::Entry* _name_entry;
+ Gtk::RadioButton* _poly_from_file_radio;
+ Gtk::RadioButton* _poly_from_parent_radio;
+ Gtk::RadioButton* _poly_from_user_radio;
+ Gtk::SpinButton* _poly_spinbutton;
+ Gtk::Button* _ok_button;
+ Gtk::Button* _cancel_button;
+};
+
+
+} // namespace GUI
+} // namespace Ingen
+
+#endif // LOADSUBPATCHWINDOW_H
diff --git a/src/libs/gui/Makefile.am b/src/libs/gui/Makefile.am
new file mode 100644
index 00000000..c94ebbb5
--- /dev/null
+++ b/src/libs/gui/Makefile.am
@@ -0,0 +1,104 @@
+MAINTAINERCLEANFILES = Makefile.in
+EXTRA_DIST = ingen_gui.gladep
+
+globalpixmapsdir = $(datadir)/pixmaps
+dist_globalpixmaps_DATA = ingen.svg ingen-icon.svg
+
+sharefilesdir = $(pkgdatadir)
+dist_sharefiles_DATA = ingen_gui.glade ingen.svg ingen-icon.svg
+
+moduledir = $(libdir)/ingen
+
+module_LTLIBRARIES = libingen_gui.la
+
+libingen_gui_la_CXXFLAGS = \
+ -DPKGDATADIR=\"$(pkgdatadir)\" \
+ -DINGEN_MODULE_DIR=\"$(libdir)/ingen\" \
+ -DGTK_DISABLE_DEPRECATED -DGDK_DISABLE_DEPRECATED \
+ @RAUL_CFLAGS@ @LOSC_CFLAGS@ @SLV2_CFLAGS@ @CURL_CFLAGS@ \
+ @GTKMM_CFLAGS@ @LIBGLADEMM_CFLAGS@ @FLOWCANVAS_CFLAGS@ @GNOMECANVASMM_CFLAGS@ \
+ -I$(top_srcdir)/src/common \
+ -I$(top_srcdir)/src/libs
+
+libingen_gui_la_LDFLAGS = -no-undefined -module -avoid-version
+
+libingen_gui_la_LIBADD = \
+ @RAUL_LIBS@ @LOSC_LIBS@ @SLV2_LIBS@ @CURL_LIBS@ \
+ @GTKMM_LIBS@ @LIBGLADEMM_LIBS@ @FLOWCANVAS_LIBS@ @GNOMECANVASMM_LIBS@ \
+ ../module/libingen_module.la \
+ ../client/libingen_client.la
+
+
+libingen_gui_la_SOURCES = \
+ gui.h \
+ gui.cpp \
+ App.cpp \
+ App.h \
+ BreadCrumb.h \
+ BreadCrumbBox.cpp \
+ BreadCrumbBox.h \
+ ConfigWindow.cpp \
+ ConfigWindow.h \
+ Configuration.cpp \
+ Configuration.h \
+ ConnectWindow.cpp \
+ ConnectWindow.h \
+ Connection.h \
+ ControlGroups.cpp \
+ ControlGroups.h \
+ ControlPanel.cpp \
+ ControlPanel.h \
+ DSSIController.cpp \
+ DSSIController.h \
+ DSSIModule.cpp \
+ DSSIModule.h \
+ GladeFactory.cpp \
+ GladeFactory.h \
+ LoadPatchWindow.cpp \
+ LoadPatchWindow.h \
+ LoadPluginWindow.cpp \
+ LoadPluginWindow.h \
+ LoadRemotePatchWindow.cpp \
+ LoadRemotePatchWindow.h \
+ LoadSubpatchWindow.cpp \
+ LoadSubpatchWindow.h \
+ MessagesWindow.cpp \
+ MessagesWindow.h \
+ NewSubpatchWindow.cpp \
+ NewSubpatchWindow.h \
+ NodeControlWindow.cpp \
+ NodeControlWindow.h \
+ NodeMenu.cpp \
+ NodeMenu.h \
+ NodeModule.cpp \
+ NodeModule.h \
+ NodePropertiesWindow.cpp \
+ NodePropertiesWindow.h \
+ PatchCanvas.cpp \
+ PatchCanvas.h \
+ PatchPortModule.cpp \
+ PatchPortModule.h \
+ PatchPropertiesWindow.cpp \
+ PatchPropertiesWindow.h \
+ PatchTreeWindow.cpp \
+ PatchTreeWindow.h \
+ PatchView.cpp \
+ PatchView.h \
+ PatchWindow.cpp \
+ PatchWindow.h \
+ Port.cpp \
+ Port.h \
+ PortPropertiesWindow.cpp \
+ PortPropertiesWindow.h \
+ RenameWindow.cpp \
+ RenameWindow.h \
+ SubpatchModule.cpp \
+ SubpatchModule.h \
+ ThreadedLoader.cpp \
+ ThreadedLoader.h \
+ UploadPatchWindow.cpp \
+ UploadPatchWindow.h \
+ WindowFactory.cpp \
+ WindowFactory.h
+
+
diff --git a/src/libs/gui/MessagesWindow.cpp b/src/libs/gui/MessagesWindow.cpp
new file mode 100644
index 00000000..b8a83c20
--- /dev/null
+++ b/src/libs/gui/MessagesWindow.cpp
@@ -0,0 +1,67 @@
+/* This file is part of Ingen.
+ * Copyright (C) 2007 Dave 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 "MessagesWindow.h"
+#include <string>
+
+namespace Ingen {
+namespace GUI {
+using std::string;
+
+
+MessagesWindow::MessagesWindow(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& glade_xml)
+: Gtk::Window(cobject)
+{
+ glade_xml->get_widget("messages_textview", _textview);
+ glade_xml->get_widget("messages_clear_button", _clear_button);
+ glade_xml->get_widget("messages_close_button", _close_button);
+
+ _clear_button->signal_clicked().connect(sigc::mem_fun(this, &MessagesWindow::clear_clicked));
+ _close_button->signal_clicked().connect(sigc::mem_fun(this, &MessagesWindow::close_clicked));
+}
+
+
+void
+MessagesWindow::post(const string& msg)
+{
+ Glib::RefPtr<Gtk::TextBuffer> text_buf = _textview->get_buffer();
+ text_buf->insert(text_buf->end(), msg);
+ text_buf->insert(text_buf->end(), "\n");
+
+ if (!_clear_button->is_sensitive())
+ _clear_button->set_sensitive(true);
+}
+
+
+void
+MessagesWindow::close_clicked()
+{
+ hide();
+}
+
+
+void
+MessagesWindow::clear_clicked()
+{
+ Glib::RefPtr<Gtk::TextBuffer> text_buf = _textview->get_buffer();
+ text_buf->erase(text_buf->begin(), text_buf->end());
+ _clear_button->set_sensitive(false);
+}
+
+
+} // namespace GUI
+} // namespace Ingen
diff --git a/src/libs/gui/MessagesWindow.h b/src/libs/gui/MessagesWindow.h
new file mode 100644
index 00000000..dea0fdd4
--- /dev/null
+++ b/src/libs/gui/MessagesWindow.h
@@ -0,0 +1,58 @@
+/* This file is part of Ingen.
+ * Copyright (C) 2007 Dave 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 MESSAGESWINDOW_H
+#define MESSAGESWINDOW_H
+
+#include <string>
+#include <gtkmm.h>
+#include <libglademm/xml.h>
+using std::string;
+
+
+namespace Ingen {
+namespace GUI {
+
+
+/** Messages Window.
+ *
+ * Loaded by libglade as a derived object.
+ * This is shown when errors occur (ie during patch loading).
+ *
+ * \ingroup GUI
+ */
+class MessagesWindow : public Gtk::Window
+{
+public:
+ MessagesWindow(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& refGlade);
+
+ void post(const string& str);
+
+private:
+ void clear_clicked();
+ void close_clicked();
+
+ Gtk::TextView* _textview;
+ Gtk::Button* _clear_button;
+ Gtk::Button* _close_button;
+};
+
+
+} // namespace GUI
+} // namespace Ingen
+
+#endif // MESSAGESWINDOW_H
diff --git a/src/libs/gui/NewSubpatchWindow.cpp b/src/libs/gui/NewSubpatchWindow.cpp
new file mode 100644
index 00000000..f0bc7caa
--- /dev/null
+++ b/src/libs/gui/NewSubpatchWindow.cpp
@@ -0,0 +1,111 @@
+/* This file is part of Ingen.
+ * Copyright (C) 2007 Dave 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 "App.h"
+#include "interface/EngineInterface.h"
+#include "client/NodeModel.h"
+#include "client/PatchModel.h"
+#include "NewSubpatchWindow.h"
+#include "PatchView.h"
+
+namespace Ingen {
+namespace GUI {
+
+
+NewSubpatchWindow::NewSubpatchWindow(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& xml)
+: Gtk::Window(cobject)
+{
+ xml->get_widget("new_subpatch_name_entry", _name_entry);
+ xml->get_widget("new_subpatch_message_label", _message_label);
+ xml->get_widget("new_subpatch_polyphony_spinbutton", _poly_spinbutton);
+ xml->get_widget("new_subpatch_ok_button", _ok_button);
+ xml->get_widget("new_subpatch_cancel_button", _cancel_button);
+
+ _name_entry->signal_changed().connect(sigc::mem_fun(this, &NewSubpatchWindow::name_changed));
+ _ok_button->signal_clicked().connect(sigc::mem_fun(this, &NewSubpatchWindow::ok_clicked));
+ _cancel_button->signal_clicked().connect(sigc::mem_fun(this, &NewSubpatchWindow::cancel_clicked));
+
+ _ok_button->property_sensitive() = false;
+}
+
+void
+NewSubpatchWindow::present(SharedPtr<PatchModel> patch, MetadataMap data)
+{
+ set_patch(patch);
+ _initial_data = data;
+ Gtk::Window::present();
+}
+
+/** Sets the patch controller for this window and initializes everything.
+ *
+ * This function MUST be called before using the window in any way!
+ */
+void
+NewSubpatchWindow::set_patch(SharedPtr<PatchModel> patch)
+{
+ _patch = patch;
+}
+
+
+/** Called every time the user types into the name input box.
+ * Used to display warning messages, and enable/disable the OK button.
+ */
+void
+NewSubpatchWindow::name_changed()
+{
+ string name = _name_entry->get_text();
+ if (!Path::is_valid_name(name)) {
+ _message_label->set_text("Name contains invalid characters.");
+ _ok_button->property_sensitive() = false;
+ } else if (_patch->get_node(name)) {
+ _message_label->set_text("An object already exists with that name.");
+ _ok_button->property_sensitive() = false;
+ } else if (name.length() == 0) {
+ _message_label->set_text("");
+ _ok_button->property_sensitive() = false;
+ } else {
+ _message_label->set_text("");
+ _ok_button->property_sensitive() = true;
+ }
+}
+
+
+void
+NewSubpatchWindow::ok_clicked()
+{
+ const Path path = _patch->path().base() + Path::nameify(_name_entry->get_text());
+ const size_t poly = _poly_spinbutton->get_value_as_int();
+
+ App::instance().engine()->create_patch(path, poly);
+ for (MetadataMap::const_iterator i = _initial_data.begin(); i != _initial_data.end(); ++i)
+ App::instance().engine()->set_metadata(path, i->first, i->second);
+
+ App::instance().engine()->enable_patch(path);
+
+ hide();
+}
+
+
+void
+NewSubpatchWindow::cancel_clicked()
+{
+ hide();
+}
+
+
+} // namespace GUI
+} // namespace Ingen
diff --git a/src/libs/gui/NewSubpatchWindow.h b/src/libs/gui/NewSubpatchWindow.h
new file mode 100644
index 00000000..e70b7f91
--- /dev/null
+++ b/src/libs/gui/NewSubpatchWindow.h
@@ -0,0 +1,67 @@
+/* This file is part of Ingen.
+ * Copyright (C) 2007 Dave 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 NEWSUBPATCHWINDOW_H
+#define NEWSUBPATCHWINDOW_H
+
+#include <libglademm/xml.h>
+#include <gtkmm.h>
+#include <raul/SharedPtr.h>
+#include "client/PatchModel.h"
+#include "client/PluginModel.h"
+using Ingen::Client::PatchModel;
+using Ingen::Client::MetadataMap;
+
+namespace Ingen {
+namespace GUI {
+
+
+/** 'New Subpatch' window.
+ *
+ * Loaded by glade as a derived object.
+ *
+ * \ingroup GUI
+ */
+class NewSubpatchWindow : public Gtk::Window
+{
+public:
+ NewSubpatchWindow(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& xml);
+
+ void set_patch(SharedPtr<PatchModel> patch);
+
+ void present(SharedPtr<PatchModel> patch, MetadataMap data);
+
+private:
+ void name_changed();
+ void ok_clicked();
+ void cancel_clicked();
+
+ MetadataMap _initial_data;
+ SharedPtr<PatchModel> _patch;
+
+ Gtk::Entry* _name_entry;
+ Gtk::Label* _message_label;
+ Gtk::SpinButton* _poly_spinbutton;
+ Gtk::Button* _ok_button;
+ Gtk::Button* _cancel_button;
+};
+
+
+} // namespace GUI
+} // namespace Ingen
+
+#endif // NEWSUBPATCHWINDOW_H
diff --git a/src/libs/gui/NodeControlWindow.cpp b/src/libs/gui/NodeControlWindow.cpp
new file mode 100644
index 00000000..26108833
--- /dev/null
+++ b/src/libs/gui/NodeControlWindow.cpp
@@ -0,0 +1,136 @@
+/* This file is part of Ingen.
+ * Copyright (C) 2007 Dave 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 alongCont
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <iostream>
+#include <cmath>
+#include "interface/EngineInterface.h"
+#include "client/NodeModel.h"
+#include "App.h"
+#include "NodeControlWindow.h"
+#include "GladeFactory.h"
+#include "ControlGroups.h"
+#include "ControlPanel.h"
+#include "PatchWindow.h"
+
+using namespace std;
+
+namespace Ingen {
+namespace GUI {
+
+
+/** Create a node control window and load a new ControlPanel for it.
+ */
+NodeControlWindow::NodeControlWindow(SharedPtr<NodeModel> node, size_t poly)
+: _node(node),
+ _position_stored(false),
+ _x(0), _y(0)
+{
+ assert(_node != NULL);
+
+ property_resizable() = true;
+ set_border_width(5);
+
+ set_title(_node->path() + " Controls");
+
+ Glib::RefPtr<Gnome::Glade::Xml> xml = GladeFactory::new_glade_reference("warehouse_win");
+ xml->get_widget_derived("control_panel_vbox", _control_panel);
+ _control_panel->reparent(*this);
+
+ _control_panel->init(_node, poly);
+
+ show_all_children();
+ resize();
+
+ _callback_enabled = true;
+}
+
+
+/** Create a node control window and with an existing ControlPanel.
+ */
+NodeControlWindow::NodeControlWindow(SharedPtr<NodeModel> node, ControlPanel* panel)
+: _node(node),
+ _control_panel(panel)
+{
+ assert(_node);
+
+ property_resizable() = true;
+ set_border_width(5);
+
+ set_title(_node->path() + " Controls");
+
+ _control_panel->reparent(*this);
+
+ show_all_children();
+ resize();
+
+ _callback_enabled = true;
+}
+
+
+NodeControlWindow::~NodeControlWindow()
+{
+ delete _control_panel;
+}
+
+
+void
+NodeControlWindow::resize()
+{
+ pair<int,int> controls_size = _control_panel->ideal_size();
+ /*int width = 400;
+ int height = controls_size.second
+ + ((_node->polyphonic()) ? 4 : 40);*/
+ int width = controls_size.first;
+ int height = controls_size.second;
+
+ if (height > property_screen().get_value()->get_height() - 64)
+ height = property_screen().get_value()->get_height() - 64;
+ if (width > property_screen().get_value()->get_width() - 64)
+ width = property_screen().get_value()->get_width() - 64;
+
+ //cerr << "Resizing to: " << width << " x " << height << endl;
+
+ Gtk::Window::resize(width, height);
+}
+
+
+void
+NodeControlWindow::on_show()
+{
+ for (PortModelList::const_iterator i = _node->ports().begin();
+ i != _node->ports().end(); ++i)
+ if ((*i)->is_control() && (*i)->is_input())
+ App::instance().engine()->request_port_value((*i)->path());
+
+ if (_position_stored)
+ move(_x, _y);
+
+ Gtk::Window::on_show();
+}
+
+
+void
+NodeControlWindow::on_hide()
+{
+ _position_stored = true;
+ get_position(_x, _y);
+ Gtk::Window::on_hide();
+}
+
+
+} // namespace GUI
+} // namespace Ingen
diff --git a/src/libs/gui/NodeControlWindow.h b/src/libs/gui/NodeControlWindow.h
new file mode 100644
index 00000000..bd6f9fc9
--- /dev/null
+++ b/src/libs/gui/NodeControlWindow.h
@@ -0,0 +1,76 @@
+/* This file is part of Ingen.
+ * Copyright (C) 2007 Dave 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 NODECONTROLWINDOW_H
+#define NODECONTROLWINDOW_H
+
+#include <vector>
+#include <string>
+#include <gtkmm.h>
+#include <libglademm.h>
+#include <sigc++/sigc++.h>
+#include <raul/SharedPtr.h>
+using std::string; using std::vector;
+
+namespace Ingen { namespace Client {
+ class NodeModel;
+} }
+using Ingen::Client::NodeModel;
+
+namespace Ingen {
+namespace GUI {
+
+class ControlGroup;
+class ControlPanel;
+
+
+/** Window with controls (sliders) for all control-rate ports on a Node.
+ *
+ * \ingroup GUI
+ */
+class NodeControlWindow : public Gtk::Window
+{
+public:
+ NodeControlWindow(SharedPtr<NodeModel> node, size_t poly);
+ NodeControlWindow(SharedPtr<NodeModel> node, ControlPanel* panel);
+ virtual ~NodeControlWindow();
+
+ SharedPtr<NodeModel> node() { return _node; }
+
+ ControlPanel* control_panel() const { return _control_panel; }
+
+ void resize();
+
+protected:
+ void on_show();
+ void on_hide();
+
+private:
+ SharedPtr<NodeModel> _node;
+ ControlPanel* _control_panel;
+ bool _callback_enabled;
+
+ bool _position_stored;
+ int _x;
+ int _y;
+};
+
+
+} // namespace GUI
+} // namespace Ingen
+
+#endif // NODECONTROLWINDOW_H
diff --git a/src/libs/gui/NodeMenu.cpp b/src/libs/gui/NodeMenu.cpp
new file mode 100644
index 00000000..cf71989c
--- /dev/null
+++ b/src/libs/gui/NodeMenu.cpp
@@ -0,0 +1,262 @@
+/* This file is part of Ingen.
+ * Copyright (C) 2007 Dave 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 <iostream>
+#include <gtkmm.h>
+#include "interface/EngineInterface.h"
+#include "client/NodeModel.h"
+#include "App.h"
+#include "NodeMenu.h"
+#include "WindowFactory.h"
+
+using std::cerr; using std::endl;
+
+namespace Ingen {
+namespace GUI {
+
+
+NodeMenu::NodeMenu(SharedPtr<NodeModel> node)
+: _node(node)
+, _controls_menuitem(NULL)
+{
+ App& app = App::instance();
+
+ Gtk::Menu_Helpers::MenuElem controls_elem = Gtk::Menu_Helpers::MenuElem("Controls",
+ sigc::bind(
+ sigc::mem_fun(app.window_factory(), &WindowFactory::present_controls),
+ node));
+ _controls_menuitem = controls_elem.get_child();
+ items().push_back(controls_elem);
+
+ items().push_back(Gtk::Menu_Helpers::SeparatorElem());
+
+ /*items().push_back(Gtk::Menu_Helpers::MenuElem("Rename...",
+ sigc::bind(
+ sigc::mem_fun(app.window_factory(), &WindowFactory::present_rename),
+ node)));*/
+ /*items().push_back(Gtk::Menu_Helpers::MenuElem("Clone",
+ sigc::bind(
+ sigc::mem_fun(app.engine(), &EngineInterface::clone),
+ node)));
+ sigc::mem_fun(this, &NodeMenu::on_menu_clone)));*/
+
+ items().push_back(Gtk::Menu_Helpers::MenuElem("Disconnect All",
+ sigc::mem_fun(this, &NodeMenu::on_menu_disconnect_all)));
+
+ items().push_back(Gtk::Menu_Helpers::MenuElem("Destroy",
+ sigc::mem_fun(this, &NodeMenu::on_menu_destroy)));
+
+ //m_controls_menuitem->property_sensitive() = false;
+
+ cerr << "FIXME: MIDI learn menu\n";
+ /*
+ if (_node->plugin() && _node->plugin()->type() == PluginModel::Internal
+ && _node->plugin()->plug_label() == "midi_control_in") {
+ items().push_back(Gtk::Menu_Helpers::MenuElem("Learn",
+ sigc::mem_fun(this, &NodeMenu::on_menu_learn)));
+ }
+ */
+
+ items().push_back(Gtk::Menu_Helpers::SeparatorElem());
+
+ items().push_back(Gtk::Menu_Helpers::MenuElem("Properties",
+ sigc::bind(
+ sigc::mem_fun(app.window_factory(), &WindowFactory::present_properties),
+ node)));
+
+ //model->new_port_sig.connect(sigc::mem_fun(this, &NodeMenu::add_port));
+ //model->destroyed_sig.connect(sigc::mem_fun(this, &NodeMenu::destroy));
+}
+
+#if 0
+NodeMenu::~NodeMenu()
+{
+ cerr << "~NodeMenu()\n";
+}
+
+void
+NodeMenu::destroy()
+{
+ cerr << "FIXME: NODE DESTROYED\n";
+ //SharedPtr<ObjectModel> model = _model;
+ //m_model.reset();
+}
+#endif
+
+void
+NodeMenu::set_path(const Path& new_path)
+{
+ cerr << "FIXME: rename\n";
+ /*
+ remove_from_store();
+
+ // Rename ports
+ for (list<PortModel*>::const_iterator i = _node->ports().begin();
+ i != _node->ports().end(); ++i) {
+ ObjectController* const pc = (*i)->controller();
+ assert(pc != NULL);
+ pc->set_path(_model->path().base() + pc->model()->name());
+ }
+
+ // Handle bridge port, if this node represents one
+ if (_bridge_port != NULL)
+ _bridge_port->set_path(new_path);
+
+ if (_module != NULL)
+ _module->canvas()->rename_module(_node->path().name(), new_path.name());
+
+ ObjectController::set_path(new_path);
+
+ add_to_store();
+ */
+}
+
+#if 0
+void
+NodeMenu::destroy()
+{
+ PatchController* pc = ((PatchController*)_model->parent()->controller());
+ assert(pc != NULL);
+
+ //remove_from_store();
+ //pc->remove_node(_model->path().name());
+ cerr << "FIXME: remove node\n";
+
+ if (_bridge_port != NULL)
+ _bridge_port->destroy();
+ _bridge_port = NULL;
+
+ //if (_module != NULL)
+ // delete _module;
+}
+#endif
+
+#if 0
+void
+NodeMenu::add_port(SharedPtr<PortModel> pm)
+{
+ assert(pm->parent().get() == _node.get());
+ assert(pm->parent() == _node);
+ assert(_node->get_port(pm->path().name()) == pm);
+
+ //cout << "[NodeMenu] Adding port " << pm->path() << endl;
+
+ /*
+ if (_module != NULL) {
+ // (formerly PortController)
+ pc->create_port(_module);
+ _module->resize();
+
+ // Enable "Controls" menu item on module
+ if (has_control_inputs())
+ enable_controls_menuitem();
+ }*/
+}
+#endif
+
+void
+NodeMenu::on_menu_destroy()
+{
+ App::instance().engine()->destroy(_node->path());
+}
+
+
+void
+NodeMenu::on_menu_clone()
+{
+ cerr << "FIXME: clone broken\n";
+ /*
+ assert(_node);
+ //assert(_parent != NULL);
+ //assert(_parent->model() != NULL);
+
+ string clone_name = _node->name();
+ int i = 2; // postfix number (ie oldname_2)
+
+ // Check if name already has a number postfix
+ if (clone_name.find_last_of("_") != string::npos) {
+ string name_postfix = clone_name.substr(clone_name.find_last_of("_")+1);
+ clone_name = clone_name.substr(0, clone_name.find_last_of("_"));
+ if (sscanf(name_postfix.c_str(), "%d", &i))
+ ++i;
+ }
+
+ char clone_postfix[4];
+ for ( ; i < 100; ++i) {
+ snprintf(clone_postfix, 4, "_%d", i);
+ if (_node->parent_patch()->get_node(clone_name + clone_postfix) == NULL)
+ break;
+ }
+
+ clone_name = clone_name + clone_postfix;
+
+ const string path = _node->parent_patch()->base() + clone_name;
+ NodeModel* nm = new NodeModel(_node->plugin(), path);
+ nm->polyphonic(_node->polyphonic());
+ nm->x(_node->x() + 20);
+ nm->y(_node->y() + 20);
+ App::instance().engine()->create_node_from_model(nm);
+ */
+}
+
+
+void
+NodeMenu::on_menu_learn()
+{
+ App::instance().engine()->midi_learn(_node->path());
+}
+
+void
+NodeMenu::on_menu_disconnect_all()
+{
+ App::instance().engine()->disconnect_all(_node->path());
+}
+
+
+bool
+NodeMenu::has_control_inputs()
+{
+ for (PortModelList::const_iterator i = _node->ports().begin();
+ i != _node->ports().end(); ++i)
+ if ((*i)->is_input() && (*i)->is_control())
+ return true;
+
+ return false;
+}
+
+
+void
+NodeMenu::enable_controls_menuitem()
+{
+ _controls_menuitem->property_sensitive() = true;
+}
+
+
+void
+NodeMenu::disable_controls_menuitem()
+{
+ _controls_menuitem->property_sensitive() = false;
+
+ //if (_control_window != NULL)
+ // _control_window->hide();
+}
+
+
+
+} // namespace GUI
+} // namespace Ingen
+
diff --git a/src/libs/gui/NodeMenu.h b/src/libs/gui/NodeMenu.h
new file mode 100644
index 00000000..65044608
--- /dev/null
+++ b/src/libs/gui/NodeMenu.h
@@ -0,0 +1,78 @@
+/* This file is part of Ingen.
+ * Copyright (C) 2007 Dave 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 NODEMENU_H
+#define NODEMENU_H
+
+#include <string>
+#include <gtkmm.h>
+#include <raul/Path.h>
+#include <raul/SharedPtr.h>
+#include "client/NodeModel.h"
+using Ingen::Client::NodeModel;
+
+using std::string;
+
+namespace Ingen {
+namespace GUI {
+
+class Controller;
+class NodeControlWindow;
+class NodePropertiesWindow;
+class PatchCanvas;
+
+/** Controller for a Node.
+ *
+ * \ingroup GUI
+ */
+class NodeMenu : public Gtk::Menu
+{
+public:
+ NodeMenu(SharedPtr<NodeModel> node);
+
+ void set_path(const Path& new_path);
+
+ virtual void program_add(int bank, int program, const string& name) {}
+ virtual void program_remove(int bank, int program) {}
+
+ bool has_control_inputs();
+
+ //virtual void show_menu(GdkEventButton* event)
+ //{ _menu.popup(event->button, event->time); }
+
+protected:
+
+ virtual void enable_controls_menuitem();
+ virtual void disable_controls_menuitem();
+
+ //virtual void add_port(SharedPtr<PortModel> pm);
+
+ void on_menu_destroy();
+ void on_menu_clone();
+ void on_menu_learn();
+ void on_menu_disconnect_all();
+
+ //Gtk::Menu _menu;
+ SharedPtr<NodeModel> _node;
+ Glib::RefPtr<Gtk::MenuItem> _controls_menuitem;
+};
+
+
+} // namespace GUI
+} // namespace Ingen
+
+#endif // NODEMENU_H
diff --git a/src/libs/gui/NodeModule.cpp b/src/libs/gui/NodeModule.cpp
new file mode 100644
index 00000000..3c80f5e2
--- /dev/null
+++ b/src/libs/gui/NodeModule.cpp
@@ -0,0 +1,153 @@
+/* This file is part of Ingen.
+ * Copyright (C) 2007 Dave 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 <raul/Atom.h>
+#include "interface/EngineInterface.h"
+#include "client/PatchModel.h"
+#include "client/NodeModel.h"
+#include "App.h"
+#include "NodeModule.h"
+#include "PatchCanvas.h"
+#include "Port.h"
+#include "GladeFactory.h"
+#include "RenameWindow.h"
+#include "PatchWindow.h"
+#include "WindowFactory.h"
+#include "SubpatchModule.h"
+#include "NodeControlWindow.h"
+
+namespace Ingen {
+namespace GUI {
+
+
+NodeModule::NodeModule(boost::shared_ptr<PatchCanvas> canvas, SharedPtr<NodeModel> node)
+: LibFlowCanvas::Module(canvas, node->path().name()),
+ _node(node),
+ _menu(node)
+{
+ assert(_node);
+
+ if (node->polyphonic()) {
+ set_border_width(2.0);
+ }
+
+ node->new_port_sig.connect(sigc::bind(sigc::mem_fun(this, &NodeModule::add_port), true));
+ node->removed_port_sig.connect(sigc::mem_fun(this, &NodeModule::remove_port));
+ node->metadata_update_sig.connect(sigc::mem_fun(this, &NodeModule::metadata_update));
+
+ signal_clicked.connect(sigc::mem_fun(this, &NodeModule::on_click));
+}
+
+
+NodeModule::~NodeModule()
+{
+ NodeControlWindow* win = App::instance().window_factory()->control_window(_node);
+
+ if (win) {
+ // Should remove from window factory via signal
+ delete win;
+ }
+}
+
+
+boost::shared_ptr<NodeModule>
+NodeModule::create(boost::shared_ptr<PatchCanvas> canvas, SharedPtr<NodeModel> node)
+{
+ boost::shared_ptr<NodeModule> ret;
+
+ SharedPtr<PatchModel> patch = PtrCast<PatchModel>(node);
+ if (patch)
+ ret = boost::shared_ptr<NodeModule>(new SubpatchModule(canvas, patch));
+ else
+ ret = boost::shared_ptr<NodeModule>(new NodeModule(canvas, node));
+
+ for (MetadataMap::const_iterator m = node->metadata().begin(); m != node->metadata().end(); ++m)
+ ret->metadata_update(m->first, m->second);
+
+ for (PortModelList::const_iterator p = node->ports().begin(); p != node->ports().end(); ++p)
+ ret->add_port(*p, false);
+
+ ret->resize();
+
+ return ret;
+}
+
+
+void
+NodeModule::add_port(SharedPtr<PortModel> port, bool resize_to_fit)
+{
+ Module::add_port(boost::shared_ptr<Port>(new Port(
+ PtrCast<NodeModule>(shared_from_this()), port)));
+
+ if (resize_to_fit)
+ resize();
+}
+
+
+void
+NodeModule::remove_port(SharedPtr<PortModel> port)
+{
+ SharedPtr<LibFlowCanvas::Port> p = Module::remove_port(port->path().name());
+ p.reset();
+}
+
+
+void
+NodeModule::show_control_window()
+{
+ App::instance().window_factory()->present_controls(_node);
+}
+
+
+void
+NodeModule::store_location()
+{
+ const float x = static_cast<float>(property_x());
+ const float y = static_cast<float>(property_y());
+
+ const Atom& existing_x = _node->get_metadata("ingenuity:canvas-x");
+ const Atom& existing_y = _node->get_metadata("ingenuity:canvas-y");
+
+ if (existing_x.type() != Atom::FLOAT || existing_y.type() != Atom::FLOAT
+ || existing_x.get_float() != x || existing_y.get_float() != y) {
+ App::instance().engine()->set_metadata(_node->path(), "ingenuity:canvas-x", Atom(x));
+ App::instance().engine()->set_metadata(_node->path(), "ingenuity:canvas-y", Atom(y));
+ }
+}
+
+
+void
+NodeModule::on_click(GdkEventButton* event)
+{
+ if (event->button == 3)
+ _menu.popup(event->button, event->time);
+}
+
+
+void
+NodeModule::metadata_update(const string& key, const Atom& value)
+{
+ if (key == "ingenuity:canvas-x" && value.type() == Atom::FLOAT)
+ move_to(value.get_float(), property_y());
+ else if (key == "ingenuity:canvas-y" && value.type() == Atom::FLOAT)
+ move_to(property_x(), value.get_float());
+}
+
+
+} // namespace GUI
+} // namespace Ingen
diff --git a/src/libs/gui/NodeModule.h b/src/libs/gui/NodeModule.h
new file mode 100644
index 00000000..8e88b34c
--- /dev/null
+++ b/src/libs/gui/NodeModule.h
@@ -0,0 +1,90 @@
+/* This file is part of In* Copyright (C) 2007 Dave 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 NODEMODULE_H
+#define NODEMODULE_H
+
+#include <string>
+#include <libgnomecanvasmm.h>
+#include <flowcanvas/Module.h>
+#include <raul/SharedPtr.h>
+#include "Port.h"
+#include "NodeMenu.h"
+using std::string;
+
+class Atom;
+
+namespace Ingen { namespace Client {
+ class PortModel;
+ class NodeModel;
+ class ControlModel;
+} }
+using namespace Ingen::Client;
+
+namespace Ingen {
+namespace GUI {
+
+class PatchCanvas;
+class Port;
+
+
+/** A module in a patch.
+ *
+ * This base class is extended for various types of modules - SubpatchModule,
+ * DSSIModule, etc.
+ *
+ * \ingroup GUI
+ */
+class NodeModule : public LibFlowCanvas::Module
+{
+public:
+ static boost::shared_ptr<NodeModule> create (boost::shared_ptr<PatchCanvas> canvas, SharedPtr<NodeModel> node);
+
+ virtual ~NodeModule();
+
+ boost::shared_ptr<Port> port(const string& port_name) {
+ return boost::dynamic_pointer_cast<Ingen::GUI::Port>(
+ Module::get_port(port_name));
+ }
+
+ virtual void store_location();
+
+ void on_click(GdkEventButton* event);
+
+ void show_control_window();
+
+ SharedPtr<NodeModel> node() const { return _node; }
+
+protected:
+ NodeModule(boost::shared_ptr<PatchCanvas> canvas, SharedPtr<NodeModel> node);
+
+ virtual void on_double_click(GdkEventButton* ev) { show_control_window(); }
+ virtual void on_middle_click(GdkEventButton* ev) { show_control_window(); }
+
+ void metadata_update(const string& key, const Atom& value);
+
+ void add_port(SharedPtr<PortModel> port, bool resize=true);
+ void remove_port(SharedPtr<PortModel> port);
+
+ SharedPtr<NodeModel> _node;
+ NodeMenu _menu;
+};
+
+
+} // namespace GUI
+} // namespace Ingen
+
+#endif // NODEMODULE_H
diff --git a/src/libs/gui/NodePropertiesWindow.cpp b/src/libs/gui/NodePropertiesWindow.cpp
new file mode 100644
index 00000000..0cd59e80
--- /dev/null
+++ b/src/libs/gui/NodePropertiesWindow.cpp
@@ -0,0 +1,67 @@
+/* This file is part of Ingen.
+ * Copyright (C) 2007 Dave 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 <string>
+#include "client/NodeModel.h"
+#include "client/PluginModel.h"
+#include "NodePropertiesWindow.h"
+
+namespace Ingen {
+namespace GUI {
+using std::string;
+
+
+NodePropertiesWindow::NodePropertiesWindow(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& glade_xml)
+: Gtk::Window(cobject)
+{
+ glade_xml->get_widget("node_properties_path_label", _node_path_label);
+ glade_xml->get_widget("node_properties_polyphonic_checkbutton", _node_polyphonic_toggle);
+ glade_xml->get_widget("node_properties_plugin_type_label", _plugin_type_label);
+ glade_xml->get_widget("node_properties_plugin_uri_label", _plugin_uri_label);
+ glade_xml->get_widget("node_properties_plugin_name_label", _plugin_name_label);
+}
+
+
+/** Set the node this window is associated with.
+ * This function MUST be called before using this object in any way.
+ */
+void
+NodePropertiesWindow::set_node(SharedPtr<NodeModel> node_model)
+{
+ assert(node_model);
+
+ _node_model = node_model;
+
+ set_title(node_model->path() + " Properties");
+
+ _node_path_label->set_text(node_model->path());
+ _node_polyphonic_toggle->set_active(node_model->polyphonic());
+
+ SharedPtr<PluginModel> pm = node_model->plugin();
+
+ if (pm) {
+ _plugin_type_label->set_text(pm->type_uri());
+ _plugin_uri_label->set_text(pm->uri());
+ _plugin_name_label->set_text(pm->name());
+ }
+}
+
+
+} // namespace GUI
+} // namespace Ingen
+
diff --git a/src/libs/gui/NodePropertiesWindow.h b/src/libs/gui/NodePropertiesWindow.h
new file mode 100644
index 00000000..057de4ec
--- /dev/null
+++ b/src/libs/gui/NodePropertiesWindow.h
@@ -0,0 +1,58 @@
+/* This file is part of Ingen.
+ * Copyright (C) 2007 Dave 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 NODEPROPERTIESWINDOW_H
+#define NODEPROPERTIESWINDOW_H
+
+#include <gtkmm.h>
+#include <libglademm.h>
+#include <raul/SharedPtr.h>
+#include "client/NodeModel.h"
+using namespace Ingen::Client;
+
+namespace Ingen {
+namespace GUI {
+
+
+/** Node properties window.
+ *
+ * Loaded by libglade as a derived object.
+ *
+ * \ingroup GUI
+ */
+class NodePropertiesWindow : public Gtk::Window
+{
+public:
+ NodePropertiesWindow(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& refGlade);
+
+ void present(SharedPtr<NodeModel> node_model) { set_node(node_model); Gtk::Window::present(); }
+ void set_node(SharedPtr<NodeModel> node_model);
+
+private:
+
+ SharedPtr<NodeModel> _node_model;
+ Gtk::Label* _node_path_label;
+ Gtk::CheckButton* _node_polyphonic_toggle;
+ Gtk::Label* _plugin_type_label;
+ Gtk::Label* _plugin_uri_label;
+ Gtk::Label* _plugin_name_label;
+};
+
+} // namespace GUI
+} // namespace Ingen
+
+#endif // NODEPROPERTIESWINDOW_H
diff --git a/src/libs/gui/PatchCanvas.cpp b/src/libs/gui/PatchCanvas.cpp
new file mode 100644
index 00000000..623d0c7c
--- /dev/null
+++ b/src/libs/gui/PatchCanvas.cpp
@@ -0,0 +1,525 @@
+/* This file is part of Ingen.
+ * Copyright (C) 2007 Dave 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 <flowcanvas/FlowCanvas.h>
+#include "interface/EngineInterface.h"
+#include "client/PluginModel.h"
+#include "client/PatchModel.h"
+#include "client/NodeModel.h"
+#include "client/Store.h"
+#include "client/Serializer.h"
+#include "App.h"
+#include "PatchCanvas.h"
+#include "PatchWindow.h"
+#include "PatchPortModule.h"
+#include "LoadPluginWindow.h"
+#include "LoadSubpatchWindow.h"
+#include "NewSubpatchWindow.h"
+#include "Port.h"
+#include "Connection.h"
+#include "NodeModule.h"
+#include "SubpatchModule.h"
+#include "GladeFactory.h"
+#include "WindowFactory.h"
+#include "config.h"
+using Ingen::Client::Store;
+using Ingen::Client::Serializer;
+using Ingen::Client::PluginModel;
+
+namespace Ingen {
+namespace GUI {
+
+
+PatchCanvas::PatchCanvas(SharedPtr<PatchModel> patch, int width, int height)
+: FlowCanvas(width, height),
+ _patch(patch),
+ _last_click_x(0),
+ _last_click_y(0)
+{
+ Glib::RefPtr<Gnome::Glade::Xml> xml = GladeFactory::new_glade_reference();
+ xml->get_widget("canvas_menu", _menu);
+
+ xml->get_widget("canvas_menu_add_audio_input", _menu_add_audio_input);
+ xml->get_widget("canvas_menu_add_audio_output", _menu_add_audio_output);
+ xml->get_widget("canvas_menu_add_control_input", _menu_add_control_input);
+ xml->get_widget("canvas_menu_add_control_output", _menu_add_control_output);
+ xml->get_widget("canvas_menu_add_midi_input", _menu_add_midi_input);
+ xml->get_widget("canvas_menu_add_midi_output", _menu_add_midi_output);
+ xml->get_widget("canvas_menu_load_plugin", _menu_load_plugin);
+ xml->get_widget("canvas_menu_load_patch", _menu_load_patch);
+ xml->get_widget("canvas_menu_new_patch", _menu_new_patch);
+
+ // Add port menu items
+ _menu_add_audio_input->signal_activate().connect(
+ sigc::bind(sigc::mem_fun(this, &PatchCanvas::menu_add_port),
+ "audio_input", "ingen:audio", false));
+ _menu_add_audio_output->signal_activate().connect(
+ sigc::bind(sigc::mem_fun(this, &PatchCanvas::menu_add_port),
+ "audio_output", "ingen:audio", true));
+ _menu_add_control_input->signal_activate().connect(
+ sigc::bind(sigc::mem_fun(this, &PatchCanvas::menu_add_port),
+ "control_input", "ingen:control", false));
+ _menu_add_control_output->signal_activate().connect(
+ sigc::bind(sigc::mem_fun(this, &PatchCanvas::menu_add_port),
+ "control_output", "ingen:control", true));
+ _menu_add_midi_input->signal_activate().connect(
+ sigc::bind(sigc::mem_fun(this, &PatchCanvas::menu_add_port),
+ "midi_input", "ingen:midi", false));
+ _menu_add_midi_output->signal_activate().connect(
+ sigc::bind(sigc::mem_fun(this, &PatchCanvas::menu_add_port),
+ "midi_output", "ingen:midi", true));
+
+ build_plugin_menu();
+
+ // Connect to model signals to track state
+ _patch->new_node_sig.connect(sigc::mem_fun(this, &PatchCanvas::add_node));
+ _patch->removed_node_sig.connect(sigc::mem_fun(this, &PatchCanvas::remove_node));
+ _patch->new_port_sig.connect(sigc::mem_fun(this, &PatchCanvas::add_port));
+ _patch->removed_port_sig.connect(sigc::mem_fun(this, &PatchCanvas::remove_port));
+ _patch->new_connection_sig.connect(sigc::mem_fun(this, &PatchCanvas::connection));
+ _patch->removed_connection_sig.connect(sigc::mem_fun(this, &PatchCanvas::disconnection));
+
+ // Connect widget signals to do things
+ _menu_load_plugin->signal_activate().connect(sigc::mem_fun(this, &PatchCanvas::menu_load_plugin));
+ _menu_load_patch->signal_activate().connect(sigc::mem_fun(this, &PatchCanvas::menu_load_patch));
+ _menu_new_patch->signal_activate().connect(sigc::mem_fun(this, &PatchCanvas::menu_new_patch));
+}
+
+
+void
+PatchCanvas::build_plugin_class_menu(Gtk::Menu* menu,
+ SLV2PluginClass plugin_class, SLV2PluginClasses classes)
+{
+#ifdef HAVE_SLV2
+ // Add submenus
+ for (unsigned i=0; i < slv2_plugin_classes_size(classes); ++i) {
+ SLV2PluginClass c = slv2_plugin_classes_get_at(classes, i);
+ const char* parent = slv2_plugin_class_get_parent_uri(c);
+
+ if (parent && !strcmp(parent, slv2_plugin_class_get_uri(plugin_class))) {
+ menu->items().push_back(Gtk::Menu_Helpers::MenuElem(
+ slv2_plugin_class_get_label(c)));
+ Gtk::MenuItem* menu_item = &(menu->items().back());
+ Gtk::Menu* submenu = Gtk::manage(new Gtk::Menu());
+ menu_item->set_submenu(*submenu);
+ build_plugin_class_menu(submenu, c, classes);
+ }
+ }
+
+
+ const Store::Plugins& plugins = App::instance().store()->plugins();
+
+ // Add plugins
+ for (Store::Plugins::const_iterator i = plugins.begin(); i != plugins.end(); ++i) {
+ SLV2Plugin p = i->second->slv2_plugin();
+ if (p && slv2_plugin_get_class(p) == plugin_class)
+ menu->items().push_back(Gtk::Menu_Helpers::MenuElem(i->second->name(),
+ sigc::bind(sigc::mem_fun(this, &PatchCanvas::load_plugin),
+ i->second)));
+ }
+
+
+#endif
+}
+
+
+void
+PatchCanvas::build_plugin_menu()
+{
+#ifdef HAVE_SLV2
+ _menu->items().push_back(Gtk::Menu_Helpers::ImageMenuElem("Plugin",
+ *(manage(new Gtk::Image(Gtk::Stock::EXECUTE, Gtk::ICON_SIZE_MENU)))));
+ Gtk::MenuItem* plugin_menu_item = &(_menu->items().back());
+ Gtk::Menu* plugin_menu = Gtk::manage(new Gtk::Menu());
+ plugin_menu_item->set_submenu(*plugin_menu);
+ _menu->reorder_child(*plugin_menu_item, 2);
+
+ SLV2PluginClass lv2_plugin = slv2_world_get_plugin_class(PluginModel::slv2_world());
+ SLV2PluginClasses classes = slv2_world_get_plugin_classes(PluginModel::slv2_world());
+
+ build_plugin_class_menu(plugin_menu, lv2_plugin, classes);
+#endif
+}
+
+
+void
+PatchCanvas::build()
+{
+ boost::shared_ptr<PatchCanvas> shared_this =
+ boost::dynamic_pointer_cast<PatchCanvas>(shared_from_this());
+
+ // Create modules for nodes
+ for (NodeModelMap::const_iterator i = _patch->nodes().begin();
+ i != _patch->nodes().end(); ++i) {
+ add_node((*i).second);
+ }
+
+ // Create pseudo modules for ports (ports on this canvas, not on our module)
+ for (PortModelList::const_iterator i = _patch->ports().begin();
+ i != _patch->ports().end(); ++i) {
+ add_port(*i);
+ }
+
+ // Create connections
+ for (list<SharedPtr<ConnectionModel> >::const_iterator i = _patch->connections().begin();
+ i != _patch->connections().end(); ++i) {
+ connection(*i);
+ }
+}
+
+
+void
+PatchCanvas::arrange()
+{
+ LibFlowCanvas::FlowCanvas::arrange();
+
+ for (list<boost::shared_ptr<Item> >::iterator i = _items.begin(); i != _items.end(); ++i)
+ (*i)->store_location();
+}
+
+
+void
+PatchCanvas::add_node(SharedPtr<NodeModel> nm)
+{
+ boost::shared_ptr<PatchCanvas> shared_this =
+ boost::dynamic_pointer_cast<PatchCanvas>(shared_from_this());
+
+ SharedPtr<PatchModel> pm = PtrCast<PatchModel>(nm);
+ SharedPtr<NodeModule> module;
+ if (pm)
+ module = SubpatchModule::create(shared_this, pm);
+ else
+ module = NodeModule::create(shared_this, nm);
+
+ add_item(module);
+ module->show();
+ _views.insert(std::make_pair(nm, module));
+}
+
+
+void
+PatchCanvas::remove_node(SharedPtr<NodeModel> nm)
+{
+ Views::iterator i = _views.find(nm);
+
+ if (i != _views.end()) {
+ remove_item(i->second);
+ _views.erase(i);
+ }
+}
+
+
+void
+PatchCanvas::add_port(SharedPtr<PortModel> pm)
+{
+ boost::shared_ptr<PatchCanvas> shared_this =
+ boost::dynamic_pointer_cast<PatchCanvas>(shared_from_this());
+
+ SharedPtr<PatchPortModule> view = PatchPortModule::create(shared_this, pm);
+ _views.insert(std::make_pair(pm, view));
+ add_item(view);
+ view->show();
+}
+
+
+void
+PatchCanvas::remove_port(SharedPtr<PortModel> pm)
+{
+ Views::iterator i = _views.find(pm);
+
+ if (i != _views.end()) {
+ remove_item(i->second);
+ _views.erase(i);
+ }
+}
+
+
+SharedPtr<LibFlowCanvas::Port>
+PatchCanvas::get_port_view(SharedPtr<PortModel> port)
+{
+ SharedPtr<LibFlowCanvas::Port> ret;
+ SharedPtr<LibFlowCanvas::Module> module = _views[port];
+
+ // Port on this patch
+ if (module) {
+ ret = (PtrCast<PatchPortModule>(module))
+ ? *(PtrCast<PatchPortModule>(module)->ports().begin())
+ : PtrCast<LibFlowCanvas::Port>(module);
+ } else {
+ module = PtrCast<NodeModule>(_views[port->parent()]);
+ if (module)
+ ret = module->get_port(port->path().name());
+ }
+
+ return ret;
+}
+
+
+void
+PatchCanvas::connection(SharedPtr<ConnectionModel> cm)
+{
+ const SharedPtr<LibFlowCanvas::Port> src = get_port_view(cm->src_port());
+ const SharedPtr<LibFlowCanvas::Port> dst = get_port_view(cm->dst_port());
+
+ if (src && dst)
+ add_connection(boost::shared_ptr<Connection>(new Connection(shared_from_this(),
+ cm, src, dst, src->color() + 0x22222200)));
+ else
+ cerr << "[PatchCanvas] ERROR: Unable to find ports to connect "
+ << cm->src_port_path() << " -> " << cm->dst_port_path() << endl;
+}
+
+
+void
+PatchCanvas::disconnection(SharedPtr<ConnectionModel> cm)
+{
+ const SharedPtr<LibFlowCanvas::Port> src = get_port_view(cm->src_port());
+ const SharedPtr<LibFlowCanvas::Port> dst = get_port_view(cm->dst_port());
+
+ if (src && dst)
+ remove_connection(src, dst);
+ else
+ cerr << "[PatchCanvas] ERROR: Unable to find ports to disconnect "
+ << cm->src_port_path() << " -> " << cm->dst_port_path() << endl;
+}
+
+
+void
+PatchCanvas::connect(boost::shared_ptr<LibFlowCanvas::Connectable> src_port,
+ boost::shared_ptr<LibFlowCanvas::Connectable> dst_port)
+{
+ const boost::shared_ptr<Ingen::GUI::Port> src
+ = boost::dynamic_pointer_cast<Ingen::GUI::Port>(src_port);
+
+ const boost::shared_ptr<Ingen::GUI::Port> dst
+ = boost::dynamic_pointer_cast<Ingen::GUI::Port>(dst_port);
+
+ if (!src || !dst)
+ return;
+
+ // Midi binding/learn shortcut
+ if (src->model()->is_midi() && dst->model()->is_control())
+ {
+ cerr << "FIXME: MIDI binding" << endl;
+#if 0
+ SharedPtr<PluginModel> pm(new PluginModel(PluginModel::Internal, "", "midi_control_in", ""));
+ SharedPtr<NodeModel> nm(new NodeModel(pm, _patch->path().base()
+ + src->name() + "-" + dst->name(), false));
+ nm->set_metadata("canvas-x", Atom((float)
+ (dst->module()->property_x() - dst->module()->width() - 20)));
+ nm->set_metadata("canvas-y", Atom((float)
+ (dst->module()->property_y())));
+ App::instance().engine()->create_node_from_model(nm.get());
+ App::instance().engine()->connect(src->model()->path(), nm->path() + "/MIDI_In");
+ App::instance().engine()->connect(nm->path() + "/Out_(CR)", dst->model()->path());
+ App::instance().engine()->midi_learn(nm->path());
+
+ // Set control node range to port's user range
+
+ App::instance().engine()->set_port_value_queued(nm->path().base() + "Min",
+ dst->model()->get_metadata("user-min").get_float());
+ App::instance().engine()->set_port_value_queued(nm->path().base() + "Max",
+ dst->model()->get_metadata("user-max").get_float());
+#endif
+ } else {
+ App::instance().engine()->connect(src->model()->path(), dst->model()->path());
+ }
+}
+
+
+void
+PatchCanvas::disconnect(boost::shared_ptr<LibFlowCanvas::Connectable> src_port,
+ boost::shared_ptr<LibFlowCanvas::Connectable> dst_port)
+{
+ const boost::shared_ptr<Ingen::GUI::Port> src
+ = boost::dynamic_pointer_cast<Ingen::GUI::Port>(src_port);
+
+ const boost::shared_ptr<Ingen::GUI::Port> dst
+ = boost::dynamic_pointer_cast<Ingen::GUI::Port>(dst_port);
+
+ App::instance().engine()->disconnect(src->model()->path(),
+ dst->model()->path());
+}
+
+
+bool
+PatchCanvas::canvas_event(GdkEvent* event)
+{
+ assert(event);
+
+ switch (event->type) {
+
+ case GDK_BUTTON_PRESS:
+ if (event->button.button == 3) {
+ _last_click_x = (int)event->button.x;
+ _last_click_y = (int)event->button.y;
+ show_menu(event);
+ }
+ break;
+
+ /*case GDK_KEY_PRESS:
+ if (event->key.keyval == GDK_Delete)
+ destroy_selected();
+ break;
+ */
+
+ default:
+ break;
+ }
+
+ return FlowCanvas::canvas_event(event);
+}
+
+
+void
+PatchCanvas::destroy_selection()
+{
+ for (list<boost::shared_ptr<Item> >::iterator m = _selected_items.begin(); m != _selected_items.end(); ++m) {
+ boost::shared_ptr<NodeModule> module = boost::dynamic_pointer_cast<NodeModule>(*m);
+ if (module) {
+ App::instance().engine()->destroy(module->node()->path());
+ } else {
+ boost::shared_ptr<PatchPortModule> port_module = boost::dynamic_pointer_cast<PatchPortModule>(*m);
+ if (port_module)
+ App::instance().engine()->destroy(port_module->port()->path());
+ }
+ }
+
+}
+
+
+void
+PatchCanvas::copy_selection()
+{
+ Serializer serializer(*App::instance().rdf_world());
+ serializer.start_to_string();
+
+ for (list<boost::shared_ptr<Item> >::iterator m = _selected_items.begin(); m != _selected_items.end(); ++m) {
+ boost::shared_ptr<NodeModule> module = boost::dynamic_pointer_cast<NodeModule>(*m);
+ if (module) {
+ serializer.serialize(module->node());
+ } else {
+ boost::shared_ptr<PatchPortModule> port_module = boost::dynamic_pointer_cast<PatchPortModule>(*m);
+ if (port_module)
+ serializer.serialize(port_module->port());
+ }
+ }
+
+ for (list<boost::shared_ptr<LibFlowCanvas::Connection> >::iterator c = _selected_connections.begin();
+ c != _selected_connections.end(); ++c) {
+ boost::shared_ptr<Connection> connection = boost::dynamic_pointer_cast<Connection>(*c);
+ if (connection)
+ serializer.serialize_connection(connection->model());
+ }
+
+ string result = serializer.finish();
+
+ Glib::RefPtr<Gtk::Clipboard> clipboard = Gtk::Clipboard::get();
+ clipboard->set_text(result);
+}
+
+
+string
+PatchCanvas::generate_port_name(const string& base) {
+ string name = base;
+
+ char num_buf[5];
+ for (uint i=1; i < 9999; ++i) {
+ snprintf(num_buf, 5, "%u", i);
+ name = base + "_";
+ name += num_buf;
+ if (!_patch->get_port(name))
+ break;
+ }
+
+ assert(Path::is_valid(string("/") + name));
+
+ return name;
+}
+
+
+void
+PatchCanvas::menu_add_port(const string& name, const string& type, bool is_output)
+{
+ const Path& path = _patch->path().base() + generate_port_name(name);
+ App::instance().engine()->create_port(path, type, is_output);
+ MetadataMap data = get_initial_data();
+ for (MetadataMap::const_iterator i = data.begin(); i != data.end(); ++i)
+ App::instance().engine()->set_metadata(path, i->first, i->second);
+}
+
+
+void
+PatchCanvas::load_plugin(SharedPtr<PluginModel> plugin)
+{
+ const Path& path = _patch->path().base() + plugin->default_node_name(_patch);
+ // FIXME: polyphony?
+ App::instance().engine()->create_node(path, plugin->uri(), false);
+ MetadataMap data = get_initial_data();
+ for (MetadataMap::const_iterator i = data.begin(); i != data.end(); ++i)
+ App::instance().engine()->set_metadata(path, i->first, i->second);
+}
+
+
+/** Try to guess a suitable location for a new module.
+ */
+void
+PatchCanvas::get_new_module_location(double& x, double& y)
+{
+ int scroll_x;
+ int scroll_y;
+ get_scroll_offsets(scroll_x, scroll_y);
+ x = scroll_x + 20;
+ y = scroll_y + 20;
+}
+
+
+MetadataMap
+PatchCanvas::get_initial_data()
+{
+ MetadataMap result;
+
+ result["ingenuity:canvas-x"] = Atom((float)_last_click_x);
+ result["ingenuity:canvas-y"] = Atom((float)_last_click_y);
+
+ return result;
+}
+
+void
+PatchCanvas::menu_load_plugin()
+{
+ App::instance().window_factory()->present_load_plugin(_patch, get_initial_data());
+}
+
+
+void
+PatchCanvas::menu_load_patch()
+{
+ App::instance().window_factory()->present_load_subpatch(_patch, get_initial_data());
+}
+
+
+void
+PatchCanvas::menu_new_patch()
+{
+ App::instance().window_factory()->present_new_subpatch(_patch, get_initial_data());
+}
+
+
+} // namespace GUI
+} // namespace Ingen
diff --git a/src/libs/gui/PatchCanvas.h b/src/libs/gui/PatchCanvas.h
new file mode 100644
index 00000000..8634b7bf
--- /dev/null
+++ b/src/libs/gui/PatchCanvas.h
@@ -0,0 +1,129 @@
+/* This file is part of Ingen.
+ * Copyright (C) 2007 Dave 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 PATCHCANVAS_H
+#define PATCHCANVAS_H
+
+#include <string>
+#include <map>
+#include <boost/shared_ptr.hpp>
+#include <flowcanvas/FlowCanvas.h>
+#include <flowcanvas/Module.h>
+#include <raul/SharedPtr.h>
+#include <raul/Path.h>
+#include "client/ConnectionModel.h"
+#include "client/PatchModel.h"
+#include "NodeModule.h"
+
+using std::string;
+using namespace LibFlowCanvas;
+
+using LibFlowCanvas::Port;
+using Ingen::Client::ConnectionModel;
+using Ingen::Client::PatchModel;
+using Ingen::Client::NodeModel;
+using Ingen::Client::PortModel;
+using Ingen::Client::MetadataMap;
+
+namespace Ingen {
+namespace GUI {
+
+class NodeModule;
+
+
+/** Patch canvas widget.
+ *
+ * \ingroup GUI
+ */
+class PatchCanvas : public LibFlowCanvas::FlowCanvas
+{
+public:
+ PatchCanvas(SharedPtr<PatchModel> patch, int width, int height);
+
+ virtual ~PatchCanvas() {}
+
+ /*boost::shared_ptr<NodeModule> find_module(const string& name) {
+ return boost::dynamic_pointer_cast<NodeModule>(
+ FlowCanvas::get_item(name));
+ }*/
+
+ void build();
+ void arrange();
+
+ void add_node(SharedPtr<NodeModel> nm);
+ void remove_node(SharedPtr<NodeModel> nm);
+ void add_port(SharedPtr<PortModel> pm);
+ void remove_port(SharedPtr<PortModel> pm);
+ void connection(SharedPtr<ConnectionModel> cm);
+ void disconnection(SharedPtr<ConnectionModel> cm);
+
+ void get_new_module_location(double& x, double& y);
+
+ void destroy_selection();
+ void copy_selection();
+
+ void show_menu(GdkEvent* event)
+ { _menu->popup(event->button.button, event->button.time); }
+
+private:
+ string generate_port_name(const string& base);
+ void menu_add_port(const string& name, const string& type, bool is_output);
+ void menu_load_plugin();
+ void menu_new_patch();
+ void menu_load_patch();
+ void load_plugin(SharedPtr<PluginModel> plugin);
+ void build_plugin_menu();
+ void build_plugin_class_menu(Gtk::Menu* menu,
+ SLV2PluginClass plugin_class, SLV2PluginClasses classes);
+
+ MetadataMap get_initial_data();
+
+ bool canvas_event(GdkEvent* event);
+
+ SharedPtr<LibFlowCanvas::Port> get_port_view(SharedPtr<PortModel> port);
+
+ void connect(boost::shared_ptr<LibFlowCanvas::Connectable> src,
+ boost::shared_ptr<LibFlowCanvas::Connectable> dst);
+
+ void disconnect(boost::shared_ptr<LibFlowCanvas::Connectable> src,
+ boost::shared_ptr<LibFlowCanvas::Connectable> dst);
+
+ SharedPtr<PatchModel> _patch;
+
+ typedef std::map<SharedPtr<ObjectModel>, SharedPtr<LibFlowCanvas::Module> > Views;
+ Views _views;
+
+ int _last_click_x;
+ int _last_click_y;
+
+ Gtk::Menu* _menu;
+ Gtk::MenuItem* _menu_add_audio_input;
+ Gtk::MenuItem* _menu_add_audio_output;
+ Gtk::MenuItem* _menu_add_control_input;
+ Gtk::MenuItem* _menu_add_control_output;
+ Gtk::MenuItem* _menu_add_midi_input;
+ Gtk::MenuItem* _menu_add_midi_output;
+ Gtk::MenuItem* _menu_load_plugin;
+ Gtk::MenuItem* _menu_load_patch;
+ Gtk::MenuItem* _menu_new_patch;
+};
+
+
+} // namespace GUI
+} // namespace Ingen
+
+#endif // PATCHCANVAS_H
diff --git a/src/libs/gui/PatchPortModule.cpp b/src/libs/gui/PatchPortModule.cpp
new file mode 100644
index 00000000..2a812296
--- /dev/null
+++ b/src/libs/gui/PatchPortModule.cpp
@@ -0,0 +1,113 @@
+/* This file is part of Ingen.
+ * Copyright (C) 2007 Dave 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 "PatchPortModule.h"
+#include <cassert>
+#include "interface/EngineInterface.h"
+#include "client/PatchModel.h"
+#include "client/NodeModel.h"
+#include "App.h"
+#include "PatchCanvas.h"
+#include "Port.h"
+#include "GladeFactory.h"
+#include "RenameWindow.h"
+#include "PatchWindow.h"
+
+namespace Ingen {
+namespace GUI {
+
+
+PatchPortModule::PatchPortModule(boost::shared_ptr<PatchCanvas> canvas, SharedPtr<PortModel> port)
+: LibFlowCanvas::Module(canvas, port->path().name(), 0, 0, false), // FIXME: coords?
+ _port(port)
+{
+ /*if (port_model()->polyphonic() && port_model()->parent() != NULL
+ && port_model()->parent_patch()->poly() > 1) {
+ border_width(2.0);
+ }*/
+
+ assert(canvas);
+ assert(port);
+
+ assert(PtrCast<PatchModel>(port->parent()));
+
+ /*resize();
+
+ const Atom& x_atom = port->get_metadata("ingenuity:canvas-x");
+ const Atom& y_atom = port->get_metadata("ingenuity:canvas-y");
+
+ if (x_atom && y_atom && x_atom.type() == Atom::FLOAT && y_atom.type() == Atom::FLOAT) {
+ move_to(x_atom.get_float(), y_atom.get_float());
+ } else {
+ double default_x;
+ double default_y;
+ canvas->get_new_module_location(default_x, default_y);
+ move_to(default_x, default_y);
+ }*/
+
+ port->metadata_update_sig.connect(sigc::mem_fun(this, &PatchPortModule::metadata_update));
+}
+
+
+boost::shared_ptr<PatchPortModule>
+PatchPortModule::create(boost::shared_ptr<PatchCanvas> canvas, SharedPtr<PortModel> port)
+{
+ boost::shared_ptr<PatchPortModule> ret = boost::shared_ptr<PatchPortModule>(
+ new PatchPortModule(canvas, port));
+ assert(ret);
+
+ ret->_patch_port = boost::shared_ptr<Port>(new Port(ret, port, true, true));
+ ret->add_port(ret->_patch_port);
+
+ for (MetadataMap::const_iterator m = port->metadata().begin(); m != port->metadata().end(); ++m)
+ ret->metadata_update(m->first, m->second);
+
+ ret->resize();
+
+ return ret;
+}
+
+
+void
+PatchPortModule::store_location()
+{
+ const float x = static_cast<float>(property_x());
+ const float y = static_cast<float>(property_y());
+
+ const Atom& existing_x = _port->get_metadata("ingenuity:canvas-x");
+ const Atom& existing_y = _port->get_metadata("ingenuity:canvas-y");
+
+ if (existing_x.type() != Atom::FLOAT || existing_y.type() != Atom::FLOAT
+ || existing_x.get_float() != x || existing_y.get_float() != y) {
+ App::instance().engine()->set_metadata(_port->path(), "ingenuity:canvas-x", Atom(x));
+ App::instance().engine()->set_metadata(_port->path(), "ingenuity:canvas-y", Atom(y));
+ }
+}
+
+
+void
+PatchPortModule::metadata_update(const string& key, const Atom& value)
+{
+ if (key == "ingenuity:canvas-x" && value.type() == Atom::FLOAT)
+ move_to(value.get_float(), property_y());
+ else if (key == "ingenuity:canvas-y" && value.type() == Atom::FLOAT)
+ move_to(property_x(), value.get_float());
+}
+
+
+} // namespace GUI
+} // namespace Ingen
diff --git a/src/libs/gui/PatchPortModule.h b/src/libs/gui/PatchPortModule.h
new file mode 100644
index 00000000..ac39eadf
--- /dev/null
+++ b/src/libs/gui/PatchPortModule.h
@@ -0,0 +1,80 @@
+/* This file is part of Ingen.
+ * Copyright (C) 2007 Dave 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 PATCHPORTMODULE_H
+#define PATCHPORTMODULE_H
+
+#include <string>
+#include <boost/enable_shared_from_this.hpp>
+#include <libgnomecanvasmm.h>
+#include <flowcanvas/Module.h>
+#include <raul/Atom.h>
+#include "Port.h"
+using std::string;
+
+namespace Ingen { namespace Client {
+ class PortModel;
+ class NodeModel;
+ class ControlModel;
+} }
+using namespace Ingen::Client;
+
+namespace Ingen {
+namespace GUI {
+
+class PatchCanvas;
+class Port;
+
+
+/** A "module" to represent a patch's port on it's own canvas.
+ *
+ * Translation: This is the nameless single port pseudo module thingy.
+ *
+ * \ingroup GUI
+ */
+class PatchPortModule : public boost::enable_shared_from_this<LibFlowCanvas::Module>,
+ public LibFlowCanvas::Module
+{
+public:
+ static boost::shared_ptr<PatchPortModule> create(boost::shared_ptr<PatchCanvas> canvas,
+ SharedPtr<PortModel> port);
+
+ virtual ~PatchPortModule() {}
+
+ virtual void store_location();
+
+ //void on_right_click(GdkEventButton* event) { _port->show_menu(event); }
+
+ SharedPtr<PortModel> port() const { return _port; }
+
+protected:
+ PatchPortModule(boost::shared_ptr<PatchCanvas> canvas, SharedPtr<PortModel> port);
+
+ //virtual void on_double_click(GdkEventButton* ev) { show_control_window(); }
+ //virtual void on_middle_click(GdkEventButton* ev) { show_control_window(); }
+
+ void metadata_update(const string& key, const Raul::Atom& value);
+
+ SharedPtr<PortModel> _port;
+ boost::shared_ptr<Port> _patch_port; ///< Port on this 'anonymous' module
+};
+
+
+} // namespace GUI
+} // namespace Ingen
+
+#endif // PATCHPORTMODULE_H
diff --git a/src/libs/gui/PatchPropertiesWindow.cpp b/src/libs/gui/PatchPropertiesWindow.cpp
new file mode 100644
index 00000000..1e310f0b
--- /dev/null
+++ b/src/libs/gui/PatchPropertiesWindow.cpp
@@ -0,0 +1,89 @@
+/* This file is part of Ingen.
+ * Copyright (C) 2007 Dave 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 <string>
+#include "client/PatchModel.h"
+#include "PatchPropertiesWindow.h"
+
+namespace Ingen {
+namespace GUI {
+using std::string;
+
+
+PatchPropertiesWindow::PatchPropertiesWindow(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& glade_xml)
+: Gtk::Window(cobject)
+{
+ glade_xml->get_widget("properties_author_entry", _author_entry);
+ glade_xml->get_widget("properties_description_textview", _textview);
+ glade_xml->get_widget("properties_cancel_button", _cancel_button);
+ glade_xml->get_widget("properties_ok_button", _ok_button);
+
+ _cancel_button->signal_clicked().connect(sigc::mem_fun(this, &PatchPropertiesWindow::cancel_clicked));
+ _ok_button->signal_clicked().connect(sigc::mem_fun(this, &PatchPropertiesWindow::ok_clicked));
+}
+
+
+/** Set the patch model this description is for.
+ *
+ * This function is a "post-constructor" - it MUST be called before using
+ * the window in any way.
+ */
+void
+PatchPropertiesWindow::set_patch(SharedPtr<PatchModel> patch_model)
+{
+ property_title() = patch_model->path() + " Properties";
+ _patch_model = patch_model;
+
+ const Atom& author_atom = _patch_model->get_metadata("author");
+ _author_entry->set_text(
+ (author_atom.type() == Atom::STRING) ? author_atom.get_string() : "" );
+
+ const Atom& desc_atom = _patch_model->get_metadata("description");
+ _textview->get_buffer()->set_text(
+ (desc_atom.type() == Atom::STRING) ? desc_atom.get_string() : "" );
+}
+
+
+void
+PatchPropertiesWindow::cancel_clicked()
+{
+ const Atom& author_atom = _patch_model->get_metadata("author");
+ _author_entry->set_text(
+ (author_atom.type() == Atom::STRING) ? author_atom.get_string() : "" );
+
+ const Atom& desc_atom = _patch_model->get_metadata("description");
+ _textview->get_buffer()->set_text(
+ (desc_atom.type() == Atom::STRING) ? desc_atom.get_string() : "" );
+
+ hide();
+}
+
+
+void
+PatchPropertiesWindow::ok_clicked()
+{
+ cerr << "FIXME: properties\n";
+
+ //m_patch_model->set_metadata("author", Atom(_author_entry->get_text().c_str()));
+ //m_patch_model->set_metadata("description", Atom(_textview->get_buffer()->get_text().c_str()));
+ hide();
+}
+
+
+
+} // namespace GUI
+} // namespace Ingen
diff --git a/src/libs/gui/PatchPropertiesWindow.h b/src/libs/gui/PatchPropertiesWindow.h
new file mode 100644
index 00000000..0640a198
--- /dev/null
+++ b/src/libs/gui/PatchPropertiesWindow.h
@@ -0,0 +1,64 @@
+/* This file is part of Ingen.
+ * Copyright (C) 2007 Dave 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 PATCHPROPERTIESWINDOW_H
+#define PATCHPROPERTIESWINDOW_H
+
+#include <string>
+#include <gtkmm.h>
+#include <libglademm/xml.h>
+#include <raul/SharedPtr.h>
+using std::string;
+
+namespace Ingen { namespace Client { class PatchModel; } }
+using Ingen::Client::PatchModel;
+
+namespace Ingen {
+namespace GUI {
+
+
+/** Patch Properties Window.
+ *
+ * Loaded by libglade as a derived object.
+ *
+ * \ingroup GUI
+ */
+class PatchPropertiesWindow : public Gtk::Window
+{
+public:
+ PatchPropertiesWindow(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& refGlade);
+
+ void present(SharedPtr<PatchModel> patch_model) { set_patch(patch_model); Gtk::Window::present(); }
+ void set_patch(SharedPtr<PatchModel> patch_model);
+
+ void cancel_clicked();
+ void ok_clicked();
+
+private:
+ SharedPtr<PatchModel> _patch_model;
+
+ Gtk::Entry* _author_entry;
+ Gtk::TextView* _textview;
+ Gtk::Button* _cancel_button;
+ Gtk::Button* _ok_button;
+};
+
+
+} // namespace GUI
+} // namespace Ingen
+
+#endif // PATCHPROPERTIESWINDOW_H
diff --git a/src/libs/gui/PatchTreeWindow.cpp b/src/libs/gui/PatchTreeWindow.cpp
new file mode 100644
index 00000000..b77508c1
--- /dev/null
+++ b/src/libs/gui/PatchTreeWindow.cpp
@@ -0,0 +1,270 @@
+/* This file is part of Ingen.
+ * Copyright (C) 2007 Dave 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 <raul/Path.h>
+#include "interface/EngineInterface.h"
+#include "client/OSCEngineSender.h"
+#include "client/Store.h"
+#include "client/PatchModel.h"
+#include "App.h"
+#include "PatchTreeWindow.h"
+#include "SubpatchModule.h"
+#include "WindowFactory.h"
+
+namespace Ingen {
+namespace GUI {
+
+
+PatchTreeWindow::PatchTreeWindow(BaseObjectType* cobject,
+ const Glib::RefPtr<Gnome::Glade::Xml>& xml)
+: Gtk::Window(cobject),
+ _enable_signal(true)
+{
+ xml->get_widget_derived("patches_treeview", _patches_treeview);
+
+ _patch_treestore = Gtk::TreeStore::create(_patch_tree_columns);
+ _patches_treeview->set_window(this);
+ _patches_treeview->set_model(_patch_treestore);
+ Gtk::TreeViewColumn* name_col = Gtk::manage(new Gtk::TreeViewColumn(
+ "Patch", _patch_tree_columns.name_col));
+ Gtk::TreeViewColumn* enabled_col = Gtk::manage(new Gtk::TreeViewColumn(
+ "Run", _patch_tree_columns.enabled_col));
+ name_col->set_resizable(true);
+ name_col->set_expand(true);
+
+ _patches_treeview->append_column(*name_col);
+ _patches_treeview->append_column(*enabled_col);
+ Gtk::CellRendererToggle* enabled_renderer = dynamic_cast<Gtk::CellRendererToggle*>(
+ _patches_treeview->get_column_cell_renderer(1));
+ enabled_renderer->property_activatable() = true;
+
+ _patch_tree_selection = _patches_treeview->get_selection();
+
+ //m_patch_tree_selection->signal_changed().connect(
+ // sigc::mem_fun(this, &PatchTreeWindow::event_patch_selected));
+ _patches_treeview->signal_row_activated().connect(
+ sigc::mem_fun(this, &PatchTreeWindow::event_patch_activated));
+ enabled_renderer->signal_toggled().connect(
+ sigc::mem_fun(this, &PatchTreeWindow::event_patch_enabled_toggled));
+
+ _patches_treeview->columns_autosize();
+}
+
+
+void
+PatchTreeWindow::init(Store& store)
+{
+ store.new_object_sig.connect(sigc::mem_fun(this, &PatchTreeWindow::new_object));
+}
+
+
+void
+PatchTreeWindow::new_object(SharedPtr<ObjectModel> object)
+{
+ SharedPtr<PatchModel> patch = PtrCast<PatchModel>(object);
+ if (patch)
+ add_patch(patch);
+}
+
+
+void
+PatchTreeWindow::add_patch(SharedPtr<PatchModel> pm)
+{
+ if (!pm->parent()) {
+ Gtk::TreeModel::iterator iter = _patch_treestore->append();
+ Gtk::TreeModel::Row row = *iter;
+ if (pm->path() == "/") {
+ SharedPtr<OSCEngineSender> osc_sender = PtrCast<OSCEngineSender>(App::instance().engine());
+ string root_name = osc_sender ? osc_sender->engine_url() : "Internal";
+ // Hack off trailing '/' if it's there (ugly)
+ //if (root_name.substr(root_name.length()-1,1) == "/")
+ // root_name = root_name.substr(0, root_name.length()-1);
+ //root_name.append(":/");
+ row[_patch_tree_columns.name_col] = root_name;
+ } else {
+ row[_patch_tree_columns.name_col] = pm->path().name();
+ }
+ row[_patch_tree_columns.enabled_col] = false;
+ row[_patch_tree_columns.patch_model_col] = pm;
+ _patches_treeview->expand_row(_patch_treestore->get_path(iter), true);
+ } else {
+ Gtk::TreeModel::Children children = _patch_treestore->children();
+ Gtk::TreeModel::iterator c = find_patch(children, pm->parent()->path());
+
+ if (c != children.end()) {
+ Gtk::TreeModel::iterator iter = _patch_treestore->append(c->children());
+ Gtk::TreeModel::Row row = *iter;
+ row[_patch_tree_columns.name_col] = pm->path().name();
+ row[_patch_tree_columns.enabled_col] = false;
+ row[_patch_tree_columns.patch_model_col] = pm;
+ _patches_treeview->expand_row(_patch_treestore->get_path(iter), true);
+ }
+ }
+
+ pm->enabled_sig.connect(sigc::bind(sigc::mem_fun(this, &PatchTreeWindow::patch_enabled), pm->path()));
+ pm->disabled_sig.connect(sigc::bind(sigc::mem_fun(this, &PatchTreeWindow::patch_disabled), pm->path()));
+}
+
+
+void
+PatchTreeWindow::remove_patch(const Path& path)
+{
+ Gtk::TreeModel::iterator i = find_patch(_patch_treestore->children(), path);
+ if (i != _patch_treestore->children().end())
+ _patch_treestore->erase(i);
+}
+
+
+Gtk::TreeModel::iterator
+PatchTreeWindow::find_patch(Gtk::TreeModel::Children root, const Path& path)
+{
+ for (Gtk::TreeModel::iterator c = root.begin(); c != root.end(); ++c) {
+ SharedPtr<PatchModel> pm = (*c)[_patch_tree_columns.patch_model_col];
+ if (pm->path() == path) {
+ return c;
+ } else if ((*c)->children().size() > 0) {
+ Gtk::TreeModel::iterator ret = find_patch(c->children(), path);
+ if (ret != c->children().end())
+ return ret;
+ }
+ }
+ return root.end();
+}
+
+/*
+void
+PatchTreeWindow::event_patch_selected()
+{
+ Gtk::TreeModel::iterator active = _patch_tree_selection->get_selected();
+ if (active) {
+ Gtk::TreeModel::Row row = *active;
+ SharedPtr<PatchModel> pm = row[_patch_tree_columns.patch_model_col];
+ }
+}
+*/
+
+
+/** Show the context menu for the selected patch in the patches treeview.
+ */
+void
+PatchTreeWindow::show_patch_menu(GdkEventButton* ev)
+{
+ Gtk::TreeModel::iterator active = _patch_tree_selection->get_selected();
+ if (active) {
+ Gtk::TreeModel::Row row = *active;
+ SharedPtr<PatchModel> pm = row[_patch_tree_columns.patch_model_col];
+ if (pm)
+ cerr << "FIXME: patch menu\n";
+ //pm->show_menu(ev);
+ }
+}
+
+
+void
+PatchTreeWindow::event_patch_activated(const Gtk::TreeModel::Path& path, Gtk::TreeView::Column* col)
+{
+ Gtk::TreeModel::iterator active = _patch_treestore->get_iter(path);
+ Gtk::TreeModel::Row row = *active;
+ SharedPtr<PatchModel> pm = row[_patch_tree_columns.patch_model_col];
+
+ App::instance().window_factory()->present_patch(pm);
+}
+
+
+void
+PatchTreeWindow::event_patch_enabled_toggled(const Glib::ustring& path_str)
+{
+ Gtk::TreeModel::Path path(path_str);
+ Gtk::TreeModel::iterator active = _patch_treestore->get_iter(path);
+ Gtk::TreeModel::Row row = *active;
+
+ SharedPtr<PatchModel> pm = row[_patch_tree_columns.patch_model_col];
+ Glib::ustring patch_path = pm->path();
+
+ assert(pm);
+
+ if ( ! pm->enabled()) {
+ if (_enable_signal)
+ App::instance().engine()->enable_patch(patch_path);
+ //row[_patch_tree_columns.enabled_col] = true;
+ } else {
+ if (_enable_signal)
+ App::instance().engine()->disable_patch(patch_path);
+ //row[_patch_tree_columns.enabled_col] = false;
+ }
+}
+
+
+void
+PatchTreeWindow::patch_enabled(const Path& path)
+{
+ _enable_signal = false;
+
+ Gtk::TreeModel::iterator i
+ = find_patch(_patch_treestore->children(), path);
+
+ if (i != _patch_treestore->children().end()) {
+ Gtk::TreeModel::Row row = *i;
+ row[_patch_tree_columns.enabled_col] = true;
+ } else {
+ cerr << "[PatchTreeWindow] Unable to find patch " << path << endl;
+ }
+
+ _enable_signal = true;
+}
+
+
+void
+PatchTreeWindow::patch_disabled(const Path& path)
+{
+ _enable_signal = false;
+
+ Gtk::TreeModel::iterator i
+ = find_patch(_patch_treestore->children(), path);
+
+ if (i != _patch_treestore->children().end()) {
+ Gtk::TreeModel::Row row = *i;
+ row[_patch_tree_columns.enabled_col] = false;
+ } else {
+ cerr << "[PatchTreeWindow] Unable to find patch " << path << endl;
+ }
+
+ _enable_signal = true;
+}
+
+
+void
+PatchTreeWindow::patch_renamed(const Path& old_path, const Path& new_path)
+{
+ _enable_signal = false;
+
+ Gtk::TreeModel::iterator i
+ = find_patch(_patch_treestore->children(), old_path);
+
+ if (i != _patch_treestore->children().end()) {
+ Gtk::TreeModel::Row row = *i;
+ row[_patch_tree_columns.name_col] = new_path.name();
+ } else {
+ cerr << "[PatchTreeWindow] Unable to find patch " << old_path << endl;
+ }
+
+ _enable_signal = true;
+}
+
+
+} // namespace GUI
+} // namespace Ingen
diff --git a/src/libs/gui/PatchTreeWindow.h b/src/libs/gui/PatchTreeWindow.h
new file mode 100644
index 00000000..9868d363
--- /dev/null
+++ b/src/libs/gui/PatchTreeWindow.h
@@ -0,0 +1,112 @@
+/* This file is part of Ingen.
+ * Copyright (C) 2007 Dave 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 PATCHTREEWINDOW_H
+#define PATCHTREEWINDOW_H
+
+#include <gtkmm.h>
+#include <libglademm.h>
+#include <raul/Path.h>
+
+namespace Ingen { namespace Client {
+ class Store;
+} }
+using Ingen::Client::Store;
+
+namespace Ingen {
+namespace GUI {
+
+class PatchWindow;
+class PatchTreeView;
+
+
+/** Window with a TreeView of all loaded patches.
+ *
+ * \ingroup GUI
+ */
+class PatchTreeWindow : public Gtk::Window
+{
+public:
+ PatchTreeWindow(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& refGlade);
+
+ void init(Store& store);
+
+ void new_object(SharedPtr<ObjectModel> object);
+
+ void patch_enabled(const Path& path);
+ void patch_disabled(const Path& path);
+ void patch_renamed(const Path& old_path, const Path& new_path);
+
+ void add_patch(SharedPtr<PatchModel> pm);
+ void remove_patch(const Path& path);
+ void show_patch_menu(GdkEventButton* ev);
+
+protected:
+ //void event_patch_selected();
+ void event_patch_activated(const Gtk::TreeModel::Path& path, Gtk::TreeView::Column* col);
+ void event_patch_enabled_toggled(const Glib::ustring& path_str);
+
+ Gtk::TreeModel::iterator find_patch(Gtk::TreeModel::Children root, const Path& path);
+
+ PatchTreeView* _patches_treeview;
+
+ struct PatchTreeModelColumns : public Gtk::TreeModel::ColumnRecord
+ {
+ PatchTreeModelColumns()
+ { add(name_col); add(enabled_col); add(patch_model_col); }
+
+ Gtk::TreeModelColumn<Glib::ustring> name_col;
+ Gtk::TreeModelColumn<bool> enabled_col;
+ Gtk::TreeModelColumn<SharedPtr<PatchModel> > patch_model_col;
+ };
+
+ bool _enable_signal;
+ PatchTreeModelColumns _patch_tree_columns;
+ Glib::RefPtr<Gtk::TreeStore> _patch_treestore;
+ Glib::RefPtr<Gtk::TreeSelection> _patch_tree_selection;
+};
+
+
+/** Derived TreeView class to support context menus for patches */
+class PatchTreeView : public Gtk::TreeView
+{
+public:
+ PatchTreeView(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& xml)
+ : Gtk::TreeView(cobject)
+ {}
+
+ void set_window(PatchTreeWindow* win) { _window = win; }
+
+ bool on_button_press_event(GdkEventButton* ev) {
+ bool ret = Gtk::TreeView::on_button_press_event(ev);
+
+ if ((ev->type == GDK_BUTTON_PRESS) && (ev->button == 3))
+ _window->show_patch_menu(ev);
+
+ return ret;
+ }
+
+private:
+ PatchTreeWindow* _window;
+
+}; // struct PatchTreeView
+
+
+} // namespace GUI
+} // namespace Ingen
+
+#endif // PATCHTREEWINDOW_H
diff --git a/src/libs/gui/PatchView.cpp b/src/libs/gui/PatchView.cpp
new file mode 100644
index 00000000..7b12fe40
--- /dev/null
+++ b/src/libs/gui/PatchView.cpp
@@ -0,0 +1,162 @@
+/* This file is part of Ingen.
+ * Copyright (C) 2007 Dave 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 <iostream>
+#include <cassert>
+#include <fstream>
+#include "interface/EngineInterface.h"
+#include "client/PatchModel.h"
+#include "App.h"
+#include "PatchView.h"
+#include "PatchCanvas.h"
+#include "LoadPluginWindow.h"
+#include "NewSubpatchWindow.h"
+#include "LoadSubpatchWindow.h"
+#include "NodeControlWindow.h"
+#include "PatchPropertiesWindow.h"
+#include "PatchTreeWindow.h"
+#include "GladeFactory.h"
+
+namespace Ingen {
+namespace GUI {
+
+
+PatchView::PatchView(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& xml)
+: Gtk::Box(cobject),
+ _breadcrumb_container(NULL),
+ _enable_signal(true)
+{
+ property_visible() = false;
+
+ xml->get_widget("patch_view_breadcrumb_container", _breadcrumb_container);
+ xml->get_widget("patch_view_process_but", _process_but);
+ xml->get_widget("patch_view_poly_spin", _poly_spin);
+ xml->get_widget("patch_view_clear_but", _clear_but);
+ xml->get_widget("patch_view_destroy_but", _destroy_but);
+ xml->get_widget("patch_view_refresh_but", _refresh_but);
+ xml->get_widget("patch_view_save_but", _save_but);
+ xml->get_widget("patch_view_zoom_full_but", _zoom_full_but);
+ xml->get_widget("patch_view_zoom_normal_but", _zoom_normal_but);
+ xml->get_widget("patch_view_scrolledwindow", _canvas_scrolledwindow);
+}
+
+
+void
+PatchView::set_patch(SharedPtr<PatchModel> patch)
+{
+ assert(!_canvas); // FIXME: remove
+
+ cerr << "Creating view for " << patch->path() << endl;
+
+ assert(_breadcrumb_container); // ensure created
+
+ _patch = patch;
+ _canvas = SharedPtr<PatchCanvas>(new PatchCanvas(patch, 1600*2, 1200*2));
+ _canvas->build();
+
+ _canvas_scrolledwindow->add(*_canvas);
+
+ _poly_spin->set_value(patch->poly());
+ _destroy_but->set_sensitive(patch->path() != "/");
+ patch->enabled() ? enable() : disable();
+
+ // Connect model signals to track state
+ patch->enabled_sig.connect(sigc::mem_fun(this, &PatchView::enable));
+ patch->disabled_sig.connect(sigc::mem_fun(this, &PatchView::disable));
+
+ // Connect widget signals to do things
+ _process_but->signal_toggled().connect(sigc::mem_fun(this, &PatchView::process_toggled));
+ _clear_but->signal_clicked().connect(sigc::mem_fun(this, &PatchView::clear_clicked));
+ _refresh_but->signal_clicked().connect(sigc::mem_fun(this, &PatchView::refresh_clicked));
+
+ _zoom_normal_but->signal_clicked().connect(sigc::bind(sigc::mem_fun(
+ _canvas.get(), &FlowCanvas::set_zoom), 1.0));
+
+ _zoom_full_but->signal_clicked().connect(
+ sigc::mem_fun(_canvas.get(), &FlowCanvas::zoom_full));
+}
+
+
+PatchView::~PatchView()
+{
+ cerr << "Destroying view for " << _patch->path() << endl;
+}
+
+
+SharedPtr<PatchView>
+PatchView::create(SharedPtr<PatchModel> patch)
+
+{
+ const Glib::RefPtr<Gnome::Glade::Xml>& xml = GladeFactory::new_glade_reference("patch_view_box");
+ PatchView* result = NULL;
+ xml->get_widget_derived("patch_view_box", result);
+ assert(result);
+ result->set_patch(patch);
+ return SharedPtr<PatchView>(result);
+}
+
+
+void
+PatchView::process_toggled()
+{
+ if (!_enable_signal)
+ return;
+
+ if (_process_but->get_active()) {
+ App::instance().engine()->enable_patch(_patch->path());
+ App::instance().patch_tree()->patch_enabled(_patch->path());
+ } else {
+ App::instance().engine()->disable_patch(_patch->path());
+ App::instance().patch_tree()->patch_disabled(_patch->path());
+ }
+}
+
+
+void
+PatchView::clear_clicked()
+{
+ App::instance().engine()->clear_patch(_patch->path());
+}
+
+
+void
+PatchView::refresh_clicked()
+{
+ App::instance().engine()->request_object(_patch->path());
+}
+
+
+void
+PatchView::enable()
+{
+ _enable_signal = false;
+ _process_but->set_active(true);
+ _enable_signal = true;
+}
+
+
+void
+PatchView::disable()
+{
+ _enable_signal = false;
+ _process_but->set_active(false);
+ _enable_signal = true;
+}
+
+
+} // namespace GUI
+} // namespace Ingen
diff --git a/src/libs/gui/PatchView.h b/src/libs/gui/PatchView.h
new file mode 100644
index 00000000..72d773fa
--- /dev/null
+++ b/src/libs/gui/PatchView.h
@@ -0,0 +1,102 @@
+/* This file is part of Ingen.
+ * Copyright (C) 2007 Dave 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 PATCHVIEW_H
+#define PATCHVIEW_H
+
+#include <string>
+#include <gtkmm.h>
+#include <libglademm/xml.h>
+#include <libglademm.h>
+#include <raul/SharedPtr.h>
+#include "client/PatchModel.h"
+
+using std::string;
+
+namespace Ingen { namespace Client {
+ class PortModel;
+ class ControlModel;
+ class MetadataModel;
+} }
+using namespace Ingen::Client;
+
+
+namespace Ingen {
+namespace GUI {
+
+class PatchCanvas;
+class LoadPluginWindow;
+class NewSubpatchWindow;
+class LoadSubpatchWindow;
+class NewSubpatchWindow;
+class NodeControlWindow;
+class PatchDescriptionWindow;
+class SubpatchModule;
+class OmPort;
+
+
+/** The patch specific contents of a PatchWindow (ie the canvas and whatever else).
+ *
+ * \ingroup GUI
+ */
+class PatchView : public Gtk::Box
+{
+public:
+ PatchView(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& glade_xml);
+ ~PatchView();
+
+ SharedPtr<PatchCanvas> canvas() const { return _canvas; }
+ SharedPtr<PatchModel> patch() const { return _patch; }
+ Gtk::Viewport* breadcrumb_container() const { return _breadcrumb_container; }
+
+ static SharedPtr<PatchView> create(SharedPtr<PatchModel> patch);
+
+private:
+ void set_patch(SharedPtr<PatchModel> patch);
+
+ void process_toggled();
+ void clear_clicked();
+ void refresh_clicked();
+
+ void enable();
+ void disable();
+
+ void zoom_full();
+
+ SharedPtr<PatchModel> _patch;
+ SharedPtr<PatchCanvas> _canvas;
+
+ Gtk::ScrolledWindow* _canvas_scrolledwindow;
+
+ Gtk::ToggleToolButton* _process_but;
+ Gtk::SpinButton* _poly_spin;
+ Gtk::ToolButton* _clear_but;
+ Gtk::ToolButton* _destroy_but;
+ Gtk::ToolButton* _refresh_but;
+ Gtk::ToolButton* _save_but;
+ Gtk::ToolButton* _zoom_normal_but;
+ Gtk::ToolButton* _zoom_full_but;
+ Gtk::Viewport* _breadcrumb_container;
+
+ bool _enable_signal;
+};
+
+
+} // namespace GUI
+} // namespace Ingen
+
+#endif // PATCHVIEW_H
diff --git a/src/libs/gui/PatchWindow.cpp b/src/libs/gui/PatchWindow.cpp
new file mode 100644
index 00000000..ff2a6a4a
--- /dev/null
+++ b/src/libs/gui/PatchWindow.cpp
@@ -0,0 +1,467 @@
+/* This file is part of Ingen.
+ * Copyright (C) 2007 Dave 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 "PatchWindow.h"
+#include <iostream>
+#include <cassert>
+#include <fstream>
+#include "interface/EngineInterface.h"
+#include "client/PatchModel.h"
+#include "client/Store.h"
+#include "App.h"
+#include "PatchCanvas.h"
+#include "LoadPluginWindow.h"
+#include "NewSubpatchWindow.h"
+#include "LoadPatchWindow.h"
+#include "LoadSubpatchWindow.h"
+#include "NodeControlWindow.h"
+#include "PatchPropertiesWindow.h"
+#include "ConfigWindow.h"
+#include "MessagesWindow.h"
+#include "PatchTreeWindow.h"
+#include "BreadCrumbBox.h"
+#include "ConnectWindow.h"
+#include "ThreadedLoader.h"
+#include "WindowFactory.h"
+#include "PatchView.h"
+
+namespace Ingen {
+namespace GUI {
+
+
+PatchWindow::PatchWindow(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& xml)
+: Gtk::Window(cobject),
+ _enable_signal(true),
+ _position_stored(false),
+ _x(0),
+ _y(0),
+ _breadcrumb_box(NULL)
+{
+ property_visible() = false;
+
+ xml->get_widget("patch_win_vbox", _vbox);
+ xml->get_widget("patch_win_viewport", _viewport);
+ //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_import_location_menuitem", _menu_import_location);
+ //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_upload_menuitem", _menu_upload);
+ 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_close_menuitem", _menu_close);
+ xml->get_widget("patch_configuration_menuitem", _menu_configuration);
+ 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_arrange_menuitem", _menu_arrange);
+ xml->get_widget("patch_clear_menuitem", _menu_clear);
+ xml->get_widget("patch_destroy_menuitem", _menu_destroy_patch);
+ 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);
+
+ _menu_view_control_window->property_sensitive() = false;
+ //m_status_bar->push(App::instance().engine()->engine_url());
+ //m_status_bar->pack_start(*Gtk::manage(new Gtk::Image(Gtk::Stock::CONNECT, Gtk::ICON_SIZE_MENU)), false, false);
+
+ /*_menu_open->signal_activate().connect(
+ sigc::mem_fun(this, &PatchWindow::event_open));*/
+ _menu_import->signal_activate().connect(
+ sigc::mem_fun(this, &PatchWindow::event_import));
+ _menu_import_location->signal_activate().connect(
+ sigc::mem_fun(this, &PatchWindow::event_import_location));
+ _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_upload->signal_activate().connect(
+ sigc::mem_fun(this, &PatchWindow::event_upload));
+ _menu_copy->signal_activate().connect(
+ sigc::mem_fun(this, &PatchWindow::event_copy));
+ _menu_delete->signal_activate().connect(
+ sigc::mem_fun(this, &PatchWindow::event_delete));
+ _menu_quit->signal_activate().connect(
+ sigc::mem_fun(this, &PatchWindow::event_quit));
+ _menu_configuration->signal_activate().connect(
+ sigc::mem_fun(App::instance().configuration_dialog(), &ConfigWindow::show));
+ _menu_fullscreen->signal_activate().connect(
+ sigc::mem_fun(this, &PatchWindow::event_fullscreen_toggled));
+ _menu_arrange->signal_activate().connect(
+ sigc::mem_fun(this, &PatchWindow::event_arrange));
+ _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));
+ _menu_destroy_patch->signal_activate().connect(
+ sigc::mem_fun(this, &PatchWindow::event_destroy));
+ _menu_clear->signal_activate().connect(
+ sigc::mem_fun(this, &PatchWindow::event_clear));
+ _menu_view_messages_window->signal_activate().connect(
+ sigc::mem_fun<void>(App::instance().messages_dialog(), &MessagesWindow::present));
+ _menu_view_patch_tree_window->signal_activate().connect(
+ sigc::mem_fun<void>(App::instance().patch_tree(), &PatchTreeWindow::present));
+
+ _menu_help_about->signal_activate().connect(
+ sigc::mem_fun<void>(App::instance().about_dialog(), &Gtk::Dialog::present));
+
+ _breadcrumb_box = new BreadCrumbBox();
+ _breadcrumb_box->signal_patch_selected.connect(sigc::mem_fun(this, &PatchWindow::set_patch_from_path));
+}
+
+
+PatchWindow::~PatchWindow()
+{
+ // Prevents deletion
+ //m_patch->claim_patch_view();
+
+ delete _breadcrumb_box;
+}
+
+
+/** Set the patch controller from a Path (for use by eg. BreadCrumbBox)
+ */
+void
+PatchWindow::set_patch_from_path(const Path& path, SharedPtr<PatchView> view)
+{
+ if (view) {
+ assert(view->patch()->path() == path);
+ App::instance().window_factory()->present_patch(view->patch(), this, view);
+ } else {
+ SharedPtr<PatchModel> model = PtrCast<PatchModel>(App::instance().store()->object(path));
+ if (model)
+ App::instance().window_factory()->present_patch(model, this);
+ }
+}
+
+
+/** 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<PatchModel> patch, SharedPtr<PatchView> view)
+{
+ if (!patch || patch == _patch)
+ return;
+
+ _enable_signal = false;
+
+ _patch = patch;
+
+ _view = view;
+
+ if (!_view)
+ _view = _breadcrumb_box->view(patch->path());
+
+ if (!_view)
+ _view = PatchView::create(patch);
+
+ assert(_view);
+
+ // Add view to our viewport
+ if (_view->get_parent())
+ _view->get_parent()->remove(*_view.get());
+
+ _viewport->remove();
+ _viewport->add(*_view.get());
+
+
+ if (_breadcrumb_box->get_parent())
+ _breadcrumb_box->get_parent()->remove(*_breadcrumb_box);
+
+ _view->breadcrumb_container()->remove();
+ _view->breadcrumb_container()->add(*_breadcrumb_box);
+ _view->breadcrumb_container()->show();
+
+ _breadcrumb_box->build(patch->path(), _view);
+ _breadcrumb_box->show();
+
+ _menu_view_control_window->property_sensitive() = false;
+
+ for (PortModelList::const_iterator p = patch->ports().begin();
+ p != patch->ports().end(); ++p) {
+ if ((*p)->is_control()) {
+ _menu_view_control_window->property_sensitive() = true;
+ break;
+ }
+ }
+
+ int width, height;
+ get_size(width, height);
+ _view->canvas()->scroll_to(
+ ((int)_view->canvas()->width() - width)/2,
+ ((int)_view->canvas()->height() - height)/2);
+
+ set_title(_patch->path() + " - Ingenuity");
+
+ //m_properties_window->patch_model(pc->patch_model());
+
+ if (patch->path() == "/")
+ _menu_destroy_patch->set_sensitive(false);
+ else
+ _menu_destroy_patch->set_sensitive(true);
+
+ show_all();
+
+ _enable_signal = true;
+}
+
+
+void
+PatchWindow::event_show_engine()
+{
+ if (_patch)
+ App::instance().connect_window()->show();
+}
+
+
+void
+PatchWindow::event_show_controls()
+{
+ App::instance().window_factory()->present_controls(_patch);
+}
+
+
+void
+PatchWindow::event_show_properties()
+{
+ App::instance().window_factory()->present_properties(_patch);
+}
+
+
+void
+PatchWindow::event_import()
+{
+ App::instance().window_factory()->present_load_patch(_patch);
+}
+
+
+void
+PatchWindow::event_import_location()
+{
+ App::instance().window_factory()->present_load_remote_patch(_patch);
+}
+
+
+void
+PatchWindow::event_save()
+{
+ if (_patch->filename() == "")
+ event_save_as();
+ else
+ App::instance().loader()->save_patch(_patch, _patch->filename(), false);
+}
+
+
+void
+PatchWindow::event_save_as()
+{
+ Gtk::FileChooserDialog dialog(*this, "Save Patch", Gtk::FILE_CHOOSER_ACTION_SAVE);
+
+ /*Gtk::VBox* box = dialog.get_vbox();
+ Gtk::Label warning("Warning: Recursively saving will overwrite any subpatch files \
+ without confirmation.");
+ box->pack_start(warning, false, false, 2);
+ Gtk::CheckButton recursive_checkbutton("Recursively save all subpatches");
+ box->pack_start(recursive_checkbutton, false, false, 0);
+ recursive_checkbutton.show();
+ */
+
+ 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;
+
+ // Set current folder to most sensible default
+ const string& current_filename = _patch->filename();
+ if (current_filename.length() > 0)
+ dialog.set_filename(current_filename);
+ else if (App::instance().configuration()->patch_folder().length() > 0)
+ dialog.set_current_folder(App::instance().configuration()->patch_folder());
+
+ int result = dialog.run();
+ //bool recursive = recursive_checkbutton.get_active();
+
+ assert(result == Gtk::RESPONSE_OK || result == Gtk::RESPONSE_CANCEL || result == Gtk::RESPONSE_NONE);
+
+ if (result == Gtk::RESPONSE_OK) {
+ string filename = dialog.get_filename();
+ if (filename.length() < 11 || filename.substr(filename.length()-10) != ".ingen.ttl")
+ filename += ".ingen.ttl";
+
+ bool confirm = false;
+ std::fstream fin;
+ fin.open(filename.c_str(), std::ios::in);
+ if (fin.is_open()) { // File exists
+ string msg = "File already exists! Are you sure you want to overwrite ";
+ msg += filename + "?";
+ Gtk::MessageDialog confirm_dialog(*this,
+ msg, false, Gtk::MESSAGE_WARNING, Gtk::BUTTONS_YES_NO, true);
+ if (confirm_dialog.run() == Gtk::RESPONSE_YES)
+ confirm = true;
+ else
+ confirm = false;
+ } else { // File doesn't exist
+ confirm = true;
+ }
+ fin.close();
+
+ if (confirm) {
+ App::instance().loader()->save_patch(_patch, filename, true);
+ _patch->set_filename(filename);
+ //_patch->set_metadata("filename", Atom(filename.c_str()));
+ }
+ }
+ App::instance().configuration()->set_patch_folder(dialog.get_current_folder());
+}
+
+
+void
+PatchWindow::event_upload()
+{
+ App::instance().window_factory()->present_upload_patch(_patch);
+}
+
+
+void
+PatchWindow::event_copy()
+{
+ if (_view)
+ _view->canvas()->copy_selection();
+}
+
+
+void
+PatchWindow::event_delete()
+{
+ if (_view)
+ _view->canvas()->destroy_selection();
+}
+
+
+void
+PatchWindow::on_show()
+{
+ if (_position_stored)
+ move(_x, _y);
+
+ Gtk::Window::on_show();
+}
+
+
+void
+PatchWindow::on_hide()
+{
+ _position_stored = true;
+ get_position(_x, _y);
+ Gtk::Window::on_hide();
+}
+
+
+bool
+PatchWindow::on_key_press_event(GdkEventKey* event)
+{
+ if (event->keyval == GDK_Delete) {
+ cerr << "FIXME: delete key\n";
+ /*
+ if (_patch && _patch->get_view()) {
+ assert(_patch->get_view()->canvas());
+ _patch->get_view()->canvas()->destroy_selected();
+ }*/
+ return true;
+ } else {
+ return Gtk::Window::on_key_press_event(event);
+ }
+}
+
+
+void
+PatchWindow::event_quit()
+{
+ Gtk::MessageDialog d(*this, "Would you like to quit just Ingenuity\nor kill the engine as well?",
+ true, Gtk::MESSAGE_QUESTION, Gtk::BUTTONS_NONE, true);
+ d.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
+
+ Gtk::Button* b = d.add_button(Gtk::Stock::REMOVE, 2); // kill
+ b->set_label("_Kill Engine");
+ Gtk::Widget* kill_img = Gtk::manage(new Gtk::Image(Gtk::Stock::CLOSE, Gtk::ICON_SIZE_BUTTON));
+ b->set_image(*kill_img);
+
+ b = d.add_button(Gtk::Stock::QUIT, 1); // just exit
+ b->set_label("_Quit");
+ Gtk::Widget* close_img = Gtk::manage(new Gtk::Image(Gtk::Stock::QUIT, Gtk::ICON_SIZE_BUTTON));
+ b->set_image(*close_img);
+ b->grab_default();
+
+ int ret = d.run();
+ if (ret == 1) {
+ App::instance().quit();
+ } else if (ret == 2) {
+ App::instance().engine()->quit();
+ App::instance().quit();
+ }
+ // Otherwise cancelled, do nothing
+}
+
+
+void
+PatchWindow::event_destroy()
+{
+ App::instance().engine()->destroy(_patch->path());
+}
+
+
+void
+PatchWindow::event_clear()
+{
+ App::instance().engine()->clear_patch(_patch->path());
+}
+
+
+void
+PatchWindow::event_arrange()
+{
+ _view->canvas()->arrange();
+}
+
+
+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;
+ }
+}
+
+
+} // namespace GUI
+} // namespace Ingen
diff --git a/src/libs/gui/PatchWindow.h b/src/libs/gui/PatchWindow.h
new file mode 100644
index 00000000..6e95c3d8
--- /dev/null
+++ b/src/libs/gui/PatchWindow.h
@@ -0,0 +1,143 @@
+/* This file is part of Ingen.
+ * Copyright (C) 2007 Dave 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 PATCHWINDOW_H
+#define PATCHWINDOW_H
+
+#include <string>
+#include <list>
+#include <gtkmm.h>
+#include <libglademm/xml.h>
+#include <libglademm.h>
+#include <raul/Path.h>
+#include <raul/SharedPtr.h>
+#include "client/PatchModel.h"
+#include "PatchView.h"
+using Ingen::Client::PatchModel;
+
+using std::string; using std::list;
+
+
+namespace Ingen { namespace Client {
+ class PatchModel;
+ class PortModel;
+ class ControlModel;
+ class MetadataModel;
+} }
+using namespace Ingen::Client;
+
+
+namespace Ingen {
+namespace GUI {
+
+class LoadPluginWindow;
+class LoadPatchWindow;
+class NewSubpatchWindow;
+class LoadSubpatchWindow;
+class NewSubpatchWindow;
+class NodeControlWindow;
+class PatchDescriptionWindow;
+class SubpatchModule;
+class OmPort;
+class BreadCrumbBox;
+
+
+/** A window for a patch.
+ *
+ * \ingroup GUI
+ */
+class PatchWindow : public Gtk::Window
+{
+public:
+ PatchWindow(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& glade_xml);
+ ~PatchWindow();
+
+ void set_patch_from_path(const Path& path, SharedPtr<PatchView> view);
+ void set_patch(SharedPtr<PatchModel> pc, SharedPtr<PatchView> view);
+
+ SharedPtr<PatchModel> patch() const { return _patch; }
+
+ Gtk::MenuItem* menu_view_control_window() { return _menu_view_control_window; }
+
+protected:
+ void on_show();
+ void on_hide();
+ bool on_key_press_event(GdkEventKey* event);
+
+private:
+ void event_import();
+ void event_import_location();
+ void event_save();
+ void event_save_as();
+ void event_upload();
+ void event_copy();
+ void event_delete();
+ void event_quit();
+ void event_destroy();
+ void event_clear();
+ void event_fullscreen_toggled();
+ void event_arrange();
+ void event_show_properties();
+ void event_show_controls();
+ void event_show_engine();
+
+ SharedPtr<PatchModel> _patch;
+ SharedPtr<PatchView> _view;
+
+ bool _enable_signal;
+ bool _position_stored;
+ int _x;
+ int _y;
+
+ Gtk::MenuItem* _menu_import;
+ Gtk::MenuItem* _menu_import_location;
+ Gtk::MenuItem* _menu_save;
+ Gtk::MenuItem* _menu_save_as;
+ Gtk::MenuItem* _menu_upload;
+ Gtk::MenuItem* _menu_cut;
+ Gtk::MenuItem* _menu_copy;
+ Gtk::MenuItem* _menu_paste;
+ Gtk::MenuItem* _menu_delete;
+ Gtk::MenuItem* _menu_configuration;
+ Gtk::MenuItem* _menu_close;
+ Gtk::MenuItem* _menu_quit;
+ Gtk::MenuItem* _menu_fullscreen;
+ Gtk::MenuItem* _menu_clear;
+ Gtk::MenuItem* _menu_destroy_patch;
+ 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::Viewport* _viewport;
+ BreadCrumbBox* _breadcrumb_box;
+
+ //Gtk::Statusbar* _status_bar;
+
+ /** Invisible bin used to store breadcrumbs when not shown by a view */
+ Gtk::Alignment _breadcrumb_bin;
+};
+
+
+} // namespace GUI
+} // namespace Ingen
+
+#endif // PATCHWINDOW_H
diff --git a/src/libs/gui/Port.cpp b/src/libs/gui/Port.cpp
new file mode 100644
index 00000000..9b21b8be
--- /dev/null
+++ b/src/libs/gui/Port.cpp
@@ -0,0 +1,61 @@
+/* This file is part of Ingen.
+ * Copyright (C) 2007 Dave 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 <iostream>
+#include "interface/EngineInterface.h"
+#include "client/PatchModel.h"
+#include "client/PortModel.h"
+#include "client/ControlModel.h"
+#include "Configuration.h"
+#include "App.h"
+#include "Port.h"
+using std::cerr; using std::endl;
+
+using namespace Ingen::Client;
+
+namespace Ingen {
+namespace GUI {
+
+
+/** @param flip Make an input port appear as an output port, and vice versa.
+ */
+Port::Port(boost::shared_ptr<LibFlowCanvas::Module> module, SharedPtr<PortModel> pm, bool flip, bool destroyable)
+: LibFlowCanvas::Port(module,
+ pm->path().name(),
+ flip ? (!pm->is_input()) : pm->is_input(),
+ App::instance().configuration()->get_port_color(pm.get())),
+ _port_model(pm)
+{
+ assert(module);
+ assert(_port_model);
+
+ if (destroyable)
+ _menu.items().push_back(Gtk::Menu_Helpers::MenuElem("Destroy",
+ sigc::mem_fun(this, &Port::on_menu_destroy)));
+}
+
+
+void
+Port::on_menu_destroy()
+{
+ App::instance().engine()->destroy(_port_model->path());
+}
+
+
+} // namespace GUI
+} // namespace Ingen
diff --git a/src/libs/gui/Port.h b/src/libs/gui/Port.h
new file mode 100644
index 00000000..b798cbca
--- /dev/null
+++ b/src/libs/gui/Port.h
@@ -0,0 +1,57 @@
+/* This file is part of Ingen.
+ * Copyright (C) 2007 Dave 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 PORT_H
+#define PORT_H
+
+#include <cassert>
+#include <string>
+#include <flowcanvas/Port.h>
+#include <raul/SharedPtr.h>
+
+namespace Ingen { namespace Client { class PortModel; } }
+using Ingen::Client::PortModel;
+
+namespace Ingen {
+namespace GUI {
+
+
+/** A Port on an Module.
+ *
+ * \ingroup GUI
+ */
+class Port : public LibFlowCanvas::Port
+{
+public:
+ Port(boost::shared_ptr<LibFlowCanvas::Module> module, SharedPtr<PortModel> pm, bool flip = false, bool destroyable = false);
+
+ virtual ~Port() {}
+
+ SharedPtr<PortModel> model() const { return _port_model; }
+
+private:
+
+ void on_menu_destroy();
+
+ SharedPtr<PortModel> _port_model;
+};
+
+
+} // namespace GUI
+} // namespace Ingen
+
+#endif // PORT_H
diff --git a/src/libs/gui/PortPropertiesWindow.cpp b/src/libs/gui/PortPropertiesWindow.cpp
new file mode 100644
index 00000000..10b65641
--- /dev/null
+++ b/src/libs/gui/PortPropertiesWindow.cpp
@@ -0,0 +1,175 @@
+/* This file is part of Ingen.
+ * Copyright (C) 2007 Dave 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 <string>
+#include "interface/EngineInterface.h"
+#include "client/NodeModel.h"
+#include "client/PluginModel.h"
+#include "App.h"
+#include "ControlGroups.h"
+#include "PortPropertiesWindow.h"
+
+using std::string;
+
+namespace Ingen {
+namespace GUI {
+
+
+PortPropertiesWindow::PortPropertiesWindow(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& xml)
+ : Gtk::Dialog(cobject)
+ , _enable_signal(false)
+ , _control(NULL)
+{
+ xml->get_widget("port_properties_min_spinner", _min_spinner);
+ xml->get_widget("port_properties_max_spinner", _max_spinner);
+ xml->get_widget("port_properties_cancel_button", _cancel_button);
+ xml->get_widget("port_properties_ok_button", _ok_button);
+
+ _cancel_button->signal_clicked().connect(sigc::mem_fun(this,
+ &PortPropertiesWindow::cancel));
+
+ _ok_button->signal_clicked().connect(sigc::mem_fun(this,
+ &PortPropertiesWindow::ok));
+}
+
+
+/** Set the port this window is associated with.
+ * This function MUST be called before using this object in any way.
+ */
+void
+PortPropertiesWindow::init(ControlGroup* control, SharedPtr<PortModel> pm)
+{
+ assert(pm);
+ assert(control);
+
+ _port_model = pm;
+ _control = control;
+
+
+ set_title(pm->path() + " Properties");
+
+ // FIXME: code duplication w/ ControlGroups.cpp
+ float min = 0.0f;
+ float max = 1.0f;
+
+ const Atom& min_atom = pm->get_metadata("ingen:minimum");
+ const Atom& max_atom = pm->get_metadata("ingen_maximum");
+ if (min_atom.type() == Atom::FLOAT && max_atom.type() == Atom::FLOAT) {
+ min = min_atom.get_float();
+ max = max_atom.get_float();
+ }
+
+ const SharedPtr<NodeModel> parent = PtrCast<NodeModel>(pm->parent());
+
+ if (parent && parent->plugin() && parent->plugin()->type() == PluginModel::LV2) {
+ min = slv2_port_get_minimum_value(
+ parent->plugin()->slv2_plugin(),
+ slv2_plugin_get_port_by_symbol(parent->plugin()->slv2_plugin(),
+ pm->path().name().c_str()));
+ max = slv2_port_get_maximum_value(
+ parent->plugin()->slv2_plugin(),
+ slv2_plugin_get_port_by_symbol(parent->plugin()->slv2_plugin(),
+ pm->path().name().c_str()));
+ }
+
+ if (max <= min)
+ max = min + 1.0f;
+
+ _initial_min = min;
+ _initial_max = max;
+
+ _min_spinner->set_value(min);
+ _min_spinner->signal_value_changed().connect(sigc::mem_fun(*this, &PortPropertiesWindow::min_changed));
+ _max_spinner->set_value(max);
+ _max_spinner->signal_value_changed().connect(sigc::mem_fun(*this, &PortPropertiesWindow::max_changed));
+
+ pm->metadata_update_sig.connect(sigc::mem_fun(this, &PortPropertiesWindow::metadata_update));
+
+ _enable_signal = true;
+}
+
+
+void
+PortPropertiesWindow::metadata_update(const string& key, const Atom& value)
+{
+ _enable_signal = false;
+
+ if ( (key == "ingen:minimum") && value.type() == Atom::FLOAT)
+ _min_spinner->set_value(value.get_float());
+ else if ( (key == "ingen:maximum") && value.type() == Atom::FLOAT)
+ _max_spinner->set_value(value.get_float());
+
+ _enable_signal = true;
+}
+
+
+void
+PortPropertiesWindow::min_changed()
+{
+ float min = _min_spinner->get_value();
+ const float max = _max_spinner->get_value();
+
+ if (min >= max) {
+ min = max - 1.0;
+ _min_spinner->set_value(min);
+ }
+
+ _control->set_range(min, max);
+
+ if (_enable_signal)
+ App::instance().engine()->set_metadata(_port_model->path(), "ingen:minimum", min);
+}
+
+
+void
+PortPropertiesWindow::max_changed()
+{
+ const float min = _min_spinner->get_value();
+ float max = _max_spinner->get_value();
+
+ if (max <= min) {
+ max = min + 1.0;
+ _max_spinner->set_value(max);
+ }
+
+ _control->set_range(min, max);
+
+ if (_enable_signal)
+ App::instance().engine()->set_metadata(_port_model->path(), "ingen:maximum", max);
+}
+
+
+void
+PortPropertiesWindow::cancel()
+{
+ App::instance().engine()->set_metadata(_port_model->path(), "ingen:minimum", _initial_min);
+ App::instance().engine()->set_metadata(_port_model->path(), "ingen:maximum", _initial_max);
+ delete this;
+}
+
+
+void
+PortPropertiesWindow::ok()
+{
+ delete this;
+}
+
+
+} // namespace GUI
+} // namespace Ingen
+
diff --git a/src/libs/gui/PortPropertiesWindow.h b/src/libs/gui/PortPropertiesWindow.h
new file mode 100644
index 00000000..b7900bc6
--- /dev/null
+++ b/src/libs/gui/PortPropertiesWindow.h
@@ -0,0 +1,70 @@
+/* This file is part of Ingen.
+ * Copyright (C) 2007 Dave 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 PORTPROPERTIESWINDOW_H
+#define PORTPROPERTIESWINDOW_H
+
+#include <gtkmm.h>
+#include <libglademm.h>
+#include <raul/SharedPtr.h>
+#include "client/PortModel.h"
+using namespace Ingen::Client;
+
+namespace Ingen {
+namespace GUI {
+
+class ControlGroup;
+
+
+/** Port properties window.
+ *
+ * Loaded by libglade as a derived object.
+ *
+ * \ingroup GUI
+ */
+class PortPropertiesWindow : public Gtk::Dialog
+{
+public:
+ PortPropertiesWindow(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& refGlade);
+
+ void init(ControlGroup* control, SharedPtr<PortModel> port_model);
+
+private:
+ void metadata_update(const string& key, const Atom& value);
+ void min_changed();
+ void max_changed();
+
+ void ok();
+ void cancel();
+
+ bool _enable_signal;
+
+ float _initial_min;
+ float _initial_max;
+
+ ControlGroup* _control;
+ SharedPtr<PortModel> _port_model;
+ Gtk::SpinButton* _min_spinner;
+ Gtk::SpinButton* _max_spinner;
+ Gtk::Button* _cancel_button;
+ Gtk::Button* _ok_button;
+};
+
+} // namespace GUI
+} // namespace Ingen
+
+#endif // PORTPROPERTIESWINDOW_H
diff --git a/src/libs/gui/RenameWindow.cpp b/src/libs/gui/RenameWindow.cpp
new file mode 100644
index 00000000..d9c474a8
--- /dev/null
+++ b/src/libs/gui/RenameWindow.cpp
@@ -0,0 +1,117 @@
+/* This file is part of Ingen.
+ * Copyright (C) 2007 Dave 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 <string>
+#include "interface/EngineInterface.h"
+#include "client/ObjectModel.h"
+#include "client/Store.h"
+#include "App.h"
+#include "RenameWindow.h"
+
+using std::string;
+
+namespace Ingen {
+namespace GUI {
+
+
+RenameWindow::RenameWindow(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& glade_xml)
+: Gtk::Window(cobject)
+{
+ glade_xml->get_widget("rename_name_entry", _name_entry);
+ glade_xml->get_widget("rename_message_label", _message_label);
+ glade_xml->get_widget("rename_cancel_button", _cancel_button);
+ glade_xml->get_widget("rename_ok_button", _ok_button);
+
+ _name_entry->signal_changed().connect(sigc::mem_fun(this, &RenameWindow::name_changed));
+ _cancel_button->signal_clicked().connect(sigc::mem_fun(this, &RenameWindow::cancel_clicked));
+ _ok_button->signal_clicked().connect(sigc::mem_fun(this, &RenameWindow::ok_clicked));
+
+ _ok_button->property_sensitive() = false;
+}
+
+
+/** Set the object this window is renaming.
+ * This function MUST be called before using this object in any way.
+ */
+void
+RenameWindow::set_object(SharedPtr<ObjectModel> object)
+{
+ _object = object;
+ _name_entry->set_text(object->path().name());
+}
+
+
+/** Called every time the user types into the name input box.
+ * Used to display warning messages, and enable/disable the rename button.
+ */
+void
+RenameWindow::name_changed()
+{
+ assert(_name_entry);
+ assert(_message_label);
+ assert(_object);
+ assert(_object->parent());
+
+ string name = _name_entry->get_text();
+ if (name.find("/") != string::npos) {
+ _message_label->set_text("Name may not contain '/'");
+ _ok_button->property_sensitive() = false;
+ //} else if (_object->parent()->patch_model()->get_node(name) != NULL) {
+ } else if (App::instance().store()->object(_object->parent()->path().base() + name)) {
+ _message_label->set_text("An object already exists with that name.");
+ _ok_button->property_sensitive() = false;
+ } else if (name.length() == 0) {
+ _message_label->set_text("");
+ _ok_button->property_sensitive() = false;
+ } else {
+ _message_label->set_text("");
+ _ok_button->property_sensitive() = true;
+ }
+}
+
+
+void
+RenameWindow::cancel_clicked()
+{
+ cout << "cancel\n";
+ _name_entry->set_text("");
+ hide();
+}
+
+
+/** Rename the object.
+ *
+ * It shouldn't be possible for this to be called with an invalid name set
+ * (since the Rename button should be deactivated). This is just shinification
+ * though - the engine will handle invalid names gracefully.
+ */
+void
+RenameWindow::ok_clicked()
+{
+ string name = _name_entry->get_text();
+ assert(name.length() > 0);
+ assert(name.find("/") == string::npos);
+
+ App::instance().engine()->rename(_object->path(), name);
+
+ hide();
+}
+
+
+} // namespace GUI
+} // namespace Ingen
diff --git a/src/libs/gui/RenameWindow.h b/src/libs/gui/RenameWindow.h
new file mode 100644
index 00000000..56ee3d5a
--- /dev/null
+++ b/src/libs/gui/RenameWindow.h
@@ -0,0 +1,60 @@
+/* This file is part of Ingen.
+ * Copyright (C) 2007 Dave 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 RENAMEWINDOW_H
+#define RENAMEWINDOW_H
+
+#include <gtkmm.h>
+#include <libglademm.h>
+#include <raul/SharedPtr.h>
+#include "client/ObjectModel.h"
+using Ingen::Client::ObjectModel;
+
+namespace Ingen {
+namespace GUI {
+
+
+/** Rename window. Handles renaming of any (Ingen) object.
+ *
+ * \ingroup GUI
+ */
+class RenameWindow : public Gtk::Window
+{
+public:
+ RenameWindow(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& refGlade);
+
+ void present(SharedPtr<ObjectModel> object) { set_object(object); Gtk::Window::present(); }
+
+private:
+ void set_object(SharedPtr<ObjectModel> object);
+
+ void name_changed();
+ void cancel_clicked();
+ void ok_clicked();
+
+ SharedPtr<ObjectModel> _object;
+
+ Gtk::Entry* _name_entry;
+ Gtk::Label* _message_label;
+ Gtk::Button* _cancel_button;
+ Gtk::Button* _ok_button;
+};
+
+} // namespace GUI
+} // namespace Ingen
+
+#endif // RENAMEWINDOW_H
diff --git a/src/libs/gui/SubpatchModule.cpp b/src/libs/gui/SubpatchModule.cpp
new file mode 100644
index 00000000..475774bc
--- /dev/null
+++ b/src/libs/gui/SubpatchModule.cpp
@@ -0,0 +1,95 @@
+/* This file is part of Ingen.
+ * Copyright (C) 2007 Dave 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 "SubpatchModule.h"
+#include <cassert>
+#include <iostream>
+#include "interface/EngineInterface.h"
+#include "client/PatchModel.h"
+#include "App.h"
+#include "NodeModule.h"
+#include "NodeControlWindow.h"
+#include "PatchWindow.h"
+#include "PatchCanvas.h"
+#include "Port.h"
+#include "WindowFactory.h"
+using std::cerr; using std::cout; using std::endl;
+
+namespace Ingen {
+namespace GUI {
+
+
+SubpatchModule::SubpatchModule(boost::shared_ptr<PatchCanvas> canvas, SharedPtr<PatchModel> patch)
+: NodeModule(canvas, patch),
+ _patch(patch)
+{
+ assert(canvas);
+ assert(patch);
+}
+
+
+void
+SubpatchModule::on_double_click(GdkEventButton* event)
+{
+ assert(_patch);
+
+ SharedPtr<PatchModel> parent = PtrCast<PatchModel>(_patch->parent());
+
+ PatchWindow* const preferred = ( (parent && (event->state & GDK_SHIFT_MASK))
+ ? NULL
+ : App::instance().window_factory()->patch_window(parent) );
+
+ App::instance().window_factory()->present_patch(_patch, preferred);
+}
+
+
+
+/** Browse to this patch in current (parent's) window
+ * (unless an existing window is displaying it)
+ */
+void
+SubpatchModule::browse_to_patch()
+{
+ assert(_patch->parent());
+
+ SharedPtr<PatchModel> parent = PtrCast<PatchModel>(_patch->parent());
+
+ PatchWindow* const preferred = ( (parent)
+ ? App::instance().window_factory()->patch_window(parent)
+ : NULL );
+
+ App::instance().window_factory()->present_patch(_patch, preferred);
+}
+
+
+
+void
+SubpatchModule::show_dialog()
+{
+ cerr << "FIXME: dialog\n";
+ //m_patch->show_control_window();
+}
+
+
+void
+SubpatchModule::menu_remove()
+{
+ App::instance().engine()->destroy(_patch->path());
+}
+
+} // namespace GUI
+} // namespace Ingen
diff --git a/src/libs/gui/SubpatchModule.h b/src/libs/gui/SubpatchModule.h
new file mode 100644
index 00000000..4dc17e8b
--- /dev/null
+++ b/src/libs/gui/SubpatchModule.h
@@ -0,0 +1,71 @@
+/* This file is part of Ingen.
+ * Copyright (C) 2007 Dave 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 SUBPATCHMODULE_H
+#define SUBPATCHMODULE_H
+
+#include <string>
+#include <libgnomecanvasmm.h>
+#include <raul/SharedPtr.h>
+#include "client/PatchModel.h"
+#include "PatchPortModule.h"
+#include "NodeModule.h"
+using std::string; using std::list;
+
+namespace Ingen { namespace Client {
+ class PatchModel;
+ class NodeModel;
+ class PortModel;
+ class PatchWindow;
+} }
+using namespace Ingen::Client;
+
+namespace Ingen {
+namespace GUI {
+
+class PatchCanvas;
+class NodeControlWindow;
+
+
+/** A module to represent a subpatch
+ *
+ * \ingroup GUI
+ */
+class SubpatchModule : public NodeModule
+{
+public:
+ SubpatchModule(boost::shared_ptr<PatchCanvas> canvas, SharedPtr<PatchModel> controller);
+ virtual ~SubpatchModule() {}
+
+ void on_double_click(GdkEventButton* ev);
+
+ void show_dialog();
+ void browse_to_patch();
+ void menu_remove();
+
+ SharedPtr<PatchModel> patch() { return _patch; }
+
+protected:
+ SharedPtr<PatchModel> _patch;
+};
+
+
+} // namespace GUI
+} // namespace Ingen
+
+#endif // SUBPATCHMODULE_H
diff --git a/src/libs/gui/ThreadedLoader.cpp b/src/libs/gui/ThreadedLoader.cpp
new file mode 100644
index 00000000..120d9b6c
--- /dev/null
+++ b/src/libs/gui/ThreadedLoader.cpp
@@ -0,0 +1,141 @@
+/* This file is part of Ingen.
+ * Copyright (C) 2007 Dave 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 <fstream>
+#include <cassert>
+#include <string>
+#include "client/PatchModel.h"
+#include "module/Module.h"
+#include "App.h"
+#include "ThreadedLoader.h"
+using std::cout; using std::endl;
+
+namespace Ingen {
+namespace GUI {
+
+
+ThreadedLoader::ThreadedLoader(SharedPtr<EngineInterface> engine)
+ : _serialisation_module(Ingen::Shared::load_module("ingen_serialisation"))
+ , _engine(engine)
+ , _deprecated_loader(engine)
+ , _serializer(*App::instance().rdf_world())
+{
+ // FIXME: rework this so the thread is only present when it's doing something (save mem)
+ if (_serialisation_module) {
+ Loader* (*new_loader)() = NULL;
+ bool found = _serialisation_module->get_symbol("new_loader", (void*&)new_loader);
+ if (found)
+ _loader = SharedPtr<Loader>(new_loader());
+ }
+
+ if (_loader) {
+ start();
+ } else {
+ cerr << "WARNING: Failed to load ingen_serialisation module, unable to load patches." << endl;;
+ cerr << "If you are running from the source tree, run ingenuity_dev." << endl;
+ }
+}
+
+
+ThreadedLoader::~ThreadedLoader()
+{
+}
+
+
+void
+ThreadedLoader::_whipped()
+{
+ _mutex.lock();
+
+ while ( ! _events.empty() ) {
+ _events.front()();
+ _events.pop_front();
+ }
+
+ _mutex.unlock();
+}
+
+/** FIXME: use poly parameter */
+void
+ThreadedLoader::load_patch(bool merge,
+ const string& data_base_uri,
+ const Path& data_path,
+ MetadataMap engine_data,
+ optional<Path> engine_parent,
+ optional<const string&> engine_name,
+ optional<size_t> engine_poly)
+{
+ _mutex.lock();
+
+ // FIXME: Filthy hack to load deprecated patches based on file extension
+ if (data_base_uri.substr(data_base_uri.length()-3) == ".om") {
+ _events.push_back(sigc::hide_return(sigc::bind(
+ sigc::mem_fun(_deprecated_loader, &DeprecatedLoader::load_patch),
+ data_base_uri,
+ engine_parent,
+ (engine_name) ? engine_name.get() : "",
+ (engine_poly) ? engine_poly.get() : 1,
+ engine_data,
+ false)));
+ } else {
+ _events.push_back(sigc::hide_return(sigc::bind(
+ sigc::mem_fun(_loader.get(), &Ingen::Serialisation::Loader::load),
+ App::instance().engine(),
+ App::instance().rdf_world(),
+ data_base_uri,
+ engine_parent,
+ (engine_name) ? engine_name.get() : "",
+ // FIXME: poly here
+ "",
+ engine_data )));
+ }
+
+ _mutex.unlock();
+
+ whip();
+}
+
+
+void
+ThreadedLoader::save_patch(SharedPtr<PatchModel> model, const string& filename, bool recursive)
+{
+ _mutex.lock();
+
+ _events.push_back(sigc::hide_return(sigc::bind(
+ sigc::mem_fun(this, &ThreadedLoader::save_patch_event),
+ model, filename, recursive)));
+
+ _mutex.unlock();
+
+ whip();
+}
+
+
+void
+ThreadedLoader::save_patch_event(SharedPtr<PatchModel> model, const string& filename, bool recursive)
+{
+ if (recursive)
+ cerr << "FIXME: Recursive save." << endl;
+
+ _serializer.start_to_filename(filename);
+ _serializer.serialize(model);
+ _serializer.finish();
+}
+
+
+} // namespace GUI
+} // namespace Ingen
diff --git a/src/libs/gui/ThreadedLoader.h b/src/libs/gui/ThreadedLoader.h
new file mode 100644
index 00000000..58a18fae
--- /dev/null
+++ b/src/libs/gui/ThreadedLoader.h
@@ -0,0 +1,99 @@
+/* This file is part of Ingen.
+ * Copyright (C) 2007 Dave 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 THREADEDLOADER_H
+#define THREADEDLOADER_H
+
+#include <string>
+#include <list>
+#include <cassert>
+#include <boost/optional/optional.hpp>
+#include <raul/Thread.h>
+#include <raul/Slave.h>
+#include <raul/Mutex.h>
+#include <raul/Condition.h>
+#include "interface/EngineInterface.h"
+#include "client/PatchModel.h"
+#include "client/Serializer.h"
+#include "client/DeprecatedLoader.h"
+#include "serialisation/Loader.h"
+using std::string;
+using std::list;
+using boost::optional;
+
+using namespace Ingen::Client;
+using namespace Ingen::Serialisation;
+
+namespace Ingen {
+namespace GUI {
+
+
+/** Thread for loading patch files.
+ *
+ * This is a seperate thread so it can send all the loading message without
+ * blocking everything else, so the app can respond to the incoming events
+ * caused as a result of the patch loading, while the patch loads.
+ *
+ * Implemented as a slave with a list of closures (events) which processes
+ * all events in the (mutex protected) list each time it's whipped.
+ *
+ * \ingroup GUI
+ */
+class ThreadedLoader : public Raul::Slave
+{
+public:
+ ThreadedLoader(SharedPtr<EngineInterface> engine);
+ ~ThreadedLoader();
+
+ // FIXME: there's a pattern here....
+ // (same core interface as Loader/Serializer)
+
+ void load_patch(bool merge,
+ const string& data_base_uri,
+ const Path& data_path,
+ MetadataMap engine_data,
+ optional<Path> engine_parent,
+ optional<const string&> engine_name = optional<const string&>(),
+ optional<size_t> engine_poly = optional<size_t>());
+
+ void save_patch(SharedPtr<PatchModel> model, const string& filename, bool recursive);
+
+private:
+
+ void save_patch_event(SharedPtr<PatchModel> model, const string& filename, bool recursive);
+
+ /** Returns nothing and takes no parameters (because they have all been bound) */
+ typedef sigc::slot<void> Closure;
+
+ void _whipped();
+
+ SharedPtr<Glib::Module> _serialisation_module;
+
+ SharedPtr<EngineInterface> _engine;
+ SharedPtr<Loader> _loader;
+
+ DeprecatedLoader _deprecated_loader;
+ Serializer _serializer;
+ Raul::Mutex _mutex;
+ list<Closure> _events;
+};
+
+
+} // namespace GUI
+} // namespace Ingen
+
+#endif // LOADERRTHREAD_H
diff --git a/src/libs/gui/UploadPatchWindow.cpp b/src/libs/gui/UploadPatchWindow.cpp
new file mode 100644
index 00000000..7def01a0
--- /dev/null
+++ b/src/libs/gui/UploadPatchWindow.cpp
@@ -0,0 +1,283 @@
+/* This file is part of Ingen.
+ * Copyright (C) 2007 Dave 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 <sstream>
+#include <algorithm>
+#include <sys/types.h>
+#include <dirent.h>
+#include <boost/optional/optional.hpp>
+#include <curl/curl.h>
+#include <raul/RDFQuery.h>
+#include "interface/EngineInterface.h"
+#include "client/Serializer.h"
+#include "client/PatchModel.h"
+#include "UploadPatchWindow.h"
+#include "App.h"
+#include "Configuration.h"
+#include "ThreadedLoader.h"
+
+using boost::optional;
+using namespace Raul;
+using namespace std;
+
+namespace Ingen {
+namespace GUI {
+
+
+UploadPatchWindow::UploadPatchWindow(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& xml)
+ : Gtk::Dialog(cobject)
+ , _thread(NULL)
+ , _progress_pct(0)
+ , _response(0)
+{
+ xml->get_widget("upload_patch_symbol_entry", _symbol_entry);
+ xml->get_widget("upload_patch_short_name_entry", _short_name_entry);
+ xml->get_widget("upload_patch_progress", _upload_progress);
+ xml->get_widget("upload_patch_cancel_button", _cancel_button);
+ xml->get_widget("upload_patch_upload_button", _upload_button);
+
+
+ _symbol_entry->signal_changed().connect(sigc::mem_fun(this, &UploadPatchWindow::symbol_changed));
+ _short_name_entry->signal_changed().connect(sigc::mem_fun(this, &UploadPatchWindow::short_name_changed));
+ _cancel_button->signal_clicked().connect(sigc::mem_fun(this, &UploadPatchWindow::cancel_clicked));
+ _upload_button->signal_clicked().connect(sigc::mem_fun(this, &UploadPatchWindow::upload_clicked));
+}
+
+
+void
+UploadPatchWindow::present(SharedPtr<PatchModel> patch)
+{
+ _patch = patch;
+
+ Gtk::Window::present();
+}
+
+
+void
+UploadPatchWindow::on_show()
+{
+ Gtk::Dialog::on_show();
+
+ Raul::Atom atom = _patch->get_metadata("lv2:symbol");
+ if (atom)
+ _symbol_entry->set_text(atom.get_string());
+
+ atom = _patch->get_metadata("doap:name");
+ if (atom)
+ _short_name_entry->set_text(atom.get_string());
+}
+
+
+void
+UploadPatchWindow::on_hide()
+{
+ Gtk::Dialog::on_hide();
+
+ delete _thread;
+ _thread = NULL;
+}
+
+
+bool
+UploadPatchWindow::is_symbol(const Glib::ustring& s)
+{
+ if (s.length() == 0)
+ return false;
+
+ for (unsigned i=0; i < s.length(); ++i)
+ if ( !( (s[i] >= 'a' && s[i] <= 'z')
+ || (s[i] >= 'A' && s[i] <= 'Z')
+ || (s[i] == '_')
+ || (i > 0 && s[i] >= '0' && s[i] <= '9') ) )
+ return false;
+
+ return true;
+}
+
+
+void
+UploadPatchWindow::symbol_changed()
+{
+ _upload_button->property_sensitive() = (
+ is_symbol(_symbol_entry->get_text())
+ && _short_name_entry->get_text().length() > 0);
+}
+
+
+void
+UploadPatchWindow::short_name_changed()
+{
+ _upload_button->property_sensitive() = (
+ is_symbol(_symbol_entry->get_text())
+ && _short_name_entry->get_text().length() > 0);
+}
+
+
+size_t
+UploadThread::curl_read_cb(void *ptr, size_t size, size_t nmemb, void *data)
+{
+ assert(size == 1);
+
+ istringstream* ss = (istringstream*)data;
+
+ return ss->readsome((char*)ptr, nmemb);
+}
+
+
+int
+UploadThread::curl_progress_cb(void *thread,
+ double dltotal,
+ double dlnow,
+ double ultotal,
+ double ulnow)
+{
+ UploadThread* me = (UploadThread*)thread;
+ me->_win->set_progress(min(
+ (int)(min(ulnow, (double)me->_length) / me->_length * 100.0),
+ 99));
+ return 0;
+}
+
+
+UploadThread::UploadThread(UploadPatchWindow* win, const string& str, const string& url)
+ : Thread("Upload")
+ , _curl(NULL)
+ , _headers(NULL)
+ , _win(win)
+ , _length(str.length())
+ , _stream(str)
+ , _url(url)
+{
+ _curl = curl_easy_init();
+ _headers = curl_slist_append(NULL, "Content-type: application/x-turtle");
+
+ curl_easy_setopt(_curl, CURLOPT_URL, url.c_str());
+ curl_easy_setopt(_curl, CURLOPT_HTTPHEADER, _headers);
+ curl_easy_setopt(_curl, CURLOPT_UPLOAD, 1);
+ curl_easy_setopt(_curl, CURLOPT_READDATA, &_stream);
+ curl_easy_setopt(_curl, CURLOPT_READFUNCTION, &UploadThread::curl_read_cb);
+ curl_easy_setopt(_curl, CURLOPT_INFILESIZE, sizeof(char) * str.length());
+ curl_easy_setopt(_curl, CURLOPT_NOPROGRESS, FALSE);
+ curl_easy_setopt(_curl, CURLOPT_PROGRESSFUNCTION, &UploadThread::curl_progress_cb);
+ curl_easy_setopt(_curl, CURLOPT_PROGRESSDATA, this);
+}
+
+
+void
+UploadThread::_run()
+{
+ curl_easy_perform(_curl);
+
+ long response;
+ curl_easy_getinfo(_curl, CURLINFO_RESPONSE_CODE, &response);
+
+ printf("Server returned %ld\n", response);
+
+ _win->set_response(response);
+ _win->set_progress(100);
+
+ curl_slist_free_all(_headers);
+ curl_easy_cleanup(_curl);
+
+ _headers = NULL;
+ _curl = NULL;
+}
+
+
+bool
+UploadPatchWindow::progress_callback()
+{
+ const int progress = _progress_pct.get();
+ const int response = _response.get();
+
+ _upload_progress->set_fraction(progress / 100.0);
+
+ if (progress == 100) {
+ if (response == 200) {
+ _upload_progress->set_text("Transfer completed");
+ } else {
+ _upload_progress->set_fraction(0.0);
+ char status[4];
+ snprintf(status, 4, "%d", (unsigned)response);
+ string msg = "Transfer failed: Server returned ";
+ msg.append(status);
+ _upload_progress->set_text(msg);
+ }
+ delete _thread;
+ _thread = NULL;
+ _upload_button->set_sensitive(true);
+ return false;
+ } else {
+ return true;
+ }
+}
+
+
+void
+UploadPatchWindow::upload_clicked()
+{
+ assert(!_thread);
+
+ Glib::ustring symbol = _symbol_entry->get_text();
+ Glib::ustring short_name = _short_name_entry->get_text();
+
+ _patch->set_metadata("lv2:symbol", Atom(symbol));
+ App::instance().engine()->set_metadata(_patch->path(), "lv2:symbol", Atom(symbol));
+
+ _patch->set_metadata("doap:name", Atom(short_name));
+ App::instance().engine()->set_metadata(_patch->path(), "doap:name", Atom(short_name));
+
+ _response = 0;
+ _progress_pct = 0;
+
+ _upload_progress->set_fraction(0.0);
+ _upload_progress->set_text("");
+
+ Serializer s(*App::instance().rdf_world());
+ s.start_to_string();
+ s.serialize(_patch);
+ const string str = s.finish();
+ istringstream stream(str);
+
+ string url = "http://rdf.drobilla.net/ingen_patches/";
+ url += symbol + ".ingen.ttl";
+
+ _thread = new UploadThread(this, str, url);
+
+ _thread->start();
+
+ _upload_button->set_sensitive(false);
+
+ Glib::signal_timeout().connect(
+ sigc::mem_fun(this, &UploadPatchWindow::progress_callback), 100);
+}
+
+
+void
+UploadPatchWindow::cancel_clicked()
+{
+ if (_thread) {
+ delete _thread;
+ _thread = NULL;
+ }
+
+ hide();
+}
+
+
+} // namespace GUI
+} // namespace Ingen
diff --git a/src/libs/gui/UploadPatchWindow.h b/src/libs/gui/UploadPatchWindow.h
new file mode 100644
index 00000000..13272215
--- /dev/null
+++ b/src/libs/gui/UploadPatchWindow.h
@@ -0,0 +1,105 @@
+/* This file is part of Ingen.
+ * Copyright (C) 2007 Dave 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 UPLOADPATCHWINDOW_H
+#define UPLOADPATCHWINDOW_H
+
+#include <sstream>
+#include <libglademm/xml.h>
+#include <gtkmm.h>
+#include <curl/curl.h>
+#include <raul/SharedPtr.h>
+#include <raul/Thread.h>
+#include <raul/AtomicInt.h>
+#include "client/PatchModel.h"
+#include "client/PluginModel.h"
+using Ingen::Client::PatchModel;
+using Ingen::Client::MetadataMap;
+
+namespace Ingen {
+namespace GUI {
+
+class UploadPatchWindow;
+
+
+class UploadThread : public Raul::Thread {
+public:
+ UploadThread(UploadPatchWindow* win,
+ const string& str,
+ const string& url);
+
+private:
+ static size_t curl_read_cb(void* ptr, size_t size, size_t nmemb, void *stream);
+ static int curl_progress_cb(void* thread, double dltotal, double dlnow, double ultotal, double ulnow);
+
+ void _run();
+
+ CURL* _curl;
+ curl_slist* _headers;
+ UploadPatchWindow* _win;
+ size_t _length;
+ std::istringstream _stream;
+ std::string _url;
+};
+
+
+/* Upload patch dialog.
+ *
+ * \ingroup GUI
+ */
+class UploadPatchWindow : public Gtk::Dialog
+{
+public:
+ UploadPatchWindow(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& xml);
+
+ void present(SharedPtr<PatchModel> patch);
+
+ Gtk::ProgressBar* progress_bar() { return _upload_progress; }
+
+ void set_response(int response) { _response = response; }
+ void set_progress(int pct) { _progress_pct = pct; }
+
+private:
+ bool is_symbol(const Glib::ustring& str);
+ void symbol_changed();
+ void short_name_changed();
+ void cancel_clicked();
+ void upload_clicked();
+ void on_show();
+ void on_hide();
+ bool progress_callback();
+
+ UploadThread* _thread;
+
+ SharedPtr<PatchModel> _patch;
+
+ Raul::AtomicInt _progress_pct;
+ Raul::AtomicInt _response;
+
+ Gtk::Entry* _symbol_entry;
+ Gtk::Entry* _short_name_entry;
+ Gtk::ProgressBar* _upload_progress;
+ Gtk::Button* _cancel_button;
+ Gtk::Button* _upload_button;
+
+};
+
+
+} // namespace GUI
+} // namespace Ingen
+
+#endif // UPLOADPATCHWINDOW_H
diff --git a/src/libs/gui/WindowFactory.cpp b/src/libs/gui/WindowFactory.cpp
new file mode 100644
index 00000000..7c2e3eaa
--- /dev/null
+++ b/src/libs/gui/WindowFactory.cpp
@@ -0,0 +1,357 @@
+/* This file is part of Ingen.
+ * Copyright (C) 2007 Dave 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 "WindowFactory.h"
+#include "App.h"
+#include "PatchWindow.h"
+#include "GladeFactory.h"
+#include "NodePropertiesWindow.h"
+#include "PatchPropertiesWindow.h"
+#include "NodeControlWindow.h"
+#include "LoadPluginWindow.h"
+#include "LoadPatchWindow.h"
+#include "LoadRemotePatchWindow.h"
+#include "UploadPatchWindow.h"
+#include "LoadSubpatchWindow.h"
+#include "RenameWindow.h"
+#include "NewSubpatchWindow.h"
+
+namespace Ingen {
+namespace GUI {
+
+
+WindowFactory::WindowFactory()
+: _load_plugin_win(NULL)
+, _load_patch_win(NULL)
+, _load_remote_patch_win(NULL)
+, _upload_patch_win(NULL)
+, _new_subpatch_win(NULL)
+, _load_subpatch_win(NULL)
+, _node_properties_win(NULL)
+, _patch_properties_win(NULL)
+{
+ Glib::RefPtr<Gnome::Glade::Xml> xml = GladeFactory::new_glade_reference();
+
+ xml->get_widget_derived("load_plugin_win", _load_plugin_win);
+ xml->get_widget_derived("load_patch_win", _load_patch_win);
+ xml->get_widget_derived("load_remote_patch_win", _load_remote_patch_win);
+ xml->get_widget_derived("upload_patch_win", _upload_patch_win);
+ xml->get_widget_derived("new_subpatch_win", _new_subpatch_win);
+ xml->get_widget_derived("load_subpatch_win", _load_subpatch_win);
+ xml->get_widget_derived("node_properties_win", _node_properties_win);
+ xml->get_widget_derived("patch_properties_win", _patch_properties_win);
+}
+
+
+WindowFactory::~WindowFactory()
+{
+ for (PatchWindowMap::iterator i = _patch_windows.begin(); i != _patch_windows.end(); ++i)
+ delete i->second;
+
+ for (ControlWindowMap::iterator i = _control_windows.begin(); i != _control_windows.end(); ++i)
+ delete i->second;
+
+}
+
+
+void
+WindowFactory::clear()
+{
+ for (PatchWindowMap::iterator i = _patch_windows.begin(); i != _patch_windows.end(); ++i)
+ delete i->second;
+
+ _patch_windows.clear();
+
+ for (ControlWindowMap::iterator i = _control_windows.begin(); i != _control_windows.end(); ++i)
+ delete i->second;
+
+ _control_windows.clear();
+}
+
+
+/** Returns the number of Patch windows currently visible.
+ */
+size_t
+WindowFactory::num_open_patch_windows()
+{
+ size_t ret = 0;
+ for (PatchWindowMap::iterator i = _patch_windows.begin(); i != _patch_windows.end(); ++i)
+ if (i->second->is_visible())
+ ++ret;
+
+ return ret;
+}
+
+
+
+PatchWindow*
+WindowFactory::patch_window(SharedPtr<PatchModel> patch)
+{
+ PatchWindowMap::iterator w = _patch_windows.find(patch->path());
+
+ return (w == _patch_windows.end()) ? NULL : w->second;
+}
+
+
+NodeControlWindow*
+WindowFactory::control_window(SharedPtr<NodeModel> node)
+{
+ ControlWindowMap::iterator w = _control_windows.find(node->path());
+
+ return (w == _control_windows.end()) ? NULL : w->second;
+}
+
+
+/** Present a PatchWindow for a Patch.
+ *
+ * If @a preferred is not NULL, it will be set to display @a patch if the patch
+ * does not already have a visible window, otherwise that window will be presented and
+ * @a preferred left unmodified.
+ */
+void
+WindowFactory::present_patch(SharedPtr<PatchModel> patch, PatchWindow* preferred, SharedPtr<PatchView> view)
+{
+ assert( !view || view->patch() == patch);
+
+ PatchWindowMap::iterator w = _patch_windows.find(patch->path());
+
+ if (w != _patch_windows.end()) {
+ (*w).second->present();
+ } else if (preferred) {
+ w = _patch_windows.find(preferred->patch()->path());
+ assert((*w).second == preferred);
+
+ preferred->set_patch(patch, view);
+ _patch_windows.erase(w);
+ _patch_windows[patch->path()] = preferred;
+ preferred->present();
+
+ } else {
+ PatchWindow* win = new_patch_window(patch, view);
+ win->present();
+ }
+}
+
+
+PatchWindow*
+WindowFactory::new_patch_window(SharedPtr<PatchModel> patch, SharedPtr<PatchView> view)
+{
+ assert( !view || view->patch() == patch);
+
+ Glib::RefPtr<Gnome::Glade::Xml> xml = GladeFactory::new_glade_reference("patch_win");
+
+ PatchWindow* win = NULL;
+ xml->get_widget_derived("patch_win", win);
+ assert(win);
+
+ win->set_patch(patch, view);
+ _patch_windows[patch->path()] = win;
+
+ win->signal_delete_event().connect(sigc::bind<0>(
+ sigc::mem_fun(this, &WindowFactory::remove_patch_window), win));
+
+ return win;
+}
+
+
+bool
+WindowFactory::remove_patch_window(PatchWindow* win, GdkEventAny* ignored)
+{
+ if (_patch_windows.size() <= 1) {
+ Gtk::MessageDialog d(*win, "This is the last remaining open patch "
+ "window. Closing this window will exit Ingenuity (the engine will "
+ "remain running).\n\nAre you sure you want to quit?",
+ true, Gtk::MESSAGE_WARNING, Gtk::BUTTONS_NONE, true);
+ d.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
+ d.add_button(Gtk::Stock::QUIT, Gtk::RESPONSE_CLOSE);
+ int ret = d.run();
+ if (ret == Gtk::RESPONSE_CLOSE)
+ App::instance().quit();
+ else
+ return true;
+ }
+
+ PatchWindowMap::iterator w = _patch_windows.find(win->patch()->path());
+
+ assert((*w).second == win);
+ _patch_windows.erase(w);
+
+ delete win;
+
+ return false;
+}
+
+
+void
+WindowFactory::present_controls(SharedPtr<NodeModel> node)
+{
+ NodeControlWindow* win = control_window(node);
+
+ if (win) {
+ win->present();
+ } else {
+ win = new_control_window(node);
+ win->present();
+ }
+}
+
+
+NodeControlWindow*
+WindowFactory::new_control_window(SharedPtr<NodeModel> node)
+{
+ size_t poly = 1;
+ if (node->polyphonic())
+ poly = ((PatchModel*)node->parent().get())->poly();
+
+ NodeControlWindow* win = new NodeControlWindow(node, poly);
+
+ _control_windows[node->path()] = win;
+
+ win->signal_delete_event().connect(sigc::bind<0>(
+ sigc::mem_fun(this, &WindowFactory::remove_control_window), win));
+
+ return win;
+}
+
+
+bool
+WindowFactory::remove_control_window(NodeControlWindow* win, GdkEventAny* ignored)
+{
+ ControlWindowMap::iterator w = _control_windows.find(win->node()->path());
+
+ assert((*w).second == win);
+ _control_windows.erase(w);
+
+ delete win;
+
+ return true;
+}
+
+
+void
+WindowFactory::present_load_plugin(SharedPtr<PatchModel> patch, MetadataMap data)
+{
+ PatchWindowMap::iterator w = _patch_windows.find(patch->path());
+
+ if (w != _patch_windows.end())
+ _load_plugin_win->set_transient_for(*w->second);
+
+ _load_plugin_win->present(patch, data);
+}
+
+
+void
+WindowFactory::present_load_patch(SharedPtr<PatchModel> patch, MetadataMap data)
+{
+ PatchWindowMap::iterator w = _patch_windows.find(patch->path());
+
+ if (w != _patch_windows.end())
+ _load_patch_win->set_transient_for(*w->second);
+
+ _load_patch_win->set_merge(); // Import is the only choice
+
+ _load_patch_win->present(patch, data);
+}
+
+
+void
+WindowFactory::present_load_remote_patch(SharedPtr<PatchModel> patch, MetadataMap data)
+{
+ PatchWindowMap::iterator w = _patch_windows.find(patch->path());
+
+ if (w != _patch_windows.end())
+ _load_remote_patch_win->set_transient_for(*w->second);
+
+ _load_remote_patch_win->set_merge(); // Import is the only choice
+
+ _load_remote_patch_win->present(patch, data);
+}
+
+
+void
+WindowFactory::present_upload_patch(SharedPtr<PatchModel> patch)
+{
+ PatchWindowMap::iterator w = _patch_windows.find(patch->path());
+
+ if (w != _patch_windows.end())
+ _upload_patch_win->set_transient_for(*w->second);
+
+ _upload_patch_win->present(patch);
+}
+
+
+void
+WindowFactory::present_new_subpatch(SharedPtr<PatchModel> patch, MetadataMap data)
+{
+ PatchWindowMap::iterator w = _patch_windows.find(patch->path());
+
+ if (w != _patch_windows.end())
+ _new_subpatch_win->set_transient_for(*w->second);
+
+ _new_subpatch_win->present(patch, data);
+}
+
+
+void
+WindowFactory::present_load_subpatch(SharedPtr<PatchModel> patch, MetadataMap data)
+{
+ PatchWindowMap::iterator w = _patch_windows.find(patch->path());
+
+ if (w != _patch_windows.end())
+ _load_subpatch_win->set_transient_for(*w->second);
+
+ _load_subpatch_win->present(patch, data);
+}
+
+
+void
+WindowFactory::present_rename(SharedPtr<ObjectModel> object)
+{
+ PatchWindowMap::iterator w = _patch_windows.find(object->path());
+
+ if (w != _patch_windows.end())
+ _rename_win->set_transient_for(*w->second);
+
+ _rename_win->present(object);
+}
+
+
+void
+WindowFactory::present_properties(SharedPtr<NodeModel> node)
+{
+ SharedPtr<PatchModel> patch = PtrCast<PatchModel>(node);
+ if (patch) {
+
+ PatchWindowMap::iterator w = _patch_windows.find(patch->path());
+ if (w != _patch_windows.end())
+ _patch_properties_win->set_transient_for(*w->second);
+
+ _patch_properties_win->present(patch);
+
+ } else {
+
+ PatchWindowMap::iterator w = _patch_windows.find(node->parent()->path());
+ if (w != _patch_windows.end())
+ _node_properties_win->set_transient_for(*w->second);
+
+ _node_properties_win->present(node);
+ }
+}
+
+
+} // namespace GUI
+} // namespace Ingen
diff --git a/src/libs/gui/WindowFactory.h b/src/libs/gui/WindowFactory.h
new file mode 100644
index 00000000..c3170dce
--- /dev/null
+++ b/src/libs/gui/WindowFactory.h
@@ -0,0 +1,105 @@
+/* This file is part of Ingen.
+ * Copyright (C) 2007 Dave 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 WINDOW_FACTORY_H
+#define WINDOW_FACTORY_H
+
+#include <map>
+#include <gtkmm.h>
+#include <raul/SharedPtr.h>
+#include "client/PatchModel.h"
+#include "PatchView.h"
+
+using Ingen::Client::PatchModel;
+
+namespace Ingen {
+namespace GUI {
+
+class PatchWindow;
+class NodeControlWindow;
+class NodePropertiesWindow;
+class PatchPropertiesWindow;
+class LoadPatchWindow;
+class LoadRemotePatchWindow;
+class UploadPatchWindow;
+class RenameWindow;
+
+
+/** Manager/Factory for all windows.
+ *
+ * This serves as a nice centralized spot for all window management issues,
+ * as well as an enumeration of all windows (the goal being to reduce that
+ * number as much as possible).
+ */
+class WindowFactory {
+public:
+ WindowFactory();
+ ~WindowFactory();
+
+ size_t num_open_patch_windows();
+
+ PatchWindow* patch_window(SharedPtr<PatchModel> patch);
+ NodeControlWindow* control_window(SharedPtr<NodeModel> node);
+
+ void present_patch(SharedPtr<PatchModel> patch,
+ PatchWindow* preferred = NULL,
+ SharedPtr<PatchView> patch = SharedPtr<PatchView>());
+
+ void present_controls(SharedPtr<NodeModel> node);
+
+ void present_load_plugin(SharedPtr<PatchModel> patch, MetadataMap data = MetadataMap());
+ void present_load_patch(SharedPtr<PatchModel> patch, MetadataMap data = MetadataMap());
+ void present_load_remote_patch(SharedPtr<PatchModel> patch, MetadataMap data = MetadataMap());
+ void present_upload_patch(SharedPtr<PatchModel> patch);
+ void present_new_subpatch(SharedPtr<PatchModel> patch, MetadataMap data = MetadataMap());
+ void present_load_subpatch(SharedPtr<PatchModel> patch, MetadataMap data = MetadataMap());
+ void present_rename(SharedPtr<ObjectModel> object);
+ void present_properties(SharedPtr<NodeModel> node);
+
+ bool remove_patch_window(PatchWindow* win, GdkEventAny* ignored = NULL);
+
+ void clear();
+
+private:
+ typedef std::map<Path, PatchWindow*> PatchWindowMap;
+ typedef std::map<Path, NodeControlWindow*> ControlWindowMap;
+
+ PatchWindow* new_patch_window(SharedPtr<PatchModel> patch, SharedPtr<PatchView> view);
+
+
+ NodeControlWindow* new_control_window(SharedPtr<NodeModel> node);
+ bool remove_control_window(NodeControlWindow* win, GdkEventAny* ignored);
+
+ PatchWindowMap _patch_windows;
+ ControlWindowMap _control_windows;
+
+ LoadPluginWindow* _load_plugin_win;
+ LoadPatchWindow* _load_patch_win;
+ LoadRemotePatchWindow* _load_remote_patch_win;
+ UploadPatchWindow* _upload_patch_win;
+ NewSubpatchWindow* _new_subpatch_win;
+ LoadSubpatchWindow* _load_subpatch_win;
+ NodePropertiesWindow* _node_properties_win;
+ PatchPropertiesWindow* _patch_properties_win;
+ RenameWindow* _rename_win;
+};
+
+
+} // namespace GUI
+} // namespace Ingen
+
+#endif // WINDOW_FACTORY_H
diff --git a/src/libs/gui/cmdline.h b/src/libs/gui/cmdline.h
new file mode 100644
index 00000000..6f1f9259
--- /dev/null
+++ b/src/libs/gui/cmdline.h
@@ -0,0 +1,86 @@
+/* cmdline.h */
+
+/* File autogenerated by gengetopt version 2.19.1 */
+
+#ifndef CMDLINE_H
+#define CMDLINE_H
+
+/* If we use autoconf. */
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+#ifndef CMDLINE_PARSER_PACKAGE
+#define CMDLINE_PARSER_PACKAGE "ingen"
+#endif
+
+#ifndef CMDLINE_PARSER_VERSION
+#define CMDLINE_PARSER_VERSION VERSION
+#endif
+
+struct gengetopt_args_info
+{
+ const char *help_help; /* Print help and exit help description. */
+ const char *version_help; /* Print version and exit help description. */
+ int engine_flag; /* Run (JACK) engine (default=off). */
+ const char *engine_help; /* Run (JACK) engine help description. */
+ int engine_port_arg; /* Engine OSC port (default='16180'). */
+ char * engine_port_orig; /* Engine OSC port original value given at command line. */
+ const char *engine_port_help; /* Engine OSC port help description. */
+ char * connect_arg; /* Connect to existing engine at OSC URI (default='osc.udp://localhost:16180'). */
+ char * connect_orig; /* Connect to existing engine at OSC URI original value given at command line. */
+ const char *connect_help; /* Connect to existing engine at OSC URI help description. */
+ int gui_flag; /* Launch the GTK graphical interface (default=on). */
+ const char *gui_help; /* Launch the GTK graphical interface help description. */
+ int client_port_arg; /* Client OSC port. */
+ char * client_port_orig; /* Client OSC port original value given at command line. */
+ const char *client_port_help; /* Client OSC port help description. */
+ char * load_arg; /* Load patch. */
+ char * load_orig; /* Load patch original value given at command line. */
+ const char *load_help; /* Load patch help description. */
+ char * path_arg; /* Target path for loaded patch. */
+ char * path_orig; /* Target path for loaded patch original value given at command line. */
+ const char *path_help; /* Target path for loaded patch help description. */
+
+ int help_given ; /* Whether help was given. */
+ int version_given ; /* Whether version was given. */
+ int engine_given ; /* Whether engine was given. */
+ int engine_port_given ; /* Whether engine-port was given. */
+ int connect_given ; /* Whether connect was given. */
+ int gui_given ; /* Whether gui was given. */
+ int client_port_given ; /* Whether client-port was given. */
+ int load_given ; /* Whether load was given. */
+ int path_given ; /* Whether path was given. */
+
+} ;
+
+extern const char *gengetopt_args_info_purpose;
+extern const char *gengetopt_args_info_usage;
+extern const char *gengetopt_args_info_help[];
+
+int cmdline_parser (int argc, char * const *argv,
+ struct gengetopt_args_info *args_info);
+int cmdline_parser2 (int argc, char * const *argv,
+ struct gengetopt_args_info *args_info,
+ int override, int initialize, int check_required);
+int cmdline_parser_file_save(const char *filename,
+ struct gengetopt_args_info *args_info);
+
+void cmdline_parser_print_help(void);
+void cmdline_parser_print_version(void);
+
+void cmdline_parser_init (struct gengetopt_args_info *args_info);
+void cmdline_parser_free (struct gengetopt_args_info *args_info);
+
+int cmdline_parser_required (struct gengetopt_args_info *args_info,
+ const char *prog_name);
+
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+#endif /* CMDLINE_H */
diff --git a/src/libs/gui/ingen-icon.svg b/src/libs/gui/ingen-icon.svg
new file mode 100644
index 00000000..a15ed7e7
--- /dev/null
+++ b/src/libs/gui/ingen-icon.svg
@@ -0,0 +1,54 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://web.resource.org/cc/"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ id="svg2"
+ sodipodi:version="0.32"
+ inkscape:version="0.44.1"
+ width="64"
+ height="64"
+ version="1.0"
+ sodipodi:docbase="/home/dave/code/codesonnet/ingen/src/progs/ingenuity"
+ sodipodi:docname="ingen-icon.svg">
+ <metadata
+ id="metadata7">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <defs
+ id="defs5" />
+ <sodipodi:namedview
+ inkscape:window-height="575"
+ inkscape:window-width="853"
+ inkscape:pageshadow="2"
+ inkscape:pageopacity="0.0"
+ guidetolerance="10.0"
+ gridtolerance="10.0"
+ objecttolerance="10.0"
+ borderopacity="1.0"
+ bordercolor="#666666"
+ pagecolor="#ffffff"
+ id="base"
+ inkscape:zoom="5.921875"
+ inkscape:cx="32"
+ inkscape:cy="31.915567"
+ inkscape:window-x="569"
+ inkscape:window-y="167"
+ inkscape:current-layer="svg2" />
+ <path
+ style="fill:black"
+ d="M 7.3978252,58.655244 C 3.8646112,57.233665 3.1688652,52.819494 3.1688652,31.824538 C 3.1688652,10.379073 3.8359622,6.4337347 7.7136682,4.9457196 C 9.8253032,4.135409 54.512427,4.135409 56.624063,4.9457196 C 60.501768,6.4337347 61.168865,10.379073 61.168865,31.824538 C 61.168865,53.270003 60.501768,57.215342 56.624063,58.703357 C 54.702346,59.440788 9.2397932,59.396354 7.3978252,58.655244 z M 55.234606,55.289354 C 57.058692,54.313133 57.168865,52.976602 57.168865,31.824538 C 57.168865,10.672474 57.058692,9.3359433 55.234606,8.3597221 C 52.64791,6.9753642 11.68982,6.9753642 9.1031242,8.3597221 C 7.2790962,9.3359123 7.1688652,10.672309 7.1688652,31.810032 C 7.1688652,52.102806 7.3345622,54.321667 8.9188652,55.244439 C 11.279929,56.619633 52.673952,56.659775 55.234606,55.289354 z M 37.168865,48.891926 C 37.168865,47.331323 36.446365,46.230001 35.153333,45.819608 C 34.04479,45.46777 31.607131,42.787082 29.736313,39.862522 C 25.549347,33.31724 24.555639,32.64737 23.756486,35.831449 C 23.178349,38.13493 22.684027,38.324538 17.256802,38.324538 C 10.799575,38.324538 10.168865,37.746144 10.168865,31.824538 C 10.168865,25.902932 10.799575,25.324538 17.256802,25.324538 C 22.684027,25.324538 23.178349,25.514146 23.756486,27.817627 C 24.555639,31.001706 25.549347,30.331836 29.736313,23.786554 C 31.607131,20.861994 34.04479,18.181306 35.153333,17.829468 C 36.446365,17.419075 37.168865,16.317753 37.168865,14.75715 C 37.168865,12.479647 37.514343,12.324538 42.587078,12.324538 C 50.54284,12.324538 51.168865,12.761099 51.168865,18.309082 C 51.168865,24.371452 49.795116,25.518459 43.000041,25.129613 C 38.343077,24.86312 37.628453,24.53999 37.349314,22.574538 C 36.852569,19.076888 35.536131,19.853195 31.414126,26.074538 L 27.604413,31.824538 L 31.418918,37.574538 C 35.547694,43.798272 36.853067,44.568677 37.349314,41.074538 C 37.628453,39.109086 38.343077,38.785956 43.000041,38.519463 C 49.795116,38.130617 51.168865,39.277624 51.168865,45.339994 C 51.168865,50.887977 50.54284,51.324538 42.587078,51.324538 C 37.514343,51.324538 37.168865,51.169429 37.168865,48.891926 z "
+ id="path1873" />
+</svg>
diff --git a/src/libs/gui/ingen.svg b/src/libs/gui/ingen.svg
new file mode 100644
index 00000000..a15ed7e7
--- /dev/null
+++ b/src/libs/gui/ingen.svg
@@ -0,0 +1,54 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://web.resource.org/cc/"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ id="svg2"
+ sodipodi:version="0.32"
+ inkscape:version="0.44.1"
+ width="64"
+ height="64"
+ version="1.0"
+ sodipodi:docbase="/home/dave/code/codesonnet/ingen/src/progs/ingenuity"
+ sodipodi:docname="ingen-icon.svg">
+ <metadata
+ id="metadata7">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <defs
+ id="defs5" />
+ <sodipodi:namedview
+ inkscape:window-height="575"
+ inkscape:window-width="853"
+ inkscape:pageshadow="2"
+ inkscape:pageopacity="0.0"
+ guidetolerance="10.0"
+ gridtolerance="10.0"
+ objecttolerance="10.0"
+ borderopacity="1.0"
+ bordercolor="#666666"
+ pagecolor="#ffffff"
+ id="base"
+ inkscape:zoom="5.921875"
+ inkscape:cx="32"
+ inkscape:cy="31.915567"
+ inkscape:window-x="569"
+ inkscape:window-y="167"
+ inkscape:current-layer="svg2" />
+ <path
+ style="fill:black"
+ d="M 7.3978252,58.655244 C 3.8646112,57.233665 3.1688652,52.819494 3.1688652,31.824538 C 3.1688652,10.379073 3.8359622,6.4337347 7.7136682,4.9457196 C 9.8253032,4.135409 54.512427,4.135409 56.624063,4.9457196 C 60.501768,6.4337347 61.168865,10.379073 61.168865,31.824538 C 61.168865,53.270003 60.501768,57.215342 56.624063,58.703357 C 54.702346,59.440788 9.2397932,59.396354 7.3978252,58.655244 z M 55.234606,55.289354 C 57.058692,54.313133 57.168865,52.976602 57.168865,31.824538 C 57.168865,10.672474 57.058692,9.3359433 55.234606,8.3597221 C 52.64791,6.9753642 11.68982,6.9753642 9.1031242,8.3597221 C 7.2790962,9.3359123 7.1688652,10.672309 7.1688652,31.810032 C 7.1688652,52.102806 7.3345622,54.321667 8.9188652,55.244439 C 11.279929,56.619633 52.673952,56.659775 55.234606,55.289354 z M 37.168865,48.891926 C 37.168865,47.331323 36.446365,46.230001 35.153333,45.819608 C 34.04479,45.46777 31.607131,42.787082 29.736313,39.862522 C 25.549347,33.31724 24.555639,32.64737 23.756486,35.831449 C 23.178349,38.13493 22.684027,38.324538 17.256802,38.324538 C 10.799575,38.324538 10.168865,37.746144 10.168865,31.824538 C 10.168865,25.902932 10.799575,25.324538 17.256802,25.324538 C 22.684027,25.324538 23.178349,25.514146 23.756486,27.817627 C 24.555639,31.001706 25.549347,30.331836 29.736313,23.786554 C 31.607131,20.861994 34.04479,18.181306 35.153333,17.829468 C 36.446365,17.419075 37.168865,16.317753 37.168865,14.75715 C 37.168865,12.479647 37.514343,12.324538 42.587078,12.324538 C 50.54284,12.324538 51.168865,12.761099 51.168865,18.309082 C 51.168865,24.371452 49.795116,25.518459 43.000041,25.129613 C 38.343077,24.86312 37.628453,24.53999 37.349314,22.574538 C 36.852569,19.076888 35.536131,19.853195 31.414126,26.074538 L 27.604413,31.824538 L 31.418918,37.574538 C 35.547694,43.798272 36.853067,44.568677 37.349314,41.074538 C 37.628453,39.109086 38.343077,38.785956 43.000041,38.519463 C 49.795116,38.130617 51.168865,39.277624 51.168865,45.339994 C 51.168865,50.887977 50.54284,51.324538 42.587078,51.324538 C 37.514343,51.324538 37.168865,51.169429 37.168865,48.891926 z "
+ id="path1873" />
+</svg>
diff --git a/src/libs/gui/ingen_gui.glade b/src/libs/gui/ingen_gui.glade
new file mode 100644
index 00000000..6eaf300e
--- /dev/null
+++ b/src/libs/gui/ingen_gui.glade
@@ -0,0 +1,3044 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!DOCTYPE glade-interface SYSTEM "glade-2.0.dtd">
+<!--*- mode: xml -*-->
+<glade-interface>
+ <widget class="GtkWindow" id="patch_win">
+ <property name="title" translatable="yes">Ingenuity</property>
+ <property name="default_width">640</property>
+ <property name="default_height">480</property>
+ <property name="icon">ingen.svg</property>
+ <child>
+ <widget class="GtkVBox" id="patch_win_vbox">
+ <property name="visible">True</property>
+ <child>
+ <widget class="GtkMenuBar" id="menubar">
+ <property name="visible">True</property>
+ <child>
+ <widget class="GtkMenuItem" id="patch_file_menu">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_File</property>
+ <property name="use_underline">True</property>
+ <child>
+ <widget class="GtkMenu" id="patch_file_menu_menu">
+ <child>
+ <widget class="GtkImageMenuItem" id="patch_import_menuitem">
+ <property name="visible">True</property>
+ <property name="tooltip" translatable="yes">Load a patch into the current patch (merge with existing contents).</property>
+ <property name="label" translatable="yes">_Import...</property>
+ <property name="use_underline">True</property>
+ <signal name="activate" handler="on_patch_import_menuitem_activate"/>
+ <accelerator key="I" modifiers="GDK_CONTROL_MASK" signal="activate"/>
+ <child internal-child="image">
+ <widget class="GtkImage" id="image2123">
+ <property name="visible">True</property>
+ <property name="stock">gtk-open</property>
+ <property name="icon_size">1</property>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ <child>
+ <widget class="GtkImageMenuItem" id="patch_import_location_menuitem">
+ <property name="visible">True</property>
+ <property name="tooltip" translatable="yes">Import a patch from a URI</property>
+ <property name="label" translatable="yes">Import _Location...</property>
+ <property name="use_underline">True</property>
+ <signal name="activate" handler="on_import_location1_activate"/>
+ <accelerator key="L" modifiers="GDK_CONTROL_MASK" signal="activate"/>
+ <child internal-child="image">
+ <widget class="GtkImage" id="image2124">
+ <property name="visible">True</property>
+ <property name="stock">gtk-open</property>
+ <property name="icon_size">1</property>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ <child>
+ <widget class="GtkSeparatorMenuItem" id="separator9">
+ <property name="visible">True</property>
+ </widget>
+ </child>
+ <child>
+ <widget class="GtkImageMenuItem" id="patch_save_menuitem">
+ <property name="visible">True</property>
+ <property name="tooltip" translatable="yes">Save this patch</property>
+ <property name="label">gtk-save</property>
+ <property name="use_underline">True</property>
+ <property name="use_stock">True</property>
+ <signal name="activate" handler="on_file_save_patch_menuitem_activate"/>
+ </widget>
+ </child>
+ <child>
+ <widget class="GtkImageMenuItem" id="patch_save_as_menuitem">
+ <property name="visible">True</property>
+ <property name="tooltip" translatable="yes">Save this patch to a specific file name</property>
+ <property name="label" translatable="yes">Save _As...</property>
+ <property name="use_underline">True</property>
+ <signal name="activate" handler="on_patch_save_as_menuitem_activate"/>
+ <child internal-child="image">
+ <widget class="GtkImage" id="image2125">
+ <property name="visible">True</property>
+ <property name="stock">gtk-save-as</property>
+ <property name="icon_size">1</property>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ <child>
+ <widget class="GtkImageMenuItem" id="patch_upload_menuitem">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_Upload...</property>
+ <property name="use_underline">True</property>
+ <signal name="activate" handler="on_patch_upload_menuitem_activate"/>
+ <accelerator key="U" modifiers="GDK_CONTROL_MASK" signal="activate"/>
+ <child internal-child="image">
+ <widget class="GtkImage" id="image2126">
+ <property name="visible">True</property>
+ <property name="stock">gtk-network</property>
+ <property name="icon_size">1</property>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ <child>
+ <widget class="GtkSeparatorMenuItem" id="separator10">
+ <property name="visible">True</property>
+ </widget>
+ </child>
+ <child>
+ <widget class="GtkImageMenuItem" id="patch_configuration_menuitem">
+ <property name="visible">True</property>
+ <property name="tooltip" translatable="yes">Configure OmGtk</property>
+ <property name="label" translatable="yes">Confi_guration...</property>
+ <property name="use_underline">True</property>
+ <signal name="activate" handler="on_patch_configuration_menuitem_activate"/>
+ <child internal-child="image">
+ <widget class="GtkImage" id="image2127">
+ <property name="visible">True</property>
+ <property name="stock">gtk-preferences</property>
+ <property name="icon_size">1</property>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ <child>
+ <widget class="GtkSeparatorMenuItem" id="separator11">
+ <property name="visible">True</property>
+ </widget>
+ </child>
+ <child>
+ <widget class="GtkImageMenuItem" id="patch_close_menuitem">
+ <property name="visible">True</property>
+ <property name="tooltip" translatable="yes">Close this window (patch will not be destroyed)</property>
+ <property name="label">gtk-close</property>
+ <property name="use_underline">True</property>
+ <property name="use_stock">True</property>
+ <signal name="activate" handler="on_patch_file_close_menuitem_activate"/>
+ </widget>
+ </child>
+ <child>
+ <widget class="GtkSeparatorMenuItem" id="separator6">
+ <property name="visible">True</property>
+ </widget>
+ </child>
+ <child>
+ <widget class="GtkImageMenuItem" id="patch_quit_menuitem">
+ <property name="visible">True</property>
+ <property name="tooltip" translatable="yes">Quit Ingenuity (engine may continue running)</property>
+ <property name="label">gtk-quit</property>
+ <property name="use_underline">True</property>
+ <property name="use_stock">True</property>
+ <signal name="activate" handler="on_patch_file_quit_nokill_menuitem_activate"/>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ <child>
+ <widget class="GtkMenuItem" id="edit2">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_Edit</property>
+ <property name="use_underline">True</property>
+ <child>
+ <widget class="GtkMenu" id="edit2_menu">
+ <child>
+ <widget class="GtkImageMenuItem" id="patch_clear_menuitem">
+ <property name="visible">True</property>
+ <property name="tooltip" translatable="yes">Remove all objects from patch</property>
+ <property name="label">gtk-clear</property>
+ <property name="use_underline">True</property>
+ <property name="use_stock">True</property>
+ <signal name="activate" handler="on_patch_clear_menuitem_activate"/>
+ </widget>
+ </child>
+ <child>
+ <widget class="GtkSeparatorMenuItem" id="separator99">
+ <property name="visible">True</property>
+ </widget>
+ </child>
+ <child>
+ <widget class="GtkImageMenuItem" id="patch_cut_menuitem">
+ <property name="visible">True</property>
+ <property name="sensitive">False</property>
+ <property name="label">gtk-cut</property>
+ <property name="use_underline">True</property>
+ <property name="use_stock">True</property>
+ <signal name="activate" handler="on_patch_cut_menuitem_activate"/>
+ </widget>
+ </child>
+ <child>
+ <widget class="GtkImageMenuItem" id="patch_copy_menuitem">
+ <property name="visible">True</property>
+ <property name="label">gtk-copy</property>
+ <property name="use_underline">True</property>
+ <property name="use_stock">True</property>
+ <signal name="activate" handler="on_patch_copy_menuitem_activate"/>
+ </widget>
+ </child>
+ <child>
+ <widget class="GtkImageMenuItem" id="patch_paste_menuitem">
+ <property name="visible">True</property>
+ <property name="sensitive">False</property>
+ <property name="label">gtk-paste</property>
+ <property name="use_underline">True</property>
+ <property name="use_stock">True</property>
+ <signal name="activate" handler="on_patch_paste_menuitem_activate"/>
+ </widget>
+ </child>
+ <child>
+ <widget class="GtkImageMenuItem" id="patch_delete_menuitem">
+ <property name="visible">True</property>
+ <property name="label">gtk-delete</property>
+ <property name="use_underline">True</property>
+ <property name="use_stock">True</property>
+ <signal name="activate" handler="on_patch_delete_menuitem_activate"/>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ <child>
+ <widget class="GtkMenuItem" id="patch_patch_menu">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_Patch</property>
+ <property name="use_underline">True</property>
+ <child>
+ <widget class="GtkMenu" id="patch_patch_menu_menu">
+ <child>
+ <widget class="GtkImageMenuItem" id="patch_fullscreen_menuitem">
+ <property name="visible">True</property>
+ <property name="label">gtk-fullscreen</property>
+ <property name="use_underline">True</property>
+ <property name="use_stock">True</property>
+ <signal name="activate" handler="patch_fullscreen_menuitem"/>
+ </widget>
+ </child>
+ <child>
+ <widget class="GtkSeparatorMenuItem" id="separator12">
+ <property name="visible">True</property>
+ </widget>
+ </child>
+ <child>
+ <widget class="GtkImageMenuItem" id="patch_arrange_menuitem">
+ <property name="visible">True</property>
+ <property name="tooltip" translatable="yes">Automatically arrange canvas</property>
+ <property name="label" translatable="yes">_Arrange</property>
+ <property name="use_underline">True</property>
+ <accelerator key="G" modifiers="GDK_CONTROL_MASK" signal="activate"/>
+ <child internal-child="image">
+ <widget class="GtkImage" id="image2128">
+ <property name="visible">True</property>
+ <property name="stock">gtk-sort-ascending</property>
+ <property name="icon_size">1</property>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ <child>
+ <widget class="GtkImageMenuItem" id="patch_view_control_window_menuitem">
+ <property name="visible">True</property>
+ <property name="tooltip" translatable="yes">View/Edit controls for this patch</property>
+ <property name="label" translatable="yes">_Controls...</property>
+ <property name="use_underline">True</property>
+ <signal name="activate" handler="on_patch_view_control_window_menuitem_activate"/>
+ <accelerator key="C" modifiers="GDK_CONTROL_MASK" signal="activate"/>
+ <child internal-child="image">
+ <widget class="GtkImage" id="image2129">
+ <property name="visible">True</property>
+ <property name="stock">gtk-preferences</property>
+ <property name="icon_size">1</property>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ <child>
+ <widget class="GtkImageMenuItem" id="patch_properties_menuitem">
+ <property name="visible">True</property>
+ <property name="tooltip" translatable="yes">View/Edit properties for this patch</property>
+ <property name="label" translatable="yes">_Properties...</property>
+ <property name="use_underline">True</property>
+ <signal name="activate" handler="on_patch_properties_menuitem_activate"/>
+ <accelerator key="P" modifiers="GDK_CONTROL_MASK" signal="activate"/>
+ <child internal-child="image">
+ <widget class="GtkImage" id="image2130">
+ <property name="visible">True</property>
+ <property name="stock">gtk-properties</property>
+ <property name="icon_size">1</property>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ <child>
+ <widget class="GtkImageMenuItem" id="patch_destroy_menuitem">
+ <property name="visible">True</property>
+ <property name="tooltip" translatable="yes">Destoy this patch (remove it from the engine)</property>
+ <property name="label" translatable="yes">_Destroy</property>
+ <property name="use_underline">True</property>
+ <signal name="activate" handler="on_patch_destroy_menuitem_activate"/>
+ <child internal-child="image">
+ <widget class="GtkImage" id="image2131">
+ <property name="visible">True</property>
+ <property name="stock">gtk-delete</property>
+ <property name="icon_size">1</property>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ <child>
+ <widget class="GtkMenuItem" id="view1">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_Windows</property>
+ <property name="use_underline">True</property>
+ <signal name="activate" handler="on_view1_activate"/>
+ <child>
+ <widget class="GtkMenu" id="view1_menu">
+ <child>
+ <widget class="GtkImageMenuItem" id="patch_view_engine_window_menuitem">
+ <property name="visible">True</property>
+ <property name="tooltip" translatable="yes">Connect to, Disconnect from, or Launch Engine</property>
+ <property name="label" translatable="yes">_Engine...</property>
+ <property name="use_underline">True</property>
+ <signal name="activate" handler="on_patch_view_engine_window_menuitem_activate"/>
+ <accelerator key="E" modifiers="GDK_CONTROL_MASK" signal="activate"/>
+ <child internal-child="image">
+ <widget class="GtkImage" id="image2132">
+ <property name="visible">True</property>
+ <property name="stock">gtk-execute</property>
+ <property name="icon_size">1</property>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ <child>
+ <widget class="GtkImageMenuItem" id="patch_view_patch_tree_window_menuitem">
+ <property name="visible">True</property>
+ <property name="tooltip" translatable="yes">View all patches in the engine as a heirarchial list</property>
+ <property name="label" translatable="yes">_Patch Tree...</property>
+ <property name="use_underline">True</property>
+ <signal name="activate" handler="on_patch_view_tree_window_menuitem_activate"/>
+ <accelerator key="T" modifiers="GDK_CONTROL_MASK" signal="activate"/>
+ <child internal-child="image">
+ <widget class="GtkImage" id="image2133">
+ <property name="visible">True</property>
+ <property name="stock">gtk-index</property>
+ <property name="icon_size">1</property>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ <child>
+ <widget class="GtkImageMenuItem" id="patch_view_messages_window_menuitem">
+ <property name="visible">True</property>
+ <property name="tooltip" translatable="yes">View error messages from the engine</property>
+ <property name="label" translatable="yes">_Messages...</property>
+ <property name="use_underline">True</property>
+ <signal name="activate" handler="on_patch_view_messages_window_menuitem_activate"/>
+ <accelerator key="M" modifiers="GDK_CONTROL_MASK" signal="activate"/>
+ <child internal-child="image">
+ <widget class="GtkImage" id="image2134">
+ <property name="visible">True</property>
+ <property name="stock">gtk-info</property>
+ <property name="icon_size">1</property>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ <child>
+ <widget class="GtkMenuItem" id="help_menu">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_Help</property>
+ <property name="use_underline">True</property>
+ <signal name="activate" handler="on_help_menu_activate"/>
+ <child>
+ <widget class="GtkMenu" id="help_menu_menu">
+ <child>
+ <widget class="GtkImageMenuItem" id="right-click_the_canvas_to_add_objects1">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Right-click the canvas to add objects</property>
+ <property name="use_underline">True</property>
+ <signal name="activate" handler="on_right-click_the_canvas_to_add_objects1_activate"/>
+ <child internal-child="image">
+ <widget class="GtkImage" id="image2135">
+ <property name="visible">True</property>
+ <property name="stock">gtk-info</property>
+ <property name="icon_size">1</property>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ <child>
+ <widget class="GtkSeparatorMenuItem" id="separator13">
+ <property name="visible">True</property>
+ </widget>
+ </child>
+ <child>
+ <widget class="GtkImageMenuItem" id="patch_help_about_menuitem">
+ <property name="visible">True</property>
+ <property name="label">gtk-about</property>
+ <property name="use_underline">True</property>
+ <property name="use_stock">True</property>
+ <signal name="activate" handler="on_about1_activate"/>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkScrolledWindow" id="patch_win_scrolledwin">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hscrollbar_policy">GTK_POLICY_NEVER</property>
+ <property name="vscrollbar_policy">GTK_POLICY_NEVER</property>
+ <child>
+ <widget class="GtkViewport" id="patch_win_viewport">
+ <property name="visible">True</property>
+ <property name="shadow_type">GTK_SHADOW_NONE</property>
+ <child>
+ <placeholder/>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ <widget class="GtkWindow" id="load_plugin_win">
+ <property name="border_width">8</property>
+ <property name="title" translatable="yes">Load Plugin</property>
+ <property name="window_position">GTK_WIN_POS_CENTER_ON_PARENT</property>
+ <property name="default_width">640</property>
+ <property name="default_height">480</property>
+ <property name="icon">ingen.svg</property>
+ <child>
+ <widget class="GtkVBox" id="vbox9">
+ <property name="visible">True</property>
+ <property name="spacing">1</property>
+ <child>
+ <widget class="GtkScrolledWindow" id="scrolledwindow3">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="border_width">2</property>
+ <property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+ <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+ <child>
+ <widget class="GtkTreeView" id="load_plugin_plugins_treeview">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="tooltip" translatable="yes">All plugins available for loading</property>
+ <property name="border_width">2</property>
+ <property name="reorderable">True</property>
+ <property name="rules_hint">True</property>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ <child>
+ <widget class="GtkTable" id="table16">
+ <property name="visible">True</property>
+ <property name="n_rows">3</property>
+ <property name="n_columns">3</property>
+ <property name="row_spacing">12</property>
+ <child>
+ <widget class="GtkButton" id="load_plugin_clear_button">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="tooltip" translatable="yes">Clear filter text (show all plugins)</property>
+ <property name="label">gtk-clear</property>
+ <property name="use_stock">True</property>
+ </widget>
+ <packing>
+ <property name="left_attach">2</property>
+ <property name="right_attach">3</property>
+ <property name="x_options">GTK_FILL</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkComboBox" id="load_plugin_filter_combo">
+ <property name="visible">True</property>
+ <property name="items" translatable="yes">Name contains: </property>
+ </widget>
+ <packing>
+ <property name="x_options">GTK_FILL</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkEntry" id="load_plugin_search_entry">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="has_focus">True</property>
+ <property name="tooltip" translatable="yes">Search string to filter plugin list</property>
+ <property name="invisible_char">*</property>
+ </widget>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="x_padding">6</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkButton" id="load_plugin_add_button">
+ <property name="visible">True</property>
+ <property name="sensitive">False</property>
+ <property name="can_focus">True</property>
+ <property name="tooltip" translatable="yes">Add selected plugin to patch</property>
+ <property name="label">gtk-add</property>
+ <property name="use_stock">True</property>
+ </widget>
+ <packing>
+ <property name="left_attach">2</property>
+ <property name="right_attach">3</property>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ <property name="x_options">GTK_FILL</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkHBox" id="hbox63">
+ <property name="visible">True</property>
+ <child>
+ <widget class="GtkEntry" id="load_plugin_name_entry">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="tooltip" translatable="yes">Name of new Module</property>
+ <property name="invisible_char">*</property>
+ </widget>
+ </child>
+ <child>
+ <widget class="GtkCheckButton" id="load_plugin_polyphonic_checkbutton">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="label" translatable="yes">Polyphonic</property>
+ <property name="use_underline">True</property>
+ <property name="active">True</property>
+ <property name="draw_indicator">True</property>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="padding">8</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ <property name="x_options">GTK_FILL</property>
+ <property name="y_options">GTK_FILL</property>
+ <property name="x_padding">6</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkHSeparator" id="hseparator3">
+ <property name="visible">True</property>
+ </widget>
+ <packing>
+ <property name="left_attach">2</property>
+ <property name="right_attach">3</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options">GTK_FILL</property>
+ <property name="y_options">GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkHSeparator" id="hseparator2">
+ <property name="visible">True</property>
+ </widget>
+ <packing>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options">GTK_FILL</property>
+ <property name="y_options">GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkHSeparator" id="hseparator1">
+ <property name="visible">True</property>
+ </widget>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options">GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkLabel" id="label66">
+ <property name="visible">True</property>
+ <property name="xalign">1</property>
+ <property name="label" translatable="yes">Node Name:</property>
+ <property name="use_markup">True</property>
+ </widget>
+ <packing>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ <property name="x_options">GTK_FILL</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ <widget class="GtkWindow" id="new_subpatch_win">
+ <property name="width_request">320</property>
+ <property name="border_width">8</property>
+ <property name="title" translatable="yes">Create Subpatch</property>
+ <property name="window_position">GTK_WIN_POS_CENTER_ON_PARENT</property>
+ <property name="icon">ingen.svg</property>
+ <property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property>
+ <child>
+ <widget class="GtkVBox" id="vbox4">
+ <property name="visible">True</property>
+ <child>
+ <widget class="GtkTable" id="table1">
+ <property name="visible">True</property>
+ <property name="n_rows">2</property>
+ <property name="n_columns">2</property>
+ <child>
+ <widget class="GtkEntry" id="new_subpatch_name_entry">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="has_focus">True</property>
+ <property name="invisible_char">*</property>
+ <property name="activates_default">True</property>
+ </widget>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="y_options"></property>
+ <property name="y_padding">4</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkSpinButton" id="new_subpatch_polyphony_spinbutton">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="adjustment">1 0 100 1 10 10</property>
+ <property name="climb_rate">1</property>
+ </widget>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options">GTK_FILL</property>
+ <property name="y_options"></property>
+ <property name="y_padding">4</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkLabel" id="label9">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">Polyphony: </property>
+ </widget>
+ <packing>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options">GTK_FILL</property>
+ <property name="y_options">GTK_EXPAND</property>
+ <property name="x_padding">5</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkLabel" id="label8">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">Name: </property>
+ </widget>
+ <packing>
+ <property name="x_options">GTK_FILL</property>
+ <property name="y_options">GTK_EXPAND</property>
+ <property name="x_padding">5</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+ <child>
+ <widget class="GtkLabel" id="new_subpatch_message_label">
+ <property name="visible">True</property>
+ <property name="wrap">True</property>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkHButtonBox" id="hbuttonbox5">
+ <property name="visible">True</property>
+ <property name="spacing">4</property>
+ <property name="layout_style">GTK_BUTTONBOX_END</property>
+ <child>
+ <widget class="GtkButton" id="new_subpatch_cancel_button">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="label">gtk-cancel</property>
+ <property name="use_stock">True</property>
+ </widget>
+ </child>
+ <child>
+ <widget class="GtkButton" id="new_subpatch_ok_button">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="can_default">True</property>
+ <property name="has_default">True</property>
+ <child>
+ <widget class="GtkAlignment" id="alignment2">
+ <property name="visible">True</property>
+ <property name="xscale">0</property>
+ <property name="yscale">0</property>
+ <child>
+ <widget class="GtkHBox" id="hbox54">
+ <property name="visible">True</property>
+ <property name="spacing">2</property>
+ <child>
+ <widget class="GtkImage" id="image259">
+ <property name="visible">True</property>
+ <property name="stock">gtk-ok</property>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkLabel" id="label96">
+ <property name="visible">True</property>
+ <property name="label">Create</property>
+ <property name="use_underline">True</property>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ <widget class="GtkFileChooserDialog" id="load_subpatch_win">
+ <property name="title" translatable="yes">Load Subpatch</property>
+ <property name="window_position">GTK_WIN_POS_CENTER_ON_PARENT</property>
+ <property name="icon">ingen.svg</property>
+ <property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property>
+ <child internal-child="vbox">
+ <widget class="GtkVBox" id="dialog-vbox1">
+ <property name="visible">True</property>
+ <property name="spacing">24</property>
+ <child>
+ <widget class="GtkTable" id="table6">
+ <property name="visible">True</property>
+ <property name="n_rows">2</property>
+ <property name="n_columns">4</property>
+ <property name="column_spacing">12</property>
+ <property name="row_spacing">4</property>
+ <child>
+ <widget class="GtkHBox" id="hbox45">
+ <property name="visible">True</property>
+ <child>
+ <widget class="GtkRadioButton" id="load_subpatch_name_from_user_radio">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="tooltip" translatable="yes">Specify the name for the new patch</property>
+ <property name="label" translatable="yes">Specify: </property>
+ <property name="use_underline">True</property>
+ <property name="draw_indicator">True</property>
+ <property name="group">load_subpatch_name_from_file_radio</property>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkEntry" id="load_subpatch_name_entry">
+ <property name="visible">True</property>
+ <property name="sensitive">False</property>
+ <property name="can_focus">True</property>
+ <property name="tooltip" translatable="yes">Specify the name for the new patch</property>
+ <property name="invisible_char">*</property>
+ <property name="activates_default">True</property>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="left_attach">3</property>
+ <property name="right_attach">4</property>
+ <property name="y_options">GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkLabel" id="label104">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ </widget>
+ <packing>
+ <property name="left_attach">2</property>
+ <property name="right_attach">3</property>
+ <property name="x_options">GTK_FILL</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkRadioButton" id="load_subpatch_poly_from_parent_radio">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="tooltip" translatable="yes">Set polyphony to the same value as the parent (containing) patch</property>
+ <property name="label" translatable="yes">Same as parent (?)</property>
+ <property name="use_underline">True</property>
+ <property name="draw_indicator">True</property>
+ <property name="group">load_subpatch_poly_from_file_radio</property>
+ </widget>
+ <packing>
+ <property name="left_attach">2</property>
+ <property name="right_attach">3</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options">GTK_FILL</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkHBox" id="hbox46">
+ <property name="visible">True</property>
+ <child>
+ <widget class="GtkRadioButton" id="load_subpatch_poly_from_user_radio">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="tooltip" translatable="yes">Specify a custom polyphony value for new patch</property>
+ <property name="label" translatable="yes">Specify: </property>
+ <property name="use_underline">True</property>
+ <property name="draw_indicator">True</property>
+ <property name="group">load_subpatch_poly_from_file_radio</property>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkSpinButton" id="load_subpatch_poly_spinbutton">
+ <property name="visible">True</property>
+ <property name="sensitive">False</property>
+ <property name="can_focus">True</property>
+ <property name="tooltip" translatable="yes">Specify a custom polyphony value for new patch</property>
+ <property name="adjustment">1 0 1000 1 10 10</property>
+ <property name="climb_rate">1</property>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="left_attach">3</property>
+ <property name="right_attach">4</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options">GTK_FILL</property>
+ <property name="y_options">GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkRadioButton" id="load_subpatch_poly_from_file_radio">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="tooltip" translatable="yes">Use the polyphony value stored in the patch file</property>
+ <property name="label" translatable="yes">Load from file</property>
+ <property name="use_underline">True</property>
+ <property name="active">True</property>
+ <property name="draw_indicator">True</property>
+ </widget>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options">GTK_FILL</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkRadioButton" id="load_subpatch_name_from_file_radio">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="tooltip" translatable="yes">Use the name stored in the patch file</property>
+ <property name="label" translatable="yes">Load from file</property>
+ <property name="use_underline">True</property>
+ <property name="active">True</property>
+ <property name="draw_indicator">True</property>
+ </widget>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="x_options">GTK_FILL</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkLabel" id="label80">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">&lt;b&gt;Polyphony: &lt;/b&gt;</property>
+ <property name="use_markup">True</property>
+ </widget>
+ <packing>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options">GTK_FILL</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkLabel" id="label79">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">&lt;b&gt;Name: &lt;/b&gt;</property>
+ <property name="use_markup">True</property>
+ </widget>
+ <packing>
+ <property name="x_options">GTK_FILL</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ <child internal-child="action_area">
+ <widget class="GtkHButtonBox" id="dialog-action_area1">
+ <property name="visible">True</property>
+ <property name="layout_style">GTK_BUTTONBOX_END</property>
+ <child>
+ <widget class="GtkButton" id="load_subpatch_cancel_button">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="can_default">True</property>
+ <property name="label">gtk-cancel</property>
+ <property name="use_stock">True</property>
+ <property name="response_id">-6</property>
+ </widget>
+ </child>
+ <child>
+ <widget class="GtkButton" id="load_subpatch_ok_button">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="can_default">True</property>
+ <property name="has_default">True</property>
+ <property name="label">gtk-open</property>
+ <property name="use_stock">True</property>
+ <property name="response_id">-5</property>
+ </widget>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="pack_type">GTK_PACK_END</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ <widget class="GtkFileChooserDialog" id="load_patch_win">
+ <property name="title" translatable="yes">Load Patch</property>
+ <property name="window_position">GTK_WIN_POS_CENTER_ON_PARENT</property>
+ <property name="icon">ingen.svg</property>
+ <property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property>
+ <child internal-child="vbox">
+ <widget class="GtkVBox" id="vbox11">
+ <property name="spacing">24</property>
+ <child>
+ <widget class="GtkTable" id="table14">
+ <property name="visible">True</property>
+ <property name="n_rows">1</property>
+ <property name="n_columns">4</property>
+ <property name="column_spacing">12</property>
+ <property name="row_spacing">4</property>
+ <child>
+ <widget class="GtkHBox" id="hbox58">
+ <property name="visible">True</property>
+ <child>
+ <widget class="GtkRadioButton" id="load_patch_poly_from_user_radio">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="label" translatable="yes">Specify:</property>
+ <property name="use_underline">True</property>
+ <property name="draw_indicator">True</property>
+ <property name="group">load_patch_poly_from_current_radio</property>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkSpinButton" id="load_patch_poly_spinbutton">
+ <property name="visible">True</property>
+ <property name="sensitive">False</property>
+ <property name="can_focus">True</property>
+ <property name="tooltip" translatable="yes">Specify a custom polyphony value for new patch</property>
+ <property name="adjustment">1 0 100 1 10 10</property>
+ <property name="climb_rate">1</property>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="left_attach">3</property>
+ <property name="right_attach">4</property>
+ <property name="x_options">GTK_FILL</property>
+ <property name="y_options">GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkRadioButton" id="load_patch_poly_from_file_radio">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="tooltip" translatable="yes">Use the polyphony value stored in the patch file</property>
+ <property name="label" translatable="yes">Load from file</property>
+ <property name="use_underline">True</property>
+ <property name="draw_indicator">True</property>
+ <property name="group">load_patch_poly_from_current_radio</property>
+ </widget>
+ <packing>
+ <property name="left_attach">2</property>
+ <property name="right_attach">3</property>
+ <property name="x_options">GTK_FILL</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkRadioButton" id="load_patch_poly_from_current_radio">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="tooltip" translatable="yes">Use the same polyphony as the current patch</property>
+ <property name="label" translatable="yes">Keep current</property>
+ <property name="use_underline">True</property>
+ <property name="active">True</property>
+ <property name="draw_indicator">True</property>
+ </widget>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="x_options">GTK_FILL</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkLabel" id="label123">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">&lt;b&gt;Polyphony: &lt;/b&gt;</property>
+ <property name="use_markup">True</property>
+ </widget>
+ <packing>
+ <property name="x_options">GTK_FILL</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ <child internal-child="action_area">
+ <widget class="GtkHButtonBox" id="hbuttonbox1">
+ <property name="visible">True</property>
+ <property name="layout_style">GTK_BUTTONBOX_END</property>
+ <child>
+ <widget class="GtkButton" id="load_patch_cancel_button">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="can_default">True</property>
+ <property name="label">gtk-cancel</property>
+ <property name="use_stock">True</property>
+ <property name="response_id">-6</property>
+ </widget>
+ </child>
+ <child>
+ <widget class="GtkButton" id="load_patch_ok_button">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="can_default">True</property>
+ <property name="has_default">True</property>
+ <property name="label">gtk-open</property>
+ <property name="use_stock">True</property>
+ <property name="response_id">-5</property>
+ </widget>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="pack_type">GTK_PACK_END</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ <widget class="GtkWindow" id="warehouse_win">
+ <property name="title" translatable="yes">window1</property>
+ <child>
+ <widget class="GtkTable" id="table8">
+ <property name="visible">True</property>
+ <property name="n_rows">4</property>
+ <property name="n_columns">2</property>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <widget class="GtkVBox" id="control_strip">
+ <property name="visible">True</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ <child>
+ <widget class="GtkHBox" id="hbox1">
+ <property name="visible">True</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ <child>
+ <widget class="GtkLabel" id="control_strip_name_label">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="xpad">4</property>
+ <property name="ypad">4</property>
+ <property name="label" translatable="yes">&lt;b&gt;Name&lt;/b&gt;</property>
+ <property name="use_markup">True</property>
+ <property name="single_line_mode">True</property>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkAlignment" id="alignment3">
+ <property name="visible">True</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ <property name="xalign">1</property>
+ <property name="yalign">1</property>
+ <property name="xscale">0</property>
+ <property name="yscale">0</property>
+ <property name="top_padding">2</property>
+ <property name="left_padding">2</property>
+ <property name="right_padding">2</property>
+ <child>
+ <widget class="GtkSpinButton" id="control_strip_spinner">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ <property name="width_chars">12</property>
+ <property name="adjustment">0 -9.9999999999999999e+45 1.0000000000000001e+63 1 10 10</property>
+ <property name="digits">4</property>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkHScale" id="control_strip_slider">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ <property name="adjustment">0 -1e+113 1e+137 0 0 0</property>
+ <property name="digits">63</property>
+ <property name="draw_value">False</property>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="top_attach">3</property>
+ <property name="bottom_attach">4</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkHSeparator" id="hseparator5">
+ <property name="visible">True</property>
+ </widget>
+ <packing>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options">GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkVBox" id="patch_view_box">
+ <property name="visible">True</property>
+ <child>
+ <widget class="GtkHBox" id="hbox70">
+ <property name="visible">True</property>
+ <child>
+ <widget class="GtkToolbar" id="toolbar6">
+ <property name="visible">True</property>
+ <property name="toolbar_style">GTK_TOOLBAR_BOTH_HORIZ</property>
+ <child>
+ <widget class="GtkToolItem" id="patch_view_breadcrumb_toolitem">
+ <property name="visible">True</property>
+ <child>
+ <widget class="GtkAlignment" id="alignment4">
+ <property name="visible">True</property>
+ <child>
+ <widget class="GtkViewport" id="patch_view_breadcrumb_container">
+ <property name="visible">True</property>
+ <property name="shadow_type">GTK_SHADOW_NONE</property>
+ <child>
+ <placeholder/>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="homogeneous">False</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkToolItem" id="toolitem14">
+ <property name="visible">True</property>
+ <child>
+ <widget class="GtkLabel" id="label132">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes"> </property>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="homogeneous">False</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+ <child>
+ <widget class="GtkToolbar" id="toolbar3">
+ <property name="visible">True</property>
+ <property name="show_arrow">False</property>
+ <child>
+ <widget class="GtkSeparatorToolItem" id="separatortoolitem17">
+ <property name="visible">True</property>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="homogeneous">False</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkToggleToolButton" id="patch_view_process_but">
+ <property name="visible">True</property>
+ <property name="tooltip" translatable="yes">Enable DSP processing</property>
+ <property name="use_underline">True</property>
+ <property name="stock_id">gtk-execute</property>
+ <property name="active">True</property>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkToolItem" id="toolitem7">
+ <property name="visible">True</property>
+ <child>
+ <widget class="GtkImage" id="image1978">
+ <property name="visible">True</property>
+ <property name="xpad">4</property>
+ <property name="stock">gtk-copy</property>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="homogeneous">False</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkToolItem" id="toolitem10">
+ <property name="visible">True</property>
+ <child>
+ <widget class="GtkSpinButton" id="patch_view_poly_spin">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="adjustment">1 0 100 1 10 10</property>
+ <property name="climb_rate">1</property>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="homogeneous">False</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkSeparatorToolItem" id="separatortoolitem18">
+ <property name="visible">True</property>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="homogeneous">False</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkToolButton" id="patch_view_save_but">
+ <property name="visible">True</property>
+ <property name="tooltip" translatable="yes">Save patch to a file</property>
+ <property name="stock_id">gtk-save</property>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkSeparatorToolItem" id="separatortoolitem19">
+ <property name="visible">True</property>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="homogeneous">False</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkToolButton" id="patch_view_clear_but">
+ <property name="visible">True</property>
+ <property name="tooltip" translatable="yes">Clear (Destroy all children)</property>
+ <property name="use_underline">True</property>
+ <property name="stock_id">gtk-clear</property>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkToolButton" id="patch_view_destroy_but">
+ <property name="visible">True</property>
+ <property name="tooltip" translatable="yes">Destroy this patch</property>
+ <property name="stock_id">gtk-delete</property>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkSeparatorToolItem" id="separatortoolitem22">
+ <property name="visible">True</property>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="homogeneous">False</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkToolButton" id="patch_view_refresh_but">
+ <property name="visible">True</property>
+ <property name="tooltip" translatable="yes">Refresh view</property>
+ <property name="stock_id">gtk-refresh</property>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkToolButton" id="patch_view_zoom_normal_but">
+ <property name="visible">True</property>
+ <property name="tooltip" translatable="yes">Zoom to normal size</property>
+ <property name="use_underline">True</property>
+ <property name="stock_id">gtk-zoom-100</property>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkToolButton" id="patch_view_zoom_full_but">
+ <property name="visible">True</property>
+ <property name="tooltip" translatable="yes">Fit patch to window</property>
+ <property name="use_underline">True</property>
+ <property name="stock_id">gtk-zoom-fit</property>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkScrolledWindow" id="patch_view_scrolledwindow">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="border_width">1</property>
+ <property name="shadow_type">GTK_SHADOW_IN</property>
+ <child>
+ <placeholder/>
+ </child>
+ </widget>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ <property name="x_options">GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkVBox" id="control_panel_vbox">
+ <property name="visible">True</property>
+ <child>
+ <widget class="GtkAlignment" id="alignment6">
+ <property name="visible">True</property>
+ <property name="yalign">0</property>
+ <child>
+ <widget class="GtkScrolledWindow" id="scrolledwin1">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hscrollbar_policy">GTK_POLICY_NEVER</property>
+ <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+ <child>
+ <widget class="GtkViewport" id="viewport1">
+ <property name="visible">True</property>
+ <property name="shadow_type">GTK_SHADOW_NONE</property>
+ <child>
+ <widget class="GtkVBox" id="control_panel_controls_box">
+ <property name="visible">True</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ <child>
+ <widget class="GtkHBox" id="control_panel_voice_controls_box">
+ <property name="visible">True</property>
+ <property name="homogeneous">True</property>
+ <child>
+ <widget class="GtkRadioButton" id="control_panel_all_voices_radio">
+ <property name="can_focus">True</property>
+ <property name="tooltip" translatable="yes">Apply changed controls to all voices</property>
+ <property name="label" translatable="yes">All Voices</property>
+ <property name="use_underline">True</property>
+ <property name="draw_indicator">True</property>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkHBox" id="hbox32">
+ <property name="visible">True</property>
+ <property name="spacing">5</property>
+ <child>
+ <widget class="GtkRadioButton" id="control_panel_specific_voice_radio">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="tooltip" translatable="yes">Apply changed controls to one voice only</property>
+ <property name="label" translatable="yes">Specific Voice:</property>
+ <property name="use_underline">True</property>
+ <property name="draw_indicator">True</property>
+ <property name="group">control_panel_all_voices_radio</property>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkSpinButton" id="control_panel_voice_spinbutton">
+ <property name="visible">True</property>
+ <property name="sensitive">False</property>
+ <property name="can_focus">True</property>
+ <property name="tooltip" translatable="yes">Voice control changes are applied to</property>
+ <property name="adjustment">1 1 100 1 10 10</property>
+ <property name="climb_rate">1</property>
+ <property name="numeric">True</property>
+ </widget>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="padding">5</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ <widget class="GtkWindow" id="messages_win">
+ <property name="width_request">400</property>
+ <property name="height_request">180</property>
+ <property name="border_width">8</property>
+ <property name="title" translatable="yes">Messages - Ingenuity</property>
+ <property name="icon">ingen.svg</property>
+ <child>
+ <widget class="GtkVBox" id="vbox12">
+ <property name="visible">True</property>
+ <property name="spacing">6</property>
+ <child>
+ <widget class="GtkScrolledWindow" id="scrolledwindow2">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+ <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+ <property name="shadow_type">GTK_SHADOW_IN</property>
+ <child>
+ <widget class="GtkTextView" id="messages_textview">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="tooltip" translatable="yes">Error messages from the engine since the last time "Clear" was pressed</property>
+ <property name="pixels_above_lines">5</property>
+ <property name="pixels_below_lines">5</property>
+ <property name="editable">False</property>
+ <property name="wrap_mode">GTK_WRAP_WORD</property>
+ <property name="left_margin">5</property>
+ <property name="right_margin">5</property>
+ <property name="cursor_visible">False</property>
+ <property name="accepts_tab">False</property>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ <child>
+ <widget class="GtkHButtonBox" id="hbuttonbox8">
+ <property name="visible">True</property>
+ <property name="spacing">6</property>
+ <property name="layout_style">GTK_BUTTONBOX_END</property>
+ <child>
+ <widget class="GtkButton" id="messages_clear_button">
+ <property name="visible">True</property>
+ <property name="sensitive">False</property>
+ <property name="can_focus">True</property>
+ <property name="can_default">True</property>
+ <property name="label">gtk-clear</property>
+ <property name="use_stock">True</property>
+ </widget>
+ </child>
+ <child>
+ <widget class="GtkButton" id="messages_close_button">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="label">gtk-close</property>
+ <property name="use_stock">True</property>
+ </widget>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ <widget class="GtkWindow" id="config_win">
+ <property name="border_width">8</property>
+ <property name="title" translatable="yes">Configuration - Ingenuity</property>
+ <property name="icon">ingen.svg</property>
+ <child>
+ <widget class="GtkVBox" id="vbox13">
+ <property name="visible">True</property>
+ <property name="spacing">6</property>
+ <child>
+ <widget class="GtkTable" id="table9">
+ <property name="visible">True</property>
+ <property name="n_rows">2</property>
+ <property name="n_columns">2</property>
+ <child>
+ <widget class="GtkLabel" id="label103">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ </widget>
+ <packing>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options">GTK_FILL</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkLabel" id="label91">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">&lt;i&gt;Example: /foo/bar:/home/john/patches:/usr/share/om/patches&lt;/i&gt;</property>
+ <property name="use_markup">True</property>
+ </widget>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options">GTK_FILL</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkEntry" id="config_path_entry">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="invisible_char">*</property>
+ </widget>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkLabel" id="label90">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">&lt;b&gt;Patch Search Path: &lt;/b&gt;</property>
+ <property name="use_markup">True</property>
+ </widget>
+ <packing>
+ <property name="x_options">GTK_FILL</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkHButtonBox" id="hbuttonbox2">
+ <property name="visible">True</property>
+ <property name="spacing">6</property>
+ <property name="layout_style">GTK_BUTTONBOX_END</property>
+ <child>
+ <widget class="GtkButton" id="config_save_button">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="tooltip" translatable="yes">Save these settings for future sessions</property>
+ <property name="label">gtk-save</property>
+ <property name="use_stock">True</property>
+ </widget>
+ </child>
+ <child>
+ <widget class="GtkButton" id="config_cancel_button">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="label">gtk-cancel</property>
+ <property name="use_stock">True</property>
+ </widget>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkButton" id="config_ok_button">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="tooltip" translatable="yes">Apply these settings to this session only</property>
+ <property name="label">gtk-ok</property>
+ <property name="use_stock">True</property>
+ </widget>
+ <packing>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ <widget class="GtkWindow" id="patch_properties_win">
+ <property name="width_request">400</property>
+ <property name="height_request">200</property>
+ <property name="border_width">8</property>
+ <property name="title" translatable="yes">Patch Description</property>
+ <property name="window_position">GTK_WIN_POS_CENTER_ON_PARENT</property>
+ <property name="icon">ingen.svg</property>
+ <child>
+ <widget class="GtkVBox" id="vbox14">
+ <property name="visible">True</property>
+ <property name="spacing">6</property>
+ <child>
+ <widget class="GtkHBox" id="hbox51">
+ <property name="visible">True</property>
+ <property name="spacing">5</property>
+ <child>
+ <widget class="GtkLabel" id="label93">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Author:</property>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkEntry" id="properties_author_entry">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="invisible_char">*</property>
+ </widget>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkScrolledWindow" id="scrolledwindow9">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+ <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+ <property name="shadow_type">GTK_SHADOW_IN</property>
+ <child>
+ <widget class="GtkTextView" id="properties_description_textview">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="tooltip" translatable="yes">A short description of the patch to be included in the patch file</property>
+ <property name="wrap_mode">GTK_WRAP_WORD</property>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkHButtonBox" id="hbuttonbox3">
+ <property name="visible">True</property>
+ <property name="spacing">5</property>
+ <property name="layout_style">GTK_BUTTONBOX_END</property>
+ <child>
+ <widget class="GtkButton" id="properties_cancel_button">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="can_default">True</property>
+ <property name="label">gtk-cancel</property>
+ <property name="use_stock">True</property>
+ </widget>
+ </child>
+ <child>
+ <widget class="GtkButton" id="properties_ok_button">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="can_default">True</property>
+ <property name="tooltip" translatable="yes">Apply these changes to be saved the next time the patch is saved</property>
+ <property name="label">gtk-ok</property>
+ <property name="use_stock">True</property>
+ </widget>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ <widget class="GtkWindow" id="rename_win">
+ <property name="width_request">250</property>
+ <property name="title" translatable="yes">Rename</property>
+ <property name="window_position">GTK_WIN_POS_CENTER_ON_PARENT</property>
+ <property name="icon">ingen.svg</property>
+ <child>
+ <widget class="GtkVBox" id="vbox15">
+ <property name="visible">True</property>
+ <property name="border_width">5</property>
+ <child>
+ <widget class="GtkHBox" id="hbox53">
+ <property name="visible">True</property>
+ <child>
+ <widget class="GtkLabel" id="label95">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">New name: </property>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkEntry" id="rename_name_entry">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="invisible_char">*</property>
+ <property name="activates_default">True</property>
+ </widget>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+ <child>
+ <widget class="GtkLabel" id="rename_message_label">
+ <property name="visible">True</property>
+ <property name="wrap">True</property>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="padding">6</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkHButtonBox" id="hbuttonbox4">
+ <property name="visible">True</property>
+ <property name="spacing">5</property>
+ <property name="layout_style">GTK_BUTTONBOX_END</property>
+ <child>
+ <widget class="GtkButton" id="rename_cancel_button">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="can_default">True</property>
+ <property name="label">gtk-cancel</property>
+ <property name="use_stock">True</property>
+ </widget>
+ </child>
+ <child>
+ <widget class="GtkButton" id="rename_ok_button">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="can_default">True</property>
+ <property name="has_default">True</property>
+ <child>
+ <widget class="GtkAlignment" id="alignment1">
+ <property name="visible">True</property>
+ <property name="xscale">0</property>
+ <property name="yscale">0</property>
+ <child>
+ <widget class="GtkHBox" id="hbox52">
+ <property name="visible">True</property>
+ <property name="spacing">2</property>
+ <child>
+ <widget class="GtkImage" id="image258">
+ <property name="visible">True</property>
+ <property name="stock">gtk-ok</property>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkLabel" id="label94">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Rename</property>
+ <property name="use_underline">True</property>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ <widget class="GtkWindow" id="node_properties_win">
+ <property name="border_width">6</property>
+ <property name="title" translatable="yes">Node Properties - Ingenuity</property>
+ <property name="icon">ingen.svg</property>
+ <child>
+ <widget class="GtkVBox" id="vbox17">
+ <property name="visible">True</property>
+ <child>
+ <widget class="GtkLabel" id="label105">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">&lt;b&gt;Node&lt;/b&gt;</property>
+ <property name="use_markup">True</property>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkVBox" id="vbox18">
+ <property name="visible">True</property>
+ <property name="border_width">12</property>
+ <property name="spacing">6</property>
+ <child>
+ <widget class="GtkHBox" id="hbox56">
+ <property name="visible">True</property>
+ <property name="spacing">4</property>
+ <child>
+ <widget class="GtkLabel" id="label121">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Path: </property>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkLabel" id="node_properties_path_label">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">-</property>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkCheckButton" id="node_properties_polyphonic_checkbutton">
+ <property name="visible">True</property>
+ <property name="sensitive">False</property>
+ <property name="can_focus">True</property>
+ <property name="label" translatable="yes">Polyphonic</property>
+ <property name="use_underline">True</property>
+ <property name="draw_indicator">True</property>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">6</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkLabel" id="label106">
+ <property name="width_request">240</property>
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">&lt;b&gt;Plugin&lt;/b&gt;</property>
+ <property name="use_markup">True</property>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkTable" id="table13">
+ <property name="visible">True</property>
+ <property name="border_width">12</property>
+ <property name="n_rows">3</property>
+ <property name="n_columns">2</property>
+ <property name="column_spacing">10</property>
+ <property name="row_spacing">6</property>
+ <child>
+ <widget class="GtkLabel" id="node_properties_plugin_name_label">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">-</property>
+ </widget>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ <property name="x_options">GTK_FILL</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkLabel" id="label116">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">Name: </property>
+ </widget>
+ <packing>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ <property name="x_options">GTK_FILL</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkLabel" id="node_properties_plugin_uri_label">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">-</property>
+ </widget>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options">GTK_FILL</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkLabel" id="label120">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">URI: </property>
+ </widget>
+ <packing>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options">GTK_FILL</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkLabel" id="label114">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">Type: </property>
+ </widget>
+ <packing>
+ <property name="x_options">GTK_FILL</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkLabel" id="node_properties_plugin_type_label">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">-</property>
+ </widget>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="x_options">GTK_FILL</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="position">3</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ <widget class="GtkAboutDialog" id="about_win">
+ <property name="destroy_with_parent">True</property>
+ <property name="type_hint">GDK_WINDOW_TYPE_HINT_NORMAL</property>
+ <property name="name">Ingenuity</property>
+ <property name="copyright" translatable="yes">Copyright (C) 2005-2007 Dave Robillard &lt;http://drobilla.net&gt;</property>
+ <property name="comments" translatable="yes">A graphical client for the Ingen audio system</property>
+ <property name="website">http://drobilla.net/software/ingen</property>
+ <property name="license" translatable="yes">Licensed under the GNU GPL, Version 2.
+
+See COPYING file included with this distribution, or http://www.gnu.org/licenses/gpl.txt for more information</property>
+ <property name="authors">Author:
+ Dave Robillard &lt;dave@drobilla.net&gt;
+
+Contributors:
+ Lars Luthman - DSSI enhancements, bugfixes
+ Mario Lang - SuperCollider bindings, bugfixes
+ Leonard Ritter - Python bindings
+</property>
+ <property name="translator_credits" translatable="yes" comments="TRANSLATORS: Replace this string with your names, one name per line.">translator-credits</property>
+ <property name="artists">Usability / UI Design:
+ Thorsten Wilms</property>
+ <property name="logo">ingen.svg</property>
+ <property name="wrap_license">True</property>
+ <child internal-child="vbox">
+ <widget class="GtkVBox" id="dialog-vbox3">
+ <child internal-child="action_area">
+ <widget class="GtkHButtonBox" id="dialog-action_area3">
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="pack_type">GTK_PACK_END</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ <widget class="GtkWindow" id="patch_tree_win">
+ <property name="width_request">320</property>
+ <property name="height_request">340</property>
+ <property name="border_width">8</property>
+ <property name="title" translatable="yes">Patches - Ingenuity</property>
+ <property name="icon">ingen.svg</property>
+ <child>
+ <widget class="GtkScrolledWindow" id="scrolledwindow8">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="border_width">3</property>
+ <property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+ <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+ <property name="shadow_type">GTK_SHADOW_IN</property>
+ <child>
+ <widget class="GtkTreeView" id="patches_treeview">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="tooltip" translatable="yes">All patches loaded in the engine</property>
+ <property name="rules_hint">True</property>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ <widget class="GtkDialog" id="connect_win">
+ <property name="border_width">6</property>
+ <property name="title" translatable="yes">Engine - Ingenuity</property>
+ <property name="resizable">False</property>
+ <property name="icon">ingen.svg</property>
+ <property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property>
+ <child internal-child="vbox">
+ <widget class="GtkVBox" id="dialog-vbox3">
+ <property name="visible">True</property>
+ <property name="spacing">6</property>
+ <child>
+ <widget class="GtkVBox" id="vbox19">
+ <property name="visible">True</property>
+ <child>
+ <widget class="GtkHBox" id="hbox61">
+ <property name="visible">True</property>
+ <child>
+ <widget class="GtkImage" id="connect_icon">
+ <property name="visible">True</property>
+ <property name="xpad">12</property>
+ <property name="stock">gtk-disconnect</property>
+ <property name="icon_size">3</property>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkVBox" id="vbox20">
+ <property name="visible">True</property>
+ <property name="border_width">5</property>
+ <property name="homogeneous">True</property>
+ <child>
+ <widget class="GtkProgressBar" id="connect_progress_bar">
+ <property name="visible">True</property>
+ <property name="pulse_step">0.10000000149</property>
+ <property name="text" translatable="yes"></property>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkLabel" id="connect_progress_label">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">Not Connected</property>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkHSeparator" id="hseparator4">
+ <property name="visible">True</property>
+ </widget>
+ <packing>
+ <property name="padding">4</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkTable" id="table18">
+ <property name="visible">True</property>
+ <property name="n_rows">3</property>
+ <property name="n_columns">2</property>
+ <property name="row_spacing">8</property>
+ <child>
+ <widget class="GtkLabel" id="label131">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ </widget>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ <property name="x_options">GTK_FILL</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkRadioButton" id="connect_internal_radiobutton">
+ <property name="visible">True</property>
+ <property name="sensitive">False</property>
+ <property name="can_focus">True</property>
+ <property name="label" translatable="yes">Use internal engine</property>
+ <property name="use_underline">True</property>
+ <property name="draw_indicator">True</property>
+ <property name="group">connect_server_radiobutton</property>
+ </widget>
+ <packing>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ <property name="x_options">GTK_FILL</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkRadioButton" id="connect_launch_radiobutton">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="label" translatable="yes">Launch and connect to server on port: </property>
+ <property name="use_underline">True</property>
+ <property name="draw_indicator">True</property>
+ <property name="group">connect_server_radiobutton</property>
+ </widget>
+ <packing>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options">GTK_FILL</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkRadioButton" id="connect_server_radiobutton">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="label" translatable="yes">Connect to running server at: </property>
+ <property name="use_underline">True</property>
+ <property name="draw_indicator">True</property>
+ </widget>
+ <packing>
+ <property name="x_options">GTK_FILL</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkHBox" id="hbox67">
+ <property name="visible">True</property>
+ <child>
+ <widget class="GtkEntry" id="connect_url_entry">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="invisible_char">*</property>
+ <property name="activates_default">True</property>
+ <property name="width_chars">28</property>
+ <property name="text" translatable="yes">osc.udp://localhost:16180</property>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="x_options">GTK_FILL</property>
+ <property name="y_options">GTK_FILL</property>
+ <property name="x_padding">8</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkHBox" id="hbox64">
+ <property name="visible">True</property>
+ <child>
+ <widget class="GtkSpinButton" id="connect_port_spinbutton">
+ <property name="visible">True</property>
+ <property name="sensitive">False</property>
+ <property name="can_focus">True</property>
+ <property name="adjustment">16180 1 65535 1 10 10</property>
+ <property name="climb_rate">1</property>
+ <property name="numeric">True</property>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="y_options">GTK_FILL</property>
+ <property name="x_padding">8</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ <child internal-child="action_area">
+ <widget class="GtkHButtonBox" id="dialog-action_area3">
+ <property name="visible">True</property>
+ <property name="layout_style">GTK_BUTTONBOX_END</property>
+ <child>
+ <widget class="GtkButton" id="connect_quit_button">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="can_default">True</property>
+ <property name="label">gtk-quit</property>
+ <property name="use_stock">True</property>
+ </widget>
+ </child>
+ <child>
+ <widget class="GtkButton" id="connect_disconnect_button">
+ <property name="visible">True</property>
+ <property name="sensitive">False</property>
+ <property name="can_focus">True</property>
+ <property name="can_default">True</property>
+ <property name="label">gtk-disconnect</property>
+ <property name="use_stock">True</property>
+ <property name="response_id">-6</property>
+ </widget>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkButton" id="connect_connect_button">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="can_default">True</property>
+ <property name="has_default">True</property>
+ <property name="label">gtk-connect</property>
+ <property name="use_stock">True</property>
+ <property name="response_id">-6</property>
+ </widget>
+ <packing>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="pack_type">GTK_PACK_END</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ <widget class="GtkMenu" id="canvas_menu">
+ <property name="visible">True</property>
+ <child>
+ <widget class="GtkImageMenuItem" id="input1">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Input</property>
+ <property name="use_underline">True</property>
+ <child>
+ <widget class="GtkMenu" id="input1_menu">
+ <child>
+ <widget class="GtkMenuItem" id="canvas_menu_add_audio_input">
+ <property name="visible">True</property>
+ <property name="tooltip" translatable="yes">Add an audio input to this patch</property>
+ <property name="label" translatable="yes">Audio</property>
+ <property name="use_underline">True</property>
+ <signal name="activate" handler="on_canvas_menu_add_audio_input_activate"/>
+ </widget>
+ </child>
+ <child>
+ <widget class="GtkMenuItem" id="canvas_menu_add_control_input">
+ <property name="visible">True</property>
+ <property name="tooltip" translatable="yes">Add a control input (and a control slider for it) to this patch</property>
+ <property name="label" translatable="yes">Control</property>
+ <property name="use_underline">True</property>
+ <signal name="activate" handler="on_canvas_menu_add_control_input_activate"/>
+ </widget>
+ </child>
+ <child>
+ <widget class="GtkMenuItem" id="canvas_menu_add_midi_input">
+ <property name="visible">True</property>
+ <property name="tooltip" translatable="yes">Add a MIDI input to this patch</property>
+ <property name="label" translatable="yes">MIDI</property>
+ <property name="use_underline">True</property>
+ <signal name="activate" handler="on_canvas_menu_add_midi_input_activate"/>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ <child internal-child="image">
+ <widget class="GtkImage" id="image1886">
+ <property name="visible">True</property>
+ <property name="stock">gtk-connect</property>
+ <property name="icon_size">1</property>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ <child>
+ <widget class="GtkImageMenuItem" id="output1">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Output</property>
+ <property name="use_underline">True</property>
+ <child>
+ <widget class="GtkMenu" id="output1_menu">
+ <child>
+ <widget class="GtkMenuItem" id="canvas_menu_add_audio_output">
+ <property name="visible">True</property>
+ <property name="tooltip" translatable="yes">Add an audio output to this patch</property>
+ <property name="label" translatable="yes">Audio</property>
+ <property name="use_underline">True</property>
+ <signal name="activate" handler="on_canvas_menu_add_audio_output_activate"/>
+ </widget>
+ </child>
+ <child>
+ <widget class="GtkMenuItem" id="canvas_menu_add_control_output">
+ <property name="visible">True</property>
+ <property name="tooltip" translatable="yes">Add a control output to this patch</property>
+ <property name="label" translatable="yes">Control</property>
+ <property name="use_underline">True</property>
+ <signal name="activate" handler="on_canvas_menu_add_control_output_activate"/>
+ </widget>
+ </child>
+ <child>
+ <widget class="GtkMenuItem" id="canvas_menu_add_midi_output">
+ <property name="visible">True</property>
+ <property name="tooltip" translatable="yes">Add a MIDI output to this patch</property>
+ <property name="label" translatable="yes">MIDI</property>
+ <property name="use_underline">True</property>
+ <signal name="activate" handler="on_canvas_menu_add_midi_output_activate"/>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ <child internal-child="image">
+ <widget class="GtkImage" id="image1887">
+ <property name="visible">True</property>
+ <property name="stock">gtk-connect</property>
+ <property name="icon_size">1</property>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ <child>
+ <widget class="GtkImageMenuItem" id="canvas_menu_load_plugin">
+ <property name="visible">True</property>
+ <property name="tooltip" translatable="yes">Load a plugin as a child of this patch</property>
+ <property name="label" translatable="yes">_Plugin...</property>
+ <property name="use_underline">True</property>
+ <signal name="activate" handler="on_canvas_menu_add_plugin_activate"/>
+ <child internal-child="image">
+ <widget class="GtkImage" id="image1888">
+ <property name="visible">True</property>
+ <property name="stock">gtk-execute</property>
+ <property name="icon_size">1</property>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ <child>
+ <widget class="GtkImageMenuItem" id="canvas_menu_load_patch">
+ <property name="visible">True</property>
+ <property name="tooltip" translatable="yes">Load a patch as a child of this patch</property>
+ <property name="label" translatable="yes">_Load Patch...</property>
+ <property name="use_underline">True</property>
+ <signal name="activate" handler="on_canvas_menu_load_patch_activate"/>
+ <child internal-child="image">
+ <widget class="GtkImage" id="image1889">
+ <property name="visible">True</property>
+ <property name="stock">gtk-open</property>
+ <property name="icon_size">1</property>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ <child>
+ <widget class="GtkImageMenuItem" id="canvas_menu_new_patch">
+ <property name="visible">True</property>
+ <property name="tooltip" translatable="yes">Create a new (empty) patch as a child of this patch</property>
+ <property name="label" translatable="yes">_New Patch...</property>
+ <property name="use_underline">True</property>
+ <signal name="activate" handler="on_canvas_menu_new_patch_activate"/>
+ <child internal-child="image">
+ <widget class="GtkImage" id="image1890">
+ <property name="visible">True</property>
+ <property name="stock">gtk-new</property>
+ <property name="icon_size">1</property>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ <widget class="GtkDialog" id="load_remote_patch_win">
+ <property name="border_width">8</property>
+ <property name="title" translatable="yes">Load Remote Patch</property>
+ <property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property>
+ <child internal-child="vbox">
+ <widget class="GtkVBox" id="dialog-vbox4">
+ <property name="visible">True</property>
+ <property name="spacing">8</property>
+ <child>
+ <widget class="GtkVBox" id="vbox21">
+ <property name="visible">True</property>
+ <property name="spacing">8</property>
+ <child>
+ <widget class="GtkScrolledWindow" id="load_remote_patch_treeview_sw">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="has_focus">True</property>
+ <property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+ <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+ <property name="shadow_type">GTK_SHADOW_IN</property>
+ <child>
+ <widget class="GtkTreeView" id="load_remote_patch_treeview">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ <child>
+ <widget class="GtkHBox" id="hbox71">
+ <property name="visible">True</property>
+ <child>
+ <widget class="GtkLabel" id="label133">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">URI: </property>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkEntry" id="load_remote_patch_uri_entry">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="invisible_char">*</property>
+ <property name="width_chars">78</property>
+ </widget>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ <child internal-child="action_area">
+ <widget class="GtkHButtonBox" id="dialog-action_area4">
+ <property name="visible">True</property>
+ <property name="layout_style">GTK_BUTTONBOX_END</property>
+ <child>
+ <widget class="GtkButton" id="load_remote_patch_cancel_button">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="can_default">True</property>
+ <property name="label">gtk-cancel</property>
+ <property name="use_stock">True</property>
+ <property name="response_id">-6</property>
+ </widget>
+ </child>
+ <child>
+ <widget class="GtkButton" id="load_remote_patch_open_button">
+ <property name="visible">True</property>
+ <property name="sensitive">False</property>
+ <property name="can_focus">True</property>
+ <property name="can_default">True</property>
+ <property name="has_default">True</property>
+ <property name="label">gtk-open</property>
+ <property name="use_stock">True</property>
+ <property name="response_id">-5</property>
+ </widget>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="pack_type">GTK_PACK_END</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ <widget class="GtkDialog" id="upload_patch_win">
+ <property name="border_width">8</property>
+ <property name="title" translatable="yes">Upload Patch</property>
+ <property name="resizable">False</property>
+ <property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property>
+ <child internal-child="vbox">
+ <widget class="GtkVBox" id="dialog-vbox5">
+ <property name="visible">True</property>
+ <property name="spacing">9</property>
+ <child>
+ <widget class="GtkTable" id="table19">
+ <property name="visible">True</property>
+ <property name="n_rows">2</property>
+ <property name="n_columns">2</property>
+ <property name="row_spacing">8</property>
+ <child>
+ <widget class="GtkLabel" id="label136">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">Short Name: </property>
+ </widget>
+ <packing>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options">GTK_FILL</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkLabel" id="label135">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">Symbol: </property>
+ </widget>
+ <packing>
+ <property name="x_options">GTK_FILL</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkEntry" id="upload_patch_short_name_entry">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="tooltip" translatable="yes">Enter a short name for this patch, e.g. "Mega Synth"</property>
+ <property name="invisible_char">*</property>
+ <property name="activates_default">True</property>
+ </widget>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkEntry" id="upload_patch_symbol_entry">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="tooltip" translatable="yes">Enter a short name suitable for use as an identifier or filename.
+
+The first character must be one of _, a-z or A-Z and subsequenct characters can be from _, a-z, A-Z or 0-9.
+</property>
+ <property name="invisible_char">*</property>
+ <property name="activates_default">True</property>
+ </widget>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkLabel" id="label137">
+ <property name="visible">True</property>
+ <property name="ypad">4</property>
+ <property name="label" translatable="yes">Succesfully uploaded patches will be available immediately in the remote patch browser.
+
+By uploading patches, you agree to license them under the Creative Commons Attribution-Share Alike 3.0 License.
+
+Thank you for contributing.</property>
+ <property name="wrap">True</property>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">3</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkProgressBar" id="upload_patch_progress">
+ <property name="visible">True</property>
+ <property name="tooltip" translatable="yes">Upload progress</property>
+ <property name="pulse_step">0.10000000149</property>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">4</property>
+ </packing>
+ </child>
+ <child internal-child="action_area">
+ <widget class="GtkHButtonBox" id="dialog-action_area5">
+ <property name="visible">True</property>
+ <property name="layout_style">GTK_BUTTONBOX_END</property>
+ <child>
+ <widget class="GtkButton" id="upload_patch_cancel_button">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="can_default">True</property>
+ <property name="label">gtk-close</property>
+ <property name="use_stock">True</property>
+ <property name="response_id">-7</property>
+ </widget>
+ </child>
+ <child>
+ <widget class="GtkButton" id="upload_patch_upload_button">
+ <property name="visible">True</property>
+ <property name="sensitive">False</property>
+ <property name="can_focus">True</property>
+ <property name="can_default">True</property>
+ <property name="has_default">True</property>
+ <property name="response_id">-5</property>
+ <child>
+ <widget class="GtkAlignment" id="alignment5">
+ <property name="visible">True</property>
+ <property name="xscale">0</property>
+ <property name="yscale">0</property>
+ <child>
+ <widget class="GtkHBox" id="hbox72">
+ <property name="visible">True</property>
+ <property name="spacing">2</property>
+ <child>
+ <widget class="GtkImage" id="image2136">
+ <property name="visible">True</property>
+ <property name="stock">gtk-ok</property>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkLabel" id="label134">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Upload</property>
+ <property name="use_underline">True</property>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="pack_type">GTK_PACK_END</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ <widget class="GtkMenu" id="port_control_menu">
+ <child>
+ <widget class="GtkImageMenuItem" id="port_control_menu_properties">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_Properties...</property>
+ <property name="use_underline">True</property>
+ <signal name="activate" handler="on_port_control_menu_properties_activate"/>
+ <child internal-child="image">
+ <widget class="GtkImage" id="image2137">
+ <property name="visible">True</property>
+ <property name="stock">gtk-properties</property>
+ <property name="icon_size">1</property>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ <widget class="GtkDialog" id="port_properties_win">
+ <property name="border_width">8</property>
+ <property name="type">GTK_WINDOW_POPUP</property>
+ <property name="title" translatable="yes">Port Properties - Ingenuity</property>
+ <property name="resizable">False</property>
+ <property name="window_position">GTK_WIN_POS_MOUSE</property>
+ <property name="destroy_with_parent">True</property>
+ <property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property>
+ <child internal-child="vbox">
+ <widget class="GtkVBox" id="dialog-vbox6">
+ <property name="visible">True</property>
+ <property name="spacing">8</property>
+ <child>
+ <widget class="GtkTable" id="table20">
+ <property name="visible">True</property>
+ <property name="n_rows">2</property>
+ <property name="n_columns">2</property>
+ <property name="column_spacing">2</property>
+ <property name="row_spacing">4</property>
+ <child>
+ <widget class="GtkLabel" id="label139">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">Maximum Value: </property>
+ </widget>
+ <packing>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options">GTK_FILL</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkLabel" id="label138">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">Minimum Value: </property>
+ </widget>
+ <packing>
+ <property name="x_options">GTK_FILL</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkSpinButton" id="port_properties_max_spinner">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="adjustment">1 -99999 99999 1 10 10</property>
+ <property name="climb_rate">1</property>
+ <property name="digits">5</property>
+ <property name="numeric">True</property>
+ </widget>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkSpinButton" id="port_properties_min_spinner">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="adjustment">0 -100000000 100000000 1 10 10</property>
+ <property name="climb_rate">1</property>
+ <property name="digits">5</property>
+ <property name="numeric">True</property>
+ </widget>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ <child internal-child="action_area">
+ <widget class="GtkHButtonBox" id="dialog-action_area6">
+ <property name="visible">True</property>
+ <property name="layout_style">GTK_BUTTONBOX_END</property>
+ <child>
+ <widget class="GtkButton" id="port_properties_cancel_button">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="can_default">True</property>
+ <property name="label">gtk-cancel</property>
+ <property name="use_stock">True</property>
+ <property name="response_id">-6</property>
+ </widget>
+ </child>
+ <child>
+ <widget class="GtkButton" id="port_properties_ok_button">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="can_default">True</property>
+ <property name="has_default">True</property>
+ <property name="label">gtk-ok</property>
+ <property name="use_stock">True</property>
+ <property name="response_id">-5</property>
+ </widget>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="pack_type">GTK_PACK_END</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+ </widget>
+</glade-interface>
diff --git a/src/libs/gui/ingen_gui.gladep b/src/libs/gui/ingen_gui.gladep
new file mode 100644
index 00000000..7cd9c6ce
--- /dev/null
+++ b/src/libs/gui/ingen_gui.gladep
@@ -0,0 +1,9 @@
+<?xml version="1.0" standalone="no"?> <!--*- mode: xml -*-->
+<!DOCTYPE glade-project SYSTEM "http://glade.gnome.org/glade-project-2.0.dtd">
+
+<glade-project>
+ <name>Ingenuity</name>
+ <program_name>ingenuity</program_name>
+ <language>C++</language>
+ <gnome_support>FALSE</gnome_support>
+</glade-project>