diff options
Diffstat (limited to 'src/gui')
75 files changed, 14366 insertions, 0 deletions
diff --git a/src/gui/App.cpp b/src/gui/App.cpp new file mode 100644 index 00000000..f49d394c --- /dev/null +++ b/src/gui/App.cpp @@ -0,0 +1,440 @@ +/* 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_PATH +#include "App.hpp" +#include <cassert> +#include <string> +#include <fstream> +#include <libgnomecanvasmm.h> +#include <time.h> +#include <sys/time.h> +#include <raul/Path.hpp> +#include <flowcanvas/Connection.hpp> +#include "module/global.hpp" +#include "module/Module.hpp" +#include "module/World.hpp" +#include "interface/EngineInterface.hpp" +#include "serialisation/serialisation.hpp" +#include "client/ObjectModel.hpp" +#include "client/PatchModel.hpp" +#include "client/ClientStore.hpp" +#include "engine/Engine.hpp" +#include "NodeModule.hpp" +#include "ControlPanel.hpp" +#include "SubpatchModule.hpp" +#include "LoadPluginWindow.hpp" +#include "PatchWindow.hpp" +#include "MessagesWindow.hpp" +#include "GladeFactory.hpp" +#include "PatchTreeWindow.hpp" +#include "Configuration.hpp" +#include "ConnectWindow.hpp" +#include "ThreadedLoader.hpp" +#include "WindowFactory.hpp" +#include "Port.hpp" +#ifdef HAVE_SLV2 +#include <slv2/slv2.h> +#endif + +using namespace std; +using namespace Ingen::Client; + +namespace Ingen { namespace Client { class PluginModel; } } + +namespace Ingen { +namespace GUI { + +class Port; + + +/// Singleton instance +App* App::_instance = 0; + + +App::App(Ingen::Shared::World* world) + : _configuration(new Configuration()) + , _about_dialog(NULL) + , _window_factory(new WindowFactory()) + , _world(world) + , _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("about_win", _about_dialog); + _about_dialog->property_program_name() = "Ingen"; + + PluginModel::set_rdf_world(*world->rdf_world); + +#ifdef HAVE_SLV2 + PluginModel::set_slv2_world(world->slv2_world); +#endif +} + + +App::~App() +{ +} + +void +App::run(int argc, char** argv, Ingen::Shared::World* world) +{ + Gnome::Canvas::init(); + Gtk::Main main(argc, argv); + + if (!_instance) + _instance = new App(world); + + // Load configuration settings + _instance->configuration()->load_settings(); + _instance->configuration()->apply_settings(); + + // Set default window icon + const Glib::ustring icon_path = INGEN_DATA_DIR "/ingen.svg"; + try { + if (Glib::file_test(icon_path, Glib::FILE_TEST_EXISTS)) + Gtk::Window::set_default_icon_from_file(icon_path); + } catch (Gdk::PixbufError err) { + cerr << "Unable to load window icon " << icon_path << ": " << err.what() << endl; + } + + // Set style for embedded node GUIs + const string rc_style = + "style \"ingen_embedded_node_gui_style\" {" + " bg[NORMAL] = \"#212222\"" + " bg[ACTIVE] = \"#505050\"" + " bg[PRELIGHT] = \"#525454\"" + " bg[SELECTED] = \"#99A0A0\"" + " bg[INSENSITIVE] = \"#F03030\"" + " fg[NORMAL] = \"#FFFFFF\"" + " fg[ACTIVE] = \"#FFFFFF\"" + " fg[PRELIGHT] = \"#FFFFFF\"" + " fg[SELECTED] = \"#FFFFFF\"" + " fg[INSENSITIVE] = \"#FFFFFF\"" + "}\n" + "widget \"*ingen_embedded_node_gui_container*\" style \"ingen_embedded_node_gui_style\"\n"; + + Gtk::RC::parse_string(rc_style); + + App::instance().connect_window()->start(world); + + main.run(); + + cout << "Gtk exiting." << endl; +} + + +void +App::attach(SharedPtr<SigClientInterface> client, + SharedPtr<Raul::Deletable> handle) +{ + assert( ! _client); + assert( ! _store); + assert( ! _loader); + + _world->engine->register_client(client.get()); + + _client = client; + _handle = handle; + _store = SharedPtr<ClientStore>(new ClientStore(_world->engine, client)); + _loader = SharedPtr<ThreadedLoader>(new ThreadedLoader(_world->engine)); + + _patch_tree_window->init(*_store); + + _client->signal_response_error.connect(sigc::mem_fun(this, &App::error_response)); + _client->signal_error.connect(sigc::mem_fun(this, &App::error_message)); +} + + +void +App::detach() +{ + if (_world->engine) { + _window_factory->clear(); + _store->clear(); + + _loader.reset(); + _store.reset(); + _client.reset(); + _handle.reset(); + _world->engine.reset(); + } +} + + +const SharedPtr<Serialiser>& +App::serialiser() +{ + if (!_serialiser) { + if (!_world->serialisation_module) + _world->serialisation_module = Ingen::Shared::load_module("ingen_serialisation"); + + if (_world->serialisation_module) + _serialiser = SharedPtr<Serialiser>(Ingen::Serialisation::new_serialiser(_world, _store)); + + if (!_serialiser) + cerr << "WARNING: Failed to load ingen_serialisation module, save disabled." << endl; + } + return _serialiser; +} + + +void +App::error_response(int32_t id, const string& str) +{ + error_message(str); +} + + +void +App::error_message(const string& str) +{ + _messages_window->post(str); + + if (!_messages_window->is_visible()) + _messages_window->present(); + + _messages_window->set_urgency_hint(true); +} + + +void +App::port_activity(Port* port) +{ + std::pair<ActivityPorts::iterator, bool> inserted = _activity_ports.insert(make_pair(port, false)); + if (inserted.second) + inserted.first->second = false; + + if (port->is_output()) { + for (Port::Connections::const_iterator i = port->connections().begin(); i != port->connections().end(); ++i) { + const SharedPtr<Port> dst = PtrCast<Port>(i->lock()->dest().lock()); + if (dst) + port_activity(dst.get()); + } + } + + port->set_highlighted(true, false, true, false); +} + + +void +App::activity_port_destroyed(Port* port) +{ + ActivityPorts::iterator i = _activity_ports.find(port); + if (i != _activity_ports.end()) + _activity_ports.erase(i); + + return; +} + + +bool +App::animate() +{ + for (ActivityPorts::iterator i = _activity_ports.begin(); i != _activity_ports.end() ; ) { + ActivityPorts::iterator next = i; + ++next; + + if ((*i).second) { // saw it last time, unhighlight and pop + (*i).first->set_highlighted(false, false, true, false); + _activity_ports.erase(i); + } else { + (*i).second = true; + } + + i = next; + } + + 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::register_callbacks() +{ + Glib::signal_timeout().connect( + sigc::mem_fun(App::instance(), &App::gtk_main_iteration), 25, G_PRIORITY_DEFAULT); + + Glib::signal_timeout().connect( + sigc::mem_fun(App::instance(), &App::animate), 50, G_PRIORITY_DEFAULT); +} + + +bool +App::gtk_main_iteration() +{ + if (!_client) + return false; + + if (_world->local_engine) { + _world->local_engine->main_iteration(); + } else { + _enable_signal = false; + _client->emit_signals(); + _enable_signal = true; + } + + return true; +} + + +void +App::show_about() +{ + _about_dialog->run(); + _about_dialog->hide(); +} + + +void +App::quit() +{ + Gtk::Main::quit(); +} + + +Glib::RefPtr<Gdk::Pixbuf> +App::icon_from_path(const string& path, int size) +{ + /* If weak references to Glib::Objects are needed somewhere else it will + probably be a good idea to create a proper WeakPtr class instead of + using raw pointers, but for a single use this will do. */ + + Glib::RefPtr<Gdk::Pixbuf> buf; + if (path.length() == 0) + return buf; + + Icons::iterator iter = _icons.find(make_pair(path, size)); + + if (iter != _icons.end()) { + // we need to reference manually since the RefPtr constructor doesn't do it + iter->second->reference(); + return Glib::RefPtr<Gdk::Pixbuf>(iter->second); + } + + try { + buf = Gdk::Pixbuf::create_from_file(path, size, size); + _icons.insert(make_pair(make_pair(path, size), buf.operator->())); + buf->add_destroy_notify_callback(new pair<string, int>(path, size), + &App::icon_destroyed); + cerr << "Loaded icon " << path << " with size " << size << endl; + } catch (Glib::Error e) { + cerr << "Error loading icon: " << e.what() << endl; + } + return buf; +} + + +void* +App::icon_destroyed(void* data) +{ + pair<string, int>* p = static_cast<pair<string, int>*>(data); + cerr << "Destroyed icon " << p->first << " with size " << p->second << endl; + Icons::iterator iter = instance()._icons.find(*p); + if (iter != instance()._icons.end()) + instance()._icons.erase(iter); + + delete p; // allocated in App::icon_from_path + + return NULL; +} + + +} // namespace GUI +} // namespace Ingen + diff --git a/src/gui/App.hpp b/src/gui/App.hpp new file mode 100644 index 00000000..4a4b6580 --- /dev/null +++ b/src/gui/App.hpp @@ -0,0 +1,173 @@ +/* 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 INGEN_APP_HPP +#define INGEN_APP_HPP + +#include <cassert> +#include <string> +#include <map> +#include <utility> +#include <iostream> +#include <libgnomecanvasmm.h> +#include <gtkmm.h> +#include <libglademm.h> +#include <raul/SharedPtr.hpp> +#include <raul/Deletable.hpp> +#include <module/World.hpp> + +namespace Ingen { + class Engine; + namespace Shared { + class EngineInterface; + class ClientInterface; + class World; + } + namespace Client { + class PatchModel; + class PluginModel; + class ClientStore; + class SigClientInterface; + } + namespace Serialisation { + class Serialiser; + } +} + +/** \defgroup GUI GTK GUI + */ + +namespace Ingen { +namespace GUI { + +class MessagesWindow; +class PatchCanvas; +class PatchTreeView; +class PatchTreeWindow; +class ConnectWindow; +class Configuration; +class ThreadedLoader; +class WindowFactory; +class Port; + + +/** 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 std::string& msg); + + void attach(SharedPtr<Client::SigClientInterface> client, + SharedPtr<Raul::Deletable> handle=SharedPtr<Raul::Deletable>()); + + void detach(); + + void register_callbacks(); + bool gtk_main_iteration(); + + void show_about(); + void quit(); + + void port_activity(Port* port); + void activity_port_destroyed(Port* port); + + bool signal() const { return _enable_signal; } + bool disable_signals() { bool old = _enable_signal; _enable_signal = false; return old; } + void enable_signals(bool b) { _enable_signal = b; } + + ConnectWindow* connect_window() const { return _connect_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; } + + Glib::RefPtr<Gdk::Pixbuf> icon_from_path(const std::string& path, int size); + + const SharedPtr<Shared::EngineInterface>& engine() const { return _world->engine; } + const SharedPtr<Client::SigClientInterface>& client() const { return _client; } + const SharedPtr<Client::ClientStore>& store() const { return _store; } + const SharedPtr<ThreadedLoader>& loader() const { return _loader; } + + const SharedPtr<Serialisation::Serialiser>& serialiser(); + + static inline App& instance() { assert(_instance); return *_instance; } + + static void run(int argc, char** argv, Ingen::Shared::World* world); + + Ingen::Shared::World* world() { return _world; } + +protected: + + /** This is needed for the icon map. */ + template <typename A, typename B> + struct LexicalCompare { + bool operator()(const std::pair<A, B>& p1, const std::pair<A, B>& p2) { + return (p1.first < p2.first) || + ((p1.first == p2.first) && (p1.second < p2.second)); + } + }; + + typedef std::map< std::pair<std::string, int>, + Gdk::Pixbuf*, + LexicalCompare<std::string, int> > Icons; + Icons _icons; + + App(Ingen::Shared::World* world); + + bool animate(); + void error_response(int32_t id, const std::string& str); + + static void* icon_destroyed(void* data); + + static App* _instance; + + SharedPtr<Client::SigClientInterface> _client; + SharedPtr<Raul::Deletable> _handle; + SharedPtr<Client::ClientStore> _store; + SharedPtr<Serialisation::Serialiser> _serialiser; + SharedPtr<ThreadedLoader> _loader; + + Configuration* _configuration; + + ConnectWindow* _connect_window; + MessagesWindow* _messages_window; + PatchTreeWindow* _patch_tree_window; + Gtk::AboutDialog* _about_dialog; + WindowFactory* _window_factory; + + Ingen::Shared::World* _world; + + typedef std::map<Port*, bool> ActivityPorts; + ActivityPorts _activity_ports; + + bool _enable_signal; +}; + + +} // namespace GUI +} // namespace Ingen + +#endif // INGEN_APP_HPP + diff --git a/src/gui/BreadCrumb.hpp b/src/gui/BreadCrumb.hpp new file mode 100644 index 00000000..c3b05e01 --- /dev/null +++ b/src/gui/BreadCrumb.hpp @@ -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.hpp> +#include <raul/SharedPtr.hpp> +#include "PatchView.hpp" + +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().c_str(); + 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/gui/BreadCrumbBox.cpp b/src/gui/BreadCrumbBox.cpp new file mode 100644 index 00000000..7edaed9f --- /dev/null +++ b/src/gui/BreadCrumbBox.cpp @@ -0,0 +1,210 @@ +/* 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.hpp" +#include "BreadCrumb.hpp" +#include "App.hpp" +#include "client/SigClientInterface.hpp" +namespace Ingen { +namespace GUI { + + +BreadCrumbBox::BreadCrumbBox() + : Gtk::HBox() + , _active_path("/") + , _full_path("/") + , _enable_signal(true) +{ + App::instance().client()->signal_object_destroyed.connect( + sigc::mem_fun(this, &BreadCrumbBox::object_destroyed)); +} + + +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_destroyed(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/gui/BreadCrumbBox.hpp b/src/gui/BreadCrumbBox.hpp new file mode 100644 index 00000000..b5650252 --- /dev/null +++ b/src/gui/BreadCrumbBox.hpp @@ -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.hpp> +#include <raul/SharedPtr.hpp> +#include "PatchView.hpp" + +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_destroyed(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/gui/Configuration.cpp b/src/gui/Configuration.cpp new file mode 100644 index 00000000..758dc744 --- /dev/null +++ b/src/gui/Configuration.cpp @@ -0,0 +1,109 @@ +/* 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.hpp" +#include <cstdlib> +#include <cassert> +#include <iostream> +#include <fstream> +#include <map> +#include "client/PortModel.hpp" +#include "client/PluginModel.hpp" +#include "serialisation/Parser.hpp" +#include "App.hpp" + +using namespace std; + +namespace Ingen { +namespace GUI { + +using namespace Ingen::Client; + + +Configuration::Configuration() + // Colours from the Tango palette with modified V and alpha + : _name_style(HUMAN) + , _audio_port_color( 0x244678C0) + , _control_port_color(0x4A8A0EC0) + , _event_port_color( 0x960909C0) +// , _midi_port_color( 0x960909C0) +// , _osc_port_color( 0x5C3566C0) +{ +} + + +Configuration::~Configuration() +{ +} + + +/** Loads settings from the rc file. Passing no parameter will load from + * the default location. + */ +void +Configuration::load_settings(string filename) +{ + /* ... */ +} + + +/** Saves settings to rc file. Passing no parameter will save to the + * default location. + */ +void +Configuration::save_settings(string filename) +{ + /* ... */ +} + + +/** Applies the current loaded settings to whichever parts of the app + * need updating. + */ +void +Configuration::apply_settings() +{ + /* ... */ +} + + +uint32_t +Configuration::get_port_color(const PortModel* p) +{ + assert(p != NULL); + + if (p->type().is_control()) { + return _control_port_color; + } else if (p->type().is_audio()) { + return _audio_port_color; + } else if (p->type().is_event()) { + return _event_port_color; + }/* else if (p->type().is_midi()) { + return _midi_port_color; + } else if (p->type().is_osc()) { + return _osc_port_color; + }*/ + + cerr << "[Configuration] Unknown port type " << p->type().uri() + << ", port will appear black." << endl; + + return 0x000000FF; +} + + +} // namespace GUI +} // namespace Ingen diff --git a/src/gui/Configuration.hpp b/src/gui/Configuration.hpp new file mode 100644 index 00000000..124b41c8 --- /dev/null +++ b/src/gui/Configuration.hpp @@ -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 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 { + + +/** 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(); + + const string& patch_folder() { return _patch_folder; } + void set_patch_folder(const string& f) { _patch_folder = f; } + + uint32_t get_port_color(const PortModel* pi); + + enum NameStyle { PATH, HUMAN }; + + NameStyle name_style() const { return _name_style; } + void set_name_style(NameStyle s) { _name_style = s; } + +private: + /** Most recent patch folder shown in open dialog */ + string _patch_folder; + + NameStyle _name_style; + + uint32_t _audio_port_color; + uint32_t _control_port_color; + uint32_t _event_port_color; + //uint32_t _midi_port_color; + //uint32_t _osc_port_color; +}; + + +} // namespace GUI +} // namespace Ingen + +#endif // CONFIG_H + + diff --git a/src/gui/ConnectWindow.cpp b/src/gui/ConnectWindow.cpp new file mode 100644 index 00000000..5600a0b2 --- /dev/null +++ b/src/gui/ConnectWindow.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 <string> +#include <time.h> +#include <sys/time.h> +#include <stdlib.h> +#include <sys/time.h> +#include <sys/resource.h> +#include <raul/Process.hpp> +#include CONFIG_H_PATH +#include "interface/EngineInterface.hpp" +#include "module/World.hpp" +#include "engine/tuning.hpp" +#include "engine/Engine.hpp" +#include "engine/QueuedEngineInterface.hpp" +#include "client/OSCClientReceiver.hpp" +#include "client/HTTPClientReceiver.hpp" +#include "client/OSCEngineSender.hpp" +#include "client/ThreadedSigClientInterface.hpp" +#include "client/ClientStore.hpp" +#include "client/PatchModel.hpp" +#include "module/Module.hpp" +#include "App.hpp" +#include "WindowFactory.hpp" +#include "ConnectWindow.hpp" +using Ingen::QueuedEngineInterface; +using Ingen::Client::ThreadedSigClientInterface; +using namespace std; + +namespace Ingen { +namespace GUI { + + +// ConnectWindow + + +ConnectWindow::ConnectWindow(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& xml) + : Gtk::Dialog(cobject) + , _xml(xml) + , _mode(CONNECT_REMOTE) + , _ping_id(-1) + , _attached(false) + , _widgets_loaded(false) + , _connect_stage(0) + , _new_engine(NULL) +{ +} + + +void +ConnectWindow::start(Ingen::Shared::World* world) +{ + if (world->local_engine) { + _mode = INTERNAL; + if (_widgets_loaded) + _internal_radio->set_active(true); + } + + set_connected_to(world->engine); + + connect(true); +} + + +void +ConnectWindow::set_connected_to(SharedPtr<Shared::EngineInterface> engine) +{ + App::instance().world()->engine = engine; + + if (!_widgets_loaded) + return; + + if (engine) { + _icon->set(Gtk::Stock::CONNECT, Gtk::ICON_SIZE_LARGE_TOOLBAR); + _progress_bar->set_fraction(1.0); + _progress_label->set_text("Connected to engine"); + _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); + } else { + _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); + + if (_new_engine) + _internal_radio->set_sensitive(true); + else + _internal_radio->set_sensitive(false); + + _server_radio->set_sensitive(true); + _launch_radio->set_sensitive(true); + + if (_mode == CONNECT_REMOTE ) + _url_entry->set_sensitive(true); + else if (_mode == LAUNCH_REMOTE ) + _port_spinbutton->set_sensitive(true); + + _progress_label->set_text(string("Disconnected")); + } +} + + +void +ConnectWindow::set_connecting_widget_states() +{ + if (!_widgets_loaded) + return; + + _connect_button->set_sensitive(false); + _disconnect_button->set_label("gtk-cancel"); + _disconnect_button->set_sensitive(true); + _server_radio->set_sensitive(false); + _launch_radio->set_sensitive(false); + _internal_radio->set_sensitive(false); + _url_entry->set_sensitive(false); + _port_spinbutton->set_sensitive(false); +} + + +/** Launch (if applicable) and connect to the Engine. + * + * This will create the EngineInterface and ClientInterface and initialize + * the App with them. + */ +void +ConnectWindow::connect(bool existing) +{ + if (_attached) + _attached = false; + + assert(!App::instance().client()); + + _connect_stage = 0; + set_connecting_widget_states(); + + Ingen::Shared::World* world = App::instance().world(); + + if (_mode == CONNECT_REMOTE) { + if (!existing) { + const string url = (_widgets_loaded ? (string)_url_entry->get_text() : world->engine->uri()); + world->engine = SharedPtr<EngineInterface>(new OSCEngineSender(url)); + } + + SharedPtr<ThreadedSigClientInterface> tsci(new ThreadedSigClientInterface(1024)); + SharedPtr<Raul::Deletable> client; + + const string& uri = world->engine->uri(); + const string& scheme = uri.substr(0, uri.find(":")); + if (scheme == "osc.udp" || scheme == "osc.tcp") + client = SharedPtr<OSCClientReceiver>(new OSCClientReceiver(16181, tsci)); // FIXME: port + else if (scheme == "http") + client = SharedPtr<HTTPClientReceiver>(new HTTPClientReceiver(world, uri, tsci)); + + App::instance().attach(tsci, client); + App::instance().register_callbacks(); + + Glib::signal_timeout().connect( + sigc::mem_fun(this, &ConnectWindow::gtk_callback), 40); + + } 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 -e --engine-port=").append(port_str); + + if (Raul::Process::launch(cmd)) { + world->engine = SharedPtr<EngineInterface>( + new OSCEngineSender(string("osc.udp://localhost:").append(port_str))); + + // FIXME: static args + SharedPtr<ThreadedSigClientInterface> tsci(new ThreadedSigClientInterface(1024)); + SharedPtr<OSCClientReceiver> client(new OSCClientReceiver(16181, tsci)); + + App::instance().attach(tsci, client); + App::instance().register_callbacks(); + + Glib::signal_timeout().connect( + sigc::mem_fun(this, &ConnectWindow::gtk_callback), 40); + + } else { + cerr << "Failed to launch ingen process." << endl; + } + + } else if (_mode == INTERNAL) { + Ingen::Shared::World* world = App::instance().world(); + if ( ! world->local_engine) { + assert(_new_engine); + world->local_engine = SharedPtr<Engine>(_new_engine(world)); + } + + if ( ! world->engine) + world->engine = world->local_engine->new_queued_interface(); + + SharedPtr<SigClientInterface> client(new SigClientInterface()); + + world->local_engine->start_jack_driver(); + world->local_engine->activate(1); // FIXME: parallelism + + App::instance().attach(client); + App::instance().register_callbacks(); + + Glib::signal_timeout().connect( + sigc::mem_fun(this, &ConnectWindow::gtk_callback), 10); + } +} + + +void +ConnectWindow::disconnect() +{ + _connect_stage = -1; + _attached = false; + + App::instance().detach(); + set_connected_to(SharedPtr<Ingen::Shared::EngineInterface>()); + + if (!_widgets_loaded) + return; + + _progress_bar->set_fraction(0.0); + _connect_button->set_sensitive(false); + _disconnect_button->set_sensitive(false); + + _connect_button->set_sensitive(true); + _disconnect_button->set_sensitive(false); +} + + +void +ConnectWindow::on_show() +{ + if (!_widgets_loaded) { + load_widgets(); + if (_attached) + set_connected_to(App::instance().engine()); + } + + Gtk::Dialog::on_show(); +} + + +void +ConnectWindow::load_widgets() +{ + _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::bind( + sigc::mem_fun(this, &ConnectWindow::connect), false)); + _quit_button->signal_clicked().connect(sigc::mem_fun(this, &ConnectWindow::quit)); + + _progress_bar->set_pulse_step(0.01); + _widgets_loaded = true; + + _engine_module = Ingen::Shared::load_module("ingen_engine"); + if (!_engine_module) + cerr << "Unable to load ingen_engine module, internal engine unavailable." << 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(); + } + + server_toggled(); +} + + +void +ConnectWindow::on_hide() +{ + Gtk::Dialog::on_hide(); + if (!_attached) + Gtk::Main::quit(); +} + + +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() +{ + /* If I call this a "state machine" it's not ugly code any more */ + + // Timing stuff for repeated attach attempts + timeval now; + gettimeofday(&now, NULL); + static const timeval start = now; + static timeval last = now; + + // Show if attempted connection goes on for a noticeable amount of time + if (!is_visible()) { + const float ms_since_start = (now.tv_sec - start.tv_sec) * 1000.0f + + (now.tv_usec - start.tv_usec) * 0.001f; + if (ms_since_start > 500) { + present(); + set_connecting_widget_states(); + } + } + + /* Connecting to engine */ + if (_connect_stage == 0) { + + _attached = false; + + assert(App::instance().engine()); + assert(App::instance().client()); + + App::instance().client()->signal_response_ok.connect( + sigc::mem_fun(this, &ConnectWindow::on_response)); + + _ping_id = abs(rand()) / 2 * 2; // avoid -1 + App::instance().engine()->set_next_response_id(_ping_id); + App::instance().engine()->ping(); + + if (_widgets_loaded) { + _progress_label->set_text("Connecting to engine..."); + _progress_bar->set_pulse_step(0.01); + } + + ++_connect_stage; + + } else if (_connect_stage == 1) { + if (_attached || App::instance().client()->enabled()) { + 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) { + App::instance().engine()->request_all_objects(); + if (_widgets_loaded) + _progress_label->set_text(string("Requesting root patch...")); + ++_connect_stage; + } else if (_connect_stage == 3) { + if (App::instance().store()->size() > 0) { + SharedPtr<PatchModel> root = PtrCast<PatchModel>(App::instance().store()->object("/")); + if (root) { + set_connected_to(App::instance().engine()); + App::instance().window_factory()->present_patch(root); + App::instance().engine()->load_plugins(); + if (_widgets_loaded) + _progress_label->set_text(string("Loading plugins...")); + ++_connect_stage; + } + } + } else if (_connect_stage == 4) { + App::instance().engine()->request_plugins(); + hide(); + if (_widgets_loaded) + _progress_label->set_text("Connected to engine"); + _connect_stage = 0; // set ourselves up for next time (if there is one) + return false; // deregister this callback + } + + if (_widgets_loaded) + _progress_bar->pulse(); + + if (_connect_stage == -1) { // we were cancelled + if (_widgets_loaded) { + _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/gui/ConnectWindow.hpp b/src/gui/ConnectWindow.hpp new file mode 100644 index 00000000..fb75d4b2 --- /dev/null +++ b/src/gui/ConnectWindow.hpp @@ -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 CONNECT_WINDOW_H +#define CONNECT_WINDOW_H + +#include CONFIG_H_PATH + +#ifdef HAVE_SLV2 +#include <slv2/slv2.h> +#endif + +#include <gtkmm.h> +#include <libglademm/xml.h> +#include <libglademm.h> +#include <raul/SharedPtr.hpp> +#include "client/ThreadedSigClientInterface.hpp" +using Ingen::Client::SigClientInterface; + +namespace Ingen { class Engine; class QueuedEngineInterface; } + +namespace Ingen { +namespace GUI { + +class App; + + +/** 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 set_connected_to(SharedPtr<Shared::EngineInterface> engine); + void start(Ingen::Shared::World* world); + void on_response(int32_t id) { _attached = true; } + +private: + enum Mode { CONNECT_REMOTE, LAUNCH_REMOTE, INTERNAL }; + + void server_toggled(); + void launch_toggled(); + void internal_toggled(); + + void disconnect(); + void connect(bool existing); + void quit(); + void on_show(); + void on_hide(); + + void load_widgets(); + void set_connecting_widget_states(); + + bool gtk_callback(); + + const Glib::RefPtr<Gnome::Glade::Xml> _xml; + + Mode _mode; + int32_t _ping_id; + bool _attached; + + bool _widgets_loaded; + int _connect_stage; + + SharedPtr<Glib::Module> _engine_module; + Ingen::Engine* (*_new_engine)(Ingen::Shared::World* world); + + 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/gui/Connection.hpp b/src/gui/Connection.hpp new file mode 100644 index 00000000..55378891 --- /dev/null +++ b/src/gui/Connection.hpp @@ -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 GUI_CONNECTION_H +#define GUI_CONNECTION_H + +#include <cassert> +#include <string> +#include <flowcanvas/Connection.hpp> +#include <raul/SharedPtr.hpp> +#include "client/ConnectionModel.hpp" +using Ingen::Client::ConnectionModel; + +namespace Ingen { +namespace GUI { + + +/** A Connection in a Patch. + * + * \ingroup GUI + */ +class Connection : public FlowCanvas::Connection +{ +public: + Connection(boost::shared_ptr<FlowCanvas::Canvas> canvas, + boost::shared_ptr<ConnectionModel> model, + boost::shared_ptr<FlowCanvas::Connectable> src, + boost::shared_ptr<FlowCanvas::Connectable> dst, + uint32_t color) + : FlowCanvas::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 // GUI_CONNECTION_H diff --git a/src/gui/ControlPanel.cpp b/src/gui/ControlPanel.cpp new file mode 100644 index 00000000..7da07dbb --- /dev/null +++ b/src/gui/ControlPanel.cpp @@ -0,0 +1,269 @@ +/* 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.hpp" +#include "client/PatchModel.hpp" +#include "client/NodeModel.hpp" +#include "client/PortModel.hpp" +#include "client/PluginModel.hpp" +#include "App.hpp" +#include "ControlPanel.hpp" +#include "Controls.hpp" +#include "GladeFactory.hpp" + +using namespace std; + +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)); + + show_all(); +} + + +ControlPanel::~ControlPanel() +{ + for (vector<Control*>::iterator i = _controls.begin(); i != _controls.end(); ++i) + delete (*i); +} + + +void +ControlPanel::init(SharedPtr<NodeModel> node, uint32_t poly) +{ + assert(node != NULL); + assert(poly > 0); + + if (node->polyphonic()) { + _voice_spinbutton->set_range(0, poly - 1); + _voice_control_box->show(); + } else { + _voice_control_box->hide(); + } + + for (NodeModel::Ports::const_iterator i = node->ports().begin(); i != node->ports().end(); ++i) { + add_port(*i); + } + + node->signal_property.connect(bind( + sigc::mem_fun(this, &ControlPanel::property_changed), false)); + + if (node->parent()) { + node->signal_property.connect(bind( + sigc::mem_fun(this, &ControlPanel::property_changed), true)); + } else { + cerr << "[ControlPanel] No parent, polyphonic controls disabled" << endl; + } + + _callback_enabled = true; +} + + +Control* +ControlPanel::find_port(const Path& path) const +{ + for (vector<Control*>::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->type().is_control() && pm->is_input()) { + Control* control = NULL; + + if (pm->is_toggle()) { + ToggleControl* tc; + Glib::RefPtr<Gnome::Glade::Xml> xml = GladeFactory::new_glade_reference("toggle_control"); + xml->get_widget_derived("toggle_control", tc); + control = tc; + } else { + SliderControl* sc; + Glib::RefPtr<Gnome::Glade::Xml> xml = GladeFactory::new_glade_reference("control_strip"); + xml->get_widget_derived("control_strip", sc); + control = sc; + } + + control->init(this, pm); + + if (_controls.size() > 0) + _control_box->pack_start(*Gtk::manage(new Gtk::HSeparator()), false, false, 4); + + _controls.push_back(control); + _control_box->pack_start(*control, false, false, 0); + + control->enable(); + control->show(); + } + + 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<Control*>::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<Control*>::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<Control*>::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<Control*>::iterator i = _controls.begin(); i != _controls.end(); ++i) { + if ((*i)->port_model()->path() == path) { + (*i)->disable(); + return; + } + } +} +#endif + +/** Callback for Controls to notify this of a change. + */ +void +ControlPanel::value_changed(SharedPtr<PortModel> port, float val) +{ + if (_callback_enabled) { + if (_all_voices_radio->get_active()) { + App::instance().engine()->set_port_value(port->path(), Atom(val)); + port->value(val); + } else { + int voice = _voice_spinbutton->get_value_as_int() - 1; + App::instance().engine()->set_voice_value(port->path(), voice, Atom(val)); + port->value(val); + } + } +} + +void +ControlPanel::all_voices_selected() +{ + _voice_spinbutton->property_sensitive() = false; +} + + +void +ControlPanel::specific_voice_selected() +{ + _voice_spinbutton->property_sensitive() = true; +} + + +void +ControlPanel::property_changed(const std::string& predicate, const Raul::Atom& value, bool parent) +{ + if (!parent && predicate == "ingen:polyphonic" && value.type() == Atom::BOOL) { + if (value.get_bool()) + _voice_control_box->show(); + else + _voice_control_box->hide(); + } else if (parent && predicate == "ingen:polyphony" && value.type() == Atom::INT) { + _voice_spinbutton->set_range(0, value.get_int32() - 1); + } +} + + +} // namespace GUI +} // namespace Ingen diff --git a/src/gui/ControlPanel.hpp b/src/gui/ControlPanel.hpp new file mode 100644 index 00000000..8af5a728 --- /dev/null +++ b/src/gui/ControlPanel.hpp @@ -0,0 +1,91 @@ +/* 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.hpp> +#include "Controls.hpp" + +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, uint32_t poly); + + Control* 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(); } + std::pair<int,int> ideal_size() const { return _ideal_size; } + + // Callback for Control + void value_changed(SharedPtr<PortModel> port_path, float val); + +private: + void all_voices_selected(); + void specific_voice_selected(); + + void property_changed(const std::string& predicate, const Raul::Atom& value, bool parent); + + bool _callback_enabled; + + std::pair<int,int> _ideal_size; + + std::vector<Control*> _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/gui/Controls.cpp b/src/gui/Controls.cpp new file mode 100644 index 00000000..cc5383a8 --- /dev/null +++ b/src/gui/Controls.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 CONFIG_H_PATH + +#include <cmath> +#include <iostream> +#include <algorithm> +#include "interface/EngineInterface.hpp" +#include "client/PluginModel.hpp" +#include "client/NodeModel.hpp" +#include "client/PortModel.hpp" +#include "Controls.hpp" +#include "ControlPanel.hpp" +#include "PortPropertiesWindow.hpp" +#include "GladeFactory.hpp" +#include "App.hpp" + +using namespace std; +using namespace Ingen::Client; + +namespace Ingen { +namespace GUI { + + +// ////////////////////// Control ///////////////////////////////// // + +Control::Control(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& glade_xml) + : Gtk::VBox(cobject) + , _control_panel(NULL) + , _enable_signal(false) +{ + 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, &SliderControl::menu_properties)); +} + + +Control::~Control() +{ + _enable_signal = false; + _control_connection.disconnect(); + _port_model.reset(); +} + + +void +Control::init(ControlPanel* panel, SharedPtr<PortModel> pm) +{ + _control_panel = panel; + _port_model = pm, + + assert(_port_model); + assert(panel); + + _control_connection = pm->signal_value_changed.connect(sigc::mem_fun(this, &Control::set_value)); +} + + +void +Control::menu_properties() +{ + Glib::RefPtr<Gnome::Glade::Xml> xml = GladeFactory::new_glade_reference(); + + PortPropertiesWindow* window; + xml->get_widget_derived("port_properties_win", window); + window->present(_port_model); +} + + +// ////////////////// SliderControl ////////////////////// // + + +SliderControl::SliderControl(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& xml) + : Control(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); +} + + +void +SliderControl::init(ControlPanel* panel, SharedPtr<PortModel> pm) +{ + _enable_signal = false; + _enabled = true; + + Control::init(panel, pm); + + assert(_name_label); + assert(_slider); + + set_name(pm->path().name()); + + _slider->set_draw_value(false); + + signal_button_press_event().connect(sigc::mem_fun(*this, &SliderControl::clicked)); + _slider->signal_button_press_event().connect(sigc::mem_fun(*this, &SliderControl::clicked)); + + _slider->signal_event().connect( + sigc::mem_fun(*this, &SliderControl::slider_pressed)); + + _slider->signal_value_changed().connect( + sigc::mem_fun(*this, &SliderControl::update_value_from_slider)); + + _value_spinner->signal_value_changed().connect( + sigc::mem_fun(*this, &SliderControl::update_value_from_spinner)); + + float min = 0.0f, max = 1.0f; + + boost::shared_ptr<NodeModel> parent = PtrCast<NodeModel>(_port_model->parent()); + if (parent) + parent->port_value_range(_port_model, min, max); + + if (pm->is_integer() || pm->is_toggle()) { + _slider->set_increments(1, 10); + _value_spinner->set_digits(0); + } else { + _slider->set_increments(0, 0); + } + + pm->signal_variable.connect(sigc::mem_fun(this, &SliderControl::port_variable_change)); + + _slider->set_range(std::min(min, pm->value().get_float()), std::max(max, pm->value().get_float())); + //_value_spinner->set_range(min, max); + + set_value(pm->value()); + + _enable_signal = true; + + show_all(); +} + + +bool +SliderControl::clicked(GdkEventButton* ev) +{ + if (ev->button == 3) { + _menu->popup(ev->button, ev->time); + return true; + } else { + return false; + } +} + + +void +SliderControl::set_value(const Atom& atom) +{ + float val = atom.get_float(); + + if (_port_model->is_integer()) + val = lrintf(val); + + _enable_signal = false; + if (_enabled) { + if (_slider->get_value() != val) { + const Gtk::Adjustment* range = _slider->get_adjustment(); + const float lower = range->get_lower(); + const float upper = range->get_upper(); + if (val < lower || val > upper) + set_range(min(lower, val), max(lower, val)); + _slider->set_value(val); + } + if (_value_spinner->get_value() != val) + _value_spinner->set_value(val); + } + _enable_signal = true; +} + + +void +SliderControl::port_variable_change(const string& key, const Atom& value) +{ + if ( (key == "ingen:minimum") && value.type() == Atom::FLOAT) + set_range(value.get_float(), _slider->get_adjustment()->get_upper()); + else if ( (key == "ingen:maximum") && value.type() == Atom::FLOAT) + set_range(_slider->get_adjustment()->get_lower(), value.get_float()); +} + + +void +SliderControl::set_range(float min, float max) +{ + _slider->set_range(min, max); + //_value_spinner->set_range(min, max); +} + + +void +SliderControl::set_name(const string& name) +{ + string name_label = "<span weight=\"bold\">"; + name_label += name + "</span>"; + _name_label->set_markup(name_label); +} + + +void +SliderControl::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 +SliderControl::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 +SliderControl::update_value_from_slider() +{ + if (_enable_signal) { + float value = _slider->get_value(); + bool change = true; + + _enable_signal = false; + + if (_port_model->is_integer()) { + value = lrintf(value); + if (value == lrintf(_port_model->value().get_float())) + change = false; + } + + if (change) { + _value_spinner->set_value(value); + _control_panel->value_changed(_port_model, value); + } + + _enable_signal = true; + } +} + + +void +SliderControl::update_value_from_spinner() +{ + if (_enable_signal) { + _enable_signal = false; + const float value = _value_spinner->get_value(); + + 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 +SliderControl::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; +} + + +// ///////////// IntegerControl ////////////// // + +#if 0 +IntegerControl::IntegerControl(ControlPanel* panel, SharedPtr<PortModel> pm) +: Control(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, &IntegerControl::update_value)); + _spinner.set_increments(1, 10); + + _alignment.add(_spinner); + pack_start(_name_label); + pack_start(_alignment); + + _enable_signal = true; + + show_all(); +} + + +void +IntegerControl::set_name(const string& name) +{ + string name_label = "<span weight=\"bold\">"; + name_label += name + "</span>"; + _name_label->set_markup(name_label); +} + + +void +IntegerControl::set_value(float val) +{ + //cerr << "[IntegerControl] Setting value to " << val << endl; + _enable_signal = false; + _spinner.set_value(val); + _enable_signal = true; +} + + +void +IntegerControl::enable() +{ + _spinner.property_sensitive() = true; + _name_label->property_sensitive() = true; +} + + +void +IntegerControl::disable() +{ + _spinner.property_sensitive() = false; + _name_label->property_sensitive() = false; +} + + +void +IntegerControl::update_value() +{ + if (_enable_signal) { + float value = _spinner.get_value(); + _control_panel->value_changed(_port_model, value); + //m_port_model->value(value); + } +} +#endif + + +// ///////////// ToggleControl ////////////// // + + +ToggleControl::ToggleControl(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& xml) + : Control(cobject, xml) +{ + xml->get_widget("toggle_control_name_label", _name_label); + xml->get_widget("toggle_control_check", _checkbutton); +} + + +void +ToggleControl::init(ControlPanel* panel, SharedPtr<PortModel> pm) +{ + _enable_signal = false; + + Control::init(panel, pm); + + assert(_name_label); + assert(_checkbutton); + + set_name(pm->path().name()); + + _checkbutton->signal_toggled().connect(sigc::mem_fun(*this, &ToggleControl::toggled)); + set_value(pm->value()); + + _enable_signal = true; + show_all(); +} + + +void +ToggleControl::set_name(const string& name) +{ + string name_label = "<span weight=\"bold\">"; + name_label += name + "</span>"; + _name_label->set_markup(name_label); +} + + +void +ToggleControl::set_value(const Atom& val) +{ + bool enable = false; + switch (val.type()) { + case Atom::FLOAT: + enable = (val.get_float() != 0.0f); + break; + case Atom::INT: + enable = (val.get_int32() != 0); + break; + case Atom::BOOL: + enable = (val.get_bool()); + default: + cerr << "Unsupported value type for toggle control" << endl; + } + + _enable_signal = false; + _checkbutton->set_active(enable); + _enable_signal = true; +} + + +void +ToggleControl::enable() +{ + _checkbutton->property_sensitive() = true; + _name_label->property_sensitive() = true; +} + + +void +ToggleControl::disable() +{ + _checkbutton->property_sensitive() = false; + _name_label->property_sensitive() = false; +} + + +void +ToggleControl::toggled() +{ + if (_enable_signal) { + float value = _checkbutton->get_active() ? 1.0f : 0.0f; + _control_panel->value_changed(_port_model, value); + //m_port_model->value(value); + } +} + + +} // namespace GUI +} // namespace Ingen diff --git a/src/gui/Controls.hpp b/src/gui/Controls.hpp new file mode 100644 index 00000000..8a9c4064 --- /dev/null +++ b/src/gui/Controls.hpp @@ -0,0 +1,164 @@ +/* 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.hpp" +#include <raul/SharedPtr.hpp> + +namespace Ingen { namespace Client { class PortModel; } } +using namespace Ingen::Client; + +namespace Ingen { +namespace GUI { + +class ControlPanel; + + +/** A group of controls (for a single Port) in a ControlPanel. + * + * \ingroup GUI + */ +class Control : public Gtk::VBox +{ +public: + Control(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& glade_xml); + virtual ~Control(); + + virtual void init(ControlPanel* panel, SharedPtr<PortModel> pm); + + virtual void enable() = 0; + virtual void disable() = 0; + + inline const SharedPtr<PortModel> port_model() const { return _port_model; } + +protected: + virtual void set_value(const Atom& value) = 0; + virtual void set_range(float min, float max) {} + + void menu_properties(); + + ControlPanel* _control_panel; + SharedPtr<PortModel> _port_model; + sigc::connection _control_connection; + bool _enable_signal; + + Gtk::Menu* _menu; + Gtk::MenuItem* _menu_properties; +}; + + +/** A slider for a continuous control. + * + * \ingroup GUI + */ +class SliderControl : public Control +{ +public: + SliderControl(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); + void set_value(const Atom& value); + void set_range(float min, float max); + + void port_variable_change(const string& key, const Raul::Atom& value); + + void update_range(); + void update_value_from_slider(); + void update_value_from_spinner(); + + bool slider_pressed(GdkEvent* ev); + bool clicked(GdkEventButton* ev); + + bool _enabled; + + Gtk::Label* _name_label; + Gtk::SpinButton* _value_spinner; + Gtk::HScale* _slider; +}; + + +#if 0 + +/** A spinbutton for integer controls. + * + * \ingroup GUI + */ +class IntegerControl : public Control +{ +public: + IntegerControl(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; +}; +#endif + + +/** A radio button for toggle controls. + * + * \ingroup GUI + */ +class ToggleControl : public Control +{ +public: + ToggleControl(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& xml); + + void init(ControlPanel* panel, SharedPtr<PortModel> pm); + + void enable(); + void disable(); + +private: + void set_name(const string& name); + void set_value(const Atom& value); + + void toggled(); + + Gtk::Label* _name_label; + Gtk::CheckButton* _checkbutton; +}; + + +} // namespace GUI +} // namespace Ingen + +#endif // CONTROLGROUPS_H diff --git a/src/gui/GladeFactory.cpp b/src/gui/GladeFactory.cpp new file mode 100644 index 00000000..2b4d3ec9 --- /dev/null +++ b/src/gui/GladeFactory.cpp @@ -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 + */ + +#include "GladeFactory.hpp" +#include <iostream> +#include <fstream> + +using namespace std; + +namespace Ingen { +namespace GUI { + + +Glib::ustring GladeFactory::glade_filename = ""; + + +void +GladeFactory::find_glade_file() +{ + char* env_path = getenv("INGEN_GLADE_PATH"); + if (env_path) + glade_filename = env_path; + else + glade_filename = "./ingen_gui.glade"; + + ifstream fs(glade_filename.c_str()); + if (fs.fail()) { // didn't find it, check INGEN_DATA_DIR + fs.clear(); + glade_filename = INGEN_DATA_DIR; + glade_filename += "/ingen_gui.glade"; + + fs.open(glade_filename.c_str()); + if (fs.fail()) { + cerr << "[GladeFactory] Unable to find ingen_gui.glade in current directory or " << INGEN_DATA_DIR << "." << 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/gui/GladeFactory.hpp b/src/gui/GladeFactory.hpp new file mode 100644 index 00000000..6da2da5f --- /dev/null +++ b/src/gui/GladeFactory.hpp @@ -0,0 +1,47 @@ +/* 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> + +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 std::string& toplevel_widget = ""); + +private: + static void find_glade_file(); + static Glib::ustring glade_filename; +}; + + +} // namespace GUI +} // namespace Ingen + +#endif // GLADEFACTORY_H diff --git a/src/gui/LoadPatchWindow.cpp b/src/gui/LoadPatchWindow.cpp new file mode 100644 index 00000000..70d3ec5b --- /dev/null +++ b/src/gui/LoadPatchWindow.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 <sys/types.h> +#include <dirent.h> +#include <boost/optional/optional.hpp> +#include "LoadPatchWindow.hpp" +#include "interface/EngineInterface.hpp" +#include "client/PatchModel.hpp" +#include "App.hpp" +#include "Configuration.hpp" +#include "ThreadedLoader.hpp" + +using namespace Ingen::Serialisation; +using boost::optional; +using namespace std; + +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 = INGEN_DATA_DIR; + 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, GraphObject::Variables 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 (!_patch) + return; + + // If unset load_patch will load value + optional<Path> parent; + optional<Symbol> symbol; + + if (_poly_from_user_radio->get_active()) + _initial_data.insert(make_pair("ingen:polyphony", _poly_spinbutton->get_value_as_int())); + + if (_replace) + App::instance().engine()->clear_patch(_patch->path()); + + //if (_patch->path() != "/") + // parent = _patch->path().parent(); + parent = _patch->path(); + + _patch.reset(); + hide(); + + App::instance().loader()->load_patch(true, get_uri(), "/", + _initial_data, parent, symbol); +} + + +void +LoadPatchWindow::cancel_clicked() +{ + hide(); +} + + +} // namespace GUI +} // namespace Ingen diff --git a/src/gui/LoadPatchWindow.hpp b/src/gui/LoadPatchWindow.hpp new file mode 100644 index 00000000..4f3521e5 --- /dev/null +++ b/src/gui/LoadPatchWindow.hpp @@ -0,0 +1,82 @@ +/* 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.hpp> +#include "interface/GraphObject.hpp" +#include "client/PluginModel.hpp" +#include "client/PatchModel.hpp" +using Ingen::Client::PatchModel; +using namespace Ingen::Shared; + +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, GraphObject::Variables data); + +protected: + void on_show(); + +private: + void poly_from_file_selected(); + void poly_from_user_selected(); + void ok_clicked(); + void cancel_clicked(); + + GraphObject::Variables _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/gui/LoadPluginWindow.cpp b/src/gui/LoadPluginWindow.cpp new file mode 100644 index 00000000..28815ba9 --- /dev/null +++ b/src/gui/LoadPluginWindow.cpp @@ -0,0 +1,464 @@ +/* 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 <ctype.h> +#include "interface/EngineInterface.hpp" +#include "client/NodeModel.hpp" +#include "client/PatchModel.hpp" +#include "client/ClientStore.hpp" +#include "App.hpp" +#include "LoadPluginWindow.hpp" +#include "PatchWindow.hpp" +#include "PatchView.hpp" +#include "PatchCanvas.hpp" + +using namespace std; + + +namespace Ingen { +namespace GUI { + +LoadPluginWindow::LoadPluginWindow(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& xml) + : Gtk::Window(cobject) + , _plugin_name_offset(0) + , _has_shown(false) + , _refresh_list(true) +{ + 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("", _plugins_columns._col_icon); + _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(1)->set_sort_column(_plugins_columns._col_name); + _plugins_treeview->get_column(2)->set_sort_column(_plugins_columns._col_type); + _plugins_treeview->get_column(3)->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, GraphObject::Variables 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 (App::instance().store()->find_child(_patch, 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) +{ + if (_patch) { + _patch = patch; + plugin_selection_changed(); + } else { + _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) { + App::instance().store()->signal_new_plugin.connect( + sigc::mem_fun(this, &LoadPluginWindow::add_plugin)); + _has_shown = true; + } + + if (_refresh_list) { + set_plugins(App::instance().store()->plugins()); + _refresh_list = false; + } + + 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_plugins(SharedPtr<const ClientStore::Plugins> m) +{ + _plugins_liststore->clear(); + + for (ClientStore::Plugins::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_icon] = App::instance().icon_from_path(plugin->icon_path(), 20); + row[_plugins_columns._col_name] = plugin->name(); + //row[_plugins_columns._col_label] = plugin->plug_label(); + if (!strcmp(plugin->type_uri(), "ingen:Internal")) + row[_plugins_columns._col_type] = "Internal"; + else if (!strcmp(plugin->type_uri(), "ingen:LV2")) + row[_plugins_columns._col_type] = "LV2"; + else if (!strcmp(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::new_plugin(SharedPtr<PluginModel> pm) +{ + if (is_visible()) + add_plugin(pm); + else + _refresh_list = true; +} + + +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() +{ + Gtk::TreeModel::iterator iter = _selection->get_selected(); + if (iter) { + Gtk::TreeModel::Row row = *iter; + boost::shared_ptr<PluginModel> p = row.get_value(_plugins_columns._col_plugin_model); + _plugin_name_offset = App::instance().store()->child_name_offset( + _patch->path(), p->default_node_symbol()); + _node_name_entry->set_text(generate_module_name(_plugin_name_offset)); + } else { + _plugin_name_offset = 0; + _node_name_entry->set_text(""); + } +} + + +/** 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); + std::stringstream ss; + ss << plugin->default_node_symbol(); + if (offset != 0) + ss << "_" << offset + 1; + name = ss.str(); + } + + 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()->new_node(path, plugin->uri()); + App::instance().engine()->set_property(path, "ingen:polyphonic", polyphonic); + for (GraphObject::Variables::const_iterator i = _initial_data.begin(); i != _initial_data.end(); ++i) + App::instance().engine()->set_variable(path, i->first, i->second); + _node_name_entry->set_text(generate_module_name(++_plugin_name_offset)); + + // Cascade + Atom& x = _initial_data["ingenuity:canvas-x"]; + x = Atom(x.get_float() + 20.0f); + Atom& y = _initial_data["ingenuity:canvas-y"]; + y = Atom(y.get_float() + 20.0f); + } + } +} + + +/* +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 (ClientStore::Plugins::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_plugins(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/gui/LoadPluginWindow.hpp b/src/gui/LoadPluginWindow.hpp new file mode 100644 index 00000000..98644dde --- /dev/null +++ b/src/gui/LoadPluginWindow.hpp @@ -0,0 +1,155 @@ +/* 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 <libglademm/xml.h> +#include <libglademm.h> +#include <gtkmm.h> +#include <raul/SharedPtr.hpp> +#include <raul/Table.hpp> +#include "interface/GraphObject.hpp" +#include "client/PatchModel.hpp" +#include "client/PluginModel.hpp" +#include "client/ClientStore.hpp" +using Ingen::Client::PluginModel; +using Ingen::Client::PatchModel; +using namespace Ingen::Shared; + +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_icon); + 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::RefPtr<Gdk::Pixbuf> > _col_icon; + 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_plugins(SharedPtr<const Client::ClientStore::Plugins> plugins); + + void add_plugin(SharedPtr<PluginModel> plugin); + + void present(SharedPtr<PatchModel> patch, GraphObject::Variables 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(); + + void new_plugin(SharedPtr<PluginModel> plugin); + + 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); + + GraphObject::Variables _initial_data; + + SharedPtr<PatchModel> _patch; + + 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 + + bool _has_shown; + bool _refresh_list; + 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/gui/LoadRemotePatchWindow.cpp b/src/gui/LoadRemotePatchWindow.cpp new file mode 100644 index 00000000..5567d67b --- /dev/null +++ b/src/gui/LoadRemotePatchWindow.cpp @@ -0,0 +1,164 @@ +/* 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 <boost/optional/optional.hpp> +#include <redlandmm/Query.hpp> +#include "client/PatchModel.hpp" +#include "interface/EngineInterface.hpp" +#include "module/World.hpp" +#include "module/global.hpp" +#include "App.hpp" +#include "Configuration.hpp" +#include "LoadRemotePatchWindow.hpp" +#include "ThreadedLoader.hpp" + +using boost::optional; +using namespace Raul; +using namespace std; + +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, GraphObject::Variables data) +{ + _liststore->clear(); + + set_patch(patch); + _initial_data = data; + + Redland::Model model(*App::instance().world()->rdf_world, + "http://rdf.drobilla.net/ingen_patches/index.ttl", + "http://rdf.drobilla.net/ingen_patches/"); + + Redland::Query query(*App::instance().world()->rdf_world, Glib::ustring( + "SELECT DISTINCT ?name ?uri WHERE {" + " ?uri a ingen:Patch ;" + " doap:name ?name ." + "}")); + + Redland::Query::Results results = query.run(*App::instance().world()->rdf_world, model); + + for (Redland::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<Path> parent; + optional<Symbol> symbol; + + 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, symbol); + + hide(); +} + + +void +LoadRemotePatchWindow::cancel_clicked() +{ + hide(); +} + + +} // namespace GUI +} // namespace Ingen diff --git a/src/gui/LoadRemotePatchWindow.hpp b/src/gui/LoadRemotePatchWindow.hpp new file mode 100644 index 00000000..ca0ca814 --- /dev/null +++ b/src/gui/LoadRemotePatchWindow.hpp @@ -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 LOADREMOTEPATCHWINDOW_H +#define LOADREMOTEPATCHWINDOW_H + +#include <libglademm/xml.h> +#include <gtkmm.h> +#include <raul/SharedPtr.hpp> +#include "interface/GraphObject.hpp" +#include "client/PatchModel.hpp" +#include "client/PluginModel.hpp" +using namespace Ingen::Shared; +using Ingen::Client::PatchModel; + +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, GraphObject::Variables 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(); + + GraphObject::Variables _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/gui/LoadSubpatchWindow.cpp b/src/gui/LoadSubpatchWindow.cpp new file mode 100644 index 00000000..71dbfb36 --- /dev/null +++ b/src/gui/LoadSubpatchWindow.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 <sys/types.h> +#include <dirent.h> +#include <cassert> +#include <boost/optional.hpp> +#include "interface/EngineInterface.hpp" +#include "client/NodeModel.hpp" +#include "client/PatchModel.hpp" +#include "App.hpp" +#include "LoadSubpatchWindow.hpp" +#include "PatchView.hpp" +#include "Configuration.hpp" +#include "ThreadedLoader.hpp" +using boost::optional; +using namespace std; + +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); + + property_select_multiple() = true; + + // Add global examples directory to "shortcut folders" (bookmarks) + string examples_dir = INGEN_DATA_DIR; + 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, GraphObject::Variables 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, "%u", 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<Symbol> symbol; + string name_str = ""; + + if (_name_from_user_radio->get_active()) { + name_str = _name_entry->get_text(); + symbol = Symbol::symbolify(name_str); + } + + if (_poly_from_user_radio->get_active()) { + cerr << "Overriding poly: " << _poly_spinbutton->get_value_as_int() << endl; + _initial_data.insert(make_pair("ingen:polyphony", (int)_poly_spinbutton->get_value_as_int())); + } else if (_poly_from_parent_radio->get_active()) { + _initial_data.insert(make_pair("ingen:polyphony", (int)_patch->poly())); + } + + std::list<Glib::ustring> uris = get_uris(); + for (std::list<Glib::ustring>::iterator i = uris.begin(); i != uris.end(); ++i) { + // Cascade + Atom& x = _initial_data["ingenuity:canvas-x"]; + x = Atom(x.get_float() + 20.0f); + Atom& y = _initial_data["ingenuity:canvas-y"]; + y = Atom(y.get_float() + 20.0f); + + App::instance().loader()->load_patch(false, *i, "/", _initial_data, _patch->path(), symbol); + } + + hide(); +} + + +void +LoadSubpatchWindow::cancel_clicked() +{ + hide(); +} + + +} // namespace GUI +} // namespace Ingen diff --git a/src/gui/LoadSubpatchWindow.hpp b/src/gui/LoadSubpatchWindow.hpp new file mode 100644 index 00000000..ee8a5a16 --- /dev/null +++ b/src/gui/LoadSubpatchWindow.hpp @@ -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 LOADSUBPATCHWINDOW_H +#define LOADSUBPATCHWINDOW_H + +#include <libglademm/xml.h> +#include <gtkmm.h> +#include <raul/SharedPtr.hpp> +#include "client/PatchModel.hpp" +#include "client/PluginModel.hpp" +#include "interface/GraphObject.hpp" +using namespace Ingen::Shared; +using Ingen::Client::PatchModel; + +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, GraphObject::Variables 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(); + + GraphObject::Variables _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/gui/Makefile.am b/src/gui/Makefile.am new file mode 100644 index 00000000..41458812 --- /dev/null +++ b/src/gui/Makefile.am @@ -0,0 +1,125 @@ +MAINTAINERCLEANFILES = Makefile.in +EXTRA_DIST = ingen_gui.gladep + +if BUILD_GTK_CLIENT + +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 = \ + -DINGEN_MODULE_DIR=\"$(libdir)/ingen\" \ + -DINGEN_DATA_DIR=\"$(pkgdatadir)\" \ + @CURL_CFLAGS@ \ + @FLOWCANVAS_CFLAGS@ \ + @GNOMECANVASMM_CFLAGS@ \ + @GTKMM_CFLAGS@ \ + @INGEN_CFLAGS@ \ + @LIBGLADEMM_CFLAGS@ \ + @LIBLO_CFLAGS@ \ + @REDLANDMM_CFLAGS@ \ + @RAUL_CFLAGS@ \ + @SLV2_CFLAGS@ \ + @SOUP_CFLAGS@ + + +libingen_gui_la_LDFLAGS = -no-undefined -module -avoid-version + +libingen_gui_la_LIBADD = \ + ../module/libingen_module.la \ + ../shared/libingen_shared.la \ + ../client/libingen_client.la \ + @CURL_LIBS@ \ + @FLOWCANVAS_LIBS@ \ + @GNOMECANVASMM_LIBS@ \ + @GTKMM_LIBS@ \ + @LIBGLADEMM_LIBS@ \ + @LIBLO_LIBS@ \ + @REDLANDMM_LIBS@ \ + @RAUL_LIBS@ \ + @SLV2_LIBS@ \ + @SOUP_LIBS@ + +libingen_gui_la_SOURCES = \ + gui.hpp \ + gui.cpp \ + App.cpp \ + App.hpp \ + BreadCrumb.hpp \ + BreadCrumbBox.cpp \ + BreadCrumbBox.hpp \ + Configuration.cpp \ + Configuration.hpp \ + ConnectWindow.cpp \ + ConnectWindow.hpp \ + Connection.hpp \ + ControlPanel.cpp \ + ControlPanel.hpp \ + Controls.cpp \ + Controls.hpp \ + GladeFactory.cpp \ + GladeFactory.hpp \ + LoadPatchWindow.cpp \ + LoadPatchWindow.hpp \ + LoadPluginWindow.cpp \ + LoadPluginWindow.hpp \ + LoadRemotePatchWindow.cpp \ + LoadRemotePatchWindow.hpp \ + LoadSubpatchWindow.cpp \ + LoadSubpatchWindow.hpp \ + MessagesWindow.cpp \ + MessagesWindow.hpp \ + NewSubpatchWindow.cpp \ + NewSubpatchWindow.hpp \ + NodeControlWindow.cpp \ + NodeControlWindow.hpp \ + ObjectMenu.hpp \ + ObjectMenu.cpp \ + NodeMenu.hpp \ + NodeMenu.cpp \ + PortMenu.hpp \ + PortMenu.cpp \ + NodeModule.cpp \ + NodeModule.hpp \ + NodePropertiesWindow.cpp \ + NodePropertiesWindow.hpp \ + PatchCanvas.cpp \ + PatchCanvas.hpp \ + PatchPortModule.cpp \ + PatchPortModule.hpp \ + PatchPropertiesWindow.cpp \ + PatchPropertiesWindow.hpp \ + PatchTreeWindow.cpp \ + PatchTreeWindow.hpp \ + PatchView.cpp \ + PatchView.hpp \ + PatchWindow.cpp \ + PatchWindow.hpp \ + Port.cpp \ + Port.hpp \ + PortPropertiesWindow.cpp \ + PortPropertiesWindow.hpp \ + RenameWindow.cpp \ + RenameWindow.hpp \ + SubpatchModule.cpp \ + SubpatchModule.hpp \ + ThreadedLoader.cpp \ + ThreadedLoader.hpp \ + WindowFactory.cpp \ + WindowFactory.hpp + +if WITH_CURL +libingen_gui_la_SOURCES += \ + UploadPatchWindow.cpp \ + UploadPatchWindow.hpp +endif + +endif + + diff --git a/src/gui/MessagesWindow.cpp b/src/gui/MessagesWindow.cpp new file mode 100644 index 00000000..3da810f6 --- /dev/null +++ b/src/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.hpp" +#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/gui/MessagesWindow.hpp b/src/gui/MessagesWindow.hpp new file mode 100644 index 00000000..dea0fdd4 --- /dev/null +++ b/src/gui/MessagesWindow.hpp @@ -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/gui/NewSubpatchWindow.cpp b/src/gui/NewSubpatchWindow.cpp new file mode 100644 index 00000000..580ebb51 --- /dev/null +++ b/src/gui/NewSubpatchWindow.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 "App.hpp" +#include "interface/EngineInterface.hpp" +#include "client/NodeModel.hpp" +#include "client/PatchModel.hpp" +#include "client/ClientStore.hpp" +#include "NewSubpatchWindow.hpp" +#include "PatchView.hpp" + +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, GraphObject::Variables 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 (App::instance().store()->find(_patch->path().base() + name) + != App::instance().store()->end()) { + _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 uint32_t poly = _poly_spinbutton->get_value_as_int(); + + App::instance().engine()->new_patch(path, poly); + for (GraphObject::Variables::const_iterator i = _initial_data.begin(); i != _initial_data.end(); ++i) + App::instance().engine()->set_variable(path, i->first, i->second); + + App::instance().engine()->set_property(_patch->path(), "ingen:enabled", (bool)true); + + hide(); +} + + +void +NewSubpatchWindow::cancel_clicked() +{ + hide(); +} + + +} // namespace GUI +} // namespace Ingen diff --git a/src/gui/NewSubpatchWindow.hpp b/src/gui/NewSubpatchWindow.hpp new file mode 100644 index 00000000..00fb6a3b --- /dev/null +++ b/src/gui/NewSubpatchWindow.hpp @@ -0,0 +1,68 @@ +/* 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.hpp> +#include "interface/GraphObject.hpp" +#include "client/PatchModel.hpp" +#include "client/PluginModel.hpp" +using namespace Ingen::Shared; +using Ingen::Client::PatchModel; + +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, GraphObject::Variables data); + +private: + void name_changed(); + void ok_clicked(); + void cancel_clicked(); + + GraphObject::Variables _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/gui/NodeControlWindow.cpp b/src/gui/NodeControlWindow.cpp new file mode 100644 index 00000000..293d28f5 --- /dev/null +++ b/src/gui/NodeControlWindow.cpp @@ -0,0 +1,139 @@ +/* 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.hpp" +#include "client/NodeModel.hpp" +#include "App.hpp" +#include "NodeControlWindow.hpp" +#include "GladeFactory.hpp" +#include "Controls.hpp" +#include "ControlPanel.hpp" +#include "PatchWindow.hpp" + +using namespace std; + +namespace Ingen { +namespace GUI { + + +/** Create a node control window and load a new ControlPanel for it. + */ +NodeControlWindow::NodeControlWindow(SharedPtr<NodeModel> node, uint32_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); + + show_all_children(); + + _control_panel->reparent(*this); + _control_panel->init(_node, poly); + _control_panel->show(); + + 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 (NodeModel::Ports::const_iterator i = _node->ports().begin(); + i != _node->ports().end(); ++i) + if ((*i)->type().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/gui/NodeControlWindow.hpp b/src/gui/NodeControlWindow.hpp new file mode 100644 index 00000000..84e462c5 --- /dev/null +++ b/src/gui/NodeControlWindow.hpp @@ -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.hpp> +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, uint32_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/gui/NodeMenu.cpp b/src/gui/NodeMenu.cpp new file mode 100644 index 00000000..529eb52c --- /dev/null +++ b/src/gui/NodeMenu.cpp @@ -0,0 +1,169 @@ +/* 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.hpp" +#include "client/NodeModel.hpp" +#include "App.hpp" +#include "NodeMenu.hpp" +#include "WindowFactory.hpp" + +using namespace std; +using std::cerr; using std::endl; + +namespace Ingen { +namespace GUI { + + +NodeMenu::NodeMenu(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& xml) + : ObjectMenu(cobject, xml) + , _controls_menuitem(NULL) +{ + Gtk::Menu* node_menu = NULL; + xml->get_widget("node_menu", node_menu); + xml->get_widget("node_controls_menuitem", _controls_menuitem); + xml->get_widget("node_popup_gui_menuitem", _popup_gui_menuitem); + xml->get_widget("node_embed_gui_menuitem", _embed_gui_menuitem); + + node_menu->remove(*_controls_menuitem); + node_menu->remove(*_popup_gui_menuitem); + node_menu->remove(*_embed_gui_menuitem); + items().push_front(Gtk::Menu_Helpers::SeparatorElem()); + insert(*_controls_menuitem, 0); + insert(*_popup_gui_menuitem, 0); + insert(*_embed_gui_menuitem, 0); +} + + +void +NodeMenu::init(SharedPtr<NodeModel> node) +{ + ObjectMenu::init(node); + + _controls_menuitem->signal_activate().connect(sigc::bind( + sigc::mem_fun(App::instance().window_factory(), &WindowFactory::present_controls), + node)); + + _popup_gui_menuitem->signal_activate().connect(sigc::mem_fun(signal_popup_gui, + &sigc::signal<void>::emit)); + _embed_gui_menuitem->signal_toggled().connect(sigc::mem_fun(this, + &NodeMenu::on_menu_embed_gui)); + + const PluginModel* plugin = dynamic_cast<const PluginModel*>(node->plugin()); + if (plugin && plugin->type() == PluginModel::LV2 && plugin->has_ui()) { + _popup_gui_menuitem->show(); + _embed_gui_menuitem->show(); + } else { + _popup_gui_menuitem->hide(); + _embed_gui_menuitem->hide(); + } + + _enable_signal = true; +} + + +void +NodeMenu::on_menu_embed_gui() +{ + signal_embed_gui.emit(_embed_gui_menuitem->get_active()); +} + + +void +NodeMenu::on_menu_clone() +{ + cerr << "[NodeMenu] 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(_object->path()); +} + + +void +NodeMenu::on_menu_disconnect() +{ + App::instance().engine()->disconnect_all(_object->parent()->path(), _object->path()); +} + + +bool +NodeMenu::has_control_inputs() +{ + const NodeModel* const nm = (NodeModel*)_object.get(); + for (NodeModel::Ports::const_iterator i = nm->ports().begin(); i != nm->ports().end(); ++i) + if ((*i)->is_input() && (*i)->type().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; +} + + + +} // namespace GUI +} // namespace Ingen + diff --git a/src/gui/NodeMenu.hpp b/src/gui/NodeMenu.hpp new file mode 100644 index 00000000..2a10e473 --- /dev/null +++ b/src/gui/NodeMenu.hpp @@ -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 + */ + +#ifndef NODEMENU_H +#define NODEMENU_H + +#include <string> +#include <gtkmm.h> +#include <raul/Path.hpp> +#include <raul/SharedPtr.hpp> +#include "client/NodeModel.hpp" +#include "ObjectMenu.hpp" + +using Ingen::Client::NodeModel; + +namespace Ingen { +namespace GUI { + + +/** Menu for a Node. + * + * \ingroup GUI + */ +class NodeMenu : public ObjectMenu +{ +public: + NodeMenu(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& xml); + + virtual void program_add(int bank, int program, const std::string& name) {} + virtual void program_remove(int bank, int program) {} + + void init(SharedPtr<NodeModel> node); + + bool has_control_inputs(); + + sigc::signal<void> signal_popup_gui; + sigc::signal<void, bool> signal_embed_gui; + +protected: + + virtual void enable_controls_menuitem(); + virtual void disable_controls_menuitem(); + + void on_menu_disconnect(); + void on_menu_clone(); + void on_menu_learn(); + void on_menu_embed_gui(); + + Gtk::MenuItem* _controls_menuitem; + Gtk::MenuItem* _popup_gui_menuitem; + Gtk::CheckMenuItem* _embed_gui_menuitem; +}; + + +} // namespace GUI +} // namespace Ingen + +#endif // NODEMENU_H diff --git a/src/gui/NodeModule.cpp b/src/gui/NodeModule.cpp new file mode 100644 index 00000000..97edaa84 --- /dev/null +++ b/src/gui/NodeModule.cpp @@ -0,0 +1,386 @@ +/* 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.hpp> +#include "interface/EngineInterface.hpp" +#include "client/NodeModel.hpp" +#include "client/PatchModel.hpp" +#include "client/PluginUI.hpp" +#include "App.hpp" +#include "GladeFactory.hpp" +#include "NodeControlWindow.hpp" +#include "NodeModule.hpp" +#include "PatchCanvas.hpp" +#include "PatchWindow.hpp" +#include "Port.hpp" +#include "RenameWindow.hpp" +#include "SubpatchModule.hpp" +#include "WindowFactory.hpp" +#include "Configuration.hpp" + +using namespace std; + +namespace Ingen { +namespace GUI { + + +NodeModule::NodeModule(boost::shared_ptr<PatchCanvas> canvas, SharedPtr<NodeModel> node) + : FlowCanvas::Module(canvas, node->path().name()) + , _node(node) + , _gui_widget(NULL) + , _gui_window(NULL) +{ + assert(_node); + + node->signal_new_port.connect(sigc::bind(sigc::mem_fun(this, &NodeModule::add_port), true)); + node->signal_removed_port.connect(sigc::mem_fun(this, &NodeModule::remove_port)); + node->signal_variable.connect(sigc::mem_fun(this, &NodeModule::set_variable)); + node->signal_property.connect(sigc::mem_fun(this, &NodeModule::set_property)); + node->signal_renamed.connect(sigc::mem_fun(this, &NodeModule::rename)); +} + + +NodeModule::~NodeModule() +{ + NodeControlWindow* win = App::instance().window_factory()->control_window(_node); + + if (win) { + // Should remove from window factory via signal + delete win; + } +} + + +void +NodeModule::create_menu() +{ + Glib::RefPtr<Gnome::Glade::Xml> xml = GladeFactory::new_glade_reference(); + xml->get_widget_derived("object_menu", _menu); + _menu->init(_node); + _menu->signal_embed_gui.connect(sigc::mem_fun(this, &NodeModule::embed_gui)); + _menu->signal_popup_gui.connect(sigc::hide_return(sigc::mem_fun(this, &NodeModule::popup_gui))); + + set_menu(_menu); +} + + +boost::shared_ptr<NodeModule> +NodeModule::create(boost::shared_ptr<PatchCanvas> canvas, SharedPtr<NodeModel> node, bool human) +{ + 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 (GraphObject::Variables::const_iterator m = node->variables().begin(); m != node->variables().end(); ++m) + ret->set_variable(m->first, m->second); + + for (NodeModel::Ports::const_iterator p = node->ports().begin(); p != node->ports().end(); ++p) { + ret->add_port(*p, false); + } + + if (human) + ret->show_human_names(human); + + ret->resize(); + ret->set_stacked_border(node->polyphonic()); + + return ret; +} + + +void +NodeModule::show_human_names(bool b) +{ + if (b && node()->plugin()) + set_name(((PluginModel*)node()->plugin())->human_name()); + else + b = false; + + if (!b) + set_name(node()->symbol()); + + uint32_t index = 0; + for (PortVector::const_iterator i = ports().begin(); i != ports().end(); ++i) { + if (b) { + string hn = ((PluginModel*)node()->plugin())->port_human_name(index); + if (hn != "") + (*i)->set_name(hn); + } else { + (*i)->set_name(node()->port(index)->symbol()); + } + ++index; + } + + resize(); +} + + +void +NodeModule::value_changed(uint32_t index, const Atom& value) +{ + float control = 0.0f; + switch (value.type()) { + case Atom::FLOAT: + control = value.get_float(); + if (_plugin_ui) { + SLV2UIInstance inst = _plugin_ui->instance(); + const LV2UI_Descriptor* ui_descriptor = slv2_ui_instance_get_descriptor(inst); + LV2UI_Handle ui_handle = slv2_ui_instance_get_handle(inst); + if (ui_descriptor->port_event) + ui_descriptor->port_event(ui_handle, index, 4, 0, &control); + } + break; + case Atom::STRING: + cout << "Port value type is a string? (\"" << value.get_string() << "\")" << endl; + break; + default: + break; + } +} + + +void +NodeModule::embed_gui(bool embed) +{ + if (embed) { + + if (_gui_window) { + cerr << "LV2 GUI already popped up, cannot embed" << endl; + return; + } + + if (!_plugin_ui) { + const PluginModel* const pm = dynamic_cast<const PluginModel*>(_node->plugin()); + assert(pm); + _plugin_ui = pm->ui(App::instance().world(), _node); + } + + if (_plugin_ui) { + GtkWidget* c_widget = (GtkWidget*)slv2_ui_instance_get_widget(_plugin_ui->instance()); + _gui_widget = Glib::wrap(c_widget); + assert(_gui_widget); + + Gtk::Container* container = new Gtk::EventBox(); + container->set_name("ingen_embedded_node_gui_container"); + container->add(*_gui_widget); + FlowCanvas::Module::embed(container); + } else { + cerr << "ERROR: Failed to create LV2 UI" << endl; + } + + if (_gui_widget) { + _gui_widget->show_all(); + + for (NodeModel::Ports::const_iterator p = _node->ports().begin(); + p != _node->ports().end(); ++p) + if ((*p)->type().is_control() && (*p)->is_output()) + App::instance().engine()->set_property((*p)->path(), "ingen:broadcast", true); + } + + } else { // un-embed + + FlowCanvas::Module::embed(NULL); + _plugin_ui.reset(); + + for (NodeModel::Ports::const_iterator p = _node->ports().begin(); p != _node->ports().end(); ++p) + if ((*p)->type().is_control() && (*p)->is_output()) + App::instance().engine()->set_property((*p)->path(), "ingen:broadcast", false); + } + + if (embed && _embed_item) { + initialise_gui_values(); + set_base_color(0x212222FF); + } else { + set_default_base_color(); + } + + resize(); +} + + +void +NodeModule::rename() +{ + set_name(_node->path().name()); + resize(); +} + + +void +NodeModule::add_port(SharedPtr<PortModel> port, bool resize_to_fit) +{ + uint32_t index = _ports.size(); // FIXME: kludge, engine needs to tell us this + + string name = port->path().name(); + if (App::instance().configuration()->name_style() == Configuration::HUMAN && node()->plugin()) + name = ((PluginModel*)node()->plugin())->port_human_name(index); + + Module::add_port(boost::shared_ptr<Port>( + new Port(PtrCast<NodeModule>(shared_from_this()), port, name))); + + port->signal_value_changed.connect(sigc::bind<0>( + sigc::mem_fun(this, &NodeModule::value_changed), index)); + + if (resize_to_fit) + resize(); +} + + +void +NodeModule::remove_port(SharedPtr<PortModel> port) +{ + SharedPtr<FlowCanvas::Port> p = Module::remove_port(port->path().name()); + p.reset(); +} + + +bool +NodeModule::popup_gui() +{ +#ifdef HAVE_SLV2 + if (_node->plugin() && _node->plugin()->type() == PluginModel::LV2) { + if (_plugin_ui) { + cerr << "LV2 GUI already embedded, cannot pop up" << endl; + return false; + } + + const PluginModel* const plugin = dynamic_cast<const PluginModel*>(_node->plugin()); + assert(plugin); + + _plugin_ui = plugin->ui(App::instance().world(), _node); + + if (_plugin_ui) { + GtkWidget* c_widget = (GtkWidget*)slv2_ui_instance_get_widget(_plugin_ui->instance()); + _gui_widget = Glib::wrap(c_widget); + + _gui_window = new Gtk::Window(); + _gui_window->add(*_gui_widget); + _gui_widget->show_all(); + initialise_gui_values(); + + _gui_window->signal_unmap().connect( + sigc::mem_fun(this, &NodeModule::on_gui_window_close)); + _gui_window->present(); + + return true; + } else { + cerr << "No LV2 GUI" << endl; + } + } +#endif + return false; +} + + +void +NodeModule::on_gui_window_close() +{ + delete _gui_window; + _gui_window = NULL; + _plugin_ui.reset(); + _gui_widget = NULL; +} + + +void +NodeModule::initialise_gui_values() +{ + uint32_t index=0; + for (NodeModel::Ports::const_iterator p = _node->ports().begin(); p != _node->ports().end(); ++p) { + if ((*p)->type().is_control()) + value_changed(index, (*p)->value()); + ++index; + } +} + + +void +NodeModule::show_control_window() +{ + App::instance().window_factory()->present_controls(_node); +} + + +void +NodeModule::on_double_click(GdkEventButton* ev) +{ + if ( ! popup_gui() ) + show_control_window(); +} + + +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_variable("ingenuity:canvas-x"); + const Atom& existing_y = _node->get_variable("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_variable(_node->path(), "ingenuity:canvas-x", Atom(x)); + App::instance().engine()->set_variable(_node->path(), "ingenuity:canvas-y", Atom(y)); + } +} + + +void +NodeModule::set_variable(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()); +} + + +void +NodeModule::set_property(const string& key, const Atom& value) +{ + if (key == "ingen:polyphonic" && value.type() == Atom::BOOL) { + set_stacked_border(value.get_bool()); + } else if (key == "ingen:selected" && value.type() == Atom::BOOL) { + if (value.get_bool() != selected()) { + if (value.get_bool()) + _canvas.lock()->select_item(shared_from_this()); + else + _canvas.lock()->unselect_item(shared_from_this()); + } + } +} + + +void +NodeModule::set_selected(bool b) +{ + if (b != selected()) { + Module::set_selected(b); + if (App::instance().signal()) + App::instance().engine()->set_property(_node->path(), "ingen:selected", b); + } +} + + +} // namespace GUI +} // namespace Ingen diff --git a/src/gui/NodeModule.hpp b/src/gui/NodeModule.hpp new file mode 100644 index 00000000..de9556fd --- /dev/null +++ b/src/gui/NodeModule.hpp @@ -0,0 +1,102 @@ +/* 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.hpp> +#include <raul/SharedPtr.hpp> +#include "Port.hpp" +#include "NodeMenu.hpp" + +class Atom; + +namespace Ingen { namespace Client { + class PortModel; + class NodeModel; +} } +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. + * + * \ingroup GUI + */ +class NodeModule : public FlowCanvas::Module +{ +public: + static boost::shared_ptr<NodeModule> create ( + boost::shared_ptr<PatchCanvas> canvas, + SharedPtr<NodeModel> node, + bool human_names); + + virtual ~NodeModule(); + + boost::shared_ptr<Port> port(const std::string& port_name) { + return boost::dynamic_pointer_cast<Ingen::GUI::Port>( + Module::get_port(port_name)); + } + + virtual void store_location(); + void show_human_names(bool b); + + SharedPtr<NodeModel> node() const { return _node; } + +protected: + NodeModule(boost::shared_ptr<PatchCanvas> canvas, SharedPtr<NodeModel> node); + + void on_double_click(GdkEventButton* ev); + + void show_control_window(); + void embed_gui(bool embed); + bool popup_gui(); + void on_gui_window_close(); + void set_selected(bool b); + + void rename(); + void set_variable(const std::string& key, const Atom& value); + void set_property(const std::string& predicate, const Raul::Atom& value); + + void add_port(SharedPtr<PortModel> port, bool resize=true); + void remove_port(SharedPtr<PortModel> port); + + void value_changed(uint32_t index, const Atom& value); + void initialise_gui_values(); + + void create_menu(); + + SharedPtr<NodeModel> _node; + NodeMenu* _menu; + SharedPtr<PluginUI> _plugin_ui; + Gtk::Widget* _gui_widget; + Gtk::Window* _gui_window; ///< iff popped up +}; + + +} // namespace GUI +} // namespace Ingen + +#endif // NODEMODULE_H diff --git a/src/gui/NodePropertiesWindow.cpp b/src/gui/NodePropertiesWindow.cpp new file mode 100644 index 00000000..ae2c23b5 --- /dev/null +++ b/src/gui/NodePropertiesWindow.cpp @@ -0,0 +1,66 @@ +/* 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.hpp" +#include "client/PluginModel.hpp" +#include "NodePropertiesWindow.hpp" + +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()); + + const PluginModel* pm = dynamic_cast<const PluginModel*>(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/gui/NodePropertiesWindow.hpp b/src/gui/NodePropertiesWindow.hpp new file mode 100644 index 00000000..248c8b42 --- /dev/null +++ b/src/gui/NodePropertiesWindow.hpp @@ -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.hpp> +#include "client/NodeModel.hpp" +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/gui/ObjectMenu.cpp b/src/gui/ObjectMenu.cpp new file mode 100644 index 00000000..becbf964 --- /dev/null +++ b/src/gui/ObjectMenu.cpp @@ -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 + */ + +#include <gtkmm.h> +#include "interface/EngineInterface.hpp" +#include "client/ObjectModel.hpp" +#include "App.hpp" +#include "ObjectMenu.hpp" +#include "WindowFactory.hpp" + +namespace Ingen { +namespace GUI { + + +ObjectMenu::ObjectMenu(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& xml) + : Gtk::Menu(cobject) + , _enable_signal(false) + , _polyphonic_menuitem(NULL) + , _disconnect_menuitem(NULL) + , _rename_menuitem(NULL) + , _destroy_menuitem(NULL) + , _properties_menuitem(NULL) +{ + xml->get_widget("object_polyphonic_menuitem", _polyphonic_menuitem); + xml->get_widget("object_disconnect_menuitem", _disconnect_menuitem); + xml->get_widget("object_rename_menuitem", _rename_menuitem); + xml->get_widget("object_destroy_menuitem", _destroy_menuitem); + xml->get_widget("object_properties_menuitem", _properties_menuitem); +} + + +void +ObjectMenu::init(SharedPtr<ObjectModel> object) +{ + _object = object; + + App& app = App::instance(); + + _polyphonic_menuitem->signal_toggled().connect( + sigc::mem_fun(this, &ObjectMenu::on_menu_polyphonic)); + + _polyphonic_menuitem->set_active(object->polyphonic()); + + _disconnect_menuitem->signal_activate().connect( + sigc::mem_fun(this, &ObjectMenu::on_menu_disconnect)); + + _rename_menuitem->signal_activate().connect(sigc::bind( + sigc::mem_fun(app.window_factory(), &WindowFactory::present_rename), + object)); + + _destroy_menuitem->signal_activate().connect( + sigc::mem_fun(this, &ObjectMenu::on_menu_destroy)); + + _properties_menuitem->signal_activate().connect( + sigc::mem_fun(this, &ObjectMenu::on_menu_properties)); + + object->signal_property.connect(sigc::mem_fun(this, &ObjectMenu::property_changed)); + + _enable_signal = true; +} + + +void +ObjectMenu::on_menu_polyphonic() +{ + if (_enable_signal) + App::instance().engine()->set_property( + _object->path(), "ingen:polyphonic", _polyphonic_menuitem->get_active()); +} + + +void +ObjectMenu::property_changed(const std::string& predicate, const Raul::Atom& value) +{ + _enable_signal = false; + if (predicate == "ingen:polyphonic" && value.type() == Atom::BOOL) + _polyphonic_menuitem->set_active(value.get_bool()); + _enable_signal = true; +} + + +void +ObjectMenu::on_menu_destroy() +{ + App::instance().engine()->destroy(_object->path()); +} + + +void +ObjectMenu::on_menu_properties() +{ + App::instance().window_factory()->present_properties(_object); +} + + +} // namespace GUI +} // namespace Ingen + diff --git a/src/gui/ObjectMenu.hpp b/src/gui/ObjectMenu.hpp new file mode 100644 index 00000000..b5c5bf49 --- /dev/null +++ b/src/gui/ObjectMenu.hpp @@ -0,0 +1,69 @@ +/* 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 OBJECTMENU_H +#define OBJECTMENU_H + +#include <string> +#include <gtkmm.h> +#include <libglademm/xml.h> +#include <raul/Path.hpp> +#include <raul/SharedPtr.hpp> +#include "client/ObjectModel.hpp" +using Ingen::Client::ObjectModel; + +namespace Ingen { +namespace GUI { + +class ObjectControlWindow; +class ObjectPropertiesWindow; +class PatchCanvas; + +/** Menu for a Object. + * + * \ingroup GUI + */ +class ObjectMenu : public Gtk::Menu +{ +public: + ObjectMenu(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& xml); + + void init(SharedPtr<ObjectModel> object); + +protected: + + virtual void on_menu_disconnect() = 0; + void on_menu_polyphonic(); + void on_menu_destroy(); + void on_menu_properties(); + + void property_changed(const std::string& predicate, const Raul::Atom& value); + + bool _enable_signal; + SharedPtr<ObjectModel> _object; + Gtk::CheckMenuItem* _polyphonic_menuitem; + Gtk::MenuItem* _disconnect_menuitem; + Gtk::MenuItem* _rename_menuitem; + Gtk::MenuItem* _destroy_menuitem; + Gtk::MenuItem* _properties_menuitem; +}; + + +} // namespace GUI +} // namespace Ingen + +#endif // OBJECTMENU_H diff --git a/src/gui/PatchCanvas.cpp b/src/gui/PatchCanvas.cpp new file mode 100644 index 00000000..4f01a7f7 --- /dev/null +++ b/src/gui/PatchCanvas.cpp @@ -0,0 +1,773 @@ +/* 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_PATH +#include "module/global.hpp" +#include "module/World.hpp" + +#include <cassert> +#include <flowcanvas/Canvas.hpp> +#include <flowcanvas/Ellipse.hpp> +#include "interface/EngineInterface.hpp" +#include "shared/Builder.hpp" +#include "shared/ClashAvoider.hpp" +#include "serialisation/Serialiser.hpp" +#include "client/PluginModel.hpp" +#include "client/PatchModel.hpp" +#include "client/NodeModel.hpp" +#include "client/ClientStore.hpp" +#include "App.hpp" +#include "PatchCanvas.hpp" +#include "PatchWindow.hpp" +#include "PatchPortModule.hpp" +#include "LoadPluginWindow.hpp" +#include "LoadSubpatchWindow.hpp" +#include "NewSubpatchWindow.hpp" +#include "Port.hpp" +#include "Connection.hpp" +#include "NodeModule.hpp" +#include "SubpatchModule.hpp" +#include "GladeFactory.hpp" +#include "WindowFactory.hpp" +#include "ThreadedLoader.hpp" +using Ingen::Client::ClientStore; +using Ingen::Serialisation::Serialiser; +using Ingen::Client::PluginModel; +using namespace std; + +namespace Ingen { +namespace GUI { + + +PatchCanvas::PatchCanvas(SharedPtr<PatchModel> patch, int width, int height) + : Canvas(width, height) + , _patch(patch) + , _last_click_x(0) + , _last_click_y(0) + , _refresh_menu(false) + , _human_names(true) + , _menu(NULL) + , _internal_menu(NULL) + , _plugin_menu(NULL) +{ + Glib::RefPtr<Gnome::Glade::Xml> xml = GladeFactory::new_glade_reference(); + xml->get_widget("canvas_menu", _menu); + + /*xml->get_widget("canvas_menu_add_number_control", _menu_add_number_control); + xml->get_widget("canvas_menu_add_button_control", _menu_add_button_control);*/ + 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_add_osc_input", _menu_add_osc_input); + xml->get_widget("canvas_menu_add_osc_output", _menu_add_osc_output); + xml->get_widget("canvas_menu_add_event_input", _menu_add_event_input); + xml->get_widget("canvas_menu_add_event_output", _menu_add_event_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:AudioPort", false)); + _menu_add_audio_output->signal_activate().connect( + sigc::bind(sigc::mem_fun(this, &PatchCanvas::menu_add_port), + "audio_output", "ingen:AudioPort", true)); + _menu_add_control_input->signal_activate().connect( + sigc::bind(sigc::mem_fun(this, &PatchCanvas::menu_add_port), + "control_input", "ingen:ControlPort", false)); + _menu_add_control_output->signal_activate().connect( + sigc::bind(sigc::mem_fun(this, &PatchCanvas::menu_add_port), + "control_output", "ingen:ControlPort", true)); + _menu_add_midi_input->signal_activate().connect( + sigc::bind(sigc::mem_fun(this, &PatchCanvas::menu_add_port), + "midi_input", "ingen:MIDIPort", false)); + _menu_add_midi_output->signal_activate().connect( + sigc::bind(sigc::mem_fun(this, &PatchCanvas::menu_add_port), + "midi_output", "ingen:MIDIPort", true)); + _menu_add_osc_input->signal_activate().connect( + sigc::bind(sigc::mem_fun(this, &PatchCanvas::menu_add_port), + "osc_input", "ingen:OSCPort", false)); + _menu_add_osc_output->signal_activate().connect( + sigc::bind(sigc::mem_fun(this, &PatchCanvas::menu_add_port), + "osc_output", "ingen:OSCPort", true)); + _menu_add_event_input->signal_activate().connect( + sigc::bind(sigc::mem_fun(this, &PatchCanvas::menu_add_port), + "event_input", "ingen:EventPort", false)); + _menu_add_event_output->signal_activate().connect( + sigc::bind(sigc::mem_fun(this, &PatchCanvas::menu_add_port), + "event_output", "ingen:EventPort", true)); + + // Add control menu items + /*_menu_add_number_control->signal_activate().connect( + sigc::bind(sigc::mem_fun(this, &PatchCanvas::menu_add_control), NUMBER)); + _menu_add_button_control->signal_activate().connect( + sigc::bind(sigc::mem_fun(this, &PatchCanvas::menu_add_control), BUTTON));*/ + + // Connect to model signals to track state + _patch->signal_new_node.connect(sigc::mem_fun(this, &PatchCanvas::add_node)); + _patch->signal_removed_node.connect(sigc::mem_fun(this, &PatchCanvas::remove_node)); + _patch->signal_new_port.connect(sigc::mem_fun(this, &PatchCanvas::add_port)); + _patch->signal_removed_port.connect(sigc::mem_fun(this, &PatchCanvas::remove_port)); + _patch->signal_new_connection.connect(sigc::mem_fun(this, &PatchCanvas::connection)); + _patch->signal_removed_connection.connect(sigc::mem_fun(this, &PatchCanvas::disconnection)); + + App::instance().store()->signal_new_plugin.connect(sigc::mem_fun(this, &PatchCanvas::add_plugin)); + + // 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::show_menu(GdkEvent* event) +{ + if (!_internal_menu || !_plugin_menu || _refresh_menu) { + build_internal_menu(); +#ifdef HAVE_SLV2 + build_plugin_menu(); +#endif + _refresh_menu = false; + } + _menu->popup(event->button.button, event->button.time); +} + + +void +PatchCanvas::build_internal_menu() +{ + if (_internal_menu) { + _internal_menu->items().clear(); + } else { + _menu->items().push_back(Gtk::Menu_Helpers::ImageMenuElem("Internal", + *(manage(new Gtk::Image(Gtk::Stock::EXECUTE, Gtk::ICON_SIZE_MENU))))); + Gtk::MenuItem* internal_menu_item = &(_menu->items().back()); + _internal_menu = Gtk::manage(new Gtk::Menu()); + internal_menu_item->set_submenu(*_internal_menu); + _menu->reorder_child(*internal_menu_item, 2); + } + + SharedPtr<const ClientStore::Plugins> plugins = App::instance().store()->plugins(); + + // Add Internal plugins + for (ClientStore::Plugins::const_iterator i = plugins->begin(); i != plugins->end(); ++i) { + SharedPtr<PluginModel> p = i->second; + if (p->type() == Plugin::Internal) { + _internal_menu->items().push_back(Gtk::Menu_Helpers::MenuElem(p->name(), + sigc::bind(sigc::mem_fun(this, &PatchCanvas::load_plugin), p))); + } + } +} + + +#ifdef HAVE_SLV2 +size_t +PatchCanvas::build_plugin_class_menu(Gtk::Menu* menu, + SLV2PluginClass plugin_class, SLV2PluginClasses classes) +{ + size_t num_items = 0; + + // Add submenus + for (unsigned i=0; i < slv2_plugin_classes_size(classes); ++i) { + SLV2PluginClass c = slv2_plugin_classes_get_at(classes, i); + SLV2Value parent = slv2_plugin_class_get_parent_uri(c); + + if (parent && slv2_value_equals(parent, slv2_plugin_class_get_uri(plugin_class))) { + Gtk::Menu_Helpers::MenuElem menu_elem = Gtk::Menu_Helpers::MenuElem( + slv2_value_as_string(slv2_plugin_class_get_label(c))); + + Gtk::Menu* submenu = Gtk::manage(new Gtk::Menu()); + size_t sub_num_items = build_plugin_class_menu(submenu, c, classes); + + if (sub_num_items > 0) { + menu->items().push_back(menu_elem); + Gtk::MenuItem* menu_item = &(menu->items().back()); + menu_item->set_submenu(*submenu); + ++num_items; + } + } + } + + SharedPtr<const ClientStore::Plugins> plugins = App::instance().store()->plugins(); + + // Add LV2 plugins + for (ClientStore::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) { + Glib::RefPtr<Gdk::Pixbuf> icon + = App::instance().icon_from_path(PluginModel::get_lv2_icon_path(p), 16); + if (icon) { + Gtk::Image* image = new Gtk::Image(icon); + menu->items().push_back(Gtk::Menu_Helpers::ImageMenuElem(i->second->name(), + *image, + sigc::bind(sigc::mem_fun(this, &PatchCanvas::load_plugin), i->second))); + } else { + menu->items().push_back(Gtk::Menu_Helpers::MenuElem(i->second->name(), + sigc::bind(sigc::mem_fun(this, &PatchCanvas::load_plugin), i->second))); + ++num_items; + } + } + } + + return num_items; +} + + +void +PatchCanvas::build_plugin_menu() +{ + if (_plugin_menu) { + _plugin_menu->items().clear(); + } else { + _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()); + _plugin_menu = Gtk::manage(new Gtk::Menu()); + plugin_menu_item->set_submenu(*_plugin_menu); + _menu->reorder_child(*plugin_menu_item, 3); + } + + Glib::Mutex::Lock lock(PluginModel::rdf_world()->mutex()); + 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 (ObjectModel::const_iterator i = App::instance().store()->children_begin(_patch); + i != App::instance().store()->children_end(_patch); ++i) { + SharedPtr<NodeModel> node = PtrCast<NodeModel>(i->second); + if (node && node->parent() == _patch) + add_node(node); + } + + // Create pseudo modules for ports (ports on this canvas, not on our module) + for (NodeModel::Ports::const_iterator i = _patch->ports().begin(); + i != _patch->ports().end(); ++i) { + add_port(*i); + } + + // Create connections + for (PatchModel::Connections::const_iterator i = _patch->connections().begin(); + i != _patch->connections().end(); ++i) { + connection(PtrCast<ConnectionModel>(*i)); + } +} + + +void +PatchCanvas::arrange(bool ingen_doesnt_use_length_hints) +{ + FlowCanvas::Canvas::arrange(false); + + for (list<boost::shared_ptr<Item> >::iterator i = _items.begin(); i != _items.end(); ++i) + (*i)->store_location(); +} + + +void +PatchCanvas::show_human_names(bool b) +{ + _human_names = b; + for (ItemList::iterator m = _items.begin(); m != _items.end(); ++m) { + boost::shared_ptr<NodeModule> mod = boost::dynamic_pointer_cast<NodeModule>(*m); + if (mod) + mod->show_human_names(b); + } +} + + +void +PatchCanvas::add_plugin(SharedPtr<PluginModel> pm) +{ + _refresh_menu = true; +} + + +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, _human_names); + } else { + module = NodeModule::create(shared_this, nm, _human_names); + const PluginModel* plugm = dynamic_cast<const PluginModel*>(nm->plugin()); + if (plugm && plugm->icon_path() != "") + module->set_icon(App::instance().icon_from_path(plugm->icon_path(), 100)); + } + + 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<FlowCanvas::Port> +PatchCanvas::get_port_view(SharedPtr<PortModel> port) +{ + SharedPtr<FlowCanvas::Module> module = _views[port]; + + // Port on this patch + if (module) { + return (PtrCast<PatchPortModule>(module)) + ? *(PtrCast<PatchPortModule>(module)->ports().begin()) + : PtrCast<FlowCanvas::Port>(module); + } else { + module = PtrCast<NodeModule>(_views[port->parent()]); + if (module) { + for (PortVector::const_iterator p = module->ports().begin(); + p != module->ports().end(); ++p) { + boost::shared_ptr<GUI::Port> pv = boost::dynamic_pointer_cast<GUI::Port>(*p); + if (pv && pv->model() == port) + return pv; + } + } + } + + return SharedPtr<FlowCanvas::Port>(); +} + + +void +PatchCanvas::connection(SharedPtr<ConnectionModel> cm) +{ + assert(cm); + + const SharedPtr<FlowCanvas::Port> src = get_port_view(cm->src_port()); + const SharedPtr<FlowCanvas::Port> dst = get_port_view(cm->dst_port()); + + if (src && dst) { + add_connection(boost::shared_ptr<GUI::Connection>(new GUI::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<FlowCanvas::Port> src = get_port_view(cm->src_port()); + const SharedPtr<FlowCanvas::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<FlowCanvas::Connectable> src_port, + boost::shared_ptr<FlowCanvas::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()->type().is_event() && dst->model()->type().is_control()) { + cerr << "[PatchCanvas] FIXME: MIDI binding shortcut" << 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_variable("canvas-x", Atom((float) + (dst->module()->property_x() - dst->module()->width() - 20))); + nm->set_variable("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_variable("user-min").get_float()); + App::instance().engine()->set_port_value_queued(nm->path().base() + "Max", + dst->model()->get_variable("user-max").get_float()); +#endif + } else { + App::instance().engine()->connect(src->model()->path(), dst->model()->path()); + } +} + + +void +PatchCanvas::disconnect(boost::shared_ptr<FlowCanvas::Connectable> src_port, + boost::shared_ptr<FlowCanvas::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); + + bool ret = false; + + 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); + ret = true; + } + break; + + case GDK_KEY_PRESS: + case GDK_KEY_RELEASE: + ret = canvas_key_event(&event->key); + + default: + break; + } + + return (ret ? true : Canvas::canvas_event(event)); +} + + +bool +PatchCanvas::canvas_key_event(GdkEventKey* event) +{ + switch (event->type) { + case GDK_KEY_PRESS: + switch (event->keyval) { + case GDK_Delete: + destroy_selection(); + return true; + case GDK_e: + if (event->state == 0) { + if (_patch->get_editable() == true) + _patch->set_editable(false); + else + _patch->set_editable(true); + return true; + } else { + return false; + } + default: + return false; + } + default: + return false; + } +} + + +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::select_all() +{ + unselect_ports(); + for (list<boost::shared_ptr<Item> >::iterator m = _items.begin(); m != _items.end(); ++m) + if (boost::dynamic_pointer_cast<FlowCanvas::Module>(*m)) + if (!(*m)->selected()) + select_item(*m); +} + +void +PatchCanvas::copy_selection() +{ + Serialiser serialiser(*App::instance().world(), App::instance().store()); + serialiser.start_to_string(_patch->path(), "http://example.org/"); + + 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) { + serialiser.serialise(module->node()); + } else { + boost::shared_ptr<PatchPortModule> port_module = boost::dynamic_pointer_cast<PatchPortModule>(*m); + if (port_module) + serialiser.serialise(port_module->port()); + } + } + + for (list<boost::shared_ptr<FlowCanvas::Connection> >::iterator c = _selected_connections.begin(); + c != _selected_connections.end(); ++c) { + boost::shared_ptr<Connection> connection = boost::dynamic_pointer_cast<Connection>(*c); + if (connection) + serialiser.serialise_connection(_patch, connection->model()); + } + + string result = serialiser.finish(); + + Glib::RefPtr<Gtk::Clipboard> clipboard = Gtk::Clipboard::get(); + clipboard->set_text(result); +} + + +void +PatchCanvas::paste() +{ + Glib::ustring str = Gtk::Clipboard::get()->wait_for_text(); + SharedPtr<Parser> parser = App::instance().loader()->parser(); + if (!parser) { + cerr << "Unable to load parser, paste unavailable" << endl; + return; + } + + clear_selection(); + + Builder builder(*App::instance().engine()); + ClientStore clipboard; + clipboard.set_plugins(App::instance().store()->plugins()); + clipboard.new_patch("/", _patch->poly()); + + ClashAvoider avoider(*App::instance().store().get(), _patch->path(), clipboard, &clipboard); + parser->parse_string(App::instance().world(), &avoider, str, "/", _patch->path()); + + for (Store::iterator i = clipboard.begin(); i != clipboard.end(); ++i) { + cout << "************ OBJECT: " << i->first << endl; + if (_patch->path() == "/" && i->first == "/") { + //cout << "SKIPPING ROOT " << _patch->path() << " :: " << i->first << endl; + continue; + } else if (i->first.parent() != "/") { + //cout << "SKIPPING NON ROOTED OBJECT " << i->first << endl; + continue; + } + GraphObject::Variables::iterator x = i->second->variables().find("ingenuity:canvas-x"); + if (x != i->second->variables().end()) + x->second = x->second.get_float() + 20.0f; + GraphObject::Variables::iterator y = i->second->variables().find("ingenuity:canvas-y"); + if (y != i->second->variables().end()) + y->second = y->second.get_float() + 20.0f; + if (i->first.parent() == "/") { + GraphObject::Properties::iterator s = i->second->properties().find("ingen:selected"); + if (s != i->second->properties().end()) + s->second = true; + else + i->second->properties().insert(make_pair("ingen:selected", true)); + } + builder.build(_patch->path(), i->second); + } + + //avoider.set_target(*App::instance().engine()); + + for (ClientStore::ConnectionRecords::const_iterator i = clipboard.connection_records().begin(); + i != clipboard.connection_records().end(); ++i) { + cout << "CONNECTING " << i->first << " -> " << i->second << endl; + App::instance().engine()->connect(i->first, i->second); + } +} + + +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_control(ControlType type) +{ + // FIXME: bundleify + + GraphObject::Variables data = get_initial_data(); + float x = data["ingenuity:canvas-x"].get_float(); + float y = data["ingenuity:canvas-y"].get_float(); + + cerr << "ADD CONTROL: " << (unsigned)type << " @ " << x << ", " << y << endl; + + add_item(boost::shared_ptr<FlowCanvas::Item>( + new FlowCanvas::Ellipse(shared_from_this(), "control", x, y, 20, 20, true))); +} + +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()->bundle_begin(); + App::instance().engine()->new_port(path, _patch->num_ports(), type, is_output); + GraphObject::Variables data = get_initial_data(); + for (GraphObject::Variables::const_iterator i = data.begin(); i != data.end(); ++i) + App::instance().engine()->set_variable(path, i->first, i->second); + App::instance().engine()->bundle_end(); +} + + +void +PatchCanvas::load_plugin(SharedPtr<PluginModel> plugin) +{ + string name = plugin->default_node_symbol(); + unsigned offset = App::instance().store()->child_name_offset(_patch->path(), name); + if (offset != 0) { + std::stringstream ss; + ss << name << "_" << offset; + name = ss.str(); + } + + const Path path = _patch->path().base() + name; + // FIXME: polyphony? + App::instance().engine()->new_node(path, plugin->uri()); + GraphObject::Variables data = get_initial_data(); + for (GraphObject::Variables::const_iterator i = data.begin(); i != data.end(); ++i) + App::instance().engine()->set_variable(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; +} + + +GraphObject::Variables +PatchCanvas::get_initial_data() +{ + GraphObject::Variables 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/gui/PatchCanvas.hpp b/src/gui/PatchCanvas.hpp new file mode 100644 index 00000000..fcf68e76 --- /dev/null +++ b/src/gui/PatchCanvas.hpp @@ -0,0 +1,155 @@ +/* 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 CONFIG_H_PATH + +#include <string> +#include <map> +#include <boost/shared_ptr.hpp> +#include <flowcanvas/Canvas.hpp> +#include <flowcanvas/Module.hpp> +#include <raul/SharedPtr.hpp> +#include <raul/Path.hpp> +#include "client/ConnectionModel.hpp" +#include "client/PatchModel.hpp" +#include "interface/GraphObject.hpp" +#include "NodeModule.hpp" + +using namespace FlowCanvas; +using namespace Ingen::Shared; + +using std::string; +using FlowCanvas::Port; +using Ingen::Client::ConnectionModel; +using Ingen::Client::PatchModel; +using Ingen::Client::NodeModel; +using Ingen::Client::PortModel; + +namespace Ingen { +namespace GUI { + +class NodeModule; + + +/** Patch canvas widget. + * + * \ingroup GUI + */ +class PatchCanvas : public FlowCanvas::Canvas +{ +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>( + Canvas::get_item(name)); + }*/ + + void build(); + void arrange(bool use_length_hints); + void show_human_names(bool show); + + void add_plugin(SharedPtr<PluginModel> pm); + 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 paste(); + void select_all(); + + void show_menu(GdkEvent* event); + + bool canvas_key_event(GdkEventKey* event); + +private: + enum ControlType { NUMBER, BUTTON }; + void menu_add_control(ControlType type); + + 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_internal_menu(); +#ifdef HAVE_SLV2 + void build_plugin_menu(); + size_t build_plugin_class_menu(Gtk::Menu* menu, + SLV2PluginClass plugin_class, SLV2PluginClasses classes); +#endif + + GraphObject::Variables get_initial_data(); + + bool canvas_event(GdkEvent* event); + + SharedPtr<FlowCanvas::Port> get_port_view(SharedPtr<PortModel> port); + + void connect(boost::shared_ptr<FlowCanvas::Connectable> src, + boost::shared_ptr<FlowCanvas::Connectable> dst); + + void disconnect(boost::shared_ptr<FlowCanvas::Connectable> src, + boost::shared_ptr<FlowCanvas::Connectable> dst); + + SharedPtr<PatchModel> _patch; + + typedef std::map<SharedPtr<ObjectModel>, SharedPtr<FlowCanvas::Module> > Views; + Views _views; + + int _last_click_x; + int _last_click_y; + + bool _refresh_menu; + bool _human_names; + Gtk::Menu* _menu; + Gtk::Menu* _internal_menu; + Gtk::Menu* _plugin_menu; + /*Gtk::MenuItem* _menu_add_number_control; + Gtk::MenuItem* _menu_add_button_control;*/ + 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_event_input; + Gtk::MenuItem* _menu_add_event_output; + Gtk::MenuItem* _menu_add_midi_input; + Gtk::MenuItem* _menu_add_midi_output; + Gtk::MenuItem* _menu_add_osc_input; + Gtk::MenuItem* _menu_add_osc_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/gui/PatchPortModule.cpp b/src/gui/PatchPortModule.cpp new file mode 100644 index 00000000..d8aaa91d --- /dev/null +++ b/src/gui/PatchPortModule.cpp @@ -0,0 +1,158 @@ +/* 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 "PatchPortModule.hpp" +#include "interface/EngineInterface.hpp" +#include "client/PatchModel.hpp" +#include "client/NodeModel.hpp" +#include "App.hpp" +#include "PatchCanvas.hpp" +#include "Port.hpp" +#include "GladeFactory.hpp" +#include "RenameWindow.hpp" +#include "PatchWindow.hpp" +#include "WindowFactory.hpp" +#include "PortMenu.hpp" + +namespace Ingen { +namespace GUI { + + +PatchPortModule::PatchPortModule(boost::shared_ptr<PatchCanvas> canvas, SharedPtr<PortModel> port) +: FlowCanvas::Module(canvas, port->path().name(), 0, 0, false), // FIXME: coords? + _port(port) +{ + assert(canvas); + assert(port); + + assert(PtrCast<PatchModel>(port->parent())); + + /*resize(); + + const Atom& x_atom = port->get_variable("ingenuity:canvas-x"); + const Atom& y_atom = port->get_variable("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); + }*/ + + set_stacked_border(port->polyphonic()); + + port->signal_variable.connect(sigc::mem_fun(this, &PatchPortModule::set_variable)); + port->signal_property.connect(sigc::mem_fun(this, &PatchPortModule::set_property)); +} + + +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, port->symbol(), true)); + + ret->add_port(ret->_patch_port); + + ret->set_menu(ret->_patch_port->menu()); + + for (GraphObject::Variables::const_iterator m = port->variables().begin(); m != port->variables().end(); ++m) + ret->set_variable(m->first, m->second); + + for (GraphObject::Properties::const_iterator m = port->properties().begin(); m != port->properties().end(); ++m) + ret->set_property(m->first, m->second); + + ret->resize(); + + return ret; +} + + +void +PatchPortModule::create_menu() +{ + Glib::RefPtr<Gnome::Glade::Xml> xml = GladeFactory::new_glade_reference(); + xml->get_widget_derived("object_menu", _menu); + _menu->init(_port, true); + + set_menu(_menu); +} + + +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_variable("ingenuity:canvas-x"); + const Atom& existing_y = _port->get_variable("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_variable(_port->path(), "ingenuity:canvas-x", Atom(x)); + App::instance().engine()->set_variable(_port->path(), "ingenuity:canvas-y", Atom(y)); + } +} + + +void +PatchPortModule::set_variable(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()); +} + + +void +PatchPortModule::set_property(const string& key, const Atom& value) +{ + if (key == "ingen:polyphonic" && value.type() == Atom::BOOL) { + set_stacked_border(value.get_bool()); + } else if (key == "ingen:selected" && value.type() == Atom::BOOL) { + if (value.get_bool() != selected()) { + if (value.get_bool()) + _canvas.lock()->select_item(shared_from_this()); + else + _canvas.lock()->unselect_item(shared_from_this()); + } + } +} + + +void +PatchPortModule::set_selected(bool b) +{ + if (b != selected()) { + Module::set_selected(b); + if (App::instance().signal()) + App::instance().engine()->set_property(_port->path(), "ingen:selected", b); + } +} + + + +} // namespace GUI +} // namespace Ingen diff --git a/src/gui/PatchPortModule.hpp b/src/gui/PatchPortModule.hpp new file mode 100644 index 00000000..d6715834 --- /dev/null +++ b/src/gui/PatchPortModule.hpp @@ -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 PATCHPORTMODULE_H +#define PATCHPORTMODULE_H + +#include <string> +#include <boost/enable_shared_from_this.hpp> +#include <libgnomecanvasmm.h> +#include <flowcanvas/Module.hpp> +#include <raul/Atom.hpp> +#include "Port.hpp" +using std::string; + +namespace Ingen { namespace Client { + class PortModel; + class NodeModel; +} } +using namespace Ingen::Client; + +namespace Ingen { +namespace GUI { + +class PatchCanvas; +class Port; +class PortMenu; + + +/** 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 FlowCanvas::Module +{ +public: + static boost::shared_ptr<PatchPortModule> create(boost::shared_ptr<PatchCanvas> canvas, + SharedPtr<PortModel> port); + + virtual ~PatchPortModule() {} + + virtual void store_location(); + + SharedPtr<PortModel> port() const { return _port; } + +protected: + PatchPortModule(boost::shared_ptr<PatchCanvas> canvas, SharedPtr<PortModel> port); + + void create_menu(); + void set_selected(bool b); + + void set_variable(const std::string& predicate, const Raul::Atom& value); + void set_property(const std::string& predicate, const Raul::Atom& value); + + SharedPtr<PortModel> _port; + PortMenu* _menu; + SharedPtr<Port> _patch_port; ///< Port on this 'anonymous' module +}; + + +} // namespace GUI +} // namespace Ingen + +#endif // PATCHPORTMODULE_H diff --git a/src/gui/PatchPropertiesWindow.cpp b/src/gui/PatchPropertiesWindow.cpp new file mode 100644 index 00000000..f4003d31 --- /dev/null +++ b/src/gui/PatchPropertiesWindow.cpp @@ -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 + */ + +#include <string> +#include <iostream> +#include "client/PatchModel.hpp" +#include "PatchPropertiesWindow.hpp" +#include "App.hpp" + +using namespace std; + +namespace Ingen { +namespace GUI { + + +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_variable("dc:creator"); + _author_entry->set_text( + (author_atom.type() == Atom::STRING) ? author_atom.get_string() : "" ); + + const Atom& desc_atom = _patch_model->get_variable("dc: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_variable("dc:creator"); + _author_entry->set_text( + (author_atom.type() == Atom::STRING) ? author_atom.get_string() : "" ); + + const Atom& desc_atom = _patch_model->get_variable("dc:description"); + _textview->get_buffer()->set_text( + (desc_atom.type() == Atom::STRING) ? desc_atom.get_string() : "" ); + + hide(); +} + + +void +PatchPropertiesWindow::ok_clicked() +{ + App::instance().engine()->set_variable(_patch_model->path(), "dc:creator", + Atom(_author_entry->get_text())); + App::instance().engine()->set_variable(_patch_model->path(), "dc:description", + Atom(_textview->get_buffer()->get_text())); + hide(); +} + + + +} // namespace GUI +} // namespace Ingen diff --git a/src/gui/PatchPropertiesWindow.hpp b/src/gui/PatchPropertiesWindow.hpp new file mode 100644 index 00000000..0f60b147 --- /dev/null +++ b/src/gui/PatchPropertiesWindow.hpp @@ -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.hpp> +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/gui/PatchTreeWindow.cpp b/src/gui/PatchTreeWindow.cpp new file mode 100644 index 00000000..4730da2b --- /dev/null +++ b/src/gui/PatchTreeWindow.cpp @@ -0,0 +1,243 @@ +/* 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.hpp> +#include "interface/EngineInterface.hpp" +#include "client/OSCEngineSender.hpp" +#include "client/ClientStore.hpp" +#include "client/PatchModel.hpp" +#include "App.hpp" +#include "PatchTreeWindow.hpp" +#include "SubpatchModule.hpp" +#include "WindowFactory.hpp" + +using namespace std; + +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(ClientStore& store) +{ + store.signal_new_object.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->uri() : "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->signal_property.connect(sigc::bind(sigc::mem_fun(this, &PatchTreeWindow::patch_property_changed), 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 (_enable_signal) + App::instance().engine()->set_property(patch_path, "ingen:enabled", (bool)!pm->enabled()); +} + + +void +PatchTreeWindow::patch_property_changed(const string& key, const Raul::Atom& value, const Path& path) +{ + _enable_signal = false; + if (key == "ingen:enabled" && value.type() == Atom::BOOL) { + 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] = value.get_bool(); + } 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/gui/PatchTreeWindow.hpp b/src/gui/PatchTreeWindow.hpp new file mode 100644 index 00000000..f549a322 --- /dev/null +++ b/src/gui/PatchTreeWindow.hpp @@ -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 + */ + +#ifndef PATCHTREEWINDOW_H +#define PATCHTREEWINDOW_H + +#include <gtkmm.h> +#include <libglademm.h> +#include <raul/Path.hpp> + +namespace Ingen { namespace Client { + class ClientStore; +} } +using Ingen::Client::ClientStore; + +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(ClientStore& store); + + void new_object(SharedPtr<Client::ObjectModel> object); + + void patch_property_changed(const string& key, const Raul::Atom& value, const Path& path); + void patch_renamed(const Path& old_path, const Path& new_path); + + void add_patch(SharedPtr<Client::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<Client::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/gui/PatchView.cpp b/src/gui/PatchView.cpp new file mode 100644 index 00000000..13689806 --- /dev/null +++ b/src/gui/PatchView.cpp @@ -0,0 +1,194 @@ +/* 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.hpp" +#include "client/PatchModel.hpp" +#include "App.hpp" +#include "PatchView.hpp" +#include "PatchCanvas.hpp" +#include "LoadPluginWindow.hpp" +#include "NewSubpatchWindow.hpp" +#include "LoadSubpatchWindow.hpp" +#include "NodeControlWindow.hpp" +#include "PatchPropertiesWindow.hpp" +#include "PatchTreeWindow.hpp" +#include "GladeFactory.hpp" + +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_toolbar", _toolbar); + 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_edit_mode_but", _edit_mode_but); + xml->get_widget("patch_view_scrolledwindow", _canvas_scrolledwindow); + + _toolbar->set_toolbar_style(Gtk::TOOLBAR_ICONS); + _canvas_scrolledwindow->property_hadjustment().get_value()->set_step_increment(10); + _canvas_scrolledwindow->property_vadjustment().get_value()->set_step_increment(10); + +} + + +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() != "/"); + + for (GraphObject::Properties::const_iterator i = patch->properties().begin(); + i != patch->properties().end(); ++i) + property_changed(i->first, i->second); + + // Connect model signals to track state + patch->signal_property.connect(sigc::mem_fun(this, &PatchView::property_changed)); + + // 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(), &PatchCanvas::set_zoom), 1.0)); + + _zoom_full_but->signal_clicked().connect( + sigc::mem_fun(_canvas.get(), &PatchCanvas::zoom_full)); + + patch->signal_editable.connect(sigc::mem_fun( + *this, &PatchView::on_editable_sig)); + + _edit_mode_but->signal_toggled().connect(sigc::mem_fun( + *this, &PatchView::editable_toggled)); + + _poly_spin->signal_value_changed().connect( + sigc::mem_fun(*this, &PatchView::poly_changed)); + + _canvas->grab_focus(); +} + + +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::on_editable_sig(bool editable) +{ + _edit_mode_but->set_active(editable); + _canvas->lock(!editable); +} + + +void +PatchView::editable_toggled() +{ + const bool editable = _edit_mode_but->get_active(); + _patch->set_editable(editable); + _canvas->lock(!editable); +} + + +void +PatchView::process_toggled() +{ + if (!_enable_signal) + return; + + App::instance().engine()->set_property(_patch->path(), "ingen:enabled", + (bool)_process_but->get_active()); +} + + +void +PatchView::poly_changed() +{ + App::instance().engine()->set_property(_patch->path(), "ingen:polyphony", + _poly_spin->get_value_as_int()); +} + + +void +PatchView::clear_clicked() +{ + App::instance().engine()->clear_patch(_patch->path()); +} + + +void +PatchView::refresh_clicked() +{ + App::instance().engine()->request_object(_patch->path()); +} + + +void +PatchView::property_changed(const std::string& predicate, const Raul::Atom& value) +{ + _enable_signal = false; + if (predicate == "ingen:enabled" && value.type() == Atom::BOOL) + _process_but->set_active(value.get_bool()); + _enable_signal = true; +} + + +} // namespace GUI +} // namespace Ingen diff --git a/src/gui/PatchView.hpp b/src/gui/PatchView.hpp new file mode 100644 index 00000000..2c0570bd --- /dev/null +++ b/src/gui/PatchView.hpp @@ -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 PATCHVIEW_H +#define PATCHVIEW_H + +#include <string> +#include <gtkmm.h> +#include <libglademm/xml.h> +#include <libglademm.h> +#include <raul/SharedPtr.hpp> +#include "client/PatchModel.hpp" + +using std::string; + +namespace Ingen { namespace Client { + class PortModel; + 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 poly_changed(); + void clear_clicked(); + void refresh_clicked(); + void on_editable_sig(bool locked); + void editable_toggled(); + + void property_changed(const std::string& predicate, const Raul::Atom& value); + + void zoom_full(); + + SharedPtr<PatchModel> _patch; + SharedPtr<PatchCanvas> _canvas; + + Gtk::ScrolledWindow* _canvas_scrolledwindow; + + Gtk::Toolbar* _toolbar; + 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::ToggleToolButton* _edit_mode_but; + Gtk::Viewport* _breadcrumb_container; + + bool _enable_signal; +}; + + +} // namespace GUI +} // namespace Ingen + +#endif // PATCHVIEW_H diff --git a/src/gui/PatchWindow.cpp b/src/gui/PatchWindow.cpp new file mode 100644 index 00000000..d7499bfb --- /dev/null +++ b/src/gui/PatchWindow.cpp @@ -0,0 +1,556 @@ +/* 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.hpp" +#include <iostream> +#include <cassert> +#include <fstream> +#include "interface/EngineInterface.hpp" +#include "client/PatchModel.hpp" +#include "client/ClientStore.hpp" +#include "App.hpp" +#include "PatchCanvas.hpp" +#include "LoadPluginWindow.hpp" +#include "NewSubpatchWindow.hpp" +#include "LoadPatchWindow.hpp" +#include "LoadSubpatchWindow.hpp" +#include "NodeControlWindow.hpp" +#include "PatchPropertiesWindow.hpp" +#include "Configuration.hpp" +#include "MessagesWindow.hpp" +#include "PatchTreeWindow.hpp" +#include "BreadCrumbBox.hpp" +#include "ConnectWindow.hpp" +#include "ThreadedLoader.hpp" +#include "WindowFactory.hpp" +#include "PatchView.hpp" + +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_select_all_menuitem", _menu_select_all); + xml->get_widget("patch_close_menuitem", _menu_close); + xml->get_widget("patch_quit_menuitem", _menu_quit); + xml->get_widget("patch_view_control_window_menuitem", _menu_view_control_window); + xml->get_widget("patch_view_engine_window_menuitem", _menu_view_engine_window); + xml->get_widget("patch_properties_menuitem", _menu_view_patch_properties); + xml->get_widget("patch_fullscreen_menuitem", _menu_fullscreen); + xml->get_widget("patch_human_names_menuitem", _menu_human_names); + xml->get_widget("patch_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_paste->signal_activate().connect( + sigc::mem_fun(this, &PatchWindow::event_paste)); + _menu_delete->signal_activate().connect( + sigc::mem_fun(this, &PatchWindow::event_delete)); + _menu_select_all->signal_activate().connect( + sigc::mem_fun(this, &PatchWindow::event_select_all)); + _menu_quit->signal_activate().connect( + sigc::mem_fun(this, &PatchWindow::event_quit)); + _menu_fullscreen->signal_activate().connect( + sigc::mem_fun(this, &PatchWindow::event_fullscreen_toggled)); + _menu_human_names->signal_activate().connect( + sigc::mem_fun(this, &PatchWindow::event_human_names_toggled)); + _menu_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::hide_return( + sigc::mem_fun(App::instance(), &App::show_about))); + + _breadcrumb_box = new BreadCrumbBox(); + _breadcrumb_box->signal_patch_selected.connect(sigc::mem_fun(this, &PatchWindow::set_patch_from_path)); + +#ifndef HAVE_CURL + _menu_upload->hide(); +#endif + + Glib::RefPtr<Gtk::Clipboard> clipboard = Gtk::Clipboard::get(); + clipboard->signal_owner_change().connect(sigc::mem_fun(this, &PatchWindow::event_clipboard_changed)); +} + + +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; + + new_port_connection.disconnect(); + removed_port_connection.disconnect(); + + _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 (NodeModel::Ports::const_iterator p = patch->ports().begin(); + p != patch->ports().end(); ++p) { + if ((*p)->type().is_control() && (*p)->is_input()) { + _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() + " - Ingen"); + + //m_properties_window->patch_model(pc->patch_model()); + + if (patch->path() == "/") + _menu_destroy_patch->set_sensitive(false); + else + _menu_destroy_patch->set_sensitive(true); + + new_port_connection = patch->signal_new_port.connect(sigc::mem_fun(this, &PatchWindow::patch_port_added)); + removed_port_connection = patch->signal_removed_port.connect(sigc::mem_fun(this, &PatchWindow::patch_port_removed)); + show_all(); + + _enable_signal = true; +} + + +void +PatchWindow::patch_port_added(SharedPtr<PortModel> port) +{ + if (port->type().is_control() && port->is_input()) { + _menu_view_control_window->property_sensitive() = true; + } +} + + +void +PatchWindow::patch_port_removed(SharedPtr<PortModel> port) +{ + if (port->type().is_control() && port->is_input()) { + + bool found_control = false; + + for (NodeModel::Ports::const_iterator i = _patch->ports().begin(); i != _patch->ports().end(); ++i) { + if ((*i)->type().is_control() && (*i)->is_input()) { + found_control = true; + break; + } + } + + _menu_view_control_window->property_sensitive() = found_control; + } +} + + + +void +PatchWindow::event_show_engine() +{ + if (_patch) + App::instance().connect_window()->show(); +} + + +void +PatchWindow::event_clipboard_changed(GdkEventOwnerChange* ev) +{ + Glib::RefPtr<Gtk::Clipboard> clipboard = Gtk::Clipboard::get(); + _menu_paste->set_sensitive(clipboard->wait_is_text_available()); +} + + +void +PatchWindow::event_show_controls() +{ + App::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() +{ + GraphObject::Variables::const_iterator doc = _patch->variables().find("ingen:document"); + if (doc == _patch->variables().end()) + event_save_as(); + else + App::instance().loader()->save_patch(_patch, doc->second.get_string()); +} + + +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 + GraphObject::Variables::const_iterator doc = _patch->variables().find("ingen:document"); + if (doc != _patch->variables().end()) + dialog.set_uri(doc->second.get_string()); + 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(); + + 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); + } + } + 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_paste() +{ + if (_view) + _view->canvas()->paste(); +} + + +void +PatchWindow::event_delete() +{ + if (_view) + _view->canvas()->destroy_selection(); +} + + +void +PatchWindow::event_select_all() +{ + if (_view) + _view->canvas()->select_all(); +} + + +void +PatchWindow::on_show() +{ + 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) +{ + bool ret = false; + + ret = _view->canvas()->canvas_key_event(event); + + if (!ret) + ret = Gtk::Window::on_key_press_event(event); + + return ret; +} + + +bool +PatchWindow::on_key_release_event(GdkEventKey* event) +{ + bool ret = false; + + ret = _view->canvas()->canvas_key_event(event); + + if (!ret) + ret = Gtk::Window::on_key_release_event(event); + + return ret; +} + + +void +PatchWindow::event_quit() +{ + Gtk::MessageDialog d(*this, "Would you like to quit just this GUI\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(false); +} + + +void +PatchWindow::event_fullscreen_toggled() +{ + // FIXME: ugh, use GTK signals to track state and know for sure + static bool is_fullscreen = false; + + if (!is_fullscreen) { + fullscreen(); + is_fullscreen = true; + } else { + unfullscreen(); + is_fullscreen = false; + } +} + + +void +PatchWindow::event_human_names_toggled() +{ + _view->canvas()->show_human_names(_menu_human_names->get_active()); + if (_menu_human_names->get_active()) + App::instance().configuration()->set_name_style(Configuration::HUMAN); + else + App::instance().configuration()->set_name_style(Configuration::PATH); +} + + +} // namespace GUI +} // namespace Ingen diff --git a/src/gui/PatchWindow.hpp b/src/gui/PatchWindow.hpp new file mode 100644 index 00000000..4d4f0f1e --- /dev/null +++ b/src/gui/PatchWindow.hpp @@ -0,0 +1,155 @@ +/* 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.hpp> +#include <raul/SharedPtr.hpp> +#include "client/PatchModel.hpp" +#include "PatchView.hpp" +using Ingen::Client::PatchModel; + +using std::string; using std::list; + + +namespace Ingen { namespace Client { + class PatchModel; + class PortModel; + 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); + bool on_key_release_event(GdkEventKey* event); + +private: + + void patch_port_added(SharedPtr<PortModel> port); + void patch_port_removed(SharedPtr<PortModel> port); + + void event_import(); + void event_import_location(); + void event_save(); + void event_save_as(); + void event_upload(); + void event_copy(); + void event_paste(); + void event_delete(); + void event_select_all(); + void event_quit(); + void event_destroy(); + void event_clear(); + void event_fullscreen_toggled(); + void event_human_names_toggled(); + void event_arrange(); + void event_show_properties(); + void event_show_controls(); + void event_show_engine(); + void event_clipboard_changed(GdkEventOwnerChange* ev); + + SharedPtr<PatchModel> _patch; + SharedPtr<PatchView> _view; + + sigc::connection new_port_connection; + sigc::connection removed_port_connection; + + 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_select_all; + Gtk::MenuItem* _menu_close; + Gtk::MenuItem* _menu_quit; + Gtk::CheckMenuItem* _menu_human_names; + 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/gui/Port.cpp b/src/gui/Port.cpp new file mode 100644 index 00000000..031c74c5 --- /dev/null +++ b/src/gui/Port.cpp @@ -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 + */ + +#include <cassert> +#include <iostream> +#include "interface/EngineInterface.hpp" +#include "flowcanvas/Module.hpp" +#include "client/PatchModel.hpp" +#include "client/PortModel.hpp" +#include "Configuration.hpp" +#include "App.hpp" +#include "Port.hpp" +#include "PortMenu.hpp" +#include "GladeFactory.hpp" + +using namespace Ingen::Client; +using namespace std; + +namespace Ingen { +namespace GUI { + + +/** @param flip Make an input port appear as an output port, and vice versa. + */ +Port::Port( + boost::shared_ptr<FlowCanvas::Module> module, + SharedPtr<PortModel> pm, + const string& name, + bool flip) + : FlowCanvas::Port(module, name, + flip ? (!pm->is_input()) : pm->is_input(), + App::instance().configuration()->get_port_color(pm.get())) + , _port_model(pm) + , _flipped(flip) +{ + assert(module); + assert(_port_model); + + delete _menu; + _menu = NULL; + + _port_model->signal_renamed.connect(sigc::mem_fun(this, &Port::renamed)); + + if (pm->type().is_control()) { + set_toggled(pm->is_toggle()); + show_control(); + + float min = 0.0f, max = 1.0f; + boost::shared_ptr<NodeModel> parent = PtrCast<NodeModel>(_port_model->parent()); + if (parent) + parent->port_value_range(_port_model, min, max); + + set_control_min(min); + set_control_max(max); + + pm->signal_variable.connect(sigc::mem_fun(this, &Port::variable_changed)); + _port_model->signal_value_changed.connect(sigc::mem_fun(this, &Port::value_changed)); + } + + _port_model->signal_activity.connect(sigc::mem_fun(this, &Port::activity)); + + value_changed(_port_model->value()); +} + + +Port::~Port() +{ + App::instance().activity_port_destroyed(this); +} + + +void +Port::create_menu() +{ + PortMenu* menu = NULL; + Glib::RefPtr<Gnome::Glade::Xml> xml = GladeFactory::new_glade_reference(); + xml->get_widget_derived("object_menu", menu); + menu->init(_port_model, _flipped); + set_menu(menu); +} + + +void +Port::renamed() +{ + set_name(_port_model->path().name()); + module().lock()->resize(); +} + + +void +Port::value_changed(const Atom& value) +{ + if (value.type() == Atom::FLOAT) + FlowCanvas::Port::set_control(value.get_float()); + else + cerr << "WARNING: Unknown port value type " << (unsigned)value.type() << endl; +} + + +void +Port::activity() +{ + App::instance().port_activity(this); +} + + +void +Port::set_control(float value, bool signal) +{ + if (signal) { + if (_port_model->type() == DataType::CONTROL) { + App::instance().engine()->set_port_value(_port_model->path(), Atom(value)); + } else if (_port_model->type() == DataType::EVENT) { + App::instance().engine()->set_port_value(_port_model->path(), + Atom("<http://example.org/ev#BangEvent>", 0, NULL)); + } + } + + FlowCanvas::Port::set_control(value); +} + + +void +Port::variable_changed(const string& key, const Atom& value) +{ + if ( (key == "ingen:minimum") && value.type() == Atom::FLOAT) + set_control_min(value.get_float()); + else if ( (key == "ingen:maximum") && value.type() == Atom::FLOAT) + set_control_max(value.get_float()); + else if ( (key == "ingen:toggled") && value.type() == Atom::BOOL) + set_toggled(value.get_bool()); +} + + +} // namespace GUI +} // namespace Ingen diff --git a/src/gui/Port.hpp b/src/gui/Port.hpp new file mode 100644 index 00000000..7b347194 --- /dev/null +++ b/src/gui/Port.hpp @@ -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 GUI_PORT_H +#define GUI_PORT_H + +#include <cassert> +#include <string> +#include <flowcanvas/Port.hpp> +#include <raul/SharedPtr.hpp> +#include <raul/Atom.hpp> + +namespace Ingen { namespace Client { class PortModel; } } +using Ingen::Client::PortModel; + +namespace Ingen { +namespace GUI { + + +/** A Port on an Module. + * + * \ingroup GUI + */ +class Port : public FlowCanvas::Port +{ +public: + Port(boost::shared_ptr<FlowCanvas::Module> module, + SharedPtr<PortModel> pm, + const std::string& name, + bool flip=false); + + ~Port(); + + SharedPtr<PortModel> model() const { return _port_model; } + + void create_menu(); + + virtual void set_control(float value, bool signal); + void value_changed(const Raul::Atom& value); + void activity(); + +private: + + void variable_changed(const std::string& key, const Raul::Atom& value); + + void renamed(); + + SharedPtr<PortModel> _port_model; + bool _flipped; +}; + + +} // namespace GUI +} // namespace Ingen + +#endif // GUI_PORT_H diff --git a/src/gui/PortMenu.cpp b/src/gui/PortMenu.cpp new file mode 100644 index 00000000..45b216b6 --- /dev/null +++ b/src/gui/PortMenu.cpp @@ -0,0 +1,69 @@ +/* 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 <raul/SharedPtr.hpp> +#include "interface/EngineInterface.hpp" +#include "client/PortModel.hpp" +#include "App.hpp" +#include "PortMenu.hpp" +#include "WindowFactory.hpp" + +namespace Ingen { +namespace GUI { + + +PortMenu::PortMenu(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& xml) + : ObjectMenu(cobject, xml) + , _patch_port(NULL) +{ +} + + +void +PortMenu::init(SharedPtr<PortModel> port, bool patch_port) +{ + ObjectMenu::init(port); + _patch_port = patch_port; + + if ( ! PtrCast<PatchModel>(port->parent()) ) { + _polyphonic_menuitem->set_sensitive(false); + _rename_menuitem->hide(); + _destroy_menuitem->hide(); + } + + _enable_signal = true; +} + + +void +PortMenu::on_menu_disconnect() +{ + if (_patch_port) { + App::instance().engine()->disconnect_all( + _object->parent()->path(), _object->path()); + } else { + App::instance().engine()->disconnect_all( + _object->parent()->path().parent(), _object->path()); + } +} + + +} // namespace GUI +} // namespace Ingen + diff --git a/src/gui/PortMenu.hpp b/src/gui/PortMenu.hpp new file mode 100644 index 00000000..520c5809 --- /dev/null +++ b/src/gui/PortMenu.hpp @@ -0,0 +1,55 @@ +/* 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 PORTMENU_H +#define PORTMENU_H + +#include <string> +#include <gtkmm.h> +#include <raul/Path.hpp> +#include <raul/SharedPtr.hpp> +#include "client/PortModel.hpp" +#include "ObjectMenu.hpp" + +using Ingen::Client::PortModel; + +namespace Ingen { +namespace GUI { + + +/** Menu for a Port. + * + * \ingroup GUI + */ +class PortMenu : public ObjectMenu +{ +public: + PortMenu(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& xml); + + void init(SharedPtr<PortModel> port, bool patch_port = false); + +private: + void on_menu_disconnect(); + + bool _patch_port; +}; + + +} // namespace GUI +} // namespace Ingen + +#endif // PORTMENU_H diff --git a/src/gui/PortPropertiesWindow.cpp b/src/gui/PortPropertiesWindow.cpp new file mode 100644 index 00000000..ddab5715 --- /dev/null +++ b/src/gui/PortPropertiesWindow.cpp @@ -0,0 +1,156 @@ +/* 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.hpp" +#include "client/NodeModel.hpp" +#include "client/PluginModel.hpp" +#include "App.hpp" +#include "Controls.hpp" +#include "PortPropertiesWindow.hpp" + +using namespace std; + +namespace Ingen { +namespace GUI { + + +PortPropertiesWindow::PortPropertiesWindow(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& xml) + : Gtk::Window(cobject) + , _enable_signal(false) +{ + 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::present(SharedPtr<PortModel> pm) +{ + assert(pm); + + for (list<sigc::connection>::iterator i = _connections.begin(); i != _connections.end(); ++i) + (*i).disconnect(); + + _connections.clear(); + + _port_model = pm; + + set_title(pm->path() + " Properties"); + + float min = 0.0f, max = 1.0f; + boost::shared_ptr<NodeModel> parent = PtrCast<NodeModel>(_port_model->parent()); + if (parent) + parent->port_value_range(_port_model, min, max); + + _initial_min = min; + _initial_max = max; + + _min_spinner->set_value(min); + _connections.push_back(_min_spinner->signal_value_changed().connect( + sigc::mem_fun(*this, &PortPropertiesWindow::min_changed))); + + _max_spinner->set_value(max); + _connections.push_back(_max_spinner->signal_value_changed().connect( + sigc::mem_fun(*this, &PortPropertiesWindow::max_changed))); + + _connections.push_back(pm->signal_variable.connect( + sigc::mem_fun(this, &PortPropertiesWindow::variable_change))); + + _enable_signal = true; + + Gtk::Window::present(); +} + + +void +PortPropertiesWindow::variable_change(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() +{ + 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); + } + + if (_enable_signal) + App::instance().engine()->set_variable(_port_model->path(), "ingen:minimum", min); +} + + +void +PortPropertiesWindow::max_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); + } + + if (_enable_signal) + App::instance().engine()->set_variable(_port_model->path(), "ingen:maximum", max); +} + + +void +PortPropertiesWindow::cancel() +{ + App::instance().engine()->set_variable(_port_model->path(), "ingen:minimum", _initial_min); + App::instance().engine()->set_variable(_port_model->path(), "ingen:maximum", _initial_max); + hide(); +} + + +void +PortPropertiesWindow::ok() +{ + hide(); +} + + +} // namespace GUI +} // namespace Ingen + diff --git a/src/gui/PortPropertiesWindow.hpp b/src/gui/PortPropertiesWindow.hpp new file mode 100644 index 00000000..4580c7c6 --- /dev/null +++ b/src/gui/PortPropertiesWindow.hpp @@ -0,0 +1,68 @@ +/* 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.hpp> +#include "client/PortModel.hpp" +using namespace Ingen::Client; + +namespace Ingen { +namespace GUI { + + +/** Port properties window. + * + * Loaded by libglade as a derived object. + * + * \ingroup GUI + */ +class PortPropertiesWindow : public Gtk::Window +{ +public: + PortPropertiesWindow(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& refGlade); + + void present(SharedPtr<PortModel> port_model); + +private: + void variable_change(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; + + SharedPtr<PortModel> _port_model; + Gtk::SpinButton* _min_spinner; + Gtk::SpinButton* _max_spinner; + Gtk::Button* _cancel_button; + Gtk::Button* _ok_button; + std::list<sigc::connection> _connections; +}; + +} // namespace GUI +} // namespace Ingen + +#endif // PORTPROPERTIESWINDOW_H diff --git a/src/gui/RenameWindow.cpp b/src/gui/RenameWindow.cpp new file mode 100644 index 00000000..b9c252d0 --- /dev/null +++ b/src/gui/RenameWindow.cpp @@ -0,0 +1,119 @@ +/* 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.hpp" +#include "client/ObjectModel.hpp" +#include "client/ClientStore.hpp" +#include "App.hpp" +#include "RenameWindow.hpp" + +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 (!Path::is_valid_name(name)) { + _message_label->set_text("Name contains invalid characters"); + _ok_button->property_sensitive() = false; + } else if ((App::instance().store()->object(_object->parent()->path().base() + name)) && + (name != _object->path().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() +{ + _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/gui/RenameWindow.hpp b/src/gui/RenameWindow.hpp new file mode 100644 index 00000000..3cf37f7c --- /dev/null +++ b/src/gui/RenameWindow.hpp @@ -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.hpp> +#include "client/ObjectModel.hpp" +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/gui/SubpatchModule.cpp b/src/gui/SubpatchModule.cpp new file mode 100644 index 00000000..b2a4d43f --- /dev/null +++ b/src/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.hpp" +#include <cassert> +#include <iostream> +#include "interface/EngineInterface.hpp" +#include "client/PatchModel.hpp" +#include "App.hpp" +#include "NodeModule.hpp" +#include "NodeControlWindow.hpp" +#include "PatchWindow.hpp" +#include "PatchCanvas.hpp" +#include "Port.hpp" +#include "WindowFactory.hpp" +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/gui/SubpatchModule.hpp b/src/gui/SubpatchModule.hpp new file mode 100644 index 00000000..44e37e62 --- /dev/null +++ b/src/gui/SubpatchModule.hpp @@ -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.hpp> +#include "client/PatchModel.hpp" +#include "PatchPortModule.hpp" +#include "NodeModule.hpp" +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/gui/ThreadedLoader.cpp b/src/gui/ThreadedLoader.cpp new file mode 100644 index 00000000..008b14c4 --- /dev/null +++ b/src/gui/ThreadedLoader.cpp @@ -0,0 +1,155 @@ +/* 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 <string> +#include "module/global.hpp" +#include "module/World.hpp" +#include "module/Module.hpp" +#include "client/PatchModel.hpp" +#include "App.hpp" +#include "ThreadedLoader.hpp" + +using namespace std; + +namespace Ingen { +namespace GUI { + + +ThreadedLoader::ThreadedLoader(SharedPtr<EngineInterface> engine) + : _engine(engine) + , _deprecated_loader(engine) +{ + set_name("Loader"); + + if (parser()) + start(); + else + cerr << "WARNING: Failed to load ingen_serialisation module, load disabled." << endl; +} + + +SharedPtr<Parser> +ThreadedLoader::parser() +{ + if (_parser) + return _parser; + + World* world = App::instance().world(); + if (!world->serialisation_module) + world->serialisation_module = Ingen::Shared::load_module("ingen_serialisation"); + + if (world->serialisation_module) { + Parser* (*new_parser)() = NULL; + + bool found = App::instance().world()->serialisation_module->get_symbol( + "new_parser", (void*&)new_parser); + + if (found) + _parser = SharedPtr<Parser>(new_parser()); + } + + return _parser; +} + + +void +ThreadedLoader::_whipped() +{ + _mutex.lock(); + + while ( ! _events.empty() ) { + _events.front()(); + _events.pop_front(); + } + + _mutex.unlock(); +} + +void +ThreadedLoader::load_patch(bool merge, + const Glib::ustring& data_base_uri, + const Path& data_path, + GraphObject::Variables engine_data, + optional<Path> engine_parent, + optional<Symbol> engine_symbol) +{ + _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_symbol) ? engine_symbol.get() : "", + engine_data, + false))); + } else { + Glib::ustring engine_base = ""; + if (engine_parent) { + if (merge) + engine_base = engine_parent.get(); + else + engine_base = engine_parent.get().base(); + } + + if (merge && (!engine_parent || engine_parent.get() == "/")) + engine_base = engine_base.substr(0, engine_base.find_last_of("/")); + + _events.push_back(sigc::hide_return(sigc::bind( + sigc::mem_fun(_parser.get(), &Ingen::Serialisation::Parser::parse_document), + App::instance().world(), + App::instance().world()->engine.get(), + data_base_uri, // document + data_base_uri + data_path.substr(1), // object URI document + engine_base, + engine_symbol, + engine_data))); + } + + whip(); + + _mutex.unlock(); +} + + +void +ThreadedLoader::save_patch(SharedPtr<PatchModel> model, const string& filename) +{ + _mutex.lock(); + + _events.push_back(sigc::hide_return(sigc::bind( + sigc::mem_fun(this, &ThreadedLoader::save_patch_event), + model, filename))); + + _mutex.unlock(); + + whip(); +} + + +void +ThreadedLoader::save_patch_event(SharedPtr<PatchModel> model, const string& filename) +{ + if (App::instance().serialiser()) + App::instance().serialiser()->to_file(model, filename); +} + + +} // namespace GUI +} // namespace Ingen diff --git a/src/gui/ThreadedLoader.hpp b/src/gui/ThreadedLoader.hpp new file mode 100644 index 00000000..3cf88cf9 --- /dev/null +++ b/src/gui/ThreadedLoader.hpp @@ -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 THREADEDLOADER_H +#define THREADEDLOADER_H + +#include <string> +#include <list> +#include <cassert> +#include <boost/optional/optional.hpp> +#include <raul/Thread.hpp> +#include <raul/Slave.hpp> +#include <glibmm/thread.h> +#include "interface/EngineInterface.hpp" +#include "client/PatchModel.hpp" +#include "client/DeprecatedLoader.hpp" +#include "serialisation/Serialiser.hpp" +#include "serialisation/Parser.hpp" +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); + + void load_patch(bool merge, + const Glib::ustring& data_base_uri, + const Path& data_path, + GraphObject::Variables engine_data, + optional<Path> engine_parent = optional<Path>(), + optional<Symbol> engine_symbol = optional<Symbol>()); + + void save_patch(SharedPtr<PatchModel> model, const string& filename); + + SharedPtr<Parser> parser(); + +private: + + void save_patch_event(SharedPtr<PatchModel> model, const string& filename); + + /** Returns nothing and takes no parameters (because they have all been bound) */ + typedef sigc::slot<void> Closure; + + void _whipped(); + + SharedPtr<EngineInterface> _engine; + SharedPtr<Parser> _parser; + + DeprecatedLoader _deprecated_loader; + Glib::Mutex _mutex; + list<Closure> _events; +}; + + +} // namespace GUI +} // namespace Ingen + +#endif // LOADERRTHREAD_H diff --git a/src/gui/UploadPatchWindow.cpp b/src/gui/UploadPatchWindow.cpp new file mode 100644 index 00000000..2ca54f3c --- /dev/null +++ b/src/gui/UploadPatchWindow.cpp @@ -0,0 +1,282 @@ +/* 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 <redlandmm/Query.hpp> +#include "module/global.hpp" +#include "module/World.hpp" +#include "client/ClientStore.hpp" +#include "interface/EngineInterface.hpp" +#include "serialisation/Serialiser.hpp" +#include "client/PatchModel.hpp" +#include "UploadPatchWindow.hpp" +#include "App.hpp" +#include "Configuration.hpp" +#include "ThreadedLoader.hpp" + +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_variable("lv2:symbol"); + if (atom.is_valid()) + _symbol_entry->set_text(atom.get_string()); + + atom = _patch->get_variable("doap:name"); + if (atom.is_valid()) + _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(); + + GraphObject::Variables extra_rdf; + extra_rdf["lv2:symbol"] = Atom(symbol); + extra_rdf["doap:name"] = Atom(short_name); + + _response = 0; + _progress_pct = 0; + + _upload_progress->set_fraction(0.0); + _upload_progress->set_text(""); + + Serialiser s(*App::instance().world(), App::instance().store()); + + const string uri = string("http://rdf.drobilla.net/ingen_patches/") + .append(symbol).append(".ingen.ttl"); + + const string str = s.to_string(_patch, uri, extra_rdf); + + _thread = new UploadThread(this, str, uri); + + _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/gui/UploadPatchWindow.hpp b/src/gui/UploadPatchWindow.hpp new file mode 100644 index 00000000..af064bdb --- /dev/null +++ b/src/gui/UploadPatchWindow.hpp @@ -0,0 +1,104 @@ +/* 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.hpp> +#include <raul/Thread.hpp> +#include <raul/AtomicInt.hpp> +#include "client/PatchModel.hpp" +#include "client/PluginModel.hpp" +using Ingen::Client::PatchModel; + +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/gui/WindowFactory.cpp b/src/gui/WindowFactory.cpp new file mode 100644 index 00000000..125a242b --- /dev/null +++ b/src/gui/WindowFactory.cpp @@ -0,0 +1,381 @@ +/* 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_PATH +#include "WindowFactory.hpp" +#include "App.hpp" +#include "PatchWindow.hpp" +#include "GladeFactory.hpp" +#include "PatchPropertiesWindow.hpp" +#include "NodePropertiesWindow.hpp" +#include "PortPropertiesWindow.hpp" +#include "NodeControlWindow.hpp" +#include "LoadPluginWindow.hpp" +#include "LoadPatchWindow.hpp" +#include "LoadRemotePatchWindow.hpp" +#include "LoadSubpatchWindow.hpp" +#include "RenameWindow.hpp" +#include "NewSubpatchWindow.hpp" +#ifdef HAVE_CURL +#include "UploadPatchWindow.hpp" +#endif + +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) + , _patch_properties_win(NULL) + , _node_properties_win(NULL) + , _port_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("new_subpatch_win", _new_subpatch_win); + xml->get_widget_derived("load_subpatch_win", _load_subpatch_win); + xml->get_widget_derived("patch_properties_win", _patch_properties_win); + xml->get_widget_derived("node_properties_win", _node_properties_win); + xml->get_widget_derived("port_properties_win", _port_properties_win); + xml->get_widget_derived("rename_win", _rename_win); + +#ifdef HAVE_CURL + xml->get_widget_derived("upload_patch_win", _upload_patch_win); +#endif +} + + +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 the GUI (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) +{ + uint32_t poly = 1; + if (node->polyphonic() && node->parent()) + poly = ((PatchModel*)node->parent().get())->internal_polyphony(); + + 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, GraphObject::Variables 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->set_modal(false); + _load_plugin_win->present(patch, data); +} + + +void +WindowFactory::present_load_patch(SharedPtr<PatchModel> patch, GraphObject::Variables 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, GraphObject::Variables 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) +{ +#ifdef HAVE_CURL + 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); +#endif +} + +void +WindowFactory::present_new_subpatch(SharedPtr<PatchModel> patch, GraphObject::Variables 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, GraphObject::Variables 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<ObjectModel> object) +{ + SharedPtr<PatchModel> patch = PtrCast<PatchModel>(object); + 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); + return; + } + + SharedPtr<NodeModel> node = PtrCast<NodeModel>(object); + if (node) { + PatchWindowMap::iterator w = _patch_windows.find(node->path().parent()); + if (w != _patch_windows.end()) + _node_properties_win->set_transient_for(*w->second); + + _node_properties_win->present(node); + return; + } + + SharedPtr<PortModel> port = PtrCast<PortModel>(object); + if (port) { + PatchWindowMap::iterator w = _patch_windows.find(port->path().parent().parent()); + if (w != _patch_windows.end()) + _patch_properties_win->set_transient_for(*w->second); + + _port_properties_win->present(port); + return; + } +} + + +} // namespace GUI +} // namespace Ingen diff --git a/src/gui/WindowFactory.hpp b/src/gui/WindowFactory.hpp new file mode 100644 index 00000000..618b027c --- /dev/null +++ b/src/gui/WindowFactory.hpp @@ -0,0 +1,109 @@ +/* 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.hpp> +#include "interface/GraphObject.hpp" +#include "client/PatchModel.hpp" +#include "PatchView.hpp" + +using Ingen::Client::PatchModel; +using namespace Ingen::Shared; + +namespace Ingen { +namespace GUI { + +class PatchWindow; +class NodeControlWindow; +class PatchPropertiesWindow; +class NodePropertiesWindow; +class PortPropertiesWindow; +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> model, + PatchWindow* preferred = NULL, + SharedPtr<PatchView> view = SharedPtr<PatchView>()); + + void present_controls(SharedPtr<NodeModel> node); + + void present_load_plugin(SharedPtr<PatchModel> patch, GraphObject::Variables data=GraphObject::Variables()); + void present_load_patch(SharedPtr<PatchModel> patch, GraphObject::Variables data=GraphObject::Variables()); + void present_load_remote_patch(SharedPtr<PatchModel> patch, GraphObject::Variables data=GraphObject::Variables()); + void present_upload_patch(SharedPtr<PatchModel> patch); + void present_new_subpatch(SharedPtr<PatchModel> patch, GraphObject::Variables data=GraphObject::Variables()); + void present_load_subpatch(SharedPtr<PatchModel> patch, GraphObject::Variables data=GraphObject::Variables()); + void present_rename(SharedPtr<ObjectModel> object); + void present_properties(SharedPtr<ObjectModel> object); + + 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; + PatchPropertiesWindow* _patch_properties_win; + NodePropertiesWindow* _node_properties_win; + PortPropertiesWindow* _port_properties_win; + RenameWindow* _rename_win; +}; + + +} // namespace GUI +} // namespace Ingen + +#endif // WINDOW_FACTORY_H diff --git a/src/gui/cmdline.h b/src/gui/cmdline.h new file mode 100644 index 00000000..6f1f9259 --- /dev/null +++ b/src/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/gui/gui.cpp b/src/gui/gui.cpp new file mode 100644 index 00000000..10af6d9b --- /dev/null +++ b/src/gui/gui.cpp @@ -0,0 +1,35 @@ +/* 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 "gui.hpp" +#include "ConnectWindow.hpp" +#include "App.hpp" +#include "Configuration.hpp" + +namespace Ingen { +namespace GUI { + + +void run(int argc, char** argv, Ingen::Shared::World* world) +{ + App::run(argc, argv, world); +} + + +} // namespace GUI +} // namespace Ingen + diff --git a/src/gui/gui.hpp b/src/gui/gui.hpp new file mode 100644 index 00000000..a959f17a --- /dev/null +++ b/src/gui/gui.hpp @@ -0,0 +1,45 @@ +/* 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 INGEN_GUI_H +#define INGEN_GUI_H + +#include CONFIG_H_PATH +#include "module/global.hpp" +#include <raul/SharedPtr.hpp> + +namespace Ingen { + +class Engine; + +namespace Shared { class EngineInterface; } + +namespace GUI { + + +extern "C" { + + void run(int argc, char** argv, Ingen::Shared::World* world); + +} + + +} // namesace GUI +} // namespace Ingen + +#endif // INGEN_GUI_H + diff --git a/src/gui/ingen-icon.svg b/src/gui/ingen-icon.svg new file mode 100644 index 00000000..a15ed7e7 --- /dev/null +++ b/src/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/gui/ingen.svg b/src/gui/ingen.svg new file mode 100644 index 00000000..a15ed7e7 --- /dev/null +++ b/src/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/gui/ingen_gui.glade b/src/gui/ingen_gui.glade new file mode 100644 index 00000000..aa0680ea --- /dev/null +++ b/src/gui/ingen_gui.glade @@ -0,0 +1,3337 @@ +<?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">Ingen</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"/> + <accelerator key="S" modifiers="GDK_SHIFT_MASK | GDK_CONTROL_MASK" signal="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="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 GUI (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_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="tooltip" translatable="yes">Delete the selected object(s)</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"/> + <accelerator key="Delete" modifiers="" signal="activate"/> + </widget> + </child> + <child> + <widget class="GtkImageMenuItem" id="patch_select_all_menuitem"> + <property name="visible">True</property> + <property name="tooltip" translatable="yes">Select all objects in a patch</property> + <property name="label">gtk-select-all</property> + <property name="use_underline">True</property> + <property name="use_stock">True</property> + <signal name="activate" handler="on_patch_select_all_menuitem_activate"/> + <accelerator key="A" modifiers="GDK_CONTROL_MASK" signal="activate"/> + </widget> + </child> + <child> + <widget class="GtkSeparatorMenuItem" id="menuitem1"> + <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_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="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 Patch</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> + <child> + <widget class="GtkSeparatorMenuItem" id="menuitem2"> + <property name="visible">True</property> + </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">C_ontrols...</property> + <property name="use_underline">True</property> + <signal name="activate" handler="on_patch_view_control_window_menuitem_activate"/> + <accelerator key="O" 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> + </widget> + </child> + </widget> + </child> + <child> + <widget class="GtkMenuItem" id="patch_patch_menu"> + <property name="visible">True</property> + <property name="label" translatable="yes">_View</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"/> + <accelerator key="F11" modifiers="" signal="activate"/> + </widget> + </child> + <child> + <widget class="GtkCheckMenuItem" id="patch_human_names_menuitem"> + <property name="visible">True</property> + <property name="tooltip" translatable="yes">Show human readable names instead of identifiers</property> + <property name="label" translatable="yes">_Human names</property> + <property name="use_underline">True</property> + <property name="active">True</property> + <accelerator key="H" modifiers="GDK_CONTROL_MASK" signal="activate"/> + </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> + <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="GtkImageMenuItem" id="help_e_to_edit_menu_item"> + <property name="visible">True</property> + <property name="label" translatable="yes">Press 'e' to toggle edit mode</property> + <property name="use_underline">True</property> + <child internal-child="image"> + <widget class="GtkImage" id="image1"> + <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_AUTOMATIC</property> + <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</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="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> + <property name="response_id">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="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> + <property name="response_id">0</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="response_id">0</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> + <property name="response_id">0</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> + <property name="response_id">0</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="response_id">0</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="response_id">0</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="response_id">0</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="response_id">0</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="response_id">0</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"><b>Polyphony: </b></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"><b>Name: </b></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="response_id">0</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="response_id">0</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="response_id">0</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"><b>Polyphony: </b></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">5</property> + <property name="n_columns">2</property> + <child> + <placeholder/> + </child> + <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="yalign">1</property> + <property name="xpad">4</property> + <property name="label" translatable="yes"><b>Name</b></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="yalign">1</property> + <property name="yscale">0</property> + <property name="bottom_padding">1</property> + <property name="left_padding">1</property> + <property name="right_padding">4</property> + <child> + <widget class="GtkHSeparator" id="hseparator6"> + <property name="visible">True</property> + </widget> + </child> + </widget> + <packing> + <property name="position">1</property> + </packing> + </child> + <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> + <property name="numeric">True</property> + </widget> + <packing> + <property name="expand">False</property> + <property name="position">2</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">9.0923076923076922e+136 -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> + <property name="y_padding">8</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="expand">False</property> + <property name="homogeneous">False</property> + </packing> + </child> + </widget> + </child> + <child> + <widget class="GtkToolbar" id="patch_view_toolbar"> + <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 1 512 1 10 10</property> + <property name="climb_rate">1</property> + <property name="numeric">True</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> + <child> + <widget class="GtkSeparatorToolItem" id="toolbutton1"> + <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> + <packing> + <property name="expand">False</property> + <property name="homogeneous">False</property> + </packing> + </child> + <child> + <widget class="GtkToggleToolButton" id="patch_view_edit_mode_but"> + <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="tooltip" translatable="yes">Enable edit mode to move objects and make connections, disable to play controls ('e' to toggle)</property> + <property name="label" translatable="yes">Edit</property> + <property name="stock_id">gtk-edit</property> + <property name="active">True</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="has_focus">True</property> + <property name="is_focus">True</property> + <property name="can_default">True</property> + <property name="has_default">True</property> + <property name="events">GDK_EXPOSURE_MASK | GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_MOTION_MASK | GDK_BUTTON1_MOTION_MASK | GDK_BUTTON2_MOTION_MASK | GDK_BUTTON3_MOTION_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK | GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK | GDK_FOCUS_CHANGE_MASK | GDK_STRUCTURE_MASK | GDK_PROPERTY_CHANGE_MASK | GDK_VISIBILITY_NOTIFY_MASK | GDK_PROXIMITY_IN_MASK | GDK_PROXIMITY_OUT_MASK | GDK_SUBSTRUCTURE_MASK | GDK_SCROLL_MASK</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="visible">True</property> + <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="response_id">0</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="response_id">0</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> + <child> + <widget class="GtkVBox" id="toggle_control"> + <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="hbox2"> + <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="toggle_control_name_label"> + <property name="visible">True</property> + <property name="xalign">0</property> + <property name="yalign">1</property> + <property name="xpad">4</property> + <property name="label" translatable="yes"><b>Name</b></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="alignment7"> + <property name="visible">True</property> + <property name="yalign">1</property> + <property name="yscale">0</property> + <property name="bottom_padding">1</property> + <property name="left_padding">1</property> + <property name="right_padding">4</property> + <child> + <widget class="GtkHSeparator" id="hseparator7"> + <property name="visible">True</property> + </widget> + </child> + </widget> + <packing> + <property name="position">1</property> + </packing> + </child> + <child> + <widget class="GtkCheckButton" id="toggle_control_check"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="response_id">0</property> + <property name="draw_indicator">True</property> + </widget> + <packing> + <property name="expand">False</property> + <property name="position">2</property> + </packing> + </child> + </widget> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + </widget> + <packing> + <property name="top_attach">4</property> + <property name="bottom_attach">5</property> + <property name="y_padding">8</property> + </packing> + </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 - Ingen</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> + <property name="response_id">0</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> + <property name="response_id">0</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 - Ingen</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"><i>Example: /foo/bar:/home/john/patches:/usr/share/om/patches</i></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"><b>Patch Search Path: </b></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> + <property name="response_id">0</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> + <property name="response_id">0</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> + <property name="response_id">0</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> + <property name="response_id">0</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> + <property name="response_id">0</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> + <property name="response_id">0</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> + <property name="response_id">0</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">8</property> + <property name="title" translatable="yes">Node Properties - Ingen</property> + <property name="window_position">GTK_WIN_POS_MOUSE</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"><b>Node</b></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="response_id">0</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"><b>Plugin</b></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="copyright" translatable="yes">Copyright (C) 2005-2007 Dave Robillard <http://drobilla.net></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 <dave@drobilla.net> + +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 - Ingen</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 - Ingen</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-vbox4"> + <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="expand">False</property> + <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="response_id">0</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="response_id">0</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="response_id">0</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_area4"> + <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> + <property name="response_id">0</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> + <child> + <widget class="GtkMenuItem" id="canvas_menu_add_event_input"> + <property name="visible">True</property> + <property name="tooltip" translatable="yes">Add a generic event input to this patch</property> + <property name="label" translatable="yes">Event</property> + <property name="use_underline">True</property> + <signal name="activate" handler="on_canvas_menu_add_event_input_activate"/> + </widget> + </child> + <child> + <widget class="GtkMenuItem" id="canvas_menu_add_osc_input"> + <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="tooltip" translatable="yes">Add an OSC input to this patch</property> + <property name="label" translatable="yes">OSC</property> + <property name="use_underline">True</property> + </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> + <child> + <widget class="GtkMenuItem" id="canvas_menu_add_event_output"> + <property name="visible">True</property> + <property name="tooltip" translatable="yes">Add a generic event output to this patch</property> + <property name="label" translatable="yes">Event</property> + <property name="use_underline">True</property> + <signal name="activate" handler="on_canvas_menu_add_event_output_activate"/> + </widget> + </child> + <child> + <widget class="GtkMenuItem" id="canvas_menu_add_osc_output"> + <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="tooltip" translatable="yes">Add an OSC output to this patch</property> + <property name="label" translatable="yes">OSC</property> + <property name="use_underline">True</property> + </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-vbox5"> + <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_area5"> + <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-vbox6"> + <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_area6"> + <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="GtkWindow" id="port_properties_win"> + <property name="border_width">8</property> + <property name="title" translatable="yes">Port Properties - Ingen</property> + <property name="resizable">False</property> + <property name="window_position">GTK_WIN_POS_MOUSE</property> + <child> + <widget class="GtkVBox" id="dialog-vbox7"> + <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> + <widget class="GtkHButtonBox" id="dialog-action_area7"> + <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> + <widget class="GtkMenu" id="object_menu"> + <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="GtkCheckMenuItem" id="object_polyphonic_menuitem"> + <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="tooltip" translatable="yes">Expose individual voices</property> + <property name="label" translatable="yes">Polyphonic</property> + <property name="use_underline">True</property> + </widget> + </child> + <child> + <widget class="GtkImageMenuItem" id="object_disconnect_menuitem"> + <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="tooltip" translatable="yes">Disconnect all connections</property> + <property name="label" translatable="yes">gtk-disconnect</property> + <property name="use_underline">True</property> + <property name="use_stock">True</property> + </widget> + </child> + <child> + <widget class="GtkImageMenuItem" id="object_rename_menuitem"> + <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="tooltip" translatable="yes">Rename this object</property> + <property name="label" translatable="yes">Rename...</property> + <property name="use_underline">True</property> + <child internal-child="image"> + <widget class="GtkImage" id="menu-item-image20"> + <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="stock">gtk-find-and-replace</property> + </widget> + </child> + </widget> + </child> + <child> + <widget class="GtkImageMenuItem" id="object_destroy_menuitem"> + <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="tooltip" translatable="yes">Destroy this object</property> + <property name="label" translatable="yes">gtk-delete</property> + <property name="use_underline">True</property> + <property name="use_stock">True</property> + </widget> + </child> + <child> + <widget class="GtkSeparatorMenuItem" id="separator1"> + <property name="visible">True</property> + </widget> + </child> + <child> + <widget class="GtkImageMenuItem" id="object_properties_menuitem"> + <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="tooltip" translatable="yes">View and edit properties</property> + <property name="label" translatable="yes">gtk-properties</property> + <property name="use_underline">True</property> + <property name="use_stock">True</property> + </widget> + </child> + </widget> + <widget class="GtkMenu" id="node_menu"> + <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="GtkImageMenuItem" id="node_controls_menuitem"> + <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="tooltip" translatable="yes">Manipulate controls in a separate window</property> + <property name="label" translatable="yes">Controls...</property> + <property name="use_underline">True</property> + <child internal-child="image"> + <widget class="GtkImage" id="menu-item-image21"> + <property name="stock">gtk-edit</property> + </widget> + </child> + </widget> + </child> + <child> + <widget class="GtkImageMenuItem" id="node_popup_gui_menuitem"> + <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="tooltip" translatable="yes">Show this node's custom graphical interface in a separate window</property> + <property name="label" translatable="yes">GUI...</property> + <property name="use_underline">True</property> + <child internal-child="image"> + <widget class="GtkImage" id="menu-item-image22"> + <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="stock">gtk-edit</property> + </widget> + </child> + </widget> + </child> + <child> + <widget class="GtkCheckMenuItem" id="node_embed_gui_menuitem"> + <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="tooltip" translatable="yes">Embed the custom GUI for this plugin in the patch canvas</property> + <property name="label" translatable="yes">Embed GUI</property> + <property name="use_underline">True</property> + </widget> + </child> + </widget> +</glade-interface> diff --git a/src/gui/ingen_gui.gladep b/src/gui/ingen_gui.gladep new file mode 100644 index 00000000..7cd9c6ce --- /dev/null +++ b/src/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> diff --git a/src/gui/wscript b/src/gui/wscript new file mode 100644 index 00000000..8895c7bc --- /dev/null +++ b/src/gui/wscript @@ -0,0 +1,65 @@ +#!/usr/bin/env python +import Params + +def build(bld): + obj = bld.create_obj('cpp', 'shlib') + obj.source = ''' + App.cpp + BreadCrumbBox.cpp + Configuration.cpp + ConnectWindow.cpp + ControlPanel.cpp + Controls.cpp + GladeFactory.cpp + LoadPatchWindow.cpp + LoadPluginWindow.cpp + LoadRemotePatchWindow.cpp + LoadSubpatchWindow.cpp + MessagesWindow.cpp + NewSubpatchWindow.cpp + NodeControlWindow.cpp + NodeMenu.cpp + NodeModule.cpp + NodePropertiesWindow.cpp + ObjectMenu.cpp + PatchCanvas.cpp + PatchPortModule.cpp + PatchPropertiesWindow.cpp + PatchTreeWindow.cpp + PatchView.cpp + PatchWindow.cpp + Port.cpp + PortMenu.cpp + PortPropertiesWindow.cpp + RenameWindow.cpp + SubpatchModule.cpp + ThreadedLoader.cpp + WindowFactory.cpp + gui.cpp + ''' + + if bld.env()['HAVE_CURL']: + obj.source += 'UploadPatchWindow.cpp' + + dir = Params.g_options.moduledir or bld.env()['PREFIX'] + 'lib/ingen' + + obj.includes = ['..', '../../common', '../..'] + obj.defines = 'INGEN_DATA_DIR=\\\"' + dir + '\\\"' + obj.name = 'libingen_gui' + obj.target = 'ingen_gui' + obj.vnum = '0.0.0' + obj.uselib_local = 'libingen_client libingen_shared' + obj.uselib = ''' + CURL + FLOWCANVAS + GLADEMM + GLIBMM + GNOMECANVASMM + GTKMM + RAUL + REDLANDMM + SIGCPP + SLV2 + SOUP + XML2 + ''' |